xref: /illumos-gate/usr/src/cmd/devfsadm/devalloc.c (revision 27954b0d964ffcb749cf19296906e7fecdf3da1b)
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
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
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
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
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 *
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