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