xref: /titanic_44/usr/src/lib/libdevinfo/devinfo_finddev.c (revision 73de625b17d1950886e89164b146fc139ea7e5fb)
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*73de625bSjg  * Copyright 2007 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>
43*73de625bSjg #include <zone.h>
44facf4a8dSllai1 #include <sys/modctl.h>
45facf4a8dSllai1 #include <syslog.h>
46*73de625bSjg #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 
57*73de625bSjg /*
58*73de625bSjg  * Return true if a device exists
59*73de625bSjg  * If the path refers into the /dev filesystem, use a
60*73de625bSjg  * private interface to query if the device exists but
61*73de625bSjg  * without triggering an implicit reconfig if it does not.
62*73de625bSjg  * Note: can only function properly with absolute pathnames
63*73de625bSjg  * and only functions for persisted global /dev names, ie
64*73de625bSjg  * those managed by devfsadm.  For paths other than
65*73de625bSjg  * /dev, stat(2) is sufficient.
66*73de625bSjg  */
67facf4a8dSllai1 int
68facf4a8dSllai1 device_exists(const char *devname)
69facf4a8dSllai1 {
70facf4a8dSllai1 	int	rv;
71*73de625bSjg 	struct stat st;
72facf4a8dSllai1 
73*73de625bSjg 	if ((getzoneid() == GLOBAL_ZONEID) &&
74*73de625bSjg 	    ((strcmp(devname, "/dev") == 0) ||
75*73de625bSjg 	    (strncmp(devname, "/dev/", 5) == 0))) {
76facf4a8dSllai1 		rv = modctl(MODDEVEXISTS, devname, strlen(devname));
77facf4a8dSllai1 		return ((rv == 0) ? 1 : 0);
78facf4a8dSllai1 	}
79*73de625bSjg 	if (stat(devname, &st) == 0)
80*73de625bSjg 		return (1);
81*73de625bSjg 	return (0);
82*73de625bSjg }
83facf4a8dSllai1 
84*73de625bSjg 
85*73de625bSjg /*
86*73de625bSjg  * Use the standard library readdir to read the contents of
87*73de625bSjg  * directories on alternate root mounted filesystems.
88*73de625bSjg  * Return results as per dev_readdir_devfs().
89*73de625bSjg  *
90*73de625bSjg  * The directory is traversed twice.  First, to calculate
91*73de625bSjg  * the size of the buffer required; second, to copy the
92*73de625bSjg  * directory contents into the buffer.  If the directory
93*73de625bSjg  * contents grow in between passes, which should almost
94*73de625bSjg  * never happen, start over again.
95*73de625bSjg  */
96*73de625bSjg static int
97*73de625bSjg finddev_readdir_alt(const char *path, finddevhdl_t *handlep)
98*73de625bSjg {
99*73de625bSjg 	struct finddevhdl *handle;
100*73de625bSjg 	DIR *dir;
101*73de625bSjg 	struct dirent *dp;
102*73de625bSjg 	size_t n;
103*73de625bSjg 
104*73de625bSjg 	*handlep = NULL;
105*73de625bSjg 	if ((dir = opendir(path)) == NULL)
106*73de625bSjg 		return (ENOENT);
107*73de625bSjg 
108*73de625bSjg restart:
109*73de625bSjg 	handle = calloc(1, sizeof (struct finddevhdl));
110*73de625bSjg 	if (handle == NULL) {
111*73de625bSjg 		(void) closedir(dir);
112*73de625bSjg 		return (ENOMEM);
113*73de625bSjg 	}
114*73de625bSjg 
115*73de625bSjg 	handle->npaths = 0;
116*73de625bSjg 	handle->curpath = 0;
117*73de625bSjg 	handle->paths = NULL;
118*73de625bSjg 
119*73de625bSjg 	n = 0;
120*73de625bSjg 	rewinddir(dir);
121*73de625bSjg 	while ((dp = readdir(dir)) != NULL) {
122*73de625bSjg 		if ((strcmp(dp->d_name, ".") == 0) ||
123*73de625bSjg 		    (strcmp(dp->d_name, "..") == 0))
124*73de625bSjg 			continue;
125*73de625bSjg 		n++;
126*73de625bSjg 	}
127*73de625bSjg 
128*73de625bSjg 	handle->npaths = n;
129*73de625bSjg 	handle->paths = calloc(n, sizeof (char *));
130*73de625bSjg 	if (handle->paths == NULL) {
131*73de625bSjg 		free(handle);
132*73de625bSjg 		(void) closedir(dir);
133*73de625bSjg 		return (ENOMEM);
134*73de625bSjg 	}
135*73de625bSjg 
136*73de625bSjg 	n = 0;
137*73de625bSjg 	rewinddir(dir);
138*73de625bSjg 	while ((dp = readdir(dir)) != NULL) {
139*73de625bSjg 		if ((strcmp(dp->d_name, ".") == 0) ||
140*73de625bSjg 		    (strcmp(dp->d_name, "..") == 0))
141*73de625bSjg 			continue;
142*73de625bSjg 		if (n == handle->npaths) {
143*73de625bSjg 			/*
144*73de625bSjg 			 * restart if directory contents have out-grown
145*73de625bSjg 			 * buffer allocated in the first pass.
146*73de625bSjg 			 */
147*73de625bSjg 			finddev_close((finddevhdl_t)handle);
148*73de625bSjg 			goto restart;
149*73de625bSjg 		}
150*73de625bSjg 		handle->paths[n] = strdup(dp->d_name);
151*73de625bSjg 		if (handle->paths[n] == NULL) {
152*73de625bSjg 			(void) closedir(dir);
153*73de625bSjg 			finddev_close((finddevhdl_t)handle);
154*73de625bSjg 			return (ENOMEM);
155*73de625bSjg 		}
156*73de625bSjg 		n++;
157*73de625bSjg 	}
158*73de625bSjg 	(void) closedir(dir);
159*73de625bSjg 	*handlep = (finddevhdl_t)handle;
160*73de625bSjg 	return (0);
161*73de625bSjg }
162*73de625bSjg 
163*73de625bSjg /*
164*73de625bSjg  * Use of the dev filesystem's private readdir does not trigger
165*73de625bSjg  * the implicit device reconfiguration.
166*73de625bSjg  *
167*73de625bSjg  * Note: only useable with paths mounted on an instance of the
168*73de625bSjg  * dev filesystem.
169*73de625bSjg  *
170*73de625bSjg  * Does not return the . and .. entries.
171*73de625bSjg  * Empty directories are returned as an zero-length list.
172*73de625bSjg  * ENOENT is returned as a NULL list pointer.
173*73de625bSjg  */
174*73de625bSjg static int
175*73de625bSjg finddev_readdir_devfs(const char *path, finddevhdl_t *handlep)
176facf4a8dSllai1 {
177facf4a8dSllai1 	struct finddevhdl	*handle;
178facf4a8dSllai1 	int			n;
179facf4a8dSllai1 	int			rv;
180facf4a8dSllai1 	int64_t			bufsiz;
181facf4a8dSllai1 	char			*pathlist;
182facf4a8dSllai1 	char			*p;
183facf4a8dSllai1 	int			len;
184facf4a8dSllai1 
185facf4a8dSllai1 	*handlep = NULL;
186facf4a8dSllai1 	handle = calloc(1, sizeof (struct finddevhdl));
187facf4a8dSllai1 	if (handle == NULL)
188facf4a8dSllai1 		return (ENOMEM);
189facf4a8dSllai1 
190facf4a8dSllai1 	handle->npaths = 0;
191facf4a8dSllai1 	handle->curpath = 0;
192facf4a8dSllai1 	handle->paths = NULL;
193facf4a8dSllai1 
194*73de625bSjg 	rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz);
195facf4a8dSllai1 	if (rv != 0) {
196facf4a8dSllai1 		free(handle);
197facf4a8dSllai1 		return (rv);
198facf4a8dSllai1 	}
199facf4a8dSllai1 
200facf4a8dSllai1 	for (;;) {
201facf4a8dSllai1 		assert(bufsiz != 0);
202facf4a8dSllai1 		if ((pathlist = malloc(bufsiz)) == NULL) {
203facf4a8dSllai1 			free(handle);
204facf4a8dSllai1 			return (ENOMEM);
205facf4a8dSllai1 		}
206facf4a8dSllai1 
207*73de625bSjg 		rv = modctl(MODDEVREADDIR, path, strlen(path),
208facf4a8dSllai1 		    pathlist, &bufsiz);
209facf4a8dSllai1 		if (rv == 0) {
210facf4a8dSllai1 			for (n = 0, p = pathlist;
211facf4a8dSllai1 			    (len = strlen(p)) > 0; p += len+1) {
212facf4a8dSllai1 				n++;
213facf4a8dSllai1 			}
214facf4a8dSllai1 			handle->npaths = n;
215facf4a8dSllai1 			handle->paths = calloc(n, sizeof (char *));
216facf4a8dSllai1 			if (handle->paths == NULL) {
217facf4a8dSllai1 				free(handle);
218facf4a8dSllai1 				free(pathlist);
219facf4a8dSllai1 				return (ENOMEM);
220facf4a8dSllai1 			}
221facf4a8dSllai1 			for (n = 0, p = pathlist;
222facf4a8dSllai1 			    (len = strlen(p)) > 0; p += len+1, n++) {
223facf4a8dSllai1 				handle->paths[n] = strdup(p);
224facf4a8dSllai1 				if (handle->paths[n] == NULL) {
225facf4a8dSllai1 					finddev_close((finddevhdl_t)handle);
226facf4a8dSllai1 					free(pathlist);
227facf4a8dSllai1 					return (ENOMEM);
228facf4a8dSllai1 				}
229facf4a8dSllai1 			}
230facf4a8dSllai1 			*handlep = (finddevhdl_t)handle;
231facf4a8dSllai1 			free(pathlist);
232facf4a8dSllai1 			return (0);
233facf4a8dSllai1 		}
234facf4a8dSllai1 		free(pathlist);
235facf4a8dSllai1 		switch (errno) {
236facf4a8dSllai1 		case EAGAIN:
237facf4a8dSllai1 			break;
238facf4a8dSllai1 		case ENOENT:
239facf4a8dSllai1 		default:
240facf4a8dSllai1 			free(handle);
241facf4a8dSllai1 			return (errno);
242facf4a8dSllai1 		}
243facf4a8dSllai1 	}
244facf4a8dSllai1 	/*NOTREACHED*/
245facf4a8dSllai1 }
246facf4a8dSllai1 
247*73de625bSjg int
248*73de625bSjg finddev_readdir(const char *path, finddevhdl_t *handlep)
249*73de625bSjg {
250*73de625bSjg 	if ((getzoneid() == GLOBAL_ZONEID) &&
251*73de625bSjg 	    ((strcmp(path, "/dev") == 0) ||
252*73de625bSjg 	    (strncmp(path, "/dev/", 4) == 0))) {
253*73de625bSjg 		return (finddev_readdir_devfs(path, handlep));
254*73de625bSjg 	}
255*73de625bSjg 	return (finddev_readdir_alt(path, handlep));
256*73de625bSjg }
257*73de625bSjg 
258facf4a8dSllai1 void
259facf4a8dSllai1 finddev_close(finddevhdl_t arg)
260facf4a8dSllai1 {
261facf4a8dSllai1 	struct finddevhdl *handle = (struct finddevhdl *)arg;
262facf4a8dSllai1 	int i;
263facf4a8dSllai1 
264facf4a8dSllai1 	for (i = 0; i < handle->npaths; i++) {
265facf4a8dSllai1 		if (handle->paths[i])
266facf4a8dSllai1 			free(handle->paths[i]);
267facf4a8dSllai1 	}
268facf4a8dSllai1 	free(handle->paths);
269facf4a8dSllai1 	free(handle);
270facf4a8dSllai1 }
271facf4a8dSllai1 
272facf4a8dSllai1 const char *
273facf4a8dSllai1 finddev_next(finddevhdl_t arg)
274facf4a8dSllai1 {
275facf4a8dSllai1 	struct finddevhdl *handle = (struct finddevhdl *)arg;
276facf4a8dSllai1 	const char *path = NULL;
277facf4a8dSllai1 
278facf4a8dSllai1 	if (handle->curpath < handle->npaths) {
279facf4a8dSllai1 		path = handle->paths[handle->curpath];
280facf4a8dSllai1 		handle->curpath++;
281facf4a8dSllai1 	}
282facf4a8dSllai1 	return (path);
283facf4a8dSllai1 }
284