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