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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include "cfga_scsi.h" 28 #include <libgen.h> 29 #include <limits.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_CLIENT_OFFLINE, 0, 1, "failed to offline a client device: "}, 127 {ERRARG_RCM_ONLINE, 0, 1, "failed to online: "}, 128 {ERRARG_RCM_REMOVE, 0, 1, "failed to remove: "}, 129 130 /* Commands */ 131 {CMD_INSERT_DEV, 0, 0, "insert_device"}, 132 {CMD_REMOVE_DEV, 0, 0, "remove_device"}, 133 {CMD_LED_DEV, 0, 0, "led"}, 134 {CMD_LOCATOR_DEV, 0, 0, "locator"}, 135 {CMD_REPLACE_DEV, 0, 0, "replace_device"}, 136 {CMD_RESET_DEV, 0, 0, "reset_device"}, 137 {CMD_RESET_BUS, 0, 0, "reset_bus"}, 138 {CMD_RESET_ALL, 0, 0, "reset_all"}, 139 140 /* help messages */ 141 {MSG_HELP_HDR, 0, 1, "\nSCSI specific commands and options:\n"}, 142 {MSG_HELP_USAGE, 0, 0, "\t-x insert_device ap_id [ap_id... ]\n" 143 "\t-x remove_device ap_id [ap_id... ]\n" 144 "\t-x replace_device ap_id [ap_id... ]\n" 145 "\t-x locator[=on|off] ap_id [ap_id... ]\n" 146 "\t-x led[=LED,mode=on|off|blink] " 147 "ap_id [ap_id... ]\n" 148 "\t-x reset_device ap_id [ap_id... ]\n" 149 "\t-x reset_bus ap_id [ap_id... ]\n" 150 "\t-x reset_all ap_id [ap_id... ]\n"}, 151 152 /* hotplug messages */ 153 {MSG_INSDEV, 1, 1, "Adding device to SCSI HBA: "}, 154 {MSG_RMDEV, 1, 1, "Removing SCSI device: "}, 155 {MSG_REPLDEV, 1, 1, "Replacing SCSI device: "}, 156 {MSG_WAIT_LOCK, 0, 1, "Waiting for quiesce lock... "}, 157 158 /* Hotplugging confirmation prompts */ 159 {CONF_QUIESCE_1, 1, 1, 160 "This operation will suspend activity on SCSI bus: "}, 161 162 {CONF_QUIESCE_2, 0, 1, "\nContinue"}, 163 164 {CONF_UNQUIESCE, 0, 1, 165 "SCSI bus quiesced successfully.\n" 166 "It is now safe to proceed with hotplug operation." 167 "\nEnter y if operation is complete or n to abort"}, 168 169 {CONF_NO_QUIESCE, 0, 1, 170 "Proceed with hotplug operation." 171 "\nEnter y if operation is complete or n to abort"}, 172 173 /* Misc. */ 174 {WARN_DISCONNECT, 0, 1, 175 "WARNING: Disconnecting critical partitions may cause system hang." 176 "\nContinue"}, 177 178 /* LED messages */ 179 {MSG_LED_HDR, 0, 1, "Disk Led"}, 180 {MSG_MISSING_LED_NAME, 0, 1, "Missing LED name"}, 181 {MSG_MISSING_LED_MODE, 0, 1, "Missing LED mode"} 182 }; 183 184 char * 185 led_strs[] = { 186 "fault", 187 "power", 188 "attn", 189 "active", 190 "locator", 191 NULL 192 }; 193 194 char * 195 led_mode_strs[] = { 196 "off", 197 "on", 198 "blink", 199 "faulted", 200 "unknown", 201 NULL 202 }; 203 204 205 206 207 #define N_STRS (sizeof (str_tbl) / sizeof (str_tbl[0])) 208 209 #define GET_MSG_NARGS(i) (str_tbl[msg_idx(i)].nargs) 210 #define GET_MSG_INTL(i) (str_tbl[msg_idx(i)].intl) 211 212 static errcvt_t err_cvt_tbl[] = { 213 { SCFGA_OK, CFGA_OK }, 214 { SCFGA_LIB_ERR, CFGA_LIB_ERROR }, 215 { SCFGA_APID_NOEXIST, CFGA_APID_NOEXIST }, 216 { SCFGA_NACK, CFGA_NACK }, 217 { SCFGA_BUSY, CFGA_BUSY }, 218 { SCFGA_SYSTEM_BUSY, CFGA_SYSTEM_BUSY }, 219 { SCFGA_OPNOTSUPP, CFGA_OPNOTSUPP }, 220 { SCFGA_PRIV, CFGA_PRIV }, 221 { SCFGA_UNKNOWN_ERR, CFGA_ERROR }, 222 { SCFGA_ERR, CFGA_ERROR } 223 }; 224 225 #define N_ERR_CVT_TBL (sizeof (err_cvt_tbl)/sizeof (err_cvt_tbl[0])) 226 227 #define DEV_OP 0 228 #define BUS_OP 1 229 static set_state_cmd_t set_state_cmds[] = { 230 231 { SCFGA_BUS_QUIESCE, BUS_OP, devctl_bus_quiesce }, 232 { SCFGA_BUS_UNQUIESCE, BUS_OP, devctl_bus_unquiesce }, 233 { SCFGA_BUS_CONFIGURE, BUS_OP, devctl_bus_configure }, 234 { SCFGA_BUS_UNCONFIGURE, BUS_OP, devctl_bus_unconfigure }, 235 { SCFGA_RESET_BUS, BUS_OP, devctl_bus_reset }, 236 { SCFGA_RESET_ALL, BUS_OP, devctl_bus_resetall }, 237 { SCFGA_DEV_CONFIGURE, DEV_OP, devctl_device_online }, 238 { SCFGA_DEV_UNCONFIGURE, DEV_OP, devctl_device_offline }, 239 { SCFGA_DEV_REMOVE, DEV_OP, devctl_device_remove }, 240 { SCFGA_RESET_DEV, DEV_OP, devctl_device_reset } 241 242 }; 243 244 #define N_SET_STATE_CMDS (sizeof (set_state_cmds)/sizeof (set_state_cmds[0])) 245 246 static get_state_cmd_t get_state_cmds[] = { 247 { SCFGA_BUS_GETSTATE, BUS_OP, devctl_bus_getstate }, 248 { SCFGA_DEV_GETSTATE, DEV_OP, devctl_device_getstate } 249 }; 250 251 #define N_GET_STATE_CMDS (sizeof (get_state_cmds)/sizeof (get_state_cmds[0])) 252 253 /* 254 * SCSI hardware specific commands 255 */ 256 static hw_cmd_t hw_cmds[] = { 257 /* Command string Command ID Function */ 258 259 { CMD_INSERT_DEV, SCFGA_INSERT_DEV, dev_insert }, 260 { CMD_REMOVE_DEV, SCFGA_REMOVE_DEV, dev_remove }, 261 { CMD_REPLACE_DEV, SCFGA_REPLACE_DEV, dev_replace }, 262 { CMD_LED_DEV, SCFGA_LED_DEV, dev_led }, 263 { CMD_LOCATOR_DEV, SCFGA_LOCATOR_DEV, dev_led }, 264 { CMD_RESET_DEV, SCFGA_RESET_DEV, reset_common }, 265 { CMD_RESET_BUS, SCFGA_RESET_BUS, reset_common }, 266 { CMD_RESET_ALL, SCFGA_RESET_ALL, reset_common }, 267 }; 268 #define N_HW_CMDS (sizeof (hw_cmds) / sizeof (hw_cmds[0])) 269 270 271 cfga_err_t 272 err_cvt(scfga_ret_t s_err) 273 { 274 int i; 275 276 for (i = 0; i < N_ERR_CVT_TBL; i++) { 277 if (err_cvt_tbl[i].scsi_err == s_err) { 278 return (err_cvt_tbl[i].cfga_err); 279 } 280 } 281 282 return (CFGA_ERROR); 283 } 284 285 /* 286 * Removes duplicate slashes from a pathname and any trailing slashes. 287 * Returns "/" if input is "/" 288 */ 289 static char * 290 pathdup(const char *path, int *l_errnop) 291 { 292 int prev_was_slash = 0; 293 char c, *dp = NULL, *dup = NULL; 294 const char *sp = NULL; 295 296 *l_errnop = 0; 297 298 if (path == NULL) { 299 return (NULL); 300 } 301 302 if ((dup = calloc(1, strlen(path) + 1)) == NULL) { 303 *l_errnop = errno; 304 return (NULL); 305 } 306 307 prev_was_slash = 0; 308 for (sp = path, dp = dup; (c = *sp) != '\0'; sp++) { 309 if (!prev_was_slash || c != '/') { 310 *dp++ = c; 311 } 312 if (c == '/') { 313 prev_was_slash = 1; 314 } else { 315 prev_was_slash = 0; 316 } 317 } 318 319 /* Remove trailing slash except if it is the first char */ 320 if (prev_was_slash && dp != dup && dp - 1 != dup) { 321 *(--dp) = '\0'; 322 } else { 323 *dp = '\0'; 324 } 325 326 return (dup); 327 } 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 } else { 359 apidp->dyntype = NODYNCOMP; 360 } 361 362 /* get dyn comp type */ 363 if (dyncomp != NULL) { 364 if (strstr(dyncomp, PATH_APID_DYN_SEP) != NULL) { 365 apidp->dyntype = PATH_APID; 366 } else { 367 apidp->dyntype = DEV_APID; 368 } 369 } 370 371 /* Create the path */ 372 if ((ret = apid_to_path(hba_phys, dyncomp, &path, 373 &l_errno)) != SCFGA_OK) { 374 cfga_err(errstring, l_errno, ERR_OP_FAILED, 0); 375 goto err; 376 } 377 378 assert(path != NULL); 379 assert(hba_phys != NULL); 380 381 apidp->hba_phys = hba_phys; 382 apidp->dyncomp = dyncomp; 383 apidp->path = path; 384 apidp->flags = 0; 385 386 return (SCFGA_OK); 387 388 err: 389 S_FREE(hba_phys); 390 S_FREE(dyncomp); 391 S_FREE(path); 392 return (ret); 393 } 394 395 void 396 apidt_free(apid_t *apidp) 397 { 398 if (apidp == NULL) 399 return; 400 401 S_FREE(apidp->hba_phys); 402 S_FREE(apidp->dyncomp); 403 S_FREE(apidp->path); 404 } 405 406 scfga_ret_t 407 walk_tree( 408 const char *physpath, 409 void *arg, 410 uint_t init_flags, 411 walkarg_t *up, 412 scfga_cmd_t cmd, 413 int *l_errnop) 414 { 415 int rv; 416 di_node_t root, walk_root; 417 char *root_path, *cp = NULL, *init_path; 418 size_t len; 419 scfga_ret_t ret; 420 421 *l_errnop = 0; 422 423 if ((root_path = strdup(physpath)) == NULL) { 424 *l_errnop = errno; 425 return (SCFGA_LIB_ERR); 426 } 427 428 /* Fix up path for di_init() */ 429 len = strlen(DEVICES_DIR); 430 if (strncmp(root_path, DEVICES_DIR SLASH, 431 len + strlen(SLASH)) == 0) { 432 cp = root_path + len; 433 (void) memmove(root_path, cp, strlen(cp) + 1); 434 } else if (*root_path != '/') { 435 *l_errnop = 0; 436 ret = SCFGA_ERR; 437 goto out; 438 } 439 440 /* Remove dynamic component if any */ 441 if ((cp = GET_DYN(root_path)) != NULL) { 442 *cp = '\0'; 443 } 444 445 /* Remove minor name if any */ 446 if ((cp = strrchr(root_path, ':')) != NULL) { 447 *cp = '\0'; 448 } 449 450 /* 451 * Cached snapshots are always rooted at "/" 452 */ 453 init_path = root_path; 454 if ((init_flags & DINFOCACHE) == DINFOCACHE) { 455 init_path = "/"; 456 } 457 458 /* Get a snapshot */ 459 if ((root = di_init(init_path, init_flags)) == DI_NODE_NIL) { 460 *l_errnop = errno; 461 ret = SCFGA_LIB_ERR; 462 goto out; 463 } 464 465 /* 466 * Lookup the subtree of interest 467 */ 468 walk_root = root; 469 if ((init_flags & DINFOCACHE) == DINFOCACHE) { 470 walk_root = di_lookup_node(root, root_path); 471 } 472 473 if (walk_root == DI_NODE_NIL) { 474 *l_errnop = errno; 475 di_fini(root); 476 ret = SCFGA_LIB_ERR; 477 goto out; 478 } 479 480 /* Walk the tree */ 481 errno = 0; 482 if (cmd == SCFGA_WALK_NODE) { 483 rv = di_walk_node(walk_root, up->node_args.flags, arg, 484 up->node_args.fcn); 485 } else if (cmd == SCFGA_WALK_PATH) { 486 rv = stat_path_info(walk_root, arg, l_errnop); 487 } else { 488 assert(cmd == SCFGA_WALK_MINOR); 489 rv = di_walk_minor(walk_root, up->minor_args.nodetype, 0, arg, 490 up->minor_args.fcn); 491 } 492 493 if (rv != 0) { 494 *l_errnop = errno; 495 ret = SCFGA_LIB_ERR; 496 } else { 497 *l_errnop = 0; 498 ret = SCFGA_OK; 499 } 500 501 di_fini(root); 502 503 /*FALLTHRU*/ 504 out: 505 S_FREE(root_path); 506 return (ret); 507 } 508 509 scfga_ret_t 510 invoke_cmd( 511 const char *func, 512 apid_t *apidtp, 513 prompt_t *prp, 514 cfga_flags_t flags, 515 char **errstring) 516 { 517 int i; 518 int len; 519 520 521 /* 522 * Determine if the func has an equal sign; only compare up to 523 * the equals 524 */ 525 for (len = 0; func[len] != 0 && func[len] != '='; len++) { 526 }; 527 528 for (i = 0; i < N_HW_CMDS; i++) { 529 const char *s = GET_MSG_STR(hw_cmds[i].str_id); 530 if (strncmp(func, s, len) == 0 && s[len] == 0) { 531 return (hw_cmds[i].fcn(func, hw_cmds[i].cmd, apidtp, 532 prp, flags, errstring)); 533 } 534 } 535 536 cfga_err(errstring, 0, ERRARG_HWCMD_INVAL, func, 0); 537 return (SCFGA_ERR); 538 } 539 540 int 541 msg_idx(msgid_t msgid) 542 { 543 int idx = 0; 544 545 /* The string table index and the error id may or may not be same */ 546 if (msgid >= 0 && msgid <= N_STRS - 1 && 547 str_tbl[msgid].msgid == msgid) { 548 idx = msgid; 549 } else { 550 for (idx = 0; idx < N_STRS; idx++) { 551 if (str_tbl[idx].msgid == msgid) 552 break; 553 } 554 if (idx >= N_STRS) { 555 idx = UNKNOWN_ERR_IDX; 556 } 557 } 558 559 return (idx); 560 } 561 562 /* 563 * cfga_err() accepts a variable number of message IDs and constructs 564 * a corresponding error string which is returned via the errstring argument. 565 * cfga_err() calls dgettext() to internationalize proper messages. 566 * May be called with a NULL argument. 567 */ 568 void 569 cfga_err(char **errstring, int l_errno, ...) 570 { 571 va_list ap; 572 int append_newline = 0; 573 574 if (errstring == NULL || *errstring != NULL) { 575 return; 576 } 577 578 /* 579 * Don't append a newline, the application (for example cfgadm) 580 * should do that. 581 */ 582 append_newline = 0; 583 584 va_start(ap, l_errno); 585 msg_common(errstring, append_newline, l_errno, ap); 586 va_end(ap); 587 } 588 589 /* 590 * This routine accepts a variable number of message IDs and constructs 591 * a corresponding message string which is printed via the message print 592 * routine argument. 593 */ 594 void 595 cfga_msg(struct cfga_msg *msgp, ...) 596 { 597 char *p = NULL; 598 int append_newline = 0, l_errno = 0; 599 va_list ap; 600 601 if (msgp == NULL || msgp->message_routine == NULL) { 602 return; 603 } 604 605 /* Append a newline after message */ 606 append_newline = 1; 607 l_errno = 0; 608 609 va_start(ap, msgp); 610 msg_common(&p, append_newline, l_errno, ap); 611 va_end(ap); 612 613 (void) (*msgp->message_routine)(msgp->appdata_ptr, p); 614 615 S_FREE(p); 616 } 617 618 619 /* 620 * This routine prints the value of an led for a disk. 621 */ 622 void 623 cfga_led_msg(struct cfga_msg *msgp, apid_t *apidp, led_strid_t led, 624 led_modeid_t mode) 625 { 626 char led_msg[MAX_INPUT]; /* 512 bytes */ 627 628 if ((msgp == NULL) || (msgp->message_routine == NULL)) { 629 return; 630 } 631 if ((apidp == NULL) || (apidp->dyncomp == NULL)) { 632 return; 633 } 634 (void) snprintf(led_msg, sizeof (led_msg), "%-23s\t%s=%s\n", 635 basename(apidp->dyncomp), 636 dgettext(TEXT_DOMAIN, led_strs[led]), 637 dgettext(TEXT_DOMAIN, led_mode_strs[mode])); 638 (void) (*msgp->message_routine)(msgp->appdata_ptr, led_msg); 639 } 640 641 /* 642 * Get internationalized string corresponding to message id 643 * Caller must free the memory allocated. 644 */ 645 char * 646 cfga_str(int append_newline, ...) 647 { 648 char *p = NULL; 649 int l_errno = 0; 650 va_list ap; 651 652 va_start(ap, append_newline); 653 msg_common(&p, append_newline, l_errno, ap); 654 va_end(ap); 655 656 return (p); 657 } 658 659 static void 660 msg_common(char **msgpp, int append_newline, int l_errno, va_list ap) 661 { 662 int a = 0; 663 size_t len = 0; 664 int i = 0, n = 0; 665 char *s = NULL, *t = NULL; 666 strlist_t dummy; 667 strlist_t *savep = NULL, *sp = NULL, *tailp = NULL; 668 669 if (*msgpp != NULL) { 670 return; 671 } 672 673 dummy.next = NULL; 674 tailp = &dummy; 675 for (len = 0; (a = va_arg(ap, int)) != 0; ) { 676 n = GET_MSG_NARGS(a); /* 0 implies no additional args */ 677 for (i = 0; i <= n; i++) { 678 sp = calloc(1, sizeof (*sp)); 679 if (sp == NULL) { 680 goto out; 681 } 682 if (i == 0 && GET_MSG_INTL(a)) { 683 sp->str = dgettext(TEXT_DOMAIN, GET_MSG_STR(a)); 684 } else if (i == 0) { 685 sp->str = GET_MSG_STR(a); 686 } else { 687 sp->str = va_arg(ap, char *); 688 } 689 len += (strlen(sp->str)); 690 sp->next = NULL; 691 tailp->next = sp; 692 tailp = sp; 693 } 694 } 695 696 len += 1; /* terminating NULL */ 697 698 s = t = NULL; 699 if (l_errno) { 700 s = dgettext(TEXT_DOMAIN, ": "); 701 t = S_STR(strerror(l_errno)); 702 if (s != NULL && t != NULL) { 703 len += strlen(s) + strlen(t); 704 } 705 } 706 707 if (append_newline) { 708 len++; 709 } 710 711 if ((*msgpp = calloc(1, len)) == NULL) { 712 goto out; 713 } 714 715 **msgpp = '\0'; 716 for (sp = dummy.next; sp != NULL; sp = sp->next) { 717 (void) strcat(*msgpp, sp->str); 718 } 719 720 if (s != NULL && t != NULL) { 721 (void) strcat(*msgpp, s); 722 (void) strcat(*msgpp, t); 723 } 724 725 if (append_newline) { 726 (void) strcat(*msgpp, dgettext(TEXT_DOMAIN, "\n")); 727 } 728 729 /* FALLTHROUGH */ 730 out: 731 sp = dummy.next; 732 while (sp != NULL) { 733 savep = sp->next; 734 S_FREE(sp); 735 sp = savep; 736 } 737 } 738 739 /* 740 * Check to see if the given pi_node is the last path to the client device. 741 * 742 * Return: 743 * 0: if there is another path avialable. 744 * -1: if no other paths available. 745 */ 746 static int 747 check_available_path( 748 di_node_t client_node, 749 di_path_t pi_node) 750 { 751 di_path_state_t pi_state; 752 di_path_t next_pi = DI_PATH_NIL; 753 754 if (((pi_state = di_path_state(pi_node)) != DI_PATH_STATE_ONLINE) && 755 (pi_state != DI_PATH_STATE_STANDBY)) { 756 /* it is not last available path */ 757 return (0); 758 } 759 760 while (next_pi = di_path_client_next_path(client_node, next_pi)) { 761 /* if anohter pi node is avaialble, return 0 */ 762 if ((next_pi != pi_node) && 763 (((pi_state = di_path_state(next_pi)) == 764 DI_PATH_STATE_ONLINE) || 765 pi_state == DI_PATH_STATE_STANDBY)) { 766 return (0); 767 } 768 } 769 return (-1); 770 } 771 772 scfga_ret_t 773 path_apid_state_change( 774 apid_t *apidp, 775 scfga_cmd_t cmd, 776 cfga_flags_t flags, 777 char **errstring, 778 int *l_errnop, 779 msgid_t errid) 780 { 781 di_node_t root, walk_root, client_node; 782 di_path_t pi_node = DI_PATH_NIL; 783 char *root_path, *cp, *client_path, devpath[MAXPATHLEN]; 784 int len, found = 0; 785 scfga_ret_t ret; 786 char *dev_list[2] = {NULL}; 787 788 *l_errnop = 0; 789 790 /* Make sure apid is pathinfo associated apid. */ 791 if ((apidp->dyntype != PATH_APID) || (apidp->dyncomp == NULL)) { 792 return (SCFGA_LIB_ERR); 793 } 794 795 if ((cmd != SCFGA_DEV_CONFIGURE) && (cmd != SCFGA_DEV_UNCONFIGURE)) { 796 return (SCFGA_LIB_ERR); 797 } 798 799 if ((root_path = strdup(apidp->hba_phys)) == NULL) { 800 *l_errnop = errno; 801 return (SCFGA_LIB_ERR); 802 } 803 804 /* Fix up path for di_init() */ 805 len = strlen(DEVICES_DIR); 806 if (strncmp(root_path, DEVICES_DIR SLASH, 807 len + strlen(SLASH)) == 0) { 808 cp = root_path + len; 809 (void) memmove(root_path, cp, strlen(cp) + 1); 810 } else if (*root_path != '/') { 811 *l_errnop = 0; 812 S_FREE(root_path); 813 return (SCFGA_ERR); 814 } 815 816 /* Remove dynamic component if any */ 817 if ((cp = GET_DYN(root_path)) != NULL) { 818 *cp = '\0'; 819 } 820 821 /* Remove minor name if any */ 822 if ((cp = strrchr(root_path, ':')) != NULL) { 823 *cp = '\0'; 824 } 825 826 /* 827 * Cached snapshots are always rooted at "/" 828 */ 829 830 /* Get a snapshot */ 831 if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) { 832 *l_errnop = errno; 833 S_FREE(root_path); 834 return (SCFGA_ERR); 835 } 836 837 /* 838 * Lookup the subtree of interest 839 */ 840 walk_root = di_lookup_node(root, root_path); 841 842 if (walk_root == DI_NODE_NIL) { 843 *l_errnop = errno; 844 di_fini(root); 845 S_FREE(root_path); 846 return (SCFGA_LIB_ERR); 847 } 848 849 850 if ((pi_node = di_path_next_client(walk_root, pi_node)) == 851 DI_PATH_NIL) { 852 /* the path apid not found */ 853 di_fini(root); 854 S_FREE(root_path); 855 return (SCFGA_APID_NOEXIST); 856 } 857 858 do { 859 /* check the length first. */ 860 if (strlen(di_path_bus_addr(pi_node)) != 861 strlen(apidp->dyncomp)) { 862 continue; 863 } 864 865 /* compare bus addr. */ 866 if (strcmp(di_path_bus_addr(pi_node), apidp->dyncomp) == 0) { 867 found = 1; 868 break; 869 } 870 pi_node = di_path_next_client(root, pi_node); 871 } while (pi_node != DI_PATH_NIL); 872 873 if (!found) { 874 di_fini(root); 875 S_FREE(root_path); 876 return (SCFGA_APID_NOEXIST); 877 } 878 879 /* Get client node path. */ 880 client_node = di_path_client_node(pi_node); 881 if (client_node == DI_NODE_NIL) { 882 di_fini(root); 883 S_FREE(root_path); 884 return (SCFGA_ERR); 885 } else { 886 client_path = di_devfs_path(client_node); 887 if (client_path == NULL) { 888 di_fini(root); 889 S_FREE(root_path); 890 return (SCFGA_ERR); 891 } 892 893 if ((apidp->flags & FLAG_DISABLE_RCM) == 0) { 894 if (cmd == SCFGA_DEV_UNCONFIGURE) { 895 if (check_available_path(client_node, 896 pi_node) != 0) { 897 /* 898 * last path. check if unconfiguring 899 * is okay. 900 */ 901 (void) snprintf(devpath, 902 strlen(DEVICES_DIR) + 903 strlen(client_path) + 1, "%s%s", 904 DEVICES_DIR, client_path); 905 dev_list[0] = devpath; 906 flags |= FLAG_CLIENT_DEV; 907 ret = scsi_rcm_offline(dev_list, 908 errstring, flags); 909 if (ret != SCFGA_OK) { 910 di_fini(root); 911 di_devfs_path_free(client_path); 912 S_FREE(root_path); 913 return (ret); 914 } 915 } 916 } 917 } 918 } 919 920 ret = devctl_cmd(apidp->path, cmd, NULL, l_errnop); 921 if (ret != SCFGA_OK) { 922 cfga_err(errstring, *l_errnop, errid, 0); 923 924 /* 925 * If an unconfigure fails, cancel the RCM offline. 926 * Discard any RCM failures so that the devctl 927 * failure will still be reported. 928 */ 929 if ((apidp->flags & FLAG_DISABLE_RCM) == 0) { 930 if (cmd == SCFGA_DEV_UNCONFIGURE) 931 (void) scsi_rcm_online(dev_list, 932 errstring, flags); 933 } 934 } 935 936 di_devfs_path_free(client_path); 937 di_fini(root); 938 S_FREE(root_path); 939 940 return (ret); 941 } 942 943 944 scfga_ret_t 945 devctl_cmd( 946 const char *physpath, 947 scfga_cmd_t cmd, 948 uint_t *statep, 949 int *l_errnop) 950 { 951 int rv = -1, i, type; 952 devctl_hdl_t hdl = NULL; 953 char *cp = NULL, *path = NULL; 954 int (*func)(const devctl_hdl_t); 955 int (*state_func)(const devctl_hdl_t, uint_t *); 956 957 *l_errnop = 0; 958 959 if (statep != NULL) *statep = 0; 960 961 func = NULL; 962 state_func = NULL; 963 type = 0; 964 965 for (i = 0; i < N_GET_STATE_CMDS; i++) { 966 if (get_state_cmds[i].cmd == cmd) { 967 state_func = get_state_cmds[i].state_fcn; 968 type = get_state_cmds[i].type; 969 assert(statep != NULL); 970 break; 971 } 972 } 973 974 if (state_func == NULL) { 975 for (i = 0; i < N_SET_STATE_CMDS; i++) { 976 if (set_state_cmds[i].cmd == cmd) { 977 func = set_state_cmds[i].fcn; 978 type = set_state_cmds[i].type; 979 assert(statep == NULL); 980 break; 981 } 982 } 983 } 984 985 assert(type == BUS_OP || type == DEV_OP); 986 987 if (func == NULL && state_func == NULL) { 988 return (SCFGA_ERR); 989 } 990 991 /* 992 * Fix up path for calling devctl. 993 */ 994 if ((path = strdup(physpath)) == NULL) { 995 *l_errnop = errno; 996 return (SCFGA_LIB_ERR); 997 } 998 999 /* Remove dynamic component if any */ 1000 if ((cp = GET_DYN(path)) != NULL) { 1001 *cp = '\0'; 1002 } 1003 1004 /* Remove minor name */ 1005 if ((cp = strrchr(path, ':')) != NULL) { 1006 *cp = '\0'; 1007 } 1008 1009 errno = 0; 1010 1011 if (type == BUS_OP) { 1012 hdl = devctl_bus_acquire(path, 0); 1013 } else { 1014 hdl = devctl_device_acquire(path, 0); 1015 } 1016 *l_errnop = errno; 1017 1018 S_FREE(path); 1019 1020 if (hdl == NULL) { 1021 return (SCFGA_ERR); 1022 } 1023 1024 errno = 0; 1025 /* Only getstate functions require a second argument */ 1026 if (func != NULL && statep == NULL) { 1027 rv = func(hdl); 1028 *l_errnop = errno; 1029 } else if (state_func != NULL && statep != NULL) { 1030 rv = state_func(hdl, statep); 1031 *l_errnop = errno; 1032 } else { 1033 rv = -1; 1034 *l_errnop = 0; 1035 } 1036 1037 devctl_release(hdl); 1038 1039 return ((rv == -1) ? SCFGA_ERR : SCFGA_OK); 1040 } 1041 1042 /* 1043 * Is device in a known state ? (One of BUSY, ONLINE, OFFLINE) 1044 * BUSY --> One or more device special files are open. Implies online 1045 * ONLINE --> driver attached 1046 * OFFLINE --> CF1 with offline flag set. 1047 * UNKNOWN --> None of the above 1048 */ 1049 int 1050 known_state(di_node_t node) 1051 { 1052 uint_t state; 1053 1054 state = di_state(node); 1055 1056 /* 1057 * CF1 without offline flag set is considered unknown state. 1058 * We are in a known state if either CF2 (driver attached) or 1059 * offline. 1060 */ 1061 if ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE || 1062 (state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) { 1063 return (1); 1064 } 1065 1066 return (0); 1067 } 1068 1069 void 1070 list_free(ldata_list_t **llpp) 1071 { 1072 ldata_list_t *lp, *olp; 1073 1074 lp = *llpp; 1075 while (lp != NULL) { 1076 olp = lp; 1077 lp = olp->next; 1078 S_FREE(olp); 1079 } 1080 1081 *llpp = NULL; 1082 } 1083 1084 /* 1085 * Obtain the devlink from a /devices path 1086 */ 1087 typedef struct walk_link { 1088 char *path; 1089 char len; 1090 char **linkpp; 1091 } walk_link_t; 1092 1093 static int 1094 get_link(di_devlink_t devlink, void *arg) 1095 { 1096 walk_link_t *larg = (walk_link_t *)arg; 1097 1098 /* 1099 * When path is specified, it's the node path without minor 1100 * name. Therefore, the ../.. prefixes needs to be stripped. 1101 */ 1102 if (larg->path) { 1103 char *content = (char *)di_devlink_content(devlink); 1104 char *start = strstr(content, "/devices/"); 1105 1106 /* line content must have minor node */ 1107 if (start == NULL || 1108 strncmp(start, larg->path, larg->len) != 0 || 1109 start[larg->len] != ':') 1110 return (DI_WALK_CONTINUE); 1111 } 1112 1113 *(larg->linkpp) = strdup(di_devlink_path(devlink)); 1114 return (DI_WALK_TERMINATE); 1115 } 1116 1117 scfga_ret_t 1118 physpath_to_devlink( 1119 char *node_path, 1120 char **logpp, 1121 int *l_errnop, 1122 int match_minor) 1123 { 1124 walk_link_t larg; 1125 di_devlink_handle_t hdl; 1126 char *minor_path; 1127 1128 if ((hdl = di_devlink_init(NULL, 0)) == NULL) { 1129 *l_errnop = errno; 1130 return (SCFGA_LIB_ERR); 1131 } 1132 1133 *logpp = NULL; 1134 larg.linkpp = logpp; 1135 if (match_minor) { 1136 minor_path = node_path + strlen(DEVICES_DIR); 1137 larg.path = NULL; 1138 } else { 1139 minor_path = NULL; 1140 larg.len = strlen(node_path); 1141 larg.path = node_path; 1142 } 1143 1144 (void) di_devlink_walk(hdl, NULL, minor_path, DI_PRIMARY_LINK, 1145 (void *)&larg, get_link); 1146 1147 (void) di_devlink_fini(&hdl); 1148 1149 if (*logpp == NULL) 1150 return (SCFGA_LIB_ERR); 1151 1152 return (SCFGA_OK); 1153 } 1154 1155 int 1156 hba_dev_cmp(const char *hba, const char *devpath) 1157 { 1158 char *cp = NULL; 1159 int rv; 1160 size_t hba_len, dev_len; 1161 char l_hba[MAXPATHLEN], l_dev[MAXPATHLEN]; 1162 1163 (void) snprintf(l_hba, sizeof (l_hba), "%s", hba); 1164 (void) snprintf(l_dev, sizeof (l_dev), "%s", devpath); 1165 1166 /* Remove dynamic component if any */ 1167 if ((cp = GET_DYN(l_hba)) != NULL) { 1168 *cp = '\0'; 1169 } 1170 1171 if ((cp = GET_DYN(l_dev)) != NULL) { 1172 *cp = '\0'; 1173 } 1174 1175 1176 /* Remove minor names */ 1177 if ((cp = strrchr(l_hba, ':')) != NULL) { 1178 *cp = '\0'; 1179 } 1180 1181 if ((cp = strrchr(l_dev, ':')) != NULL) { 1182 *cp = '\0'; 1183 } 1184 1185 hba_len = strlen(l_hba); 1186 dev_len = strlen(l_dev); 1187 1188 /* Check if HBA path is component of device path */ 1189 if (rv = strncmp(l_hba, l_dev, hba_len)) { 1190 return (rv); 1191 } 1192 1193 /* devpath must have '/' and 1 char in addition to hba path */ 1194 if (dev_len >= hba_len + 2 && l_dev[hba_len] == '/') { 1195 return (0); 1196 } else { 1197 return (-1); 1198 } 1199 } 1200 1201 int 1202 dev_cmp(const char *dev1, const char *dev2, int match_minor) 1203 { 1204 char l_dev1[MAXPATHLEN], l_dev2[MAXPATHLEN]; 1205 char *mn1, *mn2; 1206 int rv; 1207 1208 (void) snprintf(l_dev1, sizeof (l_dev1), "%s", dev1); 1209 (void) snprintf(l_dev2, sizeof (l_dev2), "%s", dev2); 1210 1211 if ((mn1 = GET_DYN(l_dev1)) != NULL) { 1212 *mn1 = '\0'; 1213 } 1214 1215 if ((mn2 = GET_DYN(l_dev2)) != NULL) { 1216 *mn2 = '\0'; 1217 } 1218 1219 /* Separate out the minor names */ 1220 if ((mn1 = strrchr(l_dev1, ':')) != NULL) { 1221 *mn1++ = '\0'; 1222 } 1223 1224 if ((mn2 = strrchr(l_dev2, ':')) != NULL) { 1225 *mn2++ = '\0'; 1226 } 1227 1228 if ((rv = strcmp(l_dev1, l_dev2)) != 0 || !match_minor) { 1229 return (rv); 1230 } 1231 1232 /* 1233 * Compare minor names 1234 */ 1235 if (mn1 == NULL && mn2 == NULL) { 1236 return (0); 1237 } else if (mn1 == NULL) { 1238 return (-1); 1239 } else if (mn2 == NULL) { 1240 return (1); 1241 } else { 1242 return (strcmp(mn1, mn2)); 1243 } 1244 } 1245