sd: remove 'ssd' driver support
[unleashed/tickless.git] / usr / src / lib / libdevinfo / devfsmap.c
blobdaf6cb6bb14566a40099aacd8ca59baf9358830f
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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <strings.h>
30 #include <stdarg.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <stropts.h>
36 #include <time.h>
37 #include <sys/param.h>
38 #include <sys/vfstab.h>
39 #include <dirent.h>
40 #ifdef __sparc
41 #include <sys/scsi/adapters/scsi_vhci.h>
42 #include <sys/sunmdi.h>
43 #endif /* __sparc */
44 #include "libdevinfo.h"
45 #include "device_info.h"
46 #include <regex.h>
48 #define isnewline(ch) ((ch) == '\n' || (ch) == '\r' || (ch) == '\f')
49 #define isnamechar(ch) (isalpha(ch) || isdigit(ch) || (ch) == '_' ||\
50 (ch) == '-')
51 #define MAX_TOKEN_SIZE 1024
52 #define BUFSIZE 1024
53 #define STRVAL(s) ((s) ? (s) : "NULL")
55 #define SCSI_VHCI_CONF "/kernel/drv/scsi_vhci.conf"
56 #define QLC_CONF "/kernel/drv/qlc.conf"
57 #define FP_CONF "/kernel/drv/fp.conf"
58 #define DRIVER_CLASSES "/etc/driver_classes"
59 #define FP_AT "fp@"
60 #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl"
61 #define SLASH_DEVICES "/devices"
62 #define SLASH_DEVICES_SLASH "/devices/"
63 #define SLASH_FP_AT "/fp@"
64 #define SLASH_SCSI_VHCI "/scsi_vhci"
65 #define META_DEV "/dev/md/dsk/"
66 #define SLASH_DEV_SLASH "/dev/"
69 * Macros to produce a quoted string containing the value of a
70 * preprocessor macro. For example, if SIZE is defined to be 256,
71 * VAL2STR(SIZE) is "256". This is used to construct format
72 * strings for scanf-family functions below.
74 #define QUOTE(x) #x
75 #define VAL2STR(x) QUOTE(x)
77 typedef enum {
78 CLIENT_TYPE_UNKNOWN,
79 CLIENT_TYPE_PHCI,
80 CLIENT_TYPE_VHCI
81 } client_type_t;
83 typedef enum {
84 T_EQUALS,
85 T_AMPERSAND,
86 T_BIT_OR,
87 T_STAR,
88 T_POUND,
89 T_COLON,
90 T_SEMICOLON,
91 T_COMMA,
92 T_SLASH,
93 T_WHITE_SPACE,
94 T_NEWLINE,
95 T_EOF,
96 T_STRING,
97 T_HEXVAL,
98 T_DECVAL,
99 T_NAME
100 } token_t;
102 typedef enum {
103 begin, parent, drvname, drvclass, prop,
104 parent_equals, name_equals, drvclass_equals,
105 parent_equals_string, name_equals_string,
106 drvclass_equals_string,
107 prop_equals, prop_equals_string, prop_equals_integer,
108 prop_equals_string_comma, prop_equals_integer_comma
109 } conf_state_t;
111 /* structure to hold entries with mpxio-disable property in driver.conf file */
112 struct conf_entry {
113 char *name;
114 char *parent;
115 char *class;
116 char *unit_address;
117 int port;
118 int mpxio_disable;
119 struct conf_entry *next;
122 struct conf_file {
123 char *filename;
124 FILE *fp;
125 int linenum;
128 static char *tok_err = "Unexpected token '%s'\n";
131 /* #define DEBUG */
133 #ifdef DEBUG
135 int devfsmap_debug = 0;
136 /* /var/run is not mounted at install time. Therefore use /tmp */
137 char *devfsmap_logfile = "/tmp/devfsmap.log";
138 static FILE *logfp;
139 #define logdmsg(args) log_debug_msg args
140 static void vlog_debug_msg(char *, va_list);
141 static void log_debug_msg(char *, ...);
142 #ifdef __sparc
143 static void log_confent_list(char *, struct conf_entry *, int);
144 static void log_pathlist(char **);
145 #endif /* __sparc */
147 #else /* DEBUG */
148 #define logdmsg(args) /* nothing */
149 #endif /* DEBUG */
153 * Leave NEWLINE as the next character.
155 static void
156 find_eol(FILE *fp)
158 int ch;
160 while ((ch = getc(fp)) != EOF) {
161 if (isnewline(ch)) {
162 (void) ungetc(ch, fp);
163 break;
168 /* ignore parsing errors */
169 /*ARGSUSED*/
170 static void
171 file_err(struct conf_file *filep, char *fmt, ...)
173 #ifdef DEBUG
174 va_list ap;
176 va_start(ap, fmt);
177 log_debug_msg("WARNING: %s line # %d: ",
178 filep->filename, filep->linenum);
179 vlog_debug_msg(fmt, ap);
180 va_end(ap);
181 #endif /* DEBUG */
184 /* return the next token from the given driver.conf file, or -1 on error */
185 static token_t
186 lex(struct conf_file *filep, char *val, size_t size)
188 char *cp;
189 int ch, oval, badquote;
190 size_t remain;
191 token_t token;
192 FILE *fp = filep->fp;
194 if (size < 2)
195 return (-1);
197 cp = val;
198 while ((ch = getc(fp)) == ' ' || ch == '\t')
201 remain = size - 1;
202 *cp++ = (char)ch;
203 switch (ch) {
204 case '=':
205 token = T_EQUALS;
206 break;
207 case '&':
208 token = T_AMPERSAND;
209 break;
210 case '|':
211 token = T_BIT_OR;
212 break;
213 case '*':
214 token = T_STAR;
215 break;
216 case '#':
217 token = T_POUND;
218 break;
219 case ':':
220 token = T_COLON;
221 break;
222 case ';':
223 token = T_SEMICOLON;
224 break;
225 case ',':
226 token = T_COMMA;
227 break;
228 case '/':
229 token = T_SLASH;
230 break;
231 case ' ':
232 case '\t':
233 case '\f':
234 while ((ch = getc(fp)) == ' ' ||
235 ch == '\t' || ch == '\f') {
236 if (--remain == 0) {
237 *cp = '\0';
238 return (-1);
240 *cp++ = (char)ch;
242 (void) ungetc(ch, fp);
243 token = T_WHITE_SPACE;
244 break;
245 case '\n':
246 case '\r':
247 token = T_NEWLINE;
248 break;
249 case '"':
250 remain++;
251 cp--;
252 badquote = 0;
253 while (!badquote && (ch = getc(fp)) != '"') {
254 switch (ch) {
255 case '\n':
256 case EOF:
257 file_err(filep, "Missing \"\n");
258 remain = size - 1;
259 cp = val;
260 *cp++ = '\n';
261 badquote = 1;
262 /* since we consumed the newline/EOF */
263 (void) ungetc(ch, fp);
264 break;
266 case '\\':
267 if (--remain == 0) {
268 *cp = '\0';
269 return (-1);
271 ch = (char)getc(fp);
272 if (!isdigit(ch)) {
273 /* escape the character */
274 *cp++ = (char)ch;
275 break;
277 oval = 0;
278 while (ch >= '0' && ch <= '7') {
279 ch -= '0';
280 oval = (oval << 3) + ch;
281 ch = (char)getc(fp);
283 (void) ungetc(ch, fp);
284 /* check for character overflow? */
285 if (oval > 127) {
286 file_err(filep,
287 "Character "
288 "overflow detected.\n");
290 *cp++ = (char)oval;
291 break;
292 default:
293 if (--remain == 0) {
294 *cp = '\0';
295 return (-1);
297 *cp++ = (char)ch;
298 break;
301 token = T_STRING;
302 break;
304 case EOF:
305 token = T_EOF;
306 break;
308 default:
310 * detect a lone '-' (including at the end of a line), and
311 * identify it as a 'name'
313 if (ch == '-') {
314 if (--remain == 0) {
315 *cp = '\0';
316 return (-1);
318 *cp++ = (char)(ch = getc(fp));
319 if (ch == ' ' || ch == '\t' || ch == '\n') {
320 (void) ungetc(ch, fp);
321 remain++;
322 cp--;
323 token = T_NAME;
324 break;
326 } else if (ch == '~' || ch == '-') {
327 if (--remain == 0) {
328 *cp = '\0';
329 return (-1);
331 *cp++ = (char)(ch = getc(fp));
335 if (isdigit(ch)) {
336 if (ch == '0') {
337 if ((ch = getc(fp)) == 'x') {
338 if (--remain == 0) {
339 *cp = '\0';
340 return (-1);
342 *cp++ = (char)ch;
343 ch = getc(fp);
344 while (isxdigit(ch)) {
345 if (--remain == 0) {
346 *cp = '\0';
347 return (-1);
349 *cp++ = (char)ch;
350 ch = getc(fp);
352 (void) ungetc(ch, fp);
353 token = T_HEXVAL;
354 } else {
355 goto digit;
357 } else {
358 ch = getc(fp);
359 digit:
360 while (isdigit(ch)) {
361 if (--remain == 0) {
362 *cp = '\0';
363 return (-1);
365 *cp++ = (char)ch;
366 ch = getc(fp);
368 (void) ungetc(ch, fp);
369 token = T_DECVAL;
371 } else if (isalpha(ch) || ch == '\\') {
372 if (ch != '\\') {
373 ch = getc(fp);
374 } else {
376 * if the character was a backslash,
377 * back up so we can overwrite it with
378 * the next (i.e. escaped) character.
380 remain++;
381 cp--;
383 while (isnamechar(ch) || ch == '\\') {
384 if (ch == '\\')
385 ch = getc(fp);
386 if (--remain == 0) {
387 *cp = '\0';
388 return (-1);
390 *cp++ = (char)ch;
391 ch = getc(fp);
393 (void) ungetc(ch, fp);
394 token = T_NAME;
395 } else {
396 return (-1);
398 break;
401 *cp = '\0';
403 return (token);
406 #ifdef __sparc
408 static void
409 free_confent(struct conf_entry *confent)
411 free(confent->name);
412 free(confent->parent);
413 free(confent->class);
414 free(confent->unit_address);
415 free(confent);
418 static void
419 free_confent_list(struct conf_entry *confent_list)
421 struct conf_entry *confent, *next;
423 for (confent = confent_list; confent != NULL; confent = next) {
424 next = confent->next;
425 free_confent(confent);
430 * Parse the next entry from the driver.conf file and return in the form of
431 * a pointer to the conf_entry.
433 static struct conf_entry *
434 parse_conf_entry(struct conf_file *filep, char *tokbuf, size_t linesize)
436 char *prop_name, *string;
437 token_t token;
438 struct conf_entry *confent;
439 conf_state_t state;
440 int failed = 1;
442 if ((confent = calloc(1, sizeof (*confent))) == NULL)
443 return (NULL);
445 confent->port = -1;
446 confent->mpxio_disable = -1;
448 state = begin;
449 token = T_NAME;
450 prop_name = NULL;
451 string = NULL;
452 do {
453 switch (token) {
454 case T_NAME:
455 switch (state) {
456 case prop_equals_string:
457 case prop_equals_integer:
458 case begin:
459 state = prop;
460 if ((prop_name = strdup(tokbuf)) == NULL)
461 goto bad;
462 break;
463 default:
464 file_err(filep, tok_err, tokbuf);
466 break;
467 case T_EQUALS:
468 switch (state) {
469 case prop:
470 state = prop_equals;
471 break;
472 default:
473 file_err(filep, tok_err, tokbuf);
475 break;
476 case T_STRING:
477 switch (state) {
478 case prop_equals:
479 if ((string = strdup(tokbuf)) == NULL)
480 goto bad;
482 state = begin;
483 if (strcmp(prop_name, "PARENT") == 0 ||
484 strcmp(prop_name, "parent") == 0) {
485 if (confent->parent) {
486 file_err(filep,
487 "'parent' property already specified\n");
488 goto bad;
490 confent->parent = string;
491 } else if (strcmp(prop_name, "NAME") == 0 ||
492 strcmp(prop_name, "name") == 0) {
493 if (confent->name) {
494 file_err(filep,
495 "'name' property already specified\n");
496 goto bad;
498 confent->name = string;
499 } else if (strcmp(prop_name, "CLASS") == 0 ||
500 strcmp(prop_name, "class") == 0) {
501 if (confent->class) {
502 file_err(filep,
503 "'class' property already specified\n");
504 goto bad;
506 confent->class = string;
507 } else if (strcmp(prop_name, "unit-address")
508 == 0) {
509 if (confent->unit_address) {
510 file_err(filep,
511 "'unit-address' property already specified\n");
512 goto bad;
514 confent->unit_address = string;
515 } else if (strcmp(prop_name, "mpxio-disable")
516 == 0) {
517 if (confent->mpxio_disable != -1) {
518 file_err(filep,
519 "'mpxio-disable' property already specified\n");
520 goto bad;
522 if (strcmp(string, "yes") == 0)
523 confent->mpxio_disable = 1;
524 else if (strcmp(string, "no") == 0)
525 confent->mpxio_disable = 0;
526 else {
527 file_err(filep,
528 "'mpxio-disable' property setting is invalid. "
529 "The value must be either \"yes\" or \"no\"\n");
530 goto bad;
532 free(string);
533 } else {
534 free(string);
535 state = prop_equals_string;
537 string = NULL;
538 free(prop_name);
539 prop_name = NULL;
540 break;
542 case prop_equals_string_comma:
543 state = prop_equals_string;
544 break;
545 default:
546 file_err(filep, tok_err, tokbuf);
548 break;
549 case T_HEXVAL:
550 case T_DECVAL:
551 switch (state) {
552 case prop_equals:
553 if (strcmp(prop_name, "port") == 0) {
554 if (confent->port != -1) {
555 file_err(filep,
556 "'port' property already specified\n");
557 goto bad;
559 confent->port =
560 (int)strtol(tokbuf, NULL, 0);
561 state = begin;
562 } else
563 state = prop_equals_integer;
564 free(prop_name);
565 prop_name = NULL;
566 break;
568 case prop_equals_integer_comma:
569 state = prop_equals_integer;
570 break;
571 default:
572 file_err(filep, tok_err, tokbuf);
574 break;
575 case T_COMMA:
576 switch (state) {
577 case prop_equals_string:
578 state = prop_equals_string_comma;
579 break;
580 case prop_equals_integer:
581 state = prop_equals_integer_comma;
582 break;
583 default:
584 file_err(filep, tok_err, tokbuf);
586 break;
587 case T_NEWLINE:
588 filep->linenum++;
589 break;
590 case T_POUND:
591 find_eol(filep->fp);
592 break;
593 case T_EOF:
594 file_err(filep, "Unexpected EOF\n");
595 goto bad;
596 default:
597 file_err(filep, tok_err, tokbuf);
598 goto bad;
600 } while ((token = lex(filep, tokbuf, linesize)) != T_SEMICOLON);
602 failed = 0;
604 bad:
605 free(prop_name);
606 free(string);
607 if (failed == 1) {
608 free_confent(confent);
609 return (NULL);
611 return (confent);
615 * Parse all entries with mpxio-disable property in the given driver.conf
616 * file.
618 * fname driver.conf file name
619 * confent_list on return *confent_list will contain the list of
620 * driver.conf file entries with mpxio-disable property.
621 * mpxio_disable on return *mpxio_disable is set to the setting of the
622 * driver global mpxio-dissable property as follows.
623 * 0 if driver mpxio-disable="no"
624 * 1 if driver mpxio-disable="yes"
625 * -1 if driver mpxio-disable property isn't specified.
627 static void
628 parse_conf_file(char *fname, struct conf_entry **confent_list,
629 int *mpxio_disable)
631 struct conf_entry *confent, *tail = NULL;
632 token_t token;
633 struct conf_file file;
634 char tokval[MAX_TOKEN_SIZE];
636 *confent_list = NULL;
637 *mpxio_disable = -1;
638 if ((file.fp = fopen(fname, "r")) == NULL)
639 return;
641 file.filename = fname;
642 file.linenum = 1;
644 while ((token = lex(&file, tokval, MAX_TOKEN_SIZE)) != T_EOF) {
645 switch (token) {
646 case T_POUND:
648 * Skip comments.
650 find_eol(file.fp);
651 break;
652 case T_NAME:
653 if ((confent = parse_conf_entry(&file, tokval,
654 MAX_TOKEN_SIZE)) == NULL)
655 break;
657 * No name indicates global property.
658 * Make sure parent and class not NULL.
660 if (confent->name == NULL) {
661 if (confent->parent ||
662 confent->class) {
663 file_err(&file,
664 "missing name attribute\n");
665 } else if (confent->mpxio_disable != -1) {
666 if (*mpxio_disable == -1)
667 *mpxio_disable =
668 confent->mpxio_disable;
669 else
670 file_err(&file,
671 "'mpxio-disable' property already specified\n");
673 free_confent(confent);
674 break;
678 * This is a node spec, either parent or class
679 * must be specified.
681 if (confent->parent == NULL && confent->class == NULL) {
682 file_err(&file,
683 "missing parent or class attribute\n");
684 free_confent(confent);
685 break;
688 /* only need entries with mpxio_disable property */
689 if (confent->mpxio_disable == -1) {
690 free_confent(confent);
691 break;
694 if (tail)
695 tail->next = confent;
696 else
697 *confent_list = confent;
698 tail = confent;
699 break;
701 case T_NEWLINE:
702 file.linenum++;
703 break;
704 default:
705 break;
709 (void) fclose(file.fp);
713 * Return the driver class of the given driver_name.
714 * The memory for the driver class is allocated by this function and the
715 * caller must free it.
717 static char *
718 get_driver_class(char *rootdir, char *driver_name)
720 FILE *fp;
721 char buf[BUFSIZE];
722 char driver[BUFSIZE];
723 char class_name[BUFSIZE];
725 logdmsg(("get_driver_class: rootdir = %s, driver name = %s\n",
726 rootdir, driver_name));
728 (void) snprintf(buf, sizeof (buf), "%s%s", rootdir, DRIVER_CLASSES);
730 if ((fp = fopen(buf, "r")) == NULL) {
731 logdmsg(("get_driver_class: failed to open %s: %s\n",
732 buf, strerror(errno)));
733 return (NULL);
736 while (fgets(buf, sizeof (buf), fp) != NULL) {
737 /* LINTED - unbounded string specifier */
738 if ((sscanf(buf, "%s %s", driver, class_name) == 2) &&
739 driver[0] != '#' && strcmp(driver, driver_name) == 0) {
740 logdmsg(("get_driver_class: driver class = %s\n",
741 class_name));
742 (void) fclose(fp);
743 return (strdup(class_name));
747 (void) fclose(fp);
748 return (NULL);
751 static int
752 lookup_in_confent_list(struct conf_entry *confent_list,
753 int match_class, char *parent, char *unit_addr, int port)
755 struct conf_entry *confent;
756 char *par;
758 logdmsg(("lookup_in_confent_list: %s = \"%s\", unit_addr = \"%s\", "
759 "port = %d\n", (match_class) ? "class" : "parent", parent,
760 STRVAL(unit_addr), port));
762 for (confent = confent_list; confent != NULL; confent = confent->next) {
763 par = (match_class) ? confent->class : confent->parent;
764 if (unit_addr) {
765 if (confent->unit_address != NULL &&
766 strcmp(confent->unit_address, unit_addr) == 0 &&
767 par != NULL && strcmp(par, parent) == 0)
768 return (confent->mpxio_disable);
769 } else {
770 if (confent->port == port &&
771 par != NULL && strcmp(par, parent) == 0)
772 return (confent->mpxio_disable);
775 return (-1);
779 * lookup mpxio-disabled property setting for the given path in the given
780 * driver.conf file. Match the entries from most specific to least specific.
782 * conf_file the path name of either fp.conf, qlc.conf or scsi_vhci.conf
783 * path /devices node path without the /devices prefix.
784 * If the conf_file is fp.conf, path must be a fp node path
785 * if the conf_file is qlc.conf, path must be a qlc node path.
786 * if the conf_file is scsi_vhci.conf, path must be NULL.
787 * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0
788 * /pci@8,600000/SUNW,qlc@4
790 * returns:
791 * 0 if mpxio-disable="no"
792 * 1 if mpxio-disable="yes"
793 * -1 if mpxio-disable property isn't specified.
795 static int
796 lookup_in_conf_file(char *rootdir, char *conf_file, char *path)
798 struct conf_entry *confent_list = NULL;
799 int mpxio_disable;
800 di_node_t par_node = DI_NODE_NIL;
801 char *node_name = NULL, *node_addr = NULL;
802 char *unit_addr = NULL;
803 int port = -1;
804 char *par_node_name = NULL, *par_node_addr = NULL;
805 char *par_binding_name = NULL, *par_driver_name = NULL;
806 char *par_driver_class = NULL, *par_node_name_addr;
807 int rv = -1;
808 char buf[MAXPATHLEN];
810 logdmsg(("lookup_in_conf_file: rootdir = \"%s\", conf_file = \"%s\", "
811 "path = \"%s\"\n", rootdir, conf_file, STRVAL(path)));
813 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, conf_file);
814 parse_conf_file(buf, &confent_list, &mpxio_disable);
815 #ifdef DEBUG
816 log_confent_list(buf, confent_list, mpxio_disable);
817 #endif
819 /* if path is NULL, return driver global mpxio-disable setting */
820 if (path == NULL) {
821 rv = mpxio_disable;
822 goto done;
825 if ((node_name = strrchr(path, '/')) == NULL)
826 goto done;
828 *node_name = '\0';
829 node_name++;
831 if ((node_addr = strchr(node_name, '@')) == NULL)
832 goto done;
834 *node_addr = '\0';
835 node_addr++;
837 if (strcmp(node_name, "fp") == 0) {
838 /* get port number; encoded in the node addr as a hex number */
839 port = (int)strtol(node_addr, NULL, 16);
840 } else
841 unit_addr = node_addr;
844 * Match from most specific to least specific;
845 * first, start the lookup based on full path.
847 if ((rv = lookup_in_confent_list(confent_list, 0, path,
848 unit_addr, port)) != -1)
849 goto done;
851 /* lookup nodename@address */
852 if ((par_node_name_addr = strrchr(path, '/')) != NULL) {
853 par_node_name_addr++;
854 if ((rv = lookup_in_confent_list(confent_list, 0,
855 par_node_name_addr, unit_addr, port)) != -1)
856 goto done;
859 /* di_init() doesn't work when 0 is passed in flags */
860 par_node = di_init(path, DINFOMINOR);
861 if (par_node != DI_NODE_NIL) {
862 par_node_name = di_node_name(par_node);
863 par_node_addr = di_bus_addr(par_node);
864 par_binding_name = di_binding_name(par_node);
865 par_driver_name = di_driver_name(par_node);
868 logdmsg(("par_node_name = %s\n", STRVAL(par_node_name)));
869 logdmsg(("par_node_addr = %s\n", STRVAL(par_node_addr)));
870 logdmsg(("par_binding_name = %s\n", STRVAL(par_binding_name)));
871 logdmsg(("par_driver_name = %s\n", STRVAL(par_driver_name)));
873 /* lookup bindingname@address */
874 if (par_binding_name != NULL && par_binding_name != par_node_name &&
875 par_node_addr != NULL) {
876 (void) snprintf(buf, sizeof (buf), "%s@%s", par_binding_name,
877 par_node_addr);
878 if ((rv = lookup_in_confent_list(confent_list, 0,
879 buf, unit_addr, port)) != -1)
880 goto done;
883 /* lookup binding name */
884 if (par_binding_name != NULL) {
885 if ((rv = lookup_in_confent_list(confent_list, 0,
886 par_binding_name, unit_addr, port)) != -1)
887 goto done;
890 if (par_driver_name != NULL) {
891 /* lookup driver name */
892 if ((rv = lookup_in_confent_list(confent_list, 0,
893 par_driver_name, unit_addr, port)) != -1)
894 goto done;
896 /* finally, lookup class name */
897 par_driver_class = get_driver_class(rootdir, par_driver_name);
898 if (par_driver_class != NULL) {
899 if ((rv = lookup_in_confent_list(confent_list, 1,
900 par_driver_class, unit_addr, port)) != -1)
901 goto done;
906 * no match so far;
907 * use the driver global mpxio-disable setting if exists.
909 rv = mpxio_disable;
911 done:
912 if (node_name != NULL)
913 *(node_name - 1) = '/';
914 if (node_addr != NULL)
915 *(node_addr - 1) = '@';
916 free(par_driver_class);
917 if (confent_list != NULL)
918 free_confent_list(confent_list);
919 if (par_node != DI_NODE_NIL)
920 di_fini(par_node);
922 return (rv);
926 * Given client_name return whether it is a phci or vhci based name.
927 * client_name is /devices name of a client without the /devices prefix.
929 * client_name Return value
930 * .../fp@xxx/ssd@yyy CLIENT_TYPE_PHCI
931 * .../scsi_vhci/ssd@yyy CLIENT_TYPE_VHCI
932 * other CLIENT_TYPE_UNKNOWN
934 static client_type_t
935 client_name_type(char *client_name)
937 client_type_t client_type;
938 char *p1, *p2;
940 logdmsg(("client_name_type: client_name = %s\n", client_name));
942 if (strncmp(client_name, SLASH_SCSI_VHCI,
943 sizeof (SLASH_SCSI_VHCI) - 1) == 0)
944 return (CLIENT_TYPE_VHCI);
946 if (*client_name != '/')
947 return (CLIENT_TYPE_UNKNOWN);
949 if ((p1 = strrchr(client_name, '/')) == NULL)
950 return (CLIENT_TYPE_UNKNOWN);
952 *p1 = '\0';
954 if ((p2 = strrchr(client_name, '/')) != NULL &&
955 strncmp(p2, SLASH_FP_AT, sizeof (SLASH_FP_AT) - 1) == 0)
956 client_type = CLIENT_TYPE_PHCI;
957 else
958 client_type = CLIENT_TYPE_UNKNOWN;
960 *p1 = '/';
961 return (client_type);
965 * Compare controller name portion of dev1 and dev2.
967 * rootdir root directory of the target environment
968 * dev1 can be either a /dev link or /devices name in the target
969 * environemnt
970 * dev2 /devices name of a device without the /devices prefix
972 * Returns:
973 * 0 if controller names match
974 * 1 if controller names don't match
975 * -1 an error occurred.
977 static int
978 compare_controller(char *rootdir, char *dev1, char *dev2)
980 int linksize;
981 char *p1, *p;
982 char physdev1[MAXPATHLEN];
983 char buf[MAXPATHLEN];
985 logdmsg(("compare_controller: rootdir = %s, dev1 = %s, dev2 = %s\n",
986 rootdir, dev1, dev2));
988 if (strncmp(dev1, SLASH_DEV_SLASH, sizeof (SLASH_DEV_SLASH) - 1)
989 == 0) {
990 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, dev1);
991 if ((linksize = readlink(buf, physdev1, MAXPATHLEN)) > 0 &&
992 linksize < (MAXPATHLEN - 1)) {
993 physdev1[linksize] = '\0';
994 logdmsg(("compare_controller: physdev1 = %s\n",
995 physdev1));
996 } else
997 return (-1);
998 } else
999 (void) strlcpy(physdev1, dev1, MAXPATHLEN);
1001 if ((p1 = strstr(physdev1, SLASH_DEVICES)) == NULL)
1002 return (-1);
1004 p1 += sizeof (SLASH_DEVICES) - 1;
1005 /* strip the device portion */
1006 if ((p = strrchr(p1, '/')) == NULL)
1007 return (-1);
1008 *p = '\0';
1010 if ((p = strrchr(dev2, '/')) == NULL)
1011 return (-1);
1012 *p = '\0';
1014 logdmsg(("compare_controller: path1 = %s, path2 = %s\n",
1015 p1, dev2));
1016 if (strcmp(p1, dev2) == 0) {
1017 *p = '/';
1018 return (0);
1019 } else {
1020 *p = '/';
1021 return (1);
1026 * Check if the specified device path is on the root controller.
1028 * rootdir root directory of the target environment
1029 * path /devices name of a device without the /devices prefix
1031 * Returns
1032 * 1 if the path is on the root controller
1033 * 0 if the path is not on the root controller
1034 * -1 if an error occurs
1036 static int
1037 is_root_controller(char *rootdir, char *path)
1039 FILE *fp;
1040 char *tmpfile;
1041 int rv = -1;
1042 struct vfstab vfsent;
1043 char buf[MAXPATHLEN];
1044 char ctd[MAXNAMELEN + 1];
1046 logdmsg(("is_root_controller: rootdir = %s, path = %s\n", rootdir,
1047 path));
1049 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, VFSTAB);
1051 if ((fp = fopen(buf, "r")) == NULL) {
1052 logdmsg(("is_root_controller: failed to open %s: %s\n",
1053 buf, strerror(errno)));
1054 return (-1);
1057 if (getvfsfile(fp, &vfsent, "/") != 0) {
1058 logdmsg(("is_root_controller: getvfsfile: failed to read "
1059 "vfstab entry for mount point \"/\": %s\n",
1060 strerror(errno)));
1061 (void) fclose(fp);
1062 return (-1);
1064 (void) fclose(fp);
1066 /* check if the root is an svm metadisk */
1067 if (strncmp(vfsent.vfs_special, META_DEV, sizeof (META_DEV) - 1) != 0) {
1068 if (compare_controller(rootdir, vfsent.vfs_special, path) == 0)
1069 return (1);
1070 else
1071 return (0);
1074 /* Don't use /var/run as it is not mounted in miniroot */
1075 if ((tmpfile = tempnam("/tmp", "diirc")) == NULL) {
1076 logdmsg(("is_root_controller: tempnam: failed: %s\n",
1077 strerror(errno)));
1078 return (-1);
1081 /* get metadisk components using metastat command */
1082 (void) snprintf(buf, MAXPATHLEN,
1083 "/usr/sbin/metastat -p %s 2>/dev/null | "
1084 "/usr/bin/grep ' 1 1 ' | "
1085 "/usr/bin/sed -e 's/^.* 1 1 //' | "
1086 "/usr/bin/cut -f1 -d ' ' > %s",
1087 vfsent.vfs_special + sizeof (META_DEV) - 1, tmpfile);
1089 logdmsg(("is_root_controller: command = %s\n", buf));
1090 fp = NULL;
1091 if (system(buf) == 0 && (fp = fopen(tmpfile, "r")) != NULL) {
1092 while (fscanf(fp, "%" VAL2STR(MAXNAMELEN) "s", ctd) == 1) {
1093 (void) snprintf(buf, MAXPATHLEN, "/dev/dsk/%s", ctd);
1094 if (compare_controller(rootdir, buf, path) == 0) {
1095 rv = 1;
1096 goto out;
1099 rv = 0;
1102 out:
1103 if (fp)
1104 (void) fclose(fp);
1105 (void) unlink(tmpfile);
1106 free(tmpfile);
1107 return (rv);
1110 static int
1111 file_exists(char *rootdir, char *path)
1113 struct stat stbuf;
1114 char fullpath[MAXPATHLEN];
1115 int x;
1117 (void) snprintf(fullpath, MAXPATHLEN, "%s%s", rootdir, path);
1119 x = stat(fullpath, &stbuf);
1120 logdmsg(("file_exists: %s: %s\n", fullpath, (x == 0) ? "yes" : "no"));
1121 if (x == 0)
1122 return (1);
1123 else
1124 return (0);
1128 * Check if mpxio is enabled or disabled on the specified device path.
1129 * Looks through the .conf files to determine the mpxio setting.
1131 * rootdir root directory of the target environment
1132 * path /devices name of a device without the /devices prefix and
1133 * minor name component.
1135 * Returns
1136 * 1 if mpxio is disabled
1137 * 0 if mpxio is enabled
1138 * -1 if an error occurs
1140 static int
1141 is_mpxio_disabled(char *rootdir, char *path)
1143 int mpxio_disable;
1144 char *p;
1145 int check_root_controller;
1147 logdmsg(("is_mpxio_disabled: rootdir = %s, path = %s\n",
1148 rootdir, path));
1150 if (file_exists(rootdir, SCSI_VHCI_CONF) == 0) {
1152 * scsi_vhci.conf doesn't exist:
1153 * if upgrading from a pre solaris 9 release. or
1154 * if this function is called during fresh or flash install
1155 * prior to installing scsi_vhci.conf file.
1157 if (file_exists(rootdir, "/kernel/drv"))
1158 /* upgrading from pre solaris 9 */
1159 return (1);
1160 else
1161 /* fresh or flash install */
1162 return (0);
1165 mpxio_disable = lookup_in_conf_file(rootdir, SCSI_VHCI_CONF, NULL);
1168 * scsi_vhci.conf contains mpxio-disable property only in s9 and
1169 * s8+sfkpatch. This property is no longer present from s10 onwards.
1171 if (mpxio_disable == 1) {
1172 /* upgrading from s8 or s9 with mpxio globally disabled */
1173 return (1);
1174 } else if (mpxio_disable == 0) {
1175 /* upgrading from s8 or s9 with mpxio globally enabled */
1176 check_root_controller = 1;
1177 } else {
1179 * We are looking at the s10 version of the file. This is
1180 * the case if this function is called after installing the
1181 * new scsi_vhci.conf file.
1183 check_root_controller = 0;
1186 if ((mpxio_disable = lookup_in_conf_file(rootdir, FP_CONF, path))
1187 != -1)
1188 return (mpxio_disable);
1190 if ((p = strrchr(path, '/')) == NULL)
1191 return (-1);
1193 *p = '\0';
1194 if ((mpxio_disable = lookup_in_conf_file(rootdir, QLC_CONF, path))
1195 != -1) {
1196 *p = '/';
1197 return (mpxio_disable);
1199 *p = '/';
1202 * mpxio-disable setting is not found in the .conf files.
1203 * The default is to enable mpxio, except if the path is on the root
1204 * controller.
1206 * In s8 and s9 mpxio is not supported on the root controller.
1207 * NWS supplies a patch to enable root controller support in s8 and s9.
1208 * If the system had the patch installed, the fp.conf file would have
1209 * explicit "mpxio-disable=no" for the root controller. So we would
1210 * have found the mpxio-disable setting when we looked up this property
1211 * in the fp.conf file.
1213 if (check_root_controller) {
1214 mpxio_disable = is_root_controller(rootdir, path);
1215 logdmsg(("is_mpxio_disabled: is_root_controller returned %d\n",
1216 mpxio_disable));
1217 } else
1218 mpxio_disable = 0;
1220 return (mpxio_disable);
1223 static int
1224 vhci_ctl(sv_iocdata_t *iocp, int cmd)
1226 int fd, rv;
1228 if ((fd = open(VHCI_CTL_NODE, O_RDWR)) < 0)
1229 return (-1);
1230 rv = ioctl(fd, cmd, iocp);
1231 (void) close(fd);
1232 return (rv);
1236 * Convert a phci client name to vhci client name.
1238 * phci_name phci client /devices name without the /devices prefix and
1239 * minor name component.
1240 * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
1242 * Returns on success, vhci client name is returned. The memory for
1243 * the vhci name is allocated by this function and the caller
1244 * must free it.
1245 * on failure, NULL is returned.
1247 static char *
1248 phci_to_vhci(char *phci_name)
1250 sv_iocdata_t ioc;
1251 char *slash, *addr, *retp;
1252 char vhci_name_buf[MAXPATHLEN];
1253 char phci_name_buf[MAXPATHLEN];
1254 char addr_buf[MAXNAMELEN];
1256 logdmsg(("phci_to_vhci: pchi_name = %s\n", phci_name));
1257 (void) strlcpy(phci_name_buf, phci_name, MAXPATHLEN);
1259 if ((slash = strrchr(phci_name_buf, '/')) == NULL ||
1260 (addr = strchr(slash, '@')) == NULL)
1261 return (NULL);
1263 *slash = '\0';
1264 addr++;
1265 (void) strlcpy(addr_buf, addr, MAXNAMELEN);
1267 bzero(&ioc, sizeof (sv_iocdata_t));
1268 ioc.client = vhci_name_buf;
1269 ioc.phci = phci_name_buf;
1270 ioc.addr = addr_buf;
1271 if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_NAME) != 0) {
1272 logdmsg(("phci_to_vhci: vhci_ctl failed: %s\n",
1273 strerror(errno)));
1274 return (NULL);
1277 retp = strdup(vhci_name_buf);
1278 logdmsg(("phci_to_vhci: vhci name = %s\n", STRVAL(retp)));
1279 return (retp);
1282 static int
1283 add_to_phci_list(char **phci_list, sv_path_info_t *pi, int npaths, int state,
1284 char *node_name)
1286 int rv = 0;
1287 char name[MAXPATHLEN];
1289 while (npaths--) {
1290 if (state == pi->ret_state) {
1291 (void) snprintf(name, MAXPATHLEN, "%s/%s@%s",
1292 pi->device.ret_phci, node_name, pi->ret_addr);
1293 if ((*phci_list = strdup(name)) == NULL)
1294 return (-1);
1295 phci_list++;
1296 rv++;
1298 pi++;
1301 return (rv);
1304 static void
1305 free_pathlist(char **pathlist)
1307 char **p;
1309 if (pathlist != NULL) {
1310 for (p = pathlist; *p != NULL; p++)
1311 free(*p);
1312 free(pathlist);
1318 * Convert a vhci client name to phci client names.
1320 * vhci_name vhci client /devices name without the /devices prefix and
1321 * minor name component.
1322 * num_paths On return, *num_paths is set to the number paths in the
1323 * returned path list.
1325 * Returns NULL terminated path list containing phci client paths is
1326 * returned on success. The memory for the path list is
1327 * allocated by this function and the caller must free it by
1328 * calling free_pathlist().
1329 * NULL is returned on failure.
1331 static char **
1332 vhci_to_phci(char *vhci_name, int *num_paths)
1334 sv_iocdata_t ioc;
1335 uint_t npaths;
1336 int n;
1337 char **phci_list = NULL;
1338 char *node_name, *at;
1339 char vhci_name_buf[MAXPATHLEN];
1341 logdmsg(("vhci_to_phci: vchi_name = %s\n", vhci_name));
1343 *num_paths = 0;
1344 (void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN);
1346 /* first get the number paths */
1347 bzero(&ioc, sizeof (sv_iocdata_t));
1348 ioc.client = vhci_name_buf;
1349 ioc.ret_elem = &npaths;
1350 if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO) != 0 ||
1351 npaths == 0) {
1352 logdmsg(("vhci_to_phci: vhci_ctl failed to get npaths: %s\n",
1353 strerror(errno)));
1354 return (NULL);
1357 /* now allocate memory for the path information and get all paths */
1358 bzero(&ioc, sizeof (sv_iocdata_t));
1359 ioc.client = vhci_name_buf;
1360 ioc.buf_elem = npaths;
1361 ioc.ret_elem = &npaths;
1362 if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths,
1363 sizeof (sv_path_info_t))) == NULL)
1364 return (NULL);
1365 if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO) != 0 ||
1366 npaths == 0) {
1367 logdmsg(("vhci_to_phci: vhci_ctl failed: %s\n",
1368 strerror(errno)));
1369 goto out;
1372 if (ioc.buf_elem < npaths)
1373 npaths = ioc.buf_elem;
1375 if ((node_name = strrchr(vhci_name_buf, '/')) == NULL ||
1376 (at = strchr(node_name, '@')) == NULL)
1377 goto out;
1379 node_name++;
1380 *at = '\0';
1382 /* allocate one more (than npaths) for the terminating NULL pointer */
1383 if ((phci_list = calloc(npaths + 1, sizeof (char *))) == NULL)
1384 goto out;
1387 * add only online paths as non-online paths may not be accessible
1388 * in the target environment.
1390 if ((n = add_to_phci_list(phci_list, ioc.ret_buf, npaths,
1391 MDI_PATHINFO_STATE_ONLINE, node_name)) <= 0)
1392 goto out;
1394 free(ioc.ret_buf);
1395 *num_paths = n;
1397 #ifdef DEBUG
1398 logdmsg(("vhci_to_phci: phci list:\n"));
1399 log_pathlist(phci_list);
1400 #endif
1401 return (phci_list);
1403 out:
1404 free(ioc.ret_buf);
1405 if (phci_list)
1406 free_pathlist(phci_list);
1407 return (NULL);
1411 * build list of paths accessible from the target environment
1413 static int
1414 build_pathlist(char *rootdir, char *vhcipath, char **pathlist, int npaths)
1416 int mpxio_disabled;
1417 int i, j;
1418 char *vpath = NULL;
1420 for (i = 0; i < npaths; i++) {
1421 mpxio_disabled = is_mpxio_disabled(rootdir, pathlist[i]);
1422 logdmsg(("build_pathlist: mpxio_disabled = %d "
1423 "on path %s\n", mpxio_disabled, pathlist[i]));
1424 if (mpxio_disabled == -1)
1425 return (-1);
1426 if (mpxio_disabled == 0) {
1428 * mpxio is enabled on this phci path.
1429 * So use vhci path instead of phci path.
1431 if (vpath == NULL) {
1432 if ((vpath = strdup(vhcipath)) == NULL)
1433 return (-1);
1434 free(pathlist[i]);
1435 /* keep vhci path at beginning of the list */
1436 for (j = i; j > 0; j--)
1437 pathlist[j] = pathlist[j - 1];
1438 pathlist[0] = vpath;
1439 } else {
1440 free(pathlist[i]);
1441 npaths--;
1442 for (j = i; j < npaths; j++)
1443 pathlist[j] = pathlist[j + 1];
1444 pathlist[npaths] = NULL;
1445 /* compensate for i++ in the for loop */
1446 i--;
1451 #ifdef DEBUG
1452 logdmsg(("build_pathlist: returning npaths = %d, pathlist:\n", npaths));
1453 log_pathlist(pathlist);
1454 #endif
1455 return (npaths);
1459 * Check if the specified device is refenced in the vfstab file.
1460 * Return 1 if referenced, 0 if not.
1462 * rootdir root directory of the target environment
1463 * nodepath /devices path of a device in the target environment without
1464 * the /devices prefix and minor component.
1466 static int
1467 is_dev_in_vfstab(char *rootdir, char *nodepath)
1469 FILE *fp;
1470 int linksize;
1471 struct vfstab vfsent;
1472 char *abspath, *minor;
1473 char physpath[MAXPATHLEN];
1474 char buf[MAXPATHLEN];
1476 logdmsg(("is_dev_in_vfstab: rootdir = %s, nodepath = %s\n",
1477 rootdir, nodepath));
1479 (void) snprintf(buf, sizeof (buf), "%s%s", rootdir, VFSTAB);
1481 if ((fp = fopen(buf, "r")) == NULL)
1482 return (0);
1485 * read device specials from vfstab and compare names at physical
1486 * node path level.
1488 while (getvfsent(fp, &vfsent) == 0) {
1489 if (strncmp(vfsent.vfs_special, SLASH_DEV_SLASH,
1490 sizeof (SLASH_DEV_SLASH) - 1) == 0) {
1491 (void) snprintf(buf, MAXPATHLEN, "%s%s",
1492 rootdir, vfsent.vfs_special);
1493 if ((linksize = readlink(buf, physpath,
1494 MAXPATHLEN)) > 0 && linksize < (MAXPATHLEN - 1)) {
1495 physpath[linksize] = '\0';
1496 if ((abspath = strstr(physpath,
1497 SLASH_DEVICES_SLASH)) == NULL)
1498 continue;
1499 } else
1500 continue;
1501 } else if (strncmp(vfsent.vfs_special, SLASH_DEVICES_SLASH,
1502 sizeof (SLASH_DEVICES_SLASH) - 1) == 0) {
1503 (void) strlcpy(physpath, vfsent.vfs_special,
1504 MAXPATHLEN);
1505 abspath = physpath;
1506 } else
1507 continue;
1509 /* point to / after /devices */
1510 abspath += sizeof (SLASH_DEVICES_SLASH) - 2;
1511 /* strip minor component */
1512 if ((minor = strrchr(abspath, ':')) != NULL)
1513 *minor = '\0';
1515 if (strcmp(nodepath, abspath) == 0) {
1516 (void) fclose(fp);
1517 logdmsg(("is_dev_in_vfstab: returning 1\n"));
1518 return (1);
1522 (void) fclose(fp);
1523 return (0);
1526 #endif /* __sparc */
1528 static int
1529 devlink_callback(di_devlink_t devlink, void *argp)
1531 const char *link;
1533 if ((link = di_devlink_path(devlink)) != NULL)
1534 (void) strlcpy((char *)argp, link, MAXPATHLEN);
1536 return (DI_WALK_CONTINUE);
1540 * Get the /dev name in the install environment corresponding to physpath.
1542 * physpath /devices path in the install environment without the /devices
1543 * prefix.
1544 * buf caller supplied buffer where the /dev name is placed on return
1545 * bufsz length of the buffer
1547 * Returns strlen of the /dev name on success, -1 on failure.
1549 static int
1550 get_install_devlink(char *physpath, char *buf, size_t bufsz)
1552 di_devlink_handle_t devlink_hdl;
1553 char devname[MAXPATHLEN];
1554 int tries = 0;
1555 int sleeptime = 2; /* number of seconds to sleep between retries */
1556 int maxtries = 10; /* maximum number of tries */
1558 logdmsg(("get_install_devlink: physpath = %s\n", physpath));
1561 * devlink_db sync happens after MINOR_FINI_TIMEOUT_DEFAULT secs
1562 * after dev link creation. So wait for minimum that amout of time.
1565 retry:
1566 (void) sleep(sleeptime);
1568 if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
1569 logdmsg(("get_install_devlink: di_devlink_init() failed: %s\n",
1570 strerror(errno)));
1571 return (-1);
1574 devname[0] = '\0';
1575 if (di_devlink_walk(devlink_hdl, NULL, physpath, DI_PRIMARY_LINK,
1576 devname, devlink_callback) == 0) {
1577 if (devname[0] == '\0' && tries < maxtries) {
1578 tries++;
1579 (void) di_devlink_fini(&devlink_hdl);
1580 goto retry;
1581 } else if (devname[0] == '\0') {
1582 logdmsg(("get_install_devlink: di_devlink_walk"
1583 " failed: %s\n", strerror(errno)));
1584 (void) di_devlink_fini(&devlink_hdl);
1585 return (-1);
1587 } else {
1588 logdmsg(("get_install_devlink: di_devlink_walk failed: %s\n",
1589 strerror(errno)));
1590 (void) di_devlink_fini(&devlink_hdl);
1591 return (-1);
1594 (void) di_devlink_fini(&devlink_hdl);
1596 logdmsg(("get_install_devlink: devlink = %s\n", devname));
1597 return (strlcpy(buf, devname, bufsz));
1601 * Get the /dev name in the target environment corresponding to physpath.
1603 * rootdir root directory of the target environment
1604 * physpath /devices path in the target environment without the /devices
1605 * prefix.
1606 * buf caller supplied buffer where the /dev name is placed on return
1607 * bufsz length of the buffer
1609 * Returns strlen of the /dev name on success, -1 on failure.
1611 static int
1612 get_target_devlink(char *rootdir, char *physpath, char *buf, size_t bufsz)
1614 char *p;
1615 int linksize;
1616 DIR *dirp;
1617 struct dirent *direntry;
1618 char dirpath[MAXPATHLEN];
1619 char devname[MAXPATHLEN];
1620 char physdev[MAXPATHLEN];
1622 logdmsg(("get_target_devlink: rootdir = %s, physpath = %s\n",
1623 rootdir, physpath));
1625 if ((p = strrchr(physpath, '/')) == NULL)
1626 return (-1);
1628 if (strstr(p, ",raw") != NULL) {
1629 (void) snprintf(dirpath, MAXPATHLEN, "%s/dev/rdsk", rootdir);
1630 } else {
1631 (void) snprintf(dirpath, MAXPATHLEN, "%s/dev/dsk", rootdir);
1634 if ((dirp = opendir(dirpath)) == NULL)
1635 return (-1);
1637 while ((direntry = readdir(dirp)) != NULL) {
1638 if (strcmp(direntry->d_name, ".") == 0 ||
1639 strcmp(direntry->d_name, "..") == 0)
1640 continue;
1642 (void) snprintf(devname, MAXPATHLEN, "%s/%s",
1643 dirpath, direntry->d_name);
1645 if ((linksize = readlink(devname, physdev, MAXPATHLEN)) > 0 &&
1646 linksize < (MAXPATHLEN - 1)) {
1647 physdev[linksize] = '\0';
1648 if ((p = strstr(physdev, SLASH_DEVICES_SLASH)) !=
1649 NULL && strcmp(p + sizeof (SLASH_DEVICES) - 1,
1650 physpath) == 0) {
1651 (void) closedir(dirp);
1652 logdmsg(("get_target_devlink: devlink = %s\n",
1653 devname + strlen(rootdir)));
1654 return (strlcpy(buf, devname + strlen(rootdir),
1655 bufsz));
1660 (void) closedir(dirp);
1661 return (-1);
1665 * Convert device name to physpath.
1667 * rootdir root directory
1668 * devname a /dev name or /devices name under rootdir
1669 * physpath caller supplied buffer where the /devices path will be placed
1670 * on return (without the /devices prefix).
1671 * physpathlen length of the physpath buffer
1673 * Returns 0 on success, -1 on failure.
1675 static int
1676 devname2physpath(char *rootdir, char *devname, char *physpath, int physpathlen)
1678 int linksize;
1679 char *p;
1680 char devlink[MAXPATHLEN];
1681 char tmpphyspath[MAXPATHLEN];
1683 logdmsg(("devname2physpath: rootdir = %s, devname = %s\n",
1684 rootdir, devname));
1686 if (strncmp(devname, SLASH_DEVICES_SLASH,
1687 sizeof (SLASH_DEVICES_SLASH) - 1) != 0) {
1688 if (*rootdir == '\0')
1689 linksize = readlink(devname, tmpphyspath, MAXPATHLEN);
1690 else {
1691 (void) snprintf(devlink, MAXPATHLEN, "%s%s",
1692 rootdir, devname);
1693 linksize = readlink(devlink, tmpphyspath, MAXPATHLEN);
1695 if (linksize > 0 && linksize < (MAXPATHLEN - 1)) {
1696 tmpphyspath[linksize] = '\0';
1697 if ((p = strstr(tmpphyspath, SLASH_DEVICES_SLASH))
1698 == NULL)
1699 return (-1);
1700 } else
1701 return (-1);
1702 } else
1703 p = devname;
1705 (void) strlcpy(physpath, p + sizeof (SLASH_DEVICES) - 1, physpathlen);
1706 logdmsg(("devname2physpath: physpath = %s\n", physpath));
1707 return (0);
1711 * Map a device name (devname) from the target environment to the
1712 * install environment.
1714 * rootdir root directory of the target environment
1715 * devname /dev or /devices name under the target environment
1716 * buf caller supplied buffer where the mapped /dev name is placed
1717 * on return
1718 * bufsz length of the buffer
1720 * Returns strlen of the mapped /dev name on success, -1 on failure.
1723 devfs_target2install(const char *rootdir, const char *devname, char *buf,
1724 size_t bufsz)
1726 char physpath[MAXPATHLEN];
1728 logdmsg(("devfs_target2install: rootdir = %s, devname = %s\n",
1729 STRVAL(rootdir), STRVAL(devname)));
1731 if (rootdir == NULL || devname == NULL || buf == NULL || bufsz == 0)
1732 return (-1);
1734 if (strcmp(rootdir, "/") == 0)
1735 rootdir = "";
1737 if (devname2physpath((char *)rootdir, (char *)devname, physpath,
1738 MAXPATHLEN) != 0)
1739 return (-1);
1741 #ifdef __sparc
1742 if (client_name_type(physpath) == CLIENT_TYPE_PHCI) {
1743 char *mapped_node_path, *minor;
1744 char minorbuf[MAXNAMELEN];
1746 /* strip minor component if present */
1747 if ((minor = strrchr(physpath, ':')) != NULL) {
1748 *minor = '\0';
1749 minor++;
1750 (void) strlcpy(minorbuf, minor, MAXNAMELEN);
1752 if ((mapped_node_path = phci_to_vhci(physpath)) != NULL) {
1753 if (minor)
1754 (void) snprintf(physpath, MAXPATHLEN,
1755 "%s:%s", mapped_node_path, minorbuf);
1756 else
1757 (void) strlcpy(physpath, mapped_node_path,
1758 MAXPATHLEN);
1759 free(mapped_node_path);
1760 logdmsg(("devfs_target2install: mapped physpath: %s\n",
1761 physpath));
1763 } else if (minor)
1764 *(minor - 1) = ':';
1766 #endif /* __sparc */
1768 return (get_install_devlink(physpath, buf, bufsz));
1772 * Map a device name (devname) from the install environment to the target
1773 * environment.
1775 * rootdir root directory of the target environment
1776 * devname /dev or /devices name under the install environment
1777 * buf caller supplied buffer where the mapped /dev name is placed
1778 * on return
1779 * bufsz length of the buffer
1781 * Returns strlen of the mapped /dev name on success, -1 on failure.
1784 devfs_install2target(const char *rootdir, const char *devname, char *buf,
1785 size_t bufsz)
1787 char physpath[MAXPATHLEN];
1789 logdmsg(("devfs_install2target: rootdir = %s, devname = %s\n",
1790 STRVAL(rootdir), STRVAL(devname)));
1792 if (rootdir == NULL || devname == NULL || buf == NULL || bufsz == 0)
1793 return (-1);
1795 if (strcmp(rootdir, "/") == 0)
1796 rootdir = "";
1798 if (devname2physpath("", (char *)devname, physpath, MAXPATHLEN) != 0)
1799 return (-1);
1801 #ifdef __sparc
1802 if (client_name_type(physpath) == CLIENT_TYPE_VHCI) {
1803 char **pathlist;
1804 int npaths, i, j;
1805 char *minor;
1806 char minorbuf[MAXNAMELEN];
1808 /* strip minor component if present */
1809 if ((minor = strrchr(physpath, ':')) != NULL) {
1810 *minor = '\0';
1811 minor++;
1812 (void) strlcpy(minorbuf, minor, MAXNAMELEN);
1815 if ((pathlist = vhci_to_phci(physpath, &npaths)) == NULL)
1816 return (-1);
1818 if ((npaths = build_pathlist((char *)rootdir, physpath,
1819 pathlist, npaths)) <= 0) {
1820 free_pathlist(pathlist);
1821 return (-1);
1825 * in case of more than one path, try to use the path
1826 * referenced in the vfstab file, otherwise use the first path.
1828 j = 0;
1829 if (npaths > 1) {
1830 for (i = 0; i < npaths; i++) {
1831 if (is_dev_in_vfstab((char *)rootdir,
1832 pathlist[i])) {
1833 j = i;
1834 break;
1839 if (minor)
1840 (void) snprintf(physpath, MAXPATHLEN,
1841 "%s:%s", pathlist[j], minorbuf);
1842 else
1843 (void) strlcpy(physpath, pathlist[j], MAXPATHLEN);
1844 free_pathlist(pathlist);
1846 #endif /* __sparc */
1848 return (get_target_devlink((char *)rootdir, physpath, buf, bufsz));
1852 * A parser for /etc/path_to_inst.
1853 * The user-supplied callback is called once for each entry in the file.
1854 * Returns 0 on success, ENOMEM/ENOENT/EINVAL on error.
1855 * Callback may return DI_WALK_TERMINATE to terminate the walk,
1856 * otherwise DI_WALK_CONTINUE.
1859 devfs_parse_binding_file(const char *binding_file,
1860 int (*callback)(void *, const char *, int,
1861 const char *), void *cb_arg)
1863 token_t token;
1864 struct conf_file file;
1865 char tokval[MAX_TOKEN_SIZE];
1866 enum { STATE_RESET, STATE_DEVPATH, STATE_INSTVAL } state;
1867 char *devpath;
1868 char *bindname;
1869 int instval = 0;
1870 int rv;
1872 if ((devpath = calloc(1, MAXPATHLEN)) == NULL)
1873 return (ENOMEM);
1874 if ((bindname = calloc(1, MAX_TOKEN_SIZE)) == NULL) {
1875 free(devpath);
1876 return (ENOMEM);
1879 if ((file.fp = fopen(binding_file, "r")) == NULL) {
1880 free(devpath);
1881 free(bindname);
1882 return (errno);
1885 file.filename = (char *)binding_file;
1886 file.linenum = 1;
1888 state = STATE_RESET;
1889 while ((token = lex(&file, tokval, MAX_TOKEN_SIZE)) != T_EOF) {
1890 switch (token) {
1891 case T_POUND:
1893 * Skip comments.
1895 find_eol(file.fp);
1896 break;
1897 case T_NAME:
1898 case T_STRING:
1899 switch (state) {
1900 case STATE_RESET:
1901 if (strlcpy(devpath, tokval,
1902 MAXPATHLEN) >= MAXPATHLEN)
1903 goto err;
1904 state = STATE_DEVPATH;
1905 break;
1906 case STATE_INSTVAL:
1907 if (strlcpy(bindname, tokval,
1908 MAX_TOKEN_SIZE) >= MAX_TOKEN_SIZE)
1909 goto err;
1910 rv = callback(cb_arg,
1911 devpath, instval, bindname);
1912 if (rv == DI_WALK_TERMINATE)
1913 goto done;
1914 if (rv != DI_WALK_CONTINUE)
1915 goto err;
1916 state = STATE_RESET;
1917 break;
1918 default:
1919 file_err(&file, tok_err, tokval);
1920 state = STATE_RESET;
1921 break;
1923 break;
1924 case T_DECVAL:
1925 case T_HEXVAL:
1926 switch (state) {
1927 case STATE_DEVPATH:
1928 instval = (int)strtol(tokval, NULL, 0);
1929 state = STATE_INSTVAL;
1930 break;
1931 default:
1932 file_err(&file, tok_err, tokval);
1933 state = STATE_RESET;
1934 break;
1936 break;
1937 case T_NEWLINE:
1938 file.linenum++;
1939 state = STATE_RESET;
1940 break;
1941 default:
1942 file_err(&file, tok_err, tokval);
1943 state = STATE_RESET;
1944 break;
1948 done:
1949 (void) fclose(file.fp);
1950 free(devpath);
1951 free(bindname);
1952 return (0);
1954 err:
1955 (void) fclose(file.fp);
1956 free(devpath);
1957 free(bindname);
1958 return (EINVAL);
1962 * Walk the minor nodes of all children below the specified device
1963 * by calling the provided callback with the path to each minor.
1965 static int
1966 devfs_walk_children_minors(const char *device_path, struct stat *st,
1967 int (*callback)(void *, const char *), void *cb_arg, int *terminate)
1969 DIR *dir;
1970 struct dirent *dp;
1971 char *minor_path = NULL;
1972 int need_close = 0;
1973 int rv;
1975 if ((minor_path = calloc(1, MAXPATHLEN)) == NULL)
1976 return (ENOMEM);
1978 if ((dir = opendir(device_path)) == NULL) {
1979 rv = ENOENT;
1980 goto err;
1982 need_close = 1;
1984 while ((dp = readdir(dir)) != NULL) {
1985 if ((strcmp(dp->d_name, ".") == 0) ||
1986 (strcmp(dp->d_name, "..") == 0))
1987 continue;
1988 (void) snprintf(minor_path, MAXPATHLEN,
1989 "%s/%s", device_path, dp->d_name);
1990 if (stat(minor_path, st) == -1)
1991 continue;
1992 if (S_ISDIR(st->st_mode)) {
1993 rv = devfs_walk_children_minors(
1994 (const char *)minor_path, st,
1995 callback, cb_arg, terminate);
1996 if (rv != 0)
1997 goto err;
1998 if (*terminate)
1999 break;
2000 } else {
2001 rv = callback(cb_arg, minor_path);
2002 if (rv == DI_WALK_TERMINATE) {
2003 *terminate = 1;
2004 break;
2006 if (rv != DI_WALK_CONTINUE) {
2007 rv = EINVAL;
2008 goto err;
2013 rv = 0;
2014 err:
2015 if (need_close)
2016 (void) closedir(dir);
2017 free(minor_path);
2018 return (rv);
2022 * Return the path to each minor node for a device by
2023 * calling the provided callback.
2025 static int
2026 devfs_walk_device_minors(const char *device_path, struct stat *st,
2027 int (*callback)(void *, const char *), void *cb_arg, int *terminate)
2029 char *minor_path;
2030 char *devpath;
2031 char *expr;
2032 regex_t regex;
2033 int need_regfree = 0;
2034 int need_close = 0;
2035 DIR *dir;
2036 struct dirent *dp;
2037 int rv;
2038 char *p;
2040 minor_path = calloc(1, MAXPATHLEN);
2041 devpath = calloc(1, MAXPATHLEN);
2042 expr = calloc(1, MAXNAMELEN);
2043 if (devpath == NULL || expr == NULL || minor_path == NULL) {
2044 rv = ENOMEM;
2045 goto err;
2048 rv = EINVAL;
2049 if (strlcpy(devpath, device_path, MAXPATHLEN) >= MAXPATHLEN)
2050 goto err;
2051 if ((p = strrchr(devpath, '/')) == NULL)
2052 goto err;
2053 *p++ = 0;
2054 if (strlen(p) == 0)
2055 goto err;
2056 if (snprintf(expr, MAXNAMELEN, "%s:.*", p) >= MAXNAMELEN)
2057 goto err;
2058 if (regcomp(&regex, expr, REG_EXTENDED) != 0)
2059 goto err;
2060 need_regfree = 1;
2062 if ((dir = opendir(devpath)) == NULL) {
2063 rv = ENOENT;
2064 goto err;
2066 need_close = 1;
2068 while ((dp = readdir(dir)) != NULL) {
2069 if ((strcmp(dp->d_name, ".") == 0) ||
2070 (strcmp(dp->d_name, "..") == 0))
2071 continue;
2072 (void) snprintf(minor_path, MAXPATHLEN,
2073 "%s/%s", devpath, dp->d_name);
2074 if (stat(minor_path, st) == -1)
2075 continue;
2076 if ((S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) &&
2077 regexec(&regex, dp->d_name, 0, NULL, 0) == 0) {
2078 rv = callback(cb_arg, minor_path);
2079 if (rv == DI_WALK_TERMINATE) {
2080 *terminate = 1;
2081 break;
2083 if (rv != DI_WALK_CONTINUE) {
2084 rv = EINVAL;
2085 goto err;
2090 rv = 0;
2091 err:
2092 if (need_close)
2093 (void) closedir(dir);
2094 if (need_regfree)
2095 regfree(&regex);
2096 free(devpath);
2097 free(minor_path);
2098 free(expr);
2099 return (rv);
2103 * Perform a walk of all minor nodes for the specified device,
2104 * and minor nodes below the device.
2107 devfs_walk_minor_nodes(const char *device_path,
2108 int (*callback)(void *, const char *), void *cb_arg)
2110 struct stat stbuf;
2111 int rv;
2112 int terminate = 0;
2114 rv = devfs_walk_device_minors(device_path,
2115 &stbuf, callback, cb_arg, &terminate);
2116 if (rv == 0 && terminate == 0) {
2117 rv = devfs_walk_children_minors(device_path,
2118 &stbuf, callback, cb_arg, &terminate);
2120 return (rv);
2123 #ifdef DEBUG
2125 static void
2126 vlog_debug_msg(char *fmt, va_list ap)
2128 time_t clock;
2129 struct tm t;
2131 if (!devfsmap_debug)
2132 return;
2134 if (logfp == NULL) {
2135 if (*devfsmap_logfile != '\0') {
2136 logfp = fopen(devfsmap_logfile, "a");
2137 if (logfp)
2138 (void) fprintf(logfp, "\nNew Log:\n");
2141 if (logfp == NULL)
2142 logfp = stdout;
2145 clock = time(NULL);
2146 (void) localtime_r(&clock, &t);
2147 (void) fprintf(logfp, "%02d:%02d:%02d ", t.tm_hour, t.tm_min,
2148 t.tm_sec);
2149 (void) vfprintf(logfp, fmt, ap);
2150 (void) fflush(logfp);
2153 static void
2154 log_debug_msg(char *fmt, ...)
2156 va_list ap;
2158 va_start(ap, fmt);
2159 vlog_debug_msg(fmt, ap);
2160 va_end(ap);
2163 #ifdef __sparc
2165 static char *
2166 mpxio_disable_string(int mpxio_disable)
2168 if (mpxio_disable == 0)
2169 return ("no");
2170 else if (mpxio_disable == 1)
2171 return ("yes");
2172 else
2173 return ("not specified");
2176 static void
2177 log_confent_list(char *filename, struct conf_entry *confent_list,
2178 int global_mpxio_disable)
2180 struct conf_entry *confent;
2182 log_debug_msg("log_confent_list: filename = %s:\n", filename);
2183 if (global_mpxio_disable != -1)
2184 log_debug_msg("\tdriver global mpxio_disable = \"%s\"\n\n",
2185 mpxio_disable_string(global_mpxio_disable));
2187 for (confent = confent_list; confent != NULL; confent = confent->next) {
2188 if (confent->name)
2189 log_debug_msg("\tname = %s\n", confent->name);
2190 if (confent->parent)
2191 log_debug_msg("\tparent = %s\n", confent->parent);
2192 if (confent->class)
2193 log_debug_msg("\tclass = %s\n", confent->class);
2194 if (confent->unit_address)
2195 log_debug_msg("\tunit_address = %s\n",
2196 confent->unit_address);
2197 if (confent->port != -1)
2198 log_debug_msg("\tport = %d\n", confent->port);
2199 log_debug_msg("\tmpxio_disable = \"%s\"\n\n",
2200 mpxio_disable_string(confent->mpxio_disable));
2204 static void
2205 log_pathlist(char **pathlist)
2207 char **p;
2209 for (p = pathlist; *p != NULL; p++)
2210 log_debug_msg("\t%s\n", *p);
2213 #endif /* __sparc */
2215 #endif /* DEBUG */