/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VHCI_CTL_NODE "/devices/scsi_vhci:devctl" #define SLASH_DEVICES "/devices/" #ifdef sparc #define DISK_NODE_NAME "ssd" #define DISK_DRV_NAME "ssd" #else /* sparc */ #define DISK_NODE_NAME "disk" #define DISK_DRV_NAME "sd" #endif #define DISK_AT_G "disk@g" #define SLASH_FP_AT "/fp@" #define SLASH_SCSI_VHCI "/scsi_vhci" #define DEV_DSK "/dev/dsk/" #define DEV_RDSK "/dev/rdsk/" #define SYS_FILENAME_LEN 256 /* * Save directory is the directory in which system files are saved. * Save directory must be under the root filesystem, as this program is * typically run before any other filesystems are mounted. */ #define SAVE_DIR "/etc/mpxio" /* fcp driver publishes this property */ #define NODE_WWN_PROP "node-wwn" /* * For SAS, we look for "sas-$drivername", eg sas-mpt, but * we strncat the driver name later once we've parsed the * args passed in from the shell. */ #define SASPROP "sas-" typedef enum { CLIENT_TYPE_UNKNOWN, CLIENT_TYPE_PHCI, CLIENT_TYPE_VHCI } client_type_t; struct devlink_cbarg { char *devlink; size_t len; }; static di_node_t devinfo_root = DI_NODE_NIL; static di_devlink_handle_t devlink_hdl = NULL; static int vhci_fd = -1; static int patch_vfstab, cap_m_option, debug; static int list_option, list_guid_mappings, list_controllernum = -1; static char *mapdev = ""; static char *map_vhciname = ""; static char *stmsboot = "stmsboot"; char *drvname = (char *)NULL; /* "fp" or "mpt" or ... */ /* "node-wwn" if drvname=fp, or "sas-$drivername" otherwise */ char *drvprop = (char *)NULL; static int parent = 0; /* for "-n" usage */ static int make_temp(char *, char *, char *, size_t); static void commit_change(char *, char *, char *, int); static int map_devname(char *, char *, size_t, int); static int update_vfstab(char *, char *); static int list_mappings(int, int); static int canopen(char *); static client_type_t client_by_props(char *path); static void list_nodes(char *drivername); static int canread(char *, char *); static void logerr(char *, ...); static void logdmsg(char *, ...); static void *s_malloc(const size_t); static char *s_strdup(const char *); static void s_strlcpy(char *, const char *, size_t); static int map_openable_vhciname(char *, char *, size_t); /* * Using an exit function not marked __NORETURN causes a warning with gcc. * To suppress the warning, use __NORETURN attribute. */ static void clean_exit(int)__NORETURN; /* * Print usage and exit. */ static void usage(char *argv0) { char *progname; progname = strrchr(argv0, '/'); if (progname != NULL) progname++; else progname = argv0; /* * -u update /etc/vfstab * -m devname * if devname is phci based name and not open-able, map it to * vhci based /devices name. * if devname is vhci based name and not open-able, map it to * phci based /devices name. * -M devname * same as -m except that /dev link is printed instead of * /devices name. * -l controller * list non-STMS to STMS device name mappings for the specific * controller * -L list non-STMS to STMS device name mappings for all controllers * -p devname * if devname is vhci based name and open-able, get the first * onlined phci based name without /devices prefix. * Used in stmsboot to update the phci based bootpath. * -D drvname * if supplied, indicates that we're going to operate on * devices attached to this driver * -n * if supplied, returns name of the node containing "fp" or * "sas-$driver", appends "sd@" or "ssd@" or "disk@". Can only * be used if -D drv is specified as well */ (void) fprintf(stderr, gettext("usage: %s -u | -m devname | " "-M devname | -l controller | -L | \n" "\t\t-p devname | -D { fp | mpt } | -n\n"), progname); exit(2); } /* * Parse command line arguments. */ static void parse_args(int argc, char *argv[]) { char opt; int n = 0; if (argc == 1) { usage(argv[0]); /*NOTREACHED*/ } while ((opt = getopt(argc, argv, "udm:M:Ll:gp:D:n")) != EOF) { switch (opt) { case 'u': patch_vfstab = 1; n++; break; case 'd': debug = 1; break; case 'm': mapdev = s_strdup(optarg); n++; break; case 'M': mapdev = s_strdup(optarg); cap_m_option = 1; n++; break; case 'L': list_option = 1; n++; break; case 'l': list_option = 1; list_controllernum = (int)atol(optarg); if (list_controllernum < 0) { logerr(gettext("controller number %d is " "invalid\n"), list_controllernum); clean_exit(1); } n++; break; case 'g': /* * private option to display non-STMS device name * to GUID mappings. */ list_guid_mappings = 1; break; case 'p': /* * map openable vhci based name to phci base name */ map_vhciname = s_strdup(optarg); n++; break; case 'D': /* * Grab the driver name we need to look for. Each * time we add support for a new SAS or FC driver * to this utility, make sure that its driver name * is checked here. */ drvname = s_malloc(sizeof (optarg) + 1); drvname = s_strdup(optarg); if (strcmp(drvname, "fp") == 0) { drvprop = s_malloc(sizeof (NODE_WWN_PROP)); (void) snprintf(drvprop, sizeof (NODE_WWN_PROP), NODE_WWN_PROP); } else if (strcmp(drvname, "mpt") == 0) { drvprop = s_malloc(sizeof (SASPROP) + sizeof (drvname) + 1); (void) snprintf(drvprop, sizeof (SASPROP) + sizeof (drvname), "%s%s", SASPROP, drvname); } else { logerr(gettext("Driver %s is not supported\n"), drvname); clean_exit(1); } break; case 'n': ++parent; n++; break; default: usage(argv[0]); /*NOTREACHED*/ } } if (n != 1) { usage(argv[0]); /*NOTREACHED*/ } } int main(int argc, char *argv[]) { char save_vfstab[SYS_FILENAME_LEN], tmp_vfstab[SYS_FILENAME_LEN]; int vfstab_updated; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if (getuid() != 0) { logerr(gettext("must be super-user to run this program\n")); clean_exit(1); } parse_args(argc, argv); (void) umask(022); /* * NOTE: The mpxio boot-up script executes this program with the * mapping (-m) option before the /usr is even mounted and when the * root filesystem is still mounted read-only. */ if (*mapdev != '\0') { char newname[MAXPATHLEN]; if (map_devname(mapdev, newname, sizeof (newname), cap_m_option) == 0) { (void) printf("%s\n", newname); clean_exit(0); } clean_exit(1); } if (*map_vhciname != '\0') { char newname[MAXPATHLEN]; if (map_openable_vhciname(map_vhciname, newname, sizeof (newname)) == 0) { (void) printf("%s\n", newname); clean_exit(0); } clean_exit(1); } if (list_option || list_guid_mappings) { if (list_mappings(list_controllernum, list_guid_mappings) == 0) clean_exit(0); clean_exit(1); } if (parent > 0) { if (strcmp(drvname, "") == 0) { usage(argv[0]); clean_exit(1); } else { list_nodes(drvname); clean_exit(0); } } /* create a directory where a copy of the system files are saved */ if (patch_vfstab) { if (mkdirp(SAVE_DIR, 0755) != 0 && errno != EEXIST) { logerr(gettext("mkdirp: failed to create %1$s: %2$s\n"), SAVE_DIR, strerror(errno)); clean_exit(1); } if (make_temp(VFSTAB, save_vfstab, tmp_vfstab, SYS_FILENAME_LEN) != 0) clean_exit(1); /* build new vfstab without modifying the existing one */ if ((vfstab_updated = update_vfstab(VFSTAB, tmp_vfstab)) == -1) { logerr(gettext("failed to update %s\n"), VFSTAB); clean_exit(1); } commit_change(VFSTAB, save_vfstab, tmp_vfstab, vfstab_updated); } clean_exit(0); /*NOTREACHED*/ } /* * Make saved and temporary filenames in SAVE_DIR. * * ex: if the filename is /etc/vfstab then the save_filename and tmp_filename * would be SAVE_DIR/vfstab and SAVE_DIR/vfstab.tmp respectively. * * Returns 0 on success, -1 on failure. */ static int make_temp(char *filename, char *save_filename, char *tmp_filename, size_t len) { char *ptr; if ((ptr = strrchr(filename, '/')) == NULL) { logdmsg("invalid file %s\n", filename); return (-1); } (void) snprintf(save_filename, len, "%s%s", SAVE_DIR, ptr); (void) snprintf(tmp_filename, len, "%s%s.tmp", SAVE_DIR, ptr); logdmsg("make_temp: %s: save = %s, temp = %s\n", filename, save_filename, tmp_filename); return (0); } /* * Commit the changes made to the system file */ static void commit_change(char *filename, char *save_filename, char *tmp_filename, int updated) { int x; if (updated) { /* save the original */ if ((x = rename(filename, save_filename)) != 0) { logerr(gettext("rename %1$s to %2$s failed: %3$s\n"), filename, save_filename, strerror(errno)); } /* now rename the new file to the actual file */ if (rename(tmp_filename, filename) != 0) { logerr(gettext("rename %1$s to %2$s failed: %3$s\n"), tmp_filename, filename, strerror(errno)); /* restore the original */ if (x == 0 && rename(save_filename, filename) != 0) { logerr( gettext("rename %1$s to %2$s failed: %3$s\n" "%4$s is a copy of the original %5$s file" "\n"), save_filename, filename, strerror(errno), save_filename, filename); } } else (void) printf(gettext("%1$s: %2$s has been updated.\n"), stmsboot, filename); } else { /* remove the temp file */ (void) unlink(tmp_filename); (void) printf(gettext("%1$s: %2$s was not modified as no " "changes were needed.\n"), stmsboot, filename); } } /* * Get the GUID of the device. * * physpath /devices name without the /devices prefix and minor name * component. * guid caller supplied buffer where the GUID will be placed on return * guid_len length of the caller supplied guid buffer. * no_delay_flag if set open the device with O_NDELAY * node di_node corresponding to physpath if already available, * otherwise pass DI_NODE_NIL. * * Returns 0 on success, -1 on failure. */ static int get_guid(char *physpath, char *guid, int guid_len, int no_delay_flag, di_node_t node) { int fd; ddi_devid_t devid; int rv = -1; char *i_guid = NULL; char physpath_raw[MAXPATHLEN]; uchar_t *wwnp; int i, n, snapshot_taken = 0; logdmsg("get_guid: physpath = %s\n", physpath); #ifdef sparc (void) snprintf(physpath_raw, MAXPATHLEN, "/devices%s:a,raw", physpath); #else (void) snprintf(physpath_raw, MAXPATHLEN, "/devices%s:c,raw", physpath); #endif *guid = '\0'; if (no_delay_flag) no_delay_flag = O_NDELAY; /* * Open the raw device * Without the O_DELAY flag, the open will fail on standby paths of * T3 if its mp_support mode is "mpxio". */ if ((fd = open(physpath_raw, O_RDONLY | no_delay_flag)) == -1) { logdmsg("get_guid: failed to open %s: %s\n", physpath_raw, strerror(errno)); return (-1); } if (devid_get(fd, &devid) == 0) { i_guid = devid_to_guid(devid); devid_free(devid); if (i_guid != NULL) { s_strlcpy(guid, i_guid, guid_len); devid_free_guid(i_guid); rv = 0; goto out; } else { logdmsg("get_guid: devid_to_guid() failed\n"); logdmsg("Unable to get a GUID for device " "%s\n", physpath_raw); } } else logdmsg("get_guid: devid_get() failed: %s\n", strerror(errno)); /* * Unless we're looking at an fp-attached device, we now * fallback to node name as the guid as this is what the * fcp driver does. A sas-attached device will have the * client-guid property set. */ if (node == DI_NODE_NIL) { if ((node = di_init(physpath, DINFOCPYALL | DINFOFORCE)) == DI_NODE_NIL) { logdmsg("get_guid: di_init on %s failed: %s\n", physpath, strerror(errno)); goto out; } snapshot_taken = 1; } /* non-fp fallout */ if (strstr(physpath, "fp") == (char *)NULL) { if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "client-guid", &guid) < 0) { logdmsg("get_guid: non-fp-attached device, " "bailing out\n"); goto out; } } if ((n = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, NODE_WWN_PROP, &wwnp)) == -1) { logdmsg("get_guid: di_prop_lookup_bytes() failed to lookup " "%s: %s\n", NODE_WWN_PROP, strerror(errno)); goto out; } if (guid_len >= ((n * 2) + 1)) { for (i = 0; i < n; i++) { (void) sprintf(guid + (i * 2), "%02x", (uint_t)(*wwnp)); wwnp++; } rv = 0; } else logerr(gettext("insufficient buffer size: need %1$d " "bytes, passed %2$d bytes\n"), (n * 2) + 1, guid_len); out: if (snapshot_taken) di_fini(node); (void) close(fd); logdmsg("get_guid: GUID = %s\n", guid); return (rv); } /* * Given client_name return whether it is a phci or vhci based name. * client_name is /devices name of a client without the /devices prefix. * * client_name Return value * on sparc: * .../fp@xxx/ssd@yyy CLIENT_TYPE_PHCI (fc) * .../LSILogic,sas@xxx/sd@yyy CLIENT_TYPE_PHCI (sas) * .../scsi_vhci/ssd@yyy CLIENT_TYPE_VHCI (fc) * .../scsi_vhci/disk@yyy CLIENT_TYPE_VHCI (sas) * other CLIENT_TYPE_UNKNOWN * on x86: * .../fp@xxx/disk@yyy CLIENT_TYPE_PHCI (fc) * .../pci1000,????@xxx/sd@yyy CLIENT_TYPE_PHCI (sas) * .../scsi_vhci/disk@yyy CLIENT_TYPE_VHCI * other CLIENT_TYPE_UNKNOWN */ static client_type_t client_name_type(char *client_name) { client_type_t client_type = CLIENT_TYPE_UNKNOWN; char *p1; char *client_path; client_path = s_strdup(client_name); logdmsg("client_name_type: client is %s\n", client_path); if (*client_name != '/') return (CLIENT_TYPE_UNKNOWN); if ((p1 = strrchr(client_name, '/')) == NULL || ((strncmp(p1, "/ssd@", sizeof ("/ssd@") - 1) != 0) && (strncmp(p1, "/sd@", sizeof ("/sd@") - 1) != 0) && (strncmp(p1, "/disk@", sizeof ("/disk@") - 1) != 0))) { logdmsg("client_name_type: p1 = %s\n", p1); return (CLIENT_TYPE_UNKNOWN); } *p1 = '\0'; /* * Courtesy of the if (..) block above, we know that any * device path we have now is either PHCI or VHCI */ client_type = client_by_props(client_path); logdmsg("client_name_type: client_type = %d\n", client_type); *p1 = '/'; return (client_type); } /* * client_by_props() is called to determine what the client type * is, based on properties in the device tree: * * drivername property type * ------------------------------------- * fp node-wwn CLIENT_TYPE_PHCI * mpt sas-mpt CLIENT_TYPE_PHCI * mpt client-guid CLIENT_TYPE_PHCI (corner case) * * Normally, the "client-guid" property only shows up for a node * if we've enumerated that node under scsi_vhci. During testing * of this function, one particular corner case was found which * requires an exception handler. */ static client_type_t client_by_props(char *path) { di_node_t clientnode = DI_NODE_NIL; di_node_t parentnode = DI_NODE_NIL; unsigned int rval = CLIENT_TYPE_UNKNOWN; uchar_t *byteprop[32]; char *charprop = NULL; char *physpath; char *parentpath; physpath = s_malloc(MAXPATHLEN); bzero(physpath, MAXPATHLEN); physpath = s_strdup(path); logdmsg("client_by_props: physpath = (%s)\n", physpath); /* easy short-circuits */ if (strstr(physpath, "scsi_vhci") != (char *)NULL) { logdmsg("client_by_props: found " "'scsi_vhci' on path (%s)\n", physpath); rval = CLIENT_TYPE_VHCI; goto out; } else if ((strstr(physpath, "ide") != (char *)NULL) || (strstr(physpath, "storage") != (char *)NULL)) { logdmsg("client_by_props: ignoring this device\n"); goto out; } parentpath = s_malloc(MAXPATHLEN); bzero(parentpath, MAXPATHLEN); (void) strncpy(parentpath, physpath, strlen(physpath) - strlen(strrchr(physpath, '/'))); if ((parentnode = di_init(parentpath, DINFOCPYALL | DINFOFORCE)) == DI_NODE_NIL) { logdmsg("client_by_props: unable to di_init(%s)\n", parentpath); goto out; } if (strstr(physpath, "fp") != (char *)NULL) { if (drvprop == (char *)NULL) { drvprop = s_malloc(strlen(NODE_WWN_PROP) + 1); } logdmsg("NODE_WWN_PROP\n"); (void) snprintf(drvprop, strlen(NODE_WWN_PROP) + 1, NODE_WWN_PROP); } else { if (drvname == (char *)NULL) { drvname = di_driver_name(parentnode); logdmsg("client_by_props: drvname = %s\n", drvname); } if (drvprop == (char *)NULL) { drvprop = s_malloc(sizeof (SASPROP) + sizeof (drvname) + 1); } (void) snprintf(drvprop, sizeof (SASPROP) + sizeof (drvname), "%s%s", SASPROP, drvname); logdmsg("parentpath: %s\nphyspath: %s\n" "length %d, strrchr: %d\n", parentpath, physpath, strlen(physpath), strlen(strrchr(physpath, '/'))); } logdmsg("client_by_props: searching for property '%s'\n", drvprop); if ((clientnode = di_init(physpath, DINFOCPYALL | DINFOFORCE)) == DI_NODE_NIL) { logdmsg("client_by_props: unable to di_init(%s)\n", physpath); /* * On x86/x64 systems, we won't be able to di_init() the * node we want in the device tree, however the parent * node will still have 'mpxio-disable' set, so we can * check for that property and make our decision on type */ if (di_prop_lookup_strings(DDI_DEV_T_ANY, parentnode, "mpxio-disable", &charprop) > -1) { rval = CLIENT_TYPE_PHCI; di_fini(parentnode); logdmsg("client_by_props: device %s is PHCI\n", physpath); } goto out; } if (di_prop_lookup_bytes(DDI_DEV_T_ANY, clientnode, drvprop, byteprop) > -1) { logdmsg("client_by_props: found prop %s on " "path %s\n", drvprop, physpath); rval = CLIENT_TYPE_PHCI; } else if (di_prop_lookup_strings(DDI_DEV_T_ANY, clientnode, "client-guid", &charprop) > -1) { /* * A corner case was seen during testing where * scsi_vhci was loaded, but not all applicable * devices were enumerated under it. That left * the phci mapping along with the "client-guid" * property. */ logdmsg("client_by_props: weird... \n"); rval = CLIENT_TYPE_PHCI; } else { logdmsg("client_by_props: unable to find " "property 'client-guid', 'mpxio-disable' " "or '%s' anywhere on path (%s)\n", drvprop, physpath); logdmsg("client_by_props: this node is unknown\n"); } di_fini(parentnode); di_fini(clientnode); out: free(physpath); return (rval); } /* * Given a phci or vhci devname which is either a /dev link or /devices name * get the corresponding physical node path (without the /devices prefix) * and minor name. * * Returns 0 on success, -1 on failure. */ static int get_physname_minor(char *devname, char *physname, int physname_len, char *minorname, int minorname_len) { int linksize; char buf[MAXPATHLEN]; char *p, *m; if (strncmp(devname, DEV_DSK, sizeof (DEV_DSK) - 1) == 0 || strncmp(devname, DEV_RDSK, sizeof (DEV_RDSK) - 1) == 0) { if ((linksize = readlink(devname, buf, MAXPATHLEN)) > 0 && linksize <= (MAXPATHLEN - 1)) { buf[linksize] = '\0'; } else return (-1); } else s_strlcpy(buf, devname, MAXPATHLEN); if ((p = strstr(buf, SLASH_DEVICES)) == NULL) return (-1); /* point to '/' after /devices */ p += sizeof (SLASH_DEVICES) - 2; if ((m = strrchr(p, ':')) == NULL) { logdmsg("get_physname_minor: no minor name component in %s\n", buf); return (-1); } *m = '\0'; m++; if (client_name_type(p) == CLIENT_TYPE_UNKNOWN) return (-1); s_strlcpy(physname, p, physname_len); s_strlcpy(minorname, m, minorname_len); logdmsg("get_physname_minor: %s: physname = %s, minor = %s\n", devname, physname, minorname); return (0); } /* * Map phci based client name to vhci based client name. * * phci_name * phci based client /devices name without the /devices prefix and * minor name component. * ex: * * (FC) * for sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 * for x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 * * (SAS) * for sparc: /pci@0,2/LSILogic,sas@1/disk@6,0 * for x86: /pci1000,3060@3/sd@0,0 * * vhci_name * Caller supplied buffer where vhci /devices name will be placed on * return (without the /devices prefix and minor name component). * ex: * * (FC) * for sparc: /scsi_vhci/ssd@g2000002037cd9f72 * for x86: /scsi_vhci/disk@g2000002037cd9f72 * * (SAS) * both: /scsi_vhci/disk@g600a0b8000254d3e00000284453ed8ac * * vhci_name_len * Length of the caller supplied vhci_name buffer. * * Returns 0 on success, -1 on failure. */ static int phci_to_vhci(char *phci_name, char *vhci_name, size_t vhci_name_len) { sv_iocdata_t ioc; char *slash, *at; char vhci_name_buf[MAXPATHLEN]; char phci_name_buf[MAXPATHLEN]; char addr_buf[MAXNAMELEN]; logdmsg("phci_to_vhci: client = %s\n", phci_name); s_strlcpy(phci_name_buf, phci_name, MAXPATHLEN); if (client_name_type(phci_name_buf) != CLIENT_TYPE_PHCI || (slash = strrchr(phci_name_buf, '/')) == NULL || ((strncmp(slash, "/ssd@", sizeof ("/ssd@") - 1) != 0) && (strncmp(slash, "/sd@", sizeof ("/sd@") - 1) != 0) && (strncmp(slash, "/disk@", sizeof ("/disk@") - 1) != 0))) { logdmsg("phci_to_vhci: %s is not of CLIENT_TYPE_PHCI\n", phci_name); return (-1); } if (vhci_fd < 0) { if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0) return (-1); } *slash = '\0'; at = strchr(slash + 1, '@'); s_strlcpy(addr_buf, at + 1, MAXNAMELEN); bzero(&ioc, sizeof (sv_iocdata_t)); ioc.client = vhci_name_buf; ioc.phci = phci_name_buf; ioc.addr = addr_buf; if (ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_NAME, &ioc) != 0) { logdmsg("SCSI_VHCI_GET_CLIENT_NAME on %s " "failed: %s\n", phci_name, strerror(errno)); return (-1); } s_strlcpy(vhci_name, vhci_name_buf, vhci_name_len); logdmsg("phci_to_vhci: %s maps to %s\n", phci_name, vhci_name); return (0); } /* * Map vhci based client name to phci based client name. * If the client has multiple paths, only one of the paths with which client * can be accessed is returned. This function does not use SCSI_VHCI ioctls * as it is called on mpxio disabled paths. * * vhci_name * vhci based client /devices name without the /devices prefix and * minor name component. * ex: * sparc: /scsi_vhci/ssd@g2000002037cd9f72 * x86: /scsi_vhci/disk@g2000002037cd9f72 * * phci_name * Caller supplied buffer where phci /devices name will be placed on * return (without the /devices prefix and minor name component). * ex: * sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 * x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 * * phci_name_len * Length of the caller supplied phci_name buffer. * * minor * The slice of the disk of interest. * * Returns 0 on success, -1 on failure. */ static int vhci_to_phci(char *vhci_name, char *phci_name, size_t phci_name_len, char *minor) { di_node_t node = DI_NODE_NIL; char *vhci_guid, *devfspath; char phci_guid[MAXPATHLEN]; char root_guid[MAXPATHLEN]; char root_phys[MAXPATHLEN]; char root_minor[MAXPATHLEN]; char root_path[MAXPATHLEN]; char *node_name; FILE *mntfp; struct mnttab mntpref, rootmnt; logdmsg("vhci_to_phci: client = %s\n", vhci_name); bzero(&mntpref, sizeof (mntpref)); mntpref.mnt_mountp = "/"; if (!(mntfp = fopen(MNTTAB, "r"))) { logdmsg("vhci_to_phci: can't open %s\n", MNTTAB); return (-1); } if (getmntany(mntfp, &rootmnt, &mntpref)) { logdmsg("vhci_to_phci: can't find / in %s\n", MNTTAB); return (-1); } (void) fclose(mntfp); if (client_name_type(vhci_name) != CLIENT_TYPE_VHCI) { logdmsg("vhci_to_phci: %s is not of CLIENT_TYPE_VHCI\n", vhci_name); return (-1); } if ((vhci_guid = strrchr(vhci_name, '@')) == NULL || *(++vhci_guid) != 'g') { logerr(gettext("couldn't get guid from %s\n"), vhci_name); return (-1); } /* point to guid */ ++vhci_guid; /* * Get devinfo snapshot and walk all ssd nodes whose parent is fp. * For each node get the guid and match it with vhci_guid. */ if (devinfo_root == DI_NODE_NIL) { logdmsg("vhci_to_phci: taking devinfo snapshot\n"); if ((devinfo_root = di_init("/", DINFOCPYALL | DINFOFORCE)) == DI_NODE_NIL) { logerr(gettext("di_init failed: %s\n"), strerror(errno)); return (-1); } logdmsg("vhci_to_phci: done taking devinfo snapshot\n"); } if (strncmp(rootmnt.mnt_special, SLASH_DEVICES, sizeof (SLASH_DEVICES)-1)) (void) snprintf(root_path, sizeof (root_path), "/devices%s", rootmnt.mnt_special); else (void) strcpy(root_path, rootmnt.mnt_special); /* * remove the /devices and minor components to call get_guid() * if we can't get the guid, drop through to the regular processing. */ if ((get_physname_minor(root_path, root_phys, sizeof (root_phys), root_minor, sizeof (root_minor)) || (get_guid(root_phys, root_guid, sizeof (root_guid), 0, node) != 0))) { logdmsg("vhci_to_phci: can't get_guid for / (%s)\n", rootmnt.mnt_special); (void) strcpy(root_guid, ""); } /* * We check the guid of the root device against the vhci guid so we * can return a preferred path. */ if ((strcmp(root_guid, vhci_guid) == 0) && (canread(root_phys, minor))) { s_strlcpy(phci_name, root_phys, phci_name_len); logdmsg("vhci_to_phci: %s maps to %s preferred path\n", vhci_name, phci_name); return (0); } /* * When we finally get a unified "sd" driver for all * architectures that Solaris runs on, we can remove this * first loop around for "ssd" */ for (node = di_drv_first_node("ssd", devinfo_root); node != DI_NODE_NIL; node = di_drv_next_node(node)) { if ((node_name = di_node_name(node)) == NULL) continue; if ((strcmp(node_name, "disk") != 0) && (strcmp(node_name, "sd") != 0) && (strcmp(node_name, "ssd") != 0)) continue; if (di_parent_node(node) == DI_NODE_NIL) continue; if ((devfspath = di_devfs_path(node)) == NULL) continue; /* * Don't set no_delay_flag to have get_guid() fail on * standby paths of T3. So we'll find the preferred paths. */ if (get_guid(devfspath, phci_guid, sizeof (phci_guid), 0, node) != 0) continue; /* * If the GUID's match, and we can read data from the path of * interest, we conclude we have the correct path to use. */ if ((strcmp(phci_guid, vhci_guid) == 0) && (canread(devfspath, minor))) { s_strlcpy(phci_name, devfspath, phci_name_len); di_devfs_path_free(devfspath); logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name, phci_name); return (0); } di_devfs_path_free(devfspath); } for (node = di_drv_first_node("sd", devinfo_root); node != DI_NODE_NIL; node = di_drv_next_node(node)) { if ((node_name = di_node_name(node)) == NULL) continue; if ((strcmp(node_name, "disk") != 0) && (strcmp(node_name, "sd") != 0) && (strcmp(node_name, "ssd") != 0)) continue; if (di_parent_node(node) == DI_NODE_NIL) continue; if ((devfspath = di_devfs_path(node)) == NULL) continue; /* * Don't set no_delay_flag to have get_guid() fail on * standby paths of T3. So we'll find the preferred paths. */ if (get_guid(devfspath, phci_guid, sizeof (phci_guid), 0, node) != 0) continue; /* * If the GUID's match, and we can read data from the path of * interest, we conclude we have the correct path to use. */ if ((strcmp(phci_guid, vhci_guid) == 0) && (canread(devfspath, minor))) { s_strlcpy(phci_name, devfspath, phci_name_len); di_devfs_path_free(devfspath); logdmsg("vhci_to_phci: %s maps to %s\n", vhci_name, phci_name); return (0); } di_devfs_path_free(devfspath); } logdmsg("vhci_to_phci: couldn't get phci name for %s\n", vhci_name); return (-1); } /* * Map vhci based client name to phci based client name. * If the client has multiple paths, only one of the paths with which client * can be accessed is returned. * This function uses SCSI_VHCI ioctls to get the phci paths * * vhci_name * vhci based client /devices name without the /devices prefix and * minor name component. * ex: * sparc: /scsi_vhci/ssd@g2000002037cd9f72 * x86: /scsi_vhci/disk@g2000002037cd9f72 * * phci_name * Caller supplied buffer where phci /devices name will be placed on * return (without the /devices prefix and minor name component). * ex: * sparc: /pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w2100002037cd9f72,0 * x86: /pci@8,600000/SUNW,qlc@4/fp@0,0/disk@w2100002037cd9f72,0 * * phci_name_len * Length of the caller supplied phci_name buffer. * * Returns 0 on success, -1 on failure. */ static int vhci_to_phci_by_ioctl(char *vhci_name, char *phci_name, size_t phci_name_len) { sv_iocdata_t ioc; uint_t npaths; char *node_name, *at; char vhci_name_buf[MAXPATHLEN]; int ret; sv_path_info_t *pi; logdmsg("vhci_to_phci_by_ioctl: client = %s\n", vhci_name); if (vhci_fd < 0) { if ((vhci_fd = open(VHCI_CTL_NODE, O_RDWR)) < 0) return (-1); } (void) strlcpy(vhci_name_buf, vhci_name, MAXPATHLEN); /* first get the number paths */ bzero(&ioc, sizeof (sv_iocdata_t)); ioc.client = vhci_name_buf; ioc.ret_elem = &npaths; if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc)) != 0 || npaths == 0) { logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s " "failed: %s\n", vhci_name, ret?strerror(errno):"got 0 paths"); return (-1); } /* now allocate memory for the path information and get all paths */ bzero(&ioc, sizeof (sv_iocdata_t)); ioc.client = vhci_name_buf; ioc.buf_elem = npaths; ioc.ret_elem = &npaths; if ((ioc.ret_buf = (sv_path_info_t *)calloc(npaths, sizeof (sv_path_info_t))) == NULL) return (-1); if ((ret = ioctl(vhci_fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, &ioc)) != 0 || npaths == 0) { logdmsg("SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO on %s " "failed: %s\n", vhci_name, ret?strerror(errno):"got 0 paths"); goto out; } if (ioc.buf_elem < npaths) npaths = ioc.buf_elem; if ((node_name = strrchr(vhci_name_buf, '/')) == NULL || (at = strchr(node_name, '@')) == NULL) goto out; node_name++; *at = '\0'; logdmsg("vhci_to_phci_by_ioctl: node_name is %s\n", node_name); #ifndef sparc /* * We need to use a libdevinfo call to get this info * in an architecturally-neutral fashion. Phase-II for sure! */ node_name = "sd"; #endif /* * return the first online paths as non-online paths may * not be accessible in the target environment. */ pi = (sv_path_info_t *)ioc.ret_buf; while (npaths--) { if (MDI_PATHINFO_STATE_ONLINE == pi->ret_state) { (void) snprintf(phci_name, phci_name_len, "%s/%s@%s", pi->device.ret_phci, node_name, pi->ret_addr); logdmsg("vhci_to_phci_by_ioctl: %s maps to %s\n", vhci_name, phci_name); free(ioc.ret_buf); return (0); } pi++; } out: logdmsg("vhci_to_phci_by_ioctl: couldn't get phci name for %s\n", vhci_name); free(ioc.ret_buf); return (-1); } /* * Map physname from phci name space to vhci name space or vice-versa * * physname * phci or vhci based client /devices name without the /devices prefix and * minor name component. * * new_physname * Caller supplied buffer where the mapped physical name is stored on * return (without the /devices prefix and minor name component). * * len * Length of the caller supplied new_physname buffer. * * minor * The slice of the disk of interest. * * Returns 0 on success, -1 on failure. */ static int map_physname(char *physname, char *new_physname, size_t len, char *minor) { int type; int rv; type = client_name_type(physname); logdmsg("map_physname: type (%d) physname = %s\n", type, physname); if (type == CLIENT_TYPE_VHCI) rv = vhci_to_phci(physname, new_physname, len, minor); else if (type == CLIENT_TYPE_PHCI) rv = phci_to_vhci(physname, new_physname, len); else rv = -1; logdmsg("map_physname: returning %d\n", rv); return (rv); } static int devlink_callback(di_devlink_t devlink, void *argptr) { const char *link; struct devlink_cbarg *argp = argptr; if ((link = di_devlink_path(devlink)) != NULL) { s_strlcpy(argp->devlink, link, argp->len); return (DI_WALK_TERMINATE); } return (DI_WALK_CONTINUE); } /* * Lookup the /dev link corresponding to physname and minorname. * * physname client /devices path without the /devices prefix and minor * name component. * minorname client minor name. * devlink caller supplied buffer where the /dev link is placed on return. * len caller supplied devlink buffer length * * Returns 0 on success, -1 on failure. */ static int lookup_devlink(char *physname, char *minorname, char *devlink, size_t len) { char buf[MAXPATHLEN]; struct devlink_cbarg arg; if (devlink_hdl == NULL) { logdmsg("lookup_devlink: taking devlink snapshot\n"); if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) { logerr(gettext("di_devlink_init failed: %s\n"), strerror(errno)); clean_exit(1); } } *devlink = '\0'; (void) snprintf(buf, MAXPATHLEN, "%s:%s", physname, minorname); arg.devlink = devlink; arg.len = len; if (di_devlink_walk(devlink_hdl, NULL, buf, DI_PRIMARY_LINK, &arg, devlink_callback) != 0) { logdmsg("lookup_devlink: di_devlink_walk on %s failed: %s\n", buf, strerror(errno)); return (-1); } if (*devlink == '\0') { logdmsg("lookup_devlink: failed to lookup devlink for %s\n", buf); return (-1); } logdmsg("lookup_devlink: /dev link for %s:%s = %s\n", physname, minorname, devlink); return (0); } /* * open infile for reading and return its file pointer in *fp_in. * open outfile for writing and return its file pointer in *fp_out. * * Returns 0 on success, -1 on failure. */ static int open_in_out_files(char *infile, char *outfile, FILE **fp_in, FILE **fp_out) { FILE *fin = NULL; FILE *fout = NULL; struct stat sbuf; if ((fin = fopen(infile, "r")) == NULL) { logerr(gettext("failed to fopen %1$s: %2$s\n"), infile, strerror(errno)); goto out; } if (fstat(fileno(fin), &sbuf) != 0) { logerr(gettext("fstat failed on %1$s: %2$s\n"), infile, strerror(errno)); goto out; } if ((fout = fopen(outfile, "w")) == NULL) { logerr(gettext("failed to fopen %1$s: %2$s\n"), outfile, strerror(errno)); goto out; } if (fchmod(fileno(fout), (sbuf.st_mode & 0777)) != 0) { logerr(gettext("failed to fchmod %1$s to 0%2$o: %3$s\n"), outfile, sbuf.st_mode & 0777, strerror(errno)); goto out; } if (fchown(fileno(fout), sbuf.st_uid, sbuf.st_gid) != 0) { logerr(gettext("failed to fchown %1$s to uid %2$d and " "gid %3$d: %4$s\n"), outfile, sbuf.st_uid, sbuf.st_gid, strerror(errno)); goto out; } *fp_in = fin; *fp_out = fout; return (0); out: if (fin != NULL) (void) fclose(fin); if (fout != NULL) (void) fclose(fout); return (-1); } /* * If the devname is a phci based name and not open-able, map it to vhci * based name. If the devname is a vhci based name and not open-able, map it * to phci based name. * * devname either a /dev link or /devices name to client device * new_devname caller supplied buffer where the mapped device name is * placed on return. * len caller supplied new_devname buffer length * devlink_flag pass 1 if requesting the /dev link to the mapped device. * pass 0 if requesting the /devices name of the mapped device. * * Returns 0 on success, -1 on failure. */ static int map_devname(char *devname, char *new_devname, size_t len, int devlink_flag) { char physname[MAXPATHLEN]; char minor[MAXNAMELEN]; char new_physname[MAXPATHLEN]; logdmsg("map_devname: checking devname %s\n", devname); if ((get_physname_minor(devname, physname, sizeof (physname), minor, sizeof (minor)) == 0) && (canopen(devname) == 0) && (map_physname(physname, new_physname, sizeof (new_physname), minor) == 0)) { logdmsg("map_devname: now looking up devlink\n"); if (devlink_flag) { if (lookup_devlink(new_physname, minor, new_devname, len) == 0) return (0); } else { (void) snprintf(new_devname, len, "/devices%s:%s", new_physname, minor); return (0); } } logdmsg("map_devname: failed to find mapping for %s\n", devname); return (-1); } /* * If the devname is a vhci based name and open-able, map it to phci * based name. * * devname either a /dev link or /devices name to client device * new_devname caller supplied buffer where the mapped device name without * /devices prefix is placed on return. * len caller supplied new_devname buffer length */ static int map_openable_vhciname(char *devname, char *new_devname, size_t len) { char physname[MAXPATHLEN]; char minor[MAXNAMELEN]; char new_physname[MAXPATHLEN]; if (get_physname_minor(devname, physname, sizeof (physname), minor, sizeof (minor)) == 0 && canopen(devname) == 1 && client_name_type(physname) == CLIENT_TYPE_VHCI && vhci_to_phci_by_ioctl(physname, new_physname, sizeof (new_physname)) == 0) { (void) snprintf(new_devname, len, "%s:%s", new_physname, minor); return (0); } return (-1); } /* * Make a new /etc/vfstab: * Read vfstab_in, convert the device name entries to appropriate vhci or phci * based names, and write to vfstab_out. Only device names whose physical * paths are either phci or vhci based names and not open-able are considered * for conversion. Open-able device name entries are not converted as it * means that the device is already accessible; hence no need to convert. * * Returns: * 0 successful but vfstab_out contents are the same as vfstab_in * 1 successful and vfstab_out changed from vfstab_in * -1 failed */ static int update_vfstab(char *vfstab_in, char *vfstab_out) { FILE *fp_in, *fp_out; char *buf, *tmpbuf; char *vfs_cache[2]; int idx = 0, count = 0; int rv = -1; int vfstab_updated = 0; int i; char cdev[MAXPATHLEN]; char bdev[MAXPATHLEN]; char mntpt[MAXPATHLEN]; char fstype[512]; char fsckpass[512]; char mntboot[512]; char mntopt[MAX_MNTOPT_STR]; char phys_bdev[MAXPATHLEN], phys_cdev[MAXPATHLEN]; char bdev_minor[MAXNAMELEN], cdev_minor[MAXNAMELEN]; char new_physname[MAXPATHLEN]; char new_bdevlink[MAXPATHLEN], new_cdevlink[MAXPATHLEN]; char fmt[80]; if (open_in_out_files(vfstab_in, vfstab_out, &fp_in, &fp_out) != 0) return (-1); /* * Read one line at time from vfstab_in. If no conversion is needed * for the line simply write the line to vfstab_out. If conversion is * needed, first write the existing line as a comment to vfstab_out * and then write the converted line. * * To avoid commented entries piling up in vfstab in case if the * user runs stmsboot multiple times to switch on and off from mpxio, * add the commented line only if not already there. To do this * cache the last two vfstab lines processed and add the commented * entry only if it is not found in the cache. We only need to cache * the last two lines because a device can have at most two names - * one mpxio and one non-mpxio name. Therefore for any device name * entry we at most add two comments - one with mpxio name and one * with non-mpxio name - no matter how many times stmsboot is run. */ buf = (char *)s_malloc(VFS_LINE_MAX); tmpbuf = (char *)s_malloc(VFS_LINE_MAX); vfs_cache[0] = (char *)s_malloc(VFS_LINE_MAX); vfs_cache[1] = (char *)s_malloc(VFS_LINE_MAX); (void) snprintf(fmt, sizeof (fmt), "%%%ds %%%ds %%%ds %%%ds %%%ds %%%ds %%%ds", sizeof (bdev) - 1, sizeof (cdev) - 1, sizeof (mntpt) - 1, sizeof (fstype) - 1, sizeof (fsckpass) - 1, sizeof (mntboot) - 1, sizeof (mntopt) - 1); while (fgets(buf, VFS_LINE_MAX, fp_in) != NULL) { if (strlen(buf) == (VFS_LINE_MAX - 1) && buf[VFS_LINE_MAX-2] != '\n') { logerr(gettext("%1$s line size too long, " "exceeded %2$d: \"%3$s\"\n"), VFSTAB, VFS_LINE_MAX - 2, buf); goto out; } /* LINTED - format specifier */ if ((sscanf(buf, fmt, bdev, cdev, mntpt, fstype, fsckpass, mntboot, mntopt) != 7) || (bdev[0] == '#') || (get_physname_minor(bdev, phys_bdev, sizeof (phys_bdev), bdev_minor, sizeof (bdev_minor)) != 0) || (strcmp(fstype, "swap") != 0 && ((get_physname_minor(cdev, phys_cdev, sizeof (phys_cdev), cdev_minor, sizeof (cdev_minor)) != 0) || (strcmp(phys_bdev, phys_cdev) != 0))) || canopen(bdev) || (map_physname(phys_bdev, new_physname, sizeof (new_physname), bdev_minor) != 0) || (lookup_devlink(new_physname, bdev_minor, new_bdevlink, sizeof (new_bdevlink)) != 0) || (strcmp(fstype, "swap") != 0 && (lookup_devlink(new_physname, cdev_minor, new_cdevlink, sizeof (new_cdevlink)) != 0))) { /* cache the last two entries */ (void) strlcpy(vfs_cache[idx], buf, VFS_LINE_MAX); idx = (idx == 0) ? 1 : 0; if (count < 2) count++; if (fputs(buf, fp_out) == EOF) { logerr(gettext("fputs \"%1$s\" to %2$s " "failed: %3$s\n"), buf, vfstab_out, strerror(errno)); goto out; } } else { /* * comment the entry in vfstab only if it is not * already in the cache. */ if (client_name_type(phys_bdev) == CLIENT_TYPE_VHCI) (void) snprintf(tmpbuf, VFS_LINE_MAX, "# mpxio: %s", buf); else (void) snprintf(tmpbuf, VFS_LINE_MAX, "# non-mpxio: %s", buf); for (i = 0; i < count; i++) { if (strcmp(vfs_cache[i], tmpbuf) == 0) break; } if (i == count) { if (fputs(tmpbuf, fp_out) == EOF) { logerr(gettext("fputs \"%1$s\" to %2$s " "failed: %3$s\n"), tmpbuf, vfstab_out, strerror(errno)); goto out; } } count = 0; idx = 0; if (fprintf(fp_out, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", new_bdevlink, (strcmp(fstype, "swap") != 0) ? new_cdevlink : cdev, mntpt, fstype, fsckpass, mntboot, mntopt) < 0) { logerr(gettext("fprintf failed to write to " "%1$s: %2$s\n"), vfstab_out, strerror(errno)); goto out; } vfstab_updated = 1; } } rv = vfstab_updated; out: (void) fclose(fp_in); (void) fclose(fp_out); free(buf); free(tmpbuf); free(vfs_cache[0]); free(vfs_cache[1]); return (rv); } /* * if guidmap is 0, list non-STMS to STMS device name mappings for the * specified controller. * if guidmap is 1, list non-STMS to GUID mappings for the specified controller. * If controller is -1 list mappings for all controllers. * * Returns 0 on success, -1 on failure. */ static int list_mappings(int controller, int guidmap) { int cnum, len, mapped; int header = 1; char *p1, *p2; DIR *dirp; struct dirent *direntry; char devname[MAXPATHLEN]; char physname[MAXPATHLEN]; char new_devname[MAXPATHLEN]; char new_physname[MAXPATHLEN]; char guid[MAXPATHLEN]; char minor[MAXNAMELEN]; if ((dirp = opendir("/dev/rdsk")) == NULL) return (-1); while ((direntry = readdir(dirp)) != NULL) { if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0 || (len = strlen(direntry->d_name)) < 2 || strcmp(direntry->d_name + len - 2, "s0") != 0 || sscanf(direntry->d_name, "c%dt", &cnum) != 1 || (controller != -1 && controller != cnum)) continue; (void) snprintf(devname, MAXPATHLEN, "/dev/rdsk/%s", direntry->d_name); if (get_physname_minor(devname, physname, sizeof (physname), minor, sizeof (minor)) != 0 || client_name_type(physname) != CLIENT_TYPE_PHCI) { logdmsg("list_mappings: continuing\n"); continue; } /* * First try phci_to_vhci() mapping. It will work if the * device is under MPxIO control. If the device is not under * MPxIO, phci_to_vhci() will fail in which case try to lookup * if an old mapping exists using guid lookup. */ mapped = 1; if (phci_to_vhci(physname, new_physname, sizeof (new_physname)) != 0) { if (get_guid(physname, guid, sizeof (guid), 1, DI_NODE_NIL) == 0) (void) snprintf(new_physname, MAXPATHLEN, "/scsi_vhci/%s%s", DISK_AT_G, guid); else mapped = 0; } if (mapped == 0) continue; /* strip the slice number part */ devname[strlen(devname) - 2] = '\0'; if (guidmap == 0) { if (lookup_devlink(new_physname, minor, new_devname, sizeof (new_devname)) != 0) continue; /* strip the slice number part */ new_devname[strlen(new_devname) - 2] = '\0'; if (header) { (void) printf( gettext("non-STMS device name\t\t\t" "STMS device name\n" "------------------------------------------" "------------------------\n")); header = 0; } (void) printf("%s\t\t%s\n", devname, new_devname); } else { /* extract guid part */ /* we should be using a getguid() call instead */ if ((p1 = strstr(new_physname, "@")) == NULL) { logdmsg("invalid vhci: %s\n", new_physname); continue; } logdmsg("\tp1 = %s\n", p1); p1 += 2; /* "@" + [nwg] */ if ((p2 = strrchr(p1, ':')) != NULL) *p2 = '\0'; if (header) { (void) printf( gettext("non-STMS device name\t\t\tGUID\n" "------------------------------------------" "------------------------\n")); header = 0; } (void) printf("%s\t\t%s\n", devname, p1); } } (void) closedir(dirp); return (0); } /* * Check if the file can be opened. * * Return 1 if the file can be opened, 0 otherwise. */ static int canopen(char *filename) { int fd; if ((fd = open(filename, O_RDONLY)) == -1) return (0); logdmsg("canopen: was able to open %s\n", filename); (void) close(fd); return (1); } /* * This function traverses the device tree looking for nodes * which have "drivername" as a property. We return a list of * these nodes, without duplicate entries. * Since there can be many different pci/pcie devices that all * share the same driver but which have different pci vid/did * combinations, we have to be smart about returning only those * pci vid/dids which have the "sas-*" property unless the * drivername is "fp", in which case we're searching for "node-wwn" */ static void list_nodes(char *drivername) { di_node_t devroot = DI_NODE_NIL; di_node_t thisnode = DI_NODE_NIL; char *aliaslist; char *iitype = NULL; /* the "initiator-interconnect-type" property */ int *intprop = NULL; int i = 1; /* fencepost */ int irval = 0; int crval = 0; /* * Since the "fp" driver enumerates with its own name, * we can special-case its handling. */ if (strcmp(drvname, "fp") == 0) { (void) fprintf(stdout, "fp\n"); } else { if ((devroot = di_init("/", DINFOCPYALL | DINFOFORCE)) == DI_NODE_NIL) { logerr(gettext("list_nodes: di_init failed: " "%s\n"), strerror(errno)); } if ((thisnode = di_drv_first_node(drivername, devroot)) != NULL) { logdmsg("list_nodes: searching for property " "%s\n", drvprop); aliaslist = s_malloc(1024 * sizeof (char)); bzero(aliaslist, 1024); while (thisnode != DI_NODE_NIL) { logdmsg("devfs-name %s driver-name %s " "node-name %s\n", di_devfs_path(thisnode), di_driver_name(thisnode), di_node_name(thisnode)); /* We check the child node for drvprop */ irval = di_prop_lookup_ints(DDI_DEV_T_ANY, di_child_node(thisnode), drvprop, &intprop); /* and this node for the correct initiator type */ crval = di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode, "initiator-interconnect-type", &iitype); /* * examine the return codes from di_prop_lookup*() * functions to guard against library errors */ if ((irval > -1) || ((crval > -1) && (strncmp(iitype, "SATA", 4) == 0))) { if (strstr(aliaslist, di_node_name(thisnode)) == (char *)NULL) { char *nname; nname = di_node_name(thisnode); if (i) { (void) snprintf(aliaslist, strlen(nname) + 1, "%s", nname); --i; } else { if (strstr(aliaslist, di_node_name(thisnode)) == (char *)NULL) { /* add 2 for the n-1 + "|" */ (void) snprintf(aliaslist, strlen(nname) + 2 + strlen(aliaslist), "%s|%s", aliaslist, nname); } } } } else { logdmsg("unable to lookup property %s " "for node %s. Error %d: %s\n", drvprop, di_devfs_path(thisnode), errno, strerror(errno)); } thisnode = di_drv_next_node(thisnode); } (void) fprintf(stdout, "%s\n", aliaslist); } di_fini(devroot); } } static void logerr(char *msg, ...) { va_list ap; (void) fprintf(stderr, "%s: ", stmsboot); va_start(ap, msg); /* LINTED - format specifier */ (void) vfprintf(stderr, msg, ap); va_end(ap); } /* log debug message */ static void logdmsg(char *msg, ...) { va_list ap; if (debug) { va_start(ap, msg); /* LINTED - format specifier */ (void) vprintf(msg, ap); va_end(ap); } } static void * s_malloc(const size_t size) { void *rp; if ((rp = malloc(size)) == NULL) { logerr(gettext("malloc failed to allocate %d bytes\n"), size); clean_exit(1); } return (rp); } static char * s_strdup(const char *ptr) { void *rp; if ((rp = strdup(ptr)) == NULL) { logerr(gettext("strdup failed to dup %s\n"), ptr); clean_exit(1); } return (rp); } static void s_strlcpy(char *dst, const char *src, size_t dstsize) { int n; if ((n = strlcpy(dst, src, dstsize)) >= dstsize) { logerr(gettext("strlcpy: destination buffer size is %1$d " "bytes, need to at least %2$d bytes\n"), dstsize, n + 1); clean_exit(1); } } static void clean_exit(int status) { if (devinfo_root != DI_NODE_NIL) di_fini(devinfo_root); if (devlink_hdl != NULL) (void) di_devlink_fini(&devlink_hdl); if (vhci_fd != -1) (void) close(vhci_fd); exit(status); } /* * Attempt to read some data from the specified slice from the device. */ static int canread(char *physname, char *minor) { char devname[MAXPATHLEN]; int fd, rv = 0; char tbuf[512]; (void) snprintf(devname, MAXPATHLEN, "/devices%s:%s", physname, minor); if ((fd = open(devname, O_RDONLY)) == -1) { logdmsg("canread: failed to open %s: %s\n", devname, strerror(errno)); return (rv); } if (read(fd, tbuf, sizeof (tbuf)) < 0) logdmsg("canread: failed to read %s: %s\n", devname, strerror(errno)); else rv = 1; (void) close(fd); return (rv); }