1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 28 /*LINTLIBRARY*/ 29 30 31 /* 32 * Hotplug program for SENA, RSM and SSA 33 * subsystems and individual FC_AL devices. 34 */ 35 36 /* #define _POSIX_SOURCE 1 */ 37 38 /* 39 * I18N message number ranges 40 * This file: 5500 - 5999 41 * Shared common messages: 1 - 1999 42 */ 43 44 45 /* Includes */ 46 #include <stdlib.h> 47 #include <stdio.h> 48 #include <sys/file.h> 49 #include <sys/errno.h> 50 #include <sys/types.h> 51 #include <sys/stat.h> 52 #include <sys/utsname.h> 53 #include <fcntl.h> 54 #include <unistd.h> 55 #include <errno.h> 56 #include <string.h> 57 #include <sys/sunddi.h> 58 #include <sys/ddi.h> /* for min */ 59 #include <sys/scsi/scsi.h> 60 #include <nl_types.h> 61 #include <dirent.h> 62 #include <sys/wait.h> 63 #include <l_common.h> 64 #include <l_error.h> 65 #include <stgcom.h> 66 #include <a_state.h> 67 #include <a5k.h> 68 #include <rom.h> 69 #include "hot.h" 70 #include "common.h" 71 #include "luxadm.h" 72 73 74 /* Internal variables. */ 75 static char *cmdStrg[][4] = { 76 { "disks", "-C", 0, 0 }, 77 { "disks", 0, 0, 0 }, 78 { "drvconfig", "-i", "ssd", 0 }, 79 { "drvconfig", 0, 0, 0 }, 80 { "devlinks", 0, 0, 0 }, 81 { "tapes", "-C", 0, 0 } 82 }; 83 84 /* External variables */ 85 extern char *dtype[]; /* From adm.c */ 86 extern int Options; 87 extern const int OPTION_CAPF; 88 89 /* Internal functions */ 90 /* SENA and Individual FC device Hotplug */ 91 static int h_pre_insert_encl_dev(timestruc_t *, timestruc_t *, 92 timestruc_t *); 93 static int h_post_insert_dev(timestruc_t, timestruc_t); 94 static int h_pre_remove_dev(Hotplug_Devlist *, 95 WWN_list *wwn_list, int, int); 96 static int h_post_remove_dev(Hotplug_Devlist *, int, int); 97 static int h_pre_hotplug(Hotplug_Devlist **, 98 WWN_list *, int, int, int); 99 static int h_post_hotplug(Hotplug_Devlist *, 100 WWN_list *, int, int, int, int); 101 static int h_post_insert_encl(timestruc_t); 102 static int h_pre_hotplug_sena(Hotplug_Devlist *, 103 WWN_list *, int, int, int); 104 static int h_post_hotplug_sena(Hotplug_Devlist *, 105 WWN_list *, int, int, int, int); 106 static int h_remove_ses_nodes(struct dlist *); 107 static int h_print_list_warn(Hotplug_Devlist *, int, int); 108 static int h_display_logical_nodes(struct dlist *); 109 static void h_print_logical_nodes(struct dlist *); 110 static int h_remove_nodes(struct dlist *); 111 static int h_print_list(Hotplug_Devlist *, int *, int); 112 static int h_get_fcdev_state(char *, char *, int, int *, int *, int); 113 static int h_chk_dev_busy(Hotplug_Devlist *, 114 WWN_list *, int *, int, int); 115 static int h_execCmnd(char **, int); 116 int hotplug(int, char **, int, int); 117 int h_insertSena_fcdev(); 118 static int h_find_new_device_link(char *, timestruc_t); 119 120 121 122 /* 123 * Assists the user in hot inserting FC_AL 124 * individual device(s) and SENA enclosure(s). 125 * 126 * RETURNS: 127 * 0 if OK 128 * non-zero otherwise 129 */ 130 int 131 h_insertSena_fcdev() 132 { 133 timestruc_t ses_time, dsk_time, rmt_time; 134 int err; 135 struct stat ses_stat; 136 137 if ((err = h_pre_insert_encl_dev(&ses_time, &dsk_time, 138 &rmt_time)) != 0) { 139 return (err); 140 } 141 (void) fprintf(stdout, MSGSTR(5500, 142 "Please hit <RETURN> when you have finished" 143 " adding Fibre Channel Enclosure(s)/Device(s): ")); 144 (void) getchar(); 145 146 if ((err = h_post_insert_dev(dsk_time, rmt_time)) != 0) { 147 return (err); 148 } 149 150 if (stat(SES_DIR, &ses_stat) < 0) { 151 /* 152 * Non existence of /dev/es dir indicates 153 * no ses devices inserted. 154 * No need to call h_post_insert_encl(). 155 */ 156 if (errno == ENOENT) { 157 (void) fprintf(stdout, MSGSTR(5662, 158 " No new enclosure(s) were added!!\n\n")); 159 return (0); 160 } else { 161 return (L_LSTAT_ES_DIR_ERROR); 162 } 163 } 164 165 /* 166 * if the latest mod time of /dev/es is not newer than 167 * the original mod time no need to call 168 * h_post_insert_encl(). 169 */ 170 if ((&ses_time != (timestruc_t *)NULL) && 171 !(NEWER(ses_stat.st_ctim, ses_time))) { 172 (void) fprintf(stdout, MSGSTR(5662, 173 " No new enclosure(s) were added!!\n\n")); 174 return (0); 175 } 176 if ((err = h_post_insert_encl(ses_time)) != 0) { 177 return (err); 178 } 179 return (0); 180 } 181 182 183 184 /* 185 * gets the devices state - check for disk's reservations. 186 * 187 * RETURNS: 188 * 0 if OK 189 * non-zero otherwise 190 */ 191 static int 192 h_get_fcdev_state(char *fc_dev, char *path_phys, int force_flag, 193 int *busy_flag, int *reserve_flag, int verbose_flag) 194 { 195 int err; 196 L_inquiry inq; 197 L_disk_state l_disk_state; 198 199 200 if ((err = g_get_inquiry(path_phys, &inq)) != 0) { 201 (void) fprintf(stderr, 202 MSGSTR(5501, 203 "Inquiry failed for %s\n"), 204 path_phys); 205 return (err); 206 } 207 if (inq.inq_port) { 208 if ((err = l_get_disk_port_status(path_phys, &l_disk_state, 209 FC_PORT_B, verbose_flag)) != 0) { 210 return (err); 211 } 212 } else { 213 if ((err = l_get_disk_port_status(path_phys, &l_disk_state, 214 FC_PORT_A, verbose_flag)) != 0) { 215 return (err); 216 } 217 } 218 219 /* 220 * Don't print error msg. if disk is reserved 221 * and tried to be removed from the same port. 222 * If force flag is set, remove the disk without 223 * checking the disk reservations. 224 */ 225 if (!force_flag) { 226 if (((inq.inq_port) && 227 (l_disk_state.g_disk_state.d_state_flags[FC_PORT_B] & 228 L_RESERVED)) || 229 ((!inq.inq_port) && 230 (l_disk_state.g_disk_state.d_state_flags[FC_PORT_A] & 231 L_RESERVED))) { 232 *reserve_flag = 1; 233 } 234 } 235 return (0); 236 } 237 238 239 /* 240 * Forks a child process and let the child to 241 * execute a given command string by calling the 242 * the execvp() function. Then, the parent process 243 * waits for the child to exit. Once the parent process 244 * is notified by the kernel with the termination of 245 * the child, then the parent checks for the exit 246 * status of the child and return to the caller with -1 in case 247 * of error and zero otherwise. 248 * 249 * RETURNS: 250 * 0 if OK 251 * non-zero otherwise 252 */ 253 int 254 h_execCmnd(char *argStr[], int nArg) 255 { 256 pid_t pid; 257 int ix, status; 258 259 if ((pid = fork()) < 0) { 260 (void) fprintf(stderr, 261 MSGSTR(133, 262 "Error: Failed to fork a process.\n")); 263 return (-1); 264 } else if (pid == 0) { 265 /* child process */ 266 if (execvp(argStr[0], argStr) < 0) { 267 (void) fprintf(stderr, 268 MSGSTR(5502, 269 " Error: execvp() failed to run " 270 "the command:")); 271 for (ix = 0; ix < nArg; ix++) { 272 (void) fprintf(stderr, 273 " %s", argStr[ix]); 274 } 275 (void) fprintf(stderr, "\n"); 276 /* let parent know about the error. */ 277 exit(ENOEXEC); 278 } 279 } 280 281 /* parent executes the following. */ 282 if (waitpid(pid, &status, 0) != pid) { 283 (void) fprintf(stderr, 284 MSGSTR(5503, 285 "Error: waitpid() failed.\n")); 286 return (-1); 287 } 288 if (WIFEXITED(status) && 289 WEXITSTATUS(status) == ENOEXEC) { 290 /* child failed to run the command string. */ 291 return (-1); 292 } 293 return (0); 294 295 } 296 297 298 299 300 /* 301 * frees the hotplug disk list structure. 302 * 303 * RETURNS: 304 * N/A 305 */ 306 void 307 h_free_hotplug_dlist(Hotplug_Devlist **hotplug_dlist) 308 { 309 Hotplug_Devlist *list = NULL; 310 311 while (*hotplug_dlist != NULL) { 312 list = *hotplug_dlist; 313 *hotplug_dlist = (*hotplug_dlist)->next; 314 (void) g_free_multipath(list->seslist); 315 (void) g_free_multipath(list->dlhead); 316 (void) free((void *)list); 317 } 318 } 319 320 321 /* 322 * finds whether device (SENA or an FCAL device) is busy or not. 323 * 324 * OUTPUT: 325 * busy_flag = 1 (if device busy) 326 * 327 * RETURNS: 328 * 0 if O.K. 329 * non-zero otherwise 330 */ 331 static int 332 h_chk_dev_busy(Hotplug_Devlist *hotplug_dev, WWN_list *wwn_list, 333 int *busy_flag, int force_flag, int verbose_flag) 334 { 335 int err; 336 struct dlist *dlist; 337 338 if (hotplug_dev->dev_type == DTYPE_ESI) { 339 if ((err = l_offline_photon(hotplug_dev, wwn_list, 340 force_flag, verbose_flag)) != 0) { 341 if (err == L_DEV_BUSY) { 342 *busy_flag = 1; 343 } else { 344 return (err); 345 } 346 } 347 for (dlist = hotplug_dev->dlhead; 348 dlist != NULL; dlist = dlist->next) { 349 (void) g_online_drive(dlist->multipath, 350 force_flag); 351 } 352 } else { 353 if ((err = g_offline_drive(hotplug_dev->dlhead, 354 force_flag)) != 0) { 355 if (err == L_DEV_BUSY) { 356 *busy_flag = 1; 357 } else { 358 return (err); 359 } 360 } 361 (void) g_online_drive(hotplug_dev->dlhead, force_flag); 362 } 363 return (0); 364 } 365 366 367 368 /* 369 * prints the given list to stdout, 370 * gets the input from user whether 371 * to skip the busy devices or quit 372 * and passes that input to the calling 373 * function. 374 * 375 * OUTPUT: 376 * int *action 377 * s = Skip 378 * q = Quit 379 * 380 * RETURNS: 381 * 0 if OK 382 * non-zero otherwise 383 */ 384 static int 385 h_print_list(Hotplug_Devlist *bsyRsrv_disk_list, int *action, int enc_type) 386 { 387 Hotplug_Devlist *list; 388 int i = 1; 389 char choice[2]; 390 391 (void) fprintf(stdout, 392 MSGSTR(5504, "The list of devices being used" 393 " (either busy or reserved) by the host:\n")); 394 for (list = bsyRsrv_disk_list; list != NULL; list = list->next, i++) { 395 if ((list->dev_type == DTYPE_DIRECT) && 396 (list->dev_location == SENA)) { 397 if (list->f_flag != 0) { 398 if (enc_type == DAK_ENC_TYPE) { 399 (void) fprintf(stdout, MSGSTR(5663, 400 " %d: Box Name: \"%s\" slot %d\n"), 401 i, list->box_name, list->slot); 402 } else { 403 (void) fprintf(stdout, MSGSTR(137, 404 " %d: Box Name: \"%s\" front slot %d\n"), 405 i, list->box_name, list->slot); 406 } 407 } else { 408 if (enc_type == DAK_ENC_TYPE) { 409 (void) fprintf(stdout, MSGSTR(5663, 410 " %d: Box Name: \"%s\" slot %d\n"), 411 i, list->box_name, list->slot + (MAX_DRIVES_DAK/2)); 412 } else { 413 (void) fprintf(stdout, MSGSTR(136, 414 " %d: Box Name: \"%s\" rear slot %d\n"), 415 i, list->box_name, list->slot); 416 } 417 } 418 } else if (((list->dev_type == DTYPE_DIRECT) || 419 (list->dev_type == DTYPE_SEQUENTIAL)) && 420 (list->dev_location == NON_SENA)) { 421 (void) fprintf(stdout, MSGSTR(5505, 422 " %d: Device %s\n"), 423 i, list->dev_name); 424 } else if (list->dev_type == DTYPE_ESI) { 425 (void) fprintf(stdout, MSGSTR(5506, 426 " %d: Box: %s\n"), 427 i, list->box_name); 428 } 429 } 430 431 /* Get the user input and continue accordingly. */ 432 (void) fprintf(stdout, 433 MSGSTR(5507, 434 "\n\nPlease enter 's' or <CR> to Skip the \"busy/reserved\"" 435 " device(s) or\n'q' to Quit and run the" 436 " subcommand with\n-F (force) option. [Default: s]: ")); 437 for (;;) { 438 (void) gets(choice); 439 if (choice[0] == 'q' || choice[0] == 'Q' || 440 choice[0] == 's' || choice[0] == 'S' || 441 choice[0] == '\0') { 442 break; 443 } 444 (void) fprintf(stdout, MSGSTR(5508, 445 " Enter an appropriate option [s,<CR>,q]: ")); 446 } 447 if (choice[0] == 'q' || choice[0] == 'Q') { 448 *action = QUIT; 449 } else { 450 *action = SKIP; 451 } 452 (void) fprintf(stdout, "\n\n"); 453 return (0); 454 } 455 456 457 458 /* 459 * prints the warning message. 460 * 461 * RETURNS: 462 * None. 463 */ 464 static void 465 h_prt_warning() 466 { 467 (void) fprintf(stderr, 468 MSGSTR(5509, 469 "\n WARNING!!! Please ensure that no" 470 " filesystems are mounted on these device(s).\n" 471 " All data on these devices should have been" 472 " backed up.\n\n\n")); 473 } 474 475 476 477 /* 478 * handle helper-mode hotplug commands 479 * 480 * RETURNS: 481 * 0 if OK 482 * non-zero otherwise 483 */ 484 int 485 hotplug(int todo, char **argv, int verbose_flag, int force_flag) 486 { 487 char ses_path[MAXPATHLEN], dev_path[MAXPATHLEN]; 488 char *path_phys = NULL, code, node_wwn_s[WWN_S_LEN]; 489 char inq_path[MAXNAMELEN], *ptr = NULL; 490 uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; 491 int tid, slot = 0, path_index, dtype, f_r, err = 0; 492 int al_pa, i, dev_location = 0, found_nullwwn = 0; 493 int busy_flag = 0, reserve_flag = 0, action = 0; 494 int pathcnt = 1; 495 L_state l_state; 496 gfc_map_t map; 497 Path_struct *path_struct; 498 WWN_list *wwn_list = NULL; 499 Box_list *box_list; 500 Hotplug_Devlist *disk_list, *disk_list_head, *disk_list_tail; 501 Hotplug_Devlist *bsyRsrv_dskLst_head, *bsyRsrv_dskLst_tail; 502 int enc_type = 0; 503 L_inquiry inq; 504 char *physpath; 505 Path_struct *p_pathstruct; 506 char temp2path[MAXPATHLEN]; 507 mp_pathlist_t pathlist; 508 int p_pw = 0, p_on = 0, p_st = 0; 509 510 /* Initialize structures and pointers here */ 511 disk_list_head = disk_list_tail = (Hotplug_Devlist *)NULL; 512 bsyRsrv_dskLst_head = (Hotplug_Devlist *)NULL; 513 bsyRsrv_dskLst_tail = (Hotplug_Devlist *)NULL; 514 map.dev_addr = NULL; 515 516 #ifdef DEBUG 517 (void) fprintf(stderr, 518 "DEBUG: luxadm: hotplug() entering for \"%s\" ...\n", 519 argv[0] ? argv[0] : "<null ptr>"); 520 #endif 521 if ((err = l_get_box_list(&box_list, 0)) != 0) { 522 return (err); 523 } 524 525 if (todo == REMOVE_DEVICE) { 526 (void) h_prt_warning(); 527 } 528 529 /* 530 * At this point user want to insert or remove 531 * one or more pathnames they've specified. 532 */ 533 if ((err = g_get_wwn_list(&wwn_list, verbose_flag)) != 0) { 534 (void) l_free_box_list(&box_list); 535 return (err); 536 } 537 for (path_index = 0; argv[path_index] != NULL; path_index++) { 538 if ((err = l_convert_name(argv[path_index], &path_phys, 539 &path_struct, verbose_flag)) != 0) { 540 /* Make sure we have a device path. */ 541 (void) strcpy(inq_path, argv[path_index]); 542 if (((ptr = strstr(inq_path, ",")) != NULL) && 543 ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') || 544 (*(ptr +1) == 's')) && 545 todo == REMOVE_DEVICE) { 546 if (err != -1) { 547 (void) print_errString(err, 548 argv[path_index]); 549 err = 0; 550 continue; 551 } 552 *ptr = '\0'; 553 slot = path_struct->slot; 554 f_r = path_struct->f_flag; 555 if ((err = l_convert_name(inq_path, &path_phys, 556 &path_struct, verbose_flag)) != 0) { 557 (void) fprintf(stderr, "\n"); 558 (void) fprintf(stderr, 559 MSGSTR(33, 560 " Error: converting" 561 " %s to physical path.\n" 562 " Invalid pathname.\n"), 563 argv[path_index]); 564 if (err != -1) { 565 (void) print_errString(err, 566 argv[path_index]); 567 } 568 err = 0; 569 continue; 570 } 571 if ((err = print_devState(argv[path_index], 572 path_struct->p_physical_path, 573 f_r, slot, verbose_flag)) != 0) { 574 err = 0; 575 continue; 576 } 577 } 578 if (path_struct->ib_path_flag) { 579 path_phys = path_struct->p_physical_path; 580 } else { 581 if (err != -1) { 582 (void) print_errString(err, 583 argv[path_index]); 584 } else { 585 (void) fprintf(stderr, "\n"); 586 (void) fprintf(stderr, 587 MSGSTR(33, 588 " Error: converting" 589 " %s to physical path.\n" 590 " Invalid pathname.\n"), 591 argv[path_index]); 592 } 593 err = 0; 594 continue; 595 } 596 } 597 if (path_struct->slot_valid || 598 strstr(path_phys, DRV_NAME_SSD)) { 599 dtype = DTYPE_DIRECT; 600 } else if (strstr(path_phys, SLSH_DRV_NAME_ST)) { 601 dtype = DTYPE_SEQUENTIAL; 602 } else { 603 dtype = DTYPE_ESI; 604 } 605 606 if (strstr(path_phys, SCSI_VHCI) != NULL) { 607 /* obtain phci */ 608 (void) strcpy(temp2path, path_phys); 609 if (err = g_get_pathlist(temp2path, &pathlist)) { 610 (void) print_errString(err, NULL); 611 exit(-1); 612 } 613 pathcnt = pathlist.path_count; 614 p_pw = p_on = p_st = 0; 615 for (i = 0; i < pathcnt; i++) { 616 if (pathlist.path_info[i].path_state < 617 MAXPATHSTATE) { 618 if (strstr(pathlist.path_info[i]. 619 path_addr, 620 path_struct->argv) != NULL) { 621 p_pw = i; 622 break; 623 } 624 if (pathlist.path_info[i].path_state == 625 MDI_PATHINFO_STATE_ONLINE) { 626 p_on = i; 627 } 628 if (pathlist.path_info[i].path_state == 629 MDI_PATHINFO_STATE_STANDBY) { 630 p_st = i; 631 } 632 } 633 } 634 if (strstr(pathlist.path_info[p_pw].path_addr, 635 path_struct->argv) != NULL) { 636 /* matching input pwwn */ 637 (void) strcpy(temp2path, 638 pathlist.path_info[p_pw].path_hba); 639 } else if (pathlist.path_info[p_on].path_state == 640 MDI_PATHINFO_STATE_ONLINE) { 641 /* on_line path */ 642 (void) strcpy(temp2path, 643 pathlist.path_info[p_on].path_hba); 644 } else { 645 /* standby or path0 */ 646 (void) strcpy(temp2path, 647 pathlist.path_info[p_st].path_hba); 648 } 649 free(pathlist.path_info); 650 (void) strcat(temp2path, FC_CTLR); 651 } else { 652 (void) strcpy(temp2path, path_phys); 653 } 654 655 if ((err = g_get_dev_map(temp2path, &map, verbose_flag)) 656 != 0) { 657 return (err); 658 } 659 660 if ((map.hba_addr.port_topology == FC_TOP_PUBLIC_LOOP) || 661 (map.hba_addr.port_topology == FC_TOP_FABRIC)) { 662 /* public or fabric loop device */ 663 free((void *)map.dev_addr); 664 (void) fprintf(stderr, MSGSTR(5540, 665 "This operation is not " 666 "supported in this topology.\n")); 667 exit(-1); 668 } 669 670 if (todo == REPLACE_DEVICE) { 671 (void) fprintf(stderr, 672 MSGSTR(5511, 673 "Error:" 674 " replace_device is not supported" 675 " on this subsystem.\n")); 676 exit(-1); 677 } 678 679 if ((todo == REMOVE_DEVICE) && 680 (dtype == DTYPE_DIRECT || 681 dtype == DTYPE_SEQUENTIAL || 682 dtype == DTYPE_UNKNOWN)) { 683 if (l_chk_null_wwn(path_struct, ses_path, 684 &l_state, verbose_flag) == 1) { 685 found_nullwwn = 1; 686 /* 687 * set dev_path to NULL, 688 * if disk has null wwn. 689 */ 690 *dev_path = '\0'; 691 dev_location = SENA; 692 goto getinfo; 693 } 694 } 695 696 (void) strcpy(ses_path, path_phys); 697 698 if (strstr(ses_path, "ses") == NULL && 699 l_get_ses_path(path_phys, ses_path, &map, 700 verbose_flag) != 0) { 701 702 /* Could be a non-photon disk device */ 703 if ((todo == REMOVE_DEVICE) && 704 (dtype == DTYPE_DIRECT || 705 dtype == DTYPE_SEQUENTIAL)) { 706 dev_location = NON_SENA; 707 708 if ((err = h_get_fcdev_state(argv[path_index], 709 path_phys, force_flag, 710 &busy_flag, &reserve_flag, 711 verbose_flag)) != 0) { 712 goto done; 713 } 714 (void) strcpy(dev_path, path_phys); 715 if ((err = g_get_wwn(dev_path, port_wwn, 716 node_wwn, &al_pa, 717 verbose_flag)) != 0) { 718 goto done; 719 } 720 (void) sprintf(node_wwn_s, 721 "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", 722 node_wwn[0], node_wwn[1], node_wwn[2], 723 node_wwn[3], node_wwn[4], node_wwn[5], 724 node_wwn[6], node_wwn[7]); 725 tid = g_sf_alpa_to_switch[al_pa]; 726 goto loop; 727 } 728 continue; 729 } 730 731 if (strstr(ses_path, "ses") != NULL) { 732 dev_location = SENA; 733 if ((err = l_convert_name(ses_path, &physpath, 734 &p_pathstruct, 0)) != 0) { 735 free(physpath); 736 free(p_pathstruct); 737 goto done; 738 739 } 740 if ((err = g_get_inquiry(physpath, &inq)) != 0) { 741 free(physpath); 742 free(p_pathstruct); 743 goto done; 744 } 745 enc_type = l_get_enc_type(inq); 746 747 } 748 if ((err = l_get_status(ses_path, 749 &l_state, verbose_flag)) != 0) { 750 goto done; 751 } 752 if (dtype == DTYPE_ESI) { 753 /* could be removing a photon */ 754 if (todo == REMOVE_DEVICE) { 755 /* 756 * Need the select ID (tid) for the IB. 757 */ 758 if ((err = g_get_wwn(ses_path, port_wwn, 759 node_wwn, &al_pa, 760 verbose_flag)) != 0) { 761 goto done; 762 } 763 (void) sprintf(node_wwn_s, 764 "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", 765 node_wwn[0], node_wwn[1], node_wwn[2], 766 node_wwn[3], node_wwn[4], node_wwn[5], 767 node_wwn[6], node_wwn[7]); 768 tid = g_sf_alpa_to_switch[al_pa]; 769 *dev_path = '\0'; 770 /* 771 * Check if any disk in this photon 772 * is reserved by another host 773 */ 774 if (!force_flag) { 775 for ( 776 i = 0; 777 i < l_state.total_num_drv/2; 778 i++) { 779 if ((l_state.drv_front[i].g_disk_state.d_state_flags[PORT_A] & 780 L_RESERVED) || 781 (l_state.drv_front[i].g_disk_state.d_state_flags[PORT_B] & 782 L_RESERVED) || 783 (l_state.drv_rear[i].g_disk_state.d_state_flags[PORT_A] & 784 L_RESERVED) || 785 (l_state.drv_rear[i].g_disk_state.d_state_flags[PORT_B] & 786 L_RESERVED)) { 787 reserve_flag = 1; 788 } 789 } 790 } 791 goto loop; 792 } 793 (void) fprintf(stderr, 794 MSGSTR(5512, 795 "Error: %s already exists!!\n"), 796 argv[path_index]); 797 goto done; 798 } 799 getinfo: 800 if (!path_struct->slot_valid) { 801 /* We are passing the disks path */ 802 if ((err = l_get_slot(path_struct, &l_state, 803 verbose_flag)) != 0) { 804 goto done; 805 } 806 } 807 808 slot = path_struct->slot; 809 if (path_struct->f_flag) { 810 tid = l_state.drv_front[slot].ib_status.sel_id; 811 code = l_state.drv_front[slot].ib_status.code; 812 (void) strcpy(node_wwn_s, 813 l_state.drv_front[slot].g_disk_state.node_wwn_s); 814 } else { 815 tid = l_state.drv_rear[slot].ib_status.sel_id; 816 code = l_state.drv_rear[slot].ib_status.code; 817 (void) strcpy(node_wwn_s, 818 l_state.drv_rear[slot].g_disk_state.node_wwn_s); 819 } 820 821 if (found_nullwwn) { 822 goto loop; 823 } 824 825 l_make_node(ses_path, tid, dev_path, &map, 0); 826 827 if ((todo == INSERT_DEVICE) && 828 (g_device_in_map(&map, tid) || 829 (code != S_NOT_INSTALLED))) { 830 (void) fprintf(stderr, 831 MSGSTR(5513, "\nNotice: %s may " 832 "already be present.\n"), 833 argv[path_index]); 834 if (path_struct->f_flag) { 835 if ((l_state.drv_front[slot].l_state_flag 836 != L_NO_PATH_FOUND) && 837 (!l_state.drv_front[slot].ib_status.dev_off)) 838 continue; 839 } else { 840 if ((l_state.drv_rear[slot].l_state_flag 841 != L_NO_PATH_FOUND) && 842 (!l_state.drv_rear[slot].ib_status.dev_off)) 843 continue; 844 } 845 } 846 847 /* Check if disk is reserved */ 848 if ((todo == REMOVE_DEVICE) && (!force_flag)) { 849 if (path_struct->f_flag) { 850 if ((l_state.drv_front[slot].g_disk_state.d_state_flags[PORT_A] & 851 L_RESERVED) || 852 (l_state.drv_front[slot].g_disk_state.d_state_flags[PORT_B] & 853 L_RESERVED)) { 854 reserve_flag = 1; 855 } 856 } else { 857 if ((l_state.drv_rear[slot].g_disk_state.d_state_flags[PORT_A] & 858 L_RESERVED) || 859 (l_state.drv_rear[slot].g_disk_state.d_state_flags[PORT_B] & 860 L_RESERVED)) { 861 reserve_flag = 1; 862 } 863 } 864 } 865 866 loop: 867 if ((disk_list = (Hotplug_Devlist *) 868 calloc(1, sizeof (Hotplug_Devlist))) == NULL) { 869 (void) print_errString(L_MALLOC_FAILED, NULL); 870 goto done; 871 } 872 873 /* 874 * dev_path is NULL when removing a whole encloser. We 875 * don't want to call g_get_multipath while removing whole 876 * enclosure. Its being taken care later in the code path 877 */ 878 879 if ((todo != INSERT_DEVICE) && (dtype != DTYPE_ESI)) { 880 if ((err = g_get_multipath(dev_path, 881 &(disk_list->dlhead), 882 wwn_list, verbose_flag)) != 0) { 883 if (disk_list->dlhead != NULL) { 884 (void) g_free_multipath( 885 disk_list->dlhead); 886 } 887 goto done; 888 } 889 } 890 disk_list->dev_type = dtype; 891 disk_list->dev_location = dev_location; 892 (void) strcpy(disk_list->dev_name, 893 argv[path_index]); 894 disk_list->tid = tid; 895 (void) strcpy(disk_list->node_wwn_s, node_wwn_s); 896 if (dev_location == SENA) { 897 if ((err = l_get_allses(ses_path, box_list, 898 &(disk_list->seslist), 0)) != 0) { 899 if (disk_list->seslist != NULL) { 900 (void) g_free_multipath(disk_list->seslist); 901 } 902 goto done; 903 } 904 (void) strcpy(disk_list->box_name, 905 (char *)l_state.ib_tbl.enclosure_name); 906 disk_list->slot = slot; 907 disk_list->f_flag = path_struct->f_flag; 908 } 909 if (todo == REMOVE_DEVICE && !force_flag && !reserve_flag) { 910 if ((err = h_chk_dev_busy(disk_list, wwn_list, 911 &busy_flag, force_flag, verbose_flag)) != 0) { 912 goto done; 913 } 914 } 915 916 if (reserve_flag || busy_flag) { 917 if (reserve_flag) 918 disk_list->reserve_flag = 1; 919 if (busy_flag) 920 disk_list->busy_flag = 1; 921 922 if (bsyRsrv_dskLst_head == NULL) { 923 bsyRsrv_dskLst_head = 924 bsyRsrv_dskLst_tail = disk_list; 925 } else { 926 disk_list->prev = bsyRsrv_dskLst_tail; 927 bsyRsrv_dskLst_tail->next = disk_list; 928 bsyRsrv_dskLst_tail = disk_list; 929 } 930 reserve_flag = 0; 931 busy_flag = 0; 932 933 } else if (disk_list_head == NULL) { 934 disk_list_head = disk_list_tail = disk_list; 935 } else { 936 disk_list->prev = disk_list_tail; 937 disk_list_tail->next = disk_list; 938 disk_list_tail = disk_list; 939 } 940 } 941 942 if (bsyRsrv_dskLst_head != NULL) { 943 if ((err = h_print_list(bsyRsrv_dskLst_head, 944 &action, enc_type)) != 0) { 945 goto done; 946 } 947 if (action == SKIP) { 948 (void) h_free_hotplug_dlist(&bsyRsrv_dskLst_head); 949 } else if (action == QUIT) { 950 goto done; 951 } 952 } 953 if (disk_list_head != NULL) { 954 if ((h_print_list_warn(disk_list_head, todo, enc_type)) != 0) { 955 goto done; 956 } 957 if ((err = h_pre_hotplug(&disk_list_head, wwn_list, todo, verbose_flag, 958 force_flag)) != 0) { 959 goto done; 960 } 961 if (disk_list_head != NULL) { 962 if (todo == REMOVE_DEVICE) { 963 (void) fprintf(stdout, MSGSTR(5514, 964 "\nHit <Return> after " 965 "removing the device(s).")); 966 } else { 967 (void) fprintf(stdout, MSGSTR(5515, 968 "\nHit <Return> after " 969 "inserting the device(s).")); 970 } 971 (void) getchar(); 972 (void) fprintf(stdout, "\n"); 973 if ((err = h_post_hotplug(disk_list_head, wwn_list, 974 todo, verbose_flag, force_flag, 975 enc_type)) != 0) { 976 goto done; 977 } 978 } 979 } 980 done: 981 (void) l_free_box_list(&box_list); 982 (void) g_free_wwn_list(&wwn_list); 983 if (err && err != -1) { 984 return (err); 985 } 986 free((void *)map.dev_addr); 987 return (0); 988 } 989 990 991 992 993 /* 994 * Internal routine to clean up ../'s in paths. 995 * returns 0 if no "../" are left. 996 * 997 * Wouldn't it be nice if there was a standard system library 998 * routine to do this...? 999 */ 1000 static int 1001 cleanup_dotdot_path(char *path) 1002 { 1003 char holder[MAXPATHLEN]; 1004 char *dotdot; 1005 char *previous_slash; 1006 1007 /* Find the first "/../" in the string */ 1008 dotdot = strstr(path, "/../"); 1009 if (dotdot == NULL) { 1010 return (0); 1011 } 1012 1013 1014 /* 1015 * If the [0] character is '/' and "../" immediatly 1016 * follows it, then we can strip the ../ 1017 * 1018 * /../../foo/bar == /foo/bar 1019 * 1020 */ 1021 if (dotdot == path) { 1022 strcpy(holder, &path[3]); /* strip "/.." */ 1023 strcpy(path, holder); 1024 return (1); 1025 } 1026 1027 /* 1028 * Now look for the LAST "/" before the "/../" 1029 * as this is the parent dir we can get rid of. 1030 * We do this by temporarily truncating the string 1031 * at the '/' just before "../" using the dotdot pointer. 1032 */ 1033 *dotdot = '\0'; 1034 previous_slash = strrchr(path, '/'); 1035 if (previous_slash == NULL) { 1036 /* 1037 * hmm, somethings wrong. path looks something 1038 * like "foo/../bar/" so we can't really deal with it. 1039 */ 1040 return (0); 1041 } 1042 /* 1043 * Now truncate the path just after the previous '/' 1044 * and slam everything after the "../" back on 1045 */ 1046 *(previous_slash+1) = '\0'; 1047 (void) strcat(path, dotdot+4); 1048 return (1); /* We may have more "../"s */ 1049 } 1050 1051 1052 /* 1053 * Follow symbolic links from the logical device name to 1054 * the /devfs physical device name. To be complete, we 1055 * handle the case of multiple links. This function 1056 * either returns NULL (no links, or some other error), 1057 * or the physical device name, alloc'ed on the heap. 1058 * 1059 * For S10 the physical path may be non-existent. 1060 * 1061 * NOTE: If the path is relative, it will be forced into 1062 * an absolute path by pre-pending the pwd to it. 1063 */ 1064 char * 1065 h_get_physical_name_from_link(char *path) 1066 { 1067 struct stat stbuf; 1068 char source[MAXPATHLEN]; 1069 char scratch[MAXPATHLEN]; 1070 char pwd[MAXPATHLEN]; 1071 char *tmp; 1072 int cnt; 1073 1074 /* return NULL if path is NULL */ 1075 if (path == NULL) { 1076 return (NULL); 1077 } 1078 1079 strcpy(source, path); 1080 for (;;) { 1081 1082 /* 1083 * First make sure the path is absolute. If not, make it. 1084 * If it's already an absolute path, we have no need 1085 * to determine the cwd, so the program should still 1086 * function within security-by-obscurity directories. 1087 */ 1088 if (source[0] != '/') { 1089 tmp = getcwd(pwd, MAXPATHLEN); 1090 if (tmp == NULL) { 1091 O_DPRINTF("getcwd() failed - %s\n", 1092 strerror(errno)); 1093 return (NULL); 1094 } 1095 /* 1096 * Handle special case of "./foo/bar" 1097 */ 1098 if (source[0] == '.' && source[1] == '/') { 1099 strcpy(scratch, source+2); 1100 } else { /* no "./" so just take everything */ 1101 strcpy(scratch, source); 1102 } 1103 strcpy(source, pwd); 1104 (void) strcat(source, "/"); 1105 (void) strcat(source, scratch); 1106 } 1107 1108 /* 1109 * Clean up any "../"s that are in the path 1110 */ 1111 while (cleanup_dotdot_path(source)); 1112 1113 /* 1114 * source is now an absolute path to the link we're 1115 * concerned with 1116 * 1117 * S10: Do NOT ignore dangling links, pointing to devfs nodes. 1118 */ 1119 if (strstr(source, "/devices")) { 1120 return (g_alloc_string(source)); 1121 } 1122 1123 if (lstat(source, &stbuf) == -1) { 1124 O_DPRINTF("lstat() failed for - %s\n", 1125 source, strerror(errno)); 1126 return (NULL); 1127 } 1128 /* 1129 * If the file is not a link, we're done one 1130 * way or the other. If there were links, 1131 * return the full pathname of the resulting 1132 * file. 1133 * 1134 * Note: All of our temp's are on the stack, 1135 * so we have to copy the final result to the heap. 1136 */ 1137 if (!S_ISLNK(stbuf.st_mode)) { 1138 return (g_alloc_string(source)); 1139 } 1140 cnt = readlink(source, scratch, sizeof (scratch)); 1141 if (cnt < 0) { 1142 O_DPRINTF("readlink() failed - %s\n", 1143 strerror(errno)); 1144 return (NULL); 1145 } 1146 /* 1147 * scratch is on the heap, and for some reason readlink 1148 * doesn't always terminate things properly so we have 1149 * to make certain we're properly terminated 1150 */ 1151 scratch[cnt] = '\0'; 1152 1153 /* 1154 * Now check to see if the link is relative. If so, 1155 * then we have to append it to the directory 1156 * which the source was in. (This is non trivial) 1157 */ 1158 if (scratch[0] != '/') { 1159 tmp = strrchr(source, '/'); 1160 if (tmp == NULL) { /* Whoa! Something's hosed! */ 1161 O_DPRINTF("Internal error... corrupt path.\n"); 1162 return (NULL); 1163 } 1164 /* Now strip off just the directory path */ 1165 *(tmp+1) = '\0'; /* Keeping the last '/' */ 1166 /* and append the new link */ 1167 (void) strcat(source, scratch); 1168 /* 1169 * Note: At this point, source should have "../"s 1170 * but we'll clean it up in the next pass through 1171 * the loop. 1172 */ 1173 } else { 1174 /* It's an absolute link so no worries */ 1175 strcpy(source, scratch); 1176 } 1177 } 1178 /* Never reach here */ 1179 } 1180 1181 /* 1182 * Function for getting physical pathnames 1183 * 1184 * For S10 the physical path may not exist at the time devctl calls 1185 * are made. So we should not return error if stat fails on /devices path. 1186 * 1187 * This function can handle 2 different inputs. 1188 * 1189 * 1) Inputs of the form /dev/rdsk/cNtNdNsN 1190 * These are identified by being a link 1191 * The physical path they are linked to is returned. 1192 * 1193 * 2) Inputs of the form /devices/... 1194 * These are actual physical names. 1195 * They are not converted. 1196 */ 1197 char * 1198 h_get_physical_name(char *path) 1199 { 1200 struct stat stbuf; 1201 char s[MAXPATHLEN]; 1202 char savedir[MAXPATHLEN]; 1203 char *result = NULL; 1204 int status = 0; 1205 1206 /* return invalid path if path NULL */ 1207 if (path == NULL) { 1208 return (NULL); 1209 } 1210 1211 (void) strcpy(s, path); 1212 1213 status = lstat(s, &stbuf); 1214 1215 /* 1216 * S10: If string is devfs node we allow failed lstat. 1217 */ 1218 if ((status == -1) || !S_ISLNK(stbuf.st_mode)) { 1219 /* Make sure a full path as that is required. */ 1220 if (strstr(s, "/devices")) { 1221 result = g_alloc_string(s); 1222 } else { 1223 if (getcwd(savedir, 1224 sizeof (savedir)) == NULL) { 1225 return (NULL); 1226 } 1227 /* 1228 * Check for this format: 1229 * ./ssd@0,1:g,raw 1230 */ 1231 if (s[0] == '.') { 1232 (void) strcat(savedir, &s[1]); 1233 } else { 1234 (void) strcat(savedir, "/"); 1235 (void) strcat(savedir, s); 1236 } 1237 if ((status != -1) || strstr(s, "/devices")) { 1238 result = g_alloc_string(savedir); 1239 } 1240 } 1241 } else { 1242 /* 1243 * Entry is linked file 1244 * so follow link to physical name 1245 */ 1246 result = h_get_physical_name_from_link(path); 1247 } 1248 1249 exit: 1250 return (result); 1251 } 1252 1253 1254 /* 1255 * handle expert-mode hotplug commands 1256 * 1257 * return 0 iff all is okay 1258 */ 1259 int 1260 hotplug_e(int todo, char **argv, int verbose_flag, int force_flag) 1261 { 1262 char *path_phys = NULL; 1263 char bus_path[MAXPATHLEN]; 1264 char *ptr; 1265 int exit_code = 1; 1266 devctl_hdl_t dcp; 1267 uint_t devstate; 1268 int i = 0, pathcnt = 1; 1269 mp_pathlist_t pathlist; 1270 int p_pw = 0, p_on = 0, p_st = 0; 1271 1272 1273 switch (todo) { 1274 case DEV_ONLINE: 1275 case DEV_OFFLINE: 1276 case DEV_GETSTATE: 1277 case DEV_RESET: 1278 /* get physical name */ 1279 if ((path_phys = h_get_physical_name(argv[0])) == NULL) { 1280 1281 (void) fprintf(stderr, 1282 MSGSTR(112, "Error: Invalid pathname (%s)"), 1283 argv[0]); 1284 (void) fprintf(stderr, "\n"); 1285 return (1); 1286 } 1287 1288 if (verbose_flag) { 1289 (void) fprintf(stdout, 1290 MSGSTR(5516, 1291 "phys path = \"%s\"\n"), 1292 path_phys); 1293 } 1294 1295 /* acquire rights to hack on device */ 1296 if ((dcp = devctl_device_acquire(path_phys, 1297 force_flag ? 0 : DC_EXCL)) == NULL) { 1298 1299 (void) fprintf(stderr, MSGSTR(5517, 1300 "Error: can't acquire \"%s\": %s\n"), 1301 path_phys, strerror(errno)); 1302 return (1); 1303 } 1304 1305 switch (todo) { 1306 case DEV_ONLINE: 1307 exit_code = devctl_device_online(dcp); 1308 break; 1309 case DEV_OFFLINE: 1310 exit_code = devctl_device_offline(dcp); 1311 break; 1312 case DEV_GETSTATE: 1313 if ((exit_code = devctl_device_getstate(dcp, 1314 &devstate)) == 0) { 1315 print_dev_state(argv[0], devstate); 1316 } 1317 break; 1318 case DEV_RESET: 1319 exit_code = devctl_device_reset(dcp); 1320 break; 1321 } 1322 1323 if (exit_code != 0) { 1324 perror(MSGSTR(5518, "devctl")); 1325 } 1326 1327 /* all done now -- release device */ 1328 devctl_release(dcp); 1329 break; 1330 1331 /* for hotplugging bus operations */ 1332 case BUS_QUIESCE: 1333 case BUS_UNQUIESCE: 1334 case BUS_GETSTATE: 1335 case BUS_RESET: 1336 case BUS_RESETALL: 1337 /* get physical name */ 1338 if ((path_phys = h_get_physical_name(argv[0])) == 1339 NULL) { 1340 (void) fprintf(stderr, 1341 MSGSTR(112, "Error: Invalid pathname (%s)"), 1342 argv[0]); 1343 (void) fprintf(stderr, "\n"); 1344 return (1); 1345 } 1346 if (verbose_flag) { 1347 printf(MSGSTR(5519, "phys path = \"%s\"\n"), path_phys); 1348 } 1349 1350 /* acquire rights to hack on device */ 1351 /* delete leaf part from path_phys. */ 1352 if (strstr(path_phys, SCSI_VHCI) != NULL) { 1353 /* obtain phci */ 1354 (void) strcpy(bus_path, path_phys); 1355 if (g_get_pathlist(bus_path, &pathlist)) { 1356 (void) fprintf(stderr, 1357 MSGSTR(112, "Error: Invalid pathname (%s)"), 1358 path_phys); 1359 (void) fprintf(stderr, "\n"); 1360 return (1); 1361 } 1362 pathcnt = pathlist.path_count; 1363 p_pw = p_on = p_st = 0; 1364 for (i = 0; i < pathcnt; i++) { 1365 if (pathlist.path_info[i].path_state < 1366 MAXPATHSTATE) { 1367 if (strstr(pathlist.path_info[i]. 1368 path_addr, 1369 argv[0]) != NULL) { 1370 p_pw = i; 1371 break; 1372 } 1373 if (pathlist.path_info[i].path_state == 1374 MDI_PATHINFO_STATE_ONLINE) { 1375 p_on = i; 1376 } 1377 if (pathlist.path_info[i].path_state == 1378 MDI_PATHINFO_STATE_STANDBY) { 1379 p_st = i; 1380 } 1381 } 1382 } 1383 if (strstr(pathlist.path_info[p_pw].path_addr, 1384 argv[0]) != NULL) { 1385 /* matching input pwwn */ 1386 (void) strcpy(bus_path, 1387 pathlist.path_info[p_pw].path_hba); 1388 } else if (pathlist.path_info[p_on].path_state == 1389 MDI_PATHINFO_STATE_ONLINE) { 1390 /* on_line path */ 1391 (void) strcpy(bus_path, 1392 pathlist.path_info[p_on].path_hba); 1393 } else { 1394 /* standby or path0 */ 1395 (void) strcpy(bus_path, 1396 pathlist.path_info[p_st].path_hba); 1397 } 1398 free(pathlist.path_info); 1399 } else { 1400 1401 (void) strcpy(bus_path, path_phys); 1402 ptr = strrchr(bus_path, '/'); 1403 if (ptr) { 1404 *ptr = '\0'; 1405 } else { 1406 (void) fprintf(stderr, 1407 MSGSTR(112, "Error: Invalid pathname (%s)"), 1408 path_phys); 1409 (void) fprintf(stderr, "\n"); 1410 return (1); 1411 } 1412 } 1413 1414 if ((dcp = devctl_bus_acquire(bus_path, 1415 force_flag ? 0 : DC_EXCL)) == NULL) { 1416 (void) fprintf(stderr, 1417 MSGSTR(5521, 1418 " Error: can't acquire bus node from" 1419 " the path \"%s\": %s\n"), 1420 bus_path, strerror(errno)); 1421 return (1); 1422 } 1423 1424 switch (todo) { 1425 case BUS_QUIESCE: 1426 exit_code = devctl_bus_quiesce(dcp); 1427 break; 1428 case BUS_UNQUIESCE: 1429 exit_code = devctl_bus_unquiesce(dcp); 1430 break; 1431 case BUS_GETSTATE: 1432 if ((exit_code = devctl_bus_getstate(dcp, 1433 &devstate)) == 0) { 1434 print_bus_state(argv[0], devstate); 1435 } 1436 break; 1437 case BUS_RESET: 1438 exit_code = devctl_bus_reset(dcp); 1439 break; 1440 case BUS_RESETALL: 1441 exit_code = devctl_bus_resetall(dcp); 1442 break; 1443 } 1444 1445 if (exit_code != 0) { 1446 perror(MSGSTR(5522, "devctl")); 1447 } 1448 1449 /* all done now -- release device */ 1450 devctl_release(dcp); 1451 break; 1452 } 1453 1454 return (exit_code); 1455 } 1456 1457 1458 1459 /* 1460 * Prepares an individual FC_AL device 1461 * to be removed from the specified 1462 * slot. 1463 * 1464 * RETURNS: 1465 * 0 if OK 1466 * non-zero otherwise. 1467 */ 1468 static int 1469 h_pre_remove_dev(Hotplug_Devlist *hotplug_disk, WWN_list *wwn_list, 1470 int verbose_flag, int force_flag) 1471 { 1472 char *dev_path, device_name[MAXNAMELEN]; 1473 int err; 1474 1475 /* Initialize pointers */ 1476 dev_path = NULL; 1477 1478 if (hotplug_disk->dlhead != NULL) { 1479 dev_path = hotplug_disk->dlhead->dev_path; 1480 (void) strcpy(device_name, (hotplug_disk->dlhead)->logical_path); 1481 } 1482 (void) fprintf(stdout, 1483 MSGSTR(157, 1484 "stopping: %s...."), device_name); 1485 if (!(strstr(dev_path, SLSH_DRV_NAME_ST))) { 1486 if ((err = g_dev_stop(dev_path, wwn_list, verbose_flag)) != 0) 1487 return (err); 1488 } 1489 (void) fprintf(stdout, MSGSTR(156, "Done\n")); 1490 1491 (void) fprintf(stdout, 1492 MSGSTR(158, "offlining: %s...."), device_name); 1493 if ((err = g_offline_drive(hotplug_disk->dlhead, 1494 force_flag)) != 0) { 1495 (void) fprintf(stdout, 1496 MSGSTR(160, 1497 "\nonlining: %s\n"), device_name); 1498 1499 (void) g_online_drive(hotplug_disk->dlhead, force_flag); 1500 (void) fprintf(stdout, 1501 MSGSTR(159, "starting: %s...."), 1502 device_name); 1503 if ((err = g_dev_start(dev_path, 0)) != 0) { 1504 return (err); 1505 } 1506 (void) fprintf(stdout, MSGSTR(156, "Done\n")); 1507 return (err); 1508 } 1509 (void) fprintf(stdout, MSGSTR(156, "Done\n")); 1510 return (0); 1511 } 1512 1513 1514 1515 /* 1516 * Prepares a SENA enclosure or SENA FC_AL device 1517 * to be inserted/removed from a specified slot. 1518 * 1519 * RETURNS: 1520 * 0 if OK 1521 * non-zero otherwise. 1522 */ 1523 static int 1524 h_pre_hotplug_sena(Hotplug_Devlist *hotplug_dev, 1525 WWN_list *wwn_list, int todo, 1526 int verbose_flag, int force_flag) 1527 { 1528 int slot, f_r, i, found_null_wwn = 0, err; 1529 char *ses_path, *dev_path, code; 1530 char node_wwn_s[WWN_SIZE], device_name[MAXNAMELEN]; 1531 struct l_state_struct l_state; 1532 struct dlist *dl; 1533 1534 1535 if (hotplug_dev->dev_type == DTYPE_ESI) { 1536 /* entire photon is being removed */ 1537 if ((err = l_offline_photon(hotplug_dev, wwn_list, 1538 force_flag, verbose_flag)) != 0) { 1539 return (err); 1540 } 1541 return (0); 1542 } 1543 1544 /* if device is an individual sena disk */ 1545 dl = hotplug_dev->seslist; 1546 while (dl) { 1547 ses_path = dl->dev_path; 1548 if ((err = l_get_status(ses_path, &l_state, 1549 verbose_flag)) == 0) 1550 break; 1551 dl = dl->next; 1552 } 1553 if (dl == NULL) { 1554 return (L_GET_STATUS_FAILED); 1555 } 1556 1557 f_r = hotplug_dev->f_flag; 1558 slot = hotplug_dev->slot; 1559 (void) l_get_drive_name(device_name, slot, f_r, hotplug_dev->box_name); 1560 1561 /* check if disk has null wwn */ 1562 if (f_r) { 1563 (void) strncpy(node_wwn_s, 1564 l_state.drv_front[slot].g_disk_state.node_wwn_s, WWN_SIZE); 1565 } else { 1566 (void) strncpy(node_wwn_s, 1567 l_state.drv_rear[slot].g_disk_state.node_wwn_s, WWN_SIZE); 1568 } 1569 for (i = 0; i < WWN_SIZE; i++) { 1570 if (node_wwn_s[i] != '0') 1571 break; 1572 found_null_wwn = 1; 1573 } 1574 1575 switch (todo) { 1576 case INSERT_DEVICE: 1577 if (hotplug_dev->f_flag) { 1578 code = 1579 l_state.drv_front[slot].ib_status.code; 1580 } else { 1581 code = 1582 l_state.drv_rear[slot].ib_status.code; 1583 } 1584 if (code & S_NOT_INSTALLED) { 1585 /* 1586 * At this point we know that the drive is not 1587 * there. Turn on the RQST INSERT bit to make 1588 * the LED blink 1589 */ 1590 if ((err = l_encl_status_page_funcs 1591 (SET_RQST_INSRT, 0, todo, 1592 ses_path, &l_state, f_r, slot, 1593 verbose_flag)) != 0) { 1594 (void) print_errString(err, 1595 device_name); 1596 (void) fprintf(stderr, 1597 MSGSTR(5530, 1598 " %s: could not turn " 1599 "on LED\n"), 1600 device_name); 1601 } 1602 } else { 1603 /* 1604 * Drive is there so start it. 1605 */ 1606 if ((err = l_encl_status_page_funcs 1607 (SET_DRV_ON, 0, todo, 1608 ses_path, &l_state, f_r, slot, 1609 verbose_flag)) != 0) { 1610 (void) print_errString(err, 1611 device_name); 1612 (void) fprintf(stderr, 1613 MSGSTR(5531, 1614 " could not enable" 1615 " %s\n"), 1616 device_name); 1617 } 1618 } 1619 break; 1620 1621 case REMOVE_DEVICE: 1622 /* 1623 * if disk has null wwn, then 1624 * there is no need to check the 1625 * disk/loop status. 1626 */ 1627 if (found_null_wwn == 1) { 1628 if (getenv("_LUX_W_DEBUG") != NULL) { 1629 (void) fprintf(stdout, 1630 "Device %s has " 1631 "null WWN.\n", 1632 device_name); 1633 } 1634 goto rmv; 1635 } 1636 if (hotplug_dev->f_flag) { 1637 if ( 1638 l_state.drv_front[slot].ib_status.code 1639 == S_NOT_INSTALLED) { 1640 (void) fprintf(stderr, 1641 MSGSTR(86, 1642 " Notice: %s may already" 1643 " be removed.\n"), 1644 device_name); 1645 return (0); 1646 } 1647 } else if ( 1648 l_state.drv_rear[slot].ib_status.code 1649 == S_NOT_INSTALLED) { 1650 (void) fprintf(stderr, 1651 MSGSTR(86, 1652 " Notice: %s may already" 1653 " be removed.\n"), 1654 device_name); 1655 return (0); 1656 } 1657 1658 rmv: 1659 if (hotplug_dev->dlhead == NULL) { 1660 dev_path = NULL; 1661 } else { 1662 dev_path = hotplug_dev->dlhead->dev_path; 1663 } 1664 1665 (void) fprintf(stdout, 1666 MSGSTR(157, 1667 "stopping: %s...."), device_name); 1668 if ((err = g_dev_stop(dev_path, wwn_list, 0)) != 0) { 1669 return (err); 1670 } 1671 (void) fprintf(stdout, MSGSTR(156, "Done\n")); 1672 1673 (void) fprintf(stdout, 1674 MSGSTR(158, "offlining: %s...."), 1675 device_name); 1676 if ((err = g_offline_drive(hotplug_dev->dlhead, 1677 force_flag)) != 0) { 1678 (void) fprintf(stdout, 1679 MSGSTR(160, 1680 "\nonlining: %s\n"), device_name); 1681 (void) g_online_drive(hotplug_dev->dlhead, force_flag); 1682 1683 (void) fprintf(stdout, 1684 MSGSTR(159, "starting: %s...."), 1685 device_name); 1686 (void) g_dev_start(dev_path, 0); 1687 (void) fprintf(stdout, MSGSTR(156, "Done\n")); 1688 return (err); 1689 } 1690 (void) fprintf(stdout, MSGSTR(156, "Done\n")); 1691 1692 /* 1693 * Take the drive off the loop 1694 * and blink the LED. 1695 */ 1696 if (hotplug_dev->dev_location == SENA) { 1697 if ((err = l_encl_status_page_funcs(SET_RQST_RMV, 0, 1698 todo, ses_path, &l_state, f_r, 1699 slot, verbose_flag)) != 0) { 1700 (void) print_errString(err, device_name); 1701 (void) fprintf(stderr, 1702 MSGSTR(5539, 1703 " %s: could not blink" 1704 " the yellow LED\n"), 1705 device_name); 1706 } 1707 } 1708 break; 1709 } 1710 return (0); 1711 } 1712 1713 1714 1715 /* 1716 * Performs the post removal operations for 1717 * a SENA enclosure or a SENA FC_AL disk. 1718 * 1719 * RETURNS: 1720 * 0 if OK 1721 * non-zero otherwise 1722 */ 1723 static int 1724 h_post_hotplug_sena(Hotplug_Devlist *hotplug_dev, 1725 WWN_list *wwn_list, int todo, 1726 int verbose_flag, int force_flag, int enc_type) 1727 { 1728 char *ses_path = NULL, *dev_path = NULL, device_name[MAXNAMELEN]; 1729 int tid, slot, f_r, al_pa, timeout = 0; 1730 uchar_t port_wwn[WWN_SIZE], node_wwn[WWN_SIZE]; 1731 char code; 1732 int wait_spinup_flag = 0, wait_map_flag = 0; 1733 int wait_node_flag = 0, err = 0, nArg; 1734 gfc_map_t map; 1735 WWN_list *newWwn_list = NULL; 1736 struct dlist *dl, *dl1; 1737 struct l_state_struct l_state; 1738 1739 1740 dl = hotplug_dev->seslist; 1741 slot = hotplug_dev->slot; 1742 f_r = hotplug_dev->f_flag; 1743 tid = hotplug_dev->tid; 1744 1745 if (hotplug_dev->dev_type == DTYPE_ESI) { 1746 /* 1747 * See if photon has really been removed. If not, 1748 * try onlining the devices if applicable 1749 */ 1750 H_DPRINTF(" post_hotplug_sena: Seeing if enclosure " 1751 "has really been removed:\n" 1752 " tid=0x%x, ses_path %s\n", 1753 tid, dl->dev_path); 1754 1755 while (dl) { 1756 ses_path = dl->dev_path; 1757 if ((err = g_get_dev_map(ses_path, &map, 0)) == 0) { 1758 if ((map.hba_addr.port_topology == 1759 FC_TOP_PUBLIC_LOOP) || 1760 (map.hba_addr.port_topology == 1761 FC_TOP_FABRIC)) { 1762 /* public or fabric loop device */ 1763 free((void *)map.dev_addr); 1764 (void) fprintf(stdout, MSGSTR(5540, 1765 "This operation is not " 1766 "supported in this topology.\n")); 1767 return (0); 1768 } 1769 if ((err = g_get_wwn(ses_path, port_wwn, 1770 node_wwn, &al_pa, verbose_flag)) == 0) { 1771 tid = g_sf_alpa_to_switch[al_pa]; 1772 if (g_device_in_map(&map, tid)) { 1773 free((void *)map.dev_addr); 1774 break; 1775 } 1776 } 1777 FREE_DEV_ADDR(map.dev_addr); 1778 } 1779 1780 dl = dl->next; 1781 } 1782 FREE_DEV_ADDR(map.dev_addr); 1783 if (dl) { 1784 (void) fprintf(stdout, MSGSTR(5640, 1785 "Photon \"%s\" not removed." 1786 " Onlining Drives in enclosure.\n"), 1787 hotplug_dev->box_name); 1788 for (dl = hotplug_dev->dlhead; dl; ) { 1789 (void) g_online_drive(dl->multipath, 1790 force_flag); 1791 (void) g_free_multipath(dl->multipath); 1792 dl1 = dl; 1793 dl = dl->next; 1794 (void) free(dl1); 1795 } 1796 hotplug_dev->dlhead = NULL; 1797 return (0); 1798 } 1799 /* 1800 * Remove logical nodes for this 1801 * photon, this includes ses and 1802 * /dev/dsk entries. 1803 * In Solaris7, disks with -C option 1804 * removes the /dev/dsk entries. 1805 * The -C option is available 1806 * only for Solaris7. From Solaris8 1807 * or higher releases, the "disks" 1808 * program will be replaced by the 1809 * devfsadm program. 1810 */ 1811 /* pass "disks -C" as cmdStrg. */ 1812 nArg = 2; 1813 if (h_execCmnd(cmdStrg[0], nArg) != 0) { 1814 for (dl = hotplug_dev->dlhead; 1815 dl != NULL; dl = dl->next) { 1816 if ((err = h_remove_nodes(dl->multipath)) 1817 != 0) { 1818 return (err); 1819 } 1820 } 1821 } else { 1822 (void) fprintf(stdout, 1823 MSGSTR(5541, 1824 " Logical Nodes being removed" 1825 " under /dev/dsk/ and /dev/rdsk:\n")); 1826 for (dl = hotplug_dev->dlhead; 1827 dl != NULL; dl = dl->next) { 1828 (void) h_print_logical_nodes(dl->multipath); 1829 } 1830 } 1831 1832 for (dl = hotplug_dev->dlhead; dl != NULL; ) { 1833 (void) g_free_multipath(dl->multipath); 1834 dl1 = dl; 1835 dl = dl->next; 1836 (void) free(dl1); 1837 } 1838 hotplug_dev->dlhead = NULL; 1839 if ((err = h_remove_ses_nodes(hotplug_dev->seslist)) != 0) { 1840 return (err); 1841 } 1842 return (0); 1843 } 1844 1845 /* post hotplug operations for a SENA disk. */ 1846 if (enc_type == DAK_ENC_TYPE) { 1847 (void) sprintf(device_name, MSGSTR(5664, 1848 " Drive in Box Name \"%s\" slot %d"), 1849 hotplug_dev->box_name, 1850 f_r ? slot : slot + (MAX_DRIVES_DAK/2)); 1851 } else { 1852 if (tid & 0x10) { 1853 (void) sprintf(device_name, MSGSTR(5542, 1854 " Drive in Box Name \"%s\" rear slot %d"), 1855 hotplug_dev->box_name, slot); 1856 } else { 1857 (void) sprintf(device_name, MSGSTR(5543, 1858 " Drive in Box Name \"%s\" front slot %d"), 1859 hotplug_dev->box_name, slot); 1860 } 1861 } 1862 (void) fprintf(stdout, "%s\n", device_name); 1863 1864 dl = hotplug_dev->seslist; 1865 while (dl) { 1866 ses_path = dl->dev_path; 1867 if ((err = l_get_status(ses_path, &l_state, 1868 verbose_flag)) == 0) 1869 break; 1870 dl = dl->next; 1871 } 1872 if (dl == NULL) { 1873 print_errString(err, ses_path); 1874 return (L_GET_STATUS_FAILED); 1875 } 1876 1877 code = 0; 1878 while (((err = l_encl_status_page_funcs(OVERALL_STATUS, 1879 &code, todo, ses_path, &l_state, f_r, slot, 1880 verbose_flag)) != 0) || (code != 0)) { 1881 if (err) { 1882 (void) print_errString(err, ses_path); 1883 } else if (todo == REMOVE_DEVICE) { 1884 if (code == S_OK) { 1885 (void) fprintf(stderr, 1886 MSGSTR(5544, 1887 "\n Warning: Device has not been" 1888 " removed from the enclosure\n" 1889 " and is still on the loop.")); 1890 return (0); 1891 } else { 1892 (void) fprintf(stderr, 1893 MSGSTR(5545, 1894 " Notice: Device has not been" 1895 " removed from the enclosure.\n" 1896 " It has been removed from the" 1897 " loop and is ready to be\n" 1898 " removed" 1899 " from the enclosure, and" 1900 " the LED is blinking.\n\n")); 1901 } 1902 goto loop2; 1903 } else if ((todo == INSERT_DEVICE) && 1904 ((code != S_NOT_AVAILABLE) || 1905 (timeout > 1906 PHOTON_SPINUP_TIMEOUT) || 1907 err)) { 1908 (void) fprintf(stderr, 1909 MSGSTR(5546, 1910 "\n Warning: Disk status is" 1911 " Not OK!\n\n")); 1912 return (0); 1913 } 1914 (void) sleep(PHOTON_SPINUP_DELAY); 1915 if (wait_spinup_flag++ == 0) { 1916 (void) fprintf(stdout, MSGSTR(5547, 1917 " Waiting for the disk to spin up:")); 1918 } else { 1919 (void) fprintf(stdout, "."); 1920 } 1921 timeout++; 1922 } 1923 if (wait_spinup_flag) { 1924 (void) fprintf(stdout, "\n"); 1925 } 1926 loop2: 1927 switch (todo) { 1928 case INSERT_DEVICE: 1929 /* check loop map that drive is present */ 1930 for (;;) { 1931 dl = hotplug_dev->seslist; 1932 map.dev_addr = (gfc_port_dev_info_t *)NULL; 1933 while (dl) { 1934 ses_path = dl->dev_path; 1935 if ((err = g_get_dev_map(ses_path, 1936 &map, verbose_flag)) != 0) { 1937 (void) fprintf(stderr, 1938 MSGSTR(5548, 1939 " Error: Could not get" 1940 " map for %s.\n"), 1941 ses_path); 1942 return (err); 1943 } 1944 if (g_device_in_map(&map, tid)) { 1945 goto loop3; 1946 } 1947 FREE_DEV_ADDR(map.dev_addr); 1948 dl = dl->next; 1949 } 1950 if (timeout > PHOTON_SPINUP_TIMEOUT) { 1951 (void) fprintf(stderr, 1952 MSGSTR(5549, 1953 " Warning: Device not in" 1954 " loop map.\n")); 1955 FREE_DEV_ADDR(map.dev_addr); 1956 return (0); 1957 } 1958 if (wait_map_flag++ == 0) { 1959 (void) fprintf(stdout, 1960 MSGSTR(5550, 1961 " Waiting for the device " 1962 "to appear in the loop map:")); 1963 } else { 1964 (void) fprintf(stdout, "."); 1965 } 1966 timeout++; 1967 (void) sleep(PHOTON_SPINUP_DELAY); 1968 } 1969 loop3: 1970 if (wait_map_flag) { 1971 (void) fprintf(stdout, "\n"); 1972 } 1973 1974 /* 1975 * Run drvconfig and disks to create 1976 * logical nodes 1977 */ 1978 for (;;) { 1979 /* pass "disks" as cmdStrg */ 1980 nArg = 3; 1981 if (h_execCmnd(cmdStrg[2], nArg) != 0) { 1982 (void) fprintf(stderr, 1983 MSGSTR(5551, 1984 " Could not " 1985 "run drvconfig.\n")); 1986 FREE_DEV_ADDR(map.dev_addr); 1987 return (L_DRVCONFIG_ERROR); 1988 } 1989 1990 if (l_device_present(ses_path, tid, &map, 1991 verbose_flag, &dev_path) == 1) 1992 break; 1993 if (timeout > PHOTON_SPINUP_TIMEOUT) { 1994 (void) fprintf(stderr, 1995 MSGSTR(5552, 1996 " Warning: Could not find " 1997 "any node for inserted " 1998 "device\n")); 1999 FREE_DEV_ADDR(map.dev_addr); 2000 return (0); 2001 } 2002 if (wait_node_flag++ == 0) { 2003 (void) fprintf(stdout, 2004 MSGSTR(5553, 2005 " Waiting for the logical " 2006 "node to be created:")); 2007 } else { 2008 (void) fprintf(stdout, "."); 2009 } 2010 timeout++; 2011 (void) sleep(PHOTON_SPINUP_DELAY); 2012 } 2013 FREE_DEV_ADDR(map.dev_addr); 2014 if (wait_node_flag) { 2015 (void) fprintf(stdout, "\n"); 2016 } 2017 /* 2018 * In Solaris7, disks with -C 2019 * option creates the new links 2020 * and removes any stale links. 2021 * In pre-Solaris7 releases, just 2022 * disks should do it all. 2023 */ 2024 /* pass "disks -C" as cmdStrg */ 2025 nArg = 2; 2026 if (h_execCmnd(cmdStrg[0], nArg) != 0) { 2027 return (L_DISKS_ERROR); 2028 } 2029 /* 2030 * Get a new wwn list here in order to 2031 * get the multiple paths to a newly added 2032 * device. 2033 */ 2034 if ((err = g_get_wwn_list(&newWwn_list, 2035 verbose_flag)) != 0) { 2036 return (err); 2037 } 2038 if ((err = g_get_multipath(dev_path, &dl, 2039 newWwn_list, 0)) != 0) { 2040 return (err); 2041 } 2042 if ((err = h_display_logical_nodes(dl)) != 0) { 2043 return (err); 2044 } 2045 break; 2046 2047 case REMOVE_DEVICE: 2048 /* 2049 * TBD 2050 * Need to check all loops. 2051 */ 2052 /* check whether device is still in loop map */ 2053 if ((err = g_get_dev_map(ses_path, &map, 2054 verbose_flag)) != 0) { 2055 return (err); 2056 } 2057 2058 if ((map.hba_addr.port_topology == 2059 FC_TOP_PUBLIC_LOOP) || 2060 (map.hba_addr.port_topology == 2061 FC_TOP_FABRIC)) { 2062 /* public or fabric loop device */ 2063 free((void *)map.dev_addr); 2064 (void) fprintf(stderr, MSGSTR(5540, 2065 "This operation is not " 2066 "supported in this topology.\n")); 2067 /* 2068 * calling routine expects a 0 return code 2069 * or a pre-defined luxadm error code. 2070 * Here we do not have a pre-defined error 2071 * code, a 0 is returned. 2072 */ 2073 return (0); 2074 } 2075 2076 if (g_device_in_map(&map, tid)) { 2077 (void) fprintf(stderr, MSGSTR(5554, 2078 " Warning: Device still in the loop map.\n")); 2079 FREE_DEV_ADDR(map.dev_addr); 2080 return (0); 2081 } 2082 FREE_DEV_ADDR(map.dev_addr); 2083 /* 2084 * In Solaris7, "disks -C" program 2085 * removes the /dev/{r}dsk entries. 2086 * The -C option is available only 2087 * for Solaris7. From Solaris8 or 2088 * higher releases, the "disks" program 2089 * will be replaced by devfsadm. 2090 */ 2091 /* pass "disks -C" as cmdStrg */ 2092 nArg = 2; 2093 if (h_execCmnd(cmdStrg[0], nArg) != 0) { 2094 return (L_DISKS_ERROR); 2095 } 2096 (void) fprintf(stdout, 2097 MSGSTR(5555, 2098 " Logical Nodes being removed" 2099 " under /dev/dsk/ and /dev/rdsk:\n")); 2100 (void) h_print_logical_nodes( 2101 hotplug_dev->dlhead); 2102 break; 2103 } 2104 return (0); 2105 } 2106 2107 2108 2109 2110 /* 2111 * Creates new ses entries under /dev/es 2112 * directory for the newly added 2113 * enclosures. 2114 * 2115 * RETURNS: 2116 * 0 if OK 2117 * non-zero otherwise 2118 */ 2119 static int 2120 h_post_insert_encl(timestruc_t ses_lastmtim) 2121 { 2122 struct stat ses_stat; 2123 char lname[MAXPATHLEN]; 2124 int err, found_newlink = 0; 2125 DIR *dir; 2126 struct dirent *dirent; 2127 Box_list *bl1, *box_list = NULL; 2128 2129 2130 if ((dir = opendir(SES_DIR)) == NULL) { 2131 return (L_OPEN_ES_DIR_FAILED); 2132 } 2133 if ((err = l_get_box_list(&box_list, 0)) != 0) { 2134 closedir(dir); 2135 return (err); 2136 } 2137 2138 /* 2139 * The mod time of /dev/es was newer than the mod time prior to 2140 * insert so dir entry is checked at this time. 2141 */ 2142 while ((dirent = readdir(dir)) != (struct dirent *)NULL) { 2143 if (strcmp(dirent->d_name, ".") == 0 || 2144 strcmp(dirent->d_name, "..") == 0) 2145 continue; 2146 2147 (void) sprintf(lname, SES_DIR"/%s", dirent->d_name); 2148 if (lstat(lname, &ses_stat) < 0) { 2149 (void) print_errString(L_LSTAT_ES_DIR_ERROR, 2150 lname); 2151 continue; 2152 } 2153 2154 for (bl1 = box_list; bl1; bl1 = bl1->box_next) { 2155 if (strstr(lname, bl1->b_physical_path)) 2156 break; 2157 } 2158 2159 if (box_list && bl1) 2160 continue; 2161 2162 if (NEWER(ses_stat.st_ctim, ses_lastmtim)) { 2163 /* New enclosure was detected. */ 2164 found_newlink++; 2165 if (found_newlink == 1) { 2166 (void) fprintf(stdout, MSGSTR(5556, 2167 " New Logical Nodes under /dev/es:\n")); 2168 } 2169 (void) fprintf(stdout, "\t%s\n", 2170 dirent->d_name); 2171 } 2172 } 2173 if (!found_newlink) { 2174 (void) fprintf(stdout, MSGSTR(5662, 2175 " No new enclosure(s) were added!!\n\n")); 2176 } 2177 2178 closedir(dir); 2179 2180 (void) l_free_box_list(&box_list); 2181 return (0); 2182 } 2183 2184 2185 2186 /* 2187 * performs the post removal of individual 2188 * FC_AL disks. 2189 * 2190 * RETURNS: 2191 * 0 if OK 2192 * non-zero otherwise 2193 */ 2194 static int 2195 h_post_remove_dev(Hotplug_Devlist *hotplug_disk, 2196 int todo, int verbose_flag) 2197 { 2198 char device_name[MAXNAMELEN], *dev_path = NULL; 2199 int tid, err; 2200 gfc_map_t map; 2201 int nArg; 2202 2203 2204 tid = hotplug_disk->tid; 2205 (void) sprintf(device_name, 2206 MSGSTR(5557, 2207 "\n Device: %s"), 2208 (hotplug_disk->dlhead)->logical_path); 2209 2210 (void) fprintf(stdout, "%s\n", device_name); 2211 2212 dev_path = (hotplug_disk->dlhead)->dev_path; 2213 2214 /* 2215 * On qlc, after a forcelip on a FC combo box, it sometimes takes 17 2216 * seconds for the loop to come back online. During this 17 seconds, 2217 * g_get_dev_map * will return L_NO_DEVICES_FOUND. This delay 2218 * has been added to assure that the L_NO_DEVICES_FOUND returned from 2219 * g_get_dev_map is not the result of the 17 second delay on FC combo. 2220 * This only affects qlc. 2221 */ 2222 if ((err = g_get_dev_map(dev_path, &map, verbose_flag)) != 0) { 2223 if ((err == L_NO_DEVICES_FOUND) && 2224 (strstr(dev_path, "SUNW,qlc@") != NULL)) { 2225 sleep(QLC_LIP_DELAY); 2226 if ((err = g_get_dev_map(dev_path, &map, verbose_flag)) 2227 != 0) { 2228 if (err != L_NO_DEVICES_FOUND) 2229 return (err); 2230 } 2231 } else if (err != L_NO_DEVICES_FOUND) 2232 return (err); 2233 } 2234 2235 /* 2236 * if g_get_dev_map returns L_NO_DEVICES_FOUND, then there are not 2237 * devices attached to the HBA and there is no sense in calling 2238 * g_device_in_map(). 2239 */ 2240 if (err != L_NO_DEVICES_FOUND) { 2241 if ((map.hba_addr.port_topology == FC_TOP_PUBLIC_LOOP) || 2242 (map.hba_addr.port_topology == FC_TOP_FABRIC)) { 2243 /* public or fabric loop device */ 2244 free((void *)map.dev_addr); 2245 (void) fprintf(stderr, MSGSTR(5540, 2246 "This operation is not " 2247 "supported in this topology.\n")); 2248 return (0); 2249 } 2250 2251 if (g_device_in_map(&map, tid) != 0) { 2252 (void) fprintf(stderr, 2253 MSGSTR(5558, 2254 " Warning: Device has" 2255 " not been removed from\n" 2256 " the slot and is still" 2257 " in the loop map.\n\n")); 2258 free((void *)map.dev_addr); 2259 return (0); 2260 } 2261 free((void *)map.dev_addr); 2262 } 2263 /* 2264 * In Solaris7, "disks -C" program 2265 * removes the /dev/{r}dsk entries. 2266 * The -C option is available only 2267 * for Solaris7. From Solaris8 or 2268 * higher releases, the "disks" program 2269 * will be replaced by devfsadm. 2270 */ 2271 /* pass "disks -C" as cmdStrg. */ 2272 nArg = 2; 2273 if (h_execCmnd(cmdStrg[0], nArg) != 0) { 2274 return (L_DISKS_ERROR); 2275 } 2276 /* pass "tapes -C as cmdStrg. */ 2277 if (h_execCmnd(cmdStrg[5], nArg) != 0) { 2278 return (L_TAPES_ERROR); 2279 } 2280 (void) h_print_logical_nodes(hotplug_disk->dlhead); 2281 2282 return (0); 2283 } 2284 2285 2286 2287 /* 2288 * Gets the last modification time for 2289 * /dev/es/ and /dev/rdsk directories 2290 * and passes these values to the caller. 2291 * 2292 * RETURNS: 2293 * 0 if OK 2294 * non-zero in case of error 2295 */ 2296 static int 2297 h_pre_insert_encl_dev(timestruc_t *ses_time, timestruc_t *dsk_time, 2298 timestruc_t *rmt_time) 2299 { 2300 struct stat ses_stat, dsk_stat, rmt_stat; 2301 2302 if (stat(SES_DIR, &ses_stat) < 0) { 2303 /* 2304 * Even if there exists no /dev/es don't fail it. 2305 * The host doesn't have to have any enclosure device 2306 * configured. 2307 */ 2308 if (errno == ENOENT) { 2309 ses_time = (timestruc_t *)NULL; 2310 } else { 2311 return (L_LSTAT_ES_DIR_ERROR); 2312 } 2313 } else { 2314 *ses_time = ses_stat.st_mtim; 2315 } 2316 2317 if (stat(DEV_DSK_DIR, &dsk_stat) < 0) { 2318 return (L_STAT_DEV_DIR_ERROR); 2319 } else { 2320 *dsk_time = dsk_stat.st_mtim; 2321 } 2322 if (stat(DEV_TAPE_DIR, &rmt_stat) < 0) { 2323 /* 2324 * Even if there exists no /dev/rmt don't fail it. 2325 * The host doesn't have to have any tape device 2326 * configured. 2327 */ 2328 if (errno == ENOENT) { 2329 rmt_time = (timestruc_t *)NULL; 2330 } else { 2331 return (L_STAT_RMT_DIR_ERROR); 2332 } 2333 } else { 2334 *rmt_time = rmt_stat.st_mtim; 2335 } 2336 2337 return (0); 2338 } 2339 2340 2341 2342 /* 2343 * Waits for loop intialization to complete 2344 * and runs drvconfig, disks and devlinks to create device nodes 2345 * for devices that are being added and prints the newly created 2346 * /dev/rdsk entries. 2347 * 2348 * RETURNS: 2349 * 0 if OK 2350 * non-zero in case of error 2351 */ 2352 2353 static int 2354 h_post_insert_dev(timestruc_t dsk_lastmtim, timestruc_t rmt_lastmtim) 2355 { 2356 int found_newlink = 0, nArg; 2357 2358 (void) fprintf(stdout, 2359 MSGSTR(5560, 2360 "\nWaiting for Loop Initialization to complete...\n")); 2361 2362 /* 2363 * We sleep here to let the system create nodes. Not sleeping 2364 * could cause the drvconfig below to run too soon. 2365 */ 2366 2367 (void) sleep(NODE_CREATION_TIME); 2368 2369 /* 2370 * Run drvconfig and disks to create 2371 * logical nodes 2372 */ 2373 /* pass "drvconfig" as cmdStrg */ 2374 nArg = 1; 2375 if (h_execCmnd(cmdStrg[3], nArg) != 0) { 2376 return (L_DRVCONFIG_ERROR); 2377 } 2378 2379 /* 2380 * In 2.7, disks with the -C 2381 * option should be used to 2382 * create new links and remove 2383 * any stale links. 2384 * In pre-2.7 releases, just 2385 * disks should do it all. 2386 */ 2387 2388 /* pass "disks -C" as cmdStrg */ 2389 nArg = 2; 2390 if (h_execCmnd(cmdStrg[0], nArg) != 0) { 2391 return (L_DISKS_ERROR); 2392 } 2393 /* pass "tapes -C as cmdStrg */ 2394 if (h_execCmnd(cmdStrg[5], nArg) != 0) { 2395 return (L_TAPES_ERROR); 2396 } 2397 2398 /* pass "devlinks" as cmdStrg */ 2399 nArg = 1; 2400 if (h_execCmnd(cmdStrg[4], nArg) != 0) { 2401 return (L_DEVLINKS_ERROR); 2402 } 2403 2404 /* check /dev/dsk and /dev/rmt for new links */ 2405 found_newlink = h_find_new_device_link(DEV_DSK_DIR, dsk_lastmtim) + 2406 h_find_new_device_link(DEV_TAPE_DIR, rmt_lastmtim); 2407 2408 if (!found_newlink) { 2409 (void) fprintf(stdout, MSGSTR(5562, 2410 " No new device(s) were added!!\n\n")); 2411 } 2412 2413 return (0); 2414 } 2415 2416 2417 2418 /* 2419 * Performs the pre hotplug operations on SENA enclosure(s), 2420 * SENA disk(s) and individual fcal disk(s). 2421 * If the device is failed to remove, then it removes the device from the 2422 * hotplug list and continues with the next device in the list. 2423 * 2424 * RETURNS: 2425 * 0 if OK 2426 * prints an error message to stderr and returns 0 2427 */ 2428 static int 2429 h_pre_hotplug(Hotplug_Devlist **disk_list_head_ptr, 2430 WWN_list *wwn_list, int todo, 2431 int verbose_flag, int force_flag) 2432 { 2433 Hotplug_Devlist *list, *disk_list; 2434 int err = 0; 2435 2436 disk_list = *disk_list_head_ptr; 2437 while (disk_list != NULL) { 2438 if ((disk_list->dev_type == DTYPE_ESI) || 2439 (disk_list->dev_location == SENA)) { 2440 if ((err = h_pre_hotplug_sena(disk_list, wwn_list, 2441 todo, verbose_flag, force_flag)) != 0) { 2442 (void) print_errString(err, 2443 disk_list->dev_name); 2444 goto delete; 2445 } 2446 } else if (disk_list->dev_location == NON_SENA) { 2447 if ((err = h_pre_remove_dev(disk_list, wwn_list, 2448 verbose_flag, force_flag)) != 0) { 2449 (void) print_errString(err, 2450 disk_list->dev_name); 2451 goto delete; 2452 } 2453 } 2454 disk_list = disk_list->next; 2455 continue; 2456 delete: 2457 list = disk_list->prev; 2458 if (list != NULL) { 2459 list->next = disk_list->next; 2460 if (list->next != NULL) 2461 list->next->prev = list; 2462 } 2463 list = disk_list; 2464 disk_list = disk_list->next; 2465 if (list == *disk_list_head_ptr) 2466 *disk_list_head_ptr = disk_list; 2467 (void) g_free_multipath(list->seslist); 2468 (void) g_free_multipath(list->dlhead); 2469 (void) free(list); 2470 } 2471 return (0); 2472 } 2473 2474 2475 2476 /* 2477 * Performs the post removal of a list of SENA enclosure(s), 2478 * SENA disk(s) and individual fcal disk(s). 2479 * 2480 * RETURNS: 2481 * 0 O.K. 2482 * non-zero otherwise 2483 */ 2484 static int 2485 h_post_hotplug(Hotplug_Devlist *hotplug_dlist, 2486 WWN_list *wwn_list, int todo, 2487 int verbose_flag, int force_flag, int enc_type) 2488 { 2489 Hotplug_Devlist *list; 2490 int err; 2491 2492 /* Do a lip on every loop so that we get the latest loop maps */ 2493 if (todo != INSERT_DEVICE) { 2494 if ((err = g_forcelip_all(hotplug_dlist)) != 0) { 2495 return (err); 2496 } 2497 } 2498 2499 while (hotplug_dlist != NULL) { 2500 if ((hotplug_dlist->dev_location == SENA) || 2501 (hotplug_dlist->dev_type == DTYPE_ESI)) { 2502 if ((err = h_post_hotplug_sena(hotplug_dlist, wwn_list, todo, 2503 verbose_flag, force_flag, enc_type)) != 0) 2504 (void) print_errString(err, hotplug_dlist->dev_name); 2505 } else if (hotplug_dlist->dev_location == NON_SENA) { 2506 if ((err = h_post_remove_dev(hotplug_dlist, 2507 todo, verbose_flag)) != 0) 2508 (void) print_errString(err, 2509 hotplug_dlist->dev_name); 2510 } 2511 list = hotplug_dlist; 2512 hotplug_dlist = hotplug_dlist->next; 2513 (void) g_free_multipath(list->seslist); 2514 (void) g_free_multipath(list->dlhead); 2515 (void) free(list); 2516 } 2517 return (0); 2518 } 2519 2520 2521 /* 2522 * removes the device's logical paths. 2523 * 2524 * RETURNS: 2525 * 0 if OK 2526 * non-zero otherwise 2527 */ 2528 static int 2529 h_remove_nodes(struct dlist *dl) 2530 { 2531 char link[MAXPATHLEN], path[MAXPATHLEN]; 2532 char lname[MAXPATHLEN], *ptr; 2533 DIR *dir; 2534 struct dirent *dirent; 2535 struct dlist *dlist; 2536 2537 if ((dir = opendir(DEV_DSK_DIR)) == NULL) { 2538 return (L_READ_DEV_DIR_ERROR); 2539 } 2540 if (dl == NULL) { 2541 /* pass "disks" as cmdStrg */ 2542 if (h_execCmnd(cmdStrg[1], 1) != 0) { 2543 return (L_DISKS_ERROR); 2544 } 2545 } 2546 2547 (void) fprintf(stdout, 2548 MSGSTR(5563, 2549 " Removing Logical Nodes: \n")); 2550 2551 while ((dirent = readdir(dir)) != (struct dirent *)NULL) { 2552 if (strcmp(dirent->d_name, ".") == 0 || 2553 strcmp(dirent->d_name, "..") == 0) { 2554 continue; 2555 } 2556 (void) sprintf(lname, DEV_DSK_DIR"/%s", dirent->d_name); 2557 if (readlink((const char *)lname, (char *)link, 2558 (size_t)MAXPATHLEN) <= 0) { 2559 (void) fprintf(stderr, 2560 MSGSTR(5564, 2561 " Error: Could not read %s\n"), 2562 lname); 2563 continue; 2564 } 2565 for (dlist = dl; dlist != NULL; dlist = dlist->next) { 2566 (void) strcpy(path, dlist->dev_path); 2567 ptr = strrchr(path, ':'); 2568 if (ptr) 2569 *ptr = '\0'; 2570 if (strstr(link, path)) { 2571 (void) unlink(lname); 2572 (void) sprintf(lname, "/dev/rdsk/%s", 2573 dirent->d_name); 2574 (void) fprintf(stdout, 2575 MSGSTR(5565, 2576 "\tRemoving %s\n"), 2577 dirent->d_name); 2578 (void) unlink(lname); 2579 } 2580 } 2581 } 2582 closedir(dir); 2583 return (0); 2584 } 2585 2586 2587 2588 /* 2589 * removes the SENA's ses paths. 2590 * 2591 * RETURNS: 2592 * 0 if OK 2593 * non-zero otherwise 2594 */ 2595 static int 2596 h_remove_ses_nodes(struct dlist *dlist) 2597 { 2598 char link[MAXPATHLEN], lname[MAXPATHLEN]; 2599 DIR *dir; 2600 struct dirent *dirent; 2601 struct dlist *dl; 2602 2603 2604 if ((dir = opendir(SES_DIR)) == NULL) { 2605 return (L_READ_DEV_DIR_ERROR); 2606 } 2607 2608 (void) fprintf(stdout, MSGSTR(5566, " Removing Ses Nodes:\n")); 2609 2610 /* 2611 * Remove the ses entries 2612 * of the form ses<#> 2613 * from the /dev/es directory. 2614 */ 2615 2616 while ((dirent = readdir(dir)) != (struct dirent *)NULL) { 2617 if (strcmp(dirent->d_name, ".") == 0 || 2618 strcmp(dirent->d_name, "..") == 0) 2619 continue; 2620 2621 (void) sprintf(lname, SES_DIR"/%s", dirent->d_name); 2622 if (readlink((const char *)lname, (char *)link, 2623 (size_t)MAXPATHLEN) <= 0) { 2624 (void) fprintf(stderr, 2625 MSGSTR(5564, 2626 " Error: Could not read %s\n"), 2627 lname); 2628 continue; 2629 } 2630 for (dl = dlist; dl != NULL; dl = dl->next) { 2631 if (strstr(link, dl->dev_path)) { 2632 (void) fprintf(stdout, 2633 MSGSTR(5568, 2634 "\tRemoving %s\n"), 2635 lname); 2636 (void) unlink(lname); 2637 } 2638 } 2639 } 2640 closedir(dir); 2641 (void) g_free_multipath(dlist); 2642 return (0); 2643 } 2644 2645 2646 /* 2647 * prints the device's logical 2648 * paths for disks to stdout. 2649 * 2650 * RETURNS: 2651 * 0 if OK 2652 * non-zero otherwise 2653 */ 2654 static void 2655 h_print_logical_nodes(struct dlist *disk_list) 2656 { 2657 char *lpath, *ptr, *buf_ptr, buf[MAXNAMELEN], dev[MAXNAMELEN]; 2658 struct dlist *dlist; 2659 int i, found_dev = 0; 2660 char *tape_entries[] = { "", "b", "bn", "c", "cb", "cbn", "cn", 2661 "h", "hb", "hbn", "hn", "l", "lb", 2662 "lbn", "ln", "m", "mb", "mbn", "mn", 2663 "n", "u", "ub", "ubn", "un", NULL}; 2664 2665 for (dlist = disk_list; dlist != NULL; dlist = dlist->next) { 2666 lpath = dlist->logical_path; 2667 if ((ptr = strrchr(lpath, 'c')) == NULL) 2668 continue; 2669 (void) strcpy(buf, ptr); 2670 if ((ptr = strrchr(buf, 's')) == NULL) 2671 continue; 2672 *(++ptr) = '\0'; 2673 found_dev++; 2674 if (found_dev == 1) 2675 (void) fprintf(stdout, 2676 MSGSTR(5559, " Logical Nodes being " 2677 "removed under /dev/dsk/ and " 2678 "/dev/rdsk:\n")); 2679 for (i = 0; i <= 7; i++) { 2680 (void) sprintf(dev, "%s%d", buf, i); 2681 (void) fprintf(stdout, "\t%s\n", dev); 2682 } 2683 } 2684 found_dev = 0; 2685 for (dlist = disk_list; dlist != NULL; dlist = dlist->next) { 2686 lpath = dlist->logical_path; 2687 if (strstr(lpath, DEV_TAPE_DIR)) { 2688 if ((ptr = strrchr(lpath, '/')) == NULL) 2689 continue; 2690 found_dev++; 2691 if (found_dev == 1) 2692 (void) fprintf(stdout, "Logical Nodes being " 2693 "removed under /dev/rmt:\n"); 2694 ptr++; 2695 buf_ptr = ptr; 2696 while (*ptr >= '0' && *ptr <= '9') 2697 ptr++; 2698 *ptr = '\0'; 2699 for (i = 0, ptr = tape_entries[0]; 2700 ptr != NULL; 2701 i++, ptr = tape_entries[i]) { 2702 (void) sprintf(dev, "%s%s", buf_ptr, ptr); 2703 (void) fprintf(stdout, "\t%s\n", dev); 2704 } 2705 } 2706 } 2707 } 2708 2709 /* 2710 * displays logical paths to a 2711 * device to stdout. 2712 * 2713 * RETURNS: 2714 * 0 if OK 2715 * non-zero otherwise 2716 */ 2717 static int 2718 h_display_logical_nodes(struct dlist *dlist) 2719 { 2720 char link[MAXPATHLEN], path[MAXPATHLEN]; 2721 char lname[MAXPATHLEN], *d1; 2722 DIR *dir; 2723 struct dirent *dirent; 2724 struct dlist *dl; 2725 2726 2727 if ((dir = opendir(DEV_DSK_DIR)) == NULL) { 2728 return (L_READ_DEV_DIR_ERROR); 2729 } 2730 (void) fprintf(stdout, 2731 MSGSTR(5569, 2732 " Logical Nodes under /dev/dsk and /dev/rdsk :\n")); 2733 2734 while ((dirent = readdir(dir)) != (struct dirent *)NULL) { 2735 if (strcmp(dirent->d_name, ".") == 0 || 2736 strcmp(dirent->d_name, "..") == 0) { 2737 continue; 2738 } 2739 (void) sprintf(lname, DEV_DSK_DIR"/%s", dirent->d_name); 2740 if (readlink((const char *)lname, (char *)link, 2741 (size_t)MAXPATHLEN) <= 0) { 2742 (void) print_errString(L_SYMLINK_ERROR, lname); 2743 continue; 2744 } 2745 for (dl = dlist; dl; dl = dl->next) { 2746 (void) strcpy(path, dl->dev_path); 2747 d1 = strrchr(path, ':'); 2748 if (d1) 2749 *d1 = '\0'; 2750 if (strstr(link, path)) { 2751 (void) fprintf(stdout, 2752 "\t%s\n", 2753 dirent->d_name); 2754 } 2755 } 2756 } 2757 2758 closedir(dir); 2759 return (0); 2760 } 2761 2762 2763 2764 /* 2765 * prints a list of devices which 2766 * will be inserted or removed 2767 * to the stdout and asks for 2768 * the user's confirmation. 2769 * 2770 * RETURNS: 2771 * 0 if OK 2772 * non-zero otherwise 2773 */ 2774 static int 2775 h_print_list_warn(Hotplug_Devlist *disk_list_head, int todo, int enc_type) 2776 { 2777 int i; 2778 char choice[2]; 2779 struct dlist *dl_ses, *dl_multi; 2780 Hotplug_Devlist *disk_list = disk_list_head; 2781 2782 (void) fprintf(stdout, 2783 MSGSTR(5570, "The list of devices which will be ")); 2784 switch (todo) { 2785 case INSERT_DEVICE: 2786 (void) fprintf(stdout, 2787 MSGSTR(5571, "inserted is:\n")); 2788 break; 2789 case REMOVE_DEVICE: 2790 (void) fprintf(stdout, 2791 MSGSTR(5572, "removed is:\n")); 2792 break; 2793 } 2794 2795 for (i = 1; disk_list; i++, disk_list = disk_list->next) { 2796 if ((disk_list->dev_type == DTYPE_DIRECT) && 2797 (disk_list->dev_location == SENA)) { 2798 if (disk_list->f_flag != 0) { 2799 if (enc_type == DAK_ENC_TYPE) { 2800 (void) fprintf(stdout, MSGSTR(5665, 2801 " %d: Box Name: \"%s\" slot %d\n"), 2802 i, disk_list->box_name, disk_list->slot); 2803 } else { 2804 (void) fprintf(stdout, MSGSTR(137, 2805 " %d: Box Name: \"%s\" front slot %d\n"), 2806 i, disk_list->box_name, disk_list->slot); 2807 } 2808 } else { 2809 if (enc_type == DAK_ENC_TYPE) { 2810 (void) fprintf(stdout, MSGSTR(5665, 2811 " %d: Box Name: \"%s\" slot %d\n"), 2812 i, disk_list->box_name, 2813 disk_list->slot + (MAX_DRIVES_DAK/2)); 2814 } else { 2815 (void) fprintf(stdout, MSGSTR(136, 2816 " %d: Box Name: \"%s\" rear slot %d\n"), 2817 i, disk_list->box_name, disk_list->slot); 2818 } 2819 } 2820 } else if (((disk_list->dev_type == DTYPE_DIRECT) || 2821 (disk_list->dev_type == DTYPE_SEQUENTIAL)) && 2822 (disk_list->dev_location == NON_SENA)) { 2823 (void) fprintf(stdout, MSGSTR(5573, 2824 " %d: Device name: %s\n"), 2825 i, disk_list->dev_name); 2826 } else if (disk_list->dev_type == DTYPE_ESI) { 2827 (void) fprintf(stdout, MSGSTR(5574, 2828 " %d: Box name: %s\n"), 2829 i, disk_list->box_name); 2830 } 2831 if (getenv("_LUX_H_DEBUG") != NULL) { 2832 if (disk_list->dev_location == SENA) { 2833 (void) fprintf(stdout, 2834 " Select ID:\t0x%x\n", 2835 disk_list->tid); 2836 if (disk_list->dev_type != DTYPE_ESI) { 2837 if (enc_type == DAK_ENC_TYPE) { 2838 (void) fprintf(stdout, 2839 " Location: \tSlot %d \n", 2840 disk_list->f_flag 2841 ? disk_list->slot 2842 : disk_list->slot 2843 +MAX_DRIVES_DAK/2); 2844 } else { 2845 (void) fprintf(stdout, 2846 " Location: \tSlot %d %s \n", 2847 disk_list->slot, disk_list->f_flag 2848 ? "front" : "rear"); 2849 } 2850 } 2851 } 2852 } 2853 if (todo == REMOVE_DEVICE) { 2854 (void) fprintf(stdout, " "); 2855 (void) fprintf(stdout, MSGSTR(90, "Node WWN:")); 2856 (void) fprintf(stdout, " %s\n", 2857 disk_list->node_wwn_s); 2858 2859 (void) fprintf(stdout, " "); 2860 (void) fprintf(stdout, MSGSTR(35, "Device Type:")); 2861 if (disk_list->dev_type == DTYPE_ESI) { 2862 (void) fprintf(stdout, MSGSTR(5581, 2863 " SENA (%s)\n"), 2864 dtype[disk_list->dev_type]); 2865 } else { 2866 (void) fprintf(stdout, "%s\n", 2867 dtype[disk_list->dev_type]); 2868 } 2869 2870 if (disk_list->dev_type == DTYPE_ESI) { 2871 dl_ses = disk_list->seslist; 2872 (void) fprintf(stdout, MSGSTR(5575, 2873 " SES Paths:\n")); 2874 while (dl_ses) { 2875 (void) fprintf(stdout, MSGSTR(5576, 2876 " %s\n"), dl_ses->dev_path); 2877 dl_ses = dl_ses->next; 2878 } 2879 } else { 2880 dl_multi = disk_list->dlhead; 2881 (void) fprintf(stdout, MSGSTR(5577, 2882 " Device Paths:\n")); 2883 while (dl_multi) { 2884 (void) fprintf(stdout, MSGSTR(5578, 2885 " %s\n"), 2886 dl_multi->logical_path); 2887 dl_multi = dl_multi->next; 2888 } 2889 } 2890 } 2891 (void) fprintf(stdout, "\n"); 2892 } 2893 (void) fprintf(stdout, MSGSTR(5579, 2894 "\nPlease verify the above list of devices" 2895 " and\nthen enter 'c' or <CR> to Continue" 2896 " or 'q' to Quit. [Default: c]: ")); 2897 2898 /* Get the user input and continue accordingly. */ 2899 for (;;) { 2900 (void) gets(choice); 2901 if (choice[0] == 'c' || choice[0] == 'C' || 2902 choice[0] == 'q' || choice[0] == 'Q' || 2903 choice[0] == '\0') { 2904 break; 2905 } 2906 (void) fprintf(stdout, MSGSTR(5580, 2907 " Enter an appropriate option [c,<CR>,q]: ")); 2908 } 2909 2910 if (choice[0] == 'q' || choice[0] == 'Q') { 2911 return (-1); 2912 } 2913 return (0); 2914 } 2915 2916 2917 static int 2918 h_find_new_device_link(char *device_dir, timestruc_t lastmtim) 2919 { 2920 struct stat dsk_stat; 2921 char lname[MAXPATHLEN], link[MAXPATHLEN]; 2922 char *link_ptr; 2923 DIR *dir; 2924 struct dirent *dirent; 2925 int found_newlink = 0; 2926 2927 2928 if ((dir = opendir(device_dir)) == NULL) { 2929 if (errno == ENOENT) { 2930 return (0); 2931 } else { 2932 return (L_READ_DEV_DIR_ERROR); 2933 } 2934 } 2935 2936 while ((dirent = readdir(dir)) != (struct dirent *)NULL) { 2937 if (strcmp(dirent->d_name, ".") == 0 || 2938 strcmp(dirent->d_name, "..") == 0) { 2939 continue; 2940 } 2941 (void) sprintf(lname, "%s/%s", device_dir, dirent->d_name); 2942 if (lstat(lname, &dsk_stat) < 0) { 2943 (void) print_errString(L_LSTAT_ES_DIR_ERROR, 2944 lname); 2945 continue; 2946 } 2947 if (readlink((const char *)lname, (char *)link, 2948 (size_t)MAXPATHLEN) <= 0) { 2949 (void) print_errString(L_SYMLINK_ERROR, lname); 2950 continue; 2951 } 2952 2953 /* 2954 * "link" can be a relative pathname. But, since 2955 * g_get_path_type() only accepts absolute paths, we 2956 * will skip to the part where "/devices/" begins and pass a 2957 * pointer from there. Since "link" is got from readlink(), 2958 * it is unlikely that it will not have /devices string, but 2959 * we will check for it anyways. 2960 */ 2961 if (!(link_ptr = strstr(link, "/devices/"))) 2962 continue; 2963 if (!g_get_path_type(link_ptr)) { 2964 continue; 2965 } 2966 if (NEWER(dsk_stat.st_ctim, lastmtim)) { 2967 found_newlink++; 2968 if (found_newlink == 1) { 2969 if (! (strcmp(device_dir, DEV_DSK_DIR))) { 2970 (void) fprintf(stdout, MSGSTR(5561, 2971 " New Logical Nodes under " 2972 "/dev/dsk and /dev/rdsk :\n")); 2973 } else { /* device_dir is /dev/rmt */ 2974 (void) fprintf(stdout, "New Logical " 2975 "Node under /dev/rmt:\n"); 2976 } 2977 } 2978 (void) fprintf(stdout, "\t%s\n", dirent->d_name); 2979 } 2980 } 2981 closedir(dir); 2982 return (found_newlink); 2983 } 2984 2985 2986 /* 2987 * prints the device state. 2988 * 2989 * RETURNS: 2990 * None. 2991 */ 2992 void 2993 print_dev_state(char *devname, int state) 2994 { 2995 (void) printf("\t%s: ", devname); 2996 if (state & DEVICE_ONLINE) { 2997 (void) printf(MSGSTR(3000, "Online")); 2998 if (state & DEVICE_BUSY) { 2999 (void) printf(" "); 3000 (void) printf(MSGSTR(37, "Busy")); 3001 } 3002 if (state & DEVICE_DOWN) { 3003 (void) printf(" "); 3004 (void) printf(MSGSTR(118, "Down")); 3005 } 3006 } else { 3007 if (state & DEVICE_OFFLINE) { 3008 (void) printf(MSGSTR(3001, "Offline")); 3009 if (state & DEVICE_DOWN) { 3010 (void) printf(" "); 3011 (void) printf(MSGSTR(118, "Down")); 3012 } 3013 } 3014 } 3015 (void) printf("\n"); 3016 } 3017 3018 3019 /* 3020 * prints the bus state. 3021 * 3022 * RETURNS: 3023 * None. 3024 */ 3025 void 3026 print_bus_state(char *devname, int state) 3027 { 3028 (void) printf("\t%s: ", devname); 3029 if (state == BUS_QUIESCED) { 3030 (void) printf(MSGSTR(3002, "Quiesced")); 3031 } else if (state == BUS_ACTIVE) { 3032 (void) printf(MSGSTR(39, "Active")); 3033 } else if (state == BUS_SHUTDOWN) { 3034 (void) printf(MSGSTR(3003, "Shutdown")); 3035 } 3036 (void) printf("\n"); 3037 } 3038