1facf4a8dSllai /*
2facf4a8dSllai  * CDDL HEADER START
3facf4a8dSllai  *
4facf4a8dSllai  * The contents of this file are subject to the terms of the
5facf4a8dSllai  * Common Development and Distribution License (the "License").
6facf4a8dSllai  * You may not use this file except in compliance with the License.
7facf4a8dSllai  *
8facf4a8dSllai  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9facf4a8dSllai  * or http://www.opensolaris.org/os/licensing.
10facf4a8dSllai  * See the License for the specific language governing permissions
11facf4a8dSllai  * and limitations under the License.
12facf4a8dSllai  *
13facf4a8dSllai  * When distributing Covered Code, include this CDDL HEADER in each
14facf4a8dSllai  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15facf4a8dSllai  * If applicable, add the following below this CDDL HEADER, with the
16facf4a8dSllai  * fields enclosed by brackets "[]" replaced with your own identifying
17facf4a8dSllai  * information: Portions Copyright [yyyy] [name of copyright owner]
18facf4a8dSllai  *
19facf4a8dSllai  * CDDL HEADER END
20facf4a8dSllai  */
21facf4a8dSllai /*
22*e37c6c37Scth  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23facf4a8dSllai  * Use is subject to license terms.
24facf4a8dSllai  */
25facf4a8dSllai 
26facf4a8dSllai #include <stdio.h>
27facf4a8dSllai #include <unistd.h>
28facf4a8dSllai #include <fcntl.h>
29facf4a8dSllai #include <string.h>
30facf4a8dSllai #include <thread.h>
31facf4a8dSllai #include <synch.h>
32facf4a8dSllai #include <limits.h>
33facf4a8dSllai #include <stdlib.h>
34facf4a8dSllai #include <string.h>
35facf4a8dSllai #include <strings.h>
36facf4a8dSllai #include <dirent.h>
37facf4a8dSllai #include <regex.h>
38facf4a8dSllai #include <errno.h>
39facf4a8dSllai #include <stdarg.h>
40facf4a8dSllai #include <libdevinfo.h>
4173de625bSjg #include <zone.h>
42facf4a8dSllai #include <sys/modctl.h>
43facf4a8dSllai #include <syslog.h>
4473de625bSjg #include <sys/stat.h>
45facf4a8dSllai #include <assert.h>
46facf4a8dSllai 
47facf4a8dSllai 
48facf4a8dSllai struct finddevhdl {
49facf4a8dSllai 	int	npaths;
50facf4a8dSllai 	int	curpath;
51facf4a8dSllai 	char	**paths;
52facf4a8dSllai };
53facf4a8dSllai 
54facf4a8dSllai 
5525dfe2b1Sjg #define	GLOBAL_DEV_PATH(devpath)			\
5625dfe2b1Sjg 	((getzoneid() == GLOBAL_ZONEID) &&		\
5725dfe2b1Sjg 	    ((strcmp(devpath, "/dev") == 0) ||		\
5825dfe2b1Sjg 	    (strncmp(devpath, "/dev/", strlen("/dev/")) == 0)))
5925dfe2b1Sjg 
6073de625bSjg /*
6173de625bSjg  * Return true if a device exists
6273de625bSjg  * If the path refers into the /dev filesystem, use a
6373de625bSjg  * private interface to query if the device exists but
6473de625bSjg  * without triggering an implicit reconfig if it does not.
6573de625bSjg  * Note: can only function properly with absolute pathnames
6673de625bSjg  * and only functions for persisted global /dev names, ie
6773de625bSjg  * those managed by devfsadm.  For paths other than
6873de625bSjg  * /dev, stat(2) is sufficient.
6973de625bSjg  */
70facf4a8dSllai int
device_exists(const char * devname)71facf4a8dSllai device_exists(const char *devname)
72facf4a8dSllai {
73facf4a8dSllai 	int	rv;
7473de625bSjg 	struct stat st;
75facf4a8dSllai 
7625dfe2b1Sjg 	if (GLOBAL_DEV_PATH(devname)) {
7773de625bSjg 		rv = modctl(MODDEVEXISTS, devname, strlen(devname));
7873de625bSjg 		return ((rv == 0) ? 1 : 0);
7973de625bSjg 	}
8073de625bSjg 	if (stat(devname, &st) == 0)
8173de625bSjg 		return (1);
8273de625bSjg 	return (0);
83facf4a8dSllai }
84facf4a8dSllai 
8573de625bSjg 
8673de625bSjg /*
8773de625bSjg  * Use the standard library readdir to read the contents of
8873de625bSjg  * directories on alternate root mounted filesystems.
8973de625bSjg  * Return results as per dev_readdir_devfs().
9073de625bSjg  *
9173de625bSjg  * The directory is traversed twice.  First, to calculate
9273de625bSjg  * the size of the buffer required; second, to copy the
9373de625bSjg  * directory contents into the buffer.  If the directory
9473de625bSjg  * contents grow in between passes, which should almost
9573de625bSjg  * never happen, start over again.
9673de625bSjg  */
9773de625bSjg static int
finddev_readdir_alt(const char * path,finddevhdl_t * handlep)9873de625bSjg finddev_readdir_alt(const char *path, finddevhdl_t *handlep)
9973de625bSjg {
10073de625bSjg 	struct finddevhdl *handle;
10173de625bSjg 	DIR *dir;
10273de625bSjg 	struct dirent *dp;
10373de625bSjg 	size_t n;
10473de625bSjg 
10573de625bSjg 	*handlep = NULL;
10673de625bSjg 	if ((dir = opendir(path)) == NULL)
10773de625bSjg 		return (ENOENT);
10873de625bSjg 
10973de625bSjg restart:
11073de625bSjg 	handle = calloc(1, sizeof (struct finddevhdl));
11173de625bSjg 	if (handle == NULL) {
11273de625bSjg 		(void) closedir(dir);
11373de625bSjg 		return (ENOMEM);
11473de625bSjg 	}
11573de625bSjg 
11673de625bSjg 	handle->npaths = 0;
11773de625bSjg 	handle->curpath = 0;
11873de625bSjg 	handle->paths = NULL;
11973de625bSjg 
12073de625bSjg 	n = 0;
12173de625bSjg 	rewinddir(dir);
12273de625bSjg 	while ((dp = readdir(dir)) != NULL) {
12373de625bSjg 		if ((strcmp(dp->d_name, ".") == 0) ||
12473de625bSjg 		    (strcmp(dp->d_name, "..") == 0))
12573de625bSjg 			continue;
12673de625bSjg 		n++;
12773de625bSjg 	}
12873de625bSjg 
12973de625bSjg 	handle->npaths = n;
13073de625bSjg 	handle->paths = calloc(n, sizeof (char *));
13173de625bSjg 	if (handle->paths == NULL) {
13273de625bSjg 		free(handle);
13373de625bSjg 		(void) closedir(dir);
13473de625bSjg 		return (ENOMEM);
13573de625bSjg 	}
13673de625bSjg 
13773de625bSjg 	n = 0;
13873de625bSjg 	rewinddir(dir);
13973de625bSjg 	while ((dp = readdir(dir)) != NULL) {
14073de625bSjg 		if ((strcmp(dp->d_name, ".") == 0) ||
14173de625bSjg 		    (strcmp(dp->d_name, "..") == 0))
14273de625bSjg 			continue;
14373de625bSjg 		if (n == handle->npaths) {
14473de625bSjg 			/*
14573de625bSjg 			 * restart if directory contents have out-grown
14673de625bSjg 			 * buffer allocated in the first pass.
14773de625bSjg 			 */
14873de625bSjg 			finddev_close((finddevhdl_t)handle);
14973de625bSjg 			goto restart;
15073de625bSjg 		}
15173de625bSjg 		handle->paths[n] = strdup(dp->d_name);
15273de625bSjg 		if (handle->paths[n] == NULL) {
15373de625bSjg 			(void) closedir(dir);
15473de625bSjg 			finddev_close((finddevhdl_t)handle);
15573de625bSjg 			return (ENOMEM);
15673de625bSjg 		}
15773de625bSjg 		n++;
15873de625bSjg 	}
15973de625bSjg 	(void) closedir(dir);
16073de625bSjg 	*handlep = (finddevhdl_t)handle;
16173de625bSjg 	return (0);
16273de625bSjg }
16373de625bSjg 
16473de625bSjg /*
16573de625bSjg  * Use of the dev filesystem's private readdir does not trigger
16673de625bSjg  * the implicit device reconfiguration.
16773de625bSjg  *
16873de625bSjg  * Note: only useable with paths mounted on an instance of the
16973de625bSjg  * dev filesystem.
17073de625bSjg  *
17173de625bSjg  * Does not return the . and .. entries.
17273de625bSjg  * Empty directories are returned as an zero-length list.
17373de625bSjg  * ENOENT is returned as a NULL list pointer.
17473de625bSjg  */
17573de625bSjg static int
finddev_readdir_devfs(const char * path,finddevhdl_t * handlep)17673de625bSjg finddev_readdir_devfs(const char *path, finddevhdl_t *handlep)
177facf4a8dSllai {
178facf4a8dSllai 	struct finddevhdl	*handle;
179facf4a8dSllai 	int			n;
180facf4a8dSllai 	int			rv;
181facf4a8dSllai 	int64_t			bufsiz;
182facf4a8dSllai 	char			*pathlist;
183facf4a8dSllai 	char			*p;
184facf4a8dSllai 	int			len;
185facf4a8dSllai 
186facf4a8dSllai 	*handlep = NULL;
187facf4a8dSllai 	handle = calloc(1, sizeof (struct finddevhdl));
188facf4a8dSllai 	if (handle == NULL)
189facf4a8dSllai 		return (ENOMEM);
190facf4a8dSllai 
191facf4a8dSllai 	handle->npaths = 0;
192facf4a8dSllai 	handle->curpath = 0;
193facf4a8dSllai 	handle->paths = NULL;
194facf4a8dSllai 
19573de625bSjg 	rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz);
196facf4a8dSllai 	if (rv != 0) {
197facf4a8dSllai 		free(handle);
198facf4a8dSllai 		return (rv);
199facf4a8dSllai 	}
200facf4a8dSllai 
201facf4a8dSllai 	for (;;) {
202facf4a8dSllai 		assert(bufsiz != 0);
203facf4a8dSllai 		if ((pathlist = malloc(bufsiz)) == NULL) {
204facf4a8dSllai 			free(handle);
205facf4a8dSllai 			return (ENOMEM);
206facf4a8dSllai 		}
207facf4a8dSllai 
20873de625bSjg 		rv = modctl(MODDEVREADDIR, path, strlen(path),
209facf4a8dSllai 		    pathlist, &bufsiz);
210facf4a8dSllai 		if (rv == 0) {
211facf4a8dSllai 			for (n = 0, p = pathlist;
212facf4a8dSllai 			    (len = strlen(p)) > 0; p += len+1) {
213facf4a8dSllai 				n++;
214facf4a8dSllai 			}
215facf4a8dSllai 			handle->npaths = n;
216facf4a8dSllai 			handle->paths = calloc(n, sizeof (char *));
217facf4a8dSllai 			if (handle->paths == NULL) {
218facf4a8dSllai 				free(handle);
219facf4a8dSllai 				free(pathlist);
220facf4a8dSllai 				return (ENOMEM);
221facf4a8dSllai 			}
222facf4a8dSllai 			for (n = 0, p = pathlist;
223facf4a8dSllai 			    (len = strlen(p)) > 0; p += len+1, n++) {
224facf4a8dSllai 				handle->paths[n] = strdup(p);
225facf4a8dSllai 				if (handle->paths[n] == NULL) {
226facf4a8dSllai 					finddev_close((finddevhdl_t)handle);
227facf4a8dSllai 					free(pathlist);
228facf4a8dSllai 					return (ENOMEM);
229facf4a8dSllai 				}
230facf4a8dSllai 			}
231facf4a8dSllai 			*handlep = (finddevhdl_t)handle;
232facf4a8dSllai 			free(pathlist);
233facf4a8dSllai 			return (0);
234facf4a8dSllai 		}
235facf4a8dSllai 		free(pathlist);
236facf4a8dSllai 		switch (errno) {
237facf4a8dSllai 		case EAGAIN:
238facf4a8dSllai 			break;
239facf4a8dSllai 		case ENOENT:
240facf4a8dSllai 		default:
241facf4a8dSllai 			free(handle);
242facf4a8dSllai 			return (errno);
243facf4a8dSllai 		}
244facf4a8dSllai 	}
245facf4a8dSllai 	/*NOTREACHED*/
246facf4a8dSllai }
247facf4a8dSllai 
24873de625bSjg int
finddev_readdir(const char * path,finddevhdl_t * handlep)24973de625bSjg finddev_readdir(const char *path, finddevhdl_t *handlep)
25073de625bSjg {
25125dfe2b1Sjg 	if (GLOBAL_DEV_PATH(path)) {
25273de625bSjg 		return (finddev_readdir_devfs(path, handlep));
25373de625bSjg 	}
25473de625bSjg 	return (finddev_readdir_alt(path, handlep));
25573de625bSjg }
25673de625bSjg 
257*e37c6c37Scth /*
258*e37c6c37Scth  * Return true if a directory is empty
259*e37c6c37Scth  * Use the standard library readdir to determine if a directory is
260*e37c6c37Scth  * empty.
261*e37c6c37Scth  */
262*e37c6c37Scth static int
finddev_emptydir_alt(const char * path)263*e37c6c37Scth finddev_emptydir_alt(const char *path)
264*e37c6c37Scth {
265*e37c6c37Scth 	DIR		*dir;
266*e37c6c37Scth 	struct dirent	*dp;
267*e37c6c37Scth 
268*e37c6c37Scth 	if ((dir = opendir(path)) == NULL)
269*e37c6c37Scth 		return (ENOENT);
270*e37c6c37Scth 
271*e37c6c37Scth 	while ((dp = readdir(dir)) != NULL) {
272*e37c6c37Scth 		if ((strcmp(dp->d_name, ".") == 0) ||
273*e37c6c37Scth 		    (strcmp(dp->d_name, "..") == 0))
274*e37c6c37Scth 			continue;
275*e37c6c37Scth 		(void) closedir(dir);
276*e37c6c37Scth 		return (0);		/* not empty */
277*e37c6c37Scth 	}
278*e37c6c37Scth 	(void) closedir(dir);
279*e37c6c37Scth 	return (1);			/* empty */
280*e37c6c37Scth }
281*e37c6c37Scth 
282*e37c6c37Scth /*
283*e37c6c37Scth  * Use of the dev filesystem's private readdir does (not trigger
284*e37c6c37Scth  * the implicit device reconfiguration) to determine if a directory
285*e37c6c37Scth  * is empty.
286*e37c6c37Scth  *
287*e37c6c37Scth  * Note: only useable with paths mounted on an instance of the
288*e37c6c37Scth  * dev filesystem.
289*e37c6c37Scth  *
290*e37c6c37Scth  * Does not return the . and .. entries.
291*e37c6c37Scth  * Empty directories are returned as an zero-length list.
292*e37c6c37Scth  * ENOENT is returned as a NULL list pointer.
293*e37c6c37Scth  */
294*e37c6c37Scth static int
finddev_emptydir_devfs(const char * path)295*e37c6c37Scth finddev_emptydir_devfs(const char *path)
296*e37c6c37Scth {
297*e37c6c37Scth 	int	rv;
298*e37c6c37Scth 	int	empty;
299*e37c6c37Scth 
300*e37c6c37Scth 	rv = modctl(MODDEVEMPTYDIR, path, strlen(path), &empty);
301*e37c6c37Scth 	if (rv == 0) {
302*e37c6c37Scth 		return (empty);
303*e37c6c37Scth 	}
304*e37c6c37Scth 	return (0);
305*e37c6c37Scth }
306*e37c6c37Scth 
307*e37c6c37Scth int
finddev_emptydir(const char * path)308*e37c6c37Scth finddev_emptydir(const char *path)
309*e37c6c37Scth {
310*e37c6c37Scth 	if (GLOBAL_DEV_PATH(path)) {
311*e37c6c37Scth 		return (finddev_emptydir_devfs(path));
312*e37c6c37Scth 	}
313*e37c6c37Scth 	return (finddev_emptydir_alt(path));
314*e37c6c37Scth }
315*e37c6c37Scth 
316facf4a8dSllai void
finddev_close(finddevhdl_t arg)317facf4a8dSllai finddev_close(finddevhdl_t arg)
318facf4a8dSllai {
319facf4a8dSllai 	struct finddevhdl *handle = (struct finddevhdl *)arg;
320facf4a8dSllai 	int i;
321facf4a8dSllai 
322facf4a8dSllai 	for (i = 0; i < handle->npaths; i++) {
323facf4a8dSllai 		if (handle->paths[i])
324facf4a8dSllai 			free(handle->paths[i]);
325facf4a8dSllai 	}
326facf4a8dSllai 	free(handle->paths);
327facf4a8dSllai 	free(handle);
328facf4a8dSllai }
329facf4a8dSllai 
330facf4a8dSllai const char *
finddev_next(finddevhdl_t arg)331facf4a8dSllai finddev_next(finddevhdl_t arg)
332facf4a8dSllai {
333facf4a8dSllai 	struct finddevhdl *handle = (struct finddevhdl *)arg;
334facf4a8dSllai 	const char *path = NULL;
335facf4a8dSllai 
336facf4a8dSllai 	if (handle->curpath < handle->npaths) {
337facf4a8dSllai 		path = handle->paths[handle->curpath];
338facf4a8dSllai 		handle->curpath++;
339facf4a8dSllai 	}
340facf4a8dSllai 	return (path);
341facf4a8dSllai }
342