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