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 /* 2273de625bSjg * 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> 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 57*25dfe2b1Sjg #define GLOBAL_DEV_PATH(devpath) \ 58*25dfe2b1Sjg ((getzoneid() == GLOBAL_ZONEID) && \ 59*25dfe2b1Sjg ((strcmp(devpath, "/dev") == 0) || \ 60*25dfe2b1Sjg (strncmp(devpath, "/dev/", strlen("/dev/")) == 0))) 61*25dfe2b1Sjg 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 78*25dfe2b1Sjg 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 { 253*25dfe2b1Sjg if (GLOBAL_DEV_PATH(path)) { 25473de625bSjg return (finddev_readdir_devfs(path, handlep)); 25573de625bSjg } 25673de625bSjg return (finddev_readdir_alt(path, handlep)); 25773de625bSjg } 25873de625bSjg 259facf4a8dSllai1 void 260facf4a8dSllai1 finddev_close(finddevhdl_t arg) 261facf4a8dSllai1 { 262facf4a8dSllai1 struct finddevhdl *handle = (struct finddevhdl *)arg; 263facf4a8dSllai1 int i; 264facf4a8dSllai1 265facf4a8dSllai1 for (i = 0; i < handle->npaths; i++) { 266facf4a8dSllai1 if (handle->paths[i]) 267facf4a8dSllai1 free(handle->paths[i]); 268facf4a8dSllai1 } 269facf4a8dSllai1 free(handle->paths); 270facf4a8dSllai1 free(handle); 271facf4a8dSllai1 } 272facf4a8dSllai1 273facf4a8dSllai1 const char * 274facf4a8dSllai1 finddev_next(finddevhdl_t arg) 275facf4a8dSllai1 { 276facf4a8dSllai1 struct finddevhdl *handle = (struct finddevhdl *)arg; 277facf4a8dSllai1 const char *path = NULL; 278facf4a8dSllai1 279facf4a8dSllai1 if (handle->curpath < handle->npaths) { 280facf4a8dSllai1 path = handle->paths[handle->curpath]; 281facf4a8dSllai1 handle->curpath++; 282facf4a8dSllai1 } 283facf4a8dSllai1 return (path); 284facf4a8dSllai1 } 285