/*
* 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
*/
/*
*/
#include "libdevinfo.h"
#include "devinfo_devlink.h"
#include "device_info.h"
#include <syslog.h>
#include <ucred.h>
#include <priv.h>
#ifndef DEBUG
#else
#endif
#include <assert.h>
sizeof (struct db_node),
sizeof (struct db_minor),
sizeof (struct db_link),
sizeof (char)
};
/*
* List of directories/files skipped while physically walking /dev
* Paths are relative to "<root>/dev/"
*/
static const char *skip_files[] = {
"stdout",
"stdin",
"stderr"
};
/*
*
* This file contains two sets of interfaces which operate on the reverse
* links database. One set (which includes di_devlink_open()/_close())
* allows link generators like devfsadm(1M) and ucblinks(1B) (writers) to
* populate the database with /devices -> /dev mappings. Another set
* of interfaces (which includes di_devlink_init()/_fini()) allows
* applications (readers) to lookup the database for /dev links corresponding
* to a given minor.
*
* Writers operate on a cached version of the database. The cache is created
* when di_devlink_open() is called. As links in /dev are created and removed,
* the cache is updated to keep it in synch with /dev. When the /dev updates
* are complete, the link generator calls di_devlink_close() which writes
* out the cache to the database.
*
* Applications which need to lookup the database, call di_devlink_init().
* di_devlink_init() checks the database file (if one exists). If the
* database is valid, it is mapped into the address space of the
* application. The database file consists of several segments. Each
* segment can be mapped in independently and is mapped on demand.
*
* Database Layout
*
* ---------------------
* | Magic # |
* | ----------------- |
* | Version | HEADER
* | ----------------- |
* | ... |
* ---------------------
* | |
* | | NODES
* | |
* | |
* ---------------------
* | |
* | | MINORS
* | |
* | |
* ---------------------
* | |
* | | LINKS
* | |
* | |
* ---------------------
* | |
* | | STRINGS
* | |
* | |
* ---------------------
*
* Readers can lookup /dev links for a specific minor or
* lookup all /dev links. In the latter case, the node
* and minor segments are not mapped in and the reader
* walks through every link in the link segment.
*
*/
{
int err;
int retried = 0;
/*
* Allocate a read-write handle but open the DB in readonly
* mode. We do writes only to a temporary copy of the database.
*/
return (NULL);
}
/*
* We don't want to unlink the db at this point - if we did we
* would be creating a window where consumers would take a slow
* code path (and those consumers might also trigger requests for
* db creation, which we are already in the process of doing).
* When we are done with our update, we use rename to install the
* latest version of the db file.
*/
/*
* The flags argument is reserved for future use.
*/
if (flags != 0) {
return (NULL);
}
if (cache_alloc(hdp) != 0) {
handle_free(&hdp);
return (NULL);
}
if (err) {
/*
* Failed to open DB.
* The most likely cause is that DB file did not exist.
* Call di_devlink_close() to recreate the DB file and
* retry di_devlink_open().
*/
if (retried == 0) {
(void) di_devlink_close(&hdp, 0);
retried = 1;
goto retry;
}
/*
* DB cannot be opened, just return the
* handle. We will recreate the DB later.
*/
return (hdp);
}
/* Read the database into the cache */
return (hdp);
}
static void
struct di_devlink_handle *hdp,
const char *fname,
char *buf,
{
#ifdef DEBUG
dir);
}
#endif
}
}
static int
{
long page_sz;
void *cp;
#ifdef DEBUG
return (-1);
}
#endif
return (-1);
}
/*
* Use O_TRUNC flag for write access, so that the subsequent ftruncate()
* call will zero-fill the entire file
*/
} else {
}
/*
* Avoid triggering /dev reconfigure for read when not present
*/
return (-1);
}
return (-1);
}
} else {
}
if (rv != -1)
return (-1);
}
if (cp == MAP_FAILED) {
return (-1);
}
} else {
}
if (rv) {
return (-1);
} else {
return (0);
}
}
/*
* A handle can be allocated for read-only or read-write access
*/
static struct di_devlink_handle *
{
int install = 0;
int isroot = 0;
dev_dir[0] = '\0';
db_dir[0] = '\0';
/*
* NULL and the empty string are equivalent to "/"
*/
if (root_dir[0] != '/') {
return (NULL);
}
#ifdef DEBUG
/*LINTED*/
#endif
return (NULL);
}
} else {
/*
* The dev dir is at /dev i.e. we are not doing a -r /altroot
*/
isroot = 1;
}
dev_dir[0] = 0;
db_dir[0] = 0;
} else {
}
/*
* The following code is for install. Readers and writers need
* Note that we test for readonly /etc by actually creating a
* file since statvfs is not a reliable method for determining
* readonly filesystems.
*/
install = 0;
int fd;
(void) mutex_lock(&temp_file_mutex);
DI_TEST_DB, getpid());
install = 1;
if (fd != -1) {
(void) unlink(di_test_db);
}
(void) mutex_unlock(&temp_file_mutex);
} else if (isroot) {
/*
* Readers can be non-privileged so we cannot test by creating
* and is owned by root.
*/
install = 1;
}
}
/*
* Check if we are in install. If we are, the database will be in
*/
if (install)
/*
* Lock database if a read-write handle is being allocated.
* Locks are needed to protect against multiple writers.
* Readers don't need locks.
*/
return (NULL);
}
}
goto error;
}
/*
* The handle hdp now contains a pointer to local storage
* in the dev_dir field (obtained from the proto handle).
* In the following line, a dynamically allocated version
* is substituted.
*/
goto error;
}
goto error;
}
return (hdp);
/* Unlink DB file on error */
}
return (NULL);
}
static int
{
}
return (-1);
}
return (0);
}
static int
{
int i;
char *cp;
return (1);
}
return (1);
}
for (i = 0; i < DB_TYPES; i++) {
/* There must be at least 1 element of each type */
return (1);
}
}
return (1);
}
return (1);
}
return (1);
}
return (1);
}
/*
* The last character in the string segment must be a NUL char.
*/
return (1);
}
return (0);
}
static int
{
char *path;
/*
* parent node should be NULL only for the root node
*/
return (-1);
}
/*
* Insert at head of list to recreate original order
*/
break;
}
break;
}
}
return (dnp ? -1 : 0);
}
static int
{
nidx);
return (-1);
}
break;
}
break;
}
}
return (dmp ? -1 : 0);
}
/*
* If the link is dangling the corresponding minor will be absent.
*/
static int
{
" index(%u)\n", nidx);
return (-1);
}
break;
}
}
return (dlp ? -1 : 0);
}
int
{
int i, rv;
return (-1);
}
/*
* The caller encountered some error in their processing.
* so handle isn't valid. Discard it and return success.
*/
if (flag == DI_LINK_ERROR) {
handle_free(&hdp);
return (0);
}
handle_free(&hdp);
return (-1);
}
/*
* Extract the DB path before the handle is freed.
*/
/*
* update database with actual contents of /dev
*/
/*
* For performance reasons, synchronization of the database
* with /dev is turned off by default. However, applications
* with appropriate permissions can request a "sync" by
* calling di_devlink_update().
*/
"di_devlink_close: synchronizing DB\n");
(void) synchronize_db(hdp);
}
/*
* Resolve dangling links AFTER synchronizing DB with /dev as the
* synchronization process may create dangling links.
*/
/*
* All changes to the cache are complete. Write out the cache
* to the database only if it is not empty.
*/
if (CACHE_EMPTY(hdp)) {
handle_free(&hdp);
return (0);
}
handle_free(&hdp);
return (-1);
}
/*
* Keep track of array assignments. There is at least
* 1 element (the "NIL" element) per type.
*/
for (i = 0; i < DB_TYPES; i++) {
next[i] = 1;
}
handle_free(&hdp);
return (-1);
}
handle_free(&hdp);
return (0);
}
/*
* Inits the database header.
*/
static int
{
int i;
for (i = 0; i < DB_TYPES; i++) {
}
return (0);
}
static int
struct di_devlink_handle *hdp,
{
/* parent node should only be NULL for root node */
break;
}
break;
}
break;
}
/* commit write for this node */
} else {
}
break;
}
}
return (cnp ? -1 : 0);
}
static int
struct di_devlink_handle *hdp,
{
return (-1);
}
break;
}
break;
}
/* Commit writes to this minor */
break;
}
}
return (cmnp ? -1 : 0);
}
static int
struct di_devlink_handle *hdp,
{
/* A NULL minor if and only if the links are dangling */
return (-1);
}
break;
}
break;
}
break;
}
/* Commit writes to this link */
} else {
}
}
return (clp ? -1 : 0);
}
static uint32_t
{
char *dstr;
return (DB_NIL);
}
return (DB_NIL);
}
return (DB_NIL);
}
return (idx);
}
static int
{
int i, rv = 0;
#ifdef DEBUG
for (i = 0; i < DB_TYPES; i++) {
}
#endif
return (0);
}
/* Unmap header after unmapping all other mapped segments */
for (i = 0; i < DB_TYPES; i++) {
DB_SEG_PROT(hdp, i) = 0;
}
}
return (rv ? -1 : 0);
}
static void
{
/*
* Don't bother removing links from hash table chains,
* as we are freeing the hash table itself.
*/
}
}
static void
{
return;
}
/*
* Frees the tree rooted at a node. Siblings of the subtree root
* have to be handled by the caller.
*/
static void
{
return;
}
}
}
}
static void
{
int hval;
return;
return;
return;
}
}
}
static cache_link_t *
{
int hval;
return (NULL);
break;
}
}
return (NULL);
}
return (clp);
}
static cache_minor_t *
{
const char *minor_path;
/*
* For primary link, content should point to a /devices node.
*/
return (NULL);
}
}
/*
* If secondary, the primary link is derived from the secondary
* link contents. Secondary link contents can have two formats:
*/
buf[0] = '\0';
}
} else {
goto follow_link;
}
/*
* Lookup the primary link if possible and find its minor.
*/
}
/* realpath() used only as a last resort because it is expensive */
#ifdef DEBUG
/*LINTED*/
#endif
/*
* A realpath attempt to lookup a dangling link can invoke implicit
* reconfig so verify there's an actual device behind the link first.
*/
return (NULL);
return (NULL);
if (buf[0] != '/') {
char *p;
return (NULL);
*p = 0;
return (NULL);
} else {
sizeof (abspath))
return (NULL);
}
if (!device_exists(abspath))
return (NULL);
}
return (NULL);
}
}
static void
{
} else {
}
}
}
/*
* The elements are assumed to be detached from the cache tree.
*/
static void
{
return;
}
static void
{
return;
}
}
static void
{
return;
}
/*
* Returns the ':' preceding the minor name
*/
static char *
{
char *cp;
return (NULL);
}
}
static void *
struct di_devlink_handle *hdp,
const char *minor_path,
const char *nodetype,
const int flags)
{
void *vp;
char *colon;
if (minor_path == NULL) {
return (NULL);
}
return (NULL);
}
*colon = '\0';
return (vp);
}
return (NULL);
}
*colon = ':';
if (LOOKUP_CACHE(flags)) {
break;
}
}
return (*pp);
} else {
char *cp;
break;
}
return (dmp);
}
}
static void *
{
return (NULL);
}
/*
* last_minor is used for nodes of TYPE_CACHE only.
*/
static void *
{
#ifdef DEBUG
if (getenv(SKIP_LAST_CACHE)) {
"node cache\n");
return (NULL);
}
#endif
return (NULL);
}
return (cnp);
}
return (cnp);
}
return (NULL);
}
static void *
struct di_devlink_handle *hdp,
const char *devfs_path,
const char *minor_name,
int flags)
{
#ifdef DEBUG
if (getenv(SKIP_LAST_CACHE)) {
"minor cache\n");
return (NULL);
}
#endif
return (NULL);
}
return (cmnp);
}
return (cmnp);
}
return (NULL);
}
static void
{
#ifdef DEBUG
if (getenv(SKIP_LAST_CACHE)) {
"minor cache\n");
return;
}
#endif
}
}
/*
* Returns 0 if normal return or -1 otherwise.
*/
static int
char *cur,
void *arg,
{
return (-1);
}
for (;;) {
break;
while (*cur == '/')
cur++;
if (*cur == '\0')
break;
/*
* There is a next component(s). Append a "/" separator for all
* but the first (root) component.
*/
}
*slash = '\0';
*slash = '/';
} else {
}
}
return (0);
}
static int
{
break;
}
}
} else {
char *cp;
break;
}
}
}
/*
* Terminate walk if node is not found for a path component.
*/
}
static void
{
/* detach minor from node */
break;
}
} else {
}
} else {
}
/* Move all remaining links to dangling list */
}
}
static void
{
return;
return;
/* Unlink node from tree */
break;
}
} else {
}
} else {
}
}
static int
{
return (-1);
}
return (0);
}
return (0);
}
int
{
return (-1);
}
}
static void
{
else
break;
}
} else {
}
}
static void
{
return;
return;
}
int
const char *link,
const char *content,
int flags)
{
}
static cache_link_t *
struct di_devlink_handle *hdp,
const char *link,
const char *content,
int flags)
{
return (NULL);
}
} else {
return (clp);
}
}
return (NULL);
}
return (NULL);
}
} else {
/*
* Defer resolving a secondary link to a minor until the
* database is closed. This ensures that the primary link
* (required for a successful resolve) has also been created.
*/
attr = A_SECONDARY;
}
}
/*
* Returns 0 on match or 1 otherwise.
*/
static int
{
return (1);
return (1);
return (0);
}
int
{
return (-1);
}
/*
* Reset the counter to schedule a synchronization with /dev on the next
* di_devlink_close().
*/
return (0);
}
static int
{
int hval;
/*
* corresponding cached version as valid(adding new links as needed).
* Then walk through the cache and remove all unmarked links.
*/
return (-1);
}
continue;
}
/*
* The link is stale, so remove it. Since the link
* will be destroyed, use a copy of the link path to
* invoke the remove function.
*/
}
}
return (0);
}
static di_devlink_handle_t
{
int err = 0;
return (NULL);
}
if ((flags == DI_MAKE_LINK) &&
return (NULL);
}
return (devlink_snapshot(root));
}
{
}
{
}
static di_devlink_handle_t
{
int err;
static int retried = 0;
return (NULL);
}
/*
* We don't need to lock. If a consumer wants the very latest db
* then he must perform a di_devlink_init with the DI_MAKE_LINK
* flag to force a sync with devfsadm first. Otherwise, the
* current database file is opened and mmaped on demand: the rename
* associated with a db update does not change the contents
* of files already opened.
*/
/*
* 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 after
* retry, we will walk /dev in di_devlink_walk.
*/
retried++;
goto again;
}
return (hdp);
}
int
{
return (-1);
}
/* Freeing the handle also closes the DB */
return (0);
}
int
const char *re,
const char *minor_path,
void *arg,
int (*devlink_callback)(di_devlink_t, void *))
{
int rv;
return (-1);
}
if (re) {
return (-1);
}
if (check_args(&linkd)) {
rv = -1;
goto out;
}
} else {
}
out:
if (re) {
}
return (rv ? -1 : 0);
}
static int
{
flags != DI_SECONDARY_LINK) {
return (0);
}
return (1);
}
/*
* Currently allowed flags are:
* DI_PRIMARY_LINK
* DI_SECONDARY_LINK
*/
static int
{
return (-1);
return (-1);
}
/*
* Minor path can be NULL. In that case, all links will be
* selected.
*/
if (linkp->minor_path) {
return (-1);
}
}
return (0);
}
/*
* Walk all links in database if no minor path is specified.
*/
static int
{
} else {
}
}
static int
{
return (-1);
}
sz = MIN_HASH_SIZE;
return (-1);
}
}
static int
{
return (-1);
}
return (-1);
}
if (linkp->minor_path)
else
}
/* ARGSUSED */
static int
{
int flags;
return (DI_WALK_CONTINUE);
}
} else {
}
/*
* Store only the part after <root-dir>/dev/
*/
}
return (DI_WALK_CONTINUE);
}
static int
{
/* Skip the "NIL" (index == 0) link. */
/*
* Declare this local to the block with zero
* initializer so that it gets rezeroed
* for each iteration.
*/
continue;
break;
}
}
}
static int
{
/*
* If a minor matching the path exists, walk that minor's devlinks list.
* Then walk the dangling devlinks list. Non-matching devlinks will be
* filtered out in visit_link.
*/
for (;;) {
goto out;
}
break;
} else {
}
}
out:
}
static int
struct di_devlink_handle *hdp,
struct di_devlink *vlp)
{
/*
* It is legal for the link's content and type to be unknown.
* but one of absolute or relative path must be set.
*/
return (DI_WALK_CONTINUE);
}
return (DI_WALK_CONTINUE);
}
return (DI_WALK_CONTINUE);
}
}
return (DI_WALK_CONTINUE);
}
}
} else {
}
}
/*
* Filter based on minor path
*/
if (linkp->minor_path) {
/*
* derive minor path
*/
#ifdef DEBUG
/*LINTED*/
#endif
return (DI_WALK_CONTINUE);
return (DI_WALK_CONTINUE);
} else if (minor_path == NULL) {
return (DI_WALK_CONTINUE);
}
return (DI_WALK_CONTINUE);
}
/*
* Filter based on link type
*/
return (DI_WALK_CONTINUE);
}
return (DI_WALK_CONTINUE);
}
}
static int
{
return (0);
}
return (1);
}
const char *
{
if (!devlink_valid(devlink)) {
return (NULL);
}
}
const char *
{
if (!devlink_valid(devlink)) {
return (NULL);
}
}
int
{
if (!devlink_valid(devlink)) {
return (-1);
}
}
{
if (!devlink_valid(devlink)) {
return (NULL);
}
return (NULL);
}
if (!devlink_valid(duplink)) {
(void) di_devlink_free(duplink);
return (NULL);
}
return (duplink);
}
int
{
return (-1);
}
return (0);
}
/*
* Obtain path relative to dev_dir
*/
static const char *
{
return (NULL);
return (NULL);
}
static int
{
int ret = 0;
return (ret);
}
static int
const char *dir,
struct di_devlink_handle *hdp,
int *retp)
{
const char *rel;
char *d_name;
return (DI_WALK_CONTINUE);
/*
* Skip directories we are not interested in.
*/
for (i = 0; i < N_SKIP_DIRS; i++) {
dir);
return (DI_WALK_CONTINUE);
}
}
return (DI_WALK_CONTINUE);
for (;;) {
break;
break;
/*
* Skip files we are not interested in.
*/
for (i = 0; i < N_SKIP_FILES; i++) {
"do_recurse: skipping %s\n", cur);
goto next_entry;
}
}
} else {
"do_recurse: Skipping entry: %s\n", cur);
}
} else {
}
*cp = '\0';
if (rv != DI_WALK_CONTINUE)
break;
}
return (rv);
}
static int
{
switch (attr & A_LINK_TYPES) {
case A_PRIMARY:
case A_SECONDARY:
return (1);
default:
attr);
return (0);
}
}
static int
{
switch (attr & A_LINK_TYPES) {
case A_PRIMARY:
return (DI_PRIMARY_LINK);
case A_SECONDARY:
return (DI_SECONDARY_LINK);
default:
attr);
return (0);
}
}
/* Allocate new node and link it in */
static cache_node_t *
struct di_devlink_handle *hdp,
const char *path,
int insert)
{
return (NULL);
}
return (NULL);
}
return (NULL);
}
} else if (insert == INSERT_HEAD) {
} else {
;
}
return (cnp);
}
/*
* Allocate a new minor and link it in either at the tail or head
* of the minor list depending on the value of "prev".
*/
static cache_minor_t *
struct di_devlink_handle *hdp,
const char *name,
const char *nodetype,
{
return (NULL);
}
/*
* Some pseudo drivers don't specify nodetype. Assume pseudo if
* nodetype is not specified.
*/
return (NULL);
}
return (NULL);
}
/* Add to node's minor list */
} else {
}
return (cmnp);
}
static cache_link_t *
struct di_devlink_handle *hdp,
const char *path,
const char *content,
{
return (NULL);
}
return (NULL);
}
return (NULL);
}
/* Add to minor's link list */
} else {
}
return (clp);
}
static void
{
}
static struct db_node *
{
}
static struct db_node *
{
}
static struct db_minor *
{
}
static struct db_minor *
{
}
static struct db_link *
{
}
static struct db_link *
{
}
static char *
{
}
static char *
{
}
/*
* Returns the element corresponding to idx. If the portion of file involved
* is not yet mapped, does an mmap() as well. Existing mappings are not changed.
*/
static void *
struct di_devlink_handle *hdp,
int prot,
{
int s;
return (NULL);
}
return (NULL);
}
/*
* If the seg is already mapped in, use it if the access type is
* valid.
*/
"seg[%d]: idx=%u, seg_prot=%d, access=%d\n",
return (NULL);
}
}
/*
* Segment is not mapped. Mmap() the segment.
*/
for (s = 0; s < seg; s++) {
}
if (addr == MAP_FAILED) {
off);
return (NULL);
}
}
/*
* Computes the size of a segment rounded up to the nearest page boundary.
*/
static size_t
{
} else {
}
return (sz);
}
static size_t
{
int i;
/* Take "NIL" element into account */
for (i = 0; i < DB_TYPES; i++) {
count[i] = 1;
}
}
for (i = 0; i < DB_TYPES; i++) {
}
return (sz);
}
static void
{
return;
}
}
}
static void
{
return;
}
}
static void
{
return;
}
static void
{
return;
}
}
static uint_t
{
const char *cp;
return (0);
}
}
}
/*
* 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
{
int fd;
int rv;
static int did_sync = 0;
int eintrs;
/* Record locks are per-process. Protect against multiple threads. */
(void) mutex_lock(&update_mutex);
/*
* 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 reader, signal once to get files created */
if (did_sync == 0) {
did_sync = 1;
/* signal to get files created */
goto again;
}
(void) mutex_unlock(&update_mutex);
return (0); /* success, but not locked */
} else {
(void) mutex_unlock(&update_mutex);
return (-1); /* failed */
}
}
/* Enter the lock. */
break;
}
if (rv != -1) {
return (1); /* success, locked */
}
(void) mutex_unlock(&update_mutex);
return (-1);
}
/*
* Close and re-open lock file every time so that it is recreated if deleted.
*/
static void
{
return;
}
}
(void) mutex_unlock(&update_mutex);
}
/*
* returns 1 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.
*/
int
{
prefix = "../devices/";
/* mn_root should point to the / following /devices */
}
return (1);
}
prefix = "/devices/";
/* mn_root should point to the / following /devices/ */
}
return (1);
}
}
return (0);
}
static int
{
int rv;
goto bad;
goto bad;
}
return (0);
bad:
return (-1);
}
/*
* Synchronous link creation interface routines
* The scope of the operation is determined by the "name" arg.
* "name" can be NULL, a driver name or a devfs pathname (without /devices)
*
* "name" creates
* ====== =======
*
* NULL => All devlinks in system
* <driver> => devlinks for named driver
* /pci@1 => devlinks for subtree rooted at pci@1
*
* devlink_create() returns 0 on success or an errno value on failure
*/
/*
* Retry all but unrecoverable errors in case devfsadm
* has been or needs to be restarted. As EAGAIN indicates
* possible deadlock detection or resource exhaustion,
* retry that without timing out.
*/
static int
{
int retry;
int rv;
return (EPERM);
ucred_free(uc);
return (EPERM);
}
/* Convert name into arg for door_call */
ucred_free(uc);
return (EINVAL);
}
/* Attempt to call the daemon */
retry = 0;
for (;;) {
/*
* If the daemon isn't running and with full
* privileges, we are most likely in the install
* environment. We can run devfsadm directly.
*/
rv = 0;
goto out;
}
/*
* Not install so cannot do much but retry in
* the hope that the daemon restarts.
*/
} else {
goto out;
}
if (retry++ == MAX_DAEMON_ATTEMPTS)
goto out;
}
(void) sleep(DAEMON_BACKOFF_DELAY);
}
out:
ucred_free(uc);
return (rv);
}
/*
* The "name" member of "struct dca" contains data in the following order
* root'\0'minor'\0'driver'\0'
* The root component is always present at offset 0 in the "name" field.
* The driver and minor are optional. If present they have a non-zero
* offset in the "name" member.
*/
static int
{
char *cp;
dcp->dca_driver = 0;
/* Check if name is a driver name */
if (*name != '/') {
"/ %s", name);
return (0);
}
/* /devices prefix not allowed in devfs pathname */
return (-1);
*cp++ = '\0';
}
return (0);
}
/*
* Returns an indication whether the devfadm daemon is running,
* as determined by the existence of the sync door file.
* If the daemon is functioning, status of the door call
* is returned in dcp->dca_error.
*/
static int
{
int fd;
int rv;
/*
* If the door used to communicate with the devfsadm daemon
* doesn't exist or isn't a door, the daemon may not be
* available yet.
*/
return (0);
}
return (0);
}
/* Block signals until door call completes. */
(void) sigfillset(&nset);
(void) sigemptyset(&oset);
}
if (rv != 0)
return (1);
/*LINTED*/
/*
* The doors interface may return data in a different buffer
* If that happens, deallocate buffer via munmap()
*/
return (1);
}
static void
{
int i;
i = 0;
/* Load the specified driver if driver name provided */
if (dcp->dca_driver) {
argv[i++] = "-i";
} else {
argv[i++] = "-n";
}
argv[i++] = "-r";
}
}
static int
{
int i;
#ifdef DEBUG
}
#endif
return (-1);
}
if (cpid == 0) { /* child process */
int fd;
} else {
}
_exit(-1);
}
/* Parent process */
if (WIFEXITED(i)) {
if (WEXITSTATUS(i) == 0) {
"do_exec: child exited normally\n");
return (0);
} else
} else {
/*
* The child was interrupted by a signal
*/
}
} else {
}
return (-1);
}
static int
{
int i;
/*
* First search the links under the specified minor. On the
* 2nd pass, search the dangling list - secondary links may
* exist on this list since they are not resolved during the
* /dev walk.
*/
for (i = 0; i < 2; i++) {
!= DI_WALK_CONTINUE) {
goto out;
}
}
}
out:
/* If i < 2, we terminated the walk prematurely */
}
static void
{
int i;
return;
}
}
}
}
static void
{
} else {
}
}
static void
{
return;
}
break;
}
}
/*
* Private function
*
* Walk cached links corresponding to the given path.
*
* path path to a node or minor node.
*
* flags specifies the type of devlinks to be selected.
* If DI_PRIMARY_LINK is used, only primary links are selected.
* If DI_SECONDARY_LINK is specified, only secondary links
* are selected.
* If neither flag is specified, all devlinks are selected.
*
* re An extended regular expression in regex(5) format which
* selects the /dev links to be returned. The regular
* expression should use link pathnames relative to
* /dev. i.e. without the leading "/dev/" prefix.
* A NULL value matches all devlinks.
*/
int
const char *re,
const char *path,
void *arg,
int (*devlink_callback)(di_devlink_t, void *))
{
return (-1);
}
if (re) {
return (-1);
}
} else {
}
if (re)
return (0);
}
/*
* debug level is initialized to -1.
* On first call into this routine, debug level is set.
* If debug level is zero, debugging msgs are disabled.
*/
static void
{
char *cp;
int save;
/*
* We shouldn't be here if debug is disabled
*/
assert(_devlink_debug != 0);
/*
* Set debug level on first call into this routine
*/
if (_devlink_debug < 0) {
_devlink_debug = 0;
return;
}
errno = 0;
if (errno != 0 || _devlink_debug < 0) {
_devlink_debug = 0;
return;
}
if (!_devlink_debug)
return;
}
/* debug msgs are enabled */
assert(_devlink_debug > 0);
if (_devlink_debug < msglevel)
return;
return;
/* Print a distinctive label for error msgs */
}
}
/* ARGSUSED */
/* PRINTFLIKE2 */
void
{
if (!_devlink_debug)
return;
}