xref: /onnv-gate/usr/src/lib/libdevinfo/devinfo_finddev.c (revision 6065:b05c5c670963)
12621Sllai1 /*
22621Sllai1  * CDDL HEADER START
32621Sllai1  *
42621Sllai1  * The contents of this file are subject to the terms of the
52621Sllai1  * Common Development and Distribution License (the "License").
62621Sllai1  * You may not use this file except in compliance with the License.
72621Sllai1  *
82621Sllai1  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92621Sllai1  * or http://www.opensolaris.org/os/licensing.
102621Sllai1  * See the License for the specific language governing permissions
112621Sllai1  * and limitations under the License.
122621Sllai1  *
132621Sllai1  * When distributing Covered Code, include this CDDL HEADER in each
142621Sllai1  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152621Sllai1  * If applicable, add the following below this CDDL HEADER, with the
162621Sllai1  * fields enclosed by brackets "[]" replaced with your own identifying
172621Sllai1  * information: Portions Copyright [yyyy] [name of copyright owner]
182621Sllai1  *
192621Sllai1  * CDDL HEADER END
202621Sllai1  */
212621Sllai1 /*
22*6065Scth  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
232621Sllai1  * Use is subject to license terms.
242621Sllai1  */
252621Sllai1 
262621Sllai1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
272621Sllai1 
282621Sllai1 #include <stdio.h>
292621Sllai1 #include <unistd.h>
302621Sllai1 #include <fcntl.h>
312621Sllai1 #include <string.h>
322621Sllai1 #include <thread.h>
332621Sllai1 #include <synch.h>
342621Sllai1 #include <limits.h>
352621Sllai1 #include <stdlib.h>
362621Sllai1 #include <string.h>
372621Sllai1 #include <strings.h>
382621Sllai1 #include <dirent.h>
392621Sllai1 #include <regex.h>
402621Sllai1 #include <errno.h>
412621Sllai1 #include <stdarg.h>
422621Sllai1 #include <libdevinfo.h>
433702Sjg #include <zone.h>
442621Sllai1 #include <sys/modctl.h>
452621Sllai1 #include <syslog.h>
463702Sjg #include <sys/stat.h>
472621Sllai1 #include <assert.h>
482621Sllai1 
492621Sllai1 
502621Sllai1 struct finddevhdl {
512621Sllai1 	int	npaths;
522621Sllai1 	int	curpath;
532621Sllai1 	char	**paths;
542621Sllai1 };
552621Sllai1 
562621Sllai1 
573747Sjg #define	GLOBAL_DEV_PATH(devpath)			\
583747Sjg 	((getzoneid() == GLOBAL_ZONEID) &&		\
593747Sjg 	    ((strcmp(devpath, "/dev") == 0) ||		\
603747Sjg 	    (strncmp(devpath, "/dev/", strlen("/dev/")) == 0)))
613747Sjg 
623702Sjg /*
633702Sjg  * Return true if a device exists
643702Sjg  * If the path refers into the /dev filesystem, use a
653702Sjg  * private interface to query if the device exists but
663702Sjg  * without triggering an implicit reconfig if it does not.
673702Sjg  * Note: can only function properly with absolute pathnames
683702Sjg  * and only functions for persisted global /dev names, ie
693702Sjg  * those managed by devfsadm.  For paths other than
703702Sjg  * /dev, stat(2) is sufficient.
713702Sjg  */
722621Sllai1 int
device_exists(const char * devname)732621Sllai1 device_exists(const char *devname)
742621Sllai1 {
752621Sllai1 	int	rv;
763702Sjg 	struct stat st;
772621Sllai1 
783747Sjg 	if (GLOBAL_DEV_PATH(devname)) {
793702Sjg 		rv = modctl(MODDEVEXISTS, devname, strlen(devname));
803702Sjg 		return ((rv == 0) ? 1 : 0);
813702Sjg 	}
823702Sjg 	if (stat(devname, &st) == 0)
833702Sjg 		return (1);
843702Sjg 	return (0);
852621Sllai1 }
862621Sllai1 
873702Sjg 
883702Sjg /*
893702Sjg  * Use the standard library readdir to read the contents of
903702Sjg  * directories on alternate root mounted filesystems.
913702Sjg  * Return results as per dev_readdir_devfs().
923702Sjg  *
933702Sjg  * The directory is traversed twice.  First, to calculate
943702Sjg  * the size of the buffer required; second, to copy the
953702Sjg  * directory contents into the buffer.  If the directory
963702Sjg  * contents grow in between passes, which should almost
973702Sjg  * never happen, start over again.
983702Sjg  */
993702Sjg static int
finddev_readdir_alt(const char * path,finddevhdl_t * handlep)1003702Sjg finddev_readdir_alt(const char *path, finddevhdl_t *handlep)
1013702Sjg {
1023702Sjg 	struct finddevhdl *handle;
1033702Sjg 	DIR *dir;
1043702Sjg 	struct dirent *dp;
1053702Sjg 	size_t n;
1063702Sjg 
1073702Sjg 	*handlep = NULL;
1083702Sjg 	if ((dir = opendir(path)) == NULL)
1093702Sjg 		return (ENOENT);
1103702Sjg 
1113702Sjg restart:
1123702Sjg 	handle = calloc(1, sizeof (struct finddevhdl));
1133702Sjg 	if (handle == NULL) {
1143702Sjg 		(void) closedir(dir);
1153702Sjg 		return (ENOMEM);
1163702Sjg 	}
1173702Sjg 
1183702Sjg 	handle->npaths = 0;
1193702Sjg 	handle->curpath = 0;
1203702Sjg 	handle->paths = NULL;
1213702Sjg 
1223702Sjg 	n = 0;
1233702Sjg 	rewinddir(dir);
1243702Sjg 	while ((dp = readdir(dir)) != NULL) {
1253702Sjg 		if ((strcmp(dp->d_name, ".") == 0) ||
1263702Sjg 		    (strcmp(dp->d_name, "..") == 0))
1273702Sjg 			continue;
1283702Sjg 		n++;
1293702Sjg 	}
1303702Sjg 
1313702Sjg 	handle->npaths = n;
1323702Sjg 	handle->paths = calloc(n, sizeof (char *));
1333702Sjg 	if (handle->paths == NULL) {
1343702Sjg 		free(handle);
1353702Sjg 		(void) closedir(dir);
1363702Sjg 		return (ENOMEM);
1373702Sjg 	}
1383702Sjg 
1393702Sjg 	n = 0;
1403702Sjg 	rewinddir(dir);
1413702Sjg 	while ((dp = readdir(dir)) != NULL) {
1423702Sjg 		if ((strcmp(dp->d_name, ".") == 0) ||
1433702Sjg 		    (strcmp(dp->d_name, "..") == 0))
1443702Sjg 			continue;
1453702Sjg 		if (n == handle->npaths) {
1463702Sjg 			/*
1473702Sjg 			 * restart if directory contents have out-grown
1483702Sjg 			 * buffer allocated in the first pass.
1493702Sjg 			 */
1503702Sjg 			finddev_close((finddevhdl_t)handle);
1513702Sjg 			goto restart;
1523702Sjg 		}
1533702Sjg 		handle->paths[n] = strdup(dp->d_name);
1543702Sjg 		if (handle->paths[n] == NULL) {
1553702Sjg 			(void) closedir(dir);
1563702Sjg 			finddev_close((finddevhdl_t)handle);
1573702Sjg 			return (ENOMEM);
1583702Sjg 		}
1593702Sjg 		n++;
1603702Sjg 	}
1613702Sjg 	(void) closedir(dir);
1623702Sjg 	*handlep = (finddevhdl_t)handle;
1633702Sjg 	return (0);
1643702Sjg }
1653702Sjg 
1663702Sjg /*
1673702Sjg  * Use of the dev filesystem's private readdir does not trigger
1683702Sjg  * the implicit device reconfiguration.
1693702Sjg  *
1703702Sjg  * Note: only useable with paths mounted on an instance of the
1713702Sjg  * dev filesystem.
1723702Sjg  *
1733702Sjg  * Does not return the . and .. entries.
1743702Sjg  * Empty directories are returned as an zero-length list.
1753702Sjg  * ENOENT is returned as a NULL list pointer.
1763702Sjg  */
1773702Sjg static int
finddev_readdir_devfs(const char * path,finddevhdl_t * handlep)1783702Sjg finddev_readdir_devfs(const char *path, finddevhdl_t *handlep)
1792621Sllai1 {
1802621Sllai1 	struct finddevhdl	*handle;
1812621Sllai1 	int			n;
1822621Sllai1 	int			rv;
1832621Sllai1 	int64_t			bufsiz;
1842621Sllai1 	char			*pathlist;
1852621Sllai1 	char			*p;
1862621Sllai1 	int			len;
1872621Sllai1 
1882621Sllai1 	*handlep = NULL;
1892621Sllai1 	handle = calloc(1, sizeof (struct finddevhdl));
1902621Sllai1 	if (handle == NULL)
1912621Sllai1 		return (ENOMEM);
1922621Sllai1 
1932621Sllai1 	handle->npaths = 0;
1942621Sllai1 	handle->curpath = 0;
1952621Sllai1 	handle->paths = NULL;
1962621Sllai1 
1973702Sjg 	rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz);
1982621Sllai1 	if (rv != 0) {
1992621Sllai1 		free(handle);
2002621Sllai1 		return (rv);
2012621Sllai1 	}
2022621Sllai1 
2032621Sllai1 	for (;;) {
2042621Sllai1 		assert(bufsiz != 0);
2052621Sllai1 		if ((pathlist = malloc(bufsiz)) == NULL) {
2062621Sllai1 			free(handle);
2072621Sllai1 			return (ENOMEM);
2082621Sllai1 		}
2092621Sllai1 
2103702Sjg 		rv = modctl(MODDEVREADDIR, path, strlen(path),
2112621Sllai1 		    pathlist, &bufsiz);
2122621Sllai1 		if (rv == 0) {
2132621Sllai1 			for (n = 0, p = pathlist;
2142621Sllai1 			    (len = strlen(p)) > 0; p += len+1) {
2152621Sllai1 				n++;
2162621Sllai1 			}
2172621Sllai1 			handle->npaths = n;
2182621Sllai1 			handle->paths = calloc(n, sizeof (char *));
2192621Sllai1 			if (handle->paths == NULL) {
2202621Sllai1 				free(handle);
2212621Sllai1 				free(pathlist);
2222621Sllai1 				return (ENOMEM);
2232621Sllai1 			}
2242621Sllai1 			for (n = 0, p = pathlist;
2252621Sllai1 			    (len = strlen(p)) > 0; p += len+1, n++) {
2262621Sllai1 				handle->paths[n] = strdup(p);
2272621Sllai1 				if (handle->paths[n] == NULL) {
2282621Sllai1 					finddev_close((finddevhdl_t)handle);
2292621Sllai1 					free(pathlist);
2302621Sllai1 					return (ENOMEM);
2312621Sllai1 				}
2322621Sllai1 			}
2332621Sllai1 			*handlep = (finddevhdl_t)handle;
2342621Sllai1 			free(pathlist);
2352621Sllai1 			return (0);
2362621Sllai1 		}
2372621Sllai1 		free(pathlist);
2382621Sllai1 		switch (errno) {
2392621Sllai1 		case EAGAIN:
2402621Sllai1 			break;
2412621Sllai1 		case ENOENT:
2422621Sllai1 		default:
2432621Sllai1 			free(handle);
2442621Sllai1 			return (errno);
2452621Sllai1 		}
2462621Sllai1 	}
2472621Sllai1 	/*NOTREACHED*/
2482621Sllai1 }
2492621Sllai1 
2503702Sjg int
finddev_readdir(const char * path,finddevhdl_t * handlep)2513702Sjg finddev_readdir(const char *path, finddevhdl_t *handlep)
2523702Sjg {
2533747Sjg 	if (GLOBAL_DEV_PATH(path)) {
2543702Sjg 		return (finddev_readdir_devfs(path, handlep));
2553702Sjg 	}
2563702Sjg 	return (finddev_readdir_alt(path, handlep));
2573702Sjg }
2583702Sjg 
259*6065Scth /*
260*6065Scth  * Return true if a directory is empty
261*6065Scth  * Use the standard library readdir to determine if a directory is
262*6065Scth  * empty.
263*6065Scth  */
264*6065Scth static int
finddev_emptydir_alt(const char * path)265*6065Scth finddev_emptydir_alt(const char *path)
266*6065Scth {
267*6065Scth 	DIR		*dir;
268*6065Scth 	struct dirent	*dp;
269*6065Scth 
270*6065Scth 	if ((dir = opendir(path)) == NULL)
271*6065Scth 		return (ENOENT);
272*6065Scth 
273*6065Scth 	while ((dp = readdir(dir)) != NULL) {
274*6065Scth 		if ((strcmp(dp->d_name, ".") == 0) ||
275*6065Scth 		    (strcmp(dp->d_name, "..") == 0))
276*6065Scth 			continue;
277*6065Scth 		(void) closedir(dir);
278*6065Scth 		return (0);		/* not empty */
279*6065Scth 	}
280*6065Scth 	(void) closedir(dir);
281*6065Scth 	return (1);			/* empty */
282*6065Scth }
283*6065Scth 
284*6065Scth /*
285*6065Scth  * Use of the dev filesystem's private readdir does (not trigger
286*6065Scth  * the implicit device reconfiguration) to determine if a directory
287*6065Scth  * is empty.
288*6065Scth  *
289*6065Scth  * Note: only useable with paths mounted on an instance of the
290*6065Scth  * dev filesystem.
291*6065Scth  *
292*6065Scth  * Does not return the . and .. entries.
293*6065Scth  * Empty directories are returned as an zero-length list.
294*6065Scth  * ENOENT is returned as a NULL list pointer.
295*6065Scth  */
296*6065Scth static int
finddev_emptydir_devfs(const char * path)297*6065Scth finddev_emptydir_devfs(const char *path)
298*6065Scth {
299*6065Scth 	int	rv;
300*6065Scth 	int	empty;
301*6065Scth 
302*6065Scth 	rv = modctl(MODDEVEMPTYDIR, path, strlen(path), &empty);
303*6065Scth 	if (rv == 0) {
304*6065Scth 		return (empty);
305*6065Scth 	}
306*6065Scth 	return (0);
307*6065Scth }
308*6065Scth 
309*6065Scth int
finddev_emptydir(const char * path)310*6065Scth finddev_emptydir(const char *path)
311*6065Scth {
312*6065Scth 	if (GLOBAL_DEV_PATH(path)) {
313*6065Scth 		return (finddev_emptydir_devfs(path));
314*6065Scth 	}
315*6065Scth 	return (finddev_emptydir_alt(path));
316*6065Scth }
317*6065Scth 
3182621Sllai1 void
finddev_close(finddevhdl_t arg)3192621Sllai1 finddev_close(finddevhdl_t arg)
3202621Sllai1 {
3212621Sllai1 	struct finddevhdl *handle = (struct finddevhdl *)arg;
3222621Sllai1 	int i;
3232621Sllai1 
3242621Sllai1 	for (i = 0; i < handle->npaths; i++) {
3252621Sllai1 		if (handle->paths[i])
3262621Sllai1 			free(handle->paths[i]);
3272621Sllai1 	}
3282621Sllai1 	free(handle->paths);
3292621Sllai1 	free(handle);
3302621Sllai1 }
3312621Sllai1 
3322621Sllai1 const char *
finddev_next(finddevhdl_t arg)3332621Sllai1 finddev_next(finddevhdl_t arg)
3342621Sllai1 {
3352621Sllai1 	struct finddevhdl *handle = (struct finddevhdl *)arg;
3362621Sllai1 	const char *path = NULL;
3372621Sllai1 
3382621Sllai1 	if (handle->curpath < handle->npaths) {
3392621Sllai1 		path = handle->paths[handle->curpath];
3402621Sllai1 		handle->curpath++;
3412621Sllai1 	}
3422621Sllai1 	return (path);
3432621Sllai1 }
344