Home | History | Annotate | Download | only in libdevinfo
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <stdio.h>
     29 #include <unistd.h>
     30 #include <fcntl.h>
     31 #include <string.h>
     32 #include <thread.h>
     33 #include <synch.h>
     34 #include <limits.h>
     35 #include <stdlib.h>
     36 #include <string.h>
     37 #include <strings.h>
     38 #include <dirent.h>
     39 #include <regex.h>
     40 #include <errno.h>
     41 #include <stdarg.h>
     42 #include <libdevinfo.h>
     43 #include <zone.h>
     44 #include <sys/modctl.h>
     45 #include <syslog.h>
     46 #include <sys/stat.h>
     47 #include <assert.h>
     48 
     49 
     50 struct finddevhdl {
     51 	int	npaths;
     52 	int	curpath;
     53 	char	**paths;
     54 };
     55 
     56 
     57 #define	GLOBAL_DEV_PATH(devpath)			\
     58 	((getzoneid() == GLOBAL_ZONEID) &&		\
     59 	    ((strcmp(devpath, "/dev") == 0) ||		\
     60 	    (strncmp(devpath, "/dev/", strlen("/dev/")) == 0)))
     61 
     62 /*
     63  * Return true if a device exists
     64  * If the path refers into the /dev filesystem, use a
     65  * private interface to query if the device exists but
     66  * without triggering an implicit reconfig if it does not.
     67  * Note: can only function properly with absolute pathnames
     68  * and only functions for persisted global /dev names, ie
     69  * those managed by devfsadm.  For paths other than
     70  * /dev, stat(2) is sufficient.
     71  */
     72 int
     73 device_exists(const char *devname)
     74 {
     75 	int	rv;
     76 	struct stat st;
     77 
     78 	if (GLOBAL_DEV_PATH(devname)) {
     79 		rv = modctl(MODDEVEXISTS, devname, strlen(devname));
     80 		return ((rv == 0) ? 1 : 0);
     81 	}
     82 	if (stat(devname, &st) == 0)
     83 		return (1);
     84 	return (0);
     85 }
     86 
     87 
     88 /*
     89  * Use the standard library readdir to read the contents of
     90  * directories on alternate root mounted filesystems.
     91  * Return results as per dev_readdir_devfs().
     92  *
     93  * The directory is traversed twice.  First, to calculate
     94  * the size of the buffer required; second, to copy the
     95  * directory contents into the buffer.  If the directory
     96  * contents grow in between passes, which should almost
     97  * never happen, start over again.
     98  */
     99 static int
    100 finddev_readdir_alt(const char *path, finddevhdl_t *handlep)
    101 {
    102 	struct finddevhdl *handle;
    103 	DIR *dir;
    104 	struct dirent *dp;
    105 	size_t n;
    106 
    107 	*handlep = NULL;
    108 	if ((dir = opendir(path)) == NULL)
    109 		return (ENOENT);
    110 
    111 restart:
    112 	handle = calloc(1, sizeof (struct finddevhdl));
    113 	if (handle == NULL) {
    114 		(void) closedir(dir);
    115 		return (ENOMEM);
    116 	}
    117 
    118 	handle->npaths = 0;
    119 	handle->curpath = 0;
    120 	handle->paths = NULL;
    121 
    122 	n = 0;
    123 	rewinddir(dir);
    124 	while ((dp = readdir(dir)) != NULL) {
    125 		if ((strcmp(dp->d_name, ".") == 0) ||
    126 		    (strcmp(dp->d_name, "..") == 0))
    127 			continue;
    128 		n++;
    129 	}
    130 
    131 	handle->npaths = n;
    132 	handle->paths = calloc(n, sizeof (char *));
    133 	if (handle->paths == NULL) {
    134 		free(handle);
    135 		(void) closedir(dir);
    136 		return (ENOMEM);
    137 	}
    138 
    139 	n = 0;
    140 	rewinddir(dir);
    141 	while ((dp = readdir(dir)) != NULL) {
    142 		if ((strcmp(dp->d_name, ".") == 0) ||
    143 		    (strcmp(dp->d_name, "..") == 0))
    144 			continue;
    145 		if (n == handle->npaths) {
    146 			/*
    147 			 * restart if directory contents have out-grown
    148 			 * buffer allocated in the first pass.
    149 			 */
    150 			finddev_close((finddevhdl_t)handle);
    151 			goto restart;
    152 		}
    153 		handle->paths[n] = strdup(dp->d_name);
    154 		if (handle->paths[n] == NULL) {
    155 			(void) closedir(dir);
    156 			finddev_close((finddevhdl_t)handle);
    157 			return (ENOMEM);
    158 		}
    159 		n++;
    160 	}
    161 	(void) closedir(dir);
    162 	*handlep = (finddevhdl_t)handle;
    163 	return (0);
    164 }
    165 
    166 /*
    167  * Use of the dev filesystem's private readdir does not trigger
    168  * the implicit device reconfiguration.
    169  *
    170  * Note: only useable with paths mounted on an instance of the
    171  * dev filesystem.
    172  *
    173  * Does not return the . and .. entries.
    174  * Empty directories are returned as an zero-length list.
    175  * ENOENT is returned as a NULL list pointer.
    176  */
    177 static int
    178 finddev_readdir_devfs(const char *path, finddevhdl_t *handlep)
    179 {
    180 	struct finddevhdl	*handle;
    181 	int			n;
    182 	int			rv;
    183 	int64_t			bufsiz;
    184 	char			*pathlist;
    185 	char			*p;
    186 	int			len;
    187 
    188 	*handlep = NULL;
    189 	handle = calloc(1, sizeof (struct finddevhdl));
    190 	if (handle == NULL)
    191 		return (ENOMEM);
    192 
    193 	handle->npaths = 0;
    194 	handle->curpath = 0;
    195 	handle->paths = NULL;
    196 
    197 	rv = modctl(MODDEVREADDIR, path, strlen(path), NULL, &bufsiz);
    198 	if (rv != 0) {
    199 		free(handle);
    200 		return (rv);
    201 	}
    202 
    203 	for (;;) {
    204 		assert(bufsiz != 0);
    205 		if ((pathlist = malloc(bufsiz)) == NULL) {
    206 			free(handle);
    207 			return (ENOMEM);
    208 		}
    209 
    210 		rv = modctl(MODDEVREADDIR, path, strlen(path),
    211 		    pathlist, &bufsiz);
    212 		if (rv == 0) {
    213 			for (n = 0, p = pathlist;
    214 			    (len = strlen(p)) > 0; p += len+1) {
    215 				n++;
    216 			}
    217 			handle->npaths = n;
    218 			handle->paths = calloc(n, sizeof (char *));
    219 			if (handle->paths == NULL) {
    220 				free(handle);
    221 				free(pathlist);
    222 				return (ENOMEM);
    223 			}
    224 			for (n = 0, p = pathlist;
    225 			    (len = strlen(p)) > 0; p += len+1, n++) {
    226 				handle->paths[n] = strdup(p);
    227 				if (handle->paths[n] == NULL) {
    228 					finddev_close((finddevhdl_t)handle);
    229 					free(pathlist);
    230 					return (ENOMEM);
    231 				}
    232 			}
    233 			*handlep = (finddevhdl_t)handle;
    234 			free(pathlist);
    235 			return (0);
    236 		}
    237 		free(pathlist);
    238 		switch (errno) {
    239 		case EAGAIN:
    240 			break;
    241 		case ENOENT:
    242 		default:
    243 			free(handle);
    244 			return (errno);
    245 		}
    246 	}
    247 	/*NOTREACHED*/
    248 }
    249 
    250 int
    251 finddev_readdir(const char *path, finddevhdl_t *handlep)
    252 {
    253 	if (GLOBAL_DEV_PATH(path)) {
    254 		return (finddev_readdir_devfs(path, handlep));
    255 	}
    256 	return (finddev_readdir_alt(path, handlep));
    257 }
    258 
    259 /*
    260  * Return true if a directory is empty
    261  * Use the standard library readdir to determine if a directory is
    262  * empty.
    263  */
    264 static int
    265 finddev_emptydir_alt(const char *path)
    266 {
    267 	DIR		*dir;
    268 	struct dirent	*dp;
    269 
    270 	if ((dir = opendir(path)) == NULL)
    271 		return (ENOENT);
    272 
    273 	while ((dp = readdir(dir)) != NULL) {
    274 		if ((strcmp(dp->d_name, ".") == 0) ||
    275 		    (strcmp(dp->d_name, "..") == 0))
    276 			continue;
    277 		(void) closedir(dir);
    278 		return (0);		/* not empty */
    279 	}
    280 	(void) closedir(dir);
    281 	return (1);			/* empty */
    282 }
    283 
    284 /*
    285  * Use of the dev filesystem's private readdir does (not trigger
    286  * the implicit device reconfiguration) to determine if a directory
    287  * is empty.
    288  *
    289  * Note: only useable with paths mounted on an instance of the
    290  * dev filesystem.
    291  *
    292  * Does not return the . and .. entries.
    293  * Empty directories are returned as an zero-length list.
    294  * ENOENT is returned as a NULL list pointer.
    295  */
    296 static int
    297 finddev_emptydir_devfs(const char *path)
    298 {
    299 	int	rv;
    300 	int	empty;
    301 
    302 	rv = modctl(MODDEVEMPTYDIR, path, strlen(path), &empty);
    303 	if (rv == 0) {
    304 		return (empty);
    305 	}
    306 	return (0);
    307 }
    308 
    309 int
    310 finddev_emptydir(const char *path)
    311 {
    312 	if (GLOBAL_DEV_PATH(path)) {
    313 		return (finddev_emptydir_devfs(path));
    314 	}
    315 	return (finddev_emptydir_alt(path));
    316 }
    317 
    318 void
    319 finddev_close(finddevhdl_t arg)
    320 {
    321 	struct finddevhdl *handle = (struct finddevhdl *)arg;
    322 	int i;
    323 
    324 	for (i = 0; i < handle->npaths; i++) {
    325 		if (handle->paths[i])
    326 			free(handle->paths[i]);
    327 	}
    328 	free(handle->paths);
    329 	free(handle);
    330 }
    331 
    332 const char *
    333 finddev_next(finddevhdl_t arg)
    334 {
    335 	struct finddevhdl *handle = (struct finddevhdl *)arg;
    336 	const char *path = NULL;
    337 
    338 	if (handle->curpath < handle->npaths) {
    339 		path = handle->paths[handle->curpath];
    340 		handle->curpath++;
    341 	}
    342 	return (path);
    343 }
    344