xref: /illumos-gate/usr/src/cmd/luxadm/hotplug.c (revision 438b5f69eb4f998015cc0835f19f2f80663f2f53)
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 != 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);
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, 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;
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 = 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]);
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 = NULL;
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;
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, *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) = 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);
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 = 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);
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 != 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);
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