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