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