xref: /illumos-gate/usr/src/lib/cfgadm_plugins/scsi/common/cfga_utils.c (revision a5f69788de7ac07553de47f7fec8c05a9a94c105)
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 2004 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 /*
32  * This file contains helper routines for the SCSI plugin
33  */
34 
35 #if !defined(TEXT_DOMAIN)
36 #define	TEXT_DOMAIN	"SYS_TEST"
37 #endif
38 
39 typedef struct strlist {
40 	const char *str;
41 	struct strlist *next;
42 } strlist_t;
43 
44 typedef	struct {
45 	scfga_ret_t scsi_err;
46 	cfga_err_t  cfga_err;
47 } errcvt_t;
48 
49 typedef struct {
50 	scfga_cmd_t cmd;
51 	int type;
52 	int (*fcn)(const devctl_hdl_t);
53 } set_state_cmd_t;
54 
55 typedef struct {
56 	scfga_cmd_t cmd;
57 	int type;
58 	int (*state_fcn)(const devctl_hdl_t, uint_t *);
59 } get_state_cmd_t;
60 
61 /* Function prototypes */
62 static char *pathdup(const char *path, int *l_errnop);
63 static void msg_common(char **err_msgpp, int append_newline, int l_errno,
64     va_list ap);
65 
66 /*
67  * The string table contains most of the strings used by the scsi cfgadm plugin.
68  * All strings which are to be internationalized must be in this table.
69  * Some strings which are not internationalized are also included here.
70  * Arguments to messages are NOT internationalized.
71  */
72 msgcvt_t str_tbl[] = {
73 
74 /*
75  * The first element (ERR_UNKNOWN) MUST always be present in the array.
76  */
77 #define	UNKNOWN_ERR_IDX		0	/* Keep the index in sync */
78 
79 
80 /* msg_code	num_args, I18N	msg_string				*/
81 
82 /* ERRORS */
83 {ERR_UNKNOWN,		0, 1,	"unknown error"},
84 {ERR_OP_FAILED,		0, 1,	"operation failed"},
85 {ERR_CMD_INVAL,		0, 1,	"invalid command"},
86 {ERR_NOT_BUSAPID,	0, 1,	"not a SCSI bus apid"},
87 {ERR_APID_INVAL,	0, 1,	"invalid SCSI ap_id"},
88 {ERR_NOT_BUSOP,		0, 1,	"operation not supported for SCSI bus"},
89 {ERR_NOT_DEVOP,		0, 1,	"operation not supported for SCSI device"},
90 {ERR_UNAVAILABLE,	0, 1,	"unavailable"},
91 {ERR_CTRLR_CRIT,	0, 1,	"critical partition controlled by SCSI HBA"},
92 {ERR_BUS_GETSTATE,	0, 1,	"failed to get state for SCSI bus"},
93 {ERR_BUS_NOTCONNECTED,	0, 1,	"SCSI bus not connected"},
94 {ERR_BUS_CONNECTED,	0, 1,	"SCSI bus not disconnected"},
95 {ERR_BUS_QUIESCE,	0, 1,	"SCSI bus quiesce failed"},
96 {ERR_BUS_UNQUIESCE,	0, 1,	"SCSI bus unquiesce failed"},
97 {ERR_BUS_CONFIGURE,	0, 1,	"failed to configure devices on SCSI bus"},
98 {ERR_BUS_UNCONFIGURE,	0, 1,	"failed to unconfigure SCSI bus"},
99 {ERR_DEV_CONFIGURE,	0, 1,	"failed to configure SCSI device"},
100 {ERR_DEV_RECONFIGURE,	1, 1,	"failed to reconfigure device: "},
101 {ERR_DEV_UNCONFIGURE,	0, 1,	"failed to unconfigure SCSI device"},
102 {ERR_DEV_REMOVE,	0, 1,	"remove operation failed"},
103 {ERR_DEV_REPLACE,	0, 1,	"replace operation failed"},
104 {ERR_DEV_INSERT,	0, 1,	"insert operation failed"},
105 {ERR_DEV_GETSTATE,	0, 1,	"failed to get state for SCSI device"},
106 {ERR_RESET,		0, 1,	"reset failed"},
107 {ERR_LIST,		0, 1,	"list operation failed"},
108 {ERR_MAYBE_BUSY,	0, 1,	"device may be busy"},
109 {ERR_BUS_DEV_MISMATCH,	0, 1,	"mismatched SCSI bus and device"},
110 {ERR_VAR_RUN,		0, 1,	"/var/run is not mounted"},
111 {ERR_FORK,		0, 1,	"failed to fork cleanup handler"},
112 
113 /* Errors with arguments */
114 {ERRARG_OPT_INVAL,	1, 1,	"invalid option: "},
115 {ERRARG_HWCMD_INVAL,	1, 1,	"invalid command: "},
116 {ERRARG_DEVINFO,	1, 1,	"libdevinfo failed on path: "},
117 {ERRARG_OPEN,		1, 1,	"open failed: "},
118 {ERRARG_LOCK,		1, 1,	"lock failed: "},
119 {ERRARG_QUIESCE_LOCK,	1, 1,	"cannot acquire quiesce lock: "},
120 
121 /* RCM Errors */
122 {ERR_RCM_HANDLE,	0, 1,	"cannot get RCM handle"},
123 {ERRARG_RCM_SUSPEND,	0, 1,	"failed to suspend: "},
124 {ERRARG_RCM_RESUME,	0, 1,	"failed to resume: "},
125 {ERRARG_RCM_OFFLINE,	0, 1,	"failed to offline: "},
126 {ERRARG_RCM_ONLINE,	0, 1,	"failed to online: "},
127 {ERRARG_RCM_REMOVE,	0, 1,	"failed to remove: "},
128 
129 /* Commands */
130 {CMD_INSERT_DEV,	0, 0,	"insert_device"},
131 {CMD_REMOVE_DEV,	0, 0,	"remove_device"},
132 {CMD_REPLACE_DEV,	0, 0,	"replace_device"},
133 {CMD_RESET_DEV,		0, 0,	"reset_device"},
134 {CMD_RESET_BUS,		0, 0,	"reset_bus"},
135 {CMD_RESET_ALL,		0, 0,	"reset_all"},
136 
137 /* help messages */
138 {MSG_HELP_HDR,		0, 1,	"\nSCSI specific commands and options:\n"},
139 {MSG_HELP_USAGE,	0, 0,	"\t-x insert_device ap_id [ap_id... ]\n"
140 				"\t-x remove_device ap_id [ap_id... ]\n"
141 				"\t-x replace_device ap_id [ap_id... ]\n"
142 				"\t-x reset_device ap_id [ap_id... ]\n"
143 				"\t-x reset_bus ap_id [ap_id... ]\n"
144 				"\t-x reset_all ap_id [ap_id... ]\n"},
145 
146 /* hotplug messages */
147 {MSG_INSDEV,		1, 1,	"Adding device to SCSI HBA: "},
148 {MSG_RMDEV,		1, 1,	"Removing SCSI device: "},
149 {MSG_REPLDEV,		1, 1,	"Replacing SCSI device: "},
150 {MSG_WAIT_LOCK,		0, 1,	"Waiting for quiesce lock... "},
151 
152 /* Hotplugging confirmation prompts */
153 {CONF_QUIESCE_1,	1, 1,
154 	"This operation will suspend activity on SCSI bus: "},
155 
156 {CONF_QUIESCE_2,	0, 1,	"\nContinue"},
157 
158 {CONF_UNQUIESCE,	0, 1,
159 	"SCSI bus quiesced successfully.\n"
160 	"It is now safe to proceed with hotplug operation."
161 	"\nEnter y if operation is complete or n to abort"},
162 
163 {CONF_NO_QUIESCE,	0, 1,
164 	"Proceed with hotplug operation."
165 	"\nEnter y if operation is complete or n to abort"},
166 
167 /* Misc. */
168 {WARN_DISCONNECT,	0, 1,
169 	"WARNING: Disconnecting critical partitions may cause system hang."
170 	"\nContinue"}
171 };
172 
173 
174 #define	N_STRS	(sizeof (str_tbl) / sizeof (str_tbl[0]))
175 
176 #define	GET_MSG_NARGS(i)	(str_tbl[msg_idx(i)].nargs)
177 #define	GET_MSG_INTL(i)		(str_tbl[msg_idx(i)].intl)
178 
179 static errcvt_t err_cvt_tbl[] = {
180 	{ SCFGA_OK,		CFGA_OK			},
181 	{ SCFGA_LIB_ERR,	CFGA_LIB_ERROR		},
182 	{ SCFGA_APID_NOEXIST,	CFGA_APID_NOEXIST	},
183 	{ SCFGA_NACK,		CFGA_NACK		},
184 	{ SCFGA_BUSY,		CFGA_BUSY		},
185 	{ SCFGA_SYSTEM_BUSY,	CFGA_SYSTEM_BUSY	},
186 	{ SCFGA_OPNOTSUPP,	CFGA_OPNOTSUPP		},
187 	{ SCFGA_PRIV,		CFGA_PRIV		},
188 	{ SCFGA_UNKNOWN_ERR,	CFGA_ERROR		},
189 	{ SCFGA_ERR,		CFGA_ERROR		}
190 };
191 
192 #define	N_ERR_CVT_TBL	(sizeof (err_cvt_tbl)/sizeof (err_cvt_tbl[0]))
193 
194 #define	DEV_OP	0
195 #define	BUS_OP	1
196 static set_state_cmd_t set_state_cmds[] = {
197 
198 { SCFGA_BUS_QUIESCE,		BUS_OP,		devctl_bus_quiesce	},
199 { SCFGA_BUS_UNQUIESCE,		BUS_OP,		devctl_bus_unquiesce	},
200 { SCFGA_BUS_CONFIGURE,		BUS_OP,		devctl_bus_configure	},
201 { SCFGA_BUS_UNCONFIGURE, 	BUS_OP,		devctl_bus_unconfigure	},
202 { SCFGA_RESET_BUS,		BUS_OP,		devctl_bus_reset	},
203 { SCFGA_RESET_ALL, 		BUS_OP,		devctl_bus_resetall	},
204 { SCFGA_DEV_CONFIGURE,		DEV_OP,		devctl_device_online	},
205 { SCFGA_DEV_UNCONFIGURE,	DEV_OP,		devctl_device_offline	},
206 { SCFGA_DEV_REMOVE,		DEV_OP,		devctl_device_remove	},
207 { SCFGA_RESET_DEV,		DEV_OP,		devctl_device_reset	}
208 
209 };
210 
211 #define	N_SET_STATE_CMDS (sizeof (set_state_cmds)/sizeof (set_state_cmds[0]))
212 
213 static get_state_cmd_t get_state_cmds[] = {
214 { SCFGA_BUS_GETSTATE,		BUS_OP,		devctl_bus_getstate	},
215 { SCFGA_DEV_GETSTATE,		DEV_OP,		devctl_device_getstate	}
216 };
217 
218 #define	N_GET_STATE_CMDS (sizeof (get_state_cmds)/sizeof (get_state_cmds[0]))
219 
220 /*
221  * SCSI hardware specific commands
222  */
223 static hw_cmd_t hw_cmds[] = {
224 	/* Command string	Command ID		Function	*/
225 
226 	{ CMD_INSERT_DEV,	SCFGA_INSERT_DEV,	dev_insert	},
227 	{ CMD_REMOVE_DEV,	SCFGA_REMOVE_DEV,	dev_remove	},
228 	{ CMD_REPLACE_DEV,	SCFGA_REPLACE_DEV,	dev_replace	},
229 	{ CMD_RESET_DEV,	SCFGA_RESET_DEV,	reset_common	},
230 	{ CMD_RESET_BUS,	SCFGA_RESET_BUS,	reset_common	},
231 	{ CMD_RESET_ALL,	SCFGA_RESET_ALL,	reset_common	},
232 };
233 #define	N_HW_CMDS (sizeof (hw_cmds) / sizeof (hw_cmds[0]))
234 
235 
236 cfga_err_t
237 err_cvt(scfga_ret_t s_err)
238 {
239 	int i;
240 
241 	for (i = 0; i < N_ERR_CVT_TBL; i++) {
242 		if (err_cvt_tbl[i].scsi_err == s_err) {
243 			return (err_cvt_tbl[i].cfga_err);
244 		}
245 	}
246 
247 	return (CFGA_ERROR);
248 }
249 
250 /*
251  * Removes duplicate slashes from a pathname and any trailing slashes.
252  * Returns "/" if input is "/"
253  */
254 static char *
255 pathdup(const char *path, int *l_errnop)
256 {
257 	int prev_was_slash = 0;
258 	char c, *dp = NULL, *dup = NULL;
259 	const char *sp = NULL;
260 
261 	*l_errnop = 0;
262 
263 	if (path == NULL) {
264 		return (NULL);
265 	}
266 
267 	if ((dup = calloc(1, strlen(path) + 1)) == NULL) {
268 		*l_errnop = errno;
269 		return (NULL);
270 	}
271 
272 	prev_was_slash = 0;
273 	for (sp = path, dp = dup; (c = *sp) != '\0'; sp++) {
274 		if (!prev_was_slash || c != '/') {
275 			*dp++ = c;
276 		}
277 		if (c == '/') {
278 			prev_was_slash = 1;
279 		} else {
280 			prev_was_slash = 0;
281 		}
282 	}
283 
284 	/* Remove trailing slash except if it is the first char */
285 	if (prev_was_slash && dp != dup && dp - 1 != dup) {
286 		*(--dp) = '\0';
287 	} else {
288 		*dp = '\0';
289 	}
290 
291 	return (dup);
292 }
293 
294 scfga_ret_t
295 apidt_create(const char *ap_id, apid_t *apidp, char **errstring)
296 {
297 	char *hba_phys = NULL, *dyn = NULL;
298 	char *dyncomp = NULL, *path = NULL;
299 	int l_errno = 0;
300 	size_t len = 0;
301 	scfga_ret_t ret;
302 
303 	if ((hba_phys = pathdup(ap_id, &l_errno)) == NULL) {
304 		cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
305 		return (SCFGA_LIB_ERR);
306 	}
307 
308 	/* Extract the base(hba) and dynamic(device) component if any */
309 	dyncomp = NULL;
310 	if ((dyn = GET_DYN(hba_phys)) != NULL) {
311 		len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
312 		dyncomp = calloc(1, len);
313 		if (dyncomp == NULL) {
314 			cfga_err(errstring, errno, ERR_OP_FAILED, 0);
315 			ret = SCFGA_LIB_ERR;
316 			goto err;
317 		}
318 		(void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
319 
320 		/* Remove the dynamic component from the base */
321 		*dyn = '\0';
322 	}
323 
324 	/* Create the path */
325 	if ((ret = apid_to_path(hba_phys, dyncomp, &path, &l_errno))
326 	    != SCFGA_OK) {
327 		cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
328 		goto err;
329 	}
330 
331 	assert(path != NULL);
332 	assert(hba_phys != NULL);
333 
334 	apidp->hba_phys = hba_phys;
335 	apidp->dyncomp = dyncomp;
336 	apidp->path = path;
337 	apidp->flags = 0;
338 
339 	return (SCFGA_OK);
340 
341 err:
342 	S_FREE(hba_phys);
343 	S_FREE(dyncomp);
344 	S_FREE(path);
345 	return (ret);
346 }
347 
348 void
349 apidt_free(apid_t *apidp)
350 {
351 	if (apidp == NULL)
352 		return;
353 
354 	S_FREE(apidp->hba_phys);
355 	S_FREE(apidp->dyncomp);
356 	S_FREE(apidp->path);
357 }
358 
359 scfga_ret_t
360 walk_tree(
361 	const char	*physpath,
362 	void		*arg,
363 	uint_t		init_flags,
364 	walkarg_t	*up,
365 	scfga_cmd_t	cmd,
366 	int		*l_errnop)
367 {
368 	int rv;
369 	di_node_t root, walk_root;
370 	char *root_path, *cp = NULL, *init_path;
371 	size_t len;
372 	scfga_ret_t ret;
373 
374 	*l_errnop = 0;
375 
376 	if ((root_path = strdup(physpath)) == NULL) {
377 		*l_errnop = errno;
378 		return (SCFGA_LIB_ERR);
379 	}
380 
381 	/* Fix up path for di_init() */
382 	len = strlen(DEVICES_DIR);
383 	if (strncmp(root_path, DEVICES_DIR SLASH,
384 	    len + strlen(SLASH)) == 0) {
385 		cp = root_path + len;
386 		(void) memmove(root_path, cp, strlen(cp) + 1);
387 	} else if (*root_path != '/') {
388 		*l_errnop = 0;
389 		ret = SCFGA_ERR;
390 		goto out;
391 	}
392 
393 	/* Remove dynamic component if any */
394 	if ((cp = GET_DYN(root_path)) != NULL) {
395 		*cp = '\0';
396 	}
397 
398 	/* Remove minor name if any */
399 	if ((cp = strrchr(root_path, ':')) != NULL) {
400 		*cp = '\0';
401 	}
402 
403 	/*
404 	 * Cached snapshots are always rooted at "/"
405 	 */
406 	init_path = root_path;
407 	if ((init_flags & DINFOCACHE) == DINFOCACHE) {
408 		init_path = "/";
409 	}
410 
411 	/* Get a snapshot */
412 	if ((root = di_init(init_path, init_flags)) == DI_NODE_NIL) {
413 		*l_errnop = errno;
414 		ret = SCFGA_LIB_ERR;
415 		goto out;
416 	}
417 
418 	/*
419 	 * Lookup the subtree of interest
420 	 */
421 	walk_root = root;
422 	if ((init_flags & DINFOCACHE) == DINFOCACHE) {
423 		walk_root = di_lookup_node(root, root_path);
424 	}
425 
426 	if (walk_root == DI_NODE_NIL) {
427 		*l_errnop = errno;
428 		di_fini(root);
429 		ret = SCFGA_LIB_ERR;
430 		goto out;
431 	}
432 
433 	/* Walk the tree */
434 	errno = 0;
435 	if (cmd == SCFGA_WALK_NODE) {
436 		rv = di_walk_node(walk_root, up->node_args.flags, arg,
437 		    up->node_args.fcn);
438 	} else {
439 		assert(cmd == SCFGA_WALK_MINOR);
440 		rv = di_walk_minor(walk_root, up->minor_args.nodetype, 0, arg,
441 		    up->minor_args.fcn);
442 	}
443 
444 	if (rv != 0) {
445 		*l_errnop = errno;
446 		ret = SCFGA_LIB_ERR;
447 	} else {
448 		*l_errnop = 0;
449 		ret = SCFGA_OK;
450 	}
451 
452 	di_fini(root);
453 
454 	/*FALLTHRU*/
455 out:
456 	S_FREE(root_path);
457 	return (ret);
458 }
459 
460 scfga_ret_t
461 invoke_cmd(
462 	const char *func,
463 	apid_t *apidtp,
464 	prompt_t *prp,
465 	cfga_flags_t flags,
466 	char **errstring)
467 {
468 	int i;
469 
470 	for (i = 0; i < N_HW_CMDS; i++) {
471 		if (strcmp(func, GET_MSG_STR(hw_cmds[i].str_id)) == 0) {
472 			return (hw_cmds[i].fcn(hw_cmds[i].cmd, apidtp,
473 			    prp, flags, errstring));
474 		}
475 	}
476 
477 	cfga_err(errstring, 0, ERRARG_HWCMD_INVAL, func, 0);
478 	return (SCFGA_ERR);
479 }
480 
481 int
482 msg_idx(msgid_t msgid)
483 {
484 	int idx = 0;
485 
486 	/* The string table index and the error id may or may not be same */
487 	if (msgid >= 0 && msgid <= N_STRS - 1 &&
488 	    str_tbl[msgid].msgid == msgid) {
489 		idx = msgid;
490 	} else {
491 		for (idx = 0; idx < N_STRS; idx++) {
492 			if (str_tbl[idx].msgid == msgid)
493 				break;
494 		}
495 		if (idx >= N_STRS) {
496 			idx =  UNKNOWN_ERR_IDX;
497 		}
498 	}
499 
500 	return (idx);
501 }
502 
503 /*
504  * cfga_err() accepts a variable number of message IDs and constructs
505  * a corresponding error string which is returned via the errstring argument.
506  * cfga_err() calls dgettext() to internationalize proper messages.
507  * May be called with a NULL argument.
508  */
509 void
510 cfga_err(char **errstring, int l_errno, ...)
511 {
512 	va_list ap;
513 	int append_newline = 0;
514 
515 	if (errstring == NULL || *errstring != NULL) {
516 		return;
517 	}
518 
519 	/*
520 	 * Don't append a newline, the application (for example cfgadm)
521 	 * should do that.
522 	 */
523 	append_newline = 0;
524 
525 	va_start(ap, l_errno);
526 	msg_common(errstring, append_newline, l_errno, ap);
527 	va_end(ap);
528 }
529 
530 /*
531  * This routine accepts a variable number of message IDs and constructs
532  * a corresponding message string which is printed via the message print
533  * routine argument.
534  */
535 void
536 cfga_msg(struct cfga_msg *msgp, ...)
537 {
538 	char *p = NULL;
539 	int append_newline = 0, l_errno = 0;
540 	va_list ap;
541 
542 	if (msgp == NULL || msgp->message_routine == NULL) {
543 		return;
544 	}
545 
546 	/* Append a newline after message */
547 	append_newline = 1;
548 	l_errno = 0;
549 
550 	va_start(ap, msgp);
551 	msg_common(&p, append_newline, l_errno, ap);
552 	va_end(ap);
553 
554 	(void) (*msgp->message_routine)(msgp->appdata_ptr, p);
555 
556 	S_FREE(p);
557 }
558 
559 /*
560  * Get internationalized string corresponding to message id
561  * Caller must free the memory allocated.
562  */
563 char *
564 cfga_str(int append_newline, ...)
565 {
566 	char *p = NULL;
567 	int l_errno = 0;
568 	va_list ap;
569 
570 	va_start(ap, append_newline);
571 	msg_common(&p, append_newline, l_errno, ap);
572 	va_end(ap);
573 
574 	return (p);
575 }
576 
577 static void
578 msg_common(char **msgpp, int append_newline, int l_errno, va_list ap)
579 {
580 	int a = 0;
581 	size_t len = 0;
582 	int i = 0, n = 0;
583 	char *s = NULL, *t = NULL;
584 	strlist_t dummy;
585 	strlist_t *savep = NULL, *sp = NULL, *tailp = NULL;
586 
587 	if (*msgpp != NULL) {
588 		return;
589 	}
590 
591 	dummy.next = NULL;
592 	tailp = &dummy;
593 	for (len = 0; (a = va_arg(ap, int)) != 0; ) {
594 		n = GET_MSG_NARGS(a); /* 0 implies no additional args */
595 		for (i = 0; i <= n; i++) {
596 			sp = calloc(1, sizeof (*sp));
597 			if (sp == NULL) {
598 				goto out;
599 			}
600 			if (i == 0 && GET_MSG_INTL(a)) {
601 				sp->str = dgettext(TEXT_DOMAIN, GET_MSG_STR(a));
602 			} else if (i == 0) {
603 				sp->str = GET_MSG_STR(a);
604 			} else {
605 				sp->str = va_arg(ap, char *);
606 			}
607 			len += (strlen(sp->str));
608 			sp->next = NULL;
609 			tailp->next = sp;
610 			tailp = sp;
611 		}
612 	}
613 
614 	len += 1;	/* terminating NULL */
615 
616 	s = t = NULL;
617 	if (l_errno) {
618 		s = dgettext(TEXT_DOMAIN, ": ");
619 		t = S_STR(strerror(l_errno));
620 		if (s != NULL && t != NULL) {
621 			len += strlen(s) + strlen(t);
622 		}
623 	}
624 
625 	if (append_newline) {
626 		len++;
627 	}
628 
629 	if ((*msgpp = calloc(1, len)) == NULL) {
630 		goto out;
631 	}
632 
633 	**msgpp = '\0';
634 	for (sp = dummy.next; sp != NULL; sp = sp->next) {
635 		(void) strcat(*msgpp, sp->str);
636 	}
637 
638 	if (s != NULL && t != NULL) {
639 		(void) strcat(*msgpp, s);
640 		(void) strcat(*msgpp, t);
641 	}
642 
643 	if (append_newline) {
644 		(void) strcat(*msgpp, dgettext(TEXT_DOMAIN, "\n"));
645 	}
646 
647 	/* FALLTHROUGH */
648 out:
649 	sp = dummy.next;
650 	while (sp != NULL) {
651 		savep = sp->next;
652 		S_FREE(sp);
653 		sp = savep;
654 	}
655 }
656 
657 scfga_ret_t
658 devctl_cmd(
659 	const char	*physpath,
660 	scfga_cmd_t	cmd,
661 	uint_t		*statep,
662 	int		*l_errnop)
663 {
664 	int rv = -1, i, type;
665 	devctl_hdl_t hdl = NULL;
666 	char *cp = NULL, *path = NULL;
667 	int (*func)(const devctl_hdl_t);
668 	int (*state_func)(const devctl_hdl_t, uint_t *);
669 
670 	*l_errnop = 0;
671 
672 	if (statep != NULL) *statep = 0;
673 
674 	func = NULL;
675 	state_func = NULL;
676 	type = 0;
677 
678 	for (i = 0; i < N_GET_STATE_CMDS; i++) {
679 		if (get_state_cmds[i].cmd == cmd) {
680 			state_func = get_state_cmds[i].state_fcn;
681 			type = get_state_cmds[i].type;
682 			assert(statep != NULL);
683 			break;
684 		}
685 	}
686 
687 	if (state_func == NULL) {
688 		for (i = 0; i < N_SET_STATE_CMDS; i++) {
689 			if (set_state_cmds[i].cmd == cmd) {
690 				func = set_state_cmds[i].fcn;
691 				type = set_state_cmds[i].type;
692 				assert(statep == NULL);
693 				break;
694 			}
695 		}
696 	}
697 
698 	assert(type == BUS_OP || type == DEV_OP);
699 
700 	if (func == NULL && state_func == NULL) {
701 		return (SCFGA_ERR);
702 	}
703 
704 	/*
705 	 * Fix up path for calling devctl.
706 	 */
707 	if ((path = strdup(physpath)) == NULL) {
708 		*l_errnop = errno;
709 		return (SCFGA_LIB_ERR);
710 	}
711 
712 	/* Remove dynamic component if any */
713 	if ((cp = GET_DYN(path)) != NULL) {
714 		*cp = '\0';
715 	}
716 
717 	/* Remove minor name */
718 	if ((cp = strrchr(path, ':')) != NULL) {
719 		*cp = '\0';
720 	}
721 
722 	errno = 0;
723 
724 	if (type == BUS_OP) {
725 		hdl = devctl_bus_acquire(path, 0);
726 	} else {
727 		hdl = devctl_device_acquire(path, 0);
728 	}
729 	*l_errnop = errno;
730 
731 	S_FREE(path);
732 
733 	if (hdl == NULL) {
734 		return (SCFGA_ERR);
735 	}
736 
737 	errno = 0;
738 	/* Only getstate functions require a second argument */
739 	if (func != NULL && statep == NULL) {
740 		rv = func(hdl);
741 		*l_errnop = errno;
742 	} else if (state_func != NULL && statep != NULL) {
743 		rv = state_func(hdl, statep);
744 		*l_errnop = errno;
745 	} else {
746 		rv = -1;
747 		*l_errnop = 0;
748 	}
749 
750 	devctl_release(hdl);
751 
752 	return ((rv == -1) ? SCFGA_ERR : SCFGA_OK);
753 }
754 
755 /*
756  * Is device in a known state ? (One of BUSY, ONLINE, OFFLINE)
757  *	BUSY --> One or more device special files are open. Implies online
758  *	ONLINE --> driver attached
759  *	OFFLINE --> CF1 with offline flag set.
760  *	UNKNOWN --> None of the above
761  */
762 int
763 known_state(di_node_t node)
764 {
765 	uint_t state;
766 
767 	state = di_state(node);
768 
769 	/*
770 	 * CF1 without offline flag set is considered unknown state.
771 	 * We are in a known state if either CF2 (driver attached) or
772 	 * offline.
773 	 */
774 	if ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
775 		(state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
776 		return (1);
777 	}
778 
779 	return (0);
780 }
781 
782 void
783 list_free(ldata_list_t **llpp)
784 {
785 	ldata_list_t *lp, *olp;
786 
787 	lp = *llpp;
788 	while (lp != NULL) {
789 		olp = lp;
790 		lp = olp->next;
791 		S_FREE(olp);
792 	}
793 
794 	*llpp = NULL;
795 }
796 
797 /*
798  * Obtain the devlink from a /devices path
799  */
800 typedef struct walk_link {
801 	char *path;
802 	char len;
803 	char **linkpp;
804 } walk_link_t;
805 
806 static int
807 get_link(di_devlink_t devlink, void *arg)
808 {
809 	walk_link_t *larg = (walk_link_t *)arg;
810 
811 	/*
812 	 * When path is specified, it's the node path without minor
813 	 * name. Therefore, the ../.. prefixes needs to be stripped.
814 	 */
815 	if (larg->path) {
816 		char *content = (char *)di_devlink_content(devlink);
817 		char *start = strstr(content, "/devices/");
818 
819 		/* line content must have minor node */
820 		if (start == NULL ||
821 		    strncmp(start, larg->path, larg->len) != 0 ||
822 		    start[larg->len] != ':')
823 			return (DI_WALK_CONTINUE);
824 	}
825 
826 	*(larg->linkpp) = strdup(di_devlink_path(devlink));
827 	return (DI_WALK_TERMINATE);
828 }
829 
830 scfga_ret_t
831 physpath_to_devlink(
832 	char *node_path,
833 	char **logpp,
834 	int *l_errnop,
835 	int match_minor)
836 {
837 	walk_link_t larg;
838 	di_devlink_handle_t hdl;
839 	char *minor_path;
840 
841 	if ((hdl = di_devlink_init(NULL, 0)) == NULL) {
842 		*l_errnop = errno;
843 		return (SCFGA_LIB_ERR);
844 	}
845 
846 	*logpp = NULL;
847 	larg.linkpp = logpp;
848 	if (match_minor) {
849 		minor_path = node_path + strlen(DEVICES_DIR);
850 		larg.path = NULL;
851 	} else {
852 		minor_path = NULL;
853 		larg.len = strlen(node_path);
854 		larg.path = node_path;
855 	}
856 
857 	(void) di_devlink_walk(hdl, NULL, minor_path, DI_PRIMARY_LINK,
858 	    (void *)&larg, get_link);
859 
860 	di_devlink_fini(&hdl);
861 
862 	if (*logpp == NULL)
863 		return (SCFGA_LIB_ERR);
864 
865 	return (SCFGA_OK);
866 }
867 
868 int
869 hba_dev_cmp(const char *hba, const char *devpath)
870 {
871 	char *cp = NULL;
872 	int rv;
873 	size_t hba_len, dev_len;
874 	char l_hba[MAXPATHLEN], l_dev[MAXPATHLEN];
875 
876 	(void) snprintf(l_hba, sizeof (l_hba), "%s", hba);
877 	(void) snprintf(l_dev, sizeof (l_dev), "%s", devpath);
878 
879 	/* Remove dynamic component if any */
880 	if ((cp = GET_DYN(l_hba)) != NULL) {
881 		*cp = '\0';
882 	}
883 
884 	if ((cp = GET_DYN(l_dev)) != NULL) {
885 		*cp = '\0';
886 	}
887 
888 
889 	/* Remove minor names */
890 	if ((cp = strrchr(l_hba, ':')) != NULL) {
891 		*cp = '\0';
892 	}
893 
894 	if ((cp = strrchr(l_dev, ':')) != NULL) {
895 		*cp = '\0';
896 	}
897 
898 	hba_len = strlen(l_hba);
899 	dev_len = strlen(l_dev);
900 
901 	/* Check if HBA path is component of device path */
902 	if (rv = strncmp(l_hba, l_dev, hba_len)) {
903 		return (rv);
904 	}
905 
906 	/* devpath must have '/' and 1 char in addition to hba path */
907 	if (dev_len >= hba_len + 2 && l_dev[hba_len] == '/') {
908 		return (0);
909 	} else {
910 		return (-1);
911 	}
912 }
913 
914 int
915 dev_cmp(const char *dev1, const char *dev2, int match_minor)
916 {
917 	char l_dev1[MAXPATHLEN], l_dev2[MAXPATHLEN];
918 	char *mn1, *mn2;
919 	int rv;
920 
921 	(void) snprintf(l_dev1, sizeof (l_dev1), "%s", dev1);
922 	(void) snprintf(l_dev2, sizeof (l_dev2), "%s", dev2);
923 
924 	if ((mn1 = GET_DYN(l_dev1)) != NULL) {
925 		*mn1 = '\0';
926 	}
927 
928 	if ((mn2 = GET_DYN(l_dev2)) != NULL) {
929 		*mn2 = '\0';
930 	}
931 
932 	/* Separate out the minor names */
933 	if ((mn1 = strrchr(l_dev1, ':')) != NULL) {
934 		*mn1++ = '\0';
935 	}
936 
937 	if ((mn2 = strrchr(l_dev2, ':')) != NULL) {
938 		*mn2++ = '\0';
939 	}
940 
941 	if ((rv = strcmp(l_dev1, l_dev2)) != 0 || !match_minor) {
942 		return (rv);
943 	}
944 
945 	/*
946 	 * Compare minor names
947 	 */
948 	if (mn1 == NULL && mn2 == NULL) {
949 		return (0);
950 	} else if (mn1 == NULL) {
951 		return (-1);
952 	} else if (mn2 == NULL) {
953 		return (1);
954 	} else {
955 		return (strcmp(mn1, mn2));
956 	}
957 }
958