Exemple #1
0
/*
 * Get Device Id from an open file descriptor
 */
int
devid_get(int fd, ddi_devid_t *devidp)
{
	int		len = 0;
	dev_t		dev;
	struct stat	statb;
	ddi_devid_t	mydevid;

	if (fstat(fd, &statb) != 0)
		return (-1);

	/* If not char or block device, then error */
	if (!S_ISCHR(statb.st_mode) && !S_ISBLK(statb.st_mode))
		return (-1);
#ifndef __APPLE__
	/* Get the device id size */
	dev = statb.st_rdev;
	if (modctl(MODSIZEOF_DEVID, dev, &len) != 0)
		return (-1);

	/* Allocate space to return device id */
	if ((mydevid = (ddi_devid_t)malloc(len)) == NULL)
		return (-1);

	/* Get the device id */
	if (modctl(MODGETDEVID, dev, len, mydevid) != 0) {
		free(mydevid);
		return (-1);
	}

	/* Return the device id copy */
	*devidp = mydevid;
#endif
	return (0);
}
Exemple #2
0
int
main(int argc, char **argv)
{
	modctl_load_t cmdargs;
	prop_dictionary_t props;
	char *propsstr;
	int ch;
	int flags;

	flags = 0;
	props = prop_dictionary_create();

	while ((ch = getopt(argc, argv, "b:fi:s:")) != -1) {
		switch (ch) {
		case 'b':
			parse_param(props, optarg, parse_bool_param);
			break;

		case 'f':
			flags |= MODCTL_LOAD_FORCE;
			break;

		case 'i':
			parse_param(props, optarg, parse_int_param);
			break;

		case 's':
			parse_param(props, optarg, parse_string_param);
			break;

		default:
			usage();
			/* NOTREACHED */
		}
	}

	argc -= optind;
	argv += optind;
	if (argc != 1)
		usage();

	propsstr = prop_dictionary_externalize(props);
	if (propsstr == NULL)
		errx(EXIT_FAILURE, "Failed to process properties");

	cmdargs.ml_filename = argv[0];
	cmdargs.ml_flags = flags;
	cmdargs.ml_props = propsstr;
	cmdargs.ml_propslen = strlen(propsstr);

	if (modctl(MODCTL_LOAD, &cmdargs)) {
		err(EXIT_FAILURE, NULL);
	}

	free(propsstr);
	prop_object_release(props);

	exit(EXIT_SUCCESS);
}
Exemple #3
0
static int
loadprivs(const char *infile)
{
	char *line, *col;
	FILE *in;
	struct fileentry *fep;
	int res = 0;

	in = fopen(infile, "r");

	if (in == NULL)
		return (0);

	while ((fep = fgetline(in)) != NULL && fep->entry != NULL) {
		line = fep->entry;

		if (*line == '\0')
			continue;

		line[strlen(line)-1] = '\0';

		col = strchr(line, ':');

		if (col != NULL) {
			major_t maj;
			*col = '\0';

			if (modctl(MODGETMAJBIND, line, col - line + 1, &maj)
			    != 0)
				continue;

			line = col + 1;
		}

		if (modctl(MODALLOCPRIV, line) != 0) {
			(void) err_print("modctl(MODALLOCPRIV, %s): %s\n",
				line, strerror(errno));
			res = -1;
		}
	}
	return (res);
}
/*
 * Use of the dev filesystem's private readdir does (not trigger
 * the implicit device reconfiguration) to determine if a directory
 * is empty.
 *
 * 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 int
finddev_emptydir_devfs(const char *path)
{
    int	rv;
    int	empty;

    rv = modctl(MODDEVEMPTYDIR, path, strlen(path), &empty);
    if (rv == 0) {
        return (empty);
    }
    return (0);
}
RTDECL(int) RTKrnlModLoadedQueryInfoAll(PRTKRNLMODINFO pahKrnlModInfo, uint32_t cEntriesMax,
                                        uint32_t *pcEntries)
{
    AssertReturn(VALID_PTR(pahKrnlModInfo) || cEntriesMax == 0, VERR_INVALID_PARAMETER);

    uint32_t cKmodsLoaded = RTKrnlModLoadedGetCount();
    if (cEntriesMax < cKmodsLoaded)
    {
        if (*pcEntries)
            *pcEntries = cKmodsLoaded;
        return VERR_BUFFER_OVERFLOW;
    }

    int rc = VINF_SUCCESS;
    int iId = -1;
    unsigned idxKrnlModInfo = 0;
    struct modinfo ModInfo;

    ModInfo.mi_info   = MI_INFO_ALL | MI_INFO_CNT;
    ModInfo.mi_id     = iId;
    ModInfo.mi_nextid = iId;
    do
    {
        int rcSol = modctl(MODINFO, iId, &ModInfo);
        if (rcSol < 0)
        {
            rc = RTErrConvertFromErrno(errno);
            if (rc == VERR_INVALID_PARAMETER && idxKrnlModInfo > 0)
                rc = VINF_SUCCESS;
            break;
        }

        ModInfo.mi_name[MODMAXNAMELEN - 1] = '\0'; /* Paranoia. */
        rc = rtKrnlModSolInfoCreate(&ModInfo, &pahKrnlModInfo[idxKrnlModInfo]);
        if (RT_SUCCESS(rc))
            idxKrnlModInfo++;

        iId = ModInfo.mi_id;
    } while (iId != -1);

    if (RT_FAILURE(rc))
    {
        /* Rollback */
        while (idxKrnlModInfo-- > 0)
            RTKrnlModInfoRelease(pahKrnlModInfo[idxKrnlModInfo]);
    }
    else if (pcEntries)
        *pcEntries = idxKrnlModInfo;

    return rc;
}
Exemple #6
0
/*
 * Get the minor name
 */
int
devid_get_minor_name(int fd, char **minor_namep)
{
	int		len = 0;
	dev_t		dev;
	int		spectype;
	char		*myminor_name;
	struct stat	statb;

	if (fstat(fd, &statb) != 0)
		return (-1);

	/* If not a char or block device, then return an error */
	if (!S_ISCHR(statb.st_mode) && !S_ISBLK(statb.st_mode))
		return (-1);

	spectype = statb.st_mode & S_IFMT;
	dev = statb.st_rdev;
#ifndef __APPLE__
	/* Get the minor name size */
	if (modctl(MODSIZEOF_MINORNAME, dev, spectype, &len) != 0)
		return (-1);

	/* Allocate space for the minor name */
	if ((myminor_name = (char *)malloc(len)) == NULL)
		return (-1);

	/* Get the minor name */
	if (modctl(MODGETMINORNAME, dev, spectype, len, myminor_name) != 0) {
		free(myminor_name);
		return (-1);
	}

	/* return the minor name copy */
	*minor_namep = myminor_name;
#endif
	return (0);
}
/*
 * Return true if a device exists
 * If the path refers into the /dev filesystem, use a
 * private interface to query if the device exists but
 * without triggering an implicit reconfig if it does not.
 * Note: can only function properly with absolute pathnames
 * and only functions for persisted global /dev names, ie
 * those managed by devfsadm.  For paths other than
 * /dev, stat(2) is sufficient.
 */
int
device_exists(const char *devname)
{
    int	rv;
    struct stat st;

    if (GLOBAL_DEV_PATH(devname)) {
        rv = modctl(MODDEVEXISTS, devname, strlen(devname));
        return ((rv == 0) ? 1 : 0);
    }
    if (stat(devname, &st) == 0)
        return (1);
    return (0);
}
Exemple #8
0
/*ARGSUSED*/
static void *
attach_devices(void *arg)
{
	di_node_t root_node;

	sleep(60);	/* let booting finish first */

	if ((root_node = di_init("/", DINFOFORCE)) == DI_NODE_NIL) {
		logerror("Failed to attach devices.");
		return (NULL);
	}
	di_fini(root_node);

	/*
	 * Unload all the modules.
	 */
	(void) modctl(MODUNLOAD, 0);

	return (NULL);
}
RTDECL(uint32_t) RTKrnlModLoadedGetCount(void)
{
    uint32_t cKmodsLoaded = 0;
    int iId = -1;
    struct modinfo ModInfo;

    ModInfo.mi_info   = MI_INFO_ALL | MI_INFO_CNT;
    ModInfo.mi_id     = iId;
    ModInfo.mi_nextid = iId;
    do
    {
        int rcSol = modctl(MODINFO, iId, &ModInfo);
        if (rcSol < 0)
            break;

        cKmodsLoaded++;

        iId = ModInfo.mi_id;
    } while (iId != -1);

    return cKmodsLoaded;
}
RTDECL(int) RTKrnlModLoadedQueryInfo(const char *pszName, PRTKRNLMODINFO phKrnlModInfo)
{
    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
    AssertPtrReturn(phKrnlModInfo, VERR_INVALID_POINTER);

    int rc = VERR_NOT_FOUND;
    int iId = -1;
    struct modinfo ModInfo;

    ModInfo.mi_info   = MI_INFO_ALL | MI_INFO_CNT;
    ModInfo.mi_id     = iId;
    ModInfo.mi_nextid = iId;
    do
    {
        int rcSol = modctl(MODINFO, iId, &ModInfo);
        if (rcSol < 0)
        {
            rc = RTErrConvertFromErrno(errno);
            break;
        }

        if (ModInfo.mi_id != -1)
        {
            ModInfo.mi_name[MODMAXNAMELEN - 1] = '\0'; /* Paranoia. */
            if (!RTStrCmp(pszName, &ModInfo.mi_name[0]))
            {
                rc = rtKrnlModSolInfoCreate(&ModInfo, phKrnlModInfo);
                break;
            }
        }

        iId = ModInfo.mi_id;
    } while (iId != -1);

    return rc;
}
Exemple #11
0
static int
loadpolicy(const char *infile)
{
	char *line;
	int nalloc = 0, cnt = 0;
	char *mem = NULL;
	devplcysys_t *dp, *dflt = NULL;
	FILE *in;
	struct fileentry *fep;
	int res;

	char *maj;
	char *tok;
	char *min;

	in = fopen(infile, "r");

	if (in == NULL) {
		err_print(OPEN_FAILED, infile, strerror(errno));
		return (-1);
	}

	while ((fep = fgetline(in)) != NULL && fep->entry != NULL) {
		line = fep->entry;
		if (cnt >= nalloc) {
			nalloc += PLCY_CHUNK;
			mem = realloc(mem, nalloc * devplcysys_sz);
			if (mem == NULL) {
				err_print(MALLOC_FAILED,
					nalloc * devplcysys_sz);
				return (-1);
			}

			/* Readjust pointer to dflt after realloc */
			if (dflt != NULL)
				/* LINTED: alignment */
				dflt = (devplcysys_t *)mem;
		}
		maj = strtok(line, "\n\t ");

		if (maj == NULL)
			continue;

		/* LINTED: alignment */
		dp = (devplcysys_t *)(mem + devplcysys_sz * cnt);

		if (strcmp(maj, "*") == 0) {
			if (dflt != NULL) {
				err_print(DPLCY_ONE_DFLT, infile);
				return (-1);
			}
			(void) memset(dp, 0, devplcysys_sz);
			dp->dps_maj = DEVPOLICY_DFLT_MAJ;
			dflt = dp;
		} else {
			if (dflt == NULL) {
				err_print(DPLCY_FIRST, infile);
				return (-1);
			}

			(void) memcpy(dp, dflt, devplcysys_sz);

			min = strchr(maj, ':');

			if (min != NULL) {
				*min++ = '\0';
				if (strchr(min, ':') != NULL) {
					(void) fprintf(stderr,
					    "Too many ``:'' in entry\n");
					return (-1);
				}
			} else
				min = "*";

			/* Silently ignore unknown devices. */
			if (modctl(MODGETMAJBIND, maj, strlen(maj) + 1,
			    &dp->dps_maj) != 0)
				continue;

			if (*min == '(') {
				/* Numeric minor range */
				char type;

				if (parse_minor_range(min, &dp->dps_lomin,
				    &dp->dps_himin, &type) == -1) {
					err_print(INVALID_MINOR, min);
					return (-1);
				}
				dp->dps_isblock = type == 'b';
			} else {
				if (strlen(min) >= sizeof (dp->dps_minornm)) {
					err_print(MINOR_TOO_LONG, maj, min);
					return (-1);
				}
				(void) strcpy(dp->dps_minornm, min);
			}
		}

		while (tok = strtok(NULL, "\n\t ")) {
			if (parse_plcy_token(tok, dp)) {
				err_print(BAD_ENTRY, fep->startline,
					fep->orgentry);
				return (-1);
			}
		}
		cnt++;
	}
	if (fep == NULL) {
		if (feof(in))
			err_print(UNEXPECTED_EOF, infile);
		else
			err_print(NO_MEMORY);
		return (-1);
	}
	qsort(mem, cnt, devplcysys_sz, qcmp);

	if ((res = modctl(MODSETDEVPOLICY, cnt, devplcysys_sz, mem)) != 0)
		err_print("modctl(MODSETDEVPOLICY): %s\n", strerror(errno));

	return (res);
}
Exemple #12
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 int
finddev_readdir_devfs(const char *path, finddevhdl_t *handlep)
{
    struct finddevhdl	*handle;
    int			n;
    int			rv;
    int64_t			bufsiz;
    char			*pathlist;
    char			*p;
    int			len;

    *handlep = NULL;
    handle = calloc(1, sizeof (struct finddevhdl));
    if (handle == NULL)
        return (ENOMEM);

    handle->npaths = 0;
    handle->curpath = 0;
    handle->paths = NULL;

    rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz);
    if (rv != 0) {
        free(handle);
        return (rv);
    }

    for (;;) {
        assert(bufsiz != 0);
        if ((pathlist = malloc(bufsiz)) == NULL) {
            free(handle);
            return (ENOMEM);
        }

        rv = modctl(MODDEVREADDIR, path, strlen(path),
                    pathlist, &bufsiz);
        if (rv == 0) {
            for (n = 0, p = pathlist;
                    (len = strlen(p)) > 0; p += len+1) {
                n++;
            }
            handle->npaths = n;
            handle->paths = calloc(n, sizeof (char *));
            if (handle->paths == NULL) {
                free(handle);
                free(pathlist);
                return (ENOMEM);
            }
            for (n = 0, p = pathlist;
                    (len = strlen(p)) > 0; p += len+1, n++) {
                handle->paths[n] = strdup(p);
                if (handle->paths[n] == NULL) {
                    finddev_close((finddevhdl_t)handle);
                    free(pathlist);
                    return (ENOMEM);
                }
            }
            *handlep = (finddevhdl_t)handle;
            free(pathlist);
            return (0);
        }
        free(pathlist);
        switch (errno) {
        case EAGAIN:
            break;
        case ENOENT:
        default:
            free(handle);
            return (errno);
        }
    }
    /*NOTREACHED*/
}
Exemple #13
0
int
main(int argc, char *argv[])
{
	int opt;
	char *basedir = NULL, *driver_name = NULL;
	int server = 0, mod_unloaded = 0;
	int modid, found;
	char maj_num[MAX_STR_MAJOR + 1];
	int err;

	(void) setlocale(LC_ALL, "");
#if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
#endif
	(void) textdomain(TEXT_DOMAIN);

	/*  must be run by root */

	if (getuid() != 0) {
		(void) fprintf(stderr, gettext(ERR_NOT_ROOT));
		exit(1);
	}

	while ((opt = getopt(argc, argv, "b:")) != -1) {
		switch (opt) {
		case 'b' :
			server = 1;
			basedir = calloc(strlen(optarg) + 1, 1);
			if (basedir == NULL) {
				(void) fprintf(stderr, gettext(ERR_NO_MEM));
				exit(1);
			}
			(void) strcat(basedir, optarg);
			break;
		case '?' :
			usage();
			exit(1);
		}
	}

	if (argv[optind] != NULL) {
		driver_name = calloc(strlen(argv[optind]) + 1, 1);
		if (driver_name == NULL) {
			(void) fprintf(stderr, gettext(ERR_NO_MEM));
			exit(1);

		}
		(void) strcat(driver_name, argv[optind]);
		/*
		 * check for extra args
		 */
		if ((optind + 1) != argc) {
			usage();
			exit(1);
		}

	} else {
		usage();
		exit(1);
	}

	/* set up add_drv filenames */
	if ((build_filenames(basedir)) == ERROR) {
		exit(1);
	}

	/* must be only running version of add_drv/mod_drv/rem_drv */
	enter_lock();

	if ((check_perms_aliases(1, 1)) == ERROR)
		err_exit();

	if ((check_name_to_major(R_OK | W_OK)) == ERROR)
		err_exit();

	/* look up the major number of the driver being removed. */
	if ((found = get_major_no(driver_name, name_to_major)) == ERROR) {
		(void) fprintf(stderr, gettext(ERR_MAX_MAJOR), name_to_major);
		err_exit();
	}
	if (found == UNIQUE) {
		(void) fprintf(stderr, gettext(ERR_NOT_INSTALLED),
		    driver_name);
		err_exit();
	}

	if (!server) {
		mod_unloaded = 1;

		/* get the module id for this driver */
		get_modid(driver_name, &modid);

		/* module is installed */
		if (modid != -1) {
			if (modctl(MODUNLOAD, modid) < 0) {
				perror(NULL);
				(void) fprintf(stderr, gettext(ERR_MODUN),
				    driver_name);
				mod_unloaded = 0;
			}
		}
		/* unload driver.conf file */
		if (modctl(MODUNLOADDRVCONF, (major_t)found) < 0) {
			perror(NULL);
			(void) fprintf(stderr,
			    gettext("cannot unload %s.conf\n"), driver_name);
		}
	}

	if (mod_unloaded && (modctl(MODREMMAJBIND, (major_t)found) < 0)) {
		perror(NULL);
		(void) fprintf(stderr, gettext(ERR_MODREMMAJ), found);
	}
	/*
	 * add driver to rem_name_to_major; if this fails, don`t
	 * delete from name_to_major
	 */
	(void) sprintf(maj_num, "%d", found);

	if (append_to_file(driver_name, maj_num,
	    rem_name_to_major, ' ', " ") == ERROR) {
		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
		    rem_name_to_major);
		err_exit();
	}

	/*
	 * If removing the driver from the running system, notify
	 * kernel dynamically to remove minor perm entries.
	 */
	if (basedir == NULL || (strcmp(basedir, "/") == 0)) {
		err = devfs_rm_minor_perm(driver_name, log_minorperm_error);
		if (err != 0) {
			(void) fprintf(stderr, gettext(ERR_UPDATE_PERM),
				driver_name, err);
		}
	}

	/*
	 * delete references to driver in add_drv/rem_drv database
	 */
	remove_entry(CLEAN_ALL, driver_name);

	/*
	 * Clean up any dangling devfs shadow nodes for this
	 * driver so that, in the event the driver is re-added
	 * to the system, newly created nodes won't incorrectly
	 * pick up these stale shadow node permissions.
	 */
	if (basedir == NULL || (strcmp(basedir, "/") == 0)) {
		err = modctl(MODREMDRVCLEANUP, driver_name, 0, NULL);
		if (err != 0) {
			(void) fprintf(stderr, gettext(ERR_REMDRV_CLEANUP),
				driver_name, err);
		}
	}

	exit_unlock();

	return (NOERR);
}
Exemple #14
0
int
main(int argc, char **argv)
{
	struct iovec iov;
	modstat_t *ms;
	size_t len;
	const char *name;
	char sbuf[32];
	int ch;

	name = NULL;

	while ((ch = getopt(argc, argv, "n:")) != -1) {
		switch (ch) {
		case 'n':
			name = optarg;
			break;
		default:
			usage();
			/* NOTREACHED */
		}
	}

	argc -= optind;
	argv += optind;
	if (argc != 0)
		usage();

	for (len = 4096;;) {
		iov.iov_base = malloc(len);
		iov.iov_len = len;
		if (modctl(MODCTL_STAT, &iov)) {
			err(EXIT_FAILURE, "modctl(MODCTL_STAT)");
		}
		if (len >= iov.iov_len) {
			break;
		}
		free(iov.iov_base);
		len = iov.iov_len;
	}

	printf("NAME\t\tCLASS\tSOURCE\tREFS\tSIZE\tREQUIRES\n");
	len = iov.iov_len / sizeof(modstat_t);
	for (ms = iov.iov_base; len != 0; ms++, len--) {
		if (name != NULL && strcmp(ms->ms_name, name) != 0) {
			continue;
		}
		if (ms->ms_required[0] == '\0') {
			ms->ms_required[0] = '-';
			ms->ms_required[1] = '\0';
		}
		if (ms->ms_size == 0) {
			sbuf[0] = '-';
			sbuf[1] = '\0';
		} else {
			snprintf(sbuf, sizeof(sbuf), "%u", ms->ms_size);
		}
		printf("%-16s%s\t%s\t%d\t%s\t%s\n",
		    ms->ms_name, classes[ms->ms_class], sources[ms->ms_source],
		    ms->ms_refcnt, sbuf, ms->ms_required);
	}

	exit(EXIT_SUCCESS);
}
Exemple #15
0
/*
 * Convert the specified devid/minor_name into a devid_nmlist_t array
 * with names that resolve into /devices or /dev depending on search_path.
 *
 * The man page indicates that:
 *
 *     This function traverses the file tree, starting at search_path.
 *
 * This is not true, we reverse engineer the paths relative to
 * the specified search path to avoid attaching all devices.
 */
int
devid_deviceid_to_nmlist(
	char		*search_path,
	ddi_devid_t	devid,
	char		*minor_name,
	devid_nmlist_t	**retlist)
{
	char			*cp;
	int			dev;
	char			*paths = NULL;
	char			*path;
	int			lens;
#ifndef __APPLE__
	di_devlink_handle_t	dlh = NULL;
#endif
	int			ret = -1;
#ifndef __APPLE__
	struct devlink_cbinfo	cbi;
#endif
	struct nmlist		*nlh = NULL;
	struct nmlist		*nl;
	devid_nmlist_t		*rl;
	int			nret;
	int			nagain = 0;
	int			err = 0;

	*retlist = NULL;

	/* verify valid search path starts with "/devices" or "/dev" */
	if ((strcmp(search_path, "/devices") == 0) ||
	    (strncmp(search_path, "/devices/", 9) == 0))
		dev = 0;
	else if ((strcmp(search_path, "/dev") == 0) ||
	    (strncmp(search_path, "/dev/", 5) == 0))
		dev = 1;
	else {
		errno = EINVAL;
		return (-1);
	}

#ifndef __APPLE__
	/* translate devid/minor_name to /devices paths */
again:	if (modctl(MODDEVID2PATHS, devid, minor_name, 0, &lens, NULL) != 0)
		goto out;
	if ((paths = (char *)malloc(lens)) == NULL)
		goto out;
	if (modctl(MODDEVID2PATHS, devid, minor_name, 0, &lens, paths) != 0) {
		if ((errno == EAGAIN) && (nagain++ < DEVICEID_NMLIST_NRETRY)) {
			free(paths);
			paths = NULL;
			goto again;
		}
		goto out;
	}

	/*
	 * initialize for /devices path to /dev path translation. To reduce
	 * overhead we reuse the last snapshot if DEVICEID_NMLIST_SLINK is set.
	 */
	if (dev) {
		dlh = devid_deviceid_to_nmlist_dlh;
		if (dlh &&
		    !(devid_deviceid_to_nmlist_flg & DEVICEID_NMLIST_SLINK)) {
			(void) di_devlink_fini(&dlh);
			dlh = devid_deviceid_to_nmlist_dlh = NULL;
		}
		if ((dlh == NULL) &&
		    ((dlh = di_devlink_init(NULL, 0)) == NULL))
				goto out;
	}

	/*
	 * iterate over all the devtspectype resolutions of the devid and
	 * convert them into the appropriate path form and add items to return
	 * to the nmlist list;
	 */
	for (path = paths; *path; path += strlen(path) + 1) {
		if (dev) {
			/* add /dev entries */
			cbi.cbi_nlhp = &nlh;
			cbi.cbi_search_path = search_path;
			cbi.cbi_error = 0;

			(void) di_devlink_walk(dlh, NULL, path,
			    devid_deviceid_to_nmlist_link,
			    (void *)&cbi, devlink_callback);
			if (cbi.cbi_error)
				goto out;
		} else {
			/* add /devices entry */
			cp = malloc(strlen("/devices") + strlen(path) + 1);
			(void) strcpy(cp, "/devices");
			(void) strcat(cp, path);
			if (strncmp(cp, search_path,
			    strlen(search_path)) == 0) {
				if (nmlist_add(&nlh, cp) == NULL) {
					free(cp);
					goto out;
				}
			}
			free(cp);
		}
	}

	/* convert from nmlist to retlist array */
	for (nl = nlh, nret = 0; nl; nl = nl->nl_next)
		nret++;
	if (nret == 0) {
		err = ENODEV;
		goto out;
	}
	if ((*retlist = calloc(nret + 1, sizeof (devid_nmlist_t))) == NULL) {
		err = ENOMEM;
		goto out;
	}
	for (nl = nlh, rl = *retlist; nl; nl = nl->nl_next, rl++) {
		rl->devname = nl->nl_devname;
		rl->dev = nl->nl_dev;
	}
	rl->devname = NULL;
	rl->dev = NODEV;

	ret = 0;

out:
	while ((nl = nlh) != NULL) {	/* free the nmlist */
		nlh = nl->nl_next;
		free(nl);
	}
	if (paths)
		free(paths);
	if (dlh) {
		if ((ret == 0) &&
		    (devid_deviceid_to_nmlist_flg & DEVICEID_NMLIST_SLINK))
			devid_deviceid_to_nmlist_dlh = dlh;
		else
			(void) di_devlink_fini(&dlh);
	}
	if (ret && *retlist)
		free(*retlist);
	if (ret && err != 0)
		errno = err;
#endif
	return (ret);
}