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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright (c) 2016 by Delphix. All rights reserved. 25 */ 26 27 #include "libdevinfo.h" 28 #include "devinfo_devlink.h" 29 #include "device_info.h" 30 31 #undef DEBUG 32 #ifndef DEBUG 33 #define NDEBUG 1 34 #else 35 #undef NDEBUG 36 #endif 37 38 #include <assert.h> 39 40 static mutex_t update_mutex = DEFAULTMUTEX; /* Protects update record lock */ 41 static mutex_t temp_file_mutex = DEFAULTMUTEX; /* for file creation tests */ 42 43 static const size_t elem_sizes[DB_TYPES] = { 44 sizeof (struct db_node), 45 sizeof (struct db_minor), 46 sizeof (struct db_link), 47 sizeof (char) 48 }; 49 50 /* 51 * List of directories/files skipped while physically walking /dev 52 * Paths are relative to "<root>/dev/" 53 */ 54 static const char *skip_dirs[] = {"fd"}; 55 static const char *skip_files[] = { 56 "stdout", 57 "stdin", 58 "stderr" 59 }; 60 61 #define N_SKIP_DIRS (sizeof (skip_dirs) / sizeof (skip_dirs[0])) 62 #define N_SKIP_FILES (sizeof (skip_files) / sizeof (skip_files[0])) 63 64 #define DI_TEST_DB ETCDEV "di_test_db" 65 66 /* 67 * 68 * This file contains two sets of interfaces which operate on the reverse 69 * links database. One set (which includes di_devlink_open()/_close()) 70 * allows link generators like devfsadm(8) and ucblinks(1B) (writers) to 71 * populate the database with /devices -> /dev mappings. Another set 72 * of interfaces (which includes di_devlink_init()/_fini()) allows 73 * applications (readers) to lookup the database for /dev links corresponding 74 * to a given minor. 75 * 76 * Writers operate on a cached version of the database. The cache is created 77 * when di_devlink_open() is called. As links in /dev are created and removed, 78 * the cache is updated to keep it in synch with /dev. When the /dev updates 79 * are complete, the link generator calls di_devlink_close() which writes 80 * out the cache to the database. 81 * 82 * Applications which need to lookup the database, call di_devlink_init(). 83 * di_devlink_init() checks the database file (if one exists). If the 84 * database is valid, it is mapped into the address space of the 85 * application. The database file consists of several segments. Each 86 * segment can be mapped in independently and is mapped on demand. 87 * 88 * Database Layout 89 * 90 * --------------------- 91 * | Magic # | 92 * | ----------------- | 93 * | Version | HEADER 94 * | ----------------- | 95 * | ... | 96 * --------------------- 97 * | | 98 * | | NODES 99 * | | 100 * | | 101 * --------------------- 102 * | | 103 * | | MINORS 104 * | | 105 * | | 106 * --------------------- 107 * | | 108 * | | LINKS 109 * | | 110 * | | 111 * --------------------- 112 * | | 113 * | | STRINGS 114 * | | 115 * | | 116 * --------------------- 117 * 118 * Readers can lookup /dev links for a specific minor or 119 * lookup all /dev links. In the latter case, the node 120 * and minor segments are not mapped in and the reader 121 * walks through every link in the link segment. 122 * 123 */ 124 di_devlink_handle_t 125 di_devlink_open(const char *root_dir, uint_t flags) 126 { 127 int err; 128 char path[PATH_MAX]; 129 struct di_devlink_handle *hdp; 130 int retried = 0; 131 132 retry: 133 /* 134 * Allocate a read-write handle but open the DB in readonly 135 * mode. We do writes only to a temporary copy of the database. 136 */ 137 if ((hdp = handle_alloc(root_dir, OPEN_RDWR)) == NULL) { 138 return (NULL); 139 } 140 141 err = open_db(hdp, OPEN_RDONLY); 142 143 /* 144 * We don't want to unlink the db at this point - if we did we 145 * would be creating a window where consumers would take a slow 146 * code path (and those consumers might also trigger requests for 147 * db creation, which we are already in the process of doing). 148 * When we are done with our update, we use rename to install the 149 * latest version of the db file. 150 */ 151 get_db_path(hdp, DB_FILE, path, sizeof (path)); 152 153 /* 154 * The flags argument is reserved for future use. 155 */ 156 if (flags != 0) { 157 handle_free(&hdp); /* also closes the DB */ 158 errno = EINVAL; 159 return (NULL); 160 } 161 162 if (cache_alloc(hdp) != 0) { 163 handle_free(&hdp); 164 return (NULL); 165 } 166 167 if (err) { 168 /* 169 * Failed to open DB. 170 * The most likely cause is that DB file did not exist. 171 * Call di_devlink_close() to recreate the DB file and 172 * retry di_devlink_open(). 173 */ 174 if (retried == 0) { 175 (void) di_devlink_close(&hdp, 0); 176 retried = 1; 177 goto retry; 178 } 179 180 /* 181 * DB cannot be opened, just return the 182 * handle. We will recreate the DB later. 183 */ 184 return (hdp); 185 } 186 187 /* Read the database into the cache */ 188 CACHE(hdp)->update_count = DB_HDR(hdp)->update_count; 189 (void) read_nodes(hdp, NULL, DB_HDR(hdp)->root_idx); 190 (void) read_links(hdp, NULL, DB_HDR(hdp)->dngl_idx); 191 192 (void) close_db(hdp); 193 194 return (hdp); 195 } 196 197 static void 198 get_db_path( 199 struct di_devlink_handle *hdp, 200 const char *fname, 201 char *buf, 202 size_t blen) 203 { 204 char *dir = NULL; 205 206 #ifdef DEBUG 207 if (dir = getenv(ALT_DB_DIR)) { 208 (void) devlink_dprintf(DBG_INFO, 209 "get_db_path: alternate db dir: %s\n", dir); 210 } 211 #endif 212 if (dir == NULL) { 213 dir = hdp->db_dir; 214 } 215 216 (void) snprintf(buf, blen, "%s/%s", dir, fname); 217 } 218 219 static int 220 open_db(struct di_devlink_handle *hdp, int flags) 221 { 222 size_t sz; 223 long page_sz; 224 int fd, rv, flg; 225 struct stat sbuf; 226 uint32_t count[DB_TYPES] = {0}; 227 char path[PATH_MAX]; 228 void *cp; 229 230 assert(!DB_OPEN(hdp)); 231 232 #ifdef DEBUG 233 if (getenv(SKIP_DB)) { 234 (void) devlink_dprintf(DBG_INFO, 235 "open_db: skipping database\n"); 236 return (-1); 237 } 238 #endif 239 if ((page_sz = sysconf(_SC_PAGE_SIZE)) == -1) { 240 return (-1); 241 } 242 243 /* 244 * Use O_TRUNC flag for write access, so that the subsequent ftruncate() 245 * call will zero-fill the entire file 246 */ 247 if (IS_RDONLY(flags)) { 248 flg = O_RDONLY; 249 get_db_path(hdp, DB_FILE, path, sizeof (path)); 250 } else { 251 flg = O_RDWR|O_CREAT|O_TRUNC; 252 get_db_path(hdp, DB_TMP, path, sizeof (path)); 253 } 254 255 /* 256 * Avoid triggering /dev reconfigure for read when not present 257 */ 258 if (IS_RDONLY(flags) && 259 (strncmp(path, "/dev/", 5) == 0) && !device_exists(path)) { 260 return (-1); 261 } 262 263 if ((fd = open(path, flg, DB_PERMS)) == -1) { 264 return (-1); 265 } 266 267 if (IS_RDONLY(flags)) { 268 flg = PROT_READ; 269 rv = fstat(fd, &sbuf); 270 sz = sbuf.st_size; 271 } else { 272 flg = PROT_READ | PROT_WRITE; 273 sz = size_db(hdp, page_sz, count); 274 rv = ftruncate(fd, sz); 275 } 276 277 if (rv == -1 || sz < HDR_LEN) { 278 if (rv != -1) 279 errno = EINVAL; 280 (void) close(fd); 281 return (-1); 282 } 283 284 cp = mmap(0, HDR_LEN, flg, MAP_SHARED, fd, 0); 285 if (cp == MAP_FAILED) { 286 (void) close(fd); 287 return (-1); 288 } 289 DB(hdp)->hdr = (struct db_hdr *)cp; 290 DB(hdp)->db_fd = fd; 291 DB(hdp)->flags = flags; 292 293 if (IS_RDONLY(flags)) { 294 rv = invalid_db(hdp, sz, page_sz); 295 } else { 296 rv = init_hdr(hdp, page_sz, count); 297 } 298 299 if (rv) { 300 (void) devlink_dprintf(DBG_ERR, "open_db: invalid DB(%s)\n", 301 path); 302 (void) close_db(hdp); 303 return (-1); 304 } else { 305 (void) devlink_dprintf(DBG_STEP, "open_db: DB(%s): opened\n", 306 path); 307 return (0); 308 } 309 } 310 311 /* 312 * A handle can be allocated for read-only or read-write access 313 */ 314 static struct di_devlink_handle * 315 handle_alloc(const char *root_dir, uint_t flags) 316 { 317 char dev_dir[PATH_MAX], path[PATH_MAX], db_dir[PATH_MAX]; 318 struct di_devlink_handle *hdp, proto = {0}; 319 int install = 0; 320 int isroot = 0; 321 struct stat sb; 322 char can_path[PATH_MAX]; 323 324 assert(flags == OPEN_RDWR || flags == OPEN_RDONLY); 325 326 dev_dir[0] = '\0'; 327 db_dir[0] = '\0'; 328 329 /* 330 * NULL and the empty string are equivalent to "/" 331 */ 332 if (root_dir && root_dir[0] != '\0') { 333 334 if (root_dir[0] != '/') { 335 errno = EINVAL; 336 return (NULL); 337 } 338 339 #ifdef DEBUG 340 /*LINTED*/ 341 assert(sizeof (dev_dir) >= PATH_MAX); 342 #endif 343 if ((realpath(root_dir, dev_dir) == NULL) || 344 (realpath(root_dir, db_dir) == NULL)) { 345 return (NULL); 346 } 347 } else { 348 /* 349 * The dev dir is at /dev i.e. we are not doing a -r /altroot 350 */ 351 isroot = 1; 352 } 353 354 if (strcmp(dev_dir, "/") == 0) { 355 dev_dir[0] = 0; 356 db_dir[0] = 0; 357 } else { 358 (void) strlcpy(db_dir, dev_dir, sizeof (db_dir)); 359 } 360 361 (void) strlcat(dev_dir, DEV, sizeof (dev_dir)); 362 (void) strlcat(db_dir, ETCDEV, sizeof (db_dir)); 363 364 /* 365 * The following code is for install. Readers and writers need 366 * to be redirected to /tmp/etc/dev for the database file. 367 * Note that we test for readonly /etc by actually creating a 368 * file since statvfs is not a reliable method for determining 369 * readonly filesystems. 370 */ 371 install = 0; 372 (void) snprintf(can_path, sizeof (can_path), "%s/%s", ETCDEV, DB_FILE); 373 if (flags == OPEN_RDWR && isroot) { 374 char di_test_db[PATH_MAX]; 375 int fd; 376 (void) mutex_lock(&temp_file_mutex); 377 (void) snprintf(di_test_db, sizeof (di_test_db), "%s.%d", 378 DI_TEST_DB, getpid()); 379 fd = open(di_test_db, O_CREAT|O_RDWR|O_EXCL, 0644); 380 if (fd == -1 && errno == EROFS && stat(can_path, &sb) == -1) 381 install = 1; 382 if (fd != -1) { 383 (void) close(fd); 384 (void) unlink(di_test_db); 385 } 386 (void) mutex_unlock(&temp_file_mutex); 387 } else if (isroot) { 388 /* 389 * Readers can be non-privileged so we cannot test by creating 390 * a file in /etc/dev. Instead we check if the database 391 * file is missing in /etc/dev and is present in /tmp/etc/dev 392 * and is owned by root. 393 */ 394 char install_path[PATH_MAX]; 395 396 (void) snprintf(install_path, sizeof (install_path), 397 "/tmp%s/%s", ETCDEV, DB_FILE); 398 if (stat(can_path, &sb) == -1 && stat(install_path, &sb) 399 != -1 && sb.st_uid == 0) { 400 install = 1; 401 } 402 } 403 404 /* 405 * Check if we are in install. If we are, the database will be in 406 * /tmp/etc/dev 407 */ 408 if (install) 409 (void) snprintf(db_dir, sizeof (db_dir), "/tmp%s", ETCDEV); 410 411 proto.dev_dir = dev_dir; 412 proto.db_dir = db_dir; 413 proto.flags = flags; 414 proto.lock_fd = -1; 415 416 /* 417 * Lock database if a read-write handle is being allocated. 418 * Locks are needed to protect against multiple writers. 419 * Readers don't need locks. 420 */ 421 if (HDL_RDWR(&proto)) { 422 if (enter_db_lock(&proto, root_dir) != 1) { 423 return (NULL); 424 } 425 } 426 427 DB(&proto)->db_fd = -1; 428 429 hdp = calloc(1, sizeof (struct di_devlink_handle)); 430 if (hdp == NULL) { 431 goto error; 432 } 433 434 *hdp = proto; 435 436 /* 437 * The handle hdp now contains a pointer to local storage 438 * in the dev_dir field (obtained from the proto handle). 439 * In the following line, a dynamically allocated version 440 * is substituted. 441 */ 442 443 if ((hdp->dev_dir = strdup(proto.dev_dir)) == NULL) { 444 free(hdp); 445 goto error; 446 } 447 448 if ((hdp->db_dir = strdup(proto.db_dir)) == NULL) { 449 free(hdp->dev_dir); 450 free(hdp); 451 goto error; 452 } 453 454 return (hdp); 455 456 error: 457 if (HDL_RDWR(&proto)) { 458 /* Unlink DB file on error */ 459 get_db_path(&proto, DB_FILE, path, sizeof (path)); 460 (void) unlink(path); 461 exit_db_lock(&proto); 462 } 463 return (NULL); 464 } 465 466 467 static int 468 cache_alloc(struct di_devlink_handle *hdp) 469 { 470 size_t hash_sz = 0; 471 472 assert(HDL_RDWR(hdp)); 473 474 if (DB_OPEN(hdp)) { 475 hash_sz = DB_NUM(hdp, DB_LINK) / AVG_CHAIN_SIZE; 476 } 477 hash_sz = (hash_sz >= MIN_HASH_SIZE) ? hash_sz : MIN_HASH_SIZE; 478 479 CACHE(hdp)->hash = calloc(hash_sz, sizeof (cache_link_t *)); 480 if (CACHE(hdp)->hash == NULL) { 481 return (-1); 482 } 483 CACHE(hdp)->hash_sz = hash_sz; 484 485 return (0); 486 } 487 488 489 static int 490 invalid_db(struct di_devlink_handle *hdp, size_t fsize, long page_sz) 491 { 492 int i; 493 char *cp; 494 size_t sz; 495 496 if (DB_HDR(hdp)->magic != DB_MAGIC || DB_HDR(hdp)->vers != DB_VERSION) { 497 return (1); 498 } 499 500 if (DB_HDR(hdp)->page_sz == 0 || DB_HDR(hdp)->page_sz != page_sz) { 501 return (1); 502 } 503 504 sz = seg_size(hdp, DB_HEADER); 505 for (i = 0; i < DB_TYPES; i++) { 506 (void) devlink_dprintf(DBG_INFO, "N[%u] = %u\n", i, 507 DB_NUM(hdp, i)); 508 /* There must be at least 1 element of each type */ 509 if (DB_NUM(hdp, i) < 1) { 510 return (1); 511 } 512 sz += seg_size(hdp, i); 513 assert(sz % page_sz == 0); 514 } 515 516 if (sz != fsize) { 517 return (1); 518 } 519 520 if (!VALID_INDEX(hdp, DB_NODE, DB_HDR(hdp)->root_idx)) { 521 return (1); 522 } 523 524 if (!VALID_INDEX(hdp, DB_LINK, DB_HDR(hdp)->dngl_idx)) { 525 return (1); 526 } 527 528 if (DB_EMPTY(hdp)) { 529 return (1); 530 } 531 532 /* 533 * The last character in the string segment must be a NUL char. 534 */ 535 cp = get_string(hdp, DB_NUM(hdp, DB_STR) - 1); 536 if (cp == NULL || *cp != '\0') { 537 return (1); 538 } 539 540 return (0); 541 } 542 543 static int 544 read_nodes(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx) 545 { 546 char *path; 547 cache_node_t *cnp; 548 struct db_node *dnp; 549 const char *fcn = "read_nodes"; 550 551 assert(HDL_RDWR(hdp)); 552 553 /* 554 * parent node should be NULL only for the root node 555 */ 556 if ((pcnp == NULL) ^ (nidx == DB_HDR(hdp)->root_idx)) { 557 (void) devlink_dprintf(DBG_ERR, 558 "%s: invalid parent or index(%u)\n", fcn, nidx); 559 SET_DB_ERR(hdp); 560 return (-1); 561 } 562 563 for (; dnp = get_node(hdp, nidx); nidx = dnp->sib) { 564 565 path = get_string(hdp, dnp->path); 566 567 /* 568 * Insert at head of list to recreate original order 569 */ 570 cnp = node_insert(hdp, pcnp, path, INSERT_HEAD); 571 if (cnp == NULL) { 572 SET_DB_ERR(hdp); 573 break; 574 } 575 576 assert(strcmp(path, "/") ^ (nidx == DB_HDR(hdp)->root_idx)); 577 assert(strcmp(path, "/") != 0 || dnp->sib == DB_NIL); 578 579 if (read_minors(hdp, cnp, dnp->minor) != 0 || 580 read_nodes(hdp, cnp, dnp->child) != 0) { 581 break; 582 } 583 584 (void) devlink_dprintf(DBG_STEP, "%s: node[%u]: %s\n", 585 fcn, nidx, cnp->path); 586 } 587 588 return (dnp ? -1 : 0); 589 } 590 591 static int 592 read_minors(struct di_devlink_handle *hdp, cache_node_t *pcnp, uint32_t nidx) 593 { 594 cache_minor_t *cmnp; 595 struct db_minor *dmp; 596 char *name, *nodetype; 597 const char *fcn = "read_minors"; 598 599 assert(HDL_RDWR(hdp)); 600 601 if (pcnp == NULL) { 602 (void) devlink_dprintf(DBG_ERR, "%s: minor[%u]: orphan minor\n", 603 fcn, nidx); 604 SET_DB_ERR(hdp); 605 return (-1); 606 } 607 608 for (; dmp = get_minor(hdp, nidx); nidx = dmp->sib) { 609 610 name = get_string(hdp, dmp->name); 611 nodetype = get_string(hdp, dmp->nodetype); 612 613 cmnp = minor_insert(hdp, pcnp, name, nodetype, NULL); 614 if (cmnp == NULL) { 615 SET_DB_ERR(hdp); 616 break; 617 } 618 619 (void) devlink_dprintf(DBG_STEP, "%s: minor[%u]: %s\n", 620 fcn, nidx, cmnp->name); 621 622 if (read_links(hdp, cmnp, dmp->link) != 0) { 623 break; 624 } 625 } 626 627 return (dmp ? -1 : 0); 628 } 629 630 /* 631 * If the link is dangling the corresponding minor will be absent. 632 */ 633 static int 634 read_links(struct di_devlink_handle *hdp, cache_minor_t *pcmp, uint32_t nidx) 635 { 636 cache_link_t *clp; 637 struct db_link *dlp; 638 char *path, *content; 639 640 assert(HDL_RDWR(hdp)); 641 642 if (nidx != DB_NIL && 643 ((pcmp == NULL) ^ (nidx == DB_HDR(hdp)->dngl_idx))) { 644 (void) devlink_dprintf(DBG_ERR, "read_links: invalid minor or" 645 " index(%u)\n", nidx); 646 SET_DB_ERR(hdp); 647 return (-1); 648 } 649 650 for (; dlp = get_link(hdp, nidx); nidx = dlp->sib) { 651 652 path = get_string(hdp, dlp->path); 653 content = get_string(hdp, dlp->content); 654 655 clp = link_insert(hdp, pcmp, path, content, dlp->attr); 656 if (clp == NULL) { 657 SET_DB_ERR(hdp); 658 break; 659 } 660 661 (void) devlink_dprintf(DBG_STEP, "read_links: link[%u]: %s%s\n", 662 nidx, clp->path, pcmp == NULL ? "(DANGLING)" : ""); 663 } 664 665 return (dlp ? -1 : 0); 666 } 667 668 int 669 di_devlink_close(di_devlink_handle_t *pp, int flag) 670 { 671 int i, rv; 672 char tmp[PATH_MAX]; 673 char file[PATH_MAX]; 674 uint32_t next[DB_TYPES] = {0}; 675 struct di_devlink_handle *hdp; 676 677 if (pp == NULL || *pp == NULL || !HDL_RDWR(*pp)) { 678 errno = EINVAL; 679 return (-1); 680 } 681 682 hdp = *pp; 683 *pp = NULL; 684 685 /* 686 * The caller encountered some error in their processing. 687 * so handle isn't valid. Discard it and return success. 688 */ 689 if (flag == DI_LINK_ERROR) { 690 handle_free(&hdp); 691 return (0); 692 } 693 694 if (DB_ERR(hdp)) { 695 handle_free(&hdp); 696 errno = EINVAL; 697 return (-1); 698 } 699 700 /* 701 * Extract the DB path before the handle is freed. 702 */ 703 get_db_path(hdp, DB_FILE, file, sizeof (file)); 704 get_db_path(hdp, DB_TMP, tmp, sizeof (tmp)); 705 706 /* 707 * update database with actual contents of /dev 708 */ 709 (void) devlink_dprintf(DBG_INFO, 710 "di_devlink_close: update_count = %u\n", CACHE(hdp)->update_count); 711 712 /* 713 * For performance reasons, synchronization of the database 714 * with /dev is turned off by default. However, applications 715 * with appropriate permissions can request a "sync" by 716 * calling di_devlink_update(). 717 */ 718 if (CACHE(hdp)->update_count == 0) { 719 CACHE(hdp)->update_count = 1; 720 (void) devlink_dprintf(DBG_INFO, 721 "di_devlink_close: synchronizing DB\n"); 722 (void) synchronize_db(hdp); 723 } 724 725 /* 726 * Resolve dangling links AFTER synchronizing DB with /dev as the 727 * synchronization process may create dangling links. 728 */ 729 resolve_dangling_links(hdp); 730 731 /* 732 * All changes to the cache are complete. Write out the cache 733 * to the database only if it is not empty. 734 */ 735 if (CACHE_EMPTY(hdp)) { 736 (void) devlink_dprintf(DBG_INFO, 737 "di_devlink_close: skipping write\n"); 738 (void) unlink(file); 739 handle_free(&hdp); 740 return (0); 741 } 742 743 if (open_db(hdp, OPEN_RDWR) != 0) { 744 handle_free(&hdp); 745 return (-1); 746 } 747 748 /* 749 * Keep track of array assignments. There is at least 750 * 1 element (the "NIL" element) per type. 751 */ 752 for (i = 0; i < DB_TYPES; i++) { 753 next[i] = 1; 754 } 755 756 (void) write_nodes(hdp, NULL, CACHE_ROOT(hdp), next); 757 (void) write_links(hdp, NULL, CACHE(hdp)->dngl, next); 758 DB_HDR(hdp)->update_count = CACHE(hdp)->update_count; 759 760 rv = close_db(hdp); 761 762 if (rv != 0 || DB_ERR(hdp) || rename(tmp, file) != 0) { 763 (void) devlink_dprintf(DBG_ERR, 764 "di_devlink_close: %s error: %s\n", 765 rv ? "close_db" : "DB or rename", strerror(errno)); 766 (void) unlink(tmp); 767 (void) unlink(file); 768 handle_free(&hdp); 769 return (-1); 770 } 771 772 handle_free(&hdp); 773 774 (void) devlink_dprintf(DBG_INFO, "di_devlink_close: wrote DB(%s)\n", 775 file); 776 777 return (0); 778 } 779 780 /* 781 * Inits the database header. 782 */ 783 static int 784 init_hdr(struct di_devlink_handle *hdp, long page_sz, uint32_t *count) 785 { 786 int i; 787 788 DB_HDR(hdp)->magic = DB_MAGIC; 789 DB_HDR(hdp)->vers = DB_VERSION; 790 DB_HDR(hdp)->root_idx = DB_NIL; 791 DB_HDR(hdp)->dngl_idx = DB_NIL; 792 DB_HDR(hdp)->page_sz = (uint32_t)page_sz; 793 794 for (i = 0; i < DB_TYPES; i++) { 795 assert(count[i] >= 1); 796 DB_NUM(hdp, i) = count[i]; 797 } 798 799 return (0); 800 } 801 802 static int 803 write_nodes( 804 struct di_devlink_handle *hdp, 805 struct db_node *pdnp, 806 cache_node_t *cnp, 807 uint32_t *next) 808 { 809 uint32_t idx; 810 struct db_node *dnp; 811 const char *fcn = "write_nodes"; 812 813 assert(HDL_RDWR(hdp)); 814 815 for (; cnp != NULL; cnp = cnp->sib) { 816 817 assert(cnp->path != NULL); 818 819 /* parent node should only be NULL for root node */ 820 if ((pdnp == NULL) ^ (cnp == CACHE_ROOT(hdp))) { 821 (void) devlink_dprintf(DBG_ERR, 822 "%s: invalid parent for: %s\n", fcn, cnp->path); 823 SET_DB_ERR(hdp); 824 break; 825 } 826 827 assert((strcmp(cnp->path, "/") != 0) ^ 828 (cnp == CACHE_ROOT(hdp))); 829 830 idx = next[DB_NODE]; 831 if ((dnp = set_node(hdp, idx)) == NULL) { 832 SET_DB_ERR(hdp); 833 break; 834 } 835 836 dnp->path = write_string(hdp, cnp->path, next); 837 if (dnp->path == DB_NIL) { 838 SET_DB_ERR(hdp); 839 break; 840 } 841 /* commit write for this node */ 842 next[DB_NODE]++; 843 844 if (pdnp == NULL) { 845 assert(DB_HDR(hdp)->root_idx == DB_NIL); 846 DB_HDR(hdp)->root_idx = idx; 847 } else { 848 dnp->sib = pdnp->child; 849 pdnp->child = idx; 850 } 851 852 (void) devlink_dprintf(DBG_STEP, "%s: node[%u]: %s\n", fcn, idx, 853 cnp->path); 854 855 if (write_minors(hdp, dnp, cnp->minor, next) != 0 || 856 write_nodes(hdp, dnp, cnp->child, next) != 0) { 857 break; 858 } 859 } 860 861 return (cnp ? -1 : 0); 862 } 863 864 static int 865 write_minors( 866 struct di_devlink_handle *hdp, 867 struct db_node *pdnp, 868 cache_minor_t *cmnp, 869 uint32_t *next) 870 { 871 uint32_t idx; 872 struct db_minor *dmp; 873 const char *fcn = "write_minors"; 874 875 assert(HDL_RDWR(hdp)); 876 877 if (pdnp == NULL) { 878 (void) devlink_dprintf(DBG_ERR, "%s: no node for minor: %s\n", 879 fcn, cmnp ? cmnp->name : "<NULL>"); 880 SET_DB_ERR(hdp); 881 return (-1); 882 } 883 884 for (; cmnp != NULL; cmnp = cmnp->sib) { 885 886 assert(cmnp->name != NULL); 887 888 idx = next[DB_MINOR]; 889 if ((dmp = set_minor(hdp, idx)) == NULL) { 890 SET_DB_ERR(hdp); 891 break; 892 } 893 894 dmp->name = write_string(hdp, cmnp->name, next); 895 dmp->nodetype = write_string(hdp, cmnp->nodetype, next); 896 if (dmp->name == DB_NIL || dmp->nodetype == DB_NIL) { 897 dmp->name = dmp->nodetype = DB_NIL; 898 SET_DB_ERR(hdp); 899 break; 900 } 901 902 /* Commit writes to this minor */ 903 next[DB_MINOR]++; 904 905 dmp->sib = pdnp->minor; 906 pdnp->minor = idx; 907 908 (void) devlink_dprintf(DBG_STEP, "%s: minor[%u]: %s\n", 909 fcn, idx, cmnp->name); 910 911 if (write_links(hdp, dmp, cmnp->link, next) != 0) { 912 break; 913 } 914 } 915 916 return (cmnp ? -1 : 0); 917 } 918 919 static int 920 write_links( 921 struct di_devlink_handle *hdp, 922 struct db_minor *pdmp, 923 cache_link_t *clp, 924 uint32_t *next) 925 { 926 uint32_t idx; 927 struct db_link *dlp; 928 const char *fcn = "write_links"; 929 930 assert(HDL_RDWR(hdp)); 931 932 /* A NULL minor if and only if the links are dangling */ 933 if (clp != NULL && ((pdmp == NULL) ^ (clp == CACHE(hdp)->dngl))) { 934 (void) devlink_dprintf(DBG_ERR, "%s: invalid minor for link\n", 935 fcn); 936 SET_DB_ERR(hdp); 937 return (-1); 938 } 939 940 for (; clp != NULL; clp = clp->sib) { 941 942 assert(clp->path != NULL); 943 944 if ((pdmp == NULL) ^ (clp->minor == NULL)) { 945 (void) devlink_dprintf(DBG_ERR, 946 "%s: invalid minor for link(%s)\n", fcn, clp->path); 947 SET_DB_ERR(hdp); 948 break; 949 } 950 951 idx = next[DB_LINK]; 952 if ((dlp = set_link(hdp, idx)) == NULL) { 953 SET_DB_ERR(hdp); 954 break; 955 } 956 957 dlp->path = write_string(hdp, clp->path, next); 958 dlp->content = write_string(hdp, clp->content, next); 959 if (dlp->path == DB_NIL || dlp->content == DB_NIL) { 960 dlp->path = dlp->content = DB_NIL; 961 SET_DB_ERR(hdp); 962 break; 963 } 964 965 dlp->attr = clp->attr; 966 967 /* Commit writes to this link */ 968 next[DB_LINK]++; 969 970 if (pdmp != NULL) { 971 dlp->sib = pdmp->link; 972 pdmp->link = idx; 973 } else { 974 dlp->sib = DB_HDR(hdp)->dngl_idx; 975 DB_HDR(hdp)->dngl_idx = idx; 976 } 977 978 (void) devlink_dprintf(DBG_STEP, "%s: link[%u]: %s%s\n", 979 fcn, idx, clp->path, pdmp == NULL ? "(DANGLING)" : ""); 980 } 981 982 return (clp ? -1 : 0); 983 } 984 985 986 static uint32_t 987 write_string(struct di_devlink_handle *hdp, const char *str, uint32_t *next) 988 { 989 char *dstr; 990 uint32_t idx; 991 992 assert(HDL_RDWR(hdp)); 993 994 if (str == NULL) { 995 (void) devlink_dprintf(DBG_ERR, 996 "write_string: NULL argument\n"); 997 return (DB_NIL); 998 } 999 1000 idx = next[DB_STR]; 1001 if (!VALID_STR(hdp, idx, str)) { 1002 (void) devlink_dprintf(DBG_ERR, 1003 "write_string: invalid index[%u], string(%s)\n", idx, str); 1004 return (DB_NIL); 1005 } 1006 1007 if ((dstr = set_string(hdp, idx)) == NULL) { 1008 return (DB_NIL); 1009 } 1010 1011 (void) strcpy(dstr, str); 1012 1013 next[DB_STR] += strlen(dstr) + 1; 1014 1015 return (idx); 1016 } 1017 1018 static int 1019 close_db(struct di_devlink_handle *hdp) 1020 { 1021 int i, rv = 0; 1022 size_t sz; 1023 1024 if (!DB_OPEN(hdp)) { 1025 #ifdef DEBUG 1026 assert(DB(hdp)->db_fd == -1); 1027 assert(DB(hdp)->flags == 0); 1028 for (i = 0; i < DB_TYPES; i++) { 1029 assert(DB_SEG(hdp, i) == NULL); 1030 assert(DB_SEG_PROT(hdp, i) == 0); 1031 } 1032 #endif 1033 return (0); 1034 } 1035 1036 /* Unmap header after unmapping all other mapped segments */ 1037 for (i = 0; i < DB_TYPES; i++) { 1038 if (DB_SEG(hdp, i)) { 1039 sz = seg_size(hdp, i); 1040 if (DB_RDWR(hdp)) 1041 rv += msync(DB_SEG(hdp, i), sz, MS_SYNC); 1042 (void) munmap(DB_SEG(hdp, i), sz); 1043 DB_SEG(hdp, i) = NULL; 1044 DB_SEG_PROT(hdp, i) = 0; 1045 } 1046 } 1047 1048 if (DB_RDWR(hdp)) 1049 rv += msync((caddr_t)DB_HDR(hdp), HDR_LEN, MS_SYNC); 1050 (void) munmap((caddr_t)DB_HDR(hdp), HDR_LEN); 1051 DB(hdp)->hdr = NULL; 1052 1053 (void) close(DB(hdp)->db_fd); 1054 DB(hdp)->db_fd = -1; 1055 DB(hdp)->flags = 0; 1056 1057 return (rv ? -1 : 0); 1058 } 1059 1060 1061 static void 1062 cache_free(struct di_devlink_handle *hdp) 1063 { 1064 cache_link_t *clp; 1065 1066 subtree_free(hdp, &(CACHE_ROOT(hdp))); 1067 assert(CACHE_LAST(hdp) == NULL); 1068 1069 /* 1070 * Don't bother removing links from hash table chains, 1071 * as we are freeing the hash table itself. 1072 */ 1073 while (CACHE(hdp)->dngl != NULL) { 1074 clp = CACHE(hdp)->dngl; 1075 CACHE(hdp)->dngl = clp->sib; 1076 assert(clp->minor == NULL); 1077 link_free(&clp); 1078 } 1079 1080 assert((CACHE(hdp)->hash == NULL) ^ (CACHE(hdp)->hash_sz != 0)); 1081 1082 free(CACHE(hdp)->hash); 1083 CACHE(hdp)->hash = NULL; 1084 CACHE(hdp)->hash_sz = 0; 1085 } 1086 1087 static void 1088 handle_free(struct di_devlink_handle **pp) 1089 { 1090 struct di_devlink_handle *hdp = *pp; 1091 1092 *pp = NULL; 1093 1094 if (hdp == NULL) 1095 return; 1096 1097 (void) close_db(hdp); 1098 cache_free(hdp); 1099 1100 if (HDL_RDWR(hdp)) 1101 exit_db_lock(hdp); 1102 assert(hdp->lock_fd == -1); 1103 1104 free(hdp->dev_dir); 1105 free(hdp->db_dir); 1106 free(hdp); 1107 } 1108 1109 /* 1110 * Frees the tree rooted at a node. Siblings of the subtree root 1111 * have to be handled by the caller. 1112 */ 1113 static void 1114 subtree_free(struct di_devlink_handle *hdp, cache_node_t **pp) 1115 { 1116 cache_node_t *np; 1117 cache_link_t *clp; 1118 cache_minor_t *cmnp; 1119 1120 if (pp == NULL || *pp == NULL) 1121 return; 1122 1123 while ((*pp)->child != NULL) { 1124 np = (*pp)->child; 1125 (*pp)->child = np->sib; 1126 subtree_free(hdp, &np); 1127 } 1128 1129 while ((*pp)->minor != NULL) { 1130 cmnp = (*pp)->minor; 1131 (*pp)->minor = cmnp->sib; 1132 1133 while (cmnp->link != NULL) { 1134 clp = cmnp->link; 1135 cmnp->link = clp->sib; 1136 rm_link_from_hash(hdp, clp); 1137 link_free(&clp); 1138 } 1139 minor_free(hdp, &cmnp); 1140 } 1141 1142 node_free(pp); 1143 } 1144 1145 static void 1146 rm_link_from_hash(struct di_devlink_handle *hdp, cache_link_t *clp) 1147 { 1148 int hval; 1149 cache_link_t **pp; 1150 1151 if (clp == NULL) 1152 return; 1153 1154 if (clp->path == NULL) 1155 return; 1156 1157 hval = hashfn(hdp, clp->path); 1158 pp = &(CACHE_HASH(hdp, hval)); 1159 for (; *pp != NULL; pp = &(*pp)->hash) { 1160 if (*pp == clp) { 1161 *pp = clp->hash; 1162 clp->hash = NULL; 1163 return; 1164 } 1165 } 1166 1167 devlink_dprintf(DBG_ERR, "rm_link_from_hash: link(%s) not found\n", 1168 clp->path); 1169 } 1170 1171 static cache_link_t * 1172 link_hash(di_devlink_handle_t hdp, const char *link, uint_t flags) 1173 { 1174 int hval; 1175 cache_link_t **pp, *clp; 1176 1177 if (link == NULL) 1178 return (NULL); 1179 1180 hval = hashfn(hdp, link); 1181 pp = &(CACHE_HASH(hdp, hval)); 1182 for (; (clp = *pp) != NULL; pp = &clp->hash) { 1183 if (strcmp(clp->path, link) == 0) { 1184 break; 1185 } 1186 } 1187 1188 if (clp == NULL) 1189 return (NULL); 1190 1191 if ((flags & UNLINK_FROM_HASH) == UNLINK_FROM_HASH) { 1192 *pp = clp->hash; 1193 clp->hash = NULL; 1194 } 1195 1196 return (clp); 1197 } 1198 1199 static cache_minor_t * 1200 link2minor(struct di_devlink_handle *hdp, cache_link_t *clp) 1201 { 1202 cache_link_t *plp; 1203 const char *minor_path; 1204 char *cp, buf[PATH_MAX], link[PATH_MAX]; 1205 char abspath[PATH_MAX]; 1206 struct stat st; 1207 1208 if (TYPE_PRI(attr2type(clp->attr))) { 1209 /* 1210 * For primary link, content should point to a /devices node. 1211 */ 1212 if (!is_minor_node(clp->content, &minor_path)) { 1213 return (NULL); 1214 } 1215 1216 return (lookup_minor(hdp, minor_path, NULL, 1217 TYPE_CACHE|CREATE_FLAG)); 1218 1219 } 1220 1221 /* 1222 * If secondary, the primary link is derived from the secondary 1223 * link contents. Secondary link contents can have two formats: 1224 * audio -> /dev/sound/0 1225 * fb0 -> fbs/afb0 1226 */ 1227 1228 buf[0] = '\0'; 1229 if (strncmp(clp->content, DEV"/", strlen(DEV"/")) == 0) { 1230 cp = &clp->content[strlen(DEV"/")]; 1231 } else if (clp->content[0] != '/') { 1232 if ((cp = strrchr(clp->path, '/')) != NULL) { 1233 char savechar = *(cp + 1); 1234 *(cp + 1) = '\0'; 1235 (void) snprintf(buf, sizeof (buf), "%s", clp->path); 1236 *(cp + 1) = savechar; 1237 } 1238 (void) strlcat(buf, clp->content, sizeof (buf)); 1239 cp = buf; 1240 } else { 1241 goto follow_link; 1242 } 1243 1244 /* 1245 * Lookup the primary link if possible and find its minor. 1246 */ 1247 if ((plp = link_hash(hdp, cp, 0)) != NULL && plp->minor != NULL) { 1248 return (plp->minor); 1249 } 1250 1251 /* realpath() used only as a last resort because it is expensive */ 1252 follow_link: 1253 (void) snprintf(link, sizeof (link), "%s/%s", hdp->dev_dir, clp->path); 1254 1255 #ifdef DEBUG 1256 /*LINTED*/ 1257 assert(sizeof (buf) >= PATH_MAX); 1258 #endif 1259 1260 /* 1261 * A realpath attempt to lookup a dangling link can invoke implicit 1262 * reconfig so verify there's an actual device behind the link first. 1263 */ 1264 if (lstat(link, &st) == -1) 1265 return (NULL); 1266 if (S_ISLNK(st.st_mode)) { 1267 if (s_readlink(link, buf, sizeof (buf)) < 0) 1268 return (NULL); 1269 if (buf[0] != '/') { 1270 char *p; 1271 size_t n = sizeof (abspath); 1272 if (strlcpy(abspath, link, n) >= n) 1273 return (NULL); 1274 p = strrchr(abspath, '/') + 1; 1275 *p = 0; 1276 n = sizeof (abspath) - strlen(p); 1277 if (strlcpy(p, buf, n) >= n) 1278 return (NULL); 1279 } else { 1280 if (strlcpy(abspath, buf, sizeof (abspath)) >= 1281 sizeof (abspath)) 1282 return (NULL); 1283 } 1284 if (!device_exists(abspath)) 1285 return (NULL); 1286 } 1287 1288 if (s_realpath(link, buf) == NULL || !is_minor_node(buf, &minor_path)) { 1289 return (NULL); 1290 } 1291 return (lookup_minor(hdp, minor_path, NULL, TYPE_CACHE|CREATE_FLAG)); 1292 } 1293 1294 1295 static void 1296 resolve_dangling_links(struct di_devlink_handle *hdp) 1297 { 1298 cache_minor_t *cmnp; 1299 cache_link_t *clp, **pp; 1300 1301 for (pp = &(CACHE(hdp)->dngl); *pp != NULL; ) { 1302 clp = *pp; 1303 if ((cmnp = link2minor(hdp, clp)) != NULL) { 1304 *pp = clp->sib; 1305 clp->sib = cmnp->link; 1306 cmnp->link = clp; 1307 assert(clp->minor == NULL); 1308 clp->minor = cmnp; 1309 } else { 1310 devlink_dprintf(DBG_INFO, 1311 "resolve_dangling_links: link(%s): unresolved\n", 1312 clp->path); 1313 pp = &clp->sib; 1314 } 1315 } 1316 } 1317 1318 1319 /* 1320 * The elements are assumed to be detached from the cache tree. 1321 */ 1322 static void 1323 node_free(cache_node_t **pp) 1324 { 1325 cache_node_t *cnp = *pp; 1326 1327 *pp = NULL; 1328 1329 if (cnp == NULL) 1330 return; 1331 1332 free(cnp->path); 1333 free(cnp); 1334 } 1335 1336 static void 1337 minor_free(struct di_devlink_handle *hdp, cache_minor_t **pp) 1338 { 1339 cache_minor_t *cmnp = *pp; 1340 1341 *pp = NULL; 1342 1343 if (cmnp == NULL) 1344 return; 1345 1346 if (CACHE_LAST(hdp) == cmnp) { 1347 devlink_dprintf(DBG_STEP, "minor_free: last_minor(%s)\n", 1348 cmnp->name); 1349 CACHE_LAST(hdp) = NULL; 1350 } 1351 1352 free(cmnp->name); 1353 free(cmnp->nodetype); 1354 free(cmnp); 1355 } 1356 1357 static void 1358 link_free(cache_link_t **pp) 1359 { 1360 cache_link_t *clp = *pp; 1361 1362 *pp = NULL; 1363 1364 if (clp == NULL) 1365 return; 1366 1367 free(clp->path); 1368 free(clp->content); 1369 free(clp); 1370 } 1371 1372 /* 1373 * Returns the ':' preceding the minor name 1374 */ 1375 static char * 1376 minor_colon(const char *path) 1377 { 1378 char *cp; 1379 1380 if ((cp = strrchr(path, '/')) == NULL) { 1381 return (NULL); 1382 } 1383 1384 return (strchr(cp, ':')); 1385 } 1386 1387 static void * 1388 lookup_minor( 1389 struct di_devlink_handle *hdp, 1390 const char *minor_path, 1391 const char *nodetype, 1392 const int flags) 1393 { 1394 void *vp; 1395 char *colon; 1396 char pdup[PATH_MAX]; 1397 const char *fcn = "lookup_minor"; 1398 1399 if (minor_path == NULL) { 1400 errno = EINVAL; 1401 return (NULL); 1402 } 1403 1404 (void) snprintf(pdup, sizeof (pdup), "%s", minor_path); 1405 1406 if ((colon = minor_colon(pdup)) == NULL) { 1407 (void) devlink_dprintf(DBG_ERR, "%s: invalid minor path(%s)\n", 1408 fcn, minor_path); 1409 errno = EINVAL; 1410 return (NULL); 1411 } 1412 *colon = '\0'; 1413 1414 if ((vp = get_last_minor(hdp, pdup, colon + 1, flags)) != NULL) { 1415 return (vp); 1416 } 1417 1418 if ((vp = lookup_node(hdp, pdup, flags)) == NULL) { 1419 (void) devlink_dprintf(DBG_ERR, "%s: node(%s) not found\n", 1420 fcn, pdup); 1421 return (NULL); 1422 } 1423 *colon = ':'; 1424 1425 if (LOOKUP_CACHE(flags)) { 1426 cache_minor_t **pp; 1427 1428 pp = &((cache_node_t *)vp)->minor; 1429 for (; *pp != NULL; pp = &(*pp)->sib) { 1430 if (strcmp((*pp)->name, colon + 1) == 0) 1431 break; 1432 } 1433 1434 if (*pp == NULL && CREATE_ELEM(flags)) { 1435 *pp = minor_insert(hdp, vp, colon + 1, nodetype, pp); 1436 } 1437 set_last_minor(hdp, *pp, flags); 1438 1439 return (*pp); 1440 } else { 1441 char *cp; 1442 uint32_t nidx; 1443 struct db_minor *dmp; 1444 1445 nidx = (((struct db_node *)vp)->minor); 1446 for (; dmp = get_minor(hdp, nidx); nidx = dmp->sib) { 1447 cp = get_string(hdp, dmp->name); 1448 if (cp && strcmp(cp, colon + 1) == 0) 1449 break; 1450 } 1451 return (dmp); 1452 } 1453 } 1454 1455 static void * 1456 lookup_node(struct di_devlink_handle *hdp, char *path, const int flags) 1457 { 1458 struct tnode tnd = {NULL}; 1459 1460 if (tnd.node = get_last_node(hdp, path, flags)) 1461 return (tnd.node); 1462 1463 tnd.handle = hdp; 1464 tnd.flags = flags; 1465 1466 if (walk_tree(path, &tnd, visit_node) != 0) 1467 return (NULL); 1468 1469 return (tnd.node); 1470 } 1471 1472 /* 1473 * last_minor is used for nodes of TYPE_CACHE only. 1474 */ 1475 static void * 1476 get_last_node(struct di_devlink_handle *hdp, const char *path, int flags) 1477 { 1478 cache_node_t *cnp; 1479 1480 #ifdef DEBUG 1481 if (getenv(SKIP_LAST_CACHE)) { 1482 (void) devlink_dprintf(DBG_INFO, 1483 "get_last_node: SKIPPING \"last\" node cache\n"); 1484 return (NULL); 1485 } 1486 #endif 1487 1488 if (!LOOKUP_CACHE(flags) || CACHE_LAST(hdp) == NULL || 1489 CACHE_LAST(hdp)->node == NULL) { 1490 return (NULL); 1491 } 1492 1493 cnp = CACHE_LAST(hdp)->node; 1494 if (strcmp(cnp->path, path) == 0) { 1495 return (cnp); 1496 } 1497 1498 cnp = cnp->sib; 1499 if (cnp && strcmp(cnp->path, path) == 0) { 1500 return (cnp); 1501 } 1502 1503 return (NULL); 1504 } 1505 1506 static void * 1507 get_last_minor( 1508 struct di_devlink_handle *hdp, 1509 const char *devfs_path, 1510 const char *minor_name, 1511 int flags) 1512 { 1513 cache_minor_t *cmnp; 1514 1515 #ifdef DEBUG 1516 if (getenv(SKIP_LAST_CACHE)) { 1517 (void) devlink_dprintf(DBG_INFO, 1518 "get_last_minor: SKIPPING \"last\" minor cache\n"); 1519 return (NULL); 1520 } 1521 #endif 1522 1523 if (!LOOKUP_CACHE(flags) || CACHE_LAST(hdp) == NULL) { 1524 return (NULL); 1525 } 1526 1527 cmnp = CACHE_LAST(hdp); 1528 if (strcmp(cmnp->name, minor_name) == 0 && cmnp->node && 1529 strcmp(cmnp->node->path, devfs_path) == 0) { 1530 return (cmnp); 1531 } 1532 1533 cmnp = cmnp->sib; 1534 if (cmnp && strcmp(cmnp->name, minor_name) == 0 && cmnp->node && 1535 strcmp(cmnp->node->path, devfs_path) == 0) { 1536 set_last_minor(hdp, cmnp, TYPE_CACHE); 1537 return (cmnp); 1538 } 1539 1540 return (NULL); 1541 } 1542 1543 static void 1544 set_last_minor(struct di_devlink_handle *hdp, cache_minor_t *cmnp, int flags) 1545 { 1546 #ifdef DEBUG 1547 if (getenv(SKIP_LAST_CACHE)) { 1548 (void) devlink_dprintf(DBG_INFO, 1549 "set_last_minor: SKIPPING \"last\" minor cache\n"); 1550 return; 1551 } 1552 #endif 1553 1554 if (LOOKUP_CACHE(flags) && cmnp) { 1555 CACHE_LAST(hdp) = cmnp; 1556 } 1557 } 1558 1559 1560 /* 1561 * Returns 0 if normal return or -1 otherwise. 1562 */ 1563 static int 1564 walk_tree( 1565 char *cur, 1566 void *arg, 1567 int (*node_callback)(const char *path, void *arg)) 1568 { 1569 char *slash, buf[PATH_MAX]; 1570 1571 if (cur == NULL || cur[0] != '/' || strlen(cur) > sizeof (buf) - 1) { 1572 errno = EINVAL; 1573 return (-1); 1574 } 1575 1576 (void) strcpy(buf, "/"); 1577 1578 for (;;) { 1579 1580 if (node_callback(buf, arg) != DI_WALK_CONTINUE) 1581 break; 1582 1583 while (*cur == '/') 1584 cur++; 1585 1586 if (*cur == '\0') 1587 break; 1588 1589 /* 1590 * There is a next component(s). Append a "/" separator for all 1591 * but the first (root) component. 1592 */ 1593 if (buf[1] != '\0') { 1594 (void) strlcat(buf, "/", sizeof (buf)); 1595 } 1596 1597 if (slash = strchr(cur, '/')) { 1598 *slash = '\0'; 1599 (void) strlcat(buf, cur, sizeof (buf)); 1600 *slash = '/'; 1601 cur = slash; 1602 } else { 1603 (void) strlcat(buf, cur, sizeof (buf)); 1604 cur += strlen(cur); 1605 } 1606 1607 } 1608 1609 return (0); 1610 } 1611 1612 1613 static int 1614 visit_node(const char *path, void *arg) 1615 { 1616 struct tnode *tnp = arg; 1617 1618 if (LOOKUP_CACHE(tnp->flags)) { 1619 1620 cache_node_t *cnp = tnp->node; 1621 1622 cnp = (cnp) ? cnp->child : CACHE_ROOT(tnp->handle); 1623 1624 for (; cnp != NULL; cnp = cnp->sib) { 1625 if (strcmp(cnp->path, path) == 0) 1626 break; 1627 } 1628 if (cnp == NULL && CREATE_ELEM(tnp->flags)) { 1629 cnp = node_insert(tnp->handle, tnp->node, path, 1630 INSERT_TAIL); 1631 } 1632 tnp->node = cnp; 1633 } else { 1634 char *cp; 1635 struct db_node *dnp = tnp->node; 1636 1637 dnp = (dnp) ? get_node(tnp->handle, dnp->child) 1638 : get_node(tnp->handle, DB_HDR(tnp->handle)->root_idx); 1639 1640 for (; dnp != NULL; dnp = get_node(tnp->handle, dnp->sib)) { 1641 cp = get_string(tnp->handle, dnp->path); 1642 if (cp && strcmp(cp, path) == 0) { 1643 break; 1644 } 1645 } 1646 tnp->node = dnp; 1647 } 1648 1649 /* 1650 * Terminate walk if node is not found for a path component. 1651 */ 1652 return (tnp->node ? DI_WALK_CONTINUE : DI_WALK_TERMINATE); 1653 } 1654 1655 static void 1656 minor_delete(di_devlink_handle_t hdp, cache_minor_t *cmnp) 1657 { 1658 cache_link_t **lpp; 1659 cache_minor_t **mpp; 1660 const char *fcn = "minor_delete"; 1661 1662 (void) devlink_dprintf(DBG_STEP, "%s: removing minor: %s\n", 1663 fcn, cmnp->name); 1664 1665 /* detach minor from node */ 1666 if (cmnp->node != NULL) { 1667 mpp = &cmnp->node->minor; 1668 for (; *mpp != NULL; mpp = &(*mpp)->sib) { 1669 if (*mpp == cmnp) 1670 break; 1671 } 1672 1673 if (*mpp == NULL) { 1674 (void) devlink_dprintf(DBG_ERR, 1675 "%s: dangling minor: %s\n", fcn, cmnp->name); 1676 } else { 1677 *mpp = cmnp->sib; 1678 } 1679 } else { 1680 (void) devlink_dprintf(DBG_ERR, "%s: orphan minor(%s)\n", fcn, 1681 cmnp->name); 1682 } 1683 1684 delete_unused_nodes(hdp, cmnp->node); 1685 1686 cmnp->node = NULL; 1687 cmnp->sib = NULL; 1688 1689 /* Move all remaining links to dangling list */ 1690 for (lpp = &cmnp->link; *lpp != NULL; lpp = &(*lpp)->sib) { 1691 (*lpp)->minor = NULL; 1692 } 1693 *lpp = CACHE(hdp)->dngl; 1694 CACHE(hdp)->dngl = cmnp->link; 1695 cmnp->link = NULL; 1696 1697 minor_free(hdp, &cmnp); 1698 } 1699 1700 static void 1701 delete_unused_nodes(di_devlink_handle_t hdp, cache_node_t *cnp) 1702 { 1703 cache_node_t **npp; 1704 const char *fcn = "delete_unused_nodes"; 1705 1706 if (cnp == NULL) 1707 return; 1708 1709 if (cnp->minor != NULL || cnp->child != NULL) 1710 return; 1711 1712 (void) devlink_dprintf(DBG_INFO, "%s: removing unused node: %s\n", fcn, 1713 cnp->path); 1714 1715 /* Unlink node from tree */ 1716 if (cnp->parent != NULL) { 1717 npp = &cnp->parent->child; 1718 for (; *npp != NULL; npp = &(*npp)->sib) { 1719 if (*npp == cnp) 1720 break; 1721 } 1722 1723 if (*npp == NULL) { 1724 (void) devlink_dprintf(DBG_ERR, 1725 "%s: dangling node: %s\n", fcn, cnp->path); 1726 } else { 1727 *npp = cnp->sib; 1728 } 1729 } else if (cnp == CACHE_ROOT(hdp)) { 1730 CACHE_ROOT(hdp) = NULL; 1731 } else { 1732 (void) devlink_dprintf(DBG_ERR, "%s: orphan node (%s)\n", fcn, 1733 cnp->path); 1734 } 1735 1736 delete_unused_nodes(hdp, cnp->parent); 1737 1738 cnp->parent = cnp->sib = NULL; 1739 1740 node_free(&cnp); 1741 } 1742 1743 static int 1744 rm_link(di_devlink_handle_t hdp, const char *link) 1745 { 1746 cache_link_t *clp; 1747 const char *fcn = "rm_link"; 1748 1749 if (hdp == NULL || DB_ERR(hdp) || link == NULL || link[0] == '/' || 1750 (!HDL_RDWR(hdp) && !HDL_RDONLY(hdp))) { 1751 devlink_dprintf(DBG_ERR, "%s: %s: invalid args\n", 1752 fcn, link ? link : "<NULL>"); 1753 errno = EINVAL; 1754 return (-1); 1755 } 1756 1757 devlink_dprintf(DBG_STEP, "%s: link(%s)\n", fcn, link); 1758 1759 if ((clp = link_hash(hdp, link, UNLINK_FROM_HASH)) == NULL) { 1760 return (0); 1761 } 1762 1763 link_delete(hdp, clp); 1764 1765 return (0); 1766 } 1767 1768 int 1769 di_devlink_rm_link(di_devlink_handle_t hdp, const char *link) 1770 { 1771 if (hdp == NULL || !HDL_RDWR(hdp)) { 1772 errno = EINVAL; 1773 return (-1); 1774 } 1775 1776 return (rm_link(hdp, link)); 1777 } 1778 1779 static void 1780 link_delete(di_devlink_handle_t hdp, cache_link_t *clp) 1781 { 1782 cache_link_t **pp; 1783 const char *fcn = "link_delete"; 1784 1785 (void) devlink_dprintf(DBG_STEP, "%s: removing link: %s\n", 1786 fcn, clp->path); 1787 1788 if (clp->minor == NULL) 1789 pp = &(CACHE(hdp)->dngl); 1790 else 1791 pp = &clp->minor->link; 1792 1793 for (; *pp != NULL; pp = &(*pp)->sib) { 1794 if (*pp == clp) 1795 break; 1796 } 1797 1798 if (*pp == NULL) { 1799 (void) devlink_dprintf(DBG_ERR, "%s: link(%s) not on list\n", 1800 fcn, clp->path); 1801 } else { 1802 *pp = clp->sib; 1803 } 1804 1805 delete_unused_minor(hdp, clp->minor); 1806 1807 clp->minor = NULL; 1808 1809 link_free(&clp); 1810 } 1811 1812 static void 1813 delete_unused_minor(di_devlink_handle_t hdp, cache_minor_t *cmnp) 1814 { 1815 if (cmnp == NULL) 1816 return; 1817 1818 if (cmnp->link != NULL) 1819 return; 1820 1821 devlink_dprintf(DBG_STEP, "delete_unused_minor: removing minor(%s)\n", 1822 cmnp->name); 1823 1824 minor_delete(hdp, cmnp); 1825 } 1826 1827 int 1828 di_devlink_add_link( 1829 di_devlink_handle_t hdp, 1830 const char *link, 1831 const char *content, 1832 int flags) 1833 { 1834 return (add_link(hdp, link, content, flags) != NULL ? 0 : -1); 1835 } 1836 1837 static cache_link_t * 1838 add_link( 1839 struct di_devlink_handle *hdp, 1840 const char *link, 1841 const char *content, 1842 int flags) 1843 { 1844 uint32_t attr; 1845 cache_link_t *clp; 1846 cache_minor_t *cmnp; 1847 const char *fcn = "add_link"; 1848 1849 if (hdp == NULL || DB_ERR(hdp) || link == NULL || 1850 link[0] == '/' || content == NULL || !link_flag(flags) || 1851 (!HDL_RDWR(hdp) && !HDL_RDONLY(hdp))) { 1852 devlink_dprintf(DBG_ERR, "%s: %s: invalid args\n", 1853 fcn, link ? link : "<NULL>"); 1854 errno = EINVAL; 1855 return (NULL); 1856 } 1857 1858 if ((clp = link_hash(hdp, link, 0)) != NULL) { 1859 if (link_cmp(clp, content, LINK_TYPE(flags)) != 0) { 1860 (void) rm_link(hdp, link); 1861 } else { 1862 return (clp); 1863 } 1864 } 1865 1866 if (TYPE_PRI(flags)) { 1867 const char *minor_path = NULL; 1868 1869 if (!is_minor_node(content, &minor_path)) { 1870 (void) devlink_dprintf(DBG_ERR, 1871 "%s: invalid content(%s) for primary link\n", 1872 fcn, content); 1873 errno = EINVAL; 1874 return (NULL); 1875 } 1876 if ((cmnp = lookup_minor(hdp, minor_path, NULL, 1877 TYPE_CACHE|CREATE_FLAG)) == NULL) { 1878 return (NULL); 1879 } 1880 attr = A_PRIMARY; 1881 } else { 1882 /* 1883 * Defer resolving a secondary link to a minor until the 1884 * database is closed. This ensures that the primary link 1885 * (required for a successful resolve) has also been created. 1886 */ 1887 cmnp = NULL; 1888 attr = A_SECONDARY; 1889 } 1890 1891 return (link_insert(hdp, cmnp, link, content, attr)); 1892 } 1893 1894 /* 1895 * Returns 0 on match or 1 otherwise. 1896 */ 1897 static int 1898 link_cmp(cache_link_t *clp, const char *content, int type) 1899 { 1900 if (strcmp(clp->content, content) != 0) 1901 return (1); 1902 1903 if (attr2type(clp->attr) != type) 1904 return (1); 1905 1906 return (0); 1907 } 1908 1909 int 1910 di_devlink_update(di_devlink_handle_t hdp) 1911 { 1912 if (hdp == NULL || !HDL_RDWR(hdp) || DB_ERR(hdp)) { 1913 errno = EINVAL; 1914 return (-1); 1915 } 1916 1917 /* 1918 * Reset the counter to schedule a synchronization with /dev on the next 1919 * di_devlink_close(). 1920 */ 1921 CACHE(hdp)->update_count = 0; 1922 1923 return (0); 1924 } 1925 1926 static int 1927 synchronize_db(di_devlink_handle_t hdp) 1928 { 1929 int hval; 1930 cache_link_t *clp; 1931 char pdup[PATH_MAX]; 1932 recurse_t rec = {NULL}; 1933 const char *fcn = "synchronize_db"; 1934 1935 rec.data = NULL; 1936 rec.fcn = cache_dev_link; 1937 1938 /* 1939 * Walk through $ROOT/dev, reading every link and marking the 1940 * corresponding cached version as valid(adding new links as needed). 1941 * Then walk through the cache and remove all unmarked links. 1942 */ 1943 if (recurse_dev(hdp, &rec) != 0) { 1944 return (-1); 1945 } 1946 1947 for (hval = 0; hval < CACHE(hdp)->hash_sz; hval++) { 1948 for (clp = CACHE_HASH(hdp, hval); clp != NULL; ) { 1949 if (GET_VALID_ATTR(clp->attr)) { 1950 CLR_VALID_ATTR(clp->attr); 1951 clp = clp->hash; 1952 continue; 1953 } 1954 1955 /* 1956 * The link is stale, so remove it. Since the link 1957 * will be destroyed, use a copy of the link path to 1958 * invoke the remove function. 1959 */ 1960 (void) snprintf(pdup, sizeof (pdup), "%s", clp->path); 1961 clp = clp->hash; 1962 (void) devlink_dprintf(DBG_STEP, 1963 "%s: removing invalid link: %s\n", fcn, pdup); 1964 (void) di_devlink_rm_link(hdp, pdup); 1965 } 1966 } 1967 1968 (void) devlink_dprintf(DBG_STEP, "%s: update completed\n", fcn); 1969 1970 return (0); 1971 } 1972 1973 static di_devlink_handle_t 1974 di_devlink_init_impl(const char *root, const char *name, uint_t flags) 1975 { 1976 int err = 0; 1977 1978 if ((flags != 0 && flags != DI_MAKE_LINK) || 1979 (flags == 0 && name != NULL)) { 1980 errno = EINVAL; 1981 return (NULL); 1982 } 1983 1984 if ((flags == DI_MAKE_LINK) && 1985 (err = devlink_create(root, name, DCA_DEVLINK_CACHE))) { 1986 errno = err; 1987 return (NULL); 1988 } 1989 1990 (void) devlink_dprintf(DBG_INFO, "devlink_init_impl: success\n"); 1991 1992 return (devlink_snapshot(root)); 1993 } 1994 1995 di_devlink_handle_t 1996 di_devlink_init(const char *name, uint_t flags) 1997 { 1998 return (di_devlink_init_impl("/", name, flags)); 1999 } 2000 2001 di_devlink_handle_t 2002 di_devlink_init_root(const char *root, const char *name, uint_t flags) 2003 { 2004 return (di_devlink_init_impl(root, name, flags)); 2005 } 2006 2007 static di_devlink_handle_t 2008 devlink_snapshot(const char *root_dir) 2009 { 2010 struct di_devlink_handle *hdp; 2011 int err; 2012 static int retried = 0; 2013 2014 if ((hdp = handle_alloc(root_dir, OPEN_RDONLY)) == NULL) { 2015 return (NULL); 2016 } 2017 2018 /* 2019 * We don't need to lock. If a consumer wants the very latest db 2020 * then it must perform a di_devlink_init with the DI_MAKE_LINK 2021 * flag to force a sync with devfsadm first. Otherwise, the 2022 * current database file is opened and mmaped on demand: the rename 2023 * associated with a db update does not change the contents 2024 * of files already opened. 2025 */ 2026 again: err = open_db(hdp, OPEN_RDONLY); 2027 2028 /* 2029 * If we failed to open DB the most likely cause is that DB file did 2030 * not exist. If we have not done a retry, signal devfsadmd to 2031 * recreate the DB file and retry. If we fail to open the DB after 2032 * retry, we will walk /dev in di_devlink_walk. 2033 */ 2034 if (err && (retried == 0)) { 2035 retried++; 2036 (void) devlink_create(root_dir, NULL, DCA_DEVLINK_SYNC); 2037 goto again; 2038 } 2039 return (hdp); 2040 } 2041 2042 int 2043 di_devlink_fini(di_devlink_handle_t *pp) 2044 { 2045 if (pp == NULL || *pp == NULL || !HDL_RDONLY(*pp)) { 2046 errno = EINVAL; 2047 return (-1); 2048 } 2049 2050 /* Freeing the handle also closes the DB */ 2051 handle_free(pp); 2052 2053 return (0); 2054 } 2055 2056 int 2057 di_devlink_walk( 2058 di_devlink_handle_t hdp, 2059 const char *re, 2060 const char *minor_path, 2061 uint_t flags, 2062 void *arg, 2063 int (*devlink_callback)(di_devlink_t, void *)) 2064 { 2065 int rv; 2066 regex_t reg; 2067 link_desc_t linkd = {NULL}; 2068 2069 if (hdp == NULL || !HDL_RDONLY(hdp)) { 2070 errno = EINVAL; 2071 return (-1); 2072 } 2073 2074 linkd.minor_path = minor_path; 2075 linkd.flags = flags; 2076 linkd.arg = arg; 2077 linkd.fcn = devlink_callback; 2078 2079 if (re) { 2080 if (regcomp(®, re, REG_EXTENDED) != 0) 2081 return (-1); 2082 linkd.regp = ® 2083 } 2084 2085 if (check_args(&linkd)) { 2086 errno = EINVAL; 2087 rv = -1; 2088 goto out; 2089 } 2090 2091 if (DB_OPEN(hdp)) { 2092 rv = walk_db(hdp, &linkd); 2093 } else { 2094 rv = walk_dev(hdp, &linkd); 2095 } 2096 2097 out: 2098 if (re) { 2099 regfree(®); 2100 } 2101 2102 return (rv ? -1 : 0); 2103 } 2104 2105 static int 2106 link_flag(uint_t flags) 2107 { 2108 if (flags != 0 && flags != DI_PRIMARY_LINK && 2109 flags != DI_SECONDARY_LINK) { 2110 return (0); 2111 } 2112 2113 return (1); 2114 } 2115 2116 /* 2117 * Currently allowed flags are: 2118 * DI_PRIMARY_LINK 2119 * DI_SECONDARY_LINK 2120 */ 2121 static int 2122 check_args(link_desc_t *linkp) 2123 { 2124 if (linkp->fcn == NULL) 2125 return (-1); 2126 2127 if (!link_flag(linkp->flags)) { 2128 return (-1); 2129 } 2130 2131 /* 2132 * Minor path can be NULL. In that case, all links will be 2133 * selected. 2134 */ 2135 if (linkp->minor_path) { 2136 if (linkp->minor_path[0] != '/' || 2137 minor_colon(linkp->minor_path) == NULL) { 2138 return (-1); 2139 } 2140 } 2141 2142 return (0); 2143 } 2144 2145 2146 /* 2147 * Walk all links in database if no minor path is specified. 2148 */ 2149 static int 2150 walk_db(struct di_devlink_handle *hdp, link_desc_t *linkp) 2151 { 2152 assert(DB_OPEN(hdp)); 2153 2154 if (linkp->minor_path == NULL) { 2155 return (walk_all_links(hdp, linkp)); 2156 } else { 2157 return (walk_matching_links(hdp, linkp)); 2158 } 2159 } 2160 2161 static int 2162 cache_dev(struct di_devlink_handle *hdp) 2163 { 2164 size_t sz; 2165 recurse_t rec = {NULL}; 2166 2167 assert(hdp); 2168 assert(HDL_RDONLY(hdp)); 2169 2170 if (hdp == NULL || !HDL_RDONLY(hdp)) { 2171 devlink_dprintf(DBG_ERR, "cache_dev: invalid arg\n"); 2172 return (-1); 2173 } 2174 2175 sz = MIN_HASH_SIZE; 2176 2177 CACHE(hdp)->hash = calloc(sz, sizeof (cache_link_t *)); 2178 if (CACHE(hdp)->hash == NULL) { 2179 return (-1); 2180 } 2181 CACHE(hdp)->hash_sz = sz; 2182 2183 rec.data = NULL; 2184 rec.fcn = cache_dev_link; 2185 2186 return (recurse_dev(hdp, &rec)); 2187 } 2188 2189 static int 2190 walk_dev(struct di_devlink_handle *hdp, link_desc_t *linkp) 2191 { 2192 assert(hdp && linkp); 2193 assert(!DB_OPEN(hdp)); 2194 assert(HDL_RDONLY(hdp)); 2195 2196 if (hdp == NULL || !HDL_RDONLY(hdp) || DB_OPEN(hdp)) { 2197 devlink_dprintf(DBG_ERR, "walk_dev: invalid args\n"); 2198 return (-1); 2199 } 2200 2201 if (CACHE_EMPTY(hdp) && cache_dev(hdp) != 0) { 2202 devlink_dprintf(DBG_ERR, "walk_dev: /dev caching failed\n"); 2203 return (-1); 2204 } 2205 2206 if (linkp->minor_path) 2207 walk_cache_minor(hdp, linkp->minor_path, linkp); 2208 else 2209 walk_all_cache(hdp, linkp); 2210 2211 return (linkp->retval); 2212 } 2213 2214 /* ARGSUSED */ 2215 static int 2216 cache_dev_link(struct di_devlink_handle *hdp, void *data, const char *link) 2217 { 2218 int flags; 2219 cache_link_t *clp; 2220 char content[PATH_MAX]; 2221 2222 assert(HDL_RDWR(hdp) || HDL_RDONLY(hdp)); 2223 2224 if (s_readlink(link, content, sizeof (content)) < 0) { 2225 return (DI_WALK_CONTINUE); 2226 } 2227 2228 if (is_minor_node(content, NULL)) { 2229 flags = DI_PRIMARY_LINK; 2230 } else { 2231 flags = DI_SECONDARY_LINK; 2232 } 2233 2234 assert(strncmp(link, hdp->dev_dir, strlen(hdp->dev_dir)) == 0); 2235 2236 /* 2237 * Store only the part after <root-dir>/dev/ 2238 */ 2239 link += strlen(hdp->dev_dir) + 1; 2240 2241 if ((clp = add_link(hdp, link, content, flags)) != NULL) { 2242 SET_VALID_ATTR(clp->attr); 2243 } 2244 2245 return (DI_WALK_CONTINUE); 2246 } 2247 2248 2249 static int 2250 walk_all_links(struct di_devlink_handle *hdp, link_desc_t *linkp) 2251 { 2252 struct db_link *dlp; 2253 uint32_t nidx, eidx; 2254 2255 assert(DB_NUM(hdp, DB_LINK) >= 1); 2256 2257 eidx = DB_NUM(hdp, DB_LINK); 2258 2259 /* Skip the "NIL" (index == 0) link. */ 2260 for (nidx = 1; nidx < eidx; nidx++) { 2261 /* 2262 * Declare this local to the block with zero 2263 * initializer so that it gets rezeroed 2264 * for each iteration. 2265 */ 2266 struct di_devlink vlink = {NULL}; 2267 2268 if ((dlp = get_link(hdp, nidx)) == NULL) 2269 continue; 2270 2271 vlink.rel_path = get_string(hdp, dlp->path); 2272 vlink.content = get_string(hdp, dlp->content); 2273 vlink.type = attr2type(dlp->attr); 2274 2275 if (visit_link(hdp, linkp, &vlink) != DI_WALK_CONTINUE) { 2276 break; 2277 } 2278 } 2279 2280 return (linkp->retval); 2281 } 2282 2283 static int 2284 walk_matching_links(struct di_devlink_handle *hdp, link_desc_t *linkp) 2285 { 2286 uint32_t nidx; 2287 struct db_link *dlp; 2288 struct db_minor *dmp; 2289 2290 assert(linkp->minor_path != NULL); 2291 2292 dmp = lookup_minor(hdp, linkp->minor_path, NULL, TYPE_DB); 2293 2294 /* 2295 * If a minor matching the path exists, walk that minor's devlinks list. 2296 * Then walk the dangling devlinks list. Non-matching devlinks will be 2297 * filtered out in visit_link. 2298 */ 2299 for (;;) { 2300 nidx = dmp ? dmp->link : DB_HDR(hdp)->dngl_idx; 2301 for (; dlp = get_link(hdp, nidx); nidx = dlp->sib) { 2302 struct di_devlink vlink = {NULL}; 2303 2304 vlink.rel_path = get_string(hdp, dlp->path); 2305 vlink.content = get_string(hdp, dlp->content); 2306 vlink.type = attr2type(dlp->attr); 2307 2308 if (visit_link(hdp, linkp, &vlink) != DI_WALK_CONTINUE) 2309 goto out; 2310 } 2311 if (dmp == NULL) { 2312 break; 2313 } else { 2314 dmp = NULL; 2315 } 2316 } 2317 2318 out: 2319 return (linkp->retval); 2320 } 2321 2322 static int 2323 visit_link( 2324 struct di_devlink_handle *hdp, 2325 link_desc_t *linkp, 2326 struct di_devlink *vlp) 2327 { 2328 struct stat sbuf; 2329 const char *minor_path = NULL; 2330 char abs_path[PATH_MAX], cont[PATH_MAX]; 2331 2332 /* 2333 * It is legal for the link's content and type to be unknown. 2334 * but one of absolute or relative path must be set. 2335 */ 2336 if (vlp->rel_path == NULL && vlp->abs_path == NULL) { 2337 (void) devlink_dprintf(DBG_ERR, 2338 "visit_link: invalid arguments\n"); 2339 return (DI_WALK_CONTINUE); 2340 } 2341 2342 if (vlp->rel_path == NULL) { 2343 vlp->rel_path = (char *)rel_path(hdp, vlp->abs_path); 2344 if (vlp->rel_path == NULL || vlp->rel_path[0] == '\0') 2345 return (DI_WALK_CONTINUE); 2346 } 2347 2348 if (linkp->regp) { 2349 if (regexec(linkp->regp, vlp->rel_path, 0, NULL, 0) != 0) 2350 return (DI_WALK_CONTINUE); 2351 } 2352 2353 if (vlp->abs_path == NULL) { 2354 assert(vlp->rel_path[0] != '/'); 2355 (void) snprintf(abs_path, sizeof (abs_path), "%s/%s", 2356 hdp->dev_dir, vlp->rel_path); 2357 vlp->abs_path = abs_path; 2358 } 2359 2360 if (vlp->content == NULL) { 2361 if (s_readlink(vlp->abs_path, cont, sizeof (cont)) < 0) { 2362 return (DI_WALK_CONTINUE); 2363 } 2364 vlp->content = cont; 2365 } 2366 2367 2368 if (vlp->type == 0) { 2369 if (is_minor_node(vlp->content, &minor_path)) { 2370 vlp->type = DI_PRIMARY_LINK; 2371 } else { 2372 vlp->type = DI_SECONDARY_LINK; 2373 } 2374 } 2375 2376 /* 2377 * Filter based on minor path 2378 */ 2379 if (linkp->minor_path) { 2380 char tmp[PATH_MAX]; 2381 2382 /* 2383 * derive minor path 2384 */ 2385 if (vlp->type == DI_SECONDARY_LINK) { 2386 2387 #ifdef DEBUG 2388 /*LINTED*/ 2389 assert(sizeof (tmp) >= PATH_MAX); 2390 #endif 2391 if (s_realpath(vlp->abs_path, tmp) == NULL) 2392 return (DI_WALK_CONTINUE); 2393 2394 if (!is_minor_node(tmp, &minor_path)) 2395 return (DI_WALK_CONTINUE); 2396 2397 } else if (minor_path == NULL) { 2398 if (!is_minor_node(vlp->content, &minor_path)) 2399 return (DI_WALK_CONTINUE); 2400 } 2401 2402 assert(minor_path != NULL); 2403 2404 if (strcmp(linkp->minor_path, minor_path) != 0) 2405 return (DI_WALK_CONTINUE); 2406 } 2407 2408 /* 2409 * Filter based on link type 2410 */ 2411 if (!TYPE_NONE(linkp->flags) && LINK_TYPE(linkp->flags) != vlp->type) { 2412 return (DI_WALK_CONTINUE); 2413 } 2414 2415 if (lstat(vlp->abs_path, &sbuf) < 0) { 2416 devlink_dprintf(DBG_ERR, "visit_link: %s: lstat failed: %s\n", 2417 vlp->abs_path, strerror(errno)); 2418 return (DI_WALK_CONTINUE); 2419 } 2420 2421 return (linkp->fcn(vlp, linkp->arg)); 2422 } 2423 2424 static int 2425 devlink_valid(di_devlink_t devlink) 2426 { 2427 if (devlink == NULL || devlink->rel_path == NULL || 2428 devlink->abs_path == NULL || devlink->content == NULL || 2429 TYPE_NONE(devlink->type)) { 2430 return (0); 2431 } 2432 2433 return (1); 2434 } 2435 2436 const char * 2437 di_devlink_path(di_devlink_t devlink) 2438 { 2439 if (!devlink_valid(devlink)) { 2440 errno = EINVAL; 2441 return (NULL); 2442 } 2443 2444 return (devlink->abs_path); 2445 } 2446 2447 const char * 2448 di_devlink_content(di_devlink_t devlink) 2449 { 2450 if (!devlink_valid(devlink)) { 2451 errno = EINVAL; 2452 return (NULL); 2453 } 2454 2455 return (devlink->content); 2456 } 2457 2458 int 2459 di_devlink_type(di_devlink_t devlink) 2460 { 2461 if (!devlink_valid(devlink)) { 2462 errno = EINVAL; 2463 return (-1); 2464 } 2465 2466 return (devlink->type); 2467 } 2468 2469 di_devlink_t 2470 di_devlink_dup(di_devlink_t devlink) 2471 { 2472 struct di_devlink *duplink; 2473 2474 if (!devlink_valid(devlink)) { 2475 errno = EINVAL; 2476 return (NULL); 2477 } 2478 2479 if ((duplink = calloc(1, sizeof (struct di_devlink))) == NULL) { 2480 return (NULL); 2481 } 2482 2483 duplink->rel_path = strdup(devlink->rel_path); 2484 duplink->abs_path = strdup(devlink->abs_path); 2485 duplink->content = strdup(devlink->content); 2486 duplink->type = devlink->type; 2487 2488 if (!devlink_valid(duplink)) { 2489 (void) di_devlink_free(duplink); 2490 errno = ENOMEM; 2491 return (NULL); 2492 } 2493 2494 return (duplink); 2495 } 2496 2497 int 2498 di_devlink_free(di_devlink_t devlink) 2499 { 2500 if (devlink == NULL) { 2501 errno = EINVAL; 2502 return (-1); 2503 } 2504 2505 free(devlink->rel_path); 2506 free(devlink->abs_path); 2507 free(devlink->content); 2508 free(devlink); 2509 2510 return (0); 2511 } 2512 2513 /* 2514 * Obtain path relative to dev_dir 2515 */ 2516 static const char * 2517 rel_path(struct di_devlink_handle *hdp, const char *path) 2518 { 2519 const size_t len = strlen(hdp->dev_dir); 2520 2521 if (strncmp(path, hdp->dev_dir, len) != 0) 2522 return (NULL); 2523 2524 if (path[len] == '\0') 2525 return (&path[len]); 2526 2527 if (path[len] != '/') 2528 return (NULL); 2529 2530 return (&path[len+1]); 2531 } 2532 2533 static int 2534 recurse_dev(struct di_devlink_handle *hdp, recurse_t *rp) 2535 { 2536 int ret = 0; 2537 2538 (void) do_recurse(hdp->dev_dir, hdp, rp, &ret); 2539 2540 return (ret); 2541 } 2542 2543 static int 2544 do_recurse( 2545 const char *dir, 2546 struct di_devlink_handle *hdp, 2547 recurse_t *rp, 2548 int *retp) 2549 { 2550 size_t len; 2551 const char *rel; 2552 struct stat sbuf; 2553 char cur[PATH_MAX], *cp; 2554 int i, rv = DI_WALK_CONTINUE; 2555 finddevhdl_t handle; 2556 char *d_name; 2557 2558 2559 if ((rel = rel_path(hdp, dir)) == NULL) 2560 return (DI_WALK_CONTINUE); 2561 2562 /* 2563 * Skip directories we are not interested in. 2564 */ 2565 for (i = 0; i < N_SKIP_DIRS; i++) { 2566 if (strcmp(rel, skip_dirs[i]) == 0) { 2567 (void) devlink_dprintf(DBG_STEP, 2568 "do_recurse: skipping %s\n", dir); 2569 return (DI_WALK_CONTINUE); 2570 } 2571 } 2572 2573 (void) devlink_dprintf(DBG_STEP, "do_recurse: dir = %s\n", dir); 2574 2575 if (finddev_readdir(dir, &handle) != 0) 2576 return (DI_WALK_CONTINUE); 2577 2578 (void) snprintf(cur, sizeof (cur), "%s/", dir); 2579 len = strlen(cur); 2580 cp = cur + len; 2581 len = sizeof (cur) - len; 2582 2583 for (;;) { 2584 if ((d_name = (char *)finddev_next(handle)) == NULL) 2585 break; 2586 2587 if (strlcpy(cp, d_name, len) >= len) 2588 break; 2589 2590 /* 2591 * Skip files we are not interested in. 2592 */ 2593 for (i = 0; i < N_SKIP_FILES; i++) { 2594 2595 rel = rel_path(hdp, cur); 2596 if (rel == NULL || strcmp(rel, skip_files[i]) == 0) { 2597 (void) devlink_dprintf(DBG_STEP, 2598 "do_recurse: skipping %s\n", cur); 2599 goto next_entry; 2600 } 2601 } 2602 2603 if (lstat(cur, &sbuf) == 0) { 2604 if (S_ISDIR(sbuf.st_mode)) { 2605 rv = do_recurse(cur, hdp, rp, retp); 2606 } else if (S_ISLNK(sbuf.st_mode)) { 2607 rv = rp->fcn(hdp, rp->data, cur); 2608 } else { 2609 (void) devlink_dprintf(DBG_STEP, 2610 "do_recurse: Skipping entry: %s\n", cur); 2611 } 2612 } else { 2613 (void) devlink_dprintf(DBG_ERR, 2614 "do_recurse: cur(%s): lstat failed: %s\n", 2615 cur, strerror(errno)); 2616 } 2617 2618 next_entry: 2619 *cp = '\0'; 2620 2621 if (rv != DI_WALK_CONTINUE) 2622 break; 2623 } 2624 2625 finddev_close(handle); 2626 2627 return (rv); 2628 } 2629 2630 2631 static int 2632 check_attr(uint32_t attr) 2633 { 2634 switch (attr & A_LINK_TYPES) { 2635 case A_PRIMARY: 2636 case A_SECONDARY: 2637 return (1); 2638 default: 2639 devlink_dprintf(DBG_ERR, 2640 "check_attr: incorrect attr(%u)\n", attr); 2641 return (0); 2642 } 2643 } 2644 2645 static int 2646 attr2type(uint32_t attr) 2647 { 2648 switch (attr & A_LINK_TYPES) { 2649 case A_PRIMARY: 2650 return (DI_PRIMARY_LINK); 2651 case A_SECONDARY: 2652 return (DI_SECONDARY_LINK); 2653 default: 2654 devlink_dprintf(DBG_ERR, 2655 "attr2type: incorrect attr(%u)\n", attr); 2656 return (0); 2657 } 2658 } 2659 2660 /* Allocate new node and link it in */ 2661 static cache_node_t * 2662 node_insert( 2663 struct di_devlink_handle *hdp, 2664 cache_node_t *pcnp, 2665 const char *path, 2666 int insert) 2667 { 2668 cache_node_t *cnp; 2669 2670 if (path == NULL) { 2671 errno = EINVAL; 2672 SET_DB_ERR(hdp); 2673 return (NULL); 2674 } 2675 2676 if ((cnp = calloc(1, sizeof (cache_node_t))) == NULL) { 2677 SET_DB_ERR(hdp); 2678 return (NULL); 2679 } 2680 2681 if ((cnp->path = strdup(path)) == NULL) { 2682 SET_DB_ERR(hdp); 2683 free(cnp); 2684 return (NULL); 2685 } 2686 2687 cnp->parent = pcnp; 2688 2689 if (pcnp == NULL) { 2690 assert(strcmp(path, "/") == 0); 2691 assert(CACHE(hdp)->root == NULL); 2692 CACHE(hdp)->root = cnp; 2693 } else if (insert == INSERT_HEAD) { 2694 cnp->sib = pcnp->child; 2695 pcnp->child = cnp; 2696 } else if (CACHE_LAST(hdp) && CACHE_LAST(hdp)->node && 2697 CACHE_LAST(hdp)->node->parent == pcnp && 2698 CACHE_LAST(hdp)->node->sib == NULL) { 2699 2700 CACHE_LAST(hdp)->node->sib = cnp; 2701 2702 } else { 2703 cache_node_t **pp; 2704 2705 for (pp = &pcnp->child; *pp != NULL; pp = &(*pp)->sib) 2706 ; 2707 *pp = cnp; 2708 } 2709 2710 return (cnp); 2711 } 2712 2713 /* 2714 * Allocate a new minor and link it in either at the tail or head 2715 * of the minor list depending on the value of "prev". 2716 */ 2717 static cache_minor_t * 2718 minor_insert( 2719 struct di_devlink_handle *hdp, 2720 cache_node_t *pcnp, 2721 const char *name, 2722 const char *nodetype, 2723 cache_minor_t **prev) 2724 { 2725 cache_minor_t *cmnp; 2726 2727 if (pcnp == NULL || name == NULL) { 2728 errno = EINVAL; 2729 SET_DB_ERR(hdp); 2730 return (NULL); 2731 } 2732 2733 /* 2734 * Some pseudo drivers don't specify nodetype. Assume pseudo if 2735 * nodetype is not specified. 2736 */ 2737 if (nodetype == NULL) 2738 nodetype = DDI_PSEUDO; 2739 2740 if ((cmnp = calloc(1, sizeof (cache_minor_t))) == NULL) { 2741 SET_DB_ERR(hdp); 2742 return (NULL); 2743 } 2744 2745 cmnp->name = strdup(name); 2746 cmnp->nodetype = strdup(nodetype); 2747 if (cmnp->name == NULL || cmnp->nodetype == NULL) { 2748 SET_DB_ERR(hdp); 2749 free(cmnp->name); 2750 free(cmnp->nodetype); 2751 free(cmnp); 2752 return (NULL); 2753 } 2754 2755 cmnp->node = pcnp; 2756 2757 /* Add to node's minor list */ 2758 if (prev == NULL) { 2759 cmnp->sib = pcnp->minor; 2760 pcnp->minor = cmnp; 2761 } else { 2762 assert(*prev == NULL); 2763 *prev = cmnp; 2764 } 2765 2766 return (cmnp); 2767 } 2768 2769 static cache_link_t * 2770 link_insert( 2771 struct di_devlink_handle *hdp, 2772 cache_minor_t *cmnp, 2773 const char *path, 2774 const char *content, 2775 uint32_t attr) 2776 { 2777 cache_link_t *clp; 2778 2779 if (path == NULL || content == NULL || !check_attr(attr)) { 2780 errno = EINVAL; 2781 SET_DB_ERR(hdp); 2782 return (NULL); 2783 } 2784 2785 if ((clp = calloc(1, sizeof (cache_link_t))) == NULL) { 2786 SET_DB_ERR(hdp); 2787 return (NULL); 2788 } 2789 2790 clp->path = strdup(path); 2791 clp->content = strdup(content); 2792 if (clp->path == NULL || clp->content == NULL) { 2793 SET_DB_ERR(hdp); 2794 link_free(&clp); 2795 return (NULL); 2796 } 2797 2798 clp->attr = attr; 2799 hash_insert(hdp, clp); 2800 clp->minor = cmnp; 2801 2802 /* Add to minor's link list */ 2803 if (cmnp != NULL) { 2804 clp->sib = cmnp->link; 2805 cmnp->link = clp; 2806 } else { 2807 clp->sib = CACHE(hdp)->dngl; 2808 CACHE(hdp)->dngl = clp; 2809 } 2810 2811 return (clp); 2812 } 2813 2814 static void 2815 hash_insert(struct di_devlink_handle *hdp, cache_link_t *clp) 2816 { 2817 uint_t hval; 2818 2819 hval = hashfn(hdp, clp->path); 2820 clp->hash = CACHE_HASH(hdp, hval); 2821 CACHE_HASH(hdp, hval) = clp; 2822 } 2823 2824 2825 static struct db_node * 2826 get_node(struct di_devlink_handle *hdp, uint32_t idx) 2827 { 2828 return (map_seg(hdp, idx, PROT_READ, DB_NODE)); 2829 } 2830 2831 static struct db_node * 2832 set_node(struct di_devlink_handle *hdp, uint32_t idx) 2833 { 2834 return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_NODE)); 2835 } 2836 2837 static struct db_minor * 2838 get_minor(struct di_devlink_handle *hdp, uint32_t idx) 2839 { 2840 return (map_seg(hdp, idx, PROT_READ, DB_MINOR)); 2841 } 2842 2843 static struct db_minor * 2844 set_minor(struct di_devlink_handle *hdp, uint32_t idx) 2845 { 2846 return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_MINOR)); 2847 } 2848 2849 static struct db_link * 2850 get_link(struct di_devlink_handle *hdp, uint32_t idx) 2851 { 2852 return (map_seg(hdp, idx, PROT_READ, DB_LINK)); 2853 } 2854 2855 static struct db_link * 2856 set_link(struct di_devlink_handle *hdp, uint32_t idx) 2857 { 2858 return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_LINK)); 2859 } 2860 2861 static char * 2862 get_string(struct di_devlink_handle *hdp, uint32_t idx) 2863 { 2864 return (map_seg(hdp, idx, PROT_READ, DB_STR)); 2865 } 2866 2867 static char * 2868 set_string(struct di_devlink_handle *hdp, uint32_t idx) 2869 { 2870 return (map_seg(hdp, idx, PROT_READ | PROT_WRITE, DB_STR)); 2871 } 2872 2873 2874 /* 2875 * Returns the element corresponding to idx. If the portion of file involved 2876 * is not yet mapped, does an mmap() as well. Existing mappings are not changed. 2877 */ 2878 static void * 2879 map_seg( 2880 struct di_devlink_handle *hdp, 2881 uint32_t idx, 2882 int prot, 2883 db_seg_t seg) 2884 { 2885 int s; 2886 off_t off; 2887 size_t slen; 2888 caddr_t addr; 2889 2890 if (idx == DB_NIL) { 2891 return (NULL); 2892 } 2893 2894 if (!VALID_INDEX(hdp, seg, idx)) { 2895 (void) devlink_dprintf(DBG_ERR, 2896 "map_seg: seg(%d): invalid idx(%u)\n", seg, idx); 2897 return (NULL); 2898 } 2899 2900 /* 2901 * If the seg is already mapped in, use it if the access type is 2902 * valid. 2903 */ 2904 if (DB_SEG(hdp, seg) != NULL) { 2905 if (DB_SEG_PROT(hdp, seg) != prot) { 2906 (void) devlink_dprintf(DBG_ERR, 2907 "map_seg: illegal access: " 2908 "seg[%d]: idx=%u, seg_prot=%d, access=%d\n", 2909 seg, idx, DB_SEG_PROT(hdp, seg), prot); 2910 return (NULL); 2911 } 2912 return (DB_SEG(hdp, seg) + idx * elem_sizes[seg]); 2913 } 2914 2915 /* 2916 * Segment is not mapped. Mmap() the segment. 2917 */ 2918 off = seg_size(hdp, DB_HEADER); 2919 for (s = 0; s < seg; s++) { 2920 off += seg_size(hdp, s); 2921 } 2922 slen = seg_size(hdp, seg); 2923 2924 addr = mmap(0, slen, prot, MAP_SHARED, DB(hdp)->db_fd, off); 2925 if (addr == MAP_FAILED) { 2926 (void) devlink_dprintf(DBG_ERR, 2927 "map_seg: seg[%d]: mmap failed: %s\n", seg, 2928 strerror(errno)); 2929 (void) devlink_dprintf(DBG_ERR, 2930 "map_seg: args: len=%lu, prot=%d, fd=%d, off=%ld\n", 2931 (ulong_t)slen, prot, DB(hdp)->db_fd, off); 2932 return (NULL); 2933 } 2934 2935 DB_SEG(hdp, seg) = addr; 2936 DB_SEG_PROT(hdp, seg) = prot; 2937 2938 (void) devlink_dprintf(DBG_STEP, "map_seg: seg[%d]: len=%lu, prot=%d, " 2939 "fd=%d, off=%ld, seg_base=%p\n", seg, (ulong_t)slen, prot, 2940 DB(hdp)->db_fd, off, (void *)addr); 2941 2942 return (DB_SEG(hdp, seg) + idx * elem_sizes[seg]); 2943 } 2944 2945 /* 2946 * Computes the size of a segment rounded up to the nearest page boundary. 2947 */ 2948 static size_t 2949 seg_size(struct di_devlink_handle *hdp, int seg) 2950 { 2951 size_t sz; 2952 2953 assert(DB_HDR(hdp)->page_sz); 2954 2955 if (seg == DB_HEADER) { 2956 sz = HDR_LEN; 2957 } else { 2958 assert(DB_NUM(hdp, seg) >= 1); 2959 sz = DB_NUM(hdp, seg) * elem_sizes[seg]; 2960 } 2961 2962 sz = (sz / DB_HDR(hdp)->page_sz) + 1; 2963 2964 sz *= DB_HDR(hdp)->page_sz; 2965 2966 return (sz); 2967 } 2968 2969 static size_t 2970 size_db(struct di_devlink_handle *hdp, long page_sz, uint32_t *count) 2971 { 2972 int i; 2973 size_t sz; 2974 cache_link_t *clp; 2975 2976 assert(page_sz > 0); 2977 2978 /* Take "NIL" element into account */ 2979 for (i = 0; i < DB_TYPES; i++) { 2980 count[i] = 1; 2981 } 2982 2983 count_node(CACHE(hdp)->root, count); 2984 2985 for (clp = CACHE(hdp)->dngl; clp != NULL; clp = clp->sib) { 2986 count_link(clp, count); 2987 } 2988 2989 sz = ((HDR_LEN / page_sz) + 1) * page_sz; 2990 for (i = 0; i < DB_TYPES; i++) { 2991 assert(count[i] >= 1); 2992 sz += (((count[i] * elem_sizes[i]) / page_sz) + 1) * page_sz; 2993 (void) devlink_dprintf(DBG_INFO, "N[%u]=%u\n", i, count[i]); 2994 } 2995 (void) devlink_dprintf(DBG_INFO, "DB size=%lu\n", (ulong_t)sz); 2996 2997 return (sz); 2998 } 2999 3000 3001 static void 3002 count_node(cache_node_t *cnp, uint32_t *count) 3003 { 3004 cache_minor_t *cmnp; 3005 3006 if (cnp == NULL) 3007 return; 3008 3009 count[DB_NODE]++; 3010 count_string(cnp->path, count); 3011 3012 for (cmnp = cnp->minor; cmnp != NULL; cmnp = cmnp->sib) { 3013 count_minor(cmnp, count); 3014 } 3015 3016 for (cnp = cnp->child; cnp != NULL; cnp = cnp->sib) { 3017 count_node(cnp, count); 3018 } 3019 3020 } 3021 3022 static void 3023 count_minor(cache_minor_t *cmnp, uint32_t *count) 3024 { 3025 cache_link_t *clp; 3026 3027 if (cmnp == NULL) 3028 return; 3029 3030 count[DB_MINOR]++; 3031 count_string(cmnp->name, count); 3032 count_string(cmnp->nodetype, count); 3033 3034 for (clp = cmnp->link; clp != NULL; clp = clp->sib) { 3035 count_link(clp, count); 3036 } 3037 } 3038 3039 static void 3040 count_link(cache_link_t *clp, uint32_t *count) 3041 { 3042 if (clp == NULL) 3043 return; 3044 3045 count[DB_LINK]++; 3046 count_string(clp->path, count); 3047 count_string(clp->content, count); 3048 } 3049 3050 3051 static void 3052 count_string(const char *str, uint32_t *count) 3053 { 3054 if (str == NULL) { 3055 (void) devlink_dprintf(DBG_ERR, 3056 "count_string: NULL argument\n"); 3057 return; 3058 } 3059 3060 count[DB_STR] += strlen(str) + 1; 3061 } 3062 3063 static uint_t 3064 hashfn(struct di_devlink_handle *hdp, const char *str) 3065 { 3066 const char *cp; 3067 ulong_t hval = 0; 3068 3069 if (str == NULL) { 3070 return (0); 3071 } 3072 3073 assert(CACHE(hdp)->hash_sz >= MIN_HASH_SIZE); 3074 3075 for (cp = str; *cp != '\0'; cp++) { 3076 hval += *cp; 3077 } 3078 3079 return (hval % CACHE(hdp)->hash_sz); 3080 } 3081 3082 /* 3083 * enter_db_lock() 3084 * 3085 * If the handle is IS_RDWR then we lock as writer to "update" database, 3086 * if IS_RDONLY then we lock as reader to "snapshot" database. The 3087 * implementation uses advisory file locking. 3088 * 3089 * This function returns: 3090 * == 1 success and grabbed the lock file, we can open the DB. 3091 * == 0 success but did not lock the lock file, reader must walk 3092 * the /dev directory. 3093 * == -1 failure. 3094 */ 3095 static int 3096 enter_db_lock(struct di_devlink_handle *hdp, const char *root_dir) 3097 { 3098 int fd; 3099 struct flock lock; 3100 char lockfile[PATH_MAX]; 3101 int rv; 3102 int writer = HDL_RDWR(hdp); 3103 static int did_sync = 0; 3104 int eintrs; 3105 3106 assert(hdp->lock_fd < 0); 3107 3108 get_db_path(hdp, DB_LOCK, lockfile, sizeof (lockfile)); 3109 3110 devlink_dprintf(DBG_LCK, "enter_db_lock: %s BEGIN\n", 3111 writer ? "update" : "snapshot"); 3112 3113 /* Record locks are per-process. Protect against multiple threads. */ 3114 (void) mutex_lock(&update_mutex); 3115 3116 again: if ((fd = open(lockfile, 3117 (writer ? (O_RDWR|O_CREAT) : O_RDONLY), DB_LOCK_PERMS)) < 0) { 3118 /* 3119 * Typically the lock file and the database go hand in hand. 3120 * If we find that the lock file does not exist (for some 3121 * unknown reason) and we are the reader then we return 3122 * success (after triggering devfsadm to create the file and 3123 * a retry) so that we can still provide service via slow 3124 * /dev walk. If we get a failure as a writer we want the 3125 * error to manifests itself. 3126 */ 3127 if ((errno == ENOENT) && !writer) { 3128 /* If reader, signal once to get files created */ 3129 if (did_sync == 0) { 3130 did_sync = 1; 3131 devlink_dprintf(DBG_LCK, 3132 "enter_db_lock: %s OSYNC\n", 3133 writer ? "update" : "snapshot"); 3134 3135 /* signal to get files created */ 3136 (void) devlink_create(root_dir, NULL, 3137 DCA_DEVLINK_SYNC); 3138 goto again; 3139 } 3140 devlink_dprintf(DBG_LCK, 3141 "enter_db_lock: %s OPENFAILD %s: WALK\n", 3142 writer ? "update" : "snapshot", strerror(errno)); 3143 (void) mutex_unlock(&update_mutex); 3144 return (0); /* success, but not locked */ 3145 } else { 3146 devlink_dprintf(DBG_LCK, 3147 "enter_db_lock: %s OPENFAILD %s\n", 3148 writer ? "update" : "snapshot", strerror(errno)); 3149 (void) mutex_unlock(&update_mutex); 3150 return (-1); /* failed */ 3151 } 3152 } 3153 3154 lock.l_type = writer ? F_WRLCK : F_RDLCK; 3155 lock.l_whence = SEEK_SET; 3156 lock.l_start = 0; 3157 lock.l_len = 0; 3158 3159 /* Enter the lock. */ 3160 for (eintrs = 0; eintrs < MAX_LOCK_RETRY; eintrs++) { 3161 rv = fcntl(fd, F_SETLKW, &lock); 3162 if ((rv != -1) || (errno != EINTR)) 3163 break; 3164 } 3165 3166 if (rv != -1) { 3167 hdp->lock_fd = fd; 3168 devlink_dprintf(DBG_LCK, "enter_db_lock: %s LOCKED\n", 3169 writer ? "update" : "snapshot"); 3170 return (1); /* success, locked */ 3171 } 3172 3173 (void) close(fd); 3174 devlink_dprintf(DBG_ERR, "enter_db_lock: %s FAILED: %s: WALK\n", 3175 writer ? "update" : "snapshot", strerror(errno)); 3176 (void) mutex_unlock(&update_mutex); 3177 return (-1); 3178 } 3179 3180 /* 3181 * Close and re-open lock file every time so that it is recreated if deleted. 3182 */ 3183 static void 3184 exit_db_lock(struct di_devlink_handle *hdp) 3185 { 3186 struct flock unlock; 3187 int writer = HDL_RDWR(hdp); 3188 3189 if (hdp->lock_fd < 0) { 3190 return; 3191 } 3192 3193 unlock.l_type = F_UNLCK; 3194 unlock.l_whence = SEEK_SET; 3195 unlock.l_start = 0; 3196 unlock.l_len = 0; 3197 3198 devlink_dprintf(DBG_LCK, "exit_db_lock : %s UNLOCKED\n", 3199 writer ? "update" : "snapshot"); 3200 if (fcntl(hdp->lock_fd, F_SETLK, &unlock) == -1) { 3201 devlink_dprintf(DBG_ERR, "exit_db_lock : %s failed: %s\n", 3202 writer ? "update" : "snapshot", strerror(errno)); 3203 } 3204 3205 (void) close(hdp->lock_fd); 3206 3207 hdp->lock_fd = -1; 3208 3209 (void) mutex_unlock(&update_mutex); 3210 } 3211 3212 /* 3213 * returns 1 if contents is a minor node in /devices. 3214 * If mn_root is not NULL, mn_root is set to: 3215 * if contents is a /dev node, mn_root = contents 3216 * OR 3217 * if contents is a /devices node, mn_root set to the '/' 3218 * following /devices. 3219 */ 3220 int 3221 is_minor_node(const char *contents, const char **mn_root) 3222 { 3223 char *ptr, *prefix; 3224 3225 prefix = "../devices/"; 3226 3227 if ((ptr = strstr(contents, prefix)) != NULL) { 3228 3229 /* mn_root should point to the / following /devices */ 3230 if (mn_root != NULL) { 3231 *mn_root = ptr += strlen(prefix) - 1; 3232 } 3233 return (1); 3234 } 3235 3236 prefix = "/devices/"; 3237 3238 if (strncmp(contents, prefix, strlen(prefix)) == 0) { 3239 3240 /* mn_root should point to the / following /devices/ */ 3241 if (mn_root != NULL) { 3242 *mn_root = contents + strlen(prefix) - 1; 3243 } 3244 return (1); 3245 } 3246 3247 if (mn_root != NULL) { 3248 *mn_root = contents; 3249 } 3250 return (0); 3251 } 3252 3253 static int 3254 s_readlink(const char *link, char *buf, size_t blen) 3255 { 3256 int rv; 3257 3258 if ((rv = readlink(link, buf, blen)) == -1) 3259 goto bad; 3260 3261 if (rv >= blen && buf[blen - 1] != '\0') { 3262 errno = ENAMETOOLONG; 3263 goto bad; 3264 } else if (rv < blen) { 3265 buf[rv] = '\0'; 3266 } 3267 3268 return (0); 3269 bad: 3270 devlink_dprintf(DBG_ERR, "s_readlink: %s: failed: %s\n", 3271 link, strerror(errno)); 3272 return (-1); 3273 } 3274 3275 /* 3276 * Synchronous link creation interface routines 3277 * The scope of the operation is determined by the "name" arg. 3278 * "name" can be NULL, a driver name or a devfs pathname (without /devices) 3279 * 3280 * "name" creates 3281 * ====== ======= 3282 * 3283 * NULL => All devlinks in system 3284 * <driver> => devlinks for named driver 3285 * /pci@1 => devlinks for subtree rooted at pci@1 3286 * /pseudo/foo@0:X => devlinks for minor X 3287 * 3288 * devlink_create() returns 0 on success or an errno value on failure 3289 */ 3290 3291 #define MAX_DAEMON_ATTEMPTS 2 3292 3293 static int 3294 devlink_create(const char *root, const char *name, int dca_devlink_flag) 3295 { 3296 int i; 3297 int install; 3298 struct dca_off dca; 3299 3300 assert(root); 3301 3302 /* 3303 * Convert name into arg for door_call 3304 */ 3305 if (dca_init(name, &dca, dca_devlink_flag) != 0) 3306 return (EINVAL); 3307 3308 /* 3309 * Attempt to use the daemon first 3310 */ 3311 i = 0; 3312 do { 3313 install = daemon_call(root, &dca); 3314 3315 devlink_dprintf(DBG_INFO, "daemon_call() retval=%d\n", 3316 dca.dca_error); 3317 3318 /* 3319 * Retry only if door server isn't running 3320 */ 3321 if (dca.dca_error != ENOENT && dca.dca_error != EBADF) { 3322 return (dca.dca_error); 3323 } 3324 3325 dca.dca_error = 0; 3326 3327 /* 3328 * To improve performance defer this check until the first 3329 * failure. Safe to defer as door server checks perms. 3330 */ 3331 if (geteuid() != 0) 3332 return (EPERM); 3333 /* 3334 * Daemon may not be running. Try to start it. 3335 */ 3336 } while ((++i < MAX_DAEMON_ATTEMPTS) && 3337 start_daemon(root, install) == 0); 3338 3339 devlink_dprintf(DBG_INFO, "devlink_create: can't start daemon\n"); 3340 3341 assert(dca.dca_error == 0); 3342 3343 /* 3344 * If the daemon cannot be started execute the devfsadm command. 3345 */ 3346 exec_cmd(root, &dca); 3347 3348 return (dca.dca_error); 3349 } 3350 3351 /* 3352 * The "name" member of "struct dca" contains data in the following order 3353 * root'\0'minor'\0'driver'\0' 3354 * The root component is always present at offset 0 in the "name" field. 3355 * The driver and minor are optional. If present they have a non-zero 3356 * offset in the "name" member. 3357 */ 3358 static int 3359 dca_init(const char *name, struct dca_off *dcp, int dca_flags) 3360 { 3361 char *cp; 3362 3363 dcp->dca_root = 0; 3364 dcp->dca_minor = 0; 3365 dcp->dca_driver = 0; 3366 dcp->dca_error = 0; 3367 dcp->dca_flags = dca_flags; 3368 dcp->dca_name[0] = '\0'; 3369 3370 name = name ? name : "/"; 3371 3372 /* 3373 * Check if name is a driver name 3374 */ 3375 if (*name != '/') { 3376 (void) snprintf(dcp->dca_name, sizeof (dcp->dca_name), 3377 "/ %s", name); 3378 dcp->dca_root = 0; 3379 *(dcp->dca_name + 1) = '\0'; 3380 dcp->dca_driver = 2; 3381 return (0); 3382 } 3383 3384 (void) snprintf(dcp->dca_name, sizeof (dcp->dca_name), "%s", name); 3385 3386 /* 3387 * "/devices" not allowed in devfs pathname 3388 */ 3389 if (is_minor_node(name, NULL)) 3390 return (-1); 3391 3392 dcp->dca_root = 0; 3393 if (cp = strrchr(dcp->dca_name, ':')) { 3394 *cp++ = '\0'; 3395 dcp->dca_minor = cp - dcp->dca_name; 3396 } 3397 3398 return (0); 3399 } 3400 3401 3402 #define DAEMON_STARTUP_TIME 1 /* 1 second. This may need to be adjusted */ 3403 #define DEVNAME_CHECK_FILE "/etc/devname_check_RDONLY" 3404 3405 static int 3406 daemon_call(const char *root, struct dca_off *dcp) 3407 { 3408 door_arg_t arg; 3409 int fd, door_error; 3410 sigset_t oset, nset; 3411 char synch_door[PATH_MAX]; 3412 struct stat sb; 3413 char *prefix; 3414 int rofd; 3415 int rdonly; 3416 int install = 0; 3417 3418 /* 3419 * If root is readonly, there are two possibilities: 3420 * - we are in some sort of install scenario 3421 * - we are early in boot 3422 * If the latter we don't want daemon_call() to succeed. 3423 * else we want to use /tmp/etc/dev 3424 * 3425 * Both of these requrements are fulfilled if we check for 3426 * for a root owned door file in /tmp/etc/dev. If we are 3427 * early in boot, the door file won't exist, so this call 3428 * will fail. 3429 * 3430 * If we are in install, the door file will be present. 3431 * 3432 * If root is read-only, try only once, since libdevinfo 3433 * isn't capable of starting devfsadmd correctly in that 3434 * situation. 3435 * 3436 * Don't use statvfs() to check for readonly roots since it 3437 * doesn't always report the truth. 3438 */ 3439 rofd = -1; 3440 rdonly = 0; 3441 if ((rofd = open(DEVNAME_CHECK_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)) 3442 == -1 && errno == EROFS) { 3443 rdonly = 1; 3444 prefix = "/tmp"; 3445 } else { 3446 if (rofd != -1) { 3447 (void) close(rofd); 3448 (void) unlink(DEVNAME_CHECK_FILE); 3449 } 3450 prefix = (char *)root; 3451 } 3452 3453 if (rdonly && stat(DEVNAME_CHECK_FILE, &sb) != -1) 3454 install = 1; 3455 3456 (void) snprintf(synch_door, sizeof (synch_door), 3457 "%s/etc/dev/%s", prefix, DEVFSADM_SYNCH_DOOR); 3458 3459 /* 3460 * Return ENOTSUP to prevent retries if root is readonly 3461 */ 3462 if (stat(synch_door, &sb) == -1 || sb.st_uid != 0) { 3463 if (rdonly) 3464 dcp->dca_error = ENOTSUP; 3465 else 3466 dcp->dca_error = ENOENT; 3467 devlink_dprintf(DBG_ERR, 3468 "stat failed: %s: no file or not root owned\n", synch_door); 3469 return (install); 3470 } 3471 3472 if ((fd = open(synch_door, O_RDONLY)) == -1) { 3473 dcp->dca_error = errno; 3474 devlink_dprintf(DBG_ERR, "open of %s failed: %s\n", 3475 synch_door, strerror(errno)); 3476 return (install); 3477 } 3478 3479 arg.data_ptr = (char *)dcp; 3480 arg.data_size = sizeof (*dcp); 3481 arg.desc_ptr = NULL; 3482 arg.desc_num = 0; 3483 arg.rbuf = (char *)dcp; 3484 arg.rsize = sizeof (*dcp); 3485 3486 /* 3487 * Block signals to this thread until door call 3488 * completes. 3489 */ 3490 (void) sigfillset(&nset); 3491 (void) sigemptyset(&oset); 3492 (void) sigprocmask(SIG_SETMASK, &nset, &oset); 3493 if (door_call(fd, &arg)) { 3494 door_error = 1; 3495 dcp->dca_error = errno; 3496 } 3497 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 3498 3499 (void) close(fd); 3500 3501 if (door_error) 3502 return (install); 3503 3504 assert(arg.data_ptr); 3505 3506 /*LINTED*/ 3507 dcp->dca_error = ((struct dca_off *)arg.data_ptr)->dca_error; 3508 3509 /* 3510 * The doors interface may return data in a different buffer 3511 * If that happens, deallocate buffer via munmap() 3512 */ 3513 if (arg.rbuf != (char *)dcp) 3514 (void) munmap(arg.rbuf, arg.rsize); 3515 3516 return (install); 3517 } 3518 3519 #define DEVFSADM_PATH "/usr/sbin/devfsadm" 3520 #define DEVFSADM "devfsadm" 3521 3522 #define DEVFSADMD_PATH "/usr/lib/devfsadm/devfsadmd" 3523 #define DEVFSADM_DAEMON "devfsadmd" 3524 3525 static int 3526 start_daemon(const char *root, int install) 3527 { 3528 int rv, i = 0; 3529 char *argv[20]; 3530 3531 argv[i++] = DEVFSADM_DAEMON; 3532 if (install) { 3533 argv[i++] = "-a"; 3534 argv[i++] = "/tmp"; 3535 argv[i++] = "-p"; 3536 argv[i++] = "/tmp/root/etc/path_to_inst"; 3537 } else if (strcmp(root, "/")) { 3538 argv[i++] = "-r"; 3539 argv[i++] = (char *)root; 3540 } 3541 argv[i++] = NULL; 3542 3543 rv = do_exec(DEVFSADMD_PATH, argv); 3544 3545 (void) sleep(DAEMON_STARTUP_TIME); 3546 3547 return (rv); 3548 } 3549 3550 static void 3551 exec_cmd(const char *root, struct dca_off *dcp) 3552 { 3553 int i; 3554 char *argv[20]; 3555 3556 i = 0; 3557 argv[i++] = DEVFSADM; 3558 3559 /* 3560 * Load drivers only if -i is specified 3561 */ 3562 if (dcp->dca_driver) { 3563 argv[i++] = "-i"; 3564 argv[i++] = &dcp->dca_name[dcp->dca_driver]; 3565 } else { 3566 argv[i++] = "-n"; 3567 } 3568 3569 if (root != NULL && strcmp(root, "/") != 0) { 3570 argv[i++] = "-r"; 3571 argv[i++] = (char *)root; 3572 } 3573 3574 argv[i] = NULL; 3575 3576 if (do_exec(DEVFSADM_PATH, argv)) 3577 dcp->dca_error = errno; 3578 } 3579 3580 static int 3581 do_exec(const char *path, char *const argv[]) 3582 { 3583 int i; 3584 pid_t cpid; 3585 3586 #ifdef DEBUG 3587 devlink_dprintf(DBG_INFO, "Executing %s\n\tArgument list:", path); 3588 for (i = 0; argv[i] != NULL; i++) { 3589 devlink_dprintf(DBG_INFO, " %s", argv[i]); 3590 } 3591 devlink_dprintf(DBG_INFO, "\n"); 3592 #endif 3593 3594 if ((cpid = fork1()) == -1) { 3595 devlink_dprintf(DBG_ERR, "fork1 failed: %s\n", strerror(errno)); 3596 return (-1); 3597 } 3598 3599 if (cpid == 0) { /* child process */ 3600 int fd; 3601 3602 if ((fd = open("/dev/null", O_RDWR)) >= 0) { 3603 (void) dup2(fd, fileno(stdout)); 3604 (void) dup2(fd, fileno(stderr)); 3605 (void) close(fd); 3606 3607 (void) execv(path, argv); 3608 } else { 3609 devlink_dprintf(DBG_ERR, 3610 "open of /dev/null failed: %s\n", strerror(errno)); 3611 } 3612 3613 _exit(-1); 3614 } 3615 3616 /* Parent process */ 3617 if (waitpid(cpid, &i, 0) == cpid) { 3618 if (WIFEXITED(i)) { 3619 if (WEXITSTATUS(i) == 0) { 3620 devlink_dprintf(DBG_STEP, 3621 "do_exec: child exited normally\n"); 3622 return (0); 3623 } else 3624 errno = EINVAL; 3625 } else { 3626 /* 3627 * The child was interrupted by a signal 3628 */ 3629 errno = EINTR; 3630 } 3631 devlink_dprintf(DBG_ERR, "child terminated abnormally: %s\n", 3632 strerror(errno)); 3633 } else { 3634 devlink_dprintf(DBG_ERR, "waitpid failed: %s\n", 3635 strerror(errno)); 3636 } 3637 3638 return (-1); 3639 } 3640 3641 static int 3642 walk_cache_links(di_devlink_handle_t hdp, cache_link_t *clp, link_desc_t *linkp) 3643 { 3644 int i; 3645 3646 assert(HDL_RDWR(hdp) || HDL_RDONLY(hdp)); 3647 3648 devlink_dprintf(DBG_INFO, "walk_cache_links: initial link: %s\n", 3649 clp ? clp->path : "<NULL>"); 3650 3651 /* 3652 * First search the links under the specified minor. On the 3653 * 2nd pass, search the dangling list - secondary links may 3654 * exist on this list since they are not resolved during the 3655 * /dev walk. 3656 */ 3657 for (i = 0; i < 2; i++) { 3658 for (; clp != NULL; clp = clp->sib) { 3659 struct di_devlink vlink = {NULL}; 3660 3661 assert(clp->path[0] != '/'); 3662 3663 vlink.rel_path = clp->path; 3664 vlink.content = clp->content; 3665 vlink.type = attr2type(clp->attr); 3666 3667 if (visit_link(hdp, linkp, &vlink) 3668 != DI_WALK_CONTINUE) { 3669 devlink_dprintf(DBG_INFO, "walk_cache_links: " 3670 "terminating at link: %s\n", clp->path); 3671 goto out; 3672 } 3673 } 3674 3675 clp = CACHE(hdp)->dngl; 3676 } 3677 3678 out: 3679 3680 /* If i < 2, we terminated the walk prematurely */ 3681 return (i < 2 ? DI_WALK_TERMINATE : DI_WALK_CONTINUE); 3682 } 3683 3684 static void 3685 walk_all_cache(di_devlink_handle_t hdp, link_desc_t *linkp) 3686 { 3687 int i; 3688 cache_link_t *clp; 3689 3690 devlink_dprintf(DBG_INFO, "walk_all_cache: entered\n"); 3691 3692 for (i = 0; i < CACHE(hdp)->hash_sz; i++) { 3693 clp = CACHE_HASH(hdp, i); 3694 for (; clp; clp = clp->hash) { 3695 struct di_devlink vlink = {NULL}; 3696 3697 assert(clp->path[0] != '/'); 3698 3699 vlink.rel_path = clp->path; 3700 vlink.content = clp->content; 3701 vlink.type = attr2type(clp->attr); 3702 if (visit_link(hdp, linkp, &vlink) != 3703 DI_WALK_CONTINUE) { 3704 devlink_dprintf(DBG_INFO, "walk_all_cache: " 3705 "terminating walk at link: %s\n", 3706 clp->path); 3707 return; 3708 } 3709 } 3710 } 3711 } 3712 3713 static void 3714 walk_cache_minor(di_devlink_handle_t hdp, const char *mpath, link_desc_t *linkp) 3715 { 3716 cache_minor_t *cmnp; 3717 3718 assert(mpath); 3719 3720 if ((cmnp = lookup_minor(hdp, mpath, NULL, TYPE_CACHE)) != NULL) { 3721 (void) walk_cache_links(hdp, cmnp->link, linkp); 3722 } else { 3723 devlink_dprintf(DBG_ERR, "lookup minor failed: %s\n", mpath); 3724 } 3725 } 3726 3727 static void 3728 walk_cache_node(di_devlink_handle_t hdp, const char *path, link_desc_t *linkp) 3729 { 3730 cache_minor_t *cmnp; 3731 cache_node_t *cnp; 3732 3733 assert(path); 3734 3735 if ((cnp = lookup_node(hdp, (char *)path, TYPE_CACHE)) == NULL) { 3736 devlink_dprintf(DBG_ERR, "lookup node failed: %s\n", path); 3737 return; 3738 } 3739 3740 for (cmnp = cnp->minor; cmnp != NULL; cmnp = cmnp->sib) { 3741 if (walk_cache_links(hdp, cmnp->link, linkp) 3742 == DI_WALK_TERMINATE) 3743 break; 3744 } 3745 } 3746 3747 /* 3748 * Private function 3749 * 3750 * Walk cached links corresponding to the given path. 3751 * 3752 * path path to a node or minor node. 3753 * 3754 * flags specifies the type of devlinks to be selected. 3755 * If DI_PRIMARY_LINK is used, only primary links are selected. 3756 * If DI_SECONDARY_LINK is specified, only secondary links 3757 * are selected. 3758 * If neither flag is specified, all devlinks are selected. 3759 * 3760 * re An extended regular expression in regex(7) format which 3761 * selects the /dev links to be returned. The regular 3762 * expression should use link pathnames relative to 3763 * /dev. i.e. without the leading "/dev/" prefix. 3764 * A NULL value matches all devlinks. 3765 */ 3766 int 3767 di_devlink_cache_walk(di_devlink_handle_t hdp, 3768 const char *re, 3769 const char *path, 3770 uint_t flags, 3771 void *arg, 3772 int (*devlink_callback)(di_devlink_t, void *)) 3773 { 3774 regex_t reg; 3775 link_desc_t linkd = {NULL}; 3776 3777 if (hdp == NULL || path == NULL || !link_flag(flags) || 3778 !HDL_RDWR(hdp) || devlink_callback == NULL) { 3779 errno = EINVAL; 3780 return (-1); 3781 } 3782 3783 linkd.flags = flags; 3784 linkd.arg = arg; 3785 linkd.fcn = devlink_callback; 3786 3787 if (re) { 3788 if (regcomp(®, re, REG_EXTENDED) != 0) 3789 return (-1); 3790 linkd.regp = ® 3791 } 3792 3793 if (minor_colon(path) == NULL) { 3794 walk_cache_node(hdp, path, &linkd); 3795 } else { 3796 walk_cache_minor(hdp, path, &linkd); 3797 } 3798 3799 if (re) 3800 regfree(®); 3801 3802 return (0); 3803 } 3804 3805 #define DEBUG_ENV_VAR "_DEVLINK_DEBUG" 3806 static int _devlink_debug = -1; 3807 3808 /* 3809 * debug level is initialized to -1. 3810 * On first call into this routine, debug level is set. 3811 * If debug level is zero, debugging msgs are disabled. 3812 */ 3813 static void 3814 debug_print(debug_level_t msglevel, const char *fmt, va_list ap) 3815 { 3816 char *cp; 3817 int save; 3818 3819 /* 3820 * We shouldn't be here if debug is disabled 3821 */ 3822 assert(_devlink_debug != 0); 3823 3824 /* 3825 * Set debug level on first call into this routine 3826 */ 3827 if (_devlink_debug < 0) { 3828 if ((cp = getenv(DEBUG_ENV_VAR)) == NULL) { 3829 _devlink_debug = 0; 3830 return; 3831 } 3832 3833 save = errno; 3834 errno = 0; 3835 _devlink_debug = strtol(cp, NULL, 10); 3836 if (errno != 0 || _devlink_debug < 0) { 3837 _devlink_debug = 0; 3838 errno = save; 3839 return; 3840 } 3841 errno = save; 3842 3843 if (!_devlink_debug) 3844 return; 3845 } 3846 3847 /* debug msgs are enabled */ 3848 assert(_devlink_debug > 0); 3849 3850 if (_devlink_debug < msglevel) 3851 return; 3852 if ((_devlink_debug == DBG_LCK) && (msglevel != _devlink_debug)) 3853 return; 3854 3855 /* Print a distinctive label for error msgs */ 3856 if (msglevel == DBG_ERR) { 3857 (void) fprintf(stderr, "[ERROR]: "); 3858 } 3859 3860 (void) vfprintf(stderr, fmt, ap); 3861 (void) fflush(stderr); 3862 } 3863 3864 /* ARGSUSED */ 3865 /* PRINTFLIKE2 */ 3866 void 3867 devlink_dprintf(debug_level_t msglevel, const char *fmt, ...) 3868 { 3869 va_list ap; 3870 3871 assert(msglevel > 0); 3872 if (!_devlink_debug) 3873 return; 3874 3875 va_start(ap, fmt); 3876 debug_print(msglevel, fmt, ap); 3877 va_end(ap); 3878 } 3879