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