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