1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Device allocation related work.
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/dkio.h>
41 #include <sys/wait.h>
42 #include <bsm/devalloc.h>
43
44 #define DEALLOCATE "/usr/sbin/deallocate"
45 #define MKDEVALLOC "/usr/sbin/mkdevalloc"
46
47 static char *_update_dev(deventry_t *, int, const char *, char *, char *);
48 static int _make_db();
49 extern int event_driven;
50
51
52 /*
53 * _da_check_for_usb
54 * returns 1 if device pointed by 'link' is a removable hotplugged disk,
55 * else returns 0.
56 */
57 int
_da_check_for_usb(char * link,char * root_dir)58 _da_check_for_usb(char *link, char *root_dir)
59 {
60 int fd = -1;
61 int len, dstsize;
62 int removable = 0;
63 int hotpluggable = 0;
64 char *p = NULL;
65 char path[MAXPATHLEN + 4];
66 char rpath[MAXPATHLEN + 4]; /* for ",raw" */
67
68 dstsize = sizeof (path);
69 if (strcmp(root_dir, "") != 0) {
70 if (strlcat(path, root_dir, dstsize) >= dstsize)
71 return (0);
72 len = strlen(path);
73 } else {
74 len = 0;
75 }
76 (void) snprintf(path, dstsize - len, "%s", link);
77 if ((p = realpath(path, rpath)) == NULL) {
78 p = path;
79 } else {
80 if (strstr(link, "rdsk")) {
81 p = rpath;
82 } else {
83 (void) snprintf(path, dstsize, "%s%s", rpath, ",raw");
84 p = path;
85 }
86 }
87 if ((fd = open(p, O_RDONLY | O_NONBLOCK)) < 0)
88 return (0);
89 (void) ioctl(fd, DKIOCREMOVABLE, &removable);
90 (void) ioctl(fd, DKIOCHOTPLUGGABLE, &hotpluggable);
91 (void) close(fd);
92
93 if (removable && hotpluggable)
94 return (1);
95
96 return (0);
97 }
98
99 /*
100 * _reset_devalloc
101 * If device allocation is being turned on, creates device_allocate
102 * device_maps if they do not exist.
103 * Puts DEVICE_ALLOCATION=ON/OFF in device_allocate to indicate if
104 * device allocation is on/off.
105 */
106 void
_reset_devalloc(int action)107 _reset_devalloc(int action)
108 {
109 da_args dargs;
110
111 if (action == DA_ON)
112 (void) _make_db();
113 else if ((action == DA_OFF) && (open(DEVALLOC, O_RDONLY) == -1))
114 return;
115
116 if (action == DA_ON)
117 dargs.optflag = DA_ON;
118 else if (action == DA_OFF)
119 dargs.optflag = DA_OFF | DA_ALLOC_ONLY;
120
121 dargs.rootdir = NULL;
122 dargs.devnames = NULL;
123 dargs.devinfo = NULL;
124
125 (void) da_update_device(&dargs);
126 }
127
128 /*
129 * _make_db
130 * execs /usr/sbin/mkdevalloc to create device_allocate and
131 * device_maps.
132 */
133 static int
_make_db()134 _make_db()
135 {
136 int status;
137 pid_t pid, wpid;
138
139 pid = vfork();
140 switch (pid) {
141 case -1:
142 return (1);
143 case 0:
144 if (execl(MKDEVALLOC, MKDEVALLOC, DA_IS_LABELED, NULL) == -1)
145 exit((errno == ENOENT) ? 0 : 1);
146 return (1);
147 default:
148 for (;;) {
149 wpid = waitpid(pid, &status, 0);
150 if (wpid == (pid_t)-1) {
151 if (errno == EINTR)
152 continue;
153 else
154 return (1);
155 } else {
156 break;
157 }
158 }
159 break;
160 }
161
162 return ((WIFEXITED(status) == 0) ? 1 : WEXITSTATUS(status));
163 }
164
165
166 /*
167 * _update_devalloc_db
168 * Forms allocatable device entries to be written to device_allocate and
169 * device_maps.
170 *
171 * Or finds the correct entry to remove, and removes it.
172 *
173 * Note: devname is a /devices link in the REMOVE case.
174 */
175 /* ARGSUSED */
176 void
_update_devalloc_db(devlist_t * devlist,int devflag,int action,char * devname,char * root_dir)177 _update_devalloc_db(devlist_t *devlist, int devflag, int action, char *devname,
178 char *root_dir)
179 {
180 int i;
181 deventry_t *entry = NULL, *dentry = NULL;
182 char *typestring;
183 char *nickname; /* typestring + instance */
184
185 if (action == DA_ADD) {
186 for (i = 0; i < DA_COUNT; i++) {
187 switch (i) {
188 case 0:
189 dentry = devlist->audio;
190 break;
191 case 1:
192 dentry = devlist->cd;
193 break;
194 case 2:
195 dentry = devlist->floppy;
196 break;
197 case 3:
198 dentry = devlist->tape;
199 break;
200 case 4:
201 dentry = devlist->rmdisk;
202 break;
203 default:
204 return;
205 }
206 if (dentry)
207 (void) _update_dev(dentry, action, NULL, NULL,
208 NULL);
209 }
210 } else if (action == DA_REMOVE) {
211 if (devflag & DA_AUDIO) {
212 dentry = devlist->audio;
213 typestring = DA_AUDIO_TYPE;
214 } else if (devflag & DA_CD) {
215 dentry = devlist->cd;
216 typestring = DA_CD_TYPE;
217 } else if (devflag & DA_FLOPPY) {
218 dentry = devlist->floppy;
219 typestring = DA_FLOPPY_TYPE;
220 } else if (devflag & DA_TAPE) {
221 dentry = devlist->tape;
222 typestring = DA_TAPE_TYPE;
223 } else if (devflag & DA_RMDISK) {
224 dentry = devlist->rmdisk;
225 typestring = DA_RMDISK_TYPE;
226 } else
227 return;
228
229 if (event_driven) {
230 nickname = _update_dev(NULL, action, typestring, NULL,
231 devname);
232
233 if (nickname != NULL) {
234 (void) da_rm_list_entry(devlist, devname,
235 devflag, nickname);
236 free(nickname);
237 }
238 return;
239 }
240 /*
241 * Not reached as of now, could be reached if devfsadm is
242 * enhanced to clean up devalloc database more thoroughly.
243 * Will not reliably match for event-driven removes
244 */
245 for (entry = dentry; entry != NULL; entry = entry->next) {
246 if (strcmp(entry->devinfo.devname, devname) == 0)
247 break;
248 }
249 (void) _update_dev(entry, action, NULL, devname, NULL);
250 }
251 }
252
253 /*
254 * _update_dev: Update device_allocate and/or device_maps files
255 *
256 * If adding a device:
257 * dentry: A linked list of allocatable devices
258 * action: DA_ADD or DA_REMOVE
259 * devtype: type of device linked list to update on removal
260 * devname: short name (i.e. rmdisk5, cdrom0) of device if known
261 * rm_link: name of real /device from hot_cleanup
262 *
263 * If the action is ADD or if the action is triggered by an event
264 * from syseventd, read the files FIRST and treat their data as
265 * more-accurate than the dentry list, adjusting dentry contents if needed.
266 *
267 * For DA_ADD, try to add each device in the list to the files.
268 *
269 * If the action is DA_REMOVE and not a hotplug remove, adjust the files
270 * as indicated by the linked list.
271 *
272 * RETURNS:
273 * If we successfully remove a device from the files, returns
274 * a char * to strdup'd devname of the device removed.
275 *
276 * The caller is responsible for freeing the return value.
277 *
278 * NULL for all other cases, both success and failure.
279 *
280 */
281 static char *
_update_dev(deventry_t * dentry,int action,const char * devtype,char * devname,char * rm_link)282 _update_dev(deventry_t *dentry, int action, const char *devtype, char *devname,
283 char *rm_link)
284 {
285 da_args dargs;
286 deventry_t newentry, *entry;
287 int status;
288
289 dargs.rootdir = NULL;
290 dargs.devnames = NULL;
291
292 if (event_driven)
293 dargs.optflag = DA_EVENT;
294 else
295 dargs.optflag = 0;
296
297 if (action == DA_ADD) {
298 dargs.optflag |= DA_ADD;
299 /*
300 * Add Events do not have enough information to overrride the
301 * existing file contents.
302 */
303
304 for (entry = dentry; entry != NULL; entry = entry->next) {
305 dargs.devinfo = &(entry->devinfo);
306 (void) da_update_device(&dargs);
307 }
308 } else if (action == DA_REMOVE) {
309 dargs.optflag |= DA_REMOVE;
310 if (dentry) {
311 entry = dentry;
312 } else if (dargs.optflag & DA_EVENT) {
313 if (devname == NULL)
314 newentry.devinfo.devname = NULL;
315 else
316 newentry.devinfo.devname = strdup(devname);
317 newentry.devinfo.devtype = (char *)devtype;
318 newentry.devinfo.devauths =
319 newentry.devinfo.devopts =
320 newentry.devinfo.devexec = NULL;
321 newentry.devinfo.devlist = strdup(rm_link);
322 newentry.devinfo.instance = 0;
323 newentry.next = NULL;
324 entry = &newentry;
325 } else {
326 newentry.devinfo.devname = strdup(devname);
327 newentry.devinfo.devtype = (char *)devtype;
328 newentry.devinfo.devauths =
329 newentry.devinfo.devexec =
330 newentry.devinfo.devopts =
331 newentry.devinfo.devlist = NULL;
332 newentry.devinfo.instance = 0;
333 newentry.next = NULL;
334 entry = &newentry;
335 }
336 dargs.devinfo = &(entry->devinfo);
337 /*
338 * da_update_device will fill in entry devname if
339 * event_driven is true and device is in the file
340 */
341 status = da_update_device(&dargs);
342 if (event_driven)
343 if (newentry.devinfo.devlist != NULL)
344 free(newentry.devinfo.devlist);
345 if (status == 0)
346 return (dargs.devinfo->devname);
347 else free(dargs.devinfo->devname);
348 }
349 return (NULL);
350 }
351