xref: /illumos-gate/usr/src/lib/cfgadm_plugins/fp/common/cfga_utils.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 #include "cfga_fp.h"
28 
29 /*
30  * This file contains helper routines for the FP plugin
31  */
32 
33 #if !defined(TEXT_DOMAIN)
34 #define	TEXT_DOMAIN	"SYS_TEST"
35 #endif
36 
37 typedef struct strlist {
38 	const char *str;
39 	struct strlist *next;
40 } strlist_t;
41 
42 typedef	struct {
43 	fpcfga_ret_t	fp_err;
44 	cfga_err_t	cfga_err;
45 } errcvt_t;
46 
47 typedef struct {
48 	fpcfga_cmd_t cmd;
49 	int type;
50 	int (*fcn)(const devctl_hdl_t);
51 } set_state_cmd_t;
52 
53 typedef struct {
54 	fpcfga_cmd_t cmd;
55 	int type;
56 	int (*state_fcn)(const devctl_hdl_t, uint_t *);
57 } get_state_cmd_t;
58 
59 /* defines for nftw() */
60 #define	NFTW_DEPTH	1
61 #define	NFTW_CONTINUE	0
62 #define	NFTW_TERMINATE	1
63 #define	NFTW_ERROR	-1
64 #define	MAX_RETRIES	10
65 
66 /* Function prototypes */
67 static int do_recurse_dev(const char *path, const struct stat *sbuf,
68     int type, struct FTW *ftwp);
69 static fpcfga_recur_t lookup_dev(const char *lpath, void *arg);
70 static void msg_common(char **err_msgpp, int append_newline, int l_errno,
71     va_list ap);
72 static void lunlist_free(struct luninfo_list *lunlist);
73 
74 /* Globals */
75 struct {
76 	mutex_t mp;
77 	void *arg;
78 	fpcfga_recur_t (*fcn)(const char *lpath, void *arg);
79 } nftw_arg = {DEFAULTMUTEX};
80 
81 /*
82  * The string table contains most of the strings used by the fp cfgadm plugin.
83  * All strings which are to be internationalized must be in this table.
84  * Some strings which are not internationalized are also included here.
85  * Arguments to messages are NOT internationalized.
86  */
87 msgcvt_t str_tbl[] = {
88 
89 /*
90  * The first element (ERR_UNKNOWN) MUST always be present in the array.
91  */
92 #define	UNKNOWN_ERR_IDX		0	/* Keep the index in sync */
93 
94 
95 /* msg_code	num_args, I18N	msg_string				*/
96 
97 /* ERRORS */
98 {ERR_UNKNOWN,		0, 1,	"unknown error"},
99 {ERR_OP_FAILED,		0, 1,	"operation failed"},
100 {ERR_CMD_INVAL,		0, 1,	"invalid command"},
101 {ERR_NOT_BUSAPID,	0, 1,	"not a FP bus apid"},
102 {ERR_APID_INVAL,	0, 1,	"invalid FP ap_id"},
103 {ERR_NOT_BUSOP,		0, 1,	"operation not supported for FC bus"},
104 {ERR_NOT_DEVOP,		0, 1,	"operation not supported for FC device"},
105 {ERR_UNAVAILABLE,	0, 1,	"unavailable"},
106 {ERR_CTRLR_CRIT,	0, 1,	"critical partition controlled by FC HBA"},
107 {ERR_BUS_GETSTATE,	0, 1,	"failed to get state for FC bus"},
108 {ERR_BUS_NOTCONNECTED,	0, 1,	"FC bus not connected"},
109 {ERR_BUS_CONNECTED,	0, 1,	"FC bus not disconnected"},
110 {ERR_BUS_QUIESCE,	0, 1,	"FC bus quiesce failed"},
111 {ERR_BUS_UNQUIESCE,	0, 1,	"FC bus unquiesce failed"},
112 {ERR_BUS_CONFIGURE,	0, 1,	"failed to configure devices on FC bus"},
113 {ERR_BUS_UNCONFIGURE,	0, 1,	"failed to unconfigure FC bus"},
114 {ERR_DEV_CONFIGURE,	0, 1,	"failed to configure FC device"},
115 {ERR_DEV_UNCONFIGURE,	0, 1,	"failed to unconfigure FC device"},
116 {ERR_FCA_CONFIGURE,	0, 1,	"failed to configure ANY device on FCA port"},
117 {ERR_FCA_UNCONFIGURE,	0, 1,	"failed to unconfigure ANY device on FCA port"},
118 {ERR_DEV_REPLACE,	0, 1,	"replace operation failed"},
119 {ERR_DEV_INSERT,	0, 1,	"insert operation failed"},
120 {ERR_DEV_GETSTATE,	0, 1,	"failed to get state for FC device"},
121 {ERR_RESET,		0, 1,	"reset failed"},
122 {ERR_LIST,		0, 1,	"list operation failed"},
123 {ERR_SIG_STATE,		0, 1,	"could not restore signal disposition"},
124 {ERR_MAYBE_BUSY,	0, 1,	"device may be busy"},
125 {ERR_BUS_DEV_MISMATCH,	0, 1,	"mismatched FC bus and device"},
126 {ERR_MEM_ALLOC,		0, 1,	"Failed to allocated memory"},
127 {ERR_DEVCTL_OFFLINE,	0, 1,	"failed to offline device"},
128 {ERR_UPD_REP,		0, 1,	"Repository update failed"},
129 {ERR_CONF_OK_UPD_REP,	0, 1,
130 		"Configuration successful, but Repository update failed"},
131 {ERR_UNCONF_OK_UPD_REP,	0, 1,
132 		"Unconfiguration successful, but Repository update failed"},
133 {ERR_PARTIAL_SUCCESS,	0, 1,
134 			"Operation partially successful. Some failures seen"},
135 {ERR_HBA_LOAD_LIBRARY,	0, 1,
136 			"HBA load library failed"},
137 {ERR_MATCHING_HBA_PORT,	0, 1,
138 			"No match HBA port found"},
139 {ERR_NO_ADAPTER_FOUND,	0, 1,
140 			"No Fibre Channel adpaters found"},
141 
142 /* Errors with arguments */
143 {ERRARG_OPT_INVAL,	1, 1,	"invalid option: "},
144 {ERRARG_HWCMD_INVAL,	1, 1,	"invalid command: "},
145 {ERRARG_DEVINFO,	1, 1,	"libdevinfo failed on path: "},
146 {ERRARG_NOT_IN_DEVLIST,	1, 1,	"Device not found in fabric device list: "},
147 {ERRARG_NOT_IN_DEVINFO,	1, 1,	"Could not find entry in devinfo tree: "},
148 {ERRARG_DI_GET_PROP,	1, 1,	"Could not get libdevinfo property: "},
149 {ERRARG_DC_DDEF_ALLOC,	1, 1,	"failed to alloc ddef space: "},
150 {ERRARG_DC_BYTE_ARRAY,	1, 1,	"failed to add property: "},
151 {ERRARG_DC_BUS_ACQUIRE,	1, 1,	"failed to acquire bus handle: "},
152 {ERRARG_BUS_DEV_CREATE,	1, 1,	"failed to create device node: "},
153 {ERRARG_BUS_DEV_CREATE_UNKNOWN,	1, 1,
154 	"failed to create device node... Device may be unconfigurable: "},
155 {ERRARG_DEV_ACQUIRE,	1, 1,	"device acquire operation failed: "},
156 {ERRARG_DEV_REMOVE,	1, 1,	"remove operation failed: "},
157 
158 /* Fibre Channel operation Errors */
159 {ERR_FC,		0, 1,	"FC error"},
160 {ERR_FC_GET_DEVLIST,	0, 1,	"Failed to get fabric device list"},
161 {ERR_FC_GET_NEXT_DEV,	0, 1,	"Failed to get next device on device map"},
162 {ERR_FC_GET_FIRST_DEV,	0, 1,	"Failed to get first device on device map"},
163 {ERRARG_FC_DEV_MAP_INIT,	1, 1,
164 	"Failed to initialize device map for: "},
165 {ERRARG_FC_PROP_LOOKUP_BYTES,	1, 1,	"Failed to get property of "},
166 {ERRARG_FC_INQUIRY,	1, 1,	"inquiry failed: "},
167 {ERRARG_FC_REP_LUNS,	1, 1,	"report LUNs failed: "},
168 {ERRARG_FC_TOPOLOGY,	1, 1,	"failed to get port topology: "},
169 {ERRARG_PATH_TOO_LONG,	1, 1,	"Path length exceeds max possible: "},
170 {ERRARG_INVALID_PATH,	1, 1,	"Invalid path: "},
171 {ERRARG_OPENDIR,	1, 1,	"failure opening directory: "},
172 
173 /* MPXIO Errors */
174 {ERRARG_VHCI_GET_PATHLIST,	1, 1,	"failed to get path list from vHCI: "},
175 {ERRARG_XPORT_NOT_IN_PHCI_LIST,	1, 1,	"Transport not in pHCI list: "},
176 
177 /* RCM Errors */
178 {ERR_RCM_HANDLE,	0, 1,	"cannot get RCM handle"},
179 {ERRARG_RCM_SUSPEND,	1, 1,	"failed to suspend: "},
180 {ERRARG_RCM_RESUME,	1, 1,	"failed to resume: "},
181 {ERRARG_RCM_OFFLINE,	1, 1,	"failed to offline: "},
182 {ERRARG_RCM_ONLINE,	1, 1,	"failed to online: "},
183 {ERRARG_RCM_REMOVE,	1, 1,	"failed to remove: "},
184 {ERRARG_RCM_INFO,	1, 1,	"failed to query: "},
185 
186 /* Commands */
187 {CMD_INSERT_DEV,	0, 0,	"insert_device"},
188 {CMD_REMOVE_DEV,	0, 0,	"remove_device"},
189 {CMD_REPLACE_DEV,	0, 0,	"replace_device"},
190 {CMD_RESET_DEV,		0, 0,	"reset_device"},
191 {CMD_RESET_BUS,		0, 0,	"reset_bus"},
192 {CMD_RESET_ALL,		0, 0,	"reset_all"},
193 
194 /* help messages */
195 {MSG_HELP_HDR,		0, 1,	"\nfp attachment point specific options:\n"},
196 {MSG_HELP_USAGE,	0, 0,
197 		"\t-c configure -o force_update ap_id [ap_id..]\n"
198 		"\t-c configure -o no_update ap_id [ap_id...]\n"
199 		"\t-c unconfigure -o force_update ap_id [ap_id... ]\n"
200 		"\t-c unconfigure -o no_update ap_id [ap_id... ]\n"},
201 
202 /* hotplug messages */
203 {MSG_INSDEV,		1, 1,	"Adding device to FC HBA: "},
204 {MSG_RMDEV,		1, 1,	"Removing FC device: "},
205 {MSG_REPLDEV,		1, 1,	"Replacing FC device: "},
206 
207 /* Hotplugging confirmation prompts */
208 {CONF_QUIESCE_1,	1, 1,
209 	"This operation will suspend activity on FC bus: "},
210 
211 {CONF_QUIESCE_2,	0, 1,	"\nContinue"},
212 
213 {CONF_UNQUIESCE,	0, 1,
214 	"FC bus quiesced successfully.\n"
215 	"It is now safe to proceed with hotplug operation."
216 	"\nEnter y if operation is complete or n to abort"},
217 
218 /* Misc. */
219 {WARN_DISCONNECT,	0, 1,
220 	"WARNING: Disconnecting critical partitions may cause system hang."
221 	"\nContinue"}
222 };
223 
224 
225 #define	N_STRS	(sizeof (str_tbl) / sizeof (str_tbl[0]))
226 
227 #define	GET_MSG_NARGS(i)	(str_tbl[msg_idx(i)].nargs)
228 #define	GET_MSG_INTL(i)		(str_tbl[msg_idx(i)].intl)
229 
230 static errcvt_t err_cvt_tbl[] = {
231 	{ FPCFGA_OK,		CFGA_OK			},
232 	{ FPCFGA_LIB_ERR,	CFGA_LIB_ERROR		},
233 	{ FPCFGA_APID_NOEXIST,	CFGA_APID_NOEXIST	},
234 	{ FPCFGA_NACK,		CFGA_NACK		},
235 	{ FPCFGA_BUSY,		CFGA_BUSY		},
236 	{ FPCFGA_SYSTEM_BUSY,	CFGA_SYSTEM_BUSY	},
237 	{ FPCFGA_OPNOTSUPP,	CFGA_OPNOTSUPP		},
238 	{ FPCFGA_PRIV,		CFGA_PRIV		},
239 	{ FPCFGA_UNKNOWN_ERR,	CFGA_ERROR		},
240 	{ FPCFGA_ERR,		CFGA_ERROR		}
241 };
242 
243 #define	N_ERR_CVT_TBL	(sizeof (err_cvt_tbl)/sizeof (err_cvt_tbl[0]))
244 
245 #define	DEV_OP	0
246 #define	BUS_OP	1
247 static set_state_cmd_t set_state_cmds[] = {
248 
249 { FPCFGA_BUS_QUIESCE,		BUS_OP,		devctl_bus_quiesce	},
250 { FPCFGA_BUS_UNQUIESCE,		BUS_OP,		devctl_bus_unquiesce	},
251 { FPCFGA_BUS_CONFIGURE,		BUS_OP,		devctl_bus_configure	},
252 { FPCFGA_BUS_UNCONFIGURE, 	BUS_OP,		devctl_bus_unconfigure	},
253 { FPCFGA_RESET_BUS,		BUS_OP,		devctl_bus_reset	},
254 { FPCFGA_RESET_ALL, 		BUS_OP,		devctl_bus_resetall	},
255 { FPCFGA_DEV_CONFIGURE,		DEV_OP,		devctl_device_online	},
256 { FPCFGA_DEV_UNCONFIGURE,	DEV_OP,		devctl_device_offline	},
257 { FPCFGA_DEV_REMOVE,		DEV_OP,		devctl_device_remove	},
258 { FPCFGA_RESET_DEV,		DEV_OP,		devctl_device_reset	}
259 
260 };
261 
262 #define	N_SET_STATE_CMDS (sizeof (set_state_cmds)/sizeof (set_state_cmds[0]))
263 
264 static get_state_cmd_t get_state_cmds[] = {
265 { FPCFGA_BUS_GETSTATE,		BUS_OP,		devctl_bus_getstate	},
266 { FPCFGA_DEV_GETSTATE,		DEV_OP,		devctl_device_getstate	}
267 };
268 
269 #define	N_GET_STATE_CMDS (sizeof (get_state_cmds)/sizeof (get_state_cmds[0]))
270 
271 /* Order is important. Earlier directories are searched first */
272 static const char *dev_dir_hints[] = {
273 	CFGA_DEV_DIR,
274 	DEV_RMT,
275 	DEV_DSK,
276 	DEV_RDSK,
277 	DEV_DIR
278 };
279 
280 #define	N_DEV_DIR_HINTS	(sizeof (dev_dir_hints) / sizeof (dev_dir_hints[0]))
281 
282 
283 /*
284  * Routine to search the /dev directory or a subtree of /dev.
285  * If the entire /dev hierarchy is to be searched, the most likely directories
286  * are searched first.
287  */
288 fpcfga_ret_t
289 recurse_dev(
290 	const char	*basedir,
291 	void		*arg,
292 	fpcfga_recur_t (*fcn)(const char *lpath, void *arg))
293 {
294 	int i, rv = NFTW_ERROR;
295 
296 	(void) mutex_lock(&nftw_arg.mp);
297 
298 	nftw_arg.arg = arg;
299 	nftw_arg.fcn = fcn;
300 
301 	if (strcmp(basedir, DEV_DIR)) {
302 		errno = 0;
303 		rv = nftw(basedir, do_recurse_dev, NFTW_DEPTH, FTW_PHYS);
304 		goto out;
305 	}
306 
307 	/*
308 	 * Search certain selected subdirectories first if basedir == "/dev".
309 	 * Ignore errors as some of these directories may not exist.
310 	 */
311 	for (i = 0; i < N_DEV_DIR_HINTS; i++) {
312 		errno = 0;
313 		if ((rv = nftw(dev_dir_hints[i], do_recurse_dev, NFTW_DEPTH,
314 		    FTW_PHYS)) == NFTW_TERMINATE) {
315 			break;
316 		}
317 	}
318 
319 	/*FALLTHRU*/
320 out:
321 	(void) mutex_unlock(&nftw_arg.mp);
322 	return (rv == NFTW_ERROR ? FPCFGA_ERR : FPCFGA_OK);
323 }
324 
325 /*ARGSUSED*/
326 static int
327 do_recurse_dev(
328 	const char *path,
329 	const struct stat *sbuf,
330 	int type,
331 	struct FTW *ftwp)
332 {
333 	/* We want only VALID symlinks */
334 	if (type != FTW_SL) {
335 		return (NFTW_CONTINUE);
336 	}
337 
338 	assert(nftw_arg.fcn != NULL);
339 
340 	if (nftw_arg.fcn(path, nftw_arg.arg) == FPCFGA_TERMINATE) {
341 		/* terminate prematurely, but may not be error */
342 		errno = 0;
343 		return (NFTW_TERMINATE);
344 	} else {
345 		return (NFTW_CONTINUE);
346 	}
347 }
348 
349 cfga_err_t
350 err_cvt(fpcfga_ret_t fp_err)
351 {
352 	int i;
353 
354 	for (i = 0; i < N_ERR_CVT_TBL; i++) {
355 		if (err_cvt_tbl[i].fp_err == fp_err) {
356 			return (err_cvt_tbl[i].cfga_err);
357 		}
358 	}
359 
360 	return (CFGA_ERROR);
361 }
362 
363 /*
364  * Removes duplicate slashes from a pathname and any trailing slashes.
365  * Returns "/" if input is "/"
366  */
367 char *
368 pathdup(const char *path, int *l_errnop)
369 {
370 	int prev_was_slash = 0;
371 	char c, *dp = NULL, *dup = NULL;
372 	const char *sp = NULL;
373 
374 	*l_errnop = 0;
375 
376 	if (path == NULL) {
377 		return (NULL);
378 	}
379 
380 	if ((dup = calloc(1, strlen(path) + 1)) == NULL) {
381 		*l_errnop = errno;
382 		return (NULL);
383 	}
384 
385 	prev_was_slash = 0;
386 	for (sp = path, dp = dup; (c = *sp) != '\0'; sp++) {
387 		if (!prev_was_slash || c != '/') {
388 			*dp++ = c;
389 		}
390 		if (c == '/') {
391 			prev_was_slash = 1;
392 		} else {
393 			prev_was_slash = 0;
394 		}
395 	}
396 
397 	/* Remove trailing slash except if it is the first char */
398 	if (prev_was_slash && dp != dup && dp - 1 != dup) {
399 		*(--dp) = '\0';
400 	} else {
401 		*dp = '\0';
402 	}
403 
404 	return (dup);
405 }
406 
407 fpcfga_ret_t
408 apidt_create(const char *ap_id, apid_t *apidp, char **errstring)
409 {
410 	char *xport_phys = NULL, *dyn = NULL;
411 	char *dyncomp = NULL;
412 	struct luninfo_list *lunlistp = NULL;
413 	int l_errno = 0;
414 	size_t len = 0;
415 	fpcfga_ret_t ret;
416 
417 	if ((xport_phys = pathdup(ap_id, &l_errno)) == NULL) {
418 		cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
419 		return (FPCFGA_LIB_ERR);
420 	}
421 
422 	/* Extract the base(hba) and dynamic(device) component if any */
423 	dyncomp = NULL;
424 	if ((dyn = GET_DYN(xport_phys)) != NULL) {
425 		len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
426 		dyncomp = calloc(1, len);
427 		if (dyncomp == NULL) {
428 			cfga_err(errstring, errno, ERR_OP_FAILED, 0);
429 			ret = FPCFGA_LIB_ERR;
430 			goto err;
431 		}
432 		(void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
433 		if (GET_LUN_DYN(dyncomp)) {
434 			ret = FPCFGA_APID_NOEXIST;
435 			goto err;
436 		}
437 
438 		/* Remove the dynamic component from the base. */
439 		*dyn = '\0';
440 	}
441 
442 	/* Get the path of dynamic attachment point if already configured. */
443 	if (dyncomp != NULL) {
444 		ret = dyn_apid_to_path(xport_phys, dyncomp,
445 				&lunlistp, &l_errno);
446 		if ((ret != FPCFGA_OK) && (ret != FPCFGA_APID_NOCONFIGURE)) {
447 			cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
448 			goto err;
449 		}
450 	}
451 
452 	assert(xport_phys != NULL);
453 
454 	apidp->xport_phys = xport_phys;
455 	apidp->dyncomp = dyncomp;
456 	apidp->lunlist = lunlistp;
457 	apidp->flags = 0;
458 
459 	return (FPCFGA_OK);
460 
461 err:
462 	S_FREE(xport_phys);
463 	S_FREE(dyncomp);
464 	lunlist_free(lunlistp);
465 	return (ret);
466 }
467 
468 static void
469 lunlist_free(struct luninfo_list *lunlist)
470 {
471 struct luninfo_list *lunp;
472 
473 	while (lunlist != NULL) {
474 		lunp = lunlist->next;
475 		S_FREE(lunlist->path);
476 		S_FREE(lunlist);
477 		lunlist = lunp;
478 	}
479 }
480 
481 void
482 apidt_free(apid_t *apidp)
483 {
484 	if (apidp == NULL)
485 		return;
486 
487 	S_FREE(apidp->xport_phys);
488 	S_FREE(apidp->dyncomp);
489 	lunlist_free(apidp->lunlist);
490 }
491 
492 fpcfga_ret_t
493 walk_tree(
494 	const char	*physpath,
495 	void		*arg,
496 	uint_t		init_flags,
497 	walkarg_t	*up,
498 	fpcfga_cmd_t	cmd,
499 	int		*l_errnop)
500 {
501 	int rv;
502 	di_node_t root, tree_root, fpnode;
503 	char *root_path, *cp = NULL;
504 	char *devfs_fp_path;
505 	size_t len;
506 	fpcfga_ret_t ret;
507 	int	found = 0;
508 
509 	*l_errnop = 0;
510 
511 	if ((root_path = strdup(physpath)) == NULL) {
512 		*l_errnop = errno;
513 		return (FPCFGA_LIB_ERR);
514 	}
515 
516 	/* Fix up path for di_init() */
517 	len = strlen(DEVICES_DIR);
518 	if (strncmp(root_path, DEVICES_DIR SLASH,
519 	    len + strlen(SLASH)) == 0) {
520 		cp = root_path + len;
521 		(void) memmove(root_path, cp, strlen(cp) + 1);
522 	} else if (*root_path != '/') {
523 		*l_errnop = 0;
524 		ret = FPCFGA_ERR;
525 		goto out;
526 	}
527 
528 	/* Remove dynamic component if any */
529 	if ((cp = GET_DYN(root_path)) != NULL) {
530 		*cp = '\0';
531 	}
532 
533 	/* Remove minor name if any */
534 	if ((cp = strrchr(root_path, ':')) != NULL) {
535 		*cp = '\0';
536 	}
537 
538 	/*
539 	 * If force_flag is set
540 	 * do di_init with DINFOFORCE flag and get to the input fp node
541 	 * from the device tree.
542 	 *
543 	 * In order to get the link between path_info node and scsi_vhci node
544 	 * it is required to take the snapshot of the whole device tree.
545 	 * this behavior of libdevinfo is inefficient.  For a specific
546 	 * fca port DINFOPROP was sufficient on the fca path prior to
547 	 * scsi_vhci node support.
548 	 *
549 	 */
550 	if ((up->flags & FLAG_DEVINFO_FORCE) == FLAG_DEVINFO_FORCE) {
551 		tree_root = di_init("/", init_flags | DINFOFORCE);
552 	} else {
553 		tree_root = di_init("/", init_flags);
554 	}
555 
556 	if (tree_root == DI_NODE_NIL) {
557 		*l_errnop = errno;
558 		ret = FPCFGA_LIB_ERR;
559 		goto out;
560 	}
561 
562 	fpnode = di_drv_first_node("fp", tree_root);
563 
564 	while (fpnode) {
565 		devfs_fp_path = di_devfs_path(fpnode);
566 		if ((devfs_fp_path) && !(strncmp(devfs_fp_path,
567 				root_path, strlen(root_path)))) {
568 			found = 1;
569 			di_devfs_path_free(devfs_fp_path);
570 			break;
571 		}
572 		di_devfs_path_free(devfs_fp_path);
573 		fpnode = di_drv_next_node(fpnode);
574 	}
575 	if (!(found)) {
576 		ret = FPCFGA_LIB_ERR;
577 		goto out;
578 	} else {
579 		root = fpnode;
580 	}
581 
582 	/* Walk the tree */
583 	errno = 0;
584 	if (cmd == FPCFGA_WALK_NODE) {
585 		rv = di_walk_node(root, up->walkmode.node_args.flags, arg,
586 		    up->walkmode.node_args.fcn);
587 	} else {
588 		assert(cmd == FPCFGA_WALK_MINOR);
589 		rv = di_walk_minor(root, up->walkmode.minor_args.nodetype, 0,
590 			arg, up->walkmode.minor_args.fcn);
591 	}
592 
593 	if (rv != 0) {
594 		*l_errnop = errno;
595 		ret = FPCFGA_LIB_ERR;
596 	} else {
597 		if ((up->flags & FLAG_PATH_INFO_WALK) == FLAG_PATH_INFO_WALK) {
598 			ret = stat_path_info_node(root, arg, l_errnop);
599 		} else {
600 			*l_errnop = 0;
601 			ret = FPCFGA_OK;
602 		}
603 	}
604 
605 	di_fini(tree_root);
606 
607 	/*FALLTHRU*/
608 out:
609 	S_FREE(root_path);
610 	return (ret);
611 }
612 
613 
614 int
615 msg_idx(msgid_t msgid)
616 {
617 	int idx = 0;
618 
619 	/* The string table index and the error id may or may not be same */
620 	if (msgid >= 0 && msgid <= N_STRS - 1 &&
621 	    str_tbl[msgid].msgid == msgid) {
622 		idx = msgid;
623 	} else {
624 		for (idx = 0; idx < N_STRS; idx++) {
625 			if (str_tbl[idx].msgid == msgid)
626 				break;
627 		}
628 		if (idx >= N_STRS) {
629 			idx =  UNKNOWN_ERR_IDX;
630 		}
631 	}
632 
633 	return (idx);
634 }
635 
636 /*
637  * cfga_err() accepts a variable number of message IDs and constructs
638  * a corresponding error string which is returned via the errstring argument.
639  * cfga_err() calls dgettext() to internationalize proper messages.
640  * May be called with a NULL argument.
641  */
642 void
643 cfga_err(char **errstring, int l_errno, ...)
644 {
645 	va_list ap;
646 	int append_newline = 0;
647 	char *tmp_str, *tmp_err_str = NULL;
648 
649 	if (errstring == NULL) {
650 		return;
651 	}
652 
653 	/*
654 	 * Don't append a newline, the application (for example cfgadm)
655 	 * should do that.
656 	 */
657 	append_newline = 0;
658 
659 	va_start(ap, l_errno);
660 	msg_common(&tmp_err_str, append_newline, l_errno, ap);
661 	va_end(ap);
662 
663 	if (*errstring == NULL) {
664 		*errstring = tmp_err_str;
665 		return;
666 	}
667 
668 	/*
669 	 * *errstring != NULL
670 	 * There was something in errstring prior to this call.
671 	 * So, concatenate the old and new strings
672 	 */
673 	if ((tmp_str = calloc(1,
674 		strlen(*errstring) + strlen(tmp_err_str) + 2)) == NULL) {
675 		/* In case of error, retain only the earlier message */
676 		free(tmp_err_str);
677 		return;
678 	}
679 
680 	sprintf(tmp_str, "%s\n%s", *errstring, tmp_err_str);
681 	free(tmp_err_str);
682 	free(*errstring);
683 	*errstring = tmp_str;
684 }
685 
686 /*
687  * This routine accepts a variable number of message IDs and constructs
688  * a corresponding message string which is printed via the message print
689  * routine argument.
690  */
691 void
692 cfga_msg(struct cfga_msg *msgp, ...)
693 {
694 	char *p = NULL;
695 	int append_newline = 0, l_errno = 0;
696 	va_list ap;
697 
698 	if (msgp == NULL || msgp->message_routine == NULL) {
699 		return;
700 	}
701 
702 	/* Append a newline after message */
703 	append_newline = 1;
704 	l_errno = 0;
705 
706 	va_start(ap, msgp);
707 	msg_common(&p, append_newline, l_errno, ap);
708 	va_end(ap);
709 
710 	(void) (*msgp->message_routine)(msgp->appdata_ptr, p);
711 
712 	S_FREE(p);
713 }
714 
715 /*
716  * Get internationalized string corresponding to message id
717  * Caller must free the memory allocated.
718  */
719 char *
720 cfga_str(int append_newline, ...)
721 {
722 	char *p = NULL;
723 	int l_errno = 0;
724 	va_list ap;
725 
726 	va_start(ap, append_newline);
727 	msg_common(&p, append_newline, l_errno, ap);
728 	va_end(ap);
729 
730 	return (p);
731 }
732 
733 static void
734 msg_common(char **msgpp, int append_newline, int l_errno, va_list ap)
735 {
736 	int a = 0;
737 	size_t len = 0;
738 	int i = 0, n = 0;
739 	char *s = NULL, *t = NULL;
740 	strlist_t dummy;
741 	strlist_t *savep = NULL, *sp = NULL, *tailp = NULL;
742 
743 	if (*msgpp != NULL) {
744 		return;
745 	}
746 
747 	dummy.next = NULL;
748 	tailp = &dummy;
749 	for (len = 0; (a = va_arg(ap, int)) != 0; ) {
750 		n = GET_MSG_NARGS(a); /* 0 implies no additional args */
751 		for (i = 0; i <= n; i++) {
752 			sp = calloc(1, sizeof (*sp));
753 			if (sp == NULL) {
754 				goto out;
755 			}
756 			if (i == 0 && GET_MSG_INTL(a)) {
757 				sp->str = dgettext(TEXT_DOMAIN, GET_MSG_STR(a));
758 			} else if (i == 0) {
759 				sp->str = GET_MSG_STR(a);
760 			} else {
761 				sp->str = va_arg(ap, char *);
762 			}
763 			len += (strlen(sp->str));
764 			sp->next = NULL;
765 			tailp->next = sp;
766 			tailp = sp;
767 		}
768 	}
769 
770 	len += 1;	/* terminating NULL */
771 
772 	s = t = NULL;
773 	if (l_errno) {
774 		s = dgettext(TEXT_DOMAIN, ": ");
775 		t = S_STR(strerror(l_errno));
776 		if (s != NULL && t != NULL) {
777 			len += strlen(s) + strlen(t);
778 		}
779 	}
780 
781 	if (append_newline) {
782 		len++;
783 	}
784 
785 	if ((*msgpp = calloc(1, len)) == NULL) {
786 		goto out;
787 	}
788 
789 	**msgpp = '\0';
790 	for (sp = dummy.next; sp != NULL; sp = sp->next) {
791 		(void) strcat(*msgpp, sp->str);
792 	}
793 
794 	if (s != NULL && t != NULL) {
795 		(void) strcat(*msgpp, s);
796 		(void) strcat(*msgpp, t);
797 	}
798 
799 	if (append_newline) {
800 		(void) strcat(*msgpp, dgettext(TEXT_DOMAIN, "\n"));
801 	}
802 
803 	/* FALLTHROUGH */
804 out:
805 	sp = dummy.next;
806 	while (sp != NULL) {
807 		savep = sp->next;
808 		S_FREE(sp);
809 		sp = savep;
810 	}
811 }
812 
813 fpcfga_ret_t
814 devctl_cmd(
815 	const char	*physpath,
816 	fpcfga_cmd_t	cmd,
817 	uint_t		*statep,
818 	int		*l_errnop)
819 {
820 	int rv = -1, i, type;
821 	devctl_hdl_t hdl = NULL;
822 	char *cp = NULL, *path = NULL;
823 	int (*func)(const devctl_hdl_t);
824 	int (*state_func)(const devctl_hdl_t, uint_t *);
825 
826 	*l_errnop = 0;
827 
828 	if (statep != NULL) *statep = 0;
829 
830 	func = NULL;
831 	state_func = NULL;
832 	type = 0;
833 
834 	for (i = 0; i < N_GET_STATE_CMDS; i++) {
835 		if (get_state_cmds[i].cmd == cmd) {
836 			state_func = get_state_cmds[i].state_fcn;
837 			type = get_state_cmds[i].type;
838 			assert(statep != NULL);
839 			break;
840 		}
841 	}
842 
843 	if (state_func == NULL) {
844 		for (i = 0; i < N_SET_STATE_CMDS; i++) {
845 			if (set_state_cmds[i].cmd == cmd) {
846 				func = set_state_cmds[i].fcn;
847 				type = set_state_cmds[i].type;
848 				assert(statep == NULL);
849 				break;
850 			}
851 		}
852 	}
853 
854 	assert(type == BUS_OP || type == DEV_OP);
855 
856 	if (func == NULL && state_func == NULL) {
857 		return (FPCFGA_ERR);
858 	}
859 
860 	/*
861 	 * Fix up path for calling devctl.
862 	 */
863 	if ((path = strdup(physpath)) == NULL) {
864 		*l_errnop = errno;
865 		return (FPCFGA_LIB_ERR);
866 	}
867 
868 	/* Remove dynamic component if any */
869 	if ((cp = GET_DYN(path)) != NULL) {
870 		*cp = '\0';
871 	}
872 
873 	/* Remove minor name */
874 	if ((cp = strrchr(path, ':')) != NULL) {
875 		*cp = '\0';
876 	}
877 
878 	errno = 0;
879 
880 	if (type == BUS_OP) {
881 		hdl = devctl_bus_acquire(path, 0);
882 	} else {
883 		hdl = devctl_device_acquire(path, 0);
884 	}
885 	*l_errnop = errno;
886 
887 	S_FREE(path);
888 
889 	if (hdl == NULL) {
890 		return (FPCFGA_ERR);
891 	}
892 
893 	errno = 0;
894 	/* Only getstate functions require a second argument */
895 	if (func != NULL && statep == NULL) {
896 		rv = func(hdl);
897 		*l_errnop = errno;
898 	} else if (state_func != NULL && statep != NULL) {
899 		rv = state_func(hdl, statep);
900 		*l_errnop = errno;
901 	} else {
902 		rv = -1;
903 		*l_errnop = 0;
904 	}
905 
906 	devctl_release(hdl);
907 
908 	return ((rv == -1) ? FPCFGA_ERR : FPCFGA_OK);
909 }
910 
911 /*
912  * Is device in a known state ? (One of BUSY, ONLINE, OFFLINE)
913  *	BUSY --> One or more device special files are open. Implies online
914  *	ONLINE --> driver attached
915  *	OFFLINE --> CF1 with offline flag set.
916  *	UNKNOWN --> None of the above
917  */
918 int
919 known_state(di_node_t node)
920 {
921 	uint_t state;
922 
923 	state = di_state(node);
924 
925 	/*
926 	 * CF1 without offline flag set is considered unknown state.
927 	 * We are in a known state if either CF2 (driver attached) or
928 	 * offline.
929 	 */
930 	if ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
931 		(state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
932 		return (1);
933 	}
934 
935 	return (0);
936 }
937 
938 void
939 list_free(ldata_list_t **llpp)
940 {
941 	ldata_list_t *lp, *olp;
942 
943 	lp = *llpp;
944 	while (lp != NULL) {
945 		olp = lp;
946 		lp = olp->next;
947 		S_FREE(olp);
948 	}
949 
950 	*llpp = NULL;
951 }
952 
953 /*
954  * Obtain the devlink from a /devices path
955  */
956 fpcfga_ret_t
957 physpath_to_devlink(
958 	const char *basedir,
959 	char *xport_phys,
960 	char **xport_logpp,
961 	int *l_errnop,
962 	int match_minor)
963 {
964 	pathm_t pmt = {NULL};
965 	fpcfga_ret_t ret;
966 
967 	pmt.phys = xport_phys;
968 	pmt.ret = FPCFGA_NO_REC;
969 	pmt.match_minor = match_minor;
970 
971 	/*
972 	 * Search the /dev hierarchy starting at basedir.
973 	 */
974 	ret = recurse_dev(basedir, &pmt, lookup_dev);
975 	if (ret == FPCFGA_OK && (ret = pmt.ret) == FPCFGA_OK) {
976 		assert(pmt.log != NULL);
977 		*xport_logpp  = pmt.log;
978 	} else {
979 		if (pmt.log != NULL) {
980 			S_FREE(pmt.log);
981 		}
982 
983 		*xport_logpp = NULL;
984 		*l_errnop = pmt.l_errno;
985 	}
986 
987 	return (ret);
988 }
989 
990 static fpcfga_recur_t
991 lookup_dev(const char *lpath, void *arg)
992 {
993 	char ppath[PATH_MAX];
994 	pathm_t *pmtp = (pathm_t *)arg;
995 
996 	if (realpath(lpath, ppath) == NULL) {
997 		return (FPCFGA_CONTINUE);
998 	}
999 
1000 	ppath[sizeof (ppath) - 1] = '\0';
1001 
1002 	/* Is this the physical path we are looking for */
1003 	if (dev_cmp(ppath, pmtp->phys, pmtp->match_minor))  {
1004 		return (FPCFGA_CONTINUE);
1005 	}
1006 
1007 	if ((pmtp->log = strdup(lpath)) == NULL) {
1008 		pmtp->l_errno = errno;
1009 		pmtp->ret = FPCFGA_LIB_ERR;
1010 	} else {
1011 		pmtp->ret = FPCFGA_OK;
1012 	}
1013 
1014 	return (FPCFGA_TERMINATE);
1015 }
1016 
1017 /* Compare HBA physical ap_id and device path */
1018 int
1019 hba_dev_cmp(const char *hba, const char *devpath)
1020 {
1021 	char *cp = NULL;
1022 	int rv;
1023 	size_t hba_len, dev_len;
1024 	char l_hba[MAXPATHLEN], l_dev[MAXPATHLEN];
1025 
1026 	(void) snprintf(l_hba, sizeof (l_hba), "%s", hba);
1027 	(void) snprintf(l_dev, sizeof (l_dev), "%s", devpath);
1028 
1029 	/* Remove dynamic component if any */
1030 	if ((cp = GET_DYN(l_hba)) != NULL) {
1031 		*cp = '\0';
1032 	}
1033 
1034 	if ((cp = GET_DYN(l_dev)) != NULL) {
1035 		*cp = '\0';
1036 	}
1037 
1038 
1039 	/* Remove minor names */
1040 	if ((cp = strrchr(l_hba, ':')) != NULL) {
1041 		*cp = '\0';
1042 	}
1043 
1044 	if ((cp = strrchr(l_dev, ':')) != NULL) {
1045 		*cp = '\0';
1046 	}
1047 
1048 	hba_len = strlen(l_hba);
1049 	dev_len = strlen(l_dev);
1050 
1051 	/* Check if HBA path is component of device path */
1052 	if (rv = strncmp(l_hba, l_dev, hba_len)) {
1053 		return (rv);
1054 	}
1055 
1056 	/* devpath must have '/' and 1 char in addition to hba path */
1057 	if (dev_len >= hba_len + 2 && l_dev[hba_len] == '/') {
1058 		return (0);
1059 	} else {
1060 		return (-1);
1061 	}
1062 }
1063 
1064 int
1065 dev_cmp(const char *dev1, const char *dev2, int match_minor)
1066 {
1067 	char l_dev1[MAXPATHLEN], l_dev2[MAXPATHLEN];
1068 	char *mn1, *mn2;
1069 	int rv;
1070 
1071 	(void) snprintf(l_dev1, sizeof (l_dev1), "%s", dev1);
1072 	(void) snprintf(l_dev2, sizeof (l_dev2), "%s", dev2);
1073 
1074 	if ((mn1 = GET_DYN(l_dev1)) != NULL) {
1075 		*mn1 = '\0';
1076 	}
1077 
1078 	if ((mn2 = GET_DYN(l_dev2)) != NULL) {
1079 		*mn2 = '\0';
1080 	}
1081 
1082 	/* Separate out the minor names */
1083 	if ((mn1 = strrchr(l_dev1, ':')) != NULL) {
1084 		*mn1++ = '\0';
1085 	}
1086 
1087 	if ((mn2 = strrchr(l_dev2, ':')) != NULL) {
1088 		*mn2++ = '\0';
1089 	}
1090 
1091 	if ((rv = strcmp(l_dev1, l_dev2)) != 0 || !match_minor) {
1092 		return (rv);
1093 	}
1094 
1095 	/*
1096 	 * Compare minor names
1097 	 */
1098 	if (mn1 == NULL && mn2 == NULL) {
1099 		return (0);
1100 	} else if (mn1 == NULL) {
1101 		return (-1);
1102 	} else if (mn2 == NULL) {
1103 		return (1);
1104 	} else {
1105 		return (strcmp(mn1, mn2));
1106 	}
1107 }
1108 
1109 /*
1110  * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
1111  * Will handle retries if applicable.
1112  */
1113 int
1114 getAdapterAttrs(HBA_HANDLE handle, HBA_ADAPTERATTRIBUTES *attrs)
1115 {
1116 	int count = 0;
1117 	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
1118 
1119 	/* Loop as long as we have a retryable error */
1120 	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
1121 		status == HBA_STATUS_ERROR_BUSY) &&
1122 		count++ < HBA_MAX_RETRIES) {
1123 		status = HBA_GetAdapterAttributes(handle, attrs);
1124 		if (status == HBA_STATUS_OK) {
1125 			break;
1126 		}
1127 		sleep(1);
1128 	}
1129 	return (status);
1130 }
1131 
1132 /*
1133  * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
1134  * Will handle retries if applicable.
1135  */
1136 int
1137 getPortAttrsByWWN(HBA_HANDLE handle, HBA_WWN wwn, HBA_PORTATTRIBUTES *attrs)
1138 {
1139 	int count = 0;
1140 	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
1141 
1142 	/* Loop as long as we have a retryable error */
1143 	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
1144 		status == HBA_STATUS_ERROR_BUSY) &&
1145 		count++ < HBA_MAX_RETRIES) {
1146 		status = HBA_GetPortAttributesByWWN(handle, wwn, attrs);
1147 		if (status == HBA_STATUS_OK) {
1148 		    break;
1149 		}
1150 
1151 		/* The odds of this occuring are very slim, but possible. */
1152 		if (status == HBA_STATUS_ERROR_STALE_DATA) {
1153 			/*
1154 			 * If we hit a stale data scenario,
1155 			 * we'll just tell the user to try again.
1156 			 */
1157 			status = HBA_STATUS_ERROR_TRY_AGAIN;
1158 			break;
1159 		}
1160 		sleep(1);
1161 	}
1162 	return (status);
1163 }
1164 
1165 /*
1166  * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
1167  * Will handle retries if applicable.
1168  */
1169 int
1170 getAdapterPortAttrs(HBA_HANDLE handle, int portIndex,
1171 	    HBA_PORTATTRIBUTES *attrs)
1172 {
1173 	int count = 0;
1174 	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
1175 
1176 	/* Loop as long as we have a retryable error */
1177 	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
1178 		status == HBA_STATUS_ERROR_BUSY) &&
1179 		count++ < HBA_MAX_RETRIES) {
1180 		status = HBA_GetAdapterPortAttributes(handle, portIndex, attrs);
1181 		if (status == HBA_STATUS_OK) {
1182 			break;
1183 		}
1184 
1185 		/* The odds of this occuring are very slim, but possible. */
1186 		if (status == HBA_STATUS_ERROR_STALE_DATA) {
1187 			/*
1188 			 * If we hit a stale data scenario,
1189 			 * we'll just tell the user to try again.
1190 			 */
1191 			status = HBA_STATUS_ERROR_TRY_AGAIN;
1192 			break;
1193 		}
1194 		sleep(1);
1195 	}
1196 	return (status);
1197 }
1198 
1199 /*
1200  * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
1201  * Will handle retries if applicable.
1202  */
1203 int
1204 getDiscPortAttrs(HBA_HANDLE handle, int portIndex, int discIndex,
1205 	    HBA_PORTATTRIBUTES *attrs)
1206 {
1207 	int count = 0;
1208 	HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
1209 
1210 	/* Loop as long as we have a retryable error */
1211 	while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
1212 		status == HBA_STATUS_ERROR_BUSY) &&
1213 		count++ < HBA_MAX_RETRIES) {
1214 		status = HBA_GetDiscoveredPortAttributes(handle, portIndex,
1215 				discIndex, attrs);
1216 		if (status == HBA_STATUS_OK) {
1217 			break;
1218 		}
1219 
1220 		/* The odds of this occuring are very slim, but possible. */
1221 		if (status == HBA_STATUS_ERROR_STALE_DATA) {
1222 			/*
1223 			 * If we hit a stale data scenario, we'll just tell the
1224 			 * user to try again.
1225 			 */
1226 			status = HBA_STATUS_ERROR_TRY_AGAIN;
1227 			break;
1228 		}
1229 		sleep(1);
1230 	}
1231 	return (status);
1232 }
1233 
1234 /*
1235  * Find the Adapter port that matches the portPath.
1236  * When the matching port is found the caller have to close handle
1237  * and free library.
1238  */
1239 fpcfga_ret_t
1240 findMatchingAdapterPort(char *portPath, HBA_HANDLE *matchingHandle,
1241 	int *matchingPortIndex, HBA_PORTATTRIBUTES *matchingPortAttrs,
1242 	char **errstring)
1243 {
1244 	HBA_HANDLE	handle;
1245 	HBA_ADAPTERATTRIBUTES	hbaAttrs;
1246 	HBA_PORTATTRIBUTES	portAttrs;
1247 	HBA_STATUS status = HBA_STATUS_OK;
1248 	int count, retry = 0, l_errno = 0;
1249 	int adapterIndex, portIndex;
1250 	char			adapterName[256];
1251 	char			*cfg_ptr, *tmpPtr;
1252 	char			*logical_apid = NULL;
1253 
1254 	status = HBA_LoadLibrary();
1255 	if (status != HBA_STATUS_OK) {
1256 	    cfga_err(errstring, 0, ERR_HBA_LOAD_LIBRARY, 0);
1257 	    return (FPCFGA_LIB_ERR);
1258 	}
1259 	count = HBA_GetNumberOfAdapters();
1260 	if (count == 0) {
1261 	    cfga_err(errstring, 0, ERR_NO_ADAPTER_FOUND, 0);
1262 	    HBA_FreeLibrary();
1263 	    return (FPCFGA_LIB_ERR);
1264 	}
1265 
1266 	/* Loop over all HBAs */
1267 	for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
1268 	    status = HBA_GetAdapterName(adapterIndex, (char *)&adapterName);
1269 	    if (status != HBA_STATUS_OK) {
1270 		/* May have been DR'd */
1271 		continue;
1272 	    }
1273 	    handle = HBA_OpenAdapter(adapterName);
1274 	    if (handle == 0) {
1275 		/* May have been DR'd */
1276 		continue;
1277 	    }
1278 
1279 	    do {
1280 		if (getAdapterAttrs(handle, &hbaAttrs)) {
1281 		/* Should never happen */
1282 		    HBA_CloseAdapter(handle);
1283 		    continue;
1284 		}
1285 
1286 		/* Loop over all HBA Ports */
1287 		for (portIndex = 0;
1288 		    portIndex < hbaAttrs.NumberOfPorts; portIndex++) {
1289 		    if ((status = getAdapterPortAttrs(handle, portIndex,
1290 			&portAttrs)) != HBA_STATUS_OK) {
1291 			/* Need to refresh adapter */
1292 			if (status == HBA_STATUS_ERROR_STALE_DATA) {
1293 			    HBA_RefreshInformation(handle);
1294 			    break;
1295 			} else {
1296 			    continue;
1297 			}
1298 		    }
1299 
1300 			/*
1301 			 * check to see if OSDeviceName is a /dev/cfg link
1302 			 * or the physical path
1303 			 */
1304 		    if (strncmp(portAttrs.OSDeviceName, CFGA_DEV_DIR,
1305 			strlen(CFGA_DEV_DIR)) != 0) {
1306 			tmpPtr = strstr(portAttrs.OSDeviceName, MINOR_SEP);
1307 			if (tmpPtr != NULL) {
1308 				if (strncmp(portPath,
1309 					    portAttrs.OSDeviceName,
1310 					    strlen(portAttrs.OSDeviceName) -
1311 					    strlen(tmpPtr)) == 0) {
1312 					if (matchingHandle)
1313 						*matchingHandle = handle;
1314 					if (matchingPortIndex)
1315 						*matchingPortIndex = portIndex;
1316 					if (matchingPortAttrs)
1317 						*matchingPortAttrs = portAttrs;
1318 					return (FPCFGA_OK);
1319 				}
1320 			}
1321 		    } else {
1322 			/*
1323 			 * strip off the /dev/cfg/ portion of the
1324 			 * OSDeviceName
1325 			 * make sure that the OSDeviceName is at least
1326 			 * strlen("/dev/cfg") + 1 + 1 long.
1327 			 *	first 1 is for the / after /dev/cfg
1328 			 *	second 1 is to make sure there is somthing
1329 			 *	after
1330 			 */
1331 			if (strlen(portAttrs.OSDeviceName) <
1332 			    (strlen(CFGA_DEV_DIR) + 1 + 1))
1333 				continue;
1334 			cfg_ptr = portAttrs.OSDeviceName +
1335 			    strlen(CFGA_DEV_DIR) + 1;
1336 			if (logical_apid == NULL) {
1337 				/* get the /dev/cfg link from the portPath */
1338 				if (make_xport_logid(portPath, &logical_apid,
1339 					    &l_errno) != FPCFGA_OK) {
1340 					cfga_err(errstring, l_errno,
1341 					    ERR_LIST, 0);
1342 					HBA_FreeLibrary();
1343 					return (FPCFGA_LIB_ERR);
1344 				}
1345 			}
1346 			/* compare logical ap_id */
1347 			if (strcmp(logical_apid, cfg_ptr) == 0) {
1348 				if (matchingHandle)
1349 					*matchingHandle = handle;
1350 				if (matchingPortIndex)
1351 					*matchingPortIndex = portIndex;
1352 				if (matchingPortAttrs)
1353 					*matchingPortAttrs = portAttrs;
1354 				S_FREE(logical_apid);
1355 				return (FPCFGA_OK);
1356 			}
1357 		    }
1358 		}
1359 		if (logical_apid != NULL)
1360 			S_FREE(logical_apid);
1361 	    } while ((status == HBA_STATUS_ERROR_STALE_DATA) &&
1362 		(retry++ < HBA_MAX_RETRIES));
1363 
1364 	    HBA_CloseAdapter(handle);
1365 	}
1366 	free(logical_apid);
1367 
1368 	/* Got here. No mathcing adatper port found. */
1369 	cfga_err(errstring, 0, ERR_MATCHING_HBA_PORT, 0);
1370 	HBA_FreeLibrary();
1371 	return (FPCFGA_LIB_ERR);
1372 }
1373