dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / cfgadm_plugins / fp / common / cfga_utils.c
blob9f1cee82b8c7ee271e35809addaefaf2827b62eb
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 #include "cfga_fp.h"
30 * This file contains helper routines for the FP plugin
33 #if !defined(TEXT_DOMAIN)
34 #define TEXT_DOMAIN "SYS_TEST"
35 #endif
37 typedef struct strlist {
38 const char *str;
39 struct strlist *next;
40 } strlist_t;
42 typedef struct {
43 fpcfga_ret_t fp_err;
44 cfga_err_t cfga_err;
45 } errcvt_t;
47 typedef struct {
48 fpcfga_cmd_t cmd;
49 int type;
50 int (*fcn)(const devctl_hdl_t);
51 } set_state_cmd_t;
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;
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
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);
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};
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.
87 msgcvt_t str_tbl[] = {
90 * The first element (ERR_UNKNOWN) MUST always be present in the array.
92 #define UNKNOWN_ERR_IDX 0 /* Keep the index in sync */
95 /* msg_code num_args, I18N msg_string */
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 adapters found"},
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: "},
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: "},
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: "},
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: "},
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"},
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"},
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: "},
207 /* Hotplugging confirmation prompts */
208 {CONF_QUIESCE_1, 1, 1,
209 "This operation will suspend activity on FC bus: "},
211 {CONF_QUIESCE_2, 0, 1, "\nContinue"},
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"},
218 /* Misc. */
219 {WARN_DISCONNECT, 0, 1,
220 "WARNING: Disconnecting critical partitions may cause system hang."
221 "\nContinue"}
225 #define N_STRS (sizeof (str_tbl) / sizeof (str_tbl[0]))
227 #define GET_MSG_NARGS(i) (str_tbl[msg_idx(i)].nargs)
228 #define GET_MSG_INTL(i) (str_tbl[msg_idx(i)].intl)
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 }
243 #define N_ERR_CVT_TBL (sizeof (err_cvt_tbl)/sizeof (err_cvt_tbl[0]))
245 #define DEV_OP 0
246 #define BUS_OP 1
247 static set_state_cmd_t set_state_cmds[] = {
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 }
262 #define N_SET_STATE_CMDS (sizeof (set_state_cmds)/sizeof (set_state_cmds[0]))
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 }
269 #define N_GET_STATE_CMDS (sizeof (get_state_cmds)/sizeof (get_state_cmds[0]))
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
280 #define N_DEV_DIR_HINTS (sizeof (dev_dir_hints) / sizeof (dev_dir_hints[0]))
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.
288 fpcfga_ret_t
289 recurse_dev(
290 const char *basedir,
291 void *arg,
292 fpcfga_recur_t (*fcn)(const char *lpath, void *arg))
294 int i, rv = NFTW_ERROR;
296 (void) mutex_lock(&nftw_arg.mp);
298 nftw_arg.arg = arg;
299 nftw_arg.fcn = fcn;
301 if (strcmp(basedir, DEV_DIR)) {
302 errno = 0;
303 rv = nftw(basedir, do_recurse_dev, NFTW_DEPTH, FTW_PHYS);
304 goto out;
308 * Search certain selected subdirectories first if basedir == "/dev".
309 * Ignore errors as some of these directories may not exist.
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;
319 /*FALLTHRU*/
320 out:
321 (void) mutex_unlock(&nftw_arg.mp);
322 return (rv == NFTW_ERROR ? FPCFGA_ERR : FPCFGA_OK);
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)
333 /* We want only VALID symlinks */
334 if (type != FTW_SL) {
335 return (NFTW_CONTINUE);
338 assert(nftw_arg.fcn != NULL);
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);
349 cfga_err_t
350 err_cvt(fpcfga_ret_t fp_err)
352 int i;
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);
360 return (CFGA_ERROR);
364 * Removes duplicate slashes from a pathname and any trailing slashes.
365 * Returns "/" if input is "/"
367 char *
368 pathdup(const char *path, int *l_errnop)
370 int prev_was_slash = 0;
371 char c, *dp = NULL, *dup = NULL;
372 const char *sp = NULL;
374 *l_errnop = 0;
376 if (path == NULL) {
377 return (NULL);
380 if ((dup = calloc(1, strlen(path) + 1)) == NULL) {
381 *l_errnop = errno;
382 return (NULL);
385 prev_was_slash = 0;
386 for (sp = path, dp = dup; (c = *sp) != '\0'; sp++) {
387 if (!prev_was_slash || c != '/') {
388 *dp++ = c;
390 if (c == '/') {
391 prev_was_slash = 1;
392 } else {
393 prev_was_slash = 0;
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';
404 return (dup);
407 fpcfga_ret_t
408 apidt_create(const char *ap_id, apid_t *apidp, char **errstring)
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;
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);
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;
432 (void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
433 if (GET_LUN_DYN(dyncomp)) {
434 ret = FPCFGA_APID_NOEXIST;
435 goto err;
438 /* Remove the dynamic component from the base. */
439 *dyn = '\0';
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;
452 assert(xport_phys != NULL);
454 apidp->xport_phys = xport_phys;
455 apidp->dyncomp = dyncomp;
456 apidp->lunlist = lunlistp;
457 apidp->flags = 0;
459 return (FPCFGA_OK);
461 err:
462 S_FREE(xport_phys);
463 S_FREE(dyncomp);
464 lunlist_free(lunlistp);
465 return (ret);
468 static void
469 lunlist_free(struct luninfo_list *lunlist)
471 struct luninfo_list *lunp;
473 while (lunlist != NULL) {
474 lunp = lunlist->next;
475 S_FREE(lunlist->path);
476 S_FREE(lunlist);
477 lunlist = lunp;
481 void
482 apidt_free(apid_t *apidp)
484 if (apidp == NULL)
485 return;
487 S_FREE(apidp->xport_phys);
488 S_FREE(apidp->dyncomp);
489 lunlist_free(apidp->lunlist);
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)
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;
509 *l_errnop = 0;
511 if ((root_path = strdup(physpath)) == NULL) {
512 *l_errnop = errno;
513 return (FPCFGA_LIB_ERR);
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;
528 /* Remove dynamic component if any */
529 if ((cp = GET_DYN(root_path)) != NULL) {
530 *cp = '\0';
533 /* Remove minor name if any */
534 if ((cp = strrchr(root_path, ':')) != NULL) {
535 *cp = '\0';
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.
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.
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);
556 if (tree_root == DI_NODE_NIL) {
557 *l_errnop = errno;
558 ret = FPCFGA_LIB_ERR;
559 goto out;
562 fpnode = di_drv_first_node("fp", tree_root);
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;
572 di_devfs_path_free(devfs_fp_path);
573 fpnode = di_drv_next_node(fpnode);
575 if (!(found)) {
576 ret = FPCFGA_LIB_ERR;
577 goto out;
578 } else {
579 root = fpnode;
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);
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;
605 di_fini(tree_root);
607 /*FALLTHRU*/
608 out:
609 S_FREE(root_path);
610 return (ret);
615 msg_idx(msgid_t msgid)
617 int idx = 0;
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;
628 if (idx >= N_STRS) {
629 idx = UNKNOWN_ERR_IDX;
633 return (idx);
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.
642 void
643 cfga_err(char **errstring, int l_errno, ...)
645 va_list ap;
646 int append_newline = 0;
647 char *tmp_str, *tmp_err_str = NULL;
649 if (errstring == NULL) {
650 return;
654 * Don't append a newline, the application (for example cfgadm)
655 * should do that.
657 append_newline = 0;
659 va_start(ap, l_errno);
660 msg_common(&tmp_err_str, append_newline, l_errno, ap);
661 va_end(ap);
663 if (*errstring == NULL) {
664 *errstring = tmp_err_str;
665 return;
669 * *errstring != NULL
670 * There was something in errstring prior to this call.
671 * So, concatenate the old and new strings
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;
680 sprintf(tmp_str, "%s\n%s", *errstring, tmp_err_str);
681 free(tmp_err_str);
682 free(*errstring);
683 *errstring = tmp_str;
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.
691 void
692 cfga_msg(struct cfga_msg *msgp, ...)
694 char *p = NULL;
695 int append_newline = 0, l_errno = 0;
696 va_list ap;
698 if (msgp == NULL || msgp->message_routine == NULL) {
699 return;
702 /* Append a newline after message */
703 append_newline = 1;
704 l_errno = 0;
706 va_start(ap, msgp);
707 msg_common(&p, append_newline, l_errno, ap);
708 va_end(ap);
710 (void) (*msgp->message_routine)(msgp->appdata_ptr, p);
712 S_FREE(p);
716 * Get internationalized string corresponding to message id
717 * Caller must free the memory allocated.
719 char *
720 cfga_str(int append_newline, ...)
722 char *p = NULL;
723 int l_errno = 0;
724 va_list ap;
726 va_start(ap, append_newline);
727 msg_common(&p, append_newline, l_errno, ap);
728 va_end(ap);
730 return (p);
733 static void
734 msg_common(char **msgpp, int append_newline, int l_errno, va_list ap)
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;
743 if (*msgpp != NULL) {
744 return;
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;
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 *);
763 len += (strlen(sp->str));
764 sp->next = NULL;
765 tailp->next = sp;
766 tailp = sp;
770 len += 1; /* terminating NULL */
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);
781 if (append_newline) {
782 len++;
785 if ((*msgpp = calloc(1, len)) == NULL) {
786 goto out;
789 **msgpp = '\0';
790 for (sp = dummy.next; sp != NULL; sp = sp->next) {
791 (void) strcat(*msgpp, sp->str);
794 if (s != NULL && t != NULL) {
795 (void) strcat(*msgpp, s);
796 (void) strcat(*msgpp, t);
799 if (append_newline) {
800 (void) strcat(*msgpp, dgettext(TEXT_DOMAIN, "\n"));
803 /* FALLTHROUGH */
804 out:
805 sp = dummy.next;
806 while (sp != NULL) {
807 savep = sp->next;
808 S_FREE(sp);
809 sp = savep;
813 fpcfga_ret_t
814 devctl_cmd(
815 const char *physpath,
816 fpcfga_cmd_t cmd,
817 uint_t *statep,
818 int *l_errnop)
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 *);
826 *l_errnop = 0;
828 if (statep != NULL) *statep = 0;
830 func = NULL;
831 state_func = NULL;
832 type = 0;
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;
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;
854 assert(type == BUS_OP || type == DEV_OP);
856 if (func == NULL && state_func == NULL) {
857 return (FPCFGA_ERR);
861 * Fix up path for calling devctl.
863 if ((path = strdup(physpath)) == NULL) {
864 *l_errnop = errno;
865 return (FPCFGA_LIB_ERR);
868 /* Remove dynamic component if any */
869 if ((cp = GET_DYN(path)) != NULL) {
870 *cp = '\0';
873 /* Remove minor name */
874 if ((cp = strrchr(path, ':')) != NULL) {
875 *cp = '\0';
878 errno = 0;
880 if (type == BUS_OP) {
881 hdl = devctl_bus_acquire(path, 0);
882 } else {
883 hdl = devctl_device_acquire(path, 0);
885 *l_errnop = errno;
887 S_FREE(path);
889 if (hdl == NULL) {
890 return (FPCFGA_ERR);
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;
906 devctl_release(hdl);
908 return ((rv == -1) ? FPCFGA_ERR : FPCFGA_OK);
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
919 known_state(di_node_t node)
921 uint_t state;
923 state = di_state(node);
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.
930 if ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
931 (state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
932 return (1);
935 return (0);
938 void
939 list_free(ldata_list_t **llpp)
941 ldata_list_t *lp, *olp;
943 lp = *llpp;
944 while (lp != NULL) {
945 olp = lp;
946 lp = olp->next;
947 S_FREE(olp);
950 *llpp = NULL;
954 * Obtain the devlink from a /devices path
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)
964 pathm_t pmt = {NULL};
965 fpcfga_ret_t ret;
967 pmt.phys = xport_phys;
968 pmt.ret = FPCFGA_NO_REC;
969 pmt.match_minor = match_minor;
972 * Search the /dev hierarchy starting at basedir.
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);
983 *xport_logpp = NULL;
984 *l_errnop = pmt.l_errno;
987 return (ret);
990 static fpcfga_recur_t
991 lookup_dev(const char *lpath, void *arg)
993 char ppath[PATH_MAX];
994 pathm_t *pmtp = (pathm_t *)arg;
996 if (realpath(lpath, ppath) == NULL) {
997 return (FPCFGA_CONTINUE);
1000 ppath[sizeof (ppath) - 1] = '\0';
1002 /* Is this the physical path we are looking for */
1003 if (dev_cmp(ppath, pmtp->phys, pmtp->match_minor)) {
1004 return (FPCFGA_CONTINUE);
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;
1014 return (FPCFGA_TERMINATE);
1017 /* Compare HBA physical ap_id and device path */
1019 hba_dev_cmp(const char *hba, const char *devpath)
1021 char *cp = NULL;
1022 int rv;
1023 size_t hba_len, dev_len;
1024 char l_hba[MAXPATHLEN], l_dev[MAXPATHLEN];
1026 (void) snprintf(l_hba, sizeof (l_hba), "%s", hba);
1027 (void) snprintf(l_dev, sizeof (l_dev), "%s", devpath);
1029 /* Remove dynamic component if any */
1030 if ((cp = GET_DYN(l_hba)) != NULL) {
1031 *cp = '\0';
1034 if ((cp = GET_DYN(l_dev)) != NULL) {
1035 *cp = '\0';
1039 /* Remove minor names */
1040 if ((cp = strrchr(l_hba, ':')) != NULL) {
1041 *cp = '\0';
1044 if ((cp = strrchr(l_dev, ':')) != NULL) {
1045 *cp = '\0';
1048 hba_len = strlen(l_hba);
1049 dev_len = strlen(l_dev);
1051 /* Check if HBA path is component of device path */
1052 if (rv = strncmp(l_hba, l_dev, hba_len)) {
1053 return (rv);
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);
1065 dev_cmp(const char *dev1, const char *dev2, int match_minor)
1067 char l_dev1[MAXPATHLEN], l_dev2[MAXPATHLEN];
1068 char *mn1, *mn2;
1069 int rv;
1071 (void) snprintf(l_dev1, sizeof (l_dev1), "%s", dev1);
1072 (void) snprintf(l_dev2, sizeof (l_dev2), "%s", dev2);
1074 if ((mn1 = GET_DYN(l_dev1)) != NULL) {
1075 *mn1 = '\0';
1078 if ((mn2 = GET_DYN(l_dev2)) != NULL) {
1079 *mn2 = '\0';
1082 /* Separate out the minor names */
1083 if ((mn1 = strrchr(l_dev1, ':')) != NULL) {
1084 *mn1++ = '\0';
1087 if ((mn2 = strrchr(l_dev2, ':')) != NULL) {
1088 *mn2++ = '\0';
1091 if ((rv = strcmp(l_dev1, l_dev2)) != 0 || !match_minor) {
1092 return (rv);
1096 * Compare minor names
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));
1110 * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
1111 * Will handle retries if applicable.
1114 getAdapterAttrs(HBA_HANDLE handle, HBA_ADAPTERATTRIBUTES *attrs)
1116 int count = 0;
1117 HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
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;
1127 sleep(1);
1129 return (status);
1133 * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
1134 * Will handle retries if applicable.
1137 getPortAttrsByWWN(HBA_HANDLE handle, HBA_WWN wwn, HBA_PORTATTRIBUTES *attrs)
1139 int count = 0;
1140 HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
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;
1151 /* The odds of this occuring are very slim, but possible. */
1152 if (status == HBA_STATUS_ERROR_STALE_DATA) {
1154 * If we hit a stale data scenario,
1155 * we'll just tell the user to try again.
1157 status = HBA_STATUS_ERROR_TRY_AGAIN;
1158 break;
1160 sleep(1);
1162 return (status);
1166 * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
1167 * Will handle retries if applicable.
1170 getAdapterPortAttrs(HBA_HANDLE handle, int portIndex,
1171 HBA_PORTATTRIBUTES *attrs)
1173 int count = 0;
1174 HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
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;
1185 /* The odds of this occuring are very slim, but possible. */
1186 if (status == HBA_STATUS_ERROR_STALE_DATA) {
1188 * If we hit a stale data scenario,
1189 * we'll just tell the user to try again.
1191 status = HBA_STATUS_ERROR_TRY_AGAIN;
1192 break;
1194 sleep(1);
1196 return (status);
1200 * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
1201 * Will handle retries if applicable.
1204 getDiscPortAttrs(HBA_HANDLE handle, int portIndex, int discIndex,
1205 HBA_PORTATTRIBUTES *attrs)
1207 int count = 0;
1208 HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
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;
1220 /* The odds of this occuring are very slim, but possible. */
1221 if (status == HBA_STATUS_ERROR_STALE_DATA) {
1223 * If we hit a stale data scenario, we'll just tell the
1224 * user to try again.
1226 status = HBA_STATUS_ERROR_TRY_AGAIN;
1227 break;
1229 sleep(1);
1231 return (status);
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.
1239 fpcfga_ret_t
1240 findMatchingAdapterPort(char *portPath, HBA_HANDLE *matchingHandle,
1241 int *matchingPortIndex, HBA_PORTATTRIBUTES *matchingPortAttrs,
1242 char **errstring)
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;
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);
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);
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;
1273 handle = HBA_OpenAdapter(adapterName);
1274 if (handle == 0) {
1275 /* May have been DR'd */
1276 continue;
1279 do {
1280 if (getAdapterAttrs(handle, &hbaAttrs)) {
1281 /* Should never happen */
1282 HBA_CloseAdapter(handle);
1283 continue;
1286 /* Loop over all HBA Ports */
1287 for (portIndex = 0;
1288 portIndex < hbaAttrs.NumberOfPorts; portIndex++) {
1289 if ((status = getAdapterPortAttrs(handle,
1290 portIndex,
1291 &portAttrs)) != HBA_STATUS_OK) {
1292 /* Need to refresh adapter */
1293 if (status ==
1294 HBA_STATUS_ERROR_STALE_DATA) {
1295 HBA_RefreshInformation(handle);
1296 break;
1297 } else {
1298 continue;
1303 * check to see if OSDeviceName is a /dev/cfg
1304 * link or the physical path
1306 if (strncmp(portAttrs.OSDeviceName,
1307 CFGA_DEV_DIR,
1308 strlen(CFGA_DEV_DIR)) != 0) {
1309 tmpPtr = strstr(portAttrs.OSDeviceName,
1310 MINOR_SEP);
1311 if ((tmpPtr != NULL) &&
1312 strncmp(portPath,
1313 portAttrs.OSDeviceName,
1314 strlen(portAttrs.OSDeviceName) -
1315 strlen(tmpPtr)) == 0) {
1316 if (matchingHandle)
1317 *matchingHandle =
1318 handle;
1319 if (matchingPortIndex)
1320 *matchingPortIndex =
1321 portIndex;
1322 if (matchingPortAttrs)
1323 *matchingPortAttrs =
1324 portAttrs;
1325 return (FPCFGA_OK);
1327 } else {
1329 * strip off the /dev/cfg/ portion of
1330 * the OSDeviceName make sure that the
1331 * OSDeviceName is at least
1332 * strlen("/dev/cfg") + 1 + 1 long.
1333 * first 1 is for the / after /dev/cfg
1334 * second 1 is to make sure there is
1335 * somthing after
1337 if (strlen(portAttrs.OSDeviceName) <
1338 (strlen(CFGA_DEV_DIR) + 1 + 1))
1339 continue;
1340 cfg_ptr = portAttrs.OSDeviceName +
1341 strlen(CFGA_DEV_DIR) + 1;
1342 if (logical_apid == NULL) {
1344 * get the /dev/cfg link from
1345 * the portPath
1347 if (make_xport_logid(portPath,
1348 &logical_apid,
1349 &l_errno) != FPCFGA_OK) {
1350 cfga_err(errstring,
1351 l_errno,
1352 ERR_LIST, 0);
1353 HBA_FreeLibrary();
1354 return
1355 (FPCFGA_LIB_ERR);
1358 /* compare logical ap_id */
1359 if (strcmp(logical_apid,
1360 cfg_ptr) == 0) {
1361 if (matchingHandle)
1362 *matchingHandle =
1363 handle;
1364 if (matchingPortIndex)
1365 *matchingPortIndex =
1366 portIndex;
1367 if (matchingPortAttrs)
1368 *matchingPortAttrs =
1369 portAttrs;
1370 S_FREE(logical_apid);
1371 return (FPCFGA_OK);
1375 if (logical_apid != NULL)
1376 S_FREE(logical_apid);
1377 } while ((status == HBA_STATUS_ERROR_STALE_DATA) &&
1378 (retry++ < HBA_MAX_RETRIES));
1380 HBA_CloseAdapter(handle);
1382 free(logical_apid);
1384 /* Got here. No matching adapter port found. */
1385 cfga_err(errstring, 0, ERR_MATCHING_HBA_PORT, 0);
1386 HBA_FreeLibrary();
1387 return (FPCFGA_LIB_ERR);