6855420 TX: USB built-in floppy drive should automatically populate the Device Manager
authorJan Parcel <Jan.Parcel@Sun.COM>
Thu, 14 Jan 2010 17:22:31 -0800
changeset 11500 cfac7cfbdcb2
parent 11499 6190684fb6c4
child 11501 645ae1441739
6855420 TX: USB built-in floppy drive should automatically populate the Device Manager 6868082 TX: devfsadm must rely on device_maps file with multiple hot-pluggable usb devices 6885946 libbssm or libdevinfo .h changes not reflected upon make install
usr/src/cmd/allocate/mkdevalloc.c
usr/src/cmd/devfsadm/devalloc.c
usr/src/cmd/devfsadm/devfsadm.c
usr/src/lib/libbsm/Makefile
usr/src/lib/libbsm/Makefile.com
usr/src/lib/libbsm/common/devalloc.c
usr/src/lib/libbsm/common/devalloc.h
usr/src/lib/libbsm/common/getdment.c
usr/src/lib/libbsm/common/mapfile-vers
usr/src/lib/libdevinfo/Makefile
usr/src/lib/libdevinfo/device_info.h
usr/src/lib/libdevinfo/devinfo_devperm.c
usr/src/lib/libdevinfo/mapfile-vers
--- a/usr/src/cmd/allocate/mkdevalloc.c	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/cmd/allocate/mkdevalloc.c	Thu Jan 14 17:22:31 2010 -0800
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * scan /dev directory for mountable objects and construct device_allocate
  * file for allocate....
@@ -94,7 +92,7 @@
 #define	DFLT_NTAPE  10		/* size of initial array */
 #define	SIZE_OF_RST  3		/* |rmt| */
 #define	SIZE_OF_NRST 4		/* |nrmt| */
-#define	SIZE_OF_TMP  4		/* |/tmp| */
+#define	SIZE_OF_TMP 4		/* |/tmp| */
 #define	SIZE_OF_RMT  8		/* |/dev/rmt| */
 #define	TAPE_CLEAN    SECLIB"/st_clean"
 
@@ -909,7 +907,7 @@
 			continue;
 
 		/* get device # (disk #) */
-		if (sscanf(dep->d_name, "c%dt%d", &ctrl, &id) <= 0)
+		if (sscanf(dep->d_name, "c%dt%d", &ctrl, &id) != 2)
 			continue;
 
 		/* see if this is one of the cd special devices */
@@ -1117,7 +1115,7 @@
 			continue;
 
 		/* get device # (disk #) */
-		if (sscanf(dep->d_name, "c%dt%d", &ctrl, &id) <= 0)
+		if (sscanf(dep->d_name, "c%dt%d", &ctrl, &id) != 2)
 			continue;
 
 		/* see if we've already examined this device */
@@ -1165,6 +1163,9 @@
 			/* will exit(1) if insufficient memory */
 			nrmdisk = expandmem(i, (void **)&rmdisk,
 			    sizeof (struct rmdisk));
+			/* When we expand rmdisk, need to expand rmdisk_r */
+			(void) expandmem(i, (void **)&rmdisk_r,
+			    sizeof (struct rmdisk));
 		}
 		nm = (char *)malloc(SIZE_OF_DSK + 1 + strlen(dep->d_name) + 1);
 		if (nm == NULL)
--- a/usr/src/cmd/devfsadm/devalloc.c	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/cmd/devfsadm/devalloc.c	Thu Jan 14 17:22:31 2010 -0800
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Device allocation related work.
  */
@@ -46,8 +44,9 @@
 #define	DEALLOCATE	 "/usr/sbin/deallocate"
 #define	MKDEVALLOC	"/usr/sbin/mkdevalloc"
 
-static void _update_dev(deventry_t *, int, char *);
+static char *_update_dev(deventry_t *, int, const char *, char *, char *);
 static int _make_db();
+extern int event_driven;
 
 
 /*
@@ -167,6 +166,10 @@
  * _update_devalloc_db
  * 	Forms allocatable device entries to be written to device_allocate and
  *	device_maps.
+ *
+ *      Or finds the correct entry to remove, and removes it.
+ *
+ *    Note: devname is a /devices link in the REMOVE case.
  */
 /* ARGSUSED */
 void
@@ -175,6 +178,8 @@
 {
 	int		i;
 	deventry_t	*entry = NULL, *dentry = NULL;
+	char 		*typestring;
+	char 		*nickname;  /* typestring + instance */
 
 	if (action == DA_ADD) {
 		for (i = 0; i < DA_COUNT; i++) {
@@ -198,61 +203,147 @@
 				return;
 			}
 			if (dentry)
-				_update_dev(dentry, action, NULL);
+				(void) _update_dev(dentry, action, NULL, NULL,
+				    NULL);
 		}
 	} else if (action == DA_REMOVE) {
-		if (devflag & DA_AUDIO)
+		if (devflag & DA_AUDIO) {
 			dentry = devlist->audio;
-		else if (devflag & DA_CD)
+			typestring = DA_AUDIO_TYPE;
+		} else if (devflag & DA_CD) {
 			dentry = devlist->cd;
-		else if (devflag & DA_FLOPPY)
+			typestring = DA_CD_TYPE;
+		} else if (devflag & DA_FLOPPY) {
 			dentry = devlist->floppy;
-		else if (devflag & DA_TAPE)
+			typestring = DA_FLOPPY_TYPE;
+		} else if (devflag & DA_TAPE) {
 			dentry = devlist->tape;
-		else if (devflag & DA_RMDISK)
+			typestring = DA_TAPE_TYPE;
+		} else if (devflag & DA_RMDISK) {
 			dentry = devlist->rmdisk;
-		else
+			typestring = DA_RMDISK_TYPE;
+		} else
 			return;
 
+		if (event_driven) {
+			nickname = _update_dev(NULL, action, typestring, NULL,
+			    devname);
+
+			if (nickname != NULL) {
+				(void) da_rm_list_entry(devlist, devname,
+				    devflag, nickname);
+				free(nickname);
+			}
+			return;
+		}
+		/*
+		 * Not reached as of now, could be reached if devfsadm is
+		 * enhanced to clean up devalloc database more thoroughly.
+		 * Will not reliably match for event-driven removes
+		 */
 		for (entry = dentry; entry != NULL; entry = entry->next) {
 			if (strcmp(entry->devinfo.devname, devname) == 0)
 				break;
 		}
-		_update_dev(entry, action, devname);
+		(void) _update_dev(entry, action, NULL, devname, NULL);
 	}
 }
 
-static void
-_update_dev(deventry_t *dentry, int action, char *devname)
+/*
+ *	_update_dev: Update device_allocate and/or device_maps files
+ *
+ *      If adding a device:
+ *	    dentry:	A linked list of allocatable devices
+ *	    action:	DA_ADD or DA_REMOVE
+ *	    devtype:	type of device linked list to update on removal
+ *	    devname:	short name (i.e. rmdisk5, cdrom0)  of device if known
+ *	    rm_link:	name of real /device from hot_cleanup
+ *
+ *	If the action is ADD or if the action is triggered by an event
+ *      from syseventd,  read the files FIRST and treat their data as
+ *      more-accurate than the dentry list, adjusting dentry contents if needed.
+ *
+ *	For DA_ADD, try to add each device in the list to the files.
+ *
+ *      If the action is DA_REMOVE and not a hotplug remove, adjust the files
+ *	as indicated by the linked list.
+ *
+ *	RETURNS:
+ *          If we successfully remove a device from the files,  returns
+ *          a char * to strdup'd devname of the device removed.
+ *
+ *	    The caller is responsible for freeing the return value.
+ *
+ *	NULL for all other cases, both success and failure.
+ *
+ */
+static char *
+_update_dev(deventry_t *dentry, int action, const char *devtype, char *devname,
+    char *rm_link)
 {
 	da_args		dargs;
 	deventry_t	newentry, *entry;
+	int status;
 
 	dargs.rootdir = NULL;
 	dargs.devnames = NULL;
 
+	if (event_driven)
+		dargs.optflag = DA_EVENT;
+	else
+		dargs.optflag = 0;
+
 	if (action == DA_ADD) {
-		dargs.optflag = DA_ADD | DA_FORCE;
+		dargs.optflag |= DA_ADD;
+		/*
+		 * Add Events do not have enough information to overrride the
+		 * existing file contents.
+		 */
+
 		for (entry = dentry; entry != NULL; entry = entry->next) {
 			dargs.devinfo = &(entry->devinfo);
 			(void) da_update_device(&dargs);
 		}
 	} else if (action == DA_REMOVE) {
-		dargs.optflag = DA_REMOVE;
+		dargs.optflag |= DA_REMOVE;
 		if (dentry) {
 			entry = dentry;
+		} else if (dargs.optflag & DA_EVENT) {
+			if (devname == NULL)
+				newentry.devinfo.devname = NULL;
+			else
+				newentry.devinfo.devname = strdup(devname);
+			newentry.devinfo.devtype = (char *)devtype;
+			newentry.devinfo.devauths =
+			    newentry.devinfo.devopts =
+			    newentry.devinfo.devexec = NULL;
+			newentry.devinfo.devlist = strdup(rm_link);
+			newentry.devinfo.instance = 0;
+			newentry.next = NULL;
+			entry = &newentry;
 		} else {
 			newentry.devinfo.devname = strdup(devname);
-			newentry.devinfo.devtype =
+			newentry.devinfo.devtype = (char *)devtype;
 			newentry.devinfo.devauths =
-			newentry.devinfo.devexec =
-			newentry.devinfo.devopts =
-			newentry.devinfo.devlist = NULL;
+			    newentry.devinfo.devexec =
+			    newentry.devinfo.devopts =
+			    newentry.devinfo.devlist = NULL;
 			newentry.devinfo.instance = 0;
 			newentry.next = NULL;
 			entry = &newentry;
 		}
 		dargs.devinfo = &(entry->devinfo);
-		(void) da_update_device(&dargs);
+		/*
+		 * da_update_device will fill in entry devname if
+		 * event_driven is true and device is in the file
+		 */
+		status = da_update_device(&dargs);
+		if (event_driven)
+			if (newentry.devinfo.devlist != NULL)
+				free(newentry.devinfo.devlist);
+		if (status == 0)
+			return (dargs.devinfo->devname);
+		else free(dargs.devinfo->devname);
 	}
+	return (NULL);
 }
--- a/usr/src/cmd/devfsadm/devfsadm.c	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/cmd/devfsadm/devfsadm.c	Thu Jan 14 17:22:31 2010 -0800
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -96,6 +96,9 @@
 /* set if invoked via /usr/lib/devfsadm/devfsadmd */
 static int daemon_mode = FALSE;
 
+/* set if event_handler triggered */
+int event_driven = FALSE;
+
 /* output directed to syslog during daemon mode if set */
 static int logflag = FALSE;
 
@@ -1645,6 +1648,12 @@
 	int instance;
 	int branch_event = 0;
 
+	/*
+	 * If this is event-driven, then we cannot trust the static devlist
+	 * to be correct.
+	 */
+
+	event_driven = TRUE;
 	subclass = sysevent_get_subclass_name(ev);
 	vprint(EVENT_MID, "event_handler: %s id:0X%llx\n",
 	    subclass, sysevent_get_seq(ev));
@@ -4307,22 +4316,22 @@
 
 	/* update device allocation database */
 	if (system_labeled) {
-		int	ret = 0;
 		int	devtype = 0;
-		char	devname[MAXNAMELEN];
-
-		devname[0] = '\0';
-		if (strstr(node_path, DA_SOUND_NAME))
+
+		if (strstr(path, DA_SOUND_NAME))
 			devtype = DA_AUDIO;
-		else if (strstr(node_path, "disk"))
+		else if (strstr(path, "storage"))
+			devtype = DA_RMDISK;
+		else if (strstr(path, "disk"))
+			devtype = DA_RMDISK;
+		else if (strstr(path, "floppy"))
+			/* TODO: detect usb cds and floppies at insert time */
 			devtype = DA_RMDISK;
 		else
 			goto out;
-		ret = da_remove_list(&devlist, NULL, devtype, devname,
-		    sizeof (devname));
-		if (ret != -1)
-			(void) _update_devalloc_db(&devlist, devtype, DA_REMOVE,
-			    devname, root_dir);
+
+		(void) _update_devalloc_db(&devlist, devtype, DA_REMOVE,
+		    node_path, root_dir);
 	}
 
 out:
--- a/usr/src/lib/libbsm/Makefile	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/lib/libbsm/Makefile	Thu Jan 14 17:22:31 2010 -0800
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -59,7 +59,7 @@
 $(ROOTHDRS) := 	FILEMODE = 0644
 $(ROOTCHDRS) :=	FILEMODE = 0644
 
-all install lint package: $(GENSRCS) $(SUBDIRS)
+all install lint package: install_h $(GENSRCS) $(SUBDIRS)
 clean clobber delete: $(SUBDIRS)
 
 #
--- a/usr/src/lib/libbsm/Makefile.com	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/lib/libbsm/Makefile.com	Thu Jan 14 17:22:31 2010 -0800
@@ -19,11 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 LIBRARY =	libbsm.a
 VERS = 		.1
@@ -88,7 +86,7 @@
 CLEANFILES +=	$(LINTOUT) $(LINTLIB)
 
 CFLAGS	+=	$(CCVERBOSE)
-LDLIBS +=	-lsocket -lnsl -lmd -lc -lsecdb -ltsol -linetutil
+LDLIBS +=	-lsocket -lnsl -lmd -lc -ldevinfo -lsecdb -ltsol -linetutil
 
 COMDIR=		../common
 
--- a/usr/src/lib/libbsm/common/devalloc.c	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/lib/libbsm/common/devalloc.c	Thu Jan 14 17:22:31 2010 -0800
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdlib.h>
 #include <ctype.h>
 #include <unistd.h>
@@ -40,6 +38,7 @@
 #include <libintl.h>
 #include <errno.h>
 #include <auth_list.h>
+#include <syslog.h>
 #include <bsm/devices.h>
 #include <bsm/devalloc.h>
 
@@ -53,6 +52,10 @@
 extern int da_match(devalloc_t *, da_args *);
 extern int dmap_matchname(devmap_t *, char *);
 extern int dm_match(devmap_t *, da_args *);
+extern int dmap_matchtype(devmap_t *dmap, char *type);
+extern int dmap_matchdev(devmap_t *dmap, char *dev);
+extern int dmap_exact_dev(devmap_t *dmap, char *dev, int *num);
+extern char *dmap_physname(devmap_t *dmap);
 
 /*
  * The following structure is for recording old entries to be retained.
@@ -332,7 +335,7 @@
  */
 /*ARGSUSED*/
 static int
-_dmap2str(da_args *dargs, devmap_t *dmp, char *buf, int size, const char *sep)
+_dmap2str(devmap_t *dmp, char *buf, int size, const char *sep)
 {
 	int	length;
 
@@ -356,13 +359,13 @@
  *	returns pointer to decoded entry, NULL on error.
  */
 static strentry_t *
-_dmap2strentry(da_args *dargs, devmap_t *devmapp)
+_dmap2strentry(devmap_t *devmapp)
 {
 	strentry_t	*sep;
 
 	if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
 		return (NULL);
-	if (_dmap2str(dargs, devmapp, sep->se_str, sizeof (sep->se_str),
+	if (_dmap2str(devmapp, sep->se_str, sizeof (sep->se_str),
 	    KV_TOKEN_DELIMIT"\\\n\t") != 0) {
 		free(sep);
 		return (NULL);
@@ -562,8 +565,216 @@
 }
 
 /*
+ * _rebuild_lists -
+ *
+ *      If dargs->optflag & DA_EVENT, does not assume the dargs list is
+ *      complete or completely believable, since devfsadm caches
+ *      ONLY what it has been exposed to via syseventd.
+ *
+ *	Cycles through all the entries in the /etc files, stores them
+ *      in memory, takes note of device->dname numbers (i.e. rmdisk0,
+ *      rmdisk12)
+ *
+ *      Cycles through again, adds dargs entry
+ *	with the name tname%d (lowest unused number for the device type)
+ *	to the list of things for the caller to write out to a file,
+ *      IFF it is a new entry.
+ *
+ *      It is an error for it to already be there.
+ *
+ *	Add:
+ *         Returns 0 if successful and 2 on error.
+ *      Remove:
+ *         Returns 0 if not found, 1 if found,  2 on error.
+ */
+static int
+_rebuild_lists(da_args *dargs, strentry_t **head_devallocp,
+    strentry_t **head_devmapp)
+{
+	int		rc = 0;
+	devalloc_t	*devallocp;
+	devmap_t	*devmapp;
+	strentry_t	*tail_str;
+	strentry_t	*tmp_str;
+	uint64_t	tmp_bitmap = 0;
+	int		tmp = 0;
+	char		new_devname[DA_MAXNAME + 1];
+	char		errmsg[DA_MAXNAME + 1 + (PATH_MAX * 2) + 80];
+	char		*realname;
+	int		suffix = DA_MAX_DEVNO + 1;
+	int		found = 0;
+
+	if (dargs->optflag & (DA_MAPS_ONLY | DA_ALLOC_ONLY))
+		return (2);
+
+	if (dargs->optflag & DA_FORCE)
+		return (2);
+
+	/* read both files, maps first so we can compare actual devices */
+
+	/* build device_maps */
+	setdmapent();
+	while ((devmapp = getdmapent()) != NULL) {
+		if ((rc = dmap_matchtype(devmapp, dargs->devinfo->devtype))
+		    == 1) {
+			if (dargs->optflag & DA_REMOVE) {
+				if ((devmapp->dmap_devarray == NULL) ||
+				    (devmapp->dmap_devarray[0] == NULL)) {
+					freedmapent(devmapp);
+					enddmapent();
+					return (2);
+				}
+				realname = dmap_physname(devmapp);
+				if (realname == NULL) {
+					freedmapent(devmapp);
+					enddmapent();
+					return (2);
+				}
+				if (strstr(realname, dargs->devinfo->devlist)
+				    != NULL) {
+					if (dargs->devinfo->devname != NULL)
+						free(dargs->devinfo->devname);
+					dargs->devinfo->devname =
+					    strdup(devmapp->dmap_devname);
+					found = 1;
+					freedmapent(devmapp);
+					continue; /* don't retain */
+				}
+			} else if (dargs->optflag & DA_ADD) {
+				/*
+				 * Need to know which suffixes are in use
+				 */
+				rc = (dmap_exact_dev(devmapp,
+				    dargs->devinfo->devlist, &suffix));
+
+				if (rc == 0) {
+					/*
+					 * Same type, different device.  Record
+					 * device suffix already in use.
+					 */
+					if (suffix > DA_MAX_DEVNO) {
+						freedmapent(devmapp);
+						enddmapent();
+						return (2);
+					}
+					tmp_bitmap |= (uint64_t)(1LL << suffix);
+				} else {
+					/*
+					 * Match on add is an error
+					 * or mapping attempt returned error
+					 */
+					freedmapent(devmapp);
+					enddmapent();
+					return (2);
+				}
+			} else
+				/* add other transaction types as needed */
+				return (2);
+
+		}  /* if same type */
+
+		tmp_str = _dmap2strentry(devmapp);
+		if (tmp_str == NULL) {
+			freedmapent(devmapp);
+			enddmapent();
+			return (2);
+		}
+		/* retaining devmap entry: tmp_str->se_str */
+		tmp_str->se_next = NULL;
+		if (*head_devmapp == NULL) {
+			*head_devmapp = tail_str = tmp_str;
+		} else {
+			tail_str->se_next = tmp_str;
+			tail_str = tmp_str;
+		}
+		freedmapent(devmapp);
+	}
+	enddmapent();
+
+	/*
+	 * No need to rewrite the files if the item to be removed is not
+	 * in the files -- wait for another call on another darg.
+	 */
+	if ((dargs->optflag & DA_REMOVE) && !found)
+		return (0);
+
+
+	if (dargs->optflag & DA_ADD) {
+		/*
+		 * Since we got here from an event, we know the stored
+		 * devname is a useless guess, since the files had not
+		 * been read when the name was chosen, and we don't keep
+		 * them anywhere else that is sufficiently definitive.
+		 */
+
+		for (tmp = 0; tmp <= DA_MAX_DEVNO; tmp++)
+			if (!(tmp_bitmap & (1LL << tmp)))
+				break;
+		/* Future: support more than 64 hotplug devices per type? */
+		if (tmp > DA_MAX_DEVNO)
+			return (2);
+
+		(void) snprintf(new_devname, DA_MAXNAME + 1, "%s%u",
+		    dargs->devinfo->devtype, tmp);
+		if (dargs->devinfo->devname != NULL)
+			free(dargs->devinfo->devname);
+		dargs->devinfo->devname = strdup(new_devname);
+	}
+
+	/*
+	 * Now adjust devalloc list to match devmaps
+	 * Note we now have the correct devname for da_match to use.
+	 */
+	setdaent();
+	while ((devallocp = getdaent()) != NULL) {
+		rc = da_match(devallocp, dargs);
+		if (rc == 1) {
+			if (dargs->optflag & DA_ADD) {
+				/* logging is on if DA_EVENT is set */
+				if (dargs->optflag & DA_EVENT) {
+					(void) snprintf(errmsg, sizeof (errmsg),
+					    "%s and %s out of sync,"
+					    "%s only in %s.",
+					    DEVALLOC, DEVMAP,
+					    devallocp->da_devname, DEVALLOC);
+					syslog(LOG_ERR, "%s", errmsg);
+				}
+				freedaent(devallocp);
+				enddaent();
+				return (2);
+			} else if (dargs->optflag & DA_REMOVE) {
+				/* make list w/o this entry */
+				freedaent(devallocp);
+				continue;
+			}
+		}
+		tmp_str = _da2strentry(dargs, devallocp);
+		if (tmp_str == NULL) {
+			freedaent(devallocp);
+			enddaent();
+			return (2);
+		}
+		/* retaining devalloc entry: tmp_str->se_str */
+		tmp_str->se_next = NULL;
+		if (*head_devallocp == NULL) {
+			*head_devallocp = tail_str = tmp_str;
+		} else {
+			tail_str->se_next = tmp_str;
+			tail_str = tmp_str;
+		}
+		freedaent(devallocp);
+	}
+	enddaent();
+
+	/* the caller needs to know if a remove needs to rewrite files */
+	if (dargs->optflag & DA_REMOVE)
+		return (1);  /* 0 and 2 cases returned earlier */
+
+	return (0);  /* Successful DA_ADD */
+}
+/*
  * _build_lists -
- *	cycles through all the entries, stores them in memory. removes entries
+ *	Cycles through all the entries, stores them in memory. removes entries
  *	with the given search_key (device name or type).
  *	returns 0 if given entry not found, 1 if given entry removed, 2 on
  *	error.
@@ -633,7 +844,7 @@
 			rc = 0;
 		}
 		if (rc == 0) {
-			tmp_str = _dmap2strentry(dargs, devmapp);
+			tmp_str = _dmap2strentry(devmapp);
 			if (tmp_str == NULL) {
 				freedmapent(devmapp);
 				enddmapent();
@@ -675,12 +886,13 @@
 /*
  * _write_device_allocate -
  *	writes current entries in the list to device_allocate.
+ *	frees the strings
  */
 static void
 _write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp)
 {
 	int		is_on = -1;
-	strentry_t	*tmp_str;
+	strentry_t	*tmp_str, *old_str;
 	struct stat	dastat;
 
 	(void) fseek(dafp, (off_t)0, SEEK_SET);
@@ -702,18 +914,21 @@
 	while (tmp_str) {
 		(void) fputs(tmp_str->se_str, dafp);
 		(void) fputs("\n", dafp);
+		old_str = tmp_str;
 		tmp_str = tmp_str->se_next;
+		free(old_str);
 	}
 }
 
 /*
  * _write_device_maps -
  *	writes current entries in the list to device_maps.
+ *	and frees the strings
  */
 static void
 _write_device_maps(FILE *dmfp, strentry_t *head_devmapp)
 {
-	strentry_t	*tmp_str;
+	strentry_t	*tmp_str, *old_str;
 
 	(void) fseek(dmfp, (off_t)0, SEEK_SET);
 
@@ -721,7 +936,9 @@
 	while (tmp_str) {
 		(void) fputs(tmp_str->se_str, dmfp);
 		(void) fputs("\n", dmfp);
+		old_str = tmp_str;
 		tmp_str = tmp_str->se_next;
+		free(old_str);
 	}
 }
 
@@ -744,7 +961,7 @@
 		return (0);
 	(void) fprintf(fp, "%s%s", (devinfo->devtype ? devinfo->devtype : ""),
 	    KV_TOKEN_DELIMIT);
-	if ((tokp = (char *)malloc(strlen(devinfo->devopts))) != NULL) {
+	if ((tokp = (char *)malloc(strlen(devinfo->devopts) +1)) != NULL) {
 		(void) strcpy(tokp, devinfo->devopts);
 		if ((tok = strtok_r(tokp, KV_DELIMITER, &lasts)) != NULL) {
 			(void) fprintf(fp, "%s", tok);
@@ -791,7 +1008,8 @@
 		(void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED,
 		    KV_DELIMITER);
 	} else {
-		if ((tokp = (char *)malloc(strlen(devinfo->devopts))) != NULL) {
+		if ((tokp = (char *)malloc(strlen(devinfo->devopts) + 1))
+		    != NULL) {
 			(void) strcpy(tokp, devinfo->devopts);
 			if ((tok = strtok_r(tokp, KV_TOKEN_DELIMIT, &lasts)) !=
 			    NULL) {
@@ -1058,7 +1276,7 @@
 		str_found = 0;
 		nsize = dastat.st_size + actionlen + 1;
 	}
-	if ((nbuf = (char *)malloc(nsize)) == NULL)
+	if ((nbuf = (char *)malloc(nsize + 1)) == NULL)
 		return (-1);
 	nbuf[0] = '\0';
 	/* put the on/off string */
@@ -1168,8 +1386,9 @@
 
 /*
  * da_update_device -
- *	writes devices entries to device_allocate and device_maps.
- * 	returns 0 on success, -1 on error.
+ *	Writes existing entries and the SINGLE change requested by da_args,
+ *      to device_allocate and device_maps.
+ * 	Returns 0 on success, -1 on error.
  */
 int
 da_update_device(da_args *dargs)
@@ -1288,25 +1507,58 @@
 	}
 
 	/*
-	 * examine all the entries, remove an old one if forced to,
-	 * and check that they are suitable for updating.
-	 *  we need to do this only if the file exists already.
+	 * If reacting to a hotplug, read the file entries,
+	 * figure out what dname (tname + a new number) goes to the
+	 * device being added/removed, and create a good head_devallocp and
+	 * head_devmapp with everything good still in it (_rebuild_lists)
+	 *
+	 * Else examine all the entries, remove an old one if it is
+	 * a duplicate with a device being added, returning the
+	 * remaining list (_build_lists.)
+	 *
+	 * We need to do this only if the file exists already.
+	 *
+	 * Once we have built these lists, we need to free the strings
+	 * in the head_* arrays before returning.
 	 */
 	if (stat(dapathp, &dastat) == 0) {
-		if ((rc = _build_lists(dargs, &head_devallocp,
-		    &head_devmapp)) != 0) {
-			if (rc != 1) {
-				(void) close(tafd);
-				(void) unlink(apathp);
-				(void) close(lockfd);
-				return (rc);
-			}
+		/* for device allocation, the /etc files are the "master" */
+		if ((dargs->optflag & (DA_ADD| DA_EVENT)) &&
+		    (!(dargs->optflag & DA_FORCE)))
+			rc = _rebuild_lists(dargs, &head_devallocp,
+			    &head_devmapp);
+		else
+			rc = _build_lists(dargs, &head_devallocp,
+			    &head_devmapp);
+
+		if (rc != 0 && rc != 1) {
+			(void) close(tafd);
+			(void) unlink(apathp);
+			(void) close(lockfd);
+			return (-1);
 		}
+	} else
+		rc = 0;
+
+	if ((dargs->optflag & DA_REMOVE) && (rc == 0)) {
+		(void) close(tafd);
+		(void) unlink(apathp);
+		(void) close(lockfd);
+		return (0);
 	}
+	/*
+	 * TODO: clean up the workings of DA_UPDATE.
+	 * Due to da_match looking at fields that are missing
+	 * in dargs for DA_UPDATE, the da_match call returns no match,
+	 * but due to the way _da2str combines the devalloc_t info with
+	 * the *dargs info, the DA_ADD_ZONE and DA_REMOVE_ZONE work.
+	 *
+	 * This would not scale if any type of update was ever needed
+	 * from the daemon.
+	 */
 
 	/*
-	 * write back any existing devalloc entries, along with
-	 * the devalloc on/off string.
+	 * Write out devallocp along with the devalloc on/off string.
 	 */
 	_write_device_allocate(dapathp, tafp, head_devallocp);
 
@@ -1330,11 +1582,16 @@
 		return (-1);
 	}
 
-	/* write back any existing devmap entries */
+	/*
+	 * Write back any non-removed pre-existing entries.
+	 */
 	if (head_devmapp != NULL)
 		_write_device_maps(tmfp, head_devmapp);
 
 out:
+	/*
+	 * Add any new entries here.
+	 */
 	if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
 		/* add any new entries */
 		rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY);
@@ -1382,7 +1639,7 @@
 	int		new_entry = 0;
 	char		*dtype, *dexec, *tname, *kval;
 	char		*minstr = NULL, *maxstr = NULL;
-	char		dname[DA_MAXNAME];
+	char		dname[DA_MAXNAME + 1];
 	kva_t		*kva;
 	deventry_t	*dentry = NULL, *nentry = NULL, *pentry = NULL;
 	da_defs_t	*da_defs;
@@ -1500,9 +1757,7 @@
 	if ((nentry->devinfo.devlist =
 	    (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) {
 		if (new_entry) {
-			nentry->devinfo.devname = NULL;
 			free(nentry->devinfo.devname);
-			nentry = NULL;
 			free(nentry);
 			if (pentry != NULL)
 				pentry->next = NULL;
@@ -1693,6 +1948,78 @@
 }
 
 /*
+ * da_rm_list_entry -
+ *
+ *	The adding of devnames to a devlist and the removal of a
+ *	device are not symmetrical -- hot_cleanup gives a /devices
+ *	name which is used to remove the dentry whose links all point to
+ *	that /devices entry.
+ *
+ *	The link argument is present if available to make debugging
+ *	easier.
+ *
+ *	da_rm_list_entry removes an entry from the linked list of devices.
+ *
+ *	Returns 1 if the devname was removed successfully,
+ *	0 if not found, -1 for error.
+ */
+/*ARGSUSED*/
+int
+da_rm_list_entry(devlist_t *dlist, char *link, int type, char *devname)
+{
+	int		retval = 0;
+	deventry_t	**dentry, *current, *prev;
+
+	switch (type) {
+	case DA_AUDIO:
+		dentry = &(dlist->audio);
+		break;
+	case DA_CD:
+		dentry = &(dlist->cd);
+		break;
+	case DA_FLOPPY:
+		dentry = &(dlist->floppy);
+		break;
+	case DA_TAPE:
+		dentry = &(dlist->tape);
+		break;
+	case DA_RMDISK:
+		dentry = &(dlist->rmdisk);
+		break;
+	default:
+		return (-1);
+	}
+
+	/* Presumably in daemon mode, no need to remove entry, list is empty */
+	if (*dentry == (deventry_t *)NULL)
+		return (0);
+
+	prev = NULL;
+	for (current = *dentry; current != NULL;
+	    prev = current, current = current->next) {
+		if (strcmp(devname, current->devinfo.devname))
+			continue;
+		retval = 1;
+		break;
+	}
+	if (retval == 0)
+		return (0);
+	free(current->devinfo.devname);
+	if (current->devinfo.devlist != NULL)
+		free(current->devinfo.devlist);
+	if (current->devinfo.devopts != NULL)
+		free(current->devinfo.devopts);
+
+	if (prev == NULL)
+		*dentry = current->next;
+	else
+		prev->next = current->next;
+
+	free(current);
+	return (retval);
+}
+
+/*
  * da_is_on -
  *	checks if device allocation feature is turned on.
  *	returns 1 if on, 0 if off, -1 if status string not
--- a/usr/src/lib/libbsm/common/devalloc.h	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/lib/libbsm/common/devalloc.h	Thu Jan 14 17:22:31 2010 -0800
@@ -20,15 +20,13 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_DEVALLOC_H
 #define	_DEVALLOC_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -45,7 +43,7 @@
 #define	DA_UID			(uid_t)0	/* root */
 #define	DA_GID			(gid_t)3	/* sys */
 #define	ALLOC_MODE		0600
-#define	DEALLOC_MODE    	0000
+#define	DEALLOC_MODE		0000
 
 #define	LOGINDEVPERM		"/etc/logindevperm"
 #define	DA_DB_LOCK		"/etc/security/.da_db_lock"
@@ -73,6 +71,7 @@
 #define	DA_AUTHLEN		MAX_CANON   /* approx. sum of strlen of all */
 					    /* device auths in auth_list.h */
 #define	DA_MAXNAME		80
+#define	DA_MAX_DEVNO		((8 * sizeof (uint64_t)) - 1)
 #define	DA_BUFSIZE		4096
 
 #define	DA_RDWR			O_RDWR|O_CREAT|O_NONBLOCK
@@ -95,6 +94,7 @@
 #define	DA_OFF			0x00000800
 #define	DA_NO_OVERRIDE		0x00001000
 #define	DA_DEFATTRS		0x00002000
+#define	DA_EVENT		0x00004000
 
 #define	DA_AUDIO		0x00001000
 #define	DA_CD			0x00002000
@@ -171,6 +171,7 @@
 int da_update_defattrs(da_args *);
 int da_add_list(devlist_t *, char *, int, int);
 int da_remove_list(devlist_t *, char *, int, char *, int);
+int da_rm_list_entry(devlist_t *, char *, int, char *);
 void da_print_device(int, devlist_t *);
 
 
--- a/usr/src/lib/libbsm/common/getdment.c	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/lib/libbsm/common/getdment.c	Thu Jan 14 17:22:31 2010 -0800
@@ -19,14 +19,16 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <string.h>
+#include <strings.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <device_info.h>
 #include <bsm/devices.h>
 #include <bsm/devalloc.h>
 
@@ -281,6 +283,31 @@
 }
 
 /*
+ * dmap_match_one_dev -
+ *    Checks if the specified devmap_t contains strings
+ *    for the same logical link as the device specified.
+ *    This guarantees that the beginnings of a devlist build
+ *    match a more-complete devlist for the same device.
+ *
+ *    Returns 1 for a match, else returns 0.
+ */
+static int
+dmap_match_one_dev(devmap_t *dmap, char *dev)
+{
+	char **dva;
+	char *dv;
+
+	if (dmap->dmap_devarray == NULL)
+		return (0);
+
+	for (dva = dmap->dmap_devarray; (dv = *dva) != NULL; dva++) {
+		if (strstr(dev, dv) != NULL)
+			return (1);
+	}
+	return (0);
+}
+
+/*
  * dmap_matchdev -
  * 	checks if the specified devmap_t is for the device specified.
  *	returns 1 if it is, else returns 0.
@@ -302,6 +329,25 @@
 }
 
 /*
+ * Requires a match of the /dev/?dsk links, not just the logical devname
+ * Returns 1 for match found, 0 for match not found, 2 for invalid arguments.
+ */
+int
+dmap_exact_dev(devmap_t *dmap, char *dev, int *num)
+{
+	char *dv;
+
+	if ((dev == NULL) || (dmap->dmap_devname == NULL))
+		return (2);
+	dv = dmap->dmap_devname;
+	dv +=  strcspn(dmap->dmap_devname, "0123456789");
+	if (sscanf(dv, "%d", num) != 1)
+		return (2);
+	/* during some add processes, dev can be shorter than dmap */
+	return (dmap_match_one_dev(dmap, dev));
+}
+
+/*
  * dmap_matchtype -
  *	checks if the specified devmap_t is for the device specified.
  *	returns 1 if it is, else returns 0.
@@ -330,6 +376,29 @@
 }
 
 /*
+ * dmap_physname: path to /devices device
+ * Returns:
+ *	strdup'd (i.e. malloc'd) real device file if successful
+ *      NULL on error
+ */
+char *
+dmap_physname(devmap_t *dmap)
+{
+	char *oldlink;
+	char stage_link[PATH_MAX + 1];
+
+	if ((dmap == NULL) || (dmap->dmap_devarray == NULL) ||
+	    (dmap->dmap_devarray[0] == NULL))
+		return (NULL);
+
+	(void) strncpy(stage_link, dmap->dmap_devarray[0], sizeof (stage_link));
+
+	if (devfs_resolve_link(stage_link, &oldlink) == 0)
+		return (oldlink);
+	return (NULL);
+}
+
+/*
  * dm_match -
  *	calls dmap_matchname or dmap_matchtype as appropriate.
  */
--- a/usr/src/lib/libbsm/common/mapfile-vers	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/lib/libbsm/common/mapfile-vers	Thu Jan 14 17:22:31 2010 -0800
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -297,6 +297,7 @@
 	da_open_devdb;
 	da_print_device;
 	da_remove_list;
+	da_rm_list_entry;
 	da_update_defattrs;
 	da_update_device;
 	_endac;
--- a/usr/src/lib/libdevinfo/Makefile	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/lib/libdevinfo/Makefile	Thu Jan 14 17:22:31 2010 -0800
@@ -19,11 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 include	../Makefile.lib
 
@@ -46,7 +44,7 @@
 
 .KEEP_STATE:
 
-all install clean clobber lint: $(SUBDIRS)
+all install clean clobber lint: install_h $(SUBDIRS)
 
 # install rule for install_h target
 
--- a/usr/src/lib/libdevinfo/device_info.h	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/lib/libdevinfo/device_info.h	Thu Jan 14 17:22:31 2010 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -129,6 +129,12 @@
 extern void devfs_free_all_prom_names(struct devfs_prom_path *);
 
 /*
+ *  Interpret a /dev link to its /devices path (does not require path to
+ *  still exist, as long as the links exist)
+ */
+extern int devfs_resolve_link(char *, char **);
+
+/*
  * map a device name from install OS environment to target OS environment or
  * vice-versa.
  */
--- a/usr/src/lib/libdevinfo/devinfo_devperm.c	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/lib/libdevinfo/devinfo_devperm.c	Thu Jan 14 17:22:31 2010 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -269,8 +269,8 @@
  * devpath: Absolute path to /dev link
  * devfs_path: Returns malloced string: /devices path w/out "/devices"
  */
-static int
-resolve_link(char *devpath, char **devfs_path)
+int
+devfs_resolve_link(char *devpath, char **devfs_path)
 {
 	char contents[PATH_MAX + 1];
 	char stage_link[PATH_MAX + 1];
@@ -313,7 +313,7 @@
 			(void) strcat(stage_link, contents);
 
 		}
-		return (resolve_link(stage_link, devfs_path));
+		return (devfs_resolve_link(stage_link, devfs_path));
 	}
 
 	if (devfs_path) {
@@ -339,7 +339,7 @@
 	char saveline[MAX_LINELEN];
 	char *p;
 
-	if (resolve_link(path, &devfs_path) == 0) {
+	if (devfs_resolve_link(path, &devfs_path) == 0) {
 		char *p;
 		char pwd_buf[PATH_MAX];
 		di_node_t node;
--- a/usr/src/lib/libdevinfo/mapfile-vers	Fri Jan 15 08:52:23 2010 +0800
+++ b/usr/src/lib/libdevinfo/mapfile-vers	Thu Jan 14 17:22:31 2010 -0800
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -152,6 +152,7 @@
 SUNWprivate_1.1 {
     global:
 	devfs_add_minor_perm;
+	devfs_resolve_link;
 	devfs_bootdev_free_list;
 	devfs_bootdev_get_list;
 	devfs_bootdev_modifiable;