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 2006 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 <strings.h>
     30 #include <unistd.h>
     31 #include <stdarg.h>
     32 #include <fcntl.h>
     33 #include <stdlib.h>
     34 #include <libnvpair.h>
     35 #include <libdevinfo.h>
     36 #include <syslog.h>
     37 #include <sys/param.h>
     38 #include <errno.h>
     39 #include <assert.h>
     40 #include <sys/systeminfo.h>
     41 #include <sys/modctl.h>
     42 #include <sys/fs/sdev_node.h>
     43 
     44 
     45 #define	LINEMAX		1024
     46 #define	SPC		" \t\n"
     47 #define	QUOTES		"\'\""
     48 
     49 /*
     50  * This is for local file supports of DBNR configurations.
     51  */
     52 static int di_devname_getmapent_files(char *, char *, nvlist_t **);
     53 static int di_devname_get_mapinfo_files(char *, nvlist_t **);
     54 static int parse_mapinfo_file(FILE *, nvlist_t **);
     55 static FILE *open_local_map(char *);
     56 static void unquote(char *, char *);
     57 
     58 static int msglog = 1;
     59 typedef enum {
     60 	DBG_ERR = 1,
     61 	DBG_INFO,
     62 	DBG_STEP,
     63 	DBG_ALL
     64 } debug_level_t;
     65 static int devname_debug = 1;
     66 static void dprintf(debug_level_t, const char *, ...);
     67 
     68 extern int isspace(int);
     69 
     70 /* exported interfaces */
     71 void di_devname_print_mapinfo(nvlist_t *);
     72 int di_devname_get_mapinfo(char *, nvlist_t **);
     73 int di_devname_get_mapent(char *, char *, nvlist_t **);
     74 int di_devname_action_on_key(nvlist_t *, uint8_t, char *, void *);
     75 
     76 /*
     77  * Returns 0 and the valid maplist, otherwise errno.
     78  */
     79 int
     80 di_devname_get_mapinfo_files(char *mapname, nvlist_t **maplist)
     81 {
     82 	FILE *fp;
     83 	int rval = 0;
     84 	nvlist_t *nvl = NULL;
     85 
     86 	fp = open_local_map(mapname);
     87 	if (fp == NULL) {
     88 		dprintf(DBG_INFO, "di_devname_get_mapinfo_files: file %s does"
     89 		    "not exist\n", mapname);
     90 		return (ENOENT);
     91 	}
     92 
     93 	rval = parse_mapinfo_file(fp, &nvl);
     94 	if (rval == 0) {
     95 		*maplist = nvl;
     96 	}
     97 	(void) fclose(fp);
     98 
     99 	return (rval);
    100 }
    101 
    102 static FILE *
    103 open_local_map(char *mapname)
    104 {
    105 	char filename[LINEMAX];
    106 
    107 	if (*mapname != '/') {
    108 		(void) snprintf(filename, sizeof (filename), "/etc/dev/%s",
    109 		    mapname);
    110 	} else {
    111 		(void) snprintf(filename, sizeof (filename), "%s", mapname);
    112 	}
    113 
    114 	return (fopen(filename, "r"));
    115 }
    116 
    117 static void
    118 unquote(char *str, char *qbuf)
    119 {
    120 	register int escaped, inquote, quoted;
    121 	register char *ip, *bp, *qp;
    122 	char buf[LINEMAX];
    123 
    124 	escaped = inquote = quoted = 0;
    125 
    126 	for (ip = str, bp = buf, qp = qbuf; *ip; ip++) {
    127 		if (!escaped) {
    128 			if (*ip == '\\') {
    129 				escaped = 1;
    130 				quoted ++;
    131 				continue;
    132 			} else if (*ip == '"') {
    133 				inquote = !inquote;
    134 				quoted ++;
    135 				continue;
    136 			}
    137 		}
    138 
    139 		*bp++ = *ip;
    140 		*qp++ = (inquote || escaped) ? '^' : ' ';
    141 		escaped = 0;
    142 	}
    143 	*bp = '\0';
    144 	*qp = '\0';
    145 	if (quoted)
    146 		(void) strcpy(str, buf);
    147 }
    148 
    149 /*
    150  * gets the qualified characters in *p into w, which has space allocated
    151  * already
    152  */
    153 static int
    154 getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz)
    155 {
    156 	char *tmp = w;
    157 	char *tmpq = wq;
    158 	int count = wordsz;
    159 
    160 	if (wordsz <= 0) {
    161 		return (-1);
    162 	}
    163 
    164 	while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ') {
    165 		(*p)++;
    166 		(*pq)++;
    167 	}
    168 
    169 	while (**p &&
    170 		!((delim == ' ' ? isspace(**p) : **p == delim) &&
    171 			**pq == ' ')) {
    172 		if (--count <= 0) {
    173 			*tmp = '\0';
    174 			*tmpq = '\0';
    175 			dprintf(DBG_INFO, "maximum word length %d exceeded\n",
    176 				wordsz);
    177 			return (-1);
    178 		}
    179 		*w++ = *(*p)++;
    180 		*wq++ = *(*pq)++;
    181 	}
    182 	*w = '\0';
    183 	*wq = '\0';
    184 	return (0);
    185 }
    186 
    187 static int
    188 parse_mapinfo_file(FILE *fp, nvlist_t **ret_nvlp)
    189 {
    190 	int error = 0;
    191 	nvlist_t *nvl = NULL, *attrs = NULL;
    192 	char line[LINEMAX], lineq[LINEMAX];
    193 	char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1];
    194 	char *lp, *lq;
    195 
    196 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
    197 		return (EFAULT);
    198 	}
    199 
    200 	while (fgets(line, sizeof (line), fp)) {
    201 		char *name, *key, *val;
    202 
    203 		lp = (char *)line;
    204 		lq = (char *)lineq;
    205 		unquote(lp, lq);
    206 		if ((getword(word, wordq, &lp, &lq, ' ',
    207 		    sizeof (word)) == -1) || (word[0] == '\0'))
    208 			continue;
    209 
    210 		if (word[0] == '#')
    211 			continue;
    212 
    213 		name = strtok(line, SPC);
    214 		if (name == NULL)
    215 			continue;
    216 
    217 		(void) dprintf(DBG_INFO, "get a line for %s\n", name);
    218 		key = strtok(NULL, "=");
    219 		if (key == NULL) {
    220 			(void) dprintf(DBG_INFO, "no attributes specified for "
    221 			    "%s\n", name);
    222 			continue;
    223 		}
    224 
    225 		attrs = NULL;
    226 		if (nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0) != 0) {
    227 			error = EFAULT;
    228 			goto fail1;
    229 		}
    230 
    231 		while (key && *key) {
    232 			char *rest;
    233 			rest = strtok(NULL, "\n");
    234 			if (rest == NULL) {
    235 				(void) dprintf(DBG_INFO, "no value for key "
    236 				    "%s\n", key);
    237 				break;
    238 			}
    239 			if (rest[0] == ';') {
    240 				val = strdup("devname_null");
    241 				rest++;
    242 			} else {
    243 				val = strtok(rest, ";");
    244 				rest = strtok(NULL, "");
    245 			}
    246 			(void) dprintf(DBG_INFO, "parse_map_info: one entry "
    247 			    "key=%s val=%s\n", key, val);
    248 			if (nvlist_add_string(attrs, key, val) != 0) {
    249 				error = EFAULT;
    250 				goto fail;
    251 			}
    252 
    253 			key = strtok(rest, "=");
    254 		}
    255 		(void) dprintf(DBG_INFO, "parse_map_info: add entry name=%s\n",
    256 		    name);
    257 		if (nvlist_add_nvlist(nvl, name, attrs) != 0) {
    258 			error = EFAULT;
    259 			goto fail;
    260 		}
    261 	}
    262 
    263 done:
    264 	*ret_nvlp = nvl;
    265 	return (0);
    266 
    267 fail:
    268 	nvlist_free(attrs);
    269 fail1:
    270 	nvlist_free(nvl);
    271 	return (error);
    272 }
    273 
    274 void
    275 di_devname_print_mapinfo(nvlist_t *nvl)
    276 {
    277 	char *name, *key, *val;
    278 	nvlist_t *attrs;
    279 	nvpair_t *nvp, *kvp;
    280 
    281 	nvp = nvlist_next_nvpair(nvl, NULL);
    282 	while (nvp) {
    283 		name = nvpair_name(nvp);
    284 		(void) nvpair_value_nvlist(nvp, &attrs);
    285 		(void) printf("name = %s, binding attributes:\n", name);
    286 		kvp = nvlist_next_nvpair(attrs, NULL);
    287 		while (kvp) {
    288 			key = nvpair_name(kvp);
    289 			(void) nvpair_value_string(kvp, &val);
    290 			(void) printf("\t%s = %s\n", key, val);
    291 			kvp = nvlist_next_nvpair(attrs, kvp);
    292 		}
    293 		nvp = nvlist_next_nvpair(nvl, nvp);
    294 	}
    295 }
    296 
    297 static int
    298 action_mklink(char *target, char *source)
    299 {
    300 	(void) dprintf(DBG_INFO, "mklink for source %s target %s\n",
    301 	    source, target);
    302 	return (symlink(source, target));
    303 }
    304 
    305 static struct actions {
    306 	char *key;
    307 	devname_spec_t spec;
    308 	int (*action)(char *, char *);
    309 } actions[] = {
    310 	{"devices-path", DEVNAME_NS_PATH, action_mklink},
    311 	{"dev-path", DEVNAME_NS_DEV, action_mklink},
    312 	{NULL, DEVNAME_NS_NONE, NULL}
    313 };
    314 
    315 static int
    316 action_on_key(uint_t cmd, char *dir_name, char *devname, nvpair_t *attr,
    317     uint32_t  *nsmapcount, char **devfsadm_link, devname_spec_t *devfsadm_spec)
    318 {
    319 	int i = 0;
    320 	int error = 0;
    321 	char *attrname, *attrval;
    322 	int len = 0;
    323 	char *path = NULL;
    324 
    325 	attrname = nvpair_name(attr);
    326 	(void) nvpair_value_string(attr, &attrval);
    327 	(void) dprintf(DBG_INFO, "key = %s; value = %s\n", attrname, attrval);
    328 
    329 	while (actions[i].key) {
    330 		if (strcmp(actions[i].key, attrname) == 0) {
    331 			switch (cmd) {
    332 			case DEVFSADMD_NS_READDIR:
    333 				len = strlen(dir_name) + strlen(devname) + 2;
    334 				path = malloc(len);
    335 				(void) snprintf(path, len, "%s/%s", dir_name,
    336 				    devname);
    337 				error = actions[i].action(path, attrval);
    338 				free(path);
    339 				if (error) {
    340 					(void) dprintf(DBG_INFO, "action "
    341 					    "failed %d\n", error);
    342 					return (error);
    343 				} else {
    344 					(*nsmapcount)++;
    345 					(void) dprintf(DBG_INFO,
    346 					    "mapcount %d\n", *nsmapcount);
    347 				}
    348 				break;
    349 			case DEVFSADMD_NS_LOOKUP:
    350 				*devfsadm_link = strdup(attrval);
    351 				*devfsadm_spec = actions[i].spec;
    352 				break;
    353 			default:
    354 				break;
    355 			}
    356 		}
    357 		i++;
    358 	}
    359 	return (0);
    360 }
    361 
    362 int
    363 di_devname_action_on_key(nvlist_t *map, uint8_t cmd, char *dir_name, void *hdl)
    364 {
    365 	char *name = NULL;
    366 	nvpair_t *entry;
    367 	nvlist_t *attrs;
    368 	int32_t error = 0;
    369 	uint32_t ns_mapcount = 0;
    370 	char *devfsadm_link = NULL;
    371 	devname_spec_t devfsadm_spec = DEVNAME_NS_NONE;
    372 	sdev_door_res_t *resp;
    373 
    374 	entry = nvlist_next_nvpair(map, NULL);
    375 	while (entry) {
    376 		nvpair_t *attr;
    377 		name = nvpair_name(entry);
    378 		(void) dprintf(DBG_INFO, "di_devname_action_on_key: name %s\n",
    379 		    name);
    380 		(void) nvpair_value_nvlist(entry, &attrs);
    381 
    382 		attr = nvlist_next_nvpair(attrs, NULL);
    383 		while (attr) {
    384 			error = action_on_key(cmd, dir_name, name, attr,
    385 			    &ns_mapcount, &devfsadm_link, &devfsadm_spec);
    386 
    387 			/* do not continue if encountered the first error */
    388 			if (error) {
    389 				(void) dprintf(DBG_INFO, "error %d\n", error);
    390 				return ((int32_t)error);
    391 			}
    392 			attr = nvlist_next_nvpair(attrs, attr);
    393 		}
    394 		entry = nvlist_next_nvpair(map, entry);
    395 	}
    396 
    397 	resp = (sdev_door_res_t *)hdl;
    398 	(void) dprintf(DBG_INFO, "cmd is %d\n", cmd);
    399 	switch (cmd) {
    400 	case DEVFSADMD_NS_READDIR:
    401 		resp->ns_rdr_hdl.ns_mapcount = (uint32_t)ns_mapcount;
    402 		(void) dprintf(DBG_INFO, "mapcount is %d\n", ns_mapcount);
    403 		break;
    404 	case DEVFSADMD_NS_LOOKUP:
    405 		if (devfsadm_link && devfsadm_spec != DEVNAME_NS_NONE) {
    406 			(void) dprintf(DBG_INFO, "devfsadm_link is %s\n",
    407 			    devfsadm_link);
    408 			(void) snprintf(resp->ns_lkp_hdl.devfsadm_link,
    409 			    strlen(devfsadm_link) + 1, "%s", devfsadm_link);
    410 			resp->ns_lkp_hdl.devfsadm_spec = devfsadm_spec;
    411 		} else {
    412 			(void) dprintf(DBG_INFO, "error out\n");
    413 			return (1);
    414 		}
    415 		break;
    416 	default:
    417 		(void) dprintf(DBG_INFO, "error NOTSUP out\n");
    418 		return (ENOTSUP);
    419 	}
    420 
    421 	return (0);
    422 }
    423 
    424 
    425 static nvlist_t *
    426 getent_mapinfo_file(FILE *fp, char *match)
    427 {
    428 	nvlist_t *nvl, *attrs;
    429 	char line[LINEMAX], lineq[LINEMAX];
    430 	char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1];
    431 	int count = 0;
    432 	char *lp, *lq;
    433 
    434 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
    435 		return (NULL);
    436 
    437 	while (fgets(line, sizeof (line), fp)) {
    438 		char *name, *key, *val;
    439 
    440 		if (line[0] == '#')
    441 			continue;
    442 
    443 		dprintf(DBG_INFO, "getent_mapinfo_file: get a line %s\n", line);
    444 		lp = (char *)line;
    445 		lq = (char *)lineq;
    446 		unquote(lp, lq);
    447 		if ((getword(word, wordq, &lp, &lq, ' ', sizeof (word))
    448 			== -1) || (word[0] == '\0'))
    449 			continue;
    450 
    451 		name = strtok(line, SPC);
    452 		if (name == NULL)
    453 			continue;
    454 
    455 		dprintf(DBG_INFO, "macthing with the key %s match %s\n",
    456 		    name, match);
    457 		/* bypass the non-related entries */
    458 		if (strcmp(name, match) != 0)
    459 			continue;
    460 
    461 		/* get a matched entry */
    462 		key = strtok(NULL, "=");
    463 		if (key == NULL) {
    464 			(void) dprintf(DBG_INFO, "no attributes specified "
    465 			    "for %s\n", name);
    466 			goto fail1;
    467 		}
    468 
    469 		attrs = NULL;
    470 		if (nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0) != 0)
    471 			goto fail1;
    472 		while (key && *key) {
    473 			char *rest;
    474 			rest = strtok(NULL, "\n");
    475 			if (rest == NULL) {
    476 				(void) dprintf(DBG_INFO, "no value for key "
    477 				    "%s\n", key);
    478 				goto fail;
    479 			}
    480 			if (rest[0] == ';') {
    481 				val = strdup("devname_null");
    482 				rest++;
    483 			} else {
    484 				val = strtok(rest, ";");
    485 				rest = strtok(NULL, "");
    486 			}
    487 			(void) dprintf(DBG_INFO, "found entry %s %s for %s\n",
    488 			    key, val, name);
    489 			if (nvlist_add_string(attrs, key, val) != 0)
    490 				goto fail;
    491 
    492 			key = strtok(rest, "=");
    493 		}
    494 		(void) dprintf(DBG_INFO, "adding nvlist for %s\n", name);
    495 		if (nvlist_add_nvlist(nvl, name, attrs) != 0)
    496 			goto fail;
    497 		count++;
    498 		break;
    499 	}
    500 
    501 	if (count == 0)
    502 		goto fail1;
    503 
    504 	return (nvl);
    505 
    506 fail:
    507 	nvlist_free(attrs);
    508 fail1:
    509 	nvlist_free(nvl);
    510 	errno = EFAULT;
    511 	return (NULL);
    512 }
    513 
    514 static int
    515 di_devname_getmapent_files(char *key, char *mapname, nvlist_t **map)
    516 {
    517 	FILE *fp;
    518 	int rval = 0;
    519 	nvlist_t *nvl = NULL;
    520 
    521 	fp = open_local_map(mapname);
    522 	if (fp == NULL)
    523 		return (1);
    524 
    525 	nvl = getent_mapinfo_file(fp, key);
    526 	if (nvl != NULL) {
    527 		*map = nvl;
    528 	} else {
    529 		rval = errno;
    530 	}
    531 	(void) fclose(fp);
    532 
    533 	return (rval);
    534 }
    535 
    536 int
    537 di_devname_get_mapent(char *key, char *mapname, nvlist_t **map)
    538 {
    539 	dprintf(DBG_INFO, "di_devname_get_mapent: called for %s in %s\n",
    540 	    key, mapname);
    541 
    542 	return (di_devname_getmapent_files(key, mapname, map));
    543 
    544 }
    545 
    546 int
    547 di_devname_get_mapinfo(char *mapname, nvlist_t **maps)
    548 {
    549 	dprintf(DBG_INFO, "di_devname_get_mapinfo: called for %s\n", mapname);
    550 
    551 	return (di_devname_get_mapinfo_files(mapname, maps));
    552 }
    553 
    554 static void
    555 debug_print(debug_level_t msglevel, const char *fmt, va_list ap)
    556 {
    557 	if (devname_debug < msglevel)
    558 		return;
    559 
    560 	/* Print a distinctive label for error msgs */
    561 	if (msglevel == DBG_ERR) {
    562 		(void) fprintf(stderr, "[ERROR]: ");
    563 	}
    564 
    565 	if (msglog == TRUE) {
    566 		(void) vsyslog(LOG_NOTICE, fmt, ap);
    567 	} else {
    568 		(void) vfprintf(stderr, fmt, ap);
    569 	}
    570 }
    571 
    572 /* ARGSUSED */
    573 /* PRINTFLIKE2 */
    574 static void
    575 dprintf(debug_level_t msglevel, const char *fmt, ...)
    576 {
    577 	va_list ap;
    578 
    579 	assert(msglevel > 0);
    580 
    581 	if (!devname_debug)
    582 		return;
    583 
    584 	va_start(ap, fmt);
    585 	debug_print(msglevel, fmt, ap);
    586 	va_end(ap);
    587 }
    588 
    589 
    590 /*
    591  * Private interfaces for non-global /dev profile
    592  */
    593 
    594 /*
    595  * Allocate opaque data structure for passing profile to the kernel for
    596  * the given mount point.
    597  *
    598  * Note that this interface returns an empty, initialized, profile.
    599  * It does not return what may have been previously committed.
    600  */
    601 int
    602 di_prof_init(const char *mountpt, di_prof_t *profp)
    603 {
    604 	nvlist_t	*nvl;
    605 
    606 	if (nvlist_alloc(&nvl, 0, 0))
    607 		return (-1);
    608 
    609 	if (nvlist_add_string(nvl, SDEV_NVNAME_MOUNTPT, mountpt)) {
    610 		nvlist_free(nvl);
    611 		return (-1);
    612 	}
    613 
    614 	*profp = (di_prof_t)nvl;
    615 	return (0);
    616 }
    617 
    618 /*
    619  * Free space allocated by di_prof_init().
    620  */
    621 void
    622 di_prof_fini(di_prof_t prof)
    623 {
    624 	nvlist_free((nvlist_t *)prof);
    625 }
    626 
    627 /*
    628  * Sends profile to the kernel.
    629  */
    630 int
    631 di_prof_commit(di_prof_t prof)
    632 {
    633 	char	*buf = NULL;
    634 	size_t	buflen = 0;
    635 	int	rv;
    636 
    637 	if (nvlist_pack((nvlist_t *)prof, &buf, &buflen, NV_ENCODE_NATIVE, 0))
    638 		return (-1);
    639 	rv = modctl(MODDEVNAME, MODDEVNAME_PROFILE, buf, buflen);
    640 	free(buf);
    641 	return (rv);
    642 }
    643 
    644 /*
    645  * Add a device or directory to profile's include list.
    646  *
    647  * Note that there is no arbitration between conflicting
    648  * include and exclude profile entries, most recent
    649  * is the winner.
    650  */
    651 int
    652 di_prof_add_dev(di_prof_t prof, const char *dev)
    653 {
    654 	if (nvlist_add_string((nvlist_t *)prof, SDEV_NVNAME_INCLUDE, dev))
    655 		return (-1);
    656 	return (0);
    657 }
    658 
    659 /*
    660  * Add a device or directory to profile's exclude list.
    661  * This can effectively remove a previously committed device.
    662  */
    663 int
    664 di_prof_add_exclude(di_prof_t prof, const char *dev)
    665 {
    666 	if (nvlist_add_string((nvlist_t *)prof, SDEV_NVNAME_EXCLUDE, dev))
    667 		return (-1);
    668 	return (0);
    669 }
    670 
    671 /*
    672  * Add a symlink to profile.
    673  */
    674 int
    675 di_prof_add_symlink(di_prof_t prof, const char *linkname, const char *target)
    676 {
    677 	nvlist_t	*nvl = (nvlist_t *)prof;
    678 	char		*syml[2];
    679 
    680 	syml[0] = (char *)linkname;	/* 1st entry must be the symlink */
    681 	syml[1] = (char *)target;	/* 2nd entry must be the target */
    682 	if (nvlist_add_string_array(nvl, SDEV_NVNAME_SYMLINK, syml, 2))
    683 		return (-1);
    684 	return (0);
    685 }
    686 
    687 /*
    688  * Add a name mapping to profile.
    689  */
    690 int
    691 di_prof_add_map(di_prof_t prof, const char *source, const char *target)
    692 {
    693 	nvlist_t	*nvl = (nvlist_t *)prof;
    694 	char		*map[2];
    695 
    696 	map[0] = (char *)source;	/* 1st entry must be the source */
    697 	map[1] = (char *)target;	/* 2nd entry must be the target */
    698 	if (nvlist_add_string_array(nvl, SDEV_NVNAME_MAP, map, 2))
    699 		return (-1);
    700 	return (0);
    701 }
    702