1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #ifdef lint
26 #define _REENTRANT /* for localtime_r */
27 #endif
28
29 #include <stdio.h>
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <strings.h>
34 #include <stdarg.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <stropts.h>
40 #include <time.h>
41 #include <sys/param.h>
42 #include <sys/vfstab.h>
43 #include <dirent.h>
44 #ifdef __sparc
45 #include <sys/scsi/adapters/scsi_vhci.h>
46 #include <sys/sunmdi.h>
47 #endif /* __sparc */
48 #include "libdevinfo.h"
49 #include "device_info.h"
50 #include <regex.h>
51
52 #define isnewline(ch) ((ch) == '\n' || (ch) == '\r' || (ch) == '\f')
53 #define isnamechar(ch) (isalpha(ch) || isdigit(ch) || (ch) == '_' ||\
54 (ch) == '-')
55 #define MAX_TOKEN_SIZE 1024
56 #define BUFSIZE 1024
57 #define STRVAL(s) ((s) ? (s) : "NULL")
58
59 #define SCSI_VHCI_CONF "/kernel/drv/scsi_vhci.conf"
60 #define QLC_CONF "/kernel/drv/qlc.conf"
61 #define FP_CONF "/kernel/drv/fp.conf"
62 #define DRIVER_CLASSES "/etc/driver_classes"
63 #define FP_AT "fp@"
64 #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl"
65 #define SLASH_DEVICES "/devices"
66 #define SLASH_DEVICES_SLASH "/devices/"
67 #define SLASH_FP_AT "/fp@"
68 #define SLASH_SCSI_VHCI "/scsi_vhci"
69 #define META_DEV "/dev/md/dsk/"
70 #define SLASH_DEV_SLASH "/dev/"
71
72 /*
73 * Macros to produce a quoted string containing the value of a
74 * preprocessor macro. For example, if SIZE is defined to be 256,
75 * VAL2STR(SIZE) is "256". This is used to construct format
76 * strings for scanf-family functions below.
77 */
78 #define QUOTE(x) #x
79 #define VAL2STR(x) QUOTE(x)
80
81 typedef enum {
82 CLIENT_TYPE_UNKNOWN,
83 CLIENT_TYPE_PHCI,
84 CLIENT_TYPE_VHCI
85 } client_type_t;
86
87 typedef enum {
88 T_EQUALS,
89 T_AMPERSAND,
90 T_BIT_OR,
91 T_STAR,
92 T_POUND,
93 T_COLON,
94 T_SEMICOLON,
95 T_COMMA,
96 T_SLASH,
97 T_WHITE_SPACE,
98 T_NEWLINE,
99 T_EOF,
100 T_STRING,
101 T_HEXVAL,
102 T_DECVAL,
103 T_NAME
104 } token_t;
105
106 typedef enum {
107 begin, parent, drvname, drvclass, prop,
108 parent_equals, name_equals, drvclass_equals,
109 parent_equals_string, name_equals_string,
110 drvclass_equals_string,
111 prop_equals, prop_equals_string, prop_equals_integer,
112 prop_equals_string_comma, prop_equals_integer_comma
113 } conf_state_t;
114
115 /* structure to hold entries with mpxio-disable property in driver.conf file */
116 struct conf_entry {
117 char *name;
118 char *parent;
119 char *class;
120 char *unit_address;
121 int port;
122 int mpxio_disable;
123 struct conf_entry *next;
124 };
125
126 struct conf_file {
127 char *filename;
128 FILE *fp;
129 int linenum;
130 };
131
132 static char *tok_err = "Unexpected token '%s'\n";
133
134
135 /* #define DEBUG */
136
137 #ifdef DEBUG
138
139 int devfsmap_debug = 0;
140 /* /var/run is not mounted at install time. Therefore use /tmp */
141 char *devfsmap_logfile = "/tmp/devfsmap.log";
142 static FILE *logfp;
143 #define logdmsg(args) log_debug_msg args
144 static void vlog_debug_msg(char *, va_list);
145 static void log_debug_msg(char *, ...);
146 #ifdef __sparc
147 static void log_confent_list(char *, struct conf_entry *, int);
148 static void log_pathlist(char **);
149 #endif /* __sparc */
150
151 #else /* DEBUG */
152 #define logdmsg(args) /* nothing */
153 #endif /* DEBUG */
154
155
156 /*
157 * Leave NEWLINE as the next character.
158 */
159 static void
find_eol(FILE * fp)160 find_eol(FILE *fp)
161 {
162 int ch;
163
164 while ((ch = getc(fp)) != EOF) {
165 if (isnewline(ch)) {
166 (void) ungetc(ch, fp);
167 break;
168 }
169 }
170 }
171
172 /* ignore parsing errors */
173 /*ARGSUSED*/
174 static void
file_err(struct conf_file * filep,char * fmt,...)175 file_err(struct conf_file *filep, char *fmt, ...)
176 {
177 #ifdef DEBUG
178 va_list ap;
179
180 va_start(ap, fmt);
181 log_debug_msg("WARNING: %s line # %d: ",
182 filep->filename, filep->linenum);
183 vlog_debug_msg(fmt, ap);
184 va_end(ap);
185 #endif /* DEBUG */
186 }
187
188 /* return the next token from the given driver.conf file, or -1 on error */
189 static token_t
lex(struct conf_file * filep,char * val,size_t size)190 lex(struct conf_file *filep, char *val, size_t size)
191 {
192 char *cp;
193 int ch, oval, badquote;
194 size_t remain;
195 token_t token;
196 FILE *fp = filep->fp;
197
198 if (size < 2)
199 return (-1);
200
201 cp = val;
202 while ((ch = getc(fp)) == ' ' || ch == '\t')
203 ;
204
205 remain = size - 1;
206 *cp++ = (char)ch;
207 switch (ch) {
208 case '=':
209 token = T_EQUALS;
210 break;
211 case '&':
212 token = T_AMPERSAND;
213 break;
214 case '|':
215 token = T_BIT_OR;
216 break;
217 case '*':
218 token = T_STAR;
219 break;
220 case '#':
221 token = T_POUND;
222 break;
223 case ':':
224 token = T_COLON;
225 break;
226 case ';':
227 token = T_SEMICOLON;
228 break;
229 case ',':
230 token = T_COMMA;
231 break;
232 case '/':
233 token = T_SLASH;
234 break;
235 case ' ':
236 case '\t':
237 case '\f':
238 while ((ch = getc(fp)) == ' ' ||
239 ch == '\t' || ch == '\f') {
240 if (--remain == 0) {
241 *cp = '\0';
242 return (-1);
243 }
244 *cp++ = (char)ch;
245 }
246 (void) ungetc(ch, fp);
247 token = T_WHITE_SPACE;
248 break;
249 case '\n':
250 case '\r':
251 token = T_NEWLINE;
252 break;
253 case '"':
254 remain++;
255 cp--;
256 badquote = 0;
257 while (!badquote && (ch = getc(fp)) != '"') {
258 switch (ch) {
259 case '\n':
260 case EOF:
261 file_err(filep, "Missing \"\n");
262 remain = size - 1;
263 cp = val;
264 *cp++ = '\n';
265 badquote = 1;
266 /* since we consumed the newline/EOF */
267 (void) ungetc(ch, fp);
268 break;
269
270 case '\\':
271 if (--remain == 0) {
272 *cp = '\0';
273 return (-1);
274 }
275 ch = (char)getc(fp);
276 if (!isdigit(ch)) {
277 /* escape the character */
278 *cp++ = (char)ch;
279 break;
280 }
281 oval = 0;
282 while (ch >= '0' && ch <= '7') {
283 ch -= '0';
284 oval = (oval << 3) + ch;
285 ch = (char)getc(fp);
286 }
287 (void) ungetc(ch, fp);
288 /* check for character overflow? */
289 if (oval > 127) {
290 file_err(filep,
291 "Character "
292 "overflow detected.\n");
293 }
294 *cp++ = (char)oval;
295 break;
296 default:
297 if (--remain == 0) {
298 *cp = '\0';
299 return (-1);
300 }
301 *cp++ = (char)ch;
302 break;
303 }
304 }
305 token = T_STRING;
306 break;
307
308 case EOF:
309 token = T_EOF;
310 break;
311
312 default:
313 /*
314 * detect a lone '-' (including at the end of a line), and
315 * identify it as a 'name'
316 */
317 if (ch == '-') {
318 if (--remain == 0) {
319 *cp = '\0';
320 return (-1);
321 }
322 *cp++ = (char)(ch = getc(fp));
323 if (ch == ' ' || ch == '\t' || ch == '\n') {
324 (void) ungetc(ch, fp);
325 remain++;
326 cp--;
327 token = T_NAME;
328 break;
329 }
330 } else if (ch == '~' || ch == '-') {
331 if (--remain == 0) {
332 *cp = '\0';
333 return (-1);
334 }
335 *cp++ = (char)(ch = getc(fp));
336 }
337
338
339 if (isdigit(ch)) {
340 if (ch == '0') {
341 if ((ch = getc(fp)) == 'x') {
342 if (--remain == 0) {
343 *cp = '\0';
344 return (-1);
345 }
346 *cp++ = (char)ch;
347 ch = getc(fp);
348 while (isxdigit(ch)) {
349 if (--remain == 0) {
350 *cp = '\0';
351 return (-1);
352 }
353 *cp++ = (char)ch;
354 ch = getc(fp);
355 }
356 (void) ungetc(ch, fp);
357 token = T_HEXVAL;
358 } else {
359 goto digit;
360 }
361 } else {
362 ch = getc(fp);
363 digit:
364 while (isdigit(ch)) {
365 if (--remain == 0) {
366 *cp = '\0';
367 return (-1);
368 }
369 *cp++ = (char)ch;
370 ch = getc(fp);
371 }
372 (void) ungetc(ch, fp);
373 token = T_DECVAL;
374 }
375 } else if (isalpha(ch) || ch == '\\') {
376 if (ch != '\\') {
377 ch = getc(fp);
378 } else {
379 /*
380 * if the character was a backslash,
381 * back up so we can overwrite it with
382 * the next (i.e. escaped) character.
383 */
384 remain++;
385 cp--;
386 }
387 while (isnamechar(ch) || ch == '\\') {
388 if (ch == '\\')
389 ch = getc(fp);
390 if (--remain == 0) {
391 *cp = '\0';
392 return (-1);
393 }
394 *cp++ = (char)ch;
395 ch = getc(fp);
396 }
397 (void) ungetc(ch, fp);
398 token = T_NAME;
399 } else {
400 return (-1);
401 }
402 break;
403 }
404
405 *cp = '\0';
406
407 return (token);
408 }
409
410 #ifdef __sparc
411
412 static void
free_confent(struct conf_entry * confent)413 free_confent(struct conf_entry *confent)
414 {
415 if (confent->name)
416 free(confent->name);
417 if (confent->parent)
418 free(confent->parent);
419 if (confent->class)
420 free(confent->class);
421 if (confent->unit_address)
422 free(confent->unit_address);
423 free(confent);
424 }
425
426 static void
free_confent_list(struct conf_entry * confent_list)427 free_confent_list(struct conf_entry *confent_list)
428 {
429 struct conf_entry *confent, *next;
430
431 for (confent = confent_list; confent != NULL; confent = next) {
432 next = confent->next;
433 free_confent(confent);
434 }
435 }
436
437 /*
438 * Parse the next entry from the driver.conf file and return in the form of
439 * a pointer to the conf_entry.
440 */
441 static struct conf_entry *
parse_conf_entry(struct conf_file * filep,char * tokbuf,size_t linesize)442 parse_conf_entry(struct conf_file *filep, char *tokbuf, size_t linesize)
443 {
444 char *prop_name, *string;
445 token_t token;
446 struct conf_entry *confent;
447 conf_state_t state;
448 int failed = 1;
449
450 if ((confent = calloc(1, sizeof (*confent))) == NULL)
451 return (NULL);
452
453 confent->port = -1;
454 confent->mpxio_disable = -1;
455
456 state = begin;
457 token = T_NAME;
458 prop_name = NULL;
459 string = NULL;
460 do {
461 switch (token) {
462 case T_NAME:
463 switch (state) {
464 case prop_equals_string:
465 case prop_equals_integer:
466 case begin:
467 state = prop;
468 if ((prop_name = strdup(tokbuf)) == NULL)
469 goto bad;
470 break;
471 default:
472 file_err(filep, tok_err, tokbuf);
473 }
474 break;
475 case T_EQUALS:
476 switch (state) {
477 case prop:
478 state = prop_equals;
479 break;
480 default:
481 file_err(filep, tok_err, tokbuf);
482 }
483 break;
484 case T_STRING:
485 switch (state) {
486 case prop_equals:
487 if ((string = strdup(tokbuf)) == NULL)
488 goto bad;
489
490 state = begin;
491 if (strcmp(prop_name, "PARENT") == 0 ||
492 strcmp(prop_name, "parent") == 0) {
493 if (confent->parent) {
494 file_err(filep,
495 "'parent' property already specified\n");
496 goto bad;
497 }
498 confent->parent = string;
499 } else if (strcmp(prop_name, "NAME") == 0 ||
500 strcmp(prop_name, "name") == 0) {
501 if (confent->name) {
502 file_err(filep,
503 "'name' property already specified\n");
504 goto bad;
505 }
506 confent->name = string;
507 } else if (strcmp(prop_name, "CLASS") == 0 ||
508 strcmp(prop_name, "class") == 0) {
509 if (confent->class) {
510 file_err(filep,
511 "'class' property already specified\n");
512 goto bad;
513 }
514 confent->class = string;
515 } else if (strcmp(prop_name, "unit-address")
516 == 0) {
517 if (confent->unit_address) {
518 file_err(filep,
519 "'unit-address' property already specified\n");
520 goto bad;
521 }
522 confent->unit_address = string;
523 } else if (strcmp(prop_name, "mpxio-disable")
524 == 0) {
525 if (confent->mpxio_disable != -1) {
526 file_err(filep,
527 "'mpxio-disable' property already specified\n");
528 goto bad;
529 }
530 if (strcmp(string, "yes") == 0)
531 confent->mpxio_disable = 1;
532 else if (strcmp(string, "no") == 0)
533 confent->mpxio_disable = 0;
534 else {
535 file_err(filep,
536 "'mpxio-disable' property setting is invalid. "
537 "The value must be either \"yes\" or \"no\"\n");
538 goto bad;
539 }
540 free(string);
541 } else {
542 free(string);
543 state = prop_equals_string;
544 }
545 string = NULL;
546 free(prop_name);
547 prop_name = NULL;
548 break;
549
550 case prop_equals_string_comma:
551 state = prop_equals_string;
552 break;
553 default:
554 file_err(filep, tok_err, tokbuf);
555 }
556 break;
557 case T_HEXVAL:
558 case T_DECVAL:
559 switch (state) {
560 case prop_equals:
561 if (strcmp(prop_name, "port") == 0) {
562 if (confent->port != -1) {
563 file_err(filep,
564 "'port' property already specified\n");
565 goto bad;
566 }
567 confent->port =
568 (int)strtol(tokbuf, NULL, 0);
569 state = begin;
570 } else
571 state = prop_equals_integer;
572 free(prop_name);
573 prop_name = NULL;
574 break;
575
576 case prop_equals_integer_comma:
577 state = prop_equals_integer;
578 break;
579 default:
580 file_err(filep, tok_err, tokbuf);
581 }
582 break;
583 case T_COMMA:
584 switch (state) {
585 case prop_equals_string:
586 state = prop_equals_string_comma;
587 break;
588 case prop_equals_integer:
589 state = prop_equals_integer_comma;
590 break;
591 default:
592 file_err(filep, tok_err, tokbuf);
593 }
594 break;
595 case T_NEWLINE:
596 filep->linenum++;
597 break;
598 case T_POUND:
599 find_eol(filep->fp);
600 break;
601 case T_EOF:
602 file_err(filep, "Unexpected EOF\n");
603 goto bad;
604 default:
605 file_err(filep, tok_err, tokbuf);
606 goto bad;
607 }
608 } while ((token = lex(filep, tokbuf, linesize)) != T_SEMICOLON);
609
610 failed = 0;
611
612 bad:
613 if (prop_name)
614 free(prop_name);
615 if (string)
616 free(string);
617 if (failed == 1) {
618 free_confent(confent);
619 return (NULL);
620 }
621 return (confent);
622 }
623
624 /*
625 * Parse all entries with mpxio-disable property in the given driver.conf
626 * file.
627 *
628 * fname driver.conf file name
629 * confent_list on return *confent_list will contain the list of
630 * driver.conf file entries with mpxio-disable property.
631 * mpxio_disable on return *mpxio_disable is set to the setting of the
632 * driver global mpxio-dissable property as follows.
633 * 0 if driver mpxio-disable="no"
634 * 1 if driver mpxio-disable="yes"
635 * -1 if driver mpxio-disable property isn't specified.
636 */
637 static void
parse_conf_file(char * fname,struct conf_entry ** confent_list,int * mpxio_disable)638 parse_conf_file(char *fname, struct conf_entry **confent_list,
639 int *mpxio_disable)
640 {
641 struct conf_entry *confent, *tail = NULL;
642 token_t token;
643 struct conf_file file;
644 char tokval[MAX_TOKEN_SIZE];
645
646 *confent_list = NULL;
647 *mpxio_disable = -1;
648 if ((file.fp = fopen(fname, "r")) == NULL)
649 return;
650
651 file.filename = fname;
652 file.linenum = 1;
653
654 while ((token = lex(&file, tokval, MAX_TOKEN_SIZE)) != T_EOF) {
655 switch (token) {
656 case T_POUND:
657 /*
658 * Skip comments.
659 */
660 find_eol(file.fp);
661 break;
662 case T_NAME:
663 if ((confent = parse_conf_entry(&file, tokval,
664 MAX_TOKEN_SIZE)) == NULL)
665 break;
666 /*
667 * No name indicates global property.
668 * Make sure parent and class not NULL.
669 */
670 if (confent->name == NULL) {
671 if (confent->parent ||
672 confent->class) {
673 file_err(&file,
674 "missing name attribute\n");
675 } else if (confent->mpxio_disable != -1) {
676 if (*mpxio_disable == -1)
677 *mpxio_disable =
678 confent->mpxio_disable;
679 else
680 file_err(&file,
681 "'mpxio-disable' property already specified\n");
682 }
683 free_confent(confent);
684 break;
685 }
686
687 /*
688 * This is a node spec, either parent or class
689 * must be specified.
690 */
691 if (confent->parent == NULL && confent->class == NULL) {
692 file_err(&file,
693 "missing parent or class attribute\n");
694 free_confent(confent);
695 break;
696 }
697
698 /* only need entries with mpxio_disable property */
699 if (confent->mpxio_disable == -1) {
700 free_confent(confent);
701 break;
702 }
703
704 if (tail)
705 tail->next = confent;
706 else
707 *confent_list = confent;
708 tail = confent;
709 break;
710
711 case T_NEWLINE:
712 file.linenum++;
713 break;
714 default:
715 break;
716 }
717 }
718
719 (void) fclose(file.fp);
720 }
721
722 /*
723 * Return the driver class of the given driver_name.
724 * The memory for the driver class is allocated by this function and the
725 * caller must free it.
726 */
727 static char *
get_driver_class(char * rootdir,char * driver_name)728 get_driver_class(char *rootdir, char *driver_name)
729 {
730 FILE *fp;
731 char buf[BUFSIZE];
732 char driver[BUFSIZE];
733 char class_name[BUFSIZE];
734
735 logdmsg(("get_driver_class: rootdir = %s, driver name = %s\n",
736 rootdir, driver_name));
737
738 (void) snprintf(buf, sizeof (buf), "%s%s", rootdir, DRIVER_CLASSES);
739
740 if ((fp = fopen(buf, "r")) == NULL) {
741 logdmsg(("get_driver_class: failed to open %s: %s\n",
742 buf, strerror(errno)));
743 return (NULL);
744 }
745
746 while (fgets(buf, sizeof (buf), fp) != NULL) {
747 /* LINTED - unbounded string specifier */
748 if ((sscanf(buf, "%s %s", driver, class_name) == 2) &&
749 driver[0] != '#' && strcmp(driver, driver_name) == 0) {
750 logdmsg(("get_driver_class: driver class = %s\n",
751 class_name));
752 (void) fclose(fp);
753 return (strdup(class_name));
754 }
755 }
756
757 (void) fclose(fp);
758 return (NULL);
759 }
760
761 static int
lookup_in_confent_list(struct conf_entry * confent_list,int match_class,char * parent,char * unit_addr,int port)762 lookup_in_confent_list(struct conf_entry *confent_list,
763 int match_class, char *parent, char *unit_addr, int port)
764 {
765 struct conf_entry *confent;
766 char *par;
767
768 logdmsg(("lookup_in_confent_list: %s = \"%s\", unit_addr = \"%s\", "
769 "port = %d\n", (match_class) ? "class" : "parent", parent,
770 STRVAL(unit_addr), port));
771
772 for (confent = confent_list; confent != NULL; confent = confent->next) {
773 par = (match_class) ? confent->class : confent->parent;
774 if (unit_addr) {
775 if (confent->unit_address != NULL &&
776 strcmp(confent->unit_address, unit_addr) == 0 &&
777 par != NULL && strcmp(par, parent) == 0)
778 return (confent->mpxio_disable);
779 } else {
780 if (confent->port == port &&
781 par != NULL && strcmp(par, parent) == 0)
782 return (confent->mpxio_disable);
783 }
784 }
785 return (-1);
786 }
787
788 /*
789 * lookup mpxio-disabled property setting for the given path in the given
790 * driver.conf file. Match the entries from most specific to least specific.
791 *
792 * conf_file the path name of either fp.conf, qlc.conf or scsi_vhci.conf
793 * path /devices node path without the /devices prefix.
794 * If the conf_file is fp.conf, path must be a fp node path
795 * if the conf_file is qlc.conf, path must be a qlc node path.
796 * if the conf_file is scsi_vhci.conf, path must be NULL.
797 * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0
798 * /pci@8,600000/SUNW,qlc@4
799 *
800 * returns:
801 * 0 if mpxio-disable="no"
802 * 1 if mpxio-disable="yes"
803 * -1 if mpxio-disable property isn't specified.
804 */
805 static int
lookup_in_conf_file(char * rootdir,char * conf_file,char * path)806 lookup_in_conf_file(char *rootdir, char *conf_file, char *path)
807 {
808 struct conf_entry *confent_list = NULL;
809 int mpxio_disable;
810 di_node_t par_node = DI_NODE_NIL;
811 char *node_name = NULL, *node_addr = NULL;
812 char *unit_addr = NULL;
813 int port = -1;
814 char *par_node_name = NULL, *par_node_addr = NULL;
815 char *par_binding_name = NULL, *par_driver_name = NULL;
816 char *par_driver_class = NULL, *par_node_name_addr;
817 int rv = -1;
818 char buf[MAXPATHLEN];
819
820 logdmsg(("lookup_in_conf_file: rootdir = \"%s\", conf_file = \"%s\", "
821 "path = \"%s\"\n", rootdir, conf_file, STRVAL(path)));
822
823 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, conf_file);
824 parse_conf_file(buf, &confent_list, &mpxio_disable);
825 #ifdef DEBUG
826 log_confent_list(buf, confent_list, mpxio_disable);
827 #endif
828
829 /* if path is NULL, return driver global mpxio-disable setting */
830 if (path == NULL) {
831 rv = mpxio_disable;
832 goto done;
833 }
834
835 if ((node_name = strrchr(path, '/')) == NULL)
836 goto done;
837
838 *node_name = '\0';
839 node_name++;
840
841 if ((node_addr = strchr(node_name, '@')) == NULL)
842 goto done;
843
844 *node_addr = '\0';
845 node_addr++;
846
847 if (strcmp(node_name, "fp") == 0) {
848 /* get port number; encoded in the node addr as a hex number */
849 port = (int)strtol(node_addr, NULL, 16);
850 } else
851 unit_addr = node_addr;
852
853 /*
854 * Match from most specific to least specific;
855 * first, start the lookup based on full path.
856 */
857 if ((rv = lookup_in_confent_list(confent_list, 0, path,
858 unit_addr, port)) != -1)
859 goto done;
860
861 /* lookup nodename@address */
862 if ((par_node_name_addr = strrchr(path, '/')) != NULL) {
863 par_node_name_addr++;
864 if ((rv = lookup_in_confent_list(confent_list, 0,
865 par_node_name_addr, unit_addr, port)) != -1)
866 goto done;
867 }
868
869 /* di_init() doesn't work when 0 is passed in flags */
870 par_node = di_init(path, DINFOMINOR);
871 if (par_node != DI_NODE_NIL) {
872 par_node_name = di_node_name(par_node);
873 par_node_addr = di_bus_addr(par_node);
874 par_binding_name = di_binding_name(par_node);
875 par_driver_name = di_driver_name(par_node);
876 }
877
878 logdmsg(("par_node_name = %s\n", STRVAL(par_node_name)));
879 logdmsg(("par_node_addr = %s\n", STRVAL(par_node_addr)));
880 logdmsg(("par_binding_name = %s\n", STRVAL(par_binding_name)));
881 logdmsg(("par_driver_name = %s\n", STRVAL(par_driver_name)));
882
883 /* lookup bindingname@address */
884 if (par_binding_name != NULL && par_binding_name != par_node_name &&
885 par_node_addr != NULL) {
886 (void) snprintf(buf, sizeof (buf), "%s@%s", par_binding_name,
887 par_node_addr);
888 if ((rv = lookup_in_confent_list(confent_list, 0,
889 buf, unit_addr, port)) != -1)
890 goto done;
891 }
892
893 /* lookup binding name */
894 if (par_binding_name != NULL) {
895 if ((rv = lookup_in_confent_list(confent_list, 0,
896 par_binding_name, unit_addr, port)) != -1)
897 goto done;
898 }
899
900 if (par_driver_name != NULL) {
901 /* lookup driver name */
902 if ((rv = lookup_in_confent_list(confent_list, 0,
903 par_driver_name, unit_addr, port)) != -1)
904 goto done;
905
906 /* finally, lookup class name */
907 par_driver_class = get_driver_class(rootdir, par_driver_name);
908 if (par_driver_class != NULL) {
909 if ((rv = lookup_in_confent_list(confent_list, 1,
910 par_driver_class, unit_addr, port)) != -1)
911 goto done;
912 }
913 }
914
915 /*
916 * no match so far;
917 * use the driver global mpxio-disable setting if exists.
918 */
919 rv = mpxio_disable;
920
921 done:
922 if (node_name != NULL)
923 *(node_name - 1) = '/';
924 if (node_addr != NULL)
925 *(node_addr - 1) = '@';
926 if (par_driver_class != NULL)
927 free(par_driver_class);
928 if (confent_list != NULL)
929 free_confent_list(confent_list);
930 if (par_node != DI_NODE_NIL)
931 di_fini(par_node);
932
933 return (rv);
934 }
935
936 /*
937 * Given client_name return whether it is a phci or vhci based name.
938 * client_name is /devices name of a client without the /devices prefix.
939 *
940 * client_name Return value
941 * .../fp@xxx/ssd@yyy CLIENT_TYPE_PHCI
942 * .../scsi_vhci/ssd@yyy CLIENT_TYPE_VHCI
943 * other CLIENT_TYPE_UNKNOWN
944 */
945 static client_type_t
client_name_type(char * client_name)946 client_name_type(char *client_name)
947 {
948 client_type_t client_type;
949 char *p1, *p2;
950
951 logdmsg(("client_name_type: client_name = %s\n", client_name));
952
953 if (strncmp(client_name, SLASH_SCSI_VHCI,
954 sizeof (SLASH_SCSI_VHCI) - 1) == 0)
955 return (CLIENT_TYPE_VHCI);
956
957 if (*client_name != '/')
958 return (CLIENT_TYPE_UNKNOWN);
959
960 if ((p1 = strrchr(client_name, '/')) == NULL)
961 return (CLIENT_TYPE_UNKNOWN);
962
963 *p1 = '\0';
964
965 if ((p2 = strrchr(client_name, '/')) != NULL &&
966 strncmp(p2, SLASH_FP_AT, sizeof (SLASH_FP_AT) - 1) == 0)
967 client_type = CLIENT_TYPE_PHCI;
968 else
969 client_type = CLIENT_TYPE_UNKNOWN;
970
971 *p1 = '/';
972 return (client_type);
973 }
974
975 /*
976 * Compare controller name portion of dev1 and dev2.
977 *
978 * rootdir root directory of the target environment
979 * dev1 can be either a /dev link or /devices name in the target
980 * environemnt
981 * dev2 /devices name of a device without the /devices prefix
982 *
983 * Returns:
984 * 0 if controller names match
985 * 1 if controller names don't match
986 * -1 an error occurred.
987 */
988 static int
compare_controller(char * rootdir,char * dev1,char * dev2)989 compare_controller(char *rootdir, char *dev1, char *dev2)
990 {
991 int linksize;
992 char *p1, *p;
993 char physdev1[MAXPATHLEN];
994 char buf[MAXPATHLEN];
995
996 logdmsg(("compare_controller: rootdir = %s, dev1 = %s, dev2 = %s\n",
997 rootdir, dev1, dev2));
998
999 if (strncmp(dev1, SLASH_DEV_SLASH, sizeof (SLASH_DEV_SLASH) - 1)
1000 == 0) {
1001 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, dev1);
1002 if ((linksize = readlink(buf, physdev1, MAXPATHLEN)) > 0 &&
1003 linksize < (MAXPATHLEN - 1)) {
1004 physdev1[linksize] = '\0';
1005 logdmsg(("compare_controller: physdev1 = %s\n",
1006 physdev1));
1007 } else
1008 return (-1);
1009 } else
1010 (void) strlcpy(physdev1, dev1, MAXPATHLEN);
1011
1012 if ((p1 = strstr(physdev1, SLASH_DEVICES)) == NULL)
1013 return (-1);
1014
1015 p1 += sizeof (SLASH_DEVICES) - 1;
1016 /* strip the device portion */
1017 if ((p = strrchr(p1, '/')) == NULL)
1018 return (-1);
1019 *p = '\0';
1020
1021 if ((p = strrchr(dev2, '/')) == NULL)
1022 return (-1);
1023 *p = '\0';
1024
1025 logdmsg(("compare_controller: path1 = %s, path2 = %s\n",
1026 p1, dev2));
1027 if (strcmp(p1, dev2) == 0) {
1028 *p = '/';
1029 return (0);
1030 } else {
1031 *p = '/';
1032 return (1);
1033 }
1034 }
1035
1036 /*
1037 * Check if the specified device path is on the root controller.
1038 *
1039 * rootdir root directory of the target environment
1040 * path /devices name of a device without the /devices prefix
1041 *
1042 * Returns
1043 * 1 if the path is on the root controller
1044 * 0 if the path is not on the root controller
1045 * -1 if an error occurs
1046 */
1047 static int
is_root_controller(char * rootdir,char * path)1048 is_root_controller(char *rootdir, char *path)
1049 {
1050 FILE *fp;
1051 char *tmpfile;
1052 int rv = -1;
1053 struct vfstab vfsent;
1054 char buf[MAXPATHLEN];
1055 char ctd[MAXNAMELEN + 1];
1056
1057 logdmsg(("is_root_controller: rootdir = %s, path = %s\n", rootdir,
1058 path));
1059
1060 (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, VFSTAB);
1061
1062 if ((fp = fopen(buf, "r")) == NULL) {
1063 logdmsg(("is_root_controller: failed to open %s: %s\n",
1064 buf, strerror(errno)));
1065 return (-1);
1066 }
1067
1068 if (getvfsfile(fp, &vfsent, "/") != 0) {
1069 logdmsg(("is_root_controller: getvfsfile: failed to read "
1070 "vfstab entry for mount point \"/\": %s\n",
1071 strerror(errno)));
1072 (void) fclose(fp);
1073 return (-1);
1074 }
1075 (void) fclose(fp);
1076
1077 /* check if the root is an svm metadisk */
1078 if (strncmp(vfsent.vfs_special, META_DEV, sizeof (META_DEV) - 1) != 0) {
1079 if (compare_controller(rootdir, vfsent.vfs_special, path) == 0)
1080 return (1);
1081 else
1082 return (0);
1083 }
1084
1085 /* Don't use /var/run as it is not mounted in miniroot */
1086 if ((tmpfile = tempnam("/tmp", "diirc")) == NULL) {
1087 logdmsg(("is_root_controller: tempnam: failed: %s\n",
1088 strerror(errno)));
1089 return (-1);
1090 }
1091
1092 /* get metadisk components using metastat command */
1093 (void) snprintf(buf, MAXPATHLEN,
1094 "/usr/sbin/metastat -p %s 2>/dev/null | "
1095 "/usr/bin/grep ' 1 1 ' | "
1096 "/usr/bin/sed -e 's/^.* 1 1 //' | "
1097 "/usr/bin/cut -f1 -d ' ' > %s",
1098 vfsent.vfs_special + sizeof (META_DEV) - 1, tmpfile);
1099
1100 logdmsg(("is_root_controller: command = %s\n", buf));
1101 fp = NULL;
1102 if (system(buf) == 0 && (fp = fopen(tmpfile, "r")) != NULL) {
1103 while (fscanf(fp, "%" VAL2STR(MAXNAMELEN) "s", ctd) == 1) {
1104 (void) snprintf(buf, MAXPATHLEN, "/dev/dsk/%s", ctd);
1105 if (compare_controller(rootdir, buf, path) == 0) {
1106 rv = 1;
1107 goto out;
1108 }
1109 }
1110 rv = 0;
1111 }
1112
1113 out:
1114 if (fp)
1115 (void) fclose(fp);
1116 (void) unlink(tmpfile);
1117 free(tmpfile);
1118 return (rv);
1119 }
1120
1121 static int
file_exists(char * rootdir,char * path)1122 file_exists(char *rootdir, char *path)
1123 {
1124 struct stat stbuf;
1125 char fullpath[MAXPATHLEN];
1126 int x;
1127
1128 (void) snprintf(fullpath, MAXPATHLEN, "%s%s", rootdir, path);
1129
1130 x = stat(fullpath, &stbuf);
1131 logdmsg(("file_exists: %s: %s\n", fullpath, (x == 0) ? "yes" : "no"));
1132 if (x == 0)
1133 return (1);
1134 else
1135 return (0);
1136 }
1137
1138 /*
1139 * Check if mpxio is enabled or disabled on the specified device path.
1140 * Looks through the .conf files to determine the mpxio setting.
1141 *
1142 * rootdir root directory of the target environment
1143 * path /devices name of a device without the /devices prefix and
1144 * minor name component.
1145 *
1146 * Returns
1147 * 1 if mpxio is disabled
1148 * 0 if mpxio is enabled
1149 * -1 if an error occurs
1150 */
1151 static int
is_mpxio_disabled(char * rootdir,char * path)1152 is_mpxio_disabled(char *rootdir, char *path)
1153 {
1154 int mpxio_disable;
1155 char *p;
1156 int check_root_controller;
1157
1158 logdmsg(("is_mpxio_disabled: rootdir = %s, path = %s\n",
1159 rootdir, path));
1160
1161 if (file_exists(rootdir, SCSI_VHCI_CONF) == 0) {
1162 /*
1163 * scsi_vhci.conf doesn't exist:
1164 * if upgrading from a pre solaris 9 release. or
1165 * if this function is called during fresh or flash install
1166 * prior to installing scsi_vhci.conf file.
1167 */
1168 if (file_exists(rootdir, "/kernel/drv"))
1169 /* upgrading from pre solaris 9 */
1170 return (1);
1171 else
1172 /* fresh or flash install */
1173 return (0);
1174 }
1175
1176 mpxio_disable = lookup_in_conf_file(rootdir, SCSI_VHCI_CONF, NULL);
1177
1178 /*
1179 * scsi_vhci.conf contains mpxio-disable property only in s9 and
1180 * s8+sfkpatch. This property is no longer present from s10 onwards.
1181 */
1182 if (mpxio_disable == 1) {
1183 /* upgrading from s8 or s9 with mpxio globally disabled */
1184 return (1);
1185 } else if (mpxio_disable == 0) {
1186 /* upgrading from s8 or s9 with mpxio globally enabled */
1187 check_root_controller = 1;
1188 } else {
1189 /*
1190 * We are looking at the s10 version of the file. This is
1191 * the case if this function is called after installing the
1192 * new scsi_vhci.conf file.
1193 */
1194 check_root_controller = 0;
1195 }
1196
1197 if ((mpxio_disable = lookup_in_conf_file(rootdir, FP_CONF, path))
1198 != -1)
1199 return (mpxio_disable);
1200
1201 if ((p = strrchr(path, '/')) == NULL)
1202 return (-1);
1203
1204 *p = '\0';
1205 if ((mpxio_disable = lookup_in_conf_file(rootdir, QLC_CONF, path))
1206 != -1) {
1207 *p = '/';
1208 return (mpxio_disable);
1209 }
1210 *p = '/';
1211
1212 /*
1213 * mpxio-disable setting is not found in the .conf files.
1214 * The default is to enable mpxio, except if the path is on the root
1215 * controller.
1216 *
1217 * In s8 and s9 mpxio is not supported on the root controller.
1218 * NWS supplies a patch to enable root controller support in s8 and s9.
1219 * If the system had the patch installed, the fp.conf file would have
1220 * explicit "mpxio-disable=no" for the root controller. So we would
1221 * have found the mpxio-disable setting when we looked up this property
1222 * in the fp.conf file.
1223 */
1224 if (check_root_controller) {
1225 mpxio_disable = is_root_controller(rootdir, path);
1226 logdmsg(("is_mpxio_disabled: is_root_controller returned %d\n",
1227 mpxio_disable));
1228 } else
1229 mpxio_disable = 0;
1230
1231 return (mpxio_disable);
1232 }
1233
1234 static int
vhci_ctl(sv_iocdata_t * iocp,int cmd)1235 vhci_ctl(sv_iocdata_t *iocp, int cmd)
1236 {
1237 int fd, rv;
1238
1239 if ((fd = open(VHCI_CTL_NODE, O_RDWR)) < 0)
1240 return (-1);
1241 rv = ioctl(fd, cmd, iocp);
1242 (void) close(fd);
1243 return (rv);
1244 }
1245
1246 /*
1247 * Convert a phci client name to vhci client name.
1248 *
1249 * phci_name phci client /devices name without the /devices prefix and
1250 * minor name component.
1251 * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
1252 *
1253 * Returns on success, vhci client name is returned. The memory for
1254 * the vhci name is allocated by this function and the caller
1255 * must free it.
1256 * on failure, NULL is returned.
1257 */
1258 static char *
phci_to_vhci(char * phci_name)1259 phci_to_vhci(char *phci_name)
1260 {
1261 sv_iocdata_t ioc;
1262 char *slash, *addr, *retp;
1263 char vhci_name_buf[MAXPATHLEN];
1264 char phci_name_buf[MAXPATHLEN];
1265 char addr_buf[MAXNAMELEN];
1266
1267 logdmsg(("phci_to_vhci: pchi_name = %s\n", phci_name));
1268 (void) strlcpy(phci_name_buf, phci_name, MAXPATHLEN);
1269
1270 if ((slash = strrchr(phci_name_buf, '/')) == NULL ||
1271 (addr = strchr(slash, '@')) == NULL)
1272 return (NULL);
1273
1274 *slash = '\0';
1275 addr++;
1276 (void) strlcpy(addr_buf, addr, MAXNAMELEN);
1277
1278 bzero(&ioc, sizeof (sv_iocdata_t));
1279 ioc.client = vhci_name_buf;
1280 ioc.phci = phci_name_buf;
1281 ioc.addr = addr_buf;
1282 if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_NAME) != 0) {
1283 logdmsg(("phci_to_vhci: vhci_ctl failed: %s\n",
1284 strerror(errno)));
1285 return (NULL);
1286 }
1287
1288 retp = strdup(vhci_name_buf);
1289 logdmsg(("phci_to_vhci: vhci name = %s\n", STRVAL(retp)));
1290 return (retp);
1291 }
1292
1293 static int
add_to_phci_list(char ** phci_list,sv_path_info_t * pi,int npaths,int state,char * node_name)1294 add_to_phci_list(char **phci_list, sv_path_info_t *pi, int npaths, int state,
1295 char *node_name)
1296 {
1297 int rv = 0;
1298 char name[MAXPATHLEN];
1299
1300 while (npaths--) {
1301 if (state == pi->ret_state) {
1302 (void) snprintf(name, MAXPATHLEN, "%s/%s@%s",
1303 pi->device.ret_phci, node_name, pi->ret_addr);
1304 if ((*phci_list = strdup(name)) == NULL)
1305 return (-1);
1306 phci_list++;
1307 rv++;
1308 }
1309 pi++;
1310 }
1311
1312 return (rv);
1313 }
1314
1315 static void
free_pathlist(char ** pathlist)1316 free_pathlist(char **pathlist)
1317 {
1318 char **p;
1319
1320 if (pathlist != NULL) {
1321 for (p = pathlist; *p != NULL; p++)
1322 free(*p);
1323 free(pathlist);
1324 }
1325 }
1326
1327
1328 /*
1329 * Convert a vhci client name to phci client names.
1330 *
1331 * vhci_name vhci client /devices name without the /devices prefix and
1332 * minor name component.
1333 * num_paths On return, *num_paths is set to the number paths in the
1334 * returned path list.
1335 *
1336 * Returns NULL terminated path list containing phci client paths is
1337 * returned on success. The memory for the path list is
1338 * allocated by this function and the caller must free it by
1339 * calling free_pathlist().
1340 * NULL is returned on failure.
1341 */
1342 static char **
vhci_to_phci(char * vhci_name,int * num_paths)1343 vhci_to_phci(char *vhci_name, int *num_paths)
1344 {
1345 sv_iocdata_t ioc;
1346 uint_t npaths;
1347 int n;
1348 char **phci_list = NULL;
1349 char *node_name, *at;
1350 char vhci_name_buf[MAXPATHLEN];
1351
1352 logdmsg(("vhci_to_phci: vchi_name = %s\n", vhci_name));
1353
1354 *num_paths = 0;
1355 (void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN);
1356
1357 /* first get the number paths */
1358 bzero(&ioc, sizeof (sv_iocdata_t));
1359 ioc.client = vhci_name_buf;
1360 ioc.ret_elem = &npaths;
1361 if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO) != 0 ||
1362 npaths == 0) {
1363 logdmsg(("vhci_to_phci: vhci_ctl failed to get npaths: %s\n",
1364 strerror(errno)));
1365 return (NULL);
1366 }
1367
1368 /* now allocate memory for the path information and get all paths */
1369 bzero(&ioc, sizeof (sv_iocdata_t));
1370 ioc.client = vhci_name_buf;
1371 ioc.buf_elem = npaths;
1372 ioc.ret_elem = &npaths;
1373 if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths,
1374 sizeof (sv_path_info_t))) == NULL)
1375 return (NULL);
1376 if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO) != 0 ||
1377 npaths == 0) {
1378 logdmsg(("vhci_to_phci: vhci_ctl failed: %s\n",
1379 strerror(errno)));
1380 goto out;
1381 }
1382
1383 if (ioc.buf_elem < npaths)
1384 npaths = ioc.buf_elem;
1385
1386 if ((node_name = strrchr(vhci_name_buf, '/')) == NULL ||
1387 (at = strchr(node_name, '@')) == NULL)
1388 goto out;
1389
1390 node_name++;
1391 *at = '\0';
1392
1393 /* allocate one more (than npaths) for the terminating NULL pointer */
1394 if ((phci_list = calloc(npaths + 1, sizeof (char *))) == NULL)
1395 goto out;
1396
1397 /*
1398 * add only online paths as non-online paths may not be accessible
1399 * in the target environment.
1400 */
1401 if ((n = add_to_phci_list(phci_list, ioc.ret_buf, npaths,
1402 MDI_PATHINFO_STATE_ONLINE, node_name)) <= 0)
1403 goto out;
1404
1405 free(ioc.ret_buf);
1406 *num_paths = n;
1407
1408 #ifdef DEBUG
1409 logdmsg(("vhci_to_phci: phci list:\n"));
1410 log_pathlist(phci_list);
1411 #endif
1412 return (phci_list);
1413
1414 out:
1415 free(ioc.ret_buf);
1416 if (phci_list)
1417 free_pathlist(phci_list);
1418 return (NULL);
1419 }
1420
1421 /*
1422 * build list of paths accessible from the target environment
1423 */
1424 static int
build_pathlist(char * rootdir,char * vhcipath,char ** pathlist,int npaths)1425 build_pathlist(char *rootdir, char *vhcipath, char **pathlist, int npaths)
1426 {
1427 int mpxio_disabled;
1428 int i, j;
1429 char *vpath = NULL;
1430
1431 for (i = 0; i < npaths; i++) {
1432 mpxio_disabled = is_mpxio_disabled(rootdir, pathlist[i]);
1433 logdmsg(("build_pathlist: mpxio_disabled = %d "
1434 "on path %s\n", mpxio_disabled, pathlist[i]));
1435 if (mpxio_disabled == -1)
1436 return (-1);
1437 if (mpxio_disabled == 0) {
1438 /*
1439 * mpxio is enabled on this phci path.
1440 * So use vhci path instead of phci path.
1441 */
1442 if (vpath == NULL) {
1443 if ((vpath = strdup(vhcipath)) == NULL)
1444 return (-1);
1445 free(pathlist[i]);
1446 /* keep vhci path at beginning of the list */
1447 for (j = i; j > 0; j--)
1448 pathlist[j] = pathlist[j - 1];
1449 pathlist[0] = vpath;
1450 } else {
1451 free(pathlist[i]);
1452 npaths--;
1453 for (j = i; j < npaths; j++)
1454 pathlist[j] = pathlist[j + 1];
1455 pathlist[npaths] = NULL;
1456 /* compensate for i++ in the for loop */
1457 i--;
1458 }
1459 }
1460 }
1461
1462 #ifdef DEBUG
1463 logdmsg(("build_pathlist: returning npaths = %d, pathlist:\n", npaths));
1464 log_pathlist(pathlist);
1465 #endif
1466 return (npaths);
1467 }
1468
1469 /*
1470 * Check if the specified device is refenced in the vfstab file.
1471 * Return 1 if referenced, 0 if not.
1472 *
1473 * rootdir root directory of the target environment
1474 * nodepath /devices path of a device in the target environment without
1475 * the /devices prefix and minor component.
1476 */
1477 static int
is_dev_in_vfstab(char * rootdir,char * nodepath)1478 is_dev_in_vfstab(char *rootdir, char *nodepath)
1479 {
1480 FILE *fp;
1481 int linksize;
1482 struct vfstab vfsent;
1483 char *abspath, *minor;
1484 char physpath[MAXPATHLEN];
1485 char buf[MAXPATHLEN];
1486
1487 logdmsg(("is_dev_in_vfstab: rootdir = %s, nodepath = %s\n",
1488 rootdir, nodepath));
1489
1490 (void) snprintf(buf, sizeof (buf), "%s%s", rootdir, VFSTAB);
1491
1492 if ((fp = fopen(buf, "r")) == NULL)
1493 return (0);
1494
1495 /*
1496 * read device specials from vfstab and compare names at physical
1497 * node path level.
1498 */
1499 while (getvfsent(fp, &vfsent) == 0) {
1500 if (strncmp(vfsent.vfs_special, SLASH_DEV_SLASH,
1501 sizeof (SLASH_DEV_SLASH) - 1) == 0) {
1502 (void) snprintf(buf, MAXPATHLEN, "%s%s",
1503 rootdir, vfsent.vfs_special);
1504 if ((linksize = readlink(buf, physpath,
1505 MAXPATHLEN)) > 0 && linksize < (MAXPATHLEN - 1)) {
1506 physpath[linksize] = '\0';
1507 if ((abspath = strstr(physpath,
1508 SLASH_DEVICES_SLASH)) == NULL)
1509 continue;
1510 } else
1511 continue;
1512 } else if (strncmp(vfsent.vfs_special, SLASH_DEVICES_SLASH,
1513 sizeof (SLASH_DEVICES_SLASH) - 1) == 0) {
1514 (void) strlcpy(physpath, vfsent.vfs_special,
1515 MAXPATHLEN);
1516 abspath = physpath;
1517 } else
1518 continue;
1519
1520 /* point to / after /devices */
1521 abspath += sizeof (SLASH_DEVICES_SLASH) - 2;
1522 /* strip minor component */
1523 if ((minor = strrchr(abspath, ':')) != NULL)
1524 *minor = '\0';
1525
1526 if (strcmp(nodepath, abspath) == 0) {
1527 (void) fclose(fp);
1528 logdmsg(("is_dev_in_vfstab: returning 1\n"));
1529 return (1);
1530 }
1531 }
1532
1533 (void) fclose(fp);
1534 return (0);
1535 }
1536
1537 #endif /* __sparc */
1538
1539 static int
devlink_callback(di_devlink_t devlink,void * argp)1540 devlink_callback(di_devlink_t devlink, void *argp)
1541 {
1542 const char *link;
1543
1544 if ((link = di_devlink_path(devlink)) != NULL)
1545 (void) strlcpy((char *)argp, link, MAXPATHLEN);
1546
1547 return (DI_WALK_CONTINUE);
1548 }
1549
1550 /*
1551 * Get the /dev name in the install environment corresponding to physpath.
1552 *
1553 * physpath /devices path in the install environment without the /devices
1554 * prefix.
1555 * buf caller supplied buffer where the /dev name is placed on return
1556 * bufsz length of the buffer
1557 *
1558 * Returns strlen of the /dev name on success, -1 on failure.
1559 */
1560 static int
get_install_devlink(char * physpath,char * buf,size_t bufsz)1561 get_install_devlink(char *physpath, char *buf, size_t bufsz)
1562 {
1563 di_devlink_handle_t devlink_hdl;
1564 char devname[MAXPATHLEN];
1565 int tries = 0;
1566 int sleeptime = 2; /* number of seconds to sleep between retries */
1567 int maxtries = 10; /* maximum number of tries */
1568
1569 logdmsg(("get_install_devlink: physpath = %s\n", physpath));
1570
1571 /*
1572 * devlink_db sync happens after MINOR_FINI_TIMEOUT_DEFAULT secs
1573 * after dev link creation. So wait for minimum that amout of time.
1574 */
1575
1576 retry:
1577 (void) sleep(sleeptime);
1578
1579 if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
1580 logdmsg(("get_install_devlink: di_devlink_init() failed: %s\n",
1581 strerror(errno)));
1582 return (-1);
1583 }
1584
1585 devname[0] = '\0';
1586 if (di_devlink_walk(devlink_hdl, NULL, physpath, DI_PRIMARY_LINK,
1587 devname, devlink_callback) == 0) {
1588 if (devname[0] == '\0' && tries < maxtries) {
1589 tries++;
1590 (void) di_devlink_fini(&devlink_hdl);
1591 goto retry;
1592 } else if (devname[0] == '\0') {
1593 logdmsg(("get_install_devlink: di_devlink_walk"
1594 " failed: %s\n", strerror(errno)));
1595 (void) di_devlink_fini(&devlink_hdl);
1596 return (-1);
1597 }
1598 } else {
1599 logdmsg(("get_install_devlink: di_devlink_walk failed: %s\n",
1600 strerror(errno)));
1601 (void) di_devlink_fini(&devlink_hdl);
1602 return (-1);
1603 }
1604
1605 (void) di_devlink_fini(&devlink_hdl);
1606
1607 logdmsg(("get_install_devlink: devlink = %s\n", devname));
1608 return (strlcpy(buf, devname, bufsz));
1609 }
1610
1611 /*
1612 * Get the /dev name in the target environment corresponding to physpath.
1613 *
1614 * rootdir root directory of the target environment
1615 * physpath /devices path in the target environment without the /devices
1616 * prefix.
1617 * buf caller supplied buffer where the /dev name is placed on return
1618 * bufsz length of the buffer
1619 *
1620 * Returns strlen of the /dev name on success, -1 on failure.
1621 */
1622 static int
get_target_devlink(char * rootdir,char * physpath,char * buf,size_t bufsz)1623 get_target_devlink(char *rootdir, char *physpath, char *buf, size_t bufsz)
1624 {
1625 char *p;
1626 int linksize;
1627 DIR *dirp;
1628 struct dirent *direntry;
1629 char dirpath[MAXPATHLEN];
1630 char devname[MAXPATHLEN];
1631 char physdev[MAXPATHLEN];
1632
1633 logdmsg(("get_target_devlink: rootdir = %s, physpath = %s\n",
1634 rootdir, physpath));
1635
1636 if ((p = strrchr(physpath, '/')) == NULL)
1637 return (-1);
1638
1639 if (strstr(p, ",raw") != NULL) {
1640 (void) snprintf(dirpath, MAXPATHLEN, "%s/dev/rdsk", rootdir);
1641 } else {
1642 (void) snprintf(dirpath, MAXPATHLEN, "%s/dev/dsk", rootdir);
1643 }
1644
1645 if ((dirp = opendir(dirpath)) == NULL)
1646 return (-1);
1647
1648 while ((direntry = readdir(dirp)) != NULL) {
1649 if (strcmp(direntry->d_name, ".") == 0 ||
1650 strcmp(direntry->d_name, "..") == 0)
1651 continue;
1652
1653 (void) snprintf(devname, MAXPATHLEN, "%s/%s",
1654 dirpath, direntry->d_name);
1655
1656 if ((linksize = readlink(devname, physdev, MAXPATHLEN)) > 0 &&
1657 linksize < (MAXPATHLEN - 1)) {
1658 physdev[linksize] = '\0';
1659 if ((p = strstr(physdev, SLASH_DEVICES_SLASH)) !=
1660 NULL && strcmp(p + sizeof (SLASH_DEVICES) - 1,
1661 physpath) == 0) {
1662 (void) closedir(dirp);
1663 logdmsg(("get_target_devlink: devlink = %s\n",
1664 devname + strlen(rootdir)));
1665 return (strlcpy(buf, devname + strlen(rootdir),
1666 bufsz));
1667 }
1668 }
1669 }
1670
1671 (void) closedir(dirp);
1672 return (-1);
1673 }
1674
1675 /*
1676 * Convert device name to physpath.
1677 *
1678 * rootdir root directory
1679 * devname a /dev name or /devices name under rootdir
1680 * physpath caller supplied buffer where the /devices path will be placed
1681 * on return (without the /devices prefix).
1682 * physpathlen length of the physpath buffer
1683 *
1684 * Returns 0 on success, -1 on failure.
1685 */
1686 static int
devname2physpath(char * rootdir,char * devname,char * physpath,int physpathlen)1687 devname2physpath(char *rootdir, char *devname, char *physpath, int physpathlen)
1688 {
1689 int linksize;
1690 char *p;
1691 char devlink[MAXPATHLEN];
1692 char tmpphyspath[MAXPATHLEN];
1693
1694 logdmsg(("devname2physpath: rootdir = %s, devname = %s\n",
1695 rootdir, devname));
1696
1697 if (strncmp(devname, SLASH_DEVICES_SLASH,
1698 sizeof (SLASH_DEVICES_SLASH) - 1) != 0) {
1699 if (*rootdir == '\0')
1700 linksize = readlink(devname, tmpphyspath, MAXPATHLEN);
1701 else {
1702 (void) snprintf(devlink, MAXPATHLEN, "%s%s",
1703 rootdir, devname);
1704 linksize = readlink(devlink, tmpphyspath, MAXPATHLEN);
1705 }
1706 if (linksize > 0 && linksize < (MAXPATHLEN - 1)) {
1707 tmpphyspath[linksize] = '\0';
1708 if ((p = strstr(tmpphyspath, SLASH_DEVICES_SLASH))
1709 == NULL)
1710 return (-1);
1711 } else
1712 return (-1);
1713 } else
1714 p = devname;
1715
1716 (void) strlcpy(physpath, p + sizeof (SLASH_DEVICES) - 1, physpathlen);
1717 logdmsg(("devname2physpath: physpath = %s\n", physpath));
1718 return (0);
1719 }
1720
1721 /*
1722 * Map a device name (devname) from the target environment to the
1723 * install environment.
1724 *
1725 * rootdir root directory of the target environment
1726 * devname /dev or /devices name under the target environment
1727 * buf caller supplied buffer where the mapped /dev name is placed
1728 * on return
1729 * bufsz length of the buffer
1730 *
1731 * Returns strlen of the mapped /dev name on success, -1 on failure.
1732 */
1733 int
devfs_target2install(const char * rootdir,const char * devname,char * buf,size_t bufsz)1734 devfs_target2install(const char *rootdir, const char *devname, char *buf,
1735 size_t bufsz)
1736 {
1737 char physpath[MAXPATHLEN];
1738
1739 logdmsg(("devfs_target2install: rootdir = %s, devname = %s\n",
1740 STRVAL(rootdir), STRVAL(devname)));
1741
1742 if (rootdir == NULL || devname == NULL || buf == NULL || bufsz == 0)
1743 return (-1);
1744
1745 if (strcmp(rootdir, "/") == 0)
1746 rootdir = "";
1747
1748 if (devname2physpath((char *)rootdir, (char *)devname, physpath,
1749 MAXPATHLEN) != 0)
1750 return (-1);
1751
1752 #ifdef __sparc
1753 if (client_name_type(physpath) == CLIENT_TYPE_PHCI) {
1754 char *mapped_node_path, *minor;
1755 char minorbuf[MAXNAMELEN];
1756
1757 /* strip minor component if present */
1758 if ((minor = strrchr(physpath, ':')) != NULL) {
1759 *minor = '\0';
1760 minor++;
1761 (void) strlcpy(minorbuf, minor, MAXNAMELEN);
1762 }
1763 if ((mapped_node_path = phci_to_vhci(physpath)) != NULL) {
1764 if (minor)
1765 (void) snprintf(physpath, MAXPATHLEN,
1766 "%s:%s", mapped_node_path, minorbuf);
1767 else
1768 (void) strlcpy(physpath, mapped_node_path,
1769 MAXPATHLEN);
1770 free(mapped_node_path);
1771 logdmsg(("devfs_target2install: mapped physpath: %s\n",
1772 physpath));
1773
1774 } else if (minor)
1775 *(minor - 1) = ':';
1776 }
1777 #endif /* __sparc */
1778
1779 return (get_install_devlink(physpath, buf, bufsz));
1780 }
1781
1782 /*
1783 * Map a device name (devname) from the install environment to the target
1784 * environment.
1785 *
1786 * rootdir root directory of the target environment
1787 * devname /dev or /devices name under the install environment
1788 * buf caller supplied buffer where the mapped /dev name is placed
1789 * on return
1790 * bufsz length of the buffer
1791 *
1792 * Returns strlen of the mapped /dev name on success, -1 on failure.
1793 */
1794 int
devfs_install2target(const char * rootdir,const char * devname,char * buf,size_t bufsz)1795 devfs_install2target(const char *rootdir, const char *devname, char *buf,
1796 size_t bufsz)
1797 {
1798 char physpath[MAXPATHLEN];
1799
1800 logdmsg(("devfs_install2target: rootdir = %s, devname = %s\n",
1801 STRVAL(rootdir), STRVAL(devname)));
1802
1803 if (rootdir == NULL || devname == NULL || buf == NULL || bufsz == 0)
1804 return (-1);
1805
1806 if (strcmp(rootdir, "/") == 0)
1807 rootdir = "";
1808
1809 if (devname2physpath("", (char *)devname, physpath, MAXPATHLEN) != 0)
1810 return (-1);
1811
1812 #ifdef __sparc
1813 if (client_name_type(physpath) == CLIENT_TYPE_VHCI) {
1814 char **pathlist;
1815 int npaths, i, j;
1816 char *minor;
1817 char minorbuf[MAXNAMELEN];
1818
1819 /* strip minor component if present */
1820 if ((minor = strrchr(physpath, ':')) != NULL) {
1821 *minor = '\0';
1822 minor++;
1823 (void) strlcpy(minorbuf, minor, MAXNAMELEN);
1824 }
1825
1826 if ((pathlist = vhci_to_phci(physpath, &npaths)) == NULL)
1827 return (-1);
1828
1829 if ((npaths = build_pathlist((char *)rootdir, physpath,
1830 pathlist, npaths)) <= 0) {
1831 free_pathlist(pathlist);
1832 return (-1);
1833 }
1834
1835 /*
1836 * in case of more than one path, try to use the path
1837 * referenced in the vfstab file, otherwise use the first path.
1838 */
1839 j = 0;
1840 if (npaths > 1) {
1841 for (i = 0; i < npaths; i++) {
1842 if (is_dev_in_vfstab((char *)rootdir,
1843 pathlist[i])) {
1844 j = i;
1845 break;
1846 }
1847 }
1848 }
1849
1850 if (minor)
1851 (void) snprintf(physpath, MAXPATHLEN,
1852 "%s:%s", pathlist[j], minorbuf);
1853 else
1854 (void) strlcpy(physpath, pathlist[j], MAXPATHLEN);
1855 free_pathlist(pathlist);
1856 }
1857 #endif /* __sparc */
1858
1859 return (get_target_devlink((char *)rootdir, physpath, buf, bufsz));
1860 }
1861
1862 /*
1863 * A parser for /etc/path_to_inst.
1864 * The user-supplied callback is called once for each entry in the file.
1865 * Returns 0 on success, ENOMEM/ENOENT/EINVAL on error.
1866 * Callback may return DI_WALK_TERMINATE to terminate the walk,
1867 * otherwise DI_WALK_CONTINUE.
1868 */
1869 int
devfs_parse_binding_file(const char * binding_file,int (* callback)(void *,const char *,int,const char *),void * cb_arg)1870 devfs_parse_binding_file(const char *binding_file,
1871 int (*callback)(void *, const char *, int,
1872 const char *), void *cb_arg)
1873 {
1874 token_t token;
1875 struct conf_file file;
1876 char tokval[MAX_TOKEN_SIZE];
1877 enum { STATE_RESET, STATE_DEVPATH, STATE_INSTVAL } state;
1878 char *devpath;
1879 char *bindname;
1880 int instval = 0;
1881 int rv;
1882
1883 if ((devpath = calloc(1, MAXPATHLEN)) == NULL)
1884 return (ENOMEM);
1885 if ((bindname = calloc(1, MAX_TOKEN_SIZE)) == NULL) {
1886 free(devpath);
1887 return (ENOMEM);
1888 }
1889
1890 if ((file.fp = fopen(binding_file, "r")) == NULL) {
1891 free(devpath);
1892 free(bindname);
1893 return (errno);
1894 }
1895
1896 file.filename = (char *)binding_file;
1897 file.linenum = 1;
1898
1899 state = STATE_RESET;
1900 while ((token = lex(&file, tokval, MAX_TOKEN_SIZE)) != T_EOF) {
1901 switch (token) {
1902 case T_POUND:
1903 /*
1904 * Skip comments.
1905 */
1906 find_eol(file.fp);
1907 break;
1908 case T_NAME:
1909 case T_STRING:
1910 switch (state) {
1911 case STATE_RESET:
1912 if (strlcpy(devpath, tokval,
1913 MAXPATHLEN) >= MAXPATHLEN)
1914 goto err;
1915 state = STATE_DEVPATH;
1916 break;
1917 case STATE_INSTVAL:
1918 if (strlcpy(bindname, tokval,
1919 MAX_TOKEN_SIZE) >= MAX_TOKEN_SIZE)
1920 goto err;
1921 rv = callback(cb_arg,
1922 devpath, instval, bindname);
1923 if (rv == DI_WALK_TERMINATE)
1924 goto done;
1925 if (rv != DI_WALK_CONTINUE)
1926 goto err;
1927 state = STATE_RESET;
1928 break;
1929 default:
1930 file_err(&file, tok_err, tokval);
1931 state = STATE_RESET;
1932 break;
1933 }
1934 break;
1935 case T_DECVAL:
1936 case T_HEXVAL:
1937 switch (state) {
1938 case STATE_DEVPATH:
1939 instval = (int)strtol(tokval, NULL, 0);
1940 state = STATE_INSTVAL;
1941 break;
1942 default:
1943 file_err(&file, tok_err, tokval);
1944 state = STATE_RESET;
1945 break;
1946 }
1947 break;
1948 case T_NEWLINE:
1949 file.linenum++;
1950 state = STATE_RESET;
1951 break;
1952 default:
1953 file_err(&file, tok_err, tokval);
1954 state = STATE_RESET;
1955 break;
1956 }
1957 }
1958
1959 done:
1960 (void) fclose(file.fp);
1961 free(devpath);
1962 free(bindname);
1963 return (0);
1964
1965 err:
1966 (void) fclose(file.fp);
1967 free(devpath);
1968 free(bindname);
1969 return (EINVAL);
1970 }
1971
1972 /*
1973 * Walk the minor nodes of all children below the specified device
1974 * by calling the provided callback with the path to each minor.
1975 */
1976 static int
devfs_walk_children_minors(const char * device_path,struct stat * st,int (* callback)(void *,const char *),void * cb_arg,int * terminate)1977 devfs_walk_children_minors(const char *device_path, struct stat *st,
1978 int (*callback)(void *, const char *), void *cb_arg, int *terminate)
1979 {
1980 DIR *dir;
1981 struct dirent *dp;
1982 char *minor_path = NULL;
1983 int need_close = 0;
1984 int rv;
1985
1986 if ((minor_path = calloc(1, MAXPATHLEN)) == NULL)
1987 return (ENOMEM);
1988
1989 if ((dir = opendir(device_path)) == NULL) {
1990 rv = ENOENT;
1991 goto err;
1992 }
1993 need_close = 1;
1994
1995 while ((dp = readdir(dir)) != NULL) {
1996 if ((strcmp(dp->d_name, ".") == 0) ||
1997 (strcmp(dp->d_name, "..") == 0))
1998 continue;
1999 (void) snprintf(minor_path, MAXPATHLEN,
2000 "%s/%s", device_path, dp->d_name);
2001 if (stat(minor_path, st) == -1)
2002 continue;
2003 if (S_ISDIR(st->st_mode)) {
2004 rv = devfs_walk_children_minors(
2005 (const char *)minor_path, st,
2006 callback, cb_arg, terminate);
2007 if (rv != 0)
2008 goto err;
2009 if (*terminate)
2010 break;
2011 } else {
2012 rv = callback(cb_arg, minor_path);
2013 if (rv == DI_WALK_TERMINATE) {
2014 *terminate = 1;
2015 break;
2016 }
2017 if (rv != DI_WALK_CONTINUE) {
2018 rv = EINVAL;
2019 goto err;
2020 }
2021 }
2022 }
2023
2024 rv = 0;
2025 err:
2026 if (need_close)
2027 (void) closedir(dir);
2028 if (minor_path)
2029 free(minor_path);
2030 return (rv);
2031 }
2032
2033 /*
2034 * Return the path to each minor node for a device by
2035 * calling the provided callback.
2036 */
2037 static int
devfs_walk_device_minors(const char * device_path,struct stat * st,int (* callback)(void *,const char *),void * cb_arg,int * terminate)2038 devfs_walk_device_minors(const char *device_path, struct stat *st,
2039 int (*callback)(void *, const char *), void *cb_arg, int *terminate)
2040 {
2041 char *minor_path;
2042 char *devpath;
2043 char *expr;
2044 regex_t regex;
2045 int need_regfree = 0;
2046 int need_close = 0;
2047 DIR *dir;
2048 struct dirent *dp;
2049 int rv;
2050 char *p;
2051
2052 minor_path = calloc(1, MAXPATHLEN);
2053 devpath = calloc(1, MAXPATHLEN);
2054 expr = calloc(1, MAXNAMELEN);
2055 if (devpath == NULL || expr == NULL || minor_path == NULL) {
2056 rv = ENOMEM;
2057 goto err;
2058 }
2059
2060 rv = EINVAL;
2061 if (strlcpy(devpath, device_path, MAXPATHLEN) >= MAXPATHLEN)
2062 goto err;
2063 if ((p = strrchr(devpath, '/')) == NULL)
2064 goto err;
2065 *p++ = 0;
2066 if (strlen(p) == 0)
2067 goto err;
2068 if (snprintf(expr, MAXNAMELEN, "%s:.*", p) >= MAXNAMELEN)
2069 goto err;
2070 if (regcomp(®ex, expr, REG_EXTENDED) != 0)
2071 goto err;
2072 need_regfree = 1;
2073
2074 if ((dir = opendir(devpath)) == NULL) {
2075 rv = ENOENT;
2076 goto err;
2077 }
2078 need_close = 1;
2079
2080 while ((dp = readdir(dir)) != NULL) {
2081 if ((strcmp(dp->d_name, ".") == 0) ||
2082 (strcmp(dp->d_name, "..") == 0))
2083 continue;
2084 (void) snprintf(minor_path, MAXPATHLEN,
2085 "%s/%s", devpath, dp->d_name);
2086 if (stat(minor_path, st) == -1)
2087 continue;
2088 if ((S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) &&
2089 regexec(®ex, dp->d_name, 0, NULL, 0) == 0) {
2090 rv = callback(cb_arg, minor_path);
2091 if (rv == DI_WALK_TERMINATE) {
2092 *terminate = 1;
2093 break;
2094 }
2095 if (rv != DI_WALK_CONTINUE) {
2096 rv = EINVAL;
2097 goto err;
2098 }
2099 }
2100 }
2101
2102 rv = 0;
2103 err:
2104 if (need_close)
2105 (void) closedir(dir);
2106 if (need_regfree)
2107 regfree(®ex);
2108 if (devpath)
2109 free(devpath);
2110 if (minor_path)
2111 free(minor_path);
2112 if (expr)
2113 free(expr);
2114 return (rv);
2115 }
2116
2117 /*
2118 * Perform a walk of all minor nodes for the specified device,
2119 * and minor nodes below the device.
2120 */
2121 int
devfs_walk_minor_nodes(const char * device_path,int (* callback)(void *,const char *),void * cb_arg)2122 devfs_walk_minor_nodes(const char *device_path,
2123 int (*callback)(void *, const char *), void *cb_arg)
2124 {
2125 struct stat stbuf;
2126 int rv;
2127 int terminate = 0;
2128
2129 rv = devfs_walk_device_minors(device_path,
2130 &stbuf, callback, cb_arg, &terminate);
2131 if (rv == 0 && terminate == 0) {
2132 rv = devfs_walk_children_minors(device_path,
2133 &stbuf, callback, cb_arg, &terminate);
2134 }
2135 return (rv);
2136 }
2137
2138 #ifdef DEBUG
2139
2140 static void
vlog_debug_msg(char * fmt,va_list ap)2141 vlog_debug_msg(char *fmt, va_list ap)
2142 {
2143 time_t clock;
2144 struct tm t;
2145
2146 if (!devfsmap_debug)
2147 return;
2148
2149 if (logfp == NULL) {
2150 if (*devfsmap_logfile != '\0') {
2151 logfp = fopen(devfsmap_logfile, "a");
2152 if (logfp)
2153 (void) fprintf(logfp, "\nNew Log:\n");
2154 }
2155
2156 if (logfp == NULL)
2157 logfp = stdout;
2158 }
2159
2160 clock = time(NULL);
2161 (void) localtime_r(&clock, &t);
2162 (void) fprintf(logfp, "%02d:%02d:%02d ", t.tm_hour, t.tm_min,
2163 t.tm_sec);
2164 (void) vfprintf(logfp, fmt, ap);
2165 (void) fflush(logfp);
2166 }
2167
2168 static void
log_debug_msg(char * fmt,...)2169 log_debug_msg(char *fmt, ...)
2170 {
2171 va_list ap;
2172
2173 va_start(ap, fmt);
2174 vlog_debug_msg(fmt, ap);
2175 va_end(ap);
2176 }
2177
2178 #ifdef __sparc
2179
2180 static char *
mpxio_disable_string(int mpxio_disable)2181 mpxio_disable_string(int mpxio_disable)
2182 {
2183 if (mpxio_disable == 0)
2184 return ("no");
2185 else if (mpxio_disable == 1)
2186 return ("yes");
2187 else
2188 return ("not specified");
2189 }
2190
2191 static void
log_confent_list(char * filename,struct conf_entry * confent_list,int global_mpxio_disable)2192 log_confent_list(char *filename, struct conf_entry *confent_list,
2193 int global_mpxio_disable)
2194 {
2195 struct conf_entry *confent;
2196
2197 log_debug_msg("log_confent_list: filename = %s:\n", filename);
2198 if (global_mpxio_disable != -1)
2199 log_debug_msg("\tdriver global mpxio_disable = \"%s\"\n\n",
2200 mpxio_disable_string(global_mpxio_disable));
2201
2202 for (confent = confent_list; confent != NULL; confent = confent->next) {
2203 if (confent->name)
2204 log_debug_msg("\tname = %s\n", confent->name);
2205 if (confent->parent)
2206 log_debug_msg("\tparent = %s\n", confent->parent);
2207 if (confent->class)
2208 log_debug_msg("\tclass = %s\n", confent->class);
2209 if (confent->unit_address)
2210 log_debug_msg("\tunit_address = %s\n",
2211 confent->unit_address);
2212 if (confent->port != -1)
2213 log_debug_msg("\tport = %d\n", confent->port);
2214 log_debug_msg("\tmpxio_disable = \"%s\"\n\n",
2215 mpxio_disable_string(confent->mpxio_disable));
2216 }
2217 }
2218
2219 static void
log_pathlist(char ** pathlist)2220 log_pathlist(char **pathlist)
2221 {
2222 char **p;
2223
2224 for (p = pathlist; *p != NULL; p++)
2225 log_debug_msg("\t%s\n", *p);
2226 }
2227
2228 #endif /* __sparc */
2229
2230 #endif /* DEBUG */
2231