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 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include "cfga_scsi.h" 30 31 /* 32 * This file contains helper routines for the SCSI plugin 33 */ 34 35 #if !defined(TEXT_DOMAIN) 36 #define TEXT_DOMAIN "SYS_TEST" 37 #endif 38 39 typedef struct strlist { 40 const char *str; 41 struct strlist *next; 42 } strlist_t; 43 44 typedef struct { 45 scfga_ret_t scsi_err; 46 cfga_err_t cfga_err; 47 } errcvt_t; 48 49 typedef struct { 50 scfga_cmd_t cmd; 51 int type; 52 int (*fcn)(const devctl_hdl_t); 53 } set_state_cmd_t; 54 55 typedef struct { 56 scfga_cmd_t cmd; 57 int type; 58 int (*state_fcn)(const devctl_hdl_t, uint_t *); 59 } get_state_cmd_t; 60 61 /* Function prototypes */ 62 static char *pathdup(const char *path, int *l_errnop); 63 static void msg_common(char **err_msgpp, int append_newline, int l_errno, 64 va_list ap); 65 66 /* 67 * The string table contains most of the strings used by the scsi cfgadm plugin. 68 * All strings which are to be internationalized must be in this table. 69 * Some strings which are not internationalized are also included here. 70 * Arguments to messages are NOT internationalized. 71 */ 72 msgcvt_t str_tbl[] = { 73 74 /* 75 * The first element (ERR_UNKNOWN) MUST always be present in the array. 76 */ 77 #define UNKNOWN_ERR_IDX 0 /* Keep the index in sync */ 78 79 80 /* msg_code num_args, I18N msg_string */ 81 82 /* ERRORS */ 83 {ERR_UNKNOWN, 0, 1, "unknown error"}, 84 {ERR_OP_FAILED, 0, 1, "operation failed"}, 85 {ERR_CMD_INVAL, 0, 1, "invalid command"}, 86 {ERR_NOT_BUSAPID, 0, 1, "not a SCSI bus apid"}, 87 {ERR_APID_INVAL, 0, 1, "invalid SCSI ap_id"}, 88 {ERR_NOT_BUSOP, 0, 1, "operation not supported for SCSI bus"}, 89 {ERR_NOT_DEVOP, 0, 1, "operation not supported for SCSI device"}, 90 {ERR_UNAVAILABLE, 0, 1, "unavailable"}, 91 {ERR_CTRLR_CRIT, 0, 1, "critical partition controlled by SCSI HBA"}, 92 {ERR_BUS_GETSTATE, 0, 1, "failed to get state for SCSI bus"}, 93 {ERR_BUS_NOTCONNECTED, 0, 1, "SCSI bus not connected"}, 94 {ERR_BUS_CONNECTED, 0, 1, "SCSI bus not disconnected"}, 95 {ERR_BUS_QUIESCE, 0, 1, "SCSI bus quiesce failed"}, 96 {ERR_BUS_UNQUIESCE, 0, 1, "SCSI bus unquiesce failed"}, 97 {ERR_BUS_CONFIGURE, 0, 1, "failed to configure devices on SCSI bus"}, 98 {ERR_BUS_UNCONFIGURE, 0, 1, "failed to unconfigure SCSI bus"}, 99 {ERR_DEV_CONFIGURE, 0, 1, "failed to configure SCSI device"}, 100 {ERR_DEV_RECONFIGURE, 1, 1, "failed to reconfigure device: "}, 101 {ERR_DEV_UNCONFIGURE, 0, 1, "failed to unconfigure SCSI device"}, 102 {ERR_DEV_REMOVE, 0, 1, "remove operation failed"}, 103 {ERR_DEV_REPLACE, 0, 1, "replace operation failed"}, 104 {ERR_DEV_INSERT, 0, 1, "insert operation failed"}, 105 {ERR_DEV_GETSTATE, 0, 1, "failed to get state for SCSI device"}, 106 {ERR_RESET, 0, 1, "reset failed"}, 107 {ERR_LIST, 0, 1, "list operation failed"}, 108 {ERR_MAYBE_BUSY, 0, 1, "device may be busy"}, 109 {ERR_BUS_DEV_MISMATCH, 0, 1, "mismatched SCSI bus and device"}, 110 {ERR_VAR_RUN, 0, 1, "/var/run is not mounted"}, 111 {ERR_FORK, 0, 1, "failed to fork cleanup handler"}, 112 113 /* Errors with arguments */ 114 {ERRARG_OPT_INVAL, 1, 1, "invalid option: "}, 115 {ERRARG_HWCMD_INVAL, 1, 1, "invalid command: "}, 116 {ERRARG_DEVINFO, 1, 1, "libdevinfo failed on path: "}, 117 {ERRARG_OPEN, 1, 1, "open failed: "}, 118 {ERRARG_LOCK, 1, 1, "lock failed: "}, 119 {ERRARG_QUIESCE_LOCK, 1, 1, "cannot acquire quiesce lock: "}, 120 121 /* RCM Errors */ 122 {ERR_RCM_HANDLE, 0, 1, "cannot get RCM handle"}, 123 {ERRARG_RCM_SUSPEND, 0, 1, "failed to suspend: "}, 124 {ERRARG_RCM_RESUME, 0, 1, "failed to resume: "}, 125 {ERRARG_RCM_OFFLINE, 0, 1, "failed to offline: "}, 126 {ERRARG_RCM_ONLINE, 0, 1, "failed to online: "}, 127 {ERRARG_RCM_REMOVE, 0, 1, "failed to remove: "}, 128 129 /* Commands */ 130 {CMD_INSERT_DEV, 0, 0, "insert_device"}, 131 {CMD_REMOVE_DEV, 0, 0, "remove_device"}, 132 {CMD_REPLACE_DEV, 0, 0, "replace_device"}, 133 {CMD_RESET_DEV, 0, 0, "reset_device"}, 134 {CMD_RESET_BUS, 0, 0, "reset_bus"}, 135 {CMD_RESET_ALL, 0, 0, "reset_all"}, 136 137 /* help messages */ 138 {MSG_HELP_HDR, 0, 1, "\nSCSI specific commands and options:\n"}, 139 {MSG_HELP_USAGE, 0, 0, "\t-x insert_device ap_id [ap_id... ]\n" 140 "\t-x remove_device ap_id [ap_id... ]\n" 141 "\t-x replace_device ap_id [ap_id... ]\n" 142 "\t-x reset_device ap_id [ap_id... ]\n" 143 "\t-x reset_bus ap_id [ap_id... ]\n" 144 "\t-x reset_all ap_id [ap_id... ]\n"}, 145 146 /* hotplug messages */ 147 {MSG_INSDEV, 1, 1, "Adding device to SCSI HBA: "}, 148 {MSG_RMDEV, 1, 1, "Removing SCSI device: "}, 149 {MSG_REPLDEV, 1, 1, "Replacing SCSI device: "}, 150 {MSG_WAIT_LOCK, 0, 1, "Waiting for quiesce lock... "}, 151 152 /* Hotplugging confirmation prompts */ 153 {CONF_QUIESCE_1, 1, 1, 154 "This operation will suspend activity on SCSI bus: "}, 155 156 {CONF_QUIESCE_2, 0, 1, "\nContinue"}, 157 158 {CONF_UNQUIESCE, 0, 1, 159 "SCSI bus quiesced successfully.\n" 160 "It is now safe to proceed with hotplug operation." 161 "\nEnter y if operation is complete or n to abort"}, 162 163 {CONF_NO_QUIESCE, 0, 1, 164 "Proceed with hotplug operation." 165 "\nEnter y if operation is complete or n to abort"}, 166 167 /* Misc. */ 168 {WARN_DISCONNECT, 0, 1, 169 "WARNING: Disconnecting critical partitions may cause system hang." 170 "\nContinue"} 171 }; 172 173 174 #define N_STRS (sizeof (str_tbl) / sizeof (str_tbl[0])) 175 176 #define GET_MSG_NARGS(i) (str_tbl[msg_idx(i)].nargs) 177 #define GET_MSG_INTL(i) (str_tbl[msg_idx(i)].intl) 178 179 static errcvt_t err_cvt_tbl[] = { 180 { SCFGA_OK, CFGA_OK }, 181 { SCFGA_LIB_ERR, CFGA_LIB_ERROR }, 182 { SCFGA_APID_NOEXIST, CFGA_APID_NOEXIST }, 183 { SCFGA_NACK, CFGA_NACK }, 184 { SCFGA_BUSY, CFGA_BUSY }, 185 { SCFGA_SYSTEM_BUSY, CFGA_SYSTEM_BUSY }, 186 { SCFGA_OPNOTSUPP, CFGA_OPNOTSUPP }, 187 { SCFGA_PRIV, CFGA_PRIV }, 188 { SCFGA_UNKNOWN_ERR, CFGA_ERROR }, 189 { SCFGA_ERR, CFGA_ERROR } 190 }; 191 192 #define N_ERR_CVT_TBL (sizeof (err_cvt_tbl)/sizeof (err_cvt_tbl[0])) 193 194 #define DEV_OP 0 195 #define BUS_OP 1 196 static set_state_cmd_t set_state_cmds[] = { 197 198 { SCFGA_BUS_QUIESCE, BUS_OP, devctl_bus_quiesce }, 199 { SCFGA_BUS_UNQUIESCE, BUS_OP, devctl_bus_unquiesce }, 200 { SCFGA_BUS_CONFIGURE, BUS_OP, devctl_bus_configure }, 201 { SCFGA_BUS_UNCONFIGURE, BUS_OP, devctl_bus_unconfigure }, 202 { SCFGA_RESET_BUS, BUS_OP, devctl_bus_reset }, 203 { SCFGA_RESET_ALL, BUS_OP, devctl_bus_resetall }, 204 { SCFGA_DEV_CONFIGURE, DEV_OP, devctl_device_online }, 205 { SCFGA_DEV_UNCONFIGURE, DEV_OP, devctl_device_offline }, 206 { SCFGA_DEV_REMOVE, DEV_OP, devctl_device_remove }, 207 { SCFGA_RESET_DEV, DEV_OP, devctl_device_reset } 208 209 }; 210 211 #define N_SET_STATE_CMDS (sizeof (set_state_cmds)/sizeof (set_state_cmds[0])) 212 213 static get_state_cmd_t get_state_cmds[] = { 214 { SCFGA_BUS_GETSTATE, BUS_OP, devctl_bus_getstate }, 215 { SCFGA_DEV_GETSTATE, DEV_OP, devctl_device_getstate } 216 }; 217 218 #define N_GET_STATE_CMDS (sizeof (get_state_cmds)/sizeof (get_state_cmds[0])) 219 220 /* 221 * SCSI hardware specific commands 222 */ 223 static hw_cmd_t hw_cmds[] = { 224 /* Command string Command ID Function */ 225 226 { CMD_INSERT_DEV, SCFGA_INSERT_DEV, dev_insert }, 227 { CMD_REMOVE_DEV, SCFGA_REMOVE_DEV, dev_remove }, 228 { CMD_REPLACE_DEV, SCFGA_REPLACE_DEV, dev_replace }, 229 { CMD_RESET_DEV, SCFGA_RESET_DEV, reset_common }, 230 { CMD_RESET_BUS, SCFGA_RESET_BUS, reset_common }, 231 { CMD_RESET_ALL, SCFGA_RESET_ALL, reset_common }, 232 }; 233 #define N_HW_CMDS (sizeof (hw_cmds) / sizeof (hw_cmds[0])) 234 235 236 cfga_err_t 237 err_cvt(scfga_ret_t s_err) 238 { 239 int i; 240 241 for (i = 0; i < N_ERR_CVT_TBL; i++) { 242 if (err_cvt_tbl[i].scsi_err == s_err) { 243 return (err_cvt_tbl[i].cfga_err); 244 } 245 } 246 247 return (CFGA_ERROR); 248 } 249 250 /* 251 * Removes duplicate slashes from a pathname and any trailing slashes. 252 * Returns "/" if input is "/" 253 */ 254 static char * 255 pathdup(const char *path, int *l_errnop) 256 { 257 int prev_was_slash = 0; 258 char c, *dp = NULL, *dup = NULL; 259 const char *sp = NULL; 260 261 *l_errnop = 0; 262 263 if (path == NULL) { 264 return (NULL); 265 } 266 267 if ((dup = calloc(1, strlen(path) + 1)) == NULL) { 268 *l_errnop = errno; 269 return (NULL); 270 } 271 272 prev_was_slash = 0; 273 for (sp = path, dp = dup; (c = *sp) != '\0'; sp++) { 274 if (!prev_was_slash || c != '/') { 275 *dp++ = c; 276 } 277 if (c == '/') { 278 prev_was_slash = 1; 279 } else { 280 prev_was_slash = 0; 281 } 282 } 283 284 /* Remove trailing slash except if it is the first char */ 285 if (prev_was_slash && dp != dup && dp - 1 != dup) { 286 *(--dp) = '\0'; 287 } else { 288 *dp = '\0'; 289 } 290 291 return (dup); 292 } 293 294 scfga_ret_t 295 apidt_create(const char *ap_id, apid_t *apidp, char **errstring) 296 { 297 char *hba_phys = NULL, *dyn = NULL; 298 char *dyncomp = NULL, *path = NULL; 299 int l_errno = 0; 300 size_t len = 0; 301 scfga_ret_t ret; 302 303 if ((hba_phys = pathdup(ap_id, &l_errno)) == NULL) { 304 cfga_err(errstring, l_errno, ERR_OP_FAILED, 0); 305 return (SCFGA_LIB_ERR); 306 } 307 308 /* Extract the base(hba) and dynamic(device) component if any */ 309 dyncomp = NULL; 310 if ((dyn = GET_DYN(hba_phys)) != NULL) { 311 len = strlen(DYN_TO_DYNCOMP(dyn)) + 1; 312 dyncomp = calloc(1, len); 313 if (dyncomp == NULL) { 314 cfga_err(errstring, errno, ERR_OP_FAILED, 0); 315 ret = SCFGA_LIB_ERR; 316 goto err; 317 } 318 (void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn)); 319 320 /* Remove the dynamic component from the base */ 321 *dyn = '\0'; 322 } 323 324 /* Create the path */ 325 if ((ret = apid_to_path(hba_phys, dyncomp, &path, &l_errno)) 326 != SCFGA_OK) { 327 cfga_err(errstring, l_errno, ERR_OP_FAILED, 0); 328 goto err; 329 } 330 331 assert(path != NULL); 332 assert(hba_phys != NULL); 333 334 apidp->hba_phys = hba_phys; 335 apidp->dyncomp = dyncomp; 336 apidp->path = path; 337 apidp->flags = 0; 338 339 return (SCFGA_OK); 340 341 err: 342 S_FREE(hba_phys); 343 S_FREE(dyncomp); 344 S_FREE(path); 345 return (ret); 346 } 347 348 void 349 apidt_free(apid_t *apidp) 350 { 351 if (apidp == NULL) 352 return; 353 354 S_FREE(apidp->hba_phys); 355 S_FREE(apidp->dyncomp); 356 S_FREE(apidp->path); 357 } 358 359 scfga_ret_t 360 walk_tree( 361 const char *physpath, 362 void *arg, 363 uint_t init_flags, 364 walkarg_t *up, 365 scfga_cmd_t cmd, 366 int *l_errnop) 367 { 368 int rv; 369 di_node_t root, walk_root; 370 char *root_path, *cp = NULL, *init_path; 371 size_t len; 372 scfga_ret_t ret; 373 374 *l_errnop = 0; 375 376 if ((root_path = strdup(physpath)) == NULL) { 377 *l_errnop = errno; 378 return (SCFGA_LIB_ERR); 379 } 380 381 /* Fix up path for di_init() */ 382 len = strlen(DEVICES_DIR); 383 if (strncmp(root_path, DEVICES_DIR SLASH, 384 len + strlen(SLASH)) == 0) { 385 cp = root_path + len; 386 (void) memmove(root_path, cp, strlen(cp) + 1); 387 } else if (*root_path != '/') { 388 *l_errnop = 0; 389 ret = SCFGA_ERR; 390 goto out; 391 } 392 393 /* Remove dynamic component if any */ 394 if ((cp = GET_DYN(root_path)) != NULL) { 395 *cp = '\0'; 396 } 397 398 /* Remove minor name if any */ 399 if ((cp = strrchr(root_path, ':')) != NULL) { 400 *cp = '\0'; 401 } 402 403 /* 404 * Cached snapshots are always rooted at "/" 405 */ 406 init_path = root_path; 407 if ((init_flags & DINFOCACHE) == DINFOCACHE) { 408 init_path = "/"; 409 } 410 411 /* Get a snapshot */ 412 if ((root = di_init(init_path, init_flags)) == DI_NODE_NIL) { 413 *l_errnop = errno; 414 ret = SCFGA_LIB_ERR; 415 goto out; 416 } 417 418 /* 419 * Lookup the subtree of interest 420 */ 421 walk_root = root; 422 if ((init_flags & DINFOCACHE) == DINFOCACHE) { 423 walk_root = di_lookup_node(root, root_path); 424 } 425 426 if (walk_root == DI_NODE_NIL) { 427 *l_errnop = errno; 428 di_fini(root); 429 ret = SCFGA_LIB_ERR; 430 goto out; 431 } 432 433 /* Walk the tree */ 434 errno = 0; 435 if (cmd == SCFGA_WALK_NODE) { 436 rv = di_walk_node(walk_root, up->node_args.flags, arg, 437 up->node_args.fcn); 438 } else { 439 assert(cmd == SCFGA_WALK_MINOR); 440 rv = di_walk_minor(walk_root, up->minor_args.nodetype, 0, arg, 441 up->minor_args.fcn); 442 } 443 444 if (rv != 0) { 445 *l_errnop = errno; 446 ret = SCFGA_LIB_ERR; 447 } else { 448 *l_errnop = 0; 449 ret = SCFGA_OK; 450 } 451 452 di_fini(root); 453 454 /*FALLTHRU*/ 455 out: 456 S_FREE(root_path); 457 return (ret); 458 } 459 460 scfga_ret_t 461 invoke_cmd( 462 const char *func, 463 apid_t *apidtp, 464 prompt_t *prp, 465 cfga_flags_t flags, 466 char **errstring) 467 { 468 int i; 469 470 for (i = 0; i < N_HW_CMDS; i++) { 471 if (strcmp(func, GET_MSG_STR(hw_cmds[i].str_id)) == 0) { 472 return (hw_cmds[i].fcn(hw_cmds[i].cmd, apidtp, 473 prp, flags, errstring)); 474 } 475 } 476 477 cfga_err(errstring, 0, ERRARG_HWCMD_INVAL, func, 0); 478 return (SCFGA_ERR); 479 } 480 481 int 482 msg_idx(msgid_t msgid) 483 { 484 int idx = 0; 485 486 /* The string table index and the error id may or may not be same */ 487 if (msgid >= 0 && msgid <= N_STRS - 1 && 488 str_tbl[msgid].msgid == msgid) { 489 idx = msgid; 490 } else { 491 for (idx = 0; idx < N_STRS; idx++) { 492 if (str_tbl[idx].msgid == msgid) 493 break; 494 } 495 if (idx >= N_STRS) { 496 idx = UNKNOWN_ERR_IDX; 497 } 498 } 499 500 return (idx); 501 } 502 503 /* 504 * cfga_err() accepts a variable number of message IDs and constructs 505 * a corresponding error string which is returned via the errstring argument. 506 * cfga_err() calls dgettext() to internationalize proper messages. 507 * May be called with a NULL argument. 508 */ 509 void 510 cfga_err(char **errstring, int l_errno, ...) 511 { 512 va_list ap; 513 int append_newline = 0; 514 515 if (errstring == NULL || *errstring != NULL) { 516 return; 517 } 518 519 /* 520 * Don't append a newline, the application (for example cfgadm) 521 * should do that. 522 */ 523 append_newline = 0; 524 525 va_start(ap, l_errno); 526 msg_common(errstring, append_newline, l_errno, ap); 527 va_end(ap); 528 } 529 530 /* 531 * This routine accepts a variable number of message IDs and constructs 532 * a corresponding message string which is printed via the message print 533 * routine argument. 534 */ 535 void 536 cfga_msg(struct cfga_msg *msgp, ...) 537 { 538 char *p = NULL; 539 int append_newline = 0, l_errno = 0; 540 va_list ap; 541 542 if (msgp == NULL || msgp->message_routine == NULL) { 543 return; 544 } 545 546 /* Append a newline after message */ 547 append_newline = 1; 548 l_errno = 0; 549 550 va_start(ap, msgp); 551 msg_common(&p, append_newline, l_errno, ap); 552 va_end(ap); 553 554 (void) (*msgp->message_routine)(msgp->appdata_ptr, p); 555 556 S_FREE(p); 557 } 558 559 /* 560 * Get internationalized string corresponding to message id 561 * Caller must free the memory allocated. 562 */ 563 char * 564 cfga_str(int append_newline, ...) 565 { 566 char *p = NULL; 567 int l_errno = 0; 568 va_list ap; 569 570 va_start(ap, append_newline); 571 msg_common(&p, append_newline, l_errno, ap); 572 va_end(ap); 573 574 return (p); 575 } 576 577 static void 578 msg_common(char **msgpp, int append_newline, int l_errno, va_list ap) 579 { 580 int a = 0; 581 size_t len = 0; 582 int i = 0, n = 0; 583 char *s = NULL, *t = NULL; 584 strlist_t dummy; 585 strlist_t *savep = NULL, *sp = NULL, *tailp = NULL; 586 587 if (*msgpp != NULL) { 588 return; 589 } 590 591 dummy.next = NULL; 592 tailp = &dummy; 593 for (len = 0; (a = va_arg(ap, int)) != 0; ) { 594 n = GET_MSG_NARGS(a); /* 0 implies no additional args */ 595 for (i = 0; i <= n; i++) { 596 sp = calloc(1, sizeof (*sp)); 597 if (sp == NULL) { 598 goto out; 599 } 600 if (i == 0 && GET_MSG_INTL(a)) { 601 sp->str = dgettext(TEXT_DOMAIN, GET_MSG_STR(a)); 602 } else if (i == 0) { 603 sp->str = GET_MSG_STR(a); 604 } else { 605 sp->str = va_arg(ap, char *); 606 } 607 len += (strlen(sp->str)); 608 sp->next = NULL; 609 tailp->next = sp; 610 tailp = sp; 611 } 612 } 613 614 len += 1; /* terminating NULL */ 615 616 s = t = NULL; 617 if (l_errno) { 618 s = dgettext(TEXT_DOMAIN, ": "); 619 t = S_STR(strerror(l_errno)); 620 if (s != NULL && t != NULL) { 621 len += strlen(s) + strlen(t); 622 } 623 } 624 625 if (append_newline) { 626 len++; 627 } 628 629 if ((*msgpp = calloc(1, len)) == NULL) { 630 goto out; 631 } 632 633 **msgpp = '\0'; 634 for (sp = dummy.next; sp != NULL; sp = sp->next) { 635 (void) strcat(*msgpp, sp->str); 636 } 637 638 if (s != NULL && t != NULL) { 639 (void) strcat(*msgpp, s); 640 (void) strcat(*msgpp, t); 641 } 642 643 if (append_newline) { 644 (void) strcat(*msgpp, dgettext(TEXT_DOMAIN, "\n")); 645 } 646 647 /* FALLTHROUGH */ 648 out: 649 sp = dummy.next; 650 while (sp != NULL) { 651 savep = sp->next; 652 S_FREE(sp); 653 sp = savep; 654 } 655 } 656 657 scfga_ret_t 658 devctl_cmd( 659 const char *physpath, 660 scfga_cmd_t cmd, 661 uint_t *statep, 662 int *l_errnop) 663 { 664 int rv = -1, i, type; 665 devctl_hdl_t hdl = NULL; 666 char *cp = NULL, *path = NULL; 667 int (*func)(const devctl_hdl_t); 668 int (*state_func)(const devctl_hdl_t, uint_t *); 669 670 *l_errnop = 0; 671 672 if (statep != NULL) *statep = 0; 673 674 func = NULL; 675 state_func = NULL; 676 type = 0; 677 678 for (i = 0; i < N_GET_STATE_CMDS; i++) { 679 if (get_state_cmds[i].cmd == cmd) { 680 state_func = get_state_cmds[i].state_fcn; 681 type = get_state_cmds[i].type; 682 assert(statep != NULL); 683 break; 684 } 685 } 686 687 if (state_func == NULL) { 688 for (i = 0; i < N_SET_STATE_CMDS; i++) { 689 if (set_state_cmds[i].cmd == cmd) { 690 func = set_state_cmds[i].fcn; 691 type = set_state_cmds[i].type; 692 assert(statep == NULL); 693 break; 694 } 695 } 696 } 697 698 assert(type == BUS_OP || type == DEV_OP); 699 700 if (func == NULL && state_func == NULL) { 701 return (SCFGA_ERR); 702 } 703 704 /* 705 * Fix up path for calling devctl. 706 */ 707 if ((path = strdup(physpath)) == NULL) { 708 *l_errnop = errno; 709 return (SCFGA_LIB_ERR); 710 } 711 712 /* Remove dynamic component if any */ 713 if ((cp = GET_DYN(path)) != NULL) { 714 *cp = '\0'; 715 } 716 717 /* Remove minor name */ 718 if ((cp = strrchr(path, ':')) != NULL) { 719 *cp = '\0'; 720 } 721 722 errno = 0; 723 724 if (type == BUS_OP) { 725 hdl = devctl_bus_acquire(path, 0); 726 } else { 727 hdl = devctl_device_acquire(path, 0); 728 } 729 *l_errnop = errno; 730 731 S_FREE(path); 732 733 if (hdl == NULL) { 734 return (SCFGA_ERR); 735 } 736 737 errno = 0; 738 /* Only getstate functions require a second argument */ 739 if (func != NULL && statep == NULL) { 740 rv = func(hdl); 741 *l_errnop = errno; 742 } else if (state_func != NULL && statep != NULL) { 743 rv = state_func(hdl, statep); 744 *l_errnop = errno; 745 } else { 746 rv = -1; 747 *l_errnop = 0; 748 } 749 750 devctl_release(hdl); 751 752 return ((rv == -1) ? SCFGA_ERR : SCFGA_OK); 753 } 754 755 /* 756 * Is device in a known state ? (One of BUSY, ONLINE, OFFLINE) 757 * BUSY --> One or more device special files are open. Implies online 758 * ONLINE --> driver attached 759 * OFFLINE --> CF1 with offline flag set. 760 * UNKNOWN --> None of the above 761 */ 762 int 763 known_state(di_node_t node) 764 { 765 uint_t state; 766 767 state = di_state(node); 768 769 /* 770 * CF1 without offline flag set is considered unknown state. 771 * We are in a known state if either CF2 (driver attached) or 772 * offline. 773 */ 774 if ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE || 775 (state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) { 776 return (1); 777 } 778 779 return (0); 780 } 781 782 void 783 list_free(ldata_list_t **llpp) 784 { 785 ldata_list_t *lp, *olp; 786 787 lp = *llpp; 788 while (lp != NULL) { 789 olp = lp; 790 lp = olp->next; 791 S_FREE(olp); 792 } 793 794 *llpp = NULL; 795 } 796 797 /* 798 * Obtain the devlink from a /devices path 799 */ 800 typedef struct walk_link { 801 char *path; 802 char len; 803 char **linkpp; 804 } walk_link_t; 805 806 static int 807 get_link(di_devlink_t devlink, void *arg) 808 { 809 walk_link_t *larg = (walk_link_t *)arg; 810 811 /* 812 * When path is specified, it's the node path without minor 813 * name. Therefore, the ../.. prefixes needs to be stripped. 814 */ 815 if (larg->path) { 816 char *content = (char *)di_devlink_content(devlink); 817 char *start = strstr(content, "/devices/"); 818 819 /* line content must have minor node */ 820 if (start == NULL || 821 strncmp(start, larg->path, larg->len) != 0 || 822 start[larg->len] != ':') 823 return (DI_WALK_CONTINUE); 824 } 825 826 *(larg->linkpp) = strdup(di_devlink_path(devlink)); 827 return (DI_WALK_TERMINATE); 828 } 829 830 scfga_ret_t 831 physpath_to_devlink( 832 char *node_path, 833 char **logpp, 834 int *l_errnop, 835 int match_minor) 836 { 837 walk_link_t larg; 838 di_devlink_handle_t hdl; 839 char *minor_path; 840 841 if ((hdl = di_devlink_init(NULL, 0)) == NULL) { 842 *l_errnop = errno; 843 return (SCFGA_LIB_ERR); 844 } 845 846 *logpp = NULL; 847 larg.linkpp = logpp; 848 if (match_minor) { 849 minor_path = node_path + strlen(DEVICES_DIR); 850 larg.path = NULL; 851 } else { 852 minor_path = NULL; 853 larg.len = strlen(node_path); 854 larg.path = node_path; 855 } 856 857 (void) di_devlink_walk(hdl, NULL, minor_path, DI_PRIMARY_LINK, 858 (void *)&larg, get_link); 859 860 di_devlink_fini(&hdl); 861 862 if (*logpp == NULL) 863 return (SCFGA_LIB_ERR); 864 865 return (SCFGA_OK); 866 } 867 868 int 869 hba_dev_cmp(const char *hba, const char *devpath) 870 { 871 char *cp = NULL; 872 int rv; 873 size_t hba_len, dev_len; 874 char l_hba[MAXPATHLEN], l_dev[MAXPATHLEN]; 875 876 (void) snprintf(l_hba, sizeof (l_hba), "%s", hba); 877 (void) snprintf(l_dev, sizeof (l_dev), "%s", devpath); 878 879 /* Remove dynamic component if any */ 880 if ((cp = GET_DYN(l_hba)) != NULL) { 881 *cp = '\0'; 882 } 883 884 if ((cp = GET_DYN(l_dev)) != NULL) { 885 *cp = '\0'; 886 } 887 888 889 /* Remove minor names */ 890 if ((cp = strrchr(l_hba, ':')) != NULL) { 891 *cp = '\0'; 892 } 893 894 if ((cp = strrchr(l_dev, ':')) != NULL) { 895 *cp = '\0'; 896 } 897 898 hba_len = strlen(l_hba); 899 dev_len = strlen(l_dev); 900 901 /* Check if HBA path is component of device path */ 902 if (rv = strncmp(l_hba, l_dev, hba_len)) { 903 return (rv); 904 } 905 906 /* devpath must have '/' and 1 char in addition to hba path */ 907 if (dev_len >= hba_len + 2 && l_dev[hba_len] == '/') { 908 return (0); 909 } else { 910 return (-1); 911 } 912 } 913 914 int 915 dev_cmp(const char *dev1, const char *dev2, int match_minor) 916 { 917 char l_dev1[MAXPATHLEN], l_dev2[MAXPATHLEN]; 918 char *mn1, *mn2; 919 int rv; 920 921 (void) snprintf(l_dev1, sizeof (l_dev1), "%s", dev1); 922 (void) snprintf(l_dev2, sizeof (l_dev2), "%s", dev2); 923 924 if ((mn1 = GET_DYN(l_dev1)) != NULL) { 925 *mn1 = '\0'; 926 } 927 928 if ((mn2 = GET_DYN(l_dev2)) != NULL) { 929 *mn2 = '\0'; 930 } 931 932 /* Separate out the minor names */ 933 if ((mn1 = strrchr(l_dev1, ':')) != NULL) { 934 *mn1++ = '\0'; 935 } 936 937 if ((mn2 = strrchr(l_dev2, ':')) != NULL) { 938 *mn2++ = '\0'; 939 } 940 941 if ((rv = strcmp(l_dev1, l_dev2)) != 0 || !match_minor) { 942 return (rv); 943 } 944 945 /* 946 * Compare minor names 947 */ 948 if (mn1 == NULL && mn2 == NULL) { 949 return (0); 950 } else if (mn1 == NULL) { 951 return (-1); 952 } else if (mn2 == NULL) { 953 return (1); 954 } else { 955 return (strcmp(mn1, mn2)); 956 } 957 } 958