xref: /illumos-gate/usr/src/lib/cfgadm_plugins/scsi/common/cfga_utils.c (revision eb9a1df2aeb866bf1de4494433b6d7e5fa07b3ae)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include "cfga_scsi.h"
28 #include <libgen.h>
29 #include <limits.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_CLIENT_OFFLINE,	0, 1,	"failed to offline a client device: "},
127 {ERRARG_RCM_ONLINE,	0, 1,	"failed to online: "},
128 {ERRARG_RCM_REMOVE,	0, 1,	"failed to remove: "},
129 
130 /* Commands */
131 {CMD_INSERT_DEV,	0, 0,	"insert_device"},
132 {CMD_REMOVE_DEV,	0, 0,	"remove_device"},
133 {CMD_LED_DEV,		0, 0,	"led"},
134 {CMD_LOCATOR_DEV,	0, 0,	"locator"},
135 {CMD_REPLACE_DEV,	0, 0,	"replace_device"},
136 {CMD_RESET_DEV,		0, 0,	"reset_device"},
137 {CMD_RESET_BUS,		0, 0,	"reset_bus"},
138 {CMD_RESET_ALL,		0, 0,	"reset_all"},
139 
140 /* help messages */
141 {MSG_HELP_HDR,		0, 1,	"\nSCSI specific commands and options:\n"},
142 {MSG_HELP_USAGE,	0, 0,	"\t-x insert_device ap_id [ap_id... ]\n"
143 				"\t-x remove_device ap_id [ap_id... ]\n"
144 				"\t-x replace_device ap_id [ap_id... ]\n"
145 				"\t-x locator[=on|off] ap_id [ap_id... ]\n"
146 				"\t-x led[=LED,mode=on|off|blink] "
147 				    "ap_id [ap_id... ]\n"
148 				"\t-x reset_device ap_id [ap_id... ]\n"
149 				"\t-x reset_bus ap_id [ap_id... ]\n"
150 				"\t-x reset_all ap_id [ap_id... ]\n"},
151 
152 /* hotplug messages */
153 {MSG_INSDEV,		1, 1,	"Adding device to SCSI HBA: "},
154 {MSG_RMDEV,		1, 1,	"Removing SCSI device: "},
155 {MSG_REPLDEV,		1, 1,	"Replacing SCSI device: "},
156 {MSG_WAIT_LOCK,		0, 1,	"Waiting for quiesce lock... "},
157 
158 /* Hotplugging confirmation prompts */
159 {CONF_QUIESCE_1,	1, 1,
160 	"This operation will suspend activity on SCSI bus: "},
161 
162 {CONF_QUIESCE_2,	0, 1,	"\nContinue"},
163 
164 {CONF_UNQUIESCE,	0, 1,
165 	"SCSI bus quiesced successfully.\n"
166 	"It is now safe to proceed with hotplug operation."
167 	"\nEnter y if operation is complete or n to abort"},
168 
169 {CONF_NO_QUIESCE,	0, 1,
170 	"Proceed with hotplug operation."
171 	"\nEnter y if operation is complete or n to abort"},
172 
173 /* Misc. */
174 {WARN_DISCONNECT,	0, 1,
175 	"WARNING: Disconnecting critical partitions may cause system hang."
176 	"\nContinue"},
177 
178 /* LED messages */
179 {MSG_LED_HDR,		0, 1,	"Disk                    Led"},
180 {MSG_MISSING_LED_NAME,	0, 1,	"Missing LED name"},
181 {MSG_MISSING_LED_MODE,	0, 1,	"Missing LED mode"}
182 };
183 
184 char *
185 led_strs[] = {
186 	"fault",
187 	"power",
188 	"attn",
189 	"active",
190 	"locator",
191 	NULL
192 };
193 
194 char *
195 led_mode_strs[] = {
196 	"off",
197 	"on",
198 	"blink",
199 	"faulted",
200 	"unknown",
201 	NULL
202 };
203 
204 
205 
206 
207 #define	N_STRS	(sizeof (str_tbl) / sizeof (str_tbl[0]))
208 
209 #define	GET_MSG_NARGS(i)	(str_tbl[msg_idx(i)].nargs)
210 #define	GET_MSG_INTL(i)		(str_tbl[msg_idx(i)].intl)
211 
212 static errcvt_t err_cvt_tbl[] = {
213 	{ SCFGA_OK,		CFGA_OK			},
214 	{ SCFGA_LIB_ERR,	CFGA_LIB_ERROR		},
215 	{ SCFGA_APID_NOEXIST,	CFGA_APID_NOEXIST	},
216 	{ SCFGA_NACK,		CFGA_NACK		},
217 	{ SCFGA_BUSY,		CFGA_BUSY		},
218 	{ SCFGA_SYSTEM_BUSY,	CFGA_SYSTEM_BUSY	},
219 	{ SCFGA_OPNOTSUPP,	CFGA_OPNOTSUPP		},
220 	{ SCFGA_PRIV,		CFGA_PRIV		},
221 	{ SCFGA_UNKNOWN_ERR,	CFGA_ERROR		},
222 	{ SCFGA_ERR,		CFGA_ERROR		}
223 };
224 
225 #define	N_ERR_CVT_TBL	(sizeof (err_cvt_tbl)/sizeof (err_cvt_tbl[0]))
226 
227 #define	DEV_OP	0
228 #define	BUS_OP	1
229 static set_state_cmd_t set_state_cmds[] = {
230 
231 { SCFGA_BUS_QUIESCE,		BUS_OP,		devctl_bus_quiesce	},
232 { SCFGA_BUS_UNQUIESCE,		BUS_OP,		devctl_bus_unquiesce	},
233 { SCFGA_BUS_CONFIGURE,		BUS_OP,		devctl_bus_configure	},
234 { SCFGA_BUS_UNCONFIGURE, 	BUS_OP,		devctl_bus_unconfigure	},
235 { SCFGA_RESET_BUS,		BUS_OP,		devctl_bus_reset	},
236 { SCFGA_RESET_ALL, 		BUS_OP,		devctl_bus_resetall	},
237 { SCFGA_DEV_CONFIGURE,		DEV_OP,		devctl_device_online	},
238 { SCFGA_DEV_UNCONFIGURE,	DEV_OP,		devctl_device_offline	},
239 { SCFGA_DEV_REMOVE,		DEV_OP,		devctl_device_remove	},
240 { SCFGA_RESET_DEV,		DEV_OP,		devctl_device_reset	}
241 
242 };
243 
244 #define	N_SET_STATE_CMDS (sizeof (set_state_cmds)/sizeof (set_state_cmds[0]))
245 
246 static get_state_cmd_t get_state_cmds[] = {
247 { SCFGA_BUS_GETSTATE,		BUS_OP,		devctl_bus_getstate	},
248 { SCFGA_DEV_GETSTATE,		DEV_OP,		devctl_device_getstate	}
249 };
250 
251 #define	N_GET_STATE_CMDS (sizeof (get_state_cmds)/sizeof (get_state_cmds[0]))
252 
253 /*
254  * SCSI hardware specific commands
255  */
256 static hw_cmd_t hw_cmds[] = {
257 	/* Command string	Command ID		Function	*/
258 
259 	{ CMD_INSERT_DEV,	SCFGA_INSERT_DEV,	dev_insert	},
260 	{ CMD_REMOVE_DEV,	SCFGA_REMOVE_DEV,	dev_remove	},
261 	{ CMD_REPLACE_DEV,	SCFGA_REPLACE_DEV,	dev_replace	},
262 	{ CMD_LED_DEV,		SCFGA_LED_DEV,		dev_led		},
263 	{ CMD_LOCATOR_DEV,	SCFGA_LOCATOR_DEV,	dev_led		},
264 	{ CMD_RESET_DEV,	SCFGA_RESET_DEV,	reset_common	},
265 	{ CMD_RESET_BUS,	SCFGA_RESET_BUS,	reset_common	},
266 	{ CMD_RESET_ALL,	SCFGA_RESET_ALL,	reset_common	},
267 };
268 #define	N_HW_CMDS (sizeof (hw_cmds) / sizeof (hw_cmds[0]))
269 
270 
271 cfga_err_t
272 err_cvt(scfga_ret_t s_err)
273 {
274 	int i;
275 
276 	for (i = 0; i < N_ERR_CVT_TBL; i++) {
277 		if (err_cvt_tbl[i].scsi_err == s_err) {
278 			return (err_cvt_tbl[i].cfga_err);
279 		}
280 	}
281 
282 	return (CFGA_ERROR);
283 }
284 
285 /*
286  * Removes duplicate slashes from a pathname and any trailing slashes.
287  * Returns "/" if input is "/"
288  */
289 static char *
290 pathdup(const char *path, int *l_errnop)
291 {
292 	int prev_was_slash = 0;
293 	char c, *dp = NULL, *dup = NULL;
294 	const char *sp = NULL;
295 
296 	*l_errnop = 0;
297 
298 	if (path == NULL) {
299 		return (NULL);
300 	}
301 
302 	if ((dup = calloc(1, strlen(path) + 1)) == NULL) {
303 		*l_errnop = errno;
304 		return (NULL);
305 	}
306 
307 	prev_was_slash = 0;
308 	for (sp = path, dp = dup; (c = *sp) != '\0'; sp++) {
309 		if (!prev_was_slash || c != '/') {
310 			*dp++ = c;
311 		}
312 		if (c == '/') {
313 			prev_was_slash = 1;
314 		} else {
315 			prev_was_slash = 0;
316 		}
317 	}
318 
319 	/* Remove trailing slash except if it is the first char */
320 	if (prev_was_slash && dp != dup && dp - 1 != dup) {
321 		*(--dp) = '\0';
322 	} else {
323 		*dp = '\0';
324 	}
325 
326 	return (dup);
327 }
328 
329 
330 scfga_ret_t
331 apidt_create(const char *ap_id, apid_t *apidp, char **errstring)
332 {
333 	char *hba_phys = NULL, *dyn = NULL;
334 	char *dyncomp = NULL, *path = NULL;
335 	int l_errno = 0;
336 	size_t len = 0;
337 	scfga_ret_t ret;
338 
339 	if ((hba_phys = pathdup(ap_id, &l_errno)) == NULL) {
340 		cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
341 		return (SCFGA_LIB_ERR);
342 	}
343 
344 	/* Extract the base(hba) and dynamic(device) component if any */
345 	dyncomp = NULL;
346 	if ((dyn = GET_DYN(hba_phys)) != NULL) {
347 		len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
348 		dyncomp = calloc(1, len);
349 		if (dyncomp == NULL) {
350 			cfga_err(errstring, errno, ERR_OP_FAILED, 0);
351 			ret = SCFGA_LIB_ERR;
352 			goto err;
353 		}
354 		(void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
355 
356 		/* Remove the dynamic component from the base */
357 		*dyn = '\0';
358 	} else {
359 		apidp->dyntype = NODYNCOMP;
360 	}
361 
362 	/* get dyn comp type */
363 	if (dyncomp != NULL) {
364 		if (strstr(dyncomp, PATH_APID_DYN_SEP) != NULL) {
365 			apidp->dyntype = PATH_APID;
366 		} else {
367 			apidp->dyntype = DEV_APID;
368 		}
369 	}
370 
371 	/* Create the path */
372 	if ((ret = apid_to_path(hba_phys, dyncomp, &path,
373 	    &l_errno)) != SCFGA_OK) {
374 		cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
375 		goto err;
376 	}
377 
378 	assert(path != NULL);
379 	assert(hba_phys != NULL);
380 
381 	apidp->hba_phys = hba_phys;
382 	apidp->dyncomp = dyncomp;
383 	apidp->path = path;
384 	apidp->flags = 0;
385 
386 	return (SCFGA_OK);
387 
388 err:
389 	S_FREE(hba_phys);
390 	S_FREE(dyncomp);
391 	S_FREE(path);
392 	return (ret);
393 }
394 
395 void
396 apidt_free(apid_t *apidp)
397 {
398 	if (apidp == NULL)
399 		return;
400 
401 	S_FREE(apidp->hba_phys);
402 	S_FREE(apidp->dyncomp);
403 	S_FREE(apidp->path);
404 }
405 
406 scfga_ret_t
407 walk_tree(
408 	const char	*physpath,
409 	void		*arg,
410 	uint_t		init_flags,
411 	walkarg_t	*up,
412 	scfga_cmd_t	cmd,
413 	int		*l_errnop)
414 {
415 	int rv;
416 	di_node_t root, walk_root;
417 	char *root_path, *cp = NULL, *init_path;
418 	size_t len;
419 	scfga_ret_t ret;
420 
421 	*l_errnop = 0;
422 
423 	if ((root_path = strdup(physpath)) == NULL) {
424 		*l_errnop = errno;
425 		return (SCFGA_LIB_ERR);
426 	}
427 
428 	/* Fix up path for di_init() */
429 	len = strlen(DEVICES_DIR);
430 	if (strncmp(root_path, DEVICES_DIR SLASH,
431 	    len + strlen(SLASH)) == 0) {
432 		cp = root_path + len;
433 		(void) memmove(root_path, cp, strlen(cp) + 1);
434 	} else if (*root_path != '/') {
435 		*l_errnop = 0;
436 		ret = SCFGA_ERR;
437 		goto out;
438 	}
439 
440 	/* Remove dynamic component if any */
441 	if ((cp = GET_DYN(root_path)) != NULL) {
442 		*cp = '\0';
443 	}
444 
445 	/* Remove minor name if any */
446 	if ((cp = strrchr(root_path, ':')) != NULL) {
447 		*cp = '\0';
448 	}
449 
450 	/*
451 	 * Cached snapshots are always rooted at "/"
452 	 */
453 	init_path = root_path;
454 	if ((init_flags & DINFOCACHE) == DINFOCACHE) {
455 		init_path = "/";
456 	}
457 
458 	/* Get a snapshot */
459 	if ((root = di_init(init_path, init_flags)) == DI_NODE_NIL) {
460 		*l_errnop = errno;
461 		ret = SCFGA_LIB_ERR;
462 		goto out;
463 	}
464 
465 	/*
466 	 * Lookup the subtree of interest
467 	 */
468 	walk_root = root;
469 	if ((init_flags & DINFOCACHE) == DINFOCACHE) {
470 		walk_root = di_lookup_node(root, root_path);
471 	}
472 
473 	if (walk_root == DI_NODE_NIL) {
474 		*l_errnop = errno;
475 		di_fini(root);
476 		ret = SCFGA_LIB_ERR;
477 		goto out;
478 	}
479 
480 	/* Walk the tree */
481 	errno = 0;
482 	if (cmd == SCFGA_WALK_NODE) {
483 		rv = di_walk_node(walk_root, up->node_args.flags, arg,
484 		    up->node_args.fcn);
485 	} else if (cmd == SCFGA_WALK_PATH) {
486 		rv = stat_path_info(walk_root, arg, l_errnop);
487 	} else {
488 		assert(cmd == SCFGA_WALK_MINOR);
489 		rv = di_walk_minor(walk_root, up->minor_args.nodetype, 0, arg,
490 		    up->minor_args.fcn);
491 	}
492 
493 	if (rv != 0) {
494 		*l_errnop = errno;
495 		ret = SCFGA_LIB_ERR;
496 	} else {
497 		*l_errnop = 0;
498 		ret = SCFGA_OK;
499 	}
500 
501 	di_fini(root);
502 
503 	/*FALLTHRU*/
504 out:
505 	S_FREE(root_path);
506 	return (ret);
507 }
508 
509 scfga_ret_t
510 invoke_cmd(
511 	const char *func,
512 	apid_t *apidtp,
513 	prompt_t *prp,
514 	cfga_flags_t flags,
515 	char **errstring)
516 {
517 	int i;
518 	int len;
519 
520 
521 	/*
522 	 * Determine if the func has an equal sign; only compare up to
523 	 * the equals
524 	 */
525 	for (len = 0; func[len] != 0 && func[len] != '='; len++) {
526 	};
527 
528 	for (i = 0; i < N_HW_CMDS; i++) {
529 		const char *s = GET_MSG_STR(hw_cmds[i].str_id);
530 		if (strncmp(func, s, len) == 0 && s[len] == 0) {
531 			return (hw_cmds[i].fcn(func, hw_cmds[i].cmd, apidtp,
532 			    prp, flags, errstring));
533 		}
534 	}
535 
536 	cfga_err(errstring, 0, ERRARG_HWCMD_INVAL, func, 0);
537 	return (SCFGA_ERR);
538 }
539 
540 int
541 msg_idx(msgid_t msgid)
542 {
543 	int idx = 0;
544 
545 	/* The string table index and the error id may or may not be same */
546 	if (msgid >= 0 && msgid <= N_STRS - 1 &&
547 	    str_tbl[msgid].msgid == msgid) {
548 		idx = msgid;
549 	} else {
550 		for (idx = 0; idx < N_STRS; idx++) {
551 			if (str_tbl[idx].msgid == msgid)
552 				break;
553 		}
554 		if (idx >= N_STRS) {
555 			idx =  UNKNOWN_ERR_IDX;
556 		}
557 	}
558 
559 	return (idx);
560 }
561 
562 /*
563  * cfga_err() accepts a variable number of message IDs and constructs
564  * a corresponding error string which is returned via the errstring argument.
565  * cfga_err() calls dgettext() to internationalize proper messages.
566  * May be called with a NULL argument.
567  */
568 void
569 cfga_err(char **errstring, int l_errno, ...)
570 {
571 	va_list ap;
572 	int append_newline = 0;
573 
574 	if (errstring == NULL || *errstring != NULL) {
575 		return;
576 	}
577 
578 	/*
579 	 * Don't append a newline, the application (for example cfgadm)
580 	 * should do that.
581 	 */
582 	append_newline = 0;
583 
584 	va_start(ap, l_errno);
585 	msg_common(errstring, append_newline, l_errno, ap);
586 	va_end(ap);
587 }
588 
589 /*
590  * This routine accepts a variable number of message IDs and constructs
591  * a corresponding message string which is printed via the message print
592  * routine argument.
593  */
594 void
595 cfga_msg(struct cfga_msg *msgp, ...)
596 {
597 	char *p = NULL;
598 	int append_newline = 0, l_errno = 0;
599 	va_list ap;
600 
601 	if (msgp == NULL || msgp->message_routine == NULL) {
602 		return;
603 	}
604 
605 	/* Append a newline after message */
606 	append_newline = 1;
607 	l_errno = 0;
608 
609 	va_start(ap, msgp);
610 	msg_common(&p, append_newline, l_errno, ap);
611 	va_end(ap);
612 
613 	(void) (*msgp->message_routine)(msgp->appdata_ptr, p);
614 
615 	S_FREE(p);
616 }
617 
618 
619 /*
620  * This routine prints the value of an led for a disk.
621  */
622 void
623 cfga_led_msg(struct cfga_msg *msgp, apid_t *apidp, led_strid_t led,
624     led_modeid_t mode)
625 {
626 	char led_msg[MAX_INPUT];	/* 512 bytes */
627 
628 	if ((msgp == NULL) || (msgp->message_routine == NULL)) {
629 		return;
630 	}
631 	if ((apidp == NULL) || (apidp->dyncomp == NULL)) {
632 		return;
633 	}
634 	(void) snprintf(led_msg, sizeof (led_msg), "%-23s\t%s=%s\n",
635 	    basename(apidp->dyncomp),
636 	    dgettext(TEXT_DOMAIN, led_strs[led]),
637 	    dgettext(TEXT_DOMAIN, led_mode_strs[mode]));
638 	(void) (*msgp->message_routine)(msgp->appdata_ptr, led_msg);
639 }
640 
641 /*
642  * Get internationalized string corresponding to message id
643  * Caller must free the memory allocated.
644  */
645 char *
646 cfga_str(int append_newline, ...)
647 {
648 	char *p = NULL;
649 	int l_errno = 0;
650 	va_list ap;
651 
652 	va_start(ap, append_newline);
653 	msg_common(&p, append_newline, l_errno, ap);
654 	va_end(ap);
655 
656 	return (p);
657 }
658 
659 static void
660 msg_common(char **msgpp, int append_newline, int l_errno, va_list ap)
661 {
662 	int a = 0;
663 	size_t len = 0;
664 	int i = 0, n = 0;
665 	char *s = NULL, *t = NULL;
666 	strlist_t dummy;
667 	strlist_t *savep = NULL, *sp = NULL, *tailp = NULL;
668 
669 	if (*msgpp != NULL) {
670 		return;
671 	}
672 
673 	dummy.next = NULL;
674 	tailp = &dummy;
675 	for (len = 0; (a = va_arg(ap, int)) != 0; ) {
676 		n = GET_MSG_NARGS(a); /* 0 implies no additional args */
677 		for (i = 0; i <= n; i++) {
678 			sp = calloc(1, sizeof (*sp));
679 			if (sp == NULL) {
680 				goto out;
681 			}
682 			if (i == 0 && GET_MSG_INTL(a)) {
683 				sp->str = dgettext(TEXT_DOMAIN, GET_MSG_STR(a));
684 			} else if (i == 0) {
685 				sp->str = GET_MSG_STR(a);
686 			} else {
687 				sp->str = va_arg(ap, char *);
688 			}
689 			len += (strlen(sp->str));
690 			sp->next = NULL;
691 			tailp->next = sp;
692 			tailp = sp;
693 		}
694 	}
695 
696 	len += 1;	/* terminating NULL */
697 
698 	s = t = NULL;
699 	if (l_errno) {
700 		s = dgettext(TEXT_DOMAIN, ": ");
701 		t = S_STR(strerror(l_errno));
702 		if (s != NULL && t != NULL) {
703 			len += strlen(s) + strlen(t);
704 		}
705 	}
706 
707 	if (append_newline) {
708 		len++;
709 	}
710 
711 	if ((*msgpp = calloc(1, len)) == NULL) {
712 		goto out;
713 	}
714 
715 	**msgpp = '\0';
716 	for (sp = dummy.next; sp != NULL; sp = sp->next) {
717 		(void) strcat(*msgpp, sp->str);
718 	}
719 
720 	if (s != NULL && t != NULL) {
721 		(void) strcat(*msgpp, s);
722 		(void) strcat(*msgpp, t);
723 	}
724 
725 	if (append_newline) {
726 		(void) strcat(*msgpp, dgettext(TEXT_DOMAIN, "\n"));
727 	}
728 
729 	/* FALLTHROUGH */
730 out:
731 	sp = dummy.next;
732 	while (sp != NULL) {
733 		savep = sp->next;
734 		S_FREE(sp);
735 		sp = savep;
736 	}
737 }
738 
739 /*
740  * Check to see if the given pi_node is the last path to the client device.
741  *
742  * Return:
743  *	0: if there is another path avialable.
744  *	-1: if no other paths available.
745  */
746 static int
747 check_available_path(
748 	di_node_t client_node,
749 	di_path_t pi_node)
750 {
751 	di_path_state_t pi_state;
752 	di_path_t   next_pi = DI_PATH_NIL;
753 
754 	if (((pi_state = di_path_state(pi_node)) != DI_PATH_STATE_ONLINE) &&
755 	    (pi_state != DI_PATH_STATE_STANDBY)) {
756 		/* it is not last available path */
757 		return (0);
758 	}
759 
760 	while (next_pi = di_path_client_next_path(client_node, next_pi)) {
761 		/* if anohter pi node is avaialble, return 0 */
762 		if ((next_pi != pi_node) &&
763 		    (((pi_state = di_path_state(next_pi)) ==
764 		    DI_PATH_STATE_ONLINE) ||
765 		    pi_state == DI_PATH_STATE_STANDBY)) {
766 			return (0);
767 		}
768 	}
769 	return (-1);
770 }
771 
772 scfga_ret_t
773 path_apid_state_change(
774 	apid_t		*apidp,
775 	scfga_cmd_t	cmd,
776 	cfga_flags_t	flags,
777 	char		**errstring,
778 	int		*l_errnop,
779 	msgid_t		errid)
780 {
781 	di_node_t   root, walk_root, client_node;
782 	di_path_t   pi_node = DI_PATH_NIL;
783 	char	    *root_path, *cp, *client_path, devpath[MAXPATHLEN];
784 	int	    len, found = 0;
785 	scfga_ret_t ret;
786 	char *dev_list[2] = {NULL};
787 
788 	*l_errnop = 0;
789 
790 	/* Make sure apid is pathinfo associated apid. */
791 	if ((apidp->dyntype != PATH_APID) || (apidp->dyncomp == NULL)) {
792 		return (SCFGA_LIB_ERR);
793 	}
794 
795 	if ((cmd != SCFGA_DEV_CONFIGURE) && (cmd != SCFGA_DEV_UNCONFIGURE)) {
796 		return (SCFGA_LIB_ERR);
797 	}
798 
799 	if ((root_path = strdup(apidp->hba_phys)) == NULL) {
800 		*l_errnop = errno;
801 		return (SCFGA_LIB_ERR);
802 	}
803 
804 	/* Fix up path for di_init() */
805 	len = strlen(DEVICES_DIR);
806 	if (strncmp(root_path, DEVICES_DIR SLASH,
807 	    len + strlen(SLASH)) == 0) {
808 		cp = root_path + len;
809 		(void) memmove(root_path, cp, strlen(cp) + 1);
810 	} else if (*root_path != '/') {
811 		*l_errnop = 0;
812 		S_FREE(root_path);
813 		return (SCFGA_ERR);
814 	}
815 
816 	/* Remove dynamic component if any */
817 	if ((cp = GET_DYN(root_path)) != NULL) {
818 		*cp = '\0';
819 	}
820 
821 	/* Remove minor name if any */
822 	if ((cp = strrchr(root_path, ':')) != NULL) {
823 		*cp = '\0';
824 	}
825 
826 	/*
827 	 * Cached snapshots are always rooted at "/"
828 	 */
829 
830 	/* Get a snapshot */
831 	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
832 		*l_errnop = errno;
833 		S_FREE(root_path);
834 		return (SCFGA_ERR);
835 	}
836 
837 	/*
838 	 * Lookup the subtree of interest
839 	 */
840 	walk_root = di_lookup_node(root, root_path);
841 
842 	if (walk_root == DI_NODE_NIL) {
843 		*l_errnop = errno;
844 		di_fini(root);
845 		S_FREE(root_path);
846 		return (SCFGA_LIB_ERR);
847 	}
848 
849 
850 	if ((pi_node = di_path_next_client(walk_root, pi_node)) ==
851 	    DI_PATH_NIL) {
852 		/* the path apid not found */
853 		di_fini(root);
854 		S_FREE(root_path);
855 		return (SCFGA_APID_NOEXIST);
856 	}
857 
858 	do {
859 		/* check the length first. */
860 		if (strlen(di_path_bus_addr(pi_node)) !=
861 		    strlen(apidp->dyncomp)) {
862 			continue;
863 		}
864 
865 		/* compare bus addr. */
866 		if (strcmp(di_path_bus_addr(pi_node), apidp->dyncomp) == 0) {
867 			found = 1;
868 			break;
869 		}
870 		pi_node = di_path_next_client(root, pi_node);
871 	} while (pi_node != DI_PATH_NIL);
872 
873 	if (!found) {
874 		di_fini(root);
875 		S_FREE(root_path);
876 		return (SCFGA_APID_NOEXIST);
877 	}
878 
879 	/* Get client node path. */
880 	client_node = di_path_client_node(pi_node);
881 	if (client_node == DI_NODE_NIL) {
882 		di_fini(root);
883 		S_FREE(root_path);
884 		return (SCFGA_ERR);
885 	} else {
886 		client_path = di_devfs_path(client_node);
887 		if (client_path == NULL) {
888 			di_fini(root);
889 			S_FREE(root_path);
890 			return (SCFGA_ERR);
891 		}
892 
893 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
894 			if (cmd == SCFGA_DEV_UNCONFIGURE) {
895 				if (check_available_path(client_node,
896 				    pi_node) != 0) {
897 					/*
898 					 * last path. check if unconfiguring
899 					 * is okay.
900 					 */
901 					(void) snprintf(devpath,
902 					    strlen(DEVICES_DIR) +
903 					    strlen(client_path) + 1, "%s%s",
904 					    DEVICES_DIR, client_path);
905 					dev_list[0] = devpath;
906 					flags |= FLAG_CLIENT_DEV;
907 					ret = scsi_rcm_offline(dev_list,
908 					    errstring, flags);
909 					if (ret != SCFGA_OK) {
910 						di_fini(root);
911 						di_devfs_path_free(client_path);
912 						S_FREE(root_path);
913 						return (ret);
914 					}
915 				}
916 			}
917 		}
918 	}
919 
920 	ret = devctl_cmd(apidp->path, cmd, NULL, l_errnop);
921 	if (ret != SCFGA_OK) {
922 		cfga_err(errstring, *l_errnop, errid, 0);
923 
924 		/*
925 		 * If an unconfigure fails, cancel the RCM offline.
926 		 * Discard any RCM failures so that the devctl
927 		 * failure will still be reported.
928 		 */
929 		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
930 			if (cmd == SCFGA_DEV_UNCONFIGURE)
931 				(void) scsi_rcm_online(dev_list,
932 				    errstring, flags);
933 		}
934 	}
935 
936 	di_devfs_path_free(client_path);
937 	di_fini(root);
938 	S_FREE(root_path);
939 
940 	return (ret);
941 }
942 
943 
944 scfga_ret_t
945 devctl_cmd(
946 	const char	*physpath,
947 	scfga_cmd_t	cmd,
948 	uint_t		*statep,
949 	int		*l_errnop)
950 {
951 	int rv = -1, i, type;
952 	devctl_hdl_t hdl = NULL;
953 	char *cp = NULL, *path = NULL;
954 	int (*func)(const devctl_hdl_t);
955 	int (*state_func)(const devctl_hdl_t, uint_t *);
956 
957 	*l_errnop = 0;
958 
959 	if (statep != NULL) *statep = 0;
960 
961 	func = NULL;
962 	state_func = NULL;
963 	type = 0;
964 
965 	for (i = 0; i < N_GET_STATE_CMDS; i++) {
966 		if (get_state_cmds[i].cmd == cmd) {
967 			state_func = get_state_cmds[i].state_fcn;
968 			type = get_state_cmds[i].type;
969 			assert(statep != NULL);
970 			break;
971 		}
972 	}
973 
974 	if (state_func == NULL) {
975 		for (i = 0; i < N_SET_STATE_CMDS; i++) {
976 			if (set_state_cmds[i].cmd == cmd) {
977 				func = set_state_cmds[i].fcn;
978 				type = set_state_cmds[i].type;
979 				assert(statep == NULL);
980 				break;
981 			}
982 		}
983 	}
984 
985 	assert(type == BUS_OP || type == DEV_OP);
986 
987 	if (func == NULL && state_func == NULL) {
988 		return (SCFGA_ERR);
989 	}
990 
991 	/*
992 	 * Fix up path for calling devctl.
993 	 */
994 	if ((path = strdup(physpath)) == NULL) {
995 		*l_errnop = errno;
996 		return (SCFGA_LIB_ERR);
997 	}
998 
999 	/* Remove dynamic component if any */
1000 	if ((cp = GET_DYN(path)) != NULL) {
1001 		*cp = '\0';
1002 	}
1003 
1004 	/* Remove minor name */
1005 	if ((cp = strrchr(path, ':')) != NULL) {
1006 		*cp = '\0';
1007 	}
1008 
1009 	errno = 0;
1010 
1011 	if (type == BUS_OP) {
1012 		hdl = devctl_bus_acquire(path, 0);
1013 	} else {
1014 		hdl = devctl_device_acquire(path, 0);
1015 	}
1016 	*l_errnop = errno;
1017 
1018 	S_FREE(path);
1019 
1020 	if (hdl == NULL) {
1021 		return (SCFGA_ERR);
1022 	}
1023 
1024 	errno = 0;
1025 	/* Only getstate functions require a second argument */
1026 	if (func != NULL && statep == NULL) {
1027 		rv = func(hdl);
1028 		*l_errnop = errno;
1029 	} else if (state_func != NULL && statep != NULL) {
1030 		rv = state_func(hdl, statep);
1031 		*l_errnop = errno;
1032 	} else {
1033 		rv = -1;
1034 		*l_errnop = 0;
1035 	}
1036 
1037 	devctl_release(hdl);
1038 
1039 	return ((rv == -1) ? SCFGA_ERR : SCFGA_OK);
1040 }
1041 
1042 /*
1043  * Is device in a known state ? (One of BUSY, ONLINE, OFFLINE)
1044  *	BUSY --> One or more device special files are open. Implies online
1045  *	ONLINE --> driver attached
1046  *	OFFLINE --> CF1 with offline flag set.
1047  *	UNKNOWN --> None of the above
1048  */
1049 int
1050 known_state(di_node_t node)
1051 {
1052 	uint_t state;
1053 
1054 	state = di_state(node);
1055 
1056 	/*
1057 	 * CF1 without offline flag set is considered unknown state.
1058 	 * We are in a known state if either CF2 (driver attached) or
1059 	 * offline.
1060 	 */
1061 	if ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
1062 	    (state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
1063 		return (1);
1064 	}
1065 
1066 	return (0);
1067 }
1068 
1069 void
1070 list_free(ldata_list_t **llpp)
1071 {
1072 	ldata_list_t *lp, *olp;
1073 
1074 	lp = *llpp;
1075 	while (lp != NULL) {
1076 		olp = lp;
1077 		lp = olp->next;
1078 		S_FREE(olp);
1079 	}
1080 
1081 	*llpp = NULL;
1082 }
1083 
1084 /*
1085  * Obtain the devlink from a /devices path
1086  */
1087 typedef struct walk_link {
1088 	char *path;
1089 	char len;
1090 	char **linkpp;
1091 } walk_link_t;
1092 
1093 static int
1094 get_link(di_devlink_t devlink, void *arg)
1095 {
1096 	walk_link_t *larg = (walk_link_t *)arg;
1097 
1098 	/*
1099 	 * When path is specified, it's the node path without minor
1100 	 * name. Therefore, the ../.. prefixes needs to be stripped.
1101 	 */
1102 	if (larg->path) {
1103 		char *content = (char *)di_devlink_content(devlink);
1104 		char *start = strstr(content, "/devices/");
1105 
1106 		/* line content must have minor node */
1107 		if (start == NULL ||
1108 		    strncmp(start, larg->path, larg->len) != 0 ||
1109 		    start[larg->len] != ':')
1110 			return (DI_WALK_CONTINUE);
1111 	}
1112 
1113 	*(larg->linkpp) = strdup(di_devlink_path(devlink));
1114 	return (DI_WALK_TERMINATE);
1115 }
1116 
1117 scfga_ret_t
1118 physpath_to_devlink(
1119 	char *node_path,
1120 	char **logpp,
1121 	int *l_errnop,
1122 	int match_minor)
1123 {
1124 	walk_link_t larg;
1125 	di_devlink_handle_t hdl;
1126 	char *minor_path;
1127 
1128 	if ((hdl = di_devlink_init(NULL, 0)) == NULL) {
1129 		*l_errnop = errno;
1130 		return (SCFGA_LIB_ERR);
1131 	}
1132 
1133 	*logpp = NULL;
1134 	larg.linkpp = logpp;
1135 	if (match_minor) {
1136 		minor_path = node_path + strlen(DEVICES_DIR);
1137 		larg.path = NULL;
1138 	} else {
1139 		minor_path = NULL;
1140 		larg.len = strlen(node_path);
1141 		larg.path = node_path;
1142 	}
1143 
1144 	(void) di_devlink_walk(hdl, NULL, minor_path, DI_PRIMARY_LINK,
1145 	    (void *)&larg, get_link);
1146 
1147 	(void) di_devlink_fini(&hdl);
1148 
1149 	if (*logpp == NULL)
1150 		return (SCFGA_LIB_ERR);
1151 
1152 	return (SCFGA_OK);
1153 }
1154 
1155 int
1156 hba_dev_cmp(const char *hba, const char *devpath)
1157 {
1158 	char *cp = NULL;
1159 	int rv;
1160 	size_t hba_len, dev_len;
1161 	char l_hba[MAXPATHLEN], l_dev[MAXPATHLEN];
1162 
1163 	(void) snprintf(l_hba, sizeof (l_hba), "%s", hba);
1164 	(void) snprintf(l_dev, sizeof (l_dev), "%s", devpath);
1165 
1166 	/* Remove dynamic component if any */
1167 	if ((cp = GET_DYN(l_hba)) != NULL) {
1168 		*cp = '\0';
1169 	}
1170 
1171 	if ((cp = GET_DYN(l_dev)) != NULL) {
1172 		*cp = '\0';
1173 	}
1174 
1175 
1176 	/* Remove minor names */
1177 	if ((cp = strrchr(l_hba, ':')) != NULL) {
1178 		*cp = '\0';
1179 	}
1180 
1181 	if ((cp = strrchr(l_dev, ':')) != NULL) {
1182 		*cp = '\0';
1183 	}
1184 
1185 	hba_len = strlen(l_hba);
1186 	dev_len = strlen(l_dev);
1187 
1188 	/* Check if HBA path is component of device path */
1189 	if (rv = strncmp(l_hba, l_dev, hba_len)) {
1190 		return (rv);
1191 	}
1192 
1193 	/* devpath must have '/' and 1 char in addition to hba path */
1194 	if (dev_len >= hba_len + 2 && l_dev[hba_len] == '/') {
1195 		return (0);
1196 	} else {
1197 		return (-1);
1198 	}
1199 }
1200 
1201 int
1202 dev_cmp(const char *dev1, const char *dev2, int match_minor)
1203 {
1204 	char l_dev1[MAXPATHLEN], l_dev2[MAXPATHLEN];
1205 	char *mn1, *mn2;
1206 	int rv;
1207 
1208 	(void) snprintf(l_dev1, sizeof (l_dev1), "%s", dev1);
1209 	(void) snprintf(l_dev2, sizeof (l_dev2), "%s", dev2);
1210 
1211 	if ((mn1 = GET_DYN(l_dev1)) != NULL) {
1212 		*mn1 = '\0';
1213 	}
1214 
1215 	if ((mn2 = GET_DYN(l_dev2)) != NULL) {
1216 		*mn2 = '\0';
1217 	}
1218 
1219 	/* Separate out the minor names */
1220 	if ((mn1 = strrchr(l_dev1, ':')) != NULL) {
1221 		*mn1++ = '\0';
1222 	}
1223 
1224 	if ((mn2 = strrchr(l_dev2, ':')) != NULL) {
1225 		*mn2++ = '\0';
1226 	}
1227 
1228 	if ((rv = strcmp(l_dev1, l_dev2)) != 0 || !match_minor) {
1229 		return (rv);
1230 	}
1231 
1232 	/*
1233 	 * Compare minor names
1234 	 */
1235 	if (mn1 == NULL && mn2 == NULL) {
1236 		return (0);
1237 	} else if (mn1 == NULL) {
1238 		return (-1);
1239 	} else if (mn2 == NULL) {
1240 		return (1);
1241 	} else {
1242 		return (strcmp(mn1, mn2));
1243 	}
1244 }
1245