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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* Portions Copyright 2005 Cyril Plisko */ 24 25 /* 26 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <errno.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <locale.h> 37 #include <langinfo.h> 38 #include <time.h> 39 40 #if !defined(DEBUG) 41 #define NDEBUG 1 42 #else 43 #undef NDEBUG 44 #endif 45 46 #include <assert.h> 47 #include <sys/types.h> 48 #include <sys/stat.h> 49 #include <sys/param.h> 50 #include <dlfcn.h> 51 #include <synch.h> 52 #include <sys/systeminfo.h> 53 #include <sys/sunddi.h> 54 #include <libdevinfo.h> 55 #include <unistd.h> 56 #include <stdarg.h> 57 #include <limits.h> 58 #include <ftw.h> 59 #include <ctype.h> 60 61 #define CFGA_PLUGIN_LIB 62 #include <config_admin.h> 63 64 /* Limit size of sysinfo return */ 65 #define SYSINFO_LENGTH 256 66 67 /* 68 * Attachment point specifier types. 69 */ 70 typedef enum { 71 UNKNOWN_AP, 72 LOGICAL_LINK_AP, 73 LOGICAL_DRV_AP, 74 PHYSICAL_AP, 75 AP_TYPE 76 } cfga_ap_types_t; 77 78 static char *listopt_array[] = { 79 80 #define LISTOPT_CLASS 0 81 "class", 82 NULL 83 }; 84 85 typedef struct { 86 int v_min; /* Min acceptable version */ 87 int v_max; /* Max acceptable version */ 88 } vers_req_t; 89 90 #define INVALID_VERSION -1 91 #define VALID_HSL_VERS(v) (((v) >= CFGA_HSL_V1) && \ 92 ((v) <= CFGA_HSL_VERS)) 93 94 /* 95 * Incomplete definition 96 */ 97 struct cfga_vers_ops; 98 99 /* 100 * Structure that contains plugin library information. 101 */ 102 typedef struct plugin_lib { 103 struct plugin_lib *next; /* pointer to next */ 104 mutex_t lock; /* protects refcnt */ 105 int refcnt; /* reference count */ 106 void *handle; /* handle from dlopen */ 107 cfga_err_t (*cfga_change_state_p)(); 108 cfga_err_t (*cfga_private_func_p)(); 109 cfga_err_t (*cfga_test_p)(); 110 cfga_err_t (*cfga_stat_p)(); 111 cfga_err_t (*cfga_list_p)(); 112 cfga_err_t (*cfga_help_p)(); 113 int (*cfga_ap_id_cmp_p)(); 114 cfga_err_t (*cfga_list_ext_p)(); /* For V2 plug-ins only */ 115 116 int plugin_vers; /* actual plugin version */ 117 struct cfga_vers_ops *vers_ops; /* version dependant routines */ 118 char libpath[MAXPATHLEN]; /* full pathname to lib */ 119 } plugin_lib_t; 120 121 static plugin_lib_t plugin_list; 122 123 typedef struct lib_cache { 124 struct lib_cache *lc_next; 125 plugin_lib_t *lc_libp; 126 char *lc_ap_id; 127 char *lc_ap_physical; /* physical ap_id */ 128 char *lc_ap_logical; /* logical ap_id */ 129 } lib_cache_t; 130 131 static lib_cache_t *lib_cache; 132 static mutex_t lib_cache_lock; 133 134 /* 135 * Library locator data struct - used to pass down through the device 136 * tree walking code. 137 */ 138 typedef struct lib_locator { 139 char ap_base[MAXPATHLEN]; 140 char ap_logical[CFGA_LOG_EXT_LEN]; 141 char ap_physical[CFGA_PHYS_EXT_LEN]; 142 char ap_class[CFGA_CLASS_LEN]; 143 char pathname[MAXPATHLEN]; 144 plugin_lib_t *libp; 145 cfga_err_t status; 146 vers_req_t vers_req; /* plug-in version required */ 147 } lib_loc_t; 148 149 /* 150 * linked list of cfga_stat_data structs - used for 151 * config_list 152 */ 153 typedef struct stat_data_list { 154 struct stat_data_list *next; 155 cfga_stat_data_t stat_data; 156 } stat_data_list_t; 157 158 /* 159 * linked list of arrays. Each array represents a bunch 160 * of list_data_t structures returned by a single call 161 * to a plugin's cfga_list_ext() routine. 162 */ 163 typedef struct array_list { 164 struct array_list *next; 165 cfga_list_data_t *array; 166 int nelem; 167 } array_list_t; 168 169 /* 170 * encapsulate config_list args to get them through the tree 171 * walking code 172 */ 173 typedef struct list_stat { 174 const char *opts; /* Hardware specific options */ 175 char **errstr; 176 cfga_flags_t flags; 177 int *countp; /* Total number of list and stat structures */ 178 stat_data_list_t *sdl; /* Linked list of stat structures */ 179 array_list_t *al; /* Linked list of arrays of list structures */ 180 vers_req_t use_vers; /* plugin versions to be stat'ed */ 181 } list_stat_t; 182 183 /* 184 * Internal operations for libcfgadm which are version dependant 185 */ 186 struct cfga_vers_ops { 187 cfga_err_t (*resolve_lib)(plugin_lib_t *libp); 188 cfga_err_t (*stat_plugin)(list_stat_t *, lib_loc_t *, char **errstring); 189 cfga_err_t (*mklog)(di_node_t, di_minor_t, plugin_lib_t *, 190 lib_loc_t *liblocp); 191 cfga_err_t (*get_cond)(lib_loc_t *, cfga_cond_t *, char **); 192 }; 193 194 195 /* 196 * Lock to protect list of libraries 197 */ 198 static mutex_t plugin_list_lock; 199 200 /* 201 * Forward declarations 202 */ 203 204 static const char *__config_strerror(cfga_err_t); 205 static void *config_calloc_check(size_t, size_t, char **); 206 static cfga_err_t resolve_lib_ref(plugin_lib_t *, lib_loc_t *); 207 static cfga_err_t config_get_lib(const char *, lib_loc_t *, char **); 208 static int check_ap(di_node_t, di_minor_t, void *); 209 static int check_ap_phys(di_node_t, di_minor_t, void *); 210 211 static cfga_err_t find_ap_common(lib_loc_t *libloc_p, const char *rootpath, 212 int (*fcn)(di_node_t node, di_minor_t minor, void *arg), char **errstring); 213 214 static plugin_lib_t *lib_in_list(char *); 215 static cfga_err_t load_lib(di_node_t, di_minor_t, lib_loc_t *); 216 extern void bcopy(const void *, void *, size_t); 217 static void config_err(int, int, char **); 218 static void hold_lib(plugin_lib_t *); 219 static void rele_lib(plugin_lib_t *); 220 221 static cfga_err_t parse_listopt(char *listopts, char **classpp, 222 char **errstring); 223 224 static cfga_err_t list_common(list_stat_t *lstatp, const char *class); 225 static int do_list_common(di_node_t node, di_minor_t minor, void *arg); 226 static cfga_err_t stat_common(int num_ap_ids, char *const *ap_ids, 227 const char *class, list_stat_t *lstatp); 228 229 static cfga_err_t null_resolve(plugin_lib_t *libp); 230 static cfga_err_t resolve_v1(plugin_lib_t *libp); 231 static cfga_err_t resolve_v2(plugin_lib_t *libp); 232 233 static cfga_err_t mklog_common(di_node_t node, di_minor_t minor, 234 lib_loc_t *liblocp, size_t len); 235 236 static cfga_err_t null_mklog(di_node_t node, di_minor_t minor, 237 plugin_lib_t *libp, lib_loc_t *liblocp); 238 static cfga_err_t mklog_v1(di_node_t node, di_minor_t minor, 239 plugin_lib_t *libp, lib_loc_t *liblocp); 240 static cfga_err_t mklog_v2(di_node_t node, di_minor_t minor, 241 plugin_lib_t *libp, lib_loc_t *liblocp); 242 243 static cfga_err_t null_stat_plugin(list_stat_t *lstatp, lib_loc_t *libloc_p, 244 char **errstring); 245 static cfga_err_t stat_plugin_v2(list_stat_t *lstat, lib_loc_t *libloc_p, 246 char **errstring); 247 static cfga_err_t stat_plugin_v1(list_stat_t *lstat, lib_loc_t *libloc_p, 248 char **errstring); 249 250 static cfga_err_t null_get_cond(lib_loc_t *liblocp, cfga_cond_t *condp, 251 char **errstring); 252 static cfga_err_t get_cond_v1(lib_loc_t *liblocp, cfga_cond_t *condp, 253 char **errstring); 254 static cfga_err_t get_cond_v2(lib_loc_t *liblocp, cfga_cond_t *condp, 255 char **errstring); 256 257 static cfga_err_t realloc_data(cfga_stat_data_t **ap_id_list, 258 int *nlistp, list_stat_t *lstatp); 259 static cfga_err_t realloc_data_ext(cfga_list_data_t **ap_id_list, 260 int *nlistp, list_stat_t *lstatp); 261 262 static void stat_to_list(cfga_list_data_t *lp, cfga_stat_data_t *statp); 263 static void lstat_free(list_stat_t *lstatp); 264 static cfga_ap_types_t find_arg_type(const char *ap_id); 265 static int compat_plugin(vers_req_t *reqp, int plugin_vers); 266 267 static cfga_err_t check_flags(cfga_flags_t flags, cfga_flags_t mask, 268 char **errstring); 269 static cfga_err_t check_apids(int num_ap_ids, char *const *ap_ids, 270 char **errstring); 271 272 static char *get_class(di_minor_t minor); 273 static cfga_err_t split_apid(char *ap_id, char **dyncompp, char **errstring); 274 static void append_dyn(char *buf, const char *dyncomp, size_t blen); 275 static int default_ap_id_cmp(const char *ap_id1, const char *ap_id2); 276 static void destroy_cache(); 277 278 /* 279 * Plugin library search path helpers 280 */ 281 #define LIB_PATH_BASE1 "/usr/platform/" 282 #define LIB_PATH_BASE2 "/usr" 283 #if defined(__sparcv9) 284 #define LIB_PATH_MIDDLE "/lib/cfgadm/sparcv9/" 285 #elif defined(__amd64) 286 #define LIB_PATH_MIDDLE "/lib/cfgadm/amd64/" 287 #else 288 #define LIB_PATH_MIDDLE "/lib/cfgadm/" 289 #endif 290 #define LIB_PATH_TAIL ".so.1" 291 292 293 #if !defined(TEXT_DOMAIN) 294 #define TEXT_DOMAIN "SYS_TEST" 295 #endif 296 297 /* 298 * Defined constants 299 */ 300 #define DEVICES_DIR "/devices" 301 #define DOT_DOT_DEVICES "../devices" 302 #define CFGA_DEV_DIR "/dev/cfg" 303 #define SLASH "/" 304 #define S_FREE(x) (((x) != NULL) ? (free(x), (x) = NULL) : (void *)0) 305 #define GET_DYN(a) (strstr((a), CFGA_DYN_SEP)) 306 307 #define CFGA_NO_CLASS "none" 308 309 /* 310 * Error strings 311 */ 312 #define DI_INIT_FAILED 1 313 #define ALLOC_FAILED 2 314 #define INVALID_ARGS 3 315 316 static char * 317 err_strings[] = { 318 NULL, 319 "Device library initialize failed", 320 "Memory allocation failed", 321 "Invalid argument(s)" 322 }; 323 324 static const char err_sep[] = ": "; 325 326 327 /* 328 * Table of version dependant routines 329 */ 330 static struct cfga_vers_ops cfga_vers_ops[CFGA_HSL_VERS + 1] = { 331 332 {null_resolve, null_stat_plugin, null_mklog, null_get_cond }, 333 {resolve_v1, stat_plugin_v1, mklog_v1, get_cond_v1 }, 334 {resolve_v2, stat_plugin_v2, mklog_v2, get_cond_v2 } 335 336 }; 337 #define VERS_ARRAY_SZ (sizeof (cfga_vers_ops)/sizeof (cfga_vers_ops[0])) 338 339 340 /* 341 * Public interfaces for libcfgadm, as documented in config_admin.3x 342 */ 343 344 /* 345 * config_change_state 346 */ 347 348 cfga_err_t 349 config_change_state( 350 cfga_cmd_t state_change_cmd, 351 int num_ap_ids, 352 char *const *ap_id, 353 const char *options, 354 struct cfga_confirm *confp, 355 struct cfga_msg *msgp, 356 char **errstring, 357 cfga_flags_t flags) 358 { 359 /* 360 * for each arg - 361 * load hs library, 362 * if force 363 * call cfga_state_change_func 364 * return status 365 * else 366 * call it's cfga_stat 367 * check condition 368 * call cfga_state_change_func 369 * return status 370 */ 371 int i; 372 lib_loc_t libloc; 373 plugin_lib_t *libp; 374 cfga_cond_t cond; 375 376 cfga_err_t retval = CFGA_OK; 377 378 /* Sanity checks */ 379 if (state_change_cmd == CFGA_CMD_NONE) 380 return (retval); 381 382 if ((state_change_cmd < CFGA_CMD_NONE) || 383 (state_change_cmd > CFGA_CMD_UNCONFIGURE)) 384 return (CFGA_INVAL); 385 386 if (errstring != NULL) { 387 *errstring = NULL; 388 } 389 390 if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, errstring) 391 != CFGA_OK) { 392 return (CFGA_ERROR); 393 } 394 395 if (check_apids(num_ap_ids, ap_id, errstring) != CFGA_OK) { 396 return (CFGA_ERROR); 397 } 398 399 /* 400 * operate on each ap_id 401 */ 402 for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) { 403 libloc.libp = NULL; 404 if ((retval = config_get_lib(ap_id[i], &libloc, errstring)) != 405 CFGA_OK) { 406 break; 407 } 408 409 libp = libloc.libp; 410 if ((flags & CFGA_FLAG_FORCE) || 411 (state_change_cmd == CFGA_CMD_UNLOAD) || 412 (state_change_cmd == CFGA_CMD_DISCONNECT) || 413 (state_change_cmd == CFGA_CMD_UNCONFIGURE)) { 414 errno = 0; 415 retval = (*libp->cfga_change_state_p) 416 (state_change_cmd, libloc.ap_physical, options, 417 confp, msgp, errstring, flags); 418 } else { 419 /* 420 * Need to check condition before proceeding in 421 * the "configure direction" 422 */ 423 if ((retval = libp->vers_ops->get_cond(&libloc, &cond, 424 errstring)) != CFGA_OK) { 425 break; 426 } 427 428 if (cond == CFGA_COND_OK || cond == CFGA_COND_UNKNOWN) { 429 errno = 0; 430 retval = 431 (*libp->cfga_change_state_p)( 432 state_change_cmd, 433 libloc.ap_physical, options, 434 confp, msgp, errstring, 435 flags); 436 } else { 437 retval = CFGA_INSUFFICENT_CONDITION; 438 } 439 } 440 rele_lib(libp); 441 } 442 443 return (retval); 444 } 445 446 /* 447 * config_private_func 448 */ 449 450 cfga_err_t 451 config_private_func( 452 const char *function, 453 int num_ap_ids, 454 char *const *ap_ids, 455 const char *options, 456 struct cfga_confirm *confp, 457 struct cfga_msg *msgp, 458 char **errstring, 459 cfga_flags_t flags) 460 { 461 int i; 462 lib_loc_t libloc; 463 cfga_err_t retval = CFGA_OK; 464 465 466 if (errstring != NULL) { 467 *errstring = NULL; 468 } 469 470 if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, errstring) 471 != CFGA_OK) { 472 return (CFGA_ERROR); 473 } 474 475 if (check_apids(num_ap_ids, ap_ids, errstring) != CFGA_OK) { 476 return (CFGA_ERROR); 477 } 478 479 /* 480 * operate on each ap_id 481 */ 482 for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) { 483 libloc.libp = NULL; 484 if ((retval = config_get_lib(ap_ids[i], &libloc, errstring)) != 485 CFGA_OK) { 486 return (retval); 487 } 488 489 errno = 0; 490 retval = (*libloc.libp->cfga_private_func_p)(function, 491 libloc.ap_physical, options, confp, msgp, errstring, 492 flags); 493 rele_lib(libloc.libp); 494 } 495 496 return (retval); 497 } 498 499 500 /* 501 * config_test 502 */ 503 504 cfga_err_t 505 config_test( 506 int num_ap_ids, 507 char *const *ap_ids, 508 const char *options, 509 struct cfga_msg *msgp, 510 char **errstring, 511 cfga_flags_t flags) 512 { 513 int i; 514 lib_loc_t libloc; 515 cfga_err_t retval = CFGA_OK; 516 517 if (errstring != NULL) { 518 *errstring = NULL; 519 } 520 521 if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, errstring) 522 != CFGA_OK) { 523 return (CFGA_ERROR); 524 } 525 526 if (check_apids(num_ap_ids, ap_ids, errstring) != CFGA_OK) { 527 return (CFGA_ERROR); 528 } 529 530 /* 531 * operate on each ap_id 532 */ 533 for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) { 534 libloc.libp = NULL; 535 if ((retval = config_get_lib(ap_ids[i], &libloc, errstring)) != 536 CFGA_OK) { 537 return (retval); 538 } 539 540 errno = 0; 541 retval = (*libloc.libp->cfga_test_p)(libloc.ap_physical, 542 options, msgp, errstring, flags); 543 rele_lib(libloc.libp); 544 } 545 546 return (retval); 547 } 548 549 cfga_err_t 550 config_stat( 551 int num_ap_ids, 552 char *const *ap_ids, 553 struct cfga_stat_data *buf, 554 const char *options, 555 char **errstring) 556 { 557 int nstat, n, i; 558 list_stat_t lstat = {NULL}; 559 cfga_err_t rc = CFGA_OK; 560 561 if (check_apids(num_ap_ids, ap_ids, errstring) != CFGA_OK) { 562 return (CFGA_ERROR); 563 } 564 565 /* 566 * V1 entry points don't support dynamic attachment points 567 */ 568 for (i = 0; i < num_ap_ids; i++) { 569 if (GET_DYN(ap_ids[i]) != NULL) { 570 return (CFGA_APID_NOEXIST); 571 } 572 } 573 574 575 nstat = n = 0; 576 lstat.countp = &nstat; 577 lstat.opts = options; 578 lstat.errstr = errstring; 579 /* 580 * This is a V1 interface which can use only V1 plugins 581 */ 582 lstat.use_vers.v_max = lstat.use_vers.v_min = CFGA_HSL_V1; 583 584 rc = stat_common(num_ap_ids, ap_ids, NULL, &lstat); 585 if (rc == CFGA_OK) { 586 assert(*lstat.countp == num_ap_ids); 587 rc = realloc_data(&buf, &n, &lstat); 588 } 589 590 return (rc); 591 } 592 593 /* 594 * config_list 595 */ 596 cfga_err_t 597 config_list( 598 struct cfga_stat_data **ap_id_list, 599 int *nlistp, 600 const char *options, 601 char **errstring) 602 { 603 int nstat; 604 list_stat_t lstat = {NULL}; 605 cfga_err_t retval = CFGA_ERROR; 606 607 if (errstring != NULL) { 608 *errstring = NULL; 609 } 610 611 nstat = 0; 612 lstat.countp = &nstat; 613 lstat.opts = options; 614 lstat.errstr = errstring; 615 /* 616 * This is a V1 interface which can use only V1 plugins 617 */ 618 lstat.use_vers.v_max = lstat.use_vers.v_min = CFGA_HSL_V1; 619 620 621 *ap_id_list = NULL; 622 *nlistp = 0; 623 624 /* 625 * V1 interfaces don't support prefiltering, no class 626 * specified. 627 */ 628 retval = list_common(&lstat, NULL); 629 if (retval == CFGA_OK) { 630 retval = realloc_data(ap_id_list, nlistp, &lstat); 631 } 632 633 assert((ap_id_list != NULL && *nlistp != 0) || 634 (ap_id_list == NULL && *nlistp == 0)); 635 636 if (retval == CFGA_OK && *nlistp == 0) { 637 return (CFGA_NOTSUPP); 638 } else { 639 return (retval); 640 } 641 } 642 643 644 /* 645 * config_list_ext 646 */ 647 cfga_err_t 648 config_list_ext( 649 int num_ap_ids, 650 char *const *ap_ids, 651 struct cfga_list_data **ap_id_list, 652 int *nlistp, 653 const char *options, 654 const char *listopts, 655 char **errstring, 656 cfga_flags_t flags) 657 { 658 int nstat, list, prefilter; 659 list_stat_t lstat = {NULL}; 660 char *class; 661 662 cfga_err_t rc = CFGA_ERROR; 663 664 *nlistp = 0; 665 *ap_id_list = NULL; 666 667 if (errstring != NULL) { 668 *errstring = NULL; 669 } 670 671 if (check_flags(flags, CFGA_FLAG_LIST_ALL, errstring) != CFGA_OK) { 672 return (CFGA_ERROR); 673 } 674 675 class = NULL; 676 if ((rc = parse_listopt((char *)listopts, &class, errstring)) 677 != CFGA_OK) { 678 return (rc); 679 } 680 681 prefilter = (class == NULL) ? 0 : 1; 682 683 nstat = 0; 684 lstat.countp = &nstat; 685 lstat.opts = options; 686 lstat.errstr = errstring; 687 lstat.flags = flags; 688 /* 689 * We support both V1 and V2 plugins through this entry 690 * point. 691 */ 692 lstat.use_vers.v_min = CFGA_HSL_V1; 693 lstat.use_vers.v_max = CFGA_HSL_V2; 694 695 list = 0; 696 if (num_ap_ids == 0 && ap_ids == NULL) { 697 /* 698 * discover and stat all attachment points 699 */ 700 list = 1; 701 rc = list_common(&lstat, class); 702 } else if (num_ap_ids > 0 && ap_ids != NULL) { 703 /* 704 * Stat specified attachment points. With dynamic expansion 705 * more data may be returned than was specified by user. 706 */ 707 rc = stat_common(num_ap_ids, ap_ids, class, &lstat); 708 } else { 709 rc = CFGA_ERROR; 710 } 711 712 S_FREE(class); 713 714 if (rc != CFGA_OK) { 715 return (rc); 716 } 717 718 rc = realloc_data_ext(ap_id_list, nlistp, &lstat); 719 720 assert((ap_id_list != NULL && *nlistp != 0) || 721 (ap_id_list == NULL && *nlistp == 0)); 722 723 /* 724 * For the list command notify user if no attachment 725 * point is found in the system. 726 * 727 */ 728 if (list && rc == CFGA_OK && *nlistp == 0) { 729 /* 730 * If attachment points are being prefiltered, absence of data 731 * does not imply that config. admin. is not 732 * supported by the system. 733 */ 734 if (prefilter) { 735 /* 736 * Prefiltering: requested class is absent 737 */ 738 return (CFGA_APID_NOEXIST); 739 } else { 740 /* 741 * No attachment points in system 742 */ 743 return (CFGA_NOTSUPP); 744 } 745 } else { 746 return (rc); 747 } 748 } 749 750 751 /* 752 * config_unload_libs 753 * 754 * Attempts to remove all libs on the plugin list. 755 */ 756 void 757 config_unload_libs() 758 { 759 plugin_lib_t *libp, *prev = &plugin_list, *next = NULL; 760 761 /* destroy cache entries to remove refcnt agains plugins */ 762 destroy_cache(); 763 764 (void) mutex_lock(&plugin_list_lock); 765 for (libp = plugin_list.next; libp != NULL; libp = next) { 766 next = libp->next; 767 (void) mutex_lock(&libp->lock); 768 if (libp->refcnt) { 769 (void) mutex_unlock(&libp->lock); 770 prev = libp; 771 continue; 772 } 773 (void) mutex_unlock(&libp->lock); 774 prev->next = next; 775 (void) dlclose(libp->handle); 776 (void) mutex_destroy(&libp->lock); 777 free(libp); 778 } 779 (void) mutex_unlock(&plugin_list_lock); 780 } 781 782 /* 783 * config_ap_id_cmp 784 */ 785 int 786 config_ap_id_cmp( 787 const cfga_ap_log_id_t ap1, 788 const cfga_ap_log_id_t ap2) 789 { 790 int ret; 791 lib_loc_t libloc; 792 char apstat1[CFGA_PHYS_EXT_LEN]; 793 char apstat2[CFGA_PHYS_EXT_LEN]; 794 char *sep1, *sep2; 795 796 /* 797 * Extract static ap_ids 798 */ 799 (void) strlcpy(apstat1, ap1, sizeof (apstat1)); 800 (void) strlcpy(apstat2, ap2, sizeof (apstat2)); 801 802 sep1 = GET_DYN(apstat1); 803 sep2 = GET_DYN(apstat2); 804 805 if (sep1) 806 *sep1 = '\0'; 807 if (sep2) 808 *sep2 = '\0'; 809 810 /* 811 * Use the default comparator for static ap_ids 812 */ 813 ret = default_ap_id_cmp(apstat1, apstat2); 814 if (ret) 815 return (ret); 816 817 /* 818 * static components match. They belong to 819 * the same static ap_id. Check if both are dynamic 820 * If not, static < dynamic. 821 */ 822 if ((sep1 == NULL) ^ (sep2 == NULL)) 823 return (sep1 ? 1 : -1); 824 825 /* 826 * If both are static, then ap1 = ap2 827 */ 828 if (sep1 == NULL) 829 return (0); 830 831 /* 832 * Both are dynamic and belong to same static ap_id. 833 * Use the plugin comparator 834 */ 835 libloc.libp = NULL; 836 if (config_get_lib(ap1, &libloc, NULL) != CFGA_OK) { 837 return (strncmp(sep1, sep2, CFGA_PHYS_EXT_LEN)); 838 } 839 840 ret = (*libloc.libp->cfga_ap_id_cmp_p)(ap1, ap2); 841 842 rele_lib(libloc.libp); 843 844 return (ret); 845 } 846 847 /* 848 * config_strerror 849 */ 850 851 const char * 852 config_strerror(cfga_err_t cfgerrnum) 853 { 854 const char *ep = NULL; 855 856 if ((cfgerrnum < CFGA_OK) || (cfgerrnum > CFGA_ATTR_INVAL)) 857 return (NULL); 858 859 ep = __config_strerror(cfgerrnum); 860 861 return ((ep != NULL) ? dgettext(TEXT_DOMAIN, ep) : NULL); 862 } 863 864 /* 865 * config_help 866 */ 867 cfga_err_t 868 config_help( 869 int num_ap_ids, 870 char *const *ap_ids, 871 struct cfga_msg *msgp, 872 const char *options, 873 cfga_flags_t flags) 874 { 875 int i; 876 lib_loc_t libloc; 877 cfga_err_t retval = CFGA_OK; 878 879 if (check_flags(flags, CFGA_FLAG_FORCE | CFGA_FLAG_VERBOSE, NULL) 880 != CFGA_OK) { 881 return (CFGA_ERROR); 882 } 883 884 if (num_ap_ids < 0) { 885 return (CFGA_ERROR); 886 } 887 888 if (num_ap_ids > 0 && ap_ids == NULL) { 889 return (CFGA_ERROR); 890 } 891 892 /* 893 * operate on each ap_id 894 */ 895 for (i = 0; (i < num_ap_ids) && (retval == CFGA_OK); i++) { 896 libloc.libp = NULL; 897 if ((retval = config_get_lib(ap_ids[i], &libloc, 898 NULL)) != CFGA_OK) { 899 return (retval); 900 } 901 902 errno = 0; 903 retval = (*libloc.libp->cfga_help_p)(msgp, options, flags); 904 rele_lib(libloc.libp); 905 } 906 return (retval); 907 } 908 909 /* 910 * Private support routines for the public interfaces 911 */ 912 913 static const char * 914 __config_strerror(cfga_err_t cfgerrnum) 915 { 916 const char *ep = NULL; 917 918 switch (cfgerrnum) { 919 case CFGA_OK: 920 ep = "Configuration operation succeeded"; 921 break; 922 case CFGA_NACK: 923 ep = "Configuration operation cancelled"; 924 break; 925 case CFGA_INVAL: 926 ep = "Configuration operation invalid"; 927 break; 928 case CFGA_NOTSUPP: 929 ep = "Configuration administration not supported"; 930 break; 931 case CFGA_OPNOTSUPP: 932 ep = "Configuration operation not supported"; 933 break; 934 case CFGA_PRIV: 935 ep = "Insufficient privileges"; 936 break; 937 case CFGA_BUSY: 938 ep = "Component system is busy, try again"; 939 break; 940 case CFGA_SYSTEM_BUSY: 941 ep = "System is busy, try again"; 942 break; 943 case CFGA_DATA_ERROR: 944 ep = "Data error"; 945 break; 946 case CFGA_LIB_ERROR: 947 ep = "Library error"; 948 break; 949 case CFGA_NO_LIB: 950 ep = "No Library found"; 951 break; 952 case CFGA_INSUFFICENT_CONDITION: 953 ep = "Insufficient condition"; 954 break; 955 case CFGA_ERROR: 956 ep = "Hardware specific failure"; 957 break; 958 case CFGA_APID_NOEXIST: 959 ep = "Attachment point not found"; 960 break; 961 case CFGA_ATTR_INVAL: 962 ep = "No attachment point with specified attributes found"; 963 break; 964 default: 965 ep = NULL; 966 break; 967 } 968 return (ep); 969 } 970 971 /* 972 * listopts is a string in the getsubopt(3C) style: 973 * name1=value1,name2=value2, 974 */ 975 static cfga_err_t 976 parse_listopt(char *listopts, char **classpp, char **errstring) 977 { 978 char *bufp, *optp, *val = NULL; 979 cfga_err_t rc = CFGA_ERROR; 980 981 *classpp = NULL; 982 983 /* 984 * NULL is a legal value for listopts 985 */ 986 if (listopts == NULL) { 987 return (CFGA_OK); 988 } 989 990 if ((bufp = config_calloc_check(1, strlen(listopts) + 1, errstring)) 991 == NULL) { 992 return (CFGA_LIB_ERROR); 993 } 994 (void) strcpy(bufp, listopts); 995 996 optp = bufp; /* getsubopt() modifies its argument */ 997 while (*optp != '\0') { 998 switch (getsubopt(&optp, listopt_array, &val)) { 999 case LISTOPT_CLASS: 1000 if (val == NULL || *classpp != NULL) { 1001 rc = CFGA_ERROR; 1002 goto out; 1003 } 1004 if ((*classpp = config_calloc_check(1, strlen(val) + 1, 1005 errstring)) == NULL) { 1006 rc = CFGA_LIB_ERROR; 1007 goto out; 1008 } 1009 (void) strcpy(*classpp, val); 1010 break; 1011 default: 1012 rc = CFGA_ERROR; 1013 goto out; 1014 } 1015 } 1016 1017 rc = CFGA_OK; 1018 /*FALLTHRU*/ 1019 out: 1020 S_FREE(bufp); 1021 if (rc != CFGA_OK) { 1022 S_FREE(*classpp); 1023 } 1024 return (rc); 1025 } 1026 1027 /*ARGSUSED*/ 1028 static cfga_err_t 1029 null_mklog( 1030 di_node_t node, 1031 di_minor_t minor, 1032 plugin_lib_t *libp, 1033 lib_loc_t *liblocp) 1034 { 1035 return (CFGA_OK); 1036 } 1037 1038 static cfga_err_t 1039 mklog_v1( 1040 di_node_t node, 1041 di_minor_t minor, 1042 plugin_lib_t *libp, 1043 lib_loc_t *liblocp) 1044 { 1045 const size_t len = CFGA_AP_LOG_ID_LEN; 1046 1047 assert(len <= sizeof (liblocp->ap_logical)); 1048 1049 if (libp->plugin_vers != CFGA_HSL_V1) { 1050 return (CFGA_LIB_ERROR); 1051 } 1052 1053 return (mklog_common(node, minor, liblocp, len)); 1054 } 1055 1056 1057 /* 1058 * Obtain the devlink from a /devices path 1059 */ 1060 static int 1061 get_link(di_devlink_t devlink, void *arg) 1062 { 1063 char *linkp = (char *)arg; 1064 1065 (void) snprintf(linkp, CFGA_LOG_EXT_LEN, "%s", 1066 di_devlink_path(devlink)); 1067 return (DI_WALK_TERMINATE); 1068 } 1069 1070 static cfga_err_t 1071 mklog_v2( 1072 di_node_t node, 1073 di_minor_t minor, 1074 plugin_lib_t *libp, 1075 lib_loc_t *liblocp) 1076 { 1077 const size_t len = CFGA_LOG_EXT_LEN; 1078 di_devlink_handle_t hdl; 1079 1080 assert(len <= sizeof (liblocp->ap_logical)); 1081 1082 if (libp->plugin_vers != CFGA_HSL_V2) { 1083 return (CFGA_LIB_ERROR); 1084 } 1085 1086 /* open devlink database */ 1087 if ((hdl = di_devlink_init(NULL, 0)) == NULL) { 1088 return (CFGA_LIB_ERROR); 1089 } 1090 1091 liblocp->ap_logical[0] = '\0'; 1092 (void) di_devlink_walk(hdl, NULL, 1093 liblocp->ap_physical + strlen(DEVICES_DIR), 1094 DI_PRIMARY_LINK, (void *)liblocp->ap_logical, get_link); 1095 1096 (void) di_devlink_fini(&hdl); 1097 1098 if (liblocp->ap_logical[0] != '\0') 1099 return (CFGA_OK); 1100 return (mklog_common(node, minor, liblocp, len)); 1101 } 1102 1103 /* 1104 * mklog_common - make a logical name from the driver and instance 1105 */ 1106 static cfga_err_t 1107 mklog_common( 1108 di_node_t node, 1109 di_minor_t minor, 1110 lib_loc_t *libloc_p, 1111 size_t len) 1112 { 1113 int inst; 1114 char *drv, *minor_name; 1115 1116 drv = di_driver_name(node); 1117 inst = di_instance(node); 1118 minor_name = di_minor_name(minor); 1119 1120 errno = 0; 1121 if (drv != NULL && inst != -1 && minor_name != NULL && 1122 snprintf(libloc_p->ap_logical, len, "%s%d:%s", drv, inst, 1123 minor_name) < len) { /* snprintf returns strlen */ 1124 return (CFGA_OK); 1125 } 1126 1127 return (CFGA_LIB_ERROR); 1128 } 1129 1130 /* 1131 * resolve_lib_ref - relocate to use plugin lib 1132 */ 1133 static cfga_err_t 1134 resolve_lib_ref( 1135 plugin_lib_t *libp, 1136 lib_loc_t *libloc_p) 1137 { 1138 void *sym; 1139 void *libhdlp = libp->handle; 1140 int plug_vers; 1141 1142 if ((sym = dlsym(libhdlp, "cfga_version")) == NULL) { 1143 /* 1144 * Version symbol not defined, must be the first version 1145 */ 1146 plug_vers = CFGA_HSL_V1; 1147 } else { 1148 plug_vers = *((int *)sym); 1149 } 1150 1151 /* 1152 * Check if plugin version matches request. 1153 */ 1154 if (!compat_plugin(&libloc_p->vers_req, plug_vers)) { 1155 return (CFGA_NO_LIB); 1156 } 1157 1158 /* 1159 * Record the plugin version and setup version dependant routines 1160 */ 1161 assert(plug_vers < VERS_ARRAY_SZ); 1162 libp->plugin_vers = plug_vers; 1163 libp->vers_ops = &cfga_vers_ops[plug_vers]; 1164 1165 /* resolve symbols common to all versions */ 1166 if ((sym = dlsym(libhdlp, "cfga_change_state")) == NULL) { 1167 perror("dlsym: cfga_change_state"); 1168 return (CFGA_LIB_ERROR); 1169 } else 1170 libp->cfga_change_state_p = (cfga_err_t (*)(cfga_cmd_t, 1171 const char *, const char *, struct cfga_confirm *, 1172 struct cfga_msg *, char **, cfga_flags_t)) sym; 1173 1174 if ((sym = dlsym(libhdlp, "cfga_private_func")) == NULL) { 1175 perror("dlsym: cfga_private_func"); 1176 return (CFGA_LIB_ERROR); 1177 } else 1178 libp->cfga_private_func_p = (cfga_err_t (*)(const char *, 1179 const char *, const char *, struct cfga_confirm *, 1180 struct cfga_msg *, char **, cfga_flags_t))sym; 1181 1182 if ((sym = dlsym(libhdlp, "cfga_test")) == NULL) { 1183 perror("dlsym: cfga_test"); 1184 return (CFGA_LIB_ERROR); 1185 } else 1186 libp->cfga_test_p = (cfga_err_t (*)(const char *, const char *, 1187 struct cfga_msg *, char **, cfga_flags_t))sym; 1188 1189 if ((sym = dlsym(libhdlp, "cfga_help")) == NULL) { 1190 perror("dlsym: cfga_help"); 1191 return (CFGA_LIB_ERROR); 1192 } else 1193 libp->cfga_help_p = (cfga_err_t (*)(struct cfga_msg *, 1194 const char *, cfga_flags_t))sym; 1195 1196 if ((sym = dlsym(libhdlp, "cfga_ap_id_cmp")) == NULL) { 1197 libp->cfga_ap_id_cmp_p = default_ap_id_cmp; 1198 } else 1199 libp->cfga_ap_id_cmp_p = (int (*)(const 1200 cfga_ap_log_id_t, const cfga_ap_log_id_t))sym; 1201 1202 /* Resolve version specific symbols */ 1203 return (libp->vers_ops->resolve_lib(libp)); 1204 } 1205 1206 /*ARGSUSED*/ 1207 static cfga_err_t 1208 null_resolve(plugin_lib_t *libp) 1209 { 1210 return (CFGA_OK); 1211 } 1212 1213 static cfga_err_t 1214 resolve_v1(plugin_lib_t *libp) 1215 { 1216 void *sym, *libhdlp = libp->handle; 1217 1218 1219 if (libp->plugin_vers != CFGA_HSL_V1) { 1220 return (CFGA_NO_LIB); 1221 } 1222 1223 if ((sym = dlsym(libhdlp, "cfga_stat")) == NULL) { 1224 perror("dlsym: cfga_stat"); 1225 return (CFGA_LIB_ERROR); 1226 } else 1227 libp->cfga_stat_p = (cfga_err_t (*)(const char *, 1228 struct cfga_stat_data *, const char *, 1229 char **))sym; 1230 1231 if ((sym = dlsym(libhdlp, "cfga_list")) == NULL) { 1232 perror("dlsym: cfga_list"); 1233 return (CFGA_LIB_ERROR); 1234 } else 1235 libp->cfga_list_p = (cfga_err_t (*)(struct cfga_stat_data **, 1236 int *, const char *, char **))sym; 1237 1238 return (CFGA_OK); 1239 } 1240 1241 static cfga_err_t 1242 resolve_v2(plugin_lib_t *libp) 1243 { 1244 void *sym; 1245 1246 1247 if (libp->plugin_vers != CFGA_HSL_V2) { 1248 return (CFGA_NO_LIB); 1249 } 1250 1251 if ((sym = dlsym(libp->handle, "cfga_list_ext")) == NULL) { 1252 perror("dlsym: cfga_list_ext"); 1253 return (CFGA_LIB_ERROR); 1254 } else { 1255 libp->cfga_list_ext_p = (cfga_err_t (*)(const char *, 1256 struct cfga_list_data **, int *, const char *, 1257 const char *, char **, cfga_flags_t))sym; 1258 return (CFGA_OK); 1259 } 1260 } 1261 1262 /* 1263 * config_calloc_check - perform allocation, check result and 1264 * set error string 1265 */ 1266 static void * 1267 config_calloc_check( 1268 size_t nelem, 1269 size_t elsize, 1270 char **errstring) 1271 { 1272 void *p; 1273 1274 p = calloc(nelem, elsize); 1275 if (p == NULL) { 1276 config_err(0, ALLOC_FAILED, errstring); 1277 } 1278 1279 return (p); 1280 } 1281 1282 1283 /* 1284 * config_get_lib - given an ap_id find the library name 1285 * If successful, the plugin library is held. 1286 */ 1287 static cfga_err_t 1288 config_get_lib( 1289 const char *ap_id, 1290 lib_loc_t *lib_loc_p, 1291 char **errstring) 1292 { 1293 char *dyncomp, path[PATH_MAX]; 1294 char *apdup; 1295 cfga_ap_types_t type = UNKNOWN_AP; 1296 cfga_err_t ret = CFGA_ERROR; 1297 1298 if (ap_id == NULL) { 1299 config_err(0, INVALID_ARGS, errstring); 1300 return (ret); 1301 } 1302 1303 lib_loc_p->libp = NULL; 1304 1305 if ((apdup = config_calloc_check(1, strlen(ap_id) + 1, errstring)) 1306 == NULL) { 1307 return (CFGA_LIB_ERROR); 1308 } 1309 (void) strcpy(apdup, ap_id); 1310 1311 /* 1312 * Separate into base and dynamic components 1313 */ 1314 if ((ret = split_apid(apdup, &dyncomp, errstring)) != CFGA_OK) { 1315 goto out; 1316 } 1317 1318 /* 1319 * No upper limit on version 1320 */ 1321 lib_loc_p->vers_req.v_max = CFGA_HSL_VERS; 1322 if (dyncomp != NULL) { 1323 /* 1324 * We need atleast version 2 of the plug-in library 1325 * interface since the ap_id has a dynamic component. 1326 */ 1327 1328 lib_loc_p->vers_req.v_min = CFGA_HSL_V2; 1329 } else { 1330 lib_loc_p->vers_req.v_min = CFGA_HSL_V1; 1331 } 1332 1333 /* 1334 * If the ap_id is a devlink in CFGA_DEV_DIR, follow link 1335 * to get the physical ap_id. 1336 */ 1337 if ((type = find_arg_type(apdup)) == LOGICAL_LINK_AP) { 1338 (void) snprintf(lib_loc_p->ap_base, sizeof (lib_loc_p->ap_base), 1339 "%s%s", CFGA_DEV_DIR SLASH, apdup); 1340 } 1341 1342 path[sizeof (path) - 1] = '\0'; 1343 if (type == LOGICAL_LINK_AP && realpath(lib_loc_p->ap_base, path) 1344 != NULL) { 1345 (void) snprintf(lib_loc_p->ap_base, sizeof (lib_loc_p->ap_base), 1346 "%s", path); 1347 } else { 1348 (void) snprintf(lib_loc_p->ap_base, sizeof (lib_loc_p->ap_base), 1349 "%s", apdup); 1350 } 1351 1352 1353 /* 1354 * find and load the library 1355 * The base component of the ap_id is used to locate the plug-in 1356 */ 1357 if ((type = find_arg_type(lib_loc_p->ap_base)) == PHYSICAL_AP) { 1358 /* 1359 * physical ap_id: Use ap_base as root for tree walk 1360 * A link based apid (logical) will resolve to a physical 1361 * ap_id. 1362 */ 1363 ret = find_ap_common(lib_loc_p, lib_loc_p->ap_base, 1364 check_ap_phys, errstring); 1365 } else if ((type == LOGICAL_DRV_AP) || 1366 (type == AP_TYPE && dyncomp == NULL)) { 1367 /* 1368 * logical ap_id or ap_type: Use "/" as root for tree walk 1369 * Note: an aptype cannot have a dynamic component 1370 */ 1371 ret = find_ap_common(lib_loc_p, "/", check_ap, errstring); 1372 } else { 1373 ret = CFGA_APID_NOEXIST; 1374 } 1375 1376 if (ret == CFGA_OK) { 1377 #ifndef NDEBUG 1378 /* 1379 * variables used by assert() only which is disabled 1380 * by defining NDEBUG (see top of this file) 1381 */ 1382 plugin_lib_t *libp; 1383 1384 libp = lib_loc_p->libp; 1385 #endif /* NDEBUG */ 1386 1387 assert(strcmp(libp->libpath, lib_loc_p->pathname) == 0); 1388 assert(VALID_HSL_VERS(libp->plugin_vers)); 1389 1390 /* 1391 * If a dynamic component was present, v1 plug-ins are not 1392 * acceptable. 1393 */ 1394 assert(dyncomp == NULL || libp->plugin_vers >= CFGA_HSL_V2); 1395 1396 /* 1397 * ap_physical is passed to plugins as their ap_id argument. 1398 * Append dynamic component if any. 1399 */ 1400 append_dyn(lib_loc_p->ap_physical, dyncomp, 1401 sizeof (lib_loc_p->ap_physical)); 1402 } 1403 1404 /* cleanup */ 1405 lib_loc_p->vers_req.v_min = INVALID_VERSION; 1406 lib_loc_p->vers_req.v_max = INVALID_VERSION; 1407 *lib_loc_p->ap_base = '\0'; 1408 1409 /*FALLTHRU*/ 1410 out: 1411 S_FREE(apdup); 1412 S_FREE(dyncomp); 1413 if (ret != CFGA_OK) { 1414 lib_loc_p->libp = NULL; 1415 } 1416 1417 assert(ret != CFGA_OK || lib_loc_p->libp != NULL); 1418 1419 return (ret); 1420 } 1421 1422 1423 /* 1424 * load_lib - Given a library pathname, create a entry for it 1425 * in the library list, if one does not already exist, and read 1426 * lock it to keep it there. 1427 */ 1428 static cfga_err_t 1429 load_lib( 1430 di_node_t node, 1431 di_minor_t minor, 1432 lib_loc_t *libloc_p) 1433 { 1434 plugin_lib_t *libp, *list_libp; 1435 char *devfs_path; 1436 1437 /* 1438 * lock the library list 1439 */ 1440 (void) mutex_lock(&plugin_list_lock); 1441 1442 /* 1443 * see if lib exist in list, if not, allocate a new one 1444 */ 1445 list_libp = lib_in_list(libloc_p->pathname); 1446 if (list_libp != NULL) { 1447 hold_lib(list_libp); 1448 (void) mutex_unlock(&plugin_list_lock); 1449 1450 /* fill in logical and physical name in libloc_p */ 1451 libloc_p->libp = libp = list_libp; 1452 if (libp->vers_ops->mklog(node, minor, libp, libloc_p) 1453 != CFGA_OK) { 1454 rele_lib(list_libp); 1455 return (CFGA_LIB_ERROR); 1456 } 1457 1458 devfs_path = di_devfs_path(node); 1459 (void) snprintf(libloc_p->ap_physical, MAXPATHLEN, "%s%s:%s", 1460 DEVICES_DIR, devfs_path, di_minor_name(minor)); 1461 di_devfs_path_free(devfs_path); 1462 1463 return (CFGA_OK); 1464 } 1465 1466 /* allocate a new plugin_lib_t structure */ 1467 libp = config_calloc_check(1, sizeof (plugin_lib_t), NULL); 1468 if (libp == NULL) { 1469 (void) mutex_unlock(&plugin_list_lock); 1470 return (CFGA_LIB_ERROR); 1471 } 1472 1473 (void) snprintf(libp->libpath, sizeof (libp->libpath), "%s", 1474 libloc_p->pathname); 1475 1476 /* 1477 * ensure that the lib is open and linked in 1478 */ 1479 libp->handle = dlopen(libp->libpath, RTLD_NOW); 1480 if (libp->handle == NULL) { 1481 (void) mutex_unlock(&plugin_list_lock); 1482 free(libp); 1483 return (CFGA_NO_LIB); 1484 } 1485 1486 if (resolve_lib_ref(libp, libloc_p) != CFGA_OK || 1487 libp->vers_ops->mklog(node, minor, libp, libloc_p) != CFGA_OK) { 1488 (void) mutex_unlock(&plugin_list_lock); 1489 (void) dlclose(libp->handle); 1490 free(libp); 1491 return (CFGA_NO_LIB); 1492 } 1493 1494 /* 1495 * link in new entry to the end of list 1496 */ 1497 list_libp = &plugin_list; 1498 while (list_libp->next != NULL) 1499 list_libp = list_libp->next; 1500 libp->next = list_libp->next; 1501 list_libp->next = libp; 1502 1503 /* Initialize refcnt to 1 */ 1504 libp->refcnt = 1; 1505 (void) mutex_init(&libp->lock, USYNC_THREAD, NULL); 1506 1507 (void) mutex_unlock(&plugin_list_lock); 1508 1509 /* 1510 * record libp and physical node name in the libloc struct 1511 */ 1512 libloc_p->libp = libp; 1513 devfs_path = di_devfs_path(node); 1514 (void) snprintf(libloc_p->ap_physical, MAXPATHLEN, "%s%s:%s", 1515 DEVICES_DIR, devfs_path, di_minor_name(minor)); 1516 di_devfs_path_free(devfs_path); 1517 1518 return (CFGA_OK); 1519 } 1520 1521 1522 #define NUM_LIB_NAMES 2 1523 1524 /* 1525 * find_lib - Given an attachment point node find it's library 1526 * 1527 */ 1528 static cfga_err_t 1529 find_lib( 1530 di_node_t node, 1531 di_minor_t minor, 1532 lib_loc_t *libloc_p) 1533 { 1534 char lib[MAXPATHLEN]; 1535 char name[NUM_LIB_NAMES][MAXPATHLEN]; 1536 struct stat lib_stat; 1537 void *dlhandle = NULL; 1538 static char machine_name[SYSINFO_LENGTH]; 1539 static char arch_name[SYSINFO_LENGTH]; 1540 int i = 0; 1541 char *class = NULL, *drv = NULL; 1542 1543 1544 /* Make sure pathname and class is null if we fail */ 1545 *libloc_p->ap_class = *libloc_p->pathname = *lib = '\0'; 1546 1547 /* 1548 * Initialize machine name and arch name 1549 */ 1550 if (strncmp("", machine_name, MAXPATHLEN) == 0) { 1551 if (sysinfo(SI_MACHINE, machine_name, SYSINFO_LENGTH) == -1) { 1552 return (CFGA_ERROR); 1553 } 1554 if (sysinfo(SI_ARCHITECTURE, arch_name, SYSINFO_LENGTH) == -1) { 1555 return (CFGA_ERROR); 1556 } 1557 } 1558 1559 /* 1560 * Initialize possible library tags. 1561 */ 1562 1563 drv = di_driver_name(node); 1564 class = get_class(minor); 1565 1566 if (drv == NULL || class == NULL) { 1567 return (CFGA_LIB_ERROR); 1568 } 1569 1570 i = 0; 1571 (void) snprintf(&name[i++][0], sizeof (name[0]), "%s", drv); 1572 (void) snprintf(&name[i++][0], sizeof (name[0]), "%s", class); 1573 1574 /* 1575 * Cycle through the array of names to find the library. 1576 */ 1577 for (i = 0; i < NUM_LIB_NAMES; i++) { 1578 1579 /* Attachment points may not have a class (i.e. are generic) */ 1580 if (name[i][0] == '\0') { 1581 continue; 1582 } 1583 1584 /* 1585 * Try path based upon machine name 1586 */ 1587 (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s", 1588 LIB_PATH_BASE1, machine_name, LIB_PATH_MIDDLE, 1589 name[i], LIB_PATH_TAIL); 1590 1591 1592 if (stat(lib, &lib_stat) == 0) { 1593 /* file exists, is it a lib */ 1594 dlhandle = dlopen(lib, RTLD_LAZY); 1595 if (dlhandle != NULL) { 1596 goto found; 1597 } 1598 } 1599 1600 /* 1601 * Try path based upon arch name 1602 */ 1603 (void) snprintf(lib, sizeof (lib), "%s%s%s%s%s", 1604 LIB_PATH_BASE1, arch_name, LIB_PATH_MIDDLE, 1605 name[i], LIB_PATH_TAIL); 1606 1607 if (stat(lib, &lib_stat) == 0) { 1608 /* file exists, is it a lib */ 1609 dlhandle = dlopen(lib, RTLD_LAZY); 1610 if (dlhandle != NULL) { 1611 goto found; 1612 } 1613 1614 } 1615 1616 /* 1617 * Try generic location 1618 */ 1619 (void) snprintf(lib, sizeof (lib), "%s%s%s%s", 1620 LIB_PATH_BASE2, LIB_PATH_MIDDLE, name[i], LIB_PATH_TAIL); 1621 1622 1623 1624 if (stat(lib, &lib_stat) == 0) { 1625 /* file exists, is it a lib */ 1626 dlhandle = dlopen(lib, RTLD_LAZY); 1627 if (dlhandle != NULL) { 1628 goto found; 1629 } 1630 1631 } 1632 } 1633 1634 1635 return (CFGA_NO_LIB); 1636 1637 found: 1638 /* we got one! */ 1639 (void) snprintf(libloc_p->pathname, sizeof (libloc_p->pathname), "%s", 1640 lib); 1641 1642 (void) dlclose(dlhandle); 1643 1644 /* Record class name (if any) */ 1645 (void) snprintf(libloc_p->ap_class, sizeof (libloc_p->ap_class), "%s", 1646 class); 1647 1648 return (CFGA_OK); 1649 } 1650 1651 static cfga_err_t 1652 lookup_cache(lib_loc_t *libloc_p) 1653 { 1654 lib_cache_t *entry; 1655 (void) mutex_lock(&lib_cache_lock); 1656 entry = lib_cache; 1657 while (entry) { 1658 if (strcmp(entry->lc_ap_id, libloc_p->ap_base) == 0) { 1659 plugin_lib_t *libp = entry->lc_libp; 1660 libloc_p->libp = libp; 1661 hold_lib(libp); 1662 (void) strcpy(libloc_p->pathname, libp->libpath); 1663 (void) strcpy(libloc_p->ap_physical, 1664 entry->lc_ap_physical); 1665 (void) strcpy(libloc_p->ap_logical, 1666 entry->lc_ap_logical); 1667 (void) mutex_unlock(&lib_cache_lock); 1668 return (CFGA_OK); 1669 } 1670 entry = entry->lc_next; 1671 } 1672 (void) mutex_unlock(&lib_cache_lock); 1673 1674 return (CFGA_ERROR); 1675 } 1676 1677 static void 1678 update_cache(lib_loc_t *libloc_p) 1679 { 1680 lib_cache_t *entry; 1681 entry = config_calloc_check(1, sizeof (lib_cache_t), NULL); 1682 if (entry == NULL) 1683 return; 1684 1685 entry->lc_ap_id = strdup(libloc_p->ap_base); 1686 entry->lc_ap_physical = strdup(libloc_p->ap_physical); 1687 entry->lc_ap_logical = strdup(libloc_p->ap_logical); 1688 if ((entry->lc_ap_id == NULL) || (entry->lc_ap_physical == NULL) || 1689 (entry->lc_ap_logical == NULL)) { 1690 free(entry->lc_ap_id); 1691 free(entry->lc_ap_physical); 1692 free(entry->lc_ap_logical); 1693 free(entry); 1694 return; 1695 } 1696 1697 (void) mutex_lock(&lib_cache_lock); 1698 entry->lc_libp = libloc_p->libp; 1699 entry->lc_next = lib_cache; 1700 lib_cache = entry; 1701 hold_lib(entry->lc_libp); /* prevent stale cache */ 1702 (void) mutex_unlock(&lib_cache_lock); 1703 } 1704 1705 static void 1706 destroy_cache() 1707 { 1708 lib_cache_t *entry, *next; 1709 (void) mutex_lock(&lib_cache_lock); 1710 entry = lib_cache; 1711 while (entry) { 1712 next = entry->lc_next; 1713 rele_lib(entry->lc_libp); 1714 free(entry->lc_ap_id); 1715 free(entry->lc_ap_physical); 1716 free(entry->lc_ap_logical); 1717 free(entry); 1718 entry = next; 1719 } 1720 (void) mutex_unlock(&lib_cache_lock); 1721 } 1722 1723 /* 1724 * find_ap_common - locate a particular attachment point 1725 */ 1726 static cfga_err_t 1727 find_ap_common( 1728 lib_loc_t *libloc_p, 1729 const char *physpath, 1730 int (*fcn)(di_node_t node, di_minor_t minor, void *arg), 1731 char **errstring) 1732 { 1733 di_node_t rnode; 1734 char *cp, *rpath; 1735 size_t len; 1736 1737 if (lookup_cache(libloc_p) == CFGA_OK) 1738 return (CFGA_OK); 1739 1740 if ((rpath = config_calloc_check(1, strlen(physpath) + 1, 1741 errstring)) == NULL) { 1742 return (CFGA_LIB_ERROR); 1743 } 1744 1745 (void) strcpy(rpath, physpath); 1746 1747 /* Remove devices prefix (if any) */ 1748 len = strlen(DEVICES_DIR); 1749 if (strncmp(rpath, DEVICES_DIR SLASH, len + strlen(SLASH)) == 0) { 1750 (void) memmove(rpath, rpath + len, 1751 strlen(rpath + len) + 1); 1752 } 1753 1754 /* Remove dynamic component if any */ 1755 if ((cp = GET_DYN(rpath)) != NULL) { 1756 *cp = '\0'; 1757 } 1758 1759 /* Remove minor name (if any) */ 1760 if ((cp = strrchr(rpath, ':')) != NULL) { 1761 *cp = '\0'; 1762 } 1763 1764 /* 1765 * begin walk of device tree 1766 */ 1767 rnode = di_init(rpath, DINFOCPYALL); 1768 S_FREE(rpath); 1769 1770 if (rnode == DI_NODE_NIL) { 1771 /* 1772 * di_init() may fail because the ap_id does not exist 1773 */ 1774 if (errno == EINVAL || errno == ENXIO) { 1775 return (CFGA_APID_NOEXIST); 1776 } else { 1777 config_err(errno, DI_INIT_FAILED, errstring); 1778 return (CFGA_LIB_ERROR); 1779 } 1780 } 1781 1782 libloc_p->libp = NULL; 1783 libloc_p->status = CFGA_APID_NOEXIST; 1784 1785 (void) di_walk_minor(rnode, "ddi_ctl:attachment_point", 1786 DI_CHECK_ALIAS|DI_CHECK_INTERNAL_PATH, 1787 libloc_p, fcn); 1788 1789 di_fini(rnode); 1790 1791 if (libloc_p->libp != NULL) { 1792 update_cache(libloc_p); 1793 return (CFGA_OK); 1794 } else { 1795 return (libloc_p->status); 1796 } 1797 } 1798 1799 /* 1800 * check_ap - called for each attachment point found 1801 * 1802 * This is used in cases where a particular attachment point 1803 * or type of attachment point is specified via a logical name or ap_type. 1804 * Not used for physical names or in the list case with no 1805 * ap's specified. 1806 */ 1807 1808 static int 1809 check_ap( 1810 di_node_t node, 1811 di_minor_t minor, 1812 void *arg) 1813 { 1814 char *cp = NULL; 1815 char aptype[MAXPATHLEN]; 1816 char *recep_id = NULL; 1817 char *node_minor; 1818 char *drv_name; 1819 char inst[MAXPATHLEN]; 1820 char inst2[MAXPATHLEN]; 1821 lib_loc_t *libloc_p; 1822 int comparison_test; 1823 int instance; 1824 cfga_ap_types_t type; 1825 1826 1827 libloc_p = (lib_loc_t *)arg; 1828 1829 (void) snprintf(aptype, sizeof (aptype), "%s", libloc_p->ap_base); 1830 1831 /* 1832 * This routime handles only aptypes and driver based logical apids. 1833 */ 1834 type = find_arg_type(aptype); 1835 if (type == LOGICAL_DRV_AP) { 1836 cp = strchr(aptype, ':'); 1837 *cp = '\0'; 1838 recep_id = cp+1; 1839 cp--; 1840 while (isdigit(*cp) && cp != aptype) 1841 cp--; 1842 cp++; 1843 1844 (void) snprintf(inst, sizeof (inst), "%s", cp); 1845 1846 *cp = '\0'; 1847 } else if (type != AP_TYPE) { 1848 libloc_p->status = CFGA_APID_NOEXIST; 1849 return (DI_WALK_CONTINUE); 1850 } 1851 1852 node_minor = di_minor_name(minor); 1853 drv_name = di_driver_name(node); 1854 instance = di_instance(node); 1855 1856 if (node_minor == NULL || drv_name == NULL || instance == -1) { 1857 libloc_p->status = CFGA_APID_NOEXIST; 1858 return (DI_WALK_CONTINUE); 1859 } 1860 1861 (void) sprintf(inst2, "%d", instance); 1862 1863 /* 1864 * If the base matches driver and instance try and find a lib for it, 1865 * then load it. On any failure we continue the walk. 1866 * 1867 * driver based logical ap_ids are derived from driver name + instance. 1868 * Ap_types are just partial driver names. 1869 * 1870 */ 1871 1872 comparison_test = 0; 1873 if (type == AP_TYPE) { 1874 if (strncmp(aptype, drv_name, strlen(aptype)) == 0) { 1875 comparison_test = 1; 1876 } 1877 } else { 1878 if (strcmp(aptype, drv_name) == 0 && 1879 strcmp(recep_id, node_minor) == 0 && 1880 strcmp(inst, inst2) == 0) { 1881 comparison_test = 1; 1882 } 1883 } 1884 1885 if (comparison_test) { 1886 /* 1887 * save the correct type of error so user does not get confused 1888 */ 1889 if (find_lib(node, minor, libloc_p) != CFGA_OK) { 1890 libloc_p->status = CFGA_NO_LIB; 1891 return (DI_WALK_CONTINUE); 1892 } 1893 if (load_lib(node, minor, libloc_p) != CFGA_OK) { 1894 libloc_p->status = CFGA_LIB_ERROR; 1895 return (DI_WALK_CONTINUE); 1896 } 1897 libloc_p->status = CFGA_OK; 1898 return (DI_WALK_TERMINATE); 1899 } else { 1900 libloc_p->status = CFGA_APID_NOEXIST; 1901 return (DI_WALK_CONTINUE); 1902 } 1903 } 1904 1905 1906 /* 1907 * check_ap_phys - called for each attachment point found 1908 * 1909 * This is used in cases where a particular attachment point 1910 * is specified via a physical name. If the name matches then 1911 * we try and find and load the library for it. 1912 */ 1913 1914 static int 1915 check_ap_phys( 1916 di_node_t node, 1917 di_minor_t minor, 1918 void *arg) 1919 { 1920 lib_loc_t *libloc_p; 1921 char phys_name[MAXPATHLEN]; 1922 char *devfs_path; 1923 char *minor_name; 1924 1925 libloc_p = (lib_loc_t *)arg; 1926 devfs_path = di_devfs_path(node); 1927 minor_name = di_minor_name(minor); 1928 1929 if (devfs_path == NULL || minor_name == NULL) { 1930 libloc_p->status = CFGA_APID_NOEXIST; 1931 return (DI_WALK_CONTINUE); 1932 } 1933 1934 (void) snprintf(phys_name, sizeof (phys_name), "%s%s:%s", 1935 DEVICES_DIR, devfs_path, minor_name); 1936 1937 di_devfs_path_free(devfs_path); 1938 1939 if (strcmp(phys_name, libloc_p->ap_base) == 0) { 1940 if (find_lib(node, minor, libloc_p) != CFGA_OK) { 1941 libloc_p->status = CFGA_NO_LIB; 1942 return (DI_WALK_CONTINUE); 1943 } 1944 if (load_lib(node, minor, libloc_p) != CFGA_OK) { 1945 libloc_p->status = CFGA_LIB_ERROR; 1946 return (DI_WALK_CONTINUE); 1947 } 1948 libloc_p->status = CFGA_OK; 1949 return (DI_WALK_TERMINATE); 1950 } else { 1951 libloc_p->status = CFGA_APID_NOEXIST; 1952 return (DI_WALK_CONTINUE); 1953 } 1954 } 1955 1956 /* 1957 * lib_in_list 1958 * 1959 * See if library, as specified by the full pathname and controller 1960 * instance number is already represented in the plugin library list. 1961 * If the instance number is -1 it is ignored. 1962 */ 1963 static plugin_lib_t * 1964 lib_in_list(char *libpath) 1965 { 1966 plugin_lib_t *libp = NULL; 1967 1968 for (libp = plugin_list.next; libp != NULL; libp = libp->next) { 1969 if (strncmp(libpath, libp->libpath, MAXPATHLEN) == 0) { 1970 return (libp); 1971 } 1972 } 1973 return (NULL); 1974 } 1975 1976 1977 1978 1979 /* 1980 * Coalesce stat and list data into single array 1981 */ 1982 static cfga_err_t 1983 realloc_data_ext( 1984 cfga_list_data_t **ap_id_list, 1985 int *nlistp, 1986 list_stat_t *lstatp) 1987 { 1988 int i, j; 1989 stat_data_list_t *slp; 1990 cfga_list_data_t *cldp; 1991 array_list_t *alp; 1992 cfga_err_t rc = CFGA_OK; 1993 1994 1995 assert(*lstatp->countp >= 0); 1996 1997 if (*lstatp->countp == 0) { 1998 *ap_id_list = NULL; 1999 *nlistp = 0; 2000 return (CFGA_OK); 2001 } 2002 2003 /* 2004 * allocate the array 2005 */ 2006 if ((cldp = config_calloc_check(*lstatp->countp, 2007 sizeof (cfga_list_data_t), lstatp->errstr)) == NULL) { 2008 rc = CFGA_LIB_ERROR; 2009 goto out; 2010 } 2011 2012 /* 2013 * copy all the stat elements (if any) into the array 2014 */ 2015 slp = lstatp->sdl; 2016 for (i = 0; slp != NULL; i++) { 2017 if (i >= *lstatp->countp) { 2018 rc = CFGA_LIB_ERROR; 2019 goto out; 2020 } 2021 stat_to_list(&cldp[i], &slp->stat_data); 2022 slp = slp->next; 2023 } 2024 2025 /* 2026 * copy all the list elements (if any) into the array 2027 */ 2028 alp = lstatp->al; 2029 for (; alp != NULL; ) { 2030 if (i + alp->nelem > *lstatp->countp) { 2031 rc = CFGA_LIB_ERROR; 2032 goto out; 2033 } 2034 2035 for (j = 0; j < alp->nelem; i++, j++) { 2036 cldp[i] = alp->array[j]; 2037 } 2038 alp = alp->next; 2039 } 2040 2041 if (i != *lstatp->countp) { 2042 rc = CFGA_LIB_ERROR; 2043 } else { 2044 rc = CFGA_OK; 2045 } 2046 2047 /*FALLTHRU*/ 2048 2049 out: 2050 /* clean up */ 2051 lstat_free(lstatp); 2052 2053 if (rc == CFGA_OK) { 2054 *ap_id_list = cldp; 2055 *nlistp = *lstatp->countp; 2056 } else { 2057 S_FREE(cldp); 2058 *ap_id_list = NULL; 2059 *nlistp = 0; 2060 } 2061 return (rc); 2062 } 2063 2064 /* 2065 * The caller of this routine may supply a buffer through 2066 * ap_id_list for returning data. Otherwise, this routine allocates the 2067 * buffer. 2068 */ 2069 static cfga_err_t 2070 realloc_data(cfga_stat_data_t **ap_id_list, int *nlistp, list_stat_t *lstatp) 2071 { 2072 int i; 2073 stat_data_list_t *slp; 2074 cfga_stat_data_t *csdp, *buf; 2075 cfga_err_t rc; 2076 2077 2078 assert(*lstatp->countp >= 0); 2079 2080 if (*lstatp->countp == 0) { 2081 *nlistp = 0; 2082 return (CFGA_OK); 2083 } 2084 2085 2086 /* 2087 * allocate the array if caller does not supply one. 2088 */ 2089 if (*ap_id_list == NULL) { 2090 if ((buf = config_calloc_check(*lstatp->countp, 2091 sizeof (cfga_stat_data_t), lstatp->errstr)) == NULL) { 2092 rc = CFGA_LIB_ERROR; 2093 goto out; 2094 } 2095 } else { 2096 buf = *ap_id_list; 2097 } 2098 2099 /* 2100 * copy the stat elements into the array 2101 */ 2102 csdp = buf; 2103 slp = lstatp->sdl; 2104 for (i = 0; slp != NULL; i++) { 2105 if (i >= *lstatp->countp) { 2106 rc = CFGA_LIB_ERROR; 2107 goto out; 2108 } 2109 *csdp++ = slp->stat_data; 2110 slp = slp->next; 2111 } 2112 2113 rc = CFGA_OK; 2114 2115 out: 2116 if (rc == CFGA_OK) { 2117 *nlistp = *lstatp->countp; 2118 *ap_id_list = buf; 2119 } else { 2120 /* 2121 * Free buffer only if we allocated it. 2122 */ 2123 if (*ap_id_list == NULL) { 2124 free(buf); 2125 } 2126 *nlistp = 0; 2127 } 2128 2129 assert(lstatp->al == NULL); 2130 lstat_free(lstatp); 2131 2132 return (rc); 2133 } 2134 2135 2136 /* 2137 * list_common - walk the device tree and stat all attachment points. 2138 */ 2139 static cfga_err_t 2140 list_common(list_stat_t *lstatp, const char *class) 2141 { 2142 di_node_t rnode; 2143 char nodetype[MAXPATHLEN]; 2144 const char *l_class, *l_sep; 2145 2146 2147 /* 2148 * begin walk of device tree 2149 */ 2150 if ((rnode = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 2151 config_err(errno, DI_INIT_FAILED, lstatp->errstr); 2152 return (CFGA_LIB_ERROR); 2153 } 2154 2155 /* 2156 * May walk a subset of all attachment points in the device tree if 2157 * a class is specified 2158 */ 2159 if (class != NULL) { 2160 l_sep = ":"; 2161 l_class = class; 2162 } else { 2163 l_sep = l_class = ""; 2164 } 2165 2166 (void) snprintf(nodetype, sizeof (nodetype), "%s%s%s", 2167 DDI_NT_ATTACHMENT_POINT, l_sep, l_class); 2168 2169 (void) di_walk_minor(rnode, nodetype, 2170 DI_CHECK_ALIAS|DI_CHECK_INTERNAL_PATH, lstatp, do_list_common); 2171 di_fini(rnode); 2172 2173 return (CFGA_OK); 2174 } 2175 2176 static void 2177 config_err(int errnum, int err_type, char **errstring) 2178 { 2179 char *p = NULL, *q = NULL; 2180 char *syserr = NULL; 2181 char syserr_num[20]; 2182 int len = 0; 2183 2184 /* 2185 * If errstring is null it means user in not interested in getting 2186 * error status. So we don't do all the work 2187 */ 2188 if (errstring == NULL) { 2189 return; 2190 } 2191 2192 if (errnum != 0) { 2193 syserr = strerror(errnum); 2194 if (syserr == NULL) { 2195 (void) sprintf(syserr_num, "errno=%d", errnum); 2196 syserr = syserr_num; 2197 } 2198 } else 2199 syserr = NULL; 2200 2201 q = dgettext(TEXT_DOMAIN, err_strings[err_type]); 2202 2203 len = strlen(q); 2204 if (syserr != NULL) { 2205 len += strlen(err_sep) + strlen(syserr); 2206 } 2207 2208 p = malloc(len + 1); 2209 if (p == NULL) { 2210 *errstring = NULL; 2211 return; 2212 } 2213 2214 (void) strcpy(p, q); 2215 if (syserr != NULL) { 2216 (void) strcat(p, err_sep); 2217 (void) strcat(p, syserr); 2218 } 2219 2220 *errstring = p; 2221 } 2222 2223 /* 2224 * do_list_common - Routine to list attachment point as part of 2225 * a config_list opertion. Used by both v1 and v2 interfaces. 2226 * This is somewhat similar to config_get_lib() and its helper routines 2227 * except that the ap_ids are always physical and don't have dynamic 2228 * components. 2229 */ 2230 static int 2231 do_list_common( 2232 di_node_t node, 2233 di_minor_t minor, 2234 void *arg) 2235 { 2236 lib_loc_t lib_loc; 2237 plugin_lib_t *libp; 2238 list_stat_t *lstatp = NULL; 2239 cfga_err_t ret = CFGA_ERROR; 2240 2241 2242 lstatp = (list_stat_t *)arg; 2243 2244 lib_loc.libp = NULL; 2245 /* 2246 * try and find a lib for this node 2247 */ 2248 if (find_lib(node, minor, &lib_loc) != CFGA_OK) { 2249 return (DI_WALK_CONTINUE); 2250 } 2251 2252 /* 2253 * Load all plugins. We will check compatibility later in this 2254 * routine. 2255 */ 2256 lib_loc.vers_req.v_min = CFGA_HSL_V1; 2257 lib_loc.vers_req.v_max = CFGA_HSL_VERS; 2258 2259 ret = load_lib(node, minor, &lib_loc); 2260 if (ret != CFGA_OK) { 2261 return (DI_WALK_CONTINUE); 2262 } 2263 2264 libp = lib_loc.libp; 2265 assert(libp != NULL); 2266 2267 /* 2268 * Note: For list type routines (list all attachment points in 2269 * device tree) we don't pass errstring to the plugin, nor do we 2270 * stop the walk if an error occurs in the plugin. 2271 */ 2272 if (compat_plugin(&lstatp->use_vers, libp->plugin_vers)) { 2273 (void) libp->vers_ops->stat_plugin(lstatp, &lib_loc, NULL); 2274 } 2275 rele_lib(libp); 2276 2277 return (DI_WALK_CONTINUE); 2278 } 2279 2280 /* 2281 * stat_common - stat a user specified set of attachment points. 2282 */ 2283 static cfga_err_t 2284 stat_common( 2285 int num_ap_ids, 2286 char *const *ap_ids, 2287 const char *class, 2288 list_stat_t *lstatp) 2289 { 2290 int i; 2291 lib_loc_t libloc; 2292 plugin_lib_t *libp; 2293 cfga_err_t rc = CFGA_OK; 2294 2295 2296 /* 2297 * operate on each ap_id 2298 */ 2299 for (i = 0; i < num_ap_ids; i++) { 2300 libloc.libp = NULL; 2301 if ((rc = config_get_lib(ap_ids[i], &libloc, 2302 lstatp->errstr)) != CFGA_OK) { 2303 break; 2304 } 2305 assert(libloc.libp != NULL); 2306 libp = libloc.libp; 2307 2308 /* 2309 * do pre-filtering if requested 2310 */ 2311 if (class != NULL && strcmp(libloc.ap_class, class)) { 2312 rele_lib(libp); 2313 continue; 2314 } 2315 2316 /* 2317 * Unlike list type routines, while stat'ing specific 2318 * attachment points we pass errstring to the plugins 2319 * and halt if an error occurs in the plugin. 2320 */ 2321 rc = libp->vers_ops->stat_plugin(lstatp, &libloc, 2322 lstatp->errstr); 2323 rele_lib(libp); 2324 if (rc != CFGA_OK) { 2325 break; 2326 } 2327 } 2328 2329 if (rc != CFGA_OK) { 2330 lstat_free(lstatp); 2331 } 2332 return (rc); 2333 } 2334 2335 /*ARGSUSED*/ 2336 static cfga_err_t 2337 null_stat_plugin(list_stat_t *lstatp, lib_loc_t *libloc_p, char **errstring) 2338 { 2339 return (CFGA_OK); 2340 } 2341 2342 /* 2343 * Pass errstring as a separate argument. Some higher level routines need 2344 * it to be NULL. 2345 */ 2346 static cfga_err_t 2347 stat_plugin_v1(list_stat_t *lstatp, lib_loc_t *libloc_p, char **errstring) 2348 { 2349 stat_data_list_t *slp, *slp2 = NULL; 2350 cfga_err_t rc; 2351 2352 /* 2353 * allocate stat data buffer and list element 2354 */ 2355 if ((slp = config_calloc_check(1, sizeof (stat_data_list_t), 2356 errstring)) == NULL) { 2357 return (CFGA_LIB_ERROR); 2358 } 2359 2360 /* 2361 * Do the stat 2362 */ 2363 errno = 0; 2364 if ((rc = (*(libloc_p->libp->cfga_stat_p))(libloc_p->ap_physical, 2365 &slp->stat_data, lstatp->opts, errstring)) != CFGA_OK) { 2366 S_FREE(slp); 2367 return (rc); 2368 } 2369 slp->next = NULL; 2370 2371 /* 2372 * Set up the logical and physical id's. 2373 * For v1 interfaces, the generic library (libcfgadm) creates the 2374 * ap_ids. mklog() is assumed to have been called in 2375 * the caller of this routine. 2376 */ 2377 (void) snprintf(slp->stat_data.ap_log_id, CFGA_AP_LOG_ID_LEN, "%s", 2378 libloc_p->ap_logical); 2379 2380 (void) snprintf(slp->stat_data.ap_phys_id, CFGA_AP_PHYS_ID_LEN, "%s", 2381 libloc_p->ap_physical); 2382 2383 /* 2384 * link it in 2385 */ 2386 if ((slp2 = lstatp->sdl) == NULL) { 2387 lstatp->sdl = slp; 2388 } else { 2389 while (slp2->next != NULL) 2390 slp2 = slp2->next; 2391 slp2->next = slp; 2392 } 2393 2394 /* keep count */ 2395 (*lstatp->countp)++; 2396 2397 return (CFGA_OK); 2398 } 2399 2400 static cfga_err_t 2401 stat_plugin_v2(list_stat_t *lstatp, lib_loc_t *libloc_p, char **errstring) 2402 { 2403 int i; 2404 array_list_t *alp, *alp2 = NULL; 2405 cfga_err_t rc; 2406 char *class; 2407 2408 /* 2409 * allocate array list 2410 */ 2411 if ((alp = config_calloc_check(1, sizeof (array_list_t), 2412 errstring)) == NULL) { 2413 return (CFGA_LIB_ERROR); 2414 } 2415 2416 alp->array = NULL; 2417 alp->nelem = 0; 2418 2419 /* 2420 * The listopts argument is currently unused. Use NULL 2421 */ 2422 errno = 0; 2423 if ((rc = (*(libloc_p->libp->cfga_list_ext_p))( 2424 libloc_p->ap_physical, &alp->array, &alp->nelem, lstatp->opts, NULL, 2425 errstring, lstatp->flags)) != CFGA_OK || alp->nelem <= 0) { 2426 S_FREE(alp); 2427 return (rc); 2428 } 2429 alp->next = NULL; 2430 2431 /* 2432 * Set up the logical and physical id's if necessary. 2433 * For v2 interfaces, the generic library (libcfgadm) creates the 2434 * ap_ids only if there are no dynamic attachment points and the 2435 * plug-in does not create the name itself. mklog() is 2436 * assumed to have been called in the caller of this routine. 2437 */ 2438 if (alp->nelem == 1) { 2439 char cphys, clog; 2440 2441 clog = (alp->array[0]).ap_log_id[0]; 2442 cphys = (alp->array[0]).ap_phys_id[0]; 2443 2444 if (clog == '\0') { 2445 (void) snprintf((alp->array[0]).ap_log_id, 2446 sizeof ((alp->array[0]).ap_log_id), "%s", 2447 libloc_p->ap_logical); 2448 } 2449 2450 if (cphys == '\0') { 2451 (void) snprintf((alp->array[0]).ap_phys_id, 2452 sizeof ((alp->array[0]).ap_phys_id), "%s", 2453 libloc_p->ap_physical); 2454 } 2455 } 2456 2457 if (libloc_p->ap_class[0] == '\0') { 2458 class = CFGA_NO_CLASS; 2459 } else { 2460 class = libloc_p->ap_class; 2461 } 2462 2463 /* Fill in the class information for all list elements */ 2464 for (i = 0; i < alp->nelem; i++) { 2465 (void) snprintf((alp->array[i]).ap_class, 2466 sizeof ((alp->array[i]).ap_class), "%s", class); 2467 } 2468 2469 /* 2470 * link it in 2471 */ 2472 if ((alp2 = lstatp->al) == NULL) { 2473 lstatp->al = alp; 2474 } else { 2475 while (alp2->next != NULL) 2476 alp2 = alp2->next; 2477 alp2->next = alp; 2478 } 2479 2480 /* keep count */ 2481 (*lstatp->countp) += alp->nelem; 2482 2483 return (CFGA_OK); 2484 } 2485 2486 /* 2487 * Check if a plugin version is within requested limits. 2488 */ 2489 static int 2490 compat_plugin(vers_req_t *reqp, int plugin_vers) 2491 { 2492 2493 if (!VALID_HSL_VERS(reqp->v_min) || !VALID_HSL_VERS(reqp->v_max) || 2494 !VALID_HSL_VERS(plugin_vers)) { 2495 return (0); 2496 } 2497 2498 if (plugin_vers < reqp->v_min || plugin_vers > reqp->v_max) { 2499 return (0); 2500 } 2501 2502 2503 return (1); 2504 } 2505 2506 /* 2507 * find_arg_type - determine if an argument is an ap_id or an ap_type. 2508 * Adapted from cfgadm.c 2509 */ 2510 static cfga_ap_types_t 2511 find_arg_type(const char *ap_id) 2512 { 2513 struct stat sbuf; 2514 cfga_ap_types_t type = UNKNOWN_AP; 2515 char *mkr = NULL; 2516 size_t len; 2517 int size_ap = 0, size_mkr = 0, digit = 0, i = 0; 2518 char *cp, path[MAXPATHLEN], ap_base[MAXPATHLEN]; 2519 2520 2521 /* 2522 * sanity checks 2523 */ 2524 if (ap_id == NULL || *ap_id == '\0') { 2525 2526 return (UNKNOWN_AP); 2527 } 2528 2529 /* 2530 * Extract the base component 2531 */ 2532 if ((cp = GET_DYN(ap_id)) != NULL) { 2533 len = cp - ap_id; 2534 } else { 2535 len = strlen(ap_id); 2536 } 2537 2538 if (len >= sizeof (ap_base)) { 2539 return (UNKNOWN_AP); 2540 } 2541 2542 /* Copy only the first "len" chars */ 2543 (void) strncpy(ap_base, ap_id, len); 2544 ap_base[len] = '\0'; 2545 2546 /* 2547 * If it starts with a slash and is stat-able its a physical. 2548 */ 2549 if (*ap_base == '/' && stat(ap_base, &sbuf) == 0) { 2550 return (PHYSICAL_AP); 2551 } 2552 2553 /* 2554 * Is this a symlink in CFGA_DEV_DIR ? 2555 */ 2556 (void) snprintf(path, sizeof (path), "%s%s", 2557 CFGA_DEV_DIR SLASH, ap_base); 2558 2559 if (lstat(path, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) && 2560 stat(path, &sbuf) == 0) { 2561 return (LOGICAL_LINK_AP); 2562 } 2563 2564 /* 2565 * Check for ":" which is always present in an ap_id 2566 * but not in an ap_type. 2567 * we need to check that the characters right before the : are digits 2568 * since an ap_id is of the form <name><instance>:<specific ap name> 2569 */ 2570 if ((mkr = strchr(ap_base, ':')) == NULL) { 2571 type = AP_TYPE; 2572 } else { 2573 size_ap = strlen(ap_base); 2574 size_mkr = strlen(mkr); 2575 mkr = ap_base; 2576 2577 digit = 0; 2578 for (i = size_ap - size_mkr - 1; i > 0; i--) { 2579 if ((int)isdigit(mkr[i])) { 2580 digit++; 2581 break; 2582 } 2583 } 2584 if (digit == 0) { 2585 type = AP_TYPE; 2586 } else { 2587 type = LOGICAL_DRV_AP; 2588 } 2589 } 2590 2591 return (type); 2592 } 2593 2594 /*ARGSUSED*/ 2595 static cfga_err_t 2596 null_get_cond(lib_loc_t *liblocp, cfga_cond_t *condp, char **errstring) 2597 { 2598 return (CFGA_OK); 2599 } 2600 2601 static cfga_err_t 2602 get_cond_v1(lib_loc_t *liblocp, cfga_cond_t *condp, char **errstring) 2603 { 2604 plugin_lib_t *libp; 2605 cfga_stat_data_t sdbuf; 2606 cfga_err_t rc; 2607 2608 2609 libp = liblocp->libp; 2610 if (libp->plugin_vers != CFGA_HSL_V1) { 2611 return (CFGA_LIB_ERROR); 2612 } 2613 2614 errno = 0; 2615 if ((rc = (*liblocp->libp->cfga_stat_p)( 2616 liblocp->ap_physical, &sdbuf, NULL, errstring)) 2617 == CFGA_OK) { 2618 *condp = sdbuf.ap_cond; 2619 } else { 2620 *condp = CFGA_COND_UNKNOWN; 2621 } 2622 2623 return (rc); 2624 } 2625 2626 static cfga_err_t 2627 get_cond_v2(lib_loc_t *liblocp, cfga_cond_t *condp, char **errstring) 2628 { 2629 int nelem; 2630 plugin_lib_t *libp; 2631 cfga_list_data_t *ldbufp; 2632 cfga_err_t rc; 2633 2634 2635 libp = liblocp->libp; 2636 if (libp->plugin_vers != CFGA_HSL_V2) { 2637 return (CFGA_LIB_ERROR); 2638 } 2639 2640 errno = 0; 2641 nelem = 0; 2642 ldbufp = NULL; 2643 if ((rc = (*liblocp->libp->cfga_list_ext_p)( 2644 liblocp->ap_physical, &ldbufp, &nelem, NULL, NULL, 2645 errstring, 0)) == CFGA_OK) { 2646 assert(nelem == 1 && ldbufp != NULL); 2647 2648 *condp = ldbufp->ap_cond; 2649 S_FREE(ldbufp); 2650 } else { 2651 *condp = CFGA_COND_UNKNOWN; 2652 } 2653 2654 return (rc); 2655 } 2656 2657 /* mask represents the flags accepted */ 2658 static cfga_err_t 2659 check_flags(cfga_flags_t flags, cfga_flags_t mask, char **errstring) 2660 { 2661 if ((flags & ~mask) != 0) { 2662 config_err(0, INVALID_ARGS, errstring); 2663 return (CFGA_ERROR); 2664 } else { 2665 return (CFGA_OK); 2666 } 2667 } 2668 2669 static cfga_err_t 2670 check_apids(int num_ap_ids, char *const *ap_ids, char **errstring) 2671 { 2672 if (num_ap_ids <= 0 || ap_ids == NULL) { 2673 config_err(0, INVALID_ARGS, errstring); 2674 return (CFGA_ERROR); 2675 } else { 2676 return (CFGA_OK); 2677 } 2678 } 2679 2680 /* 2681 * Returns the class or the empty string if attacment point has 2682 * no class. 2683 */ 2684 static char * 2685 get_class(di_minor_t minor) 2686 { 2687 char *cp, c; 2688 size_t len; 2689 2690 2691 if (minor == DI_MINOR_NIL) { 2692 return (NULL); 2693 } 2694 2695 cp = di_minor_nodetype(minor); 2696 if (cp == NULL) { 2697 return (NULL); 2698 } 2699 2700 len = strlen(DDI_NT_ATTACHMENT_POINT); 2701 if (strncmp(cp, DDI_NT_ATTACHMENT_POINT, len)) { 2702 return (NULL); 2703 } 2704 2705 cp += len; 2706 2707 c = *cp; 2708 if (c != '\0' && c != ':') { 2709 return (NULL); 2710 } 2711 2712 if (c == ':') { 2713 cp++; 2714 } 2715 2716 return (cp); 2717 2718 } 2719 2720 /* 2721 * Transform stat data to list data 2722 */ 2723 static void 2724 stat_to_list(cfga_list_data_t *lp, cfga_stat_data_t *statp) 2725 { 2726 2727 (void) snprintf(lp->ap_log_id, sizeof (lp->ap_log_id), "%s", 2728 statp->ap_log_id); 2729 2730 (void) snprintf(lp->ap_phys_id, sizeof (lp->ap_phys_id), "%s", 2731 statp->ap_phys_id); 2732 2733 (void) snprintf(lp->ap_class, sizeof (lp->ap_class), "%s", 2734 CFGA_NO_CLASS); 2735 2736 lp->ap_r_state = statp->ap_r_state; 2737 lp->ap_o_state = statp->ap_o_state; 2738 lp->ap_cond = statp->ap_cond; 2739 lp->ap_busy = statp->ap_busy; 2740 lp->ap_status_time = statp->ap_status_time; 2741 2742 (void) snprintf(lp->ap_info, sizeof (lp->ap_info), "%s", 2743 statp->ap_info); 2744 (void) snprintf(lp->ap_type, sizeof (lp->ap_type), "%s", 2745 statp->ap_type); 2746 } 2747 2748 static void 2749 lstat_free(list_stat_t *lstatp) 2750 { 2751 stat_data_list_t *slp, *slp2; 2752 array_list_t *ap, *ap2; 2753 2754 slp = lstatp->sdl; 2755 while (slp != NULL) { 2756 slp2 = slp->next; 2757 S_FREE(slp); 2758 slp = slp2; 2759 } 2760 2761 lstatp->sdl = NULL; 2762 2763 ap = lstatp->al; 2764 while (ap != NULL) { 2765 ap2 = ap->next; 2766 S_FREE(ap->array); 2767 S_FREE(ap); 2768 ap = ap2; 2769 } 2770 2771 lstatp->al = NULL; 2772 } 2773 2774 static cfga_err_t 2775 split_apid(char *ap_id, char **dyncompp, char **errstring) 2776 { 2777 char *cp; 2778 2779 *dyncompp = NULL; 2780 2781 if (ap_id == NULL) { 2782 return (CFGA_ERROR); 2783 } 2784 2785 if ((cp = strstr(ap_id, CFGA_DYN_SEP)) == NULL) { 2786 return (CFGA_OK); 2787 } 2788 2789 *cp = '\0'; 2790 cp += strlen(CFGA_DYN_SEP); 2791 if ((*dyncompp = config_calloc_check(1, strlen(cp) + 1, 2792 errstring)) == NULL) { 2793 return (CFGA_LIB_ERROR); 2794 } 2795 (void) strcpy(*dyncompp, cp); 2796 2797 return (CFGA_OK); 2798 } 2799 2800 static void 2801 append_dyn(char *buf, const char *dyncomp, size_t blen) 2802 { 2803 if (dyncomp != NULL) { 2804 char *cp = buf + strlen(buf); 2805 size_t len = blen - strlen(buf); 2806 2807 (void) snprintf(cp, len, "%s%s", CFGA_DYN_SEP, 2808 dyncomp); 2809 } 2810 } 2811 2812 /* 2813 * Default implementation of cfga_ap_id_cmp. Works for most cases 2814 * except for long hex number sequences like world-wide-name. 2815 * 2816 * This function compares the ap's in a generic way. It does so by 2817 * determining the place of difference between the 2 aps. If the first 2818 * difference is a digit, it attempts to obtain the numbers and compare them 2819 * Otherwise it just compares the aps as strings 2820 */ 2821 static int 2822 default_ap_id_cmp(const char *ap_id1, const char *ap_id2) 2823 { 2824 int i = 0; 2825 2826 /* 2827 * Search for first different char 2828 */ 2829 while (ap_id1[i] == ap_id2[i] && ap_id1[i] != '\0') 2830 i++; 2831 2832 /* 2833 * If one of the char is a digit, back up to where the 2834 * number started, compare the number. 2835 */ 2836 if (isdigit(ap_id1[i]) || isdigit(ap_id2[i])) { 2837 while ((i > 0) && isdigit(ap_id1[i - 1])) 2838 i--; 2839 2840 if (isdigit(ap_id1[i]) && isdigit(ap_id2[i])) 2841 return (atoi(ap_id1 + i) - atoi(ap_id2 + i)); 2842 } 2843 2844 /* One of them isn't a number, compare the char */ 2845 return (ap_id1[i] - ap_id2[i]); 2846 } 2847 2848 static void 2849 hold_lib(plugin_lib_t *libp) 2850 { 2851 assert(libp->refcnt >= 0); 2852 (void) mutex_lock(&libp->lock); 2853 libp->refcnt++; 2854 (void) mutex_unlock(&libp->lock); 2855 } 2856 2857 static void 2858 rele_lib(plugin_lib_t *libp) 2859 { 2860 assert(libp->refcnt > 0); 2861 (void) mutex_lock(&libp->lock); 2862 libp->refcnt--; 2863 (void) mutex_unlock(&libp->lock); 2864 } 2865