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