devfsadm.c revision f7b6b8cf514e35127b23a2915c8853eb8b92a540
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Devfsadm replaces drvconfig, audlinks, disks, tapes, ports, devlinks
* as a general purpose device administrative utility. It creates
* devices special files in /devices and logical links in /dev, and
* coordinates updates to /etc/path_to_instance with the kernel. It
* operates in both command line mode to handle user or script invoked
* reconfiguration updates, and operates in daemon mode to handle dynamic
* reconfiguration for hotplugging support.
*/
#include <string.h>
#include <bsm/devalloc.h>
#include <utime.h>
#include "devfsadm_impl.h"
/* externs from devalloc.c */
extern void _reset_devalloc(int);
extern void _update_devalloc_db(devlist_t *, int, int, char *, char *);
extern int _da_check_for_usb(char *, char *);
/* create or remove nodes or links. unset with -n */
/* cleanup mode. Set with -C */
/* devlinks -d compatibility */
static int devlinks_debug = FALSE;
/* flag to check if system is labeled */
int system_labeled = FALSE;
static int devalloc_flag = 0;
/* flag to update device allocation database for this device type */
static int update_devdb = 0;
/*
* devices to be deallocated with -d :
* audio, floppy, cd, floppy, tape, rmdisk.
*/
/* list of allocatable devices */
/* load a single driver only. set with -i */
static int single_drv = FALSE;
/* attempt to load drivers or defer attach nodes */
static int load_attach_drv = TRUE;
static int daemon_mode = FALSE;
/* output directed to syslog during daemon mode if set */
/* build links in /dev. -x to turn off */
/* build nodes in /devices. -y to turn off */
static int build_devices = TRUE;
/* -z to turn off */
static int flush_path_to_inst_enable = TRUE;
/* variables used for path_to_inst flushing */
static int inst_count = 0;
static mutex_t count_lock;
/* variables for minor_fini thread */
static mutex_t minor_fini_mutex;
static int minor_fini_canceled = TRUE;
static int minor_fini_delayed = FALSE;
static cond_t minor_fini_cv;
static int minor_fini_timeout = MINOR_FINI_TIMEOUT_DEFAULT;
/* single-threads /dev modification */
/* the program we were invoked as; ie argv[0] */
static char *prog;
/* supports the class -c option */
static int num_classes = 0;
/* used with verbose option -v or -V */
static int num_verbose = 0;
/* set if -r alternate root given */
static char *root_dir = "";
/* /devices or <rootdir>/devices */
static char *devices_dir = DEVICES;
/* /dev or <rootdir>/dev */
static char *etc_dev_dir = ETCDEV;
/*
* writable root (for lock files and doors during install).
* This is also root dir for /dev attr dir during install.
*/
/* /etc/path_to_inst unless -p used */
static char *inst_file = INSTANCE_FILE;
static char *module_dirs = MODULE_DIRS;
/* default uid/gid used if /etc/minor_perm entry not found */
/* /etc/devlink.tab unless devlinks -t used */
static char *devlinktab_file = NULL;
/* set if /dev link is new. speeds up rm_stale_links */
/* variables for devlink.tab compat processing */
static unsigned int devlinktab_line = 0;
/* cache head for devfsadm_enumerate*() functions */
/* list list of devfsadm modules */
/* name_to_major list used in utility function */
/* cache of some links used for performance */
/* locking variables to prevent multiples writes to /dev */
static int hold_dev_lock = FALSE;
static int hold_daemon_lock = FALSE;
static int dev_lock_fd;
static int daemon_lock_fd;
static di_minor_t lminor;
/* Globals used by the link database */
static di_devlink_handle_t devlink_cache;
static int update_database = FALSE;
/* Globals used to set logindev perms */
static int login_dev_enable = FALSE;
/* Global to use devinfo snapshot cache */
static int use_snapshot_cache = FALSE;
/* Global for no-further-processing hash */
/*
* Packaged directories - not removed even when empty.
* The dirs must be listed in canonical form
* i.e. without leading "/dev/"
*/
static char *packaged_dirs[] =
/* RCM related globals */
static void *librcm_hdl;
static thread_t process_rcm_events_tid;
static mutex_t rcm_eventq_lock;
static cond_t rcm_eventq_cv;
static volatile int need_to_exit_rcm_event_thread = 0;
/* Devname globals */
static int devname_debug_msg = 1;
static int devname_first_call = 1;
static int load_devname_nsmaps = FALSE;
static int lookup_door_fd = -1;
static char *lookup_door_path;
static void load_dev_acl(void);
static void update_drvconf(major_t);
static void check_reconfig_state(void);
static void devname_setup_nsmaps(void);
static int is_blank(char *);
int
{
(void) textdomain(TEXT_DOMAIN);
} else {
prog++;
}
if (getuid() != 0) {
devfsadm_exit(1);
}
/*
*/
closefrom(3);
} else {
}
/* the default group is sys */
} else {
}
(void) umask(0);
if (system_labeled == FALSE) {
/*
* is_system_labeled() will return false in case we are
* starting before the first reboot after Trusted Extensions
* is installed. we check for a well known TX binary to
* to see if TX is installed.
*/
else {
/* test hook: see also mkdevalloc.c and allocate.c */
}
}
/* Initialize device allocation list */
if (daemon_mode == TRUE) {
/*
* Build /dev and /devices before daemonizing if
* reconfig booting and daemon invoked with alternate
* root. This is to support install.
*/
load_dev_acl();
(void) modctl(MODSETMINIROOT);
}
/*
* fork before detaching from tty in order to print error
* message if unable to acquire file lock. locks not preserved
* across forks. Even under debug we want to fork so that
* when executed at boot we don't hang.
*/
if (fork() != 0) {
devfsadm_exit(0);
}
/* set directory to / so it coredumps there */
}
/* only one daemon can run at a time */
(void *(*)(void *))instance_flush_thread,
devfsadm_exit(1);
}
/* start the minor_fini_thread */
(void *(*)(void *))minor_fini_thread,
devfsadm_exit(1);
}
/*
* No need for rcm notifications when running
* with an alternate root. So initialize rcm only
* when devfsadm is running with root dir "/".
* Similarly, logindevperms need only be set
* in daemon mode and when root dir is "/".
*/
if (root_dir[0] == '\0') {
(void) rcm_init();
}
} else {
devfsadm_exit(1);
}
} else {
/* not a daemon, so just build /dev and /devices */
if (devalloc_flag != 0)
}
return (0);
}
static void
{
major);
}
static void
{
if (load_devpolicy() != 0)
}
/*
* As devfsadm is run early in boot to provide the kernel with
* minor_perm info, we might as well check for reconfig at the
* same time to avoid running devfsadm twice. This gets invoked
* earlier than the env variable RECONFIG_BOOT is set up.
*/
static void
{
}
}
static void
{
/*
* Inform /dev that system is available, that
* implicit reconfig can now be performed.
*/
}
static void
set_lock_root(void)
{
char *lock_root;
devfsadm_exit(1);
}
}
/*
* Parse arguments for all 6 programs handled from devfsadm.
*/
static void
{
char opt;
char get_linkcompat_opts = FALSE;
char *compat_class;
int num_aliases = 0;
int len;
int retval;
compat_class = "disk";
compat_class = "tape";
compat_class = "port";
compat_class = "audio";
switch (opt) {
case 'd':
break;
case 'n':
/* prevent driver loading and deferred attach */
break;
case 'r':
if (zone_pathcheck(root_dir) !=
devfsadm_exit(1);
break;
case 's':
/*
* useful with -v or -V
*/
break;
case 't':
/* supply a non-default table file */
break;
case 'v':
/* documented verbose flag */
break;
case 'V':
/* undocumented for extra verbose levels */
break;
default:
usage();
break;
}
}
usage();
}
while ((opt =
switch (opt) {
case 'a':
if (len > MAXMODCONFNAME) {
devfsadm_exit(1);
}
} else {
}
num_aliases++;
break;
case 'b':
break;
case 'c':
break;
case 'd':
/*
* need to keep for compatibility, but
* do nothing.
*/
break;
case 'i':
single_drv = TRUE;
break;
case 'm':
break;
case 'n':
/* prevent driver loading and deferred attach */
break;
case 'p':
/* specify alternate path_to_inst file */
break;
case 'R':
/*
* Private flag for suninstall to populate
* device information on the installed root.
*/
if (zone_pathcheck(root_dir) !=
break;
case 'r':
if (zone_pathcheck(devices_dir) !=
devfsadm_exit(1);
break;
case 's':
/*
* suppress. don't create nodes
* useful with -v or -V
*/
break;
case 'v':
/* documented verbose flag */
break;
case 'V':
/* undocumented for extra verbose levels */
break;
default:
usage();
}
}
usage();
}
devfsadm_exit(1);
}
if (retval < 0) {
}
}
int init_drvconf = 0;
int init_perm = 0;
int public_mode = 0;
int init_sysavail = 0;
daemon_mode = TRUE;
}
"a:Cc:deIi:l:mnp:PR:r:sSt:vV:x:")) != EOF) {
if (public_mode)
usage();
} else {
usage();
public_mode = 1;
}
switch (opt) {
case 'a':
break;
case 'C':
break;
case 'c':
num_classes++;
sizeof (char *));
break;
case 'd':
if (daemon_mode == FALSE) {
/*
* Device allocation to be disabled.
*/
}
break;
case 'e':
if (daemon_mode == FALSE) {
/*
* Device allocation to be enabled.
*/
}
break;
case 'I': /* update kernel driver.conf cache */
if (daemon_mode == TRUE)
usage();
init_drvconf = 1;
break;
case 'i':
single_drv = TRUE;
break;
case 'l':
/* specify an alternate module load path */
break;
case 'm':
break;
case 'n':
/* prevent driver loading and deferred attach */
break;
case 'p':
/* specify alternate path_to_inst file */
break;
case 'P':
if (daemon_mode == TRUE)
usage();
/* load minor_perm and device_policy */
init_perm = 1;
break;
case 'R':
/*
* Private flag for suninstall to populate
* device information on the installed root.
*/
break;
case 'r':
break;
case 's':
/*
* useful with -v or -V
*/
break;
case 'S':
if (daemon_mode == TRUE)
usage();
init_sysavail = 1;
break;
case 't':
break;
case 'v':
/* documented verbose flag */
break;
case 'V':
/* undocumented: specify verbose lvl */
break;
case 'x':
/*
* x is the "private switch" option. The
* goal is to not suck up all the other
* option letters.
*/
/* don't build /dev */
/* don't build /devices */
/* don't flush path_to_inst */
} else {
usage();
}
break;
default:
usage();
break;
}
}
usage();
}
/*
* We're not in zone mode; Check to see if the rootpath
* collides with any zonepaths.
*/
devfsadm_exit(1);
}
/*
* Load minor perm before force-loading drivers
* so the correct permissions are picked up.
*/
if (init_perm) {
load_dev_acl();
}
if (init_drvconf)
if (init_sysavail)
devfsadm_exit(0);
/* NOTREACHED */
}
if (load_devname_nsmaps == TRUE) {
devfsadm_exit(0);
}
}
if (get_linkcompat_opts == TRUE) {
num_classes++;
sizeof (char *));
switch (opt) {
case 'C':
break;
case 'n':
/* prevent driver loading or deferred attach */
break;
case 'r':
if (zone_pathcheck(root_dir) !=
devfsadm_exit(1);
break;
case 's':
/* useful with -v or -V */
break;
case 'v':
/* documented verbose flag */
break;
case 'V':
/* undocumented for extra verbose levels */
break;
default:
usage();
}
}
usage();
}
}
}
void
usage(void)
{
} else {
}
devfsadm_exit(1);
}
static void
{
} else {
}
if (node == DI_NODE_NIL) {
/*
* Rapid hotplugging (commonly seen during USB testing),
* may remove a device before the create event for it
* has been processed. To prevent alarming users with
* a superfluous message, we suppress error messages
* for ENXIO and hotplug.
*/
return;
}
/*
* Finished creating devfs files and dev links.
* Log sysevent and notify RCM.
*/
if (ev_subclass)
node);
/* Add new device to device allocation database */
if (system_labeled && update_devdb) {
update_devdb = 0;
}
}
static void
{
/*
* The list head is not used during the deferred create phase
*/
if (flags == DCA_CREATE_LINK)
}
}
/*
* Called in non-daemon mode to take a snap shot of the devinfo tree.
* Then it calls the appropriate functions to build /devices and /dev.
* It also flushes path_to_inst.
* DINFOCACHE snapshot needs to be updated when devfsadm is run.
* This will only happen if the flags that devfsadm uses matches the flags
* that DINFOCACHE uses and that is why flags is set to
* DI_CACHE_SNAPSHOT_FLAGS.
*/
void
{
char name[MAXNAMELEN];
char *fcn = "process_devinfo_tree: ";
lock_dev();
/*
* Update kernel driver.conf cache when devfsadm/drvconfig
* is invoked to build /devices and /dev.
*/
if (load_attach_drv == TRUE)
if (single_drv == TRUE) {
/*
* load a single driver, but walk the entire devinfo tree
*/
if (load_attach_drv == FALSE)
} else if (load_attach_drv == TRUE) {
/*
* Load and attach all drivers, then walk the entire tree.
* If the cache flag is set, use DINFOCACHE to get cached
* data.
*/
if (use_snapshot_cache == TRUE) {
flags = DINFOCACHE;
} else {
flags |= DINFOFORCE;
if (cleanup) {
/*
* files.
*/
flags |= DINFOCLEANUP;
}
}
}
(build_devices == TRUE)) {
}
/* handle pre-cleanup operations desired by the modules. */
devfsadm_exit(1);
}
/* handle post-cleanup operations desired by the modules. */
}
/*ARGSUSED*/
static void
print_cache_signal(int signo)
{
devfsadm_exit(1);
}
}
static void
revoke_lookup_door(void)
{
if (lookup_door_fd != -1) {
err_print("door_revoke of %s failed - %s\n",
}
}
}
/*ARGSUSED*/
static void
catch_exit(int signo)
{
}
/*
* Register with eventd for messages. Create doors for synchronous
* link creation.
*/
static void
daemon_update(void)
{
int fd;
char *fcn = "daemon_update: ";
char door_file[MAXPATHLEN];
const char *subclass_list;
devfsadm_exit(1);
}
devfsadm_exit(1);
}
>= sizeof (door_file)) {
err_print("update_daemon failed to open sysevent service "
"door\n");
devfsadm_exit(1);
}
if ((sysevent_hp = sysevent_open_channel_alt(
devfsadm_exit(1);
}
(void) sysevent_close_channel(sysevent_hp);
devfsadm_exit(1);
}
!= 0) {
(void) sysevent_unbind_subscriber(sysevent_hp);
(void) sysevent_close_channel(sysevent_hp);
devfsadm_exit(1);
}
devfsadm_exit(1);
}
devfsadm_exit(1);
}
devfsadm_exit(1);
}
devfsadm_exit(1);
}
/*
* devname_lookup_door
*/
devfsadm_exit(1);
}
devfsadm_exit(1);
}
DOOR_REFUSE_DESC)) == -1) {
devfsadm_exit(1);
}
goto retry;
devfsadm_exit(1);
}
lookup_door_fd = fd;
/* pass down the door name to kernel for door_ki_open */
else
for (;;) {
(void) pause();
}
}
/*ARGSUSED*/
static void
{
/*
* Must be root to make this call
* If caller is not root, don't touch its data.
*/
goto out;
}
/*
* Root is always present and is the first component of "name" member
*/
/*
* The structure passed in by the door_client uses offsets
* instead of pointers to work across address space boundaries.
* Now copy the data into a structure (dca_impl) which uses
* pointers.
*/
lock_dev();
else
}
static void
lock_dev(void)
{
return;
/* lockout other threads from /dev */
/*
* Lock out other devfsadm processes from /dev.
* If this wasn't the last process to run,
* clear caches
*/
if (enter_dev_lock() != getpid()) {
}
/*
* (re)load the reverse links database if not
* already cached.
*/
if (devlink_cache == NULL)
/*
* If modules were unloaded, reload them. Also use module status
* as an indication that we should check to see if other binding
* files need to be reloaded.
*/
if (module_head == NULL) {
load_modules();
}
if (module_head != NULL)
return;
if (devlinktab_list == NULL) {
devfsadm_exit(1);
}
} else {
}
}
}
/*
* Unlock the device. If we are processing a CACHE_STATE call, we signal a
* minor_fini_thread delayed SYNC_STATE at the end of the call. If we are
* processing a SYNC_STATE call, we cancel any minor_fini_thread SYNC_STATE
* at both the start and end of the call since we will be doing the SYNC_STATE.
*/
static void
unlock_dev(int flag)
{
/* If we are starting a SYNC_STATE, cancel minor_fini_thread SYNC */
if (flag == SYNC_STATE) {
(void) mutex_lock(&minor_fini_mutex);
(void) mutex_unlock(&minor_fini_mutex);
}
return;
if (flag == SYNC_STATE) {
if (update_database)
(void) di_devlink_update(devlink_cache);
(void) di_devlink_close(&devlink_cache, 0);
}
(void) mutex_lock(&minor_fini_mutex);
if (flag == SYNC_STATE) {
/* We did a SYNC_STATE, cancel minor_fini_thread SYNC */
} else {
/* We did a CACHE_STATE, start delayed minor_fini_thread SYNC */
(void) cond_signal(&minor_fini_cv);
}
(void) mutex_unlock(&minor_fini_mutex);
}
/*
* Check that if -r is set, it is not any part of a zone--- that is, that
* the zonepath is not a substring of the root path.
*/
static int
zone_pathcheck(char *checkpath)
{
char *name;
char tmp[MAXPATHLEN];
int err = DEVFSADM_SUCCESS;
if (checkpath[0] == '\0')
return (DEVFSADM_SUCCESS);
/*
* Check if zones is available on this system.
*/
return (DEVFSADM_SUCCESS);
}
/*
* In this case the user has done "devfsadm -r" on some path
* which does not yet exist, or we got some other misc. error.
* We punt and don't resolve the path in this case.
*/
}
}
cookie = setzoneent();
/* Skip the global zone */
continue;
}
continue;
}
/*
* Zone path doesn't exist, or other misc error,
* so we try using the non-resolved pathname.
*/
}
}
/*
* Finally, the comparison. If the zone root path is a
* leading substring of the root path, fail.
*/
break;
}
}
return (err);
}
/*
* Called by the daemon when it receives an event from the devfsadm SLM
* to syseventd.
*
* The devfsadm SLM uses a private event channel for communication to
* devfsadmd set-up via private libsysevent interfaces. This handler is
* used to bind to the devfsadmd channel for event delivery.
* The devfsadmd SLM insures single calls to this routine as well as
* synchronized event delivery.
*
*/
static void
{
char *path;
char *minor;
char *subclass;
char *dev_ev_subclass;
char *driver_name;
int err = 0;
int instance;
int branch_event = 0;
return;
}
/* Check if event is an instance modification */
return;
}
return;
}
&path)) != 0)
goto out;
&dev_ev_subclass) != 0)
&driver_name) != 0)
driver_name = NULL;
&instance) != 0)
instance = -1;
&branch_event) != 0)
branch_event = 0;
} else {
&minor) != 0)
}
lock_dev();
if (branch_event) {
}
} else { /* ESC_DEVFS_DEVI_REMOVE */
if (branch_event) {
}
}
DEVFS_PATHNAME, &path)) != 0)
goto out;
/* just log ESC_DEV_BRANCH... event */
else
lock_dev();
} else
out:
if (err)
}
static void
{
}
/*
* Kernel logs a message when a devinfo node is attached. Try to create
* /dev and /devices for each minor node. minorname can be NULL.
*/
void
{
/*
* Restrict hotplug link creation if daemon
* started with -i option.
*/
if (single_drv == TRUE) {
}
/*
* We are being invoked in response to a hotplug
* event. Also, notify RCM if nodetype indicates
* a network device has been hotplugged.
*/
}
static di_node_t
{
if (clone_node == DI_NODE_NIL)
return (clone_node);
}
static int
{
while (node != DI_NODE_NIL) {
return (1);
}
return (0);
}
/*
* Checks the minor type. If it is an alias node, then lookup
* do the real work.
*/
static int
{
char *mn;
char *nt;
/*
* We match driver here instead of in minor_process
* as we want the actual driver name. This check is
* unnecessary during deferred processing.
*/
if (dep &&
return (DI_WALK_CONTINUE);
}
}
if (minor_type == DDM_MINOR) {
} else if (minor_type == DDM_ALIAS) {
if (clone_node == DI_NODE_NIL) {
return (DI_WALK_CONTINUE);
}
/*
* cache "alias" minor node and free "clone" minor
*/
}
}
return (DI_WALK_CONTINUE);
}
/*
* This is the entry point for each minor node, whether walking
* the entire tree via di_walk_minor() or processing a hotplug event
* for a single devinfo node (via hotplug ndi_devi_online()).
*/
/*ARGSUSED*/
static void
{
int defer;
/*
* if we are here to deactivate device allocation
*/
if (build_devices == TRUE) {
}
return;
}
/*
* This function will create any nodes for /etc/devlink.tab.
* If devlink.tab handles link creation, we don't call any
* devfsadm modules since that could cause duplicate caching
* in the enumerate functions if different re strings are
* passed that are logically identical. I'm still not
* convinced this would cause any harm, but better to be safe.
*
* Deferred processing is available only for devlinks
* created through devfsadm modules.
*/
return;
}
} else {
}
/*
* look for relevant link create rules in the modules, and
* invoke the link create callback function to build a link
* if there is a match.
*/
defer = 0;
continue;
}
/*
* If NOT doing the deferred creates (i.e. 1st pass) and
* rule requests deferred processing cache the minor
* data.
*
* If deferred processing (2nd pass), create links
* ONLY if rule requests deferred processing.
*/
CREATE_DEFER)) {
defer = 1;
continue;
CREATE_DEFER)) {
continue;
}
break;
}
}
}
if (defer)
}
/*
* Cache node and minor in defer list.
*/
static void
{
const char *fcn = "cache_deferred_minor";
"deferred processing. Ignoring minor\n", fcn);
return;
}
} else {
}
}
/*
* If it does, return TRUE.
*/
static int
{
char *m_nodetype, *m_drvname;
case TYPE_EXACT:
0) {
return (FALSE);
}
break;
case TYPE_PARTIAL:
return (FALSE);
}
break;
case TYPE_RE:
0, NULL, 0) != 0) {
return (FALSE);
}
break;
}
}
case DRV_EXACT:
return (FALSE);
}
break;
case DRV_RE:
0, NULL, 0) != 0) {
return (FALSE);
}
break;
}
}
return (TRUE);
}
/*
* If no classes were given on the command line, then return DEVFSADM_SUCCESS.
* Otherwise, return DEVFSADM_SUCCESS if the device "class" from the module
* matches one of the device classes given on the command line,
* otherwise, return DEVFSADM_FAILURE.
*/
static int
{
int i;
if (num_classes == 0) {
return (DEVFSADM_SUCCESS);
}
for (i = 0; i < num_classes; i++) {
return (DEVFSADM_SUCCESS);
}
}
return (DEVFSADM_FAILURE);
}
/*
* call minor_fini on active modules, then unload ALL modules
*/
static void
unload_modules(void)
{
while (create_head != NULL) {
}
}
}
while (remove_head != NULL) {
}
while (module_head != NULL) {
(void) (*(module_head->minor_fini))();
}
}
}
/*
* Load devfsadm logical link processing modules.
*/
static void
load_modules(void)
{
char *last;
char *mdir = module_dirs;
char *fcn = "load_modules: ";
while (*mdir != '\0') {
while (*mdir == ':') {
mdir++;
}
if (*mdir == '\0') {
continue;
}
}
continue;
}
continue;
}
}
}
}
static void
{
char *fcn = "load_module: ";
char *dlerrstr;
void *dlhandle;
int flags;
int n;
int i;
/* ignore any file which does not end in '.so' */
return;
}
} else {
return;
}
return;
}
/* dlsym the _devfsadm_create_reg structure */
} else {
}
/* dlsym the _devfsadm_remove_reg structure */
} else {
}
/* dlsym other module functions, to be called later */
/*
* put a ptr to each struct devfsadm_create on "create_head"
* list sorted in interpose_lvl.
*/
if (create_reg != NULL) {
for (i = 0; i < create_reg->count; i++) {
s_malloc(sizeof (create_list_t));
if (((flags & CREATE_MASK) != 0) &&
err_print("illegal flag combination in "
"module create\n");
continue;
}
err_print("flags value incompatible with "
"node_type value in module create\n");
continue;
}
err_print("illegal TYPE_* flag combination in "
"module create\n");
continue;
}
/* precompile regular expression for efficiency */
if ((n = regcomp(&(create_list_element->
REG_EXTENDED)) != 0) {
n);
continue;
}
}
}
err_print("flags value incompatible with "
"drv_name value in module create\n");
continue;
}
}
err_print("illegal DRV_* flag combination in "
"module create\n");
continue;
}
/* precompile regular expression for efficiency */
if ((n = regcomp(&(create_list_element->
REG_EXTENDED)) != 0) {
}
n);
continue;
}
}
/* add to list sorted by interpose level */
for (create_list_next = &(create_head);
(*create_list_next != NULL) &&
&((*create_list_next)->next));
}
}
/*
* put a ptr to each struct devfsadm_remove on "remove_head"
* list sorted by interpose_lvl.
*/
flags = 0;
if (remove_reg != NULL) {
flags |= RM_NOINTERPOSE;
for (i = 0; i < remove_reg->count; i++) {
s_malloc(sizeof (remove_list_t));
for (remove_list_next = &(remove_head);
(*remove_list_next != NULL) &&
&((*remove_list_next)->next));
}
}
}
/*
* After we have completed a CACHE_STATE, if a SYNC_STATE does not occur
* within 'timeout' secs the minor_fini_thread needs to do a SYNC_STATE
* so that we still call the minor_fini routines.
*/
/*ARGSUSED*/
static void
minor_fini_thread(void *arg)
{
(void) mutex_lock(&minor_fini_mutex);
for (;;) {
/* wait the gather period, or until signaled */
(void) cond_timedwait(&minor_fini_cv,
&minor_fini_mutex, &abstime);
/* if minor_fini was canceled, go wait again */
if (minor_fini_canceled == TRUE)
continue;
/* if minor_fini was delayed, go wait again */
if (minor_fini_delayed == TRUE) {
continue;
}
/* done with cancellations and delays, do the SYNC_STATE */
(void) mutex_unlock(&minor_fini_mutex);
lock_dev();
(void) mutex_lock(&minor_fini_mutex);
}
}
/*
* Attempt to initialize module, if a minor_init routine exists. Set
* the active flag if the routine exists and succeeds. If it doesn't
* exist, just set the active flag.
*/
static int
{
char *fcn = "call_minor_init: ";
return (DEVFSADM_SUCCESS);
}
return (DEVFSADM_SUCCESS);
}
return (DEVFSADM_FAILURE);
}
"new state: active\n");
return (DEVFSADM_SUCCESS);
}
/*
* Creates a symlink 'link' to the physical path of node:minor.
* Construct link contents, then call create_link_common().
*/
/*ARGSUSED*/
int
{
char *acontents;
char *dev_path;
int numslashes;
int rv;
int i, link_exists;
int last_was_slash = FALSE;
/*
* try to use devices path
*/
} else {
devfsadm_exit(1);
}
}
/* prepend link with dev_dir contents */
/*
* Calculate # of ../ to add. Account for double '//' in path.
* Ignore all leading slashes.
*/
for (i = 0; link[i] == '/'; i++)
;
if (link[i] == '/') {
if (last_was_slash == FALSE) {
numslashes++;
}
} else {
}
}
/* Don't count any trailing '/' */
numslashes--;
}
rcontents[0] = '\0';
do {
} while (numslashes-- != 0);
if (devlinks_debug == TRUE) {
}
== DEVFSADM_SUCCESS) {
/*
* Add this device to the list of allocatable devices.
*/
}
} else {
}
if (link_exists == TRUE) {
/*
* if this is a removable disk, add it
* as that to device allocation database.
*/
}
}
/* Link exists or was just created */
}
return (rv);
}
/*
* Creates a symlink link to primary_link. Calculates relative
* directory offsets, then calls link_common().
*/
/*ARGSUSED*/
int
{
int rv, link_exists;
char *fpath;
char *tpath;
char *op;
/* prepend link with dev_dir contents */
/*
* building extra link, so use first link as link contents, but first
* make it relative.
*/
}
/* Count directories to go up, if any, and add "../" */
while (*fpath != '\0') {
if (*fpath == '/') {
op += 3;
}
fpath++;
}
/*
* Back up to the start of the current path component, in
* case in the middle
*/
tpath--;
}
if (devlinks_debug == TRUE) {
}
== DEVFSADM_SUCCESS) {
/*
* we need to save the ultimate /devices contents, and not the
* secondary link, since hotcleanup only looks at /devices path.
* Since we don't have devices path here, we can try to get it
* by readlink'ing the secondary link. This assumes the primary
* link was created first.
*/
if (system_labeled &&
/*
* Add this device to the list of allocatable devices.
*/
int instance = 0;
op++;
}
} else {
}
/*
* If link exists or was just created, add it to the database
*/
if (link_exists == TRUE) {
}
return (rv);
}
/* returns pointer to the devices directory */
char *
{
return (devices_dir);
}
/*
* Does the actual link creation. VERBOSE_MID only used if there is
* a change. CHATTY_MID used otherwise.
*/
static int
{
int try;
int linksize;
int max_tries = 0;
static int prev_link_existed = TRUE;
char *hide;
/* Database is not updated when file_mods == FALSE */
if (linksize > 0) {
return (DEVFSADM_SUCCESS);
} else {
/* failure only in that the link existed */
return (DEVFSADM_FAILURE);
}
} else {
return (DEVFSADM_SUCCESS);
}
}
/*
* systems calls are expensive, so predict whether to readlink
* or symlink first, based on previous attempt
*/
if (prev_link_existed == FALSE) {
try = CREATE_LINK;
} else {
}
while (++max_tries <= 3) {
switch (try) {
case CREATE_LINK:
contents);
/* link successfully created */
return (DEVFSADM_SUCCESS);
} else {
switch (errno) {
case ENOENT:
/* dirpath to node doesn't exist */
*hide = '\0';
*hide = '/';
break;
case EEXIST:
break;
default:
return (DEVFSADM_FAILURE);
}
}
break;
case READ_LINK:
if (linksize >= 0) {
try = CREATE_LINK;
} else {
"link exists and is correct:"
" %s -> %s\n", devlink,
contents);
/* failure in that the link existed */
return (DEVFSADM_FAILURE);
}
} else {
switch (errno) {
case EINVAL:
/* not a symlink, remove and create */
default:
/* maybe it didn't exist at all */
try = CREATE_LINK;
break;
}
}
break;
}
}
return (DEVFSADM_FAILURE);
}
static void
set_logindev_perms(char *devlink)
{
int rv;
char *devfs_path = NULL;
/*
* We only want logindev perms to be set when a device is
* hotplugged or an application requests synchronous creates.
* So we enable this only in daemon mode. In addition,
* login(1) only fixes the std. /dev dir. So we don't
* change perms if alternate root is set.
* login_dev_enable is TRUE only in these cases.
*/
if (login_dev_enable != TRUE)
return;
/*
* Normally, /etc/logindevperm has few (8 - 10 entries) which
* may be regular expressions (globs were converted to RE).
* So just do a linear search through the list.
*/
NULL, 0) == 0) {
break;
}
}
return;
/*
* we have a match, now find the driver associated with this
* minor node using a snapshot on the physical path
*/
if (devfs_path) {
struct driver_list *list;
char *p;
/* truncate on : so we can take a snapshot */
if (p == NULL) {
return;
}
*p = '\0';
if (node) {
if (drv) {
}
}
/* search thru the driver list specified in logindevperm */
while (list) {
drv) == 0) {
"driver %s match!\n", drv);
break;
}
}
return;
}
}
} else {
return;
}
/*
* We have a match. We now attempt to determine the
* owner and group of the console user.
*
* stat() the console device newdev->ldev_console
* which will always exist - it will have the right owner but
* not the right group. Use getpwuid_r() to determine group for this
* uid.
* Note, it is safe to use name service here since if name services
* are not available (during boot or in single-user mode), then
* console owner will be root and its gid can be found in
* local files.
*/
return;
}
return;
}
return;
}
}
}
/*
* Reset /devices node with appropriate permissions and
* ownership as specified in /etc/minor_perm.
*/
static void
{
int devalloc_is_on = 0;
int spectype;
/* lphy_path starts with / */
devfsadm_exit(1);
}
char *driver;
} else
/*
* compare and set permissions and ownership
*
* Under devfs, a quick insertion and removal of USB devices
* would cause stat of physical path to fail. In this case,
* we emit a verbose message, but don't print errors.
*/
return;
}
/*
* If we are here for a new device
* If device allocation is on
* then
* set ownership to root:other and permissions to 0000
* else
* set ownership and permissions as specified in minor_perm
* If we are here for an existing device
* If device allocation is to be turned on
* then
* reset ownership to root:other and permissions to 0000
* else if device allocation is to be turned off
* reset ownership and permissions to those specified in
* minor_perm
* else
* preserve existing/user-modified ownership and
* permissions
*
* devfs indicates a new device by faking access time to be zero.
*/
devalloc_is_on = da_is_on();
int i;
char *nt;
/*
* Leave existing devices as they are if we are not
*/
return;
return;
for (i = 0; devalloc_list[i]; i++) {
/*
* One of the types recognized by devalloc,
* reset attrs.
*/
break;
}
if (devalloc_list[i] == NULL)
return;
}
/* Nothing more to do if simulating */
return;
}
/*
* we are here either to turn device allocation on
* or to add a new device while device allocation in on
*/
mode = DEALLOC_MODE;
}
}
}
/* Report that we actually did something */
}
/*
* Removes logical link and the minor node it refers to. If file is a
* link, we recurse and try to remove the minor node (or link if path is
* a double link) that file's link contents refer to.
*/
static void
{
char *fcn = "devfsadm_rm_work: ";
int linksize;
char *ptr;
/* TYPE_LINK split into multiple if's due to excessive indentations */
}
} else {
} else {
*ptr = '\0';
*ptr = '/';
} else {
}
}
}
}
}
}
/*
* Note: we don't remove /devices entries because they are
* covered by devfs.
*/
}
void
devfsadm_rm_link(char *file)
{
}
void
devfsadm_rm_all(char *file)
{
}
static int
{
int i;
const char *fcn = "s_rmdir";
/*
* Certain directories are created at install time by packages.
* Some of them (listed in packaged_dirs[]) are required by apps
* and need to be present even when empty.
*/
return (-1);
}
}
}
}
/*
* Try to remove any empty directories up the tree. It is assumed that
* pathname is a file that was removed, so start with its parent, and
* work up the tree.
*/
static void
rm_parent_dir_if_empty(char *pathname)
{
char *fcn = "rm_parent_dir_if_empty: ";
char *pathlist;
int len;
/*
* ascend up the dir tree, deleting all empty dirs.
* Return immediately if a dir is not empty.
*/
for (;;) {
return;
}
*ptr = '\0';
return;
}
/*
* An empty pathlist implies an empty directory
*/
if (len == 0) {
return;
}
} else {
/* some other file is here, so return */
return;
}
}
}
/*
* This function and all the functions it calls below were added to
* handle the unique problem with world wide names (WWN). The problem is
* that if a WWN device is moved to another address on the same controller
* its logical link will change, while the physical node remains the same.
* The result is that two logical links will point to the same physical path
* in /devices, the valid link and a stale link. This function will
* find all the stale nodes, though at a significant performance cost.
*
* Caching is used to increase performance.
* A cache will be built from disk if the cache tag doesn't already exist.
* The cache tag is a regular expression "dir_re", which selects a
* subset of disks to search from typically something like
* be maintained, so entries are added as new links are created, and removed
* as old links are deleted. The whole cache is flushed if we are a daemon,
* and another devfsadm process ran in between.
*
* Once the cache is built, this function finds the cache which matches
* dir_re, and then it searches all links in that cache looking for
* any link whose contents match "valid_link_contents" with a corresponding link
* which does not match "valid_link". Any such matches are stale and removed.
*/
void
{
char *valid_link_contents;
char *dev_path;
/*
* try to use devices path
*/
} else {
devfsadm_exit(1);
}
}
/*
* As an optimization, check to make sure the corresponding
* devlink was just created before continuing.
*/
return;
}
/*
* See hot_cleanup() for why we do this
*/
"valid link is: %s -> %s\n",
/*
* Use a copy of the cached link name as the
* cache entry will go away during link removal
*/
}
}
}
/*
* Return previously created cache, or create cache.
*/
static linkhead_t *
get_cached_links(char *dir_re)
{
int n;
return (linkhead);
}
}
/*
* This tag is not in cache, so add it, along with all its
* matching /dev entries. This is the only time we go to disk.
*/
REG_EXTENDED)) != 0) {
}
/* call build_devlink_list for each directory in the dir_re RE */
if (dir_re[0] == '/') {
} else {
}
return (linkhead);
}
static void
{
char *fcn = "build_devlink_list: ";
char *ptr;
char *r_contents;
char *r_devlink;
int linksize;
int i = 0;
do {
if (linksize <= 0) {
/*
* The first pass through the do loop we may readlink()
* non-symlink files(EINVAL) from false regexec matches.
* Suppress error messages in those cases or if the link
* content is the empty string.
*/
return;
}
i = 1;
/*
* assume that link contents is really a pointer to
* another link, so recurse and read its link contents.
*
* some link contents are absolute:
*/
"link. missing '/'\n", fcn,
return;
}
*ptr = '\0';
*ptr = '/';
} else {
}
} else {
newlink[0] = '\0';
}
} while (newlink[0] != '\0');
return;
}
if (r_devlink[0] != '/')
return;
r_devlink += 1;
}
/*
* to be consistent, devlink must not begin with / and must be
* relative to /dev/, whereas physpath must contain / and be
* relative to /devices.
*/
static void
{
int added = 0;
return;
}
0) == 0) {
added++;
}
}
}
/*
* Remove devlink from cache. Devlink must be relative to /dev/ and not start
* with /.
*/
static void
rm_link_from_cache(char *devlink)
{
0) == 0) {
/*
* We are removing our caller's
* "next" link. Update the nextlink
* field in the head so that our
* callers accesses the next valid
* link
*/
"CACHE\n", devlink);
} else {
}
}
}
}
}
static void
{
linkhead = nextlinkhead) {
}
}
headlinkhead = NULL;
}
/*
* Called when the kernel has modified the incore path_to_inst data. This
* function will schedule a flush of the data to the filesystem.
*/
static void
devfs_instance_mod(void)
{
char *fcn = "devfs_instance_mod: ";
/* signal instance thread */
(void) mutex_lock(&count_lock);
inst_count++;
(void) cond_signal(&cv);
(void) mutex_unlock(&count_lock);
}
static void
instance_flush_thread(void)
{
int i;
int idle;
for (;;) {
(void) mutex_lock(&count_lock);
while (inst_count == 0) {
}
inst_count = 0;
" Enter delay loop\n");
/*
* Wait MAX_IDLE_DELAY seconds after getting the last flush
* path_to_inst event before invoking a flush, but never wait
* more than MAX_DELAY seconds after getting the first event.
*/
(void) mutex_unlock(&count_lock);
(void) sleep(1);
(void) mutex_lock(&count_lock);
/* shorten the delay if we are idle */
if (inst_count == 0) {
idle++;
if (idle > MAX_IDLE_DELAY) {
break;
}
} else {
inst_count = idle = 0;
}
}
(void) mutex_unlock(&count_lock);
}
}
/*
* Helper function for flush_path_to_inst() below; this routine calls the
* inst_sync syscall to flush the path_to_inst database to the given file.
*/
static int
do_inst_sync(char *filename)
{
void (*sigsaved)(int);
int err = 0;
switch (err) {
case 0:
return (DEVFSADM_SUCCESS);
case EALREADY: /* no-op, path_to_inst already up to date */
return (EALREADY);
case ENOSYS:
break;
case EPERM:
break;
default:
break;
}
return (DEVFSADM_FAILURE);
}
/*
* Flush the kernel's path_to_inst database to /etc/path_to_inst. To do so
* safely, the database is flushed to a temporary file, then moved into place.
*
* The following files are used during this process:
* /etc/path_to_inst: The path_to_inst file
* /etc/path_to_inst.<pid>: Contains data flushed from the kernel
* /etc/path_to_inst.old: The backup file
* /etc/path_to_inst.old.<pid>: Temp file for creating backup
*
*/
static void
flush_path_to_inst(void)
{
char *new_inst_file = NULL;
char *old_inst_file = NULL;
char *old_inst_file_npid = NULL;
int err = 0;
int c;
int inst_strlen;
if (flush_path_to_inst_enable == FALSE) {
return;
}
sizeof (INSTANCE_FILE_SUFFIX));
}
goto out;
/*NOTREACHED*/
}
/*
* Now we deal with the somewhat tricky updating and renaming
* of this critical piece of kernel state.
*/
/*
* Copy the current instance file into a temporary file.
* Then rename the temporary file into the backup (.old)
* file and rename the newly flushed kernel data into
* the instance file.
* Of course if 'inst_file' doesn't exist, there's much
* less for us to do .. tee hee.
*/
/*
* No such file. Rename the new onto the old
*/
goto out;
/*NOTREACHED*/
}
}
/*
* Can't open the 'old_inst_file' file for writing.
* This is somewhat strange given that the syscall
* just succeeded to write a file out.. hmm.. maybe
* the fs just filled up or something nasty.
*
* Anyway, abort what we've done so far.
*/
goto out;
/*NOTREACHED*/
}
/*
* Copy current instance file into the temporary file
*/
err = 0;
break;
}
}
goto out;
/* NOTREACHED */
}
/*
* Set permissions to be the same on the backup as
* /etc/path_to_inst.
*/
/*
* So far, everything we've done is more or less reversible.
* But now we're going to commit ourselves.
*/
(void) snprintf(old_inst_file_npid,
inst_strlen + sizeof (INSTANCE_FILE_SUFFIX),
}
out:
if (inst_file_fp != NULL) {
}
}
}
}
}
}
/*
* detach from tty. For daemon mode.
*/
void
{
(void) setsid();
if (DEVFSADM_DEBUG_ON == TRUE) {
return;
}
(void) close(0);
(void) close(1);
(void) close(2);
(void) dup(0);
(void) dup(0);
}
/*
* Use an advisory lock to synchronize updates to /dev. If the lock is
* held by another process, block in the fcntl() system call until that
* process drops the lock or exits. The lock file itself is
* DEV_LOCK_FILE. The process id of the current and last process owning
* the lock is kept in the lock file. After acquiring the lock, read the
* process id and return it. It is the process ID which last owned the
* lock, and will be used to determine if caches need to be flushed.
*
* NOTE: if the devlink database is held open by the caller, it may
* be closed by this routine. This is to enforce the following lock ordering:
* 1) /dev lock 2) database open
*/
{
int n;
return (0);
}
if (dev_lock_fd < 0) {
devfsadm_exit(1);
}
/* try for the lock, but don't wait */
pid = 0;
(int)pid);
devfsadm_exit(1);
}
/*
* wait for the dev lock. If we have the database open,
* close it first - the order of lock acquisition should
* always be: 1) dev_lock 2) database
* This is to prevent deadlocks with any locks the
* database code may hold.
*/
(void) di_devlink_close(&devlink_cache, 0);
devfsadm_exit(1);
}
}
}
pid = 0;
return (pid);
}
devfsadm_exit(1);
}
if (n != sizeof (pid_t)) {
devfsadm_exit(1);
}
return (last_owner_pid);
}
/*
* Drop the advisory /dev lock, close lock file. Close and re-open the
* file every time so to ensure a resync if for some reason the lock file
* gets removed.
*/
void
{
if (hold_dev_lock == FALSE) {
return;
}
}
devfsadm_exit(1);
}
}
/*
*
* Use an advisory lock to ensure that only one daemon process is active
* in the system at any point in time. If the lock is held by another
* process, do not block but return the pid owner of the lock to the
* caller immediately. The lock is cleared if the holding daemon process
* exits for any reason even if the lock file remains, so the daemon can
* be restarted if necessary. The lock file is DAEMON_LOCK_FILE.
*/
enter_daemon_lock(void)
{
if (daemon_lock_fd < 0) {
devfsadm_exit(1);
}
devfsadm_exit(1);
}
}
}
return (getpid());
}
/*
* Drop the advisory daemon lock, close lock file
*/
void
exit_daemon_lock(void)
{
if (hold_daemon_lock == FALSE) {
return;
}
}
devfsadm_exit(1);
}
}
/*
* Called to removed danging nodes in two different modes: RM_PRE, RM_POST.
* RM_PRE mode is called before processing the entire devinfo tree, and RM_POST
* is called after processing the entire devinfo tree.
*/
static void
{
char *fcn = "pre_and_post_cleanup: ";
return;
/*
* the generic function recurse_dev_re is shared among different
* functions, so set the method and data that it should use for
* matches.
*/
(void) mutex_lock(&nfp_mutex);
/*
* If reached this point, RM_PRE or RM_POST cleanup is
* desired. clean_ok() decides whether to clean
* under the given circumstances.
*/
dev_dirs_re, &rd);
}
}
}
(void) mutex_unlock(&nfp_mutex);
}
/*
* clean_ok() determines whether cleanup should be done according
* to the following matrix:
*
* command line arguments RM_PRE RM_POST RM_PRE && RM_POST &&
* RM_ALWAYS RM_ALWAYS
* ---------------------- ------ ----- --------- ----------
*
* <neither -c nor -C> - - pre-clean post-clean
*
* -C pre-clean post-clean pre-clean post-clean
*
* -C -c class pre-clean post-clean pre-clean post-clean
* if class if class if class if class
* matches matches matches matches
*
* -c class - - pre-clean post-clean
* if class if class
* matches matches
*
*/
static int
{
int i;
if (single_drv == TRUE) {
/* no cleanup at all when using -i option */
return (DEVFSADM_FAILURE);
}
/*
* no cleanup if drivers are not loaded. We make an exception
* for the "disks" program however, since disks has a public
* cleanup flag (-C) and disk drivers are usually never
* unloaded.
*/
return (DEVFSADM_FAILURE);
}
/* if the cleanup flag was not specified, return false */
return (DEVFSADM_FAILURE);
}
if (num_classes == 0) {
return (DEVFSADM_SUCCESS);
}
/*
* if reached this point, check to see if the class in the given
* remove structure matches a class given on the command line
*/
for (i = 0; i < num_classes; i++) {
return (DEVFSADM_SUCCESS);
}
}
return (DEVFSADM_FAILURE);
}
/*
* Called to remove dangling nodes after receiving a hotplug event
* containing the physical node pathname to be removed.
*/
void
char *driver_name, int instance)
{
char *fcn = "hot_cleanup: ";
int path_len;
int skip;
int ret;
/*
* dev links can go away as part of hot cleanup.
* So first build event attributes in order capture dev links.
*/
if (ev_subclass != NULL)
(void) mutex_lock(&nfp_mutex);
/*
* The remove callback below may remove
* subsequent links in the list.
* Save the next link in the head. If
* the callback removes the next link
* the saved pointer in the head will be
* updated by the callback to point at
* the next valid link.
*/
/*
* if devlink is in no-further-process hash,
* skip its remove
*/
continue;
if (minor_name)
else
path_len);
if (skip ||
continue;
"%sremoving %s -> %s\n", fcn,
/*
* Use a copy of the cached link name
* as the cache entry will go away
* during link removal
*/
((void (*)(char *))
} else {
ret = ((int (*)(char *))
if (ret == DEVFSADM_TERMINATE)
}
}
}
}
(void) mutex_unlock(&nfp_mutex);
/* update device allocation database */
if (system_labeled) {
int ret = 0;
int devtype = 0;
char devname[MAXNAMELEN];
devname[0] = '\0';
else
goto out;
sizeof (devname));
if (ret != -1)
}
out:
/* now log an event */
if (nvl) {
}
}
/*
* Open the dir current_dir. For every file which matches the first dir
* component of path_re, recurse. If there are no more *dir* path
* components left in path_re (ie no more /), then call function rd->fcn.
*/
static void
{
char *slash;
char *anchored_path_re;
char *pathlist;
char *listp;
return;
}
goto out;
}
/* match */
"path = %s\n", new_path);
} else {
/* reached the leaf component of path_re */
"recurse_dev_re: calling fcn\n");
}
}
}
out:
}
/*
* Found a devpath which matches a RE in the remove structure.
* Now check to see if it is dangling.
*/
static void
{
int ret;
char *fcn = "matching_dev: ";
devpath);
/*
* If the link is in the no-further-process hash
* don't do any remove operation on it.
*/
return;
return;
}
((void (*)(char *))
else {
ret = ((int (*)(char *))
if (ret == DEVFSADM_TERMINATE) {
/*
* We want no further remove processing for
* this link. Add it to the nfp_hash;
*/
}
}
}
}
int
{
*devfs_path = NULL;
/* prepend link with dev_dir contents */
/* We *don't* want a stat of the /devices node */
}
int
devfsadm_link_valid(char *link)
{
int instance = 0;
/* prepend link with dev_dir contents */
return (DEVFSADM_FALSE);
}
type = 0;
rv = DEVFSADM_FALSE;
} else {
rv = DEVFSADM_TRUE;
}
/*
* The link exists. Add it to the database
*/
}
return (rv);
}
/*
* devpath: Absolute path to /dev link
* content_p: Returns malloced string (link content)
* type_p: Returns link type: primary or secondary
* dangle: if set, check if link is dangling
* Returns:
* TRUE if dangling
* FALSE if not or if caller doesn't care
* Caller is assumed to have initialized pointer contents to NULL
*/
static int
int dangle)
{
char *fcn = "resolve_link: ";
char *ptr;
int linksize;
if (linksize <= 0) {
return (FALSE);
} else {
}
if (content_p) {
}
/*
* Check to see if this is a link pointing to another link in /dev. The
* cheap way to do this is to look for a lack of ../devices/.
*/
if (type_p) {
}
/*
* assume that linkcontents is really a pointer to another
* link, and if so recurse and read its link contents.
*/
(void) strcpy(stage_link,
} else {
contents);
return (TRUE);
}
*ptr = '\0';
*ptr = '/';
}
dangle));
}
/* Current link points at a /devices minor node */
if (type_p) {
}
if (devfs_path)
if (dangle)
return (rv);
}
/*
* Returns the substring of interest, given a path.
*/
static char *
{
char at[] = "@";
char *fcn = "alloc_cmp_str";
/*
* extract match flags from the flags argument.
*/
/*
* MATCH_CALLBACK and MATCH_ALL are the only flags
* which may be used if "path" is a /dev path
*/
if (match == MATCH_CALLBACK) {
return (NULL);
}
return (cmp_str);
}
return (cmp_str);
}
/*
* The remaining flags make sense only for /devices
* paths
*/
goto err;
}
if (match == MATCH_MINOR) {
/* A NULL "match_arg" values implies entire minor */
goto err;
}
return (cmp_str);
}
goto err;
}
if (match == MATCH_PARENT) {
goto err;
}
} else {
*np = '\0';
}
return (cmp_str);
}
/* ap can be NULL - Leaf address may not exist or be empty string */
/* minor is no longer of interest */
*mp = '\0';
if (match == MATCH_NODE) {
if (ap)
*ap = '\0';
return (cmp_str);
} else if (match == MATCH_ADDR) {
/*
* The empty string is a valid address. The only MATCH_ADDR
* allowed in this case is against the whole address or
* the first component of the address (match_arg=NULL/"0"/"1")
* Note that in this case, the path won't have an "@"
* As a result ap will be NULL. We fake up an ap = @'\0'
* so that get_component() will work correctly.
*/
}
goto err;
}
return (cmp_str);
}
/*FALLTHRU*/
err:
return (NULL);
}
/*
* "str" is expected to be a string with components separated by ','
* The terminating null char is considered a separator.
* get_component() will remove the portion of the string beyond
* the component indicated.
* If comp_str is NULL, the entire "str" is returned.
*/
static char *
{
long comp;
char *cp;
return (NULL);
}
return (str);
}
errno = 0;
return (NULL);
}
if (comp == 0)
return (str);
comp--;
break;
}
}
if (comp == 0) {
*cp = '\0';
} else {
}
return (str);
}
/*
* Enumerate serves as a generic counter as well as a means to determine
* logical unit/controller numbers for such items as disk and tape
* drives.
*
* rules[] is an array of devfsadm_enumerate_t structures which defines
* the enumeration rules to be used for a specified set of links in /dev.
* The set of links is specified through regular expressions (of the flavor
* described in regex(5)). These regular expressions are used to determine
* the set of links in /dev to examine. The last path component in these
* regular expressions MUST contain a parenthesized subexpression surrounding
* the RE which is to be considered the enumerating component. The subexp
* member in a rule is the subexpression number of the enumerating
* component. Subexpressions in the last path component are numbered starting
* from 1.
*
* A cache of current id assignments is built up from existing symlinks and
* new assignments use the lowest unused id. Assignments are based on a
* match of a specified substring of a symlink's contents. If the specified
* component for the devfs_path argument matches the corresponding substring
* for a existing symlink's contents, the cached id is returned. Else, a new
* id is created and returned in *buf. *buf must be freed by the caller.
*
* An id assignment may be governed by a combination of rules, each rule
* applicable to a different subset of links in /dev. For example, controller
* numbers may be determined by a combination of disk symlinks in /dev/[r]dsk
* rules to derive the "substring of interest". In such cases, the rules
* array will have more than one element.
*/
int
{
}
int
{
}
/*
* Same as above, but allows a starting value to be specified.
* Private to devfsadm.... used by devlinks.
*/
static int
{
}
/*
* devfsadm_enumerate_char serves as a generic counter returning
* a single letter.
*/
int
{
}
/*
* Same as above, but allows a starting char to be specified.
* Private to devfsadm - used by ports module (port_link.c)
*/
int
{
}
/*
* For a given numeral_set (see get_cached_set for desc of numeral_set),
* search all cached entries looking for matches on a specified substring
* of devfs_path. The substring is derived from devfs_path based on the
* rule specified by "index". If a match is found on a cached entry,
* return the enumerated id in buf. Otherwise, create a new id by calling
* new_id, then cache and return that entry.
*/
static int
int multiple)
{
int matchcount = 0;
char *cmp_str;
char *fcn = "find_enum_id";
return (DEVFSADM_FAILURE);
}
if (devfs_path == NULL) {
return (DEVFSADM_FAILURE);
}
fcn, devfs_path);
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_FAILURE);
}
/*
* Check and see if a matching entry is already cached.
*/
&matchnp);
return (DEVFSADM_MULTIPLE);
else
return (DEVFSADM_FAILURE);
}
/* if matching entry already cached, return it */
if (matchcount == 1) {
return (DEVFSADM_SUCCESS);
}
/*
* no cached entry, initialize a numeral struct
* by calling new_id() and cache onto the numeral_set
*/
/* insert to head of list for fast lookups */
return (DEVFSADM_SUCCESS);
}
/*
* Looks up the specified cache for a match with a specified string
* Returns:
* -1 : on error.
* 0/1/2 : Number of matches.
* Returns the matching element only if there is a single match.
* If the "uncached" flag is set, derives the "cmp_str" afresh
* for the match instead of using cached values.
*/
static int
{
int uncached;
char *fcn = "lookup_enum_cache";
char *cp;
return (-1);
}
uncached = 0;
uncached = 1;
}
/*
* Check and see if a matching entry is already cached.
*/
return (-1);
}
if (uncached) {
return (-1);
} else {
}
if (rv == 0) {
if (matchcount++ != 0) {
break; /* more than 1 match. */
}
}
}
return (matchcount);
}
#ifdef DEBUG
static void
{
int i;
char *fcn = "dump_enum_cache";
}
}
}
#endif
/*
* For a given set of regular expressions in rules[], this function returns
* either a previously cached struct numeral_set or it will create and
* cache a new struct numeral_set. There is only one struct numeral_set
* for the combination of REs present in rules[]. Each numeral_set contains
* the regular expressions in rules[] used for cache selection AND a linked
* list of struct numerals, ONE FOR EACH *UNIQUE* numeral or character ID
* selected by the grouping parenthesized subexpression found in the last
* path component of each rules[].re. For example, the RE: "rmt/([0-9]+)"
* Each rmt/X will store a *single* struct numeral... ie 0, 1, 2 each get a
* single struct numeral. There is no need to store more than a single logical
* node matching X since the information desired in the devfspath would be
* identical for the portion of the devfspath of interest. (the part up to,
* but not including the minor name in this example.)
*
* If the given numeral_set is not yet cached, call enumerate_recurse to
* create it.
*/
static numeral_set_t *
{
/* linked list of numeral sets */
int i;
char *path_left;
char *fcn = "get_enum_cache";
/*
* See if we've already cached this numeral set.
*/
/*
* check all regexp's passed in function against
* those in cached set.
*/
continue;
}
for (i = 0; i < nrules; i++) {
break;
}
}
if (i == nrules) {
return (setp);
}
}
/*
* If the MATCH_UNCACHED flag is set, we should not be here.
*/
for (i = 0; i < nrules; i++) {
return (NULL);
}
}
/*
* Since we made it here, we have not yet cached the given set of
* logical nodes matching the passed re. Create a cached entry
* struct numeral_set and populate it with a minimal set of
* logical nodes from /dev.
*/
for (i = 0; i < nrules; i++) {
}
/* put this new cached set on the cached set list */
/*
* For each RE, search disk and cache any matches on the
* numeral list.
*/
for (i = 0; i < nrules; i++) {
}
#ifdef DEBUG
#endif
return (setp);
}
/*
* This function stats the pathname namebuf. If this is a directory
* link, and then stat and return it. This is valid for the same reason
* that we only need to read a single pathname for multiple matching
* logical ID's... ie, all the logical nodes should contain identical
* physical paths for the parts we are interested.
*/
int
{
char *cp;
char *pathlist;
char *listp;
int len;
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_SUCCESS);
}
/*
* If it is a dir, recurse down until we find a link and
* then use the link.
*/
return (DEVFSADM_FAILURE);
}
/*
* Search each dir entry looking for a symlink. Return
* the first symlink found in namebuf. Recurse dirs.
*/
*cp = '\0';
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_SUCCESS);
}
*cp = '\0';
}
}
/* no symlink found, so return error */
return (DEVFSADM_FAILURE);
}
/*
* An existing matching ID was not found, so this function is called to
* create the next lowest ID. In the INTEGER case, return the next
* lowest unused integer. In the case of LETTER, return the next lowest
* unused letter. Return empty string if all 26 are used.
* Only IDs >= min will be returned.
*/
char *
{
int imin;
char *retval;
static char tempbuff[8];
char letter[26], i;
}
for (i = 0; i < 26; i++) {
letter[i] = 0;
}
}
for (i = imin; i < 26; i++) {
if (letter[i] == 0) {
retval[0] = 'a' + i;
return (retval);
}
}
return (s_strdup(""));
}
}
/* sort list */
break;
}
}
}
}
/* now search sorted list for first hole >= imin */
imin++;
} else {
break;
}
}
}
/* free temp list */
}
}
return (s_strdup(""));
}
/*
* Search current_dir for all files which match the first path component
* of path_left, which is an RE. If a match is found, but there are more
* components of path_left, then recurse, otherwise, if we have reached
* the last component of path_left, call create_cached_numerals for each
* file. At some point, recurse_dev_re() should be rewritten so that this
* function can be eliminated.
*/
static void
{
char *slash;
char *new_path;
char *numeral_id;
char *pathlist;
char *listp;
int len;
return;
}
/* get rid of any extra '/' */
while (*path_left == '/') {
path_left++;
}
*slash = '\0';
}
/*
* Returns true if path_left matches the list entry.
* If it is the last path component, pass subexp
* so that it will return the corresponding ID in
* numeral_id.
*/
numeral_id = NULL;
} else {
if (numeral_id != NULL) {
}
}
}
}
*slash = '/';
}
}
/*
* Returns true if file matches file_re. If subexp is non-zero, it means
* we are searching the last path component and need to return the
* parenthesized subexpression subexp in id.
*
*/
static int
{
int match = 0;
int nelements;
if (subexp != 0) {
pmatch = (regmatch_t *)
} else {
nelements = 0;
}
}
return (0);
}
match = 1;
}
}
}
return (match);
}
/*
* This function is called for every file which matched the leaf
* component of the RE. If the "numeral_id" is not already on the
* numeral set's numeral list, add it and its physical path.
*/
static void
{
int linksize;
const char *fcn = "create_cached_numeral";
/*
* We found a numeral_id from an entry in /dev which matched
* the re passed in from devfsadm_enumerate. We only need to make sure
* ONE copy of numeral_id exists on the numeral list. We only need
* of controller N.
*/
return;
}
}
/* NOT on list, so add it */
/*
* If path is a dir, it is changed to the first symbolic link it find
* if it finds one.
*/
return;
}
/* If we get here, we found a symlink */
if (linksize <= 0) {
return;
}
/*
* the following just points linkptr to the root of the /devices
* node if it is a minor node, otherwise, to the first char of
* linkbuf if it is a link.
*/
return;
}
}
/*
* This should be called either before or after granting access to a
* command line version of devfsadm running, since it may have changed
* the state of /dev. It forces future enumerate calls to re-build
* cached information from /dev.
*/
void
{
int i;
/*
* check all regexp's passed in function against
* those in cached set.
*/
}
}
}
}
/*
* Copies over links from /dev to <root>/dev and device special files in
* /devices to <root>/devices, preserving the existing file modes. If
* the link or special file already exists on <root>, skip the copy. (it
* would exist only if a package hard coded it there, so assume package
* knows best?). Use /etc/name_to_major and <root>/etc/name_to_major to
* make translations for major numbers on device special files. No need to
* make a translation on minor_perm since if the file was created in the
* miniroot then it would presumably have the same minor_perm entry in
* <root>/etc/minor_perm. To be used only by install.
*/
int
devfsadm_copy(void)
{
/* load the installed root's name_to_major for translations */
return (DEVFSADM_FAILURE);
}
/* Copy /dev to target disk. No need to copy /devices with devfs */
/* Let install handle copying over path_to_inst */
return (DEVFSADM_SUCCESS);
}
/*
* This function copies links, dirs, and device special files.
* Note that it always returns DEVFSADM_SUCCESS, so that nftw doesn't
* abort.
*/
/*ARGSUSED*/
static int
{
int bytes;
const char *fcn = "devfsadm_copy_file";
/* newfile already exists, so no need to continue */
return (DEVFSADM_SUCCESS);
}
return (DEVFSADM_SUCCESS);
}
return (DEVFSADM_SUCCESS);
}
return (DEVFSADM_SUCCESS);
}
return (DEVFSADM_SUCCESS);
}
return (DEVFSADM_SUCCESS);
}
}
return (DEVFSADM_SUCCESS);
}
/*
* Given a dev_t from the running kernel, return the new_dev_t
* by translating to the major number found on the installed
* target's root name_to_major file.
*/
static int
{
char *fcn = "translate_major: ";
&oldmajor) != 0) {
return (DEVFSADM_FAILURE);
}
/* non-clone case */
/* look up major number is target's name2major */
return (DEVFSADM_FAILURE);
}
}
return (DEVFSADM_SUCCESS);
} else {
/*
* The clone is a special case. Look at its minor
* number since it is the major number of the real driver.
*/
return (DEVFSADM_FAILURE);
}
&oldminor) != 0) {
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_FAILURE);
}
}
return (DEVFSADM_SUCCESS);
}
}
/*
*
* Find the major number for driver, searching the n2m_list that was
* built in load_n2m_table().
*/
static int
{
return (DEVFSADM_SUCCESS);
}
}
return (DEVFSADM_FAILURE);
}
/*
* Loads a name_to_major table into memory. Used only for suninstall's
* private -R option to devfsadm, to translate major numbers from the
* running to the installed target disk.
*/
static int
load_n2m_table(char *file)
{
int ln = 0;
return (DEVFSADM_FAILURE);
}
ln++;
/* cut off comments starting with '#' */
*cp = '\0';
/* ignore comment or blank lines */
continue;
/* sanity-check */
continue;
}
}
}
return (DEVFSADM_SUCCESS);
}
/*
* Called at devfsadm startup to read in the devlink.tab file. Creates
* a linked list of devlinktab_list structures which will be
* searched for every minor node.
*/
static void
read_devlinktab_file(void)
{
char *selector;
char *p_link;
char *s_link;
int i;
struct stat current_sb;
if (devlinktab_file == NULL) {
return;
}
/* if already cached, check to see if it is still valid */
return;
}
while (devlinktab_list != NULL) {
}
}
} else {
}
return;
}
} else if (i == sizeof (line-1)) {
continue;
}
/* cut off comments starting with '#' */
*cp = '\0';
/* ignore comment or blank lines */
continue;
/* break each entry into fields. s_link may be NULL */
&s_link) == DEVFSADM_FAILURE) {
continue;
} else {
"p_link='%s' s_link='%s'\n\n", selector,
}
entryp = (devlinktab_list_t *)
s_malloc(sizeof (devlinktab_list_t));
continue;
}
continue;
}
continue;
}
} else {
}
/* append to end of list */
}
}
}
/*
*
* For a single line entry in devlink.tab, split the line into fields
* selector, p_link, and an optionally s_link. If s_link field is not
* present, then return NULL in s_link (not NULL string).
*/
static int
char **s_link)
{
char *tab;
*tab = '\0';
} else {
return (DEVFSADM_FAILURE);
}
if (*p_link == '\0') {
return (DEVFSADM_FAILURE);
}
*tab = '\0';
return (DEVFSADM_FAILURE);
}
} else {
}
return (DEVFSADM_SUCCESS);
}
/*
* For a given devfs_spec field, for each element in the field, add it to
* a linked list of devfs_spec structures. Return the linked list in
* devfs_spec_list.
*/
static selector_list_t *
create_selector_list(char *selector)
{
char *key;
char *val;
/* parse_devfs_spec splits the next field into keyword & value */
&val) == DEVFSADM_FAILURE) {
break;
} else {
selector_list = (selector_list_t *)
s_malloc(sizeof (selector_list_t));
selector_list->arg = 0;
FALSE) {
selector_list->arg =
} else {
break;
}
MINOR_S_LEN) == 0) {
selector_list->arg = 0;
FALSE) {
selector_list->arg =
} else {
break;
}
} else {
break;
}
}
}
return (head_selector_list);
} else {
/* parse failed. Free any allocated structs */
return (NULL);
}
}
/*
* Takes a semicolon separated list of selector elements and breaks up
* into a keyword-value pair. semicolon and equal characters are
* replaced with NULL's. On success, selector is updated to point to the
* terminating NULL character terminating the keyword-value pair, and the
* function returns DEVFSADM_SUCCESS. If there is a syntax error,
* devfs_spec is not modified and function returns DEVFSADM_FAILURE.
*/
static int
{
char *equal;
char *semi_colon;
*equal = '\0';
} else {
return (DEVFSADM_FAILURE);
}
*semi_colon = '\0';
} else {
}
return (DEVFSADM_SUCCESS);
}
/*
* link is either the second or third field of devlink.tab. Parse link
* into a linked list of devlink structures and return ptr to list. Each
* list element is either a constant string, or one of the following
* escape sequences: \M, \A, \N, or \D. The first three escape sequences
* take a numerical argument.
*/
static link_list_t *
create_link_list(char *link)
{
int x = 0;
int counter_found = FALSE;
link_list_t **ptr;
char constant[MAX_DEVLINK_LINE];
char *error_str;
return (NULL);
}
/* a non-escaped string */
}
if (x != 0) {
constant[x] = '\0';
x = 0;
} else {
switch (*(++link)) {
case 'M':
break;
case 'A':
break;
case 'N':
if (counter_found == TRUE) {
error_str = "multiple counters "
"not permitted";
} else {
}
break;
case 'D':
break;
default:
error_str = "unrecognized escape sequence";
break;
}
if (*(link++) != 'D') {
error_str = "escape sequence must be "
"followed by a digit\n";
} else {
}
}
}
/* append link_list struct to end of list */
}
}
return (head);
} else {
return (NULL);
}
}
/*
* Called for each minor node devfsadm processes; for each minor node,
* look for matches in the devlinktab_list list which was created on
* startup read_devlinktab_file(). If there is a match, call build_links()
* to build a logical devlink and a possible extra devlink.
*/
static int
{
int link_built = FALSE;
char *nodetype;
char *dev_path;
if (devlinks_debug == TRUE) {
"");
}
}
/* don't process devlink.tab if devfsadm invoked with -c <class> */
if (num_classes > 0) {
return (FALSE);
}
link_built = TRUE;
}
}
return (link_built);
}
/*
* For a given devlink.tab devlinktab_list entry, see if the selector
* field matches this minor node. If it does, return DEVFSADM_SUCCESS,
* otherwise DEVFSADM_FAILURE.
*/
static int
{
char *addr;
char *minor_name;
char *node_type;
case NAME:
return (DEVFSADM_FAILURE);
}
break;
case TYPE:
return (DEVFSADM_FAILURE);
}
break;
case ADDR:
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_FAILURE);
}
} else {
return (DEVFSADM_FAILURE);
}
}
break;
case MINOR:
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_FAILURE);
}
} else {
return (DEVFSADM_FAILURE);
}
}
break;
default:
return (DEVFSADM_FAILURE);
}
}
return (DEVFSADM_SUCCESS);
}
/*
* For the given minor node and devlinktab_list entry from devlink.tab,
* build a logical dev link and a possible extra devlink.
* Return DEVFSADM_SUCCESS if link is created, otherwise DEVFSADM_FAILURE.
*/
static int
{
char *dev_path;
devfsadm_exit(1);
}
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_SUCCESS);
}
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_SUCCESS);
}
/*
* The counter rule for devlink.tab entries is implemented via
* devfsadm_enumerate_int_start(). One of the arguments to this function
* is a path, where each path component is treated as a regular expression.
* For devlink.tab entries, this path regular expression is derived from
* the devlink spec. get_anchored_re() accepts path regular expressions derived
* from devlink.tab entries and inserts the anchors '^' and '$' at the beginning
* and end respectively of each path component. This is done to prevent
* and incorrect links will be generated.
*/
static int
{
return (DEVFSADM_FAILURE);
}
*anchored_re++ = '^';
for (; *link != '\0'; ) {
if (*link == '/') {
while (*link == '/')
link++;
*anchored_re++ = '$';
*anchored_re++ = '/';
if (*link != '\0') {
*anchored_re++ = '^';
}
} else {
*anchored_re++ = *link++;
if (*link == '\0') {
*anchored_re++ = '$';
}
}
}
*anchored_re = '\0';
return (DEVFSADM_SUCCESS);
}
static int
{
int counter_offset = -1;
char *buff;
char start[10];
char *node_path;
link[0] = '\0';
switch (link_build->type) {
case NAME:
break;
case CONSTANT:
break;
case ADDR:
return (DEVFSADM_FAILURE);
}
break;
case MINOR:
return (DEVFSADM_FAILURE);
}
break;
case COUNTER:
break;
default:
return (DEVFSADM_FAILURE);
}
}
if (counter_offset != -1) {
/*
* copy anything appended after "([0-9]+)" into
* templink
*/
!= DEVFSADM_SUCCESS) {
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_FAILURE);
}
}
return (DEVFSADM_SUCCESS);
}
/*
* Compares "field" number of the comma separated list "full_name" with
* field_item. Returns DEVFSADM_SUCCESS for match,
* DEVFSADM_FAILURE for no match.
*/
static int
{
--field;
if (*(full_name++) == ',') {
field--;
}
}
if (field != 0) {
return (DEVFSADM_FAILURE);
}
(*full_name != ',')) {
if (*(full_name++) != *(field_item++)) {
return (DEVFSADM_FAILURE);
}
}
if (*field_item != '\0') {
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_SUCCESS);
return (DEVFSADM_FAILURE);
}
/*
* strcat() field # "field" of comma separated list "name" to "link".
* Field 0 is the entire name.
* Return DEVFSADM_SUCCESS or DEVFSADM_FAILURE.
*/
static int
{
return (DEVFSADM_FAILURE);
}
if (field == 0) {
return (DEVFSADM_SUCCESS);
}
while (*link != '\0') {
link++;
}
--field;
if (*(name++) == ',') {
--field;
}
}
if (field != 0) {
return (DEVFSADM_FAILURE);
}
}
*link = '\0';
return (DEVFSADM_SUCCESS);
}
static void
{
}
}
static void
{
}
}
}
/*
* Prints only if level matches one of the debug levels
* given on command line. INFO_MID is always printed.
*
* See devfsadm.h for a listing of globally defined levels and
* meanings. Modules should prefix the level with their
* module name to prevent collisions.
*/
/*PRINTFLIKE2*/
void
{
int x;
for (x = 0; x < num_verbose; x++) {
break;
}
break;
}
}
if (x == num_verbose) {
return;
}
}
} else {
}
} else {
} else {
}
}
}
} else {
}
}
/*
* print error messages to the terminal or to syslog
*/
/*PRINTFLIKE1*/
void
devfsadm_errprint(char *message, ...)
{
} else {
}
}
/*
* return noupdate state (-s)
*/
int
devfsadm_noupdate(void)
{
}
/*
* return current root update path (-r)
*/
const char *
devfsadm_root_path(void)
{
if (root_dir[0] == '\0') {
return ("/");
} else {
return ((const char *)root_dir);
}
}
void
{
int i;
for (i = 0; i < len; i++)
}
/*
* Return all devlinks corresponding to phys_path as an array of strings.
* The number of entries in the array is returned through lenp.
* devfsadm_free_dev_names() is used to free the returned array.
* NULL is returned on failure or when there are no matching devlinks.
*
* re is an extended regular expression in regex(5) format used to further
* match devlinks pointing to phys_path; it may be NULL to match all
*/
char **
{
struct devlink_cb_arg cb_arg;
int i;
*lenp = 0;
return (NULL);
goto out;
goto out;
}
}
out:
return (dev_names);
}
/* common exit function which ensures releasing locks */
static void
devfsadm_exit(int status)
{
if (DEVFSADM_DEBUG_ON) {
}
if (rcm_hdl) {
if (thr_self() != process_rcm_events_tid) {
(void) mutex_lock(&rcm_eventq_lock);
(void) cond_broadcast(&rcm_eventq_cv);
(void) mutex_unlock(&rcm_eventq_lock);
/* wait until process_rcm_events() thread exits */
}
(void) dlclose(librcm_hdl);
}
closelog();
}
}
/*
* set root_dir, devices_dir, dev_dir using optarg.
*/
static void
set_root_devices_dev_dir(char *dir)
{
}
/*
* Removes quotes.
*/
static char *
{
char *dst;
int len;
len -= 2;
} else {
}
return (dst);
}
/*
* For a given physical device pathname and spectype, return the
* ownership and permissions attributes by looking in data from
* /etc/minor_perm. If currently in installation mode, check for
* possible major number translations from the miniroot to the installed
* root's name_to_major table. Note that there can be multiple matches,
* but the last match takes effect. pts seems to rely on this
* implementation behavior.
*/
static void
{
char *node_name;
char *minor_name;
int is_clone;
int mp_drvname_is_clone;
/*
* Get the driver name based on the major number since the name
* in /devices may be generic. Could be running with more major
* numbers than are in /etc/name_to_major, so get it from the kernel
*/
/* return default values */
goto use_defaults;
}
/* component */
goto use_defaults;
}
if (minor_name != NULL) {
*minor_name++ = '\0';
} else {
}
if (minor_name == NULL) {
goto use_defaults;
}
*minor_name++ = '\0';
/*
* mp->mp_drvname = device name from minor_perm
* mp->mp_minorname = minor part of device name from
* minor_perm
* drvname = name of driver for this device
*/
/*
* If one of the following cases is true, then we try to change
* the permissions if a "shell global pattern match" of
* mp_>mp_minorname matches minor_name.
*
* 1. mp->mp_drvname matches driver.
*
* OR
*
* 2. mp->mp_drvname matches node_name and this
* name is an alias of the driver name
*
* OR
*
* 3. /devices entry is the clone device and either
* minor_perm entry is the clone device or matches
* the minor part of the clone device.
*/
if ((mp_drvname_matches_drvname == TRUE)||
((mp_drvname_matches_node_name == TRUE) &&
((mp_drvname_is_clone == TRUE) ||
(mp_drvname_matches_minor_name == TRUE)))) {
/*
* Check that the minor part of the
* device name from the minor_perm
* entry matches and if so, set the
* permissions.
*
* Under real devfs, clone minor name is changed
* to match the driver name, but minor_perm may
* not match. We reconcile it here.
*/
minor_name = aminor;
}
}
}
return;
}
/* not found in minor_perm, so just use default values */
}
/*
* Called by devfs_read_minor_perm() to report errors
* key is:
* line number: ignoring line number error
* size: alloc errors
*/
static void
{
switch (mp_err) {
case MP_FOPEN_ERR:
break;
case MP_FCLOSE_ERR:
break;
case MP_IGNORING_LINE_ERR:
break;
case MP_ALLOC_ERR:
break;
case MP_NVLIST_ERR:
break;
case MP_CANT_FIND_USER_ERR:
break;
case MP_CANT_FIND_GROUP_ERR:
break;
}
}
static void
read_minor_perm_file(void)
{
struct stat current_sb;
/* If already cached, check to see if it is still valid */
return;
}
minor_perms = NULL;
} else {
}
}
static void
load_minor_perm_file(void)
{
}
static char *
convert_to_re(char *dev)
{
char *p, *l, *out;
int i;
++p, i++) {
if ((*p == '*') && ((l != p) && (*l == '/'))) {
out[i++] = '.';
out[i] = '+';
} else {
out[i] = *p;
}
l = p;
}
out[i] = '\0';
return (p);
}
static void
read_logindevperm_file(void)
{
struct stat current_sb;
char line[MAX_LDEV_LINE];
/* Read logindevperm only when enabled */
if (login_dev_enable != TRUE)
return;
return;
}
while (login_dev_cache != NULL) {
while (list) {
}
}
} else {
}
return;
}
/* Not fatal to devfsadm */
return;
}
ln = 0;
ln++;
/* Remove comments */
*cp = '\0';
continue; /* Blank line */
continue; /* Malformed line */
}
/*
* permstr is string in octal format. Convert to int
*/
errno = 0;
continue;
}
continue;
}
while (dev) {
sizeof (struct login_dev));
/*
* the logical device name may contain '*' which
* we convert to a regular expression
*/
if (ldev->ldev_device &&
sizeof (ldev->ldev_device_regex));
}
}
if (drv) {
&lasts);
while (drv) {
"logindevperm driver=%s\n",
drv);
/*
* create a linked list of driver
* names
*/
list = (struct driver_list *)
sizeof (struct driver_list));
sizeof (list->driver_name));
&lasts);
}
}
}
}
}
/*
* Tokens are separated by ' ', '\t', ':', '=', '&', '|', ';', '\n', or '\0'
*
* Returns DEVFSADM_SUCCESS if token found, DEVFSADM_FAILURE otherwise.
*/
static int
{
char *cp;
char *cp1;
char *tokenp;
cp++; /* skip leading spaces */
}
cp++; /* point to next character */
}
/*
* If terminating character is a space or tab, look ahead to see if
* there's another terminator that's not a space or a tab.
* (This code handles trailing spaces.)
*/
;
}
}
if (*tchar == '\0') {
*tchar = '\n';
}
}
return (DEVFSADM_FAILURE);
}
return (DEVFSADM_SUCCESS);
}
/*
* read or reread the driver aliases file
*/
static void
read_driver_aliases_file(void)
{
char line[256];
char *cp;
char *p;
char t;
int ln = 0;
struct stat current_sb;
/* If already cached, check to see if it is still valid */
return;
}
while (driver_aliases != NULL) {
}
} else {
}
devfsadm_exit(1);
}
ln++;
/* cut off comments starting with '#' */
*cp = '\0';
/* ignore comment or blank lines */
continue;
continue;
}
if (t == '\n' || t == '\0') {
continue;
}
ap = (struct driver_alias *)
s_zalloc(sizeof (struct driver_alias));
continue;
}
if (*p == '"') {
p++;
}
}
if (driver_aliases == NULL) {
driver_aliases = ap;
} else {
}
}
}
}
/*
* return TRUE if alias_name is an alias for driver_name, otherwise
* return FALSE.
*/
static int
{
/*
* check for a match
*/
return (TRUE);
}
}
return (FALSE);
}
/*
* convenience functions
*/
static int
{
int rv;
goto retry;
}
return (rv);
}
static void *
{
void *rp;
devfsadm_exit(1);
}
return (rp);
}
/*
* convenience functions
*/
static void *
{
devfsadm_exit(1);
}
return (ptr);
}
static void *
{
void *rp;
devfsadm_exit(1);
}
return (rp);
}
char *
{
void *rp;
devfsadm_exit(1);
}
return (rp);
}
static void
{
goto retry;
}
}
static void
{
}
}
}
static void
{
goto retry;
}
}
}
static void
add_verbose_id(char *mid)
{
num_verbose++;
}
/*
* returns DEVFSADM_TRUE if contents is a minor node in /devices.
* If mn_root is not NULL, mn_root is set to:
* if contents is a /dev node, mn_root = contents
* OR
* if contents is a /devices node, mn_root set to the '/'
* following /devices.
*/
static int
{
char *ptr;
char device_prefix[100];
/* mn_root should point to the / following /devices */
}
return (DEVFSADM_TRUE);
}
/* mn_root should point to the / following /devices */
}
return (DEVFSADM_TRUE);
}
}
return (DEVFSADM_FALSE);
}
/*
* Lookup nvpair corresponding to the given name and type:
*
* The standard nvlist_lookup functions in libnvpair don't work as our
* nvlist is not allocated with NV_UNIQUE_NAME or NV_UNIQUE_NAME_TYPE.
*/
static nvpair_t *
{
return (nvp);
}
return (NULL);
}
/*ARGSUSED*/
static void
process_rcm_events(void *arg)
{
int instance;
int err;
int need_to_exit;
for (;;) {
(void) mutex_lock(&rcm_eventq_lock);
while (rcm_eventq_head == NULL &&
(void) mutex_unlock(&rcm_eventq_lock);
/*
* Private notification interface to RCM:
* Do not retry the RCM notification on an error since
* we do not know whether the failure occurred in
* librcm, rcm_daemon or rcm modules or scripts.
*/
!= RCM_SUCCESS) {
== NULL) ||
path = "unknown";
== NULL) ||
driver = "unknown";
== NULL) ||
instance = -1;
}
}
if (need_to_exit)
return;
}
}
/*
* Initialize rcm related handles and function pointers.
* Since RCM need not present in miniroot, we dlopen librcm.
*/
static int
rcm_init(void)
{
#define LIBRCM_PATH "/usr/lib/librcm.so"
int err;
/*
* don't log an error here, since librcm may not be present
* in miniroot.
*/
return (-1);
}
librcm_free_handle == NULL) {
goto out;
}
/* Initialize the rcm handle */
goto out;
}
/* create a thread to notify RCM of events */
NULL, 0, &process_rcm_events_tid)) != 0) {
goto out;
}
return (0);
out:
if (hdl)
(void) dlclose(librcm_hdl);
return (-1);
}
/*
* Build an nvlist using the minor data. Pack it and add the packed nvlist
* as a byte array to nv_list parameter.
* Return 0 on success, errno on failure.
*/
static int
{
char *minor_name, *minor_node_type;
int err;
return (err);
goto error;
goto error;
minor_node_type = "";
minor_node_type)) != 0)
goto error;
goto error;
if (buf)
return (err);
}
static void
{
struct rcm_eventq *ev;
(void) mutex_lock(&rcm_eventq_lock);
if (rcm_eventq_head == NULL)
else
(void) cond_broadcast(&rcm_eventq_cv);
(void) mutex_unlock(&rcm_eventq_lock);
}
/*
* Generate an nvlist using the information given in node and minor_name.
* If minor_name is NULL the nvlist will contain information on
* all minor nodes. Otherwise the nvlist will contain information
* only on the given minor_name. Notify RCM passing the nvlist.
*
* Return 0 upon successfully notifying RCM, errno on failure.
*/
static int
{
char *path, *driver_name;
char *node_name;
int err;
driver_name = "";
goto error;
}
goto error;
!= 0)
goto error;
goto error;
node_name = "";
goto error;
goto error;
while (minor != DI_MINOR_NIL) {
if ((minor_name == NULL) ||
goto error;
}
}
return (0);
if (path)
if (nvl)
return (err);
}
/*
* Add the specified property to nvl.
* Returns:
* 0 successfully added
* -1 an error occurred
* 1 could not add the property for reasons not due to errors.
*/
static int
{
char *name;
char *attr_name;
int n, len;
char *str;
char **strarray;
int rv = 0;
int i;
return (-1);
return (-1);
switch (di_prop_type(prop)) {
case DI_PROP_TYPE_BOOLEAN:
goto out;
break;
case DI_PROP_TYPE_INT:
goto out;
if (n <= (PROP_LEN_LIMIT / sizeof (int32_t))) {
n) != 0)
goto out;
} else
rv = 1;
break;
case DI_PROP_TYPE_INT64:
goto out;
if (n <= (PROP_LEN_LIMIT / sizeof (int64_t))) {
n) != 0)
goto out;
} else
rv = 1;
break;
case DI_PROP_TYPE_BYTE:
case DI_PROP_TYPE_UNKNOWN:
goto out;
if (n <= PROP_LEN_LIMIT) {
!= 0)
goto out;
} else
rv = 1;
break;
case DI_PROP_TYPE_STRING:
goto out;
goto out;
len = 0;
for (i = 0; i < n; i++) {
}
if (len <= PROP_LEN_LIMIT) {
n) != 0) {
goto out;
}
} else
rv = 1;
break;
default:
rv = 1;
break;
}
return (rv);
out:
return (-1);
}
static void
free_dev_names(struct devlink_cb_arg *x)
{
int i;
for (i = 0; i < x->count; i++) {
free(x->link_contents[i]);
}
}
/* callback function for di_devlink_cache_walk */
static int
{
const char *path;
const char *content;
goto out;
goto out;
}
x->count++;
if (x->count >= MAX_DEV_NAME_COUNT)
return (DI_WALK_TERMINATE);
return (DI_WALK_CONTINUE);
out:
x->rv = -1;
free_dev_names(x);
return (DI_WALK_TERMINATE);
}
/*
* Lookup dev name corresponding to the phys_path.
* phys_path is path to a node or minor node.
* Returns:
* 0 with *dev_name set to the dev name
* Lookup succeeded and dev_name found
* 0 with *dev_name set to NULL
* Lookup encountered no errors but dev name not found
* -1
* Lookup failed
*/
static int
{
struct devlink_cb_arg cb_arg;
return (-1);
return (-1);
}
return (0);
}
static char *
lookup_disk_dev_name(char *node_path)
{
struct devlink_cb_arg cb_arg;
int i;
char *p;
#define DISK_RAW_MINOR ",raw"
return (NULL);
sizeof (DEV_RDSK) - 1) == 0) {
break;
}
}
/* now try lookup based on a minor name ending with ",raw" */
DISK_RAW_MINOR) == 0) {
break;
}
}
}
return (NULL);
return (NULL);
}
/* if the name contains slice or partition number strip it */
if (isdigit(*p)) {
p--;
if (*p == 's' || *p == 'p')
*p = '\0';
}
return (dev_name);
}
static char *
{
char phys_path[MAXPATHLEN];
return (NULL);
/* dlpi style-2 only interface */
"/pseudo/clone@0:%s", driver_name);
return (NULL);
}
return (dev_name);
}
static char *
lookup_printer_dev_name(char *node_path)
{
struct devlink_cb_arg cb_arg;
int i;
#define DEV_PRINTERS "/dev/printers/"
return (NULL);
sizeof (DEV_PRINTERS) - 1) == 0) {
break;
}
}
/* fallback to the first name */
return (dev_name);
}
/*
* Build an nvlist containing all attributes for devfs events.
* Returns nvlist pointer on success, NULL on failure.
*/
static nvlist_t *
{
int err = 0;
int count;
char *prop_name;
int x;
int dev_name_lookup_err = 0;
goto out;
}
goto out;
goto out;
return (nvl);
goto out;
dev_name_lookup_err = 1;
goto out;
}
== NULL) {
dev_name_lookup_err = 1;
goto out;
}
dev_name_lookup_err = 1;
goto out;
}
}
if (dev_name) {
goto out;
}
goto out;
goto out;
/* add properties */
count = 0;
continue;
count++;
else if (x == -1) {
prop_name = "";
goto out;
}
}
}
return (nvl);
out:
if (nvl)
if (dev_name)
if (dev_name_lookup_err)
else
return (NULL);
}
static void
{
}
}
static void
{
if (node != DI_NODE_NIL)
else
NULL, -1);
if (nvl) {
}
}
/*
* is_blank() returns 1 (true) if a line specified is composed of
* whitespace characters only. otherwise, it returns 0 (false).
*
* Note. the argument (line) must be null-terminated.
*/
static int
{
return (0);
return (1);
}
/*
* Functions to deal with the no-further-processing hash
*/
static void
nfphash_create(void)
{
}
static int
nfphash_fcn(char *key)
{
int i;
for (i = 0; key[i] != '\0'; i++) {
}
return (sum % NFP_HASH_SZ);
}
static item_t *
nfphash_lookup(char *key)
{
int index;
return (ip);
}
return (NULL);
}
static void
nfphash_insert(char *key)
{
int index;
}
static void
nfphash_destroy(void)
{
int i;
for (i = 0; i < NFP_HASH_SZ; i++) {
/*LINTED*/
}
}
}
static int
{
int error = 0;
switch (subcmd) {
case MODDEVNAME_NSMAPS:
if (error) {
err_print("packing MODDEVNAME_NSMAPS failed\n");
break;
}
if (error) {
"MODDEVNAME_NSMAPS) failed - %s\n",
}
break;
case MODDEVNAME_LOOKUPDOOR:
if (error) {
"MODDEVNAME_LOOKUPDOOR) failed - %s\n",
}
break;
default:
break;
}
return (error);
}
static void
devname_setup_nsmaps(void)
{
int error = 0;
if (devname_first_call) {
devname_first_call = 0;
}
if (error) {
"%s\n", DEVNAME_MASTER_MAP);
} else {
/* pass down the existing map names to kernel */
}
}
static void
{
switch (cmd) {
case DEVFSADMD_NS_LOOKUP:
goto done;
}
if (error) {
goto done;
}
if (devname_debug_msg)
cmd);
break;
case DEVFSADMD_NS_READDIR:
"\n", cmd);
goto done;
}
if (error) {
goto done;
}
if (devname_debug_msg)
break;
default:
break;
}
done:
NULL, 0);
}
/* ARGSUSED */
static void
{
goto done;
}
goto done;
}
switch (cmd) {
case DEVFSADMD_NS_LOOKUP:
case DEVFSADMD_NS_READDIR:
goto done;
}
return;
case DEVFSADMD_RUN_ALL:
/*
* run "devfsadm"
*/
lock_dev();
if (!error) {
} else {
if (DEVFSADM_DEBUG_ON) {
"DEVFSADMD_RUN_ALL failed\n");
}
}
break;
default:
/* log an error here? */
break;
}
done:
NULL, 0);
}
/*
* Use of the dev filesystem's private readdir does not trigger
* the implicit device reconfiguration.
*
* Note: only useable with paths mounted on an instance of the
* dev filesystem.
*
* Does not return the . and .. entries.
* Empty directories are returned as an zero-length list.
* ENOENT is returned as a NULL list pointer.
*/
static char *
dev_readdir(char *path)
{
int rv;
char *pathlist;
char *p;
int len;
if (rv != 0) {
return (NULL);
}
for (;;) {
if (rv == 0) {
}
return (pathlist);
}
switch (errno) {
case EAGAIN:
break;
case ENOENT:
default:
return (NULL);
}
}
}