Home | History | Annotate | Download | only in devfsadm
      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