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 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 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 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 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 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 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 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 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 * 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