xref: /illumos-gate/usr/src/lib/cfgadm_plugins/scsi/common/cfga_ctl.c (revision 70025d765b044c6d8594bb965a2247a61e991a99)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "cfga_scsi.h"
30 
31 struct larg {
32 	int ndevs;
33 	int nelem;
34 	char *dev;
35 	char **dev_list;
36 };
37 
38 #define	ETC_VFSTAB	"/etc/vfstab"
39 #define	SCFGA_LOCK	"/var/run/cfgadm_scsi"
40 
41 /* Function prototypes */
42 
43 static scfga_ret_t quiesce_confirm(apid_t *apidp,
44     msgid_t cmd_msg, prompt_t *pt, int *okp, int *quiesce, int *l_errnop);
45 static scfga_ret_t dev_hotplug(apid_t *apidp,
46     prompt_t *pt, cfga_flags_t flags, int quiesce, char **errstring);
47 static int disconnect(struct cfga_confirm *confp);
48 static int critical_ctrlr(const char *hba_phys);
49 static cfga_stat_t bus_devctl_to_recep_state(uint_t bus_dc_state);
50 static int get_hba_children(char *bus_path, char *dev_excl, char ***dev_list);
51 static char *get_node_path(char *minor_path);
52 static void free_dev_list_elements(char **dev_list);
53 static void free_dev_list(char **dev_list);
54 static int alloc_dev_list(struct larg *largp);
55 
56 /*
57  * Single thread all implicit quiesce operations
58  */
59 static mutex_t	quiesce_mutex = DEFAULTMUTEX;
60 
61 /*ARGSUSED*/
62 scfga_ret_t
63 bus_change_state(
64 	cfga_cmd_t state_change_cmd,
65 	apid_t *apidp,
66 	struct cfga_confirm *confp,
67 	cfga_flags_t flags,
68 	char **errstring)
69 {
70 	int l_errno = 0, force;
71 	uint_t state = 0;
72 	cfga_stat_t bus_state;
73 	scfga_cmd_t cmd;
74 	msgid_t errid;
75 	cfga_stat_t prereq;
76 	scfga_ret_t ret;
77 	char **dev_list = NULL;
78 
79 	assert(apidp->path != NULL);
80 	assert(apidp->hba_phys != NULL);
81 
82 	/*
83 	 * No dynamic components allowed
84 	 */
85 	if (apidp->dyncomp != NULL) {
86 		cfga_err(errstring, 0, ERR_NOT_BUSAPID, 0);
87 		return (SCFGA_ERR);
88 	}
89 
90 	/* Get bus state */
91 	if (devctl_cmd(apidp->path, SCFGA_BUS_GETSTATE, &state,
92 	    &l_errno) != SCFGA_OK) {
93 		cfga_err(errstring, l_errno, ERR_BUS_GETSTATE, 0);
94 		return (SCFGA_ERR);
95 	}
96 
97 	bus_state = bus_devctl_to_recep_state(state);
98 	force = ((flags & CFGA_FLAG_FORCE) == CFGA_FLAG_FORCE) ? 1 : 0;
99 	assert(confp->confirm != NULL);
100 
101 	switch (state_change_cmd) {
102 	case CFGA_CMD_DISCONNECT:	/* quiesce bus */
103 		/*
104 		 * If force flag not specified, check if controller is
105 		 * critical.
106 		 */
107 		if (!force) {
108 			/*
109 			 * This check is not foolproof, get user confirmation
110 			 * if test passes.
111 			 */
112 			if (critical_ctrlr(apidp->path)) {
113 				cfga_err(errstring, 0, ERR_CTRLR_CRIT, 0);
114 				ret = SCFGA_ERR;
115 				break;
116 			} else if (!disconnect(confp)) {
117 				ret = SCFGA_NACK;
118 				break;
119 			}
120 		}
121 
122 		cmd = SCFGA_BUS_QUIESCE;
123 		errid = ERR_BUS_QUIESCE;
124 		prereq = CFGA_STAT_CONNECTED;
125 
126 		goto common;
127 
128 	case CFGA_CMD_CONNECT:		/* unquiesce bus */
129 		cmd = SCFGA_BUS_UNQUIESCE;
130 		errid = ERR_BUS_UNQUIESCE;
131 		prereq = CFGA_STAT_DISCONNECTED;
132 
133 		goto common;
134 
135 	case CFGA_CMD_CONFIGURE:
136 		cmd = SCFGA_BUS_CONFIGURE;
137 		errid = ERR_BUS_CONFIGURE;
138 		prereq = CFGA_STAT_CONNECTED;
139 
140 		goto common;
141 
142 	case CFGA_CMD_UNCONFIGURE:
143 		cmd = SCFGA_BUS_UNCONFIGURE;
144 		errid = ERR_BUS_UNCONFIGURE;
145 		prereq = CFGA_STAT_CONNECTED;
146 
147 		/* FALLTHROUGH */
148 	common:
149 		if (bus_state != prereq) {
150 			cfga_err(errstring, 0,
151 			    (prereq == CFGA_STAT_CONNECTED)
152 			    ? ERR_BUS_NOTCONNECTED
153 			    : ERR_BUS_CONNECTED, 0);
154 			ret = SCFGA_ERR;
155 			break;
156 		}
157 
158 		/*
159 		 * When quiescing or unconfiguring a bus, first suspend or
160 		 * offline it through RCM.
161 		 * For unquiescing, we simple build the dev_list for
162 		 * resume notification.
163 		 */
164 		if (((apidp->flags & FLAG_DISABLE_RCM) == 0) &&
165 		    ((cmd == SCFGA_BUS_QUIESCE) ||
166 		    (cmd == SCFGA_BUS_UNQUIESCE) ||
167 		    (cmd == SCFGA_BUS_UNCONFIGURE))) {
168 			ret = get_hba_children(apidp->path, NULL, &dev_list);
169 			if (ret != SCFGA_OK) {
170 				break;
171 			}
172 			if (cmd == SCFGA_BUS_QUIESCE) {
173 				if ((ret = scsi_rcm_suspend(dev_list,
174 				    errstring, flags, 1)) != SCFGA_OK) {
175 					break;
176 				}
177 			} else if (cmd == SCFGA_BUS_UNCONFIGURE) {
178 				if ((ret = scsi_rcm_offline(dev_list,
179 				    errstring, flags)) != SCFGA_OK) {
180 					break;
181 				}
182 			}
183 		}
184 
185 		ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
186 		if (ret != SCFGA_OK) {
187 			/*
188 			 * EIO when child devices are busy may confuse user.
189 			 * So explain it.
190 			 */
191 			if (cmd == SCFGA_BUS_UNCONFIGURE && l_errno == EIO)
192 				errid = ERR_MAYBE_BUSY;
193 
194 			cfga_err(errstring, l_errno, errid, 0);
195 
196 			/*
197 			 * If the bus was suspended in RCM, then cancel the RCM
198 			 * operation.  Discard RCM failures here because the
199 			 * devctl's failure is what is most relevant.
200 			 */
201 			if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
202 				if (cmd == SCFGA_BUS_QUIESCE)
203 					(void) scsi_rcm_resume(dev_list,
204 					    errstring,
205 					    (flags & (~CFGA_FLAG_FORCE)), 1);
206 				else if (cmd == SCFGA_BUS_UNCONFIGURE) {
207 					(void) devctl_cmd(apidp->path,
208 					    SCFGA_BUS_CONFIGURE, NULL,
209 					    &l_errno);
210 					(void) scsi_rcm_online(dev_list,
211 					    errstring,
212 					    (flags & (~CFGA_FLAG_FORCE)));
213 				}
214 			}
215 
216 			break;
217 		}
218 
219 		/*
220 		 * When unquiescing or configuring a bus, resume or online it
221 		 * in RCM when the devctl command is complete.
222 		 * When unconfiguring a bus, notify removal of devices.
223 		 */
224 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
225 			if (cmd == SCFGA_BUS_UNQUIESCE) {
226 				ret = scsi_rcm_resume(dev_list, errstring,
227 				    (flags & (~CFGA_FLAG_FORCE)), 1);
228 			} else if (cmd == SCFGA_BUS_UNCONFIGURE) {
229 				ret = scsi_rcm_remove(dev_list, errstring,
230 				    (flags & (~CFGA_FLAG_FORCE)));
231 			}
232 		}
233 		break;
234 
235 	case CFGA_CMD_LOAD:
236 	case CFGA_CMD_UNLOAD:
237 		ret = SCFGA_OPNOTSUPP;
238 		break;
239 
240 	default:
241 		cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
242 		ret = SCFGA_ERR;
243 		break;
244 	}
245 
246 	free_dev_list(dev_list);
247 	return (ret);
248 }
249 
250 scfga_ret_t
251 dev_change_state(
252 	cfga_cmd_t state_change_cmd,
253 	apid_t *apidp,
254 	cfga_flags_t flags,
255 	char **errstring)
256 {
257 	uint_t state = 0;
258 	int l_errno = 0;
259 	cfga_stat_t bus_state;
260 	scfga_cmd_t cmd;
261 	msgid_t errid;
262 	scfga_ret_t ret;
263 	char *dev_list[2] = {NULL};
264 
265 	assert(apidp->path != NULL);
266 	assert(apidp->hba_phys != NULL);
267 
268 	/*
269 	 * For a device, dynamic component must be present
270 	 */
271 	if (apidp->dyncomp == NULL) {
272 		cfga_err(errstring, 0, ERR_APID_INVAL, 0);
273 		return (SCFGA_ERR);
274 	}
275 
276 	/* Get bus state */
277 	if (devctl_cmd(apidp->hba_phys, SCFGA_BUS_GETSTATE, &state,
278 	    &l_errno) != SCFGA_OK) {
279 		cfga_err(errstring, l_errno, ERR_BUS_GETSTATE, 0);
280 		return (SCFGA_ERR);
281 	}
282 
283 	bus_state = bus_devctl_to_recep_state(state);
284 
285 	switch (state_change_cmd) {
286 	case CFGA_CMD_CONFIGURE:		/* online device */
287 		cmd = SCFGA_DEV_CONFIGURE;
288 		errid = ERR_DEV_CONFIGURE;
289 		goto common;
290 
291 	case CFGA_CMD_UNCONFIGURE:		/* offline device */
292 		cmd = SCFGA_DEV_UNCONFIGURE;
293 		errid = ERR_DEV_UNCONFIGURE;
294 		/* FALLTHROUGH */
295 	common:
296 		if (bus_state != CFGA_STAT_CONNECTED) {
297 			cfga_err(errstring, 0, ERR_BUS_NOTCONNECTED, 0);
298 			ret = SCFGA_ERR;
299 			break;
300 		}
301 
302 		/* When unconfiguring a device, first offline it through RCM. */
303 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
304 			if (cmd == SCFGA_DEV_UNCONFIGURE) {
305 				dev_list[0] = get_node_path(apidp->path);
306 				if (dev_list[0] == NULL) {
307 					ret = SCFGA_ERR;
308 					break;
309 				}
310 				if ((ret = scsi_rcm_offline(dev_list,
311 				    errstring, flags)) != SCFGA_OK) {
312 					break;
313 				}
314 			}
315 		}
316 
317 		ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
318 		if (ret != SCFGA_OK) {
319 			cfga_err(errstring, l_errno, errid, 0);
320 
321 			/*
322 			 * If an unconfigure fails, cancel the RCM offline.
323 			 * Discard any RCM failures so that the devctl
324 			 * failure will still be reported.
325 			 */
326 			if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
327 				if (cmd == SCFGA_DEV_UNCONFIGURE)
328 					(void) scsi_rcm_online(dev_list,
329 					    errstring, flags);
330 			}
331 			break;
332 		}
333 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
334 			/*
335 			 * Unconfigure succeeded, call the RCM notify_remove.
336 			 */
337 			if (cmd == SCFGA_DEV_UNCONFIGURE)
338 				(void) scsi_rcm_remove(dev_list,
339 					    errstring, flags);
340 		}
341 		break;
342 
343 	/*
344 	 * Cannot disconnect/connect individual devices without affecting
345 	 * other devices on the bus. So we don't support these ops.
346 	 */
347 	case CFGA_CMD_DISCONNECT:
348 	case CFGA_CMD_CONNECT:
349 		cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
350 		ret = SCFGA_ERR;
351 		break;
352 	case CFGA_CMD_LOAD:
353 	case CFGA_CMD_UNLOAD:
354 		ret = SCFGA_OPNOTSUPP;
355 		break;
356 	default:
357 		cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
358 		ret = SCFGA_ERR;
359 		break;
360 	}
361 
362 	free_dev_list_elements(dev_list);
363 	return (ret);
364 }
365 
366 /*ARGSUSED*/
367 scfga_ret_t
368 dev_remove(
369 	scfga_cmd_t cmd,
370 	apid_t *apidp,
371 	prompt_t *prp,
372 	cfga_flags_t flags,
373 	char **errstring)
374 {
375 	int proceed, l_errno = 0;
376 	scfga_ret_t ret;
377 	int do_quiesce;
378 	char *dev_list[2] = {NULL};
379 
380 	assert(apidp->hba_phys != NULL);
381 	assert(apidp->path != NULL);
382 
383 	/* device operation only */
384 	if (apidp->dyncomp == NULL) {
385 		cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
386 		return (SCFGA_ERR);
387 	}
388 
389 	proceed = 1;
390 	ret = quiesce_confirm(apidp, MSG_RMDEV, prp, &proceed, &do_quiesce,
391 	    &l_errno);
392 	if (ret != SCFGA_OK) {
393 		cfga_err(errstring, l_errno, ERR_DEV_REMOVE, 0);
394 		return (ret);
395 	}
396 
397 	if (!proceed) {
398 		return (SCFGA_NACK);
399 	}
400 
401 	/*
402 	 * Offline the device in RCM
403 	 */
404 	if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
405 		dev_list[0] = get_node_path(apidp->path);
406 		if (dev_list[0] == NULL)
407 			return (SCFGA_ERR);
408 		if ((ret = scsi_rcm_offline(dev_list, errstring, flags))
409 		    != SCFGA_OK) {
410 			free_dev_list_elements(dev_list);
411 			return (ret);
412 		}
413 	}
414 
415 	/*
416 	 * Offline the device
417 	 */
418 	ret = devctl_cmd(apidp->path, SCFGA_DEV_UNCONFIGURE, NULL, &l_errno);
419 	if (ret != SCFGA_OK) {
420 
421 		cfga_err(errstring, l_errno, ERR_DEV_REMOVE, 0);
422 
423 		/*
424 		 * Cancel the RCM offline.  Discard the RCM failures so that
425 		 * the above devctl failure is still reported.
426 		 */
427 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
428 			(void) scsi_rcm_online(dev_list, errstring, flags);
429 		free_dev_list_elements(dev_list);
430 		return (ret);
431 	}
432 
433 	/* Do the physical removal */
434 	ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
435 
436 	if (ret == SCFGA_OK) {
437 		/*
438 		 * Complete the remove.
439 		 * Since the device is already offlined, remove shouldn't
440 		 * fail. Even if remove fails, there is no side effect.
441 		 */
442 		(void) devctl_cmd(apidp->path, SCFGA_DEV_REMOVE,
443 		    NULL, &l_errno);
444 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
445 			ret = scsi_rcm_remove(dev_list, errstring, flags);
446 	} else {
447 		/*
448 		 * Reconfigure the device and restore the device's RCM state.
449 		 * If reconfigure succeeds, restore the state to online.
450 		 * If reconfigure fails (e.g. a typo from user), we treat
451 		 * the device as removed.
452 		 */
453 		if (devctl_cmd(apidp->path, SCFGA_DEV_CONFIGURE, NULL, &l_errno)
454 		    == SCFGA_OK) {
455 			if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
456 				(void) scsi_rcm_online(dev_list, errstring,
457 				    flags);
458 		} else {
459 			char *cp = strrchr(apidp->path, ':');
460 			if (cp)
461 				*cp = '\0';
462 			cfga_err(errstring, l_errno, ERR_DEV_RECONFIGURE,
463 			    apidp->path, 0);
464 			if (cp)
465 				*cp = ':';
466 			if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
467 				(void) scsi_rcm_remove(dev_list, errstring,
468 				    flags);
469 		}
470 	}
471 
472 	free_dev_list_elements(dev_list);
473 	return (ret);
474 }
475 
476 /*ARGSUSED*/
477 scfga_ret_t
478 dev_insert(
479 	scfga_cmd_t cmd,
480 	apid_t *apidp,
481 	prompt_t *prp,
482 	cfga_flags_t flags,
483 	char **errstring)
484 {
485 	int proceed, l_errno = 0;
486 	scfga_ret_t ret;
487 	int do_quiesce;
488 
489 	assert(apidp->hba_phys != NULL);
490 	assert(apidp->path != NULL);
491 
492 	/* Currently, insert operation only allowed for bus */
493 	if (apidp->dyncomp != NULL) {
494 		cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
495 		return (SCFGA_ERR);
496 	}
497 
498 	proceed = 1;
499 	ret = quiesce_confirm(apidp, MSG_INSDEV, prp, &proceed, &do_quiesce,
500 	    &l_errno);
501 	if (ret != SCFGA_OK) {
502 		cfga_err(errstring, l_errno, ERR_DEV_INSERT, 0);
503 		return (ret);
504 	}
505 
506 	if (!proceed) {
507 		return (SCFGA_NACK);
508 	}
509 
510 	/* Do the physical addition */
511 	ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
512 	if (ret != SCFGA_OK) {
513 		return (ret);
514 	}
515 
516 	/*
517 	 * Configure bus to online new device(s).
518 	 * Previously offlined devices will not be onlined.
519 	 */
520 	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_CONFIGURE, NULL, &l_errno);
521 	if (ret != SCFGA_OK) {
522 		cfga_err(errstring, l_errno, ERR_DEV_INSERT, 0);
523 		return (SCFGA_ERR);
524 	}
525 
526 	return (SCFGA_OK);
527 }
528 
529 /*ARGSUSED*/
530 scfga_ret_t
531 dev_replace(
532 	scfga_cmd_t cmd,
533 	apid_t *apidp,
534 	prompt_t *prp,
535 	cfga_flags_t flags,
536 	char **errstring)
537 {
538 	int proceed, l_errno = 0;
539 	scfga_ret_t ret, ret2;
540 	int do_quiesce;
541 	char *dev_list[2] = {NULL};
542 
543 	assert(apidp->hba_phys != NULL);
544 	assert(apidp->path != NULL);
545 
546 	/* device operation only */
547 	if (apidp->dyncomp == NULL) {
548 		cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
549 		return (SCFGA_ERR);
550 	}
551 
552 	proceed = 1;
553 	ret = quiesce_confirm(apidp, MSG_REPLDEV, prp, &proceed, &do_quiesce,
554 	    &l_errno);
555 	if (ret != SCFGA_OK) {
556 		cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
557 		return (ret);
558 	}
559 
560 	if (!proceed) {
561 		return (SCFGA_NACK);
562 	}
563 
564 	/* Offline the device in RCM */
565 	if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
566 		dev_list[0] = get_node_path(apidp->path);
567 		if (dev_list[0] == NULL)
568 			return (SCFGA_ERR);
569 		if ((ret = scsi_rcm_offline(dev_list, errstring, flags))
570 		    != SCFGA_OK) {
571 			free_dev_list_elements(dev_list);
572 			return (ret);
573 		}
574 	}
575 
576 	ret = devctl_cmd(apidp->path, SCFGA_DEV_REMOVE, NULL, &l_errno);
577 	if (ret != SCFGA_OK) {
578 
579 		/*
580 		 * Cancel the RCM offline.  Discard any RCM failures so that
581 		 * the devctl failure can still be reported.
582 		 */
583 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0)
584 			(void) scsi_rcm_online(dev_list, errstring, flags);
585 
586 		cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
587 		free_dev_list_elements(dev_list);
588 		return (ret);
589 	}
590 
591 	/* do the physical replace */
592 	ret = dev_hotplug(apidp, prp, flags, do_quiesce, errstring);
593 
594 	/* Online the replacement, or restore state on error */
595 	ret2 = devctl_cmd(apidp->path, SCFGA_DEV_CONFIGURE, NULL, &l_errno);
596 
597 	if (ret2 != SCFGA_OK) {
598 		cfga_err(errstring, l_errno, ERR_DEV_REPLACE, 0);
599 	}
600 
601 	/*
602 	 * Remove the replaced device in RCM, or online the device in RCM
603 	 * to recover.
604 	 */
605 	if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
606 		if (ret == SCFGA_OK)
607 			ret = scsi_rcm_remove(dev_list, errstring, flags);
608 		else if (ret2 == SCFGA_OK)
609 			ret2 = scsi_rcm_online(dev_list, errstring, flags);
610 	}
611 	free_dev_list_elements(dev_list);
612 
613 	return (ret == SCFGA_OK ? ret2 : ret);
614 }
615 
616 /*ARGSUSED*/
617 scfga_ret_t
618 reset_common(
619 	scfga_cmd_t cmd,
620 	apid_t *apidp,
621 	prompt_t *prp,
622 	cfga_flags_t flags,
623 	char **errstring)
624 {
625 	int l_errno = 0;
626 	scfga_ret_t ret;
627 
628 
629 	assert(apidp->path != NULL);
630 	assert(apidp->hba_phys != NULL);
631 
632 	switch (cmd) {
633 	case SCFGA_RESET_DEV:
634 		if (apidp->dyncomp == NULL) {
635 			cfga_err(errstring, 0, ERR_NOT_BUSOP, 0);
636 			return (SCFGA_ERR);
637 		}
638 		break;
639 
640 	case SCFGA_RESET_BUS:
641 	case SCFGA_RESET_ALL:
642 		if (apidp->dyncomp != NULL) {
643 			cfga_err(errstring, 0, ERR_NOT_DEVOP, 0);
644 			return (SCFGA_ERR);
645 		}
646 		break;
647 	default:
648 		cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
649 		return (SCFGA_ERR);
650 	}
651 
652 	ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
653 	if (ret != SCFGA_OK) {
654 		cfga_err(errstring, l_errno, ERR_RESET, 0);
655 	}
656 
657 	return (ret);
658 }
659 
660 static int
661 disconnect(struct cfga_confirm *confp)
662 {
663 	int ans, append_newline;
664 	char *cq;
665 
666 	append_newline = 0;
667 	cq = cfga_str(append_newline, WARN_DISCONNECT, 0);
668 
669 	ans = confp->confirm(confp->appdata_ptr, cq);
670 
671 	S_FREE(cq);
672 
673 	return (ans == 1);
674 }
675 
676 /*
677  * Check for "scsi-no-quiesce" property
678  * Return code: -1 error, 0 quiesce not required, 1 quiesce required
679  */
680 static int
681 quiesce_required(apid_t *apidp, int *l_errnop)
682 {
683 	di_node_t bus_node, dev_node;
684 	char *bus_path, *bus_end;
685 	char *dev_path, *dev_end;
686 	int *propval;
687 
688 	/* take libdevinfo snapshot of subtree at hba */
689 	bus_path = apidp->hba_phys + strlen(DEVICES_DIR);
690 	bus_end = strrchr(bus_path, ':');
691 	if (bus_end)
692 		*bus_end = '\0';
693 
694 	bus_node = di_init(bus_path, DINFOSUBTREE|DINFOPROP);
695 	if (bus_end)
696 		*bus_end = ':';
697 	if (bus_node == DI_NODE_NIL) {
698 		*l_errnop = errno;
699 		return (-1);	/* error */
700 	}
701 
702 	/* check bus node for property */
703 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, bus_node, SCSI_NO_QUIESCE,
704 	    &propval) == 1) {
705 		di_fini(bus_node);
706 		return (0);	/* quiesce not required */
707 	}
708 
709 	/* if this ap is HBA, return with quiesce required */
710 	if (apidp->dyncomp == NULL) {
711 		di_fini(bus_node);
712 		return (1);
713 	}
714 
715 	/* check device node for property */
716 	dev_path = apidp->path + strlen(DEVICES_DIR);
717 	dev_end = strrchr(dev_path, ':');
718 	if (dev_end)
719 		*dev_end = '\0';
720 
721 	dev_node = di_child_node(bus_node);
722 	while (dev_node != DI_NODE_NIL) {
723 		char *child_path;
724 		child_path = di_devfs_path(dev_node);
725 		if (strcmp(child_path, dev_path) == 0) {
726 			di_devfs_path_free(child_path);
727 			break;
728 		}
729 		di_devfs_path_free(child_path);
730 		dev_node = di_sibling_node(dev_node);
731 	}
732 
733 	if (dev_end)
734 		*dev_end = ':';
735 	if (dev_node == DI_NODE_NIL) {
736 		di_fini(bus_node);
737 		return (1);	/* dev not found (insert case) */
738 	}
739 
740 	/* check child node for property */
741 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, dev_node, "scsi-no-quiesce",
742 	    &propval) == 1) {
743 		di_fini(bus_node);
744 		return (0);	/* quiesce not required */
745 	}
746 	return (1);	/* quiesce required */
747 }
748 
749 static scfga_ret_t
750 quiesce_confirm(
751 	apid_t *apidp,
752 	msgid_t cmd_msg,
753 	prompt_t *prp,
754 	int *okp,
755 	int *quiesce,
756 	int *l_errnop)
757 {
758 	char *buf = NULL, *hbap = NULL, *cq1 = NULL, *cq2 = NULL;
759 	char *cp;
760 	size_t len = 0;
761 	int i = 0, append_newline;
762 	scfga_ret_t ret;
763 
764 	assert(apidp->path != NULL);
765 	assert(apidp->hba_phys != NULL);
766 
767 	*quiesce = quiesce_required(apidp, l_errnop);
768 	if (*quiesce == -1)
769 		return (SCFGA_ERR);
770 	else if (*quiesce == 0)
771 		return (SCFGA_OK);
772 
773 	/*
774 	 * Try to create HBA logical ap_id.
775 	 * If that fails use physical path
776 	 */
777 	ret = make_hba_logid(apidp->hba_phys, &hbap, &i);
778 	if (ret != SCFGA_OK) {
779 		if ((hbap = get_node_path(apidp->hba_phys)) == NULL) {
780 			*l_errnop = errno;
781 			return (SCFGA_LIB_ERR);
782 		}
783 	}
784 
785 	assert(hbap != NULL);
786 
787 	append_newline = 0;
788 	cq1 = cfga_str(append_newline, CONF_QUIESCE_1, hbap, 0);
789 	cq2 = cfga_str(append_newline, CONF_QUIESCE_2, 0);
790 	len = strlen(cq1) + strlen(cq2) + 1; /* Includes term. NULL */
791 
792 	if ((buf = calloc(1, len)) == NULL) {
793 		*l_errnop = errno;
794 		ret = SCFGA_LIB_ERR;
795 		S_FREE(cq1);
796 		S_FREE(cq2);
797 		goto out;
798 	}
799 	(void) strcpy(buf, cq1);
800 	(void) strcat(buf, cq2);
801 
802 	S_FREE(cq1);
803 	S_FREE(cq2);
804 
805 
806 	/* Remove minor name (if any) from phys path */
807 	if ((cp = strrchr(apidp->path, ':')) != NULL) {
808 		*cp = '\0';
809 	}
810 
811 	/* describe operation being attempted */
812 	cfga_msg(prp->msgp, cmd_msg, apidp->path, 0);
813 
814 	/* Restore minor name */
815 	if (cp != NULL) {
816 		*cp = ':';
817 	}
818 
819 	/* request permission to quiesce */
820 	assert(prp->confp != NULL && prp->confp->confirm != NULL);
821 	*okp = prp->confp->confirm(prp->confp->appdata_ptr, buf);
822 
823 	ret = SCFGA_OK;
824 	/*FALLTHRU*/
825 out:
826 	S_FREE(buf);
827 	S_FREE(hbap);
828 	return (ret);
829 }
830 
831 static scfga_ret_t
832 suspend_in_rcm(
833 	apid_t		*apidp,
834 	char		***suspend_list_ptr,
835 	char		**errstring,
836 	cfga_flags_t	flags)
837 {
838 	scfga_ret_t	ret;
839 	char		*bus_path = NULL;
840 	char		*dev_path = NULL;
841 	char		**suspend_list = NULL;
842 
843 	*suspend_list_ptr = NULL;
844 
845 	/* Suspend the bus through RCM */
846 	if (apidp->flags & FLAG_DISABLE_RCM)
847 		return (SCFGA_OK);
848 
849 	/* The bus_path is the HBA path without its minor */
850 	if ((bus_path = get_node_path(apidp->hba_phys)) == NULL)
851 		return (SCFGA_ERR);
852 
853 	/*
854 	 * The dev_path is already initialized to NULL.  If the AP Id
855 	 * path differs from the HBA path, then the dev_path should
856 	 * instead be set to the AP Id path without its minor.
857 	 */
858 	if (strcmp(apidp->hba_phys, apidp->path) != 0) {
859 		if ((dev_path = get_node_path(apidp->path)) == NULL) {
860 			ret = SCFGA_ERR;
861 			goto out;
862 		}
863 	}
864 
865 	if ((ret = get_hba_children(bus_path, dev_path, &suspend_list))
866 	    != SCFGA_OK) {
867 		free_dev_list(suspend_list);
868 		goto out;
869 	}
870 
871 	if (scsi_rcm_suspend(suspend_list, errstring, flags, 0) != SCFGA_OK) {
872 		ret = SCFGA_ERR;
873 		free_dev_list(suspend_list);
874 	} else {
875 		ret = SCFGA_OK;
876 		*suspend_list_ptr = suspend_list;
877 	}
878 	/*FALLTHROUGH*/
879 out:
880 	S_FREE(bus_path);
881 	S_FREE(dev_path);
882 	return (ret);
883 }
884 
885 /*
886  * Resume the bus through RCM if it successfully
887  * unquiesced.
888  */
889 static void
890 resume_in_rcm(
891 	apid_t		*apidp,
892 	char		**suspend_list,
893 	char		**errstring,
894 	cfga_flags_t	flags)
895 {
896 	if (apidp->flags & FLAG_DISABLE_RCM)
897 		return;
898 
899 	(void) scsi_rcm_resume(suspend_list, errstring, flags, 0);
900 
901 	free_dev_list(suspend_list);
902 }
903 
904 static scfga_ret_t
905 wait_for_hotplug(prompt_t *pt, int msg)
906 {
907 	char		*cu = NULL;
908 	int		append_newline = 0;
909 	scfga_ret_t	ret;
910 
911 	cu = cfga_str(append_newline, msg, 0);
912 	if (pt->confp->confirm(pt->confp->appdata_ptr, cu) != 1) {
913 		ret = SCFGA_NACK;
914 	} else {
915 		ret = SCFGA_OK;
916 	}
917 	S_FREE(cu);
918 	return (ret);
919 }
920 
921 static scfga_ret_t
922 bus_quiesce(apid_t *apidp, prompt_t *pt, char **errstring, cfga_flags_t flags)
923 {
924 	int		l_errno;
925 	scfga_ret_t	ret;
926 	scfga_ret_t	hpret;
927 	char		**suspend_list = NULL;
928 
929 	ret = suspend_in_rcm(apidp, &suspend_list, errstring, flags);
930 	if (ret != SCFGA_OK) {
931 		return (ret);
932 	}
933 
934 	/*
935 	 * If the quiesce fails, then cancel the RCM suspend.
936 	 * Discard any RCM failures so that the devctl failure
937 	 * can still be reported.
938 	 */
939 	l_errno = 0;
940 	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_QUIESCE, NULL, &l_errno);
941 	if (ret != SCFGA_OK) {
942 		resume_in_rcm(apidp, suspend_list, errstring, flags);
943 		cfga_err(errstring, l_errno, ERR_BUS_QUIESCE, 0);
944 		return (ret);
945 	}
946 
947 	/*
948 	 * Prompt user to proceed with physical hotplug
949 	 * and wait until they are done.
950 	 */
951 	hpret = wait_for_hotplug(pt, CONF_UNQUIESCE);
952 
953 	/*
954 	 * The unquiesce may fail with EALREADY (which is ok)
955 	 * or some other error (which is not ok).
956 	 */
957 	l_errno = 0;
958 	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_UNQUIESCE, NULL, &l_errno);
959 	if (ret != SCFGA_OK && l_errno != EALREADY) {
960 		free_dev_list(suspend_list);
961 		cfga_err(errstring, l_errno, ERR_BUS_UNQUIESCE, 0);
962 		return (SCFGA_ERR);
963 	}
964 
965 	resume_in_rcm(apidp, suspend_list, errstring, flags);
966 
967 	return (hpret);
968 }
969 
970 #define	MAX_LOCK_TRIES		20
971 #define	MAX_UNLINK_TRIES	60
972 #define	s_getpid		(int)getpid	/* else lint causes problems */
973 
974 static void
975 s_unlink(char *file)
976 {
977 	int	count = 0;
978 
979 retry:
980 	if (unlink(file) == -1) {
981 		if (errno != EINTR && errno != EAGAIN) {
982 			CFGA_TRACE1((stdout, "s_unlink[%d]: unlink failed: "
983 			    "%s: %s\n", s_getpid(), file, strerror(errno)));
984 			return;
985 		}
986 
987 		if (++count < MAX_UNLINK_TRIES) {
988 			(void) sleep(1);
989 			goto retry;
990 		}
991 		CFGA_TRACE1((stdout, "s_unlink[%d]: retry limit: %s\n",
992 		    s_getpid(), file));
993 	} else {
994 		CFGA_TRACE3((stdout, "s_unlink[%d]: unlinked: %s\n",
995 		    s_getpid(), file));
996 	}
997 }
998 
999 static scfga_ret_t
1000 create_lock(int *fdp, struct cfga_msg *msgp, char **errstring)
1001 {
1002 	FILE			*fp;
1003 	int			count;
1004 	struct extmnttab	ent;
1005 	int			mnted;
1006 
1007 
1008 	*fdp = -1;
1009 
1010 	/*
1011 	 * Check that /var/run is mounted. In the unlikely event
1012 	 * that the lock file is left behind, we want it
1013 	 * cleared on the next reboot.
1014 	 */
1015 	errno = 0;
1016 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
1017 		cfga_err(errstring, errno, ERRARG_OPEN, MNTTAB, 0);
1018 		return (SCFGA_LIB_ERR);
1019 	}
1020 
1021 	resetmnttab(fp);
1022 
1023 	mnted = 0;
1024 	while (getextmntent(fp, &ent, sizeof (ent)) == 0) {
1025 		if (strcmp(ent.mnt_mountp, "/var/run") == 0) {
1026 			mnted = 1;
1027 			break;
1028 		}
1029 	}
1030 
1031 	(void) fclose(fp);
1032 
1033 	if (!mnted) {
1034 		cfga_err(errstring, 0, ERR_VAR_RUN, 0);
1035 		return (SCFGA_LIB_ERR);
1036 	}
1037 
1038 	/*
1039 	 * Wait for a short period of time if we cannot O_EXCL create
1040 	 * lock file. If some other cfgadm process is finishing up, we
1041 	 * can get in. If the wait required is long however, just
1042 	 * return SYSTEM_BUSY to the user - a hotplug operation is
1043 	 * probably in progress.
1044 	 */
1045 	count = 0;
1046 retry:
1047 	*fdp = open(SCFGA_LOCK, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
1048 	if (*fdp == -1 && errno == EEXIST) {
1049 		if (++count < MAX_LOCK_TRIES) {
1050 			if (count == 1)
1051 				cfga_msg(msgp, MSG_WAIT_LOCK, 0);
1052 			(void) sleep(1);
1053 			goto retry;
1054 		}
1055 	}
1056 
1057 	if (*fdp == -1 && errno == EEXIST) {
1058 		cfga_err(errstring, 0, ERRARG_QUIESCE_LOCK, SCFGA_LOCK, 0);
1059 		return (SCFGA_SYSTEM_BUSY);
1060 	} else if (*fdp == -1) {
1061 		cfga_err(errstring, errno, ERRARG_QUIESCE_LOCK, SCFGA_LOCK, 0);
1062 		return (SCFGA_LIB_ERR);
1063 	}
1064 
1065 	CFGA_TRACE3((stdout, "create_lock[%d]: created lockfile: %s\n",
1066 	    s_getpid(), SCFGA_LOCK));
1067 
1068 	return (SCFGA_OK);
1069 }
1070 
1071 static scfga_ret_t
1072 syslock(int fd, char **errstring)
1073 {
1074 	struct flock	lock;
1075 	int		count;
1076 	int		rval;
1077 
1078 	assert(fd != -1);
1079 
1080 	CFGA_TRACE3((stdout, "syslock[%d]: trying lock: %s\n",
1081 	    s_getpid(), SCFGA_LOCK));
1082 
1083 	lock.l_type = F_WRLCK;
1084 	lock.l_whence = SEEK_SET;
1085 	lock.l_start = 0;
1086 	lock.l_len = 0;
1087 
1088 	count = 0;
1089 	while ((rval = fcntl(fd, F_SETLKW, &lock)) == -1 && errno == EINTR) {
1090 		if (++count >= MAX_LOCK_TRIES) {
1091 			CFGA_TRACE1((stdout, "syslock[%d]: retry limit: %s\n",
1092 			    s_getpid(), SCFGA_LOCK));
1093 			goto badlock;
1094 		}
1095 		(void) sleep(1);
1096 	}
1097 
1098 	if (rval != -1) {
1099 		CFGA_TRACE3((stdout, "syslock[%d]: locked file: %s\n",
1100 		    s_getpid(), SCFGA_LOCK));
1101 		return (SCFGA_OK);
1102 	}
1103 
1104 	/*FALLTHROUGH*/
1105 badlock:
1106 	cfga_err(errstring, errno, ERRARG_LOCK, SCFGA_LOCK, 0);
1107 	/* trace message to display pid */
1108 	CFGA_TRACE1((stdout, "syslock[%d]: cannot lock %s\n",
1109 	    s_getpid(), SCFGA_LOCK));
1110 	return (SCFGA_LIB_ERR);
1111 }
1112 
1113 static void
1114 wait_for_child(pid_t cpid)
1115 {
1116 	int	status;
1117 	pid_t	rval;
1118 
1119 	CFGA_TRACE2((stdout, "wait_for_child[%d]: child[%d]\n",
1120 	    s_getpid(), (int)cpid));
1121 
1122 	for (;;) {
1123 		while ((rval = waitpid(cpid, &status, 0)) != cpid) {
1124 			if (errno == ECHILD) {
1125 				CFGA_TRACE1((stdout, "waitpid[%d]: child[%d] "
1126 				    "doesn't exist\n", s_getpid(), (int)cpid));
1127 				return;
1128 			}
1129 
1130 			CFGA_TRACE3((stdout, "waitpid[%d]: returned: %d"
1131 			    ": errno: %s\n", s_getpid(), (int)rval,
1132 			    strerror(errno)));
1133 		}
1134 
1135 		if (WIFEXITED(status)) {
1136 			CFGA_TRACE2((stdout, "waitpid[%d]: child[%d]: "
1137 			    "normal exit\n", s_getpid(), (int)cpid));
1138 			return;
1139 		}
1140 
1141 		if (WIFSIGNALED(status)) {
1142 			CFGA_TRACE2((stdout, "waitpid[%d]: child[%d]: "
1143 			    "signal exit\n", s_getpid(), (int)cpid));
1144 			return;
1145 		}
1146 
1147 		/*
1148 		 * The child has not terminated. We received status
1149 		 * because the child was either stopped or continued.
1150 		 * Wait for child termination by calling waitpid() again.
1151 		 */
1152 	}
1153 }
1154 
1155 static void
1156 wait_and_cleanup(int fd, apid_t *apidp)
1157 {
1158 	int		l_errno;
1159 	scfga_ret_t	ret;
1160 
1161 	/* This is the child */
1162 	CFGA_TRACE2((stdout, "child[%d]: Entering wait_cleanup\n", s_getpid()));
1163 
1164 	if (syslock(fd, NULL) != SCFGA_OK) {
1165 		CFGA_TRACE1((stdout, "child[%d]: lock failure "
1166 		    " - _exit(1)\n", s_getpid()));
1167 		/*
1168 		 * As a last resort, unlink the lock file. This is relatively
1169 		 * safe as the child doesn't unquiesce the bus in this case.
1170 		 */
1171 		s_unlink(SCFGA_LOCK);
1172 		_exit(1);
1173 	}
1174 
1175 	l_errno = 0;
1176 	ret = devctl_cmd(apidp->hba_phys, SCFGA_BUS_UNQUIESCE, NULL, &l_errno);
1177 	if (ret != SCFGA_OK) {
1178 		if (l_errno == EALREADY)
1179 			CFGA_TRACE3((stdout, "child[%d]: bus already "
1180 			    "unquiesced: %s\n", s_getpid(), apidp->hba_phys));
1181 		else
1182 			CFGA_TRACE1((stdout, "child[%d]: unquiesce failed: "
1183 			    "%s\n", s_getpid(), strerror(l_errno)));
1184 	} else {
1185 		CFGA_TRACE1((stdout, "child[%d]: unquiesced bus: %s\n",
1186 		    s_getpid(), apidp->hba_phys));
1187 	}
1188 
1189 	s_unlink(SCFGA_LOCK);
1190 
1191 	CFGA_TRACE2((stdout, "child[%d]: _exit(0)\n", s_getpid()));
1192 
1193 	_exit(0);
1194 }
1195 
1196 static void
1197 sigblk(sigset_t *osp)
1198 {
1199 	sigset_t set;
1200 
1201 	(void) sigemptyset(&set);
1202 	(void) sigemptyset(osp);
1203 	(void) sigaddset(&set, SIGHUP);
1204 	(void) sigaddset(&set, SIGINT);
1205 	(void) sigaddset(&set, SIGQUIT);
1206 	(void) sigaddset(&set, SIGTERM);
1207 	(void) sigaddset(&set, SIGUSR1);
1208 	(void) sigaddset(&set, SIGUSR2);
1209 	(void) sigprocmask(SIG_BLOCK, &set, osp);
1210 }
1211 
1212 static void
1213 sigunblk(sigset_t *osp)
1214 {
1215 	(void) sigprocmask(SIG_SETMASK, osp, NULL);
1216 }
1217 
1218 /*
1219  * Here is the algorithm used to ensure that a SCSI bus is not
1220  * left in the quiesced state:
1221  *
1222  *	lock quiesce mutex	// single threads this code
1223  *	open(O_CREAT|O_EXCL) lock file	// only 1 process at a time
1224  *	exclusive record lock on lock file
1225  *	fork1()
1226  *	quiesce bus
1227  *	do the physical hotplug operation
1228  *	unquiesce bus
1229  *	unlock record lock
1230  *		-> *child*
1231  *		-> wait for record lock
1232  *		-> unconditionally unquiesce bus
1233  *		-> unlink lock file
1234  *		-> exit
1235  *	wait for child to exit
1236  *	unlock quiesce mutex
1237  *
1238  * NOTE1: since record locks are per-process and a close() can
1239  * release a lock, to keep things MT-safe we need a quiesce mutex.
1240  *
1241  * NOTE2: To ensure that the child does not unquiesce a bus quiesced
1242  * by an unrelated cfgadm_scsi operation, exactly 1 process in the
1243  * system can be doing an implicit quiesce operation  The exclusive
1244  * creation of the lock file guarantees this.
1245  *
1246  * NOTE3: This works even if the parent process dumps core and/or is
1247  * abnormally terminated. If the parent dies before the child is
1248  * forked, the bus is not quiesced. If the parent dies after the
1249  * bus is quiesced, the child process will ensure that the bus is
1250  * unquiesced.
1251  */
1252 static scfga_ret_t
1253 dev_hotplug(
1254 	apid_t *apidp,
1255 	prompt_t *pt,
1256 	cfga_flags_t flags,
1257 	int do_quiesce,
1258 	char **errstring)
1259 {
1260 	scfga_ret_t	ret;
1261 	pid_t		cpid;
1262 	int		fd;
1263 	sigset_t	oset;
1264 
1265 	assert(apidp->hba_phys != NULL);
1266 	assert(apidp->path != NULL);
1267 
1268 	/* If no quiesce required, prompt the user to do the operation */
1269 	if (!do_quiesce)
1270 		return (wait_for_hotplug(pt, CONF_NO_QUIESCE));
1271 
1272 	(void) mutex_lock(&quiesce_mutex);
1273 
1274 	ret = create_lock(&fd, pt->msgp, errstring);
1275 	if (ret != SCFGA_OK) {
1276 		(void) mutex_unlock(&quiesce_mutex);
1277 		return (ret);
1278 	}
1279 
1280 	ret = syslock(fd, errstring);
1281 	if (ret != SCFGA_OK) {
1282 		goto bad;
1283 	}
1284 
1285 	/*
1286 	 * block signals in the child. Parent may
1287 	 * exit, causing signal to be sent to child.
1288 	 */
1289 	sigblk(&oset);
1290 
1291 	switch (cpid = fork1()) {
1292 		case 0:
1293 			/* child */
1294 			wait_and_cleanup(fd, apidp);
1295 			_exit(0); /* paranoia */
1296 			/*NOTREACHED*/
1297 		case -1:
1298 			cfga_err(errstring, errno, ERR_FORK, 0);
1299 			sigunblk(&oset);
1300 			ret = SCFGA_LIB_ERR;
1301 			goto bad;
1302 		default:
1303 			/* parent */
1304 			break;
1305 	}
1306 
1307 	sigunblk(&oset);
1308 
1309 	/* We have forked successfully - this is the parent */
1310 	ret = bus_quiesce(apidp, pt, errstring, flags);
1311 
1312 	(void) close(fd);	/* also unlocks */
1313 
1314 	wait_for_child(cpid);
1315 
1316 	(void) mutex_unlock(&quiesce_mutex);
1317 
1318 	return (ret);
1319 bad:
1320 	(void) close(fd);
1321 	s_unlink(SCFGA_LOCK);
1322 	(void) mutex_unlock(&quiesce_mutex);
1323 	return (ret);
1324 }
1325 
1326 /*
1327  * Checks if HBA controls a critical file-system (/, /usr or swap)
1328  * This routine reads /etc/vfstab and is NOT foolproof.
1329  * If an error occurs, assumes that controller is NOT critical.
1330  */
1331 static int
1332 critical_ctrlr(const char *hba_phys)
1333 {
1334 	FILE *fp;
1335 	struct vfstab vfst;
1336 	int vfsret = 1, rv = -1;
1337 	char *bufp;
1338 	const size_t buflen = PATH_MAX;
1339 	char mount[MAXPATHLEN], fstype[MAXPATHLEN], spec[MAXPATHLEN];
1340 
1341 
1342 	if ((bufp = calloc(1, buflen)) == NULL) {
1343 		return (0);
1344 	}
1345 
1346 	fp = NULL;
1347 	if ((fp = fopen(ETC_VFSTAB, "r")) == NULL) {
1348 		rv = 0;
1349 		goto out;
1350 	}
1351 
1352 	while ((vfsret = getvfsent(fp, &vfst)) == 0) {
1353 
1354 		(void) strcpy(mount, S_STR(vfst.vfs_mountp));
1355 		(void) strcpy(fstype, S_STR(vfst.vfs_fstype));
1356 		(void) strcpy(spec, S_STR(vfst.vfs_special));
1357 
1358 		/* Ignore non-critical entries */
1359 		if (strcmp(mount, "/") && strcmp(mount, "/usr") &&
1360 		    strcmp(fstype, "swap")) {
1361 			continue;
1362 		}
1363 
1364 		/* get physical path */
1365 		if (realpath(spec, bufp) == NULL) {
1366 			continue;
1367 		}
1368 
1369 		/* Check if critical partition is on the HBA */
1370 		if (!(rv = hba_dev_cmp(hba_phys, bufp))) {
1371 			break;
1372 		}
1373 	}
1374 
1375 	rv = !vfsret;
1376 
1377 	/*FALLTHRU*/
1378 out:
1379 	S_FREE(bufp);
1380 	if (fp != NULL) fclose(fp);
1381 	return (rv);
1382 }
1383 
1384 /*
1385  * Convert bus state to receptacle state
1386  */
1387 static cfga_stat_t
1388 bus_devctl_to_recep_state(uint_t bus_dc_state)
1389 {
1390 	cfga_stat_t rs;
1391 
1392 	switch (bus_dc_state) {
1393 	case BUS_ACTIVE:
1394 		rs = CFGA_STAT_CONNECTED;
1395 		break;
1396 	case BUS_QUIESCED:
1397 	case BUS_SHUTDOWN:
1398 		rs = CFGA_STAT_DISCONNECTED;
1399 		break;
1400 	default:
1401 		rs = CFGA_STAT_NONE;
1402 		break;
1403 	}
1404 
1405 	return (rs);
1406 }
1407 
1408 static int
1409 add_dev(di_node_t node, void *arg)
1410 {
1411 	int ndevs, len;
1412 	char *path, *p;
1413 	struct larg *largp = (struct larg *)arg;
1414 
1415 	/* ignore hba itself and all detached nodes */
1416 	if (di_parent_node(node) == DI_NODE_NIL ||
1417 	    di_node_state(node) < DS_ATTACHED)
1418 		return (DI_WALK_CONTINUE);
1419 
1420 	if ((path = di_devfs_path(node)) == NULL) {
1421 		largp->ndevs = -1;
1422 		return (DI_WALK_TERMINATE);
1423 	}
1424 
1425 	/* sizeof (DEVICES_DIR) includes the null terminator */
1426 	len = strlen(path) + sizeof (DEVICES_DIR);
1427 	if ((p = malloc(len)) == NULL) {
1428 		di_devfs_path_free(path);
1429 		largp->ndevs = -1;
1430 		return (DI_WALK_TERMINATE);
1431 	}
1432 	(void) snprintf(p, len, "%s%s", DEVICES_DIR, path);
1433 	di_devfs_path_free(path);
1434 
1435 	/* ignore device to be excluded */
1436 	if (largp->dev && strcmp(largp->dev, p) == 0) {
1437 		free(p);
1438 		return (DI_WALK_CONTINUE);
1439 	}
1440 
1441 	/* grow dev_list to allow room for one more device */
1442 	if (alloc_dev_list(largp) != 0) {
1443 		free(p);
1444 		return (DI_WALK_TERMINATE);
1445 	}
1446 	ndevs = largp->ndevs;
1447 	largp->ndevs++;
1448 	largp->dev_list[ndevs] = p;
1449 	largp->dev_list[ndevs + 1] = NULL;
1450 	return (DI_WALK_CONTINUE);
1451 }
1452 
1453 /*
1454  * Get list of children excluding dev_excl (if not null).
1455  */
1456 static int
1457 get_hba_children(char *bus_path, char *dev_excl, char ***dev_listp)
1458 {
1459 	int err, ret;
1460 	walkarg_t u;
1461 	struct larg larg;
1462 
1463 	*dev_listp = NULL;
1464 
1465 	u.node_args.flags = DI_WALK_CLDFIRST;
1466 	u.node_args.fcn = add_dev;
1467 
1468 	larg.ndevs = 0;
1469 	larg.nelem = 0;
1470 	larg.dev = dev_excl;
1471 	larg.dev_list = NULL;
1472 
1473 	ret = walk_tree(bus_path, &larg, DINFOSUBTREE, &u, SCFGA_WALK_NODE,
1474 	    &err);
1475 	if (larg.ndevs == -1) {
1476 		free_dev_list(larg.dev_list);
1477 		return (SCFGA_ERR);
1478 	}
1479 	*dev_listp = larg.dev_list;
1480 	return (ret);
1481 }
1482 
1483 static char *
1484 get_node_path(char *minor_path)
1485 {
1486 	char *path, *cp;
1487 
1488 	if ((path = strdup(minor_path)) == NULL)
1489 		return (NULL);
1490 	if ((cp = strrchr(path, ':')) != NULL)
1491 		*cp = '\0';
1492 	return (path);
1493 }
1494 
1495 /*
1496  * Ensure largp->dev_list has room for one more device.
1497  * Returns 0 on success, -1 on failure.
1498  */
1499 static int
1500 alloc_dev_list(struct larg *largp)
1501 {
1502 	int nelem;
1503 	char **p;
1504 
1505 	if (largp->nelem > largp->ndevs + 2)	/* +1 for NULL termination */
1506 		return (0);
1507 
1508 	nelem =  largp->nelem + 16;
1509 	p = realloc(largp->dev_list, nelem * sizeof (char *));
1510 	if (p == NULL)
1511 		return (-1);
1512 
1513 	largp->dev_list = p;
1514 	largp->nelem = nelem;
1515 	return (0);
1516 }
1517 
1518 static void
1519 free_dev_list_elements(char **dev_list)
1520 {
1521 	while (*dev_list) {
1522 		free(*dev_list);
1523 		dev_list++;
1524 	}
1525 }
1526 
1527 static void
1528 free_dev_list(char **dev_list)
1529 {
1530 	if (dev_list == NULL)
1531 		return;
1532 
1533 	free_dev_list_elements(dev_list);
1534 	free(dev_list);
1535 }
1536