diff options
author | cth <none@none> | 2007-01-25 18:03:45 -0800 |
---|---|---|
committer | cth <none@none> | 2007-01-25 18:03:45 -0800 |
commit | ff2aee480f8fc985fe6a84c8d593c7a13c7a0481 (patch) | |
tree | 0d5ff4483df33f777f33382ef13fe769fddcfe09 | |
parent | e687df1a9fd8f5934440cce2177392751cffb904 (diff) | |
download | illumos-joyent-ff2aee480f8fc985fe6a84c8d593c7a13c7a0481.tar.gz |
6452574 we should improve management of the /dev/.devlink_db file
-rw-r--r-- | usr/src/cmd/devfsadm/devfsadm.c | 192 | ||||
-rw-r--r-- | usr/src/cmd/devfsadm/devfsadm_impl.h | 16 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/Makefile.com | 4 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/device_info.h | 18 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo_devlink.c | 178 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo_devlink.h | 13 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo_realpath.c | 242 |
7 files changed, 485 insertions, 178 deletions
diff --git a/usr/src/cmd/devfsadm/devfsadm.c b/usr/src/cmd/devfsadm/devfsadm.c index 1053dd8776..0cb644663b 100644 --- a/usr/src/cmd/devfsadm/devfsadm.c +++ b/usr/src/cmd/devfsadm/devfsadm.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -44,7 +44,6 @@ #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 *); @@ -106,11 +105,12 @@ static int inst_count = 0; static mutex_t count_lock; static cond_t cv; -/* variables for minor_fini calling system */ -static int minor_fini_timeout = MINOR_FINI_TIMEOUT_DEFAULT; +/* variables for minor_fini thread */ static mutex_t minor_fini_mutex; -static int minor_fini_thread_created = FALSE; -static int minor_fini_delay_restart = FALSE; +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 */ static sema_t dev_sema; @@ -345,20 +345,29 @@ main(int argc, char *argv[]) /* only one daemon can run at a time */ if ((pid = enter_daemon_lock()) == getpid()) { - thread_t thread; detachfromtty(); (void) cond_init(&cv, USYNC_THREAD, 0); (void) mutex_init(&count_lock, USYNC_THREAD, 0); if (thr_create(NULL, NULL, - (void *(*)(void *))instance_flush_thread, - NULL, - THR_DETACHED, - &thread) != 0) { + (void *(*)(void *))instance_flush_thread, + NULL, THR_DETACHED, NULL) != 0) { err_print(CANT_CREATE_THREAD, "daemon", strerror(errno)); devfsadm_exit(1); } + /* start the minor_fini_thread */ + (void) mutex_init(&minor_fini_mutex, USYNC_THREAD, 0); + (void) cond_init(&minor_fini_cv, USYNC_THREAD, 0); + if (thr_create(NULL, NULL, + (void *(*)(void *))minor_fini_thread, + NULL, THR_DETACHED, NULL)) { + err_print(CANT_CREATE_THREAD, "minor_fini", + strerror(errno)); + devfsadm_exit(1); + } + + /* * No need for rcm notifications when running * with an alternate root. So initialize rcm only @@ -1284,17 +1293,15 @@ sync_handler(void *cookie, char *ap, size_t asize, dci.dci_arg = NULL; lock_dev(); - devi_tree_walk(&dci, DINFOCPYALL, NULL); - - unlock_dev(CACHE_STATE); - dcp->dca_error = dci.dci_error; - startup_cache_sync_thread(); + if (dcp->dca_flags & DCA_DEVLINK_SYNC) + unlock_dev(SYNC_STATE); + else + unlock_dev(CACHE_STATE); -out: - (void) door_return((char *)dcp, sizeof (*dcp), NULL, 0); +out: (void) door_return((char *)dcp, sizeof (*dcp), NULL, 0); } static void @@ -1317,7 +1324,6 @@ lock_dev(void) invalidate_enumerate_cache(); rm_all_links_from_cache(); (void) di_devlink_close(&devlink_cache, DI_LINK_ERROR); - devlink_cache = NULL; } /* @@ -1358,28 +1364,54 @@ lock_dev(void) } } +/* + * 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) { + assert(flag == SYNC_STATE || flag == CACHE_STATE); + vprint(CHATTY_MID, "unlock_dev(): entered\n"); + /* If we are starting a SYNC_STATE, cancel minor_fini_thread SYNC */ + if (flag == SYNC_STATE) { + (void) mutex_lock(&minor_fini_mutex); + minor_fini_canceled = TRUE; + minor_fini_delayed = FALSE; + (void) mutex_unlock(&minor_fini_mutex); + } + if (build_dev == FALSE) return; assert(devlink_cache); - assert(flag == SYNC_STATE || flag == CACHE_STATE); - if (flag == SYNC_STATE) { unload_modules(); if (update_database) (void) di_devlink_update(devlink_cache); (void) di_devlink_close(&devlink_cache, 0); - devlink_cache = NULL; } exit_dev_lock(); + (void) mutex_lock(&minor_fini_mutex); + if (flag == SYNC_STATE) { + /* We did a SYNC_STATE, cancel minor_fini_thread SYNC */ + minor_fini_canceled = TRUE; + minor_fini_delayed = FALSE; + } else { + /* We did a CACHE_STATE, start delayed minor_fini_thread SYNC */ + minor_fini_canceled = FALSE; + minor_fini_delayed = TRUE; + (void) cond_signal(&minor_fini_cv); + } + (void) mutex_unlock(&minor_fini_mutex); + (void) sema_post(&dev_sema); } @@ -1566,7 +1598,6 @@ event_handler(sysevent_t *ev) } unlock_dev(CACHE_STATE); - startup_cache_sync_thread(); } else if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0 || strcmp(subclass, ESC_DEVFS_BRANCH_REMOVE) == 0) { @@ -1584,7 +1615,6 @@ event_handler(sysevent_t *ev) build_and_log_event(EC_DEV_BRANCH, dev_ev_subclass, path, DI_NODE_NIL); unlock_dev(CACHE_STATE); - startup_cache_sync_thread(); } else err_print(UNKNOWN_EVENT, subclass); @@ -2255,100 +2285,50 @@ load_module(char *mname, char *cdir) } /* - * Create a thread to call minor_fini after some delay - */ -static void -startup_cache_sync_thread() -{ - vprint(INITFINI_MID, "startup_cache_sync_thread\n"); - - (void) mutex_lock(&minor_fini_mutex); - - minor_fini_delay_restart = TRUE; - - if (minor_fini_thread_created == FALSE) { - - if (thr_create(NULL, NULL, - (void *(*)(void *))call_minor_fini_thread, NULL, - THR_DETACHED, NULL)) { - err_print(CANT_CREATE_THREAD, "minor_fini", - strerror(errno)); - - (void) mutex_unlock(&minor_fini_mutex); - - /* - * just sync state here instead of - * giving up - */ - lock_dev(); - unlock_dev(SYNC_STATE); - - return; - } - minor_fini_thread_created = TRUE; - } else { - vprint(INITFINI_MID, "restarting delay\n"); - } - - (void) mutex_unlock(&minor_fini_mutex); -} - -/* - * after not receiving an event for minor_fini_timeout secs, we need - * to call the minor_fini routines + * 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 -call_minor_fini_thread(void *arg) +minor_fini_thread(void *arg) { - int count = 0; + timestruc_t abstime; - (void) mutex_lock(&minor_fini_mutex); + vprint(INITFINI_MID, "minor_fini_thread starting\n"); - vprint(INITFINI_MID, "call_minor_fini_thread starting\n"); + (void) mutex_lock(&minor_fini_mutex); + for (;;) { + /* wait the gather period, or until signaled */ + abstime.tv_sec = time(NULL) + minor_fini_timeout; + abstime.tv_nsec = 0; + (void) cond_timedwait(&minor_fini_cv, + &minor_fini_mutex, &abstime); + + /* if minor_fini was canceled, go wait again */ + if (minor_fini_canceled == TRUE) + continue; - do { - minor_fini_delay_restart = FALSE; + /* if minor_fini was delayed, go wait again */ + if (minor_fini_delayed == TRUE) { + minor_fini_delayed = FALSE; + continue; + } + /* done with cancellations and delays, do the SYNC_STATE */ (void) mutex_unlock(&minor_fini_mutex); - (void) sleep(minor_fini_timeout); - (void) mutex_lock(&minor_fini_mutex); - - /* - * if minor_fini_delay_restart is still false then - * we can call minor fini routines. - * ensure that at least periodically minor_fini gets - * called satisfying link generators depending on fini - * being eventually called - */ - if ((count++ >= FORCE_CALL_MINOR_FINI) || - (minor_fini_delay_restart == FALSE)) { - vprint(INITFINI_MID, - "call_minor_fini starting (%d)\n", count); - (void) mutex_unlock(&minor_fini_mutex); - - lock_dev(); - unlock_dev(SYNC_STATE); - - vprint(INITFINI_MID, "call_minor_fini done\n"); - - /* - * hang around before exiting just in case - * minor_fini_delay_restart is set again - */ - (void) sleep(1); - count = 0; - - (void) mutex_lock(&minor_fini_mutex); - } - } while (minor_fini_delay_restart); + lock_dev(); + unlock_dev(SYNC_STATE); + vprint(INITFINI_MID, "minor_fini sync done\n"); - minor_fini_thread_created = FALSE; - (void) mutex_unlock(&minor_fini_mutex); - vprint(INITFINI_MID, "call_minor_fini_thread exiting\n"); + (void) mutex_lock(&minor_fini_mutex); + minor_fini_canceled = TRUE; + minor_fini_delayed = FALSE; + } } + /* * Attempt to initialize module, if a minor_init routine exists. Set * the active flag if the routine exists and succeeds. If it doesn't @@ -2940,7 +2920,7 @@ reset_node_permissions(di_node_t node, di_minor_t minor) * reset ownership and permissions to those specified in * minor_perm * else - * preserve exsisting/user-modified ownership and + * preserve existing/user-modified ownership and * permissions * * devfs indicates a new device by faking access time to be zero. diff --git a/usr/src/cmd/devfsadm/devfsadm_impl.h b/usr/src/cmd/devfsadm/devfsadm_impl.h index 5c72e8124f..5217649e7b 100644 --- a/usr/src/cmd/devfsadm/devfsadm_impl.h +++ b/usr/src/cmd/devfsadm/devfsadm_impl.h @@ -18,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -109,8 +109,7 @@ extern "C" { #define DEVFSADM_DEFAULT_FILE "/etc/default/devfsadm" -#define MINOR_FINI_TIMEOUT_DEFAULT 2 -#define FORCE_CALL_MINOR_FINI 10 +#define MINOR_FINI_TIMEOUT_DEFAULT 3 #define SYNCH_DOOR_PERMS (S_IRUSR | S_IWUSR) @@ -159,14 +158,6 @@ extern "C" { #define CREATE_NODE 0x01 #define READ_NODE 0x02 -#define DCA_CREATE_LINK 0x01 -#define DCA_FREE_LIST 0x02 -#define DCA_LOAD_DRV 0x04 -#define DCA_CHECK_TYPE 0x10 -#define DCA_NOTIFY_RCM 0x20 -#define DCA_FLUSH_PATHINST 0x40 -#define DCA_HOT_PLUG 0x80 - #define CACHE_STATE 0x0 #define SYNC_STATE 0x1 @@ -374,7 +365,6 @@ struct rcm_eventq { static int devfsadm_enumerate_int_start(char *devfs_path, int index, char **buf, devfsadm_enumerate_t rules[], int nrules, char *start); -static void startup_cache_sync_thread(void); static void set_root_devices_dev_dir(char *dir); static void pre_and_post_cleanup(int flags); static void hot_cleanup(char *, char *, char *, char *, int); @@ -438,7 +428,7 @@ static int create_link_common(char *devlink, char *contents, int *exists); static char *dequote(char *src); static void parse_args(int argc, char *argv[]); static void process_devinfo_tree(void); -static void call_minor_fini_thread(void *arg); +static void minor_fini_thread(void *arg); static void *s_realloc(void *ptr, const size_t size); static void read_devlinktab_file(void); static selector_list_t *create_selector_list(char *selector); diff --git a/usr/src/lib/libdevinfo/Makefile.com b/usr/src/lib/libdevinfo/Makefile.com index 42cf90b29d..c1db80004a 100644 --- a/usr/src/lib/libdevinfo/Makefile.com +++ b/usr/src/lib/libdevinfo/Makefile.com @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -30,7 +30,7 @@ VERS= .1 OBJECTS= devfsinfo.o devinfo.o devinfo_prop_decode.o devinfo_devlink.o \ devinfo_devperm.o devfsmap.o devinfo_devname.o \ - devinfo_finddev.o devinfo_dli.o devinfo_dim.o + devinfo_finddev.o devinfo_dli.o devinfo_dim.o devinfo_realpath.o include ../../Makefile.lib include ../../Makefile.rootfs diff --git a/usr/src/lib/libdevinfo/device_info.h b/usr/src/lib/libdevinfo/device_info.h index d58b235612..d53a5b86f8 100644 --- a/usr/src/lib/libdevinfo/device_info.h +++ b/usr/src/lib/libdevinfo/device_info.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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 * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -175,6 +174,17 @@ extern int devfs_load_minor_perm(struct mperm *, extern int devfs_add_minor_perm(char *, void (*)(minorperm_err_t, int)); extern int devfs_rm_minor_perm(char *, void (*)(minorperm_err_t, int)); +/* devfsadm dca_flags values: some are used by libdevinfo devlink_create() */ +#define DCA_CREATE_LINK 0x000000001 +#define DCA_FREE_LIST 0x000000002 +#define DCA_LOAD_DRV 0x000000004 +#define DCA_CHECK_TYPE 0x000000010 +#define DCA_NOTIFY_RCM 0x000000020 +#define DCA_FLUSH_PATHINST 0x000000040 +#define DCA_HOT_PLUG 0x000000080 +#define DCA_DEVLINK_SYNC 0x000000100 +#define DCA_DEVLINK_CACHE 0x000000200 + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libdevinfo/devinfo_devlink.c b/usr/src/lib/libdevinfo/devinfo_devlink.c index 33a25b31a5..a00733bce1 100644 --- a/usr/src/lib/libdevinfo/devinfo_devlink.c +++ b/usr/src/lib/libdevinfo/devinfo_devlink.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -27,9 +27,10 @@ #include "libdevinfo.h" #include "devinfo_devlink.h" +#include "device_info.h" -#undef DEBUG -#ifndef DEBUG +#undef DEBUG +#ifndef DEBUG #define NDEBUG 1 #else #undef NDEBUG @@ -141,6 +142,10 @@ retry: /* * Unlink the database, so that consumers don't get * out of date information as the database is being updated. + * + * NOTE: A typical snapshot consumer will wait until write lock + * obtained by handle_alloc above is dropped by handle_free, + * at which point the file will have been recreated. */ get_db_path(hdp, DB_FILE, path, sizeof (path)); (void) unlink(path); @@ -352,10 +357,10 @@ handle_alloc(const char *root_dir, uint_t flags) /* * Lock database if a read-write handle is being allocated. * Locks are needed to protect against multiple writers. - * Readers don't need locks. + * Readers lock/unlock in di_devlink_snapshot. */ if (HDL_RDWR(&proto)) { - if (enter_update_lock(&proto) != 0) { + if (enter_db_lock(&proto, root_dir) != 1) { return (NULL); } } @@ -394,7 +399,7 @@ error: /* Unlink DB file on error */ get_db_path(&proto, DB_FILE, path, sizeof (path)); (void) unlink(path); - exit_update_lock(&proto); + exit_db_lock(&proto); } return (NULL); } @@ -680,7 +685,7 @@ di_devlink_close(di_devlink_handle_t *pp, int flag) } /* - * Keep track of array assignments. There is atleast + * Keep track of array assignments. There is at least * 1 element (the "NIL" element) per type. */ for (i = 0; i < DB_TYPES; i++) { @@ -1028,7 +1033,7 @@ handle_free(struct di_devlink_handle **pp) cache_free(hdp); if (HDL_RDWR(hdp)) - exit_update_lock(hdp); + exit_db_lock(hdp); assert(hdp->lock_fd == -1); free(hdp->dev_dir); @@ -1214,7 +1219,7 @@ follow_link: return (NULL); } - if (realpath(link, buf) == NULL || !is_minor_node(buf, &minor_path)) { + if (s_realpath(link, buf) == NULL || !is_minor_node(buf, &minor_path)) { return (NULL); } return (lookup_minor(hdp, minor_path, NULL, TYPE_CACHE|CREATE_FLAG)); @@ -1904,7 +1909,8 @@ di_devlink_init_impl(const char *root, const char *name, uint_t flags) return (NULL); } - if (flags == DI_MAKE_LINK && (err = devlink_create(root, name))) { + if ((flags == DI_MAKE_LINK) && + (err = devlink_create(root, name, DCA_DEVLINK_CACHE))) { errno = err; return (NULL); } @@ -1930,16 +1936,43 @@ static di_devlink_handle_t devlink_snapshot(const char *root_dir) { struct di_devlink_handle *hdp; + int locked; + int err; + int retried = 0; if ((hdp = handle_alloc(root_dir, OPEN_RDONLY)) == NULL) { return (NULL); } /* - * If we cannot open the DB below, we will walk /dev + * Enter the DB lock. This will wait for update in progress and + * provide exclusion from new updates while we establish our mmap + * to the database contents. + */ +again: locked = enter_db_lock(hdp, root_dir); + if (locked == -1) { + handle_free(&hdp); + return (NULL); + } else if (locked == 1) { + /* Open the DB and exit the lock. */ + err = open_db(hdp, OPEN_RDONLY); + exit_db_lock(hdp); + } else + return (hdp); /* walk /dev in di_devlink_walk */ + + /* + * If we failed to open DB the most likely cause is that DB file did + * not exist. If we have not done a retry, signal devfsadmd to + * recreate the DB file and retry. + * + * If we fail to open the DB with retry, we will walk /dev * in di_devlink_walk. */ - (void) open_db(hdp, OPEN_RDONLY); + if (err && (retried == 0)) { + retried++; + (void) devlink_create(root_dir, NULL, DCA_DEVLINK_SYNC); + goto again; + } return (hdp); } @@ -2292,7 +2325,7 @@ visit_link( /*LINTED*/ assert(sizeof (tmp) >= PATH_MAX); #endif - if (realpath(vlp->abs_path, tmp) == NULL) + if (s_realpath(vlp->abs_path, tmp) == NULL) return (DI_WALK_CONTINUE); if (!is_minor_node(tmp, &minor_path)) @@ -2979,52 +3012,99 @@ hashfn(struct di_devlink_handle *hdp, const char *str) return (hval % CACHE(hdp)->hash_sz); } +/* + * enter_db_lock() + * + * If the handle is IS_RDWR then we lock as writer to "update" database, + * if IS_RDONLY then we lock as reader to "snapshot" database. The + * implementation uses advisory file locking. + * + * This function returns: + * == 1 success and grabbed the lock file, we can open the DB. + * == 0 success but did not lock the lock file, reader must walk + * the /dev directory. + * == -1 failure. + */ static int -enter_update_lock(struct di_devlink_handle *hdp) +enter_db_lock(struct di_devlink_handle *hdp, const char *root_dir) { - int i, fd, rv; - struct flock lock; - char lockfile[PATH_MAX]; + int fd; + struct flock lock; + char lockfile[PATH_MAX]; + int rv; + int writer = HDL_RDWR(hdp); + int did_sync = 0; + int eintrs; assert(hdp->lock_fd < 0); get_db_path(hdp, DB_LOCK, lockfile, sizeof (lockfile)); - /* - * Record locks are per-process. Protect against multiple threads. - */ + dprintf(DBG_LCK, "enter_db_lock: %s BEGIN\n", + writer ? "update" : "snapshot"); + + /* Record locks are per-process. Protect against multiple threads. */ (void) mutex_lock(&update_mutex); - if ((fd = open(lockfile, O_RDWR|O_CREAT, DB_LOCK_PERMS)) < 0) { - goto error; +again: if ((fd = open(lockfile, + (writer ? (O_RDWR|O_CREAT) : O_RDONLY), DB_LOCK_PERMS)) < 0) { + /* + * Typically the lock file and the database go hand in hand. + * If we find that the lock file does not exist (for some + * unknown reason) and we are the reader then we return + * success (after triggering devfsadm to create the file and + * a retry) so that we can still provide service via slow + * /dev walk. If we get a failure as a writer we want the + * error to manifests itself. + */ + if ((errno == ENOENT) && !writer) { + /* If reader, signal once to get files created */ + if (did_sync == 0) { + did_sync = 1; + dprintf(DBG_LCK, "enter_db_lock: %s OSYNC\n", + writer ? "update" : "snapshot"); + + /* signal to get files created */ + (void) devlink_create(root_dir, NULL, + DCA_DEVLINK_SYNC); + goto again; + } + dprintf(DBG_LCK, "enter_db_lock: %s OPENFAILD %s: " + "WALK\n", writer ? "update" : "snapshot", + strerror(errno)); + (void) mutex_unlock(&update_mutex); + return (0); /* success, but not locked */ + } else { + dprintf(DBG_LCK, "enter_db_lock: %s OPENFAILD %s\n", + writer ? "update" : "snapshot", strerror(errno)); + (void) mutex_unlock(&update_mutex); + return (-1); /* failed */ + } } - lock.l_type = F_WRLCK; + lock.l_type = writer ? F_WRLCK : F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; - i = 1; - while ((rv = fcntl(fd, F_SETLKW, &lock)) == -1 && errno == EINTR) { - if (i < MAX_LOCK_RETRY) { - i++; - } else { + /* Enter the lock. */ + for (eintrs = 0; eintrs < MAX_LOCK_RETRY; eintrs++) { + rv = fcntl(fd, F_SETLKW, &lock); + if ((rv != -1) || (errno != EINTR)) break; - } } - if (rv == 0) { + if (rv != -1) { hdp->lock_fd = fd; - return (0); - } else { - (void) close(fd); + dprintf(DBG_LCK, "enter_db_lock: %s LOCKED\n", + writer ? "update" : "snapshot"); + return (1); /* success, locked */ } -error: + (void) close(fd); + dprintf(DBG_ERR, "enter_db_lock: %s FAILED: %s: WALK\n", + writer ? "update" : "snapshot", strerror(errno)); (void) mutex_unlock(&update_mutex); - - dprintf(DBG_ERR, "lockfile(%s): lock failed: %s\n", lockfile, - strerror(errno)); return (-1); } @@ -3032,9 +3112,10 @@ error: * Close and re-open lock file every time so that it is recreated if deleted. */ static void -exit_update_lock(struct di_devlink_handle *hdp) +exit_db_lock(struct di_devlink_handle *hdp) { - struct flock unlock; + struct flock unlock; + int writer = HDL_RDWR(hdp); if (hdp->lock_fd < 0) { return; @@ -3045,9 +3126,11 @@ exit_update_lock(struct di_devlink_handle *hdp) unlock.l_start = 0; unlock.l_len = 0; + dprintf(DBG_LCK, "exit_db_lock : %s UNLOCKED\n", + writer ? "update" : "snapshot"); if (fcntl(hdp->lock_fd, F_SETLK, &unlock) == -1) { - dprintf(DBG_ERR, "update lockfile: unlock failed: %s\n", - strerror(errno)); + dprintf(DBG_ERR, "exit_db_lock : %s failed: %s\n", + writer ? "update" : "snapshot", strerror(errno)); } (void) close(hdp->lock_fd); @@ -3139,7 +3222,7 @@ bad: #define MAX_DAEMON_ATTEMPTS 2 static int -devlink_create(const char *root, const char *name) +devlink_create(const char *root, const char *name, int dca_devlink_flag) { int i; struct dca_off dca; @@ -3149,7 +3232,7 @@ devlink_create(const char *root, const char *name) /* * Convert name into arg for door_call */ - if (dca_init(name, &dca) != 0) + if (dca_init(name, &dca, dca_devlink_flag) != 0) return (EINVAL); /* @@ -3201,7 +3284,7 @@ devlink_create(const char *root, const char *name) * offset in the "name" member. */ static int -dca_init(const char *name, struct dca_off *dcp) +dca_init(const char *name, struct dca_off *dcp, int dca_flags) { char *cp; @@ -3209,7 +3292,7 @@ dca_init(const char *name, struct dca_off *dcp) dcp->dca_minor = 0; dcp->dca_driver = 0; dcp->dca_error = 0; - dcp->dca_flags = 0; + dcp->dca_flags = dca_flags; dcp->dca_name[0] = '\0'; name = name ? name : "/"; @@ -3649,7 +3732,8 @@ debug_print(debug_level_t msglevel, const char *fmt, va_list ap) if (_devlink_debug < msglevel) return; - + if ((_devlink_debug == DBG_LCK) && (msglevel != _devlink_debug)) + return; /* Print a distinctive label for error msgs */ if (msglevel == DBG_ERR) { @@ -3657,6 +3741,7 @@ debug_print(debug_level_t msglevel, const char *fmt, va_list ap) } (void) vfprintf(stderr, fmt, ap); + (void) fflush(stderr); } /* ARGSUSED */ @@ -3667,7 +3752,6 @@ dprintf(debug_level_t msglevel, const char *fmt, ...) va_list ap; assert(msglevel > 0); - if (!_devlink_debug) return; diff --git a/usr/src/lib/libdevinfo/devinfo_devlink.h b/usr/src/lib/libdevinfo/devinfo_devlink.h index d16bb7e52d..13ce175eed 100644 --- a/usr/src/lib/libdevinfo/devinfo_devlink.h +++ b/usr/src/lib/libdevinfo/devinfo_devlink.h @@ -18,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -183,6 +183,7 @@ typedef struct recurse { */ typedef enum { DBG_ERR = 1, + DBG_LCK, DBG_INFO, DBG_STEP, DBG_ALL @@ -210,7 +211,6 @@ typedef enum { #define MAX_UPDATE_INTERVAL 5 /* Max DB writes before synching with /dev */ #define MAX_LOCK_RETRY 5 /* Max attempts at locking the update lock */ - /* * Various flags private to the implementation */ @@ -399,8 +399,8 @@ static void *get_last_minor(struct di_devlink_handle *hdp, static void set_last_minor(struct di_devlink_handle *hdp, cache_minor_t *cmnp, int flags); -static int enter_update_lock(struct di_devlink_handle *hdp); -static void exit_update_lock(struct di_devlink_handle *hdp); +static int enter_db_lock(struct di_devlink_handle *hdp, const char *root_dir); +static void exit_db_lock(struct di_devlink_handle *hdp); static char *minor_colon(const char *path); static const char *rel_path(struct di_devlink_handle *hdp, const char *path); @@ -414,14 +414,15 @@ static void delete_unused_minor(di_devlink_handle_t hdp, cache_minor_t *cmnp); static int synchronize_db(di_devlink_handle_t hdp); static void dprintf(debug_level_t msglevel, const char *fmt, ...); static di_devlink_handle_t devlink_snapshot(const char *root_dir); -static int devlink_create(const char *root, const char *name); -static int dca_init(const char *name, struct dca_off *dcp); +static int devlink_create(const char *root, const char *name, int dca_flags); +static int dca_init(const char *name, struct dca_off *dcp, int dca_flags); static void exec_cmd(const char *root, struct dca_off *dcp); static int do_exec(const char *path, char *const argv[]); static int start_daemon(const char *root); static void daemon_call(const char *root, struct dca_off *dcp); int is_minor_node(const char *contents, const char **mn_root); +char *s_realpath(const char *path, char *resolved_path); #ifdef __cplusplus } diff --git a/usr/src/lib/libdevinfo/devinfo_realpath.c b/usr/src/lib/libdevinfo/devinfo_realpath.c new file mode 100644 index 0000000000..cce83026b1 --- /dev/null +++ b/usr/src/lib/libdevinfo/devinfo_realpath.c @@ -0,0 +1,242 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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" + +/* + * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdlib/realpath.c + * $OpenBSD: realpath.c,v 1.13 2005/08/08 08:05:37 espie Exp $ + */ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <limits.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> + +/* + * char *s_realpath(const char *path, char resolved_path[MAXPATHLEN]); + * + * Find the real name of path, by removing all ".", ".." and symlink + * components. Returns (resolved) on success, or (NULL) on failure, + * in which case the path which caused trouble is left in (resolved). + * + * DEVINFO: For libdevinfo we have added code to special case symlinks into + * /devices - the path below that point is known to not point to any + * additional symlinks. This knowledge allows us to avoid causing attach. + */ +char * +s_realpath(const char *path, char *resolved) +{ + struct stat sb; + char *p, *q, *s; + size_t left_len, resolved_len; + unsigned symlinks; + int serrno, slen; + char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; + + serrno = errno; + symlinks = 0; + if (path[0] == '/') { + resolved[0] = '/'; + resolved[1] = '\0'; + if (path[1] == '\0') + return (resolved); + resolved_len = 1; + left_len = strlcpy(left, path + 1, sizeof (left)); + } else { + if (getcwd(resolved, PATH_MAX) == NULL) { + (void) strlcpy(resolved, ".", PATH_MAX); + return (NULL); + } + resolved_len = strlen(resolved); + left_len = strlcpy(left, path, sizeof (left)); + } + if (left_len >= sizeof (left) || resolved_len >= PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); + } + + /* + * Iterate over path components in `left'. + */ + while (left_len != 0) { + /* + * Extract the next path component and adjust `left' + * and its length. + */ + p = strchr(left, '/'); + s = p ? p : left + left_len; + if (s - left >= sizeof (next_token)) { + errno = ENAMETOOLONG; + return (NULL); + } + (void) memcpy(next_token, left, s - left); + next_token[s - left] = '\0'; + left_len -= s - left; + if (p != NULL) + (void) memmove(left, s + 1, left_len + 1); + if (resolved[resolved_len - 1] != '/') { + if (resolved_len + 1 >= PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); + } + resolved[resolved_len++] = '/'; + resolved[resolved_len] = '\0'; + } + if (next_token[0] == '\0') + continue; + else if (strcmp(next_token, ".") == 0) + continue; + else if (strcmp(next_token, "..") == 0) { + /* + * Strip the last path component except when we have + * single "/" + */ + if (resolved_len > 1) { + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/') + 1; + *q = '\0'; + resolved_len = q - resolved; + } + continue; + } + + /* + * Append the next path component and lstat() it. If + * lstat() fails we still can return successfully if + * there are no more path components left. + */ + resolved_len = strlcat(resolved, next_token, PATH_MAX); + if (resolved_len >= PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); + } + + /* + * DEVINFO: Check if link points into /devices and resolve + * without causing attach if that is the case - there are no + * further symlinks in /devices. + */ + if (strcmp(resolved, "/devices") == 0) { + resolved[resolved_len] = '/'; + resolved_len = strlcat(resolved, left, sizeof (left)); + left_len = 0; + continue; + } + + if (lstat(resolved, &sb) != 0) { + if (errno == ENOENT && p == NULL) { + errno = serrno; + return (resolved); + } + return (NULL); + } + + if (S_ISLNK(sb.st_mode)) { + if (symlinks++ > MAXSYMLINKS) { + errno = ELOOP; + return (NULL); + } + slen = readlink(resolved, symlink, + sizeof (symlink) - 1); + if (slen < 0) + return (NULL); + symlink[slen] = '\0'; + + if (symlink[0] == '/') { + resolved[1] = 0; + resolved_len = 1; + } else if (resolved_len > 1) { + /* Strip the last path component. */ + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/') + 1; + *q = '\0'; + resolved_len = q - resolved; + } + + /* + * If there are any path components left, then + * append them to symlink. The result is placed + * in `left'. + */ + if (p != NULL) { + if (symlink[slen - 1] != '/') { + if (slen + 1 >= sizeof (symlink)) { + errno = ENAMETOOLONG; + return (NULL); + } + symlink[slen] = '/'; + symlink[slen + 1] = 0; + } + left_len = strlcat(symlink, left, + sizeof (left)); + if (left_len >= sizeof (left)) { + errno = ENAMETOOLONG; + return (NULL); + } + } + left_len = strlcpy(left, symlink, sizeof (left)); + } + } + + /* + * Remove trailing slash except when the resolved pathname + * is a single "/". + */ + if (resolved_len > 1 && resolved[resolved_len - 1] == '/') + resolved[resolved_len - 1] = '\0'; + return (resolved); +} |