comm: add -Wall
[unleashed.git] / usr / src / cmd / luxadm / hotplug.c
blob1b86130cfa1a6e9eb32f69c3af60e9128d45ba22
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 /*LINTLIBRARY*/
32 * Hotplug program for SENA, RSM and SSA
33 * subsystems and individual FC_AL devices.
36 /* #define _POSIX_SOURCE 1 */
39 * I18N message number ranges
40 * This file: 5500 - 5999
41 * Shared common messages: 1 - 1999
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"
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 }
84 /* External variables */
85 extern char *dtype[]; /* From adm.c */
86 extern int Options;
87 extern const int OPTION_CAPF;
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);
123 * Assists the user in hot inserting FC_AL
124 * individual device(s) and SENA enclosure(s).
126 * RETURNS:
127 * 0 if OK
128 * non-zero otherwise
131 h_insertSena_fcdev()
133 timestruc_t ses_time, dsk_time, rmt_time;
134 int err;
135 struct stat ses_stat;
137 if ((err = h_pre_insert_encl_dev(&ses_time, &dsk_time,
138 &rmt_time)) != 0) {
139 return (err);
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();
146 if ((err = h_post_insert_dev(dsk_time, rmt_time)) != 0) {
147 return (err);
150 if (stat(SES_DIR, &ses_stat) < 0) {
152 * Non existence of /dev/es dir indicates
153 * no ses devices inserted.
154 * No need to call h_post_insert_encl().
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);
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().
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);
176 if ((err = h_post_insert_encl(ses_time)) != 0) {
177 return (err);
179 return (0);
185 * gets the devices state - check for disk's reservations.
187 * RETURNS:
188 * 0 if OK
189 * non-zero otherwise
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)
195 int err;
196 L_inquiry inq;
197 L_disk_state l_disk_state;
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);
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);
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);
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.
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;
235 return (0);
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.
249 * RETURNS:
250 * 0 if OK
251 * non-zero otherwise
254 h_execCmnd(char *argStr[], int nArg)
256 pid_t pid;
257 int ix, status;
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]);
275 (void) fprintf(stderr, "\n");
276 /* let parent know about the error. */
277 exit(ENOEXEC);
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);
288 if (WIFEXITED(status) &&
289 WEXITSTATUS(status) == ENOEXEC) {
290 /* child failed to run the command string. */
291 return (-1);
293 return (0);
301 * frees the hotplug disk list structure.
303 * RETURNS:
304 * N/A
306 void
307 h_free_hotplug_dlist(Hotplug_Devlist **hotplug_dlist)
309 Hotplug_Devlist *list = NULL;
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);
322 * finds whether device (SENA or an FCAL device) is busy or not.
324 * OUTPUT:
325 * busy_flag = 1 (if device busy)
327 * RETURNS:
328 * 0 if O.K.
329 * non-zero otherwise
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)
335 int err;
336 struct dlist *dlist;
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);
347 for (dlist = hotplug_dev->dlhead;
348 dlist != NULL; dlist = dlist->next) {
349 (void) g_online_drive(dlist->multipath,
350 force_flag);
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);
361 (void) g_online_drive(hotplug_dev->dlhead, force_flag);
363 return (0);
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.
375 * OUTPUT:
376 * int *action
377 * s = Skip
378 * q = Quit
380 * RETURNS:
381 * 0 if OK
382 * non-zero otherwise
384 static int
385 h_print_list(Hotplug_Devlist *bsyRsrv_disk_list, int *action, int enc_type)
387 Hotplug_Devlist *list;
388 int i = 1;
389 char choice[2];
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 != NULL) {
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);
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);
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);
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;
444 (void) fprintf(stdout, MSGSTR(5508,
445 " Enter an appropriate option [s,<CR>,q]: "));
447 if (choice[0] == 'q' || choice[0] == 'Q') {
448 *action = QUIT;
449 } else {
450 *action = SKIP;
452 (void) fprintf(stdout, "\n\n");
453 return (0);
459 * prints the warning message.
461 * RETURNS:
462 * None.
464 static void
465 h_prt_warning()
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"));
478 * handle helper-mode hotplug commands
480 * RETURNS:
481 * 0 if OK
482 * non-zero otherwise
485 hotplug(int todo, char **argv, int verbose_flag, int force_flag)
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, path_index, dtype, f_r, err = 0;
492 int al_pa, i, dev_location, 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;
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;
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;
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);
525 if (todo == REMOVE_DEVICE) {
526 (void) h_prt_warning();
530 * At this point user want to insert or remove
531 * one or more pathnames they've specified.
533 if ((err = g_get_wwn_list(&wwn_list, verbose_flag)) != 0) {
534 (void) l_free_box_list(&box_list);
535 return (err);
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;
552 *ptr = NULL;
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]);
568 err = 0;
569 continue;
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;
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]);
593 err = 0;
594 continue;
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;
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);
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;
624 if (pathlist.path_info[i].path_state ==
625 MDI_PATHINFO_STATE_ONLINE) {
626 p_on = i;
628 if (pathlist.path_info[i].path_state ==
629 MDI_PATHINFO_STATE_STANDBY) {
630 p_st = i;
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);
649 free(pathlist.path_info);
650 (void) strcat(temp2path, FC_CTLR);
651 } else {
652 (void) strcpy(temp2path, path_phys);
655 if ((err = g_get_dev_map(temp2path, &map, verbose_flag))
656 != 0) {
657 return (err);
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);
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);
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;
687 * set dev_path to NULL,
688 * if disk has null wwn.
690 *dev_path = NULL;
691 dev_location = SENA;
692 goto getinfo;
696 (void) strcpy(ses_path, path_phys);
698 if (strstr(ses_path, "ses") == NULL &&
699 l_get_ses_path(path_phys, ses_path, &map,
700 verbose_flag) != 0) {
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;
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;
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;
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;
728 continue;
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;
740 if ((err = g_get_inquiry(physpath, &inq)) != 0) {
741 free(physpath);
742 free(p_pathstruct);
743 goto done;
745 enc_type = l_get_enc_type(inq);
748 if ((err = l_get_status(ses_path,
749 &l_state, verbose_flag)) != 0) {
750 goto done;
752 if (dtype == DTYPE_ESI) {
753 /* could be removing a photon */
754 if (todo == REMOVE_DEVICE) {
756 * Need the select ID (tid) for the IB.
758 if ((err = g_get_wwn(ses_path, port_wwn,
759 node_wwn, &al_pa,
760 verbose_flag)) != 0) {
761 goto done;
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';
771 * Check if any disk in this photon
772 * is reserved by another host
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;
791 goto loop;
793 (void) fprintf(stderr,
794 MSGSTR(5512,
795 "Error: %s already exists!!\n"),
796 argv[path_index]);
797 goto done;
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;
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);
821 if (found_nullwwn) {
822 goto loop;
825 l_make_node(ses_path, tid, dev_path, &map, 0);
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;
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;
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;
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;
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
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);
887 goto done;
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);
902 goto done;
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;
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;
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;
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;
930 reserve_flag = 0;
931 busy_flag = 0;
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;
942 if (bsyRsrv_dskLst_head != NULL) {
943 if ((err = h_print_list(bsyRsrv_dskLst_head,
944 &action, enc_type)) != 0) {
945 goto done;
947 if (action == SKIP) {
948 (void) h_free_hotplug_dlist(&bsyRsrv_dskLst_head);
949 } else if (action == QUIT) {
950 goto done;
953 if (disk_list_head != NULL) {
954 if ((h_print_list_warn(disk_list_head, todo, enc_type)) != 0) {
955 goto done;
957 if ((err = h_pre_hotplug(&disk_list_head, wwn_list, todo, verbose_flag,
958 force_flag)) != 0) {
959 goto done;
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)."));
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;
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);
986 free((void *)map.dev_addr);
987 return (0);
994 * Internal routine to clean up ../'s in paths.
995 * returns 0 if no "../" are left.
997 * Wouldn't it be nice if there was a standard system library
998 * routine to do this...?
1000 static int
1001 cleanup_dotdot_path(char *path)
1003 char holder[MAXPATHLEN];
1004 char *dotdot;
1005 char *previous_slash;
1007 /* Find the first "/../" in the string */
1008 dotdot = strstr(path, "/../");
1009 if (dotdot == NULL) {
1010 return (0);
1015 * If the [0] character is '/' and "../" immediatly
1016 * follows it, then we can strip the ../
1018 * /../../foo/bar == /foo/bar
1021 if (dotdot == path) {
1022 strcpy(holder, &path[3]); /* strip "/.." */
1023 strcpy(path, holder);
1024 return (1);
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.
1033 *dotdot = '\0';
1034 previous_slash = strrchr(path, '/');
1035 if (previous_slash == NULL) {
1037 * hmm, somethings wrong. path looks something
1038 * like "foo/../bar/" so we can't really deal with it.
1040 return (0);
1043 * Now truncate the path just after the previous '/'
1044 * and slam everything after the "../" back on
1046 *(previous_slash+1) = '\0';
1047 (void) strcat(path, dotdot+4);
1048 return (1); /* We may have more "../"s */
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.
1059 * For S10 the physical path may be non-existent.
1061 * NOTE: If the path is relative, it will be forced into
1062 * an absolute path by pre-pending the pwd to it.
1064 char *
1065 h_get_physical_name_from_link(char *path)
1067 struct stat stbuf;
1068 char source[MAXPATHLEN];
1069 char scratch[MAXPATHLEN];
1070 char pwd[MAXPATHLEN];
1071 char *tmp;
1072 int cnt;
1074 /* return NULL if path is NULL */
1075 if (path == NULL) {
1076 return (NULL);
1079 strcpy(source, path);
1080 for (;;) {
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.
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);
1096 * Handle special case of "./foo/bar"
1098 if (source[0] == '.' && source[1] == '/') {
1099 strcpy(scratch, source+2);
1100 } else { /* no "./" so just take everything */
1101 strcpy(scratch, source);
1103 strcpy(source, pwd);
1104 (void) strcat(source, "/");
1105 (void) strcat(source, scratch);
1109 * Clean up any "../"s that are in the path
1111 while (cleanup_dotdot_path(source));
1114 * source is now an absolute path to the link we're
1115 * concerned with
1117 * S10: Do NOT ignore dangling links, pointing to devfs nodes.
1119 if (strstr(source, "/devices")) {
1120 return (g_alloc_string(source));
1123 if (lstat(source, &stbuf) == -1) {
1124 O_DPRINTF("lstat() failed for - %s\n",
1125 source, strerror(errno));
1126 return (NULL);
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.
1134 * Note: All of our temp's are on the stack,
1135 * so we have to copy the final result to the heap.
1137 if (!S_ISLNK(stbuf.st_mode)) {
1138 return (g_alloc_string(source));
1140 cnt = readlink(source, scratch, sizeof (scratch));
1141 if (cnt < 0) {
1142 O_DPRINTF("readlink() failed - %s\n",
1143 strerror(errno));
1144 return (NULL);
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
1151 scratch[cnt] = '\0';
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)
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);
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);
1169 * Note: At this point, source should have "../"s
1170 * but we'll clean it up in the next pass through
1171 * the loop.
1173 } else {
1174 /* It's an absolute link so no worries */
1175 strcpy(source, scratch);
1178 /* Never reach here */
1182 * Function for getting physical pathnames
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.
1187 * This function can handle 2 different inputs.
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.
1193 * 2) Inputs of the form /devices/...
1194 * These are actual physical names.
1195 * They are not converted.
1197 char *
1198 h_get_physical_name(char *path)
1200 struct stat stbuf;
1201 char s[MAXPATHLEN];
1202 char savedir[MAXPATHLEN];
1203 char *result = NULL;
1204 int status = 0;
1206 /* return invalid path if path NULL */
1207 if (path == NULL) {
1208 return (NULL);
1211 (void) strcpy(s, path);
1213 status = lstat(s, &stbuf);
1216 * S10: If string is devfs node we allow failed lstat.
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);
1228 * Check for this format:
1229 * ./ssd@0,1:g,raw
1231 if (s[0] == '.') {
1232 (void) strcat(savedir, &s[1]);
1233 } else {
1234 (void) strcat(savedir, "/");
1235 (void) strcat(savedir, s);
1237 if ((status != -1) || strstr(s, "/devices")) {
1238 result = g_alloc_string(savedir);
1241 } else {
1243 * Entry is linked file
1244 * so follow link to physical name
1246 result = h_get_physical_name_from_link(path);
1249 exit:
1250 return (result);
1255 * handle expert-mode hotplug commands
1257 * return 0 iff all is okay
1260 hotplug_e(int todo, char **argv, int verbose_flag, int force_flag)
1262 char *path_phys = NULL;
1263 char bus_path[MAXPATHLEN];
1264 char *ptr;
1265 int exit_code;
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;
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) {
1281 (void) fprintf(stderr,
1282 MSGSTR(112, "Error: Invalid pathname (%s)"),
1283 argv[0]);
1284 (void) fprintf(stderr, "\n");
1285 return (1);
1288 if (verbose_flag) {
1289 (void) fprintf(stdout,
1290 MSGSTR(5516,
1291 "phys path = \"%s\"\n"),
1292 path_phys);
1295 /* acquire rights to hack on device */
1296 if ((dcp = devctl_device_acquire(path_phys,
1297 force_flag ? 0 : DC_EXCL)) == NULL) {
1299 (void) fprintf(stderr, MSGSTR(5517,
1300 "Error: can't acquire \"%s\": %s\n"),
1301 path_phys, strerror(errno));
1302 return (1);
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);
1317 break;
1318 case DEV_RESET:
1319 exit_code = devctl_device_reset(dcp);
1320 break;
1323 if (exit_code != 0) {
1324 perror(MSGSTR(5518, "devctl"));
1327 /* all done now -- release device */
1328 devctl_release(dcp);
1329 break;
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);
1346 if (verbose_flag) {
1347 printf(MSGSTR(5519, "phys path = \"%s\"\n"), path_phys);
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);
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;
1373 if (pathlist.path_info[i].path_state ==
1374 MDI_PATHINFO_STATE_ONLINE) {
1375 p_on = i;
1377 if (pathlist.path_info[i].path_state ==
1378 MDI_PATHINFO_STATE_STANDBY) {
1379 p_st = i;
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);
1398 free(pathlist.path_info);
1399 } else {
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);
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);
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);
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;
1445 if (exit_code != 0) {
1446 perror(MSGSTR(5522, "devctl"));
1449 /* all done now -- release device */
1450 devctl_release(dcp);
1451 break;
1454 return (exit_code);
1460 * Prepares an individual FC_AL device
1461 * to be removed from the specified
1462 * slot.
1464 * RETURNS:
1465 * 0 if OK
1466 * non-zero otherwise.
1468 static int
1469 h_pre_remove_dev(Hotplug_Devlist *hotplug_disk, WWN_list *wwn_list,
1470 int verbose_flag, int force_flag)
1472 char *dev_path, device_name[MAXNAMELEN];
1473 int err;
1475 /* Initialize pointers */
1476 dev_path = NULL;
1478 if (hotplug_disk->dlhead != NULL) {
1479 dev_path = hotplug_disk->dlhead->dev_path;
1480 (void) strcpy(device_name, (hotplug_disk->dlhead)->logical_path);
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);
1489 (void) fprintf(stdout, MSGSTR(156, "Done\n"));
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);
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);
1506 (void) fprintf(stdout, MSGSTR(156, "Done\n"));
1507 return (err);
1509 (void) fprintf(stdout, MSGSTR(156, "Done\n"));
1510 return (0);
1516 * Prepares a SENA enclosure or SENA FC_AL device
1517 * to be inserted/removed from a specified slot.
1519 * RETURNS:
1520 * 0 if OK
1521 * non-zero otherwise.
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)
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;
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);
1541 return (0);
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;
1553 if (dl == NULL) {
1554 return (L_GET_STATUS_FAILED);
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);
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);
1569 for (i = 0; i < WWN_SIZE; i++) {
1570 if (node_wwn_s[i] != '0')
1571 break;
1572 found_null_wwn = 1;
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;
1584 if (code & S_NOT_INSTALLED) {
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
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);
1602 } else {
1604 * Drive is there so start it.
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);
1619 break;
1621 case REMOVE_DEVICE:
1623 * if disk has null wwn, then
1624 * there is no need to check the
1625 * disk/loop status.
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);
1634 goto rmv;
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);
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);
1658 rmv:
1659 if (hotplug_dev->dlhead == NULL) {
1660 dev_path = NULL;
1661 } else {
1662 dev_path = hotplug_dev->dlhead->dev_path;
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);
1671 (void) fprintf(stdout, MSGSTR(156, "Done\n"));
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);
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);
1690 (void) fprintf(stdout, MSGSTR(156, "Done\n"));
1693 * Take the drive off the loop
1694 * and blink the LED.
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);
1708 break;
1710 return (0);
1716 * Performs the post removal operations for
1717 * a SENA enclosure or a SENA FC_AL disk.
1719 * RETURNS:
1720 * 0 if OK
1721 * non-zero otherwise
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)
1728 char *ses_path, *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;
1740 dl = hotplug_dev->seslist;
1741 slot = hotplug_dev->slot;
1742 f_r = hotplug_dev->f_flag;
1743 tid = hotplug_dev->tid;
1745 if (hotplug_dev->dev_type == DTYPE_ESI) {
1747 * See if photon has really been removed. If not,
1748 * try onlining the devices if applicable
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);
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);
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;
1777 FREE_DEV_ADDR(map.dev_addr);
1780 dl = dl->next;
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);
1796 hotplug_dev->dlhead = NULL;
1797 return (0);
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.
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);
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);
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);
1838 hotplug_dev->dlhead = NULL;
1839 if ((err = h_remove_ses_nodes(hotplug_dev->seslist)) != 0) {
1840 return (err);
1842 return (0);
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);
1862 (void) fprintf(stdout, "%s\n", device_name);
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;
1872 if (dl == NULL) {
1873 print_errString(err, ses_path);
1874 return (L_GET_STATUS_FAILED);
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"));
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);
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, ".");
1921 timeout++;
1923 if (wait_spinup_flag) {
1924 (void) fprintf(stdout, "\n");
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);
1944 if (g_device_in_map(&map, tid)) {
1945 goto loop3;
1947 FREE_DEV_ADDR(map.dev_addr);
1948 dl = dl->next;
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);
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, ".");
1966 timeout++;
1967 (void) sleep(PHOTON_SPINUP_DELAY);
1969 loop3:
1970 if (wait_map_flag) {
1971 (void) fprintf(stdout, "\n");
1975 * Run drvconfig and disks to create
1976 * logical nodes
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);
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);
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, ".");
2010 timeout++;
2011 (void) sleep(PHOTON_SPINUP_DELAY);
2013 FREE_DEV_ADDR(map.dev_addr);
2014 if (wait_node_flag) {
2015 (void) fprintf(stdout, "\n");
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.
2024 /* pass "disks -C" as cmdStrg */
2025 nArg = 2;
2026 if (h_execCmnd(cmdStrg[0], nArg) != 0) {
2027 return (L_DISKS_ERROR);
2030 * Get a new wwn list here in order to
2031 * get the multiple paths to a newly added
2032 * device.
2034 if ((err = g_get_wwn_list(&newWwn_list,
2035 verbose_flag)) != 0) {
2036 return (err);
2038 if ((err = g_get_multipath(dev_path, &dl,
2039 newWwn_list, 0)) != 0) {
2040 return (err);
2042 if ((err = h_display_logical_nodes(dl)) != 0) {
2043 return (err);
2045 break;
2047 case REMOVE_DEVICE:
2049 * TBD
2050 * Need to check all loops.
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);
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"));
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.
2073 return (0);
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);
2082 FREE_DEV_ADDR(map.dev_addr);
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.
2091 /* pass "disks -C" as cmdStrg */
2092 nArg = 2;
2093 if (h_execCmnd(cmdStrg[0], nArg) != 0) {
2094 return (L_DISKS_ERROR);
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;
2104 return (0);
2111 * Creates new ses entries under /dev/es
2112 * directory for the newly added
2113 * enclosures.
2115 * RETURNS:
2116 * 0 if OK
2117 * non-zero otherwise
2119 static int
2120 h_post_insert_encl(timestruc_t ses_lastmtim)
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;
2130 if ((dir = opendir(SES_DIR)) == NULL) {
2131 return (L_OPEN_ES_DIR_FAILED);
2133 if ((err = l_get_box_list(&box_list, 0)) != 0) {
2134 closedir(dir);
2135 return (err);
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.
2142 while ((dirent = readdir(dir)) != NULL) {
2143 if (strcmp(dirent->d_name, ".") == 0 ||
2144 strcmp(dirent->d_name, "..") == 0)
2145 continue;
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;
2154 for (bl1 = box_list; bl1; bl1 = bl1->box_next) {
2155 if (strstr(lname, bl1->b_physical_path))
2156 break;
2159 if (box_list && bl1)
2160 continue;
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"));
2169 (void) fprintf(stdout, "\t%s\n",
2170 dirent->d_name);
2173 if (!found_newlink) {
2174 (void) fprintf(stdout, MSGSTR(5662,
2175 " No new enclosure(s) were added!!\n\n"));
2178 closedir(dir);
2180 (void) l_free_box_list(&box_list);
2181 return (0);
2187 * performs the post removal of individual
2188 * FC_AL disks.
2190 * RETURNS:
2191 * 0 if OK
2192 * non-zero otherwise
2194 static int
2195 h_post_remove_dev(Hotplug_Devlist *hotplug_disk,
2196 int todo, int verbose_flag)
2198 char device_name[MAXNAMELEN], *dev_path = NULL;
2199 int tid, err;
2200 gfc_map_t map;
2201 int nArg;
2204 tid = hotplug_disk->tid;
2205 (void) sprintf(device_name,
2206 MSGSTR(5557,
2207 "\n Device: %s"),
2208 (hotplug_disk->dlhead)->logical_path);
2210 (void) fprintf(stdout, "%s\n", device_name);
2212 dev_path = (hotplug_disk->dlhead)->dev_path;
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.
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);
2231 } else if (err != L_NO_DEVICES_FOUND)
2232 return (err);
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().
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);
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);
2261 free((void *)map.dev_addr);
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.
2271 /* pass "disks -C" as cmdStrg. */
2272 nArg = 2;
2273 if (h_execCmnd(cmdStrg[0], nArg) != 0) {
2274 return (L_DISKS_ERROR);
2276 /* pass "tapes -C as cmdStrg. */
2277 if (h_execCmnd(cmdStrg[5], nArg) != 0) {
2278 return (L_TAPES_ERROR);
2280 (void) h_print_logical_nodes(hotplug_disk->dlhead);
2282 return (0);
2288 * Gets the last modification time for
2289 * /dev/es/ and /dev/rdsk directories
2290 * and passes these values to the caller.
2292 * RETURNS:
2293 * 0 if OK
2294 * non-zero in case of error
2296 static int
2297 h_pre_insert_encl_dev(timestruc_t *ses_time, timestruc_t *dsk_time,
2298 timestruc_t *rmt_time)
2300 struct stat ses_stat, dsk_stat, rmt_stat;
2302 if (stat(SES_DIR, &ses_stat) < 0) {
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.
2308 if (errno == ENOENT) {
2309 ses_time = (timestruc_t *)NULL;
2310 } else {
2311 return (L_LSTAT_ES_DIR_ERROR);
2313 } else {
2314 *ses_time = ses_stat.st_mtim;
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;
2322 if (stat(DEV_TAPE_DIR, &rmt_stat) < 0) {
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.
2328 if (errno == ENOENT) {
2329 rmt_time = (timestruc_t *)NULL;
2330 } else {
2331 return (L_STAT_RMT_DIR_ERROR);
2333 } else {
2334 *rmt_time = rmt_stat.st_mtim;
2337 return (0);
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.
2348 * RETURNS:
2349 * 0 if OK
2350 * non-zero in case of error
2353 static int
2354 h_post_insert_dev(timestruc_t dsk_lastmtim, timestruc_t rmt_lastmtim)
2356 int found_newlink = 0, nArg;
2358 (void) fprintf(stdout,
2359 MSGSTR(5560,
2360 "\nWaiting for Loop Initialization to complete...\n"));
2363 * We sleep here to let the system create nodes. Not sleeping
2364 * could cause the drvconfig below to run too soon.
2367 (void) sleep(NODE_CREATION_TIME);
2370 * Run drvconfig and disks to create
2371 * logical nodes
2373 /* pass "drvconfig" as cmdStrg */
2374 nArg = 1;
2375 if (h_execCmnd(cmdStrg[3], nArg) != 0) {
2376 return (L_DRVCONFIG_ERROR);
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.
2388 /* pass "disks -C" as cmdStrg */
2389 nArg = 2;
2390 if (h_execCmnd(cmdStrg[0], nArg) != 0) {
2391 return (L_DISKS_ERROR);
2393 /* pass "tapes -C as cmdStrg */
2394 if (h_execCmnd(cmdStrg[5], nArg) != 0) {
2395 return (L_TAPES_ERROR);
2398 /* pass "devlinks" as cmdStrg */
2399 nArg = 1;
2400 if (h_execCmnd(cmdStrg[4], nArg) != 0) {
2401 return (L_DEVLINKS_ERROR);
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);
2408 if (!found_newlink) {
2409 (void) fprintf(stdout, MSGSTR(5562,
2410 " No new device(s) were added!!\n\n"));
2413 return (0);
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.
2424 * RETURNS:
2425 * 0 if OK
2426 * prints an error message to stderr and returns 0
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)
2433 Hotplug_Devlist *list, *disk_list;
2434 int err = 0;
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;
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;
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;
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);
2471 return (0);
2477 * Performs the post removal of a list of SENA enclosure(s),
2478 * SENA disk(s) and individual fcal disk(s).
2480 * RETURNS:
2481 * 0 O.K.
2482 * non-zero otherwise
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)
2489 Hotplug_Devlist *list;
2490 int err;
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);
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);
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);
2517 return (0);
2522 * removes the device's logical paths.
2524 * RETURNS:
2525 * 0 if OK
2526 * non-zero otherwise
2528 static int
2529 h_remove_nodes(struct dlist *dl)
2531 char link[MAXPATHLEN], path[MAXPATHLEN];
2532 char lname[MAXPATHLEN], *ptr;
2533 DIR *dir;
2534 struct dirent *dirent;
2535 struct dlist *dlist;
2537 if ((dir = opendir(DEV_DSK_DIR)) == NULL) {
2538 return (L_READ_DEV_DIR_ERROR);
2540 if (dl == NULL) {
2541 /* pass "disks" as cmdStrg */
2542 if (h_execCmnd(cmdStrg[1], 1) != 0) {
2543 return (L_DISKS_ERROR);
2547 (void) fprintf(stdout,
2548 MSGSTR(5563,
2549 " Removing Logical Nodes: \n"));
2551 while ((dirent = readdir(dir)) != NULL) {
2552 if (strcmp(dirent->d_name, ".") == 0 ||
2553 strcmp(dirent->d_name, "..") == 0) {
2554 continue;
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;
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);
2582 closedir(dir);
2583 return (0);
2589 * removes the SENA's ses paths.
2591 * RETURNS:
2592 * 0 if OK
2593 * non-zero otherwise
2595 static int
2596 h_remove_ses_nodes(struct dlist *dlist)
2598 char link[MAXPATHLEN], lname[MAXPATHLEN];
2599 DIR *dir;
2600 struct dirent *dirent;
2601 struct dlist *dl;
2604 if ((dir = opendir(SES_DIR)) == NULL) {
2605 return (L_READ_DEV_DIR_ERROR);
2608 (void) fprintf(stdout, MSGSTR(5566, " Removing Ses Nodes:\n"));
2611 * Remove the ses entries
2612 * of the form ses<#>
2613 * from the /dev/es directory.
2616 while ((dirent = readdir(dir)) != NULL) {
2617 if (strcmp(dirent->d_name, ".") == 0 ||
2618 strcmp(dirent->d_name, "..") == 0)
2619 continue;
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;
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);
2640 closedir(dir);
2641 (void) g_free_multipath(dlist);
2642 return (0);
2647 * prints the device's logical
2648 * paths for disks to stdout.
2650 * RETURNS:
2651 * 0 if OK
2652 * non-zero otherwise
2654 static void
2655 h_print_logical_nodes(struct dlist *disk_list)
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};
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) = NULL;
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);
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 = NULL;
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);
2710 * displays logical paths to a
2711 * device to stdout.
2713 * RETURNS:
2714 * 0 if OK
2715 * non-zero otherwise
2717 static int
2718 h_display_logical_nodes(struct dlist *dlist)
2720 char link[MAXPATHLEN], path[MAXPATHLEN];
2721 char lname[MAXPATHLEN], *d1;
2722 DIR *dir;
2723 struct dirent *dirent;
2724 struct dlist *dl;
2727 if ((dir = opendir(DEV_DSK_DIR)) == NULL) {
2728 return (L_READ_DEV_DIR_ERROR);
2730 (void) fprintf(stdout,
2731 MSGSTR(5569,
2732 " Logical Nodes under /dev/dsk and /dev/rdsk :\n"));
2734 while ((dirent = readdir(dir)) != NULL) {
2735 if (strcmp(dirent->d_name, ".") == 0 ||
2736 strcmp(dirent->d_name, "..") == 0) {
2737 continue;
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;
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);
2758 closedir(dir);
2759 return (0);
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.
2770 * RETURNS:
2771 * 0 if OK
2772 * non-zero otherwise
2774 static int
2775 h_print_list_warn(Hotplug_Devlist *disk_list_head, int todo, int enc_type)
2777 int i;
2778 char choice[2];
2779 struct dlist *dl_ses, *dl_multi;
2780 Hotplug_Devlist *disk_list = disk_list_head;
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;
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 != NULL) {
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);
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);
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);
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");
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);
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]);
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;
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;
2891 (void) fprintf(stdout, "\n");
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]: "));
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;
2906 (void) fprintf(stdout, MSGSTR(5580,
2907 " Enter an appropriate option [c,<CR>,q]: "));
2910 if (choice[0] == 'q' || choice[0] == 'Q') {
2911 return (-1);
2913 return (0);
2917 static int
2918 h_find_new_device_link(char *device_dir, timestruc_t lastmtim)
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;
2928 if ((dir = opendir(device_dir)) == NULL) {
2929 if (errno == ENOENT) {
2930 return (0);
2931 } else {
2932 return (L_READ_DEV_DIR_ERROR);
2936 while ((dirent = readdir(dir)) != NULL) {
2937 if (strcmp(dirent->d_name, ".") == 0 ||
2938 strcmp(dirent->d_name, "..") == 0) {
2939 continue;
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;
2947 if (readlink((const char *)lname, (char *)link,
2948 (size_t)MAXPATHLEN) <= 0) {
2949 (void) print_errString(L_SYMLINK_ERROR, lname);
2950 continue;
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.
2961 if (!(link_ptr = strstr(link, "/devices/")))
2962 continue;
2963 if (!g_get_path_type(link_ptr)) {
2964 continue;
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");
2978 (void) fprintf(stdout, "\t%s\n", dirent->d_name);
2981 closedir(dir);
2982 return (found_newlink);
2987 * prints the device state.
2989 * RETURNS:
2990 * None.
2992 void
2993 print_dev_state(char *devname, int state)
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"));
3002 if (state & DEVICE_DOWN) {
3003 (void) printf(" ");
3004 (void) printf(MSGSTR(118, "Down"));
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"));
3015 (void) printf("\n");
3020 * prints the bus state.
3022 * RETURNS:
3023 * None.
3025 void
3026 print_bus_state(char *devname, int state)
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"));
3036 (void) printf("\n");