xref: /illumos-gate/usr/src/cmd/devfsadm/devfsadm.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * Devfsadm replaces drvconfig, audlinks, disks, tapes, ports, devlinks
31*7c478bd9Sstevel@tonic-gate  * as a general purpose device administrative utility.	It creates
32*7c478bd9Sstevel@tonic-gate  * devices special files in /devices and logical links in /dev, and
33*7c478bd9Sstevel@tonic-gate  * coordinates updates to /etc/path_to_instance with the kernel.  It
34*7c478bd9Sstevel@tonic-gate  * operates in both command line mode to handle user or script invoked
35*7c478bd9Sstevel@tonic-gate  * reconfiguration updates, and operates in daemon mode to handle dynamic
36*7c478bd9Sstevel@tonic-gate  * reconfiguration for hotplugging support.
37*7c478bd9Sstevel@tonic-gate  */
38*7c478bd9Sstevel@tonic-gate 
39*7c478bd9Sstevel@tonic-gate #include "devfsadm_impl.h"
40*7c478bd9Sstevel@tonic-gate #include <utime.h>
41*7c478bd9Sstevel@tonic-gate 
42*7c478bd9Sstevel@tonic-gate /* globals */
43*7c478bd9Sstevel@tonic-gate 
44*7c478bd9Sstevel@tonic-gate /* create or remove nodes or links. unset with -n */
45*7c478bd9Sstevel@tonic-gate static int file_mods = TRUE;
46*7c478bd9Sstevel@tonic-gate 
47*7c478bd9Sstevel@tonic-gate /* cleanup mode.  Set with -C */
48*7c478bd9Sstevel@tonic-gate static int cleanup = FALSE;
49*7c478bd9Sstevel@tonic-gate 
50*7c478bd9Sstevel@tonic-gate /* devlinks -d compatibility */
51*7c478bd9Sstevel@tonic-gate static int devlinks_debug = FALSE;
52*7c478bd9Sstevel@tonic-gate 
53*7c478bd9Sstevel@tonic-gate /* turn off device allocation with devfsadm -d */
54*7c478bd9Sstevel@tonic-gate static int devalloc_off = FALSE;
55*7c478bd9Sstevel@tonic-gate 
56*7c478bd9Sstevel@tonic-gate /* devices to be deallocated with -d */
57*7c478bd9Sstevel@tonic-gate static char *devalloc[5] =
58*7c478bd9Sstevel@tonic-gate 	{DDI_NT_AUDIO, DDI_NT_CD, DDI_NT_FD, DDI_NT_TAPE, NULL};
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate /* load a single driver only.  set with -i */
61*7c478bd9Sstevel@tonic-gate static int single_drv = FALSE;
62*7c478bd9Sstevel@tonic-gate static char *driver = NULL;
63*7c478bd9Sstevel@tonic-gate 
64*7c478bd9Sstevel@tonic-gate /* attempt to load drivers or defer attach nodes */
65*7c478bd9Sstevel@tonic-gate static int load_attach_drv = TRUE;
66*7c478bd9Sstevel@tonic-gate 
67*7c478bd9Sstevel@tonic-gate /* set if invoked via /usr/lib/devfsadm/devfsadmd */
68*7c478bd9Sstevel@tonic-gate static int daemon_mode = FALSE;
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate /* output directed to syslog during daemon mode if set */
71*7c478bd9Sstevel@tonic-gate static int logflag = FALSE;
72*7c478bd9Sstevel@tonic-gate 
73*7c478bd9Sstevel@tonic-gate /* build links in /dev.  -x to turn off */
74*7c478bd9Sstevel@tonic-gate static int build_dev = TRUE;
75*7c478bd9Sstevel@tonic-gate 
76*7c478bd9Sstevel@tonic-gate /* build nodes in /devices.  -y to turn off */
77*7c478bd9Sstevel@tonic-gate static int build_devices = TRUE;
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate /* -z to turn off */
80*7c478bd9Sstevel@tonic-gate static int flush_path_to_inst_enable = TRUE;
81*7c478bd9Sstevel@tonic-gate 
82*7c478bd9Sstevel@tonic-gate /* variables used for path_to_inst flushing */
83*7c478bd9Sstevel@tonic-gate static int inst_count = 0;
84*7c478bd9Sstevel@tonic-gate static mutex_t count_lock;
85*7c478bd9Sstevel@tonic-gate static cond_t cv;
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate /* variables for minor_fini calling system */
88*7c478bd9Sstevel@tonic-gate static int minor_fini_timeout = MINOR_FINI_TIMEOUT_DEFAULT;
89*7c478bd9Sstevel@tonic-gate static mutex_t minor_fini_mutex;
90*7c478bd9Sstevel@tonic-gate static int minor_fini_thread_created = FALSE;
91*7c478bd9Sstevel@tonic-gate static int minor_fini_delay_restart = FALSE;
92*7c478bd9Sstevel@tonic-gate 
93*7c478bd9Sstevel@tonic-gate /* single-threads /dev modification */
94*7c478bd9Sstevel@tonic-gate static sema_t dev_sema;
95*7c478bd9Sstevel@tonic-gate 
96*7c478bd9Sstevel@tonic-gate /* the program we were invoked as; ie argv[0] */
97*7c478bd9Sstevel@tonic-gate static char *prog;
98*7c478bd9Sstevel@tonic-gate 
99*7c478bd9Sstevel@tonic-gate /* pointers to create/remove link lists */
100*7c478bd9Sstevel@tonic-gate static create_list_t *create_head = NULL;
101*7c478bd9Sstevel@tonic-gate static remove_list_t *remove_head = NULL;
102*7c478bd9Sstevel@tonic-gate 
103*7c478bd9Sstevel@tonic-gate /*  supports the class -c option */
104*7c478bd9Sstevel@tonic-gate static char **classes = NULL;
105*7c478bd9Sstevel@tonic-gate static int num_classes = 0;
106*7c478bd9Sstevel@tonic-gate 
107*7c478bd9Sstevel@tonic-gate /* used with verbose option -v or -V */
108*7c478bd9Sstevel@tonic-gate static int num_verbose = 0;
109*7c478bd9Sstevel@tonic-gate static char **verbose = NULL;
110*7c478bd9Sstevel@tonic-gate 
111*7c478bd9Sstevel@tonic-gate static struct mperm *minor_perms = NULL;
112*7c478bd9Sstevel@tonic-gate static driver_alias_t *driver_aliases = NULL;
113*7c478bd9Sstevel@tonic-gate 
114*7c478bd9Sstevel@tonic-gate /* set if -r alternate root given */
115*7c478bd9Sstevel@tonic-gate static char *root_dir = "";
116*7c478bd9Sstevel@tonic-gate 
117*7c478bd9Sstevel@tonic-gate /* /devices or <rootdir>/devices */
118*7c478bd9Sstevel@tonic-gate static char *devices_dir  = DEVICES;
119*7c478bd9Sstevel@tonic-gate 
120*7c478bd9Sstevel@tonic-gate /* /dev or <rootdir>/dev */
121*7c478bd9Sstevel@tonic-gate static char *dev_dir = DEV;
122*7c478bd9Sstevel@tonic-gate 
123*7c478bd9Sstevel@tonic-gate /* /dev for the global zone */
124*7c478bd9Sstevel@tonic-gate static char *global_dev_dir = DEV;
125*7c478bd9Sstevel@tonic-gate 
126*7c478bd9Sstevel@tonic-gate /* /etc/path_to_inst unless -p used */
127*7c478bd9Sstevel@tonic-gate static char *inst_file = INSTANCE_FILE;
128*7c478bd9Sstevel@tonic-gate 
129*7c478bd9Sstevel@tonic-gate /* /usr/lib/devfsadm/linkmods unless -l used */
130*7c478bd9Sstevel@tonic-gate static char *module_dirs = MODULE_DIRS;
131*7c478bd9Sstevel@tonic-gate 
132*7c478bd9Sstevel@tonic-gate /* default uid/gid used if /etc/minor_perm entry not found */
133*7c478bd9Sstevel@tonic-gate static uid_t root_uid;
134*7c478bd9Sstevel@tonic-gate static gid_t sys_gid;
135*7c478bd9Sstevel@tonic-gate 
136*7c478bd9Sstevel@tonic-gate /* /etc/devlink.tab unless devlinks -t used */
137*7c478bd9Sstevel@tonic-gate static char *devlinktab_file = NULL;
138*7c478bd9Sstevel@tonic-gate 
139*7c478bd9Sstevel@tonic-gate /* set if /dev link is new. speeds up rm_stale_links */
140*7c478bd9Sstevel@tonic-gate static int linknew = TRUE;
141*7c478bd9Sstevel@tonic-gate 
142*7c478bd9Sstevel@tonic-gate /* variables for devlink.tab compat processing */
143*7c478bd9Sstevel@tonic-gate static devlinktab_list_t *devlinktab_list = NULL;
144*7c478bd9Sstevel@tonic-gate static unsigned int devlinktab_line = 0;
145*7c478bd9Sstevel@tonic-gate 
146*7c478bd9Sstevel@tonic-gate /* cache head for devfsadm_enumerate*() functions */
147*7c478bd9Sstevel@tonic-gate static numeral_set_t *head_numeral_set = NULL;
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate /* list list of devfsadm modules */
150*7c478bd9Sstevel@tonic-gate static module_t *module_head = NULL;
151*7c478bd9Sstevel@tonic-gate 
152*7c478bd9Sstevel@tonic-gate /* name_to_major list used in utility function */
153*7c478bd9Sstevel@tonic-gate static n2m_t *n2m_list = NULL;
154*7c478bd9Sstevel@tonic-gate 
155*7c478bd9Sstevel@tonic-gate /* cache of some links used for performance */
156*7c478bd9Sstevel@tonic-gate static linkhead_t *headlinkhead = NULL;
157*7c478bd9Sstevel@tonic-gate 
158*7c478bd9Sstevel@tonic-gate /* locking variables to prevent multiples writes to /dev */
159*7c478bd9Sstevel@tonic-gate static int hold_dev_lock = FALSE;
160*7c478bd9Sstevel@tonic-gate static int hold_daemon_lock = FALSE;
161*7c478bd9Sstevel@tonic-gate static int dev_lock_fd;
162*7c478bd9Sstevel@tonic-gate static int daemon_lock_fd;
163*7c478bd9Sstevel@tonic-gate static char dev_lockfile[PATH_MAX + 1];
164*7c478bd9Sstevel@tonic-gate static char daemon_lockfile[PATH_MAX + 1];
165*7c478bd9Sstevel@tonic-gate 
166*7c478bd9Sstevel@tonic-gate /* last devinfo node/minor processed. used for performance */
167*7c478bd9Sstevel@tonic-gate static di_node_t lnode;
168*7c478bd9Sstevel@tonic-gate static di_minor_t lminor;
169*7c478bd9Sstevel@tonic-gate static char lphy_path[PATH_MAX + 1] = {""};
170*7c478bd9Sstevel@tonic-gate 
171*7c478bd9Sstevel@tonic-gate /* Globals used by the link database */
172*7c478bd9Sstevel@tonic-gate static di_devlink_handle_t devlink_cache;
173*7c478bd9Sstevel@tonic-gate static int update_database = FALSE;
174*7c478bd9Sstevel@tonic-gate static int devlink_door_fd = -1; /* fd of devlink handler door */
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate /* Globals used to set logindev perms */
177*7c478bd9Sstevel@tonic-gate static struct login_dev *login_dev_cache = NULL;
178*7c478bd9Sstevel@tonic-gate static int login_dev_enable = FALSE;
179*7c478bd9Sstevel@tonic-gate 
180*7c478bd9Sstevel@tonic-gate /* Global to use devinfo snapshot cache */
181*7c478bd9Sstevel@tonic-gate static int use_snapshot_cache = FALSE;
182*7c478bd9Sstevel@tonic-gate 
183*7c478bd9Sstevel@tonic-gate /* Zone-related information */
184*7c478bd9Sstevel@tonic-gate static int zone_cmd_mode = 0;
185*7c478bd9Sstevel@tonic-gate static mutex_t zone_mutex; /* protects zone registration/unregistration ops */
186*7c478bd9Sstevel@tonic-gate static struct zone_devinfo *zone_head;	/* linked list of zones */
187*7c478bd9Sstevel@tonic-gate 
188*7c478bd9Sstevel@tonic-gate /*
189*7c478bd9Sstevel@tonic-gate  * Packaged directories - not removed even when empty.
190*7c478bd9Sstevel@tonic-gate  * The dirs must be listed in canonical form
191*7c478bd9Sstevel@tonic-gate  * i.e. without leading "/dev/"
192*7c478bd9Sstevel@tonic-gate  */
193*7c478bd9Sstevel@tonic-gate static char *packaged_dirs[] =
194*7c478bd9Sstevel@tonic-gate 	{"dsk", "rdsk", "term", NULL};
195*7c478bd9Sstevel@tonic-gate 
196*7c478bd9Sstevel@tonic-gate /* RCM related globals */
197*7c478bd9Sstevel@tonic-gate static void *librcm_hdl;
198*7c478bd9Sstevel@tonic-gate static rcm_handle_t *rcm_hdl = NULL;
199*7c478bd9Sstevel@tonic-gate static thread_t process_rcm_events_tid;
200*7c478bd9Sstevel@tonic-gate static struct rcm_eventq *volatile rcm_eventq_head = NULL;
201*7c478bd9Sstevel@tonic-gate static struct rcm_eventq *rcm_eventq_tail = NULL;
202*7c478bd9Sstevel@tonic-gate static mutex_t rcm_eventq_lock;
203*7c478bd9Sstevel@tonic-gate static cond_t rcm_eventq_cv;
204*7c478bd9Sstevel@tonic-gate static volatile int need_to_exit_rcm_event_thread = 0;
205*7c478bd9Sstevel@tonic-gate 
206*7c478bd9Sstevel@tonic-gate static void load_dev_acl(void);
207*7c478bd9Sstevel@tonic-gate static void update_drvconf(major_t);
208*7c478bd9Sstevel@tonic-gate 
209*7c478bd9Sstevel@tonic-gate 
210*7c478bd9Sstevel@tonic-gate int
211*7c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
212*7c478bd9Sstevel@tonic-gate {
213*7c478bd9Sstevel@tonic-gate 	struct passwd *pw;
214*7c478bd9Sstevel@tonic-gate 	struct group *gp;
215*7c478bd9Sstevel@tonic-gate 	pid_t pid;
216*7c478bd9Sstevel@tonic-gate 
217*7c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
218*7c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
219*7c478bd9Sstevel@tonic-gate 
220*7c478bd9Sstevel@tonic-gate 	if ((prog = strrchr(argv[0], '/')) == NULL) {
221*7c478bd9Sstevel@tonic-gate 		prog = argv[0];
222*7c478bd9Sstevel@tonic-gate 	} else {
223*7c478bd9Sstevel@tonic-gate 		prog++;
224*7c478bd9Sstevel@tonic-gate 	}
225*7c478bd9Sstevel@tonic-gate 
226*7c478bd9Sstevel@tonic-gate 	if (getuid() != 0) {
227*7c478bd9Sstevel@tonic-gate 		err_print(MUST_BE_ROOT);
228*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
229*7c478bd9Sstevel@tonic-gate 	}
230*7c478bd9Sstevel@tonic-gate 
231*7c478bd9Sstevel@tonic-gate 	/*
232*7c478bd9Sstevel@tonic-gate 	 * Close all files except stdin/stdout/stderr
233*7c478bd9Sstevel@tonic-gate 	 */
234*7c478bd9Sstevel@tonic-gate 	closefrom(3);
235*7c478bd9Sstevel@tonic-gate 
236*7c478bd9Sstevel@tonic-gate 	if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
237*7c478bd9Sstevel@tonic-gate 		root_uid = pw->pw_uid;
238*7c478bd9Sstevel@tonic-gate 	} else {
239*7c478bd9Sstevel@tonic-gate 		err_print(CANT_FIND_USER, DEFAULT_DEV_USER);
240*7c478bd9Sstevel@tonic-gate 		root_uid = (uid_t)0;	/* assume 0 is root */
241*7c478bd9Sstevel@tonic-gate 	}
242*7c478bd9Sstevel@tonic-gate 
243*7c478bd9Sstevel@tonic-gate 	/* the default group is sys */
244*7c478bd9Sstevel@tonic-gate 
245*7c478bd9Sstevel@tonic-gate 	if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
246*7c478bd9Sstevel@tonic-gate 		sys_gid = gp->gr_gid;
247*7c478bd9Sstevel@tonic-gate 	} else {
248*7c478bd9Sstevel@tonic-gate 		err_print(CANT_FIND_GROUP, DEFAULT_DEV_GROUP);
249*7c478bd9Sstevel@tonic-gate 		sys_gid = (gid_t)3;	/* assume 3 is sys */
250*7c478bd9Sstevel@tonic-gate 	}
251*7c478bd9Sstevel@tonic-gate 
252*7c478bd9Sstevel@tonic-gate 	(void) umask(0);
253*7c478bd9Sstevel@tonic-gate 
254*7c478bd9Sstevel@tonic-gate 	parse_args(argc, argv);
255*7c478bd9Sstevel@tonic-gate 
256*7c478bd9Sstevel@tonic-gate 	(void) sema_init(&dev_sema, 1, USYNC_THREAD, NULL);
257*7c478bd9Sstevel@tonic-gate 
258*7c478bd9Sstevel@tonic-gate 	if (daemon_mode == TRUE) {
259*7c478bd9Sstevel@tonic-gate 		/*
260*7c478bd9Sstevel@tonic-gate 		 * Build /dev and /devices before daemonizing if
261*7c478bd9Sstevel@tonic-gate 		 * reconfig booting and daemon invoked with alternate
262*7c478bd9Sstevel@tonic-gate 		 * root. This is to support install.
263*7c478bd9Sstevel@tonic-gate 		 */
264*7c478bd9Sstevel@tonic-gate 		if (getenv(RECONFIG_BOOT) != NULL && root_dir[0] != '\0') {
265*7c478bd9Sstevel@tonic-gate 			vprint(INFO_MID, CONFIGURING);
266*7c478bd9Sstevel@tonic-gate 			load_dev_acl();
267*7c478bd9Sstevel@tonic-gate 			update_drvconf((major_t)-1);
268*7c478bd9Sstevel@tonic-gate 			process_devinfo_tree();
269*7c478bd9Sstevel@tonic-gate 			(void) modctl(MODSETMINIROOT);
270*7c478bd9Sstevel@tonic-gate 		}
271*7c478bd9Sstevel@tonic-gate 
272*7c478bd9Sstevel@tonic-gate 		/*
273*7c478bd9Sstevel@tonic-gate 		 * fork before detaching from tty in order to print error
274*7c478bd9Sstevel@tonic-gate 		 * message if unable to acquire file lock.  locks not preserved
275*7c478bd9Sstevel@tonic-gate 		 * across forks.  Even under debug we want to fork so that
276*7c478bd9Sstevel@tonic-gate 		 * when executed at boot we don't hang.
277*7c478bd9Sstevel@tonic-gate 		 */
278*7c478bd9Sstevel@tonic-gate 		if (fork() != 0) {
279*7c478bd9Sstevel@tonic-gate 			devfsadm_exit(0);
280*7c478bd9Sstevel@tonic-gate 		}
281*7c478bd9Sstevel@tonic-gate 
282*7c478bd9Sstevel@tonic-gate 		/* set directory to / so it coredumps there */
283*7c478bd9Sstevel@tonic-gate 		if (chdir("/") == -1) {
284*7c478bd9Sstevel@tonic-gate 			err_print(CHROOT_FAILED, strerror(errno));
285*7c478bd9Sstevel@tonic-gate 		}
286*7c478bd9Sstevel@tonic-gate 
287*7c478bd9Sstevel@tonic-gate 		/* only one daemon can run at a time */
288*7c478bd9Sstevel@tonic-gate 		if ((pid = enter_daemon_lock()) == getpid()) {
289*7c478bd9Sstevel@tonic-gate 			thread_t thread;
290*7c478bd9Sstevel@tonic-gate 			detachfromtty();
291*7c478bd9Sstevel@tonic-gate 			(void) cond_init(&cv, USYNC_THREAD, 0);
292*7c478bd9Sstevel@tonic-gate 			(void) mutex_init(&count_lock, USYNC_THREAD, 0);
293*7c478bd9Sstevel@tonic-gate 			if (thr_create(NULL, NULL,
294*7c478bd9Sstevel@tonic-gate 				(void *(*)(void *))instance_flush_thread,
295*7c478bd9Sstevel@tonic-gate 				    NULL,
296*7c478bd9Sstevel@tonic-gate 				    THR_DETACHED,
297*7c478bd9Sstevel@tonic-gate 				    &thread) != 0) {
298*7c478bd9Sstevel@tonic-gate 				err_print(CANT_CREATE_THREAD, "daemon",
299*7c478bd9Sstevel@tonic-gate 					strerror(errno));
300*7c478bd9Sstevel@tonic-gate 				devfsadm_exit(1);
301*7c478bd9Sstevel@tonic-gate 			}
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 			/*
304*7c478bd9Sstevel@tonic-gate 			 * No need for rcm notifications when running
305*7c478bd9Sstevel@tonic-gate 			 * with an alternate root. So initialize rcm only
306*7c478bd9Sstevel@tonic-gate 			 * when devfsadm is running with root dir "/".
307*7c478bd9Sstevel@tonic-gate 			 * Similarly, logindevperms need only be set
308*7c478bd9Sstevel@tonic-gate 			 * in daemon mode and when root dir is "/".
309*7c478bd9Sstevel@tonic-gate 			 */
310*7c478bd9Sstevel@tonic-gate 			if (root_dir[0] == '\0') {
311*7c478bd9Sstevel@tonic-gate 				(void) rcm_init();
312*7c478bd9Sstevel@tonic-gate 				login_dev_enable = TRUE;
313*7c478bd9Sstevel@tonic-gate 			}
314*7c478bd9Sstevel@tonic-gate 
315*7c478bd9Sstevel@tonic-gate 			daemon_update();
316*7c478bd9Sstevel@tonic-gate 		} else {
317*7c478bd9Sstevel@tonic-gate 			err_print(DAEMON_RUNNING, pid);
318*7c478bd9Sstevel@tonic-gate 			devfsadm_exit(1);
319*7c478bd9Sstevel@tonic-gate 		}
320*7c478bd9Sstevel@tonic-gate 		exit_daemon_lock();
321*7c478bd9Sstevel@tonic-gate 
322*7c478bd9Sstevel@tonic-gate 	} else {
323*7c478bd9Sstevel@tonic-gate 		/* not a daemon, so just build /dev and /devices */
324*7c478bd9Sstevel@tonic-gate 		process_devinfo_tree();
325*7c478bd9Sstevel@tonic-gate 	}
326*7c478bd9Sstevel@tonic-gate 	return (0);
327*7c478bd9Sstevel@tonic-gate }
328*7c478bd9Sstevel@tonic-gate 
329*7c478bd9Sstevel@tonic-gate static void
330*7c478bd9Sstevel@tonic-gate update_drvconf(major_t major)
331*7c478bd9Sstevel@tonic-gate {
332*7c478bd9Sstevel@tonic-gate 	if (modctl(MODLOADDRVCONF, major) != 0)
333*7c478bd9Sstevel@tonic-gate 		err_print(gettext("update_drvconf failed for major %d\n"),
334*7c478bd9Sstevel@tonic-gate 		    major);
335*7c478bd9Sstevel@tonic-gate }
336*7c478bd9Sstevel@tonic-gate 
337*7c478bd9Sstevel@tonic-gate 
338*7c478bd9Sstevel@tonic-gate static void
339*7c478bd9Sstevel@tonic-gate load_dev_acl()
340*7c478bd9Sstevel@tonic-gate {
341*7c478bd9Sstevel@tonic-gate 	if (load_devpolicy() != 0)
342*7c478bd9Sstevel@tonic-gate 		err_print(gettext("device policy load failed\n"));
343*7c478bd9Sstevel@tonic-gate 	load_minor_perm_file();
344*7c478bd9Sstevel@tonic-gate }
345*7c478bd9Sstevel@tonic-gate 
346*7c478bd9Sstevel@tonic-gate /*
347*7c478bd9Sstevel@tonic-gate  * set_zone_params sets us up to run against a specific zone.  It should
348*7c478bd9Sstevel@tonic-gate  * only be called from parse_args.
349*7c478bd9Sstevel@tonic-gate  */
350*7c478bd9Sstevel@tonic-gate static int
351*7c478bd9Sstevel@tonic-gate set_zone_params(char *zone_name)
352*7c478bd9Sstevel@tonic-gate {
353*7c478bd9Sstevel@tonic-gate 	char zpath[MAXPATHLEN];
354*7c478bd9Sstevel@tonic-gate 	zone_dochandle_t hdl;
355*7c478bd9Sstevel@tonic-gate 	void *dlhdl;
356*7c478bd9Sstevel@tonic-gate 
357*7c478bd9Sstevel@tonic-gate 	assert(daemon_mode == FALSE);
358*7c478bd9Sstevel@tonic-gate 
359*7c478bd9Sstevel@tonic-gate 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) {
360*7c478bd9Sstevel@tonic-gate 		err_print(INVALID_ZONE, zone_name);
361*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
362*7c478bd9Sstevel@tonic-gate 	}
363*7c478bd9Sstevel@tonic-gate 
364*7c478bd9Sstevel@tonic-gate 	if ((dlhdl = dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) {
365*7c478bd9Sstevel@tonic-gate 		err_print(ZONE_LIB_MISSING);
366*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
367*7c478bd9Sstevel@tonic-gate 	}
368*7c478bd9Sstevel@tonic-gate 
369*7c478bd9Sstevel@tonic-gate 	if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
370*7c478bd9Sstevel@tonic-gate 		err_print(ZONE_ROOTPATH_FAILED, zone_name, strerror(errno));
371*7c478bd9Sstevel@tonic-gate 		(void) dlclose(dlhdl);
372*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
373*7c478bd9Sstevel@tonic-gate 	}
374*7c478bd9Sstevel@tonic-gate 	set_root_devices_dev_dir(zpath, 1);
375*7c478bd9Sstevel@tonic-gate 
376*7c478bd9Sstevel@tonic-gate 	if ((hdl = zonecfg_init_handle()) == NULL) {
377*7c478bd9Sstevel@tonic-gate 		err_print(ZONE_REP_FAILED, zone_name, strerror(errno));
378*7c478bd9Sstevel@tonic-gate 		(void) dlclose(dlhdl);
379*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
380*7c478bd9Sstevel@tonic-gate 	}
381*7c478bd9Sstevel@tonic-gate 
382*7c478bd9Sstevel@tonic-gate 	if ((zonecfg_get_snapshot_handle(zone_name, hdl)) != Z_OK) {
383*7c478bd9Sstevel@tonic-gate 		err_print(ZONE_REP_FAILED, zone_name, strerror(errno));
384*7c478bd9Sstevel@tonic-gate 		zonecfg_fini_handle(hdl);
385*7c478bd9Sstevel@tonic-gate 		(void) dlclose(dlhdl);
386*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
387*7c478bd9Sstevel@tonic-gate 	}
388*7c478bd9Sstevel@tonic-gate 	(void) dlclose(dlhdl);
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate 	zone_head = s_malloc(sizeof (struct zone_devinfo));
391*7c478bd9Sstevel@tonic-gate 	zone_head->zone_path = s_strdup(zpath);
392*7c478bd9Sstevel@tonic-gate 	zone_head->zone_name = s_strdup(zone_name);
393*7c478bd9Sstevel@tonic-gate 	zone_head->zone_dochdl = hdl;
394*7c478bd9Sstevel@tonic-gate 	zone_head->zone_next = NULL;
395*7c478bd9Sstevel@tonic-gate 	zone_cmd_mode = 1;
396*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
397*7c478bd9Sstevel@tonic-gate }
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate /*
400*7c478bd9Sstevel@tonic-gate  * Parse arguments for all 6 programs handled from devfsadm.
401*7c478bd9Sstevel@tonic-gate  */
402*7c478bd9Sstevel@tonic-gate static void
403*7c478bd9Sstevel@tonic-gate parse_args(int argc, char *argv[])
404*7c478bd9Sstevel@tonic-gate {
405*7c478bd9Sstevel@tonic-gate 	char opt;
406*7c478bd9Sstevel@tonic-gate 	char get_linkcompat_opts = FALSE;
407*7c478bd9Sstevel@tonic-gate 	char *compat_class;
408*7c478bd9Sstevel@tonic-gate 	int num_aliases = 0;
409*7c478bd9Sstevel@tonic-gate 	int len;
410*7c478bd9Sstevel@tonic-gate 	int retval;
411*7c478bd9Sstevel@tonic-gate 	int add_bind = FALSE;
412*7c478bd9Sstevel@tonic-gate 	struct aliases *ap = NULL;
413*7c478bd9Sstevel@tonic-gate 	struct aliases *a_head = NULL;
414*7c478bd9Sstevel@tonic-gate 	struct aliases *a_tail = NULL;
415*7c478bd9Sstevel@tonic-gate 	struct modconfig mc;
416*7c478bd9Sstevel@tonic-gate 
417*7c478bd9Sstevel@tonic-gate 	if (strcmp(prog, DISKS) == 0) {
418*7c478bd9Sstevel@tonic-gate 		compat_class = "disk";
419*7c478bd9Sstevel@tonic-gate 		get_linkcompat_opts = TRUE;
420*7c478bd9Sstevel@tonic-gate 
421*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(prog, TAPES) == 0) {
422*7c478bd9Sstevel@tonic-gate 		compat_class = "tape";
423*7c478bd9Sstevel@tonic-gate 		get_linkcompat_opts = TRUE;
424*7c478bd9Sstevel@tonic-gate 
425*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(prog, PORTS) == 0) {
426*7c478bd9Sstevel@tonic-gate 		compat_class = "port";
427*7c478bd9Sstevel@tonic-gate 		get_linkcompat_opts = TRUE;
428*7c478bd9Sstevel@tonic-gate 
429*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(prog, AUDLINKS) == 0) {
430*7c478bd9Sstevel@tonic-gate 		compat_class = "audio";
431*7c478bd9Sstevel@tonic-gate 		get_linkcompat_opts = TRUE;
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(prog, DEVLINKS) == 0) {
434*7c478bd9Sstevel@tonic-gate 		devlinktab_file = DEVLINKTAB_FILE;
435*7c478bd9Sstevel@tonic-gate 
436*7c478bd9Sstevel@tonic-gate 		build_devices = FALSE;
437*7c478bd9Sstevel@tonic-gate 		load_attach_drv = FALSE;
438*7c478bd9Sstevel@tonic-gate 
439*7c478bd9Sstevel@tonic-gate 		while ((opt = getopt(argc, argv, "dnr:st:vV:")) != EOF) {
440*7c478bd9Sstevel@tonic-gate 			switch (opt) {
441*7c478bd9Sstevel@tonic-gate 			case 'd':
442*7c478bd9Sstevel@tonic-gate 				file_mods = FALSE;
443*7c478bd9Sstevel@tonic-gate 				flush_path_to_inst_enable = FALSE;
444*7c478bd9Sstevel@tonic-gate 				devlinks_debug = TRUE;
445*7c478bd9Sstevel@tonic-gate 				break;
446*7c478bd9Sstevel@tonic-gate 			case 'n':
447*7c478bd9Sstevel@tonic-gate 				/* prevent driver loading and deferred attach */
448*7c478bd9Sstevel@tonic-gate 				load_attach_drv = FALSE;
449*7c478bd9Sstevel@tonic-gate 				break;
450*7c478bd9Sstevel@tonic-gate 			case 'r':
451*7c478bd9Sstevel@tonic-gate 				set_root_devices_dev_dir(optarg, 0);
452*7c478bd9Sstevel@tonic-gate 				if (zone_pathcheck(root_dir) !=
453*7c478bd9Sstevel@tonic-gate 				    DEVFSADM_SUCCESS)
454*7c478bd9Sstevel@tonic-gate 					devfsadm_exit(1);
455*7c478bd9Sstevel@tonic-gate 				break;
456*7c478bd9Sstevel@tonic-gate 			case 's':
457*7c478bd9Sstevel@tonic-gate 				/*
458*7c478bd9Sstevel@tonic-gate 				 * suppress.  don't create/remove links/nodes
459*7c478bd9Sstevel@tonic-gate 				 * useful with -v or -V
460*7c478bd9Sstevel@tonic-gate 				 */
461*7c478bd9Sstevel@tonic-gate 				file_mods = FALSE;
462*7c478bd9Sstevel@tonic-gate 				flush_path_to_inst_enable = FALSE;
463*7c478bd9Sstevel@tonic-gate 				break;
464*7c478bd9Sstevel@tonic-gate 			case 't':
465*7c478bd9Sstevel@tonic-gate 				/* supply a non-default table file */
466*7c478bd9Sstevel@tonic-gate 				devlinktab_file = optarg;
467*7c478bd9Sstevel@tonic-gate 				break;
468*7c478bd9Sstevel@tonic-gate 			case 'v':
469*7c478bd9Sstevel@tonic-gate 				/* documented verbose flag */
470*7c478bd9Sstevel@tonic-gate 				add_verbose_id(VERBOSE_MID);
471*7c478bd9Sstevel@tonic-gate 				break;
472*7c478bd9Sstevel@tonic-gate 			case 'V':
473*7c478bd9Sstevel@tonic-gate 				/* undocumented for extra verbose levels */
474*7c478bd9Sstevel@tonic-gate 				add_verbose_id(optarg);
475*7c478bd9Sstevel@tonic-gate 				break;
476*7c478bd9Sstevel@tonic-gate 			default:
477*7c478bd9Sstevel@tonic-gate 				usage();
478*7c478bd9Sstevel@tonic-gate 				break;
479*7c478bd9Sstevel@tonic-gate 			}
480*7c478bd9Sstevel@tonic-gate 		}
481*7c478bd9Sstevel@tonic-gate 
482*7c478bd9Sstevel@tonic-gate 		if (optind < argc) {
483*7c478bd9Sstevel@tonic-gate 			usage();
484*7c478bd9Sstevel@tonic-gate 		}
485*7c478bd9Sstevel@tonic-gate 
486*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(prog, DRVCONFIG) == 0) {
487*7c478bd9Sstevel@tonic-gate 		build_dev = FALSE;
488*7c478bd9Sstevel@tonic-gate 
489*7c478bd9Sstevel@tonic-gate 		while ((opt =
490*7c478bd9Sstevel@tonic-gate 			getopt(argc, argv, "a:bdc:i:m:np:R:r:svV:")) != EOF) {
491*7c478bd9Sstevel@tonic-gate 			switch (opt) {
492*7c478bd9Sstevel@tonic-gate 			case 'a':
493*7c478bd9Sstevel@tonic-gate 				ap = calloc(sizeof (struct aliases), 1);
494*7c478bd9Sstevel@tonic-gate 				ap->a_name = dequote(optarg);
495*7c478bd9Sstevel@tonic-gate 				len = strlen(ap->a_name) + 1;
496*7c478bd9Sstevel@tonic-gate 				if (len > MAXMODCONFNAME) {
497*7c478bd9Sstevel@tonic-gate 					err_print(ALIAS_TOO_LONG,
498*7c478bd9Sstevel@tonic-gate 					    MAXMODCONFNAME, ap->a_name);
499*7c478bd9Sstevel@tonic-gate 					devfsadm_exit(1);
500*7c478bd9Sstevel@tonic-gate 				}
501*7c478bd9Sstevel@tonic-gate 				ap->a_len = len;
502*7c478bd9Sstevel@tonic-gate 				if (a_tail == NULL) {
503*7c478bd9Sstevel@tonic-gate 					a_head = ap;
504*7c478bd9Sstevel@tonic-gate 				} else {
505*7c478bd9Sstevel@tonic-gate 					a_tail->a_next = ap;
506*7c478bd9Sstevel@tonic-gate 				}
507*7c478bd9Sstevel@tonic-gate 				a_tail = ap;
508*7c478bd9Sstevel@tonic-gate 				num_aliases++;
509*7c478bd9Sstevel@tonic-gate 				add_bind = TRUE;
510*7c478bd9Sstevel@tonic-gate 				break;
511*7c478bd9Sstevel@tonic-gate 			case 'b':
512*7c478bd9Sstevel@tonic-gate 				add_bind = TRUE;
513*7c478bd9Sstevel@tonic-gate 				break;
514*7c478bd9Sstevel@tonic-gate 			case 'c':
515*7c478bd9Sstevel@tonic-gate 				(void) strcpy(mc.drvclass, optarg);
516*7c478bd9Sstevel@tonic-gate 				break;
517*7c478bd9Sstevel@tonic-gate 			case 'd':
518*7c478bd9Sstevel@tonic-gate 				/*
519*7c478bd9Sstevel@tonic-gate 				 * need to keep for compatibility, but
520*7c478bd9Sstevel@tonic-gate 				 * do nothing.
521*7c478bd9Sstevel@tonic-gate 				 */
522*7c478bd9Sstevel@tonic-gate 				break;
523*7c478bd9Sstevel@tonic-gate 			case 'i':
524*7c478bd9Sstevel@tonic-gate 				single_drv = TRUE;
525*7c478bd9Sstevel@tonic-gate 				(void) strcpy(mc.drvname, optarg);
526*7c478bd9Sstevel@tonic-gate 				driver = s_strdup(optarg);
527*7c478bd9Sstevel@tonic-gate 				break;
528*7c478bd9Sstevel@tonic-gate 			case 'm':
529*7c478bd9Sstevel@tonic-gate 				mc.major = atoi(optarg);
530*7c478bd9Sstevel@tonic-gate 				break;
531*7c478bd9Sstevel@tonic-gate 			case 'n':
532*7c478bd9Sstevel@tonic-gate 				/* prevent driver loading and deferred attach */
533*7c478bd9Sstevel@tonic-gate 				load_attach_drv = FALSE;
534*7c478bd9Sstevel@tonic-gate 				break;
535*7c478bd9Sstevel@tonic-gate 			case 'p':
536*7c478bd9Sstevel@tonic-gate 				/* specify alternate path_to_inst file */
537*7c478bd9Sstevel@tonic-gate 				inst_file = s_strdup(optarg);
538*7c478bd9Sstevel@tonic-gate 				break;
539*7c478bd9Sstevel@tonic-gate 			case 'R':
540*7c478bd9Sstevel@tonic-gate 				/*
541*7c478bd9Sstevel@tonic-gate 				 * Private flag for suninstall to populate
542*7c478bd9Sstevel@tonic-gate 				 * device information on the installed root.
543*7c478bd9Sstevel@tonic-gate 				 */
544*7c478bd9Sstevel@tonic-gate 				root_dir = s_strdup(optarg);
545*7c478bd9Sstevel@tonic-gate 				if (zone_pathcheck(root_dir) !=
546*7c478bd9Sstevel@tonic-gate 				    DEVFSADM_SUCCESS)
547*7c478bd9Sstevel@tonic-gate 				devfsadm_exit(devfsadm_copy());
548*7c478bd9Sstevel@tonic-gate 				break;
549*7c478bd9Sstevel@tonic-gate 			case 'r':
550*7c478bd9Sstevel@tonic-gate 				devices_dir = s_strdup(optarg);
551*7c478bd9Sstevel@tonic-gate 				if (zone_pathcheck(devices_dir) !=
552*7c478bd9Sstevel@tonic-gate 				    DEVFSADM_SUCCESS)
553*7c478bd9Sstevel@tonic-gate 					devfsadm_exit(1);
554*7c478bd9Sstevel@tonic-gate 				break;
555*7c478bd9Sstevel@tonic-gate 			case 's':
556*7c478bd9Sstevel@tonic-gate 				/*
557*7c478bd9Sstevel@tonic-gate 				 * suppress.  don't create nodes
558*7c478bd9Sstevel@tonic-gate 				 * useful with -v or -V
559*7c478bd9Sstevel@tonic-gate 				 */
560*7c478bd9Sstevel@tonic-gate 				file_mods = FALSE;
561*7c478bd9Sstevel@tonic-gate 				flush_path_to_inst_enable = FALSE;
562*7c478bd9Sstevel@tonic-gate 				break;
563*7c478bd9Sstevel@tonic-gate 			case 'v':
564*7c478bd9Sstevel@tonic-gate 				/* documented verbose flag */
565*7c478bd9Sstevel@tonic-gate 				add_verbose_id(VERBOSE_MID);
566*7c478bd9Sstevel@tonic-gate 				break;
567*7c478bd9Sstevel@tonic-gate 			case 'V':
568*7c478bd9Sstevel@tonic-gate 				/* undocumented for extra verbose levels */
569*7c478bd9Sstevel@tonic-gate 				add_verbose_id(optarg);
570*7c478bd9Sstevel@tonic-gate 				break;
571*7c478bd9Sstevel@tonic-gate 			default:
572*7c478bd9Sstevel@tonic-gate 				usage();
573*7c478bd9Sstevel@tonic-gate 			}
574*7c478bd9Sstevel@tonic-gate 		}
575*7c478bd9Sstevel@tonic-gate 
576*7c478bd9Sstevel@tonic-gate 		if (optind < argc) {
577*7c478bd9Sstevel@tonic-gate 			usage();
578*7c478bd9Sstevel@tonic-gate 		}
579*7c478bd9Sstevel@tonic-gate 
580*7c478bd9Sstevel@tonic-gate 		if ((add_bind == TRUE) && (mc.major == -1 ||
581*7c478bd9Sstevel@tonic-gate 		    mc.drvname[0] == NULL)) {
582*7c478bd9Sstevel@tonic-gate 			err_print(MAJOR_AND_B_FLAG);
583*7c478bd9Sstevel@tonic-gate 			devfsadm_exit(1);
584*7c478bd9Sstevel@tonic-gate 		}
585*7c478bd9Sstevel@tonic-gate 		if (add_bind == TRUE) {
586*7c478bd9Sstevel@tonic-gate 			mc.num_aliases = num_aliases;
587*7c478bd9Sstevel@tonic-gate 			mc.ap = a_head;
588*7c478bd9Sstevel@tonic-gate 			retval =  modctl(MODADDMAJBIND, NULL, (caddr_t)&mc);
589*7c478bd9Sstevel@tonic-gate 			if (retval < 0) {
590*7c478bd9Sstevel@tonic-gate 				err_print(MODCTL_ADDMAJBIND);
591*7c478bd9Sstevel@tonic-gate 			}
592*7c478bd9Sstevel@tonic-gate 			devfsadm_exit(retval);
593*7c478bd9Sstevel@tonic-gate 		}
594*7c478bd9Sstevel@tonic-gate 
595*7c478bd9Sstevel@tonic-gate 	} else if ((strcmp(prog, DEVFSADM) == 0) ||
596*7c478bd9Sstevel@tonic-gate 	    (strcmp(prog, DEVFSADMD) == 0)) {
597*7c478bd9Sstevel@tonic-gate 		char *zonename = NULL;
598*7c478bd9Sstevel@tonic-gate 		enum zreg_op zoneop;
599*7c478bd9Sstevel@tonic-gate 		int init_drvconf = 0;
600*7c478bd9Sstevel@tonic-gate 		int init_perm = 0;
601*7c478bd9Sstevel@tonic-gate 		int public_mode = 0;
602*7c478bd9Sstevel@tonic-gate 
603*7c478bd9Sstevel@tonic-gate 		if (strcmp(prog, DEVFSADMD) == 0) {
604*7c478bd9Sstevel@tonic-gate 			daemon_mode = TRUE;
605*7c478bd9Sstevel@tonic-gate 		}
606*7c478bd9Sstevel@tonic-gate 
607*7c478bd9Sstevel@tonic-gate 		devlinktab_file = DEVLINKTAB_FILE;
608*7c478bd9Sstevel@tonic-gate 
609*7c478bd9Sstevel@tonic-gate 		while ((opt = getopt(argc, argv,
610*7c478bd9Sstevel@tonic-gate 		    "Cc:dIi:l:np:PR:r:st:vV:x:z:Z:")) != EOF) {
611*7c478bd9Sstevel@tonic-gate 			if (opt == 'I' || opt == 'P') {
612*7c478bd9Sstevel@tonic-gate 				if (public_mode)
613*7c478bd9Sstevel@tonic-gate 					usage();
614*7c478bd9Sstevel@tonic-gate 			} else {
615*7c478bd9Sstevel@tonic-gate 				if (init_perm || init_drvconf)
616*7c478bd9Sstevel@tonic-gate 					usage();
617*7c478bd9Sstevel@tonic-gate 				public_mode = 1;
618*7c478bd9Sstevel@tonic-gate 			}
619*7c478bd9Sstevel@tonic-gate 			switch (opt) {
620*7c478bd9Sstevel@tonic-gate 			case 'C':
621*7c478bd9Sstevel@tonic-gate 				cleanup = TRUE;
622*7c478bd9Sstevel@tonic-gate 				break;
623*7c478bd9Sstevel@tonic-gate 			case 'c':
624*7c478bd9Sstevel@tonic-gate 				num_classes++;
625*7c478bd9Sstevel@tonic-gate 				classes = s_realloc(classes, num_classes *
626*7c478bd9Sstevel@tonic-gate 						    sizeof (char *));
627*7c478bd9Sstevel@tonic-gate 				classes[num_classes - 1] = optarg;
628*7c478bd9Sstevel@tonic-gate 				break;
629*7c478bd9Sstevel@tonic-gate 			case  'd':
630*7c478bd9Sstevel@tonic-gate 				if (daemon_mode == FALSE) {
631*7c478bd9Sstevel@tonic-gate 					devalloc_off = TRUE;
632*7c478bd9Sstevel@tonic-gate 					build_dev = FALSE;
633*7c478bd9Sstevel@tonic-gate 				}
634*7c478bd9Sstevel@tonic-gate 				break;
635*7c478bd9Sstevel@tonic-gate 			case 'I':	/* update kernel driver.conf cache */
636*7c478bd9Sstevel@tonic-gate 				if (daemon_mode == TRUE)
637*7c478bd9Sstevel@tonic-gate 					usage();
638*7c478bd9Sstevel@tonic-gate 				init_drvconf = 1;
639*7c478bd9Sstevel@tonic-gate 				break;
640*7c478bd9Sstevel@tonic-gate 			case 'i':
641*7c478bd9Sstevel@tonic-gate 				single_drv = TRUE;
642*7c478bd9Sstevel@tonic-gate 				driver = s_strdup(optarg);
643*7c478bd9Sstevel@tonic-gate 				break;
644*7c478bd9Sstevel@tonic-gate 			case 'l':
645*7c478bd9Sstevel@tonic-gate 				/* specify an alternate module load path */
646*7c478bd9Sstevel@tonic-gate 				module_dirs = s_strdup(optarg);
647*7c478bd9Sstevel@tonic-gate 				break;
648*7c478bd9Sstevel@tonic-gate 			case 'n':
649*7c478bd9Sstevel@tonic-gate 				/* prevent driver loading and deferred attach */
650*7c478bd9Sstevel@tonic-gate 				load_attach_drv = FALSE;
651*7c478bd9Sstevel@tonic-gate 				break;
652*7c478bd9Sstevel@tonic-gate 			case 'p':
653*7c478bd9Sstevel@tonic-gate 				/* specify alternate path_to_inst file */
654*7c478bd9Sstevel@tonic-gate 				inst_file = s_strdup(optarg);
655*7c478bd9Sstevel@tonic-gate 				break;
656*7c478bd9Sstevel@tonic-gate 			case 'P':
657*7c478bd9Sstevel@tonic-gate 				if (daemon_mode == TRUE)
658*7c478bd9Sstevel@tonic-gate 					usage();
659*7c478bd9Sstevel@tonic-gate 				/* load minor_perm and device_policy */
660*7c478bd9Sstevel@tonic-gate 				init_perm = 1;
661*7c478bd9Sstevel@tonic-gate 				break;
662*7c478bd9Sstevel@tonic-gate 			case 'R':
663*7c478bd9Sstevel@tonic-gate 				/*
664*7c478bd9Sstevel@tonic-gate 				 * Private flag for suninstall to populate
665*7c478bd9Sstevel@tonic-gate 				 * device information on the installed root.
666*7c478bd9Sstevel@tonic-gate 				 */
667*7c478bd9Sstevel@tonic-gate 				root_dir = s_strdup(optarg);
668*7c478bd9Sstevel@tonic-gate 				devfsadm_exit(devfsadm_copy());
669*7c478bd9Sstevel@tonic-gate 				break;
670*7c478bd9Sstevel@tonic-gate 			case 'r':
671*7c478bd9Sstevel@tonic-gate 				set_root_devices_dev_dir(optarg, 0);
672*7c478bd9Sstevel@tonic-gate 				break;
673*7c478bd9Sstevel@tonic-gate 			case 's':
674*7c478bd9Sstevel@tonic-gate 				/*
675*7c478bd9Sstevel@tonic-gate 				 * suppress. don't create/remove links/nodes
676*7c478bd9Sstevel@tonic-gate 				 * useful with -v or -V
677*7c478bd9Sstevel@tonic-gate 				 */
678*7c478bd9Sstevel@tonic-gate 				file_mods = FALSE;
679*7c478bd9Sstevel@tonic-gate 				flush_path_to_inst_enable = FALSE;
680*7c478bd9Sstevel@tonic-gate 				break;
681*7c478bd9Sstevel@tonic-gate 			case 't':
682*7c478bd9Sstevel@tonic-gate 				devlinktab_file = optarg;
683*7c478bd9Sstevel@tonic-gate 				break;
684*7c478bd9Sstevel@tonic-gate 			case 'v':
685*7c478bd9Sstevel@tonic-gate 				/* documented verbose flag */
686*7c478bd9Sstevel@tonic-gate 				add_verbose_id(VERBOSE_MID);
687*7c478bd9Sstevel@tonic-gate 				break;
688*7c478bd9Sstevel@tonic-gate 			case 'V':
689*7c478bd9Sstevel@tonic-gate 				/* undocumented: specify verbose lvl */
690*7c478bd9Sstevel@tonic-gate 				add_verbose_id(optarg);
691*7c478bd9Sstevel@tonic-gate 				break;
692*7c478bd9Sstevel@tonic-gate 			case 'x':
693*7c478bd9Sstevel@tonic-gate 				/*
694*7c478bd9Sstevel@tonic-gate 				 * x is the "private switch" option.  The
695*7c478bd9Sstevel@tonic-gate 				 * goal is to not suck up all the other
696*7c478bd9Sstevel@tonic-gate 				 * option letters.
697*7c478bd9Sstevel@tonic-gate 				 */
698*7c478bd9Sstevel@tonic-gate 				if (strcmp(optarg, "update_devlinksdb") == 0) {
699*7c478bd9Sstevel@tonic-gate 					update_database = TRUE;
700*7c478bd9Sstevel@tonic-gate 				} else if (strcmp(optarg, "no_dev") == 0) {
701*7c478bd9Sstevel@tonic-gate 					/* don't build /dev */
702*7c478bd9Sstevel@tonic-gate 					build_dev = FALSE;
703*7c478bd9Sstevel@tonic-gate 				} else if (strcmp(optarg, "no_devices") == 0) {
704*7c478bd9Sstevel@tonic-gate 					/* don't build /devices */
705*7c478bd9Sstevel@tonic-gate 					build_devices = FALSE;
706*7c478bd9Sstevel@tonic-gate 				} else if (strcmp(optarg, "no_p2i") == 0) {
707*7c478bd9Sstevel@tonic-gate 					/* don't flush path_to_inst */
708*7c478bd9Sstevel@tonic-gate 					flush_path_to_inst_enable = FALSE;
709*7c478bd9Sstevel@tonic-gate 				} else if (strcmp(optarg, "use_dicache") == 0) {
710*7c478bd9Sstevel@tonic-gate 					use_snapshot_cache = TRUE;
711*7c478bd9Sstevel@tonic-gate 				} else {
712*7c478bd9Sstevel@tonic-gate 					usage();
713*7c478bd9Sstevel@tonic-gate 				}
714*7c478bd9Sstevel@tonic-gate 				break;
715*7c478bd9Sstevel@tonic-gate 			case 'z':
716*7c478bd9Sstevel@tonic-gate 				zonename = optarg;
717*7c478bd9Sstevel@tonic-gate 				zoneop = ZONE_REG;
718*7c478bd9Sstevel@tonic-gate 				break;
719*7c478bd9Sstevel@tonic-gate 			case 'Z':
720*7c478bd9Sstevel@tonic-gate 				zonename = optarg;
721*7c478bd9Sstevel@tonic-gate 				zoneop = ZONE_UNREG;
722*7c478bd9Sstevel@tonic-gate 				break;
723*7c478bd9Sstevel@tonic-gate 			default:
724*7c478bd9Sstevel@tonic-gate 				usage();
725*7c478bd9Sstevel@tonic-gate 				break;
726*7c478bd9Sstevel@tonic-gate 			}
727*7c478bd9Sstevel@tonic-gate 
728*7c478bd9Sstevel@tonic-gate 		}
729*7c478bd9Sstevel@tonic-gate 		if (optind < argc) {
730*7c478bd9Sstevel@tonic-gate 			usage();
731*7c478bd9Sstevel@tonic-gate 		}
732*7c478bd9Sstevel@tonic-gate 
733*7c478bd9Sstevel@tonic-gate 		/*
734*7c478bd9Sstevel@tonic-gate 		 * We're not in zone mode; Check to see if the rootpath
735*7c478bd9Sstevel@tonic-gate 		 * collides with any zonepaths.
736*7c478bd9Sstevel@tonic-gate 		 */
737*7c478bd9Sstevel@tonic-gate 		if (zonename == NULL) {
738*7c478bd9Sstevel@tonic-gate 			if (zone_pathcheck(root_dir) != DEVFSADM_SUCCESS)
739*7c478bd9Sstevel@tonic-gate 				devfsadm_exit(1);
740*7c478bd9Sstevel@tonic-gate 		}
741*7c478bd9Sstevel@tonic-gate 
742*7c478bd9Sstevel@tonic-gate 		if (zonename != NULL) {
743*7c478bd9Sstevel@tonic-gate 			/*
744*7c478bd9Sstevel@tonic-gate 			 * -z and -Z cannot be used if we're the daemon.  The
745*7c478bd9Sstevel@tonic-gate 			 * daemon always manages all zones.
746*7c478bd9Sstevel@tonic-gate 			 */
747*7c478bd9Sstevel@tonic-gate 			if (daemon_mode == TRUE)
748*7c478bd9Sstevel@tonic-gate 				usage();
749*7c478bd9Sstevel@tonic-gate 
750*7c478bd9Sstevel@tonic-gate 			/*
751*7c478bd9Sstevel@tonic-gate 			 * -z and -Z are private flags, but to be paranoid we
752*7c478bd9Sstevel@tonic-gate 			 * check whether they have been combined with -r.
753*7c478bd9Sstevel@tonic-gate 			 */
754*7c478bd9Sstevel@tonic-gate 			if (*root_dir != '\0')
755*7c478bd9Sstevel@tonic-gate 				usage();
756*7c478bd9Sstevel@tonic-gate 
757*7c478bd9Sstevel@tonic-gate 			if (set_zone_params(optarg) != DEVFSADM_SUCCESS)
758*7c478bd9Sstevel@tonic-gate 				devfsadm_exit(1);
759*7c478bd9Sstevel@tonic-gate 
760*7c478bd9Sstevel@tonic-gate 			call_zone_register(zonename, zoneop);
761*7c478bd9Sstevel@tonic-gate 			if (zoneop == ZONE_UNREG)
762*7c478bd9Sstevel@tonic-gate 				devfsadm_exit(0);
763*7c478bd9Sstevel@tonic-gate 			/*
764*7c478bd9Sstevel@tonic-gate 			 * If we are in ZONE_REG mode we plow on, laying out
765*7c478bd9Sstevel@tonic-gate 			 * devices for this zone.
766*7c478bd9Sstevel@tonic-gate 			 */
767*7c478bd9Sstevel@tonic-gate 		}
768*7c478bd9Sstevel@tonic-gate 		if (init_drvconf || init_perm) {
769*7c478bd9Sstevel@tonic-gate 			/*
770*7c478bd9Sstevel@tonic-gate 			 * Load minor perm before force-loading drivers
771*7c478bd9Sstevel@tonic-gate 			 * so the correct permissions are picked up.
772*7c478bd9Sstevel@tonic-gate 			 */
773*7c478bd9Sstevel@tonic-gate 			if (init_perm)
774*7c478bd9Sstevel@tonic-gate 				load_dev_acl();
775*7c478bd9Sstevel@tonic-gate 			if (init_drvconf)
776*7c478bd9Sstevel@tonic-gate 				update_drvconf((major_t)-1);
777*7c478bd9Sstevel@tonic-gate 			devfsadm_exit(0);
778*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
779*7c478bd9Sstevel@tonic-gate 		}
780*7c478bd9Sstevel@tonic-gate 	}
781*7c478bd9Sstevel@tonic-gate 
782*7c478bd9Sstevel@tonic-gate 
783*7c478bd9Sstevel@tonic-gate 	if (get_linkcompat_opts == TRUE) {
784*7c478bd9Sstevel@tonic-gate 
785*7c478bd9Sstevel@tonic-gate 		build_devices = FALSE;
786*7c478bd9Sstevel@tonic-gate 		load_attach_drv = FALSE;
787*7c478bd9Sstevel@tonic-gate 		num_classes++;
788*7c478bd9Sstevel@tonic-gate 		classes = s_realloc(classes, num_classes *
789*7c478bd9Sstevel@tonic-gate 		    sizeof (char *));
790*7c478bd9Sstevel@tonic-gate 		classes[num_classes - 1] = compat_class;
791*7c478bd9Sstevel@tonic-gate 
792*7c478bd9Sstevel@tonic-gate 		while ((opt = getopt(argc, argv, "Cnr:svV:")) != EOF) {
793*7c478bd9Sstevel@tonic-gate 			switch (opt) {
794*7c478bd9Sstevel@tonic-gate 			case 'C':
795*7c478bd9Sstevel@tonic-gate 				cleanup = TRUE;
796*7c478bd9Sstevel@tonic-gate 				break;
797*7c478bd9Sstevel@tonic-gate 			case 'n':
798*7c478bd9Sstevel@tonic-gate 				/* prevent driver loading or deferred attach */
799*7c478bd9Sstevel@tonic-gate 				load_attach_drv = FALSE;
800*7c478bd9Sstevel@tonic-gate 				break;
801*7c478bd9Sstevel@tonic-gate 			case 'r':
802*7c478bd9Sstevel@tonic-gate 				set_root_devices_dev_dir(optarg, 0);
803*7c478bd9Sstevel@tonic-gate 				if (zone_pathcheck(root_dir) !=
804*7c478bd9Sstevel@tonic-gate 				    DEVFSADM_SUCCESS)
805*7c478bd9Sstevel@tonic-gate 					devfsadm_exit(1);
806*7c478bd9Sstevel@tonic-gate 				break;
807*7c478bd9Sstevel@tonic-gate 			case 's':
808*7c478bd9Sstevel@tonic-gate 				/* suppress.  don't create/remove links/nodes */
809*7c478bd9Sstevel@tonic-gate 				/* useful with -v or -V */
810*7c478bd9Sstevel@tonic-gate 				file_mods = FALSE;
811*7c478bd9Sstevel@tonic-gate 				flush_path_to_inst_enable = FALSE;
812*7c478bd9Sstevel@tonic-gate 				break;
813*7c478bd9Sstevel@tonic-gate 			case 'v':
814*7c478bd9Sstevel@tonic-gate 				/* documented verbose flag */
815*7c478bd9Sstevel@tonic-gate 				add_verbose_id(VERBOSE_MID);
816*7c478bd9Sstevel@tonic-gate 				break;
817*7c478bd9Sstevel@tonic-gate 			case 'V':
818*7c478bd9Sstevel@tonic-gate 				/* undocumented for extra verbose levels */
819*7c478bd9Sstevel@tonic-gate 				add_verbose_id(optarg);
820*7c478bd9Sstevel@tonic-gate 				break;
821*7c478bd9Sstevel@tonic-gate 			default:
822*7c478bd9Sstevel@tonic-gate 				usage();
823*7c478bd9Sstevel@tonic-gate 			}
824*7c478bd9Sstevel@tonic-gate 		}
825*7c478bd9Sstevel@tonic-gate 		if (optind < argc) {
826*7c478bd9Sstevel@tonic-gate 			usage();
827*7c478bd9Sstevel@tonic-gate 		}
828*7c478bd9Sstevel@tonic-gate 	}
829*7c478bd9Sstevel@tonic-gate }
830*7c478bd9Sstevel@tonic-gate 
831*7c478bd9Sstevel@tonic-gate void
832*7c478bd9Sstevel@tonic-gate usage(void)
833*7c478bd9Sstevel@tonic-gate {
834*7c478bd9Sstevel@tonic-gate 	if (strcmp(prog, DEVLINKS) == 0) {
835*7c478bd9Sstevel@tonic-gate 		err_print(DEVLINKS_USAGE);
836*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(prog, DRVCONFIG) == 0) {
837*7c478bd9Sstevel@tonic-gate 		err_print(DRVCONFIG_USAGE);
838*7c478bd9Sstevel@tonic-gate 	} else if ((strcmp(prog, DEVFSADM) == 0) ||
839*7c478bd9Sstevel@tonic-gate 			(strcmp(prog, DEVFSADMD) == 0)) {
840*7c478bd9Sstevel@tonic-gate 		err_print(DEVFSADM_USAGE);
841*7c478bd9Sstevel@tonic-gate 	} else {
842*7c478bd9Sstevel@tonic-gate 		err_print(COMPAT_LINK_USAGE);
843*7c478bd9Sstevel@tonic-gate 	}
844*7c478bd9Sstevel@tonic-gate 
845*7c478bd9Sstevel@tonic-gate 	devfsadm_exit(1);
846*7c478bd9Sstevel@tonic-gate }
847*7c478bd9Sstevel@tonic-gate 
848*7c478bd9Sstevel@tonic-gate static void
849*7c478bd9Sstevel@tonic-gate devi_tree_walk(struct dca_impl *dcip, int flags, char *ev_subclass)
850*7c478bd9Sstevel@tonic-gate {
851*7c478bd9Sstevel@tonic-gate 	char *msg, *name;
852*7c478bd9Sstevel@tonic-gate 	struct mlist	mlist = {0};
853*7c478bd9Sstevel@tonic-gate 	di_node_t	node;
854*7c478bd9Sstevel@tonic-gate 
855*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "devi_tree_walk: root=%s, minor=%s, driver=%s,"
856*7c478bd9Sstevel@tonic-gate 	    " error=%d, flags=%u\n", dcip->dci_root,
857*7c478bd9Sstevel@tonic-gate 	    dcip->dci_minor ? dcip->dci_minor : "<NULL>",
858*7c478bd9Sstevel@tonic-gate 	    dcip->dci_driver ? dcip->dci_driver : "<NULL>", dcip->dci_error,
859*7c478bd9Sstevel@tonic-gate 	    dcip->dci_flags);
860*7c478bd9Sstevel@tonic-gate 
861*7c478bd9Sstevel@tonic-gate 	assert(dcip->dci_root);
862*7c478bd9Sstevel@tonic-gate 
863*7c478bd9Sstevel@tonic-gate 	if (dcip->dci_flags & DCA_LOAD_DRV) {
864*7c478bd9Sstevel@tonic-gate 		node = di_init_driver(dcip->dci_driver, flags);
865*7c478bd9Sstevel@tonic-gate 		msg = DRIVER_FAILURE;
866*7c478bd9Sstevel@tonic-gate 		name = dcip->dci_driver;
867*7c478bd9Sstevel@tonic-gate 	} else {
868*7c478bd9Sstevel@tonic-gate 		node = di_init(dcip->dci_root, flags);
869*7c478bd9Sstevel@tonic-gate 		msg = DI_INIT_FAILED;
870*7c478bd9Sstevel@tonic-gate 		name = dcip->dci_root;
871*7c478bd9Sstevel@tonic-gate 	}
872*7c478bd9Sstevel@tonic-gate 
873*7c478bd9Sstevel@tonic-gate 	if (node == DI_NODE_NIL) {
874*7c478bd9Sstevel@tonic-gate 		dcip->dci_error = errno;
875*7c478bd9Sstevel@tonic-gate 		/*
876*7c478bd9Sstevel@tonic-gate 		 * Rapid hotplugging (commonly seen during USB testing),
877*7c478bd9Sstevel@tonic-gate 		 * may remove a device before the create event for it
878*7c478bd9Sstevel@tonic-gate 		 * has been processed. To prevent alarming users with
879*7c478bd9Sstevel@tonic-gate 		 * a superfluous message, we suppress error messages
880*7c478bd9Sstevel@tonic-gate 		 * for ENXIO and hotplug.
881*7c478bd9Sstevel@tonic-gate 		 */
882*7c478bd9Sstevel@tonic-gate 		if (!(errno == ENXIO && (dcip->dci_flags & DCA_HOT_PLUG)))
883*7c478bd9Sstevel@tonic-gate 			err_print(msg, name, strerror(dcip->dci_error));
884*7c478bd9Sstevel@tonic-gate 		return;
885*7c478bd9Sstevel@tonic-gate 	}
886*7c478bd9Sstevel@tonic-gate 
887*7c478bd9Sstevel@tonic-gate 	if (dcip->dci_flags & DCA_FLUSH_PATHINST)
888*7c478bd9Sstevel@tonic-gate 		flush_path_to_inst();
889*7c478bd9Sstevel@tonic-gate 
890*7c478bd9Sstevel@tonic-gate 	dcip->dci_arg = &mlist;
891*7c478bd9Sstevel@tonic-gate 
892*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "walking device tree\n");
893*7c478bd9Sstevel@tonic-gate 
894*7c478bd9Sstevel@tonic-gate 	(void) di_walk_minor(node, NULL, DI_CHECK_ALIAS, dcip,
895*7c478bd9Sstevel@tonic-gate 	    check_minor_type);
896*7c478bd9Sstevel@tonic-gate 
897*7c478bd9Sstevel@tonic-gate 	process_deferred_links(dcip, DCA_CREATE_LINK);
898*7c478bd9Sstevel@tonic-gate 
899*7c478bd9Sstevel@tonic-gate 	dcip->dci_arg = NULL;
900*7c478bd9Sstevel@tonic-gate 
901*7c478bd9Sstevel@tonic-gate 	/*
902*7c478bd9Sstevel@tonic-gate 	 * Finished creating devfs files and dev links.
903*7c478bd9Sstevel@tonic-gate 	 * Log sysevent and notify RCM.
904*7c478bd9Sstevel@tonic-gate 	 */
905*7c478bd9Sstevel@tonic-gate 	if (ev_subclass)
906*7c478bd9Sstevel@tonic-gate 		build_and_log_event(EC_DEV_ADD, ev_subclass, dcip->dci_root,
907*7c478bd9Sstevel@tonic-gate 		    node);
908*7c478bd9Sstevel@tonic-gate 
909*7c478bd9Sstevel@tonic-gate 	if ((dcip->dci_flags & DCA_NOTIFY_RCM) && rcm_hdl)
910*7c478bd9Sstevel@tonic-gate 		(void) notify_rcm(node, dcip->dci_minor);
911*7c478bd9Sstevel@tonic-gate 
912*7c478bd9Sstevel@tonic-gate 	di_fini(node);
913*7c478bd9Sstevel@tonic-gate }
914*7c478bd9Sstevel@tonic-gate 
915*7c478bd9Sstevel@tonic-gate static void
916*7c478bd9Sstevel@tonic-gate process_deferred_links(struct dca_impl *dcip, int flags)
917*7c478bd9Sstevel@tonic-gate {
918*7c478bd9Sstevel@tonic-gate 	struct mlist	*dep;
919*7c478bd9Sstevel@tonic-gate 	struct minor	*mp, *smp;
920*7c478bd9Sstevel@tonic-gate 
921*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "processing deferred links\n");
922*7c478bd9Sstevel@tonic-gate 
923*7c478bd9Sstevel@tonic-gate 	dep = dcip->dci_arg;
924*7c478bd9Sstevel@tonic-gate 
925*7c478bd9Sstevel@tonic-gate 	/*
926*7c478bd9Sstevel@tonic-gate 	 * The list head is not used during the deferred create phase
927*7c478bd9Sstevel@tonic-gate 	 */
928*7c478bd9Sstevel@tonic-gate 	dcip->dci_arg = NULL;
929*7c478bd9Sstevel@tonic-gate 
930*7c478bd9Sstevel@tonic-gate 	assert(dep);
931*7c478bd9Sstevel@tonic-gate 	assert((dep->head == NULL) ^ (dep->tail != NULL));
932*7c478bd9Sstevel@tonic-gate 	assert(flags == DCA_FREE_LIST || flags == DCA_CREATE_LINK);
933*7c478bd9Sstevel@tonic-gate 
934*7c478bd9Sstevel@tonic-gate 	for (smp = NULL, mp = dep->head; mp; mp = mp->next) {
935*7c478bd9Sstevel@tonic-gate 		if (flags == DCA_CREATE_LINK)
936*7c478bd9Sstevel@tonic-gate 			(void) check_minor_type(mp->node, mp->minor, dcip);
937*7c478bd9Sstevel@tonic-gate 		free(smp);
938*7c478bd9Sstevel@tonic-gate 		smp = mp;
939*7c478bd9Sstevel@tonic-gate 	}
940*7c478bd9Sstevel@tonic-gate 
941*7c478bd9Sstevel@tonic-gate 	free(smp);
942*7c478bd9Sstevel@tonic-gate }
943*7c478bd9Sstevel@tonic-gate 
944*7c478bd9Sstevel@tonic-gate /*
945*7c478bd9Sstevel@tonic-gate  * Called in non-daemon mode to take a snap shot of the devinfo tree.
946*7c478bd9Sstevel@tonic-gate  * Then it calls the appropriate functions to build /devices and /dev.
947*7c478bd9Sstevel@tonic-gate  * It also flushes path_to_inst.
948*7c478bd9Sstevel@tonic-gate  */
949*7c478bd9Sstevel@tonic-gate void
950*7c478bd9Sstevel@tonic-gate process_devinfo_tree()
951*7c478bd9Sstevel@tonic-gate {
952*7c478bd9Sstevel@tonic-gate 	uint_t		flags = DINFOCPYALL;
953*7c478bd9Sstevel@tonic-gate 	struct dca_impl	dci;
954*7c478bd9Sstevel@tonic-gate 	char		name[MAXNAMELEN];
955*7c478bd9Sstevel@tonic-gate 	char		*fcn = "process_devinfo_tree: ";
956*7c478bd9Sstevel@tonic-gate 
957*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "%senter\n", fcn);
958*7c478bd9Sstevel@tonic-gate 
959*7c478bd9Sstevel@tonic-gate 	dca_impl_init("/", NULL, &dci);
960*7c478bd9Sstevel@tonic-gate 
961*7c478bd9Sstevel@tonic-gate 	lock_dev();
962*7c478bd9Sstevel@tonic-gate 
963*7c478bd9Sstevel@tonic-gate 	/*
964*7c478bd9Sstevel@tonic-gate 	 * Update kernel driver.conf cache when devfsadm/drvconfig
965*7c478bd9Sstevel@tonic-gate 	 * is invoked to build /devices and /dev.
966*7c478bd9Sstevel@tonic-gate 	 */
967*7c478bd9Sstevel@tonic-gate 	if (load_attach_drv == TRUE)
968*7c478bd9Sstevel@tonic-gate 		update_drvconf((major_t)-1);
969*7c478bd9Sstevel@tonic-gate 
970*7c478bd9Sstevel@tonic-gate 	if (single_drv == TRUE) {
971*7c478bd9Sstevel@tonic-gate 		/*
972*7c478bd9Sstevel@tonic-gate 		 * load a single driver, but walk the entire devinfo tree
973*7c478bd9Sstevel@tonic-gate 		 */
974*7c478bd9Sstevel@tonic-gate 		if (load_attach_drv == FALSE)
975*7c478bd9Sstevel@tonic-gate 			err_print(DRV_LOAD_REQD);
976*7c478bd9Sstevel@tonic-gate 
977*7c478bd9Sstevel@tonic-gate 		vprint(CHATTY_MID, "%sattaching driver (%s)\n", fcn, driver);
978*7c478bd9Sstevel@tonic-gate 
979*7c478bd9Sstevel@tonic-gate 		dci.dci_flags |= DCA_LOAD_DRV;
980*7c478bd9Sstevel@tonic-gate 		(void) snprintf(name, sizeof (name), "%s", driver);
981*7c478bd9Sstevel@tonic-gate 		dci.dci_driver = name;
982*7c478bd9Sstevel@tonic-gate 
983*7c478bd9Sstevel@tonic-gate 	} else if (load_attach_drv == TRUE) {
984*7c478bd9Sstevel@tonic-gate 		/*
985*7c478bd9Sstevel@tonic-gate 		 * load and attach all drivers, then walk the entire tree.
986*7c478bd9Sstevel@tonic-gate 		 * If the cache flag is set, use DINFOCACHE to get cached
987*7c478bd9Sstevel@tonic-gate 		 * data.
988*7c478bd9Sstevel@tonic-gate 		 */
989*7c478bd9Sstevel@tonic-gate 		if (use_snapshot_cache == TRUE) {
990*7c478bd9Sstevel@tonic-gate 			flags = DINFOCACHE;
991*7c478bd9Sstevel@tonic-gate 			vprint(CHATTY_MID, "%susing snapshot cache\n", fcn);
992*7c478bd9Sstevel@tonic-gate 		} else {
993*7c478bd9Sstevel@tonic-gate 			vprint(CHATTY_MID, "%sattaching all drivers\n", fcn);
994*7c478bd9Sstevel@tonic-gate 			flags |= DINFOFORCE;
995*7c478bd9Sstevel@tonic-gate 		}
996*7c478bd9Sstevel@tonic-gate 	}
997*7c478bd9Sstevel@tonic-gate 
998*7c478bd9Sstevel@tonic-gate 	if (((load_attach_drv == TRUE) || (single_drv == TRUE)) &&
999*7c478bd9Sstevel@tonic-gate 	    (build_devices == TRUE)) {
1000*7c478bd9Sstevel@tonic-gate 		dci.dci_flags |= DCA_FLUSH_PATHINST;
1001*7c478bd9Sstevel@tonic-gate 	}
1002*7c478bd9Sstevel@tonic-gate 
1003*7c478bd9Sstevel@tonic-gate 	/* handle pre-cleanup operations desired by the modules. */
1004*7c478bd9Sstevel@tonic-gate 	pre_and_post_cleanup(RM_PRE);
1005*7c478bd9Sstevel@tonic-gate 
1006*7c478bd9Sstevel@tonic-gate 	devi_tree_walk(&dci, flags, NULL);
1007*7c478bd9Sstevel@tonic-gate 
1008*7c478bd9Sstevel@tonic-gate 	if (dci.dci_error) {
1009*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1010*7c478bd9Sstevel@tonic-gate 	}
1011*7c478bd9Sstevel@tonic-gate 
1012*7c478bd9Sstevel@tonic-gate 	/* handle post-cleanup operations desired by the modules. */
1013*7c478bd9Sstevel@tonic-gate 	pre_and_post_cleanup(RM_POST);
1014*7c478bd9Sstevel@tonic-gate 
1015*7c478bd9Sstevel@tonic-gate 	/*
1016*7c478bd9Sstevel@tonic-gate 	 * Remove dangling entries from /etc/devices/devid_cache
1017*7c478bd9Sstevel@tonic-gate 	 * if we forceloaded the entire device tree.
1018*7c478bd9Sstevel@tonic-gate 	 */
1019*7c478bd9Sstevel@tonic-gate 	if (cleanup && (flags & DINFOFORCE) == 0)
1020*7c478bd9Sstevel@tonic-gate 		(void) modctl(MODCLEANUP, NULL, 0, NULL);
1021*7c478bd9Sstevel@tonic-gate 
1022*7c478bd9Sstevel@tonic-gate 	unlock_dev(SYNC_STATE);
1023*7c478bd9Sstevel@tonic-gate }
1024*7c478bd9Sstevel@tonic-gate 
1025*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1026*7c478bd9Sstevel@tonic-gate static void
1027*7c478bd9Sstevel@tonic-gate print_cache_signal(int signo)
1028*7c478bd9Sstevel@tonic-gate {
1029*7c478bd9Sstevel@tonic-gate 
1030*7c478bd9Sstevel@tonic-gate 	if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
1031*7c478bd9Sstevel@tonic-gate 		err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
1032*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1033*7c478bd9Sstevel@tonic-gate 	}
1034*7c478bd9Sstevel@tonic-gate }
1035*7c478bd9Sstevel@tonic-gate 
1036*7c478bd9Sstevel@tonic-gate /*
1037*7c478bd9Sstevel@tonic-gate  * Register with eventd for messages. Create doors for synchronous
1038*7c478bd9Sstevel@tonic-gate  * link creation.
1039*7c478bd9Sstevel@tonic-gate  */
1040*7c478bd9Sstevel@tonic-gate static void
1041*7c478bd9Sstevel@tonic-gate daemon_update(void)
1042*7c478bd9Sstevel@tonic-gate {
1043*7c478bd9Sstevel@tonic-gate 	int fd;
1044*7c478bd9Sstevel@tonic-gate 	char *fcn = "daemon_update: ";
1045*7c478bd9Sstevel@tonic-gate 	char door_file[MAXPATHLEN];
1046*7c478bd9Sstevel@tonic-gate 	const char *subclass_list;
1047*7c478bd9Sstevel@tonic-gate 	sysevent_handle_t *sysevent_hp;
1048*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "%senter\n", fcn);
1049*7c478bd9Sstevel@tonic-gate 
1050*7c478bd9Sstevel@tonic-gate 	if (signal(SIGUSR1, print_cache_signal) == SIG_ERR) {
1051*7c478bd9Sstevel@tonic-gate 		err_print("signal SIGUSR1 failed: %s\n", strerror(errno));
1052*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1053*7c478bd9Sstevel@tonic-gate 	}
1054*7c478bd9Sstevel@tonic-gate 
1055*7c478bd9Sstevel@tonic-gate 	if (snprintf(door_file, sizeof (door_file),
1056*7c478bd9Sstevel@tonic-gate 	    "%s%s", root_dir, DEVFSADM_SERVICE_DOOR) >= sizeof (door_file)) {
1057*7c478bd9Sstevel@tonic-gate 		err_print("update_daemon failed to open sysevent service "
1058*7c478bd9Sstevel@tonic-gate 		    "door\n");
1059*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1060*7c478bd9Sstevel@tonic-gate 	}
1061*7c478bd9Sstevel@tonic-gate 	if ((sysevent_hp = sysevent_open_channel_alt(
1062*7c478bd9Sstevel@tonic-gate 	    door_file)) == NULL) {
1063*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_DOOR,
1064*7c478bd9Sstevel@tonic-gate 			door_file, strerror(errno));
1065*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1066*7c478bd9Sstevel@tonic-gate 	}
1067*7c478bd9Sstevel@tonic-gate 	if (sysevent_bind_subscriber(sysevent_hp, event_handler) != 0) {
1068*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_DOOR,
1069*7c478bd9Sstevel@tonic-gate 		    door_file, strerror(errno));
1070*7c478bd9Sstevel@tonic-gate 		(void) sysevent_close_channel(sysevent_hp);
1071*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1072*7c478bd9Sstevel@tonic-gate 	}
1073*7c478bd9Sstevel@tonic-gate 	subclass_list = EC_SUB_ALL;
1074*7c478bd9Sstevel@tonic-gate 	if (sysevent_register_event(sysevent_hp, EC_ALL, &subclass_list, 1)
1075*7c478bd9Sstevel@tonic-gate 	    != 0) {
1076*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_DOOR,
1077*7c478bd9Sstevel@tonic-gate 		    door_file, strerror(errno));
1078*7c478bd9Sstevel@tonic-gate 		(void) sysevent_unbind_subscriber(sysevent_hp);
1079*7c478bd9Sstevel@tonic-gate 		(void) sysevent_close_channel(sysevent_hp);
1080*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1081*7c478bd9Sstevel@tonic-gate 	}
1082*7c478bd9Sstevel@tonic-gate 
1083*7c478bd9Sstevel@tonic-gate 	if (snprintf(door_file, sizeof (door_file),
1084*7c478bd9Sstevel@tonic-gate 	    "%s/%s", dev_dir, ZONE_REG_DOOR) >= sizeof (door_file)) {
1085*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_ZONE_DOOR, door_file,
1086*7c478bd9Sstevel@tonic-gate 		    strerror(ENAMETOOLONG));
1087*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1088*7c478bd9Sstevel@tonic-gate 	}
1089*7c478bd9Sstevel@tonic-gate 	(void) s_unlink(door_file);
1090*7c478bd9Sstevel@tonic-gate 	if ((fd = open(door_file, O_RDWR | O_CREAT, ZONE_DOOR_PERMS)) == -1) {
1091*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_ZONE_DOOR, door_file, strerror(errno));
1092*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1093*7c478bd9Sstevel@tonic-gate 	}
1094*7c478bd9Sstevel@tonic-gate 	(void) close(fd);
1095*7c478bd9Sstevel@tonic-gate 	if ((fd = door_create(zone_reg_handler, NULL,
1096*7c478bd9Sstevel@tonic-gate 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
1097*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_ZONE_DOOR, door_file, strerror(errno));
1098*7c478bd9Sstevel@tonic-gate 		(void) s_unlink(door_file);
1099*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1100*7c478bd9Sstevel@tonic-gate 	}
1101*7c478bd9Sstevel@tonic-gate 	if (fattach(fd, door_file) == -1) {
1102*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_ZONE_DOOR, door_file, strerror(errno));
1103*7c478bd9Sstevel@tonic-gate 		(void) s_unlink(door_file);
1104*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1105*7c478bd9Sstevel@tonic-gate 	}
1106*7c478bd9Sstevel@tonic-gate 
1107*7c478bd9Sstevel@tonic-gate 	(void) snprintf(door_file, sizeof (door_file), "%s/%s", dev_dir,
1108*7c478bd9Sstevel@tonic-gate 	    DEVFSADM_SYNCH_DOOR);
1109*7c478bd9Sstevel@tonic-gate 
1110*7c478bd9Sstevel@tonic-gate 	(void) s_unlink(door_file);
1111*7c478bd9Sstevel@tonic-gate 	if ((fd = open(door_file, O_RDWR | O_CREAT, SYNCH_DOOR_PERMS)) == -1) {
1112*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1113*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1114*7c478bd9Sstevel@tonic-gate 	}
1115*7c478bd9Sstevel@tonic-gate 	(void) close(fd);
1116*7c478bd9Sstevel@tonic-gate 
1117*7c478bd9Sstevel@tonic-gate 	if ((fd = door_create(sync_handler, NULL,
1118*7c478bd9Sstevel@tonic-gate 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
1119*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1120*7c478bd9Sstevel@tonic-gate 		(void) s_unlink(door_file);
1121*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1122*7c478bd9Sstevel@tonic-gate 	}
1123*7c478bd9Sstevel@tonic-gate 
1124*7c478bd9Sstevel@tonic-gate 	if (fattach(fd, door_file) == -1) {
1125*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_DOOR, door_file, strerror(errno));
1126*7c478bd9Sstevel@tonic-gate 		(void) s_unlink(door_file);
1127*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
1128*7c478bd9Sstevel@tonic-gate 	}
1129*7c478bd9Sstevel@tonic-gate 	devlink_door_fd = fd;
1130*7c478bd9Sstevel@tonic-gate 
1131*7c478bd9Sstevel@tonic-gate 	/*
1132*7c478bd9Sstevel@tonic-gate 	 * Make sure devfsadm is managing any and all configured system zones.
1133*7c478bd9Sstevel@tonic-gate 	 */
1134*7c478bd9Sstevel@tonic-gate 	if (register_all_zones() != DEVFSADM_SUCCESS) {
1135*7c478bd9Sstevel@tonic-gate 		err_print(ZONE_LIST_FAILED, strerror(errno));
1136*7c478bd9Sstevel@tonic-gate 	}
1137*7c478bd9Sstevel@tonic-gate 
1138*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "%spausing\n", fcn);
1139*7c478bd9Sstevel@tonic-gate 	for (;;) {
1140*7c478bd9Sstevel@tonic-gate 		(void) pause();
1141*7c478bd9Sstevel@tonic-gate 	}
1142*7c478bd9Sstevel@tonic-gate }
1143*7c478bd9Sstevel@tonic-gate 
1144*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1145*7c478bd9Sstevel@tonic-gate static void
1146*7c478bd9Sstevel@tonic-gate sync_handler(void *cookie, char *ap, size_t asize,
1147*7c478bd9Sstevel@tonic-gate     door_desc_t *dp, uint_t ndesc)
1148*7c478bd9Sstevel@tonic-gate {
1149*7c478bd9Sstevel@tonic-gate 	door_cred_t	dcred;
1150*7c478bd9Sstevel@tonic-gate 	struct dca_off	*dcp, rdca;
1151*7c478bd9Sstevel@tonic-gate 	struct dca_impl dci;
1152*7c478bd9Sstevel@tonic-gate 
1153*7c478bd9Sstevel@tonic-gate 	/*
1154*7c478bd9Sstevel@tonic-gate 	 * Must be root to make this call
1155*7c478bd9Sstevel@tonic-gate 	 * If caller is not root, don't touch its data.
1156*7c478bd9Sstevel@tonic-gate 	 */
1157*7c478bd9Sstevel@tonic-gate 	if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
1158*7c478bd9Sstevel@tonic-gate 		dcp = &rdca;
1159*7c478bd9Sstevel@tonic-gate 		dcp->dca_error = EPERM;
1160*7c478bd9Sstevel@tonic-gate 		goto out;
1161*7c478bd9Sstevel@tonic-gate 	}
1162*7c478bd9Sstevel@tonic-gate 
1163*7c478bd9Sstevel@tonic-gate 	assert(ap);
1164*7c478bd9Sstevel@tonic-gate 	assert(asize == sizeof (*dcp));
1165*7c478bd9Sstevel@tonic-gate 
1166*7c478bd9Sstevel@tonic-gate 	dcp = (void *)ap;
1167*7c478bd9Sstevel@tonic-gate 
1168*7c478bd9Sstevel@tonic-gate 	/*
1169*7c478bd9Sstevel@tonic-gate 	 * Root is always present and is the first component of "name" member
1170*7c478bd9Sstevel@tonic-gate 	 */
1171*7c478bd9Sstevel@tonic-gate 	assert(dcp->dca_root == 0);
1172*7c478bd9Sstevel@tonic-gate 
1173*7c478bd9Sstevel@tonic-gate 	/*
1174*7c478bd9Sstevel@tonic-gate 	 * The structure passed in by the door_client uses offsets
1175*7c478bd9Sstevel@tonic-gate 	 * instead of pointers to work across address space boundaries.
1176*7c478bd9Sstevel@tonic-gate 	 * Now copy the data into a structure (dca_impl) which uses
1177*7c478bd9Sstevel@tonic-gate 	 * pointers.
1178*7c478bd9Sstevel@tonic-gate 	 */
1179*7c478bd9Sstevel@tonic-gate 	dci.dci_root = &dcp->dca_name[dcp->dca_root];
1180*7c478bd9Sstevel@tonic-gate 	dci.dci_minor = dcp->dca_minor ? &dcp->dca_name[dcp->dca_minor] : NULL;
1181*7c478bd9Sstevel@tonic-gate 	dci.dci_driver =
1182*7c478bd9Sstevel@tonic-gate 	    dcp->dca_driver ? &dcp->dca_name[dcp->dca_driver] : NULL;
1183*7c478bd9Sstevel@tonic-gate 	dci.dci_error = 0;
1184*7c478bd9Sstevel@tonic-gate 	dci.dci_flags = dcp->dca_flags | (dci.dci_driver ? DCA_LOAD_DRV : 0);
1185*7c478bd9Sstevel@tonic-gate 	dci.dci_arg = NULL;
1186*7c478bd9Sstevel@tonic-gate 
1187*7c478bd9Sstevel@tonic-gate 	lock_dev();
1188*7c478bd9Sstevel@tonic-gate 
1189*7c478bd9Sstevel@tonic-gate 	devi_tree_walk(&dci, DINFOCPYALL, NULL);
1190*7c478bd9Sstevel@tonic-gate 
1191*7c478bd9Sstevel@tonic-gate 	unlock_dev(CACHE_STATE);
1192*7c478bd9Sstevel@tonic-gate 
1193*7c478bd9Sstevel@tonic-gate 	dcp->dca_error = dci.dci_error;
1194*7c478bd9Sstevel@tonic-gate 
1195*7c478bd9Sstevel@tonic-gate 	startup_cache_sync_thread();
1196*7c478bd9Sstevel@tonic-gate 
1197*7c478bd9Sstevel@tonic-gate out:
1198*7c478bd9Sstevel@tonic-gate 	(void) door_return((char *)dcp, sizeof (*dcp), NULL, 0);
1199*7c478bd9Sstevel@tonic-gate }
1200*7c478bd9Sstevel@tonic-gate 
1201*7c478bd9Sstevel@tonic-gate static void
1202*7c478bd9Sstevel@tonic-gate lock_dev(void)
1203*7c478bd9Sstevel@tonic-gate {
1204*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "lock_dev(): entered\n");
1205*7c478bd9Sstevel@tonic-gate 
1206*7c478bd9Sstevel@tonic-gate 	if (build_dev == FALSE)
1207*7c478bd9Sstevel@tonic-gate 		return;
1208*7c478bd9Sstevel@tonic-gate 
1209*7c478bd9Sstevel@tonic-gate 	/* lockout other threads from /dev */
1210*7c478bd9Sstevel@tonic-gate 	while (sema_wait(&dev_sema) != 0);
1211*7c478bd9Sstevel@tonic-gate 
1212*7c478bd9Sstevel@tonic-gate 	/*
1213*7c478bd9Sstevel@tonic-gate 	 * Lock out other devfsadm processes from /dev.
1214*7c478bd9Sstevel@tonic-gate 	 * If this wasn't the last process to run,
1215*7c478bd9Sstevel@tonic-gate 	 * clear caches
1216*7c478bd9Sstevel@tonic-gate 	 */
1217*7c478bd9Sstevel@tonic-gate 	if (enter_dev_lock() != getpid()) {
1218*7c478bd9Sstevel@tonic-gate 		invalidate_enumerate_cache();
1219*7c478bd9Sstevel@tonic-gate 		rm_all_links_from_cache();
1220*7c478bd9Sstevel@tonic-gate 		(void) di_devlink_close(&devlink_cache, DI_LINK_ERROR);
1221*7c478bd9Sstevel@tonic-gate 		devlink_cache = NULL;
1222*7c478bd9Sstevel@tonic-gate 	}
1223*7c478bd9Sstevel@tonic-gate 
1224*7c478bd9Sstevel@tonic-gate 	/*
1225*7c478bd9Sstevel@tonic-gate 	 * (re)load the  reverse links database if not
1226*7c478bd9Sstevel@tonic-gate 	 * already cached.
1227*7c478bd9Sstevel@tonic-gate 	 */
1228*7c478bd9Sstevel@tonic-gate 	if (devlink_cache == NULL)
1229*7c478bd9Sstevel@tonic-gate 		devlink_cache = di_devlink_open(root_dir, 0);
1230*7c478bd9Sstevel@tonic-gate 
1231*7c478bd9Sstevel@tonic-gate 
1232*7c478bd9Sstevel@tonic-gate 	/*
1233*7c478bd9Sstevel@tonic-gate 	 * If modules were unloaded, reload them.  Also use module status
1234*7c478bd9Sstevel@tonic-gate 	 * as an indication that we should check to see if other binding
1235*7c478bd9Sstevel@tonic-gate 	 * files need to be reloaded.
1236*7c478bd9Sstevel@tonic-gate 	 */
1237*7c478bd9Sstevel@tonic-gate 	if (module_head == NULL) {
1238*7c478bd9Sstevel@tonic-gate 		load_modules();
1239*7c478bd9Sstevel@tonic-gate 		read_minor_perm_file();
1240*7c478bd9Sstevel@tonic-gate 		read_driver_aliases_file();
1241*7c478bd9Sstevel@tonic-gate 		read_devlinktab_file();
1242*7c478bd9Sstevel@tonic-gate 		read_logindevperm_file();
1243*7c478bd9Sstevel@tonic-gate 	}
1244*7c478bd9Sstevel@tonic-gate 
1245*7c478bd9Sstevel@tonic-gate 	if (module_head != NULL)
1246*7c478bd9Sstevel@tonic-gate 		return;
1247*7c478bd9Sstevel@tonic-gate 
1248*7c478bd9Sstevel@tonic-gate 	if (strcmp(prog, DEVLINKS) == 0) {
1249*7c478bd9Sstevel@tonic-gate 		if (devlinktab_list == NULL) {
1250*7c478bd9Sstevel@tonic-gate 			err_print(NO_LINKTAB, devlinktab_file);
1251*7c478bd9Sstevel@tonic-gate 			err_print(NO_MODULES, module_dirs);
1252*7c478bd9Sstevel@tonic-gate 			err_print(ABORTING);
1253*7c478bd9Sstevel@tonic-gate 			devfsadm_exit(1);
1254*7c478bd9Sstevel@tonic-gate 		}
1255*7c478bd9Sstevel@tonic-gate 	} else {
1256*7c478bd9Sstevel@tonic-gate 		err_print(NO_MODULES, module_dirs);
1257*7c478bd9Sstevel@tonic-gate 		if (strcmp(prog, DEVFSADM) == 0) {
1258*7c478bd9Sstevel@tonic-gate 			err_print(MODIFY_PATH);
1259*7c478bd9Sstevel@tonic-gate 		}
1260*7c478bd9Sstevel@tonic-gate 	}
1261*7c478bd9Sstevel@tonic-gate }
1262*7c478bd9Sstevel@tonic-gate 
1263*7c478bd9Sstevel@tonic-gate static void
1264*7c478bd9Sstevel@tonic-gate unlock_dev(int flag)
1265*7c478bd9Sstevel@tonic-gate {
1266*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "unlock_dev(): entered\n");
1267*7c478bd9Sstevel@tonic-gate 
1268*7c478bd9Sstevel@tonic-gate 	if (build_dev == FALSE)
1269*7c478bd9Sstevel@tonic-gate 		return;
1270*7c478bd9Sstevel@tonic-gate 
1271*7c478bd9Sstevel@tonic-gate 	assert(devlink_cache);
1272*7c478bd9Sstevel@tonic-gate 	assert(flag == SYNC_STATE || flag == CACHE_STATE);
1273*7c478bd9Sstevel@tonic-gate 
1274*7c478bd9Sstevel@tonic-gate 
1275*7c478bd9Sstevel@tonic-gate 	if (flag == SYNC_STATE) {
1276*7c478bd9Sstevel@tonic-gate 		unload_modules();
1277*7c478bd9Sstevel@tonic-gate 		if (update_database)
1278*7c478bd9Sstevel@tonic-gate 			(void) di_devlink_update(devlink_cache);
1279*7c478bd9Sstevel@tonic-gate 		(void) di_devlink_close(&devlink_cache, 0);
1280*7c478bd9Sstevel@tonic-gate 		devlink_cache = NULL;
1281*7c478bd9Sstevel@tonic-gate 	}
1282*7c478bd9Sstevel@tonic-gate 
1283*7c478bd9Sstevel@tonic-gate 	exit_dev_lock();
1284*7c478bd9Sstevel@tonic-gate 
1285*7c478bd9Sstevel@tonic-gate 	(void) sema_post(&dev_sema);
1286*7c478bd9Sstevel@tonic-gate }
1287*7c478bd9Sstevel@tonic-gate 
1288*7c478bd9Sstevel@tonic-gate /*
1289*7c478bd9Sstevel@tonic-gate  * Contact the daemon to register the identified zone.  We do everything with
1290*7c478bd9Sstevel@tonic-gate  * zone names, for simplicity.
1291*7c478bd9Sstevel@tonic-gate  */
1292*7c478bd9Sstevel@tonic-gate static void
1293*7c478bd9Sstevel@tonic-gate call_zone_register(char *zone_name, int regop)
1294*7c478bd9Sstevel@tonic-gate {
1295*7c478bd9Sstevel@tonic-gate 	int doorfd, ret, retries = 0;
1296*7c478bd9Sstevel@tonic-gate 	door_arg_t arg;
1297*7c478bd9Sstevel@tonic-gate 	struct zreg z;
1298*7c478bd9Sstevel@tonic-gate 	char path[MAXPATHLEN];
1299*7c478bd9Sstevel@tonic-gate 
1300*7c478bd9Sstevel@tonic-gate 	assert(regop == ZONE_REG || regop == ZONE_UNREG);
1301*7c478bd9Sstevel@tonic-gate 
1302*7c478bd9Sstevel@tonic-gate 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) {
1303*7c478bd9Sstevel@tonic-gate 		err_print(INVALID_ZONE, zone_name);
1304*7c478bd9Sstevel@tonic-gate 		return;
1305*7c478bd9Sstevel@tonic-gate 	}
1306*7c478bd9Sstevel@tonic-gate 
1307*7c478bd9Sstevel@tonic-gate 	z.zreg_error = 0;
1308*7c478bd9Sstevel@tonic-gate 	z.zreg_op = regop;
1309*7c478bd9Sstevel@tonic-gate 	(void) strlcpy(z.zreg_zonename, zone_name, ZONENAME_MAX);
1310*7c478bd9Sstevel@tonic-gate 
1311*7c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "/dev/%s", ZONE_REG_DOOR);
1312*7c478bd9Sstevel@tonic-gate 	if ((doorfd = open(path, O_RDWR)) == -1) {
1313*7c478bd9Sstevel@tonic-gate 		return;
1314*7c478bd9Sstevel@tonic-gate 	}
1315*7c478bd9Sstevel@tonic-gate 
1316*7c478bd9Sstevel@tonic-gate 	bzero(&arg, sizeof (arg));
1317*7c478bd9Sstevel@tonic-gate 	arg.data_ptr = (char *)&z;
1318*7c478bd9Sstevel@tonic-gate 	arg.data_size = sizeof (z);
1319*7c478bd9Sstevel@tonic-gate 	arg.rbuf = (char *)&z;
1320*7c478bd9Sstevel@tonic-gate 	arg.rsize = sizeof (z);
1321*7c478bd9Sstevel@tonic-gate 
1322*7c478bd9Sstevel@tonic-gate 	/*
1323*7c478bd9Sstevel@tonic-gate 	 * If the daemon is running, tell it about the zone.  If not, it's
1324*7c478bd9Sstevel@tonic-gate 	 * ok.  When it next gets run by the system (because there is
1325*7c478bd9Sstevel@tonic-gate 	 * device-related work to do), it will load the list of zones from
1326*7c478bd9Sstevel@tonic-gate 	 * the kernel.
1327*7c478bd9Sstevel@tonic-gate 	 */
1328*7c478bd9Sstevel@tonic-gate 	while (((ret = door_call(doorfd, &arg)) == -1) && retries++ < 3) {
1329*7c478bd9Sstevel@tonic-gate 		(void) sleep(retries);
1330*7c478bd9Sstevel@tonic-gate 	}
1331*7c478bd9Sstevel@tonic-gate 	(void) close(doorfd);
1332*7c478bd9Sstevel@tonic-gate 
1333*7c478bd9Sstevel@tonic-gate 	if (ret != 0) {
1334*7c478bd9Sstevel@tonic-gate 		return;
1335*7c478bd9Sstevel@tonic-gate 	}
1336*7c478bd9Sstevel@tonic-gate 
1337*7c478bd9Sstevel@tonic-gate 	switch (z.zreg_error) {
1338*7c478bd9Sstevel@tonic-gate 	case ZONE_SUCCESS:
1339*7c478bd9Sstevel@tonic-gate 		break;
1340*7c478bd9Sstevel@tonic-gate 	case ZONE_ERR_NOZONE:
1341*7c478bd9Sstevel@tonic-gate 		err_print(ZONE_REG_FAILED, zone_name, strerror(z.zreg_errno));
1342*7c478bd9Sstevel@tonic-gate 		break;
1343*7c478bd9Sstevel@tonic-gate 	case ZONE_ERR_DOOR:
1344*7c478bd9Sstevel@tonic-gate 		err_print(ZONE_DOOR_MKFAIL, zone_name, strerror(z.zreg_errno));
1345*7c478bd9Sstevel@tonic-gate 		break;
1346*7c478bd9Sstevel@tonic-gate 	case ZONE_ERR_REPOSITORY:
1347*7c478bd9Sstevel@tonic-gate 		err_print(ZONE_REP_FAILED, zone_name, strerror(z.zreg_errno));
1348*7c478bd9Sstevel@tonic-gate 		break;
1349*7c478bd9Sstevel@tonic-gate 	case ZONE_ERR_NOLIB:
1350*7c478bd9Sstevel@tonic-gate 		err_print(ZONE_LIB_MISSING);
1351*7c478bd9Sstevel@tonic-gate 		break;
1352*7c478bd9Sstevel@tonic-gate 	default:
1353*7c478bd9Sstevel@tonic-gate 		err_print(ZONE_REG_FAILED, zone_name, strerror(z.zreg_errno));
1354*7c478bd9Sstevel@tonic-gate 		break;
1355*7c478bd9Sstevel@tonic-gate 	}
1356*7c478bd9Sstevel@tonic-gate }
1357*7c478bd9Sstevel@tonic-gate 
1358*7c478bd9Sstevel@tonic-gate /*
1359*7c478bd9Sstevel@tonic-gate  * The following routines are the daemon-side code for managing the set of
1360*7c478bd9Sstevel@tonic-gate  * currently registered zones.
1361*7c478bd9Sstevel@tonic-gate  *
1362*7c478bd9Sstevel@tonic-gate  * TODO: improve brain-dead list performance--- use libuutil avl tree or hash?
1363*7c478bd9Sstevel@tonic-gate  */
1364*7c478bd9Sstevel@tonic-gate static void
1365*7c478bd9Sstevel@tonic-gate zlist_insert(struct zone_devinfo *newzone)
1366*7c478bd9Sstevel@tonic-gate {
1367*7c478bd9Sstevel@tonic-gate 	struct zone_devinfo *z;
1368*7c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&zone_mutex));
1369*7c478bd9Sstevel@tonic-gate 
1370*7c478bd9Sstevel@tonic-gate 	if (zone_head == NULL) {
1371*7c478bd9Sstevel@tonic-gate 		zone_head = newzone;
1372*7c478bd9Sstevel@tonic-gate 		return;
1373*7c478bd9Sstevel@tonic-gate 	}
1374*7c478bd9Sstevel@tonic-gate 	z = zlist_remove(newzone->zone_name);
1375*7c478bd9Sstevel@tonic-gate 	if (z != NULL)
1376*7c478bd9Sstevel@tonic-gate 		delete_zone(z);
1377*7c478bd9Sstevel@tonic-gate 	newzone->zone_next = zone_head;
1378*7c478bd9Sstevel@tonic-gate 	zone_head = newzone;
1379*7c478bd9Sstevel@tonic-gate }
1380*7c478bd9Sstevel@tonic-gate 
1381*7c478bd9Sstevel@tonic-gate static void
1382*7c478bd9Sstevel@tonic-gate delete_zone(struct zone_devinfo *z) {
1383*7c478bd9Sstevel@tonic-gate 	char door_file[PATH_MAX];
1384*7c478bd9Sstevel@tonic-gate 
1385*7c478bd9Sstevel@tonic-gate 	/*
1386*7c478bd9Sstevel@tonic-gate 	 * Tidy up by withdrawing our door from the zone.
1387*7c478bd9Sstevel@tonic-gate 	 */
1388*7c478bd9Sstevel@tonic-gate 	(void) snprintf(door_file, sizeof (door_file), "%s/dev/%s",
1389*7c478bd9Sstevel@tonic-gate 	    z->zone_path, DEVFSADM_SYNCH_DOOR);
1390*7c478bd9Sstevel@tonic-gate 	(void) s_unlink(door_file);
1391*7c478bd9Sstevel@tonic-gate 
1392*7c478bd9Sstevel@tonic-gate 	zonecfg_fini_handle(z->zone_dochdl);
1393*7c478bd9Sstevel@tonic-gate 	free(z->zone_path);
1394*7c478bd9Sstevel@tonic-gate 	free(z->zone_name);
1395*7c478bd9Sstevel@tonic-gate 	free(z);
1396*7c478bd9Sstevel@tonic-gate }
1397*7c478bd9Sstevel@tonic-gate 
1398*7c478bd9Sstevel@tonic-gate static struct zone_devinfo *
1399*7c478bd9Sstevel@tonic-gate zlist_remove(char *zone_name)
1400*7c478bd9Sstevel@tonic-gate {
1401*7c478bd9Sstevel@tonic-gate 	struct zone_devinfo *z, *unlinked = NULL, **prevnextp;
1402*7c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&zone_mutex));
1403*7c478bd9Sstevel@tonic-gate 
1404*7c478bd9Sstevel@tonic-gate 	prevnextp = &zone_head;
1405*7c478bd9Sstevel@tonic-gate 	for (z = zone_head; z != NULL; z = z->zone_next) {
1406*7c478bd9Sstevel@tonic-gate 		if (strcmp(zone_name, z->zone_name) == 0) {
1407*7c478bd9Sstevel@tonic-gate 			unlinked = z;
1408*7c478bd9Sstevel@tonic-gate 			*prevnextp = z->zone_next;
1409*7c478bd9Sstevel@tonic-gate 			return (unlinked);
1410*7c478bd9Sstevel@tonic-gate 		}
1411*7c478bd9Sstevel@tonic-gate 		prevnextp = &(z->zone_next);
1412*7c478bd9Sstevel@tonic-gate 	}
1413*7c478bd9Sstevel@tonic-gate 	return (NULL);
1414*7c478bd9Sstevel@tonic-gate }
1415*7c478bd9Sstevel@tonic-gate 
1416*7c478bd9Sstevel@tonic-gate /*
1417*7c478bd9Sstevel@tonic-gate  * Delete all zones.  Note that this should *only* be called in the exit
1418*7c478bd9Sstevel@tonic-gate  * path of the daemon, as it does not take the zone_mutex-- this is because
1419*7c478bd9Sstevel@tonic-gate  * we could wind up calling devfsadm_exit() with that zone_mutex_held.
1420*7c478bd9Sstevel@tonic-gate  */
1421*7c478bd9Sstevel@tonic-gate static void
1422*7c478bd9Sstevel@tonic-gate zlist_deleteall_unlocked(void)
1423*7c478bd9Sstevel@tonic-gate {
1424*7c478bd9Sstevel@tonic-gate 	struct zone_devinfo *tofree;
1425*7c478bd9Sstevel@tonic-gate 
1426*7c478bd9Sstevel@tonic-gate 	while (zone_head != NULL) {
1427*7c478bd9Sstevel@tonic-gate 		tofree = zone_head;
1428*7c478bd9Sstevel@tonic-gate 		zone_head = zone_head->zone_next;
1429*7c478bd9Sstevel@tonic-gate 		delete_zone(tofree);
1430*7c478bd9Sstevel@tonic-gate 	}
1431*7c478bd9Sstevel@tonic-gate 	assert(zone_head == NULL);
1432*7c478bd9Sstevel@tonic-gate }
1433*7c478bd9Sstevel@tonic-gate 
1434*7c478bd9Sstevel@tonic-gate static int
1435*7c478bd9Sstevel@tonic-gate zone_register(char *zone_name)
1436*7c478bd9Sstevel@tonic-gate {
1437*7c478bd9Sstevel@tonic-gate 	char		door_file[MAXPATHLEN], zpath[MAXPATHLEN];
1438*7c478bd9Sstevel@tonic-gate 	int		fd;
1439*7c478bd9Sstevel@tonic-gate 	int 		need_unlink = 0, error = ZONE_SUCCESS, myerrno = 0;
1440*7c478bd9Sstevel@tonic-gate 	zone_dochandle_t hdl = NULL;
1441*7c478bd9Sstevel@tonic-gate 	void		*dlhdl = NULL;
1442*7c478bd9Sstevel@tonic-gate 	struct zone_devinfo *newzone = NULL;
1443*7c478bd9Sstevel@tonic-gate 
1444*7c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&zone_mutex));
1445*7c478bd9Sstevel@tonic-gate 
1446*7c478bd9Sstevel@tonic-gate 	if ((dlhdl = dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) {
1447*7c478bd9Sstevel@tonic-gate 		error = ZONE_ERR_NOLIB;
1448*7c478bd9Sstevel@tonic-gate 		goto bad;
1449*7c478bd9Sstevel@tonic-gate 	}
1450*7c478bd9Sstevel@tonic-gate 
1451*7c478bd9Sstevel@tonic-gate 	if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
1452*7c478bd9Sstevel@tonic-gate 		error = ZONE_ERR_NOZONE;
1453*7c478bd9Sstevel@tonic-gate 		myerrno = errno;
1454*7c478bd9Sstevel@tonic-gate 		goto bad;
1455*7c478bd9Sstevel@tonic-gate 	}
1456*7c478bd9Sstevel@tonic-gate 
1457*7c478bd9Sstevel@tonic-gate 	if (snprintf(door_file, sizeof (door_file), "%s/dev/%s",
1458*7c478bd9Sstevel@tonic-gate 	    zpath, DEVFSADM_SYNCH_DOOR) >= sizeof (door_file)) {
1459*7c478bd9Sstevel@tonic-gate 		myerrno = ENAMETOOLONG;	/* synthesize a reasonable errno */
1460*7c478bd9Sstevel@tonic-gate 		error = ZONE_ERR_DOOR;
1461*7c478bd9Sstevel@tonic-gate 		goto bad;
1462*7c478bd9Sstevel@tonic-gate 	}
1463*7c478bd9Sstevel@tonic-gate 
1464*7c478bd9Sstevel@tonic-gate 	(void) s_unlink(door_file);
1465*7c478bd9Sstevel@tonic-gate 	if ((fd = open(door_file, O_RDWR | O_CREAT, ZONE_DOOR_PERMS)) == -1) {
1466*7c478bd9Sstevel@tonic-gate 		myerrno = errno;
1467*7c478bd9Sstevel@tonic-gate 		error = ZONE_ERR_DOOR;
1468*7c478bd9Sstevel@tonic-gate 		goto bad;
1469*7c478bd9Sstevel@tonic-gate 	}
1470*7c478bd9Sstevel@tonic-gate 	need_unlink = 1;
1471*7c478bd9Sstevel@tonic-gate 	(void) close(fd);
1472*7c478bd9Sstevel@tonic-gate 
1473*7c478bd9Sstevel@tonic-gate 	if (fattach(devlink_door_fd, door_file) == -1) {
1474*7c478bd9Sstevel@tonic-gate 		error = ZONE_ERR_DOOR;
1475*7c478bd9Sstevel@tonic-gate 		myerrno = errno;
1476*7c478bd9Sstevel@tonic-gate 		goto bad;
1477*7c478bd9Sstevel@tonic-gate 	}
1478*7c478bd9Sstevel@tonic-gate 
1479*7c478bd9Sstevel@tonic-gate 	if ((hdl = zonecfg_init_handle()) == NULL) {
1480*7c478bd9Sstevel@tonic-gate 		error = ZONE_ERR_REPOSITORY;
1481*7c478bd9Sstevel@tonic-gate 		myerrno = errno;
1482*7c478bd9Sstevel@tonic-gate 		goto bad;
1483*7c478bd9Sstevel@tonic-gate 	}
1484*7c478bd9Sstevel@tonic-gate 
1485*7c478bd9Sstevel@tonic-gate 	if ((zonecfg_get_snapshot_handle(zone_name, hdl)) != Z_OK) {
1486*7c478bd9Sstevel@tonic-gate 		error = ZONE_ERR_REPOSITORY;
1487*7c478bd9Sstevel@tonic-gate 		myerrno = errno;
1488*7c478bd9Sstevel@tonic-gate 		goto bad;
1489*7c478bd9Sstevel@tonic-gate 	}
1490*7c478bd9Sstevel@tonic-gate 
1491*7c478bd9Sstevel@tonic-gate 	newzone = s_malloc(sizeof (struct zone_devinfo));
1492*7c478bd9Sstevel@tonic-gate 	newzone->zone_path = s_strdup(zpath);
1493*7c478bd9Sstevel@tonic-gate 	newzone->zone_name = s_strdup(zone_name);
1494*7c478bd9Sstevel@tonic-gate 	newzone->zone_next = NULL;
1495*7c478bd9Sstevel@tonic-gate 	newzone->zone_dochdl = hdl;
1496*7c478bd9Sstevel@tonic-gate 	zlist_insert(newzone);
1497*7c478bd9Sstevel@tonic-gate 	(void) dlclose(dlhdl);
1498*7c478bd9Sstevel@tonic-gate 
1499*7c478bd9Sstevel@tonic-gate 	return (ZONE_SUCCESS);
1500*7c478bd9Sstevel@tonic-gate 
1501*7c478bd9Sstevel@tonic-gate bad:
1502*7c478bd9Sstevel@tonic-gate 	(void) devfsadm_errprint("%s[%ld]: failed to register zone %s: %s",
1503*7c478bd9Sstevel@tonic-gate 	    prog, getpid(), zone_name, strerror(myerrno));
1504*7c478bd9Sstevel@tonic-gate 
1505*7c478bd9Sstevel@tonic-gate 	assert(newzone == NULL);
1506*7c478bd9Sstevel@tonic-gate 	if (need_unlink)
1507*7c478bd9Sstevel@tonic-gate 		(void) s_unlink(door_file);
1508*7c478bd9Sstevel@tonic-gate 	if (hdl)
1509*7c478bd9Sstevel@tonic-gate 		zonecfg_fini_handle(hdl);
1510*7c478bd9Sstevel@tonic-gate 	if (dlhdl)
1511*7c478bd9Sstevel@tonic-gate 		(void) dlclose(dlhdl);
1512*7c478bd9Sstevel@tonic-gate 	errno = myerrno;
1513*7c478bd9Sstevel@tonic-gate 	return (error);
1514*7c478bd9Sstevel@tonic-gate }
1515*7c478bd9Sstevel@tonic-gate 
1516*7c478bd9Sstevel@tonic-gate static int
1517*7c478bd9Sstevel@tonic-gate zone_unregister(char *zone_name)
1518*7c478bd9Sstevel@tonic-gate {
1519*7c478bd9Sstevel@tonic-gate 	struct zone_devinfo *z;
1520*7c478bd9Sstevel@tonic-gate 
1521*7c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&zone_mutex));
1522*7c478bd9Sstevel@tonic-gate 
1523*7c478bd9Sstevel@tonic-gate 	if ((z = zlist_remove(zone_name)) == NULL)
1524*7c478bd9Sstevel@tonic-gate 		return (ZONE_ERR_NOZONE);
1525*7c478bd9Sstevel@tonic-gate 
1526*7c478bd9Sstevel@tonic-gate 	delete_zone(z);
1527*7c478bd9Sstevel@tonic-gate 	return (ZONE_SUCCESS);
1528*7c478bd9Sstevel@tonic-gate }
1529*7c478bd9Sstevel@tonic-gate 
1530*7c478bd9Sstevel@tonic-gate /*
1531*7c478bd9Sstevel@tonic-gate  *  Called by the daemon when it receives a door call to the zone registration
1532*7c478bd9Sstevel@tonic-gate  *  door.
1533*7c478bd9Sstevel@tonic-gate  */
1534*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1535*7c478bd9Sstevel@tonic-gate static void
1536*7c478bd9Sstevel@tonic-gate zone_reg_handler(void *cookie, char *ap, size_t asize, door_desc_t *dp,
1537*7c478bd9Sstevel@tonic-gate     uint_t ndesc)
1538*7c478bd9Sstevel@tonic-gate {
1539*7c478bd9Sstevel@tonic-gate 	door_cred_t	dcred;
1540*7c478bd9Sstevel@tonic-gate 	struct zreg 	*zregp, rzreg;
1541*7c478bd9Sstevel@tonic-gate 
1542*7c478bd9Sstevel@tonic-gate 	/*
1543*7c478bd9Sstevel@tonic-gate 	 * We coarsely lock the whole registration process.
1544*7c478bd9Sstevel@tonic-gate 	 */
1545*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(&zone_mutex);
1546*7c478bd9Sstevel@tonic-gate 
1547*7c478bd9Sstevel@tonic-gate 	/*
1548*7c478bd9Sstevel@tonic-gate 	 * Must be root to make this call
1549*7c478bd9Sstevel@tonic-gate 	 * If caller is not root, don't touch its data.
1550*7c478bd9Sstevel@tonic-gate 	 */
1551*7c478bd9Sstevel@tonic-gate 	if (door_cred(&dcred) != 0 || dcred.dc_euid != 0) {
1552*7c478bd9Sstevel@tonic-gate 		zregp = &rzreg;
1553*7c478bd9Sstevel@tonic-gate 		zregp->zreg_error = ZONE_ERR_REPOSITORY;
1554*7c478bd9Sstevel@tonic-gate 		zregp->zreg_errno = EPERM;
1555*7c478bd9Sstevel@tonic-gate 		goto out;
1556*7c478bd9Sstevel@tonic-gate 	}
1557*7c478bd9Sstevel@tonic-gate 
1558*7c478bd9Sstevel@tonic-gate 	assert(ap);
1559*7c478bd9Sstevel@tonic-gate 	assert(asize == sizeof (*zregp));
1560*7c478bd9Sstevel@tonic-gate 
1561*7c478bd9Sstevel@tonic-gate 	zregp = (struct zreg *)(void *)ap;
1562*7c478bd9Sstevel@tonic-gate 
1563*7c478bd9Sstevel@tonic-gate 	/*
1564*7c478bd9Sstevel@tonic-gate 	 * Kernel must know about this zone; one way of discovering this
1565*7c478bd9Sstevel@tonic-gate 	 * is by looking up the zone id.
1566*7c478bd9Sstevel@tonic-gate 	 */
1567*7c478bd9Sstevel@tonic-gate 	if (getzoneidbyname(zregp->zreg_zonename) == -1) {
1568*7c478bd9Sstevel@tonic-gate 		zregp->zreg_error = ZONE_ERR_REPOSITORY;
1569*7c478bd9Sstevel@tonic-gate 		zregp->zreg_errno = errno;
1570*7c478bd9Sstevel@tonic-gate 		goto out;
1571*7c478bd9Sstevel@tonic-gate 	}
1572*7c478bd9Sstevel@tonic-gate 
1573*7c478bd9Sstevel@tonic-gate 	if (zregp->zreg_op == ZONE_REG) {
1574*7c478bd9Sstevel@tonic-gate 		zregp->zreg_error = zone_register(zregp->zreg_zonename);
1575*7c478bd9Sstevel@tonic-gate 		zregp->zreg_errno = errno;
1576*7c478bd9Sstevel@tonic-gate 	} else {
1577*7c478bd9Sstevel@tonic-gate 		zregp->zreg_error = zone_unregister(zregp->zreg_zonename);
1578*7c478bd9Sstevel@tonic-gate 		zregp->zreg_errno = errno;
1579*7c478bd9Sstevel@tonic-gate 	}
1580*7c478bd9Sstevel@tonic-gate 
1581*7c478bd9Sstevel@tonic-gate out:
1582*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(&zone_mutex);
1583*7c478bd9Sstevel@tonic-gate 	(void) door_return((char *)zregp, sizeof (*zregp), NULL, 0);
1584*7c478bd9Sstevel@tonic-gate }
1585*7c478bd9Sstevel@tonic-gate 
1586*7c478bd9Sstevel@tonic-gate static int
1587*7c478bd9Sstevel@tonic-gate register_all_zones(void)
1588*7c478bd9Sstevel@tonic-gate {
1589*7c478bd9Sstevel@tonic-gate 	zoneid_t *zids = NULL;
1590*7c478bd9Sstevel@tonic-gate 	uint_t nzents, nzents_saved;
1591*7c478bd9Sstevel@tonic-gate 	int i;
1592*7c478bd9Sstevel@tonic-gate 
1593*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(&zone_mutex);
1594*7c478bd9Sstevel@tonic-gate 	if (zone_list(NULL, &nzents) != 0)
1595*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
1596*7c478bd9Sstevel@tonic-gate 
1597*7c478bd9Sstevel@tonic-gate again:
1598*7c478bd9Sstevel@tonic-gate 	assert(zids == NULL);
1599*7c478bd9Sstevel@tonic-gate 	assert(MUTEX_HELD(&zone_mutex));
1600*7c478bd9Sstevel@tonic-gate 	if (nzents == 0) {
1601*7c478bd9Sstevel@tonic-gate 		(void) mutex_unlock(&zone_mutex);
1602*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
1603*7c478bd9Sstevel@tonic-gate 	}
1604*7c478bd9Sstevel@tonic-gate 	zids = s_zalloc(nzents * sizeof (zoneid_t));
1605*7c478bd9Sstevel@tonic-gate 	nzents_saved = nzents;
1606*7c478bd9Sstevel@tonic-gate 	if (zone_list(zids, &nzents) != 0) {
1607*7c478bd9Sstevel@tonic-gate 		(void) mutex_unlock(&zone_mutex);
1608*7c478bd9Sstevel@tonic-gate 		free(zids);
1609*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
1610*7c478bd9Sstevel@tonic-gate 	}
1611*7c478bd9Sstevel@tonic-gate 	if (nzents != nzents_saved) {
1612*7c478bd9Sstevel@tonic-gate 		/* list changed, try again */
1613*7c478bd9Sstevel@tonic-gate 		free(zids);
1614*7c478bd9Sstevel@tonic-gate 		zids = NULL;
1615*7c478bd9Sstevel@tonic-gate 		goto again;
1616*7c478bd9Sstevel@tonic-gate 	}
1617*7c478bd9Sstevel@tonic-gate 
1618*7c478bd9Sstevel@tonic-gate 	assert(zids != NULL);
1619*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < nzents; i++) {
1620*7c478bd9Sstevel@tonic-gate 		char name[ZONENAME_MAX];
1621*7c478bd9Sstevel@tonic-gate 
1622*7c478bd9Sstevel@tonic-gate 		if (zids[i] == GLOBAL_ZONEID)
1623*7c478bd9Sstevel@tonic-gate 			continue;
1624*7c478bd9Sstevel@tonic-gate 		if (getzonenamebyid(zids[i], name, sizeof (name)) >= 0)
1625*7c478bd9Sstevel@tonic-gate 			(void) zone_register(name);
1626*7c478bd9Sstevel@tonic-gate 	}
1627*7c478bd9Sstevel@tonic-gate 
1628*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(&zone_mutex);
1629*7c478bd9Sstevel@tonic-gate 	free(zids);
1630*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
1631*7c478bd9Sstevel@tonic-gate }
1632*7c478bd9Sstevel@tonic-gate 
1633*7c478bd9Sstevel@tonic-gate /*
1634*7c478bd9Sstevel@tonic-gate  * Check that if -r is set, it is not any part of a zone--- that is, that
1635*7c478bd9Sstevel@tonic-gate  * the zonepath is not a substring of the root path.
1636*7c478bd9Sstevel@tonic-gate  */
1637*7c478bd9Sstevel@tonic-gate static int
1638*7c478bd9Sstevel@tonic-gate zone_pathcheck(char *checkpath)
1639*7c478bd9Sstevel@tonic-gate {
1640*7c478bd9Sstevel@tonic-gate 	void		*dlhdl = NULL;
1641*7c478bd9Sstevel@tonic-gate 	char		*name;
1642*7c478bd9Sstevel@tonic-gate 	char		root[MAXPATHLEN]; /* resolved devfsadm root path */
1643*7c478bd9Sstevel@tonic-gate 	char		zroot[MAXPATHLEN]; /* zone root path */
1644*7c478bd9Sstevel@tonic-gate 	char		rzroot[MAXPATHLEN]; /* resolved zone root path */
1645*7c478bd9Sstevel@tonic-gate 	char 		tmp[MAXPATHLEN];
1646*7c478bd9Sstevel@tonic-gate 	FILE		*cookie;
1647*7c478bd9Sstevel@tonic-gate 	int		err = DEVFSADM_SUCCESS;
1648*7c478bd9Sstevel@tonic-gate 
1649*7c478bd9Sstevel@tonic-gate 	if (checkpath[0] == '\0')
1650*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
1651*7c478bd9Sstevel@tonic-gate 
1652*7c478bd9Sstevel@tonic-gate 	/*
1653*7c478bd9Sstevel@tonic-gate 	 * Check if zones is available on this system.
1654*7c478bd9Sstevel@tonic-gate 	 */
1655*7c478bd9Sstevel@tonic-gate 	if ((dlhdl = dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) {
1656*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
1657*7c478bd9Sstevel@tonic-gate 	}
1658*7c478bd9Sstevel@tonic-gate 
1659*7c478bd9Sstevel@tonic-gate 	bzero(root, sizeof (root));
1660*7c478bd9Sstevel@tonic-gate 	if (resolvepath(checkpath, root, sizeof (root) - 1) == -1) {
1661*7c478bd9Sstevel@tonic-gate 		/*
1662*7c478bd9Sstevel@tonic-gate 		 * In this case the user has done 'devfsadm -r' on some path
1663*7c478bd9Sstevel@tonic-gate 		 * which does not yet exist, or we got some other misc. error.
1664*7c478bd9Sstevel@tonic-gate 		 * We punt and don't resolve the path in this case.
1665*7c478bd9Sstevel@tonic-gate 		 */
1666*7c478bd9Sstevel@tonic-gate 		(void) strlcpy(root, checkpath, sizeof (root));
1667*7c478bd9Sstevel@tonic-gate 	}
1668*7c478bd9Sstevel@tonic-gate 
1669*7c478bd9Sstevel@tonic-gate 	if (strlen(root) > 0 && (root[strlen(root) - 1] != '/')) {
1670*7c478bd9Sstevel@tonic-gate 		(void) snprintf(tmp, sizeof (tmp), "%s/", root);
1671*7c478bd9Sstevel@tonic-gate 		(void) strlcpy(root, tmp, sizeof (root));
1672*7c478bd9Sstevel@tonic-gate 	}
1673*7c478bd9Sstevel@tonic-gate 
1674*7c478bd9Sstevel@tonic-gate 	cookie = setzoneent();
1675*7c478bd9Sstevel@tonic-gate 	while ((name = getzoneent(cookie)) != NULL) {
1676*7c478bd9Sstevel@tonic-gate 		/* Skip the global zone */
1677*7c478bd9Sstevel@tonic-gate 		if (strcmp(name, GLOBAL_ZONENAME) == 0) {
1678*7c478bd9Sstevel@tonic-gate 			free(name);
1679*7c478bd9Sstevel@tonic-gate 			continue;
1680*7c478bd9Sstevel@tonic-gate 		}
1681*7c478bd9Sstevel@tonic-gate 
1682*7c478bd9Sstevel@tonic-gate 		if (zone_get_zonepath(name, zroot, sizeof (zroot)) != Z_OK) {
1683*7c478bd9Sstevel@tonic-gate 			free(name);
1684*7c478bd9Sstevel@tonic-gate 			continue;
1685*7c478bd9Sstevel@tonic-gate 		}
1686*7c478bd9Sstevel@tonic-gate 
1687*7c478bd9Sstevel@tonic-gate 		bzero(rzroot, sizeof (rzroot));
1688*7c478bd9Sstevel@tonic-gate 		if (resolvepath(zroot, rzroot, sizeof (rzroot) - 1) == -1) {
1689*7c478bd9Sstevel@tonic-gate 			/*
1690*7c478bd9Sstevel@tonic-gate 			 * Zone path doesn't exist, or other misc error,
1691*7c478bd9Sstevel@tonic-gate 			 * so we try using the non-resolved pathname.
1692*7c478bd9Sstevel@tonic-gate 			 */
1693*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(rzroot, zroot, sizeof (rzroot));
1694*7c478bd9Sstevel@tonic-gate 		}
1695*7c478bd9Sstevel@tonic-gate 		if (strlen(rzroot) > 0 && (rzroot[strlen(rzroot) - 1] != '/')) {
1696*7c478bd9Sstevel@tonic-gate 			(void) snprintf(tmp, sizeof (tmp), "%s/", rzroot);
1697*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(rzroot, tmp, sizeof (rzroot));
1698*7c478bd9Sstevel@tonic-gate 		}
1699*7c478bd9Sstevel@tonic-gate 
1700*7c478bd9Sstevel@tonic-gate 		/*
1701*7c478bd9Sstevel@tonic-gate 		 * Finally, the comparison.  If the zone root path is a
1702*7c478bd9Sstevel@tonic-gate 		 * leading substring of the root path, fail.
1703*7c478bd9Sstevel@tonic-gate 		 */
1704*7c478bd9Sstevel@tonic-gate 		if (strncmp(rzroot, root, strlen(rzroot)) == 0) {
1705*7c478bd9Sstevel@tonic-gate 			err_print(ZONE_PATHCHECK, root, name);
1706*7c478bd9Sstevel@tonic-gate 			err = DEVFSADM_FAILURE;
1707*7c478bd9Sstevel@tonic-gate 			free(name);
1708*7c478bd9Sstevel@tonic-gate 			break;
1709*7c478bd9Sstevel@tonic-gate 		}
1710*7c478bd9Sstevel@tonic-gate 		free(name);
1711*7c478bd9Sstevel@tonic-gate 	}
1712*7c478bd9Sstevel@tonic-gate 	endzoneent(cookie);
1713*7c478bd9Sstevel@tonic-gate 	(void) dlclose(dlhdl);
1714*7c478bd9Sstevel@tonic-gate 	return (err);
1715*7c478bd9Sstevel@tonic-gate }
1716*7c478bd9Sstevel@tonic-gate 
1717*7c478bd9Sstevel@tonic-gate /*
1718*7c478bd9Sstevel@tonic-gate  *  Called by the daemon when it receives an event from the devfsadm SLM
1719*7c478bd9Sstevel@tonic-gate  *  to syseventd.
1720*7c478bd9Sstevel@tonic-gate  *
1721*7c478bd9Sstevel@tonic-gate  *  The devfsadm SLM uses a private event channel for communication to
1722*7c478bd9Sstevel@tonic-gate  *  devfsadmd set-up via private libsysevent interfaces.  This handler is
1723*7c478bd9Sstevel@tonic-gate  *  used to bind to the devfsadmd channel for event delivery.
1724*7c478bd9Sstevel@tonic-gate  *  The devfsadmd SLM insures single calls to this routine as well as
1725*7c478bd9Sstevel@tonic-gate  *  synchronized event delivery.
1726*7c478bd9Sstevel@tonic-gate  *
1727*7c478bd9Sstevel@tonic-gate  */
1728*7c478bd9Sstevel@tonic-gate static void
1729*7c478bd9Sstevel@tonic-gate event_handler(sysevent_t *ev)
1730*7c478bd9Sstevel@tonic-gate {
1731*7c478bd9Sstevel@tonic-gate 	char *path;
1732*7c478bd9Sstevel@tonic-gate 	char *minor;
1733*7c478bd9Sstevel@tonic-gate 	char *subclass;
1734*7c478bd9Sstevel@tonic-gate 	char *dev_ev_subclass;
1735*7c478bd9Sstevel@tonic-gate 	char *driver_name;
1736*7c478bd9Sstevel@tonic-gate 	nvlist_t *attr_list = NULL;
1737*7c478bd9Sstevel@tonic-gate 	int err = 0;
1738*7c478bd9Sstevel@tonic-gate 	int instance;
1739*7c478bd9Sstevel@tonic-gate 	int branch_event = 0;
1740*7c478bd9Sstevel@tonic-gate 
1741*7c478bd9Sstevel@tonic-gate 	subclass = sysevent_get_subclass_name(ev);
1742*7c478bd9Sstevel@tonic-gate 	vprint(EVENT_MID, "event_handler: %s id:0X%llx\n",
1743*7c478bd9Sstevel@tonic-gate 	    subclass, sysevent_get_seq(ev));
1744*7c478bd9Sstevel@tonic-gate 
1745*7c478bd9Sstevel@tonic-gate 	/* Check if event is an instance modification */
1746*7c478bd9Sstevel@tonic-gate 	if (strcmp(subclass, ESC_DEVFS_INSTANCE_MOD) == 0) {
1747*7c478bd9Sstevel@tonic-gate 		devfs_instance_mod();
1748*7c478bd9Sstevel@tonic-gate 		return;
1749*7c478bd9Sstevel@tonic-gate 	}
1750*7c478bd9Sstevel@tonic-gate 	if (sysevent_get_attr_list(ev, &attr_list) != 0) {
1751*7c478bd9Sstevel@tonic-gate 		vprint(EVENT_MID, "event_handler: can not get attr list\n");
1752*7c478bd9Sstevel@tonic-gate 		return;
1753*7c478bd9Sstevel@tonic-gate 	}
1754*7c478bd9Sstevel@tonic-gate 
1755*7c478bd9Sstevel@tonic-gate 	if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0 ||
1756*7c478bd9Sstevel@tonic-gate 	    strcmp(subclass, ESC_DEVFS_DEVI_REMOVE) == 0 ||
1757*7c478bd9Sstevel@tonic-gate 	    strcmp(subclass, ESC_DEVFS_MINOR_CREATE) == 0 ||
1758*7c478bd9Sstevel@tonic-gate 	    strcmp(subclass, ESC_DEVFS_MINOR_REMOVE) == 0) {
1759*7c478bd9Sstevel@tonic-gate 		if ((err = nvlist_lookup_string(attr_list, DEVFS_PATHNAME,
1760*7c478bd9Sstevel@tonic-gate 		    &path)) != 0)
1761*7c478bd9Sstevel@tonic-gate 			goto out;
1762*7c478bd9Sstevel@tonic-gate 
1763*7c478bd9Sstevel@tonic-gate 		if (strcmp(subclass, ESC_DEVFS_DEVI_ADD) == 0 ||
1764*7c478bd9Sstevel@tonic-gate 		    strcmp(subclass, ESC_DEVFS_DEVI_REMOVE) == 0) {
1765*7c478bd9Sstevel@tonic-gate 			if (nvlist_lookup_string(attr_list, DEVFS_DEVI_CLASS,
1766*7c478bd9Sstevel@tonic-gate 			    &dev_ev_subclass) != 0)
1767*7c478bd9Sstevel@tonic-gate 				dev_ev_subclass = NULL;
1768*7c478bd9Sstevel@tonic-gate 
1769*7c478bd9Sstevel@tonic-gate 			if (nvlist_lookup_string(attr_list, DEVFS_DRIVER_NAME,
1770*7c478bd9Sstevel@tonic-gate 			    &driver_name) != 0)
1771*7c478bd9Sstevel@tonic-gate 				driver_name = NULL;
1772*7c478bd9Sstevel@tonic-gate 
1773*7c478bd9Sstevel@tonic-gate 			if (nvlist_lookup_int32(attr_list, DEVFS_INSTANCE,
1774*7c478bd9Sstevel@tonic-gate 			    &instance) != 0)
1775*7c478bd9Sstevel@tonic-gate 				instance = -1;
1776*7c478bd9Sstevel@tonic-gate 
1777*7c478bd9Sstevel@tonic-gate 			if (nvlist_lookup_int32(attr_list, DEVFS_BRANCH_EVENT,
1778*7c478bd9Sstevel@tonic-gate 			    &branch_event) != 0)
1779*7c478bd9Sstevel@tonic-gate 				branch_event = 0;
1780*7c478bd9Sstevel@tonic-gate 
1781*7c478bd9Sstevel@tonic-gate 		} else {
1782*7c478bd9Sstevel@tonic-gate 			if (nvlist_lookup_string(attr_list, DEVFS_MINOR_NAME,
1783*7c478bd9Sstevel@tonic-gate 			    &minor) != 0)
1784*7c478bd9Sstevel@tonic-gate 				minor = NULL;
1785*7c478bd9Sstevel@tonic-gate 		}
1786*7c478bd9Sstevel@tonic-gate 
1787*7c478bd9Sstevel@tonic-gate 		lock_dev();
1788*7c478bd9Sstevel@tonic-gate 
1789*7c478bd9Sstevel@tonic-gate 		if (strcmp(ESC_DEVFS_DEVI_ADD, subclass) == 0) {
1790*7c478bd9Sstevel@tonic-gate 			add_minor_pathname(path, NULL, dev_ev_subclass);
1791*7c478bd9Sstevel@tonic-gate 			if (branch_event) {
1792*7c478bd9Sstevel@tonic-gate 				build_and_log_event(EC_DEV_BRANCH,
1793*7c478bd9Sstevel@tonic-gate 				    ESC_DEV_BRANCH_ADD, path, DI_NODE_NIL);
1794*7c478bd9Sstevel@tonic-gate 			}
1795*7c478bd9Sstevel@tonic-gate 
1796*7c478bd9Sstevel@tonic-gate 		} else if (strcmp(ESC_DEVFS_MINOR_CREATE, subclass) == 0) {
1797*7c478bd9Sstevel@tonic-gate 			add_minor_pathname(path, minor, NULL);
1798*7c478bd9Sstevel@tonic-gate 
1799*7c478bd9Sstevel@tonic-gate 		} else if (strcmp(ESC_DEVFS_MINOR_REMOVE, subclass) == 0) {
1800*7c478bd9Sstevel@tonic-gate 			hot_cleanup(path, minor, NULL, NULL, -1);
1801*7c478bd9Sstevel@tonic-gate 
1802*7c478bd9Sstevel@tonic-gate 		} else { /* ESC_DEVFS_DEVI_REMOVE */
1803*7c478bd9Sstevel@tonic-gate 			hot_cleanup(path, NULL, dev_ev_subclass,
1804*7c478bd9Sstevel@tonic-gate 			    driver_name, instance);
1805*7c478bd9Sstevel@tonic-gate 			if (branch_event) {
1806*7c478bd9Sstevel@tonic-gate 				build_and_log_event(EC_DEV_BRANCH,
1807*7c478bd9Sstevel@tonic-gate 				    ESC_DEV_BRANCH_REMOVE, path, DI_NODE_NIL);
1808*7c478bd9Sstevel@tonic-gate 			}
1809*7c478bd9Sstevel@tonic-gate 		}
1810*7c478bd9Sstevel@tonic-gate 
1811*7c478bd9Sstevel@tonic-gate 		unlock_dev(CACHE_STATE);
1812*7c478bd9Sstevel@tonic-gate 		startup_cache_sync_thread();
1813*7c478bd9Sstevel@tonic-gate 
1814*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0 ||
1815*7c478bd9Sstevel@tonic-gate 	    strcmp(subclass, ESC_DEVFS_BRANCH_REMOVE) == 0) {
1816*7c478bd9Sstevel@tonic-gate 		if ((err = nvlist_lookup_string(attr_list,
1817*7c478bd9Sstevel@tonic-gate 		    DEVFS_PATHNAME, &path)) != 0)
1818*7c478bd9Sstevel@tonic-gate 			goto out;
1819*7c478bd9Sstevel@tonic-gate 
1820*7c478bd9Sstevel@tonic-gate 		/* just log ESC_DEV_BRANCH... event */
1821*7c478bd9Sstevel@tonic-gate 		if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0)
1822*7c478bd9Sstevel@tonic-gate 			dev_ev_subclass = ESC_DEV_BRANCH_ADD;
1823*7c478bd9Sstevel@tonic-gate 		else
1824*7c478bd9Sstevel@tonic-gate 			dev_ev_subclass = ESC_DEV_BRANCH_REMOVE;
1825*7c478bd9Sstevel@tonic-gate 
1826*7c478bd9Sstevel@tonic-gate 		build_and_log_event(EC_DEV_BRANCH, dev_ev_subclass, path,
1827*7c478bd9Sstevel@tonic-gate 		    DI_NODE_NIL);
1828*7c478bd9Sstevel@tonic-gate 
1829*7c478bd9Sstevel@tonic-gate 	} else
1830*7c478bd9Sstevel@tonic-gate 		err_print(UNKNOWN_EVENT, subclass);
1831*7c478bd9Sstevel@tonic-gate 
1832*7c478bd9Sstevel@tonic-gate out:
1833*7c478bd9Sstevel@tonic-gate 	if (err)
1834*7c478bd9Sstevel@tonic-gate 		err_print(EVENT_ATTR_LOOKUP_FAILED, strerror(err));
1835*7c478bd9Sstevel@tonic-gate 	nvlist_free(attr_list);
1836*7c478bd9Sstevel@tonic-gate }
1837*7c478bd9Sstevel@tonic-gate 
1838*7c478bd9Sstevel@tonic-gate static void
1839*7c478bd9Sstevel@tonic-gate dca_impl_init(char *root, char *minor, struct dca_impl *dcip)
1840*7c478bd9Sstevel@tonic-gate {
1841*7c478bd9Sstevel@tonic-gate 	assert(root);
1842*7c478bd9Sstevel@tonic-gate 
1843*7c478bd9Sstevel@tonic-gate 	dcip->dci_root = root;
1844*7c478bd9Sstevel@tonic-gate 	dcip->dci_minor = minor;
1845*7c478bd9Sstevel@tonic-gate 	dcip->dci_driver = NULL;
1846*7c478bd9Sstevel@tonic-gate 	dcip->dci_error = 0;
1847*7c478bd9Sstevel@tonic-gate 	dcip->dci_flags = 0;
1848*7c478bd9Sstevel@tonic-gate 	dcip->dci_arg = NULL;
1849*7c478bd9Sstevel@tonic-gate }
1850*7c478bd9Sstevel@tonic-gate 
1851*7c478bd9Sstevel@tonic-gate /*
1852*7c478bd9Sstevel@tonic-gate  *  Kernel logs a message when a devinfo node is attached.  Try to create
1853*7c478bd9Sstevel@tonic-gate  *  /dev and /devices for each minor node.  minorname can be NULL.
1854*7c478bd9Sstevel@tonic-gate  */
1855*7c478bd9Sstevel@tonic-gate void
1856*7c478bd9Sstevel@tonic-gate add_minor_pathname(char *node, char *minor, char *ev_subclass)
1857*7c478bd9Sstevel@tonic-gate {
1858*7c478bd9Sstevel@tonic-gate 	struct dca_impl	dci;
1859*7c478bd9Sstevel@tonic-gate 
1860*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "add_minor_pathname: node_path=%s minor=%s\n",
1861*7c478bd9Sstevel@tonic-gate 	    node, minor ? minor : "NULL");
1862*7c478bd9Sstevel@tonic-gate 
1863*7c478bd9Sstevel@tonic-gate 	dca_impl_init(node, minor, &dci);
1864*7c478bd9Sstevel@tonic-gate 
1865*7c478bd9Sstevel@tonic-gate 	/*
1866*7c478bd9Sstevel@tonic-gate 	 * Restrict hotplug link creation if daemon
1867*7c478bd9Sstevel@tonic-gate 	 * started  with -i option.
1868*7c478bd9Sstevel@tonic-gate 	 */
1869*7c478bd9Sstevel@tonic-gate 	if (single_drv == TRUE) {
1870*7c478bd9Sstevel@tonic-gate 		dci.dci_driver = driver;
1871*7c478bd9Sstevel@tonic-gate 	}
1872*7c478bd9Sstevel@tonic-gate 
1873*7c478bd9Sstevel@tonic-gate 	/*
1874*7c478bd9Sstevel@tonic-gate 	 * We are being invoked in response to a hotplug
1875*7c478bd9Sstevel@tonic-gate 	 * event. Also, notify RCM if nodetype indicates
1876*7c478bd9Sstevel@tonic-gate 	 * a network device has been hotplugged.
1877*7c478bd9Sstevel@tonic-gate 	 */
1878*7c478bd9Sstevel@tonic-gate 	dci.dci_flags = DCA_HOT_PLUG | DCA_CHECK_TYPE;
1879*7c478bd9Sstevel@tonic-gate 
1880*7c478bd9Sstevel@tonic-gate 	devi_tree_walk(&dci, DINFOPROP|DINFOMINOR, ev_subclass);
1881*7c478bd9Sstevel@tonic-gate }
1882*7c478bd9Sstevel@tonic-gate 
1883*7c478bd9Sstevel@tonic-gate static di_node_t
1884*7c478bd9Sstevel@tonic-gate find_clone_node()
1885*7c478bd9Sstevel@tonic-gate {
1886*7c478bd9Sstevel@tonic-gate 	static di_node_t clone_node = DI_NODE_NIL;
1887*7c478bd9Sstevel@tonic-gate 
1888*7c478bd9Sstevel@tonic-gate 	if (clone_node == DI_NODE_NIL)
1889*7c478bd9Sstevel@tonic-gate 		clone_node = di_init("/pseudo/clone@0", DINFOPROP);
1890*7c478bd9Sstevel@tonic-gate 	return (clone_node);
1891*7c478bd9Sstevel@tonic-gate }
1892*7c478bd9Sstevel@tonic-gate 
1893*7c478bd9Sstevel@tonic-gate static int
1894*7c478bd9Sstevel@tonic-gate is_descendent_of(di_node_t node, char *driver)
1895*7c478bd9Sstevel@tonic-gate {
1896*7c478bd9Sstevel@tonic-gate 	while (node != DI_NODE_NIL) {
1897*7c478bd9Sstevel@tonic-gate 		char *drv = di_driver_name(node);
1898*7c478bd9Sstevel@tonic-gate 		if (strcmp(drv, driver) == 0)
1899*7c478bd9Sstevel@tonic-gate 			return (1);
1900*7c478bd9Sstevel@tonic-gate 		node = di_parent_node(node);
1901*7c478bd9Sstevel@tonic-gate 	}
1902*7c478bd9Sstevel@tonic-gate 	return (0);
1903*7c478bd9Sstevel@tonic-gate }
1904*7c478bd9Sstevel@tonic-gate 
1905*7c478bd9Sstevel@tonic-gate /*
1906*7c478bd9Sstevel@tonic-gate  * Checks the minor type.  If it is an alias node, then lookup
1907*7c478bd9Sstevel@tonic-gate  * the real node/minor first, then call minor_process() to
1908*7c478bd9Sstevel@tonic-gate  * do the real work.
1909*7c478bd9Sstevel@tonic-gate  */
1910*7c478bd9Sstevel@tonic-gate static int
1911*7c478bd9Sstevel@tonic-gate check_minor_type(di_node_t node, di_minor_t minor, void *arg)
1912*7c478bd9Sstevel@tonic-gate {
1913*7c478bd9Sstevel@tonic-gate 	ddi_minor_type	minor_type;
1914*7c478bd9Sstevel@tonic-gate 	di_node_t	clone_node;
1915*7c478bd9Sstevel@tonic-gate 	char		*mn;
1916*7c478bd9Sstevel@tonic-gate 	char		*nt;
1917*7c478bd9Sstevel@tonic-gate 	struct mlist	*dep;
1918*7c478bd9Sstevel@tonic-gate 	struct dca_impl	*dcip = arg;
1919*7c478bd9Sstevel@tonic-gate 
1920*7c478bd9Sstevel@tonic-gate 	assert(dcip);
1921*7c478bd9Sstevel@tonic-gate 
1922*7c478bd9Sstevel@tonic-gate 	dep = dcip->dci_arg;
1923*7c478bd9Sstevel@tonic-gate 
1924*7c478bd9Sstevel@tonic-gate 	mn = di_minor_name(minor);
1925*7c478bd9Sstevel@tonic-gate 
1926*7c478bd9Sstevel@tonic-gate 	/*
1927*7c478bd9Sstevel@tonic-gate 	 * We match driver here instead of in minor_process
1928*7c478bd9Sstevel@tonic-gate 	 * as we want the actual driver name. This check is
1929*7c478bd9Sstevel@tonic-gate 	 * unnecessary during deferred processing.
1930*7c478bd9Sstevel@tonic-gate 	 */
1931*7c478bd9Sstevel@tonic-gate 	if (dep &&
1932*7c478bd9Sstevel@tonic-gate 	    ((dcip->dci_driver && !is_descendent_of(node, dcip->dci_driver)) ||
1933*7c478bd9Sstevel@tonic-gate 	    (dcip->dci_minor && strcmp(mn, dcip->dci_minor)))) {
1934*7c478bd9Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
1935*7c478bd9Sstevel@tonic-gate 	}
1936*7c478bd9Sstevel@tonic-gate 
1937*7c478bd9Sstevel@tonic-gate 	if ((dcip->dci_flags & DCA_CHECK_TYPE) &&
1938*7c478bd9Sstevel@tonic-gate 	    (nt = di_minor_nodetype(minor)) &&
1939*7c478bd9Sstevel@tonic-gate 	    (strcmp(nt, DDI_NT_NET) == 0 || strcmp(nt, DDI_NT_MAC) == 0)) {
1940*7c478bd9Sstevel@tonic-gate 		dcip->dci_flags |= DCA_NOTIFY_RCM;
1941*7c478bd9Sstevel@tonic-gate 		dcip->dci_flags &= ~DCA_CHECK_TYPE;
1942*7c478bd9Sstevel@tonic-gate 	}
1943*7c478bd9Sstevel@tonic-gate 
1944*7c478bd9Sstevel@tonic-gate 	minor_type = di_minor_type(minor);
1945*7c478bd9Sstevel@tonic-gate 
1946*7c478bd9Sstevel@tonic-gate 	if (minor_type == DDM_MINOR) {
1947*7c478bd9Sstevel@tonic-gate 		minor_process(node, minor, dep);
1948*7c478bd9Sstevel@tonic-gate 
1949*7c478bd9Sstevel@tonic-gate 	} else if (minor_type == DDM_ALIAS) {
1950*7c478bd9Sstevel@tonic-gate 		struct mlist *cdep, clone_del = {0};
1951*7c478bd9Sstevel@tonic-gate 
1952*7c478bd9Sstevel@tonic-gate 		clone_node = find_clone_node();
1953*7c478bd9Sstevel@tonic-gate 		if (clone_node == DI_NODE_NIL) {
1954*7c478bd9Sstevel@tonic-gate 			err_print(DI_INIT_FAILED, "clone", strerror(errno));
1955*7c478bd9Sstevel@tonic-gate 			return (DI_WALK_CONTINUE);
1956*7c478bd9Sstevel@tonic-gate 		}
1957*7c478bd9Sstevel@tonic-gate 
1958*7c478bd9Sstevel@tonic-gate 		cdep = dep ? &clone_del : NULL;
1959*7c478bd9Sstevel@tonic-gate 
1960*7c478bd9Sstevel@tonic-gate 		minor_process(clone_node, minor, cdep);
1961*7c478bd9Sstevel@tonic-gate 
1962*7c478bd9Sstevel@tonic-gate 		/*
1963*7c478bd9Sstevel@tonic-gate 		 * cache "alias" minor node and free "clone" minor
1964*7c478bd9Sstevel@tonic-gate 		 */
1965*7c478bd9Sstevel@tonic-gate 		if (cdep != NULL && cdep->head != NULL) {
1966*7c478bd9Sstevel@tonic-gate 			assert(cdep->tail != NULL);
1967*7c478bd9Sstevel@tonic-gate 			cache_deferred_minor(dep, node, minor);
1968*7c478bd9Sstevel@tonic-gate 			dcip->dci_arg = cdep;
1969*7c478bd9Sstevel@tonic-gate 			process_deferred_links(dcip, DCA_FREE_LIST);
1970*7c478bd9Sstevel@tonic-gate 			dcip->dci_arg = dep;
1971*7c478bd9Sstevel@tonic-gate 		}
1972*7c478bd9Sstevel@tonic-gate 	}
1973*7c478bd9Sstevel@tonic-gate 
1974*7c478bd9Sstevel@tonic-gate 	return (DI_WALK_CONTINUE);
1975*7c478bd9Sstevel@tonic-gate }
1976*7c478bd9Sstevel@tonic-gate 
1977*7c478bd9Sstevel@tonic-gate 
1978*7c478bd9Sstevel@tonic-gate /*
1979*7c478bd9Sstevel@tonic-gate  *  This is the entry point for each minor node, whether walking
1980*7c478bd9Sstevel@tonic-gate  *  the entire tree via di_walk_minor() or processing a hotplug event
1981*7c478bd9Sstevel@tonic-gate  *  for a single devinfo node (via hotplug ndi_devi_online()).
1982*7c478bd9Sstevel@tonic-gate  */
1983*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1984*7c478bd9Sstevel@tonic-gate static void
1985*7c478bd9Sstevel@tonic-gate minor_process(di_node_t node, di_minor_t minor, struct mlist *dep)
1986*7c478bd9Sstevel@tonic-gate {
1987*7c478bd9Sstevel@tonic-gate 	create_list_t	*create;
1988*7c478bd9Sstevel@tonic-gate 	int		defer;
1989*7c478bd9Sstevel@tonic-gate 
1990*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "minor_process: node=%s, minor=%s\n",
1991*7c478bd9Sstevel@tonic-gate 		di_node_name(node), di_minor_name(minor));
1992*7c478bd9Sstevel@tonic-gate 
1993*7c478bd9Sstevel@tonic-gate 	if (dep != NULL) {
1994*7c478bd9Sstevel@tonic-gate 
1995*7c478bd9Sstevel@tonic-gate 		/*
1996*7c478bd9Sstevel@tonic-gate 		 * Reset /devices node to minor_perm perm/ownership
1997*7c478bd9Sstevel@tonic-gate 		 * if we are here to deactivate device allocation
1998*7c478bd9Sstevel@tonic-gate 		 */
1999*7c478bd9Sstevel@tonic-gate 		if (build_devices == TRUE) {
2000*7c478bd9Sstevel@tonic-gate 			reset_node_permissions(node, minor);
2001*7c478bd9Sstevel@tonic-gate 		}
2002*7c478bd9Sstevel@tonic-gate 
2003*7c478bd9Sstevel@tonic-gate 		if (build_dev == FALSE) {
2004*7c478bd9Sstevel@tonic-gate 			return;
2005*7c478bd9Sstevel@tonic-gate 		}
2006*7c478bd9Sstevel@tonic-gate 
2007*7c478bd9Sstevel@tonic-gate 		/*
2008*7c478bd9Sstevel@tonic-gate 		 * This function will create any nodes for /etc/devlink.tab.
2009*7c478bd9Sstevel@tonic-gate 		 * If devlink.tab handles link creation, we don't call any
2010*7c478bd9Sstevel@tonic-gate 		 * devfsadm modules since that could cause duplicate caching
2011*7c478bd9Sstevel@tonic-gate 		 * in the enumerate functions if different re strings are
2012*7c478bd9Sstevel@tonic-gate 		 * passed that are logically identical.  I'm still not
2013*7c478bd9Sstevel@tonic-gate 		 * convinced this would cause any harm, but better to be safe.
2014*7c478bd9Sstevel@tonic-gate 		 *
2015*7c478bd9Sstevel@tonic-gate 		 * Deferred processing is available only for devlinks
2016*7c478bd9Sstevel@tonic-gate 		 * created through devfsadm modules.
2017*7c478bd9Sstevel@tonic-gate 		 */
2018*7c478bd9Sstevel@tonic-gate 		if (process_devlink_compat(minor, node) == TRUE) {
2019*7c478bd9Sstevel@tonic-gate 			return;
2020*7c478bd9Sstevel@tonic-gate 		}
2021*7c478bd9Sstevel@tonic-gate 	} else {
2022*7c478bd9Sstevel@tonic-gate 		vprint(CHATTY_MID, "minor_process: deferred processing\n");
2023*7c478bd9Sstevel@tonic-gate 	}
2024*7c478bd9Sstevel@tonic-gate 
2025*7c478bd9Sstevel@tonic-gate 	/*
2026*7c478bd9Sstevel@tonic-gate 	 * look for relevant link create rules in the modules, and
2027*7c478bd9Sstevel@tonic-gate 	 * invoke the link create callback function to build a link
2028*7c478bd9Sstevel@tonic-gate 	 * if there is a match.
2029*7c478bd9Sstevel@tonic-gate 	 */
2030*7c478bd9Sstevel@tonic-gate 	defer = 0;
2031*7c478bd9Sstevel@tonic-gate 	for (create = create_head; create != NULL; create = create->next) {
2032*7c478bd9Sstevel@tonic-gate 		if ((minor_matches_rule(node, minor, create) == TRUE) &&
2033*7c478bd9Sstevel@tonic-gate 		    class_ok(create->create->device_class) ==
2034*7c478bd9Sstevel@tonic-gate 		    DEVFSADM_SUCCESS) {
2035*7c478bd9Sstevel@tonic-gate 			if (call_minor_init(create->modptr) ==
2036*7c478bd9Sstevel@tonic-gate 			    DEVFSADM_FAILURE) {
2037*7c478bd9Sstevel@tonic-gate 				continue;
2038*7c478bd9Sstevel@tonic-gate 			}
2039*7c478bd9Sstevel@tonic-gate 
2040*7c478bd9Sstevel@tonic-gate 			/*
2041*7c478bd9Sstevel@tonic-gate 			 * If NOT doing the deferred creates (i.e. 1st pass) and
2042*7c478bd9Sstevel@tonic-gate 			 * rule requests deferred processing cache the minor
2043*7c478bd9Sstevel@tonic-gate 			 * data.
2044*7c478bd9Sstevel@tonic-gate 			 *
2045*7c478bd9Sstevel@tonic-gate 			 * If deferred processing (2nd pass), create links
2046*7c478bd9Sstevel@tonic-gate 			 * ONLY if rule requests deferred processing.
2047*7c478bd9Sstevel@tonic-gate 			 */
2048*7c478bd9Sstevel@tonic-gate 			if (dep && ((create->create->flags & CREATE_MASK) ==
2049*7c478bd9Sstevel@tonic-gate 			    CREATE_DEFER)) {
2050*7c478bd9Sstevel@tonic-gate 				defer = 1;
2051*7c478bd9Sstevel@tonic-gate 				continue;
2052*7c478bd9Sstevel@tonic-gate 			} else if (dep == NULL &&
2053*7c478bd9Sstevel@tonic-gate 			    ((create->create->flags & CREATE_MASK) !=
2054*7c478bd9Sstevel@tonic-gate 			    CREATE_DEFER)) {
2055*7c478bd9Sstevel@tonic-gate 				continue;
2056*7c478bd9Sstevel@tonic-gate 			}
2057*7c478bd9Sstevel@tonic-gate 
2058*7c478bd9Sstevel@tonic-gate 			if ((*(create->create->callback_fcn))
2059*7c478bd9Sstevel@tonic-gate 			    (minor, node) == DEVFSADM_TERMINATE) {
2060*7c478bd9Sstevel@tonic-gate 				break;
2061*7c478bd9Sstevel@tonic-gate 			}
2062*7c478bd9Sstevel@tonic-gate 		}
2063*7c478bd9Sstevel@tonic-gate 	}
2064*7c478bd9Sstevel@tonic-gate 
2065*7c478bd9Sstevel@tonic-gate 	if (defer)
2066*7c478bd9Sstevel@tonic-gate 		cache_deferred_minor(dep, node, minor);
2067*7c478bd9Sstevel@tonic-gate }
2068*7c478bd9Sstevel@tonic-gate 
2069*7c478bd9Sstevel@tonic-gate 
2070*7c478bd9Sstevel@tonic-gate /*
2071*7c478bd9Sstevel@tonic-gate  * Cache node and minor in defer list.
2072*7c478bd9Sstevel@tonic-gate  */
2073*7c478bd9Sstevel@tonic-gate static void
2074*7c478bd9Sstevel@tonic-gate cache_deferred_minor(
2075*7c478bd9Sstevel@tonic-gate 	struct mlist *dep,
2076*7c478bd9Sstevel@tonic-gate 	di_node_t node,
2077*7c478bd9Sstevel@tonic-gate 	di_minor_t minor)
2078*7c478bd9Sstevel@tonic-gate {
2079*7c478bd9Sstevel@tonic-gate 	struct minor	*mp;
2080*7c478bd9Sstevel@tonic-gate 	const char	*fcn = "cache_deferred_minor";
2081*7c478bd9Sstevel@tonic-gate 
2082*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "%s node=%s, minor=%s\n", fcn,
2083*7c478bd9Sstevel@tonic-gate 	    di_node_name(node), di_minor_name(minor));
2084*7c478bd9Sstevel@tonic-gate 
2085*7c478bd9Sstevel@tonic-gate 	if (dep == NULL) {
2086*7c478bd9Sstevel@tonic-gate 		vprint(CHATTY_MID, "%s: cannot cache during "
2087*7c478bd9Sstevel@tonic-gate 		    "deferred processing. Ignoring minor\n", fcn);
2088*7c478bd9Sstevel@tonic-gate 		return;
2089*7c478bd9Sstevel@tonic-gate 	}
2090*7c478bd9Sstevel@tonic-gate 
2091*7c478bd9Sstevel@tonic-gate 	mp = (struct minor *)s_zalloc(sizeof (struct minor));
2092*7c478bd9Sstevel@tonic-gate 	mp->node = node;
2093*7c478bd9Sstevel@tonic-gate 	mp->minor = minor;
2094*7c478bd9Sstevel@tonic-gate 	mp->next = NULL;
2095*7c478bd9Sstevel@tonic-gate 
2096*7c478bd9Sstevel@tonic-gate 	assert(dep->head == NULL || dep->tail != NULL);
2097*7c478bd9Sstevel@tonic-gate 	if (dep->head == NULL) {
2098*7c478bd9Sstevel@tonic-gate 		dep->head = mp;
2099*7c478bd9Sstevel@tonic-gate 	} else {
2100*7c478bd9Sstevel@tonic-gate 		dep->tail->next = mp;
2101*7c478bd9Sstevel@tonic-gate 	}
2102*7c478bd9Sstevel@tonic-gate 	dep->tail = mp;
2103*7c478bd9Sstevel@tonic-gate }
2104*7c478bd9Sstevel@tonic-gate 
2105*7c478bd9Sstevel@tonic-gate /*
2106*7c478bd9Sstevel@tonic-gate  *  Check to see if "create" link creation rule matches this node/minor.
2107*7c478bd9Sstevel@tonic-gate  *  If it does, return TRUE.
2108*7c478bd9Sstevel@tonic-gate  */
2109*7c478bd9Sstevel@tonic-gate static int
2110*7c478bd9Sstevel@tonic-gate minor_matches_rule(di_node_t node, di_minor_t minor, create_list_t *create)
2111*7c478bd9Sstevel@tonic-gate {
2112*7c478bd9Sstevel@tonic-gate 	char *m_nodetype, *m_drvname;
2113*7c478bd9Sstevel@tonic-gate 
2114*7c478bd9Sstevel@tonic-gate 	if (create->create->node_type != NULL) {
2115*7c478bd9Sstevel@tonic-gate 
2116*7c478bd9Sstevel@tonic-gate 		m_nodetype = di_minor_nodetype(minor);
2117*7c478bd9Sstevel@tonic-gate 		assert(m_nodetype != NULL);
2118*7c478bd9Sstevel@tonic-gate 
2119*7c478bd9Sstevel@tonic-gate 		switch (create->create->flags & TYPE_MASK) {
2120*7c478bd9Sstevel@tonic-gate 		case TYPE_EXACT:
2121*7c478bd9Sstevel@tonic-gate 			if (strcmp(create->create->node_type, m_nodetype) !=
2122*7c478bd9Sstevel@tonic-gate 			    0) {
2123*7c478bd9Sstevel@tonic-gate 				return (FALSE);
2124*7c478bd9Sstevel@tonic-gate 			}
2125*7c478bd9Sstevel@tonic-gate 			break;
2126*7c478bd9Sstevel@tonic-gate 		case TYPE_PARTIAL:
2127*7c478bd9Sstevel@tonic-gate 			if (strncmp(create->create->node_type, m_nodetype,
2128*7c478bd9Sstevel@tonic-gate 			    strlen(create->create->node_type)) != 0) {
2129*7c478bd9Sstevel@tonic-gate 				return (FALSE);
2130*7c478bd9Sstevel@tonic-gate 			}
2131*7c478bd9Sstevel@tonic-gate 			break;
2132*7c478bd9Sstevel@tonic-gate 		case TYPE_RE:
2133*7c478bd9Sstevel@tonic-gate 			if (regexec(&(create->node_type_comp), m_nodetype,
2134*7c478bd9Sstevel@tonic-gate 			    0, NULL, 0) != 0) {
2135*7c478bd9Sstevel@tonic-gate 				return (FALSE);
2136*7c478bd9Sstevel@tonic-gate 			}
2137*7c478bd9Sstevel@tonic-gate 			break;
2138*7c478bd9Sstevel@tonic-gate 		}
2139*7c478bd9Sstevel@tonic-gate 	}
2140*7c478bd9Sstevel@tonic-gate 
2141*7c478bd9Sstevel@tonic-gate 	if (create->create->drv_name != NULL) {
2142*7c478bd9Sstevel@tonic-gate 		m_drvname = di_driver_name(node);
2143*7c478bd9Sstevel@tonic-gate 		switch (create->create->flags & DRV_MASK) {
2144*7c478bd9Sstevel@tonic-gate 		case DRV_EXACT:
2145*7c478bd9Sstevel@tonic-gate 			if (strcmp(create->create->drv_name, m_drvname) != 0) {
2146*7c478bd9Sstevel@tonic-gate 				return (FALSE);
2147*7c478bd9Sstevel@tonic-gate 			}
2148*7c478bd9Sstevel@tonic-gate 			break;
2149*7c478bd9Sstevel@tonic-gate 		case DRV_RE:
2150*7c478bd9Sstevel@tonic-gate 			if (regexec(&(create->drv_name_comp), m_drvname,
2151*7c478bd9Sstevel@tonic-gate 			    0, NULL, 0) != 0) {
2152*7c478bd9Sstevel@tonic-gate 				return (FALSE);
2153*7c478bd9Sstevel@tonic-gate 			}
2154*7c478bd9Sstevel@tonic-gate 			break;
2155*7c478bd9Sstevel@tonic-gate 		}
2156*7c478bd9Sstevel@tonic-gate 	}
2157*7c478bd9Sstevel@tonic-gate 
2158*7c478bd9Sstevel@tonic-gate 	return (TRUE);
2159*7c478bd9Sstevel@tonic-gate }
2160*7c478bd9Sstevel@tonic-gate 
2161*7c478bd9Sstevel@tonic-gate /*
2162*7c478bd9Sstevel@tonic-gate  * If no classes were given on the command line, then return DEVFSADM_SUCCESS.
2163*7c478bd9Sstevel@tonic-gate  * Otherwise, return DEVFSADM_SUCCESS if the device "class" from the module
2164*7c478bd9Sstevel@tonic-gate  * matches one of the device classes given on the command line,
2165*7c478bd9Sstevel@tonic-gate  * otherwise, return DEVFSADM_FAILURE.
2166*7c478bd9Sstevel@tonic-gate  */
2167*7c478bd9Sstevel@tonic-gate static int
2168*7c478bd9Sstevel@tonic-gate class_ok(char *class)
2169*7c478bd9Sstevel@tonic-gate {
2170*7c478bd9Sstevel@tonic-gate 	int i;
2171*7c478bd9Sstevel@tonic-gate 
2172*7c478bd9Sstevel@tonic-gate 	if (num_classes == 0) {
2173*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
2174*7c478bd9Sstevel@tonic-gate 	}
2175*7c478bd9Sstevel@tonic-gate 
2176*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_classes; i++) {
2177*7c478bd9Sstevel@tonic-gate 		if (strcmp(class, classes[i]) == 0) {
2178*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_SUCCESS);
2179*7c478bd9Sstevel@tonic-gate 		}
2180*7c478bd9Sstevel@tonic-gate 	}
2181*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_FAILURE);
2182*7c478bd9Sstevel@tonic-gate }
2183*7c478bd9Sstevel@tonic-gate 
2184*7c478bd9Sstevel@tonic-gate /*
2185*7c478bd9Sstevel@tonic-gate  * call minor_fini on active modules, then unload ALL modules
2186*7c478bd9Sstevel@tonic-gate  */
2187*7c478bd9Sstevel@tonic-gate static void
2188*7c478bd9Sstevel@tonic-gate unload_modules(void)
2189*7c478bd9Sstevel@tonic-gate {
2190*7c478bd9Sstevel@tonic-gate 	module_t *module_free;
2191*7c478bd9Sstevel@tonic-gate 	create_list_t *create_free;
2192*7c478bd9Sstevel@tonic-gate 	remove_list_t *remove_free;
2193*7c478bd9Sstevel@tonic-gate 
2194*7c478bd9Sstevel@tonic-gate 	while (create_head != NULL) {
2195*7c478bd9Sstevel@tonic-gate 		create_free = create_head;
2196*7c478bd9Sstevel@tonic-gate 		create_head = create_head->next;
2197*7c478bd9Sstevel@tonic-gate 
2198*7c478bd9Sstevel@tonic-gate 		if ((create_free->create->flags & TYPE_RE) == TYPE_RE) {
2199*7c478bd9Sstevel@tonic-gate 			regfree(&(create_free->node_type_comp));
2200*7c478bd9Sstevel@tonic-gate 		}
2201*7c478bd9Sstevel@tonic-gate 		if ((create_free->create->flags & DRV_RE) == DRV_RE) {
2202*7c478bd9Sstevel@tonic-gate 			regfree(&(create_free->drv_name_comp));
2203*7c478bd9Sstevel@tonic-gate 		}
2204*7c478bd9Sstevel@tonic-gate 		free(create_free);
2205*7c478bd9Sstevel@tonic-gate 	}
2206*7c478bd9Sstevel@tonic-gate 
2207*7c478bd9Sstevel@tonic-gate 	while (remove_head != NULL) {
2208*7c478bd9Sstevel@tonic-gate 		remove_free = remove_head;
2209*7c478bd9Sstevel@tonic-gate 		remove_head = remove_head->next;
2210*7c478bd9Sstevel@tonic-gate 		free(remove_free);
2211*7c478bd9Sstevel@tonic-gate 	}
2212*7c478bd9Sstevel@tonic-gate 
2213*7c478bd9Sstevel@tonic-gate 	while (module_head != NULL) {
2214*7c478bd9Sstevel@tonic-gate 
2215*7c478bd9Sstevel@tonic-gate 		if ((module_head->minor_fini != NULL) &&
2216*7c478bd9Sstevel@tonic-gate 		    ((module_head->flags & MODULE_ACTIVE) == MODULE_ACTIVE)) {
2217*7c478bd9Sstevel@tonic-gate 			(void) (*(module_head->minor_fini))();
2218*7c478bd9Sstevel@tonic-gate 		}
2219*7c478bd9Sstevel@tonic-gate 
2220*7c478bd9Sstevel@tonic-gate 		vprint(MODLOAD_MID, "unloading module %s\n", module_head->name);
2221*7c478bd9Sstevel@tonic-gate 		free(module_head->name);
2222*7c478bd9Sstevel@tonic-gate 		(void) dlclose(module_head->dlhandle);
2223*7c478bd9Sstevel@tonic-gate 
2224*7c478bd9Sstevel@tonic-gate 		module_free = module_head;
2225*7c478bd9Sstevel@tonic-gate 		module_head = module_head->next;
2226*7c478bd9Sstevel@tonic-gate 		free(module_free);
2227*7c478bd9Sstevel@tonic-gate 	}
2228*7c478bd9Sstevel@tonic-gate }
2229*7c478bd9Sstevel@tonic-gate 
2230*7c478bd9Sstevel@tonic-gate /*
2231*7c478bd9Sstevel@tonic-gate  * Load devfsadm logical link processing modules.
2232*7c478bd9Sstevel@tonic-gate  */
2233*7c478bd9Sstevel@tonic-gate static void
2234*7c478bd9Sstevel@tonic-gate load_modules(void)
2235*7c478bd9Sstevel@tonic-gate {
2236*7c478bd9Sstevel@tonic-gate 	DIR *mod_dir;
2237*7c478bd9Sstevel@tonic-gate 	struct dirent *entp = NULL;
2238*7c478bd9Sstevel@tonic-gate 	struct dirent *retp;
2239*7c478bd9Sstevel@tonic-gate 	char cdir[PATH_MAX + 1];
2240*7c478bd9Sstevel@tonic-gate 	char *last;
2241*7c478bd9Sstevel@tonic-gate 	char *mdir = module_dirs;
2242*7c478bd9Sstevel@tonic-gate 	char *fcn = "load_modules: ";
2243*7c478bd9Sstevel@tonic-gate 
2244*7c478bd9Sstevel@tonic-gate 	while (*mdir != '\0') {
2245*7c478bd9Sstevel@tonic-gate 
2246*7c478bd9Sstevel@tonic-gate 		while (*mdir == ':') {
2247*7c478bd9Sstevel@tonic-gate 			mdir++;
2248*7c478bd9Sstevel@tonic-gate 		}
2249*7c478bd9Sstevel@tonic-gate 
2250*7c478bd9Sstevel@tonic-gate 		if (*mdir == '\0') {
2251*7c478bd9Sstevel@tonic-gate 			continue;
2252*7c478bd9Sstevel@tonic-gate 		}
2253*7c478bd9Sstevel@tonic-gate 
2254*7c478bd9Sstevel@tonic-gate 		last = strchr(mdir, ':');
2255*7c478bd9Sstevel@tonic-gate 
2256*7c478bd9Sstevel@tonic-gate 		if (last == NULL) {
2257*7c478bd9Sstevel@tonic-gate 			last = mdir + strlen(mdir);
2258*7c478bd9Sstevel@tonic-gate 		}
2259*7c478bd9Sstevel@tonic-gate 
2260*7c478bd9Sstevel@tonic-gate 		(void) strncpy(cdir, mdir, last - mdir);
2261*7c478bd9Sstevel@tonic-gate 		cdir[last - mdir] = '\0';
2262*7c478bd9Sstevel@tonic-gate 		mdir += strlen(cdir);
2263*7c478bd9Sstevel@tonic-gate 
2264*7c478bd9Sstevel@tonic-gate 		if ((mod_dir = opendir(cdir)) == NULL) {
2265*7c478bd9Sstevel@tonic-gate 			vprint(MODLOAD_MID, "%sopendir(%s): %s\n",
2266*7c478bd9Sstevel@tonic-gate 				fcn, cdir, strerror(errno));
2267*7c478bd9Sstevel@tonic-gate 			continue;
2268*7c478bd9Sstevel@tonic-gate 		}
2269*7c478bd9Sstevel@tonic-gate 
2270*7c478bd9Sstevel@tonic-gate 		entp = s_malloc(PATH_MAX + 1 + sizeof (struct  dirent));
2271*7c478bd9Sstevel@tonic-gate 
2272*7c478bd9Sstevel@tonic-gate 		while (readdir_r(mod_dir, entp, &retp) == 0) {
2273*7c478bd9Sstevel@tonic-gate 
2274*7c478bd9Sstevel@tonic-gate 			if (retp == NULL) {
2275*7c478bd9Sstevel@tonic-gate 				break;
2276*7c478bd9Sstevel@tonic-gate 			}
2277*7c478bd9Sstevel@tonic-gate 
2278*7c478bd9Sstevel@tonic-gate 			if ((strcmp(entp->d_name, ".") == 0) ||
2279*7c478bd9Sstevel@tonic-gate 			    (strcmp(entp->d_name, "..") == 0)) {
2280*7c478bd9Sstevel@tonic-gate 				continue;
2281*7c478bd9Sstevel@tonic-gate 			}
2282*7c478bd9Sstevel@tonic-gate 
2283*7c478bd9Sstevel@tonic-gate 			load_module(entp->d_name, cdir);
2284*7c478bd9Sstevel@tonic-gate 		}
2285*7c478bd9Sstevel@tonic-gate 		s_closedir(mod_dir);
2286*7c478bd9Sstevel@tonic-gate 		free(entp);
2287*7c478bd9Sstevel@tonic-gate 	}
2288*7c478bd9Sstevel@tonic-gate }
2289*7c478bd9Sstevel@tonic-gate 
2290*7c478bd9Sstevel@tonic-gate static void
2291*7c478bd9Sstevel@tonic-gate load_module(char *mname, char *cdir)
2292*7c478bd9Sstevel@tonic-gate {
2293*7c478bd9Sstevel@tonic-gate 	_devfsadm_create_reg_t *create_reg;
2294*7c478bd9Sstevel@tonic-gate 	_devfsadm_remove_reg_t *remove_reg;
2295*7c478bd9Sstevel@tonic-gate 	create_list_t *create_list_element;
2296*7c478bd9Sstevel@tonic-gate 	create_list_t **create_list_next;
2297*7c478bd9Sstevel@tonic-gate 	remove_list_t *remove_list_element;
2298*7c478bd9Sstevel@tonic-gate 	remove_list_t **remove_list_next;
2299*7c478bd9Sstevel@tonic-gate 	char epath[PATH_MAX + 1], *end;
2300*7c478bd9Sstevel@tonic-gate 	char *fcn = "load_module: ";
2301*7c478bd9Sstevel@tonic-gate 	char *dlerrstr;
2302*7c478bd9Sstevel@tonic-gate 	void *dlhandle;
2303*7c478bd9Sstevel@tonic-gate 	module_t *module;
2304*7c478bd9Sstevel@tonic-gate 	int n;
2305*7c478bd9Sstevel@tonic-gate 	int i;
2306*7c478bd9Sstevel@tonic-gate 
2307*7c478bd9Sstevel@tonic-gate 	/* ignore any file which does not end in '.so' */
2308*7c478bd9Sstevel@tonic-gate 	if ((end = strstr(mname, MODULE_SUFFIX)) != NULL) {
2309*7c478bd9Sstevel@tonic-gate 		if (end[strlen(MODULE_SUFFIX)] != '\0') {
2310*7c478bd9Sstevel@tonic-gate 			return;
2311*7c478bd9Sstevel@tonic-gate 		}
2312*7c478bd9Sstevel@tonic-gate 	} else {
2313*7c478bd9Sstevel@tonic-gate 		return;
2314*7c478bd9Sstevel@tonic-gate 	}
2315*7c478bd9Sstevel@tonic-gate 
2316*7c478bd9Sstevel@tonic-gate 	(void) snprintf(epath, sizeof (epath), "%s/%s", cdir, mname);
2317*7c478bd9Sstevel@tonic-gate 
2318*7c478bd9Sstevel@tonic-gate 	if ((dlhandle = dlopen(epath, RTLD_LAZY)) == NULL) {
2319*7c478bd9Sstevel@tonic-gate 		dlerrstr = dlerror();
2320*7c478bd9Sstevel@tonic-gate 		err_print(DLOPEN_FAILED, epath,
2321*7c478bd9Sstevel@tonic-gate 			dlerrstr ? dlerrstr : "unknown error");
2322*7c478bd9Sstevel@tonic-gate 		return;
2323*7c478bd9Sstevel@tonic-gate 	}
2324*7c478bd9Sstevel@tonic-gate 
2325*7c478bd9Sstevel@tonic-gate 	/* dlsym the _devfsadm_create_reg structure */
2326*7c478bd9Sstevel@tonic-gate 	if (NULL == (create_reg = (_devfsadm_create_reg_t *)
2327*7c478bd9Sstevel@tonic-gate 		    dlsym(dlhandle, _DEVFSADM_CREATE_REG))) {
2328*7c478bd9Sstevel@tonic-gate 		vprint(MODLOAD_MID, "dlsym(%s, %s): symbol not found\n", epath,
2329*7c478bd9Sstevel@tonic-gate 			_DEVFSADM_CREATE_REG);
2330*7c478bd9Sstevel@tonic-gate 	} else {
2331*7c478bd9Sstevel@tonic-gate 		vprint(MODLOAD_MID, "%sdlsym(%s, %s) succeeded\n",
2332*7c478bd9Sstevel@tonic-gate 			    fcn, epath, _DEVFSADM_CREATE_REG);
2333*7c478bd9Sstevel@tonic-gate 	}
2334*7c478bd9Sstevel@tonic-gate 
2335*7c478bd9Sstevel@tonic-gate 	/* dlsym the _devfsadm_remove_reg structure */
2336*7c478bd9Sstevel@tonic-gate 	if (NULL == (remove_reg = (_devfsadm_remove_reg_t *)
2337*7c478bd9Sstevel@tonic-gate 	    dlsym(dlhandle, _DEVFSADM_REMOVE_REG))) {
2338*7c478bd9Sstevel@tonic-gate 		vprint(MODLOAD_MID, "dlsym(%s,\n\t%s): symbol not found\n",
2339*7c478bd9Sstevel@tonic-gate 			epath, _DEVFSADM_REMOVE_REG);
2340*7c478bd9Sstevel@tonic-gate 	} else {
2341*7c478bd9Sstevel@tonic-gate 		vprint(MODLOAD_MID, "dlsym(%s, %s): succeeded\n",
2342*7c478bd9Sstevel@tonic-gate 			    epath, _DEVFSADM_REMOVE_REG);
2343*7c478bd9Sstevel@tonic-gate 	}
2344*7c478bd9Sstevel@tonic-gate 
2345*7c478bd9Sstevel@tonic-gate 	vprint(MODLOAD_MID, "module %s loaded\n", epath);
2346*7c478bd9Sstevel@tonic-gate 
2347*7c478bd9Sstevel@tonic-gate 	module = (module_t *)s_malloc(sizeof (module_t));
2348*7c478bd9Sstevel@tonic-gate 	module->name = s_strdup(epath);
2349*7c478bd9Sstevel@tonic-gate 	module->dlhandle = dlhandle;
2350*7c478bd9Sstevel@tonic-gate 
2351*7c478bd9Sstevel@tonic-gate 	/* dlsym other module functions, to be called later */
2352*7c478bd9Sstevel@tonic-gate 	module->minor_fini = (int (*)())dlsym(dlhandle, MINOR_FINI);
2353*7c478bd9Sstevel@tonic-gate 	module->minor_init = (int (*)())dlsym(dlhandle, MINOR_INIT);
2354*7c478bd9Sstevel@tonic-gate 	module->flags = 0;
2355*7c478bd9Sstevel@tonic-gate 
2356*7c478bd9Sstevel@tonic-gate 	/*
2357*7c478bd9Sstevel@tonic-gate 	 *  put a ptr to each struct devfsadm_create on "create_head"
2358*7c478bd9Sstevel@tonic-gate 	 *  list sorted in interpose_lvl.
2359*7c478bd9Sstevel@tonic-gate 	 */
2360*7c478bd9Sstevel@tonic-gate 	if (create_reg != NULL) {
2361*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < create_reg->count; i++) {
2362*7c478bd9Sstevel@tonic-gate 			int flags = create_reg->tblp[i].flags;
2363*7c478bd9Sstevel@tonic-gate 
2364*7c478bd9Sstevel@tonic-gate 			create_list_element = (create_list_t *)
2365*7c478bd9Sstevel@tonic-gate 				s_malloc(sizeof (create_list_t));
2366*7c478bd9Sstevel@tonic-gate 
2367*7c478bd9Sstevel@tonic-gate 			create_list_element->create = &(create_reg->tblp[i]);
2368*7c478bd9Sstevel@tonic-gate 			create_list_element->modptr = module;
2369*7c478bd9Sstevel@tonic-gate 
2370*7c478bd9Sstevel@tonic-gate 			if (((flags & CREATE_MASK) != 0) &&
2371*7c478bd9Sstevel@tonic-gate 			    ((flags & CREATE_MASK) != CREATE_DEFER)) {
2372*7c478bd9Sstevel@tonic-gate 				free(create_list_element);
2373*7c478bd9Sstevel@tonic-gate 				err_print("illegal flag combination in "
2374*7c478bd9Sstevel@tonic-gate 						"module create\n");
2375*7c478bd9Sstevel@tonic-gate 				err_print(IGNORING_ENTRY, i, epath);
2376*7c478bd9Sstevel@tonic-gate 				continue;
2377*7c478bd9Sstevel@tonic-gate 			}
2378*7c478bd9Sstevel@tonic-gate 
2379*7c478bd9Sstevel@tonic-gate 			if (((flags & TYPE_MASK) == 0) ^
2380*7c478bd9Sstevel@tonic-gate 			    (create_reg->tblp[i].node_type == NULL)) {
2381*7c478bd9Sstevel@tonic-gate 				free(create_list_element);
2382*7c478bd9Sstevel@tonic-gate 				err_print("flags value incompatible with "
2383*7c478bd9Sstevel@tonic-gate 					"node_type value in module create\n");
2384*7c478bd9Sstevel@tonic-gate 				err_print(IGNORING_ENTRY, i, epath);
2385*7c478bd9Sstevel@tonic-gate 				continue;
2386*7c478bd9Sstevel@tonic-gate 			}
2387*7c478bd9Sstevel@tonic-gate 
2388*7c478bd9Sstevel@tonic-gate 			if (((flags & TYPE_MASK) != 0) &&
2389*7c478bd9Sstevel@tonic-gate 			    ((flags & TYPE_MASK) != TYPE_EXACT) &&
2390*7c478bd9Sstevel@tonic-gate 			    ((flags & TYPE_MASK) != TYPE_RE) &&
2391*7c478bd9Sstevel@tonic-gate 			    ((flags & TYPE_MASK) != TYPE_PARTIAL)) {
2392*7c478bd9Sstevel@tonic-gate 				free(create_list_element);
2393*7c478bd9Sstevel@tonic-gate 				err_print("illegal TYPE_* flag combination in "
2394*7c478bd9Sstevel@tonic-gate 						"module create\n");
2395*7c478bd9Sstevel@tonic-gate 				err_print(IGNORING_ENTRY, i, epath);
2396*7c478bd9Sstevel@tonic-gate 				continue;
2397*7c478bd9Sstevel@tonic-gate 			}
2398*7c478bd9Sstevel@tonic-gate 
2399*7c478bd9Sstevel@tonic-gate 			/* precompile regular expression for efficiency */
2400*7c478bd9Sstevel@tonic-gate 			if ((flags & TYPE_RE) == TYPE_RE) {
2401*7c478bd9Sstevel@tonic-gate 				if ((n = regcomp(&(create_list_element->
2402*7c478bd9Sstevel@tonic-gate 				    node_type_comp),
2403*7c478bd9Sstevel@tonic-gate 				    create_reg->tblp[i].node_type,
2404*7c478bd9Sstevel@tonic-gate 				    REG_EXTENDED)) != 0) {
2405*7c478bd9Sstevel@tonic-gate 					free(create_list_element);
2406*7c478bd9Sstevel@tonic-gate 					err_print(REGCOMP_FAILED,
2407*7c478bd9Sstevel@tonic-gate 						create_reg->tblp[i].node_type,
2408*7c478bd9Sstevel@tonic-gate 						n);
2409*7c478bd9Sstevel@tonic-gate 					err_print(IGNORING_ENTRY, i, epath);
2410*7c478bd9Sstevel@tonic-gate 					continue;
2411*7c478bd9Sstevel@tonic-gate 				}
2412*7c478bd9Sstevel@tonic-gate 			}
2413*7c478bd9Sstevel@tonic-gate 
2414*7c478bd9Sstevel@tonic-gate 			if (((flags & DRV_MASK) == 0) ^
2415*7c478bd9Sstevel@tonic-gate 			    (create_reg->tblp[i].drv_name == NULL)) {
2416*7c478bd9Sstevel@tonic-gate 				if ((flags & TYPE_RE) == TYPE_RE) {
2417*7c478bd9Sstevel@tonic-gate 					regfree(&(create_list_element->
2418*7c478bd9Sstevel@tonic-gate 					    node_type_comp));
2419*7c478bd9Sstevel@tonic-gate 				}
2420*7c478bd9Sstevel@tonic-gate 				free(create_list_element);
2421*7c478bd9Sstevel@tonic-gate 				err_print("flags value incompatible with "
2422*7c478bd9Sstevel@tonic-gate 					"drv_name value in module create\n");
2423*7c478bd9Sstevel@tonic-gate 				err_print(IGNORING_ENTRY, i, epath);
2424*7c478bd9Sstevel@tonic-gate 				continue;
2425*7c478bd9Sstevel@tonic-gate 			}
2426*7c478bd9Sstevel@tonic-gate 
2427*7c478bd9Sstevel@tonic-gate 			if (((flags & DRV_MASK) != 0) &&
2428*7c478bd9Sstevel@tonic-gate 			    ((flags & DRV_MASK) != DRV_EXACT) &&
2429*7c478bd9Sstevel@tonic-gate 			    ((flags & DRV_MASK) !=  DRV_RE)) {
2430*7c478bd9Sstevel@tonic-gate 				if ((flags & TYPE_RE) == TYPE_RE) {
2431*7c478bd9Sstevel@tonic-gate 					regfree(&(create_list_element->
2432*7c478bd9Sstevel@tonic-gate 					    node_type_comp));
2433*7c478bd9Sstevel@tonic-gate 				}
2434*7c478bd9Sstevel@tonic-gate 				free(create_list_element);
2435*7c478bd9Sstevel@tonic-gate 				err_print("illegal DRV_* flag combination in "
2436*7c478bd9Sstevel@tonic-gate 					"module create\n");
2437*7c478bd9Sstevel@tonic-gate 				err_print(IGNORING_ENTRY, i, epath);
2438*7c478bd9Sstevel@tonic-gate 				continue;
2439*7c478bd9Sstevel@tonic-gate 			}
2440*7c478bd9Sstevel@tonic-gate 
2441*7c478bd9Sstevel@tonic-gate 			/* precompile regular expression for efficiency */
2442*7c478bd9Sstevel@tonic-gate 			if ((create_reg->tblp[i].flags & DRV_RE) == DRV_RE) {
2443*7c478bd9Sstevel@tonic-gate 				if ((n = regcomp(&(create_list_element->
2444*7c478bd9Sstevel@tonic-gate 				    drv_name_comp),
2445*7c478bd9Sstevel@tonic-gate 				    create_reg->tblp[i].drv_name,
2446*7c478bd9Sstevel@tonic-gate 				    REG_EXTENDED)) != 0) {
2447*7c478bd9Sstevel@tonic-gate 					if ((flags & TYPE_RE) == TYPE_RE) {
2448*7c478bd9Sstevel@tonic-gate 						regfree(&(create_list_element->
2449*7c478bd9Sstevel@tonic-gate 						    node_type_comp));
2450*7c478bd9Sstevel@tonic-gate 					}
2451*7c478bd9Sstevel@tonic-gate 					free(create_list_element);
2452*7c478bd9Sstevel@tonic-gate 					err_print(REGCOMP_FAILED,
2453*7c478bd9Sstevel@tonic-gate 						create_reg->tblp[i].drv_name,
2454*7c478bd9Sstevel@tonic-gate 						n);
2455*7c478bd9Sstevel@tonic-gate 					err_print(IGNORING_ENTRY, i, epath);
2456*7c478bd9Sstevel@tonic-gate 					continue;
2457*7c478bd9Sstevel@tonic-gate 				}
2458*7c478bd9Sstevel@tonic-gate 			}
2459*7c478bd9Sstevel@tonic-gate 
2460*7c478bd9Sstevel@tonic-gate 
2461*7c478bd9Sstevel@tonic-gate 			/* add to list sorted by interpose level */
2462*7c478bd9Sstevel@tonic-gate 			for (create_list_next = &(create_head);
2463*7c478bd9Sstevel@tonic-gate 				(*create_list_next != NULL) &&
2464*7c478bd9Sstevel@tonic-gate 				(*create_list_next)->create->interpose_lvl >=
2465*7c478bd9Sstevel@tonic-gate 				create_list_element->create->interpose_lvl;
2466*7c478bd9Sstevel@tonic-gate 				create_list_next =
2467*7c478bd9Sstevel@tonic-gate 					&((*create_list_next)->next));
2468*7c478bd9Sstevel@tonic-gate 			create_list_element->next = *create_list_next;
2469*7c478bd9Sstevel@tonic-gate 			*create_list_next = create_list_element;
2470*7c478bd9Sstevel@tonic-gate 		}
2471*7c478bd9Sstevel@tonic-gate 	}
2472*7c478bd9Sstevel@tonic-gate 
2473*7c478bd9Sstevel@tonic-gate 	/*
2474*7c478bd9Sstevel@tonic-gate 	 *  put a ptr to each struct devfsadm_remove on "remove_head"
2475*7c478bd9Sstevel@tonic-gate 	 *  list sorted by interpose_lvl.
2476*7c478bd9Sstevel@tonic-gate 	 */
2477*7c478bd9Sstevel@tonic-gate 	if (remove_reg != NULL) {
2478*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < remove_reg->count; i++) {
2479*7c478bd9Sstevel@tonic-gate 
2480*7c478bd9Sstevel@tonic-gate 			remove_list_element = (remove_list_t *)
2481*7c478bd9Sstevel@tonic-gate 				s_malloc(sizeof (remove_list_t));
2482*7c478bd9Sstevel@tonic-gate 
2483*7c478bd9Sstevel@tonic-gate 			remove_list_element->remove = &(remove_reg->tblp[i]);
2484*7c478bd9Sstevel@tonic-gate 			remove_list_element->modptr = module;
2485*7c478bd9Sstevel@tonic-gate 
2486*7c478bd9Sstevel@tonic-gate 			for (remove_list_next = &(remove_head);
2487*7c478bd9Sstevel@tonic-gate 				(*remove_list_next != NULL) &&
2488*7c478bd9Sstevel@tonic-gate 				(*remove_list_next)->remove->interpose_lvl >=
2489*7c478bd9Sstevel@tonic-gate 				remove_list_element->remove->interpose_lvl;
2490*7c478bd9Sstevel@tonic-gate 				remove_list_next =
2491*7c478bd9Sstevel@tonic-gate 					&((*remove_list_next)->next));
2492*7c478bd9Sstevel@tonic-gate 			remove_list_element->next = *remove_list_next;
2493*7c478bd9Sstevel@tonic-gate 			*remove_list_next = remove_list_element;
2494*7c478bd9Sstevel@tonic-gate 		}
2495*7c478bd9Sstevel@tonic-gate 	}
2496*7c478bd9Sstevel@tonic-gate 
2497*7c478bd9Sstevel@tonic-gate 	module->next = module_head;
2498*7c478bd9Sstevel@tonic-gate 	module_head = module;
2499*7c478bd9Sstevel@tonic-gate }
2500*7c478bd9Sstevel@tonic-gate 
2501*7c478bd9Sstevel@tonic-gate /*
2502*7c478bd9Sstevel@tonic-gate  * Create a thread to call minor_fini after some delay
2503*7c478bd9Sstevel@tonic-gate  */
2504*7c478bd9Sstevel@tonic-gate static void
2505*7c478bd9Sstevel@tonic-gate startup_cache_sync_thread()
2506*7c478bd9Sstevel@tonic-gate {
2507*7c478bd9Sstevel@tonic-gate 	vprint(INITFINI_MID, "startup_cache_sync_thread\n");
2508*7c478bd9Sstevel@tonic-gate 
2509*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(&minor_fini_mutex);
2510*7c478bd9Sstevel@tonic-gate 
2511*7c478bd9Sstevel@tonic-gate 	minor_fini_delay_restart = TRUE;
2512*7c478bd9Sstevel@tonic-gate 
2513*7c478bd9Sstevel@tonic-gate 	if (minor_fini_thread_created == FALSE) {
2514*7c478bd9Sstevel@tonic-gate 
2515*7c478bd9Sstevel@tonic-gate 		if (thr_create(NULL, NULL,
2516*7c478bd9Sstevel@tonic-gate 		    (void *(*)(void *))call_minor_fini_thread, NULL,
2517*7c478bd9Sstevel@tonic-gate 		    THR_DETACHED, NULL)) {
2518*7c478bd9Sstevel@tonic-gate 			err_print(CANT_CREATE_THREAD, "minor_fini",
2519*7c478bd9Sstevel@tonic-gate 					strerror(errno));
2520*7c478bd9Sstevel@tonic-gate 
2521*7c478bd9Sstevel@tonic-gate 			(void) mutex_unlock(&minor_fini_mutex);
2522*7c478bd9Sstevel@tonic-gate 
2523*7c478bd9Sstevel@tonic-gate 			/*
2524*7c478bd9Sstevel@tonic-gate 			 * just sync state here instead of
2525*7c478bd9Sstevel@tonic-gate 			 * giving up
2526*7c478bd9Sstevel@tonic-gate 			 */
2527*7c478bd9Sstevel@tonic-gate 			lock_dev();
2528*7c478bd9Sstevel@tonic-gate 			unlock_dev(SYNC_STATE);
2529*7c478bd9Sstevel@tonic-gate 
2530*7c478bd9Sstevel@tonic-gate 			return;
2531*7c478bd9Sstevel@tonic-gate 		}
2532*7c478bd9Sstevel@tonic-gate 		minor_fini_thread_created = TRUE;
2533*7c478bd9Sstevel@tonic-gate 	} else {
2534*7c478bd9Sstevel@tonic-gate 		vprint(INITFINI_MID, "restarting delay\n");
2535*7c478bd9Sstevel@tonic-gate 	}
2536*7c478bd9Sstevel@tonic-gate 
2537*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(&minor_fini_mutex);
2538*7c478bd9Sstevel@tonic-gate }
2539*7c478bd9Sstevel@tonic-gate 
2540*7c478bd9Sstevel@tonic-gate /*
2541*7c478bd9Sstevel@tonic-gate  * after not receiving an event for minor_fini_timeout secs, we need
2542*7c478bd9Sstevel@tonic-gate  * to call the minor_fini routines
2543*7c478bd9Sstevel@tonic-gate  */
2544*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2545*7c478bd9Sstevel@tonic-gate static void
2546*7c478bd9Sstevel@tonic-gate call_minor_fini_thread(void *arg)
2547*7c478bd9Sstevel@tonic-gate {
2548*7c478bd9Sstevel@tonic-gate 	int count = 0;
2549*7c478bd9Sstevel@tonic-gate 
2550*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(&minor_fini_mutex);
2551*7c478bd9Sstevel@tonic-gate 
2552*7c478bd9Sstevel@tonic-gate 	vprint(INITFINI_MID, "call_minor_fini_thread starting\n");
2553*7c478bd9Sstevel@tonic-gate 
2554*7c478bd9Sstevel@tonic-gate 	do {
2555*7c478bd9Sstevel@tonic-gate 		minor_fini_delay_restart = FALSE;
2556*7c478bd9Sstevel@tonic-gate 
2557*7c478bd9Sstevel@tonic-gate 		(void) mutex_unlock(&minor_fini_mutex);
2558*7c478bd9Sstevel@tonic-gate 		(void) sleep(minor_fini_timeout);
2559*7c478bd9Sstevel@tonic-gate 		(void) mutex_lock(&minor_fini_mutex);
2560*7c478bd9Sstevel@tonic-gate 
2561*7c478bd9Sstevel@tonic-gate 		/*
2562*7c478bd9Sstevel@tonic-gate 		 * if minor_fini_delay_restart is still false then
2563*7c478bd9Sstevel@tonic-gate 		 * we can call minor fini routines.
2564*7c478bd9Sstevel@tonic-gate 		 * ensure that at least periodically minor_fini gets
2565*7c478bd9Sstevel@tonic-gate 		 * called satisfying link generators depending on fini
2566*7c478bd9Sstevel@tonic-gate 		 * being eventually called
2567*7c478bd9Sstevel@tonic-gate 		 */
2568*7c478bd9Sstevel@tonic-gate 		if ((count++ >= FORCE_CALL_MINOR_FINI) ||
2569*7c478bd9Sstevel@tonic-gate 		    (minor_fini_delay_restart == FALSE)) {
2570*7c478bd9Sstevel@tonic-gate 			vprint(INITFINI_MID,
2571*7c478bd9Sstevel@tonic-gate 			    "call_minor_fini starting (%d)\n", count);
2572*7c478bd9Sstevel@tonic-gate 			(void) mutex_unlock(&minor_fini_mutex);
2573*7c478bd9Sstevel@tonic-gate 
2574*7c478bd9Sstevel@tonic-gate 			lock_dev();
2575*7c478bd9Sstevel@tonic-gate 			unlock_dev(SYNC_STATE);
2576*7c478bd9Sstevel@tonic-gate 
2577*7c478bd9Sstevel@tonic-gate 			vprint(INITFINI_MID, "call_minor_fini done\n");
2578*7c478bd9Sstevel@tonic-gate 
2579*7c478bd9Sstevel@tonic-gate 			/*
2580*7c478bd9Sstevel@tonic-gate 			 * hang around before exiting just in case
2581*7c478bd9Sstevel@tonic-gate 			 * minor_fini_delay_restart is set again
2582*7c478bd9Sstevel@tonic-gate 			 */
2583*7c478bd9Sstevel@tonic-gate 			(void) sleep(1);
2584*7c478bd9Sstevel@tonic-gate 
2585*7c478bd9Sstevel@tonic-gate 			count = 0;
2586*7c478bd9Sstevel@tonic-gate 
2587*7c478bd9Sstevel@tonic-gate 			(void) mutex_lock(&minor_fini_mutex);
2588*7c478bd9Sstevel@tonic-gate 		}
2589*7c478bd9Sstevel@tonic-gate 	} while (minor_fini_delay_restart);
2590*7c478bd9Sstevel@tonic-gate 
2591*7c478bd9Sstevel@tonic-gate 	minor_fini_thread_created = FALSE;
2592*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(&minor_fini_mutex);
2593*7c478bd9Sstevel@tonic-gate 	vprint(INITFINI_MID, "call_minor_fini_thread exiting\n");
2594*7c478bd9Sstevel@tonic-gate }
2595*7c478bd9Sstevel@tonic-gate 
2596*7c478bd9Sstevel@tonic-gate /*
2597*7c478bd9Sstevel@tonic-gate  * Attempt to initialize module, if a minor_init routine exists.  Set
2598*7c478bd9Sstevel@tonic-gate  * the active flag if the routine exists and succeeds.	If it doesn't
2599*7c478bd9Sstevel@tonic-gate  * exist, just set the active flag.
2600*7c478bd9Sstevel@tonic-gate  */
2601*7c478bd9Sstevel@tonic-gate static int
2602*7c478bd9Sstevel@tonic-gate call_minor_init(module_t *module)
2603*7c478bd9Sstevel@tonic-gate {
2604*7c478bd9Sstevel@tonic-gate 	char *fcn = "call_minor_init: ";
2605*7c478bd9Sstevel@tonic-gate 
2606*7c478bd9Sstevel@tonic-gate 	if ((module->flags & MODULE_ACTIVE) == MODULE_ACTIVE) {
2607*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
2608*7c478bd9Sstevel@tonic-gate 	}
2609*7c478bd9Sstevel@tonic-gate 
2610*7c478bd9Sstevel@tonic-gate 	vprint(INITFINI_MID, "%smodule %s.  current state: inactive\n",
2611*7c478bd9Sstevel@tonic-gate 		fcn, module->name);
2612*7c478bd9Sstevel@tonic-gate 
2613*7c478bd9Sstevel@tonic-gate 	if (module->minor_init == NULL) {
2614*7c478bd9Sstevel@tonic-gate 		module->flags |= MODULE_ACTIVE;
2615*7c478bd9Sstevel@tonic-gate 		vprint(INITFINI_MID, "minor_init not defined\n");
2616*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
2617*7c478bd9Sstevel@tonic-gate 	}
2618*7c478bd9Sstevel@tonic-gate 
2619*7c478bd9Sstevel@tonic-gate 	if ((*(module->minor_init))() == DEVFSADM_FAILURE) {
2620*7c478bd9Sstevel@tonic-gate 		err_print(FAILED_FOR_MODULE, MINOR_INIT, module->name);
2621*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
2622*7c478bd9Sstevel@tonic-gate 	}
2623*7c478bd9Sstevel@tonic-gate 
2624*7c478bd9Sstevel@tonic-gate 	vprint(INITFINI_MID, "minor_init() returns DEVFSADM_SUCCESS. "
2625*7c478bd9Sstevel@tonic-gate 		"new state: active\n");
2626*7c478bd9Sstevel@tonic-gate 
2627*7c478bd9Sstevel@tonic-gate 	module->flags |= MODULE_ACTIVE;
2628*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
2629*7c478bd9Sstevel@tonic-gate }
2630*7c478bd9Sstevel@tonic-gate 
2631*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2632*7c478bd9Sstevel@tonic-gate int
2633*7c478bd9Sstevel@tonic-gate devfsadm_mklink_zone(struct zone_devinfo *z, char *link, di_node_t node,
2634*7c478bd9Sstevel@tonic-gate     di_minor_t minor, int flags)
2635*7c478bd9Sstevel@tonic-gate {
2636*7c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
2637*7c478bd9Sstevel@tonic-gate 	char phy_path[PATH_MAX];
2638*7c478bd9Sstevel@tonic-gate 	char *dev_pathp;
2639*7c478bd9Sstevel@tonic-gate 	char *acontents, *aminor = NULL;
2640*7c478bd9Sstevel@tonic-gate 	mode_t mode;
2641*7c478bd9Sstevel@tonic-gate 	uid_t uid;
2642*7c478bd9Sstevel@tonic-gate 	gid_t gid;
2643*7c478bd9Sstevel@tonic-gate 	dev_t dev;
2644*7c478bd9Sstevel@tonic-gate 	struct zone_devtab out_match;
2645*7c478bd9Sstevel@tonic-gate 
2646*7c478bd9Sstevel@tonic-gate 	if (zonecfg_match_dev(z->zone_dochdl, link, &out_match) != Z_OK) {
2647*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
2648*7c478bd9Sstevel@tonic-gate 	}
2649*7c478bd9Sstevel@tonic-gate 
2650*7c478bd9Sstevel@tonic-gate 	vprint(ZONE_MID, "zone device match: <device match=\"%s\"> "
2651*7c478bd9Sstevel@tonic-gate 	    "matches /dev/%s\n", out_match.zone_dev_match, link);
2652*7c478bd9Sstevel@tonic-gate 
2653*7c478bd9Sstevel@tonic-gate 	/*
2654*7c478bd9Sstevel@tonic-gate 	 * In daemon mode, zone_path will be non-empty.  In non-daemon mode
2655*7c478bd9Sstevel@tonic-gate 	 * it will be empty since we've already stuck the zone into dev_dir,
2656*7c478bd9Sstevel@tonic-gate 	 * etc.
2657*7c478bd9Sstevel@tonic-gate 	 */
2658*7c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s/dev/%s", z->zone_path, link);
2659*7c478bd9Sstevel@tonic-gate 	dev = di_minor_devt(minor);
2660*7c478bd9Sstevel@tonic-gate 
2661*7c478bd9Sstevel@tonic-gate 	/*
2662*7c478bd9Sstevel@tonic-gate 	 * If this is an alias node (i.e. a clone node), we have to figure
2663*7c478bd9Sstevel@tonic-gate 	 * out the minor name.
2664*7c478bd9Sstevel@tonic-gate 	 */
2665*7c478bd9Sstevel@tonic-gate 	if (di_minor_type(minor) == DDM_ALIAS) {
2666*7c478bd9Sstevel@tonic-gate 		/* use /pseudo/clone@0:<driver> as the phys path */
2667*7c478bd9Sstevel@tonic-gate 		(void) snprintf(phy_path, sizeof (phy_path),
2668*7c478bd9Sstevel@tonic-gate 		    "/pseudo/clone@0:%s",
2669*7c478bd9Sstevel@tonic-gate 		    di_driver_name(di_minor_devinfo(minor)));
2670*7c478bd9Sstevel@tonic-gate 		aminor = di_minor_name(minor);
2671*7c478bd9Sstevel@tonic-gate 		acontents = phy_path;
2672*7c478bd9Sstevel@tonic-gate 	} else {
2673*7c478bd9Sstevel@tonic-gate 		if ((dev_pathp = di_devfs_path(node)) == NULL) {
2674*7c478bd9Sstevel@tonic-gate 			err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
2675*7c478bd9Sstevel@tonic-gate 			devfsadm_exit(1);
2676*7c478bd9Sstevel@tonic-gate 		}
2677*7c478bd9Sstevel@tonic-gate 		(void) snprintf(phy_path, sizeof (phy_path), "%s:%s",
2678*7c478bd9Sstevel@tonic-gate 		    dev_pathp, di_minor_name(minor));
2679*7c478bd9Sstevel@tonic-gate 		di_devfs_path_free(dev_pathp);
2680*7c478bd9Sstevel@tonic-gate 		acontents = phy_path;
2681*7c478bd9Sstevel@tonic-gate 	}
2682*7c478bd9Sstevel@tonic-gate 
2683*7c478bd9Sstevel@tonic-gate 
2684*7c478bd9Sstevel@tonic-gate 	getattr(acontents, aminor, di_minor_spectype(minor), dev,
2685*7c478bd9Sstevel@tonic-gate 	    &mode, &uid, &gid);
2686*7c478bd9Sstevel@tonic-gate 	vprint(ZONE_MID, "zone getattr(%s, %s, %d, %lu, 0%lo, %lu, %lu)\n",
2687*7c478bd9Sstevel@tonic-gate 	    acontents, aminor ? aminor : "<NULL>", di_minor_spectype(minor),
2688*7c478bd9Sstevel@tonic-gate 	    dev, mode, uid, gid);
2689*7c478bd9Sstevel@tonic-gate 
2690*7c478bd9Sstevel@tonic-gate 	/*
2691*7c478bd9Sstevel@tonic-gate 	 * Create the node; if the enclosing directory doesn't exist,
2692*7c478bd9Sstevel@tonic-gate 	 * make it too.
2693*7c478bd9Sstevel@tonic-gate 	 */
2694*7c478bd9Sstevel@tonic-gate top:
2695*7c478bd9Sstevel@tonic-gate 	if (mknod(path, di_minor_spectype(minor) | mode, dev) == -1) {
2696*7c478bd9Sstevel@tonic-gate 		if (errno == ENOENT) {
2697*7c478bd9Sstevel@tonic-gate 			/* dirpath to node doesn't exist */
2698*7c478bd9Sstevel@tonic-gate 			char *hide = strrchr(path, '/');
2699*7c478bd9Sstevel@tonic-gate 			*hide = '\0';
2700*7c478bd9Sstevel@tonic-gate 			s_mkdirp(path,
2701*7c478bd9Sstevel@tonic-gate 			    S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
2702*7c478bd9Sstevel@tonic-gate 			*hide = '/';
2703*7c478bd9Sstevel@tonic-gate 			goto top;
2704*7c478bd9Sstevel@tonic-gate 		} else if (errno == EEXIST) {
2705*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_SUCCESS);
2706*7c478bd9Sstevel@tonic-gate 		}
2707*7c478bd9Sstevel@tonic-gate 		err_print(MKNOD_FAILED, path, strerror(errno));
2708*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
2709*7c478bd9Sstevel@tonic-gate 	} else {
2710*7c478bd9Sstevel@tonic-gate 		/*
2711*7c478bd9Sstevel@tonic-gate 		 * If we successfully made the node, then set its owner
2712*7c478bd9Sstevel@tonic-gate 		 * and group.  Existing nodes will be unaffected.
2713*7c478bd9Sstevel@tonic-gate 		 */
2714*7c478bd9Sstevel@tonic-gate 		(void) chown(path, uid, gid);
2715*7c478bd9Sstevel@tonic-gate 	}
2716*7c478bd9Sstevel@tonic-gate 
2717*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
2718*7c478bd9Sstevel@tonic-gate }
2719*7c478bd9Sstevel@tonic-gate 
2720*7c478bd9Sstevel@tonic-gate /*
2721*7c478bd9Sstevel@tonic-gate  * Creates a symlink 'link' to the physical path of node:minor.
2722*7c478bd9Sstevel@tonic-gate  * Construct link contents, then call create_link_common().
2723*7c478bd9Sstevel@tonic-gate  */
2724*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2725*7c478bd9Sstevel@tonic-gate int
2726*7c478bd9Sstevel@tonic-gate devfsadm_mklink_default(char *link, di_node_t node, di_minor_t minor, int flags)
2727*7c478bd9Sstevel@tonic-gate {
2728*7c478bd9Sstevel@tonic-gate 	char rcontents[PATH_MAX];
2729*7c478bd9Sstevel@tonic-gate 	char devlink[PATH_MAX];
2730*7c478bd9Sstevel@tonic-gate 	char phy_path[PATH_MAX];
2731*7c478bd9Sstevel@tonic-gate 	char *acontents;
2732*7c478bd9Sstevel@tonic-gate 	char *dev_path;
2733*7c478bd9Sstevel@tonic-gate 	int numslashes;
2734*7c478bd9Sstevel@tonic-gate 	int rv;
2735*7c478bd9Sstevel@tonic-gate 	int i, link_exists;
2736*7c478bd9Sstevel@tonic-gate 	int last_was_slash = FALSE;
2737*7c478bd9Sstevel@tonic-gate 
2738*7c478bd9Sstevel@tonic-gate 	/*
2739*7c478bd9Sstevel@tonic-gate 	 * try to use devices path
2740*7c478bd9Sstevel@tonic-gate 	 */
2741*7c478bd9Sstevel@tonic-gate 	if ((node == lnode) && (minor == lminor)) {
2742*7c478bd9Sstevel@tonic-gate 		acontents = lphy_path;
2743*7c478bd9Sstevel@tonic-gate 	} else if (di_minor_type(minor) == DDM_ALIAS) {
2744*7c478bd9Sstevel@tonic-gate 		/* use /pseudo/clone@0:<driver> as the phys path */
2745*7c478bd9Sstevel@tonic-gate 		(void) snprintf(phy_path, sizeof (phy_path),
2746*7c478bd9Sstevel@tonic-gate 		    "/pseudo/clone@0:%s",
2747*7c478bd9Sstevel@tonic-gate 		    di_driver_name(di_minor_devinfo(minor)));
2748*7c478bd9Sstevel@tonic-gate 		acontents = phy_path;
2749*7c478bd9Sstevel@tonic-gate 	} else {
2750*7c478bd9Sstevel@tonic-gate 		if ((dev_path = di_devfs_path(node)) == NULL) {
2751*7c478bd9Sstevel@tonic-gate 			err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
2752*7c478bd9Sstevel@tonic-gate 			devfsadm_exit(1);
2753*7c478bd9Sstevel@tonic-gate 		}
2754*7c478bd9Sstevel@tonic-gate 		(void) snprintf(phy_path, sizeof (phy_path), "%s:%s",
2755*7c478bd9Sstevel@tonic-gate 		    dev_path, di_minor_name(minor));
2756*7c478bd9Sstevel@tonic-gate 		di_devfs_path_free(dev_path);
2757*7c478bd9Sstevel@tonic-gate 		acontents = phy_path;
2758*7c478bd9Sstevel@tonic-gate 	}
2759*7c478bd9Sstevel@tonic-gate 
2760*7c478bd9Sstevel@tonic-gate 	/* prepend link with dev_dir contents */
2761*7c478bd9Sstevel@tonic-gate 	(void) strlcpy(devlink, dev_dir, sizeof (devlink));
2762*7c478bd9Sstevel@tonic-gate 	(void) strlcat(devlink, "/", sizeof (devlink));
2763*7c478bd9Sstevel@tonic-gate 	(void) strlcat(devlink, link, sizeof (devlink));
2764*7c478bd9Sstevel@tonic-gate 
2765*7c478bd9Sstevel@tonic-gate 	/*
2766*7c478bd9Sstevel@tonic-gate 	 * Calculate # of ../ to add.  Account for double '//' in path.
2767*7c478bd9Sstevel@tonic-gate 	 * Ignore all leading slashes.
2768*7c478bd9Sstevel@tonic-gate 	 */
2769*7c478bd9Sstevel@tonic-gate 	for (i = 0; link[i] == '/'; i++)
2770*7c478bd9Sstevel@tonic-gate 		;
2771*7c478bd9Sstevel@tonic-gate 	for (numslashes = 0; link[i] != '\0'; i++) {
2772*7c478bd9Sstevel@tonic-gate 		if (link[i] == '/') {
2773*7c478bd9Sstevel@tonic-gate 			if (last_was_slash == FALSE) {
2774*7c478bd9Sstevel@tonic-gate 				numslashes++;
2775*7c478bd9Sstevel@tonic-gate 				last_was_slash = TRUE;
2776*7c478bd9Sstevel@tonic-gate 			}
2777*7c478bd9Sstevel@tonic-gate 		} else {
2778*7c478bd9Sstevel@tonic-gate 			last_was_slash = FALSE;
2779*7c478bd9Sstevel@tonic-gate 		}
2780*7c478bd9Sstevel@tonic-gate 	}
2781*7c478bd9Sstevel@tonic-gate 	/* Don't count any trailing '/' */
2782*7c478bd9Sstevel@tonic-gate 	if (link[i-1] == '/') {
2783*7c478bd9Sstevel@tonic-gate 		numslashes--;
2784*7c478bd9Sstevel@tonic-gate 	}
2785*7c478bd9Sstevel@tonic-gate 
2786*7c478bd9Sstevel@tonic-gate 	rcontents[0] = '\0';
2787*7c478bd9Sstevel@tonic-gate 	do {
2788*7c478bd9Sstevel@tonic-gate 		(void) strlcat(rcontents, "../", sizeof (rcontents));
2789*7c478bd9Sstevel@tonic-gate 	} while (numslashes-- != 0);
2790*7c478bd9Sstevel@tonic-gate 
2791*7c478bd9Sstevel@tonic-gate 	(void) strlcat(rcontents, "devices", sizeof (rcontents));
2792*7c478bd9Sstevel@tonic-gate 	(void) strlcat(rcontents, acontents, sizeof (rcontents));
2793*7c478bd9Sstevel@tonic-gate 
2794*7c478bd9Sstevel@tonic-gate 	if (devlinks_debug == TRUE) {
2795*7c478bd9Sstevel@tonic-gate 		vprint(INFO_MID, "adding link %s ==> %s\n", devlink, rcontents);
2796*7c478bd9Sstevel@tonic-gate 	}
2797*7c478bd9Sstevel@tonic-gate 
2798*7c478bd9Sstevel@tonic-gate 	if ((rv = create_link_common(devlink, rcontents, &link_exists))
2799*7c478bd9Sstevel@tonic-gate 	    == DEVFSADM_SUCCESS) {
2800*7c478bd9Sstevel@tonic-gate 		linknew = TRUE;
2801*7c478bd9Sstevel@tonic-gate 		add_link_to_cache(link, acontents);
2802*7c478bd9Sstevel@tonic-gate 	} else {
2803*7c478bd9Sstevel@tonic-gate 		linknew = FALSE;
2804*7c478bd9Sstevel@tonic-gate 	}
2805*7c478bd9Sstevel@tonic-gate 
2806*7c478bd9Sstevel@tonic-gate 	if (link_exists == TRUE) {
2807*7c478bd9Sstevel@tonic-gate 		/* Link exists or was just created */
2808*7c478bd9Sstevel@tonic-gate 		(void) di_devlink_add_link(devlink_cache, link, rcontents,
2809*7c478bd9Sstevel@tonic-gate 		    DI_PRIMARY_LINK);
2810*7c478bd9Sstevel@tonic-gate 	}
2811*7c478bd9Sstevel@tonic-gate 
2812*7c478bd9Sstevel@tonic-gate 	return (rv);
2813*7c478bd9Sstevel@tonic-gate }
2814*7c478bd9Sstevel@tonic-gate 
2815*7c478bd9Sstevel@tonic-gate int
2816*7c478bd9Sstevel@tonic-gate devfsadm_mklink(char *link, di_node_t node, di_minor_t minor, int flags)
2817*7c478bd9Sstevel@tonic-gate {
2818*7c478bd9Sstevel@tonic-gate 	struct zone_devinfo *z;
2819*7c478bd9Sstevel@tonic-gate 	int error;
2820*7c478bd9Sstevel@tonic-gate 
2821*7c478bd9Sstevel@tonic-gate 	/*
2822*7c478bd9Sstevel@tonic-gate 	 * If we're in zone mode (also implies !daemon_mode), then the
2823*7c478bd9Sstevel@tonic-gate 	 * zone devinfo list has only one element, the zone we're configuring,
2824*7c478bd9Sstevel@tonic-gate 	 * and we can just use zone_head.
2825*7c478bd9Sstevel@tonic-gate 	 */
2826*7c478bd9Sstevel@tonic-gate 	if (zone_cmd_mode)
2827*7c478bd9Sstevel@tonic-gate 		return (devfsadm_mklink_zone(zone_head, link, node,
2828*7c478bd9Sstevel@tonic-gate 		    minor, flags));
2829*7c478bd9Sstevel@tonic-gate 	else if (!daemon_mode)
2830*7c478bd9Sstevel@tonic-gate 		return (devfsadm_mklink_default(link, node, minor, flags));
2831*7c478bd9Sstevel@tonic-gate 
2832*7c478bd9Sstevel@tonic-gate 	/*
2833*7c478bd9Sstevel@tonic-gate 	 * We're in daemon mode, so we need to make the link in the global
2834*7c478bd9Sstevel@tonic-gate 	 * zone; then, walk the list of zones, creating the corresponding
2835*7c478bd9Sstevel@tonic-gate 	 * mknod'd nodes in each.
2836*7c478bd9Sstevel@tonic-gate 	 */
2837*7c478bd9Sstevel@tonic-gate 	error = devfsadm_mklink_default(link, node, minor, flags);
2838*7c478bd9Sstevel@tonic-gate 
2839*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(&zone_mutex);
2840*7c478bd9Sstevel@tonic-gate 	for (z = zone_head; z != NULL; z = z->zone_next) {
2841*7c478bd9Sstevel@tonic-gate 		(void) devfsadm_mklink_zone(z, link, node, minor, flags);
2842*7c478bd9Sstevel@tonic-gate 	}
2843*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(&zone_mutex);
2844*7c478bd9Sstevel@tonic-gate 	return (error);
2845*7c478bd9Sstevel@tonic-gate }
2846*7c478bd9Sstevel@tonic-gate 
2847*7c478bd9Sstevel@tonic-gate /*
2848*7c478bd9Sstevel@tonic-gate  * Creates a symlink link to primary_link.  Calculates relative
2849*7c478bd9Sstevel@tonic-gate  * directory offsets, then calls link_common().
2850*7c478bd9Sstevel@tonic-gate  */
2851*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2852*7c478bd9Sstevel@tonic-gate int
2853*7c478bd9Sstevel@tonic-gate devfsadm_secondary_link(char *link, char *primary_link, int flags)
2854*7c478bd9Sstevel@tonic-gate {
2855*7c478bd9Sstevel@tonic-gate 	char contents[PATH_MAX + 1];
2856*7c478bd9Sstevel@tonic-gate 	char devlink[PATH_MAX + 1];
2857*7c478bd9Sstevel@tonic-gate 	int rv, link_exists;
2858*7c478bd9Sstevel@tonic-gate 	char *fpath;
2859*7c478bd9Sstevel@tonic-gate 	char *tpath;
2860*7c478bd9Sstevel@tonic-gate 	char *op;
2861*7c478bd9Sstevel@tonic-gate 
2862*7c478bd9Sstevel@tonic-gate 	/* prepend link with dev_dir contents */
2863*7c478bd9Sstevel@tonic-gate 	(void) strcpy(devlink, dev_dir);
2864*7c478bd9Sstevel@tonic-gate 	(void) strcat(devlink, "/");
2865*7c478bd9Sstevel@tonic-gate 	(void) strcat(devlink, link);
2866*7c478bd9Sstevel@tonic-gate 	/*
2867*7c478bd9Sstevel@tonic-gate 	 * building extra link, so use first link as link contents, but first
2868*7c478bd9Sstevel@tonic-gate 	 * make it relative.
2869*7c478bd9Sstevel@tonic-gate 	 */
2870*7c478bd9Sstevel@tonic-gate 	fpath = link;
2871*7c478bd9Sstevel@tonic-gate 	tpath = primary_link;
2872*7c478bd9Sstevel@tonic-gate 	op = contents;
2873*7c478bd9Sstevel@tonic-gate 
2874*7c478bd9Sstevel@tonic-gate 	while (*fpath == *tpath && *fpath != '\0') {
2875*7c478bd9Sstevel@tonic-gate 		fpath++, tpath++;
2876*7c478bd9Sstevel@tonic-gate 	}
2877*7c478bd9Sstevel@tonic-gate 
2878*7c478bd9Sstevel@tonic-gate 	/* Count directories to go up, if any, and add "../" */
2879*7c478bd9Sstevel@tonic-gate 	while (*fpath != '\0') {
2880*7c478bd9Sstevel@tonic-gate 		if (*fpath == '/') {
2881*7c478bd9Sstevel@tonic-gate 			(void) strcpy(op, "../");
2882*7c478bd9Sstevel@tonic-gate 			op += 3;
2883*7c478bd9Sstevel@tonic-gate 		}
2884*7c478bd9Sstevel@tonic-gate 		fpath++;
2885*7c478bd9Sstevel@tonic-gate 	}
2886*7c478bd9Sstevel@tonic-gate 
2887*7c478bd9Sstevel@tonic-gate 	/*
2888*7c478bd9Sstevel@tonic-gate 	 * Back up to the start of the current path component, in
2889*7c478bd9Sstevel@tonic-gate 	 * case in the middle
2890*7c478bd9Sstevel@tonic-gate 	 */
2891*7c478bd9Sstevel@tonic-gate 	while (tpath != primary_link && *(tpath-1) != '/') {
2892*7c478bd9Sstevel@tonic-gate 		tpath--;
2893*7c478bd9Sstevel@tonic-gate 	}
2894*7c478bd9Sstevel@tonic-gate 	(void) strcpy(op, tpath);
2895*7c478bd9Sstevel@tonic-gate 
2896*7c478bd9Sstevel@tonic-gate 	if (devlinks_debug == TRUE) {
2897*7c478bd9Sstevel@tonic-gate 		vprint(INFO_MID, "adding extra link %s ==> %s\n",
2898*7c478bd9Sstevel@tonic-gate 				devlink, contents);
2899*7c478bd9Sstevel@tonic-gate 	}
2900*7c478bd9Sstevel@tonic-gate 
2901*7c478bd9Sstevel@tonic-gate 	if ((rv = create_link_common(devlink, contents, &link_exists))
2902*7c478bd9Sstevel@tonic-gate 	    == DEVFSADM_SUCCESS) {
2903*7c478bd9Sstevel@tonic-gate 		/*
2904*7c478bd9Sstevel@tonic-gate 		 * we need to save the ultimate /devices contents, and not the
2905*7c478bd9Sstevel@tonic-gate 		 * secondary link, since hotcleanup only looks at /devices path.
2906*7c478bd9Sstevel@tonic-gate 		 * Since we don't have devices path here, we can try to get it
2907*7c478bd9Sstevel@tonic-gate 		 * by readlink'ing the secondary link.  This assumes the primary
2908*7c478bd9Sstevel@tonic-gate 		 * link was created first.
2909*7c478bd9Sstevel@tonic-gate 		 */
2910*7c478bd9Sstevel@tonic-gate 		add_link_to_cache(link, lphy_path);
2911*7c478bd9Sstevel@tonic-gate 		linknew = TRUE;
2912*7c478bd9Sstevel@tonic-gate 	} else {
2913*7c478bd9Sstevel@tonic-gate 		linknew = FALSE;
2914*7c478bd9Sstevel@tonic-gate 	}
2915*7c478bd9Sstevel@tonic-gate 
2916*7c478bd9Sstevel@tonic-gate 	/*
2917*7c478bd9Sstevel@tonic-gate 	 * If link exists or was just created, add it to the database
2918*7c478bd9Sstevel@tonic-gate 	 */
2919*7c478bd9Sstevel@tonic-gate 	if (link_exists == TRUE) {
2920*7c478bd9Sstevel@tonic-gate 		(void) di_devlink_add_link(devlink_cache, link, contents,
2921*7c478bd9Sstevel@tonic-gate 		    DI_SECONDARY_LINK);
2922*7c478bd9Sstevel@tonic-gate 	}
2923*7c478bd9Sstevel@tonic-gate 
2924*7c478bd9Sstevel@tonic-gate 	return (rv);
2925*7c478bd9Sstevel@tonic-gate }
2926*7c478bd9Sstevel@tonic-gate 
2927*7c478bd9Sstevel@tonic-gate /* returns pointer to the devices directory */
2928*7c478bd9Sstevel@tonic-gate char *
2929*7c478bd9Sstevel@tonic-gate devfsadm_get_devices_dir()
2930*7c478bd9Sstevel@tonic-gate {
2931*7c478bd9Sstevel@tonic-gate 	return (devices_dir);
2932*7c478bd9Sstevel@tonic-gate }
2933*7c478bd9Sstevel@tonic-gate 
2934*7c478bd9Sstevel@tonic-gate /*
2935*7c478bd9Sstevel@tonic-gate  * Does the actual link creation.  VERBOSE_MID only used if there is
2936*7c478bd9Sstevel@tonic-gate  * a change.  CHATTY_MID used otherwise.
2937*7c478bd9Sstevel@tonic-gate  */
2938*7c478bd9Sstevel@tonic-gate static int
2939*7c478bd9Sstevel@tonic-gate create_link_common(char *devlink, char *contents, int *exists)
2940*7c478bd9Sstevel@tonic-gate {
2941*7c478bd9Sstevel@tonic-gate 	int try;
2942*7c478bd9Sstevel@tonic-gate 	int linksize;
2943*7c478bd9Sstevel@tonic-gate 	int max_tries = 0;
2944*7c478bd9Sstevel@tonic-gate 	static int prev_link_existed = TRUE;
2945*7c478bd9Sstevel@tonic-gate 	char checkcontents[PATH_MAX + 1];
2946*7c478bd9Sstevel@tonic-gate 	char *hide;
2947*7c478bd9Sstevel@tonic-gate 
2948*7c478bd9Sstevel@tonic-gate 	*exists = FALSE;
2949*7c478bd9Sstevel@tonic-gate 
2950*7c478bd9Sstevel@tonic-gate 	/* Database is not updated when file_mods == FALSE */
2951*7c478bd9Sstevel@tonic-gate 	if (file_mods == FALSE) {
2952*7c478bd9Sstevel@tonic-gate 		linksize = readlink(devlink, checkcontents, PATH_MAX);
2953*7c478bd9Sstevel@tonic-gate 		if (linksize > 0) {
2954*7c478bd9Sstevel@tonic-gate 			checkcontents[linksize] = '\0';
2955*7c478bd9Sstevel@tonic-gate 			if (strcmp(checkcontents, contents) != 0) {
2956*7c478bd9Sstevel@tonic-gate 				vprint(CHATTY_MID, REMOVING_LINK,
2957*7c478bd9Sstevel@tonic-gate 						devlink, checkcontents);
2958*7c478bd9Sstevel@tonic-gate 				return (DEVFSADM_SUCCESS);
2959*7c478bd9Sstevel@tonic-gate 			} else {
2960*7c478bd9Sstevel@tonic-gate 				vprint(CHATTY_MID, "link exists and is correct:"
2961*7c478bd9Sstevel@tonic-gate 					" %s -> %s\n", devlink, contents);
2962*7c478bd9Sstevel@tonic-gate 				/* failure only in that the link existed */
2963*7c478bd9Sstevel@tonic-gate 				return (DEVFSADM_FAILURE);
2964*7c478bd9Sstevel@tonic-gate 			}
2965*7c478bd9Sstevel@tonic-gate 		} else {
2966*7c478bd9Sstevel@tonic-gate 			vprint(VERBOSE_MID, CREATING_LINK, devlink, contents);
2967*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_SUCCESS);
2968*7c478bd9Sstevel@tonic-gate 		}
2969*7c478bd9Sstevel@tonic-gate 	}
2970*7c478bd9Sstevel@tonic-gate 
2971*7c478bd9Sstevel@tonic-gate 	/*
2972*7c478bd9Sstevel@tonic-gate 	 * systems calls are expensive, so predict whether to readlink
2973*7c478bd9Sstevel@tonic-gate 	 * or symlink first, based on previous attempt
2974*7c478bd9Sstevel@tonic-gate 	 */
2975*7c478bd9Sstevel@tonic-gate 	if (prev_link_existed == FALSE) {
2976*7c478bd9Sstevel@tonic-gate 		try = CREATE_LINK;
2977*7c478bd9Sstevel@tonic-gate 	} else {
2978*7c478bd9Sstevel@tonic-gate 		try = READ_LINK;
2979*7c478bd9Sstevel@tonic-gate 	}
2980*7c478bd9Sstevel@tonic-gate 
2981*7c478bd9Sstevel@tonic-gate 	while (++max_tries <= 3) {
2982*7c478bd9Sstevel@tonic-gate 
2983*7c478bd9Sstevel@tonic-gate 		switch (try) {
2984*7c478bd9Sstevel@tonic-gate 		case  CREATE_LINK:
2985*7c478bd9Sstevel@tonic-gate 
2986*7c478bd9Sstevel@tonic-gate 			if (symlink(contents, devlink) == 0) {
2987*7c478bd9Sstevel@tonic-gate 				vprint(VERBOSE_MID, CREATING_LINK, devlink,
2988*7c478bd9Sstevel@tonic-gate 						contents);
2989*7c478bd9Sstevel@tonic-gate 				prev_link_existed = FALSE;
2990*7c478bd9Sstevel@tonic-gate 				/* link successfully created */
2991*7c478bd9Sstevel@tonic-gate 				*exists = TRUE;
2992*7c478bd9Sstevel@tonic-gate 				set_logindev_perms(devlink);
2993*7c478bd9Sstevel@tonic-gate 				return (DEVFSADM_SUCCESS);
2994*7c478bd9Sstevel@tonic-gate 			} else {
2995*7c478bd9Sstevel@tonic-gate 				switch (errno) {
2996*7c478bd9Sstevel@tonic-gate 
2997*7c478bd9Sstevel@tonic-gate 				case ENOENT:
2998*7c478bd9Sstevel@tonic-gate 					/* dirpath to node doesn't exist */
2999*7c478bd9Sstevel@tonic-gate 					hide = strrchr(devlink, '/');
3000*7c478bd9Sstevel@tonic-gate 					*hide = '\0';
3001*7c478bd9Sstevel@tonic-gate 					s_mkdirp(devlink, S_IRWXU|S_IRGRP|
3002*7c478bd9Sstevel@tonic-gate 						S_IXGRP|S_IROTH|S_IXOTH);
3003*7c478bd9Sstevel@tonic-gate 					*hide = '/';
3004*7c478bd9Sstevel@tonic-gate 					break;
3005*7c478bd9Sstevel@tonic-gate 				case EEXIST:
3006*7c478bd9Sstevel@tonic-gate 					try = READ_LINK;
3007*7c478bd9Sstevel@tonic-gate 					break;
3008*7c478bd9Sstevel@tonic-gate 				default:
3009*7c478bd9Sstevel@tonic-gate 					err_print(SYMLINK_FAILED, devlink,
3010*7c478bd9Sstevel@tonic-gate 						contents, strerror(errno));
3011*7c478bd9Sstevel@tonic-gate 					return (DEVFSADM_FAILURE);
3012*7c478bd9Sstevel@tonic-gate 				}
3013*7c478bd9Sstevel@tonic-gate 			}
3014*7c478bd9Sstevel@tonic-gate 			break;
3015*7c478bd9Sstevel@tonic-gate 
3016*7c478bd9Sstevel@tonic-gate 		case READ_LINK:
3017*7c478bd9Sstevel@tonic-gate 
3018*7c478bd9Sstevel@tonic-gate 			linksize = readlink(devlink, checkcontents, PATH_MAX);
3019*7c478bd9Sstevel@tonic-gate 			if (linksize >= 0) {
3020*7c478bd9Sstevel@tonic-gate 				checkcontents[linksize] = '\0';
3021*7c478bd9Sstevel@tonic-gate 				if (strcmp(checkcontents, contents) != 0) {
3022*7c478bd9Sstevel@tonic-gate 					s_unlink(devlink);
3023*7c478bd9Sstevel@tonic-gate 					vprint(VERBOSE_MID, REMOVING_LINK,
3024*7c478bd9Sstevel@tonic-gate 						devlink, checkcontents);
3025*7c478bd9Sstevel@tonic-gate 					try = CREATE_LINK;
3026*7c478bd9Sstevel@tonic-gate 				} else {
3027*7c478bd9Sstevel@tonic-gate 					prev_link_existed = TRUE;
3028*7c478bd9Sstevel@tonic-gate 					vprint(CHATTY_MID,
3029*7c478bd9Sstevel@tonic-gate 						"link exists and is correct:"
3030*7c478bd9Sstevel@tonic-gate 						" %s -> %s\n", devlink,
3031*7c478bd9Sstevel@tonic-gate 						contents);
3032*7c478bd9Sstevel@tonic-gate 					*exists = TRUE;
3033*7c478bd9Sstevel@tonic-gate 					/* failure in that the link existed */
3034*7c478bd9Sstevel@tonic-gate 					return (DEVFSADM_FAILURE);
3035*7c478bd9Sstevel@tonic-gate 				}
3036*7c478bd9Sstevel@tonic-gate 			} else {
3037*7c478bd9Sstevel@tonic-gate 				switch (errno) {
3038*7c478bd9Sstevel@tonic-gate 				case EINVAL:
3039*7c478bd9Sstevel@tonic-gate 					/* not a symlink, remove and create */
3040*7c478bd9Sstevel@tonic-gate 					s_unlink(devlink);
3041*7c478bd9Sstevel@tonic-gate 				default:
3042*7c478bd9Sstevel@tonic-gate 					/* maybe it didn't exist at all */
3043*7c478bd9Sstevel@tonic-gate 					try = CREATE_LINK;
3044*7c478bd9Sstevel@tonic-gate 					break;
3045*7c478bd9Sstevel@tonic-gate 				}
3046*7c478bd9Sstevel@tonic-gate 			}
3047*7c478bd9Sstevel@tonic-gate 			break;
3048*7c478bd9Sstevel@tonic-gate 		}
3049*7c478bd9Sstevel@tonic-gate 	}
3050*7c478bd9Sstevel@tonic-gate 	err_print(MAX_ATTEMPTS, devlink, contents);
3051*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_FAILURE);
3052*7c478bd9Sstevel@tonic-gate }
3053*7c478bd9Sstevel@tonic-gate 
3054*7c478bd9Sstevel@tonic-gate static void
3055*7c478bd9Sstevel@tonic-gate set_logindev_perms(char *devlink)
3056*7c478bd9Sstevel@tonic-gate {
3057*7c478bd9Sstevel@tonic-gate 	struct login_dev *newdev;
3058*7c478bd9Sstevel@tonic-gate 	struct passwd pwd, *resp;
3059*7c478bd9Sstevel@tonic-gate 	char pwd_buf[PATH_MAX];
3060*7c478bd9Sstevel@tonic-gate 	int rv;
3061*7c478bd9Sstevel@tonic-gate 	struct stat sb;
3062*7c478bd9Sstevel@tonic-gate 	char *devfs_path = NULL;
3063*7c478bd9Sstevel@tonic-gate 
3064*7c478bd9Sstevel@tonic-gate 	/*
3065*7c478bd9Sstevel@tonic-gate 	 * We only want logindev perms to be set when a device is
3066*7c478bd9Sstevel@tonic-gate 	 * hotplugged or an application requests synchronous creates.
3067*7c478bd9Sstevel@tonic-gate 	 * So we enable this only in daemon mode. In addition,
3068*7c478bd9Sstevel@tonic-gate 	 * login(1) only fixes the std. /dev dir. So we don't
3069*7c478bd9Sstevel@tonic-gate 	 * change perms if alternate root is set.
3070*7c478bd9Sstevel@tonic-gate 	 * login_dev_enable is TRUE only in these cases.
3071*7c478bd9Sstevel@tonic-gate 	 */
3072*7c478bd9Sstevel@tonic-gate 	if (login_dev_enable != TRUE)
3073*7c478bd9Sstevel@tonic-gate 		return;
3074*7c478bd9Sstevel@tonic-gate 
3075*7c478bd9Sstevel@tonic-gate 	/*
3076*7c478bd9Sstevel@tonic-gate 	 * Normally, /etc/logindevperm has few (8 - 10 entries) which
3077*7c478bd9Sstevel@tonic-gate 	 * may be regular expressions (globs were converted to RE).
3078*7c478bd9Sstevel@tonic-gate 	 * So just do a linear search through the list.
3079*7c478bd9Sstevel@tonic-gate 	 */
3080*7c478bd9Sstevel@tonic-gate 	for (newdev = login_dev_cache; newdev; newdev = newdev->ldev_next) {
3081*7c478bd9Sstevel@tonic-gate 		vprint(FILES_MID, "matching %s with %s\n", devlink,
3082*7c478bd9Sstevel@tonic-gate 		    newdev->ldev_device);
3083*7c478bd9Sstevel@tonic-gate 
3084*7c478bd9Sstevel@tonic-gate 		if (regexec(&newdev->ldev_device_regex, devlink, 0,
3085*7c478bd9Sstevel@tonic-gate 		    NULL, 0) == 0)  {
3086*7c478bd9Sstevel@tonic-gate 			vprint(FILES_MID, "matched %s with %s\n", devlink,
3087*7c478bd9Sstevel@tonic-gate 			    newdev->ldev_device);
3088*7c478bd9Sstevel@tonic-gate 			break;
3089*7c478bd9Sstevel@tonic-gate 		}
3090*7c478bd9Sstevel@tonic-gate 	}
3091*7c478bd9Sstevel@tonic-gate 
3092*7c478bd9Sstevel@tonic-gate 	if (newdev == NULL)
3093*7c478bd9Sstevel@tonic-gate 		return;
3094*7c478bd9Sstevel@tonic-gate 
3095*7c478bd9Sstevel@tonic-gate 	/*
3096*7c478bd9Sstevel@tonic-gate 	 * we have a match, now find the driver associated with this
3097*7c478bd9Sstevel@tonic-gate 	 * minor node using a snapshot on the physical path
3098*7c478bd9Sstevel@tonic-gate 	 */
3099*7c478bd9Sstevel@tonic-gate 	(void) resolve_link(devlink, NULL, NULL, &devfs_path, 0);
3100*7c478bd9Sstevel@tonic-gate 	if (devfs_path) {
3101*7c478bd9Sstevel@tonic-gate 		di_node_t node;
3102*7c478bd9Sstevel@tonic-gate 		char *drv = NULL;
3103*7c478bd9Sstevel@tonic-gate 		struct driver_list *list;
3104*7c478bd9Sstevel@tonic-gate 		char *p;
3105*7c478bd9Sstevel@tonic-gate 
3106*7c478bd9Sstevel@tonic-gate 		/* truncate on : so we can take a snapshot */
3107*7c478bd9Sstevel@tonic-gate 		(void) strcpy(pwd_buf, devfs_path);
3108*7c478bd9Sstevel@tonic-gate 		p = strrchr(pwd_buf, ':');
3109*7c478bd9Sstevel@tonic-gate 		if (p == NULL) {
3110*7c478bd9Sstevel@tonic-gate 			free(devfs_path);
3111*7c478bd9Sstevel@tonic-gate 			return;
3112*7c478bd9Sstevel@tonic-gate 		}
3113*7c478bd9Sstevel@tonic-gate 		*p = '\0';
3114*7c478bd9Sstevel@tonic-gate 
3115*7c478bd9Sstevel@tonic-gate 		vprint(FILES_MID, "link=%s->physpath=%s\n",
3116*7c478bd9Sstevel@tonic-gate 		    devlink, pwd_buf);
3117*7c478bd9Sstevel@tonic-gate 
3118*7c478bd9Sstevel@tonic-gate 		node = di_init(pwd_buf, DINFOMINOR);
3119*7c478bd9Sstevel@tonic-gate 
3120*7c478bd9Sstevel@tonic-gate 		if (node) {
3121*7c478bd9Sstevel@tonic-gate 			drv = di_driver_name(node);
3122*7c478bd9Sstevel@tonic-gate 
3123*7c478bd9Sstevel@tonic-gate 			if (drv) {
3124*7c478bd9Sstevel@tonic-gate 				vprint(FILES_MID, "%s: driver is %s\n",
3125*7c478bd9Sstevel@tonic-gate 				    devlink, drv);
3126*7c478bd9Sstevel@tonic-gate 			}
3127*7c478bd9Sstevel@tonic-gate 			di_fini(node);
3128*7c478bd9Sstevel@tonic-gate 		}
3129*7c478bd9Sstevel@tonic-gate 		/* search thru the driver list specified in logindevperm */
3130*7c478bd9Sstevel@tonic-gate 		list = newdev->ldev_driver_list;
3131*7c478bd9Sstevel@tonic-gate 		if ((drv != NULL) && (list != NULL)) {
3132*7c478bd9Sstevel@tonic-gate 			while (list) {
3133*7c478bd9Sstevel@tonic-gate 				if (strcmp(list->driver_name,
3134*7c478bd9Sstevel@tonic-gate 				    drv) == 0) {
3135*7c478bd9Sstevel@tonic-gate 					vprint(FILES_MID,
3136*7c478bd9Sstevel@tonic-gate 					    "driver %s match!\n", drv);
3137*7c478bd9Sstevel@tonic-gate 					break;
3138*7c478bd9Sstevel@tonic-gate 				}
3139*7c478bd9Sstevel@tonic-gate 				list = list->next;
3140*7c478bd9Sstevel@tonic-gate 			}
3141*7c478bd9Sstevel@tonic-gate 			if (list == NULL) {
3142*7c478bd9Sstevel@tonic-gate 				vprint(FILES_MID, "no driver match!\n");
3143*7c478bd9Sstevel@tonic-gate 				free(devfs_path);
3144*7c478bd9Sstevel@tonic-gate 				return;
3145*7c478bd9Sstevel@tonic-gate 			}
3146*7c478bd9Sstevel@tonic-gate 		}
3147*7c478bd9Sstevel@tonic-gate 		free(devfs_path);
3148*7c478bd9Sstevel@tonic-gate 	} else {
3149*7c478bd9Sstevel@tonic-gate 		return;
3150*7c478bd9Sstevel@tonic-gate 	}
3151*7c478bd9Sstevel@tonic-gate 
3152*7c478bd9Sstevel@tonic-gate 	vprint(FILES_MID, "changing permissions of %s\n", devlink);
3153*7c478bd9Sstevel@tonic-gate 
3154*7c478bd9Sstevel@tonic-gate 	/*
3155*7c478bd9Sstevel@tonic-gate 	 * We have a match. We now attempt to determine the
3156*7c478bd9Sstevel@tonic-gate 	 * owner and group of the console user.
3157*7c478bd9Sstevel@tonic-gate 	 *
3158*7c478bd9Sstevel@tonic-gate 	 * stat() the console device newdev->ldev_console
3159*7c478bd9Sstevel@tonic-gate 	 * which will always exist - it will have the right owner but
3160*7c478bd9Sstevel@tonic-gate 	 * not the right group. Use getpwuid_r() to determine group for this
3161*7c478bd9Sstevel@tonic-gate 	 * uid.
3162*7c478bd9Sstevel@tonic-gate 	 * Note, it is safe to use name service here since if name services
3163*7c478bd9Sstevel@tonic-gate 	 * are not available (during boot or in single-user mode), then
3164*7c478bd9Sstevel@tonic-gate 	 * console owner will be root and its gid can be found in
3165*7c478bd9Sstevel@tonic-gate 	 * local files.
3166*7c478bd9Sstevel@tonic-gate 	 */
3167*7c478bd9Sstevel@tonic-gate 	if (stat(newdev->ldev_console, &sb) == -1) {
3168*7c478bd9Sstevel@tonic-gate 		vprint(VERBOSE_MID, STAT_FAILED, newdev->ldev_console,
3169*7c478bd9Sstevel@tonic-gate 		    strerror(errno));
3170*7c478bd9Sstevel@tonic-gate 		return;
3171*7c478bd9Sstevel@tonic-gate 	}
3172*7c478bd9Sstevel@tonic-gate 
3173*7c478bd9Sstevel@tonic-gate 	resp = NULL;
3174*7c478bd9Sstevel@tonic-gate 	rv = getpwuid_r(sb.st_uid, &pwd, pwd_buf, sizeof (pwd_buf), &resp);
3175*7c478bd9Sstevel@tonic-gate 	if (rv || resp == NULL) {
3176*7c478bd9Sstevel@tonic-gate 		rv = rv ? rv : EINVAL;
3177*7c478bd9Sstevel@tonic-gate 		vprint(VERBOSE_MID, GID_FAILED, sb.st_uid,
3178*7c478bd9Sstevel@tonic-gate 		    strerror(rv));
3179*7c478bd9Sstevel@tonic-gate 		return;
3180*7c478bd9Sstevel@tonic-gate 	}
3181*7c478bd9Sstevel@tonic-gate 
3182*7c478bd9Sstevel@tonic-gate 	assert(&pwd == resp);
3183*7c478bd9Sstevel@tonic-gate 
3184*7c478bd9Sstevel@tonic-gate 	sb.st_gid = resp->pw_gid;
3185*7c478bd9Sstevel@tonic-gate 
3186*7c478bd9Sstevel@tonic-gate 	if (chmod(devlink, newdev->ldev_perms) == -1) {
3187*7c478bd9Sstevel@tonic-gate 		vprint(VERBOSE_MID, CHMOD_FAILED, devlink,
3188*7c478bd9Sstevel@tonic-gate 		    strerror(errno));
3189*7c478bd9Sstevel@tonic-gate 		return;
3190*7c478bd9Sstevel@tonic-gate 	}
3191*7c478bd9Sstevel@tonic-gate 
3192*7c478bd9Sstevel@tonic-gate 	if (chown(devlink, sb.st_uid, sb.st_gid)  == -1) {
3193*7c478bd9Sstevel@tonic-gate 		vprint(VERBOSE_MID, CHOWN_FAILED, devlink,
3194*7c478bd9Sstevel@tonic-gate 		    strerror(errno));
3195*7c478bd9Sstevel@tonic-gate 	}
3196*7c478bd9Sstevel@tonic-gate }
3197*7c478bd9Sstevel@tonic-gate 
3198*7c478bd9Sstevel@tonic-gate /*
3199*7c478bd9Sstevel@tonic-gate  * Reset /devices node with appropriate permissions and
3200*7c478bd9Sstevel@tonic-gate  * ownership as specified in /etc/minor_perm.
3201*7c478bd9Sstevel@tonic-gate  */
3202*7c478bd9Sstevel@tonic-gate static void
3203*7c478bd9Sstevel@tonic-gate reset_node_permissions(di_node_t node, di_minor_t minor)
3204*7c478bd9Sstevel@tonic-gate {
3205*7c478bd9Sstevel@tonic-gate 	int spectype;
3206*7c478bd9Sstevel@tonic-gate 	char phy_path[PATH_MAX + 1];
3207*7c478bd9Sstevel@tonic-gate 	mode_t mode;
3208*7c478bd9Sstevel@tonic-gate 	dev_t dev;
3209*7c478bd9Sstevel@tonic-gate 	uid_t uid;
3210*7c478bd9Sstevel@tonic-gate 	gid_t gid;
3211*7c478bd9Sstevel@tonic-gate 	struct stat sb;
3212*7c478bd9Sstevel@tonic-gate 	char *dev_path, *aminor = NULL;
3213*7c478bd9Sstevel@tonic-gate 
3214*7c478bd9Sstevel@tonic-gate 	/* lphy_path starts with / */
3215*7c478bd9Sstevel@tonic-gate 	if ((dev_path = di_devfs_path(node)) == NULL) {
3216*7c478bd9Sstevel@tonic-gate 		err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
3217*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
3218*7c478bd9Sstevel@tonic-gate 	}
3219*7c478bd9Sstevel@tonic-gate 	(void) strcpy(lphy_path, dev_path);
3220*7c478bd9Sstevel@tonic-gate 	di_devfs_path_free(dev_path);
3221*7c478bd9Sstevel@tonic-gate 
3222*7c478bd9Sstevel@tonic-gate 	(void) strcat(lphy_path, ":");
3223*7c478bd9Sstevel@tonic-gate 	if (di_minor_type(minor) == DDM_ALIAS) {
3224*7c478bd9Sstevel@tonic-gate 		char *driver;
3225*7c478bd9Sstevel@tonic-gate 		aminor = di_minor_name(minor);
3226*7c478bd9Sstevel@tonic-gate 		driver = di_driver_name(di_minor_devinfo(minor));
3227*7c478bd9Sstevel@tonic-gate 		(void) strcat(lphy_path, driver);
3228*7c478bd9Sstevel@tonic-gate 	} else
3229*7c478bd9Sstevel@tonic-gate 		(void) strcat(lphy_path, di_minor_name(minor));
3230*7c478bd9Sstevel@tonic-gate 
3231*7c478bd9Sstevel@tonic-gate 	(void) strcpy(phy_path, devices_dir);
3232*7c478bd9Sstevel@tonic-gate 	(void) strcat(phy_path, lphy_path);
3233*7c478bd9Sstevel@tonic-gate 
3234*7c478bd9Sstevel@tonic-gate 	lnode = node;
3235*7c478bd9Sstevel@tonic-gate 	lminor = minor;
3236*7c478bd9Sstevel@tonic-gate 
3237*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "reset_node_permissions: phy_path=%s lphy_path=%s\n",
3238*7c478bd9Sstevel@tonic-gate 			phy_path, lphy_path);
3239*7c478bd9Sstevel@tonic-gate 
3240*7c478bd9Sstevel@tonic-gate 	dev = di_minor_devt(minor);
3241*7c478bd9Sstevel@tonic-gate 	spectype = di_minor_spectype(minor); /* block or char */
3242*7c478bd9Sstevel@tonic-gate 
3243*7c478bd9Sstevel@tonic-gate 	getattr(phy_path, aminor, spectype, dev, &mode, &uid, &gid);
3244*7c478bd9Sstevel@tonic-gate 
3245*7c478bd9Sstevel@tonic-gate 	/*
3246*7c478bd9Sstevel@tonic-gate 	 * compare and set permissions and ownership
3247*7c478bd9Sstevel@tonic-gate 	 *
3248*7c478bd9Sstevel@tonic-gate 	 * Under devfs, a quick insertion and removal of USB devices
3249*7c478bd9Sstevel@tonic-gate 	 * would cause stat of physical path to fail. In this case,
3250*7c478bd9Sstevel@tonic-gate 	 * we emit a verbose message, but don't print errors.
3251*7c478bd9Sstevel@tonic-gate 	 */
3252*7c478bd9Sstevel@tonic-gate 	if ((stat(phy_path, &sb) == -1) || (sb.st_rdev != dev)) {
3253*7c478bd9Sstevel@tonic-gate 		vprint(VERBOSE_MID, NO_DEVFS_NODE, phy_path);
3254*7c478bd9Sstevel@tonic-gate 		return;
3255*7c478bd9Sstevel@tonic-gate 	}
3256*7c478bd9Sstevel@tonic-gate 
3257*7c478bd9Sstevel@tonic-gate 	/*
3258*7c478bd9Sstevel@tonic-gate 	 * If we are here for deactivating device allocation, set
3259*7c478bd9Sstevel@tonic-gate 	 * default permissions. Otherwise, set default permissions
3260*7c478bd9Sstevel@tonic-gate 	 * only if this is a new device because we want to preserve
3261*7c478bd9Sstevel@tonic-gate 	 * modified user permissions.
3262*7c478bd9Sstevel@tonic-gate 	 * Devfs indicates a new device by faking an access time
3263*7c478bd9Sstevel@tonic-gate 	 * of zero.
3264*7c478bd9Sstevel@tonic-gate 	 */
3265*7c478bd9Sstevel@tonic-gate 	if (sb.st_atime != 0) {
3266*7c478bd9Sstevel@tonic-gate 		int  i;
3267*7c478bd9Sstevel@tonic-gate 		char *nt;
3268*7c478bd9Sstevel@tonic-gate 
3269*7c478bd9Sstevel@tonic-gate 		if (devalloc_off == FALSE)
3270*7c478bd9Sstevel@tonic-gate 			return;
3271*7c478bd9Sstevel@tonic-gate 
3272*7c478bd9Sstevel@tonic-gate 		nt = di_minor_nodetype(minor);
3273*7c478bd9Sstevel@tonic-gate 		if (nt == NULL)
3274*7c478bd9Sstevel@tonic-gate 			return;
3275*7c478bd9Sstevel@tonic-gate 		for (i = 0; devalloc[i]; i++) {
3276*7c478bd9Sstevel@tonic-gate 			if (strcmp(nt, devalloc[i]) == 0)
3277*7c478bd9Sstevel@tonic-gate 				break;
3278*7c478bd9Sstevel@tonic-gate 		}
3279*7c478bd9Sstevel@tonic-gate 
3280*7c478bd9Sstevel@tonic-gate 		if (devalloc[i] == NULL)
3281*7c478bd9Sstevel@tonic-gate 			return;
3282*7c478bd9Sstevel@tonic-gate 
3283*7c478bd9Sstevel@tonic-gate 		/* One of the types recognized by devalloc, reset perms */
3284*7c478bd9Sstevel@tonic-gate 	}
3285*7c478bd9Sstevel@tonic-gate 
3286*7c478bd9Sstevel@tonic-gate 	if (file_mods == FALSE) {
3287*7c478bd9Sstevel@tonic-gate 		/* Nothing more to do if simulating */
3288*7c478bd9Sstevel@tonic-gate 		vprint(VERBOSE_MID, PERM_MSG, phy_path, uid, gid, mode);
3289*7c478bd9Sstevel@tonic-gate 		return;
3290*7c478bd9Sstevel@tonic-gate 	}
3291*7c478bd9Sstevel@tonic-gate 
3292*7c478bd9Sstevel@tonic-gate 	if (sb.st_mode != mode) {
3293*7c478bd9Sstevel@tonic-gate 		if (chmod(phy_path, mode) == -1)
3294*7c478bd9Sstevel@tonic-gate 			vprint(VERBOSE_MID, CHMOD_FAILED,
3295*7c478bd9Sstevel@tonic-gate 			    phy_path, strerror(errno));
3296*7c478bd9Sstevel@tonic-gate 	}
3297*7c478bd9Sstevel@tonic-gate 	if (sb.st_uid != uid || sb.st_gid != gid) {
3298*7c478bd9Sstevel@tonic-gate 		if (chown(phy_path, uid, gid) == -1)
3299*7c478bd9Sstevel@tonic-gate 			vprint(VERBOSE_MID, CHOWN_FAILED,
3300*7c478bd9Sstevel@tonic-gate 			    phy_path, strerror(errno));
3301*7c478bd9Sstevel@tonic-gate 	}
3302*7c478bd9Sstevel@tonic-gate 
3303*7c478bd9Sstevel@tonic-gate 	/* Report that we actually did something */
3304*7c478bd9Sstevel@tonic-gate 	vprint(VERBOSE_MID, PERM_MSG, phy_path, uid, gid, mode);
3305*7c478bd9Sstevel@tonic-gate }
3306*7c478bd9Sstevel@tonic-gate 
3307*7c478bd9Sstevel@tonic-gate /*
3308*7c478bd9Sstevel@tonic-gate  * Removes logical link and the minor node it refers to.  If file is a
3309*7c478bd9Sstevel@tonic-gate  * link, we recurse and try to remove the minor node (or link if path is
3310*7c478bd9Sstevel@tonic-gate  * a double link) that file's link contents refer to.
3311*7c478bd9Sstevel@tonic-gate  */
3312*7c478bd9Sstevel@tonic-gate static void
3313*7c478bd9Sstevel@tonic-gate devfsadm_rm_work(char *file, int recurse, int file_type)
3314*7c478bd9Sstevel@tonic-gate {
3315*7c478bd9Sstevel@tonic-gate 	char *fcn = "devfsadm_rm_work: ";
3316*7c478bd9Sstevel@tonic-gate 	int linksize;
3317*7c478bd9Sstevel@tonic-gate 	char contents[PATH_MAX + 1];
3318*7c478bd9Sstevel@tonic-gate 	char nextfile[PATH_MAX + 1];
3319*7c478bd9Sstevel@tonic-gate 	char newfile[PATH_MAX + 1];
3320*7c478bd9Sstevel@tonic-gate 	char *ptr;
3321*7c478bd9Sstevel@tonic-gate 
3322*7c478bd9Sstevel@tonic-gate 	vprint(REMOVE_MID, "%s%s\n", fcn, file);
3323*7c478bd9Sstevel@tonic-gate 
3324*7c478bd9Sstevel@tonic-gate 	/* TYPE_LINK split into multiple if's due to excessive indentations */
3325*7c478bd9Sstevel@tonic-gate 	if (file_type == TYPE_LINK) {
3326*7c478bd9Sstevel@tonic-gate 		(void) strcpy(newfile, dev_dir);
3327*7c478bd9Sstevel@tonic-gate 		(void) strcat(newfile, "/");
3328*7c478bd9Sstevel@tonic-gate 		(void) strcat(newfile, file);
3329*7c478bd9Sstevel@tonic-gate 	}
3330*7c478bd9Sstevel@tonic-gate 
3331*7c478bd9Sstevel@tonic-gate 	if ((file_type == TYPE_LINK) && (recurse == TRUE) &&
3332*7c478bd9Sstevel@tonic-gate 	    ((linksize = readlink(newfile, contents, PATH_MAX)) > 0)) {
3333*7c478bd9Sstevel@tonic-gate 		contents[linksize] = '\0';
3334*7c478bd9Sstevel@tonic-gate 
3335*7c478bd9Sstevel@tonic-gate 		if (is_minor_node(contents, &ptr) == DEVFSADM_TRUE) {
3336*7c478bd9Sstevel@tonic-gate 			devfsadm_rm_work(++ptr, FALSE, TYPE_DEVICES);
3337*7c478bd9Sstevel@tonic-gate 		} else {
3338*7c478bd9Sstevel@tonic-gate 			if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0) {
3339*7c478bd9Sstevel@tonic-gate 				devfsadm_rm_work(&contents[strlen(DEV) + 1],
3340*7c478bd9Sstevel@tonic-gate 							TRUE, TYPE_LINK);
3341*7c478bd9Sstevel@tonic-gate 			} else {
3342*7c478bd9Sstevel@tonic-gate 				if ((ptr = strrchr(file, '/')) != NULL) {
3343*7c478bd9Sstevel@tonic-gate 					*ptr = '\0';
3344*7c478bd9Sstevel@tonic-gate 					(void) strcpy(nextfile, file);
3345*7c478bd9Sstevel@tonic-gate 					*ptr = '/';
3346*7c478bd9Sstevel@tonic-gate 					(void) strcat(nextfile, "/");
3347*7c478bd9Sstevel@tonic-gate 				} else {
3348*7c478bd9Sstevel@tonic-gate 					(void) strcpy(nextfile, "");
3349*7c478bd9Sstevel@tonic-gate 				}
3350*7c478bd9Sstevel@tonic-gate 				(void) strcat(nextfile, contents);
3351*7c478bd9Sstevel@tonic-gate 				devfsadm_rm_work(nextfile, TRUE, TYPE_LINK);
3352*7c478bd9Sstevel@tonic-gate 			}
3353*7c478bd9Sstevel@tonic-gate 		}
3354*7c478bd9Sstevel@tonic-gate 	}
3355*7c478bd9Sstevel@tonic-gate 
3356*7c478bd9Sstevel@tonic-gate 	if (file_type == TYPE_LINK) {
3357*7c478bd9Sstevel@tonic-gate 		vprint(VERBOSE_MID, DEVFSADM_UNLINK, newfile);
3358*7c478bd9Sstevel@tonic-gate 		if (file_mods == TRUE) {
3359*7c478bd9Sstevel@tonic-gate 			rm_link_from_cache(file);
3360*7c478bd9Sstevel@tonic-gate 			s_unlink(newfile);
3361*7c478bd9Sstevel@tonic-gate 			rm_parent_dir_if_empty(newfile);
3362*7c478bd9Sstevel@tonic-gate 			invalidate_enumerate_cache();
3363*7c478bd9Sstevel@tonic-gate 			(void) di_devlink_rm_link(devlink_cache, file);
3364*7c478bd9Sstevel@tonic-gate 		}
3365*7c478bd9Sstevel@tonic-gate 	}
3366*7c478bd9Sstevel@tonic-gate 
3367*7c478bd9Sstevel@tonic-gate 	/*
3368*7c478bd9Sstevel@tonic-gate 	 * Note: we don't remove /devices entries because they are
3369*7c478bd9Sstevel@tonic-gate 	 *	covered by devfs.
3370*7c478bd9Sstevel@tonic-gate 	 */
3371*7c478bd9Sstevel@tonic-gate }
3372*7c478bd9Sstevel@tonic-gate 
3373*7c478bd9Sstevel@tonic-gate void
3374*7c478bd9Sstevel@tonic-gate devfsadm_rm_link(char *file)
3375*7c478bd9Sstevel@tonic-gate {
3376*7c478bd9Sstevel@tonic-gate 	devfsadm_rm_work(file, FALSE, TYPE_LINK);
3377*7c478bd9Sstevel@tonic-gate }
3378*7c478bd9Sstevel@tonic-gate 
3379*7c478bd9Sstevel@tonic-gate void
3380*7c478bd9Sstevel@tonic-gate devfsadm_rm_all(char *file)
3381*7c478bd9Sstevel@tonic-gate {
3382*7c478bd9Sstevel@tonic-gate 	devfsadm_rm_work(file, TRUE, TYPE_LINK);
3383*7c478bd9Sstevel@tonic-gate }
3384*7c478bd9Sstevel@tonic-gate 
3385*7c478bd9Sstevel@tonic-gate static void
3386*7c478bd9Sstevel@tonic-gate s_rmdir(char *path)
3387*7c478bd9Sstevel@tonic-gate {
3388*7c478bd9Sstevel@tonic-gate 	int	i;
3389*7c478bd9Sstevel@tonic-gate 	char	*rpath, *dir;
3390*7c478bd9Sstevel@tonic-gate 	const char *fcn = "s_rmdir";
3391*7c478bd9Sstevel@tonic-gate 
3392*7c478bd9Sstevel@tonic-gate 	/*
3393*7c478bd9Sstevel@tonic-gate 	 * Certain directories are created at install time by packages.
3394*7c478bd9Sstevel@tonic-gate 	 * Some of them (listed in packaged_dirs[]) are required by apps
3395*7c478bd9Sstevel@tonic-gate 	 * and need to be present even when empty.
3396*7c478bd9Sstevel@tonic-gate 	 */
3397*7c478bd9Sstevel@tonic-gate 	vprint(REMOVE_MID, "%s: checking if %s is packaged\n", fcn, path);
3398*7c478bd9Sstevel@tonic-gate 
3399*7c478bd9Sstevel@tonic-gate 	rpath = path + strlen(dev_dir) + 1;
3400*7c478bd9Sstevel@tonic-gate 
3401*7c478bd9Sstevel@tonic-gate 	for (i = 0; (dir = packaged_dirs[i]) != NULL; i++) {
3402*7c478bd9Sstevel@tonic-gate 		if (*rpath == *dir) {
3403*7c478bd9Sstevel@tonic-gate 			if (strcmp(rpath, dir) == 0) {
3404*7c478bd9Sstevel@tonic-gate 				vprint(REMOVE_MID, "%s: skipping packaged dir: "
3405*7c478bd9Sstevel@tonic-gate 				    "%s\n", fcn, path);
3406*7c478bd9Sstevel@tonic-gate 				return;
3407*7c478bd9Sstevel@tonic-gate 			}
3408*7c478bd9Sstevel@tonic-gate 		}
3409*7c478bd9Sstevel@tonic-gate 	}
3410*7c478bd9Sstevel@tonic-gate 
3411*7c478bd9Sstevel@tonic-gate 	(void) rmdir(path);
3412*7c478bd9Sstevel@tonic-gate }
3413*7c478bd9Sstevel@tonic-gate 
3414*7c478bd9Sstevel@tonic-gate /*
3415*7c478bd9Sstevel@tonic-gate  * Try to remove any empty directories up the tree.  It is assumed that
3416*7c478bd9Sstevel@tonic-gate  * pathname is a file that was removed, so start with its parent, and
3417*7c478bd9Sstevel@tonic-gate  * work up the tree.
3418*7c478bd9Sstevel@tonic-gate  */
3419*7c478bd9Sstevel@tonic-gate static void
3420*7c478bd9Sstevel@tonic-gate rm_parent_dir_if_empty(char *pathname)
3421*7c478bd9Sstevel@tonic-gate {
3422*7c478bd9Sstevel@tonic-gate 	char *ptr, path[PATH_MAX + 1];
3423*7c478bd9Sstevel@tonic-gate 	char *fcn = "rm_parent_dir_if_empty: ";
3424*7c478bd9Sstevel@tonic-gate 	struct dirent *entp;
3425*7c478bd9Sstevel@tonic-gate 	struct dirent *retp;
3426*7c478bd9Sstevel@tonic-gate 	DIR *dp;
3427*7c478bd9Sstevel@tonic-gate 
3428*7c478bd9Sstevel@tonic-gate 	vprint(REMOVE_MID, "%schecking %s if empty\n", fcn, pathname);
3429*7c478bd9Sstevel@tonic-gate 
3430*7c478bd9Sstevel@tonic-gate 	(void) strcpy(path, pathname);
3431*7c478bd9Sstevel@tonic-gate 
3432*7c478bd9Sstevel@tonic-gate 	entp = (struct dirent *)s_malloc(PATH_MAX + 1 +
3433*7c478bd9Sstevel@tonic-gate 					    sizeof (struct dirent));
3434*7c478bd9Sstevel@tonic-gate 
3435*7c478bd9Sstevel@tonic-gate 	/*
3436*7c478bd9Sstevel@tonic-gate 	 * ascend up the dir tree, deleting all empty dirs.
3437*7c478bd9Sstevel@tonic-gate 	 * Return immediately if a dir is not empty.
3438*7c478bd9Sstevel@tonic-gate 	 */
3439*7c478bd9Sstevel@tonic-gate 	for (;;) {
3440*7c478bd9Sstevel@tonic-gate 
3441*7c478bd9Sstevel@tonic-gate 		if ((ptr = strrchr(path, '/')) == NULL) {
3442*7c478bd9Sstevel@tonic-gate 			return;
3443*7c478bd9Sstevel@tonic-gate 		}
3444*7c478bd9Sstevel@tonic-gate 
3445*7c478bd9Sstevel@tonic-gate 		*ptr = '\0';
3446*7c478bd9Sstevel@tonic-gate 
3447*7c478bd9Sstevel@tonic-gate 		if ((dp = opendir(path)) == NULL) {
3448*7c478bd9Sstevel@tonic-gate 			err_print(OPENDIR_FAILED, path, strerror(errno));
3449*7c478bd9Sstevel@tonic-gate 			free(entp);
3450*7c478bd9Sstevel@tonic-gate 			return;
3451*7c478bd9Sstevel@tonic-gate 		}
3452*7c478bd9Sstevel@tonic-gate 
3453*7c478bd9Sstevel@tonic-gate 		while (readdir_r(dp, entp, &retp) == 0) {
3454*7c478bd9Sstevel@tonic-gate 
3455*7c478bd9Sstevel@tonic-gate 			if (retp == NULL) {
3456*7c478bd9Sstevel@tonic-gate 				vprint(REMOVE_MID, "%sremoving empty dir %s\n",
3457*7c478bd9Sstevel@tonic-gate 						fcn, path);
3458*7c478bd9Sstevel@tonic-gate 				s_rmdir(path);
3459*7c478bd9Sstevel@tonic-gate 				break;
3460*7c478bd9Sstevel@tonic-gate 			}
3461*7c478bd9Sstevel@tonic-gate 
3462*7c478bd9Sstevel@tonic-gate 			if (strcmp(entp->d_name, ".") == 0 ||
3463*7c478bd9Sstevel@tonic-gate 			    strcmp(entp->d_name, "..") == 0) {
3464*7c478bd9Sstevel@tonic-gate 				continue;
3465*7c478bd9Sstevel@tonic-gate 			}
3466*7c478bd9Sstevel@tonic-gate 
3467*7c478bd9Sstevel@tonic-gate 			/* some other file is here, so return */
3468*7c478bd9Sstevel@tonic-gate 			vprint(REMOVE_MID, "%sdir not empty: %s\n", fcn, path);
3469*7c478bd9Sstevel@tonic-gate 			free(entp);
3470*7c478bd9Sstevel@tonic-gate 			s_closedir(dp);
3471*7c478bd9Sstevel@tonic-gate 			return;
3472*7c478bd9Sstevel@tonic-gate 
3473*7c478bd9Sstevel@tonic-gate 		}
3474*7c478bd9Sstevel@tonic-gate 		s_closedir(dp);
3475*7c478bd9Sstevel@tonic-gate 	}
3476*7c478bd9Sstevel@tonic-gate }
3477*7c478bd9Sstevel@tonic-gate 
3478*7c478bd9Sstevel@tonic-gate /*
3479*7c478bd9Sstevel@tonic-gate  * This function and all the functions it calls below were added to
3480*7c478bd9Sstevel@tonic-gate  * handle the unique problem with world wide names (WWN).  The problem is
3481*7c478bd9Sstevel@tonic-gate  * that if a WWN device is moved to another address on the same controller
3482*7c478bd9Sstevel@tonic-gate  * its logical link will change, while the physical node remains the same.
3483*7c478bd9Sstevel@tonic-gate  * The result is that two logical links will point to the same physical path
3484*7c478bd9Sstevel@tonic-gate  * in /devices, the valid link and a stale link. This function will
3485*7c478bd9Sstevel@tonic-gate  * find all the stale nodes, though at a significant performance cost.
3486*7c478bd9Sstevel@tonic-gate  *
3487*7c478bd9Sstevel@tonic-gate  * Caching is used to increase performance.
3488*7c478bd9Sstevel@tonic-gate  * A cache will be built from disk if the cache tag doesn't already exist.
3489*7c478bd9Sstevel@tonic-gate  * The cache tag is a regular expression "dir_re", which selects a
3490*7c478bd9Sstevel@tonic-gate  * subset of disks to search from typically something like
3491*7c478bd9Sstevel@tonic-gate  * "dev/cXt[0-9]+d[0-9]+s[0-9]+".  After the cache is built, consistency must
3492*7c478bd9Sstevel@tonic-gate  * be maintained, so entries are added as new links are created, and removed
3493*7c478bd9Sstevel@tonic-gate  * as old links are deleted.  The whole cache is flushed if we are a daemon,
3494*7c478bd9Sstevel@tonic-gate  * and another devfsadm process ran in between.
3495*7c478bd9Sstevel@tonic-gate  *
3496*7c478bd9Sstevel@tonic-gate  * Once the cache is built, this function finds the cache which matches
3497*7c478bd9Sstevel@tonic-gate  * dir_re, and then it searches all links in that cache looking for
3498*7c478bd9Sstevel@tonic-gate  * any link whose contents match "valid_link_contents" with a corresponding link
3499*7c478bd9Sstevel@tonic-gate  * which does not match "valid_link".  Any such matches are stale and removed.
3500*7c478bd9Sstevel@tonic-gate  */
3501*7c478bd9Sstevel@tonic-gate void
3502*7c478bd9Sstevel@tonic-gate devfsadm_rm_stale_links(char *dir_re, char *valid_link, di_node_t node,
3503*7c478bd9Sstevel@tonic-gate 			di_minor_t minor)
3504*7c478bd9Sstevel@tonic-gate {
3505*7c478bd9Sstevel@tonic-gate 	link_t *link;
3506*7c478bd9Sstevel@tonic-gate 	linkhead_t *head;
3507*7c478bd9Sstevel@tonic-gate 	char phy_path[PATH_MAX + 1];
3508*7c478bd9Sstevel@tonic-gate 	char *valid_link_contents;
3509*7c478bd9Sstevel@tonic-gate 	char *dev_path;
3510*7c478bd9Sstevel@tonic-gate 	char rmlink[PATH_MAX + 1];
3511*7c478bd9Sstevel@tonic-gate 
3512*7c478bd9Sstevel@tonic-gate 	/*
3513*7c478bd9Sstevel@tonic-gate 	 * try to use devices path
3514*7c478bd9Sstevel@tonic-gate 	 */
3515*7c478bd9Sstevel@tonic-gate 	if ((node == lnode) && (minor == lminor)) {
3516*7c478bd9Sstevel@tonic-gate 		valid_link_contents = lphy_path;
3517*7c478bd9Sstevel@tonic-gate 	} else {
3518*7c478bd9Sstevel@tonic-gate 		if ((dev_path = di_devfs_path(node)) == NULL) {
3519*7c478bd9Sstevel@tonic-gate 			err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
3520*7c478bd9Sstevel@tonic-gate 			devfsadm_exit(1);
3521*7c478bd9Sstevel@tonic-gate 		}
3522*7c478bd9Sstevel@tonic-gate 		(void) strcpy(phy_path, dev_path);
3523*7c478bd9Sstevel@tonic-gate 		di_devfs_path_free(dev_path);
3524*7c478bd9Sstevel@tonic-gate 
3525*7c478bd9Sstevel@tonic-gate 		(void) strcat(phy_path, ":");
3526*7c478bd9Sstevel@tonic-gate 		(void) strcat(phy_path, di_minor_name(minor));
3527*7c478bd9Sstevel@tonic-gate 		valid_link_contents = phy_path;
3528*7c478bd9Sstevel@tonic-gate 	}
3529*7c478bd9Sstevel@tonic-gate 
3530*7c478bd9Sstevel@tonic-gate 	/*
3531*7c478bd9Sstevel@tonic-gate 	 * As an optimization, check to make sure the corresponding
3532*7c478bd9Sstevel@tonic-gate 	 * devlink was just created before continuing.
3533*7c478bd9Sstevel@tonic-gate 	 */
3534*7c478bd9Sstevel@tonic-gate 
3535*7c478bd9Sstevel@tonic-gate 	if (linknew == FALSE) {
3536*7c478bd9Sstevel@tonic-gate 		return;
3537*7c478bd9Sstevel@tonic-gate 	}
3538*7c478bd9Sstevel@tonic-gate 
3539*7c478bd9Sstevel@tonic-gate 	head = get_cached_links(dir_re);
3540*7c478bd9Sstevel@tonic-gate 
3541*7c478bd9Sstevel@tonic-gate 	assert(head->nextlink == NULL);
3542*7c478bd9Sstevel@tonic-gate 
3543*7c478bd9Sstevel@tonic-gate 	for (link = head->link; link != NULL; link = head->nextlink) {
3544*7c478bd9Sstevel@tonic-gate 		/*
3545*7c478bd9Sstevel@tonic-gate 		 * See hot_cleanup() for why we do this
3546*7c478bd9Sstevel@tonic-gate 		 */
3547*7c478bd9Sstevel@tonic-gate 		head->nextlink = link->next;
3548*7c478bd9Sstevel@tonic-gate 		if ((strcmp(link->contents, valid_link_contents) == 0) &&
3549*7c478bd9Sstevel@tonic-gate 		    (strcmp(link->devlink, valid_link) != 0)) {
3550*7c478bd9Sstevel@tonic-gate 			vprint(CHATTY_MID, "removing %s -> %s\n"
3551*7c478bd9Sstevel@tonic-gate 				"valid link is: %s -> %s\n",
3552*7c478bd9Sstevel@tonic-gate 				link->devlink, link->contents,
3553*7c478bd9Sstevel@tonic-gate 				valid_link, valid_link_contents);
3554*7c478bd9Sstevel@tonic-gate 			/*
3555*7c478bd9Sstevel@tonic-gate 			 * Use a copy of the cached link name as the
3556*7c478bd9Sstevel@tonic-gate 			 * cache entry will go away during link removal
3557*7c478bd9Sstevel@tonic-gate 			 */
3558*7c478bd9Sstevel@tonic-gate 			(void) snprintf(rmlink, sizeof (rmlink), "%s",
3559*7c478bd9Sstevel@tonic-gate 			    link->devlink);
3560*7c478bd9Sstevel@tonic-gate 			devfsadm_rm_link(rmlink);
3561*7c478bd9Sstevel@tonic-gate 		}
3562*7c478bd9Sstevel@tonic-gate 	}
3563*7c478bd9Sstevel@tonic-gate }
3564*7c478bd9Sstevel@tonic-gate 
3565*7c478bd9Sstevel@tonic-gate /*
3566*7c478bd9Sstevel@tonic-gate  * Return previously created cache, or create cache.
3567*7c478bd9Sstevel@tonic-gate  */
3568*7c478bd9Sstevel@tonic-gate static linkhead_t *
3569*7c478bd9Sstevel@tonic-gate get_cached_links(char *dir_re)
3570*7c478bd9Sstevel@tonic-gate {
3571*7c478bd9Sstevel@tonic-gate 	recurse_dev_t rd;
3572*7c478bd9Sstevel@tonic-gate 	linkhead_t *linkhead;
3573*7c478bd9Sstevel@tonic-gate 	int n;
3574*7c478bd9Sstevel@tonic-gate 
3575*7c478bd9Sstevel@tonic-gate 	vprint(BUILDCACHE_MID, "get_cached_links: %s\n", dir_re);
3576*7c478bd9Sstevel@tonic-gate 
3577*7c478bd9Sstevel@tonic-gate 	for (linkhead = headlinkhead; linkhead != NULL;
3578*7c478bd9Sstevel@tonic-gate 		linkhead = linkhead->nexthead) {
3579*7c478bd9Sstevel@tonic-gate 		if (strcmp(linkhead->dir_re, dir_re) == 0) {
3580*7c478bd9Sstevel@tonic-gate 			return (linkhead);
3581*7c478bd9Sstevel@tonic-gate 		}
3582*7c478bd9Sstevel@tonic-gate 	}
3583*7c478bd9Sstevel@tonic-gate 
3584*7c478bd9Sstevel@tonic-gate 	/*
3585*7c478bd9Sstevel@tonic-gate 	 * This tag is not in cache, so add it, along with all its
3586*7c478bd9Sstevel@tonic-gate 	 * matching /dev entries.  This is the only time we go to disk.
3587*7c478bd9Sstevel@tonic-gate 	 */
3588*7c478bd9Sstevel@tonic-gate 	linkhead = s_malloc(sizeof (linkhead_t));
3589*7c478bd9Sstevel@tonic-gate 	linkhead->nexthead = headlinkhead;
3590*7c478bd9Sstevel@tonic-gate 	headlinkhead = linkhead;
3591*7c478bd9Sstevel@tonic-gate 	linkhead->dir_re = s_strdup(dir_re);
3592*7c478bd9Sstevel@tonic-gate 
3593*7c478bd9Sstevel@tonic-gate 	if ((n = regcomp(&(linkhead->dir_re_compiled), dir_re,
3594*7c478bd9Sstevel@tonic-gate 				REG_EXTENDED)) != 0) {
3595*7c478bd9Sstevel@tonic-gate 		err_print(REGCOMP_FAILED,  dir_re, n);
3596*7c478bd9Sstevel@tonic-gate 	}
3597*7c478bd9Sstevel@tonic-gate 
3598*7c478bd9Sstevel@tonic-gate 	linkhead->nextlink = NULL;
3599*7c478bd9Sstevel@tonic-gate 	linkhead->link = NULL;
3600*7c478bd9Sstevel@tonic-gate 
3601*7c478bd9Sstevel@tonic-gate 	rd.fcn = build_devlink_list;
3602*7c478bd9Sstevel@tonic-gate 	rd.data = (void *)linkhead;
3603*7c478bd9Sstevel@tonic-gate 
3604*7c478bd9Sstevel@tonic-gate 	vprint(BUILDCACHE_MID, "get_cached_links: calling recurse_dev_re\n");
3605*7c478bd9Sstevel@tonic-gate 
3606*7c478bd9Sstevel@tonic-gate 	/* call build_devlink_list for each directory in the dir_re RE */
3607*7c478bd9Sstevel@tonic-gate 	if (dir_re[0] == '/') {
3608*7c478bd9Sstevel@tonic-gate 		recurse_dev_re("/", &dir_re[1], &rd);
3609*7c478bd9Sstevel@tonic-gate 	} else {
3610*7c478bd9Sstevel@tonic-gate 		recurse_dev_re(dev_dir, dir_re, &rd);
3611*7c478bd9Sstevel@tonic-gate 	}
3612*7c478bd9Sstevel@tonic-gate 
3613*7c478bd9Sstevel@tonic-gate 	return (linkhead);
3614*7c478bd9Sstevel@tonic-gate }
3615*7c478bd9Sstevel@tonic-gate 
3616*7c478bd9Sstevel@tonic-gate static void
3617*7c478bd9Sstevel@tonic-gate build_devlink_list(char *devlink, void *data)
3618*7c478bd9Sstevel@tonic-gate {
3619*7c478bd9Sstevel@tonic-gate 	char *fcn = "build_devlink_list: ";
3620*7c478bd9Sstevel@tonic-gate 	char *ptr;
3621*7c478bd9Sstevel@tonic-gate 	char *r_contents;
3622*7c478bd9Sstevel@tonic-gate 	char *r_devlink;
3623*7c478bd9Sstevel@tonic-gate 	char contents[PATH_MAX + 1];
3624*7c478bd9Sstevel@tonic-gate 	char newlink[PATH_MAX + 1];
3625*7c478bd9Sstevel@tonic-gate 	char stage_link[PATH_MAX + 1];
3626*7c478bd9Sstevel@tonic-gate 	int linksize;
3627*7c478bd9Sstevel@tonic-gate 	linkhead_t *linkhead = (linkhead_t *)data;
3628*7c478bd9Sstevel@tonic-gate 	link_t *link;
3629*7c478bd9Sstevel@tonic-gate 	int i = 0;
3630*7c478bd9Sstevel@tonic-gate 
3631*7c478bd9Sstevel@tonic-gate 	vprint(BUILDCACHE_MID, "%scheck_link: %s\n", fcn, devlink);
3632*7c478bd9Sstevel@tonic-gate 
3633*7c478bd9Sstevel@tonic-gate 	(void) strcpy(newlink, devlink);
3634*7c478bd9Sstevel@tonic-gate 
3635*7c478bd9Sstevel@tonic-gate 	do {
3636*7c478bd9Sstevel@tonic-gate 		linksize = readlink(newlink, contents, PATH_MAX);
3637*7c478bd9Sstevel@tonic-gate 		if (linksize <= 0) {
3638*7c478bd9Sstevel@tonic-gate 			/*
3639*7c478bd9Sstevel@tonic-gate 			 * The first pass through the do loop we may readlink()
3640*7c478bd9Sstevel@tonic-gate 			 * non-symlink files(EINVAL) from false regexec matches.
3641*7c478bd9Sstevel@tonic-gate 			 * Suppress error messages in those cases or if the link
3642*7c478bd9Sstevel@tonic-gate 			 * content is the empty string.
3643*7c478bd9Sstevel@tonic-gate 			 */
3644*7c478bd9Sstevel@tonic-gate 			if (linksize < 0 && (i || errno != EINVAL))
3645*7c478bd9Sstevel@tonic-gate 				err_print(READLINK_FAILED, "build_devlink_list",
3646*7c478bd9Sstevel@tonic-gate 				    newlink, strerror(errno));
3647*7c478bd9Sstevel@tonic-gate 			return;
3648*7c478bd9Sstevel@tonic-gate 		}
3649*7c478bd9Sstevel@tonic-gate 		contents[linksize] = '\0';
3650*7c478bd9Sstevel@tonic-gate 		i = 1;
3651*7c478bd9Sstevel@tonic-gate 
3652*7c478bd9Sstevel@tonic-gate 		if (is_minor_node(contents, &r_contents) == DEVFSADM_FALSE) {
3653*7c478bd9Sstevel@tonic-gate 			/*
3654*7c478bd9Sstevel@tonic-gate 			 * assume that link contents is really a pointer to
3655*7c478bd9Sstevel@tonic-gate 			 * another link, so recurse and read its link contents.
3656*7c478bd9Sstevel@tonic-gate 			 *
3657*7c478bd9Sstevel@tonic-gate 			 * some link contents are absolute:
3658*7c478bd9Sstevel@tonic-gate 			 *	/dev/audio -> /dev/sound/0
3659*7c478bd9Sstevel@tonic-gate 			 */
3660*7c478bd9Sstevel@tonic-gate 			if (strncmp(contents, DEV "/",
3661*7c478bd9Sstevel@tonic-gate 				strlen(DEV) + strlen("/")) != 0) {
3662*7c478bd9Sstevel@tonic-gate 
3663*7c478bd9Sstevel@tonic-gate 				if ((ptr = strrchr(newlink, '/')) == NULL) {
3664*7c478bd9Sstevel@tonic-gate 					vprint(REMOVE_MID, "%s%s -> %s invalid "
3665*7c478bd9Sstevel@tonic-gate 						"link. missing '/'\n", fcn,
3666*7c478bd9Sstevel@tonic-gate 						newlink, contents);
3667*7c478bd9Sstevel@tonic-gate 						return;
3668*7c478bd9Sstevel@tonic-gate 				}
3669*7c478bd9Sstevel@tonic-gate 				*ptr = '\0';
3670*7c478bd9Sstevel@tonic-gate 				(void) strcpy(stage_link, newlink);
3671*7c478bd9Sstevel@tonic-gate 				*ptr = '/';
3672*7c478bd9Sstevel@tonic-gate 				(void) strcat(stage_link, "/");
3673*7c478bd9Sstevel@tonic-gate 				(void) strcat(stage_link, contents);
3674*7c478bd9Sstevel@tonic-gate 				(void) strcpy(newlink, stage_link);
3675*7c478bd9Sstevel@tonic-gate 			} else {
3676*7c478bd9Sstevel@tonic-gate 				(void) strcpy(newlink, dev_dir);
3677*7c478bd9Sstevel@tonic-gate 				(void) strcat(newlink, "/");
3678*7c478bd9Sstevel@tonic-gate 				(void) strcat(newlink,
3679*7c478bd9Sstevel@tonic-gate 					&contents[strlen(DEV) + strlen("/")]);
3680*7c478bd9Sstevel@tonic-gate 			}
3681*7c478bd9Sstevel@tonic-gate 
3682*7c478bd9Sstevel@tonic-gate 		} else {
3683*7c478bd9Sstevel@tonic-gate 			newlink[0] = '\0';
3684*7c478bd9Sstevel@tonic-gate 		}
3685*7c478bd9Sstevel@tonic-gate 	} while (newlink[0] != '\0');
3686*7c478bd9Sstevel@tonic-gate 
3687*7c478bd9Sstevel@tonic-gate 	if (strncmp(devlink, dev_dir, strlen(dev_dir)) != 0) {
3688*7c478bd9Sstevel@tonic-gate 		vprint(BUILDCACHE_MID, "%sinvalid link: %s\n", fcn, devlink);
3689*7c478bd9Sstevel@tonic-gate 		return;
3690*7c478bd9Sstevel@tonic-gate 	}
3691*7c478bd9Sstevel@tonic-gate 
3692*7c478bd9Sstevel@tonic-gate 	r_devlink = devlink + strlen(dev_dir);
3693*7c478bd9Sstevel@tonic-gate 
3694*7c478bd9Sstevel@tonic-gate 	if (r_devlink[0] != '/')
3695*7c478bd9Sstevel@tonic-gate 		return;
3696*7c478bd9Sstevel@tonic-gate 
3697*7c478bd9Sstevel@tonic-gate 	link = s_malloc(sizeof (link_t));
3698*7c478bd9Sstevel@tonic-gate 
3699*7c478bd9Sstevel@tonic-gate 	/* don't store the '/' after rootdir/dev */
3700*7c478bd9Sstevel@tonic-gate 	r_devlink += 1;
3701*7c478bd9Sstevel@tonic-gate 
3702*7c478bd9Sstevel@tonic-gate 	vprint(BUILDCACHE_MID, "%scaching link: %s\n", fcn, r_devlink);
3703*7c478bd9Sstevel@tonic-gate 	link->devlink = s_strdup(r_devlink);
3704*7c478bd9Sstevel@tonic-gate 
3705*7c478bd9Sstevel@tonic-gate 	link->contents = s_strdup(r_contents);
3706*7c478bd9Sstevel@tonic-gate 
3707*7c478bd9Sstevel@tonic-gate 	link->next = linkhead->link;
3708*7c478bd9Sstevel@tonic-gate 	linkhead->link = link;
3709*7c478bd9Sstevel@tonic-gate }
3710*7c478bd9Sstevel@tonic-gate 
3711*7c478bd9Sstevel@tonic-gate /*
3712*7c478bd9Sstevel@tonic-gate  * to be consistent, devlink must not begin with / and must be
3713*7c478bd9Sstevel@tonic-gate  * relative to /dev/, whereas physpath must contain / and be
3714*7c478bd9Sstevel@tonic-gate  * relative to /devices.
3715*7c478bd9Sstevel@tonic-gate  */
3716*7c478bd9Sstevel@tonic-gate static void
3717*7c478bd9Sstevel@tonic-gate add_link_to_cache(char *devlink, char *physpath)
3718*7c478bd9Sstevel@tonic-gate {
3719*7c478bd9Sstevel@tonic-gate 	linkhead_t *linkhead;
3720*7c478bd9Sstevel@tonic-gate 	link_t *link;
3721*7c478bd9Sstevel@tonic-gate 	int added = 0;
3722*7c478bd9Sstevel@tonic-gate 
3723*7c478bd9Sstevel@tonic-gate 	if (file_mods == FALSE) {
3724*7c478bd9Sstevel@tonic-gate 		return;
3725*7c478bd9Sstevel@tonic-gate 	}
3726*7c478bd9Sstevel@tonic-gate 
3727*7c478bd9Sstevel@tonic-gate 	vprint(CACHE_MID, "add_link_to_cache: %s -> %s ",
3728*7c478bd9Sstevel@tonic-gate 				devlink, physpath);
3729*7c478bd9Sstevel@tonic-gate 
3730*7c478bd9Sstevel@tonic-gate 	for (linkhead = headlinkhead; linkhead != NULL;
3731*7c478bd9Sstevel@tonic-gate 		linkhead = linkhead->nexthead) {
3732*7c478bd9Sstevel@tonic-gate 		if (regexec(&(linkhead->dir_re_compiled), devlink, 0, NULL,
3733*7c478bd9Sstevel@tonic-gate 			0) == 0) {
3734*7c478bd9Sstevel@tonic-gate 			added++;
3735*7c478bd9Sstevel@tonic-gate 			link = s_malloc(sizeof (link_t));
3736*7c478bd9Sstevel@tonic-gate 			link->devlink = s_strdup(devlink);
3737*7c478bd9Sstevel@tonic-gate 			link->contents = s_strdup(physpath);
3738*7c478bd9Sstevel@tonic-gate 			link->next = linkhead->link;
3739*7c478bd9Sstevel@tonic-gate 			linkhead->link = link;
3740*7c478bd9Sstevel@tonic-gate 		}
3741*7c478bd9Sstevel@tonic-gate 	}
3742*7c478bd9Sstevel@tonic-gate 
3743*7c478bd9Sstevel@tonic-gate 	vprint(CACHE_MID,
3744*7c478bd9Sstevel@tonic-gate 		" %d %s\n", added, added == 0 ? "NOT ADDED" : "ADDED");
3745*7c478bd9Sstevel@tonic-gate }
3746*7c478bd9Sstevel@tonic-gate 
3747*7c478bd9Sstevel@tonic-gate /*
3748*7c478bd9Sstevel@tonic-gate  * Remove devlink from cache.  Devlink must be relative to /dev/ and not start
3749*7c478bd9Sstevel@tonic-gate  * with /.
3750*7c478bd9Sstevel@tonic-gate  */
3751*7c478bd9Sstevel@tonic-gate static void
3752*7c478bd9Sstevel@tonic-gate rm_link_from_cache(char *devlink)
3753*7c478bd9Sstevel@tonic-gate {
3754*7c478bd9Sstevel@tonic-gate 	linkhead_t *linkhead;
3755*7c478bd9Sstevel@tonic-gate 	link_t **linkp;
3756*7c478bd9Sstevel@tonic-gate 	link_t *save;
3757*7c478bd9Sstevel@tonic-gate 
3758*7c478bd9Sstevel@tonic-gate 	vprint(CACHE_MID, "rm_link_from_cache enter: %s\n", devlink);
3759*7c478bd9Sstevel@tonic-gate 
3760*7c478bd9Sstevel@tonic-gate 	for (linkhead = headlinkhead; linkhead != NULL;
3761*7c478bd9Sstevel@tonic-gate 	    linkhead = linkhead->nexthead) {
3762*7c478bd9Sstevel@tonic-gate 		if (regexec(&(linkhead->dir_re_compiled), devlink, 0, NULL,
3763*7c478bd9Sstevel@tonic-gate 			0) == 0) {
3764*7c478bd9Sstevel@tonic-gate 
3765*7c478bd9Sstevel@tonic-gate 			for (linkp = &(linkhead->link); *linkp != NULL; ) {
3766*7c478bd9Sstevel@tonic-gate 				if ((strcmp((*linkp)->devlink, devlink) == 0)) {
3767*7c478bd9Sstevel@tonic-gate 					save = *linkp;
3768*7c478bd9Sstevel@tonic-gate 					*linkp = (*linkp)->next;
3769*7c478bd9Sstevel@tonic-gate 					/*
3770*7c478bd9Sstevel@tonic-gate 					 * We are removing our caller's
3771*7c478bd9Sstevel@tonic-gate 					 * "next" link. Update the nextlink
3772*7c478bd9Sstevel@tonic-gate 					 * field in the head so that our
3773*7c478bd9Sstevel@tonic-gate 					 * callers accesses the next valid
3774*7c478bd9Sstevel@tonic-gate 					 * link
3775*7c478bd9Sstevel@tonic-gate 					 */
3776*7c478bd9Sstevel@tonic-gate 					if (linkhead->nextlink == save)
3777*7c478bd9Sstevel@tonic-gate 						linkhead->nextlink = *linkp;
3778*7c478bd9Sstevel@tonic-gate 					free(save->devlink);
3779*7c478bd9Sstevel@tonic-gate 					free(save->contents);
3780*7c478bd9Sstevel@tonic-gate 					free(save);
3781*7c478bd9Sstevel@tonic-gate 					vprint(CACHE_MID, " %s FREED FROM "
3782*7c478bd9Sstevel@tonic-gate 						"CACHE\n", devlink);
3783*7c478bd9Sstevel@tonic-gate 				} else {
3784*7c478bd9Sstevel@tonic-gate 					linkp = &((*linkp)->next);
3785*7c478bd9Sstevel@tonic-gate 				}
3786*7c478bd9Sstevel@tonic-gate 			}
3787*7c478bd9Sstevel@tonic-gate 		}
3788*7c478bd9Sstevel@tonic-gate 	}
3789*7c478bd9Sstevel@tonic-gate }
3790*7c478bd9Sstevel@tonic-gate 
3791*7c478bd9Sstevel@tonic-gate static void
3792*7c478bd9Sstevel@tonic-gate rm_all_links_from_cache()
3793*7c478bd9Sstevel@tonic-gate {
3794*7c478bd9Sstevel@tonic-gate 	linkhead_t *linkhead;
3795*7c478bd9Sstevel@tonic-gate 	linkhead_t *nextlinkhead;
3796*7c478bd9Sstevel@tonic-gate 	link_t *link;
3797*7c478bd9Sstevel@tonic-gate 	link_t *nextlink;
3798*7c478bd9Sstevel@tonic-gate 
3799*7c478bd9Sstevel@tonic-gate 	vprint(CACHE_MID, "rm_all_links_from_cache\n");
3800*7c478bd9Sstevel@tonic-gate 
3801*7c478bd9Sstevel@tonic-gate 	for (linkhead = headlinkhead; linkhead != NULL;
3802*7c478bd9Sstevel@tonic-gate 		linkhead = nextlinkhead) {
3803*7c478bd9Sstevel@tonic-gate 
3804*7c478bd9Sstevel@tonic-gate 		nextlinkhead = linkhead->nexthead;
3805*7c478bd9Sstevel@tonic-gate 		assert(linkhead->nextlink == NULL);
3806*7c478bd9Sstevel@tonic-gate 		for (link = linkhead->link; link != NULL; link = nextlink) {
3807*7c478bd9Sstevel@tonic-gate 			nextlink = link->next;
3808*7c478bd9Sstevel@tonic-gate 			free(link->devlink);
3809*7c478bd9Sstevel@tonic-gate 			free(link->contents);
3810*7c478bd9Sstevel@tonic-gate 			free(link);
3811*7c478bd9Sstevel@tonic-gate 		}
3812*7c478bd9Sstevel@tonic-gate 		regfree(&(linkhead->dir_re_compiled));
3813*7c478bd9Sstevel@tonic-gate 		free(linkhead->dir_re);
3814*7c478bd9Sstevel@tonic-gate 		free(linkhead);
3815*7c478bd9Sstevel@tonic-gate 	}
3816*7c478bd9Sstevel@tonic-gate 	headlinkhead = NULL;
3817*7c478bd9Sstevel@tonic-gate }
3818*7c478bd9Sstevel@tonic-gate 
3819*7c478bd9Sstevel@tonic-gate /*
3820*7c478bd9Sstevel@tonic-gate  * Called when the kernel has modified the incore path_to_inst data.  This
3821*7c478bd9Sstevel@tonic-gate  * function will schedule a flush of the data to the filesystem.
3822*7c478bd9Sstevel@tonic-gate  */
3823*7c478bd9Sstevel@tonic-gate static void
3824*7c478bd9Sstevel@tonic-gate devfs_instance_mod(void)
3825*7c478bd9Sstevel@tonic-gate {
3826*7c478bd9Sstevel@tonic-gate 	char *fcn = "devfs_instance_mod: ";
3827*7c478bd9Sstevel@tonic-gate 	vprint(PATH2INST_MID, "%senter\n", fcn);
3828*7c478bd9Sstevel@tonic-gate 
3829*7c478bd9Sstevel@tonic-gate 	/* signal instance thread */
3830*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(&count_lock);
3831*7c478bd9Sstevel@tonic-gate 	inst_count++;
3832*7c478bd9Sstevel@tonic-gate 	(void) cond_signal(&cv);
3833*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(&count_lock);
3834*7c478bd9Sstevel@tonic-gate }
3835*7c478bd9Sstevel@tonic-gate 
3836*7c478bd9Sstevel@tonic-gate static void
3837*7c478bd9Sstevel@tonic-gate instance_flush_thread(void)
3838*7c478bd9Sstevel@tonic-gate {
3839*7c478bd9Sstevel@tonic-gate 	int i;
3840*7c478bd9Sstevel@tonic-gate 	int idle;
3841*7c478bd9Sstevel@tonic-gate 
3842*7c478bd9Sstevel@tonic-gate 	for (;;) {
3843*7c478bd9Sstevel@tonic-gate 
3844*7c478bd9Sstevel@tonic-gate 		(void) mutex_lock(&count_lock);
3845*7c478bd9Sstevel@tonic-gate 		while (inst_count == 0) {
3846*7c478bd9Sstevel@tonic-gate 			(void) cond_wait(&cv, &count_lock);
3847*7c478bd9Sstevel@tonic-gate 		}
3848*7c478bd9Sstevel@tonic-gate 		inst_count = 0;
3849*7c478bd9Sstevel@tonic-gate 
3850*7c478bd9Sstevel@tonic-gate 		vprint(PATH2INST_MID, "signaled to flush path_to_inst."
3851*7c478bd9Sstevel@tonic-gate 			" Enter delay loop\n");
3852*7c478bd9Sstevel@tonic-gate 		/*
3853*7c478bd9Sstevel@tonic-gate 		 * Wait MAX_IDLE_DELAY seconds after getting the last flush
3854*7c478bd9Sstevel@tonic-gate 		 * path_to_inst event before invoking a flush, but never wait
3855*7c478bd9Sstevel@tonic-gate 		 * more than MAX_DELAY seconds after getting the first event.
3856*7c478bd9Sstevel@tonic-gate 		 */
3857*7c478bd9Sstevel@tonic-gate 		for (idle = 0, i = 0; i < MAX_DELAY; i++) {
3858*7c478bd9Sstevel@tonic-gate 
3859*7c478bd9Sstevel@tonic-gate 			(void) mutex_unlock(&count_lock);
3860*7c478bd9Sstevel@tonic-gate 			(void) sleep(1);
3861*7c478bd9Sstevel@tonic-gate 			(void) mutex_lock(&count_lock);
3862*7c478bd9Sstevel@tonic-gate 
3863*7c478bd9Sstevel@tonic-gate 			/* shorten the delay if we are idle */
3864*7c478bd9Sstevel@tonic-gate 			if (inst_count == 0) {
3865*7c478bd9Sstevel@tonic-gate 				idle++;
3866*7c478bd9Sstevel@tonic-gate 				if (idle > MAX_IDLE_DELAY) {
3867*7c478bd9Sstevel@tonic-gate 					break;
3868*7c478bd9Sstevel@tonic-gate 				}
3869*7c478bd9Sstevel@tonic-gate 			} else {
3870*7c478bd9Sstevel@tonic-gate 				inst_count = idle = 0;
3871*7c478bd9Sstevel@tonic-gate 			}
3872*7c478bd9Sstevel@tonic-gate 		}
3873*7c478bd9Sstevel@tonic-gate 
3874*7c478bd9Sstevel@tonic-gate 		(void) mutex_unlock(&count_lock);
3875*7c478bd9Sstevel@tonic-gate 
3876*7c478bd9Sstevel@tonic-gate 		flush_path_to_inst();
3877*7c478bd9Sstevel@tonic-gate 	}
3878*7c478bd9Sstevel@tonic-gate }
3879*7c478bd9Sstevel@tonic-gate 
3880*7c478bd9Sstevel@tonic-gate /*
3881*7c478bd9Sstevel@tonic-gate  * Helper function for flush_path_to_inst() below; this routine calls the
3882*7c478bd9Sstevel@tonic-gate  * inst_sync syscall to flush the path_to_inst database to the given file.
3883*7c478bd9Sstevel@tonic-gate  */
3884*7c478bd9Sstevel@tonic-gate static int
3885*7c478bd9Sstevel@tonic-gate do_inst_sync(char *filename)
3886*7c478bd9Sstevel@tonic-gate {
3887*7c478bd9Sstevel@tonic-gate 	void (*sigsaved)(int);
3888*7c478bd9Sstevel@tonic-gate 	int err = 0;
3889*7c478bd9Sstevel@tonic-gate 
3890*7c478bd9Sstevel@tonic-gate 	vprint(INSTSYNC_MID, "do_inst_sync: about to flush %s\n", filename);
3891*7c478bd9Sstevel@tonic-gate 	sigsaved = sigset(SIGSYS, SIG_IGN);
3892*7c478bd9Sstevel@tonic-gate 	if (inst_sync(filename, 0) == -1)
3893*7c478bd9Sstevel@tonic-gate 		err = errno;
3894*7c478bd9Sstevel@tonic-gate 	(void) sigset(SIGSYS, sigsaved);
3895*7c478bd9Sstevel@tonic-gate 
3896*7c478bd9Sstevel@tonic-gate 	switch (err) {
3897*7c478bd9Sstevel@tonic-gate 	case 0:
3898*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
3899*7c478bd9Sstevel@tonic-gate 	case EALREADY:	/* no-op, path_to_inst already up to date */
3900*7c478bd9Sstevel@tonic-gate 		return (EALREADY);
3901*7c478bd9Sstevel@tonic-gate 	case ENOSYS:
3902*7c478bd9Sstevel@tonic-gate 		err_print(CANT_LOAD_SYSCALL);
3903*7c478bd9Sstevel@tonic-gate 		break;
3904*7c478bd9Sstevel@tonic-gate 	case EPERM:
3905*7c478bd9Sstevel@tonic-gate 		err_print(SUPER_TO_SYNC);
3906*7c478bd9Sstevel@tonic-gate 		break;
3907*7c478bd9Sstevel@tonic-gate 	default:
3908*7c478bd9Sstevel@tonic-gate 		err_print(INSTSYNC_FAILED, filename, strerror(err));
3909*7c478bd9Sstevel@tonic-gate 		break;
3910*7c478bd9Sstevel@tonic-gate 	}
3911*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_FAILURE);
3912*7c478bd9Sstevel@tonic-gate }
3913*7c478bd9Sstevel@tonic-gate 
3914*7c478bd9Sstevel@tonic-gate /*
3915*7c478bd9Sstevel@tonic-gate  * Flush the kernel's path_to_inst database to /etc/path_to_inst.  To do so
3916*7c478bd9Sstevel@tonic-gate  * safely, the database is flushed to a temporary file, then moved into place.
3917*7c478bd9Sstevel@tonic-gate  *
3918*7c478bd9Sstevel@tonic-gate  * The following files are used during this process:
3919*7c478bd9Sstevel@tonic-gate  * 	/etc/path_to_inst:	The path_to_inst file
3920*7c478bd9Sstevel@tonic-gate  * 	/etc/path_to_inst.<pid>: Contains data flushed from the kernel
3921*7c478bd9Sstevel@tonic-gate  * 	/etc/path_to_inst.old:  The backup file
3922*7c478bd9Sstevel@tonic-gate  * 	/etc/path_to_inst.old.<pid>: Temp file for creating backup
3923*7c478bd9Sstevel@tonic-gate  *
3924*7c478bd9Sstevel@tonic-gate  */
3925*7c478bd9Sstevel@tonic-gate static void
3926*7c478bd9Sstevel@tonic-gate flush_path_to_inst(void)
3927*7c478bd9Sstevel@tonic-gate {
3928*7c478bd9Sstevel@tonic-gate 	char *new_inst_file = NULL;
3929*7c478bd9Sstevel@tonic-gate 	char *old_inst_file = NULL;
3930*7c478bd9Sstevel@tonic-gate 	char *old_inst_file_npid = NULL;
3931*7c478bd9Sstevel@tonic-gate 	FILE *inst_file_fp = NULL;
3932*7c478bd9Sstevel@tonic-gate 	FILE *old_inst_file_fp = NULL;
3933*7c478bd9Sstevel@tonic-gate 	struct stat sb;
3934*7c478bd9Sstevel@tonic-gate 	int err = 0;
3935*7c478bd9Sstevel@tonic-gate 	int c;
3936*7c478bd9Sstevel@tonic-gate 	int inst_strlen;
3937*7c478bd9Sstevel@tonic-gate 
3938*7c478bd9Sstevel@tonic-gate 	vprint(PATH2INST_MID, "flush_path_to_inst: %s\n",
3939*7c478bd9Sstevel@tonic-gate 	    (flush_path_to_inst_enable == TRUE) ? "ENABLED" : "DISABLED");
3940*7c478bd9Sstevel@tonic-gate 
3941*7c478bd9Sstevel@tonic-gate 	if (flush_path_to_inst_enable == FALSE) {
3942*7c478bd9Sstevel@tonic-gate 		return;
3943*7c478bd9Sstevel@tonic-gate 	}
3944*7c478bd9Sstevel@tonic-gate 
3945*7c478bd9Sstevel@tonic-gate 	inst_strlen = strlen(inst_file);
3946*7c478bd9Sstevel@tonic-gate 	new_inst_file = s_malloc(inst_strlen + PID_STR_LEN + 2);
3947*7c478bd9Sstevel@tonic-gate 	old_inst_file = s_malloc(inst_strlen + PID_STR_LEN + 6);
3948*7c478bd9Sstevel@tonic-gate 	old_inst_file_npid = s_malloc(inst_strlen +
3949*7c478bd9Sstevel@tonic-gate 	    sizeof (INSTANCE_FILE_SUFFIX));
3950*7c478bd9Sstevel@tonic-gate 
3951*7c478bd9Sstevel@tonic-gate 	(void) snprintf(new_inst_file, inst_strlen + PID_STR_LEN + 2,
3952*7c478bd9Sstevel@tonic-gate 		"%s.%ld", inst_file, getpid());
3953*7c478bd9Sstevel@tonic-gate 
3954*7c478bd9Sstevel@tonic-gate 	if (stat(new_inst_file, &sb) == 0) {
3955*7c478bd9Sstevel@tonic-gate 		s_unlink(new_inst_file);
3956*7c478bd9Sstevel@tonic-gate 	}
3957*7c478bd9Sstevel@tonic-gate 
3958*7c478bd9Sstevel@tonic-gate 	if ((err = do_inst_sync(new_inst_file)) != DEVFSADM_SUCCESS) {
3959*7c478bd9Sstevel@tonic-gate 		goto out;
3960*7c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
3961*7c478bd9Sstevel@tonic-gate 	}
3962*7c478bd9Sstevel@tonic-gate 
3963*7c478bd9Sstevel@tonic-gate 	/*
3964*7c478bd9Sstevel@tonic-gate 	 * Now we deal with the somewhat tricky updating and renaming
3965*7c478bd9Sstevel@tonic-gate 	 * of this critical piece of kernel state.
3966*7c478bd9Sstevel@tonic-gate 	 */
3967*7c478bd9Sstevel@tonic-gate 
3968*7c478bd9Sstevel@tonic-gate 	/*
3969*7c478bd9Sstevel@tonic-gate 	 * Copy the current instance file into a temporary file.
3970*7c478bd9Sstevel@tonic-gate 	 * Then rename the temporary file into the backup (.old)
3971*7c478bd9Sstevel@tonic-gate 	 * file and rename the newly flushed kernel data into
3972*7c478bd9Sstevel@tonic-gate 	 * the instance file.
3973*7c478bd9Sstevel@tonic-gate 	 * Of course if 'inst_file' doesn't exist, there's much
3974*7c478bd9Sstevel@tonic-gate 	 * less for us to do .. tee hee.
3975*7c478bd9Sstevel@tonic-gate 	 */
3976*7c478bd9Sstevel@tonic-gate 	if ((inst_file_fp = fopen(inst_file, "r")) == NULL) {
3977*7c478bd9Sstevel@tonic-gate 		/*
3978*7c478bd9Sstevel@tonic-gate 		 * No such file.  Rename the new onto the old
3979*7c478bd9Sstevel@tonic-gate 		 */
3980*7c478bd9Sstevel@tonic-gate 		if ((err = rename(new_inst_file, inst_file)) != 0)
3981*7c478bd9Sstevel@tonic-gate 			err_print(RENAME_FAILED, inst_file, strerror(errno));
3982*7c478bd9Sstevel@tonic-gate 		goto out;
3983*7c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
3984*7c478bd9Sstevel@tonic-gate 	}
3985*7c478bd9Sstevel@tonic-gate 
3986*7c478bd9Sstevel@tonic-gate 	(void) snprintf(old_inst_file, inst_strlen + PID_STR_LEN + 6,
3987*7c478bd9Sstevel@tonic-gate 		"%s.old.%ld", inst_file, getpid());
3988*7c478bd9Sstevel@tonic-gate 
3989*7c478bd9Sstevel@tonic-gate 	if (stat(old_inst_file, &sb) == 0) {
3990*7c478bd9Sstevel@tonic-gate 		s_unlink(old_inst_file);
3991*7c478bd9Sstevel@tonic-gate 	}
3992*7c478bd9Sstevel@tonic-gate 
3993*7c478bd9Sstevel@tonic-gate 	if ((old_inst_file_fp = fopen(old_inst_file, "w")) == NULL) {
3994*7c478bd9Sstevel@tonic-gate 		/*
3995*7c478bd9Sstevel@tonic-gate 		 * Can't open the 'old_inst_file' file for writing.
3996*7c478bd9Sstevel@tonic-gate 		 * This is somewhat strange given that the syscall
3997*7c478bd9Sstevel@tonic-gate 		 * just succeeded to write a file out.. hmm.. maybe
3998*7c478bd9Sstevel@tonic-gate 		 * the fs just filled up or something nasty.
3999*7c478bd9Sstevel@tonic-gate 		 *
4000*7c478bd9Sstevel@tonic-gate 		 * Anyway, abort what we've done so far.
4001*7c478bd9Sstevel@tonic-gate 		 */
4002*7c478bd9Sstevel@tonic-gate 		err_print(CANT_UPDATE, old_inst_file);
4003*7c478bd9Sstevel@tonic-gate 		err = DEVFSADM_FAILURE;
4004*7c478bd9Sstevel@tonic-gate 		goto out;
4005*7c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
4006*7c478bd9Sstevel@tonic-gate 	}
4007*7c478bd9Sstevel@tonic-gate 
4008*7c478bd9Sstevel@tonic-gate 	/*
4009*7c478bd9Sstevel@tonic-gate 	 * Copy current instance file into the temporary file
4010*7c478bd9Sstevel@tonic-gate 	 */
4011*7c478bd9Sstevel@tonic-gate 	err = 0;
4012*7c478bd9Sstevel@tonic-gate 	while ((c = getc(inst_file_fp)) != EOF) {
4013*7c478bd9Sstevel@tonic-gate 		if ((err = putc(c, old_inst_file_fp)) == EOF) {
4014*7c478bd9Sstevel@tonic-gate 			break;
4015*7c478bd9Sstevel@tonic-gate 		}
4016*7c478bd9Sstevel@tonic-gate 	}
4017*7c478bd9Sstevel@tonic-gate 
4018*7c478bd9Sstevel@tonic-gate 	if (fclose(old_inst_file_fp) == EOF || err == EOF) {
4019*7c478bd9Sstevel@tonic-gate 		vprint(INFO_MID, CANT_UPDATE, old_inst_file);
4020*7c478bd9Sstevel@tonic-gate 		err = DEVFSADM_FAILURE;
4021*7c478bd9Sstevel@tonic-gate 		goto out;
4022*7c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
4023*7c478bd9Sstevel@tonic-gate 	}
4024*7c478bd9Sstevel@tonic-gate 
4025*7c478bd9Sstevel@tonic-gate 	/*
4026*7c478bd9Sstevel@tonic-gate 	 * Set permissions to be the same on the backup as
4027*7c478bd9Sstevel@tonic-gate 	 * /etc/path_to_inst.
4028*7c478bd9Sstevel@tonic-gate 	 */
4029*7c478bd9Sstevel@tonic-gate 	(void) chmod(old_inst_file, 0444);
4030*7c478bd9Sstevel@tonic-gate 
4031*7c478bd9Sstevel@tonic-gate 	/*
4032*7c478bd9Sstevel@tonic-gate 	 * So far, everything we've done is more or less reversible.
4033*7c478bd9Sstevel@tonic-gate 	 * But now we're going to commit ourselves.
4034*7c478bd9Sstevel@tonic-gate 	 */
4035*7c478bd9Sstevel@tonic-gate 
4036*7c478bd9Sstevel@tonic-gate 	(void) snprintf(old_inst_file_npid,
4037*7c478bd9Sstevel@tonic-gate 	    inst_strlen + sizeof (INSTANCE_FILE_SUFFIX),
4038*7c478bd9Sstevel@tonic-gate 	    "%s%s", inst_file, INSTANCE_FILE_SUFFIX);
4039*7c478bd9Sstevel@tonic-gate 
4040*7c478bd9Sstevel@tonic-gate 	if ((err = rename(old_inst_file, old_inst_file_npid)) != 0) {
4041*7c478bd9Sstevel@tonic-gate 		err_print(RENAME_FAILED, old_inst_file_npid,
4042*7c478bd9Sstevel@tonic-gate 				strerror(errno));
4043*7c478bd9Sstevel@tonic-gate 	} else if ((err = rename(new_inst_file, inst_file)) != 0) {
4044*7c478bd9Sstevel@tonic-gate 		err_print(RENAME_FAILED, inst_file, strerror(errno));
4045*7c478bd9Sstevel@tonic-gate 	}
4046*7c478bd9Sstevel@tonic-gate 
4047*7c478bd9Sstevel@tonic-gate out:
4048*7c478bd9Sstevel@tonic-gate 	if (inst_file_fp != NULL) {
4049*7c478bd9Sstevel@tonic-gate 		if (fclose(inst_file_fp) == EOF) {
4050*7c478bd9Sstevel@tonic-gate 			err_print(FCLOSE_FAILED, inst_file, strerror(errno));
4051*7c478bd9Sstevel@tonic-gate 		}
4052*7c478bd9Sstevel@tonic-gate 	}
4053*7c478bd9Sstevel@tonic-gate 
4054*7c478bd9Sstevel@tonic-gate 	if (stat(new_inst_file, &sb) == 0) {
4055*7c478bd9Sstevel@tonic-gate 		s_unlink(new_inst_file);
4056*7c478bd9Sstevel@tonic-gate 	}
4057*7c478bd9Sstevel@tonic-gate 	free(new_inst_file);
4058*7c478bd9Sstevel@tonic-gate 
4059*7c478bd9Sstevel@tonic-gate 	if (stat(old_inst_file, &sb) == 0) {
4060*7c478bd9Sstevel@tonic-gate 		s_unlink(old_inst_file);
4061*7c478bd9Sstevel@tonic-gate 	}
4062*7c478bd9Sstevel@tonic-gate 	free(old_inst_file);
4063*7c478bd9Sstevel@tonic-gate 
4064*7c478bd9Sstevel@tonic-gate 	free(old_inst_file_npid);
4065*7c478bd9Sstevel@tonic-gate 
4066*7c478bd9Sstevel@tonic-gate 	if (err != 0 && err != EALREADY) {
4067*7c478bd9Sstevel@tonic-gate 		err_print(FAILED_TO_UPDATE, inst_file);
4068*7c478bd9Sstevel@tonic-gate 	}
4069*7c478bd9Sstevel@tonic-gate }
4070*7c478bd9Sstevel@tonic-gate 
4071*7c478bd9Sstevel@tonic-gate /*
4072*7c478bd9Sstevel@tonic-gate  * detach from tty.  For daemon mode.
4073*7c478bd9Sstevel@tonic-gate  */
4074*7c478bd9Sstevel@tonic-gate void
4075*7c478bd9Sstevel@tonic-gate detachfromtty()
4076*7c478bd9Sstevel@tonic-gate {
4077*7c478bd9Sstevel@tonic-gate 	(void) setsid();
4078*7c478bd9Sstevel@tonic-gate 	if (DEVFSADM_DEBUG_ON == TRUE) {
4079*7c478bd9Sstevel@tonic-gate 		return;
4080*7c478bd9Sstevel@tonic-gate 	}
4081*7c478bd9Sstevel@tonic-gate 
4082*7c478bd9Sstevel@tonic-gate 	(void) close(0);
4083*7c478bd9Sstevel@tonic-gate 	(void) close(1);
4084*7c478bd9Sstevel@tonic-gate 	(void) close(2);
4085*7c478bd9Sstevel@tonic-gate 	(void) open("/dev/null", O_RDWR, 0);
4086*7c478bd9Sstevel@tonic-gate 	(void) dup(0);
4087*7c478bd9Sstevel@tonic-gate 	(void) dup(0);
4088*7c478bd9Sstevel@tonic-gate 	openlog(DEVFSADMD, LOG_PID, LOG_DAEMON);
4089*7c478bd9Sstevel@tonic-gate 	(void) setlogmask(LOG_UPTO(LOG_INFO));
4090*7c478bd9Sstevel@tonic-gate 	logflag = TRUE;
4091*7c478bd9Sstevel@tonic-gate }
4092*7c478bd9Sstevel@tonic-gate 
4093*7c478bd9Sstevel@tonic-gate /*
4094*7c478bd9Sstevel@tonic-gate  * Use an advisory lock to synchronize updates to /dev.  If the lock is
4095*7c478bd9Sstevel@tonic-gate  * held by another process, block in the fcntl() system call until that
4096*7c478bd9Sstevel@tonic-gate  * process drops the lock or exits.  The lock file itself is
4097*7c478bd9Sstevel@tonic-gate  * DEV_LOCK_FILE.  The process id of the current and last process owning
4098*7c478bd9Sstevel@tonic-gate  * the lock is kept in the lock file.  After acquiring the lock, read the
4099*7c478bd9Sstevel@tonic-gate  * process id and return it.  It is the process ID which last owned the
4100*7c478bd9Sstevel@tonic-gate  * lock, and will be used to determine if caches need to be flushed.
4101*7c478bd9Sstevel@tonic-gate  *
4102*7c478bd9Sstevel@tonic-gate  * NOTE: if the devlink database is held open by the caller, it may
4103*7c478bd9Sstevel@tonic-gate  * be closed by this routine. This is to enforce the following lock ordering:
4104*7c478bd9Sstevel@tonic-gate  *	1) /dev lock 2) database open
4105*7c478bd9Sstevel@tonic-gate  */
4106*7c478bd9Sstevel@tonic-gate pid_t
4107*7c478bd9Sstevel@tonic-gate enter_dev_lock()
4108*7c478bd9Sstevel@tonic-gate {
4109*7c478bd9Sstevel@tonic-gate 	struct flock lock;
4110*7c478bd9Sstevel@tonic-gate 	int n;
4111*7c478bd9Sstevel@tonic-gate 	pid_t pid;
4112*7c478bd9Sstevel@tonic-gate 	pid_t last_owner_pid;
4113*7c478bd9Sstevel@tonic-gate 
4114*7c478bd9Sstevel@tonic-gate 	if (file_mods == FALSE) {
4115*7c478bd9Sstevel@tonic-gate 		return (0);
4116*7c478bd9Sstevel@tonic-gate 	}
4117*7c478bd9Sstevel@tonic-gate 
4118*7c478bd9Sstevel@tonic-gate 	s_mkdirp(dev_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
4119*7c478bd9Sstevel@tonic-gate 	(void) snprintf(dev_lockfile, sizeof (dev_lockfile),
4120*7c478bd9Sstevel@tonic-gate 	    "%s/%s", dev_dir, DEV_LOCK_FILE);
4121*7c478bd9Sstevel@tonic-gate 
4122*7c478bd9Sstevel@tonic-gate 	vprint(LOCK_MID, "enter_dev_lock: lock file %s\n", dev_lockfile);
4123*7c478bd9Sstevel@tonic-gate 
4124*7c478bd9Sstevel@tonic-gate 	dev_lock_fd = open(dev_lockfile, O_CREAT|O_RDWR, 0644);
4125*7c478bd9Sstevel@tonic-gate 	if (dev_lock_fd < 0) {
4126*7c478bd9Sstevel@tonic-gate 		err_print(OPEN_FAILED, dev_lockfile, strerror(errno));
4127*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
4128*7c478bd9Sstevel@tonic-gate 	}
4129*7c478bd9Sstevel@tonic-gate 
4130*7c478bd9Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
4131*7c478bd9Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
4132*7c478bd9Sstevel@tonic-gate 	lock.l_start = 0;
4133*7c478bd9Sstevel@tonic-gate 	lock.l_len = 0;
4134*7c478bd9Sstevel@tonic-gate 
4135*7c478bd9Sstevel@tonic-gate 	/* try for the lock, but don't wait */
4136*7c478bd9Sstevel@tonic-gate 	if (fcntl(dev_lock_fd, F_SETLK, &lock) == -1) {
4137*7c478bd9Sstevel@tonic-gate 		if ((errno == EACCES) || (errno == EAGAIN)) {
4138*7c478bd9Sstevel@tonic-gate 			pid = 0;
4139*7c478bd9Sstevel@tonic-gate 			n = read(dev_lock_fd, &pid, sizeof (pid_t));
4140*7c478bd9Sstevel@tonic-gate 			vprint(LOCK_MID, "waiting for PID %d to complete\n",
4141*7c478bd9Sstevel@tonic-gate 				(int)pid);
4142*7c478bd9Sstevel@tonic-gate 			if (lseek(dev_lock_fd, 0, SEEK_SET) == (off_t)-1) {
4143*7c478bd9Sstevel@tonic-gate 				err_print(LSEEK_FAILED, dev_lockfile,
4144*7c478bd9Sstevel@tonic-gate 						strerror(errno));
4145*7c478bd9Sstevel@tonic-gate 				devfsadm_exit(1);
4146*7c478bd9Sstevel@tonic-gate 			}
4147*7c478bd9Sstevel@tonic-gate 			/*
4148*7c478bd9Sstevel@tonic-gate 			 * wait for the dev lock. If we have the database open,
4149*7c478bd9Sstevel@tonic-gate 			 * close it first - the order of lock acquisition should
4150*7c478bd9Sstevel@tonic-gate 			 * always be:  1) dev_lock 2) database
4151*7c478bd9Sstevel@tonic-gate 			 * This is to prevent deadlocks with any locks the
4152*7c478bd9Sstevel@tonic-gate 			 * database code may hold.
4153*7c478bd9Sstevel@tonic-gate 			 */
4154*7c478bd9Sstevel@tonic-gate 			(void) di_devlink_close(&devlink_cache, 0);
4155*7c478bd9Sstevel@tonic-gate 			if (fcntl(dev_lock_fd, F_SETLKW, &lock) == -1) {
4156*7c478bd9Sstevel@tonic-gate 				err_print(LOCK_FAILED, dev_lockfile,
4157*7c478bd9Sstevel@tonic-gate 						strerror(errno));
4158*7c478bd9Sstevel@tonic-gate 				devfsadm_exit(1);
4159*7c478bd9Sstevel@tonic-gate 			}
4160*7c478bd9Sstevel@tonic-gate 		}
4161*7c478bd9Sstevel@tonic-gate 	}
4162*7c478bd9Sstevel@tonic-gate 
4163*7c478bd9Sstevel@tonic-gate 	hold_dev_lock = TRUE;
4164*7c478bd9Sstevel@tonic-gate 	pid = 0;
4165*7c478bd9Sstevel@tonic-gate 	n = read(dev_lock_fd, &pid, sizeof (pid_t));
4166*7c478bd9Sstevel@tonic-gate 	if (n == sizeof (pid_t) && pid == getpid()) {
4167*7c478bd9Sstevel@tonic-gate 		return (pid);
4168*7c478bd9Sstevel@tonic-gate 	}
4169*7c478bd9Sstevel@tonic-gate 
4170*7c478bd9Sstevel@tonic-gate 	last_owner_pid = pid;
4171*7c478bd9Sstevel@tonic-gate 
4172*7c478bd9Sstevel@tonic-gate 	if (lseek(dev_lock_fd, 0, SEEK_SET) == (off_t)-1) {
4173*7c478bd9Sstevel@tonic-gate 		err_print(LSEEK_FAILED, dev_lockfile, strerror(errno));
4174*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
4175*7c478bd9Sstevel@tonic-gate 	}
4176*7c478bd9Sstevel@tonic-gate 	pid = getpid();
4177*7c478bd9Sstevel@tonic-gate 	n = write(dev_lock_fd, &pid, sizeof (pid_t));
4178*7c478bd9Sstevel@tonic-gate 	if (n != sizeof (pid_t)) {
4179*7c478bd9Sstevel@tonic-gate 		err_print(WRITE_FAILED, dev_lockfile, strerror(errno));
4180*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
4181*7c478bd9Sstevel@tonic-gate 	}
4182*7c478bd9Sstevel@tonic-gate 
4183*7c478bd9Sstevel@tonic-gate 	return (last_owner_pid);
4184*7c478bd9Sstevel@tonic-gate }
4185*7c478bd9Sstevel@tonic-gate 
4186*7c478bd9Sstevel@tonic-gate /*
4187*7c478bd9Sstevel@tonic-gate  * Drop the advisory /dev lock, close lock file.  Close and re-open the
4188*7c478bd9Sstevel@tonic-gate  * file every time so to ensure a resync if for some reason the lock file
4189*7c478bd9Sstevel@tonic-gate  * gets removed.
4190*7c478bd9Sstevel@tonic-gate  */
4191*7c478bd9Sstevel@tonic-gate void
4192*7c478bd9Sstevel@tonic-gate exit_dev_lock()
4193*7c478bd9Sstevel@tonic-gate {
4194*7c478bd9Sstevel@tonic-gate 	struct flock unlock;
4195*7c478bd9Sstevel@tonic-gate 
4196*7c478bd9Sstevel@tonic-gate 	if (hold_dev_lock == FALSE) {
4197*7c478bd9Sstevel@tonic-gate 		return;
4198*7c478bd9Sstevel@tonic-gate 	}
4199*7c478bd9Sstevel@tonic-gate 
4200*7c478bd9Sstevel@tonic-gate 	vprint(LOCK_MID, "exit_dev_lock: lock file %s\n", dev_lockfile);
4201*7c478bd9Sstevel@tonic-gate 
4202*7c478bd9Sstevel@tonic-gate 	unlock.l_type = F_UNLCK;
4203*7c478bd9Sstevel@tonic-gate 	unlock.l_whence = SEEK_SET;
4204*7c478bd9Sstevel@tonic-gate 	unlock.l_start = 0;
4205*7c478bd9Sstevel@tonic-gate 	unlock.l_len = 0;
4206*7c478bd9Sstevel@tonic-gate 
4207*7c478bd9Sstevel@tonic-gate 	if (fcntl(dev_lock_fd, F_SETLK, &unlock) == -1) {
4208*7c478bd9Sstevel@tonic-gate 		err_print(UNLOCK_FAILED, dev_lockfile, strerror(errno));
4209*7c478bd9Sstevel@tonic-gate 	}
4210*7c478bd9Sstevel@tonic-gate 
4211*7c478bd9Sstevel@tonic-gate 	hold_dev_lock = FALSE;
4212*7c478bd9Sstevel@tonic-gate 
4213*7c478bd9Sstevel@tonic-gate 	if (close(dev_lock_fd) == -1) {
4214*7c478bd9Sstevel@tonic-gate 		err_print(CLOSE_FAILED, dev_lockfile, strerror(errno));
4215*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
4216*7c478bd9Sstevel@tonic-gate 	}
4217*7c478bd9Sstevel@tonic-gate }
4218*7c478bd9Sstevel@tonic-gate 
4219*7c478bd9Sstevel@tonic-gate /*
4220*7c478bd9Sstevel@tonic-gate  *
4221*7c478bd9Sstevel@tonic-gate  * Use an advisory lock to ensure that only one daemon process is active
4222*7c478bd9Sstevel@tonic-gate  * in the system at any point in time.	If the lock is held by another
4223*7c478bd9Sstevel@tonic-gate  * process, do not block but return the pid owner of the lock to the
4224*7c478bd9Sstevel@tonic-gate  * caller immediately.	The lock is cleared if the holding daemon process
4225*7c478bd9Sstevel@tonic-gate  * exits for any reason even if the lock file remains, so the daemon can
4226*7c478bd9Sstevel@tonic-gate  * be restarted if necessary.  The lock file is DAEMON_LOCK_FILE.
4227*7c478bd9Sstevel@tonic-gate  */
4228*7c478bd9Sstevel@tonic-gate pid_t
4229*7c478bd9Sstevel@tonic-gate enter_daemon_lock(void)
4230*7c478bd9Sstevel@tonic-gate {
4231*7c478bd9Sstevel@tonic-gate 	struct flock lock;
4232*7c478bd9Sstevel@tonic-gate 
4233*7c478bd9Sstevel@tonic-gate 	s_mkdirp(dev_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
4234*7c478bd9Sstevel@tonic-gate 	(void) snprintf(daemon_lockfile, sizeof (daemon_lockfile),
4235*7c478bd9Sstevel@tonic-gate 	    "%s/%s", dev_dir, DAEMON_LOCK_FILE);
4236*7c478bd9Sstevel@tonic-gate 
4237*7c478bd9Sstevel@tonic-gate 	vprint(LOCK_MID, "enter_daemon_lock: lock file %s\n", daemon_lockfile);
4238*7c478bd9Sstevel@tonic-gate 
4239*7c478bd9Sstevel@tonic-gate 	daemon_lock_fd = open(daemon_lockfile, O_CREAT|O_RDWR, 0644);
4240*7c478bd9Sstevel@tonic-gate 	if (daemon_lock_fd < 0) {
4241*7c478bd9Sstevel@tonic-gate 		err_print(OPEN_FAILED, daemon_lockfile, strerror(errno));
4242*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
4243*7c478bd9Sstevel@tonic-gate 	}
4244*7c478bd9Sstevel@tonic-gate 
4245*7c478bd9Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
4246*7c478bd9Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
4247*7c478bd9Sstevel@tonic-gate 	lock.l_start = 0;
4248*7c478bd9Sstevel@tonic-gate 	lock.l_len = 0;
4249*7c478bd9Sstevel@tonic-gate 
4250*7c478bd9Sstevel@tonic-gate 	if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
4251*7c478bd9Sstevel@tonic-gate 
4252*7c478bd9Sstevel@tonic-gate 		if (errno == EAGAIN || errno == EDEADLK) {
4253*7c478bd9Sstevel@tonic-gate 			if (fcntl(daemon_lock_fd, F_GETLK, &lock) == -1) {
4254*7c478bd9Sstevel@tonic-gate 				err_print(LOCK_FAILED, daemon_lockfile,
4255*7c478bd9Sstevel@tonic-gate 						strerror(errno));
4256*7c478bd9Sstevel@tonic-gate 				devfsadm_exit(1);
4257*7c478bd9Sstevel@tonic-gate 			}
4258*7c478bd9Sstevel@tonic-gate 			return (lock.l_pid);
4259*7c478bd9Sstevel@tonic-gate 		}
4260*7c478bd9Sstevel@tonic-gate 	}
4261*7c478bd9Sstevel@tonic-gate 	hold_daemon_lock = TRUE;
4262*7c478bd9Sstevel@tonic-gate 	return (getpid());
4263*7c478bd9Sstevel@tonic-gate }
4264*7c478bd9Sstevel@tonic-gate 
4265*7c478bd9Sstevel@tonic-gate /*
4266*7c478bd9Sstevel@tonic-gate  * Drop the advisory daemon lock, close lock file
4267*7c478bd9Sstevel@tonic-gate  */
4268*7c478bd9Sstevel@tonic-gate void
4269*7c478bd9Sstevel@tonic-gate exit_daemon_lock(void)
4270*7c478bd9Sstevel@tonic-gate {
4271*7c478bd9Sstevel@tonic-gate 	struct flock lock;
4272*7c478bd9Sstevel@tonic-gate 
4273*7c478bd9Sstevel@tonic-gate 	if (hold_daemon_lock == FALSE) {
4274*7c478bd9Sstevel@tonic-gate 		return;
4275*7c478bd9Sstevel@tonic-gate 	}
4276*7c478bd9Sstevel@tonic-gate 
4277*7c478bd9Sstevel@tonic-gate 	vprint(LOCK_MID, "exit_daemon_lock: lock file %s\n", daemon_lockfile);
4278*7c478bd9Sstevel@tonic-gate 
4279*7c478bd9Sstevel@tonic-gate 	lock.l_type = F_UNLCK;
4280*7c478bd9Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
4281*7c478bd9Sstevel@tonic-gate 	lock.l_start = 0;
4282*7c478bd9Sstevel@tonic-gate 	lock.l_len = 0;
4283*7c478bd9Sstevel@tonic-gate 
4284*7c478bd9Sstevel@tonic-gate 	if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
4285*7c478bd9Sstevel@tonic-gate 		err_print(UNLOCK_FAILED, daemon_lockfile, strerror(errno));
4286*7c478bd9Sstevel@tonic-gate 	}
4287*7c478bd9Sstevel@tonic-gate 
4288*7c478bd9Sstevel@tonic-gate 	if (close(daemon_lock_fd) == -1) {
4289*7c478bd9Sstevel@tonic-gate 		err_print(CLOSE_FAILED, daemon_lockfile, strerror(errno));
4290*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
4291*7c478bd9Sstevel@tonic-gate 	}
4292*7c478bd9Sstevel@tonic-gate }
4293*7c478bd9Sstevel@tonic-gate 
4294*7c478bd9Sstevel@tonic-gate /*
4295*7c478bd9Sstevel@tonic-gate  * Called to removed danging nodes in two different modes: RM_PRE, RM_POST.
4296*7c478bd9Sstevel@tonic-gate  * RM_PRE mode is called before processing the entire devinfo tree, and RM_POST
4297*7c478bd9Sstevel@tonic-gate  * is called after processing the entire devinfo tree.
4298*7c478bd9Sstevel@tonic-gate  */
4299*7c478bd9Sstevel@tonic-gate static void
4300*7c478bd9Sstevel@tonic-gate pre_and_post_cleanup(int flags)
4301*7c478bd9Sstevel@tonic-gate {
4302*7c478bd9Sstevel@tonic-gate 	remove_list_t *rm;
4303*7c478bd9Sstevel@tonic-gate 	recurse_dev_t rd;
4304*7c478bd9Sstevel@tonic-gate 	cleanup_data_t cleanup_data;
4305*7c478bd9Sstevel@tonic-gate 	char *fcn = "pre_and_post_cleanup: ";
4306*7c478bd9Sstevel@tonic-gate 
4307*7c478bd9Sstevel@tonic-gate 	if (build_dev == FALSE)
4308*7c478bd9Sstevel@tonic-gate 		return;
4309*7c478bd9Sstevel@tonic-gate 
4310*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "attempting %s-cleanup\n",
4311*7c478bd9Sstevel@tonic-gate 	    flags == RM_PRE ? "pre" : "post");
4312*7c478bd9Sstevel@tonic-gate 	vprint(REMOVE_MID, "%sflags = %d\n", fcn, flags);
4313*7c478bd9Sstevel@tonic-gate 
4314*7c478bd9Sstevel@tonic-gate 	/*
4315*7c478bd9Sstevel@tonic-gate 	 * the generic function recurse_dev_re is shared among different
4316*7c478bd9Sstevel@tonic-gate 	 * functions, so set the method and data that it should use for
4317*7c478bd9Sstevel@tonic-gate 	 * matches.
4318*7c478bd9Sstevel@tonic-gate 	 */
4319*7c478bd9Sstevel@tonic-gate 	rd.fcn = matching_dev;
4320*7c478bd9Sstevel@tonic-gate 	rd.data = (void *)&cleanup_data;
4321*7c478bd9Sstevel@tonic-gate 	cleanup_data.flags = flags;
4322*7c478bd9Sstevel@tonic-gate 
4323*7c478bd9Sstevel@tonic-gate 	for (rm = remove_head; rm != NULL; rm = rm->next) {
4324*7c478bd9Sstevel@tonic-gate 		if ((flags & rm->remove->flags) == flags) {
4325*7c478bd9Sstevel@tonic-gate 			cleanup_data.rm = rm;
4326*7c478bd9Sstevel@tonic-gate 			/*
4327*7c478bd9Sstevel@tonic-gate 			 * If reached this point, RM_PRE or RM_POST cleanup is
4328*7c478bd9Sstevel@tonic-gate 			 * desired.  clean_ok() decides whether to clean
4329*7c478bd9Sstevel@tonic-gate 			 * under the given circumstances.
4330*7c478bd9Sstevel@tonic-gate 			 */
4331*7c478bd9Sstevel@tonic-gate 			vprint(REMOVE_MID, "%scleanup: PRE or POST\n", fcn);
4332*7c478bd9Sstevel@tonic-gate 			if (clean_ok(rm->remove) == DEVFSADM_SUCCESS) {
4333*7c478bd9Sstevel@tonic-gate 				vprint(REMOVE_MID, "cleanup: cleanup OK\n");
4334*7c478bd9Sstevel@tonic-gate 				recurse_dev_re(dev_dir, rm->remove->
4335*7c478bd9Sstevel@tonic-gate 					dev_dirs_re, &rd);
4336*7c478bd9Sstevel@tonic-gate 			}
4337*7c478bd9Sstevel@tonic-gate 		}
4338*7c478bd9Sstevel@tonic-gate 	}
4339*7c478bd9Sstevel@tonic-gate }
4340*7c478bd9Sstevel@tonic-gate 
4341*7c478bd9Sstevel@tonic-gate /*
4342*7c478bd9Sstevel@tonic-gate  * clean_ok() determines whether cleanup should be done according
4343*7c478bd9Sstevel@tonic-gate  * to the following matrix:
4344*7c478bd9Sstevel@tonic-gate  *
4345*7c478bd9Sstevel@tonic-gate  * command line arguments RM_PRE    RM_POST	  RM_PRE &&    RM_POST &&
4346*7c478bd9Sstevel@tonic-gate  *						  RM_ALWAYS    RM_ALWAYS
4347*7c478bd9Sstevel@tonic-gate  * ---------------------- ------     -----	  ---------    ----------
4348*7c478bd9Sstevel@tonic-gate  *
4349*7c478bd9Sstevel@tonic-gate  * <neither -c nor -C>	  -	    -		  pre-clean    post-clean
4350*7c478bd9Sstevel@tonic-gate  *
4351*7c478bd9Sstevel@tonic-gate  * -C			  pre-clean  post-clean   pre-clean    post-clean
4352*7c478bd9Sstevel@tonic-gate  *
4353*7c478bd9Sstevel@tonic-gate  * -C -c class		  pre-clean  post-clean   pre-clean    post-clean
4354*7c478bd9Sstevel@tonic-gate  *			  if class  if class	  if class     if class
4355*7c478bd9Sstevel@tonic-gate  *			  matches   matches	  matches      matches
4356*7c478bd9Sstevel@tonic-gate  *
4357*7c478bd9Sstevel@tonic-gate  * -c class		   -	       -	  pre-clean    post-clean
4358*7c478bd9Sstevel@tonic-gate  *						  if class     if class
4359*7c478bd9Sstevel@tonic-gate  *						  matches      matches
4360*7c478bd9Sstevel@tonic-gate  *
4361*7c478bd9Sstevel@tonic-gate  */
4362*7c478bd9Sstevel@tonic-gate static int
4363*7c478bd9Sstevel@tonic-gate clean_ok(devfsadm_remove_t *remove)
4364*7c478bd9Sstevel@tonic-gate {
4365*7c478bd9Sstevel@tonic-gate 	int i;
4366*7c478bd9Sstevel@tonic-gate 
4367*7c478bd9Sstevel@tonic-gate 	if (single_drv == TRUE) {
4368*7c478bd9Sstevel@tonic-gate 		/* no cleanup at all when using -i option */
4369*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
4370*7c478bd9Sstevel@tonic-gate 	}
4371*7c478bd9Sstevel@tonic-gate 
4372*7c478bd9Sstevel@tonic-gate 	/*
4373*7c478bd9Sstevel@tonic-gate 	 * no cleanup if drivers are not loaded. We make an exception
4374*7c478bd9Sstevel@tonic-gate 	 * for the "disks" program however, since disks has a public
4375*7c478bd9Sstevel@tonic-gate 	 * cleanup flag (-C) and disk drivers are usually never
4376*7c478bd9Sstevel@tonic-gate 	 * unloaded.
4377*7c478bd9Sstevel@tonic-gate 	 */
4378*7c478bd9Sstevel@tonic-gate 	if (load_attach_drv == FALSE && strcmp(prog, DISKS) != 0) {
4379*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
4380*7c478bd9Sstevel@tonic-gate 	}
4381*7c478bd9Sstevel@tonic-gate 
4382*7c478bd9Sstevel@tonic-gate 	/* if the cleanup flag was not specified, return false */
4383*7c478bd9Sstevel@tonic-gate 	if ((cleanup == FALSE) && ((remove->flags & RM_ALWAYS) == 0)) {
4384*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
4385*7c478bd9Sstevel@tonic-gate 	}
4386*7c478bd9Sstevel@tonic-gate 
4387*7c478bd9Sstevel@tonic-gate 	if (num_classes == 0) {
4388*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
4389*7c478bd9Sstevel@tonic-gate 	}
4390*7c478bd9Sstevel@tonic-gate 
4391*7c478bd9Sstevel@tonic-gate 	/*
4392*7c478bd9Sstevel@tonic-gate 	 * if reached this point, check to see if the class in the given
4393*7c478bd9Sstevel@tonic-gate 	 * remove structure matches a class given on the command line
4394*7c478bd9Sstevel@tonic-gate 	 */
4395*7c478bd9Sstevel@tonic-gate 
4396*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_classes; i++) {
4397*7c478bd9Sstevel@tonic-gate 		if (strcmp(remove->device_class, classes[i]) == 0) {
4398*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_SUCCESS);
4399*7c478bd9Sstevel@tonic-gate 		}
4400*7c478bd9Sstevel@tonic-gate 	}
4401*7c478bd9Sstevel@tonic-gate 
4402*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_FAILURE);
4403*7c478bd9Sstevel@tonic-gate }
4404*7c478bd9Sstevel@tonic-gate 
4405*7c478bd9Sstevel@tonic-gate /*
4406*7c478bd9Sstevel@tonic-gate  * Called to remove dangling nodes after receiving a hotplug event
4407*7c478bd9Sstevel@tonic-gate  * containing the physical node pathname to be removed.
4408*7c478bd9Sstevel@tonic-gate  */
4409*7c478bd9Sstevel@tonic-gate void
4410*7c478bd9Sstevel@tonic-gate hot_cleanup(char *node_path, char *minor_name, char *ev_subclass,
4411*7c478bd9Sstevel@tonic-gate     char *driver_name, int instance)
4412*7c478bd9Sstevel@tonic-gate {
4413*7c478bd9Sstevel@tonic-gate 	link_t *link;
4414*7c478bd9Sstevel@tonic-gate 	linkhead_t *head;
4415*7c478bd9Sstevel@tonic-gate 	remove_list_t *rm;
4416*7c478bd9Sstevel@tonic-gate 	char *fcn = "hot_cleanup: ";
4417*7c478bd9Sstevel@tonic-gate 	char path[PATH_MAX + 1];
4418*7c478bd9Sstevel@tonic-gate 	int path_len;
4419*7c478bd9Sstevel@tonic-gate 	char rmlink[PATH_MAX + 1];
4420*7c478bd9Sstevel@tonic-gate 	nvlist_t *nvl = NULL;
4421*7c478bd9Sstevel@tonic-gate 
4422*7c478bd9Sstevel@tonic-gate 	/*
4423*7c478bd9Sstevel@tonic-gate 	 * dev links can go away as part of hot cleanup.
4424*7c478bd9Sstevel@tonic-gate 	 * So first build event attributes in order capture dev links.
4425*7c478bd9Sstevel@tonic-gate 	 */
4426*7c478bd9Sstevel@tonic-gate 	if (ev_subclass != NULL)
4427*7c478bd9Sstevel@tonic-gate 		nvl = build_event_attributes(EC_DEV_REMOVE, ev_subclass,
4428*7c478bd9Sstevel@tonic-gate 		    node_path, DI_NODE_NIL, driver_name, instance);
4429*7c478bd9Sstevel@tonic-gate 
4430*7c478bd9Sstevel@tonic-gate 	(void) strcpy(path, node_path);
4431*7c478bd9Sstevel@tonic-gate 	(void) strcat(path, ":");
4432*7c478bd9Sstevel@tonic-gate 	(void) strcat(path, minor_name == NULL ? "" : minor_name);
4433*7c478bd9Sstevel@tonic-gate 
4434*7c478bd9Sstevel@tonic-gate 	path_len = strlen(path);
4435*7c478bd9Sstevel@tonic-gate 
4436*7c478bd9Sstevel@tonic-gate 	vprint(REMOVE_MID, "%spath=%s\n", fcn, path);
4437*7c478bd9Sstevel@tonic-gate 
4438*7c478bd9Sstevel@tonic-gate 	for (rm = remove_head; rm != NULL; rm = rm->next) {
4439*7c478bd9Sstevel@tonic-gate 		if ((RM_HOT & rm->remove->flags) == RM_HOT) {
4440*7c478bd9Sstevel@tonic-gate 			head = get_cached_links(rm->remove->dev_dirs_re);
4441*7c478bd9Sstevel@tonic-gate 			assert(head->nextlink == NULL);
4442*7c478bd9Sstevel@tonic-gate 			for (link = head->link;
4443*7c478bd9Sstevel@tonic-gate 			    link != NULL; link = head->nextlink) {
4444*7c478bd9Sstevel@tonic-gate 				/*
4445*7c478bd9Sstevel@tonic-gate 				 * The remove callback below may remove
4446*7c478bd9Sstevel@tonic-gate 				 * the current and/or any or all of the
4447*7c478bd9Sstevel@tonic-gate 				 * subsequent links in the list.
4448*7c478bd9Sstevel@tonic-gate 				 * Save the next link in the head. If
4449*7c478bd9Sstevel@tonic-gate 				 * the callback removes the next link
4450*7c478bd9Sstevel@tonic-gate 				 * the saved pointer in the head will be
4451*7c478bd9Sstevel@tonic-gate 				 * updated by the callback to point at
4452*7c478bd9Sstevel@tonic-gate 				 * the next valid link.
4453*7c478bd9Sstevel@tonic-gate 				 */
4454*7c478bd9Sstevel@tonic-gate 				head->nextlink = link->next;
4455*7c478bd9Sstevel@tonic-gate 				if (strncmp(link->contents, path,
4456*7c478bd9Sstevel@tonic-gate 					path_len) == 0) {
4457*7c478bd9Sstevel@tonic-gate 
4458*7c478bd9Sstevel@tonic-gate 					if (call_minor_init(rm->modptr) ==
4459*7c478bd9Sstevel@tonic-gate 					    DEVFSADM_FAILURE)
4460*7c478bd9Sstevel@tonic-gate 						continue;
4461*7c478bd9Sstevel@tonic-gate 
4462*7c478bd9Sstevel@tonic-gate 					vprint(REMOVE_MID,
4463*7c478bd9Sstevel@tonic-gate 						"%sremoving %s -> %s\n", fcn,
4464*7c478bd9Sstevel@tonic-gate 						link->devlink, link->contents);
4465*7c478bd9Sstevel@tonic-gate 					/*
4466*7c478bd9Sstevel@tonic-gate 					 * Use a copy of the cached link name
4467*7c478bd9Sstevel@tonic-gate 					 * as the cache entry will go away
4468*7c478bd9Sstevel@tonic-gate 					 * during link removal
4469*7c478bd9Sstevel@tonic-gate 					 */
4470*7c478bd9Sstevel@tonic-gate 					(void) snprintf(rmlink, sizeof (rmlink),
4471*7c478bd9Sstevel@tonic-gate 					    "%s", link->devlink);
4472*7c478bd9Sstevel@tonic-gate 					(*(rm->remove->callback_fcn))(rmlink);
4473*7c478bd9Sstevel@tonic-gate 				}
4474*7c478bd9Sstevel@tonic-gate 			}
4475*7c478bd9Sstevel@tonic-gate 		}
4476*7c478bd9Sstevel@tonic-gate 	}
4477*7c478bd9Sstevel@tonic-gate 
4478*7c478bd9Sstevel@tonic-gate 	/* now log an event */
4479*7c478bd9Sstevel@tonic-gate 	if (nvl) {
4480*7c478bd9Sstevel@tonic-gate 		log_event(EC_DEV_REMOVE, ev_subclass, nvl);
4481*7c478bd9Sstevel@tonic-gate 		free(nvl);
4482*7c478bd9Sstevel@tonic-gate 	}
4483*7c478bd9Sstevel@tonic-gate }
4484*7c478bd9Sstevel@tonic-gate 
4485*7c478bd9Sstevel@tonic-gate /*
4486*7c478bd9Sstevel@tonic-gate  * Open the dir current_dir.  For every file which matches the first dir
4487*7c478bd9Sstevel@tonic-gate  * component of path_re, recurse.  If there are no more *dir* path
4488*7c478bd9Sstevel@tonic-gate  * components left in path_re (ie no more /), then call function rd->fcn.
4489*7c478bd9Sstevel@tonic-gate  */
4490*7c478bd9Sstevel@tonic-gate static void
4491*7c478bd9Sstevel@tonic-gate recurse_dev_re(char *current_dir, char *path_re, recurse_dev_t *rd)
4492*7c478bd9Sstevel@tonic-gate {
4493*7c478bd9Sstevel@tonic-gate 	regex_t re1;
4494*7c478bd9Sstevel@tonic-gate 	char *slash;
4495*7c478bd9Sstevel@tonic-gate 	char new_path[PATH_MAX + 1];
4496*7c478bd9Sstevel@tonic-gate 	char *anchored_path_re;
4497*7c478bd9Sstevel@tonic-gate 	struct dirent *entp;
4498*7c478bd9Sstevel@tonic-gate 	struct dirent *retp;
4499*7c478bd9Sstevel@tonic-gate 	DIR *dp;
4500*7c478bd9Sstevel@tonic-gate 	size_t len;
4501*7c478bd9Sstevel@tonic-gate 
4502*7c478bd9Sstevel@tonic-gate 	vprint(RECURSEDEV_MID, "recurse_dev_re: curr = %s path=%s\n",
4503*7c478bd9Sstevel@tonic-gate 		current_dir, path_re);
4504*7c478bd9Sstevel@tonic-gate 
4505*7c478bd9Sstevel@tonic-gate 	if ((dp = opendir(current_dir)) == NULL) {
4506*7c478bd9Sstevel@tonic-gate 		return;
4507*7c478bd9Sstevel@tonic-gate 	}
4508*7c478bd9Sstevel@tonic-gate 
4509*7c478bd9Sstevel@tonic-gate 	len = strlen(path_re);
4510*7c478bd9Sstevel@tonic-gate 	if ((slash = strchr(path_re, '/')) != NULL) {
4511*7c478bd9Sstevel@tonic-gate 		len = (slash - path_re);
4512*7c478bd9Sstevel@tonic-gate 	}
4513*7c478bd9Sstevel@tonic-gate 
4514*7c478bd9Sstevel@tonic-gate 	entp = s_malloc(PATH_MAX + 1 + sizeof (struct  dirent));
4515*7c478bd9Sstevel@tonic-gate 
4516*7c478bd9Sstevel@tonic-gate 	anchored_path_re = s_malloc(len + 3);
4517*7c478bd9Sstevel@tonic-gate 	(void) sprintf(anchored_path_re, "^%.*s$", len, path_re);
4518*7c478bd9Sstevel@tonic-gate 
4519*7c478bd9Sstevel@tonic-gate 	if (regcomp(&re1, anchored_path_re, REG_EXTENDED) != 0) {
4520*7c478bd9Sstevel@tonic-gate 		free(anchored_path_re);
4521*7c478bd9Sstevel@tonic-gate 		goto out;
4522*7c478bd9Sstevel@tonic-gate 	}
4523*7c478bd9Sstevel@tonic-gate 
4524*7c478bd9Sstevel@tonic-gate 	free(anchored_path_re);
4525*7c478bd9Sstevel@tonic-gate 
4526*7c478bd9Sstevel@tonic-gate 	while (readdir_r(dp, entp, &retp) == 0) {
4527*7c478bd9Sstevel@tonic-gate 
4528*7c478bd9Sstevel@tonic-gate 		/* See 4062296 to understand readdir_r semantics */
4529*7c478bd9Sstevel@tonic-gate 		if (retp == NULL) {
4530*7c478bd9Sstevel@tonic-gate 			break;
4531*7c478bd9Sstevel@tonic-gate 		}
4532*7c478bd9Sstevel@tonic-gate 
4533*7c478bd9Sstevel@tonic-gate 		if (strcmp(entp->d_name, ".") == 0 ||
4534*7c478bd9Sstevel@tonic-gate 		    strcmp(entp->d_name, "..") == 0) {
4535*7c478bd9Sstevel@tonic-gate 			continue;
4536*7c478bd9Sstevel@tonic-gate 		}
4537*7c478bd9Sstevel@tonic-gate 
4538*7c478bd9Sstevel@tonic-gate 		if (regexec(&re1, entp->d_name, 0, NULL, 0) == 0) {
4539*7c478bd9Sstevel@tonic-gate 			/* match */
4540*7c478bd9Sstevel@tonic-gate 			(void) strcpy(new_path, current_dir);
4541*7c478bd9Sstevel@tonic-gate 			(void) strcat(new_path, "/");
4542*7c478bd9Sstevel@tonic-gate 			(void) strcat(new_path, entp->d_name);
4543*7c478bd9Sstevel@tonic-gate 
4544*7c478bd9Sstevel@tonic-gate 			vprint(RECURSEDEV_MID, "recurse_dev_re: match, new "
4545*7c478bd9Sstevel@tonic-gate 				"path = %s\n", new_path);
4546*7c478bd9Sstevel@tonic-gate 
4547*7c478bd9Sstevel@tonic-gate 			if (slash != NULL) {
4548*7c478bd9Sstevel@tonic-gate 				recurse_dev_re(new_path, slash + 1, rd);
4549*7c478bd9Sstevel@tonic-gate 			} else {
4550*7c478bd9Sstevel@tonic-gate 				/* reached the leaf component of path_re */
4551*7c478bd9Sstevel@tonic-gate 				vprint(RECURSEDEV_MID,
4552*7c478bd9Sstevel@tonic-gate 					"recurse_dev_re: calling fcn\n");
4553*7c478bd9Sstevel@tonic-gate 				(*(rd->fcn))(new_path, rd->data);
4554*7c478bd9Sstevel@tonic-gate 			}
4555*7c478bd9Sstevel@tonic-gate 		}
4556*7c478bd9Sstevel@tonic-gate 	}
4557*7c478bd9Sstevel@tonic-gate 
4558*7c478bd9Sstevel@tonic-gate 	regfree(&re1);
4559*7c478bd9Sstevel@tonic-gate 
4560*7c478bd9Sstevel@tonic-gate out:
4561*7c478bd9Sstevel@tonic-gate 	free(entp);
4562*7c478bd9Sstevel@tonic-gate 	s_closedir(dp);
4563*7c478bd9Sstevel@tonic-gate }
4564*7c478bd9Sstevel@tonic-gate 
4565*7c478bd9Sstevel@tonic-gate /*
4566*7c478bd9Sstevel@tonic-gate  *  Found a devpath which matches a RE in the remove structure.
4567*7c478bd9Sstevel@tonic-gate  *  Now check to see if it is dangling.
4568*7c478bd9Sstevel@tonic-gate  */
4569*7c478bd9Sstevel@tonic-gate static void
4570*7c478bd9Sstevel@tonic-gate matching_dev(char *devpath, void *data)
4571*7c478bd9Sstevel@tonic-gate {
4572*7c478bd9Sstevel@tonic-gate 	cleanup_data_t *cleanup_data = data;
4573*7c478bd9Sstevel@tonic-gate 	char *fcn = "matching_dev: ";
4574*7c478bd9Sstevel@tonic-gate 
4575*7c478bd9Sstevel@tonic-gate 	vprint(RECURSEDEV_MID, "%sexamining devpath = '%s'\n", fcn,
4576*7c478bd9Sstevel@tonic-gate 			devpath);
4577*7c478bd9Sstevel@tonic-gate 
4578*7c478bd9Sstevel@tonic-gate 	if (resolve_link(devpath, NULL, NULL, NULL, 1) == TRUE) {
4579*7c478bd9Sstevel@tonic-gate 		if (call_minor_init(cleanup_data->rm->modptr) ==
4580*7c478bd9Sstevel@tonic-gate 				DEVFSADM_FAILURE) {
4581*7c478bd9Sstevel@tonic-gate 			return;
4582*7c478bd9Sstevel@tonic-gate 		}
4583*7c478bd9Sstevel@tonic-gate 
4584*7c478bd9Sstevel@tonic-gate 		devpath += strlen(dev_dir) + strlen("/");
4585*7c478bd9Sstevel@tonic-gate 
4586*7c478bd9Sstevel@tonic-gate 		vprint(RECURSEDEV_MID, "%scalling"
4587*7c478bd9Sstevel@tonic-gate 			" callback %s\n", fcn, devpath);
4588*7c478bd9Sstevel@tonic-gate 		(*(cleanup_data->rm->remove->callback_fcn))(devpath);
4589*7c478bd9Sstevel@tonic-gate 	}
4590*7c478bd9Sstevel@tonic-gate }
4591*7c478bd9Sstevel@tonic-gate 
4592*7c478bd9Sstevel@tonic-gate int
4593*7c478bd9Sstevel@tonic-gate devfsadm_read_link(char *link, char **devfs_path)
4594*7c478bd9Sstevel@tonic-gate {
4595*7c478bd9Sstevel@tonic-gate 	char devlink[PATH_MAX];
4596*7c478bd9Sstevel@tonic-gate 
4597*7c478bd9Sstevel@tonic-gate 	*devfs_path = NULL;
4598*7c478bd9Sstevel@tonic-gate 
4599*7c478bd9Sstevel@tonic-gate 	/* prepend link with dev_dir contents */
4600*7c478bd9Sstevel@tonic-gate 	(void) strcpy(devlink, dev_dir);
4601*7c478bd9Sstevel@tonic-gate 	(void) strcat(devlink, "/");
4602*7c478bd9Sstevel@tonic-gate 	(void) strcat(devlink, link);
4603*7c478bd9Sstevel@tonic-gate 
4604*7c478bd9Sstevel@tonic-gate 	/* We *don't* want a stat of the /devices node */
4605*7c478bd9Sstevel@tonic-gate 	(void) resolve_link(devlink, NULL, NULL, devfs_path, 0);
4606*7c478bd9Sstevel@tonic-gate 
4607*7c478bd9Sstevel@tonic-gate 	return (*devfs_path ? DEVFSADM_SUCCESS : DEVFSADM_FAILURE);
4608*7c478bd9Sstevel@tonic-gate }
4609*7c478bd9Sstevel@tonic-gate 
4610*7c478bd9Sstevel@tonic-gate int
4611*7c478bd9Sstevel@tonic-gate devfsadm_link_valid(char *link)
4612*7c478bd9Sstevel@tonic-gate {
4613*7c478bd9Sstevel@tonic-gate 	struct stat sb;
4614*7c478bd9Sstevel@tonic-gate 	char devlink[PATH_MAX + 1], *contents;
4615*7c478bd9Sstevel@tonic-gate 	int rv, type;
4616*7c478bd9Sstevel@tonic-gate 
4617*7c478bd9Sstevel@tonic-gate 	/* prepend link with dev_dir contents */
4618*7c478bd9Sstevel@tonic-gate 	(void) strcpy(devlink, dev_dir);
4619*7c478bd9Sstevel@tonic-gate 	(void) strcat(devlink, "/");
4620*7c478bd9Sstevel@tonic-gate 	(void) strcat(devlink, link);
4621*7c478bd9Sstevel@tonic-gate 
4622*7c478bd9Sstevel@tonic-gate 	if (lstat(devlink, &sb) != 0) {
4623*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FALSE);
4624*7c478bd9Sstevel@tonic-gate 	}
4625*7c478bd9Sstevel@tonic-gate 
4626*7c478bd9Sstevel@tonic-gate 	contents = NULL;
4627*7c478bd9Sstevel@tonic-gate 	type = 0;
4628*7c478bd9Sstevel@tonic-gate 	if (resolve_link(devlink, &contents, &type, NULL, 1) == TRUE) {
4629*7c478bd9Sstevel@tonic-gate 		rv = DEVFSADM_FALSE;
4630*7c478bd9Sstevel@tonic-gate 	} else {
4631*7c478bd9Sstevel@tonic-gate 		rv = DEVFSADM_TRUE;
4632*7c478bd9Sstevel@tonic-gate 	}
4633*7c478bd9Sstevel@tonic-gate 
4634*7c478bd9Sstevel@tonic-gate 	/*
4635*7c478bd9Sstevel@tonic-gate 	 * The link exists. Add it to the database
4636*7c478bd9Sstevel@tonic-gate 	 */
4637*7c478bd9Sstevel@tonic-gate 	(void) di_devlink_add_link(devlink_cache, link, contents, type);
4638*7c478bd9Sstevel@tonic-gate 	free(contents);
4639*7c478bd9Sstevel@tonic-gate 
4640*7c478bd9Sstevel@tonic-gate 	return (rv);
4641*7c478bd9Sstevel@tonic-gate }
4642*7c478bd9Sstevel@tonic-gate 
4643*7c478bd9Sstevel@tonic-gate /*
4644*7c478bd9Sstevel@tonic-gate  * devpath: Absolute path to /dev link
4645*7c478bd9Sstevel@tonic-gate  * content_p: Returns malloced string (link content)
4646*7c478bd9Sstevel@tonic-gate  * type_p: Returns link type: primary or secondary
4647*7c478bd9Sstevel@tonic-gate  * devfs_path: Returns malloced string: /devices path w/out "/devices"
4648*7c478bd9Sstevel@tonic-gate  * dangle: if set, check if link is dangling
4649*7c478bd9Sstevel@tonic-gate  * Returns:
4650*7c478bd9Sstevel@tonic-gate  *	TRUE if dangling
4651*7c478bd9Sstevel@tonic-gate  *	FALSE if not or if caller doesn't care
4652*7c478bd9Sstevel@tonic-gate  * Caller is assumed to have initialized pointer contents to NULL
4653*7c478bd9Sstevel@tonic-gate  */
4654*7c478bd9Sstevel@tonic-gate static int
4655*7c478bd9Sstevel@tonic-gate resolve_link(char *devpath, char **content_p, int *type_p, char **devfs_path,
4656*7c478bd9Sstevel@tonic-gate     int dangle)
4657*7c478bd9Sstevel@tonic-gate {
4658*7c478bd9Sstevel@tonic-gate 	char contents[PATH_MAX + 1];
4659*7c478bd9Sstevel@tonic-gate 	char stage_link[PATH_MAX + 1];
4660*7c478bd9Sstevel@tonic-gate 	char *fcn = "resolve_link: ";
4661*7c478bd9Sstevel@tonic-gate 	char *ptr;
4662*7c478bd9Sstevel@tonic-gate 	int linksize;
4663*7c478bd9Sstevel@tonic-gate 	int rv = TRUE;
4664*7c478bd9Sstevel@tonic-gate 	struct stat sb;
4665*7c478bd9Sstevel@tonic-gate 
4666*7c478bd9Sstevel@tonic-gate 	linksize = readlink(devpath, contents, PATH_MAX);
4667*7c478bd9Sstevel@tonic-gate 
4668*7c478bd9Sstevel@tonic-gate 	if (linksize <= 0) {
4669*7c478bd9Sstevel@tonic-gate 		return (FALSE);
4670*7c478bd9Sstevel@tonic-gate 	} else {
4671*7c478bd9Sstevel@tonic-gate 		contents[linksize] = '\0';
4672*7c478bd9Sstevel@tonic-gate 	}
4673*7c478bd9Sstevel@tonic-gate 	vprint(REMOVE_MID, "%s %s -> %s\n", fcn, devpath, contents);
4674*7c478bd9Sstevel@tonic-gate 
4675*7c478bd9Sstevel@tonic-gate 	if (content_p) {
4676*7c478bd9Sstevel@tonic-gate 		*content_p = s_strdup(contents);
4677*7c478bd9Sstevel@tonic-gate 	}
4678*7c478bd9Sstevel@tonic-gate 
4679*7c478bd9Sstevel@tonic-gate 	/*
4680*7c478bd9Sstevel@tonic-gate 	 * Check to see if this is a link pointing to another link in /dev.  The
4681*7c478bd9Sstevel@tonic-gate 	 * cheap way to do this is to look for a lack of ../devices/.
4682*7c478bd9Sstevel@tonic-gate 	 */
4683*7c478bd9Sstevel@tonic-gate 
4684*7c478bd9Sstevel@tonic-gate 	if (is_minor_node(contents, &ptr) == DEVFSADM_FALSE) {
4685*7c478bd9Sstevel@tonic-gate 
4686*7c478bd9Sstevel@tonic-gate 		if (type_p) {
4687*7c478bd9Sstevel@tonic-gate 			*type_p = DI_SECONDARY_LINK;
4688*7c478bd9Sstevel@tonic-gate 		}
4689*7c478bd9Sstevel@tonic-gate 
4690*7c478bd9Sstevel@tonic-gate 		/*
4691*7c478bd9Sstevel@tonic-gate 		 * assume that linkcontents is really a pointer to another
4692*7c478bd9Sstevel@tonic-gate 		 * link, and if so recurse and read its link contents.
4693*7c478bd9Sstevel@tonic-gate 		 */
4694*7c478bd9Sstevel@tonic-gate 		if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0)  {
4695*7c478bd9Sstevel@tonic-gate 			(void) strcpy(stage_link, dev_dir);
4696*7c478bd9Sstevel@tonic-gate 			(void) strcat(stage_link, "/");
4697*7c478bd9Sstevel@tonic-gate 			(void) strcpy(stage_link,
4698*7c478bd9Sstevel@tonic-gate 					&contents[strlen(DEV) + strlen("/")]);
4699*7c478bd9Sstevel@tonic-gate 		} else {
4700*7c478bd9Sstevel@tonic-gate 			if ((ptr = strrchr(devpath, '/')) == NULL) {
4701*7c478bd9Sstevel@tonic-gate 				vprint(REMOVE_MID, "%s%s -> %s invalid link. "
4702*7c478bd9Sstevel@tonic-gate 					"missing '/'\n", fcn, devpath,
4703*7c478bd9Sstevel@tonic-gate 					contents);
4704*7c478bd9Sstevel@tonic-gate 				return (TRUE);
4705*7c478bd9Sstevel@tonic-gate 			}
4706*7c478bd9Sstevel@tonic-gate 			*ptr = '\0';
4707*7c478bd9Sstevel@tonic-gate 			(void) strcpy(stage_link, devpath);
4708*7c478bd9Sstevel@tonic-gate 			*ptr = '/';
4709*7c478bd9Sstevel@tonic-gate 			(void) strcat(stage_link, "/");
4710*7c478bd9Sstevel@tonic-gate 			(void) strcat(stage_link, contents);
4711*7c478bd9Sstevel@tonic-gate 		}
4712*7c478bd9Sstevel@tonic-gate 		return (resolve_link(stage_link, NULL, NULL, devfs_path,
4713*7c478bd9Sstevel@tonic-gate 		    dangle));
4714*7c478bd9Sstevel@tonic-gate 	}
4715*7c478bd9Sstevel@tonic-gate 
4716*7c478bd9Sstevel@tonic-gate 	/* Current link points at a /devices minor node */
4717*7c478bd9Sstevel@tonic-gate 	if (type_p) {
4718*7c478bd9Sstevel@tonic-gate 		*type_p = DI_PRIMARY_LINK;
4719*7c478bd9Sstevel@tonic-gate 	}
4720*7c478bd9Sstevel@tonic-gate 
4721*7c478bd9Sstevel@tonic-gate 	if (devfs_path)
4722*7c478bd9Sstevel@tonic-gate 		*devfs_path = s_strdup(ptr);
4723*7c478bd9Sstevel@tonic-gate 
4724*7c478bd9Sstevel@tonic-gate 	rv = FALSE;
4725*7c478bd9Sstevel@tonic-gate 	if (dangle)
4726*7c478bd9Sstevel@tonic-gate 		rv = (stat(ptr - strlen(DEVICES), &sb) == -1);
4727*7c478bd9Sstevel@tonic-gate 
4728*7c478bd9Sstevel@tonic-gate 	vprint(REMOVE_MID, "%slink=%s, returning %s\n", fcn,
4729*7c478bd9Sstevel@tonic-gate 			devpath, ((rv == TRUE) ? "TRUE" : "FALSE"));
4730*7c478bd9Sstevel@tonic-gate 
4731*7c478bd9Sstevel@tonic-gate 	return (rv);
4732*7c478bd9Sstevel@tonic-gate }
4733*7c478bd9Sstevel@tonic-gate 
4734*7c478bd9Sstevel@tonic-gate /*
4735*7c478bd9Sstevel@tonic-gate  * Returns the substring of interest, given a path.
4736*7c478bd9Sstevel@tonic-gate  */
4737*7c478bd9Sstevel@tonic-gate static char *
4738*7c478bd9Sstevel@tonic-gate alloc_cmp_str(const char *path, devfsadm_enumerate_t *dep)
4739*7c478bd9Sstevel@tonic-gate {
4740*7c478bd9Sstevel@tonic-gate 	uint_t match;
4741*7c478bd9Sstevel@tonic-gate 	char *np, *ap, *mp;
4742*7c478bd9Sstevel@tonic-gate 	char *cmp_str = NULL;
4743*7c478bd9Sstevel@tonic-gate 	char at[] = "@";
4744*7c478bd9Sstevel@tonic-gate 	char *fcn = "alloc_cmp_str";
4745*7c478bd9Sstevel@tonic-gate 
4746*7c478bd9Sstevel@tonic-gate 	np = ap = mp = NULL;
4747*7c478bd9Sstevel@tonic-gate 
4748*7c478bd9Sstevel@tonic-gate 	/*
4749*7c478bd9Sstevel@tonic-gate 	 * extract match flags from the flags argument.
4750*7c478bd9Sstevel@tonic-gate 	 */
4751*7c478bd9Sstevel@tonic-gate 	match = (dep->flags & MATCH_MASK);
4752*7c478bd9Sstevel@tonic-gate 
4753*7c478bd9Sstevel@tonic-gate 	vprint(ENUM_MID, "%s: enumeration match type: 0x%x"
4754*7c478bd9Sstevel@tonic-gate 	    " path: %s\n", fcn, match, path);
4755*7c478bd9Sstevel@tonic-gate 
4756*7c478bd9Sstevel@tonic-gate 	/*
4757*7c478bd9Sstevel@tonic-gate 	 * MATCH_CALLBACK and MATCH_ALL are the only flags
4758*7c478bd9Sstevel@tonic-gate 	 * which may be used if "path" is a /dev path
4759*7c478bd9Sstevel@tonic-gate 	 */
4760*7c478bd9Sstevel@tonic-gate 	if (match == MATCH_CALLBACK) {
4761*7c478bd9Sstevel@tonic-gate 		if (dep->sel_fcn == NULL) {
4762*7c478bd9Sstevel@tonic-gate 			vprint(ENUM_MID, "%s: invalid enumerate"
4763*7c478bd9Sstevel@tonic-gate 			    " callback: path: %s\n", fcn, path);
4764*7c478bd9Sstevel@tonic-gate 			return (NULL);
4765*7c478bd9Sstevel@tonic-gate 		}
4766*7c478bd9Sstevel@tonic-gate 		cmp_str = dep->sel_fcn(path, dep->cb_arg);
4767*7c478bd9Sstevel@tonic-gate 		return (cmp_str);
4768*7c478bd9Sstevel@tonic-gate 	}
4769*7c478bd9Sstevel@tonic-gate 
4770*7c478bd9Sstevel@tonic-gate 	cmp_str = s_strdup(path);
4771*7c478bd9Sstevel@tonic-gate 
4772*7c478bd9Sstevel@tonic-gate 	if (match == MATCH_ALL) {
4773*7c478bd9Sstevel@tonic-gate 		return (cmp_str);
4774*7c478bd9Sstevel@tonic-gate 	}
4775*7c478bd9Sstevel@tonic-gate 
4776*7c478bd9Sstevel@tonic-gate 	/*
4777*7c478bd9Sstevel@tonic-gate 	 * The remaining flags make sense only for /devices
4778*7c478bd9Sstevel@tonic-gate 	 * paths
4779*7c478bd9Sstevel@tonic-gate 	 */
4780*7c478bd9Sstevel@tonic-gate 	if ((mp = strrchr(cmp_str, ':')) == NULL) {
4781*7c478bd9Sstevel@tonic-gate 		vprint(ENUM_MID, "%s: invalid path: %s\n",
4782*7c478bd9Sstevel@tonic-gate 		    fcn, path);
4783*7c478bd9Sstevel@tonic-gate 		goto err;
4784*7c478bd9Sstevel@tonic-gate 	}
4785*7c478bd9Sstevel@tonic-gate 
4786*7c478bd9Sstevel@tonic-gate 	if (match == MATCH_MINOR) {
4787*7c478bd9Sstevel@tonic-gate 		/* A NULL "match_arg" values implies entire minor */
4788*7c478bd9Sstevel@tonic-gate 		if (get_component(mp + 1, dep->match_arg) == NULL) {
4789*7c478bd9Sstevel@tonic-gate 			vprint(ENUM_MID, "%s: invalid minor component:"
4790*7c478bd9Sstevel@tonic-gate 			    " path: %s\n", fcn, path);
4791*7c478bd9Sstevel@tonic-gate 			goto err;
4792*7c478bd9Sstevel@tonic-gate 		}
4793*7c478bd9Sstevel@tonic-gate 		return (cmp_str);
4794*7c478bd9Sstevel@tonic-gate 	}
4795*7c478bd9Sstevel@tonic-gate 
4796*7c478bd9Sstevel@tonic-gate 	if ((np = strrchr(cmp_str, '/')) == NULL) {
4797*7c478bd9Sstevel@tonic-gate 		vprint(ENUM_MID, "%s: invalid path: %s\n", fcn, path);
4798*7c478bd9Sstevel@tonic-gate 		goto err;
4799*7c478bd9Sstevel@tonic-gate 	}
4800*7c478bd9Sstevel@tonic-gate 
4801*7c478bd9Sstevel@tonic-gate 	if (match == MATCH_PARENT) {
4802*7c478bd9Sstevel@tonic-gate 		if (strcmp(cmp_str, "/") == 0) {
4803*7c478bd9Sstevel@tonic-gate 			vprint(ENUM_MID, "%s: invalid path: %s\n",
4804*7c478bd9Sstevel@tonic-gate 			    fcn, path);
4805*7c478bd9Sstevel@tonic-gate 			goto err;
4806*7c478bd9Sstevel@tonic-gate 		}
4807*7c478bd9Sstevel@tonic-gate 
4808*7c478bd9Sstevel@tonic-gate 		if (np == cmp_str) {
4809*7c478bd9Sstevel@tonic-gate 			*(np + 1) = '\0';
4810*7c478bd9Sstevel@tonic-gate 		} else {
4811*7c478bd9Sstevel@tonic-gate 			*np = '\0';
4812*7c478bd9Sstevel@tonic-gate 		}
4813*7c478bd9Sstevel@tonic-gate 		return (cmp_str);
4814*7c478bd9Sstevel@tonic-gate 	}
4815*7c478bd9Sstevel@tonic-gate 
4816*7c478bd9Sstevel@tonic-gate 	/* ap can be NULL - Leaf address may not exist or be empty string */
4817*7c478bd9Sstevel@tonic-gate 	ap = strchr(np+1, '@');
4818*7c478bd9Sstevel@tonic-gate 
4819*7c478bd9Sstevel@tonic-gate 	/* minor is no longer of interest */
4820*7c478bd9Sstevel@tonic-gate 	*mp = '\0';
4821*7c478bd9Sstevel@tonic-gate 
4822*7c478bd9Sstevel@tonic-gate 	if (match == MATCH_NODE) {
4823*7c478bd9Sstevel@tonic-gate 		if (ap)
4824*7c478bd9Sstevel@tonic-gate 			*ap = '\0';
4825*7c478bd9Sstevel@tonic-gate 		return (cmp_str);
4826*7c478bd9Sstevel@tonic-gate 	} else if (match == MATCH_ADDR) {
4827*7c478bd9Sstevel@tonic-gate 		/*
4828*7c478bd9Sstevel@tonic-gate 		 * The empty string is a valid address. The only MATCH_ADDR
4829*7c478bd9Sstevel@tonic-gate 		 * allowed in this case is against the whole address or
4830*7c478bd9Sstevel@tonic-gate 		 * the first component of the address (match_arg=NULL/"0"/"1")
4831*7c478bd9Sstevel@tonic-gate 		 * Note that in this case, the path won't have an "@"
4832*7c478bd9Sstevel@tonic-gate 		 * As a result ap will be NULL. We fake up an ap = @'\0'
4833*7c478bd9Sstevel@tonic-gate 		 * so that get_component() will work correctly.
4834*7c478bd9Sstevel@tonic-gate 		 */
4835*7c478bd9Sstevel@tonic-gate 		if (ap == NULL) {
4836*7c478bd9Sstevel@tonic-gate 			ap = at;
4837*7c478bd9Sstevel@tonic-gate 		}
4838*7c478bd9Sstevel@tonic-gate 
4839*7c478bd9Sstevel@tonic-gate 		if (get_component(ap + 1, dep->match_arg) == NULL) {
4840*7c478bd9Sstevel@tonic-gate 			vprint(ENUM_MID, "%s: invalid leaf addr. component:"
4841*7c478bd9Sstevel@tonic-gate 			    " path: %s\n", fcn, path);
4842*7c478bd9Sstevel@tonic-gate 			goto err;
4843*7c478bd9Sstevel@tonic-gate 		}
4844*7c478bd9Sstevel@tonic-gate 		return (cmp_str);
4845*7c478bd9Sstevel@tonic-gate 	}
4846*7c478bd9Sstevel@tonic-gate 
4847*7c478bd9Sstevel@tonic-gate 	vprint(ENUM_MID, "%s: invalid enumeration flags: 0x%x"
4848*7c478bd9Sstevel@tonic-gate 		" path: %s\n", fcn, dep->flags, path);
4849*7c478bd9Sstevel@tonic-gate 
4850*7c478bd9Sstevel@tonic-gate 	/*FALLTHRU*/
4851*7c478bd9Sstevel@tonic-gate err:
4852*7c478bd9Sstevel@tonic-gate 	free(cmp_str);
4853*7c478bd9Sstevel@tonic-gate 	return (NULL);
4854*7c478bd9Sstevel@tonic-gate }
4855*7c478bd9Sstevel@tonic-gate 
4856*7c478bd9Sstevel@tonic-gate 
4857*7c478bd9Sstevel@tonic-gate /*
4858*7c478bd9Sstevel@tonic-gate  * "str" is expected to be a string with components separated by ','
4859*7c478bd9Sstevel@tonic-gate  * The terminating null char is considered a separator.
4860*7c478bd9Sstevel@tonic-gate  * get_component() will remove the portion of the string beyond
4861*7c478bd9Sstevel@tonic-gate  * the component indicated.
4862*7c478bd9Sstevel@tonic-gate  * If comp_str is NULL, the entire "str" is returned.
4863*7c478bd9Sstevel@tonic-gate  */
4864*7c478bd9Sstevel@tonic-gate static char *
4865*7c478bd9Sstevel@tonic-gate get_component(char *str, const char *comp_str)
4866*7c478bd9Sstevel@tonic-gate {
4867*7c478bd9Sstevel@tonic-gate 	long comp;
4868*7c478bd9Sstevel@tonic-gate 	char *cp;
4869*7c478bd9Sstevel@tonic-gate 
4870*7c478bd9Sstevel@tonic-gate 	if (str == NULL) {
4871*7c478bd9Sstevel@tonic-gate 		return (NULL);
4872*7c478bd9Sstevel@tonic-gate 	}
4873*7c478bd9Sstevel@tonic-gate 
4874*7c478bd9Sstevel@tonic-gate 	if (comp_str == NULL) {
4875*7c478bd9Sstevel@tonic-gate 		return (str);
4876*7c478bd9Sstevel@tonic-gate 	}
4877*7c478bd9Sstevel@tonic-gate 
4878*7c478bd9Sstevel@tonic-gate 	errno = 0;
4879*7c478bd9Sstevel@tonic-gate 	comp = strtol(comp_str, &cp, 10);
4880*7c478bd9Sstevel@tonic-gate 	if (errno != 0 || *cp != '\0' || comp < 0) {
4881*7c478bd9Sstevel@tonic-gate 		return (NULL);
4882*7c478bd9Sstevel@tonic-gate 	}
4883*7c478bd9Sstevel@tonic-gate 
4884*7c478bd9Sstevel@tonic-gate 	if (comp == 0)
4885*7c478bd9Sstevel@tonic-gate 		return (str);
4886*7c478bd9Sstevel@tonic-gate 
4887*7c478bd9Sstevel@tonic-gate 	for (cp = str; ; cp++) {
4888*7c478bd9Sstevel@tonic-gate 		if (*cp == ',' || *cp == '\0')
4889*7c478bd9Sstevel@tonic-gate 			comp--;
4890*7c478bd9Sstevel@tonic-gate 		if (*cp == '\0' || comp <= 0) {
4891*7c478bd9Sstevel@tonic-gate 			break;
4892*7c478bd9Sstevel@tonic-gate 		}
4893*7c478bd9Sstevel@tonic-gate 	}
4894*7c478bd9Sstevel@tonic-gate 
4895*7c478bd9Sstevel@tonic-gate 	if (comp == 0) {
4896*7c478bd9Sstevel@tonic-gate 		*cp = '\0';
4897*7c478bd9Sstevel@tonic-gate 	} else {
4898*7c478bd9Sstevel@tonic-gate 		str = NULL;
4899*7c478bd9Sstevel@tonic-gate 	}
4900*7c478bd9Sstevel@tonic-gate 
4901*7c478bd9Sstevel@tonic-gate 	return (str);
4902*7c478bd9Sstevel@tonic-gate }
4903*7c478bd9Sstevel@tonic-gate 
4904*7c478bd9Sstevel@tonic-gate 
4905*7c478bd9Sstevel@tonic-gate /*
4906*7c478bd9Sstevel@tonic-gate  * Enumerate serves as a generic counter as well as a means to determine
4907*7c478bd9Sstevel@tonic-gate  * logical unit/controller numbers for such items as disk and tape
4908*7c478bd9Sstevel@tonic-gate  * drives.
4909*7c478bd9Sstevel@tonic-gate  *
4910*7c478bd9Sstevel@tonic-gate  * rules[] is an array of  devfsadm_enumerate_t structures which defines
4911*7c478bd9Sstevel@tonic-gate  * the enumeration rules to be used for a specified set of links in /dev.
4912*7c478bd9Sstevel@tonic-gate  * The set of links is specified through regular expressions (of the flavor
4913*7c478bd9Sstevel@tonic-gate  * described in regex(5)). These regular expressions are used to determine
4914*7c478bd9Sstevel@tonic-gate  * the set of links in /dev to examine. The last path component in these
4915*7c478bd9Sstevel@tonic-gate  * regular expressions MUST contain a parenthesized subexpression surrounding
4916*7c478bd9Sstevel@tonic-gate  * the RE which is to be considered the enumerating component. The subexp
4917*7c478bd9Sstevel@tonic-gate  * member in a rule is the subexpression number of the enumerating
4918*7c478bd9Sstevel@tonic-gate  * component. Subexpressions in the last path component are numbered starting
4919*7c478bd9Sstevel@tonic-gate  * from 1.
4920*7c478bd9Sstevel@tonic-gate  *
4921*7c478bd9Sstevel@tonic-gate  * A cache of current id assignments is built up from existing symlinks and
4922*7c478bd9Sstevel@tonic-gate  * new assignments use the lowest unused id. Assignments are based on a
4923*7c478bd9Sstevel@tonic-gate  * match of a specified substring of a symlink's contents. If the specified
4924*7c478bd9Sstevel@tonic-gate  * component for the devfs_path argument matches the corresponding substring
4925*7c478bd9Sstevel@tonic-gate  * for a existing symlink's contents, the cached id is returned. Else, a new
4926*7c478bd9Sstevel@tonic-gate  * id is created and returned in *buf. *buf must be freed by the caller.
4927*7c478bd9Sstevel@tonic-gate  *
4928*7c478bd9Sstevel@tonic-gate  * An id assignment may be governed by a combination of rules, each rule
4929*7c478bd9Sstevel@tonic-gate  * applicable to a different subset of links in /dev. For example, controller
4930*7c478bd9Sstevel@tonic-gate  * numbers may be determined by a combination of disk symlinks in /dev/[r]dsk
4931*7c478bd9Sstevel@tonic-gate  * and controller symlinks in /dev/cfg, with the two sets requiring different
4932*7c478bd9Sstevel@tonic-gate  * rules to derive the "substring of interest". In such cases, the rules
4933*7c478bd9Sstevel@tonic-gate  * array will have more than one element.
4934*7c478bd9Sstevel@tonic-gate  */
4935*7c478bd9Sstevel@tonic-gate int
4936*7c478bd9Sstevel@tonic-gate devfsadm_enumerate_int(char *devfs_path, int index, char **buf,
4937*7c478bd9Sstevel@tonic-gate 			devfsadm_enumerate_t rules[], int nrules)
4938*7c478bd9Sstevel@tonic-gate {
4939*7c478bd9Sstevel@tonic-gate 	return (find_enum_id(rules, nrules,
4940*7c478bd9Sstevel@tonic-gate 	    devfs_path, index, "0", INTEGER, buf, 0));
4941*7c478bd9Sstevel@tonic-gate }
4942*7c478bd9Sstevel@tonic-gate 
4943*7c478bd9Sstevel@tonic-gate int
4944*7c478bd9Sstevel@tonic-gate disk_enumerate_int(char *devfs_path, int index, char **buf,
4945*7c478bd9Sstevel@tonic-gate     devfsadm_enumerate_t rules[], int nrules)
4946*7c478bd9Sstevel@tonic-gate {
4947*7c478bd9Sstevel@tonic-gate 	return (find_enum_id(rules, nrules,
4948*7c478bd9Sstevel@tonic-gate 	    devfs_path, index, "0", INTEGER, buf, 1));
4949*7c478bd9Sstevel@tonic-gate }
4950*7c478bd9Sstevel@tonic-gate 
4951*7c478bd9Sstevel@tonic-gate /*
4952*7c478bd9Sstevel@tonic-gate  * Same as above, but allows a starting value to be specified.
4953*7c478bd9Sstevel@tonic-gate  * Private to devfsadm.... used by devlinks.
4954*7c478bd9Sstevel@tonic-gate  */
4955*7c478bd9Sstevel@tonic-gate static int
4956*7c478bd9Sstevel@tonic-gate devfsadm_enumerate_int_start(char *devfs_path, int index, char **buf,
4957*7c478bd9Sstevel@tonic-gate 		devfsadm_enumerate_t rules[], int nrules, char *start)
4958*7c478bd9Sstevel@tonic-gate {
4959*7c478bd9Sstevel@tonic-gate 	return (find_enum_id(rules, nrules,
4960*7c478bd9Sstevel@tonic-gate 	    devfs_path, index, start, INTEGER, buf, 0));
4961*7c478bd9Sstevel@tonic-gate }
4962*7c478bd9Sstevel@tonic-gate 
4963*7c478bd9Sstevel@tonic-gate /*
4964*7c478bd9Sstevel@tonic-gate  *  devfsadm_enumerate_char serves as a generic counter returning
4965*7c478bd9Sstevel@tonic-gate  *  a single letter.
4966*7c478bd9Sstevel@tonic-gate  */
4967*7c478bd9Sstevel@tonic-gate int
4968*7c478bd9Sstevel@tonic-gate devfsadm_enumerate_char(char *devfs_path, int index, char **buf,
4969*7c478bd9Sstevel@tonic-gate 			devfsadm_enumerate_t rules[], int nrules)
4970*7c478bd9Sstevel@tonic-gate {
4971*7c478bd9Sstevel@tonic-gate 	return (find_enum_id(rules, nrules,
4972*7c478bd9Sstevel@tonic-gate 	    devfs_path, index, "a", LETTER, buf, 0));
4973*7c478bd9Sstevel@tonic-gate }
4974*7c478bd9Sstevel@tonic-gate 
4975*7c478bd9Sstevel@tonic-gate /*
4976*7c478bd9Sstevel@tonic-gate  * Same as above, but allows a starting char to be specified.
4977*7c478bd9Sstevel@tonic-gate  * Private to devfsadm - used by ports module (port_link.c)
4978*7c478bd9Sstevel@tonic-gate  */
4979*7c478bd9Sstevel@tonic-gate int
4980*7c478bd9Sstevel@tonic-gate devfsadm_enumerate_char_start(char *devfs_path, int index, char **buf,
4981*7c478bd9Sstevel@tonic-gate 	devfsadm_enumerate_t rules[], int nrules, char *start)
4982*7c478bd9Sstevel@tonic-gate {
4983*7c478bd9Sstevel@tonic-gate 	return (find_enum_id(rules, nrules,
4984*7c478bd9Sstevel@tonic-gate 	    devfs_path, index, start, LETTER, buf, 0));
4985*7c478bd9Sstevel@tonic-gate }
4986*7c478bd9Sstevel@tonic-gate 
4987*7c478bd9Sstevel@tonic-gate 
4988*7c478bd9Sstevel@tonic-gate /*
4989*7c478bd9Sstevel@tonic-gate  * For a given numeral_set (see get_cached_set for desc of numeral_set),
4990*7c478bd9Sstevel@tonic-gate  * search all cached entries looking for matches on a specified substring
4991*7c478bd9Sstevel@tonic-gate  * of devfs_path. The substring is derived from devfs_path based on the
4992*7c478bd9Sstevel@tonic-gate  * rule specified by "index". If a match is found on a cached entry,
4993*7c478bd9Sstevel@tonic-gate  * return the enumerated id in buf. Otherwise, create a new id by calling
4994*7c478bd9Sstevel@tonic-gate  * new_id, then cache and return that entry.
4995*7c478bd9Sstevel@tonic-gate  */
4996*7c478bd9Sstevel@tonic-gate static int
4997*7c478bd9Sstevel@tonic-gate find_enum_id(devfsadm_enumerate_t rules[], int nrules,
4998*7c478bd9Sstevel@tonic-gate 	char *devfs_path, int index, char *min, int type, char **buf,
4999*7c478bd9Sstevel@tonic-gate 	int multiple)
5000*7c478bd9Sstevel@tonic-gate {
5001*7c478bd9Sstevel@tonic-gate 	numeral_t *matchnp;
5002*7c478bd9Sstevel@tonic-gate 	numeral_t *numeral;
5003*7c478bd9Sstevel@tonic-gate 	int matchcount = 0;
5004*7c478bd9Sstevel@tonic-gate 	char *cmp_str;
5005*7c478bd9Sstevel@tonic-gate 	char *fcn = "find_enum_id";
5006*7c478bd9Sstevel@tonic-gate 	numeral_set_t *set;
5007*7c478bd9Sstevel@tonic-gate 
5008*7c478bd9Sstevel@tonic-gate 	if (rules == NULL) {
5009*7c478bd9Sstevel@tonic-gate 		vprint(ENUM_MID, "%s: no rules. path: %s\n",
5010*7c478bd9Sstevel@tonic-gate 		    fcn, devfs_path ? devfs_path : "<NULL path>");
5011*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
5012*7c478bd9Sstevel@tonic-gate 	}
5013*7c478bd9Sstevel@tonic-gate 
5014*7c478bd9Sstevel@tonic-gate 	if (devfs_path == NULL) {
5015*7c478bd9Sstevel@tonic-gate 		vprint(ENUM_MID, "%s: NULL path\n", fcn);
5016*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
5017*7c478bd9Sstevel@tonic-gate 	}
5018*7c478bd9Sstevel@tonic-gate 
5019*7c478bd9Sstevel@tonic-gate 	if (nrules <= 0 || index < 0 || index >= nrules || buf == NULL) {
5020*7c478bd9Sstevel@tonic-gate 		vprint(ENUM_MID, "%s: invalid arguments. path: %s\n",
5021*7c478bd9Sstevel@tonic-gate 		    fcn, devfs_path);
5022*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
5023*7c478bd9Sstevel@tonic-gate 	}
5024*7c478bd9Sstevel@tonic-gate 
5025*7c478bd9Sstevel@tonic-gate 	*buf = NULL;
5026*7c478bd9Sstevel@tonic-gate 
5027*7c478bd9Sstevel@tonic-gate 
5028*7c478bd9Sstevel@tonic-gate 	cmp_str = alloc_cmp_str(devfs_path, &rules[index]);
5029*7c478bd9Sstevel@tonic-gate 	if (cmp_str == NULL) {
5030*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
5031*7c478bd9Sstevel@tonic-gate 	}
5032*7c478bd9Sstevel@tonic-gate 
5033*7c478bd9Sstevel@tonic-gate 	if ((set = get_enum_cache(rules, nrules)) == NULL) {
5034*7c478bd9Sstevel@tonic-gate 		free(cmp_str);
5035*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
5036*7c478bd9Sstevel@tonic-gate 	}
5037*7c478bd9Sstevel@tonic-gate 
5038*7c478bd9Sstevel@tonic-gate 	assert(nrules == set->re_count);
5039*7c478bd9Sstevel@tonic-gate 
5040*7c478bd9Sstevel@tonic-gate 	/*
5041*7c478bd9Sstevel@tonic-gate 	 * Check and see if a matching entry is already cached.
5042*7c478bd9Sstevel@tonic-gate 	 */
5043*7c478bd9Sstevel@tonic-gate 	matchcount = lookup_enum_cache(set, cmp_str, rules, index,
5044*7c478bd9Sstevel@tonic-gate 	    &matchnp);
5045*7c478bd9Sstevel@tonic-gate 
5046*7c478bd9Sstevel@tonic-gate 	if (matchcount < 0 || matchcount > 1) {
5047*7c478bd9Sstevel@tonic-gate 		free(cmp_str);
5048*7c478bd9Sstevel@tonic-gate 		if (multiple && matchcount > 1)
5049*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_MULTIPLE);
5050*7c478bd9Sstevel@tonic-gate 		else
5051*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
5052*7c478bd9Sstevel@tonic-gate 	}
5053*7c478bd9Sstevel@tonic-gate 
5054*7c478bd9Sstevel@tonic-gate 	/* if matching entry already cached, return it */
5055*7c478bd9Sstevel@tonic-gate 	if (matchcount == 1) {
5056*7c478bd9Sstevel@tonic-gate 		*buf = s_strdup(matchnp->id);
5057*7c478bd9Sstevel@tonic-gate 		free(cmp_str);
5058*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
5059*7c478bd9Sstevel@tonic-gate 	}
5060*7c478bd9Sstevel@tonic-gate 
5061*7c478bd9Sstevel@tonic-gate 	/*
5062*7c478bd9Sstevel@tonic-gate 	 * no cached entry, initialize a numeral struct
5063*7c478bd9Sstevel@tonic-gate 	 * by calling new_id() and cache onto the numeral_set
5064*7c478bd9Sstevel@tonic-gate 	 */
5065*7c478bd9Sstevel@tonic-gate 	numeral = s_malloc(sizeof (numeral_t));
5066*7c478bd9Sstevel@tonic-gate 	numeral->id = new_id(set->headnumeral, type, min);
5067*7c478bd9Sstevel@tonic-gate 	numeral->full_path = s_strdup(devfs_path);
5068*7c478bd9Sstevel@tonic-gate 	numeral->rule_index = index;
5069*7c478bd9Sstevel@tonic-gate 	numeral->cmp_str = cmp_str;
5070*7c478bd9Sstevel@tonic-gate 	cmp_str = NULL;
5071*7c478bd9Sstevel@tonic-gate 
5072*7c478bd9Sstevel@tonic-gate 	/* insert to head of list for fast lookups */
5073*7c478bd9Sstevel@tonic-gate 	numeral->next = set->headnumeral;
5074*7c478bd9Sstevel@tonic-gate 	set->headnumeral = numeral;
5075*7c478bd9Sstevel@tonic-gate 
5076*7c478bd9Sstevel@tonic-gate 	*buf = s_strdup(numeral->id);
5077*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
5078*7c478bd9Sstevel@tonic-gate }
5079*7c478bd9Sstevel@tonic-gate 
5080*7c478bd9Sstevel@tonic-gate 
5081*7c478bd9Sstevel@tonic-gate /*
5082*7c478bd9Sstevel@tonic-gate  * Looks up the specified cache for a match with a specified string
5083*7c478bd9Sstevel@tonic-gate  * Returns:
5084*7c478bd9Sstevel@tonic-gate  *	-1	: on error.
5085*7c478bd9Sstevel@tonic-gate  *	0/1/2	: Number of matches.
5086*7c478bd9Sstevel@tonic-gate  * Returns the matching element only if there is a single match.
5087*7c478bd9Sstevel@tonic-gate  * If the "uncached" flag is set, derives the "cmp_str" afresh
5088*7c478bd9Sstevel@tonic-gate  * for the match instead of using cached values.
5089*7c478bd9Sstevel@tonic-gate  */
5090*7c478bd9Sstevel@tonic-gate static int
5091*7c478bd9Sstevel@tonic-gate lookup_enum_cache(numeral_set_t *set, char *cmp_str,
5092*7c478bd9Sstevel@tonic-gate 	devfsadm_enumerate_t rules[], int index, numeral_t **matchnpp)
5093*7c478bd9Sstevel@tonic-gate {
5094*7c478bd9Sstevel@tonic-gate 	int matchcount = 0, rv = -1;
5095*7c478bd9Sstevel@tonic-gate 	int uncached;
5096*7c478bd9Sstevel@tonic-gate 	numeral_t *np;
5097*7c478bd9Sstevel@tonic-gate 	char *fcn = "lookup_enum_cache";
5098*7c478bd9Sstevel@tonic-gate 	char *cp;
5099*7c478bd9Sstevel@tonic-gate 
5100*7c478bd9Sstevel@tonic-gate 	*matchnpp = NULL;
5101*7c478bd9Sstevel@tonic-gate 
5102*7c478bd9Sstevel@tonic-gate 	assert(index < set->re_count);
5103*7c478bd9Sstevel@tonic-gate 
5104*7c478bd9Sstevel@tonic-gate 	if (cmp_str == NULL) {
5105*7c478bd9Sstevel@tonic-gate 		return (-1);
5106*7c478bd9Sstevel@tonic-gate 	}
5107*7c478bd9Sstevel@tonic-gate 
5108*7c478bd9Sstevel@tonic-gate 	uncached = 0;
5109*7c478bd9Sstevel@tonic-gate 	if ((rules[index].flags & MATCH_UNCACHED) == MATCH_UNCACHED) {
5110*7c478bd9Sstevel@tonic-gate 		uncached = 1;
5111*7c478bd9Sstevel@tonic-gate 	}
5112*7c478bd9Sstevel@tonic-gate 
5113*7c478bd9Sstevel@tonic-gate 	/*
5114*7c478bd9Sstevel@tonic-gate 	 * Check and see if a matching entry is already cached.
5115*7c478bd9Sstevel@tonic-gate 	 */
5116*7c478bd9Sstevel@tonic-gate 	for (np = set->headnumeral; np != NULL; np = np->next) {
5117*7c478bd9Sstevel@tonic-gate 		if (np->cmp_str == NULL) {
5118*7c478bd9Sstevel@tonic-gate 			vprint(ENUM_MID, "%s: invalid entry in enumerate"
5119*7c478bd9Sstevel@tonic-gate 			    " cache. path: %s\n", fcn, np->full_path);
5120*7c478bd9Sstevel@tonic-gate 			return (-1);
5121*7c478bd9Sstevel@tonic-gate 		}
5122*7c478bd9Sstevel@tonic-gate 
5123*7c478bd9Sstevel@tonic-gate 		if (uncached) {
5124*7c478bd9Sstevel@tonic-gate 			vprint(CHATTY_MID, "%s: bypassing enumerate cache."
5125*7c478bd9Sstevel@tonic-gate 			    " path: %s\n", fcn, cmp_str);
5126*7c478bd9Sstevel@tonic-gate 			cp = alloc_cmp_str(np->full_path,
5127*7c478bd9Sstevel@tonic-gate 			    &rules[np->rule_index]);
5128*7c478bd9Sstevel@tonic-gate 			if (cp == NULL)
5129*7c478bd9Sstevel@tonic-gate 				return (-1);
5130*7c478bd9Sstevel@tonic-gate 			rv = strcmp(cmp_str, cp);
5131*7c478bd9Sstevel@tonic-gate 			free(cp);
5132*7c478bd9Sstevel@tonic-gate 		} else {
5133*7c478bd9Sstevel@tonic-gate 			rv = strcmp(cmp_str, np->cmp_str);
5134*7c478bd9Sstevel@tonic-gate 		}
5135*7c478bd9Sstevel@tonic-gate 
5136*7c478bd9Sstevel@tonic-gate 		if (rv == 0) {
5137*7c478bd9Sstevel@tonic-gate 			if (matchcount++ != 0) {
5138*7c478bd9Sstevel@tonic-gate 				break; /* more than 1 match. */
5139*7c478bd9Sstevel@tonic-gate 			}
5140*7c478bd9Sstevel@tonic-gate 			*matchnpp = np;
5141*7c478bd9Sstevel@tonic-gate 		}
5142*7c478bd9Sstevel@tonic-gate 	}
5143*7c478bd9Sstevel@tonic-gate 
5144*7c478bd9Sstevel@tonic-gate 	return (matchcount);
5145*7c478bd9Sstevel@tonic-gate }
5146*7c478bd9Sstevel@tonic-gate 
5147*7c478bd9Sstevel@tonic-gate #ifdef	DEBUG
5148*7c478bd9Sstevel@tonic-gate static void
5149*7c478bd9Sstevel@tonic-gate dump_enum_cache(numeral_set_t *setp)
5150*7c478bd9Sstevel@tonic-gate {
5151*7c478bd9Sstevel@tonic-gate 	int i;
5152*7c478bd9Sstevel@tonic-gate 	numeral_t *np;
5153*7c478bd9Sstevel@tonic-gate 	char *fcn = "dump_enum_cache";
5154*7c478bd9Sstevel@tonic-gate 
5155*7c478bd9Sstevel@tonic-gate 	vprint(ENUM_MID, "%s: re_count = %d\n", fcn, setp->re_count);
5156*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < setp->re_count; i++) {
5157*7c478bd9Sstevel@tonic-gate 		vprint(ENUM_MID, "%s: re[%d] = %s\n", fcn, i, setp->re[i]);
5158*7c478bd9Sstevel@tonic-gate 	}
5159*7c478bd9Sstevel@tonic-gate 
5160*7c478bd9Sstevel@tonic-gate 	for (np = setp->headnumeral; np != NULL; np = np->next) {
5161*7c478bd9Sstevel@tonic-gate 		vprint(ENUM_MID, "%s: id: %s\n", fcn, np->id);
5162*7c478bd9Sstevel@tonic-gate 		vprint(ENUM_MID, "%s: full_path: %s\n", fcn, np->full_path);
5163*7c478bd9Sstevel@tonic-gate 		vprint(ENUM_MID, "%s: rule_index: %d\n", fcn, np->rule_index);
5164*7c478bd9Sstevel@tonic-gate 		vprint(ENUM_MID, "%s: cmp_str: %s\n", fcn, np->cmp_str);
5165*7c478bd9Sstevel@tonic-gate 	}
5166*7c478bd9Sstevel@tonic-gate }
5167*7c478bd9Sstevel@tonic-gate #endif
5168*7c478bd9Sstevel@tonic-gate 
5169*7c478bd9Sstevel@tonic-gate /*
5170*7c478bd9Sstevel@tonic-gate  * For a given set of regular expressions in rules[], this function returns
5171*7c478bd9Sstevel@tonic-gate  * either a previously cached struct numeral_set or it will create and
5172*7c478bd9Sstevel@tonic-gate  * cache a new struct numeral_set.  There is only one struct numeral_set
5173*7c478bd9Sstevel@tonic-gate  * for the combination of REs present in rules[].  Each numeral_set contains
5174*7c478bd9Sstevel@tonic-gate  * the regular expressions in rules[] used for cache selection AND a linked
5175*7c478bd9Sstevel@tonic-gate  * list of struct numerals, ONE FOR EACH *UNIQUE* numeral or character ID
5176*7c478bd9Sstevel@tonic-gate  * selected by the grouping parenthesized subexpression found in the last
5177*7c478bd9Sstevel@tonic-gate  * path component of each rules[].re.  For example, the RE: "rmt/([0-9]+)"
5178*7c478bd9Sstevel@tonic-gate  * selects all the logical nodes of the correct form in dev/rmt/.
5179*7c478bd9Sstevel@tonic-gate  * Each rmt/X will store a *single* struct numeral... ie 0, 1, 2 each get a
5180*7c478bd9Sstevel@tonic-gate  * single struct numeral. There is no need to store more than a single logical
5181*7c478bd9Sstevel@tonic-gate  * node matching X since the information desired in the devfspath would be
5182*7c478bd9Sstevel@tonic-gate  * identical for the portion of the devfspath of interest. (the part up to,
5183*7c478bd9Sstevel@tonic-gate  * but not including the minor name in this example.)
5184*7c478bd9Sstevel@tonic-gate  *
5185*7c478bd9Sstevel@tonic-gate  * If the given numeral_set is not yet cached, call enumerate_recurse to
5186*7c478bd9Sstevel@tonic-gate  * create it.
5187*7c478bd9Sstevel@tonic-gate  */
5188*7c478bd9Sstevel@tonic-gate static numeral_set_t *
5189*7c478bd9Sstevel@tonic-gate get_enum_cache(devfsadm_enumerate_t rules[], int nrules)
5190*7c478bd9Sstevel@tonic-gate {
5191*7c478bd9Sstevel@tonic-gate 	/* linked list of numeral sets */
5192*7c478bd9Sstevel@tonic-gate 	numeral_set_t *setp;
5193*7c478bd9Sstevel@tonic-gate 	int i;
5194*7c478bd9Sstevel@tonic-gate 	char *path_left;
5195*7c478bd9Sstevel@tonic-gate 	char *fcn = "get_enum_cache";
5196*7c478bd9Sstevel@tonic-gate 
5197*7c478bd9Sstevel@tonic-gate 	/*
5198*7c478bd9Sstevel@tonic-gate 	 * See if we've already cached this numeral set.
5199*7c478bd9Sstevel@tonic-gate 	 */
5200*7c478bd9Sstevel@tonic-gate 	for (setp = head_numeral_set; setp != NULL; setp = setp->next) {
5201*7c478bd9Sstevel@tonic-gate 		/*
5202*7c478bd9Sstevel@tonic-gate 		 *  check all regexp's passed in function against
5203*7c478bd9Sstevel@tonic-gate 		 *  those in cached set.
5204*7c478bd9Sstevel@tonic-gate 		 */
5205*7c478bd9Sstevel@tonic-gate 		if (nrules != setp->re_count) {
5206*7c478bd9Sstevel@tonic-gate 			continue;
5207*7c478bd9Sstevel@tonic-gate 		}
5208*7c478bd9Sstevel@tonic-gate 
5209*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < nrules; i++) {
5210*7c478bd9Sstevel@tonic-gate 			if (strcmp(setp->re[i], rules[i].re) != 0) {
5211*7c478bd9Sstevel@tonic-gate 				break;
5212*7c478bd9Sstevel@tonic-gate 			}
5213*7c478bd9Sstevel@tonic-gate 		}
5214*7c478bd9Sstevel@tonic-gate 
5215*7c478bd9Sstevel@tonic-gate 		if (i == nrules) {
5216*7c478bd9Sstevel@tonic-gate 			return (setp);
5217*7c478bd9Sstevel@tonic-gate 		}
5218*7c478bd9Sstevel@tonic-gate 	}
5219*7c478bd9Sstevel@tonic-gate 
5220*7c478bd9Sstevel@tonic-gate 	/*
5221*7c478bd9Sstevel@tonic-gate 	 * If the MATCH_UNCACHED flag is set, we should not  be here.
5222*7c478bd9Sstevel@tonic-gate 	 */
5223*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrules; i++) {
5224*7c478bd9Sstevel@tonic-gate 		if ((rules[i].flags & MATCH_UNCACHED) == MATCH_UNCACHED) {
5225*7c478bd9Sstevel@tonic-gate 			vprint(ENUM_MID, "%s: invalid enumeration flags: "
5226*7c478bd9Sstevel@tonic-gate 			    "0x%x\n", fcn, rules[i].flags);
5227*7c478bd9Sstevel@tonic-gate 			return (NULL);
5228*7c478bd9Sstevel@tonic-gate 		}
5229*7c478bd9Sstevel@tonic-gate 	}
5230*7c478bd9Sstevel@tonic-gate 
5231*7c478bd9Sstevel@tonic-gate 	/*
5232*7c478bd9Sstevel@tonic-gate 	 *  Since we made it here, we have not yet cached the given set of
5233*7c478bd9Sstevel@tonic-gate 	 *  logical nodes matching the passed re.  Create a cached entry
5234*7c478bd9Sstevel@tonic-gate 	 *  struct numeral_set and populate it with a minimal set of
5235*7c478bd9Sstevel@tonic-gate 	 *  logical nodes from /dev.
5236*7c478bd9Sstevel@tonic-gate 	 */
5237*7c478bd9Sstevel@tonic-gate 
5238*7c478bd9Sstevel@tonic-gate 	setp = s_malloc(sizeof (numeral_set_t));
5239*7c478bd9Sstevel@tonic-gate 	setp->re = s_malloc(sizeof (char *) * nrules);
5240*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrules; i++) {
5241*7c478bd9Sstevel@tonic-gate 		setp->re[i] = s_strdup(rules[i].re);
5242*7c478bd9Sstevel@tonic-gate 	}
5243*7c478bd9Sstevel@tonic-gate 	setp->re_count = nrules;
5244*7c478bd9Sstevel@tonic-gate 	setp->headnumeral = NULL;
5245*7c478bd9Sstevel@tonic-gate 
5246*7c478bd9Sstevel@tonic-gate 	/* put this new cached set on the cached set list */
5247*7c478bd9Sstevel@tonic-gate 	setp->next = head_numeral_set;
5248*7c478bd9Sstevel@tonic-gate 	head_numeral_set = setp;
5249*7c478bd9Sstevel@tonic-gate 
5250*7c478bd9Sstevel@tonic-gate 	/*
5251*7c478bd9Sstevel@tonic-gate 	 * For each RE, search disk and cache any matches on the
5252*7c478bd9Sstevel@tonic-gate 	 * numeral list.  We are careful to use global_dev_dir here since
5253*7c478bd9Sstevel@tonic-gate 	 * for zones, we want to use the global zone's enumeration as the
5254*7c478bd9Sstevel@tonic-gate 	 * source for enumeration within the zone.  Otherwise, for example,
5255*7c478bd9Sstevel@tonic-gate 	 * controller numbering would be wrong within the zone.
5256*7c478bd9Sstevel@tonic-gate 	 */
5257*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrules; i++) {
5258*7c478bd9Sstevel@tonic-gate 		path_left = s_strdup(setp->re[i]);
5259*7c478bd9Sstevel@tonic-gate 		enumerate_recurse(global_dev_dir, path_left, setp, rules, i);
5260*7c478bd9Sstevel@tonic-gate 		free(path_left);
5261*7c478bd9Sstevel@tonic-gate 	}
5262*7c478bd9Sstevel@tonic-gate 
5263*7c478bd9Sstevel@tonic-gate #ifdef	DEBUG
5264*7c478bd9Sstevel@tonic-gate 	dump_enum_cache(setp);
5265*7c478bd9Sstevel@tonic-gate #endif
5266*7c478bd9Sstevel@tonic-gate 
5267*7c478bd9Sstevel@tonic-gate 	return (setp);
5268*7c478bd9Sstevel@tonic-gate }
5269*7c478bd9Sstevel@tonic-gate 
5270*7c478bd9Sstevel@tonic-gate 
5271*7c478bd9Sstevel@tonic-gate /*
5272*7c478bd9Sstevel@tonic-gate  * This function stats the pathname namebuf.  If this is a directory
5273*7c478bd9Sstevel@tonic-gate  * entry, we recurse down dname/fname until we find the first symbolic
5274*7c478bd9Sstevel@tonic-gate  * link, and then stat and return it.  This is valid for the same reason
5275*7c478bd9Sstevel@tonic-gate  * that we only need to read a single pathname for multiple matching
5276*7c478bd9Sstevel@tonic-gate  * logical ID's... ie, all the logical nodes should contain identical
5277*7c478bd9Sstevel@tonic-gate  * physical paths for the parts we are interested.
5278*7c478bd9Sstevel@tonic-gate  */
5279*7c478bd9Sstevel@tonic-gate int
5280*7c478bd9Sstevel@tonic-gate get_stat_info(char *namebuf, struct stat *sb)
5281*7c478bd9Sstevel@tonic-gate {
5282*7c478bd9Sstevel@tonic-gate 	struct dirent *entp;
5283*7c478bd9Sstevel@tonic-gate 	struct dirent *retp;
5284*7c478bd9Sstevel@tonic-gate 	DIR *dp;
5285*7c478bd9Sstevel@tonic-gate 	char *cp;
5286*7c478bd9Sstevel@tonic-gate 
5287*7c478bd9Sstevel@tonic-gate 	if (lstat(namebuf, sb) < 0) {
5288*7c478bd9Sstevel@tonic-gate 		(void) err_print(LSTAT_FAILED, namebuf, strerror(errno));
5289*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
5290*7c478bd9Sstevel@tonic-gate 	}
5291*7c478bd9Sstevel@tonic-gate 
5292*7c478bd9Sstevel@tonic-gate 	if ((sb->st_mode & S_IFMT) == S_IFLNK) {
5293*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
5294*7c478bd9Sstevel@tonic-gate 	}
5295*7c478bd9Sstevel@tonic-gate 
5296*7c478bd9Sstevel@tonic-gate 	/*
5297*7c478bd9Sstevel@tonic-gate 	 * If it is a dir, recurse down until we find a link and
5298*7c478bd9Sstevel@tonic-gate 	 * then use the link.
5299*7c478bd9Sstevel@tonic-gate 	 */
5300*7c478bd9Sstevel@tonic-gate 	if ((sb->st_mode & S_IFMT) == S_IFDIR) {
5301*7c478bd9Sstevel@tonic-gate 
5302*7c478bd9Sstevel@tonic-gate 		if ((dp = opendir(namebuf)) == NULL) {
5303*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
5304*7c478bd9Sstevel@tonic-gate 		}
5305*7c478bd9Sstevel@tonic-gate 
5306*7c478bd9Sstevel@tonic-gate 		entp = s_malloc(PATH_MAX + 1 + sizeof (struct  dirent));
5307*7c478bd9Sstevel@tonic-gate 
5308*7c478bd9Sstevel@tonic-gate 		/*
5309*7c478bd9Sstevel@tonic-gate 		 *  Search each dir entry looking for a symlink.  Return
5310*7c478bd9Sstevel@tonic-gate 		 *  the first symlink found in namebuf.  Recurse dirs.
5311*7c478bd9Sstevel@tonic-gate 		 */
5312*7c478bd9Sstevel@tonic-gate 		while (readdir_r(dp,  entp, &retp) == 0) {
5313*7c478bd9Sstevel@tonic-gate 			if (retp == NULL) {
5314*7c478bd9Sstevel@tonic-gate 				break;
5315*7c478bd9Sstevel@tonic-gate 			}
5316*7c478bd9Sstevel@tonic-gate 			if (strcmp(entp->d_name, ".") == 0 ||
5317*7c478bd9Sstevel@tonic-gate 			    strcmp(entp->d_name, "..") == 0) {
5318*7c478bd9Sstevel@tonic-gate 				continue;
5319*7c478bd9Sstevel@tonic-gate 			}
5320*7c478bd9Sstevel@tonic-gate 
5321*7c478bd9Sstevel@tonic-gate 			cp = namebuf + strlen(namebuf);
5322*7c478bd9Sstevel@tonic-gate 			(void) strcat(namebuf, "/");
5323*7c478bd9Sstevel@tonic-gate 			(void) strcat(namebuf, entp->d_name);
5324*7c478bd9Sstevel@tonic-gate 			if (get_stat_info(namebuf, sb) == DEVFSADM_SUCCESS) {
5325*7c478bd9Sstevel@tonic-gate 				free(entp);
5326*7c478bd9Sstevel@tonic-gate 				s_closedir(dp);
5327*7c478bd9Sstevel@tonic-gate 				return (DEVFSADM_SUCCESS);
5328*7c478bd9Sstevel@tonic-gate 			}
5329*7c478bd9Sstevel@tonic-gate 			*cp = '\0';
5330*7c478bd9Sstevel@tonic-gate 		}
5331*7c478bd9Sstevel@tonic-gate 		free(entp);
5332*7c478bd9Sstevel@tonic-gate 		s_closedir(dp);
5333*7c478bd9Sstevel@tonic-gate 	}
5334*7c478bd9Sstevel@tonic-gate 
5335*7c478bd9Sstevel@tonic-gate 	/* no symlink found, so return error */
5336*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_FAILURE);
5337*7c478bd9Sstevel@tonic-gate }
5338*7c478bd9Sstevel@tonic-gate 
5339*7c478bd9Sstevel@tonic-gate /*
5340*7c478bd9Sstevel@tonic-gate  * An existing matching ID was not found, so this function is called to
5341*7c478bd9Sstevel@tonic-gate  * create the next lowest ID.  In the INTEGER case, return the next
5342*7c478bd9Sstevel@tonic-gate  * lowest unused integer.  In the case of LETTER, return the next lowest
5343*7c478bd9Sstevel@tonic-gate  * unused letter.  Return empty string if all 26 are used.
5344*7c478bd9Sstevel@tonic-gate  * Only IDs >= min will be returned.
5345*7c478bd9Sstevel@tonic-gate  */
5346*7c478bd9Sstevel@tonic-gate char *
5347*7c478bd9Sstevel@tonic-gate new_id(numeral_t *numeral, int type, char *min)
5348*7c478bd9Sstevel@tonic-gate {
5349*7c478bd9Sstevel@tonic-gate 	int imin;
5350*7c478bd9Sstevel@tonic-gate 	temp_t *temp;
5351*7c478bd9Sstevel@tonic-gate 	temp_t *ptr;
5352*7c478bd9Sstevel@tonic-gate 	temp_t **previous;
5353*7c478bd9Sstevel@tonic-gate 	temp_t *head = NULL;
5354*7c478bd9Sstevel@tonic-gate 	char *retval;
5355*7c478bd9Sstevel@tonic-gate 	static char tempbuff[8];
5356*7c478bd9Sstevel@tonic-gate 	numeral_t *np;
5357*7c478bd9Sstevel@tonic-gate 
5358*7c478bd9Sstevel@tonic-gate 	if (type == LETTER) {
5359*7c478bd9Sstevel@tonic-gate 
5360*7c478bd9Sstevel@tonic-gate 		char letter[26], i;
5361*7c478bd9Sstevel@tonic-gate 
5362*7c478bd9Sstevel@tonic-gate 		if (numeral == NULL) {
5363*7c478bd9Sstevel@tonic-gate 			return (s_strdup(min));
5364*7c478bd9Sstevel@tonic-gate 		}
5365*7c478bd9Sstevel@tonic-gate 
5366*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < 26; i++) {
5367*7c478bd9Sstevel@tonic-gate 			letter[i] = 0;
5368*7c478bd9Sstevel@tonic-gate 		}
5369*7c478bd9Sstevel@tonic-gate 
5370*7c478bd9Sstevel@tonic-gate 		for (np = numeral; np != NULL; np = np->next) {
5371*7c478bd9Sstevel@tonic-gate 			letter[*np->id - 'a']++;
5372*7c478bd9Sstevel@tonic-gate 		}
5373*7c478bd9Sstevel@tonic-gate 
5374*7c478bd9Sstevel@tonic-gate 		imin = *min - 'a';
5375*7c478bd9Sstevel@tonic-gate 
5376*7c478bd9Sstevel@tonic-gate 		for (i = imin; i < 26; i++) {
5377*7c478bd9Sstevel@tonic-gate 			if (letter[i] == 0) {
5378*7c478bd9Sstevel@tonic-gate 				retval = s_malloc(2);
5379*7c478bd9Sstevel@tonic-gate 				retval[0] = 'a' + i;
5380*7c478bd9Sstevel@tonic-gate 				retval[1] = '\0';
5381*7c478bd9Sstevel@tonic-gate 				return (retval);
5382*7c478bd9Sstevel@tonic-gate 			}
5383*7c478bd9Sstevel@tonic-gate 		}
5384*7c478bd9Sstevel@tonic-gate 
5385*7c478bd9Sstevel@tonic-gate 		return (s_strdup(""));
5386*7c478bd9Sstevel@tonic-gate 	}
5387*7c478bd9Sstevel@tonic-gate 
5388*7c478bd9Sstevel@tonic-gate 	if (type == INTEGER) {
5389*7c478bd9Sstevel@tonic-gate 
5390*7c478bd9Sstevel@tonic-gate 		if (numeral == NULL) {
5391*7c478bd9Sstevel@tonic-gate 			return (s_strdup(min));
5392*7c478bd9Sstevel@tonic-gate 		}
5393*7c478bd9Sstevel@tonic-gate 
5394*7c478bd9Sstevel@tonic-gate 		imin = atoi(min);
5395*7c478bd9Sstevel@tonic-gate 
5396*7c478bd9Sstevel@tonic-gate 		/* sort list */
5397*7c478bd9Sstevel@tonic-gate 		for (np = numeral; np != NULL; np = np->next) {
5398*7c478bd9Sstevel@tonic-gate 			temp = s_malloc(sizeof (temp_t));
5399*7c478bd9Sstevel@tonic-gate 			temp->integer = atoi(np->id);
5400*7c478bd9Sstevel@tonic-gate 			temp->next = NULL;
5401*7c478bd9Sstevel@tonic-gate 
5402*7c478bd9Sstevel@tonic-gate 			previous = &head;
5403*7c478bd9Sstevel@tonic-gate 			for (ptr = head; ptr != NULL; ptr = ptr->next) {
5404*7c478bd9Sstevel@tonic-gate 				if (temp->integer < ptr->integer) {
5405*7c478bd9Sstevel@tonic-gate 					temp->next = ptr;
5406*7c478bd9Sstevel@tonic-gate 					*previous = temp;
5407*7c478bd9Sstevel@tonic-gate 					break;
5408*7c478bd9Sstevel@tonic-gate 				}
5409*7c478bd9Sstevel@tonic-gate 				previous = &(ptr->next);
5410*7c478bd9Sstevel@tonic-gate 			}
5411*7c478bd9Sstevel@tonic-gate 			if (ptr == NULL) {
5412*7c478bd9Sstevel@tonic-gate 				*previous = temp;
5413*7c478bd9Sstevel@tonic-gate 			}
5414*7c478bd9Sstevel@tonic-gate 		}
5415*7c478bd9Sstevel@tonic-gate 
5416*7c478bd9Sstevel@tonic-gate 		/* now search sorted list for first hole >= imin */
5417*7c478bd9Sstevel@tonic-gate 		for (ptr = head; ptr != NULL; ptr = ptr->next) {
5418*7c478bd9Sstevel@tonic-gate 			if (imin == ptr->integer) {
5419*7c478bd9Sstevel@tonic-gate 				imin++;
5420*7c478bd9Sstevel@tonic-gate 			} else {
5421*7c478bd9Sstevel@tonic-gate 				if (imin < ptr->integer) {
5422*7c478bd9Sstevel@tonic-gate 					break;
5423*7c478bd9Sstevel@tonic-gate 				}
5424*7c478bd9Sstevel@tonic-gate 			}
5425*7c478bd9Sstevel@tonic-gate 
5426*7c478bd9Sstevel@tonic-gate 		}
5427*7c478bd9Sstevel@tonic-gate 
5428*7c478bd9Sstevel@tonic-gate 		/* free temp list */
5429*7c478bd9Sstevel@tonic-gate 		for (ptr = head; ptr != NULL; ) {
5430*7c478bd9Sstevel@tonic-gate 			temp = ptr;
5431*7c478bd9Sstevel@tonic-gate 			ptr = ptr->next;
5432*7c478bd9Sstevel@tonic-gate 			free(temp);
5433*7c478bd9Sstevel@tonic-gate 		}
5434*7c478bd9Sstevel@tonic-gate 
5435*7c478bd9Sstevel@tonic-gate 		(void) sprintf(tempbuff, "%d", imin);
5436*7c478bd9Sstevel@tonic-gate 		return (s_strdup(tempbuff));
5437*7c478bd9Sstevel@tonic-gate 	}
5438*7c478bd9Sstevel@tonic-gate 
5439*7c478bd9Sstevel@tonic-gate 	return (s_strdup(""));
5440*7c478bd9Sstevel@tonic-gate }
5441*7c478bd9Sstevel@tonic-gate 
5442*7c478bd9Sstevel@tonic-gate /*
5443*7c478bd9Sstevel@tonic-gate  * Search current_dir for all files which match the first path component
5444*7c478bd9Sstevel@tonic-gate  * of path_left, which is an RE.  If a match is found, but there are more
5445*7c478bd9Sstevel@tonic-gate  * components of path_left, then recurse, otherwise, if we have reached
5446*7c478bd9Sstevel@tonic-gate  * the last component of path_left, call create_cached_numerals for each
5447*7c478bd9Sstevel@tonic-gate  * file.   At some point, recurse_dev_re() should be rewritten so that this
5448*7c478bd9Sstevel@tonic-gate  * function can be eliminated.
5449*7c478bd9Sstevel@tonic-gate  */
5450*7c478bd9Sstevel@tonic-gate static void
5451*7c478bd9Sstevel@tonic-gate enumerate_recurse(char *current_dir, char *path_left, numeral_set_t *setp,
5452*7c478bd9Sstevel@tonic-gate 	    devfsadm_enumerate_t rules[], int index)
5453*7c478bd9Sstevel@tonic-gate {
5454*7c478bd9Sstevel@tonic-gate 	char *slash;
5455*7c478bd9Sstevel@tonic-gate 	char *new_path;
5456*7c478bd9Sstevel@tonic-gate 	char *numeral_id;
5457*7c478bd9Sstevel@tonic-gate 	struct dirent *entp;
5458*7c478bd9Sstevel@tonic-gate 	struct dirent *retp;
5459*7c478bd9Sstevel@tonic-gate 	DIR *dp;
5460*7c478bd9Sstevel@tonic-gate 
5461*7c478bd9Sstevel@tonic-gate 	if ((dp = opendir(current_dir)) == NULL) {
5462*7c478bd9Sstevel@tonic-gate 		return;
5463*7c478bd9Sstevel@tonic-gate 	}
5464*7c478bd9Sstevel@tonic-gate 
5465*7c478bd9Sstevel@tonic-gate 	/* get rid of any extra '/' */
5466*7c478bd9Sstevel@tonic-gate 	while (*path_left == '/') {
5467*7c478bd9Sstevel@tonic-gate 		path_left++;
5468*7c478bd9Sstevel@tonic-gate 	}
5469*7c478bd9Sstevel@tonic-gate 
5470*7c478bd9Sstevel@tonic-gate 	if (slash = strchr(path_left, '/')) {
5471*7c478bd9Sstevel@tonic-gate 		*slash = '\0';
5472*7c478bd9Sstevel@tonic-gate 	}
5473*7c478bd9Sstevel@tonic-gate 
5474*7c478bd9Sstevel@tonic-gate 	entp = s_malloc(PATH_MAX + 1 + sizeof (struct  dirent));
5475*7c478bd9Sstevel@tonic-gate 
5476*7c478bd9Sstevel@tonic-gate 	while (readdir_r(dp, entp, &retp) == 0) {
5477*7c478bd9Sstevel@tonic-gate 
5478*7c478bd9Sstevel@tonic-gate 		if (retp == NULL) {
5479*7c478bd9Sstevel@tonic-gate 			break;
5480*7c478bd9Sstevel@tonic-gate 		}
5481*7c478bd9Sstevel@tonic-gate 
5482*7c478bd9Sstevel@tonic-gate 		if (strcmp(entp->d_name, ".") == 0 ||
5483*7c478bd9Sstevel@tonic-gate 		    strcmp(entp->d_name, "..") == 0) {
5484*7c478bd9Sstevel@tonic-gate 			continue;
5485*7c478bd9Sstevel@tonic-gate 		}
5486*7c478bd9Sstevel@tonic-gate 
5487*7c478bd9Sstevel@tonic-gate 		/*
5488*7c478bd9Sstevel@tonic-gate 		 *  Returns true if path_left matches entp->d_name
5489*7c478bd9Sstevel@tonic-gate 		 *  If it is the last path component, pass subexp
5490*7c478bd9Sstevel@tonic-gate 		 *  so that it will return the corresponding ID in
5491*7c478bd9Sstevel@tonic-gate 		 *  numeral_id.
5492*7c478bd9Sstevel@tonic-gate 		 */
5493*7c478bd9Sstevel@tonic-gate 		numeral_id = NULL;
5494*7c478bd9Sstevel@tonic-gate 		if (match_path_component(path_left, entp->d_name, &numeral_id,
5495*7c478bd9Sstevel@tonic-gate 				    slash ? 0 : rules[index].subexp)) {
5496*7c478bd9Sstevel@tonic-gate 
5497*7c478bd9Sstevel@tonic-gate 			new_path = s_malloc(strlen(current_dir) +
5498*7c478bd9Sstevel@tonic-gate 					    strlen(entp->d_name) + 2);
5499*7c478bd9Sstevel@tonic-gate 
5500*7c478bd9Sstevel@tonic-gate 			(void) strcpy(new_path, current_dir);
5501*7c478bd9Sstevel@tonic-gate 			(void) strcat(new_path, "/");
5502*7c478bd9Sstevel@tonic-gate 			(void) strcat(new_path, entp->d_name);
5503*7c478bd9Sstevel@tonic-gate 
5504*7c478bd9Sstevel@tonic-gate 			if (slash != NULL) {
5505*7c478bd9Sstevel@tonic-gate 				enumerate_recurse(new_path, slash + 1,
5506*7c478bd9Sstevel@tonic-gate 				    setp, rules, index);
5507*7c478bd9Sstevel@tonic-gate 			} else {
5508*7c478bd9Sstevel@tonic-gate 				create_cached_numeral(new_path, setp,
5509*7c478bd9Sstevel@tonic-gate 				    numeral_id, rules, index);
5510*7c478bd9Sstevel@tonic-gate 				if (numeral_id != NULL) {
5511*7c478bd9Sstevel@tonic-gate 					free(numeral_id);
5512*7c478bd9Sstevel@tonic-gate 				}
5513*7c478bd9Sstevel@tonic-gate 			}
5514*7c478bd9Sstevel@tonic-gate 			free(new_path);
5515*7c478bd9Sstevel@tonic-gate 		}
5516*7c478bd9Sstevel@tonic-gate 	}
5517*7c478bd9Sstevel@tonic-gate 
5518*7c478bd9Sstevel@tonic-gate 	if (slash != NULL) {
5519*7c478bd9Sstevel@tonic-gate 		*slash = '/';
5520*7c478bd9Sstevel@tonic-gate 	}
5521*7c478bd9Sstevel@tonic-gate 	free(entp);
5522*7c478bd9Sstevel@tonic-gate 	s_closedir(dp);
5523*7c478bd9Sstevel@tonic-gate }
5524*7c478bd9Sstevel@tonic-gate 
5525*7c478bd9Sstevel@tonic-gate 
5526*7c478bd9Sstevel@tonic-gate /*
5527*7c478bd9Sstevel@tonic-gate  * Returns true if file matches file_re.  If subexp is non-zero, it means
5528*7c478bd9Sstevel@tonic-gate  * we are searching the last path component and need to return the
5529*7c478bd9Sstevel@tonic-gate  * parenthesized subexpression subexp in id.
5530*7c478bd9Sstevel@tonic-gate  *
5531*7c478bd9Sstevel@tonic-gate  */
5532*7c478bd9Sstevel@tonic-gate static int
5533*7c478bd9Sstevel@tonic-gate match_path_component(char *file_re,  char *file,  char **id, int subexp)
5534*7c478bd9Sstevel@tonic-gate {
5535*7c478bd9Sstevel@tonic-gate 	regex_t re1;
5536*7c478bd9Sstevel@tonic-gate 	int match = 0;
5537*7c478bd9Sstevel@tonic-gate 	int nelements;
5538*7c478bd9Sstevel@tonic-gate 	regmatch_t *pmatch;
5539*7c478bd9Sstevel@tonic-gate 
5540*7c478bd9Sstevel@tonic-gate 	if (subexp != 0) {
5541*7c478bd9Sstevel@tonic-gate 		nelements = subexp + 1;
5542*7c478bd9Sstevel@tonic-gate 		pmatch = (regmatch_t *)
5543*7c478bd9Sstevel@tonic-gate 			s_malloc(sizeof (regmatch_t) * nelements);
5544*7c478bd9Sstevel@tonic-gate 	} else {
5545*7c478bd9Sstevel@tonic-gate 		pmatch = NULL;
5546*7c478bd9Sstevel@tonic-gate 		nelements = 0;
5547*7c478bd9Sstevel@tonic-gate 	}
5548*7c478bd9Sstevel@tonic-gate 
5549*7c478bd9Sstevel@tonic-gate 	if (regcomp(&re1, file_re, REG_EXTENDED) != 0) {
5550*7c478bd9Sstevel@tonic-gate 		if (pmatch != NULL) {
5551*7c478bd9Sstevel@tonic-gate 			free(pmatch);
5552*7c478bd9Sstevel@tonic-gate 		}
5553*7c478bd9Sstevel@tonic-gate 		return (0);
5554*7c478bd9Sstevel@tonic-gate 	}
5555*7c478bd9Sstevel@tonic-gate 
5556*7c478bd9Sstevel@tonic-gate 	if (regexec(&re1, file, nelements, pmatch, 0) == 0) {
5557*7c478bd9Sstevel@tonic-gate 		match = 1;
5558*7c478bd9Sstevel@tonic-gate 	}
5559*7c478bd9Sstevel@tonic-gate 
5560*7c478bd9Sstevel@tonic-gate 	if ((match != 0) && (subexp != 0)) {
5561*7c478bd9Sstevel@tonic-gate 		int size = pmatch[subexp].rm_eo - pmatch[subexp].rm_so;
5562*7c478bd9Sstevel@tonic-gate 		*id = s_malloc(size + 1);
5563*7c478bd9Sstevel@tonic-gate 		(void) strncpy(*id, &file[pmatch[subexp].rm_so], size);
5564*7c478bd9Sstevel@tonic-gate 		(*id)[size] = '\0';
5565*7c478bd9Sstevel@tonic-gate 	}
5566*7c478bd9Sstevel@tonic-gate 
5567*7c478bd9Sstevel@tonic-gate 	if (pmatch != NULL) {
5568*7c478bd9Sstevel@tonic-gate 		free(pmatch);
5569*7c478bd9Sstevel@tonic-gate 	}
5570*7c478bd9Sstevel@tonic-gate 	regfree(&re1);
5571*7c478bd9Sstevel@tonic-gate 	return (match);
5572*7c478bd9Sstevel@tonic-gate }
5573*7c478bd9Sstevel@tonic-gate 
5574*7c478bd9Sstevel@tonic-gate /*
5575*7c478bd9Sstevel@tonic-gate  * This function is called for every file which matched the leaf
5576*7c478bd9Sstevel@tonic-gate  * component of the RE.  If the "numeral_id" is not already on the
5577*7c478bd9Sstevel@tonic-gate  * numeral set's numeral list, add it and its physical path.
5578*7c478bd9Sstevel@tonic-gate  */
5579*7c478bd9Sstevel@tonic-gate static void
5580*7c478bd9Sstevel@tonic-gate create_cached_numeral(char *path, numeral_set_t *setp, char *numeral_id,
5581*7c478bd9Sstevel@tonic-gate 	devfsadm_enumerate_t rules[], int index)
5582*7c478bd9Sstevel@tonic-gate {
5583*7c478bd9Sstevel@tonic-gate 	char linkbuf[PATH_MAX + 1];
5584*7c478bd9Sstevel@tonic-gate 	char lpath[PATH_MAX + 1];
5585*7c478bd9Sstevel@tonic-gate 	char *linkptr, *cmp_str;
5586*7c478bd9Sstevel@tonic-gate 	numeral_t *np;
5587*7c478bd9Sstevel@tonic-gate 	int linksize;
5588*7c478bd9Sstevel@tonic-gate 	struct stat sb;
5589*7c478bd9Sstevel@tonic-gate 	const char *fcn = "create_cached_numeral";
5590*7c478bd9Sstevel@tonic-gate 
5591*7c478bd9Sstevel@tonic-gate 	assert(index >= 0 && index < setp->re_count);
5592*7c478bd9Sstevel@tonic-gate 	assert(strcmp(rules[index].re, setp->re[index]) == 0);
5593*7c478bd9Sstevel@tonic-gate 
5594*7c478bd9Sstevel@tonic-gate 	/*
5595*7c478bd9Sstevel@tonic-gate 	 *  We found a numeral_id from an entry in /dev which matched
5596*7c478bd9Sstevel@tonic-gate 	 *  the re passed in from devfsadm_enumerate.  We only need to make sure
5597*7c478bd9Sstevel@tonic-gate 	 *  ONE copy of numeral_id exists on the numeral list.  We only need
5598*7c478bd9Sstevel@tonic-gate 	 *  to store /dev/dsk/cNtod0s0 and no other entries hanging off
5599*7c478bd9Sstevel@tonic-gate 	 *  of controller N.
5600*7c478bd9Sstevel@tonic-gate 	 */
5601*7c478bd9Sstevel@tonic-gate 	for (np = setp->headnumeral; np != NULL; np = np->next) {
5602*7c478bd9Sstevel@tonic-gate 		if (strcmp(numeral_id, np->id) == 0) {
5603*7c478bd9Sstevel@tonic-gate 			return;
5604*7c478bd9Sstevel@tonic-gate 		}
5605*7c478bd9Sstevel@tonic-gate 	}
5606*7c478bd9Sstevel@tonic-gate 
5607*7c478bd9Sstevel@tonic-gate 	/* NOT on list, so add it */
5608*7c478bd9Sstevel@tonic-gate 
5609*7c478bd9Sstevel@tonic-gate 	(void) strcpy(lpath, path);
5610*7c478bd9Sstevel@tonic-gate 	/*
5611*7c478bd9Sstevel@tonic-gate 	 * If path is a dir, it is changed to the first symbolic link it find
5612*7c478bd9Sstevel@tonic-gate 	 * if it finds one.
5613*7c478bd9Sstevel@tonic-gate 	 */
5614*7c478bd9Sstevel@tonic-gate 	if (get_stat_info(lpath, &sb) == DEVFSADM_FAILURE) {
5615*7c478bd9Sstevel@tonic-gate 		return;
5616*7c478bd9Sstevel@tonic-gate 	}
5617*7c478bd9Sstevel@tonic-gate 
5618*7c478bd9Sstevel@tonic-gate 	/* If we get here, we found a symlink */
5619*7c478bd9Sstevel@tonic-gate 	linksize = readlink(lpath, linkbuf, PATH_MAX);
5620*7c478bd9Sstevel@tonic-gate 
5621*7c478bd9Sstevel@tonic-gate 	if (linksize <= 0) {
5622*7c478bd9Sstevel@tonic-gate 		err_print(READLINK_FAILED, fcn, lpath, strerror(errno));
5623*7c478bd9Sstevel@tonic-gate 		return;
5624*7c478bd9Sstevel@tonic-gate 	}
5625*7c478bd9Sstevel@tonic-gate 
5626*7c478bd9Sstevel@tonic-gate 	linkbuf[linksize] = '\0';
5627*7c478bd9Sstevel@tonic-gate 
5628*7c478bd9Sstevel@tonic-gate 	/*
5629*7c478bd9Sstevel@tonic-gate 	 * the following just points linkptr to the root of the /devices
5630*7c478bd9Sstevel@tonic-gate 	 * node if it is a minor node, otherwise, to the first char of
5631*7c478bd9Sstevel@tonic-gate 	 * linkbuf if it is a link.
5632*7c478bd9Sstevel@tonic-gate 	 */
5633*7c478bd9Sstevel@tonic-gate 	(void) is_minor_node(linkbuf, &linkptr);
5634*7c478bd9Sstevel@tonic-gate 
5635*7c478bd9Sstevel@tonic-gate 	cmp_str = alloc_cmp_str(linkptr, &rules[index]);
5636*7c478bd9Sstevel@tonic-gate 	if (cmp_str == NULL) {
5637*7c478bd9Sstevel@tonic-gate 		return;
5638*7c478bd9Sstevel@tonic-gate 	}
5639*7c478bd9Sstevel@tonic-gate 
5640*7c478bd9Sstevel@tonic-gate 	np = s_malloc(sizeof (numeral_t));
5641*7c478bd9Sstevel@tonic-gate 
5642*7c478bd9Sstevel@tonic-gate 	np->id = s_strdup(numeral_id);
5643*7c478bd9Sstevel@tonic-gate 	np->full_path = s_strdup(linkptr);
5644*7c478bd9Sstevel@tonic-gate 	np->rule_index = index;
5645*7c478bd9Sstevel@tonic-gate 	np->cmp_str = cmp_str;
5646*7c478bd9Sstevel@tonic-gate 
5647*7c478bd9Sstevel@tonic-gate 	np->next = setp->headnumeral;
5648*7c478bd9Sstevel@tonic-gate 	setp->headnumeral = np;
5649*7c478bd9Sstevel@tonic-gate }
5650*7c478bd9Sstevel@tonic-gate 
5651*7c478bd9Sstevel@tonic-gate 
5652*7c478bd9Sstevel@tonic-gate /*
5653*7c478bd9Sstevel@tonic-gate  * This should be called either before or after granting access to a
5654*7c478bd9Sstevel@tonic-gate  * command line version of devfsadm running, since it may have changed
5655*7c478bd9Sstevel@tonic-gate  * the state of /dev.  It forces future enumerate calls to re-build
5656*7c478bd9Sstevel@tonic-gate  * cached information from /dev.
5657*7c478bd9Sstevel@tonic-gate  */
5658*7c478bd9Sstevel@tonic-gate void
5659*7c478bd9Sstevel@tonic-gate invalidate_enumerate_cache(void)
5660*7c478bd9Sstevel@tonic-gate {
5661*7c478bd9Sstevel@tonic-gate 	numeral_set_t *setp;
5662*7c478bd9Sstevel@tonic-gate 	numeral_set_t *savedsetp;
5663*7c478bd9Sstevel@tonic-gate 	numeral_t *savednumset;
5664*7c478bd9Sstevel@tonic-gate 	numeral_t *numset;
5665*7c478bd9Sstevel@tonic-gate 	int i;
5666*7c478bd9Sstevel@tonic-gate 
5667*7c478bd9Sstevel@tonic-gate 	for (setp = head_numeral_set; setp != NULL; ) {
5668*7c478bd9Sstevel@tonic-gate 		/*
5669*7c478bd9Sstevel@tonic-gate 		 *  check all regexp's passed in function against
5670*7c478bd9Sstevel@tonic-gate 		 *  those in cached set.
5671*7c478bd9Sstevel@tonic-gate 		 */
5672*7c478bd9Sstevel@tonic-gate 
5673*7c478bd9Sstevel@tonic-gate 		savedsetp = setp;
5674*7c478bd9Sstevel@tonic-gate 		setp = setp->next;
5675*7c478bd9Sstevel@tonic-gate 
5676*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < savedsetp->re_count; i++) {
5677*7c478bd9Sstevel@tonic-gate 			free(savedsetp->re[i]);
5678*7c478bd9Sstevel@tonic-gate 		}
5679*7c478bd9Sstevel@tonic-gate 		free(savedsetp->re);
5680*7c478bd9Sstevel@tonic-gate 
5681*7c478bd9Sstevel@tonic-gate 		for (numset = savedsetp->headnumeral; numset != NULL; ) {
5682*7c478bd9Sstevel@tonic-gate 			savednumset = numset;
5683*7c478bd9Sstevel@tonic-gate 			numset = numset->next;
5684*7c478bd9Sstevel@tonic-gate 			assert(savednumset->rule_index < savedsetp->re_count);
5685*7c478bd9Sstevel@tonic-gate 			free(savednumset->id);
5686*7c478bd9Sstevel@tonic-gate 			free(savednumset->full_path);
5687*7c478bd9Sstevel@tonic-gate 			free(savednumset->cmp_str);
5688*7c478bd9Sstevel@tonic-gate 			free(savednumset);
5689*7c478bd9Sstevel@tonic-gate 		}
5690*7c478bd9Sstevel@tonic-gate 		free(savedsetp);
5691*7c478bd9Sstevel@tonic-gate 	}
5692*7c478bd9Sstevel@tonic-gate 	head_numeral_set = NULL;
5693*7c478bd9Sstevel@tonic-gate }
5694*7c478bd9Sstevel@tonic-gate 
5695*7c478bd9Sstevel@tonic-gate /*
5696*7c478bd9Sstevel@tonic-gate  * Copies over links from /dev to <root>/dev and device special files in
5697*7c478bd9Sstevel@tonic-gate  * /devices to <root>/devices, preserving the existing file modes.  If
5698*7c478bd9Sstevel@tonic-gate  * the link or special file already exists on <root>, skip the copy.  (it
5699*7c478bd9Sstevel@tonic-gate  * would exist only if a package hard coded it there, so assume package
5700*7c478bd9Sstevel@tonic-gate  * knows best?).  Use /etc/name_to_major and <root>/etc/name_to_major to
5701*7c478bd9Sstevel@tonic-gate  * make translations for major numbers on device special files.	No need to
5702*7c478bd9Sstevel@tonic-gate  * make a translation on minor_perm since if the file was created in the
5703*7c478bd9Sstevel@tonic-gate  * miniroot then it would presumably have the same minor_perm entry in
5704*7c478bd9Sstevel@tonic-gate  *  <root>/etc/minor_perm.  To be used only by install.
5705*7c478bd9Sstevel@tonic-gate  */
5706*7c478bd9Sstevel@tonic-gate int
5707*7c478bd9Sstevel@tonic-gate devfsadm_copy(void)
5708*7c478bd9Sstevel@tonic-gate {
5709*7c478bd9Sstevel@tonic-gate 	char filename[PATH_MAX + 1];
5710*7c478bd9Sstevel@tonic-gate 
5711*7c478bd9Sstevel@tonic-gate 	/* load the installed root's name_to_major for translations */
5712*7c478bd9Sstevel@tonic-gate 	(void) snprintf(filename, sizeof (filename), "%s%s", root_dir,
5713*7c478bd9Sstevel@tonic-gate 	    NAME_TO_MAJOR);
5714*7c478bd9Sstevel@tonic-gate 	if (load_n2m_table(filename) == DEVFSADM_FAILURE) {
5715*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
5716*7c478bd9Sstevel@tonic-gate 	}
5717*7c478bd9Sstevel@tonic-gate 
5718*7c478bd9Sstevel@tonic-gate 	/* Copy /dev to target disk. No need to copy /devices with devfs */
5719*7c478bd9Sstevel@tonic-gate 	(void) nftw(DEV, devfsadm_copy_file, 20, FTW_PHYS);
5720*7c478bd9Sstevel@tonic-gate 
5721*7c478bd9Sstevel@tonic-gate 	/* Let install handle copying over path_to_inst */
5722*7c478bd9Sstevel@tonic-gate 
5723*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
5724*7c478bd9Sstevel@tonic-gate }
5725*7c478bd9Sstevel@tonic-gate 
5726*7c478bd9Sstevel@tonic-gate /*
5727*7c478bd9Sstevel@tonic-gate  * This function copies links, dirs, and device special files.
5728*7c478bd9Sstevel@tonic-gate  * Note that it always returns DEVFSADM_SUCCESS, so that nftw doesn't
5729*7c478bd9Sstevel@tonic-gate  * abort.
5730*7c478bd9Sstevel@tonic-gate  */
5731*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5732*7c478bd9Sstevel@tonic-gate static int
5733*7c478bd9Sstevel@tonic-gate devfsadm_copy_file(const char *file, const struct stat *stat,
5734*7c478bd9Sstevel@tonic-gate 		    int flags, struct FTW *ftw)
5735*7c478bd9Sstevel@tonic-gate {
5736*7c478bd9Sstevel@tonic-gate 	struct stat sp;
5737*7c478bd9Sstevel@tonic-gate 	dev_t newdev;
5738*7c478bd9Sstevel@tonic-gate 	char newfile[PATH_MAX + 1];
5739*7c478bd9Sstevel@tonic-gate 	char linkcontents[PATH_MAX + 1];
5740*7c478bd9Sstevel@tonic-gate 	int bytes;
5741*7c478bd9Sstevel@tonic-gate 	const char *fcn = "devfsadm_copy_file";
5742*7c478bd9Sstevel@tonic-gate 
5743*7c478bd9Sstevel@tonic-gate 	(void) strcpy(newfile, root_dir);
5744*7c478bd9Sstevel@tonic-gate 	(void) strcat(newfile, "/");
5745*7c478bd9Sstevel@tonic-gate 	(void) strcat(newfile, file);
5746*7c478bd9Sstevel@tonic-gate 
5747*7c478bd9Sstevel@tonic-gate 	if (lstat(newfile, &sp) == 0) {
5748*7c478bd9Sstevel@tonic-gate 		/* newfile already exists, so no need to continue */
5749*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
5750*7c478bd9Sstevel@tonic-gate 	}
5751*7c478bd9Sstevel@tonic-gate 
5752*7c478bd9Sstevel@tonic-gate 	if (((stat->st_mode & S_IFMT) == S_IFBLK) ||
5753*7c478bd9Sstevel@tonic-gate 	    ((stat->st_mode & S_IFMT) == S_IFCHR)) {
5754*7c478bd9Sstevel@tonic-gate 		if (translate_major(stat->st_rdev, &newdev) ==
5755*7c478bd9Sstevel@tonic-gate 		    DEVFSADM_FAILURE) {
5756*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_SUCCESS);
5757*7c478bd9Sstevel@tonic-gate 		}
5758*7c478bd9Sstevel@tonic-gate 		if (mknod(newfile, stat->st_mode, newdev) == -1) {
5759*7c478bd9Sstevel@tonic-gate 			err_print(MKNOD_FAILED, newfile, strerror(errno));
5760*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_SUCCESS);
5761*7c478bd9Sstevel@tonic-gate 		}
5762*7c478bd9Sstevel@tonic-gate 	} else if ((stat->st_mode & S_IFMT) == S_IFDIR) {
5763*7c478bd9Sstevel@tonic-gate 		if (mknod(newfile, stat->st_mode, 0) == -1) {
5764*7c478bd9Sstevel@tonic-gate 			err_print(MKNOD_FAILED, newfile, strerror(errno));
5765*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_SUCCESS);
5766*7c478bd9Sstevel@tonic-gate 		}
5767*7c478bd9Sstevel@tonic-gate 	} else if ((stat->st_mode & S_IFMT) == S_IFLNK)  {
5768*7c478bd9Sstevel@tonic-gate 		if ((bytes = readlink(file, linkcontents, PATH_MAX)) == -1)  {
5769*7c478bd9Sstevel@tonic-gate 			err_print(READLINK_FAILED, fcn, file, strerror(errno));
5770*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_SUCCESS);
5771*7c478bd9Sstevel@tonic-gate 		}
5772*7c478bd9Sstevel@tonic-gate 		linkcontents[bytes] = '\0';
5773*7c478bd9Sstevel@tonic-gate 		if (symlink(linkcontents, newfile) == -1) {
5774*7c478bd9Sstevel@tonic-gate 			err_print(SYMLINK_FAILED, newfile, newfile,
5775*7c478bd9Sstevel@tonic-gate 					strerror(errno));
5776*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_SUCCESS);
5777*7c478bd9Sstevel@tonic-gate 		}
5778*7c478bd9Sstevel@tonic-gate 	}
5779*7c478bd9Sstevel@tonic-gate 
5780*7c478bd9Sstevel@tonic-gate 	(void) lchown(newfile, stat->st_uid, stat->st_gid);
5781*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
5782*7c478bd9Sstevel@tonic-gate }
5783*7c478bd9Sstevel@tonic-gate 
5784*7c478bd9Sstevel@tonic-gate /*
5785*7c478bd9Sstevel@tonic-gate  *  Given a dev_t from the running kernel, return the new_dev_t
5786*7c478bd9Sstevel@tonic-gate  *  by translating to the major number found on the installed
5787*7c478bd9Sstevel@tonic-gate  *  target's root name_to_major file.
5788*7c478bd9Sstevel@tonic-gate  */
5789*7c478bd9Sstevel@tonic-gate static int
5790*7c478bd9Sstevel@tonic-gate translate_major(dev_t old_dev, dev_t *new_dev)
5791*7c478bd9Sstevel@tonic-gate {
5792*7c478bd9Sstevel@tonic-gate 	major_t oldmajor;
5793*7c478bd9Sstevel@tonic-gate 	major_t newmajor;
5794*7c478bd9Sstevel@tonic-gate 	minor_t oldminor;
5795*7c478bd9Sstevel@tonic-gate 	minor_t newminor;
5796*7c478bd9Sstevel@tonic-gate 	char cdriver[FILENAME_MAX + 1];
5797*7c478bd9Sstevel@tonic-gate 	char driver[FILENAME_MAX + 1];
5798*7c478bd9Sstevel@tonic-gate 	char *fcn = "translate_major: ";
5799*7c478bd9Sstevel@tonic-gate 
5800*7c478bd9Sstevel@tonic-gate 	oldmajor = major(old_dev);
5801*7c478bd9Sstevel@tonic-gate 	if (modctl(MODGETNAME, driver, sizeof (driver),
5802*7c478bd9Sstevel@tonic-gate 			    &oldmajor) != 0) {
5803*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
5804*7c478bd9Sstevel@tonic-gate 	}
5805*7c478bd9Sstevel@tonic-gate 
5806*7c478bd9Sstevel@tonic-gate 	if (strcmp(driver, "clone") != 0) {
5807*7c478bd9Sstevel@tonic-gate 		/* non-clone case */
5808*7c478bd9Sstevel@tonic-gate 
5809*7c478bd9Sstevel@tonic-gate 		/* look up major number is target's name2major */
5810*7c478bd9Sstevel@tonic-gate 		if (get_major_no(driver, &newmajor) == DEVFSADM_FAILURE) {
5811*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
5812*7c478bd9Sstevel@tonic-gate 		}
5813*7c478bd9Sstevel@tonic-gate 
5814*7c478bd9Sstevel@tonic-gate 		*new_dev = makedev(newmajor, minor(old_dev));
5815*7c478bd9Sstevel@tonic-gate 		if (old_dev != *new_dev) {
5816*7c478bd9Sstevel@tonic-gate 			vprint(CHATTY_MID, "%sdriver: %s old: %lu,%lu "
5817*7c478bd9Sstevel@tonic-gate 				"new: %lu,%lu\n", fcn, driver, major(old_dev),
5818*7c478bd9Sstevel@tonic-gate 				minor(old_dev), major(*new_dev),
5819*7c478bd9Sstevel@tonic-gate 				minor(*new_dev));
5820*7c478bd9Sstevel@tonic-gate 		}
5821*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
5822*7c478bd9Sstevel@tonic-gate 	} else {
5823*7c478bd9Sstevel@tonic-gate 		/*
5824*7c478bd9Sstevel@tonic-gate 		 *  The clone is a special case.  Look at its minor
5825*7c478bd9Sstevel@tonic-gate 		 *  number since it is the major number of the real driver.
5826*7c478bd9Sstevel@tonic-gate 		 */
5827*7c478bd9Sstevel@tonic-gate 		if (get_major_no(driver, &newmajor) == DEVFSADM_FAILURE) {
5828*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
5829*7c478bd9Sstevel@tonic-gate 		}
5830*7c478bd9Sstevel@tonic-gate 
5831*7c478bd9Sstevel@tonic-gate 		oldminor = minor(old_dev);
5832*7c478bd9Sstevel@tonic-gate 		if (modctl(MODGETNAME, cdriver, sizeof (cdriver),
5833*7c478bd9Sstevel@tonic-gate 					&oldminor) != 0) {
5834*7c478bd9Sstevel@tonic-gate 			err_print(MODGETNAME_FAILED, oldminor);
5835*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
5836*7c478bd9Sstevel@tonic-gate 		}
5837*7c478bd9Sstevel@tonic-gate 
5838*7c478bd9Sstevel@tonic-gate 		if (get_major_no(cdriver, &newminor) == DEVFSADM_FAILURE) {
5839*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
5840*7c478bd9Sstevel@tonic-gate 		}
5841*7c478bd9Sstevel@tonic-gate 
5842*7c478bd9Sstevel@tonic-gate 		*new_dev = makedev(newmajor, newminor);
5843*7c478bd9Sstevel@tonic-gate 		if (old_dev != *new_dev) {
5844*7c478bd9Sstevel@tonic-gate 			vprint(CHATTY_MID, "%sdriver: %s old: "
5845*7c478bd9Sstevel@tonic-gate 				"%lu,%lu  new: %lu,%lu\n", fcn, driver,
5846*7c478bd9Sstevel@tonic-gate 				major(old_dev), minor(old_dev),
5847*7c478bd9Sstevel@tonic-gate 				major(*new_dev), minor(*new_dev));
5848*7c478bd9Sstevel@tonic-gate 		}
5849*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
5850*7c478bd9Sstevel@tonic-gate 	}
5851*7c478bd9Sstevel@tonic-gate }
5852*7c478bd9Sstevel@tonic-gate 
5853*7c478bd9Sstevel@tonic-gate /*
5854*7c478bd9Sstevel@tonic-gate  *
5855*7c478bd9Sstevel@tonic-gate  * Find the major number for driver, searching the n2m_list that was
5856*7c478bd9Sstevel@tonic-gate  * built in load_n2m_table().
5857*7c478bd9Sstevel@tonic-gate  */
5858*7c478bd9Sstevel@tonic-gate static int
5859*7c478bd9Sstevel@tonic-gate get_major_no(char *driver, major_t *major)
5860*7c478bd9Sstevel@tonic-gate {
5861*7c478bd9Sstevel@tonic-gate 	n2m_t *ptr;
5862*7c478bd9Sstevel@tonic-gate 
5863*7c478bd9Sstevel@tonic-gate 	for (ptr = n2m_list; ptr != NULL; ptr = ptr->next) {
5864*7c478bd9Sstevel@tonic-gate 		if (strcmp(ptr->driver, driver) == 0) {
5865*7c478bd9Sstevel@tonic-gate 			*major = ptr->major;
5866*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_SUCCESS);
5867*7c478bd9Sstevel@tonic-gate 		}
5868*7c478bd9Sstevel@tonic-gate 	}
5869*7c478bd9Sstevel@tonic-gate 	err_print(FIND_MAJOR_FAILED, driver);
5870*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_FAILURE);
5871*7c478bd9Sstevel@tonic-gate }
5872*7c478bd9Sstevel@tonic-gate 
5873*7c478bd9Sstevel@tonic-gate /*
5874*7c478bd9Sstevel@tonic-gate  * Loads a name_to_major table into memory.  Used only for suninstall's
5875*7c478bd9Sstevel@tonic-gate  * private -R option to devfsadm, to translate major numbers from the
5876*7c478bd9Sstevel@tonic-gate  * running to the installed target disk.
5877*7c478bd9Sstevel@tonic-gate  */
5878*7c478bd9Sstevel@tonic-gate static int
5879*7c478bd9Sstevel@tonic-gate load_n2m_table(char *file)
5880*7c478bd9Sstevel@tonic-gate {
5881*7c478bd9Sstevel@tonic-gate 	FILE *fp;
5882*7c478bd9Sstevel@tonic-gate 	char line[1024];
5883*7c478bd9Sstevel@tonic-gate 	char driver[PATH_MAX + 1];
5884*7c478bd9Sstevel@tonic-gate 	major_t major;
5885*7c478bd9Sstevel@tonic-gate 	n2m_t *ptr;
5886*7c478bd9Sstevel@tonic-gate 	int ln = 0;
5887*7c478bd9Sstevel@tonic-gate 
5888*7c478bd9Sstevel@tonic-gate 	if ((fp = fopen(file, "r")) == NULL) {
5889*7c478bd9Sstevel@tonic-gate 		err_print(FOPEN_FAILED, file, strerror(errno));
5890*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
5891*7c478bd9Sstevel@tonic-gate 	}
5892*7c478bd9Sstevel@tonic-gate 
5893*7c478bd9Sstevel@tonic-gate 	while (fgets(line, sizeof (line), fp) != NULL) {
5894*7c478bd9Sstevel@tonic-gate 		ln++;
5895*7c478bd9Sstevel@tonic-gate 		if (line[0] == '#') {
5896*7c478bd9Sstevel@tonic-gate 			continue;
5897*7c478bd9Sstevel@tonic-gate 		}
5898*7c478bd9Sstevel@tonic-gate 		if (sscanf(line, "%1024s%lu", driver, &major) != 2) {
5899*7c478bd9Sstevel@tonic-gate 			err_print(IGNORING_LINE_IN, ln, file);
5900*7c478bd9Sstevel@tonic-gate 			continue;
5901*7c478bd9Sstevel@tonic-gate 		}
5902*7c478bd9Sstevel@tonic-gate 		ptr = (n2m_t *)s_malloc(sizeof (n2m_t));
5903*7c478bd9Sstevel@tonic-gate 		ptr->major = major;
5904*7c478bd9Sstevel@tonic-gate 		ptr->driver = s_strdup(driver);
5905*7c478bd9Sstevel@tonic-gate 		ptr->next = n2m_list;
5906*7c478bd9Sstevel@tonic-gate 		n2m_list = ptr;
5907*7c478bd9Sstevel@tonic-gate 	}
5908*7c478bd9Sstevel@tonic-gate 	if (fclose(fp) == EOF) {
5909*7c478bd9Sstevel@tonic-gate 		err_print(FCLOSE_FAILED, file, strerror(errno));
5910*7c478bd9Sstevel@tonic-gate 	}
5911*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
5912*7c478bd9Sstevel@tonic-gate }
5913*7c478bd9Sstevel@tonic-gate 
5914*7c478bd9Sstevel@tonic-gate /*
5915*7c478bd9Sstevel@tonic-gate  * Called at devfsadm startup to read in the devlink.tab file.	Creates
5916*7c478bd9Sstevel@tonic-gate  * a linked list of devlinktab_list structures which will be
5917*7c478bd9Sstevel@tonic-gate  * searched for every minor node.
5918*7c478bd9Sstevel@tonic-gate  */
5919*7c478bd9Sstevel@tonic-gate static void
5920*7c478bd9Sstevel@tonic-gate read_devlinktab_file(void)
5921*7c478bd9Sstevel@tonic-gate {
5922*7c478bd9Sstevel@tonic-gate 	devlinktab_list_t *headp = NULL;
5923*7c478bd9Sstevel@tonic-gate 	devlinktab_list_t *entryp;
5924*7c478bd9Sstevel@tonic-gate 	devlinktab_list_t **previous;
5925*7c478bd9Sstevel@tonic-gate 	devlinktab_list_t *save;
5926*7c478bd9Sstevel@tonic-gate 	char line[MAX_DEVLINK_LINE];
5927*7c478bd9Sstevel@tonic-gate 	char *selector;
5928*7c478bd9Sstevel@tonic-gate 	char *p_link;
5929*7c478bd9Sstevel@tonic-gate 	char *s_link;
5930*7c478bd9Sstevel@tonic-gate 	FILE *fp;
5931*7c478bd9Sstevel@tonic-gate 	int i;
5932*7c478bd9Sstevel@tonic-gate 	static struct stat cached_sb;
5933*7c478bd9Sstevel@tonic-gate 	struct stat current_sb;
5934*7c478bd9Sstevel@tonic-gate 	static int cached = FALSE;
5935*7c478bd9Sstevel@tonic-gate 
5936*7c478bd9Sstevel@tonic-gate 	if (devlinktab_file == NULL) {
5937*7c478bd9Sstevel@tonic-gate 		return;
5938*7c478bd9Sstevel@tonic-gate 	}
5939*7c478bd9Sstevel@tonic-gate 
5940*7c478bd9Sstevel@tonic-gate 	(void) stat(devlinktab_file, &current_sb);
5941*7c478bd9Sstevel@tonic-gate 
5942*7c478bd9Sstevel@tonic-gate 	/* if already cached, check to see if it is still valid */
5943*7c478bd9Sstevel@tonic-gate 	if (cached == TRUE) {
5944*7c478bd9Sstevel@tonic-gate 
5945*7c478bd9Sstevel@tonic-gate 		if (current_sb.st_mtime == cached_sb.st_mtime) {
5946*7c478bd9Sstevel@tonic-gate 			vprint(FILES_MID, "%s cache valid\n", devlinktab_file);
5947*7c478bd9Sstevel@tonic-gate 			return;
5948*7c478bd9Sstevel@tonic-gate 		}
5949*7c478bd9Sstevel@tonic-gate 
5950*7c478bd9Sstevel@tonic-gate 		vprint(FILES_MID, "invalidating %s cache\n", devlinktab_file);
5951*7c478bd9Sstevel@tonic-gate 
5952*7c478bd9Sstevel@tonic-gate 		while (devlinktab_list != NULL) {
5953*7c478bd9Sstevel@tonic-gate 			free_link_list(devlinktab_list->p_link);
5954*7c478bd9Sstevel@tonic-gate 			free_link_list(devlinktab_list->s_link);
5955*7c478bd9Sstevel@tonic-gate 			free_selector_list(devlinktab_list->selector);
5956*7c478bd9Sstevel@tonic-gate 			free(devlinktab_list->selector_pattern);
5957*7c478bd9Sstevel@tonic-gate 			free(devlinktab_list->p_link_pattern);
5958*7c478bd9Sstevel@tonic-gate 			if (devlinktab_list->s_link_pattern != NULL) {
5959*7c478bd9Sstevel@tonic-gate 				free(devlinktab_list->s_link_pattern);
5960*7c478bd9Sstevel@tonic-gate 			}
5961*7c478bd9Sstevel@tonic-gate 			save = devlinktab_list;
5962*7c478bd9Sstevel@tonic-gate 			devlinktab_list = devlinktab_list->next;
5963*7c478bd9Sstevel@tonic-gate 			free(save);
5964*7c478bd9Sstevel@tonic-gate 		}
5965*7c478bd9Sstevel@tonic-gate 	} else {
5966*7c478bd9Sstevel@tonic-gate 		cached = TRUE;
5967*7c478bd9Sstevel@tonic-gate 	}
5968*7c478bd9Sstevel@tonic-gate 
5969*7c478bd9Sstevel@tonic-gate 	(void) stat(devlinktab_file, &cached_sb);
5970*7c478bd9Sstevel@tonic-gate 
5971*7c478bd9Sstevel@tonic-gate 	if ((fp = fopen(devlinktab_file, "r")) == NULL) {
5972*7c478bd9Sstevel@tonic-gate 		err_print(FOPEN_FAILED, devlinktab_file, strerror(errno));
5973*7c478bd9Sstevel@tonic-gate 		return;
5974*7c478bd9Sstevel@tonic-gate 	}
5975*7c478bd9Sstevel@tonic-gate 
5976*7c478bd9Sstevel@tonic-gate 	previous = &headp;
5977*7c478bd9Sstevel@tonic-gate 
5978*7c478bd9Sstevel@tonic-gate 	while (fgets(line, sizeof (line), fp) != NULL) {
5979*7c478bd9Sstevel@tonic-gate 		devlinktab_line++;
5980*7c478bd9Sstevel@tonic-gate 		i = strlen(line);
5981*7c478bd9Sstevel@tonic-gate 		if (line[i-1] == NEWLINE) {
5982*7c478bd9Sstevel@tonic-gate 			line[i-1] = '\0';
5983*7c478bd9Sstevel@tonic-gate 		} else if (i == sizeof (line-1)) {
5984*7c478bd9Sstevel@tonic-gate 			err_print(LINE_TOO_LONG, devlinktab_line,
5985*7c478bd9Sstevel@tonic-gate 			    devlinktab_file, sizeof (line)-1);
5986*7c478bd9Sstevel@tonic-gate 			while (((i = getc(fp)) != '\n') && (i != EOF));
5987*7c478bd9Sstevel@tonic-gate 			continue;
5988*7c478bd9Sstevel@tonic-gate 		}
5989*7c478bd9Sstevel@tonic-gate 
5990*7c478bd9Sstevel@tonic-gate 		if ((line[0] == '#') || (line[0] == '\0')) {
5991*7c478bd9Sstevel@tonic-gate 			/* Ignore comments and blank lines */
5992*7c478bd9Sstevel@tonic-gate 			continue;
5993*7c478bd9Sstevel@tonic-gate 		}
5994*7c478bd9Sstevel@tonic-gate 
5995*7c478bd9Sstevel@tonic-gate 		vprint(DEVLINK_MID, "table: %s line %d: '%s'\n",
5996*7c478bd9Sstevel@tonic-gate 			devlinktab_file, devlinktab_line, line);
5997*7c478bd9Sstevel@tonic-gate 
5998*7c478bd9Sstevel@tonic-gate 		/* break each entry into fields.  s_link may be NULL */
5999*7c478bd9Sstevel@tonic-gate 		if (split_devlinktab_entry(line, &selector, &p_link,
6000*7c478bd9Sstevel@tonic-gate 		    &s_link) == DEVFSADM_FAILURE) {
6001*7c478bd9Sstevel@tonic-gate 			vprint(DEVLINK_MID, "split_entry returns failure\n");
6002*7c478bd9Sstevel@tonic-gate 			continue;
6003*7c478bd9Sstevel@tonic-gate 		} else {
6004*7c478bd9Sstevel@tonic-gate 			vprint(DEVLINK_MID, "split_entry selector='%s' "
6005*7c478bd9Sstevel@tonic-gate 				"p_link='%s' s_link='%s'\n\n", selector,
6006*7c478bd9Sstevel@tonic-gate 				p_link, (s_link == NULL) ? "" : s_link);
6007*7c478bd9Sstevel@tonic-gate 		}
6008*7c478bd9Sstevel@tonic-gate 
6009*7c478bd9Sstevel@tonic-gate 		entryp = (devlinktab_list_t *)
6010*7c478bd9Sstevel@tonic-gate 			s_malloc(sizeof (devlinktab_list_t));
6011*7c478bd9Sstevel@tonic-gate 
6012*7c478bd9Sstevel@tonic-gate 		entryp->line_number = devlinktab_line;
6013*7c478bd9Sstevel@tonic-gate 
6014*7c478bd9Sstevel@tonic-gate 		if ((entryp->selector =
6015*7c478bd9Sstevel@tonic-gate 			create_selector_list(selector)) == NULL) {
6016*7c478bd9Sstevel@tonic-gate 			free(entryp);
6017*7c478bd9Sstevel@tonic-gate 			continue;
6018*7c478bd9Sstevel@tonic-gate 		}
6019*7c478bd9Sstevel@tonic-gate 		entryp->selector_pattern = s_strdup(selector);
6020*7c478bd9Sstevel@tonic-gate 
6021*7c478bd9Sstevel@tonic-gate 		if ((entryp->p_link = create_link_list(p_link)) == NULL) {
6022*7c478bd9Sstevel@tonic-gate 			free_selector_list(entryp->selector);
6023*7c478bd9Sstevel@tonic-gate 			free(entryp->selector_pattern);
6024*7c478bd9Sstevel@tonic-gate 			free(entryp);
6025*7c478bd9Sstevel@tonic-gate 			continue;
6026*7c478bd9Sstevel@tonic-gate 		}
6027*7c478bd9Sstevel@tonic-gate 
6028*7c478bd9Sstevel@tonic-gate 		entryp->p_link_pattern = s_strdup(p_link);
6029*7c478bd9Sstevel@tonic-gate 
6030*7c478bd9Sstevel@tonic-gate 		if (s_link != NULL) {
6031*7c478bd9Sstevel@tonic-gate 			if ((entryp->s_link =
6032*7c478bd9Sstevel@tonic-gate 			    create_link_list(s_link)) == NULL) {
6033*7c478bd9Sstevel@tonic-gate 				free_selector_list(entryp->selector);
6034*7c478bd9Sstevel@tonic-gate 				free_link_list(entryp->p_link);
6035*7c478bd9Sstevel@tonic-gate 				free(entryp->selector_pattern);
6036*7c478bd9Sstevel@tonic-gate 				free(entryp->p_link_pattern);
6037*7c478bd9Sstevel@tonic-gate 				free(entryp);
6038*7c478bd9Sstevel@tonic-gate 				continue;
6039*7c478bd9Sstevel@tonic-gate 			}
6040*7c478bd9Sstevel@tonic-gate 			    entryp->s_link_pattern = s_strdup(s_link);
6041*7c478bd9Sstevel@tonic-gate 		} else {
6042*7c478bd9Sstevel@tonic-gate 			entryp->s_link = NULL;
6043*7c478bd9Sstevel@tonic-gate 			entryp->s_link_pattern = NULL;
6044*7c478bd9Sstevel@tonic-gate 
6045*7c478bd9Sstevel@tonic-gate 		}
6046*7c478bd9Sstevel@tonic-gate 
6047*7c478bd9Sstevel@tonic-gate 		/* append to end of list */
6048*7c478bd9Sstevel@tonic-gate 
6049*7c478bd9Sstevel@tonic-gate 		entryp->next = NULL;
6050*7c478bd9Sstevel@tonic-gate 		*previous = entryp;
6051*7c478bd9Sstevel@tonic-gate 		previous = &(entryp->next);
6052*7c478bd9Sstevel@tonic-gate 	}
6053*7c478bd9Sstevel@tonic-gate 	if (fclose(fp) == EOF) {
6054*7c478bd9Sstevel@tonic-gate 		err_print(FCLOSE_FAILED, devlinktab_file, strerror(errno));
6055*7c478bd9Sstevel@tonic-gate 	}
6056*7c478bd9Sstevel@tonic-gate 	devlinktab_list = headp;
6057*7c478bd9Sstevel@tonic-gate }
6058*7c478bd9Sstevel@tonic-gate 
6059*7c478bd9Sstevel@tonic-gate /*
6060*7c478bd9Sstevel@tonic-gate  *
6061*7c478bd9Sstevel@tonic-gate  * For a single line entry in devlink.tab, split the line into fields
6062*7c478bd9Sstevel@tonic-gate  * selector, p_link, and an optionally s_link.	If s_link field is not
6063*7c478bd9Sstevel@tonic-gate  * present, then return NULL in s_link (not NULL string).
6064*7c478bd9Sstevel@tonic-gate  */
6065*7c478bd9Sstevel@tonic-gate static int
6066*7c478bd9Sstevel@tonic-gate split_devlinktab_entry(char *entry, char **selector, char **p_link,
6067*7c478bd9Sstevel@tonic-gate 			char **s_link)
6068*7c478bd9Sstevel@tonic-gate {
6069*7c478bd9Sstevel@tonic-gate 	char *tab;
6070*7c478bd9Sstevel@tonic-gate 
6071*7c478bd9Sstevel@tonic-gate 	*selector = entry;
6072*7c478bd9Sstevel@tonic-gate 
6073*7c478bd9Sstevel@tonic-gate 	if ((tab = strchr(entry, TAB)) != NULL) {
6074*7c478bd9Sstevel@tonic-gate 		*tab = '\0';
6075*7c478bd9Sstevel@tonic-gate 		*p_link = ++tab;
6076*7c478bd9Sstevel@tonic-gate 	} else {
6077*7c478bd9Sstevel@tonic-gate 		err_print(MISSING_TAB, devlinktab_line, devlinktab_file);
6078*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
6079*7c478bd9Sstevel@tonic-gate 	}
6080*7c478bd9Sstevel@tonic-gate 
6081*7c478bd9Sstevel@tonic-gate 	if (*p_link == '\0') {
6082*7c478bd9Sstevel@tonic-gate 		err_print(MISSING_DEVNAME, devlinktab_line, devlinktab_file);
6083*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
6084*7c478bd9Sstevel@tonic-gate 	}
6085*7c478bd9Sstevel@tonic-gate 
6086*7c478bd9Sstevel@tonic-gate 	if ((tab = strchr(*p_link, TAB)) != NULL) {
6087*7c478bd9Sstevel@tonic-gate 		*tab = '\0';
6088*7c478bd9Sstevel@tonic-gate 		*s_link = ++tab;
6089*7c478bd9Sstevel@tonic-gate 		if (strchr(*s_link, TAB) != NULL) {
6090*7c478bd9Sstevel@tonic-gate 			err_print(TOO_MANY_FIELDS, devlinktab_line,
6091*7c478bd9Sstevel@tonic-gate 					devlinktab_file);
6092*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
6093*7c478bd9Sstevel@tonic-gate 		}
6094*7c478bd9Sstevel@tonic-gate 	} else {
6095*7c478bd9Sstevel@tonic-gate 		*s_link = NULL;
6096*7c478bd9Sstevel@tonic-gate 	}
6097*7c478bd9Sstevel@tonic-gate 
6098*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
6099*7c478bd9Sstevel@tonic-gate }
6100*7c478bd9Sstevel@tonic-gate 
6101*7c478bd9Sstevel@tonic-gate /*
6102*7c478bd9Sstevel@tonic-gate  * For a given devfs_spec field, for each element in the field, add it to
6103*7c478bd9Sstevel@tonic-gate  * a linked list of devfs_spec structures.  Return the linked list in
6104*7c478bd9Sstevel@tonic-gate  * devfs_spec_list.
6105*7c478bd9Sstevel@tonic-gate  */
6106*7c478bd9Sstevel@tonic-gate static selector_list_t *
6107*7c478bd9Sstevel@tonic-gate create_selector_list(char *selector)
6108*7c478bd9Sstevel@tonic-gate {
6109*7c478bd9Sstevel@tonic-gate 	    char *key;
6110*7c478bd9Sstevel@tonic-gate 	    char *val;
6111*7c478bd9Sstevel@tonic-gate 	    int error = FALSE;
6112*7c478bd9Sstevel@tonic-gate 	    selector_list_t *head_selector_list = NULL;
6113*7c478bd9Sstevel@tonic-gate 	    selector_list_t *selector_list;
6114*7c478bd9Sstevel@tonic-gate 
6115*7c478bd9Sstevel@tonic-gate 	    /* parse_devfs_spec splits the next field into keyword & value */
6116*7c478bd9Sstevel@tonic-gate 	    while ((*selector != NULL) && (error == FALSE)) {
6117*7c478bd9Sstevel@tonic-gate 		    if (parse_selector(&selector, &key,
6118*7c478bd9Sstevel@tonic-gate 				&val) == DEVFSADM_FAILURE) {
6119*7c478bd9Sstevel@tonic-gate 			    error = TRUE;
6120*7c478bd9Sstevel@tonic-gate 			    break;
6121*7c478bd9Sstevel@tonic-gate 		    } else {
6122*7c478bd9Sstevel@tonic-gate 			    selector_list = (selector_list_t *)
6123*7c478bd9Sstevel@tonic-gate 				    s_malloc(sizeof (selector_list_t));
6124*7c478bd9Sstevel@tonic-gate 			    if (strcmp(NAME_S, key) == 0) {
6125*7c478bd9Sstevel@tonic-gate 				    selector_list->key = NAME;
6126*7c478bd9Sstevel@tonic-gate 			    } else if (strcmp(TYPE_S, key) == 0) {
6127*7c478bd9Sstevel@tonic-gate 				    selector_list->key = TYPE;
6128*7c478bd9Sstevel@tonic-gate 			    } else if (strncmp(ADDR_S, key, ADDR_S_LEN) == 0) {
6129*7c478bd9Sstevel@tonic-gate 				    selector_list->key = ADDR;
6130*7c478bd9Sstevel@tonic-gate 				    if (key[ADDR_S_LEN] == '\0') {
6131*7c478bd9Sstevel@tonic-gate 					    selector_list->arg = 0;
6132*7c478bd9Sstevel@tonic-gate 				    } else if (isdigit(key[ADDR_S_LEN]) !=
6133*7c478bd9Sstevel@tonic-gate 						FALSE) {
6134*7c478bd9Sstevel@tonic-gate 					    selector_list->arg =
6135*7c478bd9Sstevel@tonic-gate 							atoi(&key[ADDR_S_LEN]);
6136*7c478bd9Sstevel@tonic-gate 				    } else {
6137*7c478bd9Sstevel@tonic-gate 					    error = TRUE;
6138*7c478bd9Sstevel@tonic-gate 					    free(selector_list);
6139*7c478bd9Sstevel@tonic-gate 					    err_print(BADKEYWORD, key,
6140*7c478bd9Sstevel@tonic-gate 						devlinktab_line,
6141*7c478bd9Sstevel@tonic-gate 						devlinktab_file);
6142*7c478bd9Sstevel@tonic-gate 					    break;
6143*7c478bd9Sstevel@tonic-gate 				    }
6144*7c478bd9Sstevel@tonic-gate 			    } else if (strncmp(MINOR_S, key,
6145*7c478bd9Sstevel@tonic-gate 						MINOR_S_LEN) == 0) {
6146*7c478bd9Sstevel@tonic-gate 				    selector_list->key = MINOR;
6147*7c478bd9Sstevel@tonic-gate 				    if (key[MINOR_S_LEN] == '\0') {
6148*7c478bd9Sstevel@tonic-gate 					    selector_list->arg = 0;
6149*7c478bd9Sstevel@tonic-gate 				    } else if (isdigit(key[MINOR_S_LEN]) !=
6150*7c478bd9Sstevel@tonic-gate 						FALSE) {
6151*7c478bd9Sstevel@tonic-gate 					    selector_list->arg =
6152*7c478bd9Sstevel@tonic-gate 						atoi(&key[MINOR_S_LEN]);
6153*7c478bd9Sstevel@tonic-gate 				    } else {
6154*7c478bd9Sstevel@tonic-gate 					    error = TRUE;
6155*7c478bd9Sstevel@tonic-gate 					    free(selector_list);
6156*7c478bd9Sstevel@tonic-gate 					    err_print(BADKEYWORD, key,
6157*7c478bd9Sstevel@tonic-gate 						devlinktab_line,
6158*7c478bd9Sstevel@tonic-gate 						devlinktab_file);
6159*7c478bd9Sstevel@tonic-gate 					    break;
6160*7c478bd9Sstevel@tonic-gate 				    }
6161*7c478bd9Sstevel@tonic-gate 				    vprint(DEVLINK_MID, "MINOR = %s\n", val);
6162*7c478bd9Sstevel@tonic-gate 			    } else {
6163*7c478bd9Sstevel@tonic-gate 				    err_print(UNRECOGNIZED_KEY, key,
6164*7c478bd9Sstevel@tonic-gate 					devlinktab_line, devlinktab_file);
6165*7c478bd9Sstevel@tonic-gate 				    error = TRUE;
6166*7c478bd9Sstevel@tonic-gate 				    free(selector_list);
6167*7c478bd9Sstevel@tonic-gate 				    break;
6168*7c478bd9Sstevel@tonic-gate 			    }
6169*7c478bd9Sstevel@tonic-gate 			    selector_list->val = s_strdup(val);
6170*7c478bd9Sstevel@tonic-gate 			    selector_list->next = head_selector_list;
6171*7c478bd9Sstevel@tonic-gate 			    head_selector_list = selector_list;
6172*7c478bd9Sstevel@tonic-gate 			    vprint(DEVLINK_MID, "key='%s' val='%s' arg=%d\n",
6173*7c478bd9Sstevel@tonic-gate 					key, val, selector_list->arg);
6174*7c478bd9Sstevel@tonic-gate 		    }
6175*7c478bd9Sstevel@tonic-gate 	    }
6176*7c478bd9Sstevel@tonic-gate 
6177*7c478bd9Sstevel@tonic-gate 	    if ((error == FALSE) && (head_selector_list != NULL)) {
6178*7c478bd9Sstevel@tonic-gate 		    return (head_selector_list);
6179*7c478bd9Sstevel@tonic-gate 	    } else {
6180*7c478bd9Sstevel@tonic-gate 		    /* parse failed.  Free any allocated structs */
6181*7c478bd9Sstevel@tonic-gate 		    free_selector_list(head_selector_list);
6182*7c478bd9Sstevel@tonic-gate 		    return (NULL);
6183*7c478bd9Sstevel@tonic-gate 	    }
6184*7c478bd9Sstevel@tonic-gate }
6185*7c478bd9Sstevel@tonic-gate 
6186*7c478bd9Sstevel@tonic-gate /*
6187*7c478bd9Sstevel@tonic-gate  * Takes a semicolon separated list of selector elements and breaks up
6188*7c478bd9Sstevel@tonic-gate  * into a keyword-value pair.	semicolon and equal characters are
6189*7c478bd9Sstevel@tonic-gate  * replaced with NULL's.  On success, selector is updated to point to the
6190*7c478bd9Sstevel@tonic-gate  * terminating NULL character terminating the keyword-value pair, and the
6191*7c478bd9Sstevel@tonic-gate  * function returns DEVFSADM_SUCCESS.	If there is a syntax error,
6192*7c478bd9Sstevel@tonic-gate  * devfs_spec is not modified and function returns DEVFSADM_FAILURE.
6193*7c478bd9Sstevel@tonic-gate  */
6194*7c478bd9Sstevel@tonic-gate static int
6195*7c478bd9Sstevel@tonic-gate parse_selector(char **selector, char **key, char **val)
6196*7c478bd9Sstevel@tonic-gate {
6197*7c478bd9Sstevel@tonic-gate 	char *equal;
6198*7c478bd9Sstevel@tonic-gate 	char *semi_colon;
6199*7c478bd9Sstevel@tonic-gate 
6200*7c478bd9Sstevel@tonic-gate 	*key = *selector;
6201*7c478bd9Sstevel@tonic-gate 
6202*7c478bd9Sstevel@tonic-gate 	if ((equal = strchr(*key, '=')) != NULL) {
6203*7c478bd9Sstevel@tonic-gate 		*equal = '\0';
6204*7c478bd9Sstevel@tonic-gate 	} else {
6205*7c478bd9Sstevel@tonic-gate 		err_print(MISSING_EQUAL, devlinktab_line, devlinktab_file);
6206*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
6207*7c478bd9Sstevel@tonic-gate 	}
6208*7c478bd9Sstevel@tonic-gate 
6209*7c478bd9Sstevel@tonic-gate 	*val = ++equal;
6210*7c478bd9Sstevel@tonic-gate 	if ((semi_colon = strchr(equal, ';')) != NULL) {
6211*7c478bd9Sstevel@tonic-gate 		*semi_colon = '\0';
6212*7c478bd9Sstevel@tonic-gate 		*selector = semi_colon + 1;
6213*7c478bd9Sstevel@tonic-gate 	} else {
6214*7c478bd9Sstevel@tonic-gate 		*selector = equal + strlen(equal);
6215*7c478bd9Sstevel@tonic-gate 	}
6216*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
6217*7c478bd9Sstevel@tonic-gate }
6218*7c478bd9Sstevel@tonic-gate 
6219*7c478bd9Sstevel@tonic-gate /*
6220*7c478bd9Sstevel@tonic-gate  * link is either the second or third field of devlink.tab.  Parse link
6221*7c478bd9Sstevel@tonic-gate  * into a linked list of devlink structures and return ptr to list.  Each
6222*7c478bd9Sstevel@tonic-gate  * list element is either a constant string, or one of the following
6223*7c478bd9Sstevel@tonic-gate  * escape sequences: \M, \A, \N, or \D.  The first three escape sequences
6224*7c478bd9Sstevel@tonic-gate  * take a numerical argument.
6225*7c478bd9Sstevel@tonic-gate  */
6226*7c478bd9Sstevel@tonic-gate static link_list_t *
6227*7c478bd9Sstevel@tonic-gate create_link_list(char *link)
6228*7c478bd9Sstevel@tonic-gate {
6229*7c478bd9Sstevel@tonic-gate 	int x = 0;
6230*7c478bd9Sstevel@tonic-gate 	int error = FALSE;
6231*7c478bd9Sstevel@tonic-gate 	int counter_found = FALSE;
6232*7c478bd9Sstevel@tonic-gate 	link_list_t *head = NULL;
6233*7c478bd9Sstevel@tonic-gate 	link_list_t **ptr;
6234*7c478bd9Sstevel@tonic-gate 	link_list_t *link_list;
6235*7c478bd9Sstevel@tonic-gate 	char constant[MAX_DEVLINK_LINE];
6236*7c478bd9Sstevel@tonic-gate 	char *error_str;
6237*7c478bd9Sstevel@tonic-gate 
6238*7c478bd9Sstevel@tonic-gate 	if (link == NULL) {
6239*7c478bd9Sstevel@tonic-gate 		return (NULL);
6240*7c478bd9Sstevel@tonic-gate 	}
6241*7c478bd9Sstevel@tonic-gate 
6242*7c478bd9Sstevel@tonic-gate 	while ((*link != '\0') && (error == FALSE)) {
6243*7c478bd9Sstevel@tonic-gate 		link_list = (link_list_t *)s_malloc(sizeof (link_list_t));
6244*7c478bd9Sstevel@tonic-gate 		link_list->next = NULL;
6245*7c478bd9Sstevel@tonic-gate 
6246*7c478bd9Sstevel@tonic-gate 		while ((*link != '\0') && (*link != '\\')) {
6247*7c478bd9Sstevel@tonic-gate 			/* a non-escaped string */
6248*7c478bd9Sstevel@tonic-gate 			constant[x++] = *(link++);
6249*7c478bd9Sstevel@tonic-gate 		}
6250*7c478bd9Sstevel@tonic-gate 		if (x != 0) {
6251*7c478bd9Sstevel@tonic-gate 			constant[x] = '\0';
6252*7c478bd9Sstevel@tonic-gate 			link_list->type = CONSTANT;
6253*7c478bd9Sstevel@tonic-gate 			link_list->constant = s_strdup(constant);
6254*7c478bd9Sstevel@tonic-gate 			x = 0;
6255*7c478bd9Sstevel@tonic-gate 			vprint(DEVLINK_MID, "CONSTANT FOUND %s\n", constant);
6256*7c478bd9Sstevel@tonic-gate 		} else {
6257*7c478bd9Sstevel@tonic-gate 			switch (*(++link)) {
6258*7c478bd9Sstevel@tonic-gate 			case 'M':
6259*7c478bd9Sstevel@tonic-gate 				link_list->type = MINOR;
6260*7c478bd9Sstevel@tonic-gate 				break;
6261*7c478bd9Sstevel@tonic-gate 			case 'A':
6262*7c478bd9Sstevel@tonic-gate 				link_list->type = ADDR;
6263*7c478bd9Sstevel@tonic-gate 				break;
6264*7c478bd9Sstevel@tonic-gate 			case 'N':
6265*7c478bd9Sstevel@tonic-gate 				if (counter_found == TRUE) {
6266*7c478bd9Sstevel@tonic-gate 					error = TRUE;
6267*7c478bd9Sstevel@tonic-gate 					error_str = "multiple counters "
6268*7c478bd9Sstevel@tonic-gate 						"not permitted";
6269*7c478bd9Sstevel@tonic-gate 					free(link_list);
6270*7c478bd9Sstevel@tonic-gate 				} else {
6271*7c478bd9Sstevel@tonic-gate 					counter_found = TRUE;
6272*7c478bd9Sstevel@tonic-gate 					link_list->type = COUNTER;
6273*7c478bd9Sstevel@tonic-gate 				}
6274*7c478bd9Sstevel@tonic-gate 				break;
6275*7c478bd9Sstevel@tonic-gate 			case 'D':
6276*7c478bd9Sstevel@tonic-gate 				link_list->type = NAME;
6277*7c478bd9Sstevel@tonic-gate 				break;
6278*7c478bd9Sstevel@tonic-gate 			default:
6279*7c478bd9Sstevel@tonic-gate 				error = TRUE;
6280*7c478bd9Sstevel@tonic-gate 				free(link_list);
6281*7c478bd9Sstevel@tonic-gate 				error_str = "unrecognized escape sequence";
6282*7c478bd9Sstevel@tonic-gate 				break;
6283*7c478bd9Sstevel@tonic-gate 			}
6284*7c478bd9Sstevel@tonic-gate 			if (*(link++) != 'D') {
6285*7c478bd9Sstevel@tonic-gate 				if (isdigit(*link) == FALSE) {
6286*7c478bd9Sstevel@tonic-gate 					error_str = "escape sequence must be "
6287*7c478bd9Sstevel@tonic-gate 						"followed by a digit\n";
6288*7c478bd9Sstevel@tonic-gate 					error = TRUE;
6289*7c478bd9Sstevel@tonic-gate 					free(link_list);
6290*7c478bd9Sstevel@tonic-gate 				} else {
6291*7c478bd9Sstevel@tonic-gate 					link_list->arg =
6292*7c478bd9Sstevel@tonic-gate 						(int)strtoul(link, &link, 10);
6293*7c478bd9Sstevel@tonic-gate 					vprint(DEVLINK_MID, "link_list->arg = "
6294*7c478bd9Sstevel@tonic-gate 						"%d\n", link_list->arg);
6295*7c478bd9Sstevel@tonic-gate 				}
6296*7c478bd9Sstevel@tonic-gate 			}
6297*7c478bd9Sstevel@tonic-gate 		}
6298*7c478bd9Sstevel@tonic-gate 		/* append link_list struct to end of list */
6299*7c478bd9Sstevel@tonic-gate 		if (error == FALSE) {
6300*7c478bd9Sstevel@tonic-gate 			for (ptr = &head; *ptr != NULL; ptr = &((*ptr)->next));
6301*7c478bd9Sstevel@tonic-gate 			*ptr = link_list;
6302*7c478bd9Sstevel@tonic-gate 		}
6303*7c478bd9Sstevel@tonic-gate 	}
6304*7c478bd9Sstevel@tonic-gate 
6305*7c478bd9Sstevel@tonic-gate 	if (error == FALSE) {
6306*7c478bd9Sstevel@tonic-gate 		return (head);
6307*7c478bd9Sstevel@tonic-gate 	} else {
6308*7c478bd9Sstevel@tonic-gate 		err_print(CONFIG_INCORRECT, devlinktab_line, devlinktab_file,
6309*7c478bd9Sstevel@tonic-gate 		    error_str);
6310*7c478bd9Sstevel@tonic-gate 		free_link_list(head);
6311*7c478bd9Sstevel@tonic-gate 		return (NULL);
6312*7c478bd9Sstevel@tonic-gate 	}
6313*7c478bd9Sstevel@tonic-gate }
6314*7c478bd9Sstevel@tonic-gate 
6315*7c478bd9Sstevel@tonic-gate /*
6316*7c478bd9Sstevel@tonic-gate  * Called for each minor node devfsadm processes; for each minor node,
6317*7c478bd9Sstevel@tonic-gate  * look for matches in the devlinktab_list list which was created on
6318*7c478bd9Sstevel@tonic-gate  * startup read_devlinktab_file().  If there is a match, call build_links()
6319*7c478bd9Sstevel@tonic-gate  * to build a logical devlink and a possible extra devlink.
6320*7c478bd9Sstevel@tonic-gate  */
6321*7c478bd9Sstevel@tonic-gate static int
6322*7c478bd9Sstevel@tonic-gate process_devlink_compat(di_minor_t minor, di_node_t node)
6323*7c478bd9Sstevel@tonic-gate {
6324*7c478bd9Sstevel@tonic-gate 	int link_built = FALSE;
6325*7c478bd9Sstevel@tonic-gate 	devlinktab_list_t *entry;
6326*7c478bd9Sstevel@tonic-gate 	char *nodetype;
6327*7c478bd9Sstevel@tonic-gate 	char *dev_path;
6328*7c478bd9Sstevel@tonic-gate 
6329*7c478bd9Sstevel@tonic-gate 	if (devlinks_debug == TRUE) {
6330*7c478bd9Sstevel@tonic-gate 		nodetype =  di_minor_nodetype(minor);
6331*7c478bd9Sstevel@tonic-gate 		assert(nodetype != NULL);
6332*7c478bd9Sstevel@tonic-gate 		if ((dev_path = di_devfs_path(node)) != NULL) {
6333*7c478bd9Sstevel@tonic-gate 			vprint(INFO_MID, "'%s' entry: %s:%s\n", nodetype,
6334*7c478bd9Sstevel@tonic-gate 				dev_path,
6335*7c478bd9Sstevel@tonic-gate 				di_minor_name(minor) ? di_minor_name(minor) :
6336*7c478bd9Sstevel@tonic-gate 				"");
6337*7c478bd9Sstevel@tonic-gate 			di_devfs_path_free(dev_path);
6338*7c478bd9Sstevel@tonic-gate 		}
6339*7c478bd9Sstevel@tonic-gate 
6340*7c478bd9Sstevel@tonic-gate 	}
6341*7c478bd9Sstevel@tonic-gate 
6342*7c478bd9Sstevel@tonic-gate 
6343*7c478bd9Sstevel@tonic-gate 	/* don't process devlink.tab if devfsadm invoked with -c <class> */
6344*7c478bd9Sstevel@tonic-gate 	if (num_classes > 0) {
6345*7c478bd9Sstevel@tonic-gate 		return (FALSE);
6346*7c478bd9Sstevel@tonic-gate 	}
6347*7c478bd9Sstevel@tonic-gate 
6348*7c478bd9Sstevel@tonic-gate 	for (entry = devlinktab_list; entry != NULL; entry = entry->next) {
6349*7c478bd9Sstevel@tonic-gate 		if (devlink_matches(entry, minor, node) == DEVFSADM_SUCCESS) {
6350*7c478bd9Sstevel@tonic-gate 			link_built = TRUE;
6351*7c478bd9Sstevel@tonic-gate 			(void) build_links(entry, minor, node);
6352*7c478bd9Sstevel@tonic-gate 		}
6353*7c478bd9Sstevel@tonic-gate 	}
6354*7c478bd9Sstevel@tonic-gate 	return (link_built);
6355*7c478bd9Sstevel@tonic-gate }
6356*7c478bd9Sstevel@tonic-gate 
6357*7c478bd9Sstevel@tonic-gate /*
6358*7c478bd9Sstevel@tonic-gate  * For a given devlink.tab devlinktab_list entry, see if the selector
6359*7c478bd9Sstevel@tonic-gate  * field matches this minor node.  If it does, return DEVFSADM_SUCCESS,
6360*7c478bd9Sstevel@tonic-gate  * otherwise DEVFSADM_FAILURE.
6361*7c478bd9Sstevel@tonic-gate  */
6362*7c478bd9Sstevel@tonic-gate static int
6363*7c478bd9Sstevel@tonic-gate devlink_matches(devlinktab_list_t *entry, di_minor_t minor, di_node_t node)
6364*7c478bd9Sstevel@tonic-gate {
6365*7c478bd9Sstevel@tonic-gate 	selector_list_t *selector = entry->selector;
6366*7c478bd9Sstevel@tonic-gate 	char *addr;
6367*7c478bd9Sstevel@tonic-gate 	char *minor_name;
6368*7c478bd9Sstevel@tonic-gate 	char *node_type;
6369*7c478bd9Sstevel@tonic-gate 
6370*7c478bd9Sstevel@tonic-gate 	for (; selector != NULL; selector = selector->next) {
6371*7c478bd9Sstevel@tonic-gate 		switch (selector->key) {
6372*7c478bd9Sstevel@tonic-gate 		case NAME:
6373*7c478bd9Sstevel@tonic-gate 			if (strcmp(di_node_name(node), selector->val) != 0) {
6374*7c478bd9Sstevel@tonic-gate 				return (DEVFSADM_FAILURE);
6375*7c478bd9Sstevel@tonic-gate 			}
6376*7c478bd9Sstevel@tonic-gate 			break;
6377*7c478bd9Sstevel@tonic-gate 		case TYPE:
6378*7c478bd9Sstevel@tonic-gate 			node_type = di_minor_nodetype(minor);
6379*7c478bd9Sstevel@tonic-gate 			assert(node_type != NULL);
6380*7c478bd9Sstevel@tonic-gate 			if (strcmp(node_type, selector->val) != 0) {
6381*7c478bd9Sstevel@tonic-gate 				return (DEVFSADM_FAILURE);
6382*7c478bd9Sstevel@tonic-gate 			}
6383*7c478bd9Sstevel@tonic-gate 			break;
6384*7c478bd9Sstevel@tonic-gate 		case ADDR:
6385*7c478bd9Sstevel@tonic-gate 			if ((addr = di_bus_addr(node)) == NULL) {
6386*7c478bd9Sstevel@tonic-gate 				return (DEVFSADM_FAILURE);
6387*7c478bd9Sstevel@tonic-gate 			}
6388*7c478bd9Sstevel@tonic-gate 			if (selector->arg == 0) {
6389*7c478bd9Sstevel@tonic-gate 				if (strcmp(addr, selector->val) != 0) {
6390*7c478bd9Sstevel@tonic-gate 					return (DEVFSADM_FAILURE);
6391*7c478bd9Sstevel@tonic-gate 				}
6392*7c478bd9Sstevel@tonic-gate 			} else {
6393*7c478bd9Sstevel@tonic-gate 				if (compare_field(addr, selector->val,
6394*7c478bd9Sstevel@tonic-gate 				    selector->arg) == DEVFSADM_FAILURE) {
6395*7c478bd9Sstevel@tonic-gate 					return (DEVFSADM_FAILURE);
6396*7c478bd9Sstevel@tonic-gate 				}
6397*7c478bd9Sstevel@tonic-gate 			}
6398*7c478bd9Sstevel@tonic-gate 			break;
6399*7c478bd9Sstevel@tonic-gate 		case MINOR:
6400*7c478bd9Sstevel@tonic-gate 			if ((minor_name = di_minor_name(minor)) == NULL) {
6401*7c478bd9Sstevel@tonic-gate 				return (DEVFSADM_FAILURE);
6402*7c478bd9Sstevel@tonic-gate 			}
6403*7c478bd9Sstevel@tonic-gate 			if (selector->arg == 0) {
6404*7c478bd9Sstevel@tonic-gate 				if (strcmp(minor_name, selector->val) != 0) {
6405*7c478bd9Sstevel@tonic-gate 					return (DEVFSADM_FAILURE);
6406*7c478bd9Sstevel@tonic-gate 				}
6407*7c478bd9Sstevel@tonic-gate 			} else {
6408*7c478bd9Sstevel@tonic-gate 				if (compare_field(minor_name, selector->val,
6409*7c478bd9Sstevel@tonic-gate 					selector->arg) == DEVFSADM_FAILURE) {
6410*7c478bd9Sstevel@tonic-gate 					return (DEVFSADM_FAILURE);
6411*7c478bd9Sstevel@tonic-gate 				}
6412*7c478bd9Sstevel@tonic-gate 			}
6413*7c478bd9Sstevel@tonic-gate 			break;
6414*7c478bd9Sstevel@tonic-gate 		default:
6415*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
6416*7c478bd9Sstevel@tonic-gate 		}
6417*7c478bd9Sstevel@tonic-gate 	}
6418*7c478bd9Sstevel@tonic-gate 
6419*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
6420*7c478bd9Sstevel@tonic-gate }
6421*7c478bd9Sstevel@tonic-gate 
6422*7c478bd9Sstevel@tonic-gate /*
6423*7c478bd9Sstevel@tonic-gate  * For the given minor node and devlinktab_list entry from devlink.tab,
6424*7c478bd9Sstevel@tonic-gate  * build a logical dev link and a possible extra devlink.
6425*7c478bd9Sstevel@tonic-gate  * Return DEVFSADM_SUCCESS if link is created, otherwise DEVFSADM_FAILURE.
6426*7c478bd9Sstevel@tonic-gate  */
6427*7c478bd9Sstevel@tonic-gate static int
6428*7c478bd9Sstevel@tonic-gate build_links(devlinktab_list_t *entry, di_minor_t minor, di_node_t node)
6429*7c478bd9Sstevel@tonic-gate {
6430*7c478bd9Sstevel@tonic-gate 	char secondary_link[PATH_MAX + 1];
6431*7c478bd9Sstevel@tonic-gate 	char primary_link[PATH_MAX + 1];
6432*7c478bd9Sstevel@tonic-gate 	char contents[PATH_MAX + 1];
6433*7c478bd9Sstevel@tonic-gate 	char *dev_path;
6434*7c478bd9Sstevel@tonic-gate 
6435*7c478bd9Sstevel@tonic-gate 	if ((dev_path = di_devfs_path(node)) == NULL) {
6436*7c478bd9Sstevel@tonic-gate 		err_print(DI_DEVFS_PATH_FAILED, strerror(errno));
6437*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
6438*7c478bd9Sstevel@tonic-gate 	}
6439*7c478bd9Sstevel@tonic-gate 	(void) strcpy(contents, dev_path);
6440*7c478bd9Sstevel@tonic-gate 	di_devfs_path_free(dev_path);
6441*7c478bd9Sstevel@tonic-gate 
6442*7c478bd9Sstevel@tonic-gate 	(void) strcat(contents, ":");
6443*7c478bd9Sstevel@tonic-gate 	(void) strcat(contents, di_minor_name(minor));
6444*7c478bd9Sstevel@tonic-gate 
6445*7c478bd9Sstevel@tonic-gate 	if (construct_devlink(primary_link, entry->p_link, contents,
6446*7c478bd9Sstevel@tonic-gate 				minor, node,
6447*7c478bd9Sstevel@tonic-gate 			    entry->p_link_pattern) == DEVFSADM_FAILURE) {
6448*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
6449*7c478bd9Sstevel@tonic-gate 	}
6450*7c478bd9Sstevel@tonic-gate 	(void) devfsadm_mklink(primary_link, node, minor, 0);
6451*7c478bd9Sstevel@tonic-gate 
6452*7c478bd9Sstevel@tonic-gate 	if (entry->s_link == NULL) {
6453*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
6454*7c478bd9Sstevel@tonic-gate 	}
6455*7c478bd9Sstevel@tonic-gate 
6456*7c478bd9Sstevel@tonic-gate 	if (construct_devlink(secondary_link, entry->s_link,
6457*7c478bd9Sstevel@tonic-gate 			primary_link, minor, node,
6458*7c478bd9Sstevel@tonic-gate 				entry->s_link_pattern) == DEVFSADM_FAILURE) {
6459*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
6460*7c478bd9Sstevel@tonic-gate 	}
6461*7c478bd9Sstevel@tonic-gate 
6462*7c478bd9Sstevel@tonic-gate 	(void) devfsadm_secondary_link(secondary_link, primary_link, 0);
6463*7c478bd9Sstevel@tonic-gate 
6464*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
6465*7c478bd9Sstevel@tonic-gate }
6466*7c478bd9Sstevel@tonic-gate 
6467*7c478bd9Sstevel@tonic-gate /*
6468*7c478bd9Sstevel@tonic-gate  * The counter rule for devlink.tab entries is implemented via
6469*7c478bd9Sstevel@tonic-gate  * devfsadm_enumerate_int_start(). One of the arguments to this function
6470*7c478bd9Sstevel@tonic-gate  * is a path, where each path component is treated as a regular expression.
6471*7c478bd9Sstevel@tonic-gate  * For devlink.tab entries, this path regular expression is derived from
6472*7c478bd9Sstevel@tonic-gate  * the devlink spec. get_anchored_re() accepts path regular expressions derived
6473*7c478bd9Sstevel@tonic-gate  * from devlink.tab entries and inserts the anchors '^' and '$' at the beginning
6474*7c478bd9Sstevel@tonic-gate  * and end respectively of each path component. This is done to prevent
6475*7c478bd9Sstevel@tonic-gate  * false matches. For example, without anchors, "a/([0-9]+)" will match "ab/c9"
6476*7c478bd9Sstevel@tonic-gate  * and incorrect links will be generated.
6477*7c478bd9Sstevel@tonic-gate  */
6478*7c478bd9Sstevel@tonic-gate static int
6479*7c478bd9Sstevel@tonic-gate get_anchored_re(char *link, char *anchored_re, char *pattern)
6480*7c478bd9Sstevel@tonic-gate {
6481*7c478bd9Sstevel@tonic-gate 	if (*link == '/' || *link == '\0') {
6482*7c478bd9Sstevel@tonic-gate 		err_print(INVALID_DEVLINK_SPEC, pattern);
6483*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
6484*7c478bd9Sstevel@tonic-gate 	}
6485*7c478bd9Sstevel@tonic-gate 
6486*7c478bd9Sstevel@tonic-gate 	*anchored_re++ = '^';
6487*7c478bd9Sstevel@tonic-gate 	for (; *link != '\0'; ) {
6488*7c478bd9Sstevel@tonic-gate 		if (*link == '/') {
6489*7c478bd9Sstevel@tonic-gate 			while (*link == '/')
6490*7c478bd9Sstevel@tonic-gate 				link++;
6491*7c478bd9Sstevel@tonic-gate 			*anchored_re++ = '$';
6492*7c478bd9Sstevel@tonic-gate 			*anchored_re++ = '/';
6493*7c478bd9Sstevel@tonic-gate 			if (*link != '\0') {
6494*7c478bd9Sstevel@tonic-gate 				*anchored_re++ = '^';
6495*7c478bd9Sstevel@tonic-gate 			}
6496*7c478bd9Sstevel@tonic-gate 		} else {
6497*7c478bd9Sstevel@tonic-gate 			*anchored_re++ = *link++;
6498*7c478bd9Sstevel@tonic-gate 			if (*link == '\0') {
6499*7c478bd9Sstevel@tonic-gate 				*anchored_re++ = '$';
6500*7c478bd9Sstevel@tonic-gate 			}
6501*7c478bd9Sstevel@tonic-gate 		}
6502*7c478bd9Sstevel@tonic-gate 	}
6503*7c478bd9Sstevel@tonic-gate 	*anchored_re = '\0';
6504*7c478bd9Sstevel@tonic-gate 
6505*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
6506*7c478bd9Sstevel@tonic-gate }
6507*7c478bd9Sstevel@tonic-gate 
6508*7c478bd9Sstevel@tonic-gate static int
6509*7c478bd9Sstevel@tonic-gate construct_devlink(char *link, link_list_t *link_build, char *contents,
6510*7c478bd9Sstevel@tonic-gate 			di_minor_t minor, di_node_t node, char *pattern)
6511*7c478bd9Sstevel@tonic-gate {
6512*7c478bd9Sstevel@tonic-gate 	int counter_offset = -1;
6513*7c478bd9Sstevel@tonic-gate 	devfsadm_enumerate_t rules[1] = {NULL};
6514*7c478bd9Sstevel@tonic-gate 	char templink[PATH_MAX + 1];
6515*7c478bd9Sstevel@tonic-gate 	char *buff;
6516*7c478bd9Sstevel@tonic-gate 	char start[10];
6517*7c478bd9Sstevel@tonic-gate 	char *node_path;
6518*7c478bd9Sstevel@tonic-gate 	char anchored_re[PATH_MAX + 1];
6519*7c478bd9Sstevel@tonic-gate 
6520*7c478bd9Sstevel@tonic-gate 	link[0] = '\0';
6521*7c478bd9Sstevel@tonic-gate 
6522*7c478bd9Sstevel@tonic-gate 	for (; link_build != NULL; link_build = link_build->next) {
6523*7c478bd9Sstevel@tonic-gate 		switch (link_build->type) {
6524*7c478bd9Sstevel@tonic-gate 		case NAME:
6525*7c478bd9Sstevel@tonic-gate 			(void) strcat(link, di_node_name(node));
6526*7c478bd9Sstevel@tonic-gate 			break;
6527*7c478bd9Sstevel@tonic-gate 		case CONSTANT:
6528*7c478bd9Sstevel@tonic-gate 			(void) strcat(link, link_build->constant);
6529*7c478bd9Sstevel@tonic-gate 			break;
6530*7c478bd9Sstevel@tonic-gate 		case ADDR:
6531*7c478bd9Sstevel@tonic-gate 			if (component_cat(link, di_bus_addr(node),
6532*7c478bd9Sstevel@tonic-gate 				    link_build->arg) == DEVFSADM_FAILURE) {
6533*7c478bd9Sstevel@tonic-gate 				node_path = di_devfs_path(node);
6534*7c478bd9Sstevel@tonic-gate 				err_print(CANNOT_BE_USED, pattern, node_path,
6535*7c478bd9Sstevel@tonic-gate 					    di_minor_name(minor));
6536*7c478bd9Sstevel@tonic-gate 				di_devfs_path_free(node_path);
6537*7c478bd9Sstevel@tonic-gate 				return (DEVFSADM_FAILURE);
6538*7c478bd9Sstevel@tonic-gate 			}
6539*7c478bd9Sstevel@tonic-gate 			break;
6540*7c478bd9Sstevel@tonic-gate 		case MINOR:
6541*7c478bd9Sstevel@tonic-gate 			if (component_cat(link, di_minor_name(minor),
6542*7c478bd9Sstevel@tonic-gate 				    link_build->arg) == DEVFSADM_FAILURE) {
6543*7c478bd9Sstevel@tonic-gate 				node_path = di_devfs_path(node);
6544*7c478bd9Sstevel@tonic-gate 				err_print(CANNOT_BE_USED, pattern, node_path,
6545*7c478bd9Sstevel@tonic-gate 					    di_minor_name(minor));
6546*7c478bd9Sstevel@tonic-gate 				di_devfs_path_free(node_path);
6547*7c478bd9Sstevel@tonic-gate 				return (DEVFSADM_FAILURE);
6548*7c478bd9Sstevel@tonic-gate 			}
6549*7c478bd9Sstevel@tonic-gate 			break;
6550*7c478bd9Sstevel@tonic-gate 		case COUNTER:
6551*7c478bd9Sstevel@tonic-gate 			counter_offset = strlen(link);
6552*7c478bd9Sstevel@tonic-gate 			(void) strcat(link, "([0-9]+)");
6553*7c478bd9Sstevel@tonic-gate 			(void) sprintf(start, "%d", link_build->arg);
6554*7c478bd9Sstevel@tonic-gate 			break;
6555*7c478bd9Sstevel@tonic-gate 		default:
6556*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
6557*7c478bd9Sstevel@tonic-gate 		}
6558*7c478bd9Sstevel@tonic-gate 	}
6559*7c478bd9Sstevel@tonic-gate 
6560*7c478bd9Sstevel@tonic-gate 	if (counter_offset != -1) {
6561*7c478bd9Sstevel@tonic-gate 		/*
6562*7c478bd9Sstevel@tonic-gate 		 * copy anything appended after "([0-9]+)" into
6563*7c478bd9Sstevel@tonic-gate 		 * templink
6564*7c478bd9Sstevel@tonic-gate 		 */
6565*7c478bd9Sstevel@tonic-gate 
6566*7c478bd9Sstevel@tonic-gate 		(void) strcpy(templink,
6567*7c478bd9Sstevel@tonic-gate 			    &link[counter_offset + strlen("([0-9]+)")]);
6568*7c478bd9Sstevel@tonic-gate 		if (get_anchored_re(link, anchored_re, pattern)
6569*7c478bd9Sstevel@tonic-gate 		    != DEVFSADM_SUCCESS) {
6570*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
6571*7c478bd9Sstevel@tonic-gate 		}
6572*7c478bd9Sstevel@tonic-gate 		rules[0].re = anchored_re;
6573*7c478bd9Sstevel@tonic-gate 		rules[0].subexp = 1;
6574*7c478bd9Sstevel@tonic-gate 		rules[0].flags = MATCH_ALL;
6575*7c478bd9Sstevel@tonic-gate 		if (devfsadm_enumerate_int_start(contents, 0, &buff,
6576*7c478bd9Sstevel@tonic-gate 		    rules, 1, start) == DEVFSADM_FAILURE) {
6577*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
6578*7c478bd9Sstevel@tonic-gate 		}
6579*7c478bd9Sstevel@tonic-gate 		(void) strcpy(&link[counter_offset], buff);
6580*7c478bd9Sstevel@tonic-gate 		free(buff);
6581*7c478bd9Sstevel@tonic-gate 		(void) strcat(link, templink);
6582*7c478bd9Sstevel@tonic-gate 		vprint(DEVLINK_MID, "COUNTER is	%s\n", link);
6583*7c478bd9Sstevel@tonic-gate 	}
6584*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
6585*7c478bd9Sstevel@tonic-gate }
6586*7c478bd9Sstevel@tonic-gate 
6587*7c478bd9Sstevel@tonic-gate /*
6588*7c478bd9Sstevel@tonic-gate  * Compares "field" number of the comma separated list "full_name" with
6589*7c478bd9Sstevel@tonic-gate  * field_item.	Returns DEVFSADM_SUCCESS for match,
6590*7c478bd9Sstevel@tonic-gate  * DEVFSADM_FAILURE for no match.
6591*7c478bd9Sstevel@tonic-gate  */
6592*7c478bd9Sstevel@tonic-gate static int
6593*7c478bd9Sstevel@tonic-gate compare_field(char *full_name, char *field_item, int field)
6594*7c478bd9Sstevel@tonic-gate {
6595*7c478bd9Sstevel@tonic-gate 	--field;
6596*7c478bd9Sstevel@tonic-gate 	while ((*full_name != '\0') && (field != 0)) {
6597*7c478bd9Sstevel@tonic-gate 		if (*(full_name++) == ',') {
6598*7c478bd9Sstevel@tonic-gate 			field--;
6599*7c478bd9Sstevel@tonic-gate 		}
6600*7c478bd9Sstevel@tonic-gate 	}
6601*7c478bd9Sstevel@tonic-gate 
6602*7c478bd9Sstevel@tonic-gate 	if (field != 0) {
6603*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
6604*7c478bd9Sstevel@tonic-gate 	}
6605*7c478bd9Sstevel@tonic-gate 
6606*7c478bd9Sstevel@tonic-gate 	while ((*full_name != '\0') && (*field_item != '\0') &&
6607*7c478bd9Sstevel@tonic-gate 			(*full_name != ',')) {
6608*7c478bd9Sstevel@tonic-gate 		if (*(full_name++) != *(field_item++)) {
6609*7c478bd9Sstevel@tonic-gate 			return (DEVFSADM_FAILURE);
6610*7c478bd9Sstevel@tonic-gate 		}
6611*7c478bd9Sstevel@tonic-gate 	}
6612*7c478bd9Sstevel@tonic-gate 
6613*7c478bd9Sstevel@tonic-gate 	if (*field_item != '\0') {
6614*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
6615*7c478bd9Sstevel@tonic-gate 	}
6616*7c478bd9Sstevel@tonic-gate 
6617*7c478bd9Sstevel@tonic-gate 	if ((*full_name == '\0') || (*full_name == ','))
6618*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
6619*7c478bd9Sstevel@tonic-gate 
6620*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_FAILURE);
6621*7c478bd9Sstevel@tonic-gate }
6622*7c478bd9Sstevel@tonic-gate 
6623*7c478bd9Sstevel@tonic-gate /*
6624*7c478bd9Sstevel@tonic-gate  * strcat() field # "field" of comma separated list "name" to "link".
6625*7c478bd9Sstevel@tonic-gate  * Field 0 is the entire name.
6626*7c478bd9Sstevel@tonic-gate  * Return DEVFSADM_SUCCESS or DEVFSADM_FAILURE.
6627*7c478bd9Sstevel@tonic-gate  */
6628*7c478bd9Sstevel@tonic-gate static int
6629*7c478bd9Sstevel@tonic-gate component_cat(char *link, char *name, int field)
6630*7c478bd9Sstevel@tonic-gate {
6631*7c478bd9Sstevel@tonic-gate 
6632*7c478bd9Sstevel@tonic-gate 	if (name == NULL) {
6633*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
6634*7c478bd9Sstevel@tonic-gate 	}
6635*7c478bd9Sstevel@tonic-gate 
6636*7c478bd9Sstevel@tonic-gate 	if (field == 0) {
6637*7c478bd9Sstevel@tonic-gate 		(void) strcat(link, name);
6638*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_SUCCESS);
6639*7c478bd9Sstevel@tonic-gate 	}
6640*7c478bd9Sstevel@tonic-gate 
6641*7c478bd9Sstevel@tonic-gate 	while (*link != '\0') {
6642*7c478bd9Sstevel@tonic-gate 		link++;
6643*7c478bd9Sstevel@tonic-gate 	}
6644*7c478bd9Sstevel@tonic-gate 
6645*7c478bd9Sstevel@tonic-gate 	--field;
6646*7c478bd9Sstevel@tonic-gate 	while ((*name != '\0') && (field != 0)) {
6647*7c478bd9Sstevel@tonic-gate 		if (*(name++) == ',') {
6648*7c478bd9Sstevel@tonic-gate 			--field;
6649*7c478bd9Sstevel@tonic-gate 		}
6650*7c478bd9Sstevel@tonic-gate 	}
6651*7c478bd9Sstevel@tonic-gate 
6652*7c478bd9Sstevel@tonic-gate 	if (field != 0) {
6653*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
6654*7c478bd9Sstevel@tonic-gate 	}
6655*7c478bd9Sstevel@tonic-gate 
6656*7c478bd9Sstevel@tonic-gate 	while ((*name != '\0') && (*name != ',')) {
6657*7c478bd9Sstevel@tonic-gate 		*(link++) = *(name++);
6658*7c478bd9Sstevel@tonic-gate 	}
6659*7c478bd9Sstevel@tonic-gate 
6660*7c478bd9Sstevel@tonic-gate 	*link = '\0';
6661*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
6662*7c478bd9Sstevel@tonic-gate }
6663*7c478bd9Sstevel@tonic-gate 
6664*7c478bd9Sstevel@tonic-gate static void
6665*7c478bd9Sstevel@tonic-gate free_selector_list(selector_list_t *head)
6666*7c478bd9Sstevel@tonic-gate {
6667*7c478bd9Sstevel@tonic-gate 	selector_list_t *temp;
6668*7c478bd9Sstevel@tonic-gate 
6669*7c478bd9Sstevel@tonic-gate 	while (head != NULL) {
6670*7c478bd9Sstevel@tonic-gate 		temp = head;
6671*7c478bd9Sstevel@tonic-gate 		head = head->next;
6672*7c478bd9Sstevel@tonic-gate 		free(temp->val);
6673*7c478bd9Sstevel@tonic-gate 		free(temp);
6674*7c478bd9Sstevel@tonic-gate 	}
6675*7c478bd9Sstevel@tonic-gate }
6676*7c478bd9Sstevel@tonic-gate 
6677*7c478bd9Sstevel@tonic-gate static void
6678*7c478bd9Sstevel@tonic-gate free_link_list(link_list_t *head)
6679*7c478bd9Sstevel@tonic-gate {
6680*7c478bd9Sstevel@tonic-gate 	link_list_t *temp;
6681*7c478bd9Sstevel@tonic-gate 
6682*7c478bd9Sstevel@tonic-gate 	while (head != NULL) {
6683*7c478bd9Sstevel@tonic-gate 		temp = head;
6684*7c478bd9Sstevel@tonic-gate 		head = head->next;
6685*7c478bd9Sstevel@tonic-gate 		if (temp->type == CONSTANT) {
6686*7c478bd9Sstevel@tonic-gate 			free(temp->constant);
6687*7c478bd9Sstevel@tonic-gate 		}
6688*7c478bd9Sstevel@tonic-gate 		free(temp);
6689*7c478bd9Sstevel@tonic-gate 	}
6690*7c478bd9Sstevel@tonic-gate }
6691*7c478bd9Sstevel@tonic-gate 
6692*7c478bd9Sstevel@tonic-gate /*
6693*7c478bd9Sstevel@tonic-gate  * Prints only if level matches one of the debug levels
6694*7c478bd9Sstevel@tonic-gate  * given on command line.  INFO_MID is always printed.
6695*7c478bd9Sstevel@tonic-gate  *
6696*7c478bd9Sstevel@tonic-gate  * See devfsadm.h for a listing of globally defined levels and
6697*7c478bd9Sstevel@tonic-gate  * meanings.  Modules should prefix the level with their
6698*7c478bd9Sstevel@tonic-gate  * module name to prevent collisions.
6699*7c478bd9Sstevel@tonic-gate  */
6700*7c478bd9Sstevel@tonic-gate /*PRINTFLIKE2*/
6701*7c478bd9Sstevel@tonic-gate void
6702*7c478bd9Sstevel@tonic-gate devfsadm_print(char *msgid, char *message, ...)
6703*7c478bd9Sstevel@tonic-gate {
6704*7c478bd9Sstevel@tonic-gate 	va_list ap;
6705*7c478bd9Sstevel@tonic-gate 	static int newline = TRUE;
6706*7c478bd9Sstevel@tonic-gate 	int x;
6707*7c478bd9Sstevel@tonic-gate 
6708*7c478bd9Sstevel@tonic-gate 	if (msgid != NULL) {
6709*7c478bd9Sstevel@tonic-gate 		for (x = 0; x < num_verbose; x++) {
6710*7c478bd9Sstevel@tonic-gate 			if (strcmp(verbose[x], msgid) == 0) {
6711*7c478bd9Sstevel@tonic-gate 				break;
6712*7c478bd9Sstevel@tonic-gate 			}
6713*7c478bd9Sstevel@tonic-gate 			if (strcmp(verbose[x], ALL_MID) == 0) {
6714*7c478bd9Sstevel@tonic-gate 				break;
6715*7c478bd9Sstevel@tonic-gate 			}
6716*7c478bd9Sstevel@tonic-gate 		}
6717*7c478bd9Sstevel@tonic-gate 		if (x == num_verbose) {
6718*7c478bd9Sstevel@tonic-gate 			return;
6719*7c478bd9Sstevel@tonic-gate 		}
6720*7c478bd9Sstevel@tonic-gate 	}
6721*7c478bd9Sstevel@tonic-gate 
6722*7c478bd9Sstevel@tonic-gate 	va_start(ap, message);
6723*7c478bd9Sstevel@tonic-gate 
6724*7c478bd9Sstevel@tonic-gate 	if (msgid == NULL) {
6725*7c478bd9Sstevel@tonic-gate 		if (logflag == TRUE) {
6726*7c478bd9Sstevel@tonic-gate 			(void) vsyslog(LOG_NOTICE, message, ap);
6727*7c478bd9Sstevel@tonic-gate 		} else {
6728*7c478bd9Sstevel@tonic-gate 			(void) vfprintf(stdout, message, ap);
6729*7c478bd9Sstevel@tonic-gate 		}
6730*7c478bd9Sstevel@tonic-gate 
6731*7c478bd9Sstevel@tonic-gate 	} else {
6732*7c478bd9Sstevel@tonic-gate 		if (logflag == TRUE) {
6733*7c478bd9Sstevel@tonic-gate 			(void) syslog(LOG_DEBUG, "%s[%ld]: %s: ",
6734*7c478bd9Sstevel@tonic-gate 				    prog, getpid(), msgid);
6735*7c478bd9Sstevel@tonic-gate 			(void) vsyslog(LOG_DEBUG, message, ap);
6736*7c478bd9Sstevel@tonic-gate 		} else {
6737*7c478bd9Sstevel@tonic-gate 			if (newline == TRUE) {
6738*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stdout, "%s[%ld]: %s: ",
6739*7c478bd9Sstevel@tonic-gate 					prog, getpid(), msgid);
6740*7c478bd9Sstevel@tonic-gate 			}
6741*7c478bd9Sstevel@tonic-gate 			(void) vfprintf(stdout, message, ap);
6742*7c478bd9Sstevel@tonic-gate 		}
6743*7c478bd9Sstevel@tonic-gate 	}
6744*7c478bd9Sstevel@tonic-gate 
6745*7c478bd9Sstevel@tonic-gate 	if (message[strlen(message) - 1] == '\n') {
6746*7c478bd9Sstevel@tonic-gate 		newline = TRUE;
6747*7c478bd9Sstevel@tonic-gate 	} else {
6748*7c478bd9Sstevel@tonic-gate 		newline = FALSE;
6749*7c478bd9Sstevel@tonic-gate 	}
6750*7c478bd9Sstevel@tonic-gate 	va_end(ap);
6751*7c478bd9Sstevel@tonic-gate }
6752*7c478bd9Sstevel@tonic-gate 
6753*7c478bd9Sstevel@tonic-gate /*
6754*7c478bd9Sstevel@tonic-gate  * print error messages to the terminal or to syslog
6755*7c478bd9Sstevel@tonic-gate  */
6756*7c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/
6757*7c478bd9Sstevel@tonic-gate void
6758*7c478bd9Sstevel@tonic-gate devfsadm_errprint(char *message, ...)
6759*7c478bd9Sstevel@tonic-gate {
6760*7c478bd9Sstevel@tonic-gate 	va_list ap;
6761*7c478bd9Sstevel@tonic-gate 
6762*7c478bd9Sstevel@tonic-gate 	va_start(ap, message);
6763*7c478bd9Sstevel@tonic-gate 
6764*7c478bd9Sstevel@tonic-gate 	if (logflag == TRUE) {
6765*7c478bd9Sstevel@tonic-gate 		(void) vsyslog(LOG_ERR, message, ap);
6766*7c478bd9Sstevel@tonic-gate 	} else {
6767*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", prog);
6768*7c478bd9Sstevel@tonic-gate 		(void) vfprintf(stderr, message, ap);
6769*7c478bd9Sstevel@tonic-gate 	}
6770*7c478bd9Sstevel@tonic-gate 	va_end(ap);
6771*7c478bd9Sstevel@tonic-gate }
6772*7c478bd9Sstevel@tonic-gate 
6773*7c478bd9Sstevel@tonic-gate /*
6774*7c478bd9Sstevel@tonic-gate  * return noupdate state (-s)
6775*7c478bd9Sstevel@tonic-gate  */
6776*7c478bd9Sstevel@tonic-gate int
6777*7c478bd9Sstevel@tonic-gate devfsadm_noupdate(void)
6778*7c478bd9Sstevel@tonic-gate {
6779*7c478bd9Sstevel@tonic-gate 	return (file_mods == TRUE ? DEVFSADM_TRUE : DEVFSADM_FALSE);
6780*7c478bd9Sstevel@tonic-gate }
6781*7c478bd9Sstevel@tonic-gate 
6782*7c478bd9Sstevel@tonic-gate /*
6783*7c478bd9Sstevel@tonic-gate  * return current root update path (-r)
6784*7c478bd9Sstevel@tonic-gate  */
6785*7c478bd9Sstevel@tonic-gate const char *
6786*7c478bd9Sstevel@tonic-gate devfsadm_root_path(void)
6787*7c478bd9Sstevel@tonic-gate {
6788*7c478bd9Sstevel@tonic-gate 	if (root_dir[0] == '\0') {
6789*7c478bd9Sstevel@tonic-gate 		return ("/");
6790*7c478bd9Sstevel@tonic-gate 	} else {
6791*7c478bd9Sstevel@tonic-gate 		return ((const char *)root_dir);
6792*7c478bd9Sstevel@tonic-gate 	}
6793*7c478bd9Sstevel@tonic-gate }
6794*7c478bd9Sstevel@tonic-gate 
6795*7c478bd9Sstevel@tonic-gate /* common exit function which ensures releasing locks */
6796*7c478bd9Sstevel@tonic-gate static void
6797*7c478bd9Sstevel@tonic-gate devfsadm_exit(int status)
6798*7c478bd9Sstevel@tonic-gate {
6799*7c478bd9Sstevel@tonic-gate 	if (DEVFSADM_DEBUG_ON) {
6800*7c478bd9Sstevel@tonic-gate 		vprint(INFO_MID, "exit status = %d\n", status);
6801*7c478bd9Sstevel@tonic-gate 	}
6802*7c478bd9Sstevel@tonic-gate 
6803*7c478bd9Sstevel@tonic-gate 	if (rcm_hdl) {
6804*7c478bd9Sstevel@tonic-gate 		if (thr_self() != process_rcm_events_tid) {
6805*7c478bd9Sstevel@tonic-gate 			(void) mutex_lock(&rcm_eventq_lock);
6806*7c478bd9Sstevel@tonic-gate 			need_to_exit_rcm_event_thread = 1;
6807*7c478bd9Sstevel@tonic-gate 			(void) cond_broadcast(&rcm_eventq_cv);
6808*7c478bd9Sstevel@tonic-gate 			(void) mutex_unlock(&rcm_eventq_lock);
6809*7c478bd9Sstevel@tonic-gate 
6810*7c478bd9Sstevel@tonic-gate 			/* wait until process_rcm_events() thread exits */
6811*7c478bd9Sstevel@tonic-gate 			(void) thr_join(process_rcm_events_tid, NULL, NULL);
6812*7c478bd9Sstevel@tonic-gate 		}
6813*7c478bd9Sstevel@tonic-gate 		librcm_free_handle(rcm_hdl);
6814*7c478bd9Sstevel@tonic-gate 		(void) dlclose(librcm_hdl);
6815*7c478bd9Sstevel@tonic-gate 	}
6816*7c478bd9Sstevel@tonic-gate 
6817*7c478bd9Sstevel@tonic-gate 	zlist_deleteall_unlocked();		/* dispose of all zones */
6818*7c478bd9Sstevel@tonic-gate 
6819*7c478bd9Sstevel@tonic-gate 	exit_dev_lock();
6820*7c478bd9Sstevel@tonic-gate 	exit_daemon_lock();
6821*7c478bd9Sstevel@tonic-gate 
6822*7c478bd9Sstevel@tonic-gate 	if (logflag == TRUE) {
6823*7c478bd9Sstevel@tonic-gate 		closelog();
6824*7c478bd9Sstevel@tonic-gate 	}
6825*7c478bd9Sstevel@tonic-gate 
6826*7c478bd9Sstevel@tonic-gate 	exit(status);
6827*7c478bd9Sstevel@tonic-gate }
6828*7c478bd9Sstevel@tonic-gate 
6829*7c478bd9Sstevel@tonic-gate /*
6830*7c478bd9Sstevel@tonic-gate  * set root_dir, devices_dir, dev_dir using optarg.  zone_mode determines
6831*7c478bd9Sstevel@tonic-gate  * whether we're operating on behalf of a zone; in this case, we need to
6832*7c478bd9Sstevel@tonic-gate  * reference some things from the global zone.  Note that zone mode and
6833*7c478bd9Sstevel@tonic-gate  * -R don't get along, but that should be OK since zone mode is not
6834*7c478bd9Sstevel@tonic-gate  * a public interface.
6835*7c478bd9Sstevel@tonic-gate  */
6836*7c478bd9Sstevel@tonic-gate static void
6837*7c478bd9Sstevel@tonic-gate set_root_devices_dev_dir(char *dir, int zone_mode)
6838*7c478bd9Sstevel@tonic-gate {
6839*7c478bd9Sstevel@tonic-gate 	size_t len;
6840*7c478bd9Sstevel@tonic-gate 
6841*7c478bd9Sstevel@tonic-gate 	root_dir = s_strdup(dir);
6842*7c478bd9Sstevel@tonic-gate 	len = strlen(dir) + strlen(DEVICES) + 1;
6843*7c478bd9Sstevel@tonic-gate 	devices_dir = s_malloc(len);
6844*7c478bd9Sstevel@tonic-gate 	(void) snprintf(devices_dir, len, "%s%s", root_dir, DEVICES);
6845*7c478bd9Sstevel@tonic-gate 	len = strlen(root_dir) + strlen(DEV) + 1;
6846*7c478bd9Sstevel@tonic-gate 	dev_dir = s_malloc(len);
6847*7c478bd9Sstevel@tonic-gate 	(void) snprintf(dev_dir, len, "%s%s", root_dir, DEV);
6848*7c478bd9Sstevel@tonic-gate 	if (zone_mode) {
6849*7c478bd9Sstevel@tonic-gate 		len = strlen(DEV) + 1;
6850*7c478bd9Sstevel@tonic-gate 		global_dev_dir = s_malloc(len);
6851*7c478bd9Sstevel@tonic-gate 		(void) snprintf(global_dev_dir, len, "%s", DEV);
6852*7c478bd9Sstevel@tonic-gate 	} else {
6853*7c478bd9Sstevel@tonic-gate 		global_dev_dir = s_malloc(len);
6854*7c478bd9Sstevel@tonic-gate 		(void) snprintf(global_dev_dir, len, "%s%s", root_dir, DEV);
6855*7c478bd9Sstevel@tonic-gate 	}
6856*7c478bd9Sstevel@tonic-gate }
6857*7c478bd9Sstevel@tonic-gate 
6858*7c478bd9Sstevel@tonic-gate /*
6859*7c478bd9Sstevel@tonic-gate  * Removes quotes.
6860*7c478bd9Sstevel@tonic-gate  */
6861*7c478bd9Sstevel@tonic-gate static char *
6862*7c478bd9Sstevel@tonic-gate dequote(char *src)
6863*7c478bd9Sstevel@tonic-gate {
6864*7c478bd9Sstevel@tonic-gate 	char	*dst;
6865*7c478bd9Sstevel@tonic-gate 	int	len;
6866*7c478bd9Sstevel@tonic-gate 
6867*7c478bd9Sstevel@tonic-gate 	len = strlen(src);
6868*7c478bd9Sstevel@tonic-gate 	dst = s_malloc(len + 1);
6869*7c478bd9Sstevel@tonic-gate 	if (src[0] == '\"' && src[len - 1] == '\"') {
6870*7c478bd9Sstevel@tonic-gate 		len -= 2;
6871*7c478bd9Sstevel@tonic-gate 		(void) strncpy(dst, &src[1], len);
6872*7c478bd9Sstevel@tonic-gate 		dst[len] = '\0';
6873*7c478bd9Sstevel@tonic-gate 	} else {
6874*7c478bd9Sstevel@tonic-gate 		(void) strcpy(dst, src);
6875*7c478bd9Sstevel@tonic-gate 	}
6876*7c478bd9Sstevel@tonic-gate 	return (dst);
6877*7c478bd9Sstevel@tonic-gate }
6878*7c478bd9Sstevel@tonic-gate 
6879*7c478bd9Sstevel@tonic-gate /*
6880*7c478bd9Sstevel@tonic-gate  * For a given physical device pathname and spectype, return the
6881*7c478bd9Sstevel@tonic-gate  * ownership and permissions attributes by looking in data from
6882*7c478bd9Sstevel@tonic-gate  * /etc/minor_perm.  If currently in installation mode, check for
6883*7c478bd9Sstevel@tonic-gate  * possible major number translations from the miniroot to the installed
6884*7c478bd9Sstevel@tonic-gate  * root's name_to_major table. Note that there can be multiple matches,
6885*7c478bd9Sstevel@tonic-gate  * but the last match takes effect.  pts seems to rely on this
6886*7c478bd9Sstevel@tonic-gate  * implementation behavior.
6887*7c478bd9Sstevel@tonic-gate  */
6888*7c478bd9Sstevel@tonic-gate static void
6889*7c478bd9Sstevel@tonic-gate getattr(char *phy_path, char *aminor, int spectype, dev_t dev, mode_t *mode,
6890*7c478bd9Sstevel@tonic-gate 	uid_t *uid, gid_t *gid)
6891*7c478bd9Sstevel@tonic-gate {
6892*7c478bd9Sstevel@tonic-gate 	char devname[PATH_MAX + 1];
6893*7c478bd9Sstevel@tonic-gate 	char *node_name;
6894*7c478bd9Sstevel@tonic-gate 	char *minor_name;
6895*7c478bd9Sstevel@tonic-gate 	int match = FALSE;
6896*7c478bd9Sstevel@tonic-gate 	int is_clone;
6897*7c478bd9Sstevel@tonic-gate 	int mp_drvname_matches_node_name;
6898*7c478bd9Sstevel@tonic-gate 	int mp_drvname_matches_minor_name;
6899*7c478bd9Sstevel@tonic-gate 	int mp_drvname_is_clone;
6900*7c478bd9Sstevel@tonic-gate 	int mp_drvname_matches_drvname;
6901*7c478bd9Sstevel@tonic-gate 	struct mperm *mp;
6902*7c478bd9Sstevel@tonic-gate 	major_t major_no;
6903*7c478bd9Sstevel@tonic-gate 	char driver[PATH_MAX + 1];
6904*7c478bd9Sstevel@tonic-gate 
6905*7c478bd9Sstevel@tonic-gate 	/*
6906*7c478bd9Sstevel@tonic-gate 	 * Get the driver name based on the major number since the name
6907*7c478bd9Sstevel@tonic-gate 	 * in /devices may be generic.  Could be running with more major
6908*7c478bd9Sstevel@tonic-gate 	 * numbers than are in /etc/name_to_major, so get it from the kernel
6909*7c478bd9Sstevel@tonic-gate 	 */
6910*7c478bd9Sstevel@tonic-gate 	major_no = major(dev);
6911*7c478bd9Sstevel@tonic-gate 
6912*7c478bd9Sstevel@tonic-gate 	if (modctl(MODGETNAME, driver, sizeof (driver), &major_no) != 0) {
6913*7c478bd9Sstevel@tonic-gate 		/* return default values */
6914*7c478bd9Sstevel@tonic-gate 		goto use_defaults;
6915*7c478bd9Sstevel@tonic-gate 	}
6916*7c478bd9Sstevel@tonic-gate 
6917*7c478bd9Sstevel@tonic-gate 	(void) strcpy(devname, phy_path);
6918*7c478bd9Sstevel@tonic-gate 
6919*7c478bd9Sstevel@tonic-gate 	node_name = strrchr(devname, '/'); /* node name is the last */
6920*7c478bd9Sstevel@tonic-gate 					/* component */
6921*7c478bd9Sstevel@tonic-gate 	if (node_name == NULL) {
6922*7c478bd9Sstevel@tonic-gate 		err_print(NO_NODE, devname);
6923*7c478bd9Sstevel@tonic-gate 		goto use_defaults;
6924*7c478bd9Sstevel@tonic-gate 	}
6925*7c478bd9Sstevel@tonic-gate 
6926*7c478bd9Sstevel@tonic-gate 	minor_name = strchr(++node_name, '@'); /* see if it has address part */
6927*7c478bd9Sstevel@tonic-gate 
6928*7c478bd9Sstevel@tonic-gate 	if (minor_name != NULL) {
6929*7c478bd9Sstevel@tonic-gate 		*minor_name++ = '\0';
6930*7c478bd9Sstevel@tonic-gate 	} else {
6931*7c478bd9Sstevel@tonic-gate 		minor_name = node_name;
6932*7c478bd9Sstevel@tonic-gate 	}
6933*7c478bd9Sstevel@tonic-gate 
6934*7c478bd9Sstevel@tonic-gate 	minor_name = strchr(minor_name, ':'); /* look for minor name */
6935*7c478bd9Sstevel@tonic-gate 
6936*7c478bd9Sstevel@tonic-gate 	if (minor_name == NULL) {
6937*7c478bd9Sstevel@tonic-gate 		err_print(NO_MINOR, devname);
6938*7c478bd9Sstevel@tonic-gate 		goto use_defaults;
6939*7c478bd9Sstevel@tonic-gate 	}
6940*7c478bd9Sstevel@tonic-gate 	*minor_name++ = '\0';
6941*7c478bd9Sstevel@tonic-gate 
6942*7c478bd9Sstevel@tonic-gate 	/*
6943*7c478bd9Sstevel@tonic-gate 	 * mp->mp_drvname = device name from minor_perm
6944*7c478bd9Sstevel@tonic-gate 	 * mp->mp_minorname = minor part of device name from
6945*7c478bd9Sstevel@tonic-gate 	 * minor_perm
6946*7c478bd9Sstevel@tonic-gate 	 * drvname = name of driver for this device
6947*7c478bd9Sstevel@tonic-gate 	 */
6948*7c478bd9Sstevel@tonic-gate 
6949*7c478bd9Sstevel@tonic-gate 	is_clone = (strcmp(node_name, "clone") == 0 ? TRUE : FALSE);
6950*7c478bd9Sstevel@tonic-gate 	for (mp = minor_perms; mp != NULL; mp = mp->mp_next) {
6951*7c478bd9Sstevel@tonic-gate 		mp_drvname_matches_node_name =
6952*7c478bd9Sstevel@tonic-gate 			(strcmp(mp->mp_drvname, node_name) == 0 ? TRUE : FALSE);
6953*7c478bd9Sstevel@tonic-gate 		mp_drvname_matches_minor_name =
6954*7c478bd9Sstevel@tonic-gate 			(strcmp(mp->mp_drvname, minor_name) == 0  ? TRUE:FALSE);
6955*7c478bd9Sstevel@tonic-gate 		mp_drvname_is_clone =
6956*7c478bd9Sstevel@tonic-gate 			(strcmp(mp->mp_drvname, "clone") == 0  ? TRUE : FALSE);
6957*7c478bd9Sstevel@tonic-gate 		mp_drvname_matches_drvname =
6958*7c478bd9Sstevel@tonic-gate 			(strcmp(mp->mp_drvname, driver) == 0  ? TRUE : FALSE);
6959*7c478bd9Sstevel@tonic-gate 
6960*7c478bd9Sstevel@tonic-gate 		/*
6961*7c478bd9Sstevel@tonic-gate 		 * If one of the following cases is true, then we try to change
6962*7c478bd9Sstevel@tonic-gate 		 * the permissions if a "shell global pattern match" of
6963*7c478bd9Sstevel@tonic-gate 		 * mp_>mp_minorname matches minor_name.
6964*7c478bd9Sstevel@tonic-gate 		 *
6965*7c478bd9Sstevel@tonic-gate 		 * 1.  mp->mp_drvname matches driver.
6966*7c478bd9Sstevel@tonic-gate 		 *
6967*7c478bd9Sstevel@tonic-gate 		 * OR
6968*7c478bd9Sstevel@tonic-gate 		 *
6969*7c478bd9Sstevel@tonic-gate 		 * 2.  mp->mp_drvname matches node_name and this
6970*7c478bd9Sstevel@tonic-gate 		 *	name is an alias of the driver name
6971*7c478bd9Sstevel@tonic-gate 		 *
6972*7c478bd9Sstevel@tonic-gate 		 * OR
6973*7c478bd9Sstevel@tonic-gate 		 *
6974*7c478bd9Sstevel@tonic-gate 		 * 3.  /devices entry is the clone device and either
6975*7c478bd9Sstevel@tonic-gate 		 *	minor_perm entry is the clone device or matches
6976*7c478bd9Sstevel@tonic-gate 		 *	the minor part of the clone device.
6977*7c478bd9Sstevel@tonic-gate 		 */
6978*7c478bd9Sstevel@tonic-gate 
6979*7c478bd9Sstevel@tonic-gate 		if ((mp_drvname_matches_drvname == TRUE)||
6980*7c478bd9Sstevel@tonic-gate 		    ((mp_drvname_matches_node_name == TRUE) &&
6981*7c478bd9Sstevel@tonic-gate 		    (alias(driver, node_name) == TRUE)) ||
6982*7c478bd9Sstevel@tonic-gate 		    ((is_clone == TRUE) &&
6983*7c478bd9Sstevel@tonic-gate 		    ((mp_drvname_is_clone == TRUE) ||
6984*7c478bd9Sstevel@tonic-gate 		    (mp_drvname_matches_minor_name == TRUE)))) {
6985*7c478bd9Sstevel@tonic-gate 			/*
6986*7c478bd9Sstevel@tonic-gate 			 * Check that the minor part of the
6987*7c478bd9Sstevel@tonic-gate 			 * device name from the minor_perm
6988*7c478bd9Sstevel@tonic-gate 			 * entry matches and if so, set the
6989*7c478bd9Sstevel@tonic-gate 			 * permissions.
6990*7c478bd9Sstevel@tonic-gate 			 *
6991*7c478bd9Sstevel@tonic-gate 			 * Under real devfs, clone minor name is changed
6992*7c478bd9Sstevel@tonic-gate 			 * to match the driver name, but minor_perm may
6993*7c478bd9Sstevel@tonic-gate 			 * not match. We reconcile it here.
6994*7c478bd9Sstevel@tonic-gate 			 */
6995*7c478bd9Sstevel@tonic-gate 			if (aminor != NULL)
6996*7c478bd9Sstevel@tonic-gate 				minor_name = aminor;
6997*7c478bd9Sstevel@tonic-gate 
6998*7c478bd9Sstevel@tonic-gate 			if (gmatch(minor_name, mp->mp_minorname) != 0) {
6999*7c478bd9Sstevel@tonic-gate 				*uid = mp->mp_uid;
7000*7c478bd9Sstevel@tonic-gate 				*gid = mp->mp_gid;
7001*7c478bd9Sstevel@tonic-gate 				*mode = spectype | mp->mp_mode;
7002*7c478bd9Sstevel@tonic-gate 				match = TRUE;
7003*7c478bd9Sstevel@tonic-gate 			}
7004*7c478bd9Sstevel@tonic-gate 		}
7005*7c478bd9Sstevel@tonic-gate 	}
7006*7c478bd9Sstevel@tonic-gate 
7007*7c478bd9Sstevel@tonic-gate 	if (match == TRUE) {
7008*7c478bd9Sstevel@tonic-gate 		return;
7009*7c478bd9Sstevel@tonic-gate 	}
7010*7c478bd9Sstevel@tonic-gate 
7011*7c478bd9Sstevel@tonic-gate 	use_defaults:
7012*7c478bd9Sstevel@tonic-gate 	/* not found in minor_perm, so just use default values */
7013*7c478bd9Sstevel@tonic-gate 	*uid = root_uid;
7014*7c478bd9Sstevel@tonic-gate 	*gid = sys_gid;
7015*7c478bd9Sstevel@tonic-gate 	*mode = (spectype | 0600);
7016*7c478bd9Sstevel@tonic-gate }
7017*7c478bd9Sstevel@tonic-gate 
7018*7c478bd9Sstevel@tonic-gate /*
7019*7c478bd9Sstevel@tonic-gate  * Called by devfs_read_minor_perm() to report errors
7020*7c478bd9Sstevel@tonic-gate  * key is:
7021*7c478bd9Sstevel@tonic-gate  *	line number: ignoring line number error
7022*7c478bd9Sstevel@tonic-gate  *	errno: open/close errors
7023*7c478bd9Sstevel@tonic-gate  *	size: alloc errors
7024*7c478bd9Sstevel@tonic-gate  */
7025*7c478bd9Sstevel@tonic-gate static void
7026*7c478bd9Sstevel@tonic-gate minorperm_err_cb(minorperm_err_t mp_err, int key)
7027*7c478bd9Sstevel@tonic-gate {
7028*7c478bd9Sstevel@tonic-gate 	switch (mp_err) {
7029*7c478bd9Sstevel@tonic-gate 	case MP_FOPEN_ERR:
7030*7c478bd9Sstevel@tonic-gate 		err_print(FOPEN_FAILED, MINOR_PERM_FILE, strerror(key));
7031*7c478bd9Sstevel@tonic-gate 		break;
7032*7c478bd9Sstevel@tonic-gate 	case MP_FCLOSE_ERR:
7033*7c478bd9Sstevel@tonic-gate 		err_print(FCLOSE_FAILED, MINOR_PERM_FILE, strerror(key));
7034*7c478bd9Sstevel@tonic-gate 		break;
7035*7c478bd9Sstevel@tonic-gate 	case MP_IGNORING_LINE_ERR:
7036*7c478bd9Sstevel@tonic-gate 		err_print(IGNORING_LINE_IN, key, MINOR_PERM_FILE);
7037*7c478bd9Sstevel@tonic-gate 		break;
7038*7c478bd9Sstevel@tonic-gate 	case MP_ALLOC_ERR:
7039*7c478bd9Sstevel@tonic-gate 		err_print(MALLOC_FAILED, key);
7040*7c478bd9Sstevel@tonic-gate 		break;
7041*7c478bd9Sstevel@tonic-gate 	case MP_NVLIST_ERR:
7042*7c478bd9Sstevel@tonic-gate 		err_print(NVLIST_ERROR, MINOR_PERM_FILE, strerror(key));
7043*7c478bd9Sstevel@tonic-gate 		break;
7044*7c478bd9Sstevel@tonic-gate 	case MP_CANT_FIND_USER_ERR:
7045*7c478bd9Sstevel@tonic-gate 		err_print(CANT_FIND_USER, DEFAULT_DEV_USER);
7046*7c478bd9Sstevel@tonic-gate 		break;
7047*7c478bd9Sstevel@tonic-gate 	case MP_CANT_FIND_GROUP_ERR:
7048*7c478bd9Sstevel@tonic-gate 		err_print(CANT_FIND_GROUP, DEFAULT_DEV_GROUP);
7049*7c478bd9Sstevel@tonic-gate 		break;
7050*7c478bd9Sstevel@tonic-gate 	}
7051*7c478bd9Sstevel@tonic-gate }
7052*7c478bd9Sstevel@tonic-gate 
7053*7c478bd9Sstevel@tonic-gate static void
7054*7c478bd9Sstevel@tonic-gate read_minor_perm_file(void)
7055*7c478bd9Sstevel@tonic-gate {
7056*7c478bd9Sstevel@tonic-gate 	static int cached = FALSE;
7057*7c478bd9Sstevel@tonic-gate 	static struct stat cached_sb;
7058*7c478bd9Sstevel@tonic-gate 	struct stat current_sb;
7059*7c478bd9Sstevel@tonic-gate 
7060*7c478bd9Sstevel@tonic-gate 	(void) stat(MINOR_PERM_FILE, &current_sb);
7061*7c478bd9Sstevel@tonic-gate 
7062*7c478bd9Sstevel@tonic-gate 	/* If already cached, check to see if it is still valid */
7063*7c478bd9Sstevel@tonic-gate 	if (cached == TRUE) {
7064*7c478bd9Sstevel@tonic-gate 
7065*7c478bd9Sstevel@tonic-gate 		if (current_sb.st_mtime == cached_sb.st_mtime) {
7066*7c478bd9Sstevel@tonic-gate 			vprint(FILES_MID, "%s cache valid\n", MINOR_PERM_FILE);
7067*7c478bd9Sstevel@tonic-gate 			return;
7068*7c478bd9Sstevel@tonic-gate 		}
7069*7c478bd9Sstevel@tonic-gate 		devfs_free_minor_perm(minor_perms);
7070*7c478bd9Sstevel@tonic-gate 		minor_perms = NULL;
7071*7c478bd9Sstevel@tonic-gate 	} else {
7072*7c478bd9Sstevel@tonic-gate 		cached = TRUE;
7073*7c478bd9Sstevel@tonic-gate 	}
7074*7c478bd9Sstevel@tonic-gate 
7075*7c478bd9Sstevel@tonic-gate 	(void) stat(MINOR_PERM_FILE, &cached_sb);
7076*7c478bd9Sstevel@tonic-gate 
7077*7c478bd9Sstevel@tonic-gate 	vprint(FILES_MID, "loading binding file: %s\n", MINOR_PERM_FILE);
7078*7c478bd9Sstevel@tonic-gate 
7079*7c478bd9Sstevel@tonic-gate 	minor_perms = devfs_read_minor_perm(minorperm_err_cb);
7080*7c478bd9Sstevel@tonic-gate }
7081*7c478bd9Sstevel@tonic-gate 
7082*7c478bd9Sstevel@tonic-gate static void
7083*7c478bd9Sstevel@tonic-gate load_minor_perm_file(void)
7084*7c478bd9Sstevel@tonic-gate {
7085*7c478bd9Sstevel@tonic-gate 	read_minor_perm_file();
7086*7c478bd9Sstevel@tonic-gate 	if (devfs_load_minor_perm(minor_perms, minorperm_err_cb) != 0)
7087*7c478bd9Sstevel@tonic-gate 		err_print(gettext("minor_perm load failed\n"));
7088*7c478bd9Sstevel@tonic-gate }
7089*7c478bd9Sstevel@tonic-gate 
7090*7c478bd9Sstevel@tonic-gate static char *
7091*7c478bd9Sstevel@tonic-gate convert_to_re(char *dev)
7092*7c478bd9Sstevel@tonic-gate {
7093*7c478bd9Sstevel@tonic-gate 	char *p, *l, *out;
7094*7c478bd9Sstevel@tonic-gate 	int i;
7095*7c478bd9Sstevel@tonic-gate 
7096*7c478bd9Sstevel@tonic-gate 	out = s_malloc(PATH_MAX);
7097*7c478bd9Sstevel@tonic-gate 
7098*7c478bd9Sstevel@tonic-gate 	for (l = p = dev, i = 0; (*p != '\0') && (i < (PATH_MAX - 1));
7099*7c478bd9Sstevel@tonic-gate 	    ++p, i++) {
7100*7c478bd9Sstevel@tonic-gate 		if ((*p == '*') && ((l != p) && (*l == '/'))) {
7101*7c478bd9Sstevel@tonic-gate 			out[i++] = '.';
7102*7c478bd9Sstevel@tonic-gate 			out[i] = '+';
7103*7c478bd9Sstevel@tonic-gate 		} else {
7104*7c478bd9Sstevel@tonic-gate 			out[i] = *p;
7105*7c478bd9Sstevel@tonic-gate 		}
7106*7c478bd9Sstevel@tonic-gate 		l = p;
7107*7c478bd9Sstevel@tonic-gate 	}
7108*7c478bd9Sstevel@tonic-gate 	out[i] = '\0';
7109*7c478bd9Sstevel@tonic-gate 	p = (char *)s_malloc(strlen(out) + 1);
7110*7c478bd9Sstevel@tonic-gate 	(void) strlcpy(p, out, strlen(out) + 1);
7111*7c478bd9Sstevel@tonic-gate 	free(out);
7112*7c478bd9Sstevel@tonic-gate 
7113*7c478bd9Sstevel@tonic-gate 	vprint(FILES_MID, "converted %s -> %s\n", dev, p);
7114*7c478bd9Sstevel@tonic-gate 
7115*7c478bd9Sstevel@tonic-gate 	return (p);
7116*7c478bd9Sstevel@tonic-gate }
7117*7c478bd9Sstevel@tonic-gate 
7118*7c478bd9Sstevel@tonic-gate static void
7119*7c478bd9Sstevel@tonic-gate read_logindevperm_file(void)
7120*7c478bd9Sstevel@tonic-gate {
7121*7c478bd9Sstevel@tonic-gate 	static int cached = FALSE;
7122*7c478bd9Sstevel@tonic-gate 	static struct stat cached_sb;
7123*7c478bd9Sstevel@tonic-gate 	struct stat current_sb;
7124*7c478bd9Sstevel@tonic-gate 	struct login_dev *ldev;
7125*7c478bd9Sstevel@tonic-gate 	FILE *fp;
7126*7c478bd9Sstevel@tonic-gate 	char line[MAX_LDEV_LINE];
7127*7c478bd9Sstevel@tonic-gate 	int ln, perm, rv;
7128*7c478bd9Sstevel@tonic-gate 	char *cp, *console, *devlist, *dev;
7129*7c478bd9Sstevel@tonic-gate 	char *lasts, *devlasts, *permstr, *drv;
7130*7c478bd9Sstevel@tonic-gate 	struct driver_list *list, *next;
7131*7c478bd9Sstevel@tonic-gate 
7132*7c478bd9Sstevel@tonic-gate 	/* Read logindevperm only when enabled */
7133*7c478bd9Sstevel@tonic-gate 	if (login_dev_enable != TRUE)
7134*7c478bd9Sstevel@tonic-gate 		return;
7135*7c478bd9Sstevel@tonic-gate 
7136*7c478bd9Sstevel@tonic-gate 	if (cached == TRUE) {
7137*7c478bd9Sstevel@tonic-gate 		if (stat(LDEV_FILE, &current_sb) == 0 &&
7138*7c478bd9Sstevel@tonic-gate 		    current_sb.st_mtime == cached_sb.st_mtime) {
7139*7c478bd9Sstevel@tonic-gate 			vprint(FILES_MID, "%s cache valid\n", LDEV_FILE);
7140*7c478bd9Sstevel@tonic-gate 			return;
7141*7c478bd9Sstevel@tonic-gate 		}
7142*7c478bd9Sstevel@tonic-gate 		vprint(FILES_MID, "invalidating %s cache\n", LDEV_FILE);
7143*7c478bd9Sstevel@tonic-gate 		while (login_dev_cache != NULL) {
7144*7c478bd9Sstevel@tonic-gate 
7145*7c478bd9Sstevel@tonic-gate 			ldev = login_dev_cache;
7146*7c478bd9Sstevel@tonic-gate 			login_dev_cache = ldev->ldev_next;
7147*7c478bd9Sstevel@tonic-gate 			free(ldev->ldev_console);
7148*7c478bd9Sstevel@tonic-gate 			free(ldev->ldev_device);
7149*7c478bd9Sstevel@tonic-gate 			regfree(&ldev->ldev_device_regex);
7150*7c478bd9Sstevel@tonic-gate 			list = ldev->ldev_driver_list;
7151*7c478bd9Sstevel@tonic-gate 			while (list) {
7152*7c478bd9Sstevel@tonic-gate 				next = list->next;
7153*7c478bd9Sstevel@tonic-gate 				free(list);
7154*7c478bd9Sstevel@tonic-gate 				list = next;
7155*7c478bd9Sstevel@tonic-gate 			}
7156*7c478bd9Sstevel@tonic-gate 			free(ldev);
7157*7c478bd9Sstevel@tonic-gate 		}
7158*7c478bd9Sstevel@tonic-gate 	} else {
7159*7c478bd9Sstevel@tonic-gate 		cached = TRUE;
7160*7c478bd9Sstevel@tonic-gate 	}
7161*7c478bd9Sstevel@tonic-gate 
7162*7c478bd9Sstevel@tonic-gate 	assert(login_dev_cache == NULL);
7163*7c478bd9Sstevel@tonic-gate 
7164*7c478bd9Sstevel@tonic-gate 	if (stat(LDEV_FILE, &cached_sb) != 0) {
7165*7c478bd9Sstevel@tonic-gate 		cached = FALSE;
7166*7c478bd9Sstevel@tonic-gate 		return;
7167*7c478bd9Sstevel@tonic-gate 	}
7168*7c478bd9Sstevel@tonic-gate 
7169*7c478bd9Sstevel@tonic-gate 	vprint(FILES_MID, "loading file: %s\n", LDEV_FILE);
7170*7c478bd9Sstevel@tonic-gate 
7171*7c478bd9Sstevel@tonic-gate 	if ((fp = fopen(LDEV_FILE, "r")) == NULL) {
7172*7c478bd9Sstevel@tonic-gate 		/* Not fatal to devfsadm */
7173*7c478bd9Sstevel@tonic-gate 		cached = FALSE;
7174*7c478bd9Sstevel@tonic-gate 		err_print(FOPEN_FAILED, LDEV_FILE, strerror(errno));
7175*7c478bd9Sstevel@tonic-gate 		return;
7176*7c478bd9Sstevel@tonic-gate 	}
7177*7c478bd9Sstevel@tonic-gate 
7178*7c478bd9Sstevel@tonic-gate 	ln = 0;
7179*7c478bd9Sstevel@tonic-gate 	while (fgets(line, MAX_LDEV_LINE, fp) != NULL) {
7180*7c478bd9Sstevel@tonic-gate 		ln++;
7181*7c478bd9Sstevel@tonic-gate 
7182*7c478bd9Sstevel@tonic-gate 		/* Remove comments */
7183*7c478bd9Sstevel@tonic-gate 		if ((cp = strchr(line, '#')) != NULL)
7184*7c478bd9Sstevel@tonic-gate 			*cp = '\0';
7185*7c478bd9Sstevel@tonic-gate 
7186*7c478bd9Sstevel@tonic-gate 		if ((console = strtok_r(line, LDEV_DELIMS, &lasts)) == NULL)
7187*7c478bd9Sstevel@tonic-gate 			continue;	/* Blank line */
7188*7c478bd9Sstevel@tonic-gate 
7189*7c478bd9Sstevel@tonic-gate 		if ((permstr =  strtok_r(NULL, LDEV_DELIMS, &lasts)) == NULL) {
7190*7c478bd9Sstevel@tonic-gate 			err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7191*7c478bd9Sstevel@tonic-gate 			continue;	/* Malformed line */
7192*7c478bd9Sstevel@tonic-gate 		}
7193*7c478bd9Sstevel@tonic-gate 
7194*7c478bd9Sstevel@tonic-gate 		/*
7195*7c478bd9Sstevel@tonic-gate 		 * permstr is string in octal format. Convert to int
7196*7c478bd9Sstevel@tonic-gate 		 */
7197*7c478bd9Sstevel@tonic-gate 		cp = NULL;
7198*7c478bd9Sstevel@tonic-gate 		errno = 0;
7199*7c478bd9Sstevel@tonic-gate 		perm = strtol(permstr, &cp, 8);
7200*7c478bd9Sstevel@tonic-gate 		if (errno || perm < 0 || perm > 0777 || *cp != '\0') {
7201*7c478bd9Sstevel@tonic-gate 			err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7202*7c478bd9Sstevel@tonic-gate 			continue;
7203*7c478bd9Sstevel@tonic-gate 		}
7204*7c478bd9Sstevel@tonic-gate 
7205*7c478bd9Sstevel@tonic-gate 		if ((devlist = strtok_r(NULL, LDEV_DELIMS, &lasts)) == NULL) {
7206*7c478bd9Sstevel@tonic-gate 			err_print(IGNORING_LINE_IN, ln, LDEV_FILE);
7207*7c478bd9Sstevel@tonic-gate 			continue;
7208*7c478bd9Sstevel@tonic-gate 		}
7209*7c478bd9Sstevel@tonic-gate 
7210*7c478bd9Sstevel@tonic-gate 		dev = strtok_r(devlist, LDEV_DEV_DELIM, &devlasts);
7211*7c478bd9Sstevel@tonic-gate 		while (dev) {
7212*7c478bd9Sstevel@tonic-gate 
7213*7c478bd9Sstevel@tonic-gate 			ldev = (struct login_dev *)s_zalloc(
7214*7c478bd9Sstevel@tonic-gate 			    sizeof (struct login_dev));
7215*7c478bd9Sstevel@tonic-gate 			ldev->ldev_console = s_strdup(console);
7216*7c478bd9Sstevel@tonic-gate 			ldev->ldev_perms = perm;
7217*7c478bd9Sstevel@tonic-gate 
7218*7c478bd9Sstevel@tonic-gate 			/*
7219*7c478bd9Sstevel@tonic-gate 			 * the logical device name may contain '*' which
7220*7c478bd9Sstevel@tonic-gate 			 * we convert to a regular expression
7221*7c478bd9Sstevel@tonic-gate 			 */
7222*7c478bd9Sstevel@tonic-gate 			ldev->ldev_device = convert_to_re(dev);
7223*7c478bd9Sstevel@tonic-gate 			if (ldev->ldev_device &&
7224*7c478bd9Sstevel@tonic-gate 			    (rv = regcomp(&ldev->ldev_device_regex,
7225*7c478bd9Sstevel@tonic-gate 			    ldev->ldev_device, REG_EXTENDED))) {
7226*7c478bd9Sstevel@tonic-gate 				bzero(&ldev->ldev_device_regex,
7227*7c478bd9Sstevel@tonic-gate 				    sizeof (ldev->ldev_device_regex));
7228*7c478bd9Sstevel@tonic-gate 				err_print(REGCOMP_FAILED,
7229*7c478bd9Sstevel@tonic-gate 				    ldev->ldev_device, rv);
7230*7c478bd9Sstevel@tonic-gate 			}
7231*7c478bd9Sstevel@tonic-gate 			ldev->ldev_next = login_dev_cache;
7232*7c478bd9Sstevel@tonic-gate 			login_dev_cache = ldev;
7233*7c478bd9Sstevel@tonic-gate 			dev = strtok_r(NULL, LDEV_DEV_DELIM, &devlasts);
7234*7c478bd9Sstevel@tonic-gate 		}
7235*7c478bd9Sstevel@tonic-gate 
7236*7c478bd9Sstevel@tonic-gate 		drv = strtok_r(NULL, LDEV_DRVLIST_DELIMS, &lasts);
7237*7c478bd9Sstevel@tonic-gate 		if (drv) {
7238*7c478bd9Sstevel@tonic-gate 			if (strcmp(drv, LDEV_DRVLIST_NAME) == 0) {
7239*7c478bd9Sstevel@tonic-gate 
7240*7c478bd9Sstevel@tonic-gate 				drv = strtok_r(NULL, LDEV_DRV_DELIMS,
7241*7c478bd9Sstevel@tonic-gate 					&lasts);
7242*7c478bd9Sstevel@tonic-gate 
7243*7c478bd9Sstevel@tonic-gate 				while (drv) {
7244*7c478bd9Sstevel@tonic-gate 					vprint(FILES_MID,
7245*7c478bd9Sstevel@tonic-gate 					    "logindevperm driver=%s\n",
7246*7c478bd9Sstevel@tonic-gate 					    drv);
7247*7c478bd9Sstevel@tonic-gate 
7248*7c478bd9Sstevel@tonic-gate 					/*
7249*7c478bd9Sstevel@tonic-gate 					 * create a linked list of driver
7250*7c478bd9Sstevel@tonic-gate 					 * names
7251*7c478bd9Sstevel@tonic-gate 					 */
7252*7c478bd9Sstevel@tonic-gate 					list = (struct driver_list *)
7253*7c478bd9Sstevel@tonic-gate 					    s_zalloc(
7254*7c478bd9Sstevel@tonic-gate 					    sizeof (struct driver_list));
7255*7c478bd9Sstevel@tonic-gate 					(void) strlcpy(list->driver_name, drv,
7256*7c478bd9Sstevel@tonic-gate 					    sizeof (list->driver_name));
7257*7c478bd9Sstevel@tonic-gate 					list->next = ldev->ldev_driver_list;
7258*7c478bd9Sstevel@tonic-gate 					ldev->ldev_driver_list = list;
7259*7c478bd9Sstevel@tonic-gate 					drv = strtok_r(NULL, LDEV_DRV_DELIMS,
7260*7c478bd9Sstevel@tonic-gate 					    &lasts);
7261*7c478bd9Sstevel@tonic-gate 				}
7262*7c478bd9Sstevel@tonic-gate 			}
7263*7c478bd9Sstevel@tonic-gate 		}
7264*7c478bd9Sstevel@tonic-gate 	}
7265*7c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
7266*7c478bd9Sstevel@tonic-gate }
7267*7c478bd9Sstevel@tonic-gate 
7268*7c478bd9Sstevel@tonic-gate /*
7269*7c478bd9Sstevel@tonic-gate  * Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0'
7270*7c478bd9Sstevel@tonic-gate  *
7271*7c478bd9Sstevel@tonic-gate  * Returns DEVFSADM_SUCCESS if token found, DEVFSADM_FAILURE otherwise.
7272*7c478bd9Sstevel@tonic-gate  */
7273*7c478bd9Sstevel@tonic-gate static int
7274*7c478bd9Sstevel@tonic-gate getnexttoken(char *next, char **nextp, char **tokenpp, char *tchar)
7275*7c478bd9Sstevel@tonic-gate {
7276*7c478bd9Sstevel@tonic-gate 	char *cp;
7277*7c478bd9Sstevel@tonic-gate 	char *cp1;
7278*7c478bd9Sstevel@tonic-gate 	char *tokenp;
7279*7c478bd9Sstevel@tonic-gate 
7280*7c478bd9Sstevel@tonic-gate 	cp = next;
7281*7c478bd9Sstevel@tonic-gate 	while (*cp == ' ' || *cp == '\t') {
7282*7c478bd9Sstevel@tonic-gate 		cp++;			/* skip leading spaces */
7283*7c478bd9Sstevel@tonic-gate 	}
7284*7c478bd9Sstevel@tonic-gate 	tokenp = cp;			/* start of token */
7285*7c478bd9Sstevel@tonic-gate 	while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' &&
7286*7c478bd9Sstevel@tonic-gate 		*cp != ':' && *cp != '=' && *cp != '&' &&
7287*7c478bd9Sstevel@tonic-gate 		*cp != '|' && *cp != ';') {
7288*7c478bd9Sstevel@tonic-gate 		cp++;			/* point to next character */
7289*7c478bd9Sstevel@tonic-gate 	}
7290*7c478bd9Sstevel@tonic-gate 	/*
7291*7c478bd9Sstevel@tonic-gate 	 * If terminating character is a space or tab, look ahead to see if
7292*7c478bd9Sstevel@tonic-gate 	 * there's another terminator that's not a space or a tab.
7293*7c478bd9Sstevel@tonic-gate 	 * (This code handles trailing spaces.)
7294*7c478bd9Sstevel@tonic-gate 	 */
7295*7c478bd9Sstevel@tonic-gate 	if (*cp == ' ' || *cp == '\t') {
7296*7c478bd9Sstevel@tonic-gate 		cp1 = cp;
7297*7c478bd9Sstevel@tonic-gate 		while (*++cp1 == ' ' || *cp1 == '\t')
7298*7c478bd9Sstevel@tonic-gate 			;
7299*7c478bd9Sstevel@tonic-gate 		if (*cp1 == '=' || *cp1 == ':' || *cp1 == '&' || *cp1 == '|' ||
7300*7c478bd9Sstevel@tonic-gate 			*cp1 == ';' || *cp1 == '\n' || *cp1 == '\0') {
7301*7c478bd9Sstevel@tonic-gate 			*cp = NULL;	/* terminate token */
7302*7c478bd9Sstevel@tonic-gate 			cp = cp1;
7303*7c478bd9Sstevel@tonic-gate 		}
7304*7c478bd9Sstevel@tonic-gate 	}
7305*7c478bd9Sstevel@tonic-gate 	if (tchar != NULL) {
7306*7c478bd9Sstevel@tonic-gate 		*tchar = *cp;		/* save terminating character */
7307*7c478bd9Sstevel@tonic-gate 		if (*tchar == '\0') {
7308*7c478bd9Sstevel@tonic-gate 			*tchar = '\n';
7309*7c478bd9Sstevel@tonic-gate 		}
7310*7c478bd9Sstevel@tonic-gate 	}
7311*7c478bd9Sstevel@tonic-gate 	*cp++ = '\0';			/* terminate token, point to next */
7312*7c478bd9Sstevel@tonic-gate 	*nextp = cp;			/* set pointer to next character */
7313*7c478bd9Sstevel@tonic-gate 	if (cp - tokenp - 1 == 0) {
7314*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_FAILURE);
7315*7c478bd9Sstevel@tonic-gate 	}
7316*7c478bd9Sstevel@tonic-gate 	*tokenpp = tokenp;
7317*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_SUCCESS);
7318*7c478bd9Sstevel@tonic-gate }
7319*7c478bd9Sstevel@tonic-gate 
7320*7c478bd9Sstevel@tonic-gate /*
7321*7c478bd9Sstevel@tonic-gate  * read or reread the driver aliases file
7322*7c478bd9Sstevel@tonic-gate  */
7323*7c478bd9Sstevel@tonic-gate static void
7324*7c478bd9Sstevel@tonic-gate read_driver_aliases_file(void)
7325*7c478bd9Sstevel@tonic-gate {
7326*7c478bd9Sstevel@tonic-gate 
7327*7c478bd9Sstevel@tonic-gate 	driver_alias_t *save;
7328*7c478bd9Sstevel@tonic-gate 	driver_alias_t *lst_tail;
7329*7c478bd9Sstevel@tonic-gate 	driver_alias_t *ap;
7330*7c478bd9Sstevel@tonic-gate 	static int cached = FALSE;
7331*7c478bd9Sstevel@tonic-gate 	FILE *afd;
7332*7c478bd9Sstevel@tonic-gate 	char line[256];
7333*7c478bd9Sstevel@tonic-gate 	char *cp;
7334*7c478bd9Sstevel@tonic-gate 	char *p;
7335*7c478bd9Sstevel@tonic-gate 	char t;
7336*7c478bd9Sstevel@tonic-gate 	int ln = 0;
7337*7c478bd9Sstevel@tonic-gate 	static struct stat cached_sb;
7338*7c478bd9Sstevel@tonic-gate 	struct stat current_sb;
7339*7c478bd9Sstevel@tonic-gate 
7340*7c478bd9Sstevel@tonic-gate 	(void) stat(ALIASFILE, &current_sb);
7341*7c478bd9Sstevel@tonic-gate 
7342*7c478bd9Sstevel@tonic-gate 	/* If already cached, check to see if it is still valid */
7343*7c478bd9Sstevel@tonic-gate 	if (cached == TRUE) {
7344*7c478bd9Sstevel@tonic-gate 
7345*7c478bd9Sstevel@tonic-gate 		if (current_sb.st_mtime == cached_sb.st_mtime) {
7346*7c478bd9Sstevel@tonic-gate 			vprint(FILES_MID, "%s cache valid\n", ALIASFILE);
7347*7c478bd9Sstevel@tonic-gate 			return;
7348*7c478bd9Sstevel@tonic-gate 		}
7349*7c478bd9Sstevel@tonic-gate 
7350*7c478bd9Sstevel@tonic-gate 		vprint(FILES_MID, "invalidating %s cache\n", ALIASFILE);
7351*7c478bd9Sstevel@tonic-gate 		while (driver_aliases != NULL) {
7352*7c478bd9Sstevel@tonic-gate 			free(driver_aliases->alias_name);
7353*7c478bd9Sstevel@tonic-gate 			free(driver_aliases->driver_name);
7354*7c478bd9Sstevel@tonic-gate 			save = driver_aliases;
7355*7c478bd9Sstevel@tonic-gate 			driver_aliases = driver_aliases->next;
7356*7c478bd9Sstevel@tonic-gate 			free(save);
7357*7c478bd9Sstevel@tonic-gate 		}
7358*7c478bd9Sstevel@tonic-gate 	} else {
7359*7c478bd9Sstevel@tonic-gate 		cached = TRUE;
7360*7c478bd9Sstevel@tonic-gate 	}
7361*7c478bd9Sstevel@tonic-gate 
7362*7c478bd9Sstevel@tonic-gate 	(void) stat(ALIASFILE, &cached_sb);
7363*7c478bd9Sstevel@tonic-gate 
7364*7c478bd9Sstevel@tonic-gate 	vprint(FILES_MID, "loading binding file: %s\n", ALIASFILE);
7365*7c478bd9Sstevel@tonic-gate 
7366*7c478bd9Sstevel@tonic-gate 	if ((afd = fopen(ALIASFILE, "r")) == NULL) {
7367*7c478bd9Sstevel@tonic-gate 		err_print(FOPEN_FAILED, ALIASFILE, strerror(errno));
7368*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
7369*7c478bd9Sstevel@tonic-gate 	}
7370*7c478bd9Sstevel@tonic-gate 
7371*7c478bd9Sstevel@tonic-gate 	while (fgets(line, sizeof (line) - 1, afd) != NULL) {
7372*7c478bd9Sstevel@tonic-gate 		ln++;
7373*7c478bd9Sstevel@tonic-gate 		cp = line;
7374*7c478bd9Sstevel@tonic-gate 		if (getnexttoken(cp, &cp, &p, &t) == DEVFSADM_FAILURE) {
7375*7c478bd9Sstevel@tonic-gate 			err_print(IGNORING_LINE_IN, ln, ALIASFILE);
7376*7c478bd9Sstevel@tonic-gate 			continue;
7377*7c478bd9Sstevel@tonic-gate 		}
7378*7c478bd9Sstevel@tonic-gate 		if (t == '\n' || t == '\0') {
7379*7c478bd9Sstevel@tonic-gate 			err_print(DRV_BUT_NO_ALIAS, ln, ALIASFILE);
7380*7c478bd9Sstevel@tonic-gate 			continue;
7381*7c478bd9Sstevel@tonic-gate 		}
7382*7c478bd9Sstevel@tonic-gate 		ap = (struct driver_alias *)
7383*7c478bd9Sstevel@tonic-gate 				s_zalloc(sizeof (struct driver_alias));
7384*7c478bd9Sstevel@tonic-gate 		ap->driver_name = s_strdup(p);
7385*7c478bd9Sstevel@tonic-gate 		if (getnexttoken(cp, &cp, &p, &t) == DEVFSADM_FAILURE) {
7386*7c478bd9Sstevel@tonic-gate 			err_print(DRV_BUT_NO_ALIAS, ln, ALIASFILE);
7387*7c478bd9Sstevel@tonic-gate 			free(ap->driver_name);
7388*7c478bd9Sstevel@tonic-gate 			free(ap);
7389*7c478bd9Sstevel@tonic-gate 			continue;
7390*7c478bd9Sstevel@tonic-gate 		}
7391*7c478bd9Sstevel@tonic-gate 		if (*p == '"') {
7392*7c478bd9Sstevel@tonic-gate 			if (p[strlen(p) - 1] == '"') {
7393*7c478bd9Sstevel@tonic-gate 				p[strlen(p) - 1] = '\0';
7394*7c478bd9Sstevel@tonic-gate 				p++;
7395*7c478bd9Sstevel@tonic-gate 			}
7396*7c478bd9Sstevel@tonic-gate 		}
7397*7c478bd9Sstevel@tonic-gate 		ap->alias_name = s_strdup(p);
7398*7c478bd9Sstevel@tonic-gate 		if (driver_aliases == NULL) {
7399*7c478bd9Sstevel@tonic-gate 			driver_aliases = ap;
7400*7c478bd9Sstevel@tonic-gate 			lst_tail = ap;
7401*7c478bd9Sstevel@tonic-gate 		} else {
7402*7c478bd9Sstevel@tonic-gate 			lst_tail->next = ap;
7403*7c478bd9Sstevel@tonic-gate 			lst_tail = ap;
7404*7c478bd9Sstevel@tonic-gate 		}
7405*7c478bd9Sstevel@tonic-gate 	}
7406*7c478bd9Sstevel@tonic-gate 	if (fclose(afd) == EOF) {
7407*7c478bd9Sstevel@tonic-gate 		err_print(FCLOSE_FAILED, ALIASFILE, strerror(errno));
7408*7c478bd9Sstevel@tonic-gate 	}
7409*7c478bd9Sstevel@tonic-gate }
7410*7c478bd9Sstevel@tonic-gate 
7411*7c478bd9Sstevel@tonic-gate /*
7412*7c478bd9Sstevel@tonic-gate  * return TRUE if alias_name is an alias for driver_name, otherwise
7413*7c478bd9Sstevel@tonic-gate  * return FALSE.
7414*7c478bd9Sstevel@tonic-gate  */
7415*7c478bd9Sstevel@tonic-gate static int
7416*7c478bd9Sstevel@tonic-gate alias(char *driver_name, char *alias_name)
7417*7c478bd9Sstevel@tonic-gate {
7418*7c478bd9Sstevel@tonic-gate 	driver_alias_t *alias;
7419*7c478bd9Sstevel@tonic-gate 
7420*7c478bd9Sstevel@tonic-gate 	/*
7421*7c478bd9Sstevel@tonic-gate 	 * check for a match
7422*7c478bd9Sstevel@tonic-gate 	 */
7423*7c478bd9Sstevel@tonic-gate 	for (alias = driver_aliases; alias != NULL; alias = alias->next) {
7424*7c478bd9Sstevel@tonic-gate 		if ((strcmp(alias->driver_name, driver_name) == 0) &&
7425*7c478bd9Sstevel@tonic-gate 		    (strcmp(alias->alias_name, alias_name) == 0)) {
7426*7c478bd9Sstevel@tonic-gate 			return (TRUE);
7427*7c478bd9Sstevel@tonic-gate 		}
7428*7c478bd9Sstevel@tonic-gate 	}
7429*7c478bd9Sstevel@tonic-gate 	return (FALSE);
7430*7c478bd9Sstevel@tonic-gate }
7431*7c478bd9Sstevel@tonic-gate 
7432*7c478bd9Sstevel@tonic-gate /*
7433*7c478bd9Sstevel@tonic-gate  * convenience functions
7434*7c478bd9Sstevel@tonic-gate  */
7435*7c478bd9Sstevel@tonic-gate static void *
7436*7c478bd9Sstevel@tonic-gate s_malloc(const size_t size)
7437*7c478bd9Sstevel@tonic-gate {
7438*7c478bd9Sstevel@tonic-gate 	void *rp;
7439*7c478bd9Sstevel@tonic-gate 
7440*7c478bd9Sstevel@tonic-gate 	rp = malloc(size);
7441*7c478bd9Sstevel@tonic-gate 	if (rp == NULL) {
7442*7c478bd9Sstevel@tonic-gate 		err_print(MALLOC_FAILED, size);
7443*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
7444*7c478bd9Sstevel@tonic-gate 	}
7445*7c478bd9Sstevel@tonic-gate 	return (rp);
7446*7c478bd9Sstevel@tonic-gate }
7447*7c478bd9Sstevel@tonic-gate 
7448*7c478bd9Sstevel@tonic-gate /*
7449*7c478bd9Sstevel@tonic-gate  * convenience functions
7450*7c478bd9Sstevel@tonic-gate  */
7451*7c478bd9Sstevel@tonic-gate static void *
7452*7c478bd9Sstevel@tonic-gate s_realloc(void *ptr, const size_t size)
7453*7c478bd9Sstevel@tonic-gate {
7454*7c478bd9Sstevel@tonic-gate 	ptr = realloc(ptr, size);
7455*7c478bd9Sstevel@tonic-gate 	if (ptr == NULL) {
7456*7c478bd9Sstevel@tonic-gate 		err_print(REALLOC_FAILED, size);
7457*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
7458*7c478bd9Sstevel@tonic-gate 	}
7459*7c478bd9Sstevel@tonic-gate 	return (ptr);
7460*7c478bd9Sstevel@tonic-gate }
7461*7c478bd9Sstevel@tonic-gate 
7462*7c478bd9Sstevel@tonic-gate static void *
7463*7c478bd9Sstevel@tonic-gate s_zalloc(const size_t size)
7464*7c478bd9Sstevel@tonic-gate {
7465*7c478bd9Sstevel@tonic-gate 	void *rp;
7466*7c478bd9Sstevel@tonic-gate 
7467*7c478bd9Sstevel@tonic-gate 	rp = calloc(1, size);
7468*7c478bd9Sstevel@tonic-gate 	if (rp == NULL) {
7469*7c478bd9Sstevel@tonic-gate 		err_print(CALLOC_FAILED, size);
7470*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
7471*7c478bd9Sstevel@tonic-gate 	}
7472*7c478bd9Sstevel@tonic-gate 	return (rp);
7473*7c478bd9Sstevel@tonic-gate }
7474*7c478bd9Sstevel@tonic-gate 
7475*7c478bd9Sstevel@tonic-gate char *
7476*7c478bd9Sstevel@tonic-gate s_strdup(const char *ptr)
7477*7c478bd9Sstevel@tonic-gate {
7478*7c478bd9Sstevel@tonic-gate 	void *rp;
7479*7c478bd9Sstevel@tonic-gate 
7480*7c478bd9Sstevel@tonic-gate 	rp = strdup(ptr);
7481*7c478bd9Sstevel@tonic-gate 	if (rp == NULL) {
7482*7c478bd9Sstevel@tonic-gate 		err_print(STRDUP_FAILED, ptr);
7483*7c478bd9Sstevel@tonic-gate 		devfsadm_exit(1);
7484*7c478bd9Sstevel@tonic-gate 	}
7485*7c478bd9Sstevel@tonic-gate 	return (rp);
7486*7c478bd9Sstevel@tonic-gate }
7487*7c478bd9Sstevel@tonic-gate 
7488*7c478bd9Sstevel@tonic-gate static void
7489*7c478bd9Sstevel@tonic-gate s_closedir(DIR *dirp)
7490*7c478bd9Sstevel@tonic-gate {
7491*7c478bd9Sstevel@tonic-gate retry:
7492*7c478bd9Sstevel@tonic-gate 	if (closedir(dirp) != 0) {
7493*7c478bd9Sstevel@tonic-gate 		if (errno == EINTR)
7494*7c478bd9Sstevel@tonic-gate 			goto retry;
7495*7c478bd9Sstevel@tonic-gate 		err_print(CLOSEDIR_FAILED, strerror(errno));
7496*7c478bd9Sstevel@tonic-gate 	}
7497*7c478bd9Sstevel@tonic-gate }
7498*7c478bd9Sstevel@tonic-gate 
7499*7c478bd9Sstevel@tonic-gate static void
7500*7c478bd9Sstevel@tonic-gate s_mkdirp(const char *path, const mode_t mode)
7501*7c478bd9Sstevel@tonic-gate {
7502*7c478bd9Sstevel@tonic-gate 	vprint(CHATTY_MID, "mkdirp(%s, 0x%lx)\n", path, mode);
7503*7c478bd9Sstevel@tonic-gate 	if (mkdirp(path, mode) == -1) {
7504*7c478bd9Sstevel@tonic-gate 		if (errno != EEXIST) {
7505*7c478bd9Sstevel@tonic-gate 			err_print(MKDIR_FAILED, path, mode, strerror(errno));
7506*7c478bd9Sstevel@tonic-gate 		}
7507*7c478bd9Sstevel@tonic-gate 	}
7508*7c478bd9Sstevel@tonic-gate }
7509*7c478bd9Sstevel@tonic-gate 
7510*7c478bd9Sstevel@tonic-gate static void
7511*7c478bd9Sstevel@tonic-gate s_unlink(const char *file)
7512*7c478bd9Sstevel@tonic-gate {
7513*7c478bd9Sstevel@tonic-gate retry:
7514*7c478bd9Sstevel@tonic-gate 	if (unlink(file) == -1) {
7515*7c478bd9Sstevel@tonic-gate 		if (errno == EINTR || errno == EAGAIN)
7516*7c478bd9Sstevel@tonic-gate 			goto retry;
7517*7c478bd9Sstevel@tonic-gate 		if (errno != ENOENT) {
7518*7c478bd9Sstevel@tonic-gate 			err_print(UNLINK_FAILED, file, strerror(errno));
7519*7c478bd9Sstevel@tonic-gate 		}
7520*7c478bd9Sstevel@tonic-gate 	}
7521*7c478bd9Sstevel@tonic-gate }
7522*7c478bd9Sstevel@tonic-gate 
7523*7c478bd9Sstevel@tonic-gate static void
7524*7c478bd9Sstevel@tonic-gate add_verbose_id(char *mid)
7525*7c478bd9Sstevel@tonic-gate {
7526*7c478bd9Sstevel@tonic-gate 	num_verbose++;
7527*7c478bd9Sstevel@tonic-gate 	verbose = s_realloc(verbose, num_verbose * sizeof (char *));
7528*7c478bd9Sstevel@tonic-gate 	verbose[num_verbose - 1] = mid;
7529*7c478bd9Sstevel@tonic-gate }
7530*7c478bd9Sstevel@tonic-gate 
7531*7c478bd9Sstevel@tonic-gate /*
7532*7c478bd9Sstevel@tonic-gate  * returns DEVFSADM_TRUE if contents is a minor node in /devices.
7533*7c478bd9Sstevel@tonic-gate  * If mn_root is not NULL, mn_root is set to:
7534*7c478bd9Sstevel@tonic-gate  *	if contents is a /dev node, mn_root = contents
7535*7c478bd9Sstevel@tonic-gate  * 			OR
7536*7c478bd9Sstevel@tonic-gate  *	if contents is a /devices node, mn_root set to the '/'
7537*7c478bd9Sstevel@tonic-gate  *	following /devices.
7538*7c478bd9Sstevel@tonic-gate  */
7539*7c478bd9Sstevel@tonic-gate static int
7540*7c478bd9Sstevel@tonic-gate is_minor_node(char *contents, char **mn_root)
7541*7c478bd9Sstevel@tonic-gate {
7542*7c478bd9Sstevel@tonic-gate 	char *ptr;
7543*7c478bd9Sstevel@tonic-gate 	char device_prefix[100];
7544*7c478bd9Sstevel@tonic-gate 
7545*7c478bd9Sstevel@tonic-gate 	(void) snprintf(device_prefix, sizeof (device_prefix), "../devices/");
7546*7c478bd9Sstevel@tonic-gate 
7547*7c478bd9Sstevel@tonic-gate 	if ((ptr = strstr(contents, device_prefix)) != NULL) {
7548*7c478bd9Sstevel@tonic-gate 		if (mn_root != NULL) {
7549*7c478bd9Sstevel@tonic-gate 			/* mn_root should point to the / following /devices */
7550*7c478bd9Sstevel@tonic-gate 			*mn_root = ptr += strlen(device_prefix) - 1;
7551*7c478bd9Sstevel@tonic-gate 		}
7552*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_TRUE);
7553*7c478bd9Sstevel@tonic-gate 	}
7554*7c478bd9Sstevel@tonic-gate 
7555*7c478bd9Sstevel@tonic-gate 	(void) snprintf(device_prefix, sizeof (device_prefix), "/devices/");
7556*7c478bd9Sstevel@tonic-gate 
7557*7c478bd9Sstevel@tonic-gate 	if (strncmp(contents, device_prefix, strlen(device_prefix)) == 0) {
7558*7c478bd9Sstevel@tonic-gate 		if (mn_root != NULL) {
7559*7c478bd9Sstevel@tonic-gate 			/* mn_root should point to the / following /devices */
7560*7c478bd9Sstevel@tonic-gate 			*mn_root = contents + strlen(device_prefix) - 1;
7561*7c478bd9Sstevel@tonic-gate 		}
7562*7c478bd9Sstevel@tonic-gate 		return (DEVFSADM_TRUE);
7563*7c478bd9Sstevel@tonic-gate 	}
7564*7c478bd9Sstevel@tonic-gate 
7565*7c478bd9Sstevel@tonic-gate 	if (mn_root != NULL) {
7566*7c478bd9Sstevel@tonic-gate 		*mn_root = contents;
7567*7c478bd9Sstevel@tonic-gate 	}
7568*7c478bd9Sstevel@tonic-gate 	return (DEVFSADM_FALSE);
7569*7c478bd9Sstevel@tonic-gate }
7570*7c478bd9Sstevel@tonic-gate 
7571*7c478bd9Sstevel@tonic-gate /*
7572*7c478bd9Sstevel@tonic-gate  * Lookup nvpair corresponding to the given name and type:
7573*7c478bd9Sstevel@tonic-gate  *
7574*7c478bd9Sstevel@tonic-gate  * The standard nvlist_lookup functions in libnvpair don't work as our
7575*7c478bd9Sstevel@tonic-gate  * nvlist is not allocated with NV_UNIQUE_NAME or NV_UNIQUE_NAME_TYPE.
7576*7c478bd9Sstevel@tonic-gate  */
7577*7c478bd9Sstevel@tonic-gate static nvpair_t *
7578*7c478bd9Sstevel@tonic-gate lookup_nvpair(nvlist_t *nvl, char *name, data_type_t type)
7579*7c478bd9Sstevel@tonic-gate {
7580*7c478bd9Sstevel@tonic-gate 	nvpair_t *nvp;
7581*7c478bd9Sstevel@tonic-gate 
7582*7c478bd9Sstevel@tonic-gate 	for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
7583*7c478bd9Sstevel@tonic-gate 	    nvp = nvlist_next_nvpair(nvl, nvp)) {
7584*7c478bd9Sstevel@tonic-gate 		if (strcmp(name, nvpair_name(nvp)) == 0 &&
7585*7c478bd9Sstevel@tonic-gate 		    nvpair_type(nvp) == type)
7586*7c478bd9Sstevel@tonic-gate 			return (nvp);
7587*7c478bd9Sstevel@tonic-gate 	}
7588*7c478bd9Sstevel@tonic-gate 
7589*7c478bd9Sstevel@tonic-gate 	return (NULL);
7590*7c478bd9Sstevel@tonic-gate }
7591*7c478bd9Sstevel@tonic-gate 
7592*7c478bd9Sstevel@tonic-gate /*
7593*7c478bd9Sstevel@tonic-gate  * Send the specified event to RCM if the given nvlist contains a minor
7594*7c478bd9Sstevel@tonic-gate  * node of the specified type.
7595*7c478bd9Sstevel@tonic-gate  */
7596*7c478bd9Sstevel@tonic-gate static void
7597*7c478bd9Sstevel@tonic-gate notify_event(nvlist_t *nvl, char *minor_node_type, char *event)
7598*7c478bd9Sstevel@tonic-gate {
7599*7c478bd9Sstevel@tonic-gate 	nvpair_t *nvp, *mnvp;
7600*7c478bd9Sstevel@tonic-gate 	char *path, *driver, *type;
7601*7c478bd9Sstevel@tonic-gate 	int instance;
7602*7c478bd9Sstevel@tonic-gate 	int err;
7603*7c478bd9Sstevel@tonic-gate 	boolean_t do_notify = B_FALSE;
7604*7c478bd9Sstevel@tonic-gate 	uint_t nminor;
7605*7c478bd9Sstevel@tonic-gate 	char *minor_byte_array;
7606*7c478bd9Sstevel@tonic-gate 	nvlist_t *mnvl;
7607*7c478bd9Sstevel@tonic-gate 
7608*7c478bd9Sstevel@tonic-gate 	nvp = NULL;
7609*7c478bd9Sstevel@tonic-gate 	while (!do_notify && ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL)) {
7610*7c478bd9Sstevel@tonic-gate 		if (strcmp(nvpair_name(nvp), RCM_NV_MINOR_DATA) != 0)
7611*7c478bd9Sstevel@tonic-gate 			continue;
7612*7c478bd9Sstevel@tonic-gate 		if (nvpair_value_byte_array(nvp,
7613*7c478bd9Sstevel@tonic-gate 		    (uchar_t **)&minor_byte_array, &nminor) != 0)
7614*7c478bd9Sstevel@tonic-gate 			goto error;
7615*7c478bd9Sstevel@tonic-gate 		if (nvlist_unpack(minor_byte_array,
7616*7c478bd9Sstevel@tonic-gate 		    nminor, &mnvl, 0) != 0)
7617*7c478bd9Sstevel@tonic-gate 			goto error;
7618*7c478bd9Sstevel@tonic-gate 
7619*7c478bd9Sstevel@tonic-gate 		mnvp = NULL;
7620*7c478bd9Sstevel@tonic-gate 		while (!do_notify &&
7621*7c478bd9Sstevel@tonic-gate 		    ((mnvp = nvlist_next_nvpair(mnvl, mnvp)) != NULL)) {
7622*7c478bd9Sstevel@tonic-gate 			if (strcmp(nvpair_name(mnvp),
7623*7c478bd9Sstevel@tonic-gate 			    RCM_NV_MINOR_NODE_TYPE) == 0) {
7624*7c478bd9Sstevel@tonic-gate 				if (nvpair_value_string(mnvp, &type) != 0) {
7625*7c478bd9Sstevel@tonic-gate 					nvlist_free(mnvl);
7626*7c478bd9Sstevel@tonic-gate 					goto error;
7627*7c478bd9Sstevel@tonic-gate 				}
7628*7c478bd9Sstevel@tonic-gate 				do_notify = (strcmp(type,
7629*7c478bd9Sstevel@tonic-gate 				    minor_node_type) == 0);
7630*7c478bd9Sstevel@tonic-gate 			}
7631*7c478bd9Sstevel@tonic-gate 		}
7632*7c478bd9Sstevel@tonic-gate 
7633*7c478bd9Sstevel@tonic-gate 		nvlist_free(mnvl);
7634*7c478bd9Sstevel@tonic-gate 	}
7635*7c478bd9Sstevel@tonic-gate 
7636*7c478bd9Sstevel@tonic-gate 	if (!do_notify)
7637*7c478bd9Sstevel@tonic-gate 		return;
7638*7c478bd9Sstevel@tonic-gate 
7639*7c478bd9Sstevel@tonic-gate 	if (librcm_notify_event(rcm_hdl, event, 0, nvl, NULL) == RCM_SUCCESS)
7640*7c478bd9Sstevel@tonic-gate 		return;
7641*7c478bd9Sstevel@tonic-gate 
7642*7c478bd9Sstevel@tonic-gate error:
7643*7c478bd9Sstevel@tonic-gate 	err = errno;
7644*7c478bd9Sstevel@tonic-gate 
7645*7c478bd9Sstevel@tonic-gate 	if (((nvp = lookup_nvpair(nvl,
7646*7c478bd9Sstevel@tonic-gate 	    RCM_NV_DEVFS_PATH, DATA_TYPE_STRING)) == NULL) ||
7647*7c478bd9Sstevel@tonic-gate 	    (nvpair_value_string(nvp, &path) != 0))
7648*7c478bd9Sstevel@tonic-gate 		path = "unknown";
7649*7c478bd9Sstevel@tonic-gate 
7650*7c478bd9Sstevel@tonic-gate 	if (((nvp = lookup_nvpair(nvl,
7651*7c478bd9Sstevel@tonic-gate 	    RCM_NV_DRIVER_NAME, DATA_TYPE_STRING)) == NULL) ||
7652*7c478bd9Sstevel@tonic-gate 	    (nvpair_value_string(nvp, &driver) != 0))
7653*7c478bd9Sstevel@tonic-gate 		driver = "unknown";
7654*7c478bd9Sstevel@tonic-gate 
7655*7c478bd9Sstevel@tonic-gate 	if (((nvp = lookup_nvpair(nvl,
7656*7c478bd9Sstevel@tonic-gate 	    RCM_NV_INSTANCE, DATA_TYPE_INT32)) == NULL) ||
7657*7c478bd9Sstevel@tonic-gate 	    (nvpair_value_int32(nvp, &instance) != 0))
7658*7c478bd9Sstevel@tonic-gate 		instance = -1;
7659*7c478bd9Sstevel@tonic-gate 
7660*7c478bd9Sstevel@tonic-gate 	err_print(RCM_NOTIFY_FAILED, path, driver,
7661*7c478bd9Sstevel@tonic-gate 	    instance, strerror(err));
7662*7c478bd9Sstevel@tonic-gate }
7663*7c478bd9Sstevel@tonic-gate 
7664*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7665*7c478bd9Sstevel@tonic-gate static void
7666*7c478bd9Sstevel@tonic-gate process_rcm_events(void *arg)
7667*7c478bd9Sstevel@tonic-gate {
7668*7c478bd9Sstevel@tonic-gate 	struct rcm_eventq *ev, *ev_next;
7669*7c478bd9Sstevel@tonic-gate 	int need_to_exit;
7670*7c478bd9Sstevel@tonic-gate 
7671*7c478bd9Sstevel@tonic-gate 	for (;;) {
7672*7c478bd9Sstevel@tonic-gate 		(void) mutex_lock(&rcm_eventq_lock);
7673*7c478bd9Sstevel@tonic-gate 		while (rcm_eventq_head == NULL &&
7674*7c478bd9Sstevel@tonic-gate 		    need_to_exit_rcm_event_thread == 0)
7675*7c478bd9Sstevel@tonic-gate 			(void) cond_wait(&rcm_eventq_cv, &rcm_eventq_lock);
7676*7c478bd9Sstevel@tonic-gate 
7677*7c478bd9Sstevel@tonic-gate 		need_to_exit = need_to_exit_rcm_event_thread;
7678*7c478bd9Sstevel@tonic-gate 		ev = rcm_eventq_head;
7679*7c478bd9Sstevel@tonic-gate 		rcm_eventq_head = rcm_eventq_tail = NULL;
7680*7c478bd9Sstevel@tonic-gate 		(void) mutex_unlock(&rcm_eventq_lock);
7681*7c478bd9Sstevel@tonic-gate 
7682*7c478bd9Sstevel@tonic-gate 		for (; ev != NULL; ev = ev_next) {
7683*7c478bd9Sstevel@tonic-gate 			/*
7684*7c478bd9Sstevel@tonic-gate 			 * Private notification interface to RCM:
7685*7c478bd9Sstevel@tonic-gate 			 * Do not retry the RCM notification on an error since
7686*7c478bd9Sstevel@tonic-gate 			 * we do not know whether the failure occurred in
7687*7c478bd9Sstevel@tonic-gate 			 * librcm, rcm_daemon or rcm modules or scripts.
7688*7c478bd9Sstevel@tonic-gate 			 */
7689*7c478bd9Sstevel@tonic-gate 
7690*7c478bd9Sstevel@tonic-gate 			notify_event(ev->nvl, DDI_NT_NET,
7691*7c478bd9Sstevel@tonic-gate 			    RCM_RESOURCE_NETWORK_NEW);
7692*7c478bd9Sstevel@tonic-gate 
7693*7c478bd9Sstevel@tonic-gate 			notify_event(ev->nvl, DDI_NT_MAC,
7694*7c478bd9Sstevel@tonic-gate 			    RCM_RESOURCE_MAC_NEW);
7695*7c478bd9Sstevel@tonic-gate 
7696*7c478bd9Sstevel@tonic-gate 			ev_next = ev->next;
7697*7c478bd9Sstevel@tonic-gate 			nvlist_free(ev->nvl);
7698*7c478bd9Sstevel@tonic-gate 			free(ev);
7699*7c478bd9Sstevel@tonic-gate 		}
7700*7c478bd9Sstevel@tonic-gate 
7701*7c478bd9Sstevel@tonic-gate 		if (need_to_exit)
7702*7c478bd9Sstevel@tonic-gate 			return;
7703*7c478bd9Sstevel@tonic-gate 	}
7704*7c478bd9Sstevel@tonic-gate }
7705*7c478bd9Sstevel@tonic-gate 
7706*7c478bd9Sstevel@tonic-gate /*
7707*7c478bd9Sstevel@tonic-gate  * Initialize rcm related handles and function pointers.
7708*7c478bd9Sstevel@tonic-gate  * Since RCM need not present in miniroot, we dlopen librcm.
7709*7c478bd9Sstevel@tonic-gate  */
7710*7c478bd9Sstevel@tonic-gate static int
7711*7c478bd9Sstevel@tonic-gate rcm_init(void)
7712*7c478bd9Sstevel@tonic-gate {
7713*7c478bd9Sstevel@tonic-gate #define	LIBRCM_PATH	"/usr/lib/librcm.so"
7714*7c478bd9Sstevel@tonic-gate 	rcm_handle_t *hdl = NULL;
7715*7c478bd9Sstevel@tonic-gate 	int err;
7716*7c478bd9Sstevel@tonic-gate 
7717*7c478bd9Sstevel@tonic-gate 	if ((librcm_hdl = dlopen(LIBRCM_PATH, RTLD_LAZY)) == NULL) {
7718*7c478bd9Sstevel@tonic-gate 		/*
7719*7c478bd9Sstevel@tonic-gate 		 * don't log an error here, since librcm may not be present
7720*7c478bd9Sstevel@tonic-gate 		 * in miniroot.
7721*7c478bd9Sstevel@tonic-gate 		 */
7722*7c478bd9Sstevel@tonic-gate 		return (-1);
7723*7c478bd9Sstevel@tonic-gate 	}
7724*7c478bd9Sstevel@tonic-gate 
7725*7c478bd9Sstevel@tonic-gate 	librcm_alloc_handle = (int (*)())dlsym(librcm_hdl, "rcm_alloc_handle");
7726*7c478bd9Sstevel@tonic-gate 	librcm_free_handle = (void (*)())dlsym(librcm_hdl, "rcm_free_handle");
7727*7c478bd9Sstevel@tonic-gate 	librcm_notify_event = (int (*)())dlsym(librcm_hdl, "rcm_notify_event");
7728*7c478bd9Sstevel@tonic-gate 
7729*7c478bd9Sstevel@tonic-gate 	if (librcm_alloc_handle == NULL || librcm_notify_event == NULL ||
7730*7c478bd9Sstevel@tonic-gate 	    librcm_free_handle == NULL) {
7731*7c478bd9Sstevel@tonic-gate 		err_print(MISSING_SYMBOLS, LIBRCM_PATH);
7732*7c478bd9Sstevel@tonic-gate 		goto out;
7733*7c478bd9Sstevel@tonic-gate 	}
7734*7c478bd9Sstevel@tonic-gate 
7735*7c478bd9Sstevel@tonic-gate 	/* Initialize the rcm handle */
7736*7c478bd9Sstevel@tonic-gate 	if (librcm_alloc_handle(NULL, 0, NULL, &hdl) != RCM_SUCCESS) {
7737*7c478bd9Sstevel@tonic-gate 		err_print(RCM_ALLOC_HANDLE_ERROR);
7738*7c478bd9Sstevel@tonic-gate 		goto out;
7739*7c478bd9Sstevel@tonic-gate 	}
7740*7c478bd9Sstevel@tonic-gate 
7741*7c478bd9Sstevel@tonic-gate 	(void) cond_init(&rcm_eventq_cv, USYNC_THREAD, 0);
7742*7c478bd9Sstevel@tonic-gate 	(void) mutex_init(&rcm_eventq_lock, USYNC_THREAD, 0);
7743*7c478bd9Sstevel@tonic-gate 
7744*7c478bd9Sstevel@tonic-gate 	/* create a thread to notify RCM of events */
7745*7c478bd9Sstevel@tonic-gate 	if ((err = thr_create(NULL, 0, (void *(*)(void *))process_rcm_events,
7746*7c478bd9Sstevel@tonic-gate 	    NULL, 0, &process_rcm_events_tid)) != 0) {
7747*7c478bd9Sstevel@tonic-gate 		err_print(CANT_CREATE_THREAD, "process_rcm_events",
7748*7c478bd9Sstevel@tonic-gate 		    strerror(err));
7749*7c478bd9Sstevel@tonic-gate 		goto out;
7750*7c478bd9Sstevel@tonic-gate 	}
7751*7c478bd9Sstevel@tonic-gate 
7752*7c478bd9Sstevel@tonic-gate 	rcm_hdl = hdl;
7753*7c478bd9Sstevel@tonic-gate 	return (0);
7754*7c478bd9Sstevel@tonic-gate 
7755*7c478bd9Sstevel@tonic-gate out:
7756*7c478bd9Sstevel@tonic-gate 	if (hdl)
7757*7c478bd9Sstevel@tonic-gate 		librcm_free_handle(hdl);
7758*7c478bd9Sstevel@tonic-gate 	(void) dlclose(librcm_hdl);
7759*7c478bd9Sstevel@tonic-gate 	return (-1);
7760*7c478bd9Sstevel@tonic-gate }
7761*7c478bd9Sstevel@tonic-gate 
7762*7c478bd9Sstevel@tonic-gate /*
7763*7c478bd9Sstevel@tonic-gate  * Build an nvlist using the minor data. Pack it and add the packed nvlist
7764*7c478bd9Sstevel@tonic-gate  * as a byte array to nv_list parameter.
7765*7c478bd9Sstevel@tonic-gate  * Return 0 on success, errno on failure.
7766*7c478bd9Sstevel@tonic-gate  */
7767*7c478bd9Sstevel@tonic-gate static int
7768*7c478bd9Sstevel@tonic-gate add_minor_data_to_nvl(nvlist_t *nv_list, di_minor_t minor)
7769*7c478bd9Sstevel@tonic-gate {
7770*7c478bd9Sstevel@tonic-gate 	nvlist_t *nvl = NULL;
7771*7c478bd9Sstevel@tonic-gate 	int32_t minor_type;
7772*7c478bd9Sstevel@tonic-gate 	char *minor_name, *minor_node_type;
7773*7c478bd9Sstevel@tonic-gate 	int err;
7774*7c478bd9Sstevel@tonic-gate 	char *buf = NULL;
7775*7c478bd9Sstevel@tonic-gate 	size_t buflen = 0;
7776*7c478bd9Sstevel@tonic-gate 
7777*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_alloc(&nvl, 0, 0)) != 0)
7778*7c478bd9Sstevel@tonic-gate 		return (err);
7779*7c478bd9Sstevel@tonic-gate 
7780*7c478bd9Sstevel@tonic-gate 	minor_type = (int32_t)di_minor_type(minor);
7781*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_int32(nvl, RCM_NV_MINOR_TYPE, minor_type)) != 0)
7782*7c478bd9Sstevel@tonic-gate 		goto error;
7783*7c478bd9Sstevel@tonic-gate 
7784*7c478bd9Sstevel@tonic-gate 	minor_name = di_minor_name(minor);
7785*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_string(nvl, RCM_NV_MINOR_NAME, minor_name)) != 0)
7786*7c478bd9Sstevel@tonic-gate 		goto error;
7787*7c478bd9Sstevel@tonic-gate 
7788*7c478bd9Sstevel@tonic-gate 	if ((minor_node_type = di_minor_nodetype(minor)) == NULL)
7789*7c478bd9Sstevel@tonic-gate 		minor_node_type = "";
7790*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_string(nvl, RCM_NV_MINOR_NODE_TYPE,
7791*7c478bd9Sstevel@tonic-gate 	    minor_node_type)) != 0)
7792*7c478bd9Sstevel@tonic-gate 		goto error;
7793*7c478bd9Sstevel@tonic-gate 
7794*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_NATIVE, 0)) != 0)
7795*7c478bd9Sstevel@tonic-gate 		goto error;
7796*7c478bd9Sstevel@tonic-gate 
7797*7c478bd9Sstevel@tonic-gate 	err = nvlist_add_byte_array(nv_list, RCM_NV_MINOR_DATA,
7798*7c478bd9Sstevel@tonic-gate 	    (uchar_t *)(buf), (uint_t)(buflen));
7799*7c478bd9Sstevel@tonic-gate 
7800*7c478bd9Sstevel@tonic-gate error:
7801*7c478bd9Sstevel@tonic-gate 	nvlist_free(nvl);
7802*7c478bd9Sstevel@tonic-gate 	if (buf)
7803*7c478bd9Sstevel@tonic-gate 		free(buf);
7804*7c478bd9Sstevel@tonic-gate 	return (err);
7805*7c478bd9Sstevel@tonic-gate }
7806*7c478bd9Sstevel@tonic-gate 
7807*7c478bd9Sstevel@tonic-gate static void
7808*7c478bd9Sstevel@tonic-gate enqueue_rcm_event(nvlist_t *nvl)
7809*7c478bd9Sstevel@tonic-gate {
7810*7c478bd9Sstevel@tonic-gate 	struct rcm_eventq *ev;
7811*7c478bd9Sstevel@tonic-gate 
7812*7c478bd9Sstevel@tonic-gate 	ev = (struct rcm_eventq *)s_zalloc(sizeof (struct rcm_eventq));
7813*7c478bd9Sstevel@tonic-gate 	ev->nvl = nvl;
7814*7c478bd9Sstevel@tonic-gate 
7815*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(&rcm_eventq_lock);
7816*7c478bd9Sstevel@tonic-gate 	if (rcm_eventq_head == NULL)
7817*7c478bd9Sstevel@tonic-gate 		rcm_eventq_head = ev;
7818*7c478bd9Sstevel@tonic-gate 	else
7819*7c478bd9Sstevel@tonic-gate 		rcm_eventq_tail->next = ev;
7820*7c478bd9Sstevel@tonic-gate 	rcm_eventq_tail = ev;
7821*7c478bd9Sstevel@tonic-gate 	(void) cond_broadcast(&rcm_eventq_cv);
7822*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(&rcm_eventq_lock);
7823*7c478bd9Sstevel@tonic-gate }
7824*7c478bd9Sstevel@tonic-gate 
7825*7c478bd9Sstevel@tonic-gate /*
7826*7c478bd9Sstevel@tonic-gate  * Generate an nvlist using the information given in node and minor_name.
7827*7c478bd9Sstevel@tonic-gate  * If minor_name is NULL the nvlist will contain information on
7828*7c478bd9Sstevel@tonic-gate  * all minor nodes. Otherwise the nvlist will contain information
7829*7c478bd9Sstevel@tonic-gate  * only on the given minor_name. Notify RCM passing the nvlist.
7830*7c478bd9Sstevel@tonic-gate  *
7831*7c478bd9Sstevel@tonic-gate  * Return 0 upon successfully notifying RCM, errno on failure.
7832*7c478bd9Sstevel@tonic-gate  */
7833*7c478bd9Sstevel@tonic-gate static int
7834*7c478bd9Sstevel@tonic-gate notify_rcm(di_node_t node, char *minor_name)
7835*7c478bd9Sstevel@tonic-gate {
7836*7c478bd9Sstevel@tonic-gate 	nvlist_t *nvl = NULL;
7837*7c478bd9Sstevel@tonic-gate 	char *path, *driver_name;
7838*7c478bd9Sstevel@tonic-gate 	char *node_name;
7839*7c478bd9Sstevel@tonic-gate 	int err;
7840*7c478bd9Sstevel@tonic-gate 	int32_t instance;
7841*7c478bd9Sstevel@tonic-gate 	di_minor_t minor;
7842*7c478bd9Sstevel@tonic-gate 
7843*7c478bd9Sstevel@tonic-gate 	if ((driver_name = di_driver_name(node)) == NULL)
7844*7c478bd9Sstevel@tonic-gate 		driver_name = "";
7845*7c478bd9Sstevel@tonic-gate 
7846*7c478bd9Sstevel@tonic-gate 	instance = (int32_t)di_instance(node);
7847*7c478bd9Sstevel@tonic-gate 
7848*7c478bd9Sstevel@tonic-gate 	if ((path = di_devfs_path(node)) == NULL) {
7849*7c478bd9Sstevel@tonic-gate 		err = errno;
7850*7c478bd9Sstevel@tonic-gate 		goto error;
7851*7c478bd9Sstevel@tonic-gate 	}
7852*7c478bd9Sstevel@tonic-gate 
7853*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_alloc(&nvl, 0, 0)) != 0)
7854*7c478bd9Sstevel@tonic-gate 		goto error;
7855*7c478bd9Sstevel@tonic-gate 
7856*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_string(nvl, RCM_NV_DRIVER_NAME, driver_name))
7857*7c478bd9Sstevel@tonic-gate 	    != 0)
7858*7c478bd9Sstevel@tonic-gate 		goto error;
7859*7c478bd9Sstevel@tonic-gate 
7860*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_int32(nvl, RCM_NV_INSTANCE, instance)) != 0)
7861*7c478bd9Sstevel@tonic-gate 		goto error;
7862*7c478bd9Sstevel@tonic-gate 
7863*7c478bd9Sstevel@tonic-gate 	if ((node_name = di_node_name(node)) == NULL)
7864*7c478bd9Sstevel@tonic-gate 		node_name = "";
7865*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_string(nvl, RCM_NV_NODE_NAME, node_name)) != 0)
7866*7c478bd9Sstevel@tonic-gate 		goto error;
7867*7c478bd9Sstevel@tonic-gate 
7868*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_string(nvl, RCM_NV_DEVFS_PATH, path)) != 0)
7869*7c478bd9Sstevel@tonic-gate 		goto error;
7870*7c478bd9Sstevel@tonic-gate 
7871*7c478bd9Sstevel@tonic-gate 	minor = di_minor_next(node, DI_MINOR_NIL);
7872*7c478bd9Sstevel@tonic-gate 	while (minor != DI_MINOR_NIL) {
7873*7c478bd9Sstevel@tonic-gate 		if ((minor_name == NULL) ||
7874*7c478bd9Sstevel@tonic-gate 		    (strcmp(minor_name, di_minor_name(minor)) == 0)) {
7875*7c478bd9Sstevel@tonic-gate 			if ((err = add_minor_data_to_nvl(nvl, minor)) != 0)
7876*7c478bd9Sstevel@tonic-gate 				goto error;
7877*7c478bd9Sstevel@tonic-gate 		}
7878*7c478bd9Sstevel@tonic-gate 		minor = di_minor_next(node, minor);
7879*7c478bd9Sstevel@tonic-gate 	}
7880*7c478bd9Sstevel@tonic-gate 
7881*7c478bd9Sstevel@tonic-gate 	enqueue_rcm_event(nvl);
7882*7c478bd9Sstevel@tonic-gate 	di_devfs_path_free(path);
7883*7c478bd9Sstevel@tonic-gate 	return (0);
7884*7c478bd9Sstevel@tonic-gate 
7885*7c478bd9Sstevel@tonic-gate error:
7886*7c478bd9Sstevel@tonic-gate 	err_print(RCM_NVLIST_BUILD_ERROR, ((path != NULL) ? path : "unknown"),
7887*7c478bd9Sstevel@tonic-gate 	    driver_name, instance, strerror(err));
7888*7c478bd9Sstevel@tonic-gate 
7889*7c478bd9Sstevel@tonic-gate 	if (path)
7890*7c478bd9Sstevel@tonic-gate 		di_devfs_path_free(path);
7891*7c478bd9Sstevel@tonic-gate 	if (nvl)
7892*7c478bd9Sstevel@tonic-gate 		nvlist_free(nvl);
7893*7c478bd9Sstevel@tonic-gate 	return (err);
7894*7c478bd9Sstevel@tonic-gate }
7895*7c478bd9Sstevel@tonic-gate 
7896*7c478bd9Sstevel@tonic-gate /*
7897*7c478bd9Sstevel@tonic-gate  * Add the specified property to nvl.
7898*7c478bd9Sstevel@tonic-gate  * Returns:
7899*7c478bd9Sstevel@tonic-gate  *   0	successfully added
7900*7c478bd9Sstevel@tonic-gate  *   -1	an error occurred
7901*7c478bd9Sstevel@tonic-gate  *   1	could not add the property for reasons not due to errors.
7902*7c478bd9Sstevel@tonic-gate  */
7903*7c478bd9Sstevel@tonic-gate static int
7904*7c478bd9Sstevel@tonic-gate add_property(nvlist_t *nvl, di_prop_t prop)
7905*7c478bd9Sstevel@tonic-gate {
7906*7c478bd9Sstevel@tonic-gate 	char *name;
7907*7c478bd9Sstevel@tonic-gate 	char *attr_name;
7908*7c478bd9Sstevel@tonic-gate 	int n, len;
7909*7c478bd9Sstevel@tonic-gate 	int32_t *int32p;
7910*7c478bd9Sstevel@tonic-gate 	int64_t *int64p;
7911*7c478bd9Sstevel@tonic-gate 	char *str;
7912*7c478bd9Sstevel@tonic-gate 	char **strarray;
7913*7c478bd9Sstevel@tonic-gate 	uchar_t *bytep;
7914*7c478bd9Sstevel@tonic-gate 	int rv = 0;
7915*7c478bd9Sstevel@tonic-gate 	int i;
7916*7c478bd9Sstevel@tonic-gate 
7917*7c478bd9Sstevel@tonic-gate 	if ((name = di_prop_name(prop)) == NULL)
7918*7c478bd9Sstevel@tonic-gate 		return (-1);
7919*7c478bd9Sstevel@tonic-gate 
7920*7c478bd9Sstevel@tonic-gate 	len = sizeof (DEV_PROP_PREFIX) + strlen(name);
7921*7c478bd9Sstevel@tonic-gate 	if ((attr_name = malloc(len)) == NULL)
7922*7c478bd9Sstevel@tonic-gate 		return (-1);
7923*7c478bd9Sstevel@tonic-gate 
7924*7c478bd9Sstevel@tonic-gate 	(void) strlcpy(attr_name, DEV_PROP_PREFIX, len);
7925*7c478bd9Sstevel@tonic-gate 	(void) strlcat(attr_name, name, len);
7926*7c478bd9Sstevel@tonic-gate 
7927*7c478bd9Sstevel@tonic-gate 	switch (di_prop_type(prop)) {
7928*7c478bd9Sstevel@tonic-gate 	case DI_PROP_TYPE_BOOLEAN:
7929*7c478bd9Sstevel@tonic-gate 		if (nvlist_add_boolean(nvl, attr_name) != 0)
7930*7c478bd9Sstevel@tonic-gate 			goto out;
7931*7c478bd9Sstevel@tonic-gate 		break;
7932*7c478bd9Sstevel@tonic-gate 
7933*7c478bd9Sstevel@tonic-gate 	case DI_PROP_TYPE_INT:
7934*7c478bd9Sstevel@tonic-gate 		if ((n = di_prop_ints(prop, &int32p)) < 1)
7935*7c478bd9Sstevel@tonic-gate 			goto out;
7936*7c478bd9Sstevel@tonic-gate 
7937*7c478bd9Sstevel@tonic-gate 		if (n <= (PROP_LEN_LIMIT / sizeof (int32_t))) {
7938*7c478bd9Sstevel@tonic-gate 			if (nvlist_add_int32_array(nvl, attr_name, int32p,
7939*7c478bd9Sstevel@tonic-gate 			    n) != 0)
7940*7c478bd9Sstevel@tonic-gate 				goto out;
7941*7c478bd9Sstevel@tonic-gate 		} else
7942*7c478bd9Sstevel@tonic-gate 			rv = 1;
7943*7c478bd9Sstevel@tonic-gate 		break;
7944*7c478bd9Sstevel@tonic-gate 
7945*7c478bd9Sstevel@tonic-gate 	case DI_PROP_TYPE_INT64:
7946*7c478bd9Sstevel@tonic-gate 		if ((n = di_prop_int64(prop, &int64p)) < 1)
7947*7c478bd9Sstevel@tonic-gate 			goto out;
7948*7c478bd9Sstevel@tonic-gate 
7949*7c478bd9Sstevel@tonic-gate 		if (n <= (PROP_LEN_LIMIT / sizeof (int64_t))) {
7950*7c478bd9Sstevel@tonic-gate 			if (nvlist_add_int64_array(nvl, attr_name, int64p,
7951*7c478bd9Sstevel@tonic-gate 			    n) != 0)
7952*7c478bd9Sstevel@tonic-gate 				goto out;
7953*7c478bd9Sstevel@tonic-gate 		} else
7954*7c478bd9Sstevel@tonic-gate 			rv = 1;
7955*7c478bd9Sstevel@tonic-gate 		break;
7956*7c478bd9Sstevel@tonic-gate 
7957*7c478bd9Sstevel@tonic-gate 	case DI_PROP_TYPE_BYTE:
7958*7c478bd9Sstevel@tonic-gate 	case DI_PROP_TYPE_UNKNOWN:
7959*7c478bd9Sstevel@tonic-gate 		if ((n = di_prop_bytes(prop, &bytep)) < 1)
7960*7c478bd9Sstevel@tonic-gate 			goto out;
7961*7c478bd9Sstevel@tonic-gate 
7962*7c478bd9Sstevel@tonic-gate 		if (n <= PROP_LEN_LIMIT) {
7963*7c478bd9Sstevel@tonic-gate 			if (nvlist_add_byte_array(nvl, attr_name, bytep, n)
7964*7c478bd9Sstevel@tonic-gate 			    != 0)
7965*7c478bd9Sstevel@tonic-gate 				goto out;
7966*7c478bd9Sstevel@tonic-gate 		} else
7967*7c478bd9Sstevel@tonic-gate 			rv = 1;
7968*7c478bd9Sstevel@tonic-gate 		break;
7969*7c478bd9Sstevel@tonic-gate 
7970*7c478bd9Sstevel@tonic-gate 	case DI_PROP_TYPE_STRING:
7971*7c478bd9Sstevel@tonic-gate 		if ((n = di_prop_strings(prop, &str)) < 1)
7972*7c478bd9Sstevel@tonic-gate 			goto out;
7973*7c478bd9Sstevel@tonic-gate 
7974*7c478bd9Sstevel@tonic-gate 		if ((strarray = malloc(n * sizeof (char *))) == NULL)
7975*7c478bd9Sstevel@tonic-gate 			goto out;
7976*7c478bd9Sstevel@tonic-gate 
7977*7c478bd9Sstevel@tonic-gate 		len = 0;
7978*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < n; i++) {
7979*7c478bd9Sstevel@tonic-gate 			strarray[i] = str + len;
7980*7c478bd9Sstevel@tonic-gate 			len += strlen(strarray[i]) + 1;
7981*7c478bd9Sstevel@tonic-gate 		}
7982*7c478bd9Sstevel@tonic-gate 
7983*7c478bd9Sstevel@tonic-gate 		if (len <= PROP_LEN_LIMIT) {
7984*7c478bd9Sstevel@tonic-gate 			if (nvlist_add_string_array(nvl, attr_name, strarray,
7985*7c478bd9Sstevel@tonic-gate 			    n) != 0) {
7986*7c478bd9Sstevel@tonic-gate 				free(strarray);
7987*7c478bd9Sstevel@tonic-gate 				goto out;
7988*7c478bd9Sstevel@tonic-gate 			}
7989*7c478bd9Sstevel@tonic-gate 		} else
7990*7c478bd9Sstevel@tonic-gate 			rv = 1;
7991*7c478bd9Sstevel@tonic-gate 		free(strarray);
7992*7c478bd9Sstevel@tonic-gate 		break;
7993*7c478bd9Sstevel@tonic-gate 
7994*7c478bd9Sstevel@tonic-gate 	default:
7995*7c478bd9Sstevel@tonic-gate 		rv = 1;
7996*7c478bd9Sstevel@tonic-gate 		break;
7997*7c478bd9Sstevel@tonic-gate 	}
7998*7c478bd9Sstevel@tonic-gate 
7999*7c478bd9Sstevel@tonic-gate 	free(attr_name);
8000*7c478bd9Sstevel@tonic-gate 	return (rv);
8001*7c478bd9Sstevel@tonic-gate 
8002*7c478bd9Sstevel@tonic-gate out:
8003*7c478bd9Sstevel@tonic-gate 	free(attr_name);
8004*7c478bd9Sstevel@tonic-gate 	return (-1);
8005*7c478bd9Sstevel@tonic-gate }
8006*7c478bd9Sstevel@tonic-gate 
8007*7c478bd9Sstevel@tonic-gate static void
8008*7c478bd9Sstevel@tonic-gate free_dev_names(struct devlink_cb_arg *x)
8009*7c478bd9Sstevel@tonic-gate {
8010*7c478bd9Sstevel@tonic-gate 	int i;
8011*7c478bd9Sstevel@tonic-gate 
8012*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < x->count; i++) {
8013*7c478bd9Sstevel@tonic-gate 		free(x->dev_names[i]);
8014*7c478bd9Sstevel@tonic-gate 		free(x->link_contents[i]);
8015*7c478bd9Sstevel@tonic-gate 	}
8016*7c478bd9Sstevel@tonic-gate }
8017*7c478bd9Sstevel@tonic-gate 
8018*7c478bd9Sstevel@tonic-gate /* callback function for di_devlink_cache_walk */
8019*7c478bd9Sstevel@tonic-gate static int
8020*7c478bd9Sstevel@tonic-gate devlink_cb(di_devlink_t dl, void *arg)
8021*7c478bd9Sstevel@tonic-gate {
8022*7c478bd9Sstevel@tonic-gate 	struct devlink_cb_arg *x = (struct devlink_cb_arg *)arg;
8023*7c478bd9Sstevel@tonic-gate 	const char *path;
8024*7c478bd9Sstevel@tonic-gate 	const char *content;
8025*7c478bd9Sstevel@tonic-gate 
8026*7c478bd9Sstevel@tonic-gate 	if ((path = di_devlink_path(dl)) == NULL ||
8027*7c478bd9Sstevel@tonic-gate 	    (content = di_devlink_content(dl)) == NULL ||
8028*7c478bd9Sstevel@tonic-gate 	    (x->dev_names[x->count] = strdup(path)) == NULL)
8029*7c478bd9Sstevel@tonic-gate 		goto out;
8030*7c478bd9Sstevel@tonic-gate 
8031*7c478bd9Sstevel@tonic-gate 	if ((x->link_contents[x->count] = strdup(content)) == NULL) {
8032*7c478bd9Sstevel@tonic-gate 		free(x->dev_names[x->count]);
8033*7c478bd9Sstevel@tonic-gate 		goto out;
8034*7c478bd9Sstevel@tonic-gate 	}
8035*7c478bd9Sstevel@tonic-gate 
8036*7c478bd9Sstevel@tonic-gate 	x->count++;
8037*7c478bd9Sstevel@tonic-gate 	if (x->count >= MAX_DEV_NAME_COUNT)
8038*7c478bd9Sstevel@tonic-gate 		return (DI_WALK_TERMINATE);
8039*7c478bd9Sstevel@tonic-gate 
8040*7c478bd9Sstevel@tonic-gate 	return (DI_WALK_CONTINUE);
8041*7c478bd9Sstevel@tonic-gate 
8042*7c478bd9Sstevel@tonic-gate out:
8043*7c478bd9Sstevel@tonic-gate 	x->rv = -1;
8044*7c478bd9Sstevel@tonic-gate 	free_dev_names(x);
8045*7c478bd9Sstevel@tonic-gate 	return (DI_WALK_TERMINATE);
8046*7c478bd9Sstevel@tonic-gate }
8047*7c478bd9Sstevel@tonic-gate 
8048*7c478bd9Sstevel@tonic-gate /*
8049*7c478bd9Sstevel@tonic-gate  * Lookup dev name corresponding to the phys_path.
8050*7c478bd9Sstevel@tonic-gate  * phys_path is path to a node or minor node.
8051*7c478bd9Sstevel@tonic-gate  * Returns:
8052*7c478bd9Sstevel@tonic-gate  *	0 with *dev_name set to the dev name
8053*7c478bd9Sstevel@tonic-gate  *		Lookup succeeded and dev_name found
8054*7c478bd9Sstevel@tonic-gate  *	0 with *dev_name set to NULL
8055*7c478bd9Sstevel@tonic-gate  *		Lookup encountered no errors but dev name not found
8056*7c478bd9Sstevel@tonic-gate  *	-1
8057*7c478bd9Sstevel@tonic-gate  *		Lookup failed
8058*7c478bd9Sstevel@tonic-gate  */
8059*7c478bd9Sstevel@tonic-gate static int
8060*7c478bd9Sstevel@tonic-gate lookup_dev_name(char *phys_path, char **dev_name)
8061*7c478bd9Sstevel@tonic-gate {
8062*7c478bd9Sstevel@tonic-gate 	struct devlink_cb_arg cb_arg;
8063*7c478bd9Sstevel@tonic-gate 
8064*7c478bd9Sstevel@tonic-gate 	*dev_name = NULL;
8065*7c478bd9Sstevel@tonic-gate 
8066*7c478bd9Sstevel@tonic-gate 	cb_arg.count = 0;
8067*7c478bd9Sstevel@tonic-gate 	cb_arg.rv = 0;
8068*7c478bd9Sstevel@tonic-gate 	(void) di_devlink_cache_walk(devlink_cache, NULL, phys_path,
8069*7c478bd9Sstevel@tonic-gate 	    DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8070*7c478bd9Sstevel@tonic-gate 
8071*7c478bd9Sstevel@tonic-gate 	if (cb_arg.rv == -1)
8072*7c478bd9Sstevel@tonic-gate 		return (-1);
8073*7c478bd9Sstevel@tonic-gate 
8074*7c478bd9Sstevel@tonic-gate 	if (cb_arg.count > 0) {
8075*7c478bd9Sstevel@tonic-gate 		*dev_name = strdup(cb_arg.dev_names[0]);
8076*7c478bd9Sstevel@tonic-gate 		free_dev_names(&cb_arg);
8077*7c478bd9Sstevel@tonic-gate 		if (*dev_name == NULL)
8078*7c478bd9Sstevel@tonic-gate 			return (-1);
8079*7c478bd9Sstevel@tonic-gate 	}
8080*7c478bd9Sstevel@tonic-gate 
8081*7c478bd9Sstevel@tonic-gate 	return (0);
8082*7c478bd9Sstevel@tonic-gate }
8083*7c478bd9Sstevel@tonic-gate 
8084*7c478bd9Sstevel@tonic-gate static char *
8085*7c478bd9Sstevel@tonic-gate lookup_disk_dev_name(char *node_path)
8086*7c478bd9Sstevel@tonic-gate {
8087*7c478bd9Sstevel@tonic-gate 	struct devlink_cb_arg cb_arg;
8088*7c478bd9Sstevel@tonic-gate 	char *dev_name = NULL;
8089*7c478bd9Sstevel@tonic-gate 	int i;
8090*7c478bd9Sstevel@tonic-gate 	char *p;
8091*7c478bd9Sstevel@tonic-gate 	int len1, len2;
8092*7c478bd9Sstevel@tonic-gate 
8093*7c478bd9Sstevel@tonic-gate #define	DEV_RDSK	"/dev/rdsk/"
8094*7c478bd9Sstevel@tonic-gate #define	DISK_RAW_MINOR	",raw"
8095*7c478bd9Sstevel@tonic-gate 
8096*7c478bd9Sstevel@tonic-gate 	cb_arg.count = 0;
8097*7c478bd9Sstevel@tonic-gate 	cb_arg.rv = 0;
8098*7c478bd9Sstevel@tonic-gate 	(void) di_devlink_cache_walk(devlink_cache, NULL, node_path,
8099*7c478bd9Sstevel@tonic-gate 	    DI_PRIMARY_LINK, &cb_arg, devlink_cb);
8100*7c478bd9Sstevel@tonic-gate 
8101*7c478bd9Sstevel@tonic-gate 	if (cb_arg.rv == -1 || cb_arg.count == 0)
8102*7c478bd9Sstevel@tonic-gate 		return (NULL);
8103*7c478bd9Sstevel@tonic-gate 
8104*7c478bd9Sstevel@tonic-gate 	/* first try lookup based on /dev/rdsk name */
8105*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < cb_arg.count; i++) {
8106*7c478bd9Sstevel@tonic-gate 		if (strncmp(cb_arg.dev_names[i], DEV_RDSK,
8107*7c478bd9Sstevel@tonic-gate 		    sizeof (DEV_RDSK) - 1) == 0) {
8108*7c478bd9Sstevel@tonic-gate 			dev_name = strdup(cb_arg.dev_names[i]);
8109*7c478bd9Sstevel@tonic-gate 			break;
8110*7c478bd9Sstevel@tonic-gate 		}
8111*7c478bd9Sstevel@tonic-gate 	}
8112*7c478bd9Sstevel@tonic-gate 
8113*7c478bd9Sstevel@tonic-gate 	if (dev_name == NULL) {
8114*7c478bd9Sstevel@tonic-gate 		/* now try lookup based on a minor name ending with ",raw" */
8115*7c478bd9Sstevel@tonic-gate 		len1 = sizeof (DISK_RAW_MINOR) - 1;
8116*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < cb_arg.count; i++) {
8117*7c478bd9Sstevel@tonic-gate 			len2 = strlen(cb_arg.link_contents[i]);
8118*7c478bd9Sstevel@tonic-gate 			if (len2 >= len1 &&
8119*7c478bd9Sstevel@tonic-gate 			    strcmp(cb_arg.link_contents[i] + len2 - len1,
8120*7c478bd9Sstevel@tonic-gate 			    DISK_RAW_MINOR) == 0) {
8121*7c478bd9Sstevel@tonic-gate 				dev_name = strdup(cb_arg.dev_names[i]);
8122*7c478bd9Sstevel@tonic-gate 				break;
8123*7c478bd9Sstevel@tonic-gate 			}
8124*7c478bd9Sstevel@tonic-gate 		}
8125*7c478bd9Sstevel@tonic-gate 	}
8126*7c478bd9Sstevel@tonic-gate 
8127*7c478bd9Sstevel@tonic-gate 	free_dev_names(&cb_arg);
8128*7c478bd9Sstevel@tonic-gate 
8129*7c478bd9Sstevel@tonic-gate 	if (strlen(dev_name) == 0) {
8130*7c478bd9Sstevel@tonic-gate 		free(dev_name);
8131*7c478bd9Sstevel@tonic-gate 		return (NULL);
8132*7c478bd9Sstevel@tonic-gate 	}
8133*7c478bd9Sstevel@tonic-gate 
8134*7c478bd9Sstevel@tonic-gate 	/* if the name contains slice or partition number strip it */
8135*7c478bd9Sstevel@tonic-gate 	p = dev_name + strlen(dev_name) - 1;
8136*7c478bd9Sstevel@tonic-gate 	if (isdigit(*p)) {
8137*7c478bd9Sstevel@tonic-gate 		while (p != dev_name && isdigit(*p))
8138*7c478bd9Sstevel@tonic-gate 			p--;
8139*7c478bd9Sstevel@tonic-gate 		if (*p == 's' || *p == 'p')
8140*7c478bd9Sstevel@tonic-gate 			*p = '\0';
8141*7c478bd9Sstevel@tonic-gate 	}
8142*7c478bd9Sstevel@tonic-gate 
8143*7c478bd9Sstevel@tonic-gate 	return (dev_name);
8144*7c478bd9Sstevel@tonic-gate }
8145*7c478bd9Sstevel@tonic-gate 
8146*7c478bd9Sstevel@tonic-gate static char *
8147*7c478bd9Sstevel@tonic-gate lookup_network_dev_name(char *node_path, char *driver_name)
8148*7c478bd9Sstevel@tonic-gate {
8149*7c478bd9Sstevel@tonic-gate 	char *dev_name = NULL;
8150*7c478bd9Sstevel@tonic-gate 	char phys_path[MAXPATHLEN];
8151*7c478bd9Sstevel@tonic-gate 
8152*7c478bd9Sstevel@tonic-gate 	if (lookup_dev_name(node_path, &dev_name) == -1)
8153*7c478bd9Sstevel@tonic-gate 		return (NULL);
8154*7c478bd9Sstevel@tonic-gate 
8155*7c478bd9Sstevel@tonic-gate 	if (dev_name == NULL) {
8156*7c478bd9Sstevel@tonic-gate 		/* dlpi style-2 only interface */
8157*7c478bd9Sstevel@tonic-gate 		(void) snprintf(phys_path, sizeof (phys_path),
8158*7c478bd9Sstevel@tonic-gate 		    "/pseudo/clone@0:%s", driver_name);
8159*7c478bd9Sstevel@tonic-gate 		if (lookup_dev_name(phys_path, &dev_name) == -1 ||
8160*7c478bd9Sstevel@tonic-gate 		    dev_name == NULL)
8161*7c478bd9Sstevel@tonic-gate 			return (NULL);
8162*7c478bd9Sstevel@tonic-gate 	}
8163*7c478bd9Sstevel@tonic-gate 
8164*7c478bd9Sstevel@tonic-gate 	return (dev_name);
8165*7c478bd9Sstevel@tonic-gate }
8166*7c478bd9Sstevel@tonic-gate 
8167*7c478bd9Sstevel@tonic-gate /*
8168*7c478bd9Sstevel@tonic-gate  * Build an nvlist containing all attributes for devfs events.
8169*7c478bd9Sstevel@tonic-gate  * Returns nvlist pointer on success, NULL on failure.
8170*7c478bd9Sstevel@tonic-gate  */
8171*7c478bd9Sstevel@tonic-gate static nvlist_t *
8172*7c478bd9Sstevel@tonic-gate build_event_attributes(char *class, char *subclass, char *node_path,
8173*7c478bd9Sstevel@tonic-gate     di_node_t node, char *driver_name, int instance)
8174*7c478bd9Sstevel@tonic-gate {
8175*7c478bd9Sstevel@tonic-gate 	nvlist_t *nvl;
8176*7c478bd9Sstevel@tonic-gate 	int err = 0;
8177*7c478bd9Sstevel@tonic-gate 	di_prop_t prop;
8178*7c478bd9Sstevel@tonic-gate 	int count;
8179*7c478bd9Sstevel@tonic-gate 	char *prop_name;
8180*7c478bd9Sstevel@tonic-gate 	int x;
8181*7c478bd9Sstevel@tonic-gate 	char *dev_name = NULL;
8182*7c478bd9Sstevel@tonic-gate 	int dev_name_lookup_err = 0;
8183*7c478bd9Sstevel@tonic-gate 
8184*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0) {
8185*7c478bd9Sstevel@tonic-gate 		nvl = NULL;
8186*7c478bd9Sstevel@tonic-gate 		goto out;
8187*7c478bd9Sstevel@tonic-gate 	}
8188*7c478bd9Sstevel@tonic-gate 
8189*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_int32(nvl, EV_VERSION, EV_V1)) != 0)
8190*7c478bd9Sstevel@tonic-gate 		goto out;
8191*7c478bd9Sstevel@tonic-gate 
8192*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_string(nvl, DEV_PHYS_PATH, node_path)) != 0)
8193*7c478bd9Sstevel@tonic-gate 		goto out;
8194*7c478bd9Sstevel@tonic-gate 
8195*7c478bd9Sstevel@tonic-gate 	if (strcmp(class, EC_DEV_ADD) != 0 &&
8196*7c478bd9Sstevel@tonic-gate 	    strcmp(class, EC_DEV_REMOVE) != 0)
8197*7c478bd9Sstevel@tonic-gate 		return (nvl);
8198*7c478bd9Sstevel@tonic-gate 
8199*7c478bd9Sstevel@tonic-gate 	if (driver_name == NULL || instance == -1)
8200*7c478bd9Sstevel@tonic-gate 		goto out;
8201*7c478bd9Sstevel@tonic-gate 
8202*7c478bd9Sstevel@tonic-gate 	if (strcmp(subclass, ESC_DISK) == 0) {
8203*7c478bd9Sstevel@tonic-gate 		if ((dev_name = lookup_disk_dev_name(node_path)) == NULL) {
8204*7c478bd9Sstevel@tonic-gate 			dev_name_lookup_err = 1;
8205*7c478bd9Sstevel@tonic-gate 			goto out;
8206*7c478bd9Sstevel@tonic-gate 		}
8207*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(subclass, ESC_NETWORK) == 0) {
8208*7c478bd9Sstevel@tonic-gate 		if ((dev_name = lookup_network_dev_name(node_path, driver_name))
8209*7c478bd9Sstevel@tonic-gate 		    == NULL) {
8210*7c478bd9Sstevel@tonic-gate 			dev_name_lookup_err = 1;
8211*7c478bd9Sstevel@tonic-gate 			goto out;
8212*7c478bd9Sstevel@tonic-gate 		}
8213*7c478bd9Sstevel@tonic-gate 	}
8214*7c478bd9Sstevel@tonic-gate 
8215*7c478bd9Sstevel@tonic-gate 	if (dev_name) {
8216*7c478bd9Sstevel@tonic-gate 		if ((err = nvlist_add_string(nvl, DEV_NAME, dev_name)) != 0)
8217*7c478bd9Sstevel@tonic-gate 			goto out;
8218*7c478bd9Sstevel@tonic-gate 		free(dev_name);
8219*7c478bd9Sstevel@tonic-gate 		dev_name = NULL;
8220*7c478bd9Sstevel@tonic-gate 	}
8221*7c478bd9Sstevel@tonic-gate 
8222*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_string(nvl, DEV_DRIVER_NAME, driver_name)) != 0)
8223*7c478bd9Sstevel@tonic-gate 		goto out;
8224*7c478bd9Sstevel@tonic-gate 
8225*7c478bd9Sstevel@tonic-gate 	if ((err = nvlist_add_int32(nvl, DEV_INSTANCE, instance)) != 0)
8226*7c478bd9Sstevel@tonic-gate 		goto out;
8227*7c478bd9Sstevel@tonic-gate 
8228*7c478bd9Sstevel@tonic-gate 	if (strcmp(class, EC_DEV_ADD) == 0) {
8229*7c478bd9Sstevel@tonic-gate 		/* add properties */
8230*7c478bd9Sstevel@tonic-gate 		count = 0;
8231*7c478bd9Sstevel@tonic-gate 		for (prop = di_prop_next(node, DI_PROP_NIL);
8232*7c478bd9Sstevel@tonic-gate 		    prop != DI_PROP_NIL && count < MAX_PROP_COUNT;
8233*7c478bd9Sstevel@tonic-gate 		    prop = di_prop_next(node, prop)) {
8234*7c478bd9Sstevel@tonic-gate 
8235*7c478bd9Sstevel@tonic-gate 			if (di_prop_devt(prop) != DDI_DEV_T_NONE)
8236*7c478bd9Sstevel@tonic-gate 				continue;
8237*7c478bd9Sstevel@tonic-gate 
8238*7c478bd9Sstevel@tonic-gate 			if ((x = add_property(nvl, prop)) == 0)
8239*7c478bd9Sstevel@tonic-gate 				count++;
8240*7c478bd9Sstevel@tonic-gate 			else if (x == -1) {
8241*7c478bd9Sstevel@tonic-gate 				if ((prop_name = di_prop_name(prop)) == NULL)
8242*7c478bd9Sstevel@tonic-gate 					prop_name = "";
8243*7c478bd9Sstevel@tonic-gate 				err_print(PROP_ADD_FAILED, prop_name);
8244*7c478bd9Sstevel@tonic-gate 				goto out;
8245*7c478bd9Sstevel@tonic-gate 			}
8246*7c478bd9Sstevel@tonic-gate 		}
8247*7c478bd9Sstevel@tonic-gate 	}
8248*7c478bd9Sstevel@tonic-gate 
8249*7c478bd9Sstevel@tonic-gate 	return (nvl);
8250*7c478bd9Sstevel@tonic-gate 
8251*7c478bd9Sstevel@tonic-gate out:
8252*7c478bd9Sstevel@tonic-gate 	if (nvl)
8253*7c478bd9Sstevel@tonic-gate 		nvlist_free(nvl);
8254*7c478bd9Sstevel@tonic-gate 
8255*7c478bd9Sstevel@tonic-gate 	if (dev_name)
8256*7c478bd9Sstevel@tonic-gate 		free(dev_name);
8257*7c478bd9Sstevel@tonic-gate 
8258*7c478bd9Sstevel@tonic-gate 	if (dev_name_lookup_err)
8259*7c478bd9Sstevel@tonic-gate 		err_print(DEV_NAME_LOOKUP_FAILED, node_path);
8260*7c478bd9Sstevel@tonic-gate 	else
8261*7c478bd9Sstevel@tonic-gate 		err_print(BUILD_EVENT_ATTR_FAILED, (err) ? strerror(err) : "");
8262*7c478bd9Sstevel@tonic-gate 	return (NULL);
8263*7c478bd9Sstevel@tonic-gate }
8264*7c478bd9Sstevel@tonic-gate 
8265*7c478bd9Sstevel@tonic-gate static void
8266*7c478bd9Sstevel@tonic-gate log_event(char *class, char *subclass, nvlist_t *nvl)
8267*7c478bd9Sstevel@tonic-gate {
8268*7c478bd9Sstevel@tonic-gate 	sysevent_id_t eid;
8269*7c478bd9Sstevel@tonic-gate 
8270*7c478bd9Sstevel@tonic-gate 	if (sysevent_post_event(class, subclass, "SUNW", DEVFSADMD,
8271*7c478bd9Sstevel@tonic-gate 	    nvl, &eid) != 0) {
8272*7c478bd9Sstevel@tonic-gate 		err_print(LOG_EVENT_FAILED, strerror(errno));
8273*7c478bd9Sstevel@tonic-gate 	}
8274*7c478bd9Sstevel@tonic-gate }
8275*7c478bd9Sstevel@tonic-gate 
8276*7c478bd9Sstevel@tonic-gate static void
8277*7c478bd9Sstevel@tonic-gate build_and_log_event(char *class, char *subclass, char *node_path,
8278*7c478bd9Sstevel@tonic-gate     di_node_t node)
8279*7c478bd9Sstevel@tonic-gate {
8280*7c478bd9Sstevel@tonic-gate 	nvlist_t *nvl;
8281*7c478bd9Sstevel@tonic-gate 
8282*7c478bd9Sstevel@tonic-gate 	if (node != DI_NODE_NIL)
8283*7c478bd9Sstevel@tonic-gate 		nvl = build_event_attributes(class, subclass, node_path, node,
8284*7c478bd9Sstevel@tonic-gate 		    di_driver_name(node), di_instance(node));
8285*7c478bd9Sstevel@tonic-gate 	else
8286*7c478bd9Sstevel@tonic-gate 		nvl = build_event_attributes(class, subclass, node_path, node,
8287*7c478bd9Sstevel@tonic-gate 		    NULL, -1);
8288*7c478bd9Sstevel@tonic-gate 
8289*7c478bd9Sstevel@tonic-gate 	if (nvl) {
8290*7c478bd9Sstevel@tonic-gate 		log_event(class, subclass, nvl);
8291*7c478bd9Sstevel@tonic-gate 		nvlist_free(nvl);
8292*7c478bd9Sstevel@tonic-gate 	}
8293*7c478bd9Sstevel@tonic-gate }
8294