4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2016 Toomas Soome <tsoome@me.com>
24 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
25 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
29 * Devfsadm replaces drvconfig, audlinks, disks, tapes, ports, devlinks
30 * as a general purpose device administrative utility. It creates
31 * devices special files in /devices and logical links in /dev, and
32 * coordinates updates to /etc/path_to_instance with the kernel. It
33 * operates in both command line mode to handle user or script invoked
34 * reconfiguration updates, and operates in daemon mode to handle dynamic
35 * reconfiguration for hotplugging support.
40 #include <tsol/label.h>
41 #include <bsm/devices.h>
42 #include <bsm/devalloc.h>
44 #include <sys/param.h>
45 #include <bsm/libbsm.h>
47 #include "devfsadm_impl.h"
49 /* externs from devalloc.c */
50 extern void _reset_devalloc(int);
51 extern void _update_devalloc_db(devlist_t
*, int, int, char *, char *);
52 extern int _da_check_for_usb(char *, char *);
54 /* create or remove nodes or links. unset with -n */
55 static int file_mods
= TRUE
;
57 /* cleanup mode. Set with -C */
58 static int cleanup
= FALSE
;
60 /* devlinks -d compatibility */
61 static int devlinks_debug
= FALSE
;
63 /* flag to check if system is labeled */
64 int system_labeled
= FALSE
;
66 /* flag to enable/disable device allocation with -e/-d */
67 static int devalloc_flag
= 0;
69 /* flag that indicates if device allocation is on or not */
70 static int devalloc_is_on
= 0;
72 /* flag to update device allocation database for this device type */
73 static int update_devdb
= 0;
76 * devices to be deallocated with -d :
77 * audio, floppy, cd, floppy, tape, rmdisk.
79 static char *devalloc_list
[10] = {DDI_NT_AUDIO
, DDI_NT_CD
, DDI_NT_CD_CHAN
,
80 DDI_NT_FD
, DDI_NT_TAPE
, DDI_NT_BLOCK_CHAN
,
81 DDI_NT_UGEN
, DDI_NT_USB_ATTACHMENT_POINT
,
82 DDI_NT_SCSI_NEXUS
, NULL
};
84 /* list of allocatable devices */
85 static devlist_t devlist
;
87 /* load a single driver only. set with -i */
88 static int single_drv
= FALSE
;
89 static char *driver
= NULL
;
91 /* attempt to load drivers or defer attach nodes */
92 static int load_attach_drv
= TRUE
;
94 /* reload all driver.conf files */
95 static int update_all_drivers
= FALSE
;
97 /* set if invoked via /usr/lib/devfsadm/devfsadmd */
98 static int daemon_mode
= FALSE
;
100 /* set if event_handler triggered */
101 int event_driven
= FALSE
;
103 /* output directed to syslog during daemon mode if set */
104 static int logflag
= FALSE
;
106 /* build links in /dev. -x to turn off */
107 static int build_dev
= TRUE
;
109 /* build nodes in /devices. -y to turn off */
110 static int build_devices
= TRUE
;
113 static int flush_path_to_inst_enable
= TRUE
;
115 /* variables used for path_to_inst flushing */
116 static int inst_count
= 0;
117 static mutex_t count_lock
;
120 /* variables for minor_fini thread */
121 static mutex_t minor_fini_mutex
;
122 static int minor_fini_canceled
= TRUE
;
123 static int minor_fini_delayed
= FALSE
;
124 static cond_t minor_fini_cv
;
125 static int minor_fini_timeout
= MINOR_FINI_TIMEOUT_DEFAULT
;
127 /* single-threads /dev modification */
128 static sema_t dev_sema
;
130 /* the program we were invoked as; ie argv[0] */
133 /* pointers to create/remove link lists */
134 static create_list_t
*create_head
= NULL
;
135 static remove_list_t
*remove_head
= NULL
;
137 /* supports the class -c option */
138 static char **classes
= NULL
;
139 static int num_classes
= 0;
141 /* used with verbose option -v or -V */
142 static int num_verbose
= 0;
143 static char **verbose
= NULL
;
145 static struct mperm
*minor_perms
= NULL
;
146 static driver_alias_t
*driver_aliases
= NULL
;
148 /* set if -r alternate root given */
149 static char *root_dir
= "";
151 /* /devices or <rootdir>/devices */
152 static char *devices_dir
= DEVICES
;
154 /* /dev or <rootdir>/dev */
155 static char *dev_dir
= DEV
;
157 /* /etc/dev or <rootdir>/etc/dev */
158 static char *etc_dev_dir
= ETCDEV
;
161 * writable root (for lock files and doors during install).
162 * This is also root dir for /dev attr dir during install.
164 static char *attr_root
= NULL
;
166 /* /etc/path_to_inst unless -p used */
167 static char *inst_file
= INSTANCE_FILE
;
169 /* /usr/lib/devfsadm/linkmods unless -l used */
170 static char *module_dirs
= MODULE_DIRS
;
172 /* default uid/gid used if /etc/minor_perm entry not found */
173 static uid_t root_uid
;
174 static gid_t sys_gid
;
176 /* /etc/devlink.tab unless devlinks -t used */
177 static char *devlinktab_file
= NULL
;
179 /* File and data structure to reserve enumerate IDs */
180 static char *enumerate_file
= ENUMERATE_RESERVED
;
181 static enumerate_file_t
*enumerate_reserved
= NULL
;
183 /* set if /dev link is new. speeds up rm_stale_links */
184 static int linknew
= TRUE
;
186 /* variables for devlink.tab compat processing */
187 static devlinktab_list_t
*devlinktab_list
= NULL
;
188 static unsigned int devlinktab_line
= 0;
190 /* cache head for devfsadm_enumerate*() functions */
191 static numeral_set_t
*head_numeral_set
= NULL
;
193 /* list list of devfsadm modules */
194 static module_t
*module_head
= NULL
;
196 /* name_to_major list used in utility function */
197 static n2m_t
*n2m_list
= NULL
;
199 /* cache of some links used for performance */
200 static linkhead_t
*headlinkhead
= NULL
;
202 /* locking variables to prevent multiples writes to /dev */
203 static int hold_dev_lock
= FALSE
;
204 static int hold_daemon_lock
= FALSE
;
205 static int dev_lock_fd
;
206 static int daemon_lock_fd
;
207 static char dev_lockfile
[PATH_MAX
+ 1];
208 static char daemon_lockfile
[PATH_MAX
+ 1];
210 /* last devinfo node/minor processed. used for performance */
211 static di_node_t lnode
;
212 static di_minor_t lminor
;
213 static char lphy_path
[PATH_MAX
+ 1] = {""};
215 /* Globals used by the link database */
216 static di_devlink_handle_t devlink_cache
;
217 static int update_database
= FALSE
;
219 /* Globals used to set logindev perms */
220 static struct login_dev
*login_dev_cache
= NULL
;
221 static int login_dev_enable
= FALSE
;
223 /* Global to use devinfo snapshot cache */
224 static int use_snapshot_cache
= FALSE
;
226 /* Global for no-further-processing hash */
227 static item_t
**nfp_hash
;
228 static mutex_t nfp_mutex
= DEFAULTMUTEX
;
231 * Directories not removed even when empty. They are packaged, or may
232 * be referred to from a non-global zone. The dirs must be listed in
233 * canonical form i.e. without leading "/dev/"
235 static char *sticky_dirs
[] =
236 {"dsk", "rdsk", "term", "lofi", "rlofi", NULL
};
238 /* Devname globals */
239 static int lookup_door_fd
= -1;
240 static char *lookup_door_path
;
242 static void load_dev_acl(void);
243 static void update_drvconf(major_t
, int);
244 static void check_reconfig_state(void);
245 static int s_stat(const char *, struct stat
*);
247 static int is_blank(char *);
249 /* sysevent queue related globals */
250 static mutex_t syseventq_mutex
= DEFAULTMUTEX
;
251 static syseventq_t
*syseventq_front
;
252 static syseventq_t
*syseventq_back
;
253 static void process_syseventq();
255 static di_node_t devi_root_node
= DI_NODE_NIL
;
258 main(int argc
, char *argv
[])
264 (void) setlocale(LC_ALL
, "");
265 (void) textdomain(TEXT_DOMAIN
);
267 if ((prog
= strrchr(argv
[0], '/')) == NULL
) {
274 err_print(MUST_BE_ROOT
);
279 if (getzoneid() != GLOBAL_ZONEID
) {
280 err_print(MUST_BE_GLOBAL_ZONE
);
285 * Close all files except stdin/stdout/stderr
289 if ((pw
= getpwnam(DEFAULT_DEV_USER
)) != NULL
) {
290 root_uid
= pw
->pw_uid
;
292 err_print(CANT_FIND_USER
, DEFAULT_DEV_USER
);
293 root_uid
= (uid_t
)0; /* assume 0 is root */
296 /* the default group is sys */
298 if ((gp
= getgrnam(DEFAULT_DEV_GROUP
)) != NULL
) {
299 sys_gid
= gp
->gr_gid
;
301 err_print(CANT_FIND_GROUP
, DEFAULT_DEV_GROUP
);
302 sys_gid
= (gid_t
)3; /* assume 3 is sys */
307 system_labeled
= is_system_labeled();
308 if (system_labeled
== FALSE
) {
310 * is_system_labeled() will return false in case we are
311 * starting before the first reboot after Trusted Extensions
312 * is enabled. Check the setting in /etc/system to see if
313 * TX is enabled (even if not yet booted).
315 if (defopen("/etc/system") == 0) {
316 if (defread("set sys_labeling=1") != NULL
)
317 system_labeled
= TRUE
;
319 /* close defaults file */
320 (void) defopen(NULL
);
324 * Check if device allocation is enabled.
326 devalloc_is_on
= (da_is_on() == 1) ? 1 : 0;
329 if (system_labeled
== FALSE
) {
332 /* test hook: see also mkdevalloc.c and allocate.c */
333 system_labeled
= is_system_labeled_debug(&tx_stat
);
337 parse_args(argc
, argv
);
339 (void) sema_init(&dev_sema
, 1, USYNC_THREAD
, NULL
);
341 /* Initialize device allocation list */
342 devlist
.audio
= devlist
.cd
= devlist
.floppy
= devlist
.tape
=
343 devlist
.rmdisk
= NULL
;
345 if (daemon_mode
== TRUE
) {
347 * Build /dev and /devices before daemonizing if
348 * reconfig booting and daemon invoked with alternate
349 * root. This is to support install.
351 if (getenv(RECONFIG_BOOT
) != NULL
&& root_dir
[0] != '\0') {
352 vprint(INFO_MID
, CONFIGURING
);
354 update_drvconf((major_t
)-1, 0);
355 process_devinfo_tree();
356 (void) modctl(MODSETMINIROOT
);
360 * fork before detaching from tty in order to print error
361 * message if unable to acquire file lock. locks not preserved
362 * across forks. Even under debug we want to fork so that
363 * when executed at boot we don't hang.
370 /* set directory to / so it coredumps there */
371 if (chdir("/") == -1) {
372 err_print(CHROOT_FAILED
, strerror(errno
));
375 /* only one daemon can run at a time */
376 if ((pid
= enter_daemon_lock()) == getpid()) {
378 (void) cond_init(&cv
, USYNC_THREAD
, 0);
379 (void) mutex_init(&count_lock
, USYNC_THREAD
, 0);
380 if (thr_create(NULL
, NULL
,
381 (void *(*)(void *))instance_flush_thread
,
382 NULL
, THR_DETACHED
, NULL
) != 0) {
383 err_print(CANT_CREATE_THREAD
, "daemon",
389 /* start the minor_fini_thread */
390 (void) mutex_init(&minor_fini_mutex
, USYNC_THREAD
, 0);
391 (void) cond_init(&minor_fini_cv
, USYNC_THREAD
, 0);
392 if (thr_create(NULL
, NULL
,
393 (void *(*)(void *))minor_fini_thread
,
394 NULL
, THR_DETACHED
, NULL
)) {
395 err_print(CANT_CREATE_THREAD
, "minor_fini",
403 * logindevperms need only be set
404 * in daemon mode and when root dir is "/".
406 if (root_dir
[0] == '\0')
407 login_dev_enable
= TRUE
;
412 err_print(DAEMON_RUNNING
, pid
);
417 /* not a daemon, so just build /dev and /devices */
420 * If turning off device allocation, load the
421 * minor_perm file because process_devinfo_tree() will
422 * need this in order to reset the permissions of the
425 if (devalloc_flag
== DA_OFF
) {
426 read_minor_perm_file();
429 process_devinfo_tree();
430 if (devalloc_flag
!= 0)
431 /* Enable/disable device allocation */
432 _reset_devalloc(devalloc_flag
);
438 update_drvconf(major_t major
, int flags
)
440 if (modctl(MODLOADDRVCONF
, major
, flags
) != 0)
441 err_print(gettext("update_drvconf failed for major %d\n"),
448 if (load_devpolicy() != 0)
449 err_print(gettext("device policy load failed\n"));
450 load_minor_perm_file();
454 * As devfsadm is run early in boot to provide the kernel with
455 * minor_perm info, we might as well check for reconfig at the
456 * same time to avoid running devfsadm twice. This gets invoked
457 * earlier than the env variable RECONFIG_BOOT is set up.
460 check_reconfig_state()
464 if (s_stat("/reconfigure", &sb
) == 0) {
465 (void) modctl(MODDEVNAME
, MODDEVNAME_RECONFIG
, 0);
473 * Inform /dev that system is available, that
474 * implicit reconfig can now be performed.
476 (void) modctl(MODDEVNAME
, MODDEVNAME_SYSAVAIL
, 0);
486 lock_root
= attr_root
? attr_root
: root_dir
;
488 len
= strlen(lock_root
) + strlen(ETCDEV
) + 1;
489 etc_dev_dir
= s_malloc(len
);
490 (void) snprintf(etc_dev_dir
, len
, "%s%s", lock_root
, ETCDEV
);
492 if (s_stat(etc_dev_dir
, &sb
) != 0) {
493 s_mkdirp(etc_dev_dir
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
);
494 } else if (!S_ISDIR(sb
.st_mode
)) {
495 err_print(NOT_DIR
, etc_dev_dir
);
503 * Parse arguments for all 6 programs handled from devfsadm.
506 parse_args(int argc
, char *argv
[])
509 char get_linkcompat_opts
= FALSE
;
516 int force_flag
= FALSE
;
517 struct aliases
*ap
= NULL
;
518 struct aliases
*a_head
= NULL
;
519 struct aliases
*a_tail
= NULL
;
522 (void) bzero(&mc
, sizeof (mc
));
524 if (strcmp(prog
, DISKS
) == 0) {
525 compat_class
= "disk";
526 get_linkcompat_opts
= TRUE
;
528 } else if (strcmp(prog
, TAPES
) == 0) {
529 compat_class
= "tape";
530 get_linkcompat_opts
= TRUE
;
532 } else if (strcmp(prog
, PORTS
) == 0) {
533 compat_class
= "port";
534 get_linkcompat_opts
= TRUE
;
536 } else if (strcmp(prog
, AUDLINKS
) == 0) {
537 compat_class
= "audio";
538 get_linkcompat_opts
= TRUE
;
540 } else if (strcmp(prog
, DEVLINKS
) == 0) {
541 devlinktab_file
= DEVLINKTAB_FILE
;
543 build_devices
= FALSE
;
544 load_attach_drv
= FALSE
;
546 while ((opt
= getopt(argc
, argv
, "dnr:st:vV:")) != EOF
) {
550 flush_path_to_inst_enable
= FALSE
;
551 devlinks_debug
= TRUE
;
554 /* prevent driver loading and deferred attach */
555 load_attach_drv
= FALSE
;
558 set_root_devices_dev_dir(optarg
);
559 if (zone_pathcheck(root_dir
) !=
566 * suppress. don't create/remove links/nodes
567 * useful with -v or -V
570 flush_path_to_inst_enable
= FALSE
;
573 /* supply a non-default table file */
574 devlinktab_file
= optarg
;
577 /* documented verbose flag */
578 add_verbose_id(VERBOSE_MID
);
581 /* undocumented for extra verbose levels */
582 add_verbose_id(optarg
);
594 } else if (strcmp(prog
, DRVCONFIG
) == 0) {
599 getopt(argc
, argv
, "a:bc:dfi:m:np:R:r:suvV:x")) != EOF
) {
602 ap
= calloc(sizeof (struct aliases
), 1);
603 ap
->a_name
= dequote(optarg
);
604 len
= strlen(ap
->a_name
) + 1;
605 if (len
> MAXMODCONFNAME
) {
606 err_print(ALIAS_TOO_LONG
,
607 MAXMODCONFNAME
, ap
->a_name
);
612 if (a_tail
== NULL
) {
625 (void) strcpy(mc
.drvclass
, optarg
);
629 * need to keep for compatibility, but
638 (void) strcpy(mc
.drvname
, optarg
);
639 driver
= s_strdup(optarg
);
642 mc
.major
= atoi(optarg
);
645 /* prevent driver loading and deferred attach */
646 load_attach_drv
= FALSE
;
649 /* specify alternate path_to_inst file */
650 inst_file
= s_strdup(optarg
);
654 * Private flag for suninstall to populate
655 * device information on the installed root.
657 root_dir
= s_strdup(optarg
);
658 if (zone_pathcheck(root_dir
) !=
660 devfsadm_exit(devfsadm_copy());
664 devices_dir
= s_strdup(optarg
);
665 if (zone_pathcheck(devices_dir
) !=
672 * suppress. don't create nodes
673 * useful with -v or -V
676 flush_path_to_inst_enable
= FALSE
;
680 * Invoked via update_drv(1m) to update
681 * the kernel's driver/alias binding
682 * when removing one or more aliases.
687 /* documented verbose flag */
688 add_verbose_id(VERBOSE_MID
);
691 /* undocumented for extra verbose levels */
692 add_verbose_id(optarg
);
707 if ((mc
.major
== -1) || (mc
.drvname
[0] == NULL
)) {
708 err_print(MAJOR_AND_B_FLAG
);
714 mc
.flags
|= MOD_UNBIND_OVERRIDE
;
716 mc
.flags
|= MOD_ADDMAJBIND_UPDATE
;
717 mc
.num_aliases
= num_aliases
;
719 retval
= modctl((config
== TRUE
) ? MODADDMAJBIND
:
720 MODREMDRVALIAS
, NULL
, (caddr_t
)&mc
);
722 err_print((config
== TRUE
) ? MODCTL_ADDMAJBIND
:
725 devfsadm_exit(retval
);
729 } else if ((strcmp(prog
, DEVFSADM
) == 0) ||
730 (strcmp(prog
, DEVFSADMD
) == 0)) {
731 char *zonename
= NULL
;
732 int init_drvconf
= 0;
735 int init_sysavail
= 0;
737 if (strcmp(prog
, DEVFSADMD
) == 0) {
741 devlinktab_file
= DEVLINKTAB_FILE
;
743 while ((opt
= getopt(argc
, argv
,
744 "a:Cc:deIi:l:np:PR:r:sSt:uvV:x:")) != EOF
) {
745 if (opt
== 'I' || opt
== 'P' || opt
== 'S') {
749 if (init_perm
|| init_drvconf
|| init_sysavail
)
755 attr_root
= s_strdup(optarg
);
762 classes
= s_realloc(classes
,
763 num_classes
* sizeof (char *));
764 classes
[num_classes
- 1] = optarg
;
767 if (daemon_mode
== FALSE
) {
769 * Device allocation to be disabled.
771 devalloc_flag
= DA_OFF
;
776 if (daemon_mode
== FALSE
) {
778 * Device allocation to be enabled.
780 devalloc_flag
= DA_ON
;
784 case 'I': /* update kernel driver.conf cache */
785 if (daemon_mode
== TRUE
)
791 driver
= s_strdup(optarg
);
794 /* specify an alternate module load path */
795 module_dirs
= s_strdup(optarg
);
798 /* prevent driver loading and deferred attach */
799 load_attach_drv
= FALSE
;
802 /* specify alternate path_to_inst file */
803 inst_file
= s_strdup(optarg
);
806 if (daemon_mode
== TRUE
)
808 /* load minor_perm and device_policy */
813 * Private flag for suninstall to populate
814 * device information on the installed root.
816 root_dir
= s_strdup(optarg
);
817 devfsadm_exit(devfsadm_copy());
821 set_root_devices_dev_dir(optarg
);
825 * suppress. don't create/remove links/nodes
826 * useful with -v or -V
829 flush_path_to_inst_enable
= FALSE
;
832 if (daemon_mode
== TRUE
)
837 devlinktab_file
= optarg
;
839 case 'u': /* complete configuration after */
840 /* adding a driver update-only */
841 if (daemon_mode
== TRUE
)
843 update_all_drivers
= TRUE
;
846 /* documented verbose flag */
847 add_verbose_id(VERBOSE_MID
);
850 /* undocumented: specify verbose lvl */
851 add_verbose_id(optarg
);
855 * x is the "private switch" option. The
856 * goal is to not suck up all the other
859 if (strcmp(optarg
, "update_devlinksdb") == 0) {
860 update_database
= TRUE
;
861 } else if (strcmp(optarg
, "no_dev") == 0) {
862 /* don't build /dev */
864 } else if (strcmp(optarg
, "no_devices") == 0) {
865 /* don't build /devices */
866 build_devices
= FALSE
;
867 } else if (strcmp(optarg
, "no_p2i") == 0) {
868 /* don't flush path_to_inst */
869 flush_path_to_inst_enable
= FALSE
;
870 } else if (strcmp(optarg
, "use_dicache") == 0) {
871 use_snapshot_cache
= TRUE
;
886 * We're not in zone mode; Check to see if the rootpath
887 * collides with any zonepaths.
889 if (zonename
== NULL
) {
890 if (zone_pathcheck(root_dir
) != DEVFSADM_SUCCESS
)
895 if (init_drvconf
|| init_perm
|| init_sysavail
) {
897 * Load minor perm before force-loading drivers
898 * so the correct permissions are picked up.
901 check_reconfig_state();
905 update_drvconf((major_t
)-1, 0);
914 if (get_linkcompat_opts
== TRUE
) {
916 build_devices
= FALSE
;
917 load_attach_drv
= FALSE
;
919 classes
= s_realloc(classes
, num_classes
*
921 classes
[num_classes
- 1] = compat_class
;
923 while ((opt
= getopt(argc
, argv
, "Cnr:svV:")) != EOF
) {
929 /* prevent driver loading or deferred attach */
930 load_attach_drv
= FALSE
;
933 set_root_devices_dev_dir(optarg
);
934 if (zone_pathcheck(root_dir
) !=
940 /* suppress. don't create/remove links/nodes */
941 /* useful with -v or -V */
943 flush_path_to_inst_enable
= FALSE
;
946 /* documented verbose flag */
947 add_verbose_id(VERBOSE_MID
);
950 /* undocumented for extra verbose levels */
951 add_verbose_id(optarg
);
967 if (strcmp(prog
, DEVLINKS
) == 0) {
968 err_print(DEVLINKS_USAGE
);
969 } else if (strcmp(prog
, DRVCONFIG
) == 0) {
970 err_print(DRVCONFIG_USAGE
);
971 } else if ((strcmp(prog
, DEVFSADM
) == 0) ||
972 (strcmp(prog
, DEVFSADMD
) == 0)) {
973 err_print(DEVFSADM_USAGE
);
975 err_print(COMPAT_LINK_USAGE
);
983 devi_tree_walk(struct dca_impl
*dcip
, int flags
, char *ev_subclass
)
986 struct mlist mlist
= {0};
989 vprint(CHATTY_MID
, "devi_tree_walk: root=%s, minor=%s, driver=%s,"
990 " error=%d, flags=%u\n", dcip
->dci_root
,
991 dcip
->dci_minor
? dcip
->dci_minor
: "<NULL>",
992 dcip
->dci_driver
? dcip
->dci_driver
: "<NULL>", dcip
->dci_error
,
995 assert(dcip
->dci_root
);
997 if (dcip
->dci_flags
& DCA_LOAD_DRV
) {
998 node
= di_init_driver(dcip
->dci_driver
, flags
);
999 msg
= DRIVER_FAILURE
;
1000 name
= dcip
->dci_driver
;
1002 node
= di_init(dcip
->dci_root
, flags
);
1003 msg
= DI_INIT_FAILED
;
1004 name
= dcip
->dci_root
;
1007 if (node
== DI_NODE_NIL
) {
1008 dcip
->dci_error
= errno
;
1010 * Rapid hotplugging (commonly seen during USB testing),
1011 * may remove a device before the create event for it
1012 * has been processed. To prevent alarming users with
1013 * a superfluous message, we suppress error messages
1014 * for ENXIO and hotplug.
1016 if (!(errno
== ENXIO
&& (dcip
->dci_flags
& DCA_HOT_PLUG
)))
1017 err_print(msg
, name
, strerror(dcip
->dci_error
));
1021 if (dcip
->dci_flags
& DCA_FLUSH_PATHINST
)
1022 flush_path_to_inst();
1024 dcip
->dci_arg
= &mlist
;
1025 devi_root_node
= node
; /* protected by lock_dev() */
1027 vprint(CHATTY_MID
, "walking device tree\n");
1029 (void) di_walk_minor(node
, NULL
, DI_CHECK_ALIAS
, dcip
,
1032 process_deferred_links(dcip
, DCA_CREATE_LINK
);
1034 dcip
->dci_arg
= NULL
;
1037 * Finished creating devfs files and dev links.
1041 build_and_enq_event(EC_DEV_ADD
, ev_subclass
, dcip
->dci_root
,
1042 node
, dcip
->dci_minor
);
1044 /* Add new device to device allocation database */
1045 if (system_labeled
&& update_devdb
) {
1046 _update_devalloc_db(&devlist
, 0, DA_ADD
, NULL
, root_dir
);
1050 devi_root_node
= DI_NODE_NIL
; /* protected by lock_dev() */
1055 process_deferred_links(struct dca_impl
*dcip
, int flags
)
1058 struct minor
*mp
, *smp
;
1060 vprint(CHATTY_MID
, "processing deferred links\n");
1062 dep
= dcip
->dci_arg
;
1065 * The list head is not used during the deferred create phase
1067 dcip
->dci_arg
= NULL
;
1070 assert((dep
->head
== NULL
) ^ (dep
->tail
!= NULL
));
1071 assert(flags
== DCA_FREE_LIST
|| flags
== DCA_CREATE_LINK
);
1073 for (smp
= NULL
, mp
= dep
->head
; mp
; mp
= mp
->next
) {
1074 if (flags
== DCA_CREATE_LINK
)
1075 (void) check_minor_type(mp
->node
, mp
->minor
, dcip
);
1084 * Called in non-daemon mode to take a snap shot of the devinfo tree.
1085 * Then it calls the appropriate functions to build /devices and /dev.
1086 * It also flushes path_to_inst.
1087 * Except in the devfsadm -i (single driver case), the flags used by devfsadm
1088 * needs to match DI_CACHE_SNAPSHOT_FLAGS. That will make DINFOCACHE snapshot
1092 process_devinfo_tree()
1095 struct dca_impl dci
;
1096 char name
[MAXNAMELEN
];
1097 char *fcn
= "process_devinfo_tree: ";
1099 vprint(CHATTY_MID
, "%senter\n", fcn
);
1101 dca_impl_init("/", NULL
, &dci
);
1106 * Update kernel driver.conf cache when devfsadm/drvconfig
1107 * is invoked to build /devices and /dev.
1109 if (update_all_drivers
|| load_attach_drv
) {
1110 update_drvconf((major_t
)-1,
1111 update_all_drivers
? MOD_LOADDRVCONF_RECONF
: 0);
1114 if (single_drv
== TRUE
) {
1116 * load a single driver, but walk the entire devinfo tree
1118 if (load_attach_drv
== FALSE
)
1119 err_print(DRV_LOAD_REQD
);
1121 vprint(CHATTY_MID
, "%sattaching driver (%s)\n", fcn
, driver
);
1123 dci
.dci_flags
|= DCA_LOAD_DRV
;
1124 (void) snprintf(name
, sizeof (name
), "%s", driver
);
1125 dci
.dci_driver
= name
;
1126 flags
= DINFOCPYALL
| DINFOPATH
;
1128 } else if (load_attach_drv
== TRUE
) {
1130 * Load and attach all drivers, then walk the entire tree.
1131 * If the cache flag is set, use DINFOCACHE to get cached
1134 if (use_snapshot_cache
== TRUE
) {
1136 vprint(CHATTY_MID
, "%susing snapshot cache\n", fcn
);
1138 vprint(CHATTY_MID
, "%sattaching all drivers\n", fcn
);
1139 flags
= DI_CACHE_SNAPSHOT_FLAGS
;
1142 * remove dangling entries from /etc/devices
1145 flags
|= DINFOCLEANUP
;
1150 * For devlinks, disks, ports, tapes and devfsadm -n,
1151 * just need to take a snapshot with active devices.
1153 vprint(CHATTY_MID
, "%staking snapshot of active devices\n",
1155 flags
= DINFOCPYALL
;
1158 if (((load_attach_drv
== TRUE
) || (single_drv
== TRUE
)) &&
1159 (build_devices
== TRUE
)) {
1160 dci
.dci_flags
|= DCA_FLUSH_PATHINST
;
1163 /* handle pre-cleanup operations desired by the modules. */
1164 pre_and_post_cleanup(RM_PRE
);
1166 devi_tree_walk(&dci
, flags
, NULL
);
1168 if (dci
.dci_error
) {
1173 /* handle post-cleanup operations desired by the modules. */
1174 pre_and_post_cleanup(RM_POST
);
1176 unlock_dev(SYNC_STATE
);
1181 print_cache_signal(int signo
)
1183 if (signal(SIGUSR1
, print_cache_signal
) == SIG_ERR
) {
1184 err_print("signal SIGUSR1 failed: %s\n", strerror(errno
));
1191 revoke_lookup_door(void)
1193 if (lookup_door_fd
!= -1) {
1194 if (door_revoke(lookup_door_fd
) == -1) {
1195 err_print("door_revoke of %s failed - %s\n",
1196 lookup_door_path
, strerror(errno
));
1203 catch_exit(int signo
)
1205 revoke_lookup_door();
1209 * Register with eventd for messages. Create doors for synchronous
1216 char *fcn
= "daemon_update: ";
1217 char door_file
[MAXPATHLEN
];
1218 const char *subclass_list
;
1219 sysevent_handle_t
*sysevent_hp
;
1220 vprint(CHATTY_MID
, "%senter\n", fcn
);
1222 if (signal(SIGUSR1
, print_cache_signal
) == SIG_ERR
) {
1223 err_print("signal SIGUSR1 failed: %s\n", strerror(errno
));
1227 if (signal(SIGTERM
, catch_exit
) == SIG_ERR
) {
1228 err_print("signal SIGTERM failed: %s\n", strerror(errno
));
1233 if (snprintf(door_file
, sizeof (door_file
),
1234 "%s%s", attr_root
? attr_root
: root_dir
, DEVFSADM_SERVICE_DOOR
)
1235 >= sizeof (door_file
)) {
1236 err_print("update_daemon failed to open sysevent service "
1241 if ((sysevent_hp
= sysevent_open_channel_alt(
1242 door_file
)) == NULL
) {
1243 err_print(CANT_CREATE_DOOR
,
1244 door_file
, strerror(errno
));
1248 if (sysevent_bind_subscriber(sysevent_hp
, event_handler
) != 0) {
1249 err_print(CANT_CREATE_DOOR
,
1250 door_file
, strerror(errno
));
1251 (void) sysevent_close_channel(sysevent_hp
);
1255 subclass_list
= EC_SUB_ALL
;
1256 if (sysevent_register_event(sysevent_hp
, EC_ALL
, &subclass_list
, 1)
1258 err_print(CANT_CREATE_DOOR
,
1259 door_file
, strerror(errno
));
1260 (void) sysevent_unbind_subscriber(sysevent_hp
);
1261 (void) sysevent_close_channel(sysevent_hp
);
1265 if (snprintf(door_file
, sizeof (door_file
), "%s/%s",
1266 etc_dev_dir
, DEVFSADM_SYNCH_DOOR
) >= sizeof (door_file
)) {
1267 err_print(CANT_CREATE_DOOR
, DEVFSADM_SYNCH_DOOR
,
1268 strerror(ENAMETOOLONG
));
1273 (void) s_unlink(door_file
);
1274 if ((fd
= open(door_file
, O_RDWR
| O_CREAT
, SYNCH_DOOR_PERMS
)) == -1) {
1275 err_print(CANT_CREATE_DOOR
, door_file
, strerror(errno
));
1281 if ((fd
= door_create(sync_handler
, NULL
,
1282 DOOR_REFUSE_DESC
| DOOR_NO_CANCEL
)) == -1) {
1283 err_print(CANT_CREATE_DOOR
, door_file
, strerror(errno
));
1284 (void) s_unlink(door_file
);
1289 if (fattach(fd
, door_file
) == -1) {
1290 err_print(CANT_CREATE_DOOR
, door_file
, strerror(errno
));
1291 (void) s_unlink(door_file
);
1297 * devname_lookup_door
1299 if (snprintf(door_file
, sizeof (door_file
), "%s/%s",
1300 etc_dev_dir
, DEVNAME_LOOKUP_DOOR
) >= sizeof (door_file
)) {
1301 err_print(CANT_CREATE_DOOR
, DEVNAME_LOOKUP_DOOR
,
1302 strerror(ENAMETOOLONG
));
1307 (void) s_unlink(door_file
);
1308 if ((fd
= open(door_file
, O_RDWR
| O_CREAT
, S_IRUSR
|S_IWUSR
)) == -1) {
1309 err_print(CANT_CREATE_DOOR
, door_file
, strerror(errno
));
1315 if ((fd
= door_create(devname_lookup_handler
, NULL
,
1316 DOOR_REFUSE_DESC
)) == -1) {
1317 err_print(CANT_CREATE_DOOR
, door_file
, strerror(errno
));
1318 (void) s_unlink(door_file
);
1323 (void) fdetach(door_file
);
1324 lookup_door_path
= s_strdup(door_file
);
1326 if (fattach(fd
, door_file
) == -1) {
1329 err_print(CANT_CREATE_DOOR
, door_file
, strerror(errno
));
1330 (void) s_unlink(door_file
);
1334 lookup_door_fd
= fd
;
1336 /* pass down the door name to kernel for door_ki_open */
1337 if (devname_kcall(MODDEVNAME_LOOKUPDOOR
, (void *)door_file
) != 0)
1338 err_print(DEVNAME_CONTACT_FAILED
, strerror(errno
));
1340 vprint(CHATTY_MID
, "%spausing\n", fcn
);
1348 sync_handler(void *cookie
, char *ap
, size_t asize
,
1349 door_desc_t
*dp
, uint_t ndesc
)
1352 struct dca_off
*dcp
, rdca
;
1353 struct dca_impl dci
;
1356 * Must be root to make this call
1357 * If caller is not root, don't touch its data.
1359 if (door_cred(&dcred
) != 0 || dcred
.dc_euid
!= 0) {
1361 dcp
->dca_error
= EPERM
;
1366 assert(asize
== sizeof (*dcp
));
1371 * Root is always present and is the first component of "name" member
1373 assert(dcp
->dca_root
== 0);
1376 * The structure passed in by the door_client uses offsets
1377 * instead of pointers to work across address space boundaries.
1378 * Now copy the data into a structure (dca_impl) which uses
1381 dci
.dci_root
= &dcp
->dca_name
[dcp
->dca_root
];
1382 dci
.dci_minor
= dcp
->dca_minor
? &dcp
->dca_name
[dcp
->dca_minor
] : NULL
;
1384 dcp
->dca_driver
? &dcp
->dca_name
[dcp
->dca_driver
] : NULL
;
1386 dci
.dci_flags
= dcp
->dca_flags
| (dci
.dci_driver
? DCA_LOAD_DRV
: 0);
1390 devi_tree_walk(&dci
, DINFOCPYALL
, NULL
);
1391 dcp
->dca_error
= dci
.dci_error
;
1393 if (dcp
->dca_flags
& DCA_DEVLINK_SYNC
)
1394 unlock_dev(SYNC_STATE
);
1396 unlock_dev(CACHE_STATE
);
1398 out
: (void) door_return((char *)dcp
, sizeof (*dcp
), NULL
, 0);
1404 vprint(CHATTY_MID
, "lock_dev(): entered\n");
1406 if (build_dev
== FALSE
)
1409 /* lockout other threads from /dev */
1410 while (sema_wait(&dev_sema
) != 0)
1414 * Lock out other devfsadm processes from /dev.
1415 * If this wasn't the last process to run,
1418 if (enter_dev_lock() != getpid()) {
1419 invalidate_enumerate_cache();
1420 rm_all_links_from_cache();
1421 (void) di_devlink_close(&devlink_cache
, DI_LINK_ERROR
);
1423 /* send any sysevents that were queued up. */
1424 process_syseventq();
1428 * (re)load the reverse links database if not
1431 if (devlink_cache
== NULL
)
1432 devlink_cache
= di_devlink_open(root_dir
, 0);
1435 * If modules were unloaded, reload them. Also use module status
1436 * as an indication that we should check to see if other binding
1437 * files need to be reloaded.
1439 if (module_head
== NULL
) {
1441 read_minor_perm_file();
1442 read_driver_aliases_file();
1443 read_devlinktab_file();
1444 read_logindevperm_file();
1445 read_enumerate_file();
1448 if (module_head
!= NULL
)
1451 if (strcmp(prog
, DEVLINKS
) == 0) {
1452 if (devlinktab_list
== NULL
) {
1453 err_print(NO_LINKTAB
, devlinktab_file
);
1454 err_print(NO_MODULES
, module_dirs
);
1455 err_print(ABORTING
);
1460 err_print(NO_MODULES
, module_dirs
);
1461 if (strcmp(prog
, DEVFSADM
) == 0) {
1462 err_print(MODIFY_PATH
);
1468 * Unlock the device. If we are processing a CACHE_STATE call, we signal a
1469 * minor_fini_thread delayed SYNC_STATE at the end of the call. If we are
1470 * processing a SYNC_STATE call, we cancel any minor_fini_thread SYNC_STATE
1471 * at both the start and end of the call since we will be doing the SYNC_STATE.
1474 unlock_dev(int flag
)
1476 assert(flag
== SYNC_STATE
|| flag
== CACHE_STATE
);
1478 vprint(CHATTY_MID
, "unlock_dev(): entered\n");
1480 /* If we are starting a SYNC_STATE, cancel minor_fini_thread SYNC */
1481 if (flag
== SYNC_STATE
) {
1482 (void) mutex_lock(&minor_fini_mutex
);
1483 minor_fini_canceled
= TRUE
;
1484 minor_fini_delayed
= FALSE
;
1485 (void) mutex_unlock(&minor_fini_mutex
);
1488 if (build_dev
== FALSE
)
1491 if (devlink_cache
== NULL
) {
1492 err_print(NO_DEVLINK_CACHE
);
1494 assert(devlink_cache
);
1496 if (flag
== SYNC_STATE
) {
1498 if (update_database
)
1499 (void) di_devlink_update(devlink_cache
);
1500 (void) di_devlink_close(&devlink_cache
, 0);
1503 * now that the devlinks db cache has been flushed, it is safe
1504 * to send any sysevents that were queued up.
1506 process_syseventq();
1511 (void) mutex_lock(&minor_fini_mutex
);
1512 if (flag
== SYNC_STATE
) {
1513 /* We did a SYNC_STATE, cancel minor_fini_thread SYNC */
1514 minor_fini_canceled
= TRUE
;
1515 minor_fini_delayed
= FALSE
;
1517 /* We did a CACHE_STATE, start delayed minor_fini_thread SYNC */
1518 minor_fini_canceled
= FALSE
;
1519 minor_fini_delayed
= TRUE
;
1520 (void) cond_signal(&minor_fini_cv
);
1522 (void) mutex_unlock(&minor_fini_mutex
);
1524 (void) sema_post(&dev_sema
);
1528 * Check that if -r is set, it is not any part of a zone--- that is, that
1529 * the zonepath is not a substring of the root path.
1532 zone_pathcheck(char *checkpath
)
1536 char root
[MAXPATHLEN
]; /* resolved devfsadm root path */
1537 char zroot
[MAXPATHLEN
]; /* zone root path */
1538 char rzroot
[MAXPATHLEN
]; /* resolved zone root path */
1539 char tmp
[MAXPATHLEN
];
1541 int err
= DEVFSADM_SUCCESS
;
1543 if (checkpath
[0] == '\0')
1544 return (DEVFSADM_SUCCESS
);
1547 * Check if zones is available on this system.
1549 if ((dlhdl
= dlopen(LIBZONECFG_PATH
, RTLD_LAZY
)) == NULL
) {
1550 return (DEVFSADM_SUCCESS
);
1553 bzero(root
, sizeof (root
));
1554 if (resolvepath(checkpath
, root
, sizeof (root
) - 1) == -1) {
1556 * In this case the user has done "devfsadm -r" on some path
1557 * which does not yet exist, or we got some other misc. error.
1558 * We punt and don't resolve the path in this case.
1560 (void) strlcpy(root
, checkpath
, sizeof (root
));
1563 if (strlen(root
) > 0 && (root
[strlen(root
) - 1] != '/')) {
1564 (void) snprintf(tmp
, sizeof (tmp
), "%s/", root
);
1565 (void) strlcpy(root
, tmp
, sizeof (root
));
1568 cookie
= setzoneent();
1569 while ((name
= getzoneent(cookie
)) != NULL
) {
1570 /* Skip the global zone */
1571 if (strcmp(name
, GLOBAL_ZONENAME
) == 0) {
1576 if (zone_get_zonepath(name
, zroot
, sizeof (zroot
)) != Z_OK
) {
1581 bzero(rzroot
, sizeof (rzroot
));
1582 if (resolvepath(zroot
, rzroot
, sizeof (rzroot
) - 1) == -1) {
1584 * Zone path doesn't exist, or other misc error,
1585 * so we try using the non-resolved pathname.
1587 (void) strlcpy(rzroot
, zroot
, sizeof (rzroot
));
1589 if (strlen(rzroot
) > 0 && (rzroot
[strlen(rzroot
) - 1] != '/')) {
1590 (void) snprintf(tmp
, sizeof (tmp
), "%s/", rzroot
);
1591 (void) strlcpy(rzroot
, tmp
, sizeof (rzroot
));
1595 * Finally, the comparison. If the zone root path is a
1596 * leading substring of the root path, fail.
1598 if (strncmp(rzroot
, root
, strlen(rzroot
)) == 0) {
1599 err_print(ZONE_PATHCHECK
, root
, name
);
1600 err
= DEVFSADM_FAILURE
;
1607 (void) dlclose(dlhdl
);
1612 * Called by the daemon when it receives an event from the devfsadm SLM
1615 * The devfsadm SLM uses a private event channel for communication to
1616 * devfsadmd set-up via private libsysevent interfaces. This handler is
1617 * used to bind to the devfsadmd channel for event delivery.
1618 * The devfsadmd SLM insures single calls to this routine as well as
1619 * synchronized event delivery.
1623 event_handler(sysevent_t
*ev
)
1628 char *dev_ev_subclass
;
1630 nvlist_t
*attr_list
= NULL
;
1633 int branch_event
= 0;
1636 * If this is event-driven, then we cannot trust the static devlist
1640 event_driven
= TRUE
;
1641 subclass
= sysevent_get_subclass_name(ev
);
1642 vprint(EVENT_MID
, "event_handler: %s id:0X%llx\n",
1643 subclass
, sysevent_get_seq(ev
));
1645 if (strcmp(subclass
, ESC_DEVFS_START
) == 0) {
1649 /* Check if event is an instance modification */
1650 if (strcmp(subclass
, ESC_DEVFS_INSTANCE_MOD
) == 0) {
1651 devfs_instance_mod();
1654 if (sysevent_get_attr_list(ev
, &attr_list
) != 0) {
1655 vprint(EVENT_MID
, "event_handler: can not get attr list\n");
1659 if (strcmp(subclass
, ESC_DEVFS_DEVI_ADD
) == 0 ||
1660 strcmp(subclass
, ESC_DEVFS_DEVI_REMOVE
) == 0 ||
1661 strcmp(subclass
, ESC_DEVFS_MINOR_CREATE
) == 0 ||
1662 strcmp(subclass
, ESC_DEVFS_MINOR_REMOVE
) == 0) {
1663 if ((err
= nvlist_lookup_string(attr_list
, DEVFS_PATHNAME
,
1667 if (nvlist_lookup_string(attr_list
, DEVFS_DEVI_CLASS
,
1668 &dev_ev_subclass
) != 0)
1669 dev_ev_subclass
= NULL
;
1671 if (nvlist_lookup_string(attr_list
, DEVFS_DRIVER_NAME
,
1675 if (nvlist_lookup_int32(attr_list
, DEVFS_INSTANCE
,
1679 if (nvlist_lookup_int32(attr_list
, DEVFS_BRANCH_EVENT
,
1680 &branch_event
) != 0)
1683 if (nvlist_lookup_string(attr_list
, DEVFS_MINOR_NAME
,
1689 if (strcmp(ESC_DEVFS_DEVI_ADD
, subclass
) == 0) {
1690 add_minor_pathname(path
, NULL
, dev_ev_subclass
);
1692 build_and_enq_event(EC_DEV_BRANCH
,
1693 ESC_DEV_BRANCH_ADD
, path
, DI_NODE_NIL
,
1697 } else if (strcmp(ESC_DEVFS_MINOR_CREATE
, subclass
) == 0) {
1698 add_minor_pathname(path
, minor
, dev_ev_subclass
);
1700 } else if (strcmp(ESC_DEVFS_MINOR_REMOVE
, subclass
) == 0) {
1701 hot_cleanup(path
, minor
, dev_ev_subclass
, driver_name
,
1704 } else { /* ESC_DEVFS_DEVI_REMOVE */
1705 hot_cleanup(path
, NULL
, dev_ev_subclass
,
1706 driver_name
, instance
);
1708 build_and_enq_event(EC_DEV_BRANCH
,
1709 ESC_DEV_BRANCH_REMOVE
, path
, DI_NODE_NIL
,
1714 unlock_dev(CACHE_STATE
);
1716 } else if (strcmp(subclass
, ESC_DEVFS_BRANCH_ADD
) == 0 ||
1717 strcmp(subclass
, ESC_DEVFS_BRANCH_REMOVE
) == 0) {
1718 if ((err
= nvlist_lookup_string(attr_list
,
1719 DEVFS_PATHNAME
, &path
)) != 0)
1722 /* just log ESC_DEV_BRANCH... event */
1723 if (strcmp(subclass
, ESC_DEVFS_BRANCH_ADD
) == 0)
1724 dev_ev_subclass
= ESC_DEV_BRANCH_ADD
;
1726 dev_ev_subclass
= ESC_DEV_BRANCH_REMOVE
;
1729 build_and_enq_event(EC_DEV_BRANCH
, dev_ev_subclass
, path
,
1731 unlock_dev(CACHE_STATE
);
1733 err_print(UNKNOWN_EVENT
, subclass
);
1737 err_print(EVENT_ATTR_LOOKUP_FAILED
, strerror(err
));
1738 nvlist_free(attr_list
);
1742 dca_impl_init(char *root
, char *minor
, struct dca_impl
*dcip
)
1746 dcip
->dci_root
= root
;
1747 dcip
->dci_minor
= minor
;
1748 dcip
->dci_driver
= NULL
;
1749 dcip
->dci_error
= 0;
1750 dcip
->dci_flags
= 0;
1751 dcip
->dci_arg
= NULL
;
1755 * Kernel logs a message when a devinfo node is attached. Try to create
1756 * /dev and /devices for each minor node. minorname can be NULL.
1759 add_minor_pathname(char *node
, char *minor
, char *ev_subclass
)
1761 struct dca_impl dci
;
1763 vprint(CHATTY_MID
, "add_minor_pathname: node_path=%s minor=%s\n",
1764 node
, minor
? minor
: "NULL");
1766 dca_impl_init(node
, minor
, &dci
);
1769 * Restrict hotplug link creation if daemon
1770 * started with -i option.
1772 if (single_drv
== TRUE
) {
1773 dci
.dci_driver
= driver
;
1777 * We are being invoked in response to a hotplug event.
1779 dci
.dci_flags
= DCA_HOT_PLUG
| DCA_CHECK_TYPE
;
1781 devi_tree_walk(&dci
, DINFOPROP
|DINFOMINOR
, ev_subclass
);
1787 static di_node_t clone_node
= DI_NODE_NIL
;
1789 if (clone_node
== DI_NODE_NIL
)
1790 clone_node
= di_init("/pseudo/clone@0", DINFOPROP
);
1791 return (clone_node
);
1795 is_descendent_of(di_node_t node
, char *driver
)
1797 while (node
!= DI_NODE_NIL
) {
1798 char *drv
= di_driver_name(node
);
1799 if (strcmp(drv
, driver
) == 0)
1801 node
= di_parent_node(node
);
1807 * Checks the minor type. If it is an alias node, then lookup
1808 * the real node/minor first, then call minor_process() to
1812 check_minor_type(di_node_t node
, di_minor_t minor
, void *arg
)
1814 ddi_minor_type minor_type
;
1815 di_node_t clone_node
;
1819 struct dca_impl
*dcip
= arg
;
1823 dep
= dcip
->dci_arg
;
1825 mn
= di_minor_name(minor
);
1828 * We match driver here instead of in minor_process
1829 * as we want the actual driver name. This check is
1830 * unnecessary during deferred processing.
1833 ((dcip
->dci_driver
&& !is_descendent_of(node
, dcip
->dci_driver
)) ||
1834 (dcip
->dci_minor
&& strcmp(mn
, dcip
->dci_minor
)))) {
1835 return (DI_WALK_CONTINUE
);
1838 if ((dcip
->dci_flags
& DCA_CHECK_TYPE
) &&
1839 (nt
= di_minor_nodetype(minor
)) &&
1840 (strcmp(nt
, DDI_NT_NET
) == 0)) {
1841 dcip
->dci_flags
&= ~DCA_CHECK_TYPE
;
1844 minor_type
= di_minor_type(minor
);
1846 if (minor_type
== DDM_MINOR
) {
1847 minor_process(node
, minor
, dep
);
1849 } else if (minor_type
== DDM_ALIAS
) {
1850 struct mlist
*cdep
, clone_del
= {0};
1852 clone_node
= find_clone_node();
1853 if (clone_node
== DI_NODE_NIL
) {
1854 err_print(DI_INIT_FAILED
, "clone", strerror(errno
));
1855 return (DI_WALK_CONTINUE
);
1858 cdep
= dep
? &clone_del
: NULL
;
1860 minor_process(clone_node
, minor
, cdep
);
1863 * cache "alias" minor node and free "clone" minor
1865 if (cdep
!= NULL
&& cdep
->head
!= NULL
) {
1866 assert(cdep
->tail
!= NULL
);
1867 cache_deferred_minor(dep
, node
, minor
);
1868 dcip
->dci_arg
= cdep
;
1869 process_deferred_links(dcip
, DCA_FREE_LIST
);
1870 dcip
->dci_arg
= dep
;
1874 return (DI_WALK_CONTINUE
);
1879 * This is the entry point for each minor node, whether walking
1880 * the entire tree via di_walk_minor() or processing a hotplug event
1881 * for a single devinfo node (via hotplug ndi_devi_online()).
1885 minor_process(di_node_t node
, di_minor_t minor
, struct mlist
*dep
)
1887 create_list_t
*create
;
1890 vprint(CHATTY_MID
, "minor_process: node=%s, minor=%s\n",
1891 di_node_name(node
), di_minor_name(minor
));
1896 * Reset /devices node to minor_perm perm/ownership
1897 * if we are here to deactivate device allocation
1899 if (build_devices
== TRUE
) {
1900 reset_node_permissions(node
, minor
);
1903 if (build_dev
== FALSE
) {
1908 * This function will create any nodes for /etc/devlink.tab.
1909 * If devlink.tab handles link creation, we don't call any
1910 * devfsadm modules since that could cause duplicate caching
1911 * in the enumerate functions if different re strings are
1912 * passed that are logically identical. I'm still not
1913 * convinced this would cause any harm, but better to be safe.
1915 * Deferred processing is available only for devlinks
1916 * created through devfsadm modules.
1918 if (process_devlink_compat(minor
, node
) == TRUE
) {
1922 vprint(CHATTY_MID
, "minor_process: deferred processing\n");
1926 * look for relevant link create rules in the modules, and
1927 * invoke the link create callback function to build a link
1928 * if there is a match.
1931 for (create
= create_head
; create
!= NULL
; create
= create
->next
) {
1932 if ((minor_matches_rule(node
, minor
, create
) == TRUE
) &&
1933 class_ok(create
->create
->device_class
) ==
1935 if (call_minor_init(create
->modptr
) ==
1941 * If NOT doing the deferred creates (i.e. 1st pass) and
1942 * rule requests deferred processing cache the minor
1945 * If deferred processing (2nd pass), create links
1946 * ONLY if rule requests deferred processing.
1948 if (dep
&& ((create
->create
->flags
& CREATE_MASK
) ==
1952 } else if (dep
== NULL
&&
1953 ((create
->create
->flags
& CREATE_MASK
) !=
1958 if ((*(create
->create
->callback_fcn
))
1959 (minor
, node
) == DEVFSADM_TERMINATE
) {
1966 cache_deferred_minor(dep
, node
, minor
);
1971 * Cache node and minor in defer list.
1974 cache_deferred_minor(
1980 const char *fcn
= "cache_deferred_minor";
1982 vprint(CHATTY_MID
, "%s node=%s, minor=%s\n", fcn
,
1983 di_node_name(node
), di_minor_name(minor
));
1986 vprint(CHATTY_MID
, "%s: cannot cache during "
1987 "deferred processing. Ignoring minor\n", fcn
);
1991 mp
= (struct minor
*)s_zalloc(sizeof (struct minor
));
1996 assert(dep
->head
== NULL
|| dep
->tail
!= NULL
);
1997 if (dep
->head
== NULL
) {
2000 dep
->tail
->next
= mp
;
2006 * Check to see if "create" link creation rule matches this node/minor.
2007 * If it does, return TRUE.
2010 minor_matches_rule(di_node_t node
, di_minor_t minor
, create_list_t
*create
)
2012 char *m_nodetype
, *m_drvname
;
2014 if (create
->create
->node_type
!= NULL
) {
2016 m_nodetype
= di_minor_nodetype(minor
);
2017 assert(m_nodetype
!= NULL
);
2019 switch (create
->create
->flags
& TYPE_MASK
) {
2021 if (strcmp(create
->create
->node_type
, m_nodetype
) !=
2027 if (strncmp(create
->create
->node_type
, m_nodetype
,
2028 strlen(create
->create
->node_type
)) != 0) {
2033 if (regexec(&(create
->node_type_comp
), m_nodetype
,
2041 if (create
->create
->drv_name
!= NULL
) {
2042 m_drvname
= di_driver_name(node
);
2043 switch (create
->create
->flags
& DRV_MASK
) {
2045 if (strcmp(create
->create
->drv_name
, m_drvname
) != 0) {
2050 if (regexec(&(create
->drv_name_comp
), m_drvname
,
2062 * If no classes were given on the command line, then return DEVFSADM_SUCCESS.
2063 * Otherwise, return DEVFSADM_SUCCESS if the device "class" from the module
2064 * matches one of the device classes given on the command line,
2065 * otherwise, return DEVFSADM_FAILURE.
2068 class_ok(char *class)
2072 if (num_classes
== 0) {
2073 return (DEVFSADM_SUCCESS
);
2076 for (i
= 0; i
< num_classes
; i
++) {
2077 if (strcmp(class, classes
[i
]) == 0) {
2078 return (DEVFSADM_SUCCESS
);
2081 return (DEVFSADM_FAILURE
);
2085 * call minor_fini on active modules, then unload ALL modules
2088 unload_modules(void)
2090 module_t
*module_free
;
2091 create_list_t
*create_free
;
2092 remove_list_t
*remove_free
;
2094 while (create_head
!= NULL
) {
2095 create_free
= create_head
;
2096 create_head
= create_head
->next
;
2098 if ((create_free
->create
->flags
& TYPE_RE
) == TYPE_RE
) {
2099 regfree(&(create_free
->node_type_comp
));
2101 if ((create_free
->create
->flags
& DRV_RE
) == DRV_RE
) {
2102 regfree(&(create_free
->drv_name_comp
));
2107 while (remove_head
!= NULL
) {
2108 remove_free
= remove_head
;
2109 remove_head
= remove_head
->next
;
2113 while (module_head
!= NULL
) {
2115 if ((module_head
->minor_fini
!= NULL
) &&
2116 ((module_head
->flags
& MODULE_ACTIVE
) == MODULE_ACTIVE
)) {
2117 (void) (*(module_head
->minor_fini
))();
2120 vprint(MODLOAD_MID
, "unloading module %s\n", module_head
->name
);
2121 free(module_head
->name
);
2122 (void) dlclose(module_head
->dlhandle
);
2124 module_free
= module_head
;
2125 module_head
= module_head
->next
;
2131 * Load devfsadm logical link processing modules.
2137 struct dirent
*entp
;
2138 char cdir
[PATH_MAX
+ 1];
2140 char *mdir
= module_dirs
;
2141 char *fcn
= "load_modules: ";
2143 while (*mdir
!= '\0') {
2145 while (*mdir
== ':') {
2149 if (*mdir
== '\0') {
2153 last
= strchr(mdir
, ':');
2156 last
= mdir
+ strlen(mdir
);
2159 (void) strncpy(cdir
, mdir
, last
- mdir
);
2160 cdir
[last
- mdir
] = '\0';
2161 mdir
+= strlen(cdir
);
2163 if ((mod_dir
= opendir(cdir
)) == NULL
) {
2164 vprint(MODLOAD_MID
, "%sopendir(%s): %s\n",
2165 fcn
, cdir
, strerror(errno
));
2169 while ((entp
= readdir(mod_dir
)) != NULL
) {
2171 if ((strcmp(entp
->d_name
, ".") == 0) ||
2172 (strcmp(entp
->d_name
, "..") == 0)) {
2176 load_module(entp
->d_name
, cdir
);
2178 s_closedir(mod_dir
);
2183 load_module(char *mname
, char *cdir
)
2185 _devfsadm_create_reg_t
*create_reg
;
2186 _devfsadm_remove_reg_V1_t
*remove_reg
;
2187 create_list_t
*create_list_element
;
2188 create_list_t
**create_list_next
;
2189 remove_list_t
*remove_list_element
;
2190 remove_list_t
**remove_list_next
;
2191 char epath
[PATH_MAX
+ 1], *end
;
2192 char *fcn
= "load_module: ";
2200 /* ignore any file which does not end in '.so' */
2201 if ((end
= strstr(mname
, MODULE_SUFFIX
)) != NULL
) {
2202 if (end
[strlen(MODULE_SUFFIX
)] != '\0') {
2209 (void) snprintf(epath
, sizeof (epath
), "%s/%s", cdir
, mname
);
2211 if ((dlhandle
= dlopen(epath
, RTLD_LAZY
)) == NULL
) {
2212 dlerrstr
= dlerror();
2213 err_print(DLOPEN_FAILED
, epath
,
2214 dlerrstr
? dlerrstr
: "unknown error");
2218 /* dlsym the _devfsadm_create_reg structure */
2219 if (NULL
== (create_reg
= (_devfsadm_create_reg_t
*)
2220 dlsym(dlhandle
, _DEVFSADM_CREATE_REG
))) {
2221 vprint(MODLOAD_MID
, "dlsym(%s, %s): symbol not found\n", epath
,
2222 _DEVFSADM_CREATE_REG
);
2224 vprint(MODLOAD_MID
, "%sdlsym(%s, %s) succeeded\n",
2225 fcn
, epath
, _DEVFSADM_CREATE_REG
);
2228 /* dlsym the _devfsadm_remove_reg structure */
2229 if (NULL
== (remove_reg
= (_devfsadm_remove_reg_V1_t
*)
2230 dlsym(dlhandle
, _DEVFSADM_REMOVE_REG
))) {
2231 vprint(MODLOAD_MID
, "dlsym(%s,\n\t%s): symbol not found\n",
2232 epath
, _DEVFSADM_REMOVE_REG
);
2234 vprint(MODLOAD_MID
, "dlsym(%s, %s): succeeded\n",
2235 epath
, _DEVFSADM_REMOVE_REG
);
2238 vprint(MODLOAD_MID
, "module %s loaded\n", epath
);
2240 module
= (module_t
*)s_malloc(sizeof (module_t
));
2241 module
->name
= s_strdup(epath
);
2242 module
->dlhandle
= dlhandle
;
2244 /* dlsym other module functions, to be called later */
2245 module
->minor_fini
= (int (*)())dlsym(dlhandle
, MINOR_FINI
);
2246 module
->minor_init
= (int (*)())dlsym(dlhandle
, MINOR_INIT
);
2250 * put a ptr to each struct devfsadm_create on "create_head"
2251 * list sorted in interpose_lvl.
2253 if (create_reg
!= NULL
) {
2254 for (i
= 0; i
< create_reg
->count
; i
++) {
2255 int flags
= create_reg
->tblp
[i
].flags
;
2257 create_list_element
= (create_list_t
*)
2258 s_malloc(sizeof (create_list_t
));
2260 create_list_element
->create
= &(create_reg
->tblp
[i
]);
2261 create_list_element
->modptr
= module
;
2263 if (((flags
& CREATE_MASK
) != 0) &&
2264 ((flags
& CREATE_MASK
) != CREATE_DEFER
)) {
2265 free(create_list_element
);
2266 err_print("illegal flag combination in "
2268 err_print(IGNORING_ENTRY
, i
, epath
);
2272 if (((flags
& TYPE_MASK
) == 0) ^
2273 (create_reg
->tblp
[i
].node_type
== NULL
)) {
2274 free(create_list_element
);
2275 err_print("flags value incompatible with "
2276 "node_type value in module create\n");
2277 err_print(IGNORING_ENTRY
, i
, epath
);
2281 if (((flags
& TYPE_MASK
) != 0) &&
2282 ((flags
& TYPE_MASK
) != TYPE_EXACT
) &&
2283 ((flags
& TYPE_MASK
) != TYPE_RE
) &&
2284 ((flags
& TYPE_MASK
) != TYPE_PARTIAL
)) {
2285 free(create_list_element
);
2286 err_print("illegal TYPE_* flag combination in "
2288 err_print(IGNORING_ENTRY
, i
, epath
);
2292 /* precompile regular expression for efficiency */
2293 if ((flags
& TYPE_RE
) == TYPE_RE
) {
2294 if ((n
= regcomp(&(create_list_element
->
2296 create_reg
->tblp
[i
].node_type
,
2297 REG_EXTENDED
)) != 0) {
2298 free(create_list_element
);
2299 err_print(REGCOMP_FAILED
,
2300 create_reg
->tblp
[i
].node_type
, n
);
2301 err_print(IGNORING_ENTRY
, i
, epath
);
2306 if (((flags
& DRV_MASK
) == 0) ^
2307 (create_reg
->tblp
[i
].drv_name
== NULL
)) {
2308 if ((flags
& TYPE_RE
) == TYPE_RE
) {
2309 regfree(&(create_list_element
->
2312 free(create_list_element
);
2313 err_print("flags value incompatible with "
2314 "drv_name value in module create\n");
2315 err_print(IGNORING_ENTRY
, i
, epath
);
2319 if (((flags
& DRV_MASK
) != 0) &&
2320 ((flags
& DRV_MASK
) != DRV_EXACT
) &&
2321 ((flags
& DRV_MASK
) != DRV_RE
)) {
2322 if ((flags
& TYPE_RE
) == TYPE_RE
) {
2323 regfree(&(create_list_element
->
2326 free(create_list_element
);
2327 err_print("illegal DRV_* flag combination in "
2329 err_print(IGNORING_ENTRY
, i
, epath
);
2333 /* precompile regular expression for efficiency */
2334 if ((create_reg
->tblp
[i
].flags
& DRV_RE
) == DRV_RE
) {
2335 if ((n
= regcomp(&(create_list_element
->
2337 create_reg
->tblp
[i
].drv_name
,
2338 REG_EXTENDED
)) != 0) {
2339 if ((flags
& TYPE_RE
) == TYPE_RE
) {
2340 regfree(&(create_list_element
->
2343 free(create_list_element
);
2344 err_print(REGCOMP_FAILED
,
2345 create_reg
->tblp
[i
].drv_name
, n
);
2346 err_print(IGNORING_ENTRY
, i
, epath
);
2352 /* add to list sorted by interpose level */
2353 for (create_list_next
= &(create_head
);
2354 (*create_list_next
!= NULL
) &&
2355 (*create_list_next
)->create
->interpose_lvl
>=
2356 create_list_element
->create
->interpose_lvl
;
2357 create_list_next
= &((*create_list_next
)->next
))
2359 create_list_element
->next
= *create_list_next
;
2360 *create_list_next
= create_list_element
;
2365 * put a ptr to each struct devfsadm_remove on "remove_head"
2366 * list sorted by interpose_lvl.
2369 if (remove_reg
!= NULL
) {
2370 if (remove_reg
->version
< DEVFSADM_V1
)
2371 flags
|= RM_NOINTERPOSE
;
2372 for (i
= 0; i
< remove_reg
->count
; i
++) {
2374 remove_list_element
= (remove_list_t
*)
2375 s_malloc(sizeof (remove_list_t
));
2377 remove_list_element
->remove
= &(remove_reg
->tblp
[i
]);
2378 remove_list_element
->remove
->flags
|= flags
;
2379 remove_list_element
->modptr
= module
;
2381 for (remove_list_next
= &(remove_head
);
2382 (*remove_list_next
!= NULL
) &&
2383 (*remove_list_next
)->remove
->interpose_lvl
>=
2384 remove_list_element
->remove
->interpose_lvl
;
2385 remove_list_next
= &((*remove_list_next
)->next
))
2387 remove_list_element
->next
= *remove_list_next
;
2388 *remove_list_next
= remove_list_element
;
2392 module
->next
= module_head
;
2393 module_head
= module
;
2397 * After we have completed a CACHE_STATE, if a SYNC_STATE does not occur
2398 * within 'timeout' secs the minor_fini_thread needs to do a SYNC_STATE
2399 * so that we still call the minor_fini routines.
2403 minor_fini_thread(void *arg
)
2405 timestruc_t abstime
;
2407 vprint(INITFINI_MID
, "minor_fini_thread starting\n");
2409 (void) mutex_lock(&minor_fini_mutex
);
2411 /* wait the gather period, or until signaled */
2412 abstime
.tv_sec
= time(NULL
) + minor_fini_timeout
;
2413 abstime
.tv_nsec
= 0;
2414 (void) cond_timedwait(&minor_fini_cv
,
2415 &minor_fini_mutex
, &abstime
);
2417 /* if minor_fini was canceled, go wait again */
2418 if (minor_fini_canceled
== TRUE
)
2421 /* if minor_fini was delayed, go wait again */
2422 if (minor_fini_delayed
== TRUE
) {
2423 minor_fini_delayed
= FALSE
;
2427 /* done with cancellations and delays, do the SYNC_STATE */
2428 (void) mutex_unlock(&minor_fini_mutex
);
2431 unlock_dev(SYNC_STATE
);
2432 vprint(INITFINI_MID
, "minor_fini sync done\n");
2434 (void) mutex_lock(&minor_fini_mutex
);
2440 * Attempt to initialize module, if a minor_init routine exists. Set
2441 * the active flag if the routine exists and succeeds. If it doesn't
2442 * exist, just set the active flag.
2445 call_minor_init(module_t
*module
)
2447 char *fcn
= "call_minor_init: ";
2449 if ((module
->flags
& MODULE_ACTIVE
) == MODULE_ACTIVE
) {
2450 return (DEVFSADM_SUCCESS
);
2453 vprint(INITFINI_MID
, "%smodule %s. current state: inactive\n",
2456 if (module
->minor_init
== NULL
) {
2457 module
->flags
|= MODULE_ACTIVE
;
2458 vprint(INITFINI_MID
, "minor_init not defined\n");
2459 return (DEVFSADM_SUCCESS
);
2462 if ((*(module
->minor_init
))() == DEVFSADM_FAILURE
) {
2463 err_print(FAILED_FOR_MODULE
, MINOR_INIT
, module
->name
);
2464 return (DEVFSADM_FAILURE
);
2467 vprint(INITFINI_MID
, "minor_init() returns DEVFSADM_SUCCESS. "
2468 "new state: active\n");
2470 module
->flags
|= MODULE_ACTIVE
;
2471 return (DEVFSADM_SUCCESS
);
2475 * Creates a symlink 'link' to the physical path of node:minor.
2476 * Construct link contents, then call create_link_common().
2480 devfsadm_mklink(char *link
, di_node_t node
, di_minor_t minor
, int flags
)
2482 char rcontents
[PATH_MAX
];
2483 char devlink
[PATH_MAX
];
2484 char phy_path
[PATH_MAX
];
2490 int last_was_slash
= FALSE
;
2493 * try to use devices path
2495 if ((node
== lnode
) && (minor
== lminor
)) {
2496 acontents
= lphy_path
;
2497 } else if (di_minor_type(minor
) == DDM_ALIAS
) {
2498 /* use /pseudo/clone@0:<driver> as the phys path */
2499 (void) snprintf(phy_path
, sizeof (phy_path
),
2500 "/pseudo/clone@0:%s",
2501 di_driver_name(di_minor_devinfo(minor
)));
2502 acontents
= phy_path
;
2504 if ((dev_path
= di_devfs_path(node
)) == NULL
) {
2505 err_print(DI_DEVFS_PATH_FAILED
, strerror(errno
));
2509 (void) snprintf(phy_path
, sizeof (phy_path
), "%s:%s",
2510 dev_path
, di_minor_name(minor
));
2511 di_devfs_path_free(dev_path
);
2512 acontents
= phy_path
;
2515 /* prepend link with dev_dir contents */
2516 (void) strlcpy(devlink
, dev_dir
, sizeof (devlink
));
2517 (void) strlcat(devlink
, "/", sizeof (devlink
));
2518 (void) strlcat(devlink
, link
, sizeof (devlink
));
2521 * Calculate # of ../ to add. Account for double '//' in path.
2522 * Ignore all leading slashes.
2524 for (i
= 0; link
[i
] == '/'; i
++)
2526 for (numslashes
= 0; link
[i
] != '\0'; i
++) {
2527 if (link
[i
] == '/') {
2528 if (last_was_slash
== FALSE
) {
2530 last_was_slash
= TRUE
;
2533 last_was_slash
= FALSE
;
2536 /* Don't count any trailing '/' */
2537 if (link
[i
-1] == '/') {
2541 rcontents
[0] = '\0';
2543 (void) strlcat(rcontents
, "../", sizeof (rcontents
));
2544 } while (numslashes
-- != 0);
2546 (void) strlcat(rcontents
, "devices", sizeof (rcontents
));
2547 (void) strlcat(rcontents
, acontents
, sizeof (rcontents
));
2549 if (devlinks_debug
== TRUE
) {
2550 vprint(INFO_MID
, "adding link %s ==> %s\n", devlink
, rcontents
);
2553 if ((rv
= create_link_common(devlink
, rcontents
, &link_exists
))
2554 == DEVFSADM_SUCCESS
) {
2556 add_link_to_cache(link
, acontents
);
2561 if (link_exists
== TRUE
) {
2562 /* Link exists or was just created */
2563 (void) di_devlink_add_link(devlink_cache
, link
, rcontents
,
2566 if (system_labeled
&& (flags
& DA_ADD
)) {
2568 * Add this to the list of allocatable devices. If this
2569 * is a hotplugged, removable disk, add it as rmdisk.
2571 int instance
= di_instance(node
);
2573 if ((flags
& DA_CD
) &&
2574 (_da_check_for_usb(devlink
, root_dir
) == 1)) {
2575 (void) da_add_list(&devlist
, devlink
, instance
,
2577 update_devdb
= DA_RMDISK
;
2578 } else if (linknew
== TRUE
) {
2579 (void) da_add_list(&devlist
, devlink
, instance
,
2581 update_devdb
= flags
;
2590 * Creates a symlink link to primary_link. Calculates relative
2591 * directory offsets, then calls link_common().
2595 devfsadm_secondary_link(char *link
, char *primary_link
, int flags
)
2597 char contents
[PATH_MAX
+ 1];
2598 char devlink
[PATH_MAX
+ 1];
2599 int rv
, link_exists
;
2604 /* prepend link with dev_dir contents */
2605 (void) strcpy(devlink
, dev_dir
);
2606 (void) strcat(devlink
, "/");
2607 (void) strcat(devlink
, link
);
2609 * building extra link, so use first link as link contents, but first
2613 tpath
= primary_link
;
2616 while (*fpath
== *tpath
&& *fpath
!= '\0') {
2620 /* Count directories to go up, if any, and add "../" */
2621 while (*fpath
!= '\0') {
2622 if (*fpath
== '/') {
2623 (void) strcpy(op
, "../");
2630 * Back up to the start of the current path component, in
2631 * case in the middle
2633 while (tpath
!= primary_link
&& *(tpath
-1) != '/') {
2636 (void) strcpy(op
, tpath
);
2638 if (devlinks_debug
== TRUE
) {
2639 vprint(INFO_MID
, "adding extra link %s ==> %s\n",
2643 if ((rv
= create_link_common(devlink
, contents
, &link_exists
))
2644 == DEVFSADM_SUCCESS
) {
2646 * we need to save the ultimate /devices contents, and not the
2647 * secondary link, since hotcleanup only looks at /devices path.
2648 * Since we don't have devices path here, we can try to get it
2649 * by readlink'ing the secondary link. This assumes the primary
2650 * link was created first.
2652 add_link_to_cache(link
, lphy_path
);
2654 if (system_labeled
&&
2655 ((flags
& DA_AUDIO
) && (flags
& DA_ADD
))) {
2657 * Add this device to the list of allocatable devices.
2661 op
= strrchr(contents
, '/');
2663 (void) sscanf(op
, "%d", &instance
);
2664 (void) da_add_list(&devlist
, devlink
, instance
, flags
);
2665 update_devdb
= flags
;
2672 * If link exists or was just created, add it to the database
2674 if (link_exists
== TRUE
) {
2675 (void) di_devlink_add_link(devlink_cache
, link
, contents
,
2682 /* returns pointer to the devices directory */
2684 devfsadm_get_devices_dir()
2686 return (devices_dir
);
2690 * Does the actual link creation. VERBOSE_MID only used if there is
2691 * a change. CHATTY_MID used otherwise.
2694 create_link_common(char *devlink
, char *contents
, int *exists
)
2699 static int prev_link_existed
= TRUE
;
2700 char checkcontents
[PATH_MAX
+ 1];
2705 /* Database is not updated when file_mods == FALSE */
2706 if (file_mods
== FALSE
) {
2707 /* we want *actual* link contents so no alias redirection */
2708 linksize
= readlink(devlink
, checkcontents
, PATH_MAX
);
2710 checkcontents
[linksize
] = '\0';
2711 if (strcmp(checkcontents
, contents
) != 0) {
2712 vprint(CHATTY_MID
, REMOVING_LINK
,
2713 devlink
, checkcontents
);
2714 return (DEVFSADM_SUCCESS
);
2716 vprint(CHATTY_MID
, "link exists and is correct:"
2717 " %s -> %s\n", devlink
, contents
);
2718 /* failure only in that the link existed */
2719 return (DEVFSADM_FAILURE
);
2722 vprint(VERBOSE_MID
, CREATING_LINK
, devlink
, contents
);
2723 return (DEVFSADM_SUCCESS
);
2728 * systems calls are expensive, so predict whether to readlink
2729 * or symlink first, based on previous attempt
2731 if (prev_link_existed
== FALSE
) {
2737 while (++max_tries
<= 3) {
2742 if (symlink(contents
, devlink
) == 0) {
2743 vprint(VERBOSE_MID
, CREATING_LINK
, devlink
,
2745 prev_link_existed
= FALSE
;
2746 /* link successfully created */
2748 set_logindev_perms(devlink
);
2749 return (DEVFSADM_SUCCESS
);
2754 /* dirpath to node doesn't exist */
2755 hide
= strrchr(devlink
, '/');
2757 s_mkdirp(devlink
, S_IRWXU
|S_IRGRP
|
2758 S_IXGRP
|S_IROTH
|S_IXOTH
);
2765 err_print(SYMLINK_FAILED
, devlink
,
2766 contents
, strerror(errno
));
2767 return (DEVFSADM_FAILURE
);
2775 * If there is redirection, new phys path
2776 * and old phys path will not match and the
2777 * link will be created with new phys path
2778 * which is what we want. So we want real
2781 linksize
= readlink(devlink
, checkcontents
, PATH_MAX
);
2782 if (linksize
>= 0) {
2783 checkcontents
[linksize
] = '\0';
2784 if (strcmp(checkcontents
, contents
) != 0) {
2786 vprint(VERBOSE_MID
, REMOVING_LINK
,
2787 devlink
, checkcontents
);
2790 prev_link_existed
= TRUE
;
2792 "link exists and is correct:"
2793 " %s -> %s\n", devlink
, contents
);
2795 /* failure in that the link existed */
2796 return (DEVFSADM_FAILURE
);
2801 /* not a symlink, remove and create */
2804 /* maybe it didn't exist at all */
2812 err_print(MAX_ATTEMPTS
, devlink
, contents
);
2813 return (DEVFSADM_FAILURE
);
2817 set_logindev_perms(char *devlink
)
2819 struct login_dev
*newdev
;
2820 struct passwd pwd
, *resp
;
2821 char pwd_buf
[PATH_MAX
];
2824 char *devfs_path
= NULL
;
2827 * We only want logindev perms to be set when a device is
2828 * hotplugged or an application requests synchronous creates.
2829 * So we enable this only in daemon mode. In addition,
2830 * login(1) only fixes the std. /dev dir. So we don't
2831 * change perms if alternate root is set.
2832 * login_dev_enable is TRUE only in these cases.
2834 if (login_dev_enable
!= TRUE
)
2838 * Normally, /etc/logindevperm has few (8 - 10 entries) which
2839 * may be regular expressions (globs were converted to RE).
2840 * So just do a linear search through the list.
2842 for (newdev
= login_dev_cache
; newdev
; newdev
= newdev
->ldev_next
) {
2843 vprint(FILES_MID
, "matching %s with %s\n", devlink
,
2844 newdev
->ldev_device
);
2846 if (regexec(&newdev
->ldev_device_regex
, devlink
, 0,
2848 vprint(FILES_MID
, "matched %s with %s\n", devlink
,
2849 newdev
->ldev_device
);
2858 * we have a match, now find the driver associated with this
2859 * minor node using a snapshot on the physical path
2861 (void) resolve_link(devlink
, NULL
, NULL
, &devfs_path
, 0);
2863 * We dont need redirection here - the actual link contents
2864 * whether "alias" or "current" are fine
2869 struct driver_list
*list
;
2872 /* truncate on : so we can take a snapshot */
2873 (void) strcpy(pwd_buf
, devfs_path
);
2874 p
= strrchr(pwd_buf
, ':');
2881 vprint(FILES_MID
, "link=%s->physpath=%s\n",
2884 node
= di_init(pwd_buf
, DINFOMINOR
);
2888 drv
= di_driver_name(node
);
2891 vprint(FILES_MID
, "%s: driver is %s\n",
2895 /* search thru the driver list specified in logindevperm */
2896 list
= newdev
->ldev_driver_list
;
2897 if ((drv
!= NULL
) && (list
!= NULL
)) {
2899 if (strcmp(list
->driver_name
,
2902 "driver %s match!\n", drv
);
2908 vprint(FILES_MID
, "no driver match!\n");
2919 vprint(FILES_MID
, "changing permissions of %s\n", devlink
);
2922 * We have a match. We now attempt to determine the
2923 * owner and group of the console user.
2925 * stat() the console device newdev->ldev_console
2926 * which will always exist - it will have the right owner but
2927 * not the right group. Use getpwuid_r() to determine group for this
2929 * Note, it is safe to use name service here since if name services
2930 * are not available (during boot or in single-user mode), then
2931 * console owner will be root and its gid can be found in
2934 if (stat(newdev
->ldev_console
, &sb
) == -1) {
2935 vprint(VERBOSE_MID
, STAT_FAILED
, newdev
->ldev_console
,
2941 rv
= getpwuid_r(sb
.st_uid
, &pwd
, pwd_buf
, sizeof (pwd_buf
), &resp
);
2942 if (rv
|| resp
== NULL
) {
2943 rv
= rv
? rv
: EINVAL
;
2944 vprint(VERBOSE_MID
, GID_FAILED
, sb
.st_uid
,
2949 assert(&pwd
== resp
);
2951 sb
.st_gid
= resp
->pw_gid
;
2953 if (chmod(devlink
, newdev
->ldev_perms
) == -1) {
2954 vprint(VERBOSE_MID
, CHMOD_FAILED
, devlink
,
2959 if (chown(devlink
, sb
.st_uid
, sb
.st_gid
) == -1) {
2960 vprint(VERBOSE_MID
, CHOWN_FAILED
, devlink
,
2966 * Reset /devices node with appropriate permissions and
2967 * ownership as specified in /etc/minor_perm.
2970 reset_node_permissions(di_node_t node
, di_minor_t minor
)
2973 char phy_path
[PATH_MAX
+ 1];
2979 char *dev_path
, *aminor
= NULL
;
2981 /* lphy_path starts with / */
2982 if ((dev_path
= di_devfs_path(node
)) == NULL
) {
2983 err_print(DI_DEVFS_PATH_FAILED
, strerror(errno
));
2987 (void) strcpy(lphy_path
, dev_path
);
2988 di_devfs_path_free(dev_path
);
2990 (void) strcat(lphy_path
, ":");
2991 if (di_minor_type(minor
) == DDM_ALIAS
) {
2993 aminor
= di_minor_name(minor
);
2994 driver
= di_driver_name(di_minor_devinfo(minor
));
2995 (void) strcat(lphy_path
, driver
);
2997 (void) strcat(lphy_path
, di_minor_name(minor
));
2999 (void) strcpy(phy_path
, devices_dir
);
3000 (void) strcat(phy_path
, lphy_path
);
3005 vprint(CHATTY_MID
, "reset_node_permissions: phy_path=%s lphy_path=%s\n",
3006 phy_path
, lphy_path
);
3008 dev
= di_minor_devt(minor
);
3009 spectype
= di_minor_spectype(minor
); /* block or char */
3011 getattr(phy_path
, aminor
, spectype
, dev
, &mode
, &uid
, &gid
);
3014 * compare and set permissions and ownership
3016 * Under devfs, a quick insertion and removal of USB devices
3017 * would cause stat of physical path to fail. In this case,
3018 * we emit a verbose message, but don't print errors.
3020 if ((stat(phy_path
, &sb
) == -1) || (sb
.st_rdev
!= dev
)) {
3021 vprint(VERBOSE_MID
, NO_DEVFS_NODE
, phy_path
);
3026 * If we are here for a new device
3027 * If device allocation is on
3029 * set ownership to root:other and permissions to 0000
3031 * set ownership and permissions as specified in minor_perm
3032 * If we are here for an existing device
3033 * If device allocation is to be turned on
3035 * reset ownership to root:other and permissions to 0000
3036 * else if device allocation is to be turned off
3037 * reset ownership and permissions to those specified in
3040 * preserve existing/user-modified ownership and
3043 * devfs indicates a new device by faking access time to be zero.
3045 if (sb
.st_atime
!= 0) {
3049 if ((devalloc_flag
== 0) && (devalloc_is_on
!= 1))
3051 * Leave existing devices as they are if we are not
3052 * turning device allocation on/off.
3056 nt
= di_minor_nodetype(minor
);
3061 for (i
= 0; devalloc_list
[i
]; i
++) {
3062 if (strcmp(nt
, devalloc_list
[i
]) == 0)
3064 * One of the types recognized by devalloc,
3069 if (devalloc_list
[i
] == NULL
)
3073 if (file_mods
== FALSE
) {
3074 /* Nothing more to do if simulating */
3075 vprint(VERBOSE_MID
, PERM_MSG
, phy_path
, uid
, gid
, mode
);
3079 if ((devalloc_flag
== DA_ON
) ||
3080 ((devalloc_is_on
== 1) && (devalloc_flag
!= DA_OFF
))) {
3082 * we are here either to turn device allocation on or
3083 * to add a new device while device allocation is on
3084 * (and we've confirmed that we're not turning it
3087 mode
= DEALLOC_MODE
;
3092 if ((devalloc_is_on
== 1) || (devalloc_flag
== DA_ON
) ||
3093 (sb
.st_mode
!= mode
)) {
3094 if (chmod(phy_path
, mode
) == -1)
3095 vprint(VERBOSE_MID
, CHMOD_FAILED
,
3096 phy_path
, strerror(errno
));
3098 if ((devalloc_is_on
== 1) || (devalloc_flag
== DA_ON
) ||
3099 (sb
.st_uid
!= uid
|| sb
.st_gid
!= gid
)) {
3100 if (chown(phy_path
, uid
, gid
) == -1)
3101 vprint(VERBOSE_MID
, CHOWN_FAILED
,
3102 phy_path
, strerror(errno
));
3105 /* Report that we actually did something */
3106 vprint(VERBOSE_MID
, PERM_MSG
, phy_path
, uid
, gid
, mode
);
3110 * Removes logical link and the minor node it refers to. If file is a
3111 * link, we recurse and try to remove the minor node (or link if path is
3112 * a double link) that file's link contents refer to.
3115 devfsadm_rm_work(char *file
, int recurse
, int file_type
)
3117 char *fcn
= "devfsadm_rm_work: ";
3119 char contents
[PATH_MAX
+ 1];
3120 char nextfile
[PATH_MAX
+ 1];
3121 char newfile
[PATH_MAX
+ 1];
3124 vprint(REMOVE_MID
, "%s%s\n", fcn
, file
);
3127 * Note: we don't remove /devices (non-links) entries because they are
3130 if (file_type
!= TYPE_LINK
) {
3134 /* split into multiple if's due to excessive indentations */
3135 (void) strcpy(newfile
, dev_dir
);
3136 (void) strcat(newfile
, "/");
3137 (void) strcat(newfile
, file
);
3140 * we dont care about the content of the symlink, so
3141 * redirection is not needed.
3143 if ((recurse
== TRUE
) &&
3144 ((linksize
= readlink(newfile
, contents
, PATH_MAX
)) > 0)) {
3145 contents
[linksize
] = '\0';
3148 * recurse if link points to another link
3150 if (is_minor_node(contents
, &ptr
) != DEVFSADM_TRUE
) {
3151 if (strncmp(contents
, DEV
"/", strlen(DEV
) + 1) == 0) {
3152 devfsadm_rm_work(&contents
[strlen(DEV
) + 1],
3155 if ((ptr
= strrchr(file
, '/')) != NULL
) {
3157 (void) strcpy(nextfile
, file
);
3159 (void) strcat(nextfile
, "/");
3161 (void) strcpy(nextfile
, "");
3163 (void) strcat(nextfile
, contents
);
3164 devfsadm_rm_work(nextfile
, TRUE
, TYPE_LINK
);
3169 vprint(VERBOSE_MID
, DEVFSADM_UNLINK
, newfile
);
3170 if (file_mods
== TRUE
) {
3171 rm_link_from_cache(file
);
3173 rm_parent_dir_if_empty(newfile
);
3174 invalidate_enumerate_cache();
3175 (void) di_devlink_rm_link(devlink_cache
, file
);
3180 devfsadm_rm_link(char *file
)
3182 devfsadm_rm_work(file
, FALSE
, TYPE_LINK
);
3186 devfsadm_rm_all(char *file
)
3188 devfsadm_rm_work(file
, TRUE
, TYPE_LINK
);
3196 const char *fcn
= "s_rmdir";
3199 * Certain directories are created at install time by packages.
3200 * Some of them (listed in sticky_dirs[]) are required by apps
3201 * and need to be present even when empty.
3203 vprint(REMOVE_MID
, "%s: checking if %s is sticky\n", fcn
, path
);
3205 rpath
= path
+ strlen(dev_dir
) + 1;
3207 for (i
= 0; (dir
= sticky_dirs
[i
]) != NULL
; i
++) {
3208 if (*rpath
== *dir
) {
3209 if (strcmp(rpath
, dir
) == 0) {
3210 vprint(REMOVE_MID
, "%s: skipping sticky dir: "
3218 return (rmdir(path
));
3222 * Try to remove any empty directories up the tree. It is assumed that
3223 * pathname is a file that was removed, so start with its parent, and
3227 rm_parent_dir_if_empty(char *pathname
)
3229 char *ptr
, path
[PATH_MAX
+ 1];
3230 char *fcn
= "rm_parent_dir_if_empty: ";
3232 vprint(REMOVE_MID
, "%schecking %s if empty\n", fcn
, pathname
);
3234 (void) strcpy(path
, pathname
);
3237 * ascend up the dir tree, deleting all empty dirs.
3238 * Return immediately if a dir is not empty.
3242 if ((ptr
= strrchr(path
, '/')) == NULL
) {
3248 if (finddev_emptydir(path
)) {
3249 /* directory is empty */
3250 if (s_rmdir(path
) == 0) {
3252 "%sremoving empty dir %s\n", fcn
, path
);
3253 } else if (errno
== EEXIST
) {
3255 "%sfailed to remove dir: %s\n", fcn
, path
);
3259 /* some other file is here, so return */
3260 vprint(REMOVE_MID
, "%sdir not empty: %s\n", fcn
, path
);
3267 * This function and all the functions it calls below were added to
3268 * handle the unique problem with world wide names (WWN). The problem is
3269 * that if a WWN device is moved to another address on the same controller
3270 * its logical link will change, while the physical node remains the same.
3271 * The result is that two logical links will point to the same physical path
3272 * in /devices, the valid link and a stale link. This function will
3273 * find all the stale nodes, though at a significant performance cost.
3275 * Caching is used to increase performance.
3276 * A cache will be built from disk if the cache tag doesn't already exist.
3277 * The cache tag is a regular expression "dir_re", which selects a
3278 * subset of disks to search from typically something like
3279 * "dev/cXt[0-9]+d[0-9]+s[0-9]+". After the cache is built, consistency must
3280 * be maintained, so entries are added as new links are created, and removed
3281 * as old links are deleted. The whole cache is flushed if we are a daemon,
3282 * and another devfsadm process ran in between.
3284 * Once the cache is built, this function finds the cache which matches
3285 * dir_re, and then it searches all links in that cache looking for
3286 * any link whose contents match "valid_link_contents" with a corresponding link
3287 * which does not match "valid_link". Any such matches are stale and removed.
3289 * This happens outside the context of a "reparenting" so we dont need
3293 devfsadm_rm_stale_links(char *dir_re
, char *valid_link
, di_node_t node
,
3298 char phy_path
[PATH_MAX
+ 1];
3299 char *valid_link_contents
;
3301 char rmlink
[PATH_MAX
+ 1];
3304 * try to use devices path
3306 if ((node
== lnode
) && (minor
== lminor
)) {
3307 valid_link_contents
= lphy_path
;
3309 if ((dev_path
= di_devfs_path(node
)) == NULL
) {
3310 err_print(DI_DEVFS_PATH_FAILED
, strerror(errno
));
3314 (void) strcpy(phy_path
, dev_path
);
3315 di_devfs_path_free(dev_path
);
3317 (void) strcat(phy_path
, ":");
3318 (void) strcat(phy_path
, di_minor_name(minor
));
3319 valid_link_contents
= phy_path
;
3323 * As an optimization, check to make sure the corresponding
3324 * devlink was just created before continuing.
3327 if (linknew
== FALSE
) {
3331 head
= get_cached_links(dir_re
);
3333 assert(head
->nextlink
== NULL
);
3335 for (link
= head
->link
; link
!= NULL
; link
= head
->nextlink
) {
3337 * See hot_cleanup() for why we do this
3339 head
->nextlink
= link
->next
;
3340 if ((strcmp(link
->contents
, valid_link_contents
) == 0) &&
3341 (strcmp(link
->devlink
, valid_link
) != 0)) {
3342 vprint(CHATTY_MID
, "removing %s -> %s\n"
3343 "valid link is: %s -> %s\n",
3344 link
->devlink
, link
->contents
,
3345 valid_link
, valid_link_contents
);
3347 * Use a copy of the cached link name as the
3348 * cache entry will go away during link removal
3350 (void) snprintf(rmlink
, sizeof (rmlink
), "%s",
3352 devfsadm_rm_link(rmlink
);
3358 * Return previously created cache, or create cache.
3361 get_cached_links(char *dir_re
)
3364 linkhead_t
*linkhead
;
3367 vprint(BUILDCACHE_MID
, "get_cached_links: %s\n", dir_re
);
3369 for (linkhead
= headlinkhead
; linkhead
!= NULL
;
3370 linkhead
= linkhead
->nexthead
) {
3371 if (strcmp(linkhead
->dir_re
, dir_re
) == 0) {
3377 * This tag is not in cache, so add it, along with all its
3378 * matching /dev entries. This is the only time we go to disk.
3380 linkhead
= s_malloc(sizeof (linkhead_t
));
3381 linkhead
->nexthead
= headlinkhead
;
3382 headlinkhead
= linkhead
;
3383 linkhead
->dir_re
= s_strdup(dir_re
);
3385 if ((n
= regcomp(&(linkhead
->dir_re_compiled
), dir_re
,
3386 REG_EXTENDED
)) != 0) {
3387 err_print(REGCOMP_FAILED
, dir_re
, n
);
3390 linkhead
->nextlink
= NULL
;
3391 linkhead
->link
= NULL
;
3393 rd
.fcn
= build_devlink_list
;
3394 rd
.data
= (void *)linkhead
;
3396 vprint(BUILDCACHE_MID
, "get_cached_links: calling recurse_dev_re\n");
3398 /* call build_devlink_list for each directory in the dir_re RE */
3399 if (dir_re
[0] == '/') {
3400 recurse_dev_re("/", &dir_re
[1], &rd
);
3402 recurse_dev_re(dev_dir
, dir_re
, &rd
);
3409 build_devlink_list(char *devlink
, void *data
)
3411 char *fcn
= "build_devlink_list: ";
3415 char contents
[PATH_MAX
+ 1];
3416 char newlink
[PATH_MAX
+ 1];
3417 char stage_link
[PATH_MAX
+ 1];
3419 linkhead_t
*linkhead
= (linkhead_t
*)data
;
3423 vprint(BUILDCACHE_MID
, "%scheck_link: %s\n", fcn
, devlink
);
3425 (void) strcpy(newlink
, devlink
);
3429 * None of the consumers of this function need redirection
3430 * so this readlink gets the "current" contents
3432 linksize
= readlink(newlink
, contents
, PATH_MAX
);
3433 if (linksize
<= 0) {
3435 * The first pass through the do loop we may readlink()
3436 * non-symlink files(EINVAL) from false regexec matches.
3437 * Suppress error messages in those cases or if the link
3438 * content is the empty string.
3440 if (linksize
< 0 && (i
|| errno
!= EINVAL
))
3441 err_print(READLINK_FAILED
, "build_devlink_list",
3442 newlink
, strerror(errno
));
3445 contents
[linksize
] = '\0';
3448 if (is_minor_node(contents
, &r_contents
) == DEVFSADM_FALSE
) {
3450 * assume that link contents is really a pointer to
3451 * another link, so recurse and read its link contents.
3453 * some link contents are absolute:
3454 * /dev/audio -> /dev/sound/0
3456 if (strncmp(contents
, DEV
"/",
3457 strlen(DEV
) + strlen("/")) != 0) {
3459 if ((ptr
= strrchr(newlink
, '/')) == NULL
) {
3460 vprint(REMOVE_MID
, "%s%s -> %s invalid "
3461 "link. missing '/'\n", fcn
,
3466 (void) strcpy(stage_link
, newlink
);
3468 (void) strcat(stage_link
, "/");
3469 (void) strcat(stage_link
, contents
);
3470 (void) strcpy(newlink
, stage_link
);
3472 (void) strcpy(newlink
, dev_dir
);
3473 (void) strcat(newlink
, "/");
3474 (void) strcat(newlink
,
3475 &contents
[strlen(DEV
) + strlen("/")]);
3481 } while (newlink
[0] != '\0');
3483 if (strncmp(devlink
, dev_dir
, strlen(dev_dir
)) != 0) {
3484 vprint(BUILDCACHE_MID
, "%sinvalid link: %s\n", fcn
, devlink
);
3488 r_devlink
= devlink
+ strlen(dev_dir
);
3490 if (r_devlink
[0] != '/')
3493 link
= s_malloc(sizeof (link_t
));
3495 /* don't store the '/' after rootdir/dev */
3498 vprint(BUILDCACHE_MID
, "%scaching link: %s\n", fcn
, r_devlink
);
3499 link
->devlink
= s_strdup(r_devlink
);
3501 link
->contents
= s_strdup(r_contents
);
3503 link
->next
= linkhead
->link
;
3504 linkhead
->link
= link
;
3508 * to be consistent, devlink must not begin with / and must be
3509 * relative to /dev/, whereas physpath must contain / and be
3510 * relative to /devices.
3513 add_link_to_cache(char *devlink
, char *physpath
)
3515 linkhead_t
*linkhead
;
3519 if (file_mods
== FALSE
) {
3523 vprint(CACHE_MID
, "add_link_to_cache: %s -> %s ",
3526 for (linkhead
= headlinkhead
; linkhead
!= NULL
;
3527 linkhead
= linkhead
->nexthead
) {
3528 if (regexec(&(linkhead
->dir_re_compiled
), devlink
, 0, NULL
, 0)
3531 link
= s_malloc(sizeof (link_t
));
3532 link
->devlink
= s_strdup(devlink
);
3533 link
->contents
= s_strdup(physpath
);
3534 link
->next
= linkhead
->link
;
3535 linkhead
->link
= link
;
3540 " %d %s\n", added
, added
== 0 ? "NOT ADDED" : "ADDED");
3544 * Remove devlink from cache. Devlink must be relative to /dev/ and not start
3548 rm_link_from_cache(char *devlink
)
3550 linkhead_t
*linkhead
;
3554 vprint(CACHE_MID
, "rm_link_from_cache enter: %s\n", devlink
);
3556 for (linkhead
= headlinkhead
; linkhead
!= NULL
;
3557 linkhead
= linkhead
->nexthead
) {
3558 if (regexec(&(linkhead
->dir_re_compiled
), devlink
, 0, NULL
, 0)
3561 for (linkp
= &(linkhead
->link
); *linkp
!= NULL
; ) {
3562 if ((strcmp((*linkp
)->devlink
, devlink
) == 0)) {
3564 *linkp
= (*linkp
)->next
;
3566 * We are removing our caller's
3567 * "next" link. Update the nextlink
3568 * field in the head so that our
3569 * callers accesses the next valid
3572 if (linkhead
->nextlink
== save
)
3573 linkhead
->nextlink
= *linkp
;
3574 free(save
->devlink
);
3575 free(save
->contents
);
3577 vprint(CACHE_MID
, " %s FREED FROM "
3578 "CACHE\n", devlink
);
3580 linkp
= &((*linkp
)->next
);
3588 rm_all_links_from_cache()
3590 linkhead_t
*linkhead
;
3591 linkhead_t
*nextlinkhead
;
3595 vprint(CACHE_MID
, "rm_all_links_from_cache\n");
3597 for (linkhead
= headlinkhead
; linkhead
!= NULL
;
3598 linkhead
= nextlinkhead
) {
3600 nextlinkhead
= linkhead
->nexthead
;
3601 assert(linkhead
->nextlink
== NULL
);
3602 for (link
= linkhead
->link
; link
!= NULL
; link
= nextlink
) {
3603 nextlink
= link
->next
;
3604 free(link
->devlink
);
3605 free(link
->contents
);
3608 regfree(&(linkhead
->dir_re_compiled
));
3609 free(linkhead
->dir_re
);
3612 headlinkhead
= NULL
;
3616 * Called when the kernel has modified the incore path_to_inst data. This
3617 * function will schedule a flush of the data to the filesystem.
3620 devfs_instance_mod(void)
3622 char *fcn
= "devfs_instance_mod: ";
3623 vprint(PATH2INST_MID
, "%senter\n", fcn
);
3625 /* signal instance thread */
3626 (void) mutex_lock(&count_lock
);
3628 (void) cond_signal(&cv
);
3629 (void) mutex_unlock(&count_lock
);
3633 instance_flush_thread(void)
3640 (void) mutex_lock(&count_lock
);
3641 while (inst_count
== 0) {
3642 (void) cond_wait(&cv
, &count_lock
);
3646 vprint(PATH2INST_MID
, "signaled to flush path_to_inst."
3647 " Enter delay loop\n");
3649 * Wait MAX_IDLE_DELAY seconds after getting the last flush
3650 * path_to_inst event before invoking a flush, but never wait
3651 * more than MAX_DELAY seconds after getting the first event.
3653 for (idle
= 0, i
= 0; i
< MAX_DELAY
; i
++) {
3655 (void) mutex_unlock(&count_lock
);
3657 (void) mutex_lock(&count_lock
);
3659 /* shorten the delay if we are idle */
3660 if (inst_count
== 0) {
3662 if (idle
> MAX_IDLE_DELAY
) {
3666 inst_count
= idle
= 0;
3670 (void) mutex_unlock(&count_lock
);
3672 flush_path_to_inst();
3677 * Helper function for flush_path_to_inst() below; this routine calls the
3678 * inst_sync syscall to flush the path_to_inst database to the given file.
3681 do_inst_sync(char *filename
, char *instfilename
)
3683 void (*sigsaved
)(int);
3684 int err
= 0, flags
= INST_SYNC_IF_REQUIRED
;
3687 if (stat(instfilename
, &sb
) == -1 && errno
== ENOENT
)
3688 flags
= INST_SYNC_ALWAYS
;
3690 vprint(INSTSYNC_MID
, "do_inst_sync: about to flush %s\n", filename
);
3691 sigsaved
= sigset(SIGSYS
, SIG_IGN
);
3692 if (inst_sync(filename
, flags
) == -1)
3694 (void) sigset(SIGSYS
, sigsaved
);
3698 return (DEVFSADM_SUCCESS
);
3699 case EALREADY
: /* no-op, path_to_inst already up to date */
3702 err_print(CANT_LOAD_SYSCALL
);
3705 err_print(SUPER_TO_SYNC
);
3708 err_print(INSTSYNC_FAILED
, filename
, strerror(err
));
3711 return (DEVFSADM_FAILURE
);
3715 * Flush the kernel's path_to_inst database to /etc/path_to_inst. To do so
3716 * safely, the database is flushed to a temporary file, then moved into place.
3718 * The following files are used during this process:
3719 * /etc/path_to_inst: The path_to_inst file
3720 * /etc/path_to_inst.<pid>: Contains data flushed from the kernel
3721 * /etc/path_to_inst.old: The backup file
3722 * /etc/path_to_inst.old.<pid>: Temp file for creating backup
3726 flush_path_to_inst(void)
3728 char *new_inst_file
= NULL
;
3729 char *old_inst_file
= NULL
;
3730 char *old_inst_file_npid
= NULL
;
3731 FILE *inst_file_fp
= NULL
;
3732 FILE *old_inst_file_fp
= NULL
;
3738 vprint(PATH2INST_MID
, "flush_path_to_inst: %s\n",
3739 (flush_path_to_inst_enable
== TRUE
) ? "ENABLED" : "DISABLED");
3741 if (flush_path_to_inst_enable
== FALSE
) {
3745 inst_strlen
= strlen(inst_file
);
3746 new_inst_file
= s_malloc(inst_strlen
+ PID_STR_LEN
+ 2);
3747 old_inst_file
= s_malloc(inst_strlen
+ PID_STR_LEN
+ 6);
3748 old_inst_file_npid
= s_malloc(inst_strlen
+
3749 sizeof (INSTANCE_FILE_SUFFIX
));
3751 (void) snprintf(new_inst_file
, inst_strlen
+ PID_STR_LEN
+ 2,
3752 "%s.%ld", inst_file
, getpid());
3754 if (stat(new_inst_file
, &sb
) == 0) {
3755 s_unlink(new_inst_file
);
3758 err
= do_inst_sync(new_inst_file
, inst_file
);
3759 if (err
!= DEVFSADM_SUCCESS
) {
3765 * Now we deal with the somewhat tricky updating and renaming
3766 * of this critical piece of kernel state.
3770 * Copy the current instance file into a temporary file.
3771 * Then rename the temporary file into the backup (.old)
3772 * file and rename the newly flushed kernel data into
3773 * the instance file.
3774 * Of course if 'inst_file' doesn't exist, there's much
3775 * less for us to do .. tee hee.
3777 if ((inst_file_fp
= fopen(inst_file
, "r")) == NULL
) {
3779 * No such file. Rename the new onto the old
3781 if ((err
= rename(new_inst_file
, inst_file
)) != 0)
3782 err_print(RENAME_FAILED
, inst_file
, strerror(errno
));
3787 (void) snprintf(old_inst_file
, inst_strlen
+ PID_STR_LEN
+ 6,
3788 "%s.old.%ld", inst_file
, getpid());
3790 if (stat(old_inst_file
, &sb
) == 0) {
3791 s_unlink(old_inst_file
);
3794 if ((old_inst_file_fp
= fopen(old_inst_file
, "w")) == NULL
) {
3796 * Can't open the 'old_inst_file' file for writing.
3797 * This is somewhat strange given that the syscall
3798 * just succeeded to write a file out.. hmm.. maybe
3799 * the fs just filled up or something nasty.
3801 * Anyway, abort what we've done so far.
3803 err_print(CANT_UPDATE
, old_inst_file
);
3804 err
= DEVFSADM_FAILURE
;
3810 * Copy current instance file into the temporary file
3813 while ((c
= getc(inst_file_fp
)) != EOF
) {
3814 if ((err
= putc(c
, old_inst_file_fp
)) == EOF
) {
3819 if (fclose(old_inst_file_fp
) == EOF
|| err
== EOF
) {
3820 vprint(INFO_MID
, CANT_UPDATE
, old_inst_file
);
3821 err
= DEVFSADM_FAILURE
;
3827 * Set permissions to be the same on the backup as
3828 * /etc/path_to_inst.
3830 (void) chmod(old_inst_file
, 0444);
3833 * So far, everything we've done is more or less reversible.
3834 * But now we're going to commit ourselves.
3837 (void) snprintf(old_inst_file_npid
,
3838 inst_strlen
+ sizeof (INSTANCE_FILE_SUFFIX
),
3839 "%s%s", inst_file
, INSTANCE_FILE_SUFFIX
);
3841 if ((err
= rename(old_inst_file
, old_inst_file_npid
)) != 0) {
3842 err_print(RENAME_FAILED
, old_inst_file_npid
,
3844 } else if ((err
= rename(new_inst_file
, inst_file
)) != 0) {
3845 err_print(RENAME_FAILED
, inst_file
, strerror(errno
));
3849 if (inst_file_fp
!= NULL
) {
3850 if (fclose(inst_file_fp
) == EOF
) {
3851 err_print(FCLOSE_FAILED
, inst_file
, strerror(errno
));
3855 if (stat(new_inst_file
, &sb
) == 0) {
3856 s_unlink(new_inst_file
);
3858 free(new_inst_file
);
3860 if (stat(old_inst_file
, &sb
) == 0) {
3861 s_unlink(old_inst_file
);
3863 free(old_inst_file
);
3865 free(old_inst_file_npid
);
3867 if (err
!= 0 && err
!= EALREADY
) {
3868 err_print(FAILED_TO_UPDATE
, inst_file
);
3873 * detach from tty. For daemon mode.
3879 if (DEVFSADM_DEBUG_ON
== TRUE
) {
3886 (void) open("/dev/null", O_RDWR
, 0);
3889 openlog(DEVFSADMD
, LOG_PID
, LOG_DAEMON
);
3890 (void) setlogmask(LOG_UPTO(LOG_INFO
));
3895 * Use an advisory lock to synchronize updates to /dev. If the lock is
3896 * held by another process, block in the fcntl() system call until that
3897 * process drops the lock or exits. The lock file itself is
3898 * DEV_LOCK_FILE. The process id of the current and last process owning
3899 * the lock is kept in the lock file. After acquiring the lock, read the
3900 * process id and return it. It is the process ID which last owned the
3901 * lock, and will be used to determine if caches need to be flushed.
3903 * NOTE: if the devlink database is held open by the caller, it may
3904 * be closed by this routine. This is to enforce the following lock ordering:
3905 * 1) /dev lock 2) database open
3913 pid_t last_owner_pid
;
3915 if (file_mods
== FALSE
) {
3919 (void) snprintf(dev_lockfile
, sizeof (dev_lockfile
),
3920 "%s/%s", etc_dev_dir
, DEV_LOCK_FILE
);
3922 vprint(LOCK_MID
, "enter_dev_lock: lock file %s\n", dev_lockfile
);
3924 dev_lock_fd
= open(dev_lockfile
, O_CREAT
|O_RDWR
, 0644);
3925 if (dev_lock_fd
< 0) {
3926 err_print(OPEN_FAILED
, dev_lockfile
, strerror(errno
));
3931 lock
.l_type
= F_WRLCK
;
3932 lock
.l_whence
= SEEK_SET
;
3936 /* try for the lock, but don't wait */
3937 if (fcntl(dev_lock_fd
, F_SETLK
, &lock
) == -1) {
3938 if ((errno
== EACCES
) || (errno
== EAGAIN
)) {
3940 n
= read(dev_lock_fd
, &pid
, sizeof (pid_t
));
3941 vprint(LOCK_MID
, "waiting for PID %d to complete\n",
3943 if (lseek(dev_lock_fd
, 0, SEEK_SET
) == (off_t
)-1) {
3944 err_print(LSEEK_FAILED
, dev_lockfile
,
3950 * wait for the dev lock. If we have the database open,
3951 * close it first - the order of lock acquisition should
3952 * always be: 1) dev_lock 2) database
3953 * This is to prevent deadlocks with any locks the
3954 * database code may hold.
3956 (void) di_devlink_close(&devlink_cache
, 0);
3958 /* send any sysevents that were queued up. */
3959 process_syseventq();
3961 if (fcntl(dev_lock_fd
, F_SETLKW
, &lock
) == -1) {
3962 err_print(LOCK_FAILED
, dev_lockfile
,
3970 hold_dev_lock
= TRUE
;
3972 n
= read(dev_lock_fd
, &pid
, sizeof (pid_t
));
3973 if (n
== sizeof (pid_t
) && pid
== getpid()) {
3977 last_owner_pid
= pid
;
3979 if (lseek(dev_lock_fd
, 0, SEEK_SET
) == (off_t
)-1) {
3980 err_print(LSEEK_FAILED
, dev_lockfile
, strerror(errno
));
3985 n
= write(dev_lock_fd
, &pid
, sizeof (pid_t
));
3986 if (n
!= sizeof (pid_t
)) {
3987 err_print(WRITE_FAILED
, dev_lockfile
, strerror(errno
));
3992 return (last_owner_pid
);
3996 * Drop the advisory /dev lock, close lock file. Close and re-open the
3997 * file every time so to ensure a resync if for some reason the lock file
4001 exit_dev_lock(int exiting
)
4003 struct flock unlock
;
4005 if (hold_dev_lock
== FALSE
) {
4009 vprint(LOCK_MID
, "exit_dev_lock: lock file %s, exiting = %d\n",
4010 dev_lockfile
, exiting
);
4012 unlock
.l_type
= F_UNLCK
;
4013 unlock
.l_whence
= SEEK_SET
;
4017 if (fcntl(dev_lock_fd
, F_SETLK
, &unlock
) == -1) {
4018 err_print(UNLOCK_FAILED
, dev_lockfile
, strerror(errno
));
4021 hold_dev_lock
= FALSE
;
4023 if (close(dev_lock_fd
) == -1) {
4024 err_print(CLOSE_FAILED
, dev_lockfile
, strerror(errno
));
4033 * Use an advisory lock to ensure that only one daemon process is active
4034 * in the system at any point in time. If the lock is held by another
4035 * process, do not block but return the pid owner of the lock to the
4036 * caller immediately. The lock is cleared if the holding daemon process
4037 * exits for any reason even if the lock file remains, so the daemon can
4038 * be restarted if necessary. The lock file is DAEMON_LOCK_FILE.
4041 enter_daemon_lock(void)
4045 (void) snprintf(daemon_lockfile
, sizeof (daemon_lockfile
),
4046 "%s/%s", etc_dev_dir
, DAEMON_LOCK_FILE
);
4048 vprint(LOCK_MID
, "enter_daemon_lock: lock file %s\n", daemon_lockfile
);
4050 daemon_lock_fd
= open(daemon_lockfile
, O_CREAT
|O_RDWR
, 0644);
4051 if (daemon_lock_fd
< 0) {
4052 err_print(OPEN_FAILED
, daemon_lockfile
, strerror(errno
));
4057 lock
.l_type
= F_WRLCK
;
4058 lock
.l_whence
= SEEK_SET
;
4062 if (fcntl(daemon_lock_fd
, F_SETLK
, &lock
) == -1) {
4064 if (errno
== EAGAIN
|| errno
== EDEADLK
) {
4065 if (fcntl(daemon_lock_fd
, F_GETLK
, &lock
) == -1) {
4066 err_print(LOCK_FAILED
, daemon_lockfile
,
4071 return (lock
.l_pid
);
4074 hold_daemon_lock
= TRUE
;
4079 * Drop the advisory daemon lock, close lock file
4082 exit_daemon_lock(int exiting
)
4086 if (hold_daemon_lock
== FALSE
) {
4090 vprint(LOCK_MID
, "exit_daemon_lock: lock file %s, exiting = %d\n",
4091 daemon_lockfile
, exiting
);
4093 lock
.l_type
= F_UNLCK
;
4094 lock
.l_whence
= SEEK_SET
;
4098 if (fcntl(daemon_lock_fd
, F_SETLK
, &lock
) == -1) {
4099 err_print(UNLOCK_FAILED
, daemon_lockfile
, strerror(errno
));
4102 if (close(daemon_lock_fd
) == -1) {
4103 err_print(CLOSE_FAILED
, daemon_lockfile
, strerror(errno
));
4111 * Called to removed danging nodes in two different modes: RM_PRE, RM_POST.
4112 * RM_PRE mode is called before processing the entire devinfo tree, and RM_POST
4113 * is called after processing the entire devinfo tree.
4116 pre_and_post_cleanup(int flags
)
4120 cleanup_data_t cleanup_data
;
4121 char *fcn
= "pre_and_post_cleanup: ";
4123 if (build_dev
== FALSE
)
4126 vprint(CHATTY_MID
, "attempting %s-cleanup\n",
4127 flags
== RM_PRE
? "pre" : "post");
4128 vprint(REMOVE_MID
, "%sflags = %d\n", fcn
, flags
);
4131 * the generic function recurse_dev_re is shared among different
4132 * functions, so set the method and data that it should use for
4135 rd
.fcn
= matching_dev
;
4136 rd
.data
= (void *)&cleanup_data
;
4137 cleanup_data
.flags
= flags
;
4139 (void) mutex_lock(&nfp_mutex
);
4142 for (rm
= remove_head
; rm
!= NULL
; rm
= rm
->next
) {
4143 if ((flags
& rm
->remove
->flags
) == flags
) {
4144 cleanup_data
.rm
= rm
;
4146 * If reached this point, RM_PRE or RM_POST cleanup is
4147 * desired. clean_ok() decides whether to clean
4148 * under the given circumstances.
4150 vprint(REMOVE_MID
, "%scleanup: PRE or POST\n", fcn
);
4151 if (clean_ok(rm
->remove
) == DEVFSADM_SUCCESS
) {
4152 vprint(REMOVE_MID
, "cleanup: cleanup OK\n");
4153 recurse_dev_re(dev_dir
,
4154 rm
->remove
->dev_dirs_re
, &rd
);
4159 (void) mutex_unlock(&nfp_mutex
);
4163 * clean_ok() determines whether cleanup should be done according
4164 * to the following matrix:
4166 * command line arguments RM_PRE RM_POST RM_PRE && RM_POST &&
4167 * RM_ALWAYS RM_ALWAYS
4168 * ---------------------- ------ ----- --------- ----------
4170 * <neither -c nor -C> - - pre-clean post-clean
4172 * -C pre-clean post-clean pre-clean post-clean
4174 * -C -c class pre-clean post-clean pre-clean post-clean
4175 * if class if class if class if class
4176 * matches matches matches matches
4178 * -c class - - pre-clean post-clean
4184 clean_ok(devfsadm_remove_V1_t
*remove
)
4188 if (single_drv
== TRUE
) {
4189 /* no cleanup at all when using -i option */
4190 return (DEVFSADM_FAILURE
);
4194 * no cleanup if drivers are not loaded. We make an exception
4195 * for the "disks" program however, since disks has a public
4196 * cleanup flag (-C) and disk drivers are usually never
4199 if (load_attach_drv
== FALSE
&& strcmp(prog
, DISKS
) != 0) {
4200 return (DEVFSADM_FAILURE
);
4203 /* if the cleanup flag was not specified, return false */
4204 if ((cleanup
== FALSE
) && ((remove
->flags
& RM_ALWAYS
) == 0)) {
4205 return (DEVFSADM_FAILURE
);
4208 if (num_classes
== 0) {
4209 return (DEVFSADM_SUCCESS
);
4213 * if reached this point, check to see if the class in the given
4214 * remove structure matches a class given on the command line
4217 for (i
= 0; i
< num_classes
; i
++) {
4218 if (strcmp(remove
->device_class
, classes
[i
]) == 0) {
4219 return (DEVFSADM_SUCCESS
);
4223 return (DEVFSADM_FAILURE
);
4227 * Called to remove dangling nodes after receiving a hotplug event
4228 * containing the physical node pathname to be removed.
4231 hot_cleanup(char *node_path
, char *minor_name
, char *ev_subclass
,
4232 char *driver_name
, int instance
)
4237 char *fcn
= "hot_cleanup: ";
4238 char path
[PATH_MAX
+ 1];
4240 char rmlink
[PATH_MAX
+ 1];
4241 nvlist_t
*nvl
= NULL
;
4246 * dev links can go away as part of hot cleanup.
4247 * So first build event attributes in order capture dev links.
4249 if (ev_subclass
!= NULL
)
4250 nvl
= build_event_attributes(EC_DEV_REMOVE
, ev_subclass
,
4251 node_path
, DI_NODE_NIL
, driver_name
, instance
, minor_name
);
4253 (void) strcpy(path
, node_path
);
4254 (void) strcat(path
, ":");
4255 (void) strcat(path
, minor_name
== NULL
? "" : minor_name
);
4257 path_len
= strlen(path
);
4259 vprint(REMOVE_MID
, "%spath=%s\n", fcn
, path
);
4261 (void) mutex_lock(&nfp_mutex
);
4264 for (rm
= remove_head
; rm
!= NULL
; rm
= rm
->next
) {
4265 if ((RM_HOT
& rm
->remove
->flags
) == RM_HOT
) {
4266 head
= get_cached_links(rm
->remove
->dev_dirs_re
);
4267 assert(head
->nextlink
== NULL
);
4268 for (link
= head
->link
;
4269 link
!= NULL
; link
= head
->nextlink
) {
4271 * The remove callback below may remove
4272 * the current and/or any or all of the
4273 * subsequent links in the list.
4274 * Save the next link in the head. If
4275 * the callback removes the next link
4276 * the saved pointer in the head will be
4277 * updated by the callback to point at
4278 * the next valid link.
4280 head
->nextlink
= link
->next
;
4283 * if devlink is in no-further-process hash,
4286 if (nfphash_lookup(link
->devlink
) != NULL
)
4290 skip
= strcmp(link
->contents
, path
);
4292 skip
= strncmp(link
->contents
, path
,
4295 (call_minor_init(rm
->modptr
) ==
4300 "%sremoving %s -> %s\n", fcn
,
4301 link
->devlink
, link
->contents
);
4303 * Use a copy of the cached link name
4304 * as the cache entry will go away
4305 * during link removal
4307 (void) snprintf(rmlink
, sizeof (rmlink
),
4308 "%s", link
->devlink
);
4309 if (rm
->remove
->flags
& RM_NOINTERPOSE
) {
4311 (rm
->remove
->callback_fcn
))(rmlink
);
4313 ret
= ((int (*)(char *))
4314 (rm
->remove
->callback_fcn
))(rmlink
);
4315 if (ret
== DEVFSADM_TERMINATE
)
4316 nfphash_insert(rmlink
);
4323 (void) mutex_unlock(&nfp_mutex
);
4325 /* update device allocation database */
4326 if (system_labeled
) {
4329 if (strstr(path
, DA_SOUND_NAME
))
4331 else if (strstr(path
, "storage"))
4332 devtype
= DA_RMDISK
;
4333 else if (strstr(path
, "disk"))
4334 devtype
= DA_RMDISK
;
4335 else if (strstr(path
, "floppy"))
4336 /* TODO: detect usb cds and floppies at insert time */
4337 devtype
= DA_RMDISK
;
4341 (void) _update_devalloc_db(&devlist
, devtype
, DA_REMOVE
,
4342 node_path
, root_dir
);
4346 /* now log an event */
4348 log_event(EC_DEV_REMOVE
, ev_subclass
, nvl
);
4354 * Open the dir current_dir. For every file which matches the first dir
4355 * component of path_re, recurse. If there are no more *dir* path
4356 * components left in path_re (ie no more /), then call function rd->fcn.
4359 recurse_dev_re(char *current_dir
, char *path_re
, recurse_dev_t
*rd
)
4363 char new_path
[PATH_MAX
+ 1];
4364 char *anchored_path_re
;
4366 finddevhdl_t fhandle
;
4369 vprint(RECURSEDEV_MID
, "recurse_dev_re: curr = %s path=%s\n",
4370 current_dir
, path_re
);
4372 if (finddev_readdir(current_dir
, &fhandle
) != 0)
4375 len
= strlen(path_re
);
4376 if ((slash
= strchr(path_re
, '/')) != NULL
) {
4377 len
= (slash
- path_re
);
4380 anchored_path_re
= s_malloc(len
+ 3);
4381 (void) sprintf(anchored_path_re
, "^%.*s$", len
, path_re
);
4383 if (regcomp(&re1
, anchored_path_re
, REG_EXTENDED
) != 0) {
4384 free(anchored_path_re
);
4388 free(anchored_path_re
);
4390 while ((fp
= finddev_next(fhandle
)) != NULL
) {
4392 if (regexec(&re1
, fp
, 0, NULL
, 0) == 0) {
4394 (void) strcpy(new_path
, current_dir
);
4395 (void) strcat(new_path
, "/");
4396 (void) strcat(new_path
, fp
);
4398 vprint(RECURSEDEV_MID
, "recurse_dev_re: match, new "
4399 "path = %s\n", new_path
);
4401 if (slash
!= NULL
) {
4402 recurse_dev_re(new_path
, slash
+ 1, rd
);
4404 /* reached the leaf component of path_re */
4405 vprint(RECURSEDEV_MID
,
4406 "recurse_dev_re: calling fcn\n");
4407 (*(rd
->fcn
))(new_path
, rd
->data
);
4415 finddev_close(fhandle
);
4419 * Found a devpath which matches a RE in the remove structure.
4420 * Now check to see if it is dangling.
4423 matching_dev(char *devpath
, void *data
)
4425 cleanup_data_t
*cleanup_data
= data
;
4426 int norm_len
= strlen(dev_dir
) + strlen("/");
4428 char *fcn
= "matching_dev: ";
4430 vprint(RECURSEDEV_MID
, "%sexamining devpath = '%s'\n", fcn
,
4434 * If the link is in the no-further-process hash
4435 * don't do any remove operation on it.
4437 if (nfphash_lookup(devpath
+ norm_len
) != NULL
)
4441 * Dangling check will work whether "alias" or "current"
4442 * so no need to redirect.
4444 if (resolve_link(devpath
, NULL
, NULL
, NULL
, 1) == TRUE
) {
4445 if (call_minor_init(cleanup_data
->rm
->modptr
) ==
4450 devpath
+= norm_len
;
4452 vprint(RECURSEDEV_MID
, "%scalling callback %s\n", fcn
, devpath
);
4453 if (cleanup_data
->rm
->remove
->flags
& RM_NOINTERPOSE
)
4455 (cleanup_data
->rm
->remove
->callback_fcn
))(devpath
);
4457 ret
= ((int (*)(char *))
4458 (cleanup_data
->rm
->remove
->callback_fcn
))(devpath
);
4459 if (ret
== DEVFSADM_TERMINATE
) {
4461 * We want no further remove processing for
4462 * this link. Add it to the nfp_hash;
4464 nfphash_insert(devpath
);
4471 devfsadm_read_link(di_node_t anynode
, char *link
, char **devfs_path
)
4473 char devlink
[PATH_MAX
];
4478 /* prepend link with dev_dir contents */
4479 (void) strcpy(devlink
, dev_dir
);
4480 (void) strcat(devlink
, "/");
4481 (void) strcat(devlink
, link
);
4483 /* We *don't* want a stat of the /devices node */
4485 (void) resolve_link(devlink
, NULL
, NULL
, &path
, 0);
4487 /* redirect if alias to current */
4488 *devfs_path
= di_alias2curr(anynode
, path
);
4491 return (*devfs_path
? DEVFSADM_SUCCESS
: DEVFSADM_FAILURE
);
4495 devfsadm_link_valid(di_node_t anynode
, char *link
)
4498 char devlink
[PATH_MAX
+ 1], *contents
, *raw_contents
;
4502 /* prepend link with dev_dir contents */
4503 (void) strcpy(devlink
, dev_dir
);
4504 (void) strcat(devlink
, "/");
4505 (void) strcat(devlink
, link
);
4507 if (!device_exists(devlink
) || lstat(devlink
, &sb
) != 0) {
4508 return (DEVFSADM_FALSE
);
4511 raw_contents
= NULL
;
4513 if (resolve_link(devlink
, &raw_contents
, &type
, NULL
, 1) == TRUE
) {
4514 rv
= DEVFSADM_FALSE
;
4520 * resolve alias paths for primary links
4522 contents
= raw_contents
;
4523 if (type
== DI_PRIMARY_LINK
) {
4524 contents
= di_alias2curr(anynode
, raw_contents
);
4529 * The link exists. Add it to the database
4531 (void) di_devlink_add_link(devlink_cache
, link
, contents
, type
);
4532 if (system_labeled
&& (rv
== DEVFSADM_TRUE
) &&
4533 strstr(devlink
, DA_AUDIO_NAME
) && contents
) {
4534 (void) sscanf(contents
, "%*[a-z]%d", &instance
);
4535 (void) da_add_list(&devlist
, devlink
, instance
,
4537 _update_devalloc_db(&devlist
, 0, DA_ADD
, NULL
, root_dir
);
4545 * devpath: Absolute path to /dev link
4546 * content_p: Returns malloced string (link content)
4547 * type_p: Returns link type: primary or secondary
4548 * devfs_path: Returns malloced string: /devices path w/out "/devices"
4549 * dangle: if set, check if link is dangling
4552 * FALSE if not or if caller doesn't care
4553 * Caller is assumed to have initialized pointer contents to NULL
4557 resolve_link(char *devpath
, char **content_p
, int *type_p
, char **devfs_path
,
4560 char contents
[PATH_MAX
+ 1];
4561 char stage_link
[PATH_MAX
+ 1];
4562 char *fcn
= "resolve_link: ";
4569 * This routine will return the "raw" contents. It is upto the
4570 * the caller to redirect "alias" to "current" (or vice versa)
4572 linksize
= readlink(devpath
, contents
, PATH_MAX
);
4574 if (linksize
<= 0) {
4577 contents
[linksize
] = '\0';
4579 vprint(REMOVE_MID
, "%s %s -> %s\n", fcn
, devpath
, contents
);
4582 *content_p
= s_strdup(contents
);
4586 * Check to see if this is a link pointing to another link in /dev. The
4587 * cheap way to do this is to look for a lack of ../devices/.
4590 if (is_minor_node(contents
, &ptr
) == DEVFSADM_FALSE
) {
4593 *type_p
= DI_SECONDARY_LINK
;
4597 * assume that linkcontents is really a pointer to another
4598 * link, and if so recurse and read its link contents.
4600 if (strncmp(contents
, DEV
"/", strlen(DEV
) + 1) == 0) {
4601 (void) strcpy(stage_link
, dev_dir
);
4602 (void) strcat(stage_link
, "/");
4603 (void) strcpy(stage_link
,
4604 &contents
[strlen(DEV
) + strlen("/")]);
4606 if ((ptr
= strrchr(devpath
, '/')) == NULL
) {
4607 vprint(REMOVE_MID
, "%s%s -> %s invalid link. "
4608 "missing '/'\n", fcn
, devpath
, contents
);
4612 (void) strcpy(stage_link
, devpath
);
4614 (void) strcat(stage_link
, "/");
4615 (void) strcat(stage_link
, contents
);
4617 return (resolve_link(stage_link
, NULL
, NULL
, devfs_path
,
4621 /* Current link points at a /devices minor node */
4623 *type_p
= DI_PRIMARY_LINK
;
4627 *devfs_path
= s_strdup(ptr
);
4631 rv
= (stat(ptr
- strlen(DEVICES
), &sb
) == -1);
4633 vprint(REMOVE_MID
, "%slink=%s, returning %s\n", fcn
,
4634 devpath
, ((rv
== TRUE
) ? "TRUE" : "FALSE"));
4640 * Returns the substring of interest, given a path.
4643 alloc_cmp_str(const char *path
, devfsadm_enumerate_t
*dep
)
4647 char *cmp_str
= NULL
;
4649 char *fcn
= "alloc_cmp_str";
4651 np
= ap
= mp
= NULL
;
4654 * extract match flags from the flags argument.
4656 match
= (dep
->flags
& MATCH_MASK
);
4658 vprint(ENUM_MID
, "%s: enumeration match type: 0x%x"
4659 " path: %s\n", fcn
, match
, path
);
4662 * MATCH_CALLBACK and MATCH_ALL are the only flags
4663 * which may be used if "path" is a /dev path
4665 if (match
== MATCH_CALLBACK
) {
4666 if (dep
->sel_fcn
== NULL
) {
4667 vprint(ENUM_MID
, "%s: invalid enumerate"
4668 " callback: path: %s\n", fcn
, path
);
4671 cmp_str
= dep
->sel_fcn(path
, dep
->cb_arg
);
4675 cmp_str
= s_strdup(path
);
4677 if (match
== MATCH_ALL
) {
4682 * The remaining flags make sense only for /devices
4685 if ((mp
= strrchr(cmp_str
, ':')) == NULL
) {
4686 vprint(ENUM_MID
, "%s: invalid path: %s\n",
4691 if (match
== MATCH_MINOR
) {
4692 /* A NULL "match_arg" values implies entire minor */
4693 if (get_component(mp
+ 1, dep
->match_arg
) == NULL
) {
4694 vprint(ENUM_MID
, "%s: invalid minor component:"
4695 " path: %s\n", fcn
, path
);
4701 if ((np
= strrchr(cmp_str
, '/')) == NULL
) {
4702 vprint(ENUM_MID
, "%s: invalid path: %s\n", fcn
, path
);
4706 if (match
== MATCH_PARENT
) {
4707 if (strcmp(cmp_str
, "/") == 0) {
4708 vprint(ENUM_MID
, "%s: invalid path: %s\n",
4713 if (np
== cmp_str
) {
4721 /* ap can be NULL - Leaf address may not exist or be empty string */
4722 ap
= strchr(np
+1, '@');
4724 /* minor is no longer of interest */
4727 if (match
== MATCH_NODE
) {
4731 } else if (match
== MATCH_ADDR
) {
4733 * The empty string is a valid address. The only MATCH_ADDR
4734 * allowed in this case is against the whole address or
4735 * the first component of the address (match_arg=NULL/"0"/"1")
4736 * Note that in this case, the path won't have an "@"
4737 * As a result ap will be NULL. We fake up an ap = @'\0'
4738 * so that get_component() will work correctly.
4744 if (get_component(ap
+ 1, dep
->match_arg
) == NULL
) {
4745 vprint(ENUM_MID
, "%s: invalid leaf addr. component:"
4746 " path: %s\n", fcn
, path
);
4752 vprint(ENUM_MID
, "%s: invalid enumeration flags: 0x%x"
4753 " path: %s\n", fcn
, dep
->flags
, path
);
4763 * "str" is expected to be a string with components separated by ','
4764 * The terminating null char is considered a separator.
4765 * get_component() will remove the portion of the string beyond
4766 * the component indicated.
4767 * If comp_str is NULL, the entire "str" is returned.
4770 get_component(char *str
, const char *comp_str
)
4779 if (comp_str
== NULL
) {
4784 comp
= strtol(comp_str
, &cp
, 10);
4785 if (errno
!= 0 || *cp
!= '\0' || comp
< 0) {
4792 for (cp
= str
; ; cp
++) {
4793 if (*cp
== ',' || *cp
== '\0')
4795 if (*cp
== '\0' || comp
<= 0) {
4811 * Enumerate serves as a generic counter as well as a means to determine
4812 * logical unit/controller numbers for such items as disk and tape
4815 * rules[] is an array of devfsadm_enumerate_t structures which defines
4816 * the enumeration rules to be used for a specified set of links in /dev.
4817 * The set of links is specified through regular expressions (of the flavor
4818 * described in regex(5)). These regular expressions are used to determine
4819 * the set of links in /dev to examine. The last path component in these
4820 * regular expressions MUST contain a parenthesized subexpression surrounding
4821 * the RE which is to be considered the enumerating component. The subexp
4822 * member in a rule is the subexpression number of the enumerating
4823 * component. Subexpressions in the last path component are numbered starting
4826 * A cache of current id assignments is built up from existing symlinks and
4827 * new assignments use the lowest unused id. Assignments are based on a
4828 * match of a specified substring of a symlink's contents. If the specified
4829 * component for the devfs_path argument matches the corresponding substring
4830 * for a existing symlink's contents, the cached id is returned. Else, a new
4831 * id is created and returned in *buf. *buf must be freed by the caller.
4833 * An id assignment may be governed by a combination of rules, each rule
4834 * applicable to a different subset of links in /dev. For example, controller
4835 * numbers may be determined by a combination of disk symlinks in /dev/[r]dsk
4836 * and controller symlinks in /dev/cfg, with the two sets requiring different
4837 * rules to derive the "substring of interest". In such cases, the rules
4838 * array will have more than one element.
4841 devfsadm_enumerate_int(char *devfs_path
, int index
, char **buf
,
4842 devfsadm_enumerate_t rules
[], int nrules
)
4844 return (find_enum_id(rules
, nrules
,
4845 devfs_path
, index
, "0", INTEGER
, buf
, 0));
4849 ctrl_enumerate_int(char *devfs_path
, int index
, char **buf
,
4850 devfsadm_enumerate_t rules
[], int nrules
, int multiple
,
4851 boolean_t scsi_vhci
)
4853 return (find_enum_id(rules
, nrules
,
4854 devfs_path
, index
, scsi_vhci
? "0" : "1", INTEGER
, buf
, multiple
));
4858 * Same as above, but allows a starting value to be specified.
4859 * Private to devfsadm.... used by devlinks.
4862 devfsadm_enumerate_int_start(char *devfs_path
, int index
, char **buf
,
4863 devfsadm_enumerate_t rules
[], int nrules
, char *start
)
4865 return (find_enum_id(rules
, nrules
,
4866 devfs_path
, index
, start
, INTEGER
, buf
, 0));
4870 * devfsadm_enumerate_char serves as a generic counter returning
4874 devfsadm_enumerate_char(char *devfs_path
, int index
, char **buf
,
4875 devfsadm_enumerate_t rules
[], int nrules
)
4877 return (find_enum_id(rules
, nrules
,
4878 devfs_path
, index
, "a", LETTER
, buf
, 0));
4882 * Same as above, but allows a starting char to be specified.
4883 * Private to devfsadm - used by ports module (port_link.c)
4886 devfsadm_enumerate_char_start(char *devfs_path
, int index
, char **buf
,
4887 devfsadm_enumerate_t rules
[], int nrules
, char *start
)
4889 return (find_enum_id(rules
, nrules
,
4890 devfs_path
, index
, start
, LETTER
, buf
, 0));
4895 * For a given numeral_set (see get_cached_set for desc of numeral_set),
4896 * search all cached entries looking for matches on a specified substring
4897 * of devfs_path. The substring is derived from devfs_path based on the
4898 * rule specified by "index". If a match is found on a cached entry,
4899 * return the enumerated id in buf. Otherwise, create a new id by calling
4900 * new_id, then cache and return that entry.
4903 find_enum_id(devfsadm_enumerate_t rules
[], int nrules
,
4904 char *devfs_path
, int index
, char *min
, int type
, char **buf
,
4911 char *fcn
= "find_enum_id";
4914 if (rules
== NULL
) {
4915 vprint(ENUM_MID
, "%s: no rules. path: %s\n",
4916 fcn
, devfs_path
? devfs_path
: "<NULL path>");
4917 return (DEVFSADM_FAILURE
);
4920 if (devfs_path
== NULL
) {
4921 vprint(ENUM_MID
, "%s: NULL path\n", fcn
);
4922 return (DEVFSADM_FAILURE
);
4925 if (nrules
<= 0 || index
< 0 || index
>= nrules
|| buf
== NULL
) {
4926 vprint(ENUM_MID
, "%s: invalid arguments. path: %s\n",
4928 return (DEVFSADM_FAILURE
);
4934 cmp_str
= alloc_cmp_str(devfs_path
, &rules
[index
]);
4935 if (cmp_str
== NULL
) {
4936 return (DEVFSADM_FAILURE
);
4939 if ((set
= get_enum_cache(rules
, nrules
)) == NULL
) {
4941 return (DEVFSADM_FAILURE
);
4944 assert(nrules
== set
->re_count
);
4947 * Check and see if a matching entry is already cached.
4949 matchcount
= lookup_enum_cache(set
, cmp_str
, rules
, index
,
4952 if (matchcount
< 0 || matchcount
> 1) {
4954 if (multiple
&& matchcount
> 1)
4955 return (DEVFSADM_MULTIPLE
);
4957 return (DEVFSADM_FAILURE
);
4960 /* if matching entry already cached, return it */
4961 if (matchcount
== 1) {
4962 /* should never create a link with a reserved ID */
4963 vprint(ENUM_MID
, "%s: 1 match w/ ID: %s\n", fcn
, matchnp
->id
);
4964 assert(matchnp
->flags
== 0);
4965 *buf
= s_strdup(matchnp
->id
);
4967 return (DEVFSADM_SUCCESS
);
4971 * no cached entry, initialize a numeral struct
4972 * by calling new_id() and cache onto the numeral_set
4974 numeral
= s_malloc(sizeof (numeral_t
));
4975 numeral
->id
= new_id(set
->headnumeral
, type
, min
);
4976 numeral
->full_path
= s_strdup(devfs_path
);
4977 numeral
->rule_index
= index
;
4978 numeral
->cmp_str
= cmp_str
;
4981 vprint(RSRV_MID
, "%s: alloc new_id: %s numeral flags = %d\n",
4982 fcn
, numeral
->id
, numeral
->flags
);
4985 /* insert to head of list for fast lookups */
4986 numeral
->next
= set
->headnumeral
;
4987 set
->headnumeral
= numeral
;
4989 *buf
= s_strdup(numeral
->id
);
4990 return (DEVFSADM_SUCCESS
);
4995 * Looks up the specified cache for a match with a specified string
4998 * 0/1/2 : Number of matches.
4999 * Returns the matching element only if there is a single match.
5000 * If the "uncached" flag is set, derives the "cmp_str" afresh
5001 * for the match instead of using cached values.
5004 lookup_enum_cache(numeral_set_t
*set
, char *cmp_str
,
5005 devfsadm_enumerate_t rules
[], int index
, numeral_t
**matchnpp
)
5007 int matchcount
= 0, rv
= -1;
5010 char *fcn
= "lookup_enum_cache";
5015 assert(index
< set
->re_count
);
5017 if (cmp_str
== NULL
) {
5022 if ((rules
[index
].flags
& MATCH_UNCACHED
) == MATCH_UNCACHED
) {
5027 * Check and see if a matching entry is already cached.
5029 for (np
= set
->headnumeral
; np
!= NULL
; np
= np
->next
) {
5034 if (np
->flags
& NUMERAL_RESERVED
) {
5035 vprint(RSRV_MID
, "lookup_enum_cache: "
5036 "Cannot Match with reserved ID (%s), "
5037 "skipping\n", np
->id
);
5038 assert(np
->flags
== NUMERAL_RESERVED
);
5041 vprint(RSRV_MID
, "lookup_enum_cache: "
5042 "Attempting match with numeral ID: %s"
5043 " numeral flags = %d\n", np
->id
, np
->flags
);
5044 assert(np
->flags
== 0);
5047 if (np
->cmp_str
== NULL
) {
5048 vprint(ENUM_MID
, "%s: invalid entry in enumerate"
5049 " cache. path: %s\n", fcn
, np
->full_path
);
5054 vprint(CHATTY_MID
, "%s: bypassing enumerate cache."
5055 " path: %s\n", fcn
, cmp_str
);
5056 cp
= alloc_cmp_str(np
->full_path
,
5057 &rules
[np
->rule_index
]);
5060 rv
= strcmp(cmp_str
, cp
);
5063 rv
= strcmp(cmp_str
, np
->cmp_str
);
5067 if (matchcount
++ != 0) {
5068 break; /* more than 1 match. */
5074 return (matchcount
);
5079 dump_enum_cache(numeral_set_t
*setp
)
5083 char *fcn
= "dump_enum_cache";
5085 vprint(ENUM_MID
, "%s: re_count = %d\n", fcn
, setp
->re_count
);
5086 for (i
= 0; i
< setp
->re_count
; i
++) {
5087 vprint(ENUM_MID
, "%s: re[%d] = %s\n", fcn
, i
, setp
->re
[i
]);
5090 for (np
= setp
->headnumeral
; np
!= NULL
; np
= np
->next
) {
5091 vprint(ENUM_MID
, "%s: id: %s\n", fcn
, np
->id
);
5092 vprint(ENUM_MID
, "%s: full_path: %s\n", fcn
, np
->full_path
);
5093 vprint(ENUM_MID
, "%s: rule_index: %d\n", fcn
, np
->rule_index
);
5094 vprint(ENUM_MID
, "%s: cmp_str: %s\n", fcn
, np
->cmp_str
);
5095 vprint(ENUM_MID
, "%s: flags: %d\n", fcn
, np
->flags
);
5101 * For a given set of regular expressions in rules[], this function returns
5102 * either a previously cached struct numeral_set or it will create and
5103 * cache a new struct numeral_set. There is only one struct numeral_set
5104 * for the combination of REs present in rules[]. Each numeral_set contains
5105 * the regular expressions in rules[] used for cache selection AND a linked
5106 * list of struct numerals, ONE FOR EACH *UNIQUE* numeral or character ID
5107 * selected by the grouping parenthesized subexpression found in the last
5108 * path component of each rules[].re. For example, the RE: "rmt/([0-9]+)"
5109 * selects all the logical nodes of the correct form in dev/rmt/.
5110 * Each rmt/X will store a *single* struct numeral... ie 0, 1, 2 each get a
5111 * single struct numeral. There is no need to store more than a single logical
5112 * node matching X since the information desired in the devfspath would be
5113 * identical for the portion of the devfspath of interest. (the part up to,
5114 * but not including the minor name in this example.)
5116 * If the given numeral_set is not yet cached, call enumerate_recurse to
5119 static numeral_set_t
*
5120 get_enum_cache(devfsadm_enumerate_t rules
[], int nrules
)
5122 /* linked list of numeral sets */
5123 numeral_set_t
*setp
;
5127 enumerate_file_t
*entry
;
5128 char *fcn
= "get_enum_cache";
5131 * See if we've already cached this numeral set.
5133 for (setp
= head_numeral_set
; setp
!= NULL
; setp
= setp
->next
) {
5135 * check all regexp's passed in function against
5136 * those in cached set.
5138 if (nrules
!= setp
->re_count
) {
5142 for (i
= 0; i
< nrules
; i
++) {
5143 if (strcmp(setp
->re
[i
], rules
[i
].re
) != 0) {
5154 * If the MATCH_UNCACHED flag is set, we should not be here.
5156 for (i
= 0; i
< nrules
; i
++) {
5157 if ((rules
[i
].flags
& MATCH_UNCACHED
) == MATCH_UNCACHED
) {
5158 vprint(ENUM_MID
, "%s: invalid enumeration flags: "
5159 "0x%x\n", fcn
, rules
[i
].flags
);
5165 * Since we made it here, we have not yet cached the given set of
5166 * logical nodes matching the passed re. Create a cached entry
5167 * struct numeral_set and populate it with a minimal set of
5168 * logical nodes from /dev.
5171 setp
= s_malloc(sizeof (numeral_set_t
));
5172 setp
->re
= s_malloc(sizeof (char *) * nrules
);
5173 for (i
= 0; i
< nrules
; i
++) {
5174 setp
->re
[i
] = s_strdup(rules
[i
].re
);
5176 setp
->re_count
= nrules
;
5177 setp
->headnumeral
= NULL
;
5179 /* put this new cached set on the cached set list */
5180 setp
->next
= head_numeral_set
;
5181 head_numeral_set
= setp
;
5184 * For each RE, search the "reserved" list to create numeral IDs that
5187 for (entry
= enumerate_reserved
; entry
; entry
= entry
->er_next
) {
5189 vprint(RSRV_MID
, "parsing rstring: %s\n", entry
->er_file
);
5191 for (i
= 0; i
< nrules
; i
++) {
5192 path_left
= s_strdup(setp
->re
[i
]);
5193 vprint(RSRV_MID
, "parsing rule RE: %s\n", path_left
);
5194 ret
= enumerate_parse(entry
->er_file
, path_left
,
5199 * We found the reserved ID for this entry.
5200 * We still keep the entry since it is needed
5201 * by the new link bypass code in disks
5203 vprint(RSRV_MID
, "found rsv ID: rstring: %s "
5204 "rule RE: %s\n", entry
->er_file
, path_left
);
5211 * For each RE, search disk and cache any matches on the
5214 for (i
= 0; i
< nrules
; i
++) {
5215 path_left
= s_strdup(setp
->re
[i
]);
5216 enumerate_recurse(dev_dir
, path_left
, setp
, rules
, i
);
5221 dump_enum_cache(setp
);
5229 * This function stats the pathname namebuf. If this is a directory
5230 * entry, we recurse down dname/fname until we find the first symbolic
5231 * link, and then stat and return it. This is valid for the same reason
5232 * that we only need to read a single pathname for multiple matching
5233 * logical ID's... ie, all the logical nodes should contain identical
5234 * physical paths for the parts we are interested.
5237 get_stat_info(char *namebuf
, struct stat
*sb
)
5240 finddevhdl_t fhandle
;
5243 if (lstat(namebuf
, sb
) < 0) {
5244 (void) err_print(LSTAT_FAILED
, namebuf
, strerror(errno
));
5245 return (DEVFSADM_FAILURE
);
5248 if ((sb
->st_mode
& S_IFMT
) == S_IFLNK
) {
5249 return (DEVFSADM_SUCCESS
);
5253 * If it is a dir, recurse down until we find a link and
5254 * then use the link.
5256 if ((sb
->st_mode
& S_IFMT
) == S_IFDIR
) {
5258 if (finddev_readdir(namebuf
, &fhandle
) != 0) {
5259 return (DEVFSADM_FAILURE
);
5263 * Search each dir entry looking for a symlink. Return
5264 * the first symlink found in namebuf. Recurse dirs.
5266 while ((fp
= finddev_next(fhandle
)) != NULL
) {
5267 cp
= namebuf
+ strlen(namebuf
);
5268 if ((strlcat(namebuf
, "/", PATH_MAX
) >= PATH_MAX
) ||
5269 (strlcat(namebuf
, fp
, PATH_MAX
) >= PATH_MAX
)) {
5271 finddev_close(fhandle
);
5272 return (DEVFSADM_FAILURE
);
5274 if (get_stat_info(namebuf
, sb
) == DEVFSADM_SUCCESS
) {
5275 finddev_close(fhandle
);
5276 return (DEVFSADM_SUCCESS
);
5280 finddev_close(fhandle
);
5283 /* no symlink found, so return error */
5284 return (DEVFSADM_FAILURE
);
5288 * An existing matching ID was not found, so this function is called to
5289 * create the next lowest ID. In the INTEGER case, return the next
5290 * lowest unused integer. In the case of LETTER, return the next lowest
5291 * unused letter. Return empty string if all 26 are used.
5292 * Only IDs >= min will be returned.
5295 new_id(numeral_t
*numeral
, int type
, char *min
)
5301 temp_t
*head
= NULL
;
5303 static char tempbuff
[8];
5306 if (type
== LETTER
) {
5310 if (numeral
== NULL
) {
5311 return (s_strdup(min
));
5314 for (i
= 0; i
< 26; i
++) {
5318 for (np
= numeral
; np
!= NULL
; np
= np
->next
) {
5319 assert(np
->flags
== 0 ||
5320 np
->flags
== NUMERAL_RESERVED
);
5321 letter
[*np
->id
- 'a']++;
5326 for (i
= imin
; i
< 26; i
++) {
5327 if (letter
[i
] == 0) {
5328 retval
= s_malloc(2);
5329 retval
[0] = 'a' + i
;
5335 return (s_strdup(""));
5338 if (type
== INTEGER
) {
5340 if (numeral
== NULL
) {
5341 return (s_strdup(min
));
5347 for (np
= numeral
; np
!= NULL
; np
= np
->next
) {
5348 assert(np
->flags
== 0 ||
5349 np
->flags
== NUMERAL_RESERVED
);
5350 temp
= s_malloc(sizeof (temp_t
));
5351 temp
->integer
= atoi(np
->id
);
5355 for (ptr
= head
; ptr
!= NULL
; ptr
= ptr
->next
) {
5356 if (temp
->integer
< ptr
->integer
) {
5361 previous
= &(ptr
->next
);
5368 /* now search sorted list for first hole >= imin */
5369 for (ptr
= head
; ptr
!= NULL
; ptr
= ptr
->next
) {
5370 if (imin
== ptr
->integer
) {
5373 if (imin
< ptr
->integer
) {
5380 /* free temp list */
5381 for (ptr
= head
; ptr
!= NULL
; ) {
5387 (void) sprintf(tempbuff
, "%d", imin
);
5388 return (s_strdup(tempbuff
));
5391 return (s_strdup(""));
5395 enumerate_parse(char *rsvstr
, char *path_left
, numeral_set_t
*setp
,
5396 devfsadm_enumerate_t rules
[], int index
)
5398 char *slash1
= NULL
;
5399 char *slash2
= NULL
;
5401 char *path_left_save
;
5404 static int warned
= 0;
5406 rsvstr_save
= rsvstr
;
5407 path_left_save
= path_left
;
5409 if (rsvstr
== NULL
|| rsvstr
[0] == '\0' || rsvstr
[0] == '/') {
5411 err_print("invalid reserved filepath: %s\n",
5412 rsvstr
? rsvstr
: "<NULL>");
5418 vprint(RSRV_MID
, "processing rule: %s, rstring: %s\n",
5423 /* get rid of any extra '/' in the reserve string */
5424 while (*rsvstr
== '/') {
5428 /* get rid of any extra '/' in the RE */
5429 while (*path_left
== '/') {
5433 if (slash1
= strchr(path_left
, '/')) {
5436 if (slash2
= strchr(rsvstr
, '/')) {
5440 if ((slash1
!= NULL
) ^ (slash2
!= NULL
)) {
5442 vprint(RSRV_MID
, "mismatch in # of path components\n");
5447 * Returns true if path_left matches the list entry.
5448 * If it is the last path component, pass subexp
5449 * so that it will return the corresponding ID in
5453 if (match_path_component(path_left
, rsvstr
, &numeral_id
,
5454 slash1
? 0 : rules
[index
].subexp
)) {
5456 /* We have a match. */
5457 if (slash1
== NULL
) {
5458 /* Is last path component */
5459 vprint(RSRV_MID
, "match and last component\n");
5460 create_reserved_numeral(setp
, numeral_id
);
5461 if (numeral_id
!= NULL
) {
5467 /* Not last path component. Continue parsing */
5470 path_left
= slash1
+ 1;
5471 rsvstr
= slash2
+ 1;
5473 "match and NOT last component\n");
5479 vprint(RSRV_MID
, "No match: rule RE = %s, "
5480 "rstring = %s\n", path_left
, rsvstr
);
5492 vprint(RSRV_MID
, "match: rule RE: %s, rstring: %s\n",
5493 path_left_save
, rsvstr_save
);
5495 vprint(RSRV_MID
, "NO match: rule RE: %s, rstring: %s\n",
5496 path_left_save
, rsvstr_save
);
5503 * Search current_dir for all files which match the first path component
5504 * of path_left, which is an RE. If a match is found, but there are more
5505 * components of path_left, then recurse, otherwise, if we have reached
5506 * the last component of path_left, call create_cached_numerals for each
5507 * file. At some point, recurse_dev_re() should be rewritten so that this
5508 * function can be eliminated.
5511 enumerate_recurse(char *current_dir
, char *path_left
, numeral_set_t
*setp
,
5512 devfsadm_enumerate_t rules
[], int index
)
5517 finddevhdl_t fhandle
;
5520 if (finddev_readdir(current_dir
, &fhandle
) != 0) {
5524 /* get rid of any extra '/' */
5525 while (*path_left
== '/') {
5529 if (slash
= strchr(path_left
, '/')) {
5533 while ((fp
= finddev_next(fhandle
)) != NULL
) {
5536 * Returns true if path_left matches the list entry.
5537 * If it is the last path component, pass subexp
5538 * so that it will return the corresponding ID in
5542 if (match_path_component(path_left
, (char *)fp
, &numeral_id
,
5543 slash
? 0 : rules
[index
].subexp
)) {
5545 new_path
= s_malloc(strlen(current_dir
) +
5548 (void) strcpy(new_path
, current_dir
);
5549 (void) strcat(new_path
, "/");
5550 (void) strcat(new_path
, fp
);
5552 if (slash
!= NULL
) {
5553 enumerate_recurse(new_path
, slash
+ 1,
5554 setp
, rules
, index
);
5556 create_cached_numeral(new_path
, setp
,
5557 numeral_id
, rules
, index
);
5558 if (numeral_id
!= NULL
) {
5566 if (slash
!= NULL
) {
5569 finddev_close(fhandle
);
5574 * Returns true if file matches file_re. If subexp is non-zero, it means
5575 * we are searching the last path component and need to return the
5576 * parenthesized subexpression subexp in id.
5580 match_path_component(char *file_re
, char *file
, char **id
, int subexp
)
5588 nelements
= subexp
+ 1;
5590 (regmatch_t
*)s_malloc(sizeof (regmatch_t
) * nelements
);
5596 if (regcomp(&re1
, file_re
, REG_EXTENDED
) != 0) {
5597 if (pmatch
!= NULL
) {
5603 if (regexec(&re1
, file
, nelements
, pmatch
, 0) == 0) {
5607 if ((match
!= 0) && (subexp
!= 0)) {
5608 int size
= pmatch
[subexp
].rm_eo
- pmatch
[subexp
].rm_so
;
5609 *id
= s_malloc(size
+ 1);
5610 (void) strncpy(*id
, &file
[pmatch
[subexp
].rm_so
], size
);
5614 if (pmatch
!= NULL
) {
5622 create_reserved_numeral(numeral_set_t
*setp
, char *numeral_id
)
5626 vprint(RSRV_MID
, "Attempting to create reserved numeral: %s\n",
5630 * We found a numeral_id from an entry in the enumerate_reserved file
5631 * which matched the re passed in from devfsadm_enumerate. We only
5632 * need to make sure ONE copy of numeral_id exists on the numeral list.
5633 * We only need to store /dev/dsk/cNtod0s0 and no other entries
5634 * hanging off of controller N.
5636 for (np
= setp
->headnumeral
; np
!= NULL
; np
= np
->next
) {
5637 if (strcmp(numeral_id
, np
->id
) == 0) {
5638 vprint(RSRV_MID
, "ID: %s, already reserved\n", np
->id
);
5639 assert(np
->flags
== NUMERAL_RESERVED
);
5642 assert(np
->flags
== 0 ||
5643 np
->flags
== NUMERAL_RESERVED
);
5647 /* NOT on list, so add it */
5648 np
= s_malloc(sizeof (numeral_t
));
5649 np
->id
= s_strdup(numeral_id
);
5650 np
->full_path
= NULL
;
5653 np
->flags
= NUMERAL_RESERVED
;
5654 np
->next
= setp
->headnumeral
;
5655 setp
->headnumeral
= np
;
5657 vprint(RSRV_MID
, "Reserved numeral ID: %s\n", np
->id
);
5661 * This function is called for every file which matched the leaf
5662 * component of the RE. If the "numeral_id" is not already on the
5663 * numeral set's numeral list, add it and its physical path.
5666 create_cached_numeral(char *path
, numeral_set_t
*setp
, char *numeral_id
,
5667 devfsadm_enumerate_t rules
[], int index
)
5669 char linkbuf
[PATH_MAX
+ 1];
5670 char lpath
[PATH_MAX
+ 1];
5671 char *linkptr
, *cmp_str
;
5676 const char *fcn
= "create_cached_numeral";
5678 assert(index
>= 0 && index
< setp
->re_count
);
5679 assert(strcmp(rules
[index
].re
, setp
->re
[index
]) == 0);
5682 * We found a numeral_id from an entry in /dev which matched
5683 * the re passed in from devfsadm_enumerate. We only need to make sure
5684 * ONE copy of numeral_id exists on the numeral list. We only need
5685 * to store /dev/dsk/cNtod0s0 and no other entries hanging off
5688 for (np
= setp
->headnumeral
; np
!= NULL
; np
= np
->next
) {
5689 assert(np
->flags
== 0 || np
->flags
== NUMERAL_RESERVED
);
5690 if (strcmp(numeral_id
, np
->id
) == 0) {
5692 * Note that we can't assert that the flags field
5693 * of the numeral is 0, since both reserved and
5694 * unreserved links in /dev come here
5696 if (np
->flags
== NUMERAL_RESERVED
) {
5697 vprint(RSRV_MID
, "ID derived from /dev link is"
5698 " reserved: %s\n", np
->id
);
5700 vprint(RSRV_MID
, "ID derived from /dev link is"
5701 " NOT reserved: %s\n", np
->id
);
5707 /* NOT on list, so add it */
5709 (void) strcpy(lpath
, path
);
5711 * If path is a dir, it is changed to the first symbolic link it find
5714 if (get_stat_info(lpath
, &sb
) == DEVFSADM_FAILURE
) {
5718 /* If we get here, we found a symlink */
5719 linksize
= readlink(lpath
, linkbuf
, PATH_MAX
);
5721 if (linksize
<= 0) {
5722 err_print(READLINK_FAILED
, fcn
, lpath
, strerror(errno
));
5726 linkbuf
[linksize
] = '\0';
5729 * redirect alias path to current path
5730 * devi_root_node is protected by lock_dev()
5732 contents
= di_alias2curr(devi_root_node
, linkbuf
);
5735 * the following just points linkptr to the root of the /devices
5736 * node if it is a minor node, otherwise, to the first char of
5737 * linkbuf if it is a link.
5739 (void) is_minor_node(contents
, &linkptr
);
5741 cmp_str
= alloc_cmp_str(linkptr
, &rules
[index
]);
5742 if (cmp_str
== NULL
) {
5747 np
= s_malloc(sizeof (numeral_t
));
5749 np
->id
= s_strdup(numeral_id
);
5750 np
->full_path
= s_strdup(linkptr
);
5751 np
->rule_index
= index
;
5752 np
->cmp_str
= cmp_str
;
5755 np
->next
= setp
->headnumeral
;
5756 setp
->headnumeral
= np
;
5763 * This should be called either before or after granting access to a
5764 * command line version of devfsadm running, since it may have changed
5765 * the state of /dev. It forces future enumerate calls to re-build
5766 * cached information from /dev.
5769 invalidate_enumerate_cache(void)
5771 numeral_set_t
*setp
;
5772 numeral_set_t
*savedsetp
;
5773 numeral_t
*savednumset
;
5777 for (setp
= head_numeral_set
; setp
!= NULL
; ) {
5779 * check all regexp's passed in function against
5780 * those in cached set.
5786 for (i
= 0; i
< savedsetp
->re_count
; i
++) {
5787 free(savedsetp
->re
[i
]);
5789 free(savedsetp
->re
);
5791 for (numset
= savedsetp
->headnumeral
; numset
!= NULL
; ) {
5792 savednumset
= numset
;
5793 numset
= numset
->next
;
5794 assert(savednumset
->rule_index
< savedsetp
->re_count
);
5795 free(savednumset
->id
);
5796 free(savednumset
->full_path
);
5797 free(savednumset
->cmp_str
);
5802 head_numeral_set
= NULL
;
5806 * Copies over links from /dev to <root>/dev and device special files in
5807 * /devices to <root>/devices, preserving the existing file modes. If
5808 * the link or special file already exists on <root>, skip the copy. (it
5809 * would exist only if a package hard coded it there, so assume package
5810 * knows best?). Use /etc/name_to_major and <root>/etc/name_to_major to
5811 * make translations for major numbers on device special files. No need to
5812 * make a translation on minor_perm since if the file was created in the
5813 * miniroot then it would presumably have the same minor_perm entry in
5814 * <root>/etc/minor_perm. To be used only by install.
5819 char filename
[PATH_MAX
+ 1];
5821 /* load the installed root's name_to_major for translations */
5822 (void) snprintf(filename
, sizeof (filename
), "%s%s", root_dir
,
5824 if (load_n2m_table(filename
) == DEVFSADM_FAILURE
) {
5825 return (DEVFSADM_FAILURE
);
5828 /* Copy /dev to target disk. No need to copy /devices with devfs */
5829 (void) nftw(DEV
, devfsadm_copy_file
, 20, FTW_PHYS
);
5831 /* Let install handle copying over path_to_inst */
5833 return (DEVFSADM_SUCCESS
);
5837 * This function copies links, dirs, and device special files.
5838 * Note that it always returns DEVFSADM_SUCCESS, so that nftw doesn't
5843 devfsadm_copy_file(const char *file
, const struct stat
*stat
,
5844 int flags
, struct FTW
*ftw
)
5848 char newfile
[PATH_MAX
+ 1];
5849 char linkcontents
[PATH_MAX
+ 1];
5851 const char *fcn
= "devfsadm_copy_file";
5853 (void) strcpy(newfile
, root_dir
);
5854 (void) strcat(newfile
, "/");
5855 (void) strcat(newfile
, file
);
5857 if (lstat(newfile
, &sp
) == 0) {
5858 /* newfile already exists, so no need to continue */
5859 return (DEVFSADM_SUCCESS
);
5862 if (((stat
->st_mode
& S_IFMT
) == S_IFBLK
) ||
5863 ((stat
->st_mode
& S_IFMT
) == S_IFCHR
)) {
5864 if (translate_major(stat
->st_rdev
, &newdev
) ==
5866 return (DEVFSADM_SUCCESS
);
5868 if (mknod(newfile
, stat
->st_mode
, newdev
) == -1) {
5869 err_print(MKNOD_FAILED
, newfile
, strerror(errno
));
5870 return (DEVFSADM_SUCCESS
);
5872 } else if ((stat
->st_mode
& S_IFMT
) == S_IFDIR
) {
5873 if (mknod(newfile
, stat
->st_mode
, 0) == -1) {
5874 err_print(MKNOD_FAILED
, newfile
, strerror(errno
));
5875 return (DEVFSADM_SUCCESS
);
5877 } else if ((stat
->st_mode
& S_IFMT
) == S_IFLNK
) {
5879 * No need to redirect alias paths. We want a
5880 * true copy. The system on first boot after install
5881 * will redirect paths
5883 if ((bytes
= readlink(file
, linkcontents
, PATH_MAX
)) == -1) {
5884 err_print(READLINK_FAILED
, fcn
, file
, strerror(errno
));
5885 return (DEVFSADM_SUCCESS
);
5887 linkcontents
[bytes
] = '\0';
5888 if (symlink(linkcontents
, newfile
) == -1) {
5889 err_print(SYMLINK_FAILED
, newfile
, newfile
,
5891 return (DEVFSADM_SUCCESS
);
5895 (void) lchown(newfile
, stat
->st_uid
, stat
->st_gid
);
5896 return (DEVFSADM_SUCCESS
);
5900 * Given a dev_t from the running kernel, return the new_dev_t
5901 * by translating to the major number found on the installed
5902 * target's root name_to_major file.
5905 translate_major(dev_t old_dev
, dev_t
*new_dev
)
5911 char cdriver
[FILENAME_MAX
+ 1];
5912 char driver
[FILENAME_MAX
+ 1];
5913 char *fcn
= "translate_major: ";
5915 oldmajor
= major(old_dev
);
5916 if (modctl(MODGETNAME
, driver
, sizeof (driver
), &oldmajor
) != 0) {
5917 return (DEVFSADM_FAILURE
);
5920 if (strcmp(driver
, "clone") != 0) {
5921 /* non-clone case */
5923 /* look up major number is target's name2major */
5924 if (get_major_no(driver
, &newmajor
) == DEVFSADM_FAILURE
) {
5925 return (DEVFSADM_FAILURE
);
5928 *new_dev
= makedev(newmajor
, minor(old_dev
));
5929 if (old_dev
!= *new_dev
) {
5930 vprint(CHATTY_MID
, "%sdriver: %s old: %lu,%lu "
5931 "new: %lu,%lu\n", fcn
, driver
, major(old_dev
),
5932 minor(old_dev
), major(*new_dev
), minor(*new_dev
));
5934 return (DEVFSADM_SUCCESS
);
5937 * The clone is a special case. Look at its minor
5938 * number since it is the major number of the real driver.
5940 if (get_major_no(driver
, &newmajor
) == DEVFSADM_FAILURE
) {
5941 return (DEVFSADM_FAILURE
);
5944 oldminor
= minor(old_dev
);
5945 if (modctl(MODGETNAME
, cdriver
, sizeof (cdriver
),
5947 err_print(MODGETNAME_FAILED
, oldminor
);
5948 return (DEVFSADM_FAILURE
);
5951 if (get_major_no(cdriver
, &newminor
) == DEVFSADM_FAILURE
) {
5952 return (DEVFSADM_FAILURE
);
5955 *new_dev
= makedev(newmajor
, newminor
);
5956 if (old_dev
!= *new_dev
) {
5957 vprint(CHATTY_MID
, "%sdriver: %s old: "
5958 "%lu,%lu new: %lu,%lu\n", fcn
, driver
,
5959 major(old_dev
), minor(old_dev
),
5960 major(*new_dev
), minor(*new_dev
));
5962 return (DEVFSADM_SUCCESS
);
5968 * Find the major number for driver, searching the n2m_list that was
5969 * built in load_n2m_table().
5972 get_major_no(char *driver
, major_t
*major
)
5976 for (ptr
= n2m_list
; ptr
!= NULL
; ptr
= ptr
->next
) {
5977 if (strcmp(ptr
->driver
, driver
) == 0) {
5978 *major
= ptr
->major
;
5979 return (DEVFSADM_SUCCESS
);
5982 err_print(FIND_MAJOR_FAILED
, driver
);
5983 return (DEVFSADM_FAILURE
);
5987 * Loads a name_to_major table into memory. Used only for suninstall's
5988 * private -R option to devfsadm, to translate major numbers from the
5989 * running to the installed target disk.
5992 load_n2m_table(char *file
)
5995 char line
[1024], *cp
;
5996 char driver
[PATH_MAX
+ 1];
6001 if ((fp
= fopen(file
, "r")) == NULL
) {
6002 err_print(FOPEN_FAILED
, file
, strerror(errno
));
6003 return (DEVFSADM_FAILURE
);
6006 while (fgets(line
, sizeof (line
), fp
) != NULL
) {
6008 /* cut off comments starting with '#' */
6009 if ((cp
= strchr(line
, '#')) != NULL
)
6011 /* ignore comment or blank lines */
6015 if (sscanf(line
, "%1024s%lu", driver
, &major
) != 2) {
6016 err_print(IGNORING_LINE_IN
, ln
, file
);
6019 ptr
= (n2m_t
*)s_malloc(sizeof (n2m_t
));
6021 ptr
->driver
= s_strdup(driver
);
6022 ptr
->next
= n2m_list
;
6025 if (fclose(fp
) == EOF
) {
6026 err_print(FCLOSE_FAILED
, file
, strerror(errno
));
6028 return (DEVFSADM_SUCCESS
);
6032 * Called at devfsadm startup to read the file /etc/dev/enumerate_reserved
6033 * Creates a linked list of devlinks from which reserved IDs can be derived
6036 read_enumerate_file(void)
6040 char line
[PATH_MAX
+1];
6041 enumerate_file_t
*entry
;
6042 struct stat current_sb
;
6043 static struct stat cached_sb
;
6044 static int cached
= FALSE
;
6046 assert(enumerate_file
);
6048 if (stat(enumerate_file
, ¤t_sb
) == -1) {
6049 vprint(RSRV_MID
, "No reserved file: %s\n", enumerate_file
);
6051 if (enumerate_reserved
!= NULL
) {
6052 vprint(RSRV_MID
, "invalidating %s cache\n",
6055 while (enumerate_reserved
!= NULL
) {
6056 entry
= enumerate_reserved
;
6057 enumerate_reserved
= entry
->er_next
;
6058 free(entry
->er_file
);
6065 /* if already cached, check to see if it is still valid */
6066 if (cached
== TRUE
) {
6068 if (current_sb
.st_mtime
== cached_sb
.st_mtime
) {
6069 vprint(RSRV_MID
, "%s cache valid\n", enumerate_file
);
6070 vprint(FILES_MID
, "%s cache valid\n", enumerate_file
);
6074 vprint(RSRV_MID
, "invalidating %s cache\n", enumerate_file
);
6075 vprint(FILES_MID
, "invalidating %s cache\n", enumerate_file
);
6077 while (enumerate_reserved
!= NULL
) {
6078 entry
= enumerate_reserved
;
6079 enumerate_reserved
= entry
->er_next
;
6080 free(entry
->er_file
);
6084 vprint(RSRV_MID
, "Recaching file: %s\n", enumerate_file
);
6086 vprint(RSRV_MID
, "Caching file (first time): %s\n",
6091 (void) stat(enumerate_file
, &cached_sb
);
6093 if ((fp
= fopen(enumerate_file
, "r")) == NULL
) {
6094 err_print(FOPEN_FAILED
, enumerate_file
, strerror(errno
));
6098 vprint(RSRV_MID
, "Reading reserve file: %s\n", enumerate_file
);
6100 while (fgets(line
, sizeof (line
), fp
) != NULL
) {
6105 /* remove newline */
6106 cp
= strchr(line
, '\n');
6110 vprint(RSRV_MID
, "Reserve file: line %d: %s\n", linenum
, line
);
6112 /* skip over space and tab */
6113 for (cp
= line
; *cp
== ' ' || *cp
== '\t'; cp
++)
6116 if (*cp
== '\0' || *cp
== '#') {
6117 vprint(RSRV_MID
, "Skipping line: '%s'\n", line
);
6118 continue; /* blank line or comment line */
6123 /* delete trailing blanks */
6124 for (; *cp
!= ' ' && *cp
!= '\t' && *cp
!= '\0'; cp
++)
6128 entry
= s_zalloc(sizeof (enumerate_file_t
));
6129 entry
->er_file
= s_strdup(ncp
);
6130 entry
->er_id
= NULL
;
6131 entry
->er_next
= enumerate_reserved
;
6132 enumerate_reserved
= entry
;
6135 if (fclose(fp
) == EOF
) {
6136 err_print(FCLOSE_FAILED
, enumerate_file
, strerror(errno
));
6141 * Called at devfsadm startup to read in the devlink.tab file. Creates
6142 * a linked list of devlinktab_list structures which will be
6143 * searched for every minor node.
6146 read_devlinktab_file(void)
6148 devlinktab_list_t
*headp
= NULL
;
6149 devlinktab_list_t
*entryp
;
6150 devlinktab_list_t
**previous
;
6151 devlinktab_list_t
*save
;
6152 char line
[MAX_DEVLINK_LINE
], *cp
;
6158 static struct stat cached_sb
;
6159 struct stat current_sb
;
6160 static int cached
= FALSE
;
6162 if (devlinktab_file
== NULL
) {
6166 (void) stat(devlinktab_file
, ¤t_sb
);
6168 /* if already cached, check to see if it is still valid */
6169 if (cached
== TRUE
) {
6171 if (current_sb
.st_mtime
== cached_sb
.st_mtime
) {
6172 vprint(FILES_MID
, "%s cache valid\n", devlinktab_file
);
6176 vprint(FILES_MID
, "invalidating %s cache\n", devlinktab_file
);
6178 while (devlinktab_list
!= NULL
) {
6179 free_link_list(devlinktab_list
->p_link
);
6180 free_link_list(devlinktab_list
->s_link
);
6181 free_selector_list(devlinktab_list
->selector
);
6182 free(devlinktab_list
->selector_pattern
);
6183 free(devlinktab_list
->p_link_pattern
);
6184 if (devlinktab_list
->s_link_pattern
!= NULL
) {
6185 free(devlinktab_list
->s_link_pattern
);
6187 save
= devlinktab_list
;
6188 devlinktab_list
= devlinktab_list
->next
;
6195 (void) stat(devlinktab_file
, &cached_sb
);
6197 if ((fp
= fopen(devlinktab_file
, "r")) == NULL
) {
6198 err_print(FOPEN_FAILED
, devlinktab_file
, strerror(errno
));
6204 while (fgets(line
, sizeof (line
), fp
) != NULL
) {
6207 if (line
[i
-1] == NEWLINE
) {
6209 } else if (i
== sizeof (line
-1)) {
6210 err_print(LINE_TOO_LONG
, devlinktab_line
,
6211 devlinktab_file
, sizeof (line
)-1);
6212 while (((i
= getc(fp
)) != '\n') && (i
!= EOF
))
6217 /* cut off comments starting with '#' */
6218 if ((cp
= strchr(line
, '#')) != NULL
)
6220 /* ignore comment or blank lines */
6224 vprint(DEVLINK_MID
, "table: %s line %d: '%s'\n",
6225 devlinktab_file
, devlinktab_line
, line
);
6227 /* break each entry into fields. s_link may be NULL */
6228 if (split_devlinktab_entry(line
, &selector
, &p_link
,
6229 &s_link
) == DEVFSADM_FAILURE
) {
6230 vprint(DEVLINK_MID
, "split_entry returns failure\n");
6233 vprint(DEVLINK_MID
, "split_entry selector='%s' "
6234 "p_link='%s' s_link='%s'\n\n", selector
,
6235 p_link
, (s_link
== NULL
) ? "" : s_link
);
6239 (devlinktab_list_t
*)s_malloc(sizeof (devlinktab_list_t
));
6241 entryp
->line_number
= devlinktab_line
;
6243 if ((entryp
->selector
= create_selector_list(selector
))
6248 entryp
->selector_pattern
= s_strdup(selector
);
6250 if ((entryp
->p_link
= create_link_list(p_link
)) == NULL
) {
6251 free_selector_list(entryp
->selector
);
6252 free(entryp
->selector_pattern
);
6257 entryp
->p_link_pattern
= s_strdup(p_link
);
6259 if (s_link
!= NULL
) {
6260 if ((entryp
->s_link
=
6261 create_link_list(s_link
)) == NULL
) {
6262 free_selector_list(entryp
->selector
);
6263 free_link_list(entryp
->p_link
);
6264 free(entryp
->selector_pattern
);
6265 free(entryp
->p_link_pattern
);
6269 entryp
->s_link_pattern
= s_strdup(s_link
);
6271 entryp
->s_link
= NULL
;
6272 entryp
->s_link_pattern
= NULL
;
6276 /* append to end of list */
6278 entryp
->next
= NULL
;
6280 previous
= &(entryp
->next
);
6282 if (fclose(fp
) == EOF
) {
6283 err_print(FCLOSE_FAILED
, devlinktab_file
, strerror(errno
));
6285 devlinktab_list
= headp
;
6290 * For a single line entry in devlink.tab, split the line into fields
6291 * selector, p_link, and an optionally s_link. If s_link field is not
6292 * present, then return NULL in s_link (not NULL string).
6295 split_devlinktab_entry(char *entry
, char **selector
, char **p_link
,
6302 if ((tab
= strchr(entry
, TAB
)) != NULL
) {
6306 err_print(MISSING_TAB
, devlinktab_line
, devlinktab_file
);
6307 return (DEVFSADM_FAILURE
);
6310 if (*p_link
== '\0') {
6311 err_print(MISSING_DEVNAME
, devlinktab_line
, devlinktab_file
);
6312 return (DEVFSADM_FAILURE
);
6315 if ((tab
= strchr(*p_link
, TAB
)) != NULL
) {
6318 if (strchr(*s_link
, TAB
) != NULL
) {
6319 err_print(TOO_MANY_FIELDS
, devlinktab_line
,
6321 return (DEVFSADM_FAILURE
);
6327 return (DEVFSADM_SUCCESS
);
6331 * For a given devfs_spec field, for each element in the field, add it to
6332 * a linked list of devfs_spec structures. Return the linked list in
6335 static selector_list_t
*
6336 create_selector_list(char *selector
)
6341 selector_list_t
*head_selector_list
= NULL
;
6342 selector_list_t
*selector_list
;
6344 /* parse_devfs_spec splits the next field into keyword & value */
6345 while ((*selector
!= NULL
) && (error
== FALSE
)) {
6346 if (parse_selector(&selector
, &key
, &val
) == DEVFSADM_FAILURE
) {
6350 selector_list
= (selector_list_t
*)
6351 s_malloc(sizeof (selector_list_t
));
6352 if (strcmp(NAME_S
, key
) == 0) {
6353 selector_list
->key
= NAME
;
6354 } else if (strcmp(TYPE_S
, key
) == 0) {
6355 selector_list
->key
= TYPE
;
6356 } else if (strncmp(ADDR_S
, key
, ADDR_S_LEN
) == 0) {
6357 selector_list
->key
= ADDR
;
6358 if (key
[ADDR_S_LEN
] == '\0') {
6359 selector_list
->arg
= 0;
6360 } else if (isdigit(key
[ADDR_S_LEN
]) != FALSE
) {
6361 selector_list
->arg
=
6362 atoi(&key
[ADDR_S_LEN
]);
6365 free(selector_list
);
6366 err_print(BADKEYWORD
, key
,
6367 devlinktab_line
, devlinktab_file
);
6370 } else if (strncmp(MINOR_S
, key
, MINOR_S_LEN
) == 0) {
6371 selector_list
->key
= MINOR
;
6372 if (key
[MINOR_S_LEN
] == '\0') {
6373 selector_list
->arg
= 0;
6374 } else if (isdigit(key
[MINOR_S_LEN
]) != FALSE
) {
6375 selector_list
->arg
=
6376 atoi(&key
[MINOR_S_LEN
]);
6379 free(selector_list
);
6380 err_print(BADKEYWORD
, key
,
6381 devlinktab_line
, devlinktab_file
);
6384 vprint(DEVLINK_MID
, "MINOR = %s\n", val
);
6386 err_print(UNRECOGNIZED_KEY
, key
,
6387 devlinktab_line
, devlinktab_file
);
6389 free(selector_list
);
6392 selector_list
->val
= s_strdup(val
);
6393 selector_list
->next
= head_selector_list
;
6394 head_selector_list
= selector_list
;
6395 vprint(DEVLINK_MID
, "key='%s' val='%s' arg=%d\n",
6396 key
, val
, selector_list
->arg
);
6400 if ((error
== FALSE
) && (head_selector_list
!= NULL
)) {
6401 return (head_selector_list
);
6403 /* parse failed. Free any allocated structs */
6404 free_selector_list(head_selector_list
);
6410 * Takes a semicolon separated list of selector elements and breaks up
6411 * into a keyword-value pair. semicolon and equal characters are
6412 * replaced with NULL's. On success, selector is updated to point to the
6413 * terminating NULL character terminating the keyword-value pair, and the
6414 * function returns DEVFSADM_SUCCESS. If there is a syntax error,
6415 * devfs_spec is not modified and function returns DEVFSADM_FAILURE.
6418 parse_selector(char **selector
, char **key
, char **val
)
6425 if ((equal
= strchr(*key
, '=')) != NULL
) {
6428 err_print(MISSING_EQUAL
, devlinktab_line
, devlinktab_file
);
6429 return (DEVFSADM_FAILURE
);
6433 if ((semi_colon
= strchr(equal
, ';')) != NULL
) {
6435 *selector
= semi_colon
+ 1;
6437 *selector
= equal
+ strlen(equal
);
6439 return (DEVFSADM_SUCCESS
);
6443 * link is either the second or third field of devlink.tab. Parse link
6444 * into a linked list of devlink structures and return ptr to list. Each
6445 * list element is either a constant string, or one of the following
6446 * escape sequences: \M, \A, \N, or \D. The first three escape sequences
6447 * take a numerical argument.
6449 static link_list_t
*
6450 create_link_list(char *link
)
6454 int counter_found
= FALSE
;
6455 link_list_t
*head
= NULL
;
6457 link_list_t
*link_list
;
6458 char constant
[MAX_DEVLINK_LINE
];
6465 while ((*link
!= '\0') && (error
== FALSE
)) {
6466 link_list
= (link_list_t
*)s_malloc(sizeof (link_list_t
));
6467 link_list
->next
= NULL
;
6469 while ((*link
!= '\0') && (*link
!= '\\')) {
6470 /* a non-escaped string */
6471 constant
[x
++] = *(link
++);
6475 link_list
->type
= CONSTANT
;
6476 link_list
->constant
= s_strdup(constant
);
6478 vprint(DEVLINK_MID
, "CONSTANT FOUND %s\n", constant
);
6480 switch (*(++link
)) {
6482 link_list
->type
= MINOR
;
6485 link_list
->type
= ADDR
;
6488 if (counter_found
== TRUE
) {
6491 "multiple counters not permitted";
6494 counter_found
= TRUE
;
6495 link_list
->type
= COUNTER
;
6499 link_list
->type
= NAME
;
6504 error_str
= "unrecognized escape sequence";
6507 if (*(link
++) != 'D') {
6508 if (isdigit(*link
) == FALSE
) {
6509 error_str
= "escape sequence must be "
6510 "followed by a digit\n";
6515 (int)strtoul(link
, &link
, 10);
6516 vprint(DEVLINK_MID
, "link_list->arg = "
6517 "%d\n", link_list
->arg
);
6521 /* append link_list struct to end of list */
6522 if (error
== FALSE
) {
6523 for (ptr
= &head
; *ptr
!= NULL
; ptr
= &((*ptr
)->next
))
6529 if (error
== FALSE
) {
6532 err_print(CONFIG_INCORRECT
, devlinktab_line
, devlinktab_file
,
6534 free_link_list(head
);
6540 * Called for each minor node devfsadm processes; for each minor node,
6541 * look for matches in the devlinktab_list list which was created on
6542 * startup read_devlinktab_file(). If there is a match, call build_links()
6543 * to build a logical devlink and a possible extra devlink.
6546 process_devlink_compat(di_minor_t minor
, di_node_t node
)
6548 int link_built
= FALSE
;
6549 devlinktab_list_t
*entry
;
6553 if (devlinks_debug
== TRUE
) {
6554 nodetype
= di_minor_nodetype(minor
);
6555 assert(nodetype
!= NULL
);
6556 if ((dev_path
= di_devfs_path(node
)) != NULL
) {
6557 vprint(INFO_MID
, "'%s' entry: %s:%s\n",
6559 di_minor_name(minor
) ? di_minor_name(minor
) : "");
6560 di_devfs_path_free(dev_path
);
6566 /* don't process devlink.tab if devfsadm invoked with -c <class> */
6567 if (num_classes
> 0) {
6571 for (entry
= devlinktab_list
; entry
!= NULL
; entry
= entry
->next
) {
6572 if (devlink_matches(entry
, minor
, node
) == DEVFSADM_SUCCESS
) {
6574 (void) build_links(entry
, minor
, node
);
6577 return (link_built
);
6581 * For a given devlink.tab devlinktab_list entry, see if the selector
6582 * field matches this minor node. If it does, return DEVFSADM_SUCCESS,
6583 * otherwise DEVFSADM_FAILURE.
6586 devlink_matches(devlinktab_list_t
*entry
, di_minor_t minor
, di_node_t node
)
6588 selector_list_t
*selector
= entry
->selector
;
6593 for (; selector
!= NULL
; selector
= selector
->next
) {
6594 switch (selector
->key
) {
6596 if (strcmp(di_node_name(node
), selector
->val
) != 0) {
6597 return (DEVFSADM_FAILURE
);
6601 node_type
= di_minor_nodetype(minor
);
6602 assert(node_type
!= NULL
);
6603 if (strcmp(node_type
, selector
->val
) != 0) {
6604 return (DEVFSADM_FAILURE
);
6608 if ((addr
= di_bus_addr(node
)) == NULL
) {
6609 return (DEVFSADM_FAILURE
);
6611 if (selector
->arg
== 0) {
6612 if (strcmp(addr
, selector
->val
) != 0) {
6613 return (DEVFSADM_FAILURE
);
6616 if (compare_field(addr
, selector
->val
,
6617 selector
->arg
) == DEVFSADM_FAILURE
) {
6618 return (DEVFSADM_FAILURE
);
6623 if ((minor_name
= di_minor_name(minor
)) == NULL
) {
6624 return (DEVFSADM_FAILURE
);
6626 if (selector
->arg
== 0) {
6627 if (strcmp(minor_name
, selector
->val
) != 0) {
6628 return (DEVFSADM_FAILURE
);
6631 if (compare_field(minor_name
, selector
->val
,
6632 selector
->arg
) == DEVFSADM_FAILURE
) {
6633 return (DEVFSADM_FAILURE
);
6638 return (DEVFSADM_FAILURE
);
6642 return (DEVFSADM_SUCCESS
);
6646 * For the given minor node and devlinktab_list entry from devlink.tab,
6647 * build a logical dev link and a possible extra devlink.
6648 * Return DEVFSADM_SUCCESS if link is created, otherwise DEVFSADM_FAILURE.
6651 build_links(devlinktab_list_t
*entry
, di_minor_t minor
, di_node_t node
)
6653 char secondary_link
[PATH_MAX
+ 1];
6654 char primary_link
[PATH_MAX
+ 1];
6655 char contents
[PATH_MAX
+ 1];
6658 if ((dev_path
= di_devfs_path(node
)) == NULL
) {
6659 err_print(DI_DEVFS_PATH_FAILED
, strerror(errno
));
6663 (void) strcpy(contents
, dev_path
);
6664 di_devfs_path_free(dev_path
);
6666 (void) strcat(contents
, ":");
6667 (void) strcat(contents
, di_minor_name(minor
));
6669 if (construct_devlink(primary_link
, entry
->p_link
, contents
,
6670 minor
, node
, entry
->p_link_pattern
) == DEVFSADM_FAILURE
) {
6671 return (DEVFSADM_FAILURE
);
6673 (void) devfsadm_mklink(primary_link
, node
, minor
, 0);
6675 if (entry
->s_link
== NULL
) {
6676 return (DEVFSADM_SUCCESS
);
6679 if (construct_devlink(secondary_link
, entry
->s_link
, primary_link
,
6680 minor
, node
, entry
->s_link_pattern
) == DEVFSADM_FAILURE
) {
6681 return (DEVFSADM_FAILURE
);
6684 (void) devfsadm_secondary_link(secondary_link
, primary_link
, 0);
6686 return (DEVFSADM_SUCCESS
);
6690 * The counter rule for devlink.tab entries is implemented via
6691 * devfsadm_enumerate_int_start(). One of the arguments to this function
6692 * is a path, where each path component is treated as a regular expression.
6693 * For devlink.tab entries, this path regular expression is derived from
6694 * the devlink spec. get_anchored_re() accepts path regular expressions derived
6695 * from devlink.tab entries and inserts the anchors '^' and '$' at the beginning
6696 * and end respectively of each path component. This is done to prevent
6697 * false matches. For example, without anchors, "a/([0-9]+)" will match "ab/c9"
6698 * and incorrect links will be generated.
6701 get_anchored_re(char *link
, char *anchored_re
, char *pattern
)
6703 if (*link
== '/' || *link
== '\0') {
6704 err_print(INVALID_DEVLINK_SPEC
, pattern
);
6705 return (DEVFSADM_FAILURE
);
6708 *anchored_re
++ = '^';
6709 for (; *link
!= '\0'; ) {
6711 while (*link
== '/')
6713 *anchored_re
++ = '$';
6714 *anchored_re
++ = '/';
6715 if (*link
!= '\0') {
6716 *anchored_re
++ = '^';
6719 *anchored_re
++ = *link
++;
6720 if (*link
== '\0') {
6721 *anchored_re
++ = '$';
6725 *anchored_re
= '\0';
6727 return (DEVFSADM_SUCCESS
);
6731 construct_devlink(char *link
, link_list_t
*link_build
, char *contents
,
6732 di_minor_t minor
, di_node_t node
, char *pattern
)
6734 int counter_offset
= -1;
6735 devfsadm_enumerate_t rules
[1] = {NULL
};
6736 char templink
[PATH_MAX
+ 1];
6740 char anchored_re
[PATH_MAX
+ 1];
6744 for (; link_build
!= NULL
; link_build
= link_build
->next
) {
6745 switch (link_build
->type
) {
6747 (void) strcat(link
, di_node_name(node
));
6750 (void) strcat(link
, link_build
->constant
);
6753 if (component_cat(link
, di_bus_addr(node
),
6754 link_build
->arg
) == DEVFSADM_FAILURE
) {
6755 node_path
= di_devfs_path(node
);
6756 err_print(CANNOT_BE_USED
, pattern
, node_path
,
6757 di_minor_name(minor
));
6758 di_devfs_path_free(node_path
);
6759 return (DEVFSADM_FAILURE
);
6763 if (component_cat(link
, di_minor_name(minor
),
6764 link_build
->arg
) == DEVFSADM_FAILURE
) {
6765 node_path
= di_devfs_path(node
);
6766 err_print(CANNOT_BE_USED
, pattern
, node_path
,
6767 di_minor_name(minor
));
6768 di_devfs_path_free(node_path
);
6769 return (DEVFSADM_FAILURE
);
6773 counter_offset
= strlen(link
);
6774 (void) strcat(link
, "([0-9]+)");
6775 (void) sprintf(start
, "%d", link_build
->arg
);
6778 return (DEVFSADM_FAILURE
);
6782 if (counter_offset
!= -1) {
6784 * copy anything appended after "([0-9]+)" into
6788 (void) strcpy(templink
,
6789 &link
[counter_offset
+ strlen("([0-9]+)")]);
6790 if (get_anchored_re(link
, anchored_re
, pattern
)
6791 != DEVFSADM_SUCCESS
) {
6792 return (DEVFSADM_FAILURE
);
6794 rules
[0].re
= anchored_re
;
6795 rules
[0].subexp
= 1;
6796 rules
[0].flags
= MATCH_ALL
;
6797 if (devfsadm_enumerate_int_start(contents
, 0, &buff
,
6798 rules
, 1, start
) == DEVFSADM_FAILURE
) {
6799 return (DEVFSADM_FAILURE
);
6801 (void) strcpy(&link
[counter_offset
], buff
);
6803 (void) strcat(link
, templink
);
6804 vprint(DEVLINK_MID
, "COUNTER is %s\n", link
);
6806 return (DEVFSADM_SUCCESS
);
6810 * Compares "field" number of the comma separated list "full_name" with
6811 * field_item. Returns DEVFSADM_SUCCESS for match,
6812 * DEVFSADM_FAILURE for no match.
6815 compare_field(char *full_name
, char *field_item
, int field
)
6818 while ((*full_name
!= '\0') && (field
!= 0)) {
6819 if (*(full_name
++) == ',') {
6825 return (DEVFSADM_FAILURE
);
6828 while ((*full_name
!= '\0') && (*field_item
!= '\0') &&
6829 (*full_name
!= ',')) {
6830 if (*(full_name
++) != *(field_item
++)) {
6831 return (DEVFSADM_FAILURE
);
6835 if (*field_item
!= '\0') {
6836 return (DEVFSADM_FAILURE
);
6839 if ((*full_name
== '\0') || (*full_name
== ','))
6840 return (DEVFSADM_SUCCESS
);
6842 return (DEVFSADM_FAILURE
);
6846 * strcat() field # "field" of comma separated list "name" to "link".
6847 * Field 0 is the entire name.
6848 * Return DEVFSADM_SUCCESS or DEVFSADM_FAILURE.
6851 component_cat(char *link
, char *name
, int field
)
6855 return (DEVFSADM_FAILURE
);
6859 (void) strcat(link
, name
);
6860 return (DEVFSADM_SUCCESS
);
6863 while (*link
!= '\0') {
6868 while ((*name
!= '\0') && (field
!= 0)) {
6869 if (*(name
++) == ',') {
6875 return (DEVFSADM_FAILURE
);
6878 while ((*name
!= '\0') && (*name
!= ',')) {
6879 *(link
++) = *(name
++);
6883 return (DEVFSADM_SUCCESS
);
6887 free_selector_list(selector_list_t
*head
)
6889 selector_list_t
*temp
;
6891 while (head
!= NULL
) {
6900 free_link_list(link_list_t
*head
)
6904 while (head
!= NULL
) {
6907 if (temp
->type
== CONSTANT
) {
6908 free(temp
->constant
);
6915 * Prints only if level matches one of the debug levels
6916 * given on command line. INFO_MID is always printed.
6918 * See devfsadm.h for a listing of globally defined levels and
6919 * meanings. Modules should prefix the level with their
6920 * module name to prevent collisions.
6924 devfsadm_print(char *msgid
, char *message
, ...)
6927 static int newline
= TRUE
;
6930 if (msgid
!= NULL
) {
6931 for (x
= 0; x
< num_verbose
; x
++) {
6932 if (strcmp(verbose
[x
], msgid
) == 0) {
6935 if (strcmp(verbose
[x
], ALL_MID
) == 0) {
6939 if (x
== num_verbose
) {
6944 va_start(ap
, message
);
6946 if (msgid
== NULL
) {
6947 if (logflag
== TRUE
) {
6948 (void) vsyslog(LOG_NOTICE
, message
, ap
);
6950 (void) vfprintf(stdout
, message
, ap
);
6954 if (logflag
== TRUE
) {
6955 (void) syslog(LOG_DEBUG
, "%s[%ld]: %s: ",
6956 prog
, getpid(), msgid
);
6957 (void) vsyslog(LOG_DEBUG
, message
, ap
);
6959 if (newline
== TRUE
) {
6960 (void) fprintf(stdout
, "%s[%ld]: %s: ",
6961 prog
, getpid(), msgid
);
6963 (void) vfprintf(stdout
, message
, ap
);
6967 if (message
[strlen(message
) - 1] == '\n') {
6976 * print error messages to the terminal or to syslog
6980 devfsadm_errprint(char *message
, ...)
6984 va_start(ap
, message
);
6986 if (logflag
== TRUE
) {
6987 (void) vsyslog(LOG_ERR
, message
, ap
);
6989 (void) fprintf(stderr
, "%s: ", prog
);
6990 (void) vfprintf(stderr
, message
, ap
);
6996 * return noupdate state (-s)
6999 devfsadm_noupdate(void)
7001 return (file_mods
== TRUE
? DEVFSADM_TRUE
: DEVFSADM_FALSE
);
7005 * return current root update path (-r)
7008 devfsadm_root_path(void)
7010 if (root_dir
[0] == '\0') {
7013 return ((const char *)root_dir
);
7018 devfsadm_free_dev_names(char **dev_names
, int len
)
7022 for (i
= 0; i
< len
; i
++)
7028 * Return all devlinks corresponding to phys_path as an array of strings.
7029 * The number of entries in the array is returned through lenp.
7030 * devfsadm_free_dev_names() is used to free the returned array.
7031 * NULL is returned on failure or when there are no matching devlinks.
7033 * re is an extended regular expression in regex(5) format used to further
7034 * match devlinks pointing to phys_path; it may be NULL to match all
7037 devfsadm_lookup_dev_names(char *phys_path
, char *re
, int *lenp
)
7039 struct devlink_cb_arg cb_arg
;
7040 char **dev_names
= NULL
;
7046 (void) di_devlink_cache_walk(devlink_cache
, re
, phys_path
,
7047 DI_PRIMARY_LINK
, &cb_arg
, devlink_cb
);
7049 if (cb_arg
.rv
== -1 || cb_arg
.count
<= 0)
7052 dev_names
= s_malloc(cb_arg
.count
* sizeof (char *));
7053 if (dev_names
== NULL
)
7056 for (i
= 0; i
< cb_arg
.count
; i
++) {
7057 dev_names
[i
] = s_strdup(cb_arg
.dev_names
[i
]);
7058 if (dev_names
[i
] == NULL
) {
7059 devfsadm_free_dev_names(dev_names
, i
);
7064 *lenp
= cb_arg
.count
;
7067 free_dev_names(&cb_arg
);
7071 /* common exit function which ensures releasing locks */
7073 devfsadm_exit(int status
)
7075 if (DEVFSADM_DEBUG_ON
) {
7076 vprint(INFO_MID
, "exit status = %d\n", status
);
7080 exit_daemon_lock(1);
7082 if (logflag
== TRUE
) {
7091 * set root_dir, devices_dir, dev_dir using optarg.
7094 set_root_devices_dev_dir(char *dir
)
7098 root_dir
= s_strdup(dir
);
7099 len
= strlen(dir
) + strlen(DEVICES
) + 1;
7100 devices_dir
= s_malloc(len
);
7101 (void) snprintf(devices_dir
, len
, "%s%s", root_dir
, DEVICES
);
7102 len
= strlen(root_dir
) + strlen(DEV
) + 1;
7103 dev_dir
= s_malloc(len
);
7104 (void) snprintf(dev_dir
, len
, "%s%s", root_dir
, DEV
);
7117 dst
= s_malloc(len
+ 1);
7118 if (src
[0] == '\"' && src
[len
- 1] == '\"') {
7120 (void) strncpy(dst
, &src
[1], len
);
7123 (void) strcpy(dst
, src
);
7129 * For a given physical device pathname and spectype, return the
7130 * ownership and permissions attributes by looking in data from
7131 * /etc/minor_perm. If currently in installation mode, check for
7132 * possible major number translations from the miniroot to the installed
7133 * root's name_to_major table. Note that there can be multiple matches,
7134 * but the last match takes effect. pts seems to rely on this
7135 * implementation behavior.
7138 getattr(char *phy_path
, char *aminor
, int spectype
, dev_t dev
, mode_t
*mode
,
7139 uid_t
*uid
, gid_t
*gid
)
7141 char devname
[PATH_MAX
+ 1];
7146 int mp_drvname_matches_node_name
;
7147 int mp_drvname_matches_minor_name
;
7148 int mp_drvname_is_clone
;
7149 int mp_drvname_matches_drvname
;
7152 char driver
[PATH_MAX
+ 1];
7155 * Get the driver name based on the major number since the name
7156 * in /devices may be generic. Could be running with more major
7157 * numbers than are in /etc/name_to_major, so get it from the kernel
7159 major_no
= major(dev
);
7161 if (modctl(MODGETNAME
, driver
, sizeof (driver
), &major_no
) != 0) {
7162 /* return default values */
7166 (void) strcpy(devname
, phy_path
);
7168 node_name
= strrchr(devname
, '/'); /* node name is the last */
7170 if (node_name
== NULL
) {
7171 err_print(NO_NODE
, devname
);
7175 minor_name
= strchr(++node_name
, '@'); /* see if it has address part */
7177 if (minor_name
!= NULL
) {
7178 *minor_name
++ = '\0';
7180 minor_name
= node_name
;
7183 minor_name
= strchr(minor_name
, ':'); /* look for minor name */
7185 if (minor_name
== NULL
) {
7186 err_print(NO_MINOR
, devname
);
7189 *minor_name
++ = '\0';
7192 * mp->mp_drvname = device name from minor_perm
7193 * mp->mp_minorname = minor part of device name from
7195 * drvname = name of driver for this device
7198 is_clone
= (strcmp(node_name
, "clone") == 0 ? TRUE
: FALSE
);
7199 for (mp
= minor_perms
; mp
!= NULL
; mp
= mp
->mp_next
) {
7200 mp_drvname_matches_node_name
=
7201 (strcmp(mp
->mp_drvname
, node_name
) == 0 ? TRUE
: FALSE
);
7202 mp_drvname_matches_minor_name
=
7203 (strcmp(mp
->mp_drvname
, minor_name
) == 0 ? TRUE
:FALSE
);
7204 mp_drvname_is_clone
=
7205 (strcmp(mp
->mp_drvname
, "clone") == 0 ? TRUE
: FALSE
);
7206 mp_drvname_matches_drvname
=
7207 (strcmp(mp
->mp_drvname
, driver
) == 0 ? TRUE
: FALSE
);
7210 * If one of the following cases is true, then we try to change
7211 * the permissions if a "shell global pattern match" of
7212 * mp_>mp_minorname matches minor_name.
7214 * 1. mp->mp_drvname matches driver.
7218 * 2. mp->mp_drvname matches node_name and this
7219 * name is an alias of the driver name
7223 * 3. /devices entry is the clone device and either
7224 * minor_perm entry is the clone device or matches
7225 * the minor part of the clone device.
7228 if ((mp_drvname_matches_drvname
== TRUE
)||
7229 ((mp_drvname_matches_node_name
== TRUE
) &&
7230 (alias(driver
, node_name
) == TRUE
)) ||
7231 ((is_clone
== TRUE
) &&
7232 ((mp_drvname_is_clone
== TRUE
) ||
7233 (mp_drvname_matches_minor_name
== TRUE
)))) {
7235 * Check that the minor part of the
7236 * device name from the minor_perm
7237 * entry matches and if so, set the
7240 * Under real devfs, clone minor name is changed
7241 * to match the driver name, but minor_perm may
7242 * not match. We reconcile it here.
7245 minor_name
= aminor
;
7247 if (gmatch(minor_name
, mp
->mp_minorname
) != 0) {
7250 *mode
= spectype
| mp
->mp_mode
;
7256 if (match
== TRUE
) {
7261 /* not found in minor_perm, so just use default values */
7264 *mode
= (spectype
| 0600);
7268 * Called by devfs_read_minor_perm() to report errors
7270 * line number: ignoring line number error
7271 * errno: open/close errors
7272 * size: alloc errors
7275 minorperm_err_cb(minorperm_err_t mp_err
, int key
)
7279 err_print(FOPEN_FAILED
, MINOR_PERM_FILE
, strerror(key
));
7282 err_print(FCLOSE_FAILED
, MINOR_PERM_FILE
, strerror(key
));
7284 case MP_IGNORING_LINE_ERR
:
7285 err_print(IGNORING_LINE_IN
, key
, MINOR_PERM_FILE
);
7288 err_print(MALLOC_FAILED
, key
);
7291 err_print(NVLIST_ERROR
, MINOR_PERM_FILE
, strerror(key
));
7293 case MP_CANT_FIND_USER_ERR
:
7294 err_print(CANT_FIND_USER
, DEFAULT_DEV_USER
);
7296 case MP_CANT_FIND_GROUP_ERR
:
7297 err_print(CANT_FIND_GROUP
, DEFAULT_DEV_GROUP
);
7303 read_minor_perm_file(void)
7305 static int cached
= FALSE
;
7306 static struct stat cached_sb
;
7307 struct stat current_sb
;
7309 (void) stat(MINOR_PERM_FILE
, ¤t_sb
);
7311 /* If already cached, check to see if it is still valid */
7312 if (cached
== TRUE
) {
7314 if (current_sb
.st_mtime
== cached_sb
.st_mtime
) {
7315 vprint(FILES_MID
, "%s cache valid\n", MINOR_PERM_FILE
);
7318 devfs_free_minor_perm(minor_perms
);
7324 (void) stat(MINOR_PERM_FILE
, &cached_sb
);
7326 vprint(FILES_MID
, "loading binding file: %s\n", MINOR_PERM_FILE
);
7328 minor_perms
= devfs_read_minor_perm(minorperm_err_cb
);
7332 load_minor_perm_file(void)
7334 read_minor_perm_file();
7335 if (devfs_load_minor_perm(minor_perms
, minorperm_err_cb
) != 0)
7336 err_print(gettext("minor_perm load failed\n"));
7340 convert_to_re(char *dev
)
7345 out
= s_malloc(PATH_MAX
);
7347 for (l
= p
= dev
, i
= 0; (*p
!= '\0') && (i
< (PATH_MAX
- 1));
7349 if ((*p
== '*') && ((l
!= p
) && (*l
== '/'))) {
7358 p
= (char *)s_malloc(strlen(out
) + 1);
7359 (void) strlcpy(p
, out
, strlen(out
) + 1);
7362 vprint(FILES_MID
, "converted %s -> %s\n", dev
, p
);
7368 read_logindevperm_file(void)
7370 static int cached
= FALSE
;
7371 static struct stat cached_sb
;
7372 struct stat current_sb
;
7373 struct login_dev
*ldev
;
7375 char line
[MAX_LDEV_LINE
];
7377 char *cp
, *console
, *dlist
, *dev
;
7378 char *lasts
, *devlasts
, *permstr
, *drv
;
7379 struct driver_list
*list
, *next
;
7381 /* Read logindevperm only when enabled */
7382 if (login_dev_enable
!= TRUE
)
7385 if (cached
== TRUE
) {
7386 if (stat(LDEV_FILE
, ¤t_sb
) == 0 &&
7387 current_sb
.st_mtime
== cached_sb
.st_mtime
) {
7388 vprint(FILES_MID
, "%s cache valid\n", LDEV_FILE
);
7391 vprint(FILES_MID
, "invalidating %s cache\n", LDEV_FILE
);
7392 while (login_dev_cache
!= NULL
) {
7394 ldev
= login_dev_cache
;
7395 login_dev_cache
= ldev
->ldev_next
;
7396 free(ldev
->ldev_console
);
7397 free(ldev
->ldev_device
);
7398 regfree(&ldev
->ldev_device_regex
);
7399 list
= ldev
->ldev_driver_list
;
7411 assert(login_dev_cache
== NULL
);
7413 if (stat(LDEV_FILE
, &cached_sb
) != 0) {
7418 vprint(FILES_MID
, "loading file: %s\n", LDEV_FILE
);
7420 if ((fp
= fopen(LDEV_FILE
, "r")) == NULL
) {
7421 /* Not fatal to devfsadm */
7423 err_print(FOPEN_FAILED
, LDEV_FILE
, strerror(errno
));
7428 while (fgets(line
, MAX_LDEV_LINE
, fp
) != NULL
) {
7431 /* Remove comments */
7432 if ((cp
= strchr(line
, '#')) != NULL
)
7435 if ((console
= strtok_r(line
, LDEV_DELIMS
, &lasts
)) == NULL
)
7436 continue; /* Blank line */
7438 if ((permstr
= strtok_r(NULL
, LDEV_DELIMS
, &lasts
)) == NULL
) {
7439 err_print(IGNORING_LINE_IN
, ln
, LDEV_FILE
);
7440 continue; /* Malformed line */
7444 * permstr is string in octal format. Convert to int
7448 perm
= strtol(permstr
, &cp
, 8);
7449 if (errno
|| perm
< 0 || perm
> 0777 || *cp
!= '\0') {
7450 err_print(IGNORING_LINE_IN
, ln
, LDEV_FILE
);
7454 if ((dlist
= strtok_r(NULL
, LDEV_DELIMS
, &lasts
)) == NULL
) {
7455 err_print(IGNORING_LINE_IN
, ln
, LDEV_FILE
);
7459 dev
= strtok_r(dlist
, LDEV_DEV_DELIM
, &devlasts
);
7462 ldev
= (struct login_dev
*)s_zalloc(
7463 sizeof (struct login_dev
));
7464 ldev
->ldev_console
= s_strdup(console
);
7465 ldev
->ldev_perms
= perm
;
7468 * the logical device name may contain '*' which
7469 * we convert to a regular expression
7471 ldev
->ldev_device
= convert_to_re(dev
);
7472 if (ldev
->ldev_device
&&
7473 (rv
= regcomp(&ldev
->ldev_device_regex
,
7474 ldev
->ldev_device
, REG_EXTENDED
))) {
7475 bzero(&ldev
->ldev_device_regex
,
7476 sizeof (ldev
->ldev_device_regex
));
7477 err_print(REGCOMP_FAILED
,
7478 ldev
->ldev_device
, rv
);
7480 ldev
->ldev_next
= login_dev_cache
;
7481 login_dev_cache
= ldev
;
7482 dev
= strtok_r(NULL
, LDEV_DEV_DELIM
, &devlasts
);
7485 drv
= strtok_r(NULL
, LDEV_DRVLIST_DELIMS
, &lasts
);
7487 if (strcmp(drv
, LDEV_DRVLIST_NAME
) == 0) {
7489 drv
= strtok_r(NULL
, LDEV_DRV_DELIMS
, &lasts
);
7493 "logindevperm driver=%s\n", drv
);
7496 * create a linked list of driver
7499 list
= (struct driver_list
*)
7501 sizeof (struct driver_list
));
7502 (void) strlcpy(list
->driver_name
, drv
,
7503 sizeof (list
->driver_name
));
7504 list
->next
= ldev
->ldev_driver_list
;
7505 ldev
->ldev_driver_list
= list
;
7506 drv
= strtok_r(NULL
, LDEV_DRV_DELIMS
,
7516 * Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0'
7518 * Returns DEVFSADM_SUCCESS if token found, DEVFSADM_FAILURE otherwise.
7521 getnexttoken(char *next
, char **nextp
, char **tokenpp
, char *tchar
)
7528 while (*cp
== ' ' || *cp
== '\t') {
7529 cp
++; /* skip leading spaces */
7531 tokenp
= cp
; /* start of token */
7532 while (*cp
!= '\0' && *cp
!= '\n' && *cp
!= ' ' && *cp
!= '\t' &&
7533 *cp
!= ':' && *cp
!= '=' && *cp
!= '&' &&
7534 *cp
!= '|' && *cp
!= ';') {
7535 cp
++; /* point to next character */
7538 * If terminating character is a space or tab, look ahead to see if
7539 * there's another terminator that's not a space or a tab.
7540 * (This code handles trailing spaces.)
7542 if (*cp
== ' ' || *cp
== '\t') {
7544 while (*++cp1
== ' ' || *cp1
== '\t')
7546 if (*cp1
== '=' || *cp1
== ':' || *cp1
== '&' || *cp1
== '|' ||
7547 *cp1
== ';' || *cp1
== '\n' || *cp1
== '\0') {
7548 *cp
= NULL
; /* terminate token */
7552 if (tchar
!= NULL
) {
7553 *tchar
= *cp
; /* save terminating character */
7554 if (*tchar
== '\0') {
7558 *cp
++ = '\0'; /* terminate token, point to next */
7559 *nextp
= cp
; /* set pointer to next character */
7560 if (cp
- tokenp
- 1 == 0) {
7561 return (DEVFSADM_FAILURE
);
7564 return (DEVFSADM_SUCCESS
);
7568 * read or reread the driver aliases file
7571 read_driver_aliases_file(void)
7574 driver_alias_t
*save
;
7575 driver_alias_t
*lst_tail
;
7577 static int cached
= FALSE
;
7584 static struct stat cached_sb
;
7585 struct stat current_sb
;
7587 (void) stat(ALIASFILE
, ¤t_sb
);
7589 /* If already cached, check to see if it is still valid */
7590 if (cached
== TRUE
) {
7592 if (current_sb
.st_mtime
== cached_sb
.st_mtime
) {
7593 vprint(FILES_MID
, "%s cache valid\n", ALIASFILE
);
7597 vprint(FILES_MID
, "invalidating %s cache\n", ALIASFILE
);
7598 while (driver_aliases
!= NULL
) {
7599 free(driver_aliases
->alias_name
);
7600 free(driver_aliases
->driver_name
);
7601 save
= driver_aliases
;
7602 driver_aliases
= driver_aliases
->next
;
7609 (void) stat(ALIASFILE
, &cached_sb
);
7611 vprint(FILES_MID
, "loading binding file: %s\n", ALIASFILE
);
7613 if ((afd
= fopen(ALIASFILE
, "r")) == NULL
) {
7614 err_print(FOPEN_FAILED
, ALIASFILE
, strerror(errno
));
7619 while (fgets(line
, sizeof (line
), afd
) != NULL
) {
7621 /* cut off comments starting with '#' */
7622 if ((cp
= strchr(line
, '#')) != NULL
)
7624 /* ignore comment or blank lines */
7628 if (getnexttoken(cp
, &cp
, &p
, &t
) == DEVFSADM_FAILURE
) {
7629 err_print(IGNORING_LINE_IN
, ln
, ALIASFILE
);
7632 if (t
== '\n' || t
== '\0') {
7633 err_print(DRV_BUT_NO_ALIAS
, ln
, ALIASFILE
);
7636 ap
= (struct driver_alias
*)
7637 s_zalloc(sizeof (struct driver_alias
));
7638 ap
->driver_name
= s_strdup(p
);
7639 if (getnexttoken(cp
, &cp
, &p
, &t
) == DEVFSADM_FAILURE
) {
7640 err_print(DRV_BUT_NO_ALIAS
, ln
, ALIASFILE
);
7641 free(ap
->driver_name
);
7646 if (p
[strlen(p
) - 1] == '"') {
7647 p
[strlen(p
) - 1] = '\0';
7651 ap
->alias_name
= s_strdup(p
);
7652 if (driver_aliases
== NULL
) {
7653 driver_aliases
= ap
;
7656 lst_tail
->next
= ap
;
7660 if (fclose(afd
) == EOF
) {
7661 err_print(FCLOSE_FAILED
, ALIASFILE
, strerror(errno
));
7666 * return TRUE if alias_name is an alias for driver_name, otherwise
7670 alias(char *driver_name
, char *alias_name
)
7672 driver_alias_t
*alias
;
7677 for (alias
= driver_aliases
; alias
!= NULL
; alias
= alias
->next
) {
7678 if ((strcmp(alias
->driver_name
, driver_name
) == 0) &&
7679 (strcmp(alias
->alias_name
, alias_name
) == 0)) {
7687 * convenience functions
7690 s_stat(const char *path
, struct stat
*sbufp
)
7694 if ((rv
= stat(path
, sbufp
)) == -1) {
7702 s_malloc(const size_t size
)
7708 err_print(MALLOC_FAILED
, size
);
7716 * convenience functions
7719 s_realloc(void *ptr
, const size_t size
)
7721 ptr
= realloc(ptr
, size
);
7723 err_print(REALLOC_FAILED
, size
);
7731 s_zalloc(const size_t size
)
7735 rp
= calloc(1, size
);
7737 err_print(CALLOC_FAILED
, size
);
7745 s_strdup(const char *ptr
)
7751 err_print(STRDUP_FAILED
, ptr
);
7759 s_closedir(DIR *dirp
)
7762 if (closedir(dirp
) != 0) {
7765 err_print(CLOSEDIR_FAILED
, strerror(errno
));
7770 s_mkdirp(const char *path
, const mode_t mode
)
7772 vprint(CHATTY_MID
, "mkdirp(%s, 0x%lx)\n", path
, mode
);
7773 if (mkdirp(path
, mode
) == -1) {
7774 if (errno
!= EEXIST
) {
7775 err_print(MKDIR_FAILED
, path
, mode
, strerror(errno
));
7781 s_unlink(const char *file
)
7784 if (unlink(file
) == -1) {
7785 if (errno
== EINTR
|| errno
== EAGAIN
)
7787 if (errno
!= ENOENT
) {
7788 err_print(UNLINK_FAILED
, file
, strerror(errno
));
7794 add_verbose_id(char *mid
)
7797 verbose
= s_realloc(verbose
, num_verbose
* sizeof (char *));
7798 verbose
[num_verbose
- 1] = mid
;
7802 * returns DEVFSADM_TRUE if contents is a minor node in /devices.
7803 * If mn_root is not NULL, mn_root is set to:
7804 * if contents is a /dev node, mn_root = contents
7806 * if contents is a /devices node, mn_root set to the '/'
7807 * following /devices.
7810 is_minor_node(char *contents
, char **mn_root
)
7813 char device_prefix
[100];
7815 (void) snprintf(device_prefix
, sizeof (device_prefix
), "../devices/");
7817 if ((ptr
= strstr(contents
, device_prefix
)) != NULL
) {
7818 if (mn_root
!= NULL
) {
7819 /* mn_root should point to the / following /devices */
7820 *mn_root
= ptr
+= strlen(device_prefix
) - 1;
7822 return (DEVFSADM_TRUE
);
7825 (void) snprintf(device_prefix
, sizeof (device_prefix
), "/devices/");
7827 if (strncmp(contents
, device_prefix
, strlen(device_prefix
)) == 0) {
7828 if (mn_root
!= NULL
) {
7829 /* mn_root should point to the / following /devices */
7830 *mn_root
= contents
+ strlen(device_prefix
) - 1;
7832 return (DEVFSADM_TRUE
);
7835 if (mn_root
!= NULL
) {
7836 *mn_root
= contents
;
7838 return (DEVFSADM_FALSE
);
7842 * Add the specified property to nvl.
7844 * 0 successfully added
7845 * -1 an error occurred
7846 * 1 could not add the property for reasons not due to errors.
7849 add_property(nvlist_t
*nvl
, di_prop_t prop
)
7862 if ((name
= di_prop_name(prop
)) == NULL
)
7865 len
= sizeof (DEV_PROP_PREFIX
) + strlen(name
);
7866 if ((attr_name
= malloc(len
)) == NULL
)
7869 (void) strlcpy(attr_name
, DEV_PROP_PREFIX
, len
);
7870 (void) strlcat(attr_name
, name
, len
);
7872 switch (di_prop_type(prop
)) {
7873 case DI_PROP_TYPE_BOOLEAN
:
7874 if (nvlist_add_boolean(nvl
, attr_name
) != 0)
7878 case DI_PROP_TYPE_INT
:
7879 if ((n
= di_prop_ints(prop
, &int32p
)) < 1)
7882 if (n
<= (PROP_LEN_LIMIT
/ sizeof (int32_t))) {
7883 if (nvlist_add_int32_array(nvl
, attr_name
, int32p
,
7890 case DI_PROP_TYPE_INT64
:
7891 if ((n
= di_prop_int64(prop
, &int64p
)) < 1)
7894 if (n
<= (PROP_LEN_LIMIT
/ sizeof (int64_t))) {
7895 if (nvlist_add_int64_array(nvl
, attr_name
, int64p
,
7902 case DI_PROP_TYPE_BYTE
:
7903 case DI_PROP_TYPE_UNKNOWN
:
7904 if ((n
= di_prop_bytes(prop
, &bytep
)) < 1)
7907 if (n
<= PROP_LEN_LIMIT
) {
7908 if (nvlist_add_byte_array(nvl
, attr_name
, bytep
, n
)
7915 case DI_PROP_TYPE_STRING
:
7916 if ((n
= di_prop_strings(prop
, &str
)) < 1)
7919 if ((strarray
= malloc(n
* sizeof (char *))) == NULL
)
7923 for (i
= 0; i
< n
; i
++) {
7924 strarray
[i
] = str
+ len
;
7925 len
+= strlen(strarray
[i
]) + 1;
7928 if (len
<= PROP_LEN_LIMIT
) {
7929 if (nvlist_add_string_array(nvl
, attr_name
, strarray
,
7953 free_dev_names(struct devlink_cb_arg
*x
)
7957 for (i
= 0; i
< x
->count
; i
++) {
7958 free(x
->dev_names
[i
]);
7959 free(x
->link_contents
[i
]);
7963 /* callback function for di_devlink_cache_walk */
7965 devlink_cb(di_devlink_t dl
, void *arg
)
7967 struct devlink_cb_arg
*x
= (struct devlink_cb_arg
*)arg
;
7969 const char *content
;
7971 if ((path
= di_devlink_path(dl
)) == NULL
||
7972 (content
= di_devlink_content(dl
)) == NULL
||
7973 (x
->dev_names
[x
->count
] = s_strdup(path
)) == NULL
)
7976 if ((x
->link_contents
[x
->count
] = s_strdup(content
)) == NULL
) {
7977 free(x
->dev_names
[x
->count
]);
7982 if (x
->count
>= MAX_DEV_NAME_COUNT
)
7983 return (DI_WALK_TERMINATE
);
7985 return (DI_WALK_CONTINUE
);
7990 return (DI_WALK_TERMINATE
);
7994 * Lookup dev name corresponding to the phys_path.
7995 * phys_path is path to a node or minor node.
7997 * 0 with *dev_name set to the dev name
7998 * Lookup succeeded and dev_name found
7999 * 0 with *dev_name set to NULL
8000 * Lookup encountered no errors but dev name not found
8005 lookup_dev_name(char *phys_path
, char **dev_name
)
8007 struct devlink_cb_arg cb_arg
;
8013 (void) di_devlink_cache_walk(devlink_cache
, NULL
, phys_path
,
8014 DI_PRIMARY_LINK
, &cb_arg
, devlink_cb
);
8016 if (cb_arg
.rv
== -1)
8019 if (cb_arg
.count
> 0) {
8020 *dev_name
= s_strdup(cb_arg
.dev_names
[0]);
8021 free_dev_names(&cb_arg
);
8022 if (*dev_name
== NULL
)
8030 lookup_disk_dev_name(char *node_path
)
8032 struct devlink_cb_arg cb_arg
;
8033 char *dev_name
= NULL
;
8038 #define DEV_RDSK "/dev/rdsk/"
8039 #define DISK_RAW_MINOR ",raw"
8043 (void) di_devlink_cache_walk(devlink_cache
, NULL
, node_path
,
8044 DI_PRIMARY_LINK
, &cb_arg
, devlink_cb
);
8046 if (cb_arg
.rv
== -1 || cb_arg
.count
== 0)
8049 /* first try lookup based on /dev/rdsk name */
8050 for (i
= 0; i
< cb_arg
.count
; i
++) {
8051 if (strncmp(cb_arg
.dev_names
[i
], DEV_RDSK
,
8052 sizeof (DEV_RDSK
) - 1) == 0) {
8053 dev_name
= s_strdup(cb_arg
.dev_names
[i
]);
8058 if (dev_name
== NULL
) {
8059 /* now try lookup based on a minor name ending with ",raw" */
8060 len1
= sizeof (DISK_RAW_MINOR
) - 1;
8061 for (i
= 0; i
< cb_arg
.count
; i
++) {
8062 len2
= strlen(cb_arg
.link_contents
[i
]);
8064 strcmp(cb_arg
.link_contents
[i
] + len2
- len1
,
8065 DISK_RAW_MINOR
) == 0) {
8066 dev_name
= s_strdup(cb_arg
.dev_names
[i
]);
8072 free_dev_names(&cb_arg
);
8074 if (dev_name
== NULL
)
8076 if (strlen(dev_name
) == 0) {
8081 /* if the name contains slice or partition number strip it */
8082 p
= dev_name
+ strlen(dev_name
) - 1;
8084 while (p
!= dev_name
&& isdigit(*p
))
8086 if (*p
== 's' || *p
== 'p')
8094 lookup_lofi_dev_name(char *node_path
, char *minor
)
8096 struct devlink_cb_arg cb_arg
;
8097 char *dev_name
= NULL
;
8103 (void) di_devlink_cache_walk(devlink_cache
, NULL
, node_path
,
8104 DI_PRIMARY_LINK
, &cb_arg
, devlink_cb
);
8106 if (cb_arg
.rv
== -1 || cb_arg
.count
== 0)
8109 /* lookup based on a minor name ending with ",raw" */
8110 len1
= strlen(minor
);
8111 for (i
= 0; i
< cb_arg
.count
; i
++) {
8112 len2
= strlen(cb_arg
.link_contents
[i
]);
8114 strcmp(cb_arg
.link_contents
[i
] + len2
- len1
,
8116 dev_name
= s_strdup(cb_arg
.dev_names
[i
]);
8121 free_dev_names(&cb_arg
);
8123 if (dev_name
== NULL
)
8125 if (strlen(dev_name
) == 0) {
8134 lookup_network_dev_name(char *node_path
, char *driver_name
)
8136 char *dev_name
= NULL
;
8137 char phys_path
[MAXPATHLEN
];
8139 if (lookup_dev_name(node_path
, &dev_name
) == -1)
8142 if (dev_name
== NULL
) {
8143 /* dlpi style-2 only interface */
8144 (void) snprintf(phys_path
, sizeof (phys_path
),
8145 "/pseudo/clone@0:%s", driver_name
);
8146 if (lookup_dev_name(phys_path
, &dev_name
) == -1 ||
8155 lookup_printer_dev_name(char *node_path
)
8157 struct devlink_cb_arg cb_arg
;
8158 char *dev_name
= NULL
;
8161 #define DEV_PRINTERS "/dev/printers/"
8165 (void) di_devlink_cache_walk(devlink_cache
, NULL
, node_path
,
8166 DI_PRIMARY_LINK
, &cb_arg
, devlink_cb
);
8168 if (cb_arg
.rv
== -1 || cb_arg
.count
== 0)
8171 /* first try lookup based on /dev/printers name */
8172 for (i
= 0; i
< cb_arg
.count
; i
++) {
8173 if (strncmp(cb_arg
.dev_names
[i
], DEV_PRINTERS
,
8174 sizeof (DEV_PRINTERS
) - 1) == 0) {
8175 dev_name
= s_strdup(cb_arg
.dev_names
[i
]);
8180 /* fallback to the first name */
8181 if ((dev_name
== NULL
) && (cb_arg
.count
> 0))
8182 dev_name
= s_strdup(cb_arg
.dev_names
[0]);
8184 free_dev_names(&cb_arg
);
8190 * Build an nvlist containing all attributes for devfs events.
8191 * Returns nvlist pointer on success, NULL on failure.
8194 build_event_attributes(char *class, char *subclass
, char *node_path
,
8195 di_node_t node
, char *driver_name
, int instance
, char *minor
)
8203 char *dev_name
= NULL
;
8204 int dev_name_lookup_err
= 0;
8206 if ((err
= nvlist_alloc(&nvl
, NV_UNIQUE_NAME_TYPE
, 0)) != 0) {
8211 if ((err
= nvlist_add_int32(nvl
, EV_VERSION
, EV_V1
)) != 0)
8214 if ((err
= nvlist_add_string(nvl
, DEV_PHYS_PATH
, node_path
)) != 0)
8217 if (strcmp(class, EC_DEV_ADD
) != 0 &&
8218 strcmp(class, EC_DEV_REMOVE
) != 0)
8221 if (driver_name
== NULL
|| instance
== -1)
8224 if (strcmp(subclass
, ESC_DISK
) == 0) {
8226 * While we're removing labeled lofi device, we will receive
8227 * event for every registered minor device and lastly,
8228 * an event with minor set to NULL, as in following example:
8229 * class: EC_dev_remove subclass: disk
8230 * node_path: /pseudo/lofi@1 driver: lofi minor: u,raw
8231 * class: EC_dev_remove subclass: disk
8232 * node_path: /pseudo/lofi@1 driver: lofi minor: NULL
8234 * When we receive this last event with minor set to NULL,
8235 * all lofi minor devices are already removed and the call to
8236 * lookup_disk_dev_name() would result in error.
8237 * To prevent name lookup error messages for this case, we
8238 * need to filter out that last event.
8240 if (strcmp(class, EC_DEV_REMOVE
) == 0 &&
8241 strcmp(driver_name
, "lofi") == 0 && minor
== NULL
) {
8245 if ((dev_name
= lookup_disk_dev_name(node_path
)) == NULL
) {
8246 dev_name_lookup_err
= 1;
8249 } else if (strcmp(subclass
, ESC_NETWORK
) == 0) {
8250 if ((dev_name
= lookup_network_dev_name(node_path
, driver_name
))
8252 dev_name_lookup_err
= 1;
8255 } else if (strcmp(subclass
, ESC_PRINTER
) == 0) {
8256 if ((dev_name
= lookup_printer_dev_name(node_path
)) == NULL
) {
8257 dev_name_lookup_err
= 1;
8260 } else if (strcmp(subclass
, ESC_LOFI
) == 0) {
8262 * The raw minor node is created or removed after the block
8263 * node. Lofi devfs events are dependent on this behavior.
8264 * Generate the sysevent only for the raw minor node.
8266 * If the lofi mapping is created, we will receive the following
8267 * event: class: EC_dev_add subclass: lofi minor: NULL
8269 * As in case of EC_dev_add, the minor is NULL pointer,
8270 * to get device links created, we will need to provide the
8271 * type of minor node for lookup_lofi_dev_name()
8273 * If the lofi device is unmapped, we will receive following
8275 * class: EC_dev_remove subclass: lofi minor: disk
8276 * class: EC_dev_remove subclass: lofi minor: disk,raw
8277 * class: EC_dev_remove subclass: lofi minor: NULL
8280 if (strcmp(class, EC_DEV_ADD
) == 0 && minor
== NULL
)
8283 if (minor
== NULL
|| strstr(minor
, "raw") == NULL
) {
8287 if ((dev_name
= lookup_lofi_dev_name(node_path
, minor
)) ==
8289 dev_name_lookup_err
= 1;
8295 if ((err
= nvlist_add_string(nvl
, DEV_NAME
, dev_name
)) != 0)
8301 if ((err
= nvlist_add_string(nvl
, DEV_DRIVER_NAME
, driver_name
)) != 0)
8304 if ((err
= nvlist_add_int32(nvl
, DEV_INSTANCE
, instance
)) != 0)
8307 if (strcmp(class, EC_DEV_ADD
) == 0) {
8308 /* add properties */
8310 for (prop
= di_prop_next(node
, DI_PROP_NIL
);
8311 prop
!= DI_PROP_NIL
&& count
< MAX_PROP_COUNT
;
8312 prop
= di_prop_next(node
, prop
)) {
8314 if (di_prop_devt(prop
) != DDI_DEV_T_NONE
)
8317 if ((x
= add_property(nvl
, prop
)) == 0)
8320 if ((prop_name
= di_prop_name(prop
)) == NULL
)
8322 err_print(PROP_ADD_FAILED
, prop_name
);
8336 if (dev_name_lookup_err
) {
8338 * If a lofi mount fails, the /devices node may well have
8339 * disappeared by the time we run, so let's not complain.
8341 if (strcmp(subclass
, ESC_LOFI
) != 0)
8342 err_print(DEV_NAME_LOOKUP_FAILED
, node_path
);
8344 err_print(BUILD_EVENT_ATTR_FAILED
, (err
) ? strerror(err
) : "");
8350 log_event(char *class, char *subclass
, nvlist_t
*nvl
)
8354 if (sysevent_post_event(class, subclass
, "SUNW", DEVFSADMD
,
8356 err_print(LOG_EVENT_FAILED
, strerror(errno
));
8361 * When devfsadmd needs to generate sysevents, they are queued for later
8362 * delivery this allows them to be delivered after the devlinks db cache has
8363 * been flushed guaranteeing that applications consuming these events have
8364 * access to an accurate devlinks db. The queue is a FIFO, sysevents to be
8365 * inserted in the front of the queue and consumed off the back.
8368 enqueue_sysevent(char *class, char *subclass
, nvlist_t
*nvl
)
8372 if ((tmp
= s_zalloc(sizeof (*tmp
))) == NULL
)
8375 tmp
->class = s_strdup(class);
8376 tmp
->subclass
= s_strdup(subclass
);
8379 (void) mutex_lock(&syseventq_mutex
);
8380 if (syseventq_front
!= NULL
)
8381 syseventq_front
->next
= tmp
;
8383 syseventq_back
= tmp
;
8384 syseventq_front
= tmp
;
8385 (void) mutex_unlock(&syseventq_mutex
);
8391 (void) mutex_lock(&syseventq_mutex
);
8392 while (syseventq_back
!= NULL
) {
8393 syseventq_t
*tmp
= syseventq_back
;
8395 vprint(CHATTY_MID
, "sending queued event: %s, %s\n",
8396 tmp
->class, tmp
->subclass
);
8398 log_event(tmp
->class, tmp
->subclass
, tmp
->nvl
);
8400 if (tmp
->class != NULL
)
8402 if (tmp
->subclass
!= NULL
)
8403 free(tmp
->subclass
);
8404 nvlist_free(tmp
->nvl
);
8405 syseventq_back
= syseventq_back
->next
;
8406 if (syseventq_back
== NULL
)
8407 syseventq_front
= NULL
;
8410 (void) mutex_unlock(&syseventq_mutex
);
8414 build_and_enq_event(char *class, char *subclass
, char *node_path
,
8415 di_node_t node
, char *minor
)
8419 vprint(CHATTY_MID
, "build_and_enq_event(%s, %s, %s, 0x%8.8x)\n",
8420 class, subclass
, node_path
, (int)node
);
8422 if (node
!= DI_NODE_NIL
)
8423 nvl
= build_event_attributes(class, subclass
, node_path
, node
,
8424 di_driver_name(node
), di_instance(node
), minor
);
8426 nvl
= build_event_attributes(class, subclass
, node_path
, node
,
8430 enqueue_sysevent(class, subclass
, nvl
);
8435 * is_blank() returns 1 (true) if a line specified is composed of
8436 * whitespace characters only. otherwise, it returns 0 (false).
8438 * Note. the argument (line) must be null-terminated.
8441 is_blank(char *line
)
8443 for (/* nothing */; *line
!= '\0'; line
++)
8444 if (!isspace(*line
))
8450 * Functions to deal with the no-further-processing hash
8454 nfphash_create(void)
8456 assert(nfp_hash
== NULL
);
8457 nfp_hash
= s_zalloc(NFP_HASH_SZ
* sizeof (item_t
*));
8461 nfphash_fcn(char *key
)
8466 for (i
= 0; key
[i
] != '\0'; i
++) {
8467 sum
+= (uchar_t
)key
[i
];
8470 return (sum
% NFP_HASH_SZ
);
8474 nfphash_lookup(char *key
)
8479 index
= nfphash_fcn(key
);
8483 for (ip
= nfp_hash
[index
]; ip
; ip
= ip
->i_next
) {
8484 if (strcmp(ip
->i_key
, key
) == 0)
8492 nfphash_insert(char *key
)
8497 index
= nfphash_fcn(key
);
8501 ip
= s_zalloc(sizeof (item_t
));
8502 ip
->i_key
= s_strdup(key
);
8504 ip
->i_next
= nfp_hash
[index
];
8505 nfp_hash
[index
] = ip
;
8509 nfphash_destroy(void)
8514 for (i
= 0; i
< NFP_HASH_SZ
; i
++) {
8516 while (ip
= nfp_hash
[i
]) {
8517 nfp_hash
[i
] = ip
->i_next
;
8528 devname_kcall(int subcmd
, void *args
)
8533 case MODDEVNAME_LOOKUPDOOR
:
8534 error
= modctl(MODDEVNAME
, subcmd
, (uintptr_t)args
);
8536 vprint(INFO_MID
, "modctl(MODDEVNAME, "
8537 "MODDEVNAME_LOOKUPDOOR) failed - %s\n",
8550 devname_lookup_handler(void *cookie
, char *argp
, size_t arg_size
,
8551 door_desc_t
*dp
, uint_t n_desc
)
8555 struct dca_impl dci
;
8557 sdev_door_res_t res
;
8558 sdev_door_arg_t
*args
;
8560 if (argp
== NULL
|| arg_size
== 0) {
8561 vprint(DEVNAME_MID
, "devname_lookup_handler: argp wrong\n");
8562 error
= DEVFSADM_RUN_INVALID
;
8565 vprint(DEVNAME_MID
, "devname_lookup_handler\n");
8567 if (door_cred(&dcred
) != 0 || dcred
.dc_euid
!= 0) {
8568 vprint(DEVNAME_MID
, "devname_lookup_handler: cred wrong\n");
8569 error
= DEVFSADM_RUN_EPERM
;
8573 args
= (sdev_door_arg_t
*)argp
;
8574 cmd
= args
->devfsadm_cmd
;
8576 vprint(DEVNAME_MID
, "devname_lookup_handler: cmd %d\n", cmd
);
8578 case DEVFSADMD_RUN_ALL
:
8583 dci
.dci_minor
= NULL
;
8584 dci
.dci_driver
= NULL
;
8590 update_drvconf((major_t
)-1, 0);
8591 dci
.dci_flags
|= DCA_FLUSH_PATHINST
;
8593 pre_and_post_cleanup(RM_PRE
);
8594 devi_tree_walk(&dci
, DI_CACHE_SNAPSHOT_FLAGS
, NULL
);
8595 error
= (int32_t)dci
.dci_error
;
8597 pre_and_post_cleanup(RM_POST
);
8598 update_database
= TRUE
;
8599 unlock_dev(SYNC_STATE
);
8600 update_database
= FALSE
;
8602 if (DEVFSADM_DEBUG_ON
) {
8603 vprint(INFO_MID
, "devname_lookup_handler: "
8604 "DEVFSADMD_RUN_ALL failed\n");
8607 unlock_dev(SYNC_STATE
);
8611 /* log an error here? */
8612 error
= DEVFSADM_RUN_NOTSUP
;
8617 vprint(DEVNAME_MID
, "devname_lookup_handler: error %d\n", error
);
8618 res
.devfsadm_error
= error
;
8619 (void) door_return((char *)&res
, sizeof (struct sdev_door_res
),
8625 devfsadm_devlink_cache(void)
8627 return (devlink_cache
);
8631 devfsadm_reserve_id_cache(devlink_re_t re_array
[], enumerate_file_t
*head
)
8633 enumerate_file_t
*entry
;
8642 * Check the <RE, subexp> array passed in and compile it.
8644 for (i
= 0; re_array
[i
].d_re
; i
++) {
8645 if (re_array
[i
].d_subexp
== 0) {
8646 err_print("bad subexp value in RE: %s\n",
8651 re
= re_array
[i
].d_re
;
8652 if (regcomp(&re_array
[i
].d_rcomp
, re
, REG_EXTENDED
) != 0) {
8653 err_print("reg. exp. failed to compile: %s\n", re
);
8656 subex
= re_array
[i
].d_subexp
;
8658 re_array
[i
].d_pmatch
= s_malloc(sizeof (regmatch_t
) * nelem
);
8661 entry
= head
? head
: enumerate_reserved
;
8662 for (; entry
; entry
= entry
->er_next
) {
8664 vprint(RSBY_MID
, "entry %s already has ID %s\n",
8665 entry
->er_file
, entry
->er_id
);
8668 for (i
= 0; re_array
[i
].d_re
; i
++) {
8669 subex
= re_array
[i
].d_subexp
;
8670 pmch
= re_array
[i
].d_pmatch
;
8671 if (regexec(&re_array
[i
].d_rcomp
, entry
->er_file
,
8672 subex
+ 1, pmch
, 0) != 0) {
8676 size
= pmch
[subex
].rm_eo
- pmch
[subex
].rm_so
;
8677 entry
->er_id
= s_malloc(size
+ 1);
8678 (void) strncpy(entry
->er_id
,
8679 &entry
->er_file
[pmch
[subex
].rm_so
], size
);
8680 entry
->er_id
[size
] = '\0';
8682 vprint(RSBY_MID
, "devlink(%s) matches RE(%s). "
8683 "ID is %s\n", entry
->er_file
,
8684 re_array
[i
].d_re
, entry
->er_id
);
8686 vprint(RSBY_MID
, "rsrv entry(%s) matches "
8687 "RE(%s) ID is %s\n", entry
->er_file
,
8688 re_array
[i
].d_re
, entry
->er_id
);
8694 for (i
= 0; re_array
[i
].d_re
; i
++) {
8695 regfree(&re_array
[i
].d_rcomp
);
8696 assert(re_array
[i
].d_pmatch
);
8697 free(re_array
[i
].d_pmatch
);
8700 entry
= head
? head
: enumerate_reserved
;
8701 for (; entry
; entry
= entry
->er_next
) {
8702 if (entry
->er_id
== NULL
)
8705 vprint(RSBY_MID
, "devlink: %s\n", entry
->er_file
);
8706 vprint(RSBY_MID
, "ID: %s\n", entry
->er_id
);
8708 vprint(RSBY_MID
, "reserve file entry: %s\n",
8710 vprint(RSBY_MID
, "reserve file id: %s\n",
8715 return (DEVFSADM_SUCCESS
);
8718 for (i
= i
-1; i
>= 0; i
--) {
8719 regfree(&re_array
[i
].d_rcomp
);
8720 assert(re_array
[i
].d_pmatch
);
8721 free(re_array
[i
].d_pmatch
);
8723 return (DEVFSADM_FAILURE
);
8727 * Return 1 if we have reserved links.
8730 devfsadm_have_reserved()
8732 return (enumerate_reserved
? 1 : 0);
8736 * This functions errs on the side of caution. If there is any error
8737 * we assume that the devlink is *not* reserved
8740 devfsadm_is_reserved(devlink_re_t re_array
[], char *devlink
)
8743 enumerate_file_t estruct
= {NULL
};
8744 enumerate_file_t
*entry
;
8747 estruct
.er_file
= devlink
;
8748 estruct
.er_id
= NULL
;
8749 estruct
.er_next
= NULL
;
8751 if (devfsadm_reserve_id_cache(re_array
, &estruct
) != DEVFSADM_SUCCESS
) {
8752 err_print("devfsadm_is_reserved: devlink (%s) does not "
8753 "match RE\n", devlink
);
8756 if (estruct
.er_id
== NULL
) {
8757 err_print("devfsadm_is_reserved: ID derived from devlink %s "
8758 "is NULL\n", devlink
);
8762 entry
= enumerate_reserved
;
8763 for (; entry
; entry
= entry
->er_next
) {
8764 if (entry
->er_id
== NULL
)
8766 if (strcmp(entry
->er_id
, estruct
.er_id
) != 0)
8769 vprint(RSBY_MID
, "reserve file entry (%s) and devlink (%s) "
8770 "match\n", entry
->er_file
, devlink
);
8774 free(estruct
.er_id
);