2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#ifdef lint
2N/A#define _REENTRANT /* for localtime_r */
2N/A#endif
2N/A
2N/A#include <stdio.h>
2N/A#include <ctype.h>
2N/A#include <stdlib.h>
2N/A#include <sys/types.h>
2N/A#include <strings.h>
2N/A#include <stdarg.h>
2N/A#include <sys/stat.h>
2N/A#include <fcntl.h>
2N/A#include <errno.h>
2N/A#include <unistd.h>
2N/A#include <stropts.h>
2N/A#include <time.h>
2N/A#include <sys/param.h>
2N/A#include <sys/vfstab.h>
2N/A#include <dirent.h>
2N/A#ifdef __sparc
2N/A#include <sys/scsi/adapters/scsi_vhci.h>
2N/A#include <sys/sunmdi.h>
2N/A#endif /* __sparc */
2N/A#include "libdevinfo.h"
2N/A#include "device_info.h"
2N/A#include <regex.h>
2N/A#include <paths.h>
2N/A
2N/A#define isnewline(ch) ((ch) == '\n' || (ch) == '\r' || (ch) == '\f')
2N/A#define isnamechar(ch) (isalpha(ch) || isdigit(ch) || (ch) == '_' ||\
2N/A (ch) == '-')
2N/A#define MAX_TOKEN_SIZE 1024
2N/A#define BUFSIZE 1024
2N/A#define STRVAL(s) ((s) ? (s) : "NULL")
2N/A
2N/A#define SCSI_VHCI_CONF "/kernel/drv/scsi_vhci.conf"
2N/A#define QLC_CONF "/kernel/drv/qlc.conf"
2N/A#define FP_CONF "/kernel/drv/fp.conf"
2N/A#define DRIVER_CLASSES "/etc/driver_classes"
2N/A#define FP_AT "fp@"
2N/A#define VHCI_CTL_NODE "/devices/scsi_vhci:devctl"
2N/A#define SLASH_DEVICES "/devices"
2N/A#define SLASH_DEVICES_SLASH "/devices/"
2N/A#define SLASH_FP_AT "/fp@"
2N/A#define SLASH_SCSI_VHCI "/scsi_vhci"
2N/A#define META_DEV "/dev/md/dsk/"
2N/A#define SLASH_DEV_SLASH "/dev/"
2N/A
2N/A/*
2N/A * Macros to produce a quoted string containing the value of a
2N/A * preprocessor macro. For example, if SIZE is defined to be 256,
2N/A * VAL2STR(SIZE) is "256". This is used to construct format
2N/A * strings for scanf-family functions below.
2N/A */
2N/A#define QUOTE(x) #x
2N/A#define VAL2STR(x) QUOTE(x)
2N/A
2N/Atypedef enum {
2N/A CLIENT_TYPE_UNKNOWN,
2N/A CLIENT_TYPE_PHCI,
2N/A CLIENT_TYPE_VHCI
2N/A} client_type_t;
2N/A
2N/Atypedef enum {
2N/A T_EQUALS,
2N/A T_AMPERSAND,
2N/A T_BIT_OR,
2N/A T_STAR,
2N/A T_POUND,
2N/A T_COLON,
2N/A T_SEMICOLON,
2N/A T_COMMA,
2N/A T_SLASH,
2N/A T_WHITE_SPACE,
2N/A T_NEWLINE,
2N/A T_EOF,
2N/A T_STRING,
2N/A T_HEXVAL,
2N/A T_DECVAL,
2N/A T_NAME
2N/A} token_t;
2N/A
2N/Atypedef enum {
2N/A begin, parent, drvname, drvclass, prop,
2N/A parent_equals, name_equals, drvclass_equals,
2N/A parent_equals_string, name_equals_string,
2N/A drvclass_equals_string,
2N/A prop_equals, prop_equals_string, prop_equals_integer,
2N/A prop_equals_string_comma, prop_equals_integer_comma
2N/A} conf_state_t;
2N/A
2N/A/* structure to hold entries with mpxio-disable property in driver.conf file */
2N/Astruct conf_entry {
2N/A char *name;
2N/A char *parent;
2N/A char *class;
2N/A char *unit_address;
2N/A int port;
2N/A int mpxio_disable;
2N/A struct conf_entry *next;
2N/A};
2N/A
2N/Astruct conf_file {
2N/A char *filename;
2N/A FILE *fp;
2N/A int linenum;
2N/A};
2N/A
2N/Astatic char *tok_err = "Unexpected token '%s'\n";
2N/A
2N/A
2N/A/* #define DEBUG */
2N/A
2N/A#ifdef DEBUG
2N/A
2N/Aint devfsmap_debug = 0;
2N/Achar *devfsmap_logfile = _PATH_SYSVOL "/devfsmap.log";
2N/Astatic FILE *logfp;
2N/A#define logdmsg(args) log_debug_msg args
2N/Astatic void vlog_debug_msg(char *, va_list);
2N/Astatic void log_debug_msg(char *, ...);
2N/A#ifdef __sparc
2N/Astatic void log_confent_list(char *, struct conf_entry *, int);
2N/Astatic void log_pathlist(char **);
2N/A#endif /* __sparc */
2N/A
2N/A#else /* DEBUG */
2N/A#define logdmsg(args) /* nothing */
2N/A#endif /* DEBUG */
2N/A
2N/A
2N/A/*
2N/A * Leave NEWLINE as the next character.
2N/A */
2N/Astatic void
2N/Afind_eol(FILE *fp)
2N/A{
2N/A int ch;
2N/A
2N/A while ((ch = getc(fp)) != EOF) {
2N/A if (isnewline(ch)) {
2N/A (void) ungetc(ch, fp);
2N/A break;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/* ignore parsing errors */
2N/A/*ARGSUSED*/
2N/Astatic void
2N/Afile_err(struct conf_file *filep, char *fmt, ...)
2N/A{
2N/A#ifdef DEBUG
2N/A va_list ap;
2N/A
2N/A va_start(ap, fmt);
2N/A log_debug_msg("WARNING: %s line # %d: ",
2N/A filep->filename, filep->linenum);
2N/A vlog_debug_msg(fmt, ap);
2N/A va_end(ap);
2N/A#endif /* DEBUG */
2N/A}
2N/A
2N/A/* return the next token from the given driver.conf file, or -1 on error */
2N/Astatic token_t
2N/Alex(struct conf_file *filep, char *val, size_t size)
2N/A{
2N/A char *cp;
2N/A int ch, oval, badquote;
2N/A size_t remain;
2N/A token_t token;
2N/A FILE *fp = filep->fp;
2N/A
2N/A if (size < 2)
2N/A return (-1);
2N/A
2N/A cp = val;
2N/A while ((ch = getc(fp)) == ' ' || ch == '\t')
2N/A ;
2N/A
2N/A remain = size - 1;
2N/A *cp++ = (char)ch;
2N/A switch (ch) {
2N/A case '=':
2N/A token = T_EQUALS;
2N/A break;
2N/A case '&':
2N/A token = T_AMPERSAND;
2N/A break;
2N/A case '|':
2N/A token = T_BIT_OR;
2N/A break;
2N/A case '*':
2N/A token = T_STAR;
2N/A break;
2N/A case '#':
2N/A token = T_POUND;
2N/A break;
2N/A case ':':
2N/A token = T_COLON;
2N/A break;
2N/A case ';':
2N/A token = T_SEMICOLON;
2N/A break;
2N/A case ',':
2N/A token = T_COMMA;
2N/A break;
2N/A case '/':
2N/A token = T_SLASH;
2N/A break;
2N/A case ' ':
2N/A case '\t':
2N/A case '\f':
2N/A while ((ch = getc(fp)) == ' ' ||
2N/A ch == '\t' || ch == '\f') {
2N/A if (--remain == 0) {
2N/A *cp = '\0';
2N/A return (-1);
2N/A }
2N/A *cp++ = (char)ch;
2N/A }
2N/A (void) ungetc(ch, fp);
2N/A token = T_WHITE_SPACE;
2N/A break;
2N/A case '\n':
2N/A case '\r':
2N/A token = T_NEWLINE;
2N/A break;
2N/A case '"':
2N/A remain++;
2N/A cp--;
2N/A badquote = 0;
2N/A while (!badquote && (ch = getc(fp)) != '"') {
2N/A switch (ch) {
2N/A case '\n':
2N/A case EOF:
2N/A file_err(filep, "Missing \"\n");
2N/A remain = size - 1;
2N/A cp = val;
2N/A *cp++ = '\n';
2N/A badquote = 1;
2N/A /* since we consumed the newline/EOF */
2N/A (void) ungetc(ch, fp);
2N/A break;
2N/A
2N/A case '\\':
2N/A if (--remain == 0) {
2N/A *cp = '\0';
2N/A return (-1);
2N/A }
2N/A ch = (char)getc(fp);
2N/A if (!isdigit(ch)) {
2N/A /* escape the character */
2N/A *cp++ = (char)ch;
2N/A break;
2N/A }
2N/A oval = 0;
2N/A while (ch >= '0' && ch <= '7') {
2N/A ch -= '0';
2N/A oval = (oval << 3) + ch;
2N/A ch = (char)getc(fp);
2N/A }
2N/A (void) ungetc(ch, fp);
2N/A /* check for character overflow? */
2N/A if (oval > 127) {
2N/A file_err(filep,
2N/A "Character "
2N/A "overflow detected.\n");
2N/A }
2N/A *cp++ = (char)oval;
2N/A break;
2N/A default:
2N/A if (--remain == 0) {
2N/A *cp = '\0';
2N/A return (-1);
2N/A }
2N/A *cp++ = (char)ch;
2N/A break;
2N/A }
2N/A }
2N/A token = T_STRING;
2N/A break;
2N/A
2N/A case EOF:
2N/A token = T_EOF;
2N/A break;
2N/A
2N/A default:
2N/A /*
2N/A * detect a lone '-' (including at the end of a line), and
2N/A * identify it as a 'name'
2N/A */
2N/A if (ch == '-') {
2N/A if (--remain == 0) {
2N/A *cp = '\0';
2N/A return (-1);
2N/A }
2N/A *cp++ = (char)(ch = getc(fp));
2N/A if (ch == ' ' || ch == '\t' || ch == '\n') {
2N/A (void) ungetc(ch, fp);
2N/A remain++;
2N/A cp--;
2N/A token = T_NAME;
2N/A break;
2N/A }
2N/A } else if (ch == '~' || ch == '-') {
2N/A if (--remain == 0) {
2N/A *cp = '\0';
2N/A return (-1);
2N/A }
2N/A *cp++ = (char)(ch = getc(fp));
2N/A }
2N/A
2N/A
2N/A if (isdigit(ch)) {
2N/A if (ch == '0') {
2N/A if ((ch = getc(fp)) == 'x') {
2N/A if (--remain == 0) {
2N/A *cp = '\0';
2N/A return (-1);
2N/A }
2N/A *cp++ = (char)ch;
2N/A ch = getc(fp);
2N/A while (isxdigit(ch)) {
2N/A if (--remain == 0) {
2N/A *cp = '\0';
2N/A return (-1);
2N/A }
2N/A *cp++ = (char)ch;
2N/A ch = getc(fp);
2N/A }
2N/A (void) ungetc(ch, fp);
2N/A token = T_HEXVAL;
2N/A } else {
2N/A goto digit;
2N/A }
2N/A } else {
2N/A ch = getc(fp);
2N/Adigit:
2N/A while (isdigit(ch)) {
2N/A if (--remain == 0) {
2N/A *cp = '\0';
2N/A return (-1);
2N/A }
2N/A *cp++ = (char)ch;
2N/A ch = getc(fp);
2N/A }
2N/A (void) ungetc(ch, fp);
2N/A token = T_DECVAL;
2N/A }
2N/A } else if (isalpha(ch) || ch == '\\') {
2N/A if (ch != '\\') {
2N/A ch = getc(fp);
2N/A } else {
2N/A /*
2N/A * if the character was a backslash,
2N/A * back up so we can overwrite it with
2N/A * the next (i.e. escaped) character.
2N/A */
2N/A remain++;
2N/A cp--;
2N/A }
2N/A while (isnamechar(ch) || ch == '\\') {
2N/A if (ch == '\\')
2N/A ch = getc(fp);
2N/A if (--remain == 0) {
2N/A *cp = '\0';
2N/A return (-1);
2N/A }
2N/A *cp++ = (char)ch;
2N/A ch = getc(fp);
2N/A }
2N/A (void) ungetc(ch, fp);
2N/A token = T_NAME;
2N/A } else {
2N/A return (-1);
2N/A }
2N/A break;
2N/A }
2N/A
2N/A *cp = '\0';
2N/A
2N/A return (token);
2N/A}
2N/A
2N/A#ifdef __sparc
2N/A
2N/Astatic void
2N/Afree_confent(struct conf_entry *confent)
2N/A{
2N/A if (confent->name)
2N/A free(confent->name);
2N/A if (confent->parent)
2N/A free(confent->parent);
2N/A if (confent->class)
2N/A free(confent->class);
2N/A if (confent->unit_address)
2N/A free(confent->unit_address);
2N/A free(confent);
2N/A}
2N/A
2N/Astatic void
2N/Afree_confent_list(struct conf_entry *confent_list)
2N/A{
2N/A struct conf_entry *confent, *next;
2N/A
2N/A for (confent = confent_list; confent != NULL; confent = next) {
2N/A next = confent->next;
2N/A free_confent(confent);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Parse the next entry from the driver.conf file and return in the form of
2N/A * a pointer to the conf_entry.
2N/A */
2N/Astatic struct conf_entry *
2N/Aparse_conf_entry(struct conf_file *filep, char *tokbuf, size_t linesize)
2N/A{
2N/A char *prop_name, *string;
2N/A token_t token;
2N/A struct conf_entry *confent;
2N/A conf_state_t state;
2N/A int failed = 1;
2N/A
2N/A if ((confent = calloc(1, sizeof (*confent))) == NULL)
2N/A return (NULL);
2N/A
2N/A confent->port = -1;
2N/A confent->mpxio_disable = -1;
2N/A
2N/A state = begin;
2N/A token = T_NAME;
2N/A prop_name = NULL;
2N/A string = NULL;
2N/A do {
2N/A switch (token) {
2N/A case T_NAME:
2N/A switch (state) {
2N/A case prop_equals_string:
2N/A case prop_equals_integer:
2N/A case begin:
2N/A state = prop;
2N/A if ((prop_name = strdup(tokbuf)) == NULL)
2N/A goto bad;
2N/A break;
2N/A default:
2N/A file_err(filep, tok_err, tokbuf);
2N/A }
2N/A break;
2N/A case T_EQUALS:
2N/A switch (state) {
2N/A case prop:
2N/A state = prop_equals;
2N/A break;
2N/A default:
2N/A file_err(filep, tok_err, tokbuf);
2N/A }
2N/A break;
2N/A case T_STRING:
2N/A switch (state) {
2N/A case prop_equals:
2N/A if ((string = strdup(tokbuf)) == NULL)
2N/A goto bad;
2N/A
2N/A state = begin;
2N/A if (strcmp(prop_name, "PARENT") == 0 ||
2N/A strcmp(prop_name, "parent") == 0) {
2N/A if (confent->parent) {
2N/A file_err(filep,
2N/A "'parent' property already specified\n");
2N/A goto bad;
2N/A }
2N/A confent->parent = string;
2N/A } else if (strcmp(prop_name, "NAME") == 0 ||
2N/A strcmp(prop_name, "name") == 0) {
2N/A if (confent->name) {
2N/A file_err(filep,
2N/A "'name' property already specified\n");
2N/A goto bad;
2N/A }
2N/A confent->name = string;
2N/A } else if (strcmp(prop_name, "CLASS") == 0 ||
2N/A strcmp(prop_name, "class") == 0) {
2N/A if (confent->class) {
2N/A file_err(filep,
2N/A "'class' property already specified\n");
2N/A goto bad;
2N/A }
2N/A confent->class = string;
2N/A } else if (strcmp(prop_name, "unit-address")
2N/A == 0) {
2N/A if (confent->unit_address) {
2N/A file_err(filep,
2N/A "'unit-address' property already specified\n");
2N/A goto bad;
2N/A }
2N/A confent->unit_address = string;
2N/A } else if (strcmp(prop_name, "mpxio-disable")
2N/A == 0) {
2N/A if (confent->mpxio_disable != -1) {
2N/A file_err(filep,
2N/A "'mpxio-disable' property already specified\n");
2N/A goto bad;
2N/A }
2N/A if (strcmp(string, "yes") == 0)
2N/A confent->mpxio_disable = 1;
2N/A else if (strcmp(string, "no") == 0)
2N/A confent->mpxio_disable = 0;
2N/A else {
2N/A file_err(filep,
2N/A "'mpxio-disable' property setting is invalid. "
2N/A "The value must be either \"yes\" or \"no\"\n");
2N/A goto bad;
2N/A }
2N/A free(string);
2N/A } else {
2N/A free(string);
2N/A state = prop_equals_string;
2N/A }
2N/A string = NULL;
2N/A free(prop_name);
2N/A prop_name = NULL;
2N/A break;
2N/A
2N/A case prop_equals_string_comma:
2N/A state = prop_equals_string;
2N/A break;
2N/A default:
2N/A file_err(filep, tok_err, tokbuf);
2N/A }
2N/A break;
2N/A case T_HEXVAL:
2N/A case T_DECVAL:
2N/A switch (state) {
2N/A case prop_equals:
2N/A if (strcmp(prop_name, "port") == 0) {
2N/A if (confent->port != -1) {
2N/A file_err(filep,
2N/A "'port' property already specified\n");
2N/A goto bad;
2N/A }
2N/A confent->port =
2N/A (int)strtol(tokbuf, NULL, 0);
2N/A state = begin;
2N/A } else
2N/A state = prop_equals_integer;
2N/A free(prop_name);
2N/A prop_name = NULL;
2N/A break;
2N/A
2N/A case prop_equals_integer_comma:
2N/A state = prop_equals_integer;
2N/A break;
2N/A default:
2N/A file_err(filep, tok_err, tokbuf);
2N/A }
2N/A break;
2N/A case T_COMMA:
2N/A switch (state) {
2N/A case prop_equals_string:
2N/A state = prop_equals_string_comma;
2N/A break;
2N/A case prop_equals_integer:
2N/A state = prop_equals_integer_comma;
2N/A break;
2N/A default:
2N/A file_err(filep, tok_err, tokbuf);
2N/A }
2N/A break;
2N/A case T_NEWLINE:
2N/A filep->linenum++;
2N/A break;
2N/A case T_POUND:
2N/A find_eol(filep->fp);
2N/A break;
2N/A case T_EOF:
2N/A file_err(filep, "Unexpected EOF\n");
2N/A goto bad;
2N/A default:
2N/A file_err(filep, tok_err, tokbuf);
2N/A goto bad;
2N/A }
2N/A } while ((token = lex(filep, tokbuf, linesize)) != T_SEMICOLON);
2N/A
2N/A failed = 0;
2N/A
2N/Abad:
2N/A if (prop_name)
2N/A free(prop_name);
2N/A if (string)
2N/A free(string);
2N/A if (failed == 1) {
2N/A free_confent(confent);
2N/A return (NULL);
2N/A }
2N/A return (confent);
2N/A}
2N/A
2N/A/*
2N/A * Parse all entries with mpxio-disable property in the given driver.conf
2N/A * file.
2N/A *
2N/A * fname driver.conf file name
2N/A * confent_list on return *confent_list will contain the list of
2N/A * driver.conf file entries with mpxio-disable property.
2N/A * mpxio_disable on return *mpxio_disable is set to the setting of the
2N/A * driver global mpxio-dissable property as follows.
2N/A * 0 if driver mpxio-disable="no"
2N/A * 1 if driver mpxio-disable="yes"
2N/A * -1 if driver mpxio-disable property isn't specified.
2N/A */
2N/Astatic void
2N/Aparse_conf_file(char *fname, struct conf_entry **confent_list,
2N/A int *mpxio_disable)
2N/A{
2N/A struct conf_entry *confent, *tail = NULL;
2N/A token_t token;
2N/A struct conf_file file;
2N/A char tokval[MAX_TOKEN_SIZE];
2N/A
2N/A *confent_list = NULL;
2N/A *mpxio_disable = -1;
2N/A if ((file.fp = fopen(fname, "r")) == NULL)
2N/A return;
2N/A
2N/A file.filename = fname;
2N/A file.linenum = 1;
2N/A
2N/A while ((token = lex(&file, tokval, MAX_TOKEN_SIZE)) != T_EOF) {
2N/A switch (token) {
2N/A case T_POUND:
2N/A /*
2N/A * Skip comments.
2N/A */
2N/A find_eol(file.fp);
2N/A break;
2N/A case T_NAME:
2N/A if ((confent = parse_conf_entry(&file, tokval,
2N/A MAX_TOKEN_SIZE)) == NULL)
2N/A break;
2N/A /*
2N/A * No name indicates global property.
2N/A * Make sure parent and class not NULL.
2N/A */
2N/A if (confent->name == NULL) {
2N/A if (confent->parent ||
2N/A confent->class) {
2N/A file_err(&file,
2N/A "missing name attribute\n");
2N/A } else if (confent->mpxio_disable != -1) {
2N/A if (*mpxio_disable == -1)
2N/A *mpxio_disable =
2N/A confent->mpxio_disable;
2N/A else
2N/A file_err(&file,
2N/A "'mpxio-disable' property already specified\n");
2N/A }
2N/A free_confent(confent);
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * This is a node spec, either parent or class
2N/A * must be specified.
2N/A */
2N/A if (confent->parent == NULL && confent->class == NULL) {
2N/A file_err(&file,
2N/A "missing parent or class attribute\n");
2N/A free_confent(confent);
2N/A break;
2N/A }
2N/A
2N/A /* only need entries with mpxio_disable property */
2N/A if (confent->mpxio_disable == -1) {
2N/A free_confent(confent);
2N/A break;
2N/A }
2N/A
2N/A if (tail)
2N/A tail->next = confent;
2N/A else
2N/A *confent_list = confent;
2N/A tail = confent;
2N/A break;
2N/A
2N/A case T_NEWLINE:
2N/A file.linenum++;
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A }
2N/A
2N/A (void) fclose(file.fp);
2N/A}
2N/A
2N/A/*
2N/A * Return the driver class of the given driver_name.
2N/A * The memory for the driver class is allocated by this function and the
2N/A * caller must free it.
2N/A */
2N/Astatic char *
2N/Aget_driver_class(char *rootdir, char *driver_name)
2N/A{
2N/A FILE *fp;
2N/A char buf[BUFSIZE];
2N/A char driver[BUFSIZE];
2N/A char class_name[BUFSIZE];
2N/A
2N/A logdmsg(("get_driver_class: rootdir = %s, driver name = %s\n",
2N/A rootdir, driver_name));
2N/A
2N/A (void) snprintf(buf, sizeof (buf), "%s%s", rootdir, DRIVER_CLASSES);
2N/A
2N/A if ((fp = fopen(buf, "r")) == NULL) {
2N/A logdmsg(("get_driver_class: failed to open %s: %s\n",
2N/A buf, strerror(errno)));
2N/A return (NULL);
2N/A }
2N/A
2N/A while (fgets(buf, sizeof (buf), fp) != NULL) {
2N/A /* LINTED - unbounded string specifier */
2N/A if ((sscanf(buf, "%s %s", driver, class_name) == 2) &&
2N/A driver[0] != '#' && strcmp(driver, driver_name) == 0) {
2N/A logdmsg(("get_driver_class: driver class = %s\n",
2N/A class_name));
2N/A (void) fclose(fp);
2N/A return (strdup(class_name));
2N/A }
2N/A }
2N/A
2N/A (void) fclose(fp);
2N/A return (NULL);
2N/A}
2N/A
2N/Astatic int
2N/Alookup_in_confent_list(struct conf_entry *confent_list,
2N/A int match_class, char *parent, char *unit_addr, int port)
2N/A{
2N/A struct conf_entry *confent;
2N/A char *par;
2N/A
2N/A logdmsg(("lookup_in_confent_list: %s = \"%s\", unit_addr = \"%s\", "
2N/A "port = %d\n", (match_class) ? "class" : "parent", parent,
2N/A STRVAL(unit_addr), port));
2N/A
2N/A for (confent = confent_list; confent != NULL; confent = confent->next) {
2N/A par = (match_class) ? confent->class : confent->parent;
2N/A if (unit_addr) {
2N/A if (confent->unit_address != NULL &&
2N/A strcmp(confent->unit_address, unit_addr) == 0 &&
2N/A par != NULL && strcmp(par, parent) == 0)
2N/A return (confent->mpxio_disable);
2N/A } else {
2N/A if (confent->port == port &&
2N/A par != NULL && strcmp(par, parent) == 0)
2N/A return (confent->mpxio_disable);
2N/A }
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * lookup mpxio-disabled property setting for the given path in the given
2N/A * driver.conf file. Match the entries from most specific to least specific.
2N/A *
2N/A * conf_file the path name of either fp.conf, qlc.conf or scsi_vhci.conf
2N/A * path /devices node path without the /devices prefix.
2N/A * If the conf_file is fp.conf, path must be a fp node path
2N/A * if the conf_file is qlc.conf, path must be a qlc node path.
2N/A * if the conf_file is scsi_vhci.conf, path must be NULL.
2N/A * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0
2N/A * /pci@8,600000/SUNW,qlc@4
2N/A *
2N/A * returns:
2N/A * 0 if mpxio-disable="no"
2N/A * 1 if mpxio-disable="yes"
2N/A * -1 if mpxio-disable property isn't specified.
2N/A */
2N/Astatic int
2N/Alookup_in_conf_file(char *rootdir, char *conf_file, char *path)
2N/A{
2N/A struct conf_entry *confent_list = NULL;
2N/A int mpxio_disable;
2N/A di_node_t par_node = DI_NODE_NIL;
2N/A char *node_name = NULL, *node_addr = NULL;
2N/A char *unit_addr = NULL;
2N/A int port = -1;
2N/A char *par_node_name = NULL, *par_node_addr = NULL;
2N/A char *par_binding_name = NULL, *par_driver_name = NULL;
2N/A char *par_driver_class = NULL, *par_node_name_addr;
2N/A int rv = -1;
2N/A char buf[MAXPATHLEN];
2N/A
2N/A logdmsg(("lookup_in_conf_file: rootdir = \"%s\", conf_file = \"%s\", "
2N/A "path = \"%s\"\n", rootdir, conf_file, STRVAL(path)));
2N/A
2N/A (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, conf_file);
2N/A parse_conf_file(buf, &confent_list, &mpxio_disable);
2N/A#ifdef DEBUG
2N/A log_confent_list(buf, confent_list, mpxio_disable);
2N/A#endif
2N/A
2N/A /* if path is NULL, return driver global mpxio-disable setting */
2N/A if (path == NULL) {
2N/A rv = mpxio_disable;
2N/A goto done;
2N/A }
2N/A
2N/A if ((node_name = strrchr(path, '/')) == NULL)
2N/A goto done;
2N/A
2N/A *node_name = '\0';
2N/A node_name++;
2N/A
2N/A if ((node_addr = strchr(node_name, '@')) == NULL)
2N/A goto done;
2N/A
2N/A *node_addr = '\0';
2N/A node_addr++;
2N/A
2N/A if (strcmp(node_name, "fp") == 0) {
2N/A /* get port number; encoded in the node addr as a hex number */
2N/A port = (int)strtol(node_addr, NULL, 16);
2N/A } else
2N/A unit_addr = node_addr;
2N/A
2N/A /*
2N/A * Match from most specific to least specific;
2N/A * first, start the lookup based on full path.
2N/A */
2N/A if ((rv = lookup_in_confent_list(confent_list, 0, path,
2N/A unit_addr, port)) != -1)
2N/A goto done;
2N/A
2N/A /* lookup nodename@address */
2N/A if ((par_node_name_addr = strrchr(path, '/')) != NULL) {
2N/A par_node_name_addr++;
2N/A if ((rv = lookup_in_confent_list(confent_list, 0,
2N/A par_node_name_addr, unit_addr, port)) != -1)
2N/A goto done;
2N/A }
2N/A
2N/A /* di_init() doesn't work when 0 is passed in flags */
2N/A par_node = di_init(path, DINFOMINOR);
2N/A if (par_node != DI_NODE_NIL) {
2N/A par_node_name = di_node_name(par_node);
2N/A par_node_addr = di_bus_addr(par_node);
2N/A par_binding_name = di_binding_name(par_node);
2N/A par_driver_name = di_driver_name(par_node);
2N/A }
2N/A
2N/A logdmsg(("par_node_name = %s\n", STRVAL(par_node_name)));
2N/A logdmsg(("par_node_addr = %s\n", STRVAL(par_node_addr)));
2N/A logdmsg(("par_binding_name = %s\n", STRVAL(par_binding_name)));
2N/A logdmsg(("par_driver_name = %s\n", STRVAL(par_driver_name)));
2N/A
2N/A /* lookup bindingname@address */
2N/A if (par_binding_name != NULL && par_binding_name != par_node_name &&
2N/A par_node_addr != NULL) {
2N/A (void) snprintf(buf, sizeof (buf), "%s@%s", par_binding_name,
2N/A par_node_addr);
2N/A if ((rv = lookup_in_confent_list(confent_list, 0,
2N/A buf, unit_addr, port)) != -1)
2N/A goto done;
2N/A }
2N/A
2N/A /* lookup binding name */
2N/A if (par_binding_name != NULL) {
2N/A if ((rv = lookup_in_confent_list(confent_list, 0,
2N/A par_binding_name, unit_addr, port)) != -1)
2N/A goto done;
2N/A }
2N/A
2N/A if (par_driver_name != NULL) {
2N/A /* lookup driver name */
2N/A if ((rv = lookup_in_confent_list(confent_list, 0,
2N/A par_driver_name, unit_addr, port)) != -1)
2N/A goto done;
2N/A
2N/A /* finally, lookup class name */
2N/A par_driver_class = get_driver_class(rootdir, par_driver_name);
2N/A if (par_driver_class != NULL) {
2N/A if ((rv = lookup_in_confent_list(confent_list, 1,
2N/A par_driver_class, unit_addr, port)) != -1)
2N/A goto done;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * no match so far;
2N/A * use the driver global mpxio-disable setting if exists.
2N/A */
2N/A rv = mpxio_disable;
2N/A
2N/Adone:
2N/A if (node_name != NULL)
2N/A *(node_name - 1) = '/';
2N/A if (node_addr != NULL)
2N/A *(node_addr - 1) = '@';
2N/A if (par_driver_class != NULL)
2N/A free(par_driver_class);
2N/A if (confent_list != NULL)
2N/A free_confent_list(confent_list);
2N/A if (par_node != DI_NODE_NIL)
2N/A di_fini(par_node);
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Given client_name return whether it is a phci or vhci based name.
2N/A * client_name is /devices name of a client without the /devices prefix.
2N/A *
2N/A * client_name Return value
2N/A * .../fp@xxx/ssd@yyy CLIENT_TYPE_PHCI
2N/A * .../scsi_vhci/ssd@yyy CLIENT_TYPE_VHCI
2N/A * other CLIENT_TYPE_UNKNOWN
2N/A */
2N/Astatic client_type_t
2N/Aclient_name_type(char *client_name)
2N/A{
2N/A client_type_t client_type;
2N/A char *p1, *p2;
2N/A
2N/A logdmsg(("client_name_type: client_name = %s\n", client_name));
2N/A
2N/A if (strncmp(client_name, SLASH_SCSI_VHCI,
2N/A sizeof (SLASH_SCSI_VHCI) - 1) == 0)
2N/A return (CLIENT_TYPE_VHCI);
2N/A
2N/A if (*client_name != '/')
2N/A return (CLIENT_TYPE_UNKNOWN);
2N/A
2N/A if ((p1 = strrchr(client_name, '/')) == NULL)
2N/A return (CLIENT_TYPE_UNKNOWN);
2N/A
2N/A *p1 = '\0';
2N/A
2N/A if ((p2 = strrchr(client_name, '/')) != NULL &&
2N/A strncmp(p2, SLASH_FP_AT, sizeof (SLASH_FP_AT) - 1) == 0)
2N/A client_type = CLIENT_TYPE_PHCI;
2N/A else
2N/A client_type = CLIENT_TYPE_UNKNOWN;
2N/A
2N/A *p1 = '/';
2N/A return (client_type);
2N/A}
2N/A
2N/A/*
2N/A * Compare controller name portion of dev1 and dev2.
2N/A *
2N/A * rootdir root directory of the target environment
2N/A * dev1 can be either a /dev link or /devices name in the target
2N/A * environemnt
2N/A * dev2 /devices name of a device without the /devices prefix
2N/A *
2N/A * Returns:
2N/A * 0 if controller names match
2N/A * 1 if controller names don't match
2N/A * -1 an error occurred.
2N/A */
2N/Astatic int
2N/Acompare_controller(char *rootdir, char *dev1, char *dev2)
2N/A{
2N/A int linksize;
2N/A char *p1, *p;
2N/A char physdev1[MAXPATHLEN];
2N/A char buf[MAXPATHLEN];
2N/A
2N/A logdmsg(("compare_controller: rootdir = %s, dev1 = %s, dev2 = %s\n",
2N/A rootdir, dev1, dev2));
2N/A
2N/A if (strncmp(dev1, SLASH_DEV_SLASH, sizeof (SLASH_DEV_SLASH) - 1)
2N/A == 0) {
2N/A (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, dev1);
2N/A if ((linksize = readlink(buf, physdev1, MAXPATHLEN)) > 0 &&
2N/A linksize < (MAXPATHLEN - 1)) {
2N/A physdev1[linksize] = '\0';
2N/A logdmsg(("compare_controller: physdev1 = %s\n",
2N/A physdev1));
2N/A } else
2N/A return (-1);
2N/A } else
2N/A (void) strlcpy(physdev1, dev1, MAXPATHLEN);
2N/A
2N/A if ((p1 = strstr(physdev1, SLASH_DEVICES)) == NULL)
2N/A return (-1);
2N/A
2N/A p1 += sizeof (SLASH_DEVICES) - 1;
2N/A /* strip the device portion */
2N/A if ((p = strrchr(p1, '/')) == NULL)
2N/A return (-1);
2N/A *p = '\0';
2N/A
2N/A if ((p = strrchr(dev2, '/')) == NULL)
2N/A return (-1);
2N/A *p = '\0';
2N/A
2N/A logdmsg(("compare_controller: path1 = %s, path2 = %s\n",
2N/A p1, dev2));
2N/A if (strcmp(p1, dev2) == 0) {
2N/A *p = '/';
2N/A return (0);
2N/A } else {
2N/A *p = '/';
2N/A return (1);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Check if the specified device path is on the root controller.
2N/A *
2N/A * rootdir root directory of the target environment
2N/A * path /devices name of a device without the /devices prefix
2N/A *
2N/A * Returns
2N/A * 1 if the path is on the root controller
2N/A * 0 if the path is not on the root controller
2N/A * -1 if an error occurs
2N/A */
2N/Astatic int
2N/Ais_root_controller(char *rootdir, char *path)
2N/A{
2N/A FILE *fp;
2N/A char *tmpfile;
2N/A int rv = -1;
2N/A struct vfstab vfsent;
2N/A char buf[MAXPATHLEN];
2N/A char ctd[MAXNAMELEN + 1];
2N/A
2N/A logdmsg(("is_root_controller: rootdir = %s, path = %s\n", rootdir,
2N/A path));
2N/A
2N/A (void) snprintf(buf, MAXPATHLEN, "%s%s", rootdir, VFSTAB);
2N/A
2N/A if ((fp = fopen(buf, "r")) == NULL) {
2N/A logdmsg(("is_root_controller: failed to open %s: %s\n",
2N/A buf, strerror(errno)));
2N/A return (-1);
2N/A }
2N/A
2N/A if (getvfsfile(fp, &vfsent, "/") != 0) {
2N/A logdmsg(("is_root_controller: getvfsfile: failed to read "
2N/A "vfstab entry for mount point \"/\": %s\n",
2N/A strerror(errno)));
2N/A (void) fclose(fp);
2N/A return (-1);
2N/A }
2N/A (void) fclose(fp);
2N/A
2N/A /* check if the root is an svm metadisk */
2N/A if (strncmp(vfsent.vfs_special, META_DEV, sizeof (META_DEV) - 1) != 0) {
2N/A if (compare_controller(rootdir, vfsent.vfs_special, path) == 0)
2N/A return (1);
2N/A else
2N/A return (0);
2N/A }
2N/A
2N/A if ((tmpfile = tempnam(_PATH_SYSVOL, "diirc")) == NULL) {
2N/A logdmsg(("is_root_controller: tempnam: failed: %s\n",
2N/A strerror(errno)));
2N/A return (-1);
2N/A }
2N/A
2N/A /* get metadisk components using metastat command */
2N/A (void) snprintf(buf, MAXPATHLEN,
2N/A "/usr/sbin/metastat -p %s 2>/dev/null | "
2N/A "/usr/bin/grep ' 1 1 ' | "
2N/A "/usr/bin/sed -e 's/^.* 1 1 //' | "
2N/A "/usr/bin/cut -f1 -d ' ' > %s",
2N/A vfsent.vfs_special + sizeof (META_DEV) - 1, tmpfile);
2N/A
2N/A logdmsg(("is_root_controller: command = %s\n", buf));
2N/A fp = NULL;
2N/A if (system(buf) == 0 && (fp = fopen(tmpfile, "r")) != NULL) {
2N/A while (fscanf(fp, "%" VAL2STR(MAXNAMELEN) "s", ctd) == 1) {
2N/A (void) snprintf(buf, MAXPATHLEN, "/dev/dsk/%s", ctd);
2N/A if (compare_controller(rootdir, buf, path) == 0) {
2N/A rv = 1;
2N/A goto out;
2N/A }
2N/A }
2N/A rv = 0;
2N/A }
2N/A
2N/Aout:
2N/A if (fp)
2N/A (void) fclose(fp);
2N/A (void) unlink(tmpfile);
2N/A free(tmpfile);
2N/A return (rv);
2N/A}
2N/A
2N/Astatic int
2N/Afile_exists(char *rootdir, char *path)
2N/A{
2N/A struct stat stbuf;
2N/A char fullpath[MAXPATHLEN];
2N/A int x;
2N/A
2N/A (void) snprintf(fullpath, MAXPATHLEN, "%s%s", rootdir, path);
2N/A
2N/A x = stat(fullpath, &stbuf);
2N/A logdmsg(("file_exists: %s: %s\n", fullpath, (x == 0) ? "yes" : "no"));
2N/A if (x == 0)
2N/A return (1);
2N/A else
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Check if mpxio is enabled or disabled on the specified device path.
2N/A * Looks through the .conf files to determine the mpxio setting.
2N/A *
2N/A * rootdir root directory of the target environment
2N/A * path /devices name of a device without the /devices prefix and
2N/A * minor name component.
2N/A *
2N/A * Returns
2N/A * 1 if mpxio is disabled
2N/A * 0 if mpxio is enabled
2N/A * -1 if an error occurs
2N/A */
2N/Astatic int
2N/Ais_mpxio_disabled(char *rootdir, char *path)
2N/A{
2N/A int mpxio_disable;
2N/A char *p;
2N/A int check_root_controller;
2N/A
2N/A logdmsg(("is_mpxio_disabled: rootdir = %s, path = %s\n",
2N/A rootdir, path));
2N/A
2N/A if (file_exists(rootdir, SCSI_VHCI_CONF) == 0) {
2N/A /*
2N/A * scsi_vhci.conf doesn't exist:
2N/A * if upgrading from a pre solaris 9 release. or
2N/A * if this function is called during fresh or flash install
2N/A * prior to installing scsi_vhci.conf file.
2N/A */
2N/A if (file_exists(rootdir, "/kernel/drv"))
2N/A /* upgrading from pre solaris 9 */
2N/A return (1);
2N/A else
2N/A /* fresh or flash install */
2N/A return (0);
2N/A }
2N/A
2N/A mpxio_disable = lookup_in_conf_file(rootdir, SCSI_VHCI_CONF, NULL);
2N/A
2N/A /*
2N/A * scsi_vhci.conf contains mpxio-disable property only in s9 and
2N/A * s8+sfkpatch. This property is no longer present from s10 onwards.
2N/A */
2N/A if (mpxio_disable == 1) {
2N/A /* upgrading from s8 or s9 with mpxio globally disabled */
2N/A return (1);
2N/A } else if (mpxio_disable == 0) {
2N/A /* upgrading from s8 or s9 with mpxio globally enabled */
2N/A check_root_controller = 1;
2N/A } else {
2N/A /*
2N/A * We are looking at the s10 version of the file. This is
2N/A * the case if this function is called after installing the
2N/A * new scsi_vhci.conf file.
2N/A */
2N/A check_root_controller = 0;
2N/A }
2N/A
2N/A if ((mpxio_disable = lookup_in_conf_file(rootdir, FP_CONF, path))
2N/A != -1)
2N/A return (mpxio_disable);
2N/A
2N/A if ((p = strrchr(path, '/')) == NULL)
2N/A return (-1);
2N/A
2N/A *p = '\0';
2N/A if ((mpxio_disable = lookup_in_conf_file(rootdir, QLC_CONF, path))
2N/A != -1) {
2N/A *p = '/';
2N/A return (mpxio_disable);
2N/A }
2N/A *p = '/';
2N/A
2N/A /*
2N/A * mpxio-disable setting is not found in the .conf files.
2N/A * The default is to enable mpxio, except if the path is on the root
2N/A * controller.
2N/A *
2N/A * In s8 and s9 mpxio is not supported on the root controller.
2N/A * NWS supplies a patch to enable root controller support in s8 and s9.
2N/A * If the system had the patch installed, the fp.conf file would have
2N/A * explicit "mpxio-disable=no" for the root controller. So we would
2N/A * have found the mpxio-disable setting when we looked up this property
2N/A * in the fp.conf file.
2N/A */
2N/A if (check_root_controller) {
2N/A mpxio_disable = is_root_controller(rootdir, path);
2N/A logdmsg(("is_mpxio_disabled: is_root_controller returned %d\n",
2N/A mpxio_disable));
2N/A } else
2N/A mpxio_disable = 0;
2N/A
2N/A return (mpxio_disable);
2N/A}
2N/A
2N/Astatic int
2N/Avhci_ctl(sv_iocdata_t *iocp, int cmd)
2N/A{
2N/A int fd, rv;
2N/A
2N/A if ((fd = open(VHCI_CTL_NODE, O_RDWR)) < 0)
2N/A return (-1);
2N/A rv = ioctl(fd, cmd, iocp);
2N/A (void) close(fd);
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Convert a phci client name to vhci client name.
2N/A *
2N/A * phci_name phci client /devices name without the /devices prefix and
2N/A * minor name component.
2N/A * ex: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0
2N/A *
2N/A * Returns on success, vhci client name is returned. The memory for
2N/A * the vhci name is allocated by this function and the caller
2N/A * must free it.
2N/A * on failure, NULL is returned.
2N/A */
2N/Astatic char *
2N/Aphci_to_vhci(char *phci_name)
2N/A{
2N/A sv_iocdata_t ioc;
2N/A char *slash, *addr, *retp;
2N/A char vhci_name_buf[MAXPATHLEN];
2N/A char phci_name_buf[MAXPATHLEN];
2N/A char addr_buf[MAXNAMELEN];
2N/A
2N/A logdmsg(("phci_to_vhci: pchi_name = %s\n", phci_name));
2N/A (void) strlcpy(phci_name_buf, phci_name, MAXPATHLEN);
2N/A
2N/A if ((slash = strrchr(phci_name_buf, '/')) == NULL ||
2N/A (addr = strchr(slash, '@')) == NULL)
2N/A return (NULL);
2N/A
2N/A *slash = '\0';
2N/A addr++;
2N/A (void) strlcpy(addr_buf, addr, MAXNAMELEN);
2N/A
2N/A bzero(&ioc, sizeof (sv_iocdata_t));
2N/A ioc.client = vhci_name_buf;
2N/A ioc.phci = phci_name_buf;
2N/A ioc.addr = addr_buf;
2N/A if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_NAME) != 0) {
2N/A logdmsg(("phci_to_vhci: vhci_ctl failed: %s\n",
2N/A strerror(errno)));
2N/A return (NULL);
2N/A }
2N/A
2N/A retp = strdup(vhci_name_buf);
2N/A logdmsg(("phci_to_vhci: vhci name = %s\n", STRVAL(retp)));
2N/A return (retp);
2N/A}
2N/A
2N/Astatic int
2N/Aadd_to_phci_list(char **phci_list, sv_path_info_t *pi, int npaths, int state,
2N/A char *node_name)
2N/A{
2N/A int rv = 0;
2N/A char name[MAXPATHLEN];
2N/A
2N/A while (npaths--) {
2N/A if (state == pi->ret_state) {
2N/A (void) snprintf(name, MAXPATHLEN, "%s/%s@%s",
2N/A pi->device.ret_phci, node_name, pi->ret_addr);
2N/A if ((*phci_list = strdup(name)) == NULL)
2N/A return (-1);
2N/A phci_list++;
2N/A rv++;
2N/A }
2N/A pi++;
2N/A }
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/Astatic void
2N/Afree_pathlist(char **pathlist)
2N/A{
2N/A char **p;
2N/A
2N/A if (pathlist != NULL) {
2N/A for (p = pathlist; *p != NULL; p++)
2N/A free(*p);
2N/A free(pathlist);
2N/A }
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Convert a vhci client name to phci client names.
2N/A *
2N/A * vhci_name vhci client /devices name without the /devices prefix and
2N/A * minor name component.
2N/A * num_paths On return, *num_paths is set to the number paths in the
2N/A * returned path list.
2N/A *
2N/A * Returns NULL terminated path list containing phci client paths is
2N/A * returned on success. The memory for the path list is
2N/A * allocated by this function and the caller must free it by
2N/A * calling free_pathlist().
2N/A * NULL is returned on failure.
2N/A */
2N/Astatic char **
2N/Avhci_to_phci(char *vhci_name, int *num_paths)
2N/A{
2N/A sv_iocdata_t ioc;
2N/A uint_t npaths;
2N/A int n;
2N/A char **phci_list = NULL;
2N/A char *node_name, *at;
2N/A char vhci_name_buf[MAXPATHLEN];
2N/A
2N/A logdmsg(("vhci_to_phci: vchi_name = %s\n", vhci_name));
2N/A
2N/A *num_paths = 0;
2N/A (void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN);
2N/A
2N/A /* first get the number paths */
2N/A bzero(&ioc, sizeof (sv_iocdata_t));
2N/A ioc.client = vhci_name_buf;
2N/A ioc.ret_elem = &npaths;
2N/A if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO) != 0 ||
2N/A npaths == 0) {
2N/A logdmsg(("vhci_to_phci: vhci_ctl failed to get npaths: %s\n",
2N/A strerror(errno)));
2N/A return (NULL);
2N/A }
2N/A
2N/A /* now allocate memory for the path information and get all paths */
2N/A bzero(&ioc, sizeof (sv_iocdata_t));
2N/A ioc.client = vhci_name_buf;
2N/A ioc.buf_elem = npaths;
2N/A ioc.ret_elem = &npaths;
2N/A if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths,
2N/A sizeof (sv_path_info_t))) == NULL)
2N/A return (NULL);
2N/A if (vhci_ctl(&ioc, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO) != 0 ||
2N/A npaths == 0) {
2N/A logdmsg(("vhci_to_phci: vhci_ctl failed: %s\n",
2N/A strerror(errno)));
2N/A goto out;
2N/A }
2N/A
2N/A if (ioc.buf_elem < npaths)
2N/A npaths = ioc.buf_elem;
2N/A
2N/A if ((node_name = strrchr(vhci_name_buf, '/')) == NULL ||
2N/A (at = strchr(node_name, '@')) == NULL)
2N/A goto out;
2N/A
2N/A node_name++;
2N/A *at = '\0';
2N/A
2N/A /* allocate one more (than npaths) for the terminating NULL pointer */
2N/A if ((phci_list = calloc(npaths + 1, sizeof (char *))) == NULL)
2N/A goto out;
2N/A
2N/A /*
2N/A * add only online paths as non-online paths may not be accessible
2N/A * in the target environment.
2N/A */
2N/A if ((n = add_to_phci_list(phci_list, ioc.ret_buf, npaths,
2N/A MDI_PATHINFO_STATE_ONLINE, node_name)) <= 0)
2N/A goto out;
2N/A
2N/A free(ioc.ret_buf);
2N/A *num_paths = n;
2N/A
2N/A#ifdef DEBUG
2N/A logdmsg(("vhci_to_phci: phci list:\n"));
2N/A log_pathlist(phci_list);
2N/A#endif
2N/A return (phci_list);
2N/A
2N/Aout:
2N/A free(ioc.ret_buf);
2N/A if (phci_list)
2N/A free_pathlist(phci_list);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * build list of paths accessible from the target environment
2N/A */
2N/Astatic int
2N/Abuild_pathlist(char *rootdir, char *vhcipath, char **pathlist, int npaths)
2N/A{
2N/A int mpxio_disabled;
2N/A int i, j;
2N/A char *vpath = NULL;
2N/A
2N/A for (i = 0; i < npaths; i++) {
2N/A mpxio_disabled = is_mpxio_disabled(rootdir, pathlist[i]);
2N/A logdmsg(("build_pathlist: mpxio_disabled = %d "
2N/A "on path %s\n", mpxio_disabled, pathlist[i]));
2N/A if (mpxio_disabled == -1)
2N/A return (-1);
2N/A if (mpxio_disabled == 0) {
2N/A /*
2N/A * mpxio is enabled on this phci path.
2N/A * So use vhci path instead of phci path.
2N/A */
2N/A if (vpath == NULL) {
2N/A if ((vpath = strdup(vhcipath)) == NULL)
2N/A return (-1);
2N/A free(pathlist[i]);
2N/A /* keep vhci path at beginning of the list */
2N/A for (j = i; j > 0; j--)
2N/A pathlist[j] = pathlist[j - 1];
2N/A pathlist[0] = vpath;
2N/A } else {
2N/A free(pathlist[i]);
2N/A npaths--;
2N/A for (j = i; j < npaths; j++)
2N/A pathlist[j] = pathlist[j + 1];
2N/A pathlist[npaths] = NULL;
2N/A /* compensate for i++ in the for loop */
2N/A i--;
2N/A }
2N/A }
2N/A }
2N/A
2N/A#ifdef DEBUG
2N/A logdmsg(("build_pathlist: returning npaths = %d, pathlist:\n", npaths));
2N/A log_pathlist(pathlist);
2N/A#endif
2N/A return (npaths);
2N/A}
2N/A
2N/A/*
2N/A * Check if the specified device is refenced in the vfstab file.
2N/A * Return 1 if referenced, 0 if not.
2N/A *
2N/A * rootdir root directory of the target environment
2N/A * nodepath /devices path of a device in the target environment without
2N/A * the /devices prefix and minor component.
2N/A */
2N/Astatic int
2N/Ais_dev_in_vfstab(char *rootdir, char *nodepath)
2N/A{
2N/A FILE *fp;
2N/A int linksize;
2N/A struct vfstab vfsent;
2N/A char *abspath, *minor;
2N/A char physpath[MAXPATHLEN];
2N/A char buf[MAXPATHLEN];
2N/A
2N/A logdmsg(("is_dev_in_vfstab: rootdir = %s, nodepath = %s\n",
2N/A rootdir, nodepath));
2N/A
2N/A (void) snprintf(buf, sizeof (buf), "%s%s", rootdir, VFSTAB);
2N/A
2N/A if ((fp = fopen(buf, "r")) == NULL)
2N/A return (0);
2N/A
2N/A /*
2N/A * read device specials from vfstab and compare names at physical
2N/A * node path level.
2N/A */
2N/A while (getvfsent(fp, &vfsent) == 0) {
2N/A if (strncmp(vfsent.vfs_special, SLASH_DEV_SLASH,
2N/A sizeof (SLASH_DEV_SLASH) - 1) == 0) {
2N/A (void) snprintf(buf, MAXPATHLEN, "%s%s",
2N/A rootdir, vfsent.vfs_special);
2N/A if ((linksize = readlink(buf, physpath,
2N/A MAXPATHLEN)) > 0 && linksize < (MAXPATHLEN - 1)) {
2N/A physpath[linksize] = '\0';
2N/A if ((abspath = strstr(physpath,
2N/A SLASH_DEVICES_SLASH)) == NULL)
2N/A continue;
2N/A } else
2N/A continue;
2N/A } else if (strncmp(vfsent.vfs_special, SLASH_DEVICES_SLASH,
2N/A sizeof (SLASH_DEVICES_SLASH) - 1) == 0) {
2N/A (void) strlcpy(physpath, vfsent.vfs_special,
2N/A MAXPATHLEN);
2N/A abspath = physpath;
2N/A } else
2N/A continue;
2N/A
2N/A /* point to / after /devices */
2N/A abspath += sizeof (SLASH_DEVICES_SLASH) - 2;
2N/A /* strip minor component */
2N/A if ((minor = strrchr(abspath, ':')) != NULL)
2N/A *minor = '\0';
2N/A
2N/A if (strcmp(nodepath, abspath) == 0) {
2N/A (void) fclose(fp);
2N/A logdmsg(("is_dev_in_vfstab: returning 1\n"));
2N/A return (1);
2N/A }
2N/A }
2N/A
2N/A (void) fclose(fp);
2N/A return (0);
2N/A}
2N/A
2N/A#endif /* __sparc */
2N/A
2N/Astatic int
2N/Adevlink_callback(di_devlink_t devlink, void *argp)
2N/A{
2N/A const char *link;
2N/A
2N/A if ((link = di_devlink_path(devlink)) != NULL)
2N/A (void) strlcpy((char *)argp, link, MAXPATHLEN);
2N/A
2N/A return (DI_WALK_CONTINUE);
2N/A}
2N/A
2N/A/*
2N/A * Get the /dev name in the install environment corresponding to physpath.
2N/A *
2N/A * physpath /devices path in the install environment without the /devices
2N/A * prefix.
2N/A * buf caller supplied buffer where the /dev name is placed on return
2N/A * bufsz length of the buffer
2N/A *
2N/A * Returns strlen of the /dev name on success, -1 on failure.
2N/A */
2N/Astatic int
2N/Aget_install_devlink(char *physpath, char *buf, size_t bufsz)
2N/A{
2N/A di_devlink_handle_t devlink_hdl;
2N/A char devname[MAXPATHLEN];
2N/A int tries = 0;
2N/A int sleeptime = 2; /* number of seconds to sleep between retries */
2N/A int maxtries = 10; /* maximum number of tries */
2N/A
2N/A logdmsg(("get_install_devlink: physpath = %s\n", physpath));
2N/A
2N/A /*
2N/A * devlink_db sync happens after MINOR_FINI_TIMEOUT_DEFAULT secs
2N/A * after dev link creation. So wait for minimum that amout of time.
2N/A */
2N/A
2N/Aretry:
2N/A (void) sleep(sleeptime);
2N/A
2N/A if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
2N/A logdmsg(("get_install_devlink: di_devlink_init() failed: %s\n",
2N/A strerror(errno)));
2N/A return (-1);
2N/A }
2N/A
2N/A devname[0] = '\0';
2N/A if (di_devlink_walk(devlink_hdl, NULL, physpath, DI_PRIMARY_LINK,
2N/A devname, devlink_callback) == 0) {
2N/A if (devname[0] == '\0' && tries < maxtries) {
2N/A tries++;
2N/A (void) di_devlink_fini(&devlink_hdl);
2N/A goto retry;
2N/A } else if (devname[0] == '\0') {
2N/A logdmsg(("get_install_devlink: di_devlink_walk"
2N/A " failed: %s\n", strerror(errno)));
2N/A (void) di_devlink_fini(&devlink_hdl);
2N/A return (-1);
2N/A }
2N/A } else {
2N/A logdmsg(("get_install_devlink: di_devlink_walk failed: %s\n",
2N/A strerror(errno)));
2N/A (void) di_devlink_fini(&devlink_hdl);
2N/A return (-1);
2N/A }
2N/A
2N/A (void) di_devlink_fini(&devlink_hdl);
2N/A
2N/A logdmsg(("get_install_devlink: devlink = %s\n", devname));
2N/A return (strlcpy(buf, devname, bufsz));
2N/A}
2N/A
2N/A/*
2N/A * Get the /dev name in the target environment corresponding to physpath.
2N/A *
2N/A * rootdir root directory of the target environment
2N/A * physpath /devices path in the target environment without the /devices
2N/A * prefix.
2N/A * buf caller supplied buffer where the /dev name is placed on return
2N/A * bufsz length of the buffer
2N/A *
2N/A * Returns strlen of the /dev name on success, -1 on failure.
2N/A */
2N/Astatic int
2N/Aget_target_devlink(char *rootdir, char *physpath, char *buf, size_t bufsz)
2N/A{
2N/A char *p;
2N/A int linksize;
2N/A DIR *dirp;
2N/A struct dirent *direntry;
2N/A char dirpath[MAXPATHLEN];
2N/A char devname[MAXPATHLEN];
2N/A char physdev[MAXPATHLEN];
2N/A
2N/A logdmsg(("get_target_devlink: rootdir = %s, physpath = %s\n",
2N/A rootdir, physpath));
2N/A
2N/A if ((p = strrchr(physpath, '/')) == NULL)
2N/A return (-1);
2N/A
2N/A if (strstr(p, ",raw") != NULL) {
2N/A (void) snprintf(dirpath, MAXPATHLEN, "%s/dev/rdsk", rootdir);
2N/A } else {
2N/A (void) snprintf(dirpath, MAXPATHLEN, "%s/dev/dsk", rootdir);
2N/A }
2N/A
2N/A if ((dirp = opendir(dirpath)) == NULL)
2N/A return (-1);
2N/A
2N/A while ((direntry = readdir(dirp)) != NULL) {
2N/A if (strcmp(direntry->d_name, ".") == 0 ||
2N/A strcmp(direntry->d_name, "..") == 0)
2N/A continue;
2N/A
2N/A (void) snprintf(devname, MAXPATHLEN, "%s/%s",
2N/A dirpath, direntry->d_name);
2N/A
2N/A if ((linksize = readlink(devname, physdev, MAXPATHLEN)) > 0 &&
2N/A linksize < (MAXPATHLEN - 1)) {
2N/A physdev[linksize] = '\0';
2N/A if ((p = strstr(physdev, SLASH_DEVICES_SLASH)) !=
2N/A NULL && strcmp(p + sizeof (SLASH_DEVICES) - 1,
2N/A physpath) == 0) {
2N/A (void) closedir(dirp);
2N/A logdmsg(("get_target_devlink: devlink = %s\n",
2N/A devname + strlen(rootdir)));
2N/A return (strlcpy(buf, devname + strlen(rootdir),
2N/A bufsz));
2N/A }
2N/A }
2N/A }
2N/A
2N/A (void) closedir(dirp);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Convert device name to physpath.
2N/A *
2N/A * rootdir root directory
2N/A * devname a /dev name or /devices name under rootdir
2N/A * physpath caller supplied buffer where the /devices path will be placed
2N/A * on return (without the /devices prefix).
2N/A * physpathlen length of the physpath buffer
2N/A *
2N/A * Returns 0 on success, -1 on failure.
2N/A */
2N/Astatic int
2N/Adevname2physpath(char *rootdir, char *devname, char *physpath, int physpathlen)
2N/A{
2N/A int linksize;
2N/A char *p;
2N/A char devlink[MAXPATHLEN];
2N/A char tmpphyspath[MAXPATHLEN];
2N/A
2N/A logdmsg(("devname2physpath: rootdir = %s, devname = %s\n",
2N/A rootdir, devname));
2N/A
2N/A if (strncmp(devname, SLASH_DEVICES_SLASH,
2N/A sizeof (SLASH_DEVICES_SLASH) - 1) != 0) {
2N/A if (*rootdir == '\0')
2N/A linksize = readlink(devname, tmpphyspath, MAXPATHLEN);
2N/A else {
2N/A (void) snprintf(devlink, MAXPATHLEN, "%s%s",
2N/A rootdir, devname);
2N/A linksize = readlink(devlink, tmpphyspath, MAXPATHLEN);
2N/A }
2N/A if (linksize > 0 && linksize < (MAXPATHLEN - 1)) {
2N/A tmpphyspath[linksize] = '\0';
2N/A if ((p = strstr(tmpphyspath, SLASH_DEVICES_SLASH))
2N/A == NULL)
2N/A return (-1);
2N/A } else
2N/A return (-1);
2N/A } else
2N/A p = devname;
2N/A
2N/A (void) strlcpy(physpath, p + sizeof (SLASH_DEVICES) - 1, physpathlen);
2N/A logdmsg(("devname2physpath: physpath = %s\n", physpath));
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Map a device name (devname) from the target environment to the
2N/A * install environment.
2N/A *
2N/A * rootdir root directory of the target environment
2N/A * devname /dev or /devices name under the target environment
2N/A * buf caller supplied buffer where the mapped /dev name is placed
2N/A * on return
2N/A * bufsz length of the buffer
2N/A *
2N/A * Returns strlen of the mapped /dev name on success, -1 on failure.
2N/A */
2N/Aint
2N/Adevfs_target2install(const char *rootdir, const char *devname, char *buf,
2N/A size_t bufsz)
2N/A{
2N/A char physpath[MAXPATHLEN];
2N/A
2N/A logdmsg(("devfs_target2install: rootdir = %s, devname = %s\n",
2N/A STRVAL(rootdir), STRVAL(devname)));
2N/A
2N/A if (rootdir == NULL || devname == NULL || buf == NULL || bufsz == 0)
2N/A return (-1);
2N/A
2N/A if (strcmp(rootdir, "/") == 0)
2N/A rootdir = "";
2N/A
2N/A if (devname2physpath((char *)rootdir, (char *)devname, physpath,
2N/A MAXPATHLEN) != 0)
2N/A return (-1);
2N/A
2N/A#ifdef __sparc
2N/A if (client_name_type(physpath) == CLIENT_TYPE_PHCI) {
2N/A char *mapped_node_path, *minor;
2N/A char minorbuf[MAXNAMELEN];
2N/A
2N/A /* strip minor component if present */
2N/A if ((minor = strrchr(physpath, ':')) != NULL) {
2N/A *minor = '\0';
2N/A minor++;
2N/A (void) strlcpy(minorbuf, minor, MAXNAMELEN);
2N/A }
2N/A if ((mapped_node_path = phci_to_vhci(physpath)) != NULL) {
2N/A if (minor)
2N/A (void) snprintf(physpath, MAXPATHLEN,
2N/A "%s:%s", mapped_node_path, minorbuf);
2N/A else
2N/A (void) strlcpy(physpath, mapped_node_path,
2N/A MAXPATHLEN);
2N/A free(mapped_node_path);
2N/A logdmsg(("devfs_target2install: mapped physpath: %s\n",
2N/A physpath));
2N/A
2N/A } else if (minor)
2N/A *(minor - 1) = ':';
2N/A }
2N/A#endif /* __sparc */
2N/A
2N/A return (get_install_devlink(physpath, buf, bufsz));
2N/A}
2N/A
2N/A/*
2N/A * Map a device name (devname) from the install environment to the target
2N/A * environment.
2N/A *
2N/A * rootdir root directory of the target environment
2N/A * devname /dev or /devices name under the install environment
2N/A * buf caller supplied buffer where the mapped /dev name is placed
2N/A * on return
2N/A * bufsz length of the buffer
2N/A *
2N/A * Returns strlen of the mapped /dev name on success, -1 on failure.
2N/A */
2N/Aint
2N/Adevfs_install2target(const char *rootdir, const char *devname, char *buf,
2N/A size_t bufsz)
2N/A{
2N/A char physpath[MAXPATHLEN];
2N/A
2N/A logdmsg(("devfs_install2target: rootdir = %s, devname = %s\n",
2N/A STRVAL(rootdir), STRVAL(devname)));
2N/A
2N/A if (rootdir == NULL || devname == NULL || buf == NULL || bufsz == 0)
2N/A return (-1);
2N/A
2N/A if (strcmp(rootdir, "/") == 0)
2N/A rootdir = "";
2N/A
2N/A if (devname2physpath("", (char *)devname, physpath, MAXPATHLEN) != 0)
2N/A return (-1);
2N/A
2N/A#ifdef __sparc
2N/A if (client_name_type(physpath) == CLIENT_TYPE_VHCI) {
2N/A char **pathlist;
2N/A int npaths, i, j;
2N/A char *minor;
2N/A char minorbuf[MAXNAMELEN];
2N/A
2N/A /* strip minor component if present */
2N/A if ((minor = strrchr(physpath, ':')) != NULL) {
2N/A *minor = '\0';
2N/A minor++;
2N/A (void) strlcpy(minorbuf, minor, MAXNAMELEN);
2N/A }
2N/A
2N/A if ((pathlist = vhci_to_phci(physpath, &npaths)) == NULL)
2N/A return (-1);
2N/A
2N/A if ((npaths = build_pathlist((char *)rootdir, physpath,
2N/A pathlist, npaths)) <= 0) {
2N/A free_pathlist(pathlist);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * in case of more than one path, try to use the path
2N/A * referenced in the vfstab file, otherwise use the first path.
2N/A */
2N/A j = 0;
2N/A if (npaths > 1) {
2N/A for (i = 0; i < npaths; i++) {
2N/A if (is_dev_in_vfstab((char *)rootdir,
2N/A pathlist[i])) {
2N/A j = i;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (minor)
2N/A (void) snprintf(physpath, MAXPATHLEN,
2N/A "%s:%s", pathlist[j], minorbuf);
2N/A else
2N/A (void) strlcpy(physpath, pathlist[j], MAXPATHLEN);
2N/A free_pathlist(pathlist);
2N/A }
2N/A#endif /* __sparc */
2N/A
2N/A return (get_target_devlink((char *)rootdir, physpath, buf, bufsz));
2N/A}
2N/A
2N/A/*
2N/A * A parser for /etc/path_to_inst.
2N/A * The user-supplied callback is called once for each entry in the file.
2N/A * Returns 0 on success, ENOMEM/ENOENT/EINVAL on error.
2N/A * Callback may return DI_WALK_TERMINATE to terminate the walk,
2N/A * otherwise DI_WALK_CONTINUE.
2N/A */
2N/Aint
2N/Adevfs_parse_binding_file(const char *binding_file,
2N/A int (*callback)(void *, const char *, int,
2N/A const char *), void *cb_arg)
2N/A{
2N/A token_t token;
2N/A struct conf_file file;
2N/A char tokval[MAX_TOKEN_SIZE];
2N/A enum { STATE_RESET, STATE_DEVPATH, STATE_INSTVAL } state;
2N/A char *devpath;
2N/A char *bindname;
2N/A int instval = 0;
2N/A int rv;
2N/A
2N/A if ((devpath = calloc(1, MAXPATHLEN)) == NULL)
2N/A return (ENOMEM);
2N/A if ((bindname = calloc(1, MAX_TOKEN_SIZE)) == NULL) {
2N/A free(devpath);
2N/A return (ENOMEM);
2N/A }
2N/A
2N/A if ((file.fp = fopen(binding_file, "r")) == NULL) {
2N/A free(devpath);
2N/A free(bindname);
2N/A return (errno);
2N/A }
2N/A
2N/A file.filename = (char *)binding_file;
2N/A file.linenum = 1;
2N/A
2N/A state = STATE_RESET;
2N/A while ((token = lex(&file, tokval, MAX_TOKEN_SIZE)) != T_EOF) {
2N/A switch (token) {
2N/A case T_POUND:
2N/A /*
2N/A * Skip comments.
2N/A */
2N/A find_eol(file.fp);
2N/A break;
2N/A case T_NAME:
2N/A case T_STRING:
2N/A switch (state) {
2N/A case STATE_RESET:
2N/A if (strlcpy(devpath, tokval,
2N/A MAXPATHLEN) >= MAXPATHLEN)
2N/A goto err;
2N/A state = STATE_DEVPATH;
2N/A break;
2N/A case STATE_INSTVAL:
2N/A if (strlcpy(bindname, tokval,
2N/A MAX_TOKEN_SIZE) >= MAX_TOKEN_SIZE)
2N/A goto err;
2N/A rv = callback(cb_arg,
2N/A devpath, instval, bindname);
2N/A if (rv == DI_WALK_TERMINATE)
2N/A goto done;
2N/A if (rv != DI_WALK_CONTINUE)
2N/A goto err;
2N/A state = STATE_RESET;
2N/A break;
2N/A default:
2N/A file_err(&file, tok_err, tokval);
2N/A state = STATE_RESET;
2N/A break;
2N/A }
2N/A break;
2N/A case T_DECVAL:
2N/A case T_HEXVAL:
2N/A switch (state) {
2N/A case STATE_DEVPATH:
2N/A instval = (int)strtol(tokval, NULL, 0);
2N/A state = STATE_INSTVAL;
2N/A break;
2N/A default:
2N/A file_err(&file, tok_err, tokval);
2N/A state = STATE_RESET;
2N/A break;
2N/A }
2N/A break;
2N/A case T_NEWLINE:
2N/A file.linenum++;
2N/A state = STATE_RESET;
2N/A break;
2N/A default:
2N/A file_err(&file, tok_err, tokval);
2N/A state = STATE_RESET;
2N/A break;
2N/A }
2N/A }
2N/A
2N/Adone:
2N/A (void) fclose(file.fp);
2N/A free(devpath);
2N/A free(bindname);
2N/A return (0);
2N/A
2N/Aerr:
2N/A (void) fclose(file.fp);
2N/A free(devpath);
2N/A free(bindname);
2N/A return (EINVAL);
2N/A}
2N/A
2N/A/*
2N/A * Walk the minor nodes of all children below the specified device
2N/A * by calling the provided callback with the path to each minor.
2N/A */
2N/Astatic int
2N/Adevfs_walk_children_minors(const char *device_path, struct stat *st,
2N/A int (*callback)(void *, const char *), void *cb_arg, int *terminate)
2N/A{
2N/A DIR *dir;
2N/A struct dirent *dp;
2N/A char *minor_path = NULL;
2N/A int need_close = 0;
2N/A int rv;
2N/A
2N/A if ((minor_path = calloc(1, MAXPATHLEN)) == NULL)
2N/A return (ENOMEM);
2N/A
2N/A if ((dir = opendir(device_path)) == NULL) {
2N/A rv = ENOENT;
2N/A goto err;
2N/A }
2N/A need_close = 1;
2N/A
2N/A while ((dp = readdir(dir)) != NULL) {
2N/A if ((strcmp(dp->d_name, ".") == 0) ||
2N/A (strcmp(dp->d_name, "..") == 0))
2N/A continue;
2N/A (void) snprintf(minor_path, MAXPATHLEN,
2N/A "%s/%s", device_path, dp->d_name);
2N/A if (stat(minor_path, st) == -1)
2N/A continue;
2N/A if (S_ISDIR(st->st_mode)) {
2N/A rv = devfs_walk_children_minors(
2N/A (const char *)minor_path, st,
2N/A callback, cb_arg, terminate);
2N/A if (rv != 0)
2N/A goto err;
2N/A if (*terminate)
2N/A break;
2N/A } else {
2N/A rv = callback(cb_arg, minor_path);
2N/A if (rv == DI_WALK_TERMINATE) {
2N/A *terminate = 1;
2N/A break;
2N/A }
2N/A if (rv != DI_WALK_CONTINUE) {
2N/A rv = EINVAL;
2N/A goto err;
2N/A }
2N/A }
2N/A }
2N/A
2N/A rv = 0;
2N/Aerr:
2N/A if (need_close)
2N/A (void) closedir(dir);
2N/A if (minor_path)
2N/A free(minor_path);
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Return the path to each minor node for a device by
2N/A * calling the provided callback.
2N/A */
2N/Astatic int
2N/Adevfs_walk_device_minors(const char *device_path, struct stat *st,
2N/A int (*callback)(void *, const char *), void *cb_arg, int *terminate)
2N/A{
2N/A char *minor_path;
2N/A char *devpath;
2N/A char *expr;
2N/A regex_t regex;
2N/A int need_regfree = 0;
2N/A int need_close = 0;
2N/A DIR *dir;
2N/A struct dirent *dp;
2N/A int rv;
2N/A char *p;
2N/A
2N/A minor_path = calloc(1, MAXPATHLEN);
2N/A devpath = calloc(1, MAXPATHLEN);
2N/A expr = calloc(1, MAXNAMELEN);
2N/A if (devpath == NULL || expr == NULL || minor_path == NULL) {
2N/A rv = ENOMEM;
2N/A goto err;
2N/A }
2N/A
2N/A rv = EINVAL;
2N/A if (strlcpy(devpath, device_path, MAXPATHLEN) >= MAXPATHLEN)
2N/A goto err;
2N/A if ((p = strrchr(devpath, '/')) == NULL)
2N/A goto err;
2N/A *p++ = 0;
2N/A if (strlen(p) == 0)
2N/A goto err;
2N/A if (snprintf(expr, MAXNAMELEN, "%s:.*", p) >= MAXNAMELEN)
2N/A goto err;
2N/A if (regcomp(&regex, expr, REG_EXTENDED) != 0)
2N/A goto err;
2N/A need_regfree = 1;
2N/A
2N/A if ((dir = opendir(devpath)) == NULL) {
2N/A rv = ENOENT;
2N/A goto err;
2N/A }
2N/A need_close = 1;
2N/A
2N/A while ((dp = readdir(dir)) != NULL) {
2N/A if ((strcmp(dp->d_name, ".") == 0) ||
2N/A (strcmp(dp->d_name, "..") == 0))
2N/A continue;
2N/A (void) snprintf(minor_path, MAXPATHLEN,
2N/A "%s/%s", devpath, dp->d_name);
2N/A if (stat(minor_path, st) == -1)
2N/A continue;
2N/A if ((S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) &&
2N/A regexec(&regex, dp->d_name, 0, NULL, 0) == 0) {
2N/A rv = callback(cb_arg, minor_path);
2N/A if (rv == DI_WALK_TERMINATE) {
2N/A *terminate = 1;
2N/A break;
2N/A }
2N/A if (rv != DI_WALK_CONTINUE) {
2N/A rv = EINVAL;
2N/A goto err;
2N/A }
2N/A }
2N/A }
2N/A
2N/A rv = 0;
2N/Aerr:
2N/A if (need_close)
2N/A (void) closedir(dir);
2N/A if (need_regfree)
2N/A regfree(&regex);
2N/A if (devpath)
2N/A free(devpath);
2N/A if (minor_path)
2N/A free(minor_path);
2N/A if (expr)
2N/A free(expr);
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Perform a walk of all minor nodes for the specified device,
2N/A * and minor nodes below the device.
2N/A */
2N/Aint
2N/Adevfs_walk_minor_nodes(const char *device_path,
2N/A int (*callback)(void *, const char *), void *cb_arg)
2N/A{
2N/A struct stat stbuf;
2N/A int rv;
2N/A int terminate = 0;
2N/A
2N/A rv = devfs_walk_device_minors(device_path,
2N/A &stbuf, callback, cb_arg, &terminate);
2N/A if (rv == 0 && terminate == 0) {
2N/A rv = devfs_walk_children_minors(device_path,
2N/A &stbuf, callback, cb_arg, &terminate);
2N/A }
2N/A return (rv);
2N/A}
2N/A
2N/A#ifdef DEBUG
2N/A
2N/Astatic void
2N/Avlog_debug_msg(char *fmt, va_list ap)
2N/A{
2N/A time_t clock;
2N/A struct tm t;
2N/A
2N/A if (!devfsmap_debug)
2N/A return;
2N/A
2N/A if (logfp == NULL) {
2N/A if (*devfsmap_logfile != '\0') {
2N/A logfp = fopen(devfsmap_logfile, "a");
2N/A if (logfp)
2N/A (void) fprintf(logfp, "\nNew Log:\n");
2N/A }
2N/A
2N/A if (logfp == NULL)
2N/A logfp = stdout;
2N/A }
2N/A
2N/A clock = time(NULL);
2N/A (void) localtime_r(&clock, &t);
2N/A (void) fprintf(logfp, "%02d:%02d:%02d ", t.tm_hour, t.tm_min,
2N/A t.tm_sec);
2N/A (void) vfprintf(logfp, fmt, ap);
2N/A (void) fflush(logfp);
2N/A}
2N/A
2N/Astatic void
2N/Alog_debug_msg(char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A
2N/A va_start(ap, fmt);
2N/A vlog_debug_msg(fmt, ap);
2N/A va_end(ap);
2N/A}
2N/A
2N/A#ifdef __sparc
2N/A
2N/Astatic char *
2N/Ampxio_disable_string(int mpxio_disable)
2N/A{
2N/A if (mpxio_disable == 0)
2N/A return ("no");
2N/A else if (mpxio_disable == 1)
2N/A return ("yes");
2N/A else
2N/A return ("not specified");
2N/A}
2N/A
2N/Astatic void
2N/Alog_confent_list(char *filename, struct conf_entry *confent_list,
2N/A int global_mpxio_disable)
2N/A{
2N/A struct conf_entry *confent;
2N/A
2N/A log_debug_msg("log_confent_list: filename = %s:\n", filename);
2N/A if (global_mpxio_disable != -1)
2N/A log_debug_msg("\tdriver global mpxio_disable = \"%s\"\n\n",
2N/A mpxio_disable_string(global_mpxio_disable));
2N/A
2N/A for (confent = confent_list; confent != NULL; confent = confent->next) {
2N/A if (confent->name)
2N/A log_debug_msg("\tname = %s\n", confent->name);
2N/A if (confent->parent)
2N/A log_debug_msg("\tparent = %s\n", confent->parent);
2N/A if (confent->class)
2N/A log_debug_msg("\tclass = %s\n", confent->class);
2N/A if (confent->unit_address)
2N/A log_debug_msg("\tunit_address = %s\n",
2N/A confent->unit_address);
2N/A if (confent->port != -1)
2N/A log_debug_msg("\tport = %d\n", confent->port);
2N/A log_debug_msg("\tmpxio_disable = \"%s\"\n\n",
2N/A mpxio_disable_string(confent->mpxio_disable));
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Alog_pathlist(char **pathlist)
2N/A{
2N/A char **p;
2N/A
2N/A for (p = pathlist; *p != NULL; p++)
2N/A log_debug_msg("\t%s\n", *p);
2N/A}
2N/A
2N/A#endif /* __sparc */
2N/A
2N/A#endif /* DEBUG */