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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include "cfga_scsi.h"
32 * This file contains helper routines for the SCSI plugin
35 #if !defined(TEXT_DOMAIN)
36 #define TEXT_DOMAIN "SYS_TEST"
39 typedef struct strlist
{
52 int (*fcn
)(const devctl_hdl_t
);
58 int (*state_fcn
)(const devctl_hdl_t
, uint_t
*);
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
,
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.
72 msgcvt_t str_tbl
[] = {
75 * The first element (ERR_UNKNOWN) MUST always be present in the array.
77 #define UNKNOWN_ERR_IDX 0 /* Keep the index in sync */
80 /* msg_code num_args, I18N msg_string */
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"},
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: "},
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: "},
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"},
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"},
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... "},
158 /* Hotplugging confirmation prompts */
159 {CONF_QUIESCE_1
, 1, 1,
160 "This operation will suspend activity on SCSI bus: "},
162 {CONF_QUIESCE_2
, 0, 1, "\nContinue"},
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"},
169 {CONF_NO_QUIESCE
, 0, 1,
170 "Proceed with hotplug operation."
171 "\nEnter y if operation is complete or n to abort"},
174 {WARN_DISCONNECT
, 0, 1,
175 "WARNING: Disconnecting critical partitions may cause system hang."
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"}
207 #define N_STRS (sizeof (str_tbl) / sizeof (str_tbl[0]))
209 #define GET_MSG_NARGS(i) (str_tbl[msg_idx(i)].nargs)
210 #define GET_MSG_INTL(i) (str_tbl[msg_idx(i)].intl)
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
}
225 #define N_ERR_CVT_TBL (sizeof (err_cvt_tbl)/sizeof (err_cvt_tbl[0]))
229 static set_state_cmd_t set_state_cmds
[] = {
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
}
244 #define N_SET_STATE_CMDS (sizeof (set_state_cmds)/sizeof (set_state_cmds[0]))
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
}
251 #define N_GET_STATE_CMDS (sizeof (get_state_cmds)/sizeof (get_state_cmds[0]))
254 * SCSI hardware specific commands
256 static hw_cmd_t hw_cmds
[] = {
257 /* Command string Command ID Function */
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
},
268 #define N_HW_CMDS (sizeof (hw_cmds) / sizeof (hw_cmds[0]))
272 err_cvt(scfga_ret_t s_err
)
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
);
286 * Removes duplicate slashes from a pathname and any trailing slashes.
287 * Returns "/" if input is "/"
290 pathdup(const char *path
, int *l_errnop
)
292 int prev_was_slash
= 0;
293 char c
, *dp
= NULL
, *dup
= NULL
;
294 const char *sp
= NULL
;
302 if ((dup
= calloc(1, strlen(path
) + 1)) == NULL
) {
308 for (sp
= path
, dp
= dup
; (c
= *sp
) != '\0'; sp
++) {
309 if (!prev_was_slash
|| c
!= '/') {
319 /* Remove trailing slash except if it is the first char */
320 if (prev_was_slash
&& dp
!= dup
&& dp
- 1 != dup
) {
331 apidt_create(const char *ap_id
, apid_t
*apidp
, char **errstring
)
333 char *hba_phys
= NULL
, *dyn
= NULL
;
334 char *dyncomp
= NULL
, *path
= NULL
;
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
);
344 /* Extract the base(hba) and dynamic(device) component if any */
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);
354 (void) strcpy(dyncomp
, DYN_TO_DYNCOMP(dyn
));
356 /* Remove the dynamic component from the base */
359 apidp
->dyntype
= NODYNCOMP
;
362 /* get dyn comp type */
363 if (dyncomp
!= NULL
) {
364 if (strstr(dyncomp
, PATH_APID_DYN_SEP
) != NULL
) {
365 apidp
->dyntype
= PATH_APID
;
367 apidp
->dyntype
= DEV_APID
;
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);
378 assert(path
!= NULL
);
379 assert(hba_phys
!= NULL
);
381 apidp
->hba_phys
= hba_phys
;
382 apidp
->dyncomp
= dyncomp
;
396 apidt_free(apid_t
*apidp
)
401 S_FREE(apidp
->hba_phys
);
402 S_FREE(apidp
->dyncomp
);
408 const char *physpath
,
416 di_node_t root
, walk_root
;
417 char *root_path
, *cp
= NULL
, *init_path
;
423 if ((root_path
= strdup(physpath
)) == NULL
) {
425 return (SCFGA_LIB_ERR
);
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
!= '/') {
440 /* Remove dynamic component if any */
441 if ((cp
= GET_DYN(root_path
)) != NULL
) {
445 /* Remove minor name if any */
446 if ((cp
= strrchr(root_path
, ':')) != NULL
) {
451 * Cached snapshots are always rooted at "/"
453 init_path
= root_path
;
454 if ((init_flags
& DINFOCACHE
) == DINFOCACHE
) {
459 if ((root
= di_init(init_path
, init_flags
)) == DI_NODE_NIL
) {
466 * Lookup the subtree of interest
469 if ((init_flags
& DINFOCACHE
) == DINFOCACHE
) {
470 walk_root
= di_lookup_node(root
, root_path
);
473 if (walk_root
== DI_NODE_NIL
) {
482 if (cmd
== SCFGA_WALK_NODE
) {
483 rv
= di_walk_node(walk_root
, up
->node_args
.flags
, arg
,
485 } else if (cmd
== SCFGA_WALK_PATH
) {
486 rv
= stat_path_info(walk_root
, arg
, l_errnop
);
488 assert(cmd
== SCFGA_WALK_MINOR
);
489 rv
= di_walk_minor(walk_root
, up
->minor_args
.nodetype
, 0, arg
,
522 * Determine if the func has an equal sign; only compare up to
525 for (len
= 0; func
[len
] != 0 && func
[len
] != '='; len
++) {
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
));
536 cfga_err(errstring
, 0, ERRARG_HWCMD_INVAL
, func
, 0);
541 msg_idx(msgid_t msgid
)
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
) {
550 for (idx
= 0; idx
< N_STRS
; idx
++) {
551 if (str_tbl
[idx
].msgid
== msgid
)
555 idx
= UNKNOWN_ERR_IDX
;
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.
569 cfga_err(char **errstring
, int l_errno
, ...)
572 int append_newline
= 0;
574 if (errstring
== NULL
|| *errstring
!= NULL
) {
579 * Don't append a newline, the application (for example cfgadm)
584 va_start(ap
, l_errno
);
585 msg_common(errstring
, append_newline
, l_errno
, ap
);
590 * This routine accepts a variable number of message IDs and constructs
591 * a corresponding message string which is printed via the message print
595 cfga_msg(struct cfga_msg
*msgp
, ...)
598 int append_newline
= 0, l_errno
= 0;
601 if (msgp
== NULL
|| msgp
->message_routine
== NULL
) {
605 /* Append a newline after message */
610 msg_common(&p
, append_newline
, l_errno
, ap
);
613 (void) (*msgp
->message_routine
)(msgp
->appdata_ptr
, p
);
620 * This routine prints the value of an led for a disk.
623 cfga_led_msg(struct cfga_msg
*msgp
, apid_t
*apidp
, led_strid_t led
,
626 char led_msg
[MAX_INPUT
]; /* 512 bytes */
628 if ((msgp
== NULL
) || (msgp
->message_routine
== NULL
)) {
631 if ((apidp
== NULL
) || (apidp
->dyncomp
== NULL
)) {
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
);
642 * Get internationalized string corresponding to message id
643 * Caller must free the memory allocated.
646 cfga_str(int append_newline
, ...)
652 va_start(ap
, append_newline
);
653 msg_common(&p
, append_newline
, l_errno
, ap
);
660 msg_common(char **msgpp
, int append_newline
, int l_errno
, va_list ap
)
665 char *s
= NULL
, *t
= NULL
;
667 strlist_t
*savep
= NULL
, *sp
= NULL
, *tailp
= NULL
;
669 if (*msgpp
!= NULL
) {
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
));
682 if (i
== 0 && GET_MSG_INTL(a
)) {
683 sp
->str
= dgettext(TEXT_DOMAIN
, GET_MSG_STR(a
));
685 sp
->str
= GET_MSG_STR(a
);
687 sp
->str
= va_arg(ap
, char *);
689 len
+= (strlen(sp
->str
));
696 len
+= 1; /* terminating NULL */
700 s
= dgettext(TEXT_DOMAIN
, ": ");
701 t
= S_STR(strerror(l_errno
));
702 if (s
!= NULL
&& t
!= NULL
) {
703 len
+= strlen(s
) + strlen(t
);
707 if (append_newline
) {
711 if ((*msgpp
= calloc(1, len
)) == NULL
) {
716 for (sp
= dummy
.next
; sp
!= NULL
; sp
= sp
->next
) {
717 (void) strcat(*msgpp
, sp
->str
);
720 if (s
!= NULL
&& t
!= NULL
) {
721 (void) strcat(*msgpp
, s
);
722 (void) strcat(*msgpp
, t
);
725 if (append_newline
) {
726 (void) strcat(*msgpp
, dgettext(TEXT_DOMAIN
, "\n"));
740 * Check to see if the given pi_node is the last path to the client device.
743 * 0: if there is another path avialable.
744 * -1: if no other paths available.
747 check_available_path(
748 di_node_t client_node
,
751 di_path_state_t pi_state
;
752 di_path_t next_pi
= DI_PATH_NIL
;
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 */
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
)) {
773 path_apid_state_change(
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
];
786 char *dev_list
[2] = {NULL
};
790 /* Make sure apid is pathinfo associated apid. */
791 if ((apidp
->dyntype
!= PATH_APID
) || (apidp
->dyncomp
== NULL
)) {
792 return (SCFGA_LIB_ERR
);
795 if ((cmd
!= SCFGA_DEV_CONFIGURE
) && (cmd
!= SCFGA_DEV_UNCONFIGURE
)) {
796 return (SCFGA_LIB_ERR
);
799 if ((root_path
= strdup(apidp
->hba_phys
)) == NULL
) {
801 return (SCFGA_LIB_ERR
);
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
!= '/') {
816 /* Remove dynamic component if any */
817 if ((cp
= GET_DYN(root_path
)) != NULL
) {
821 /* Remove minor name if any */
822 if ((cp
= strrchr(root_path
, ':')) != NULL
) {
827 * Cached snapshots are always rooted at "/"
831 if ((root
= di_init("/", DINFOCACHE
)) == DI_NODE_NIL
) {
838 * Lookup the subtree of interest
840 walk_root
= di_lookup_node(root
, root_path
);
842 if (walk_root
== DI_NODE_NIL
) {
846 return (SCFGA_LIB_ERR
);
850 if ((pi_node
= di_path_next_client(walk_root
, pi_node
)) ==
852 /* the path apid not found */
855 return (SCFGA_APID_NOEXIST
);
859 /* check the length first. */
860 if (strlen(di_path_bus_addr(pi_node
)) !=
861 strlen(apidp
->dyncomp
)) {
865 /* compare bus addr. */
866 if (strcmp(di_path_bus_addr(pi_node
), apidp
->dyncomp
) == 0) {
870 pi_node
= di_path_next_client(root
, pi_node
);
871 } while (pi_node
!= DI_PATH_NIL
);
876 return (SCFGA_APID_NOEXIST
);
879 /* Get client node path. */
880 client_node
= di_path_client_node(pi_node
);
881 if (client_node
== DI_NODE_NIL
) {
886 client_path
= di_devfs_path(client_node
);
887 if (client_path
== NULL
) {
893 if ((apidp
->flags
& FLAG_DISABLE_RCM
) == 0) {
894 if (cmd
== SCFGA_DEV_UNCONFIGURE
) {
895 if (check_available_path(client_node
,
898 * last path. check if unconfiguring
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
,
909 if (ret
!= SCFGA_OK
) {
911 di_devfs_path_free(client_path
);
920 ret
= devctl_cmd(apidp
->path
, cmd
, NULL
, l_errnop
);
921 if (ret
!= SCFGA_OK
) {
922 cfga_err(errstring
, *l_errnop
, errid
, 0);
925 * If an unconfigure fails, cancel the RCM offline.
926 * Discard any RCM failures so that the devctl
927 * failure will still be reported.
929 if ((apidp
->flags
& FLAG_DISABLE_RCM
) == 0) {
930 if (cmd
== SCFGA_DEV_UNCONFIGURE
)
931 (void) scsi_rcm_online(dev_list
,
936 di_devfs_path_free(client_path
);
946 const char *physpath
,
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
*);
959 if (statep
!= NULL
) *statep
= 0;
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
);
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
);
985 assert(type
== BUS_OP
|| type
== DEV_OP
);
987 if (func
== NULL
&& state_func
== NULL
) {
992 * Fix up path for calling devctl.
994 if ((path
= strdup(physpath
)) == NULL
) {
996 return (SCFGA_LIB_ERR
);
999 /* Remove dynamic component if any */
1000 if ((cp
= GET_DYN(path
)) != NULL
) {
1004 /* Remove minor name */
1005 if ((cp
= strrchr(path
, ':')) != NULL
) {
1011 if (type
== BUS_OP
) {
1012 hdl
= devctl_bus_acquire(path
, 0);
1014 hdl
= devctl_device_acquire(path
, 0);
1025 /* Only getstate functions require a second argument */
1026 if (func
!= NULL
&& statep
== NULL
) {
1029 } else if (state_func
!= NULL
&& statep
!= NULL
) {
1030 rv
= state_func(hdl
, statep
);
1037 devctl_release(hdl
);
1039 return ((rv
== -1) ? SCFGA_ERR
: SCFGA_OK
);
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
1050 known_state(di_node_t node
)
1054 state
= di_state(node
);
1057 * CF1 without offline flag set is considered unknown state.
1058 * We are in a known state if either CF2 (driver attached) or
1061 if ((state
& DI_DEVICE_OFFLINE
) == DI_DEVICE_OFFLINE
||
1062 (state
& DI_DRIVER_DETACHED
) != DI_DRIVER_DETACHED
) {
1070 list_free(ldata_list_t
**llpp
)
1072 ldata_list_t
*lp
, *olp
;
1075 while (lp
!= NULL
) {
1085 * Obtain the devlink from a /devices path
1087 typedef struct walk_link
{
1094 get_link(di_devlink_t devlink
, void *arg
)
1096 walk_link_t
*larg
= (walk_link_t
*)arg
;
1099 * When path is specified, it's the node path without minor
1100 * name. Therefore, the ../.. prefixes needs to be stripped.
1103 char *content
= (char *)di_devlink_content(devlink
);
1104 char *start
= strstr(content
, "/devices/");
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
);
1113 *(larg
->linkpp
) = strdup(di_devlink_path(devlink
));
1114 return (DI_WALK_TERMINATE
);
1118 physpath_to_devlink(
1125 di_devlink_handle_t hdl
;
1128 if ((hdl
= di_devlink_init(NULL
, 0)) == NULL
) {
1130 return (SCFGA_LIB_ERR
);
1134 larg
.linkpp
= logpp
;
1136 minor_path
= node_path
+ strlen(DEVICES_DIR
);
1140 larg
.len
= strlen(node_path
);
1141 larg
.path
= node_path
;
1144 (void) di_devlink_walk(hdl
, NULL
, minor_path
, DI_PRIMARY_LINK
,
1145 (void *)&larg
, get_link
);
1147 (void) di_devlink_fini(&hdl
);
1150 return (SCFGA_LIB_ERR
);
1156 hba_dev_cmp(const char *hba
, const char *devpath
)
1160 size_t hba_len
, dev_len
;
1161 char l_hba
[MAXPATHLEN
], l_dev
[MAXPATHLEN
];
1163 (void) snprintf(l_hba
, sizeof (l_hba
), "%s", hba
);
1164 (void) snprintf(l_dev
, sizeof (l_dev
), "%s", devpath
);
1166 /* Remove dynamic component if any */
1167 if ((cp
= GET_DYN(l_hba
)) != NULL
) {
1171 if ((cp
= GET_DYN(l_dev
)) != NULL
) {
1176 /* Remove minor names */
1177 if ((cp
= strrchr(l_hba
, ':')) != NULL
) {
1181 if ((cp
= strrchr(l_dev
, ':')) != NULL
) {
1185 hba_len
= strlen(l_hba
);
1186 dev_len
= strlen(l_dev
);
1188 /* Check if HBA path is component of device path */
1189 if (rv
= strncmp(l_hba
, l_dev
, hba_len
)) {
1193 /* devpath must have '/' and 1 char in addition to hba path */
1194 if (dev_len
>= hba_len
+ 2 && l_dev
[hba_len
] == '/') {
1202 dev_cmp(const char *dev1
, const char *dev2
, int match_minor
)
1204 char l_dev1
[MAXPATHLEN
], l_dev2
[MAXPATHLEN
];
1208 (void) snprintf(l_dev1
, sizeof (l_dev1
), "%s", dev1
);
1209 (void) snprintf(l_dev2
, sizeof (l_dev2
), "%s", dev2
);
1211 if ((mn1
= GET_DYN(l_dev1
)) != NULL
) {
1215 if ((mn2
= GET_DYN(l_dev2
)) != NULL
) {
1219 /* Separate out the minor names */
1220 if ((mn1
= strrchr(l_dev1
, ':')) != NULL
) {
1224 if ((mn2
= strrchr(l_dev2
, ':')) != NULL
) {
1228 if ((rv
= strcmp(l_dev1
, l_dev2
)) != 0 || !match_minor
) {
1233 * Compare minor names
1235 if (mn1
== NULL
&& mn2
== NULL
) {
1237 } else if (mn1
== NULL
) {
1239 } else if (mn2
== NULL
) {
1242 return (strcmp(mn1
, mn2
));