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 default:
147 for (;;) {
148 wpid = waitpid(pid, &status, 0);
149 if (wpid == (pid_t)-1) {
150 if (errno == EINTR)
151 continue;
152 else
153 return (1);
154 } else {
155 break;
156 }
157 }
158 break;
159 }
160
161 return ((WIFEXITED(status) == 0) ? 1 : WEXITSTATUS(status));
162 }
163
164
165 /*
166 * _update_devalloc_db
167 * Forms allocatable device entries to be written to device_allocate and
168 * device_maps.
169 *
170 * Or finds the correct entry to remove, and removes it.
171 *
172 * Note: devname is a /devices link in the REMOVE case.
173 */
174 /* ARGSUSED */
175 void
_update_devalloc_db(devlist_t * devlist,int devflag,int action,char * devname,char * root_dir)176 _update_devalloc_db(devlist_t *devlist, int devflag, int action, char *devname,
177 char *root_dir)
178 {
179 int i;
180 deventry_t *entry = NULL, *dentry = NULL;
181 char *typestring;
182 char *nickname; /* typestring + instance */
183
184 if (action == DA_ADD) {
185 for (i = 0; i < DA_COUNT; i++) {
186 switch (i) {
187 case 0:
188 dentry = devlist->audio;
189 break;
190 case 1:
191 dentry = devlist->cd;
192 break;
193 case 2:
194 dentry = devlist->floppy;
195 break;
196 case 3:
197 dentry = devlist->tape;
198 break;
199 case 4:
200 dentry = devlist->rmdisk;
201 break;
202 default:
203 return;
204 }
205 if (dentry)
206 (void) _update_dev(dentry, action, NULL, NULL,
207 NULL);
208 }
209 } else if (action == DA_REMOVE) {
210 if (devflag & DA_AUDIO) {
211 dentry = devlist->audio;
212 typestring = DA_AUDIO_TYPE;
213 } else if (devflag & DA_CD) {
214 dentry = devlist->cd;
215 typestring = DA_CD_TYPE;
216 } else if (devflag & DA_FLOPPY) {
217 dentry = devlist->floppy;
218 typestring = DA_FLOPPY_TYPE;
219 } else if (devflag & DA_TAPE) {
220 dentry = devlist->tape;
221 typestring = DA_TAPE_TYPE;
222 } else if (devflag & DA_RMDISK) {
223 dentry = devlist->rmdisk;
224 typestring = DA_RMDISK_TYPE;
225 } else
226 return;
227
228 if (event_driven) {
229 nickname = _update_dev(NULL, action, typestring, NULL,
230 devname);
231
232 if (nickname != NULL) {
233 (void) da_rm_list_entry(devlist, devname,
234 devflag, nickname);
235 free(nickname);
236 }
237 return;
238 }
239 /*
240 * Not reached as of now, could be reached if devfsadm is
241 * enhanced to clean up devalloc database more thoroughly.
242 * Will not reliably match for event-driven removes
243 */
244 for (entry = dentry; entry != NULL; entry = entry->next) {
245 if (strcmp(entry->devinfo.devname, devname) == 0)
246 break;
247 }
248 (void) _update_dev(entry, action, NULL, devname, NULL);
249 }
250 }
251
252 /*
253 * _update_dev: Update device_allocate and/or device_maps files
254 *
255 * If adding a device:
256 * dentry: A linked list of allocatable devices
257 * action: DA_ADD or DA_REMOVE
258 * devtype: type of device linked list to update on removal
259 * devname: short name (i.e. rmdisk5, cdrom0) of device if known
260 * rm_link: name of real /device from hot_cleanup
261 *
262 * If the action is ADD or if the action is triggered by an event
263 * from syseventd, read the files FIRST and treat their data as
264 * more-accurate than the dentry list, adjusting dentry contents if needed.
265 *
266 * For DA_ADD, try to add each device in the list to the files.
267 *
268 * If the action is DA_REMOVE and not a hotplug remove, adjust the files
269 * as indicated by the linked list.
270 *
271 * RETURNS:
272 * If we successfully remove a device from the files, returns
273 * a char * to strdup'd devname of the device removed.
274 *
275 * The caller is responsible for freeing the return value.
276 *
277 * NULL for all other cases, both success and failure.
278 *
279 */
280 static char *
_update_dev(deventry_t * dentry,int action,const char * devtype,char * devname,char * rm_link)281 _update_dev(deventry_t *dentry, int action, const char *devtype, char *devname,
282 char *rm_link)
283 {
284 da_args dargs;
285 deventry_t newentry, *entry;
286 int status;
287
288 dargs.rootdir = NULL;
289 dargs.devnames = NULL;
290
291 if (event_driven)
292 dargs.optflag = DA_EVENT;
293 else
294 dargs.optflag = 0;
295
296 if (action == DA_ADD) {
297 dargs.optflag |= DA_ADD;
298 /*
299 * Add Events do not have enough information to overrride the
300 * existing file contents.
301 */
302
303 for (entry = dentry; entry != NULL; entry = entry->next) {
304 dargs.devinfo = &(entry->devinfo);
305 (void) da_update_device(&dargs);
306 }
307 } else if (action == DA_REMOVE) {
308 dargs.optflag |= DA_REMOVE;
309 if (dentry) {
310 entry = dentry;
311 } else if (dargs.optflag & DA_EVENT) {
312 if (devname == NULL)
313 newentry.devinfo.devname = NULL;
314 else
315 newentry.devinfo.devname = strdup(devname);
316 newentry.devinfo.devtype = (char *)devtype;
317 newentry.devinfo.devauths =
318 newentry.devinfo.devopts =
319 newentry.devinfo.devexec = NULL;
320 newentry.devinfo.devlist = strdup(rm_link);
321 newentry.devinfo.instance = 0;
322 newentry.next = NULL;
323 entry = &newentry;
324 } else {
325 newentry.devinfo.devname = strdup(devname);
326 newentry.devinfo.devtype = (char *)devtype;
327 newentry.devinfo.devauths =
328 newentry.devinfo.devexec =
329 newentry.devinfo.devopts =
330 newentry.devinfo.devlist = NULL;
331 newentry.devinfo.instance = 0;
332 newentry.next = NULL;
333 entry = &newentry;
334 }
335 dargs.devinfo = &(entry->devinfo);
336 /*
337 * da_update_device will fill in entry devname if
338 * event_driven is true and device is in the file
339 */
340 status = da_update_device(&dargs);
341 if (event_driven)
342 if (newentry.devinfo.devlist != NULL)
343 free(newentry.devinfo.devlist);
344 if (status == 0)
345 return (dargs.devinfo->devname);
346 else free(dargs.devinfo->devname);
347 }
348 return (NULL);
349 }
350