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