/* * 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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2016 Nexenta Systems, Inc. */ #include #include /* * Dependent on types.h, but not including it... */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dsr.h" #include "statcommon.h" /* where we get kstat name translation information from */ static di_node_t di_root; /* from di_init: for devid */ static di_dim_t di_dim; /* from di_dim_init: for /dev names */ static int scsi_vhci_fd = -1; /* from scsi_vhci: for mpxio path */ /* disk/tape/misc info */ typedef struct { char *minor_name; int minor_isdisk; } minor_match_t; static minor_match_t mm_disk = {"a", 1}; static minor_match_t mm_tape = {"", 0}; static minor_match_t mm_misc = {"0", 0}; static minor_match_t *mma_disk_tape_misc[] = {&mm_disk, &mm_tape, &mm_misc, NULL}; #define DISKLIST_MOD 256 /* ^2 instance mod hash */ static disk_list_t *disklist[DISKLIST_MOD]; /* nfs info */ extern kstat_ctl_t *kc; extern mnt_t *nfs; static int nfs_tried; static char *get_nfs_by_minor(uint_t); static char *cur_hostname(uint_t, kstat_ctl_t *); static char *cur_special(char *, char *); /* * Clear the snapshot so a cache miss in lookup_ks_name() will cause a fresh * snapshot in drvinstpart2dev(). */ void cleanup_iodevs_snapshot() { if (di_dim) { di_dim_fini(di_dim); di_dim = NULL; } if (di_root) { di_fini(di_root); di_root = DI_NODE_NIL; } nfs_tried = 0; } /* * Find information for (driver, instance) device: return zero on failure. * * NOTE: Failure of drvinstpart2dev works out OK for the caller if the kstat * name is the same as public name: the caller will just use kstat name. */ static int drvinstpart2dev(char *driver, int instance, char *part, char **devpathp, char **adevpathp, char **devidp) { minor_match_t *mm = NULL, **mma = mma_disk_tape_misc; char *devpath = NULL; char *devid; char *devicespath; di_node_t node; /* setup "no result" return values */ if (devpathp != NULL) *devpathp = NULL; if (adevpathp != NULL) *adevpathp = NULL; if (devidp != NULL) *devidp = NULL; /* take snapshot if not established */ if (di_dim == NULL) { di_dim = di_dim_init(); if (di_dim == NULL) return (0); } if (part != NULL) { devpath = di_dim_path_dev(di_dim, driver, instance, part); } else { /* Try to find a minor_match that works */ for (mm = *mma++; mm != NULL; mm = *mma++) { if ((devpath = di_dim_path_dev(di_dim, driver, instance, mm->minor_name)) != NULL) break; } } if (devpath == NULL) return (0); /* * At this point we have a devpath result. Return the information about * the result that the caller is asking for. */ if (devpathp != NULL) /* devpath */ *devpathp = safe_strdup(devpath); if (adevpathp != NULL) { /* abbreviated devpath */ char *a; a = strrchr(devpath, '/'); if (a == NULL) { free(devpath); return (0); } a++; if (part == NULL && mm->minor_isdisk) { /* * For disk kstats without a partition we return the * last component with trailing "s#" or "p#" stripped * off (i.e. partition/slice information is removed). * For example for devpath of "/dev/dsk/c0t0d0s0" the * abbreviated devpath would be "c0t0d0". */ char *s; if ((s = strrchr(a, 's')) == NULL && (s = strrchr(a, 'p')) == NULL) { free(devpath); return (0); } /* don't include slice information in devpath */ *s = '\0'; } *adevpathp = safe_strdup(a); } if (devidp != NULL) { /* lookup the devid */ /* take snapshot if not established */ if (di_root == DI_NODE_NIL) di_root = di_init("/", DINFOCACHE); if (di_root != NULL) { /* get path to /devices devinfo node */ devicespath = di_dim_path_devices(di_dim, driver, instance, NULL); if (devicespath) { /* find the node in the snapshot */ node = di_lookup_node(di_root, devicespath); free(devicespath); /* and lookup devid property on the node */ if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, DEVID_PROP_NAME, &devid) != -1) *devidp = devid; } } } free(devpath); return (1); /* success */ } /* * Do to 'target-port' translation */ static int drvpid2port(uint_t pid, char **target_portp) { sv_iocdata_t ioc; char target_port[MAXNAMELEN]; /* setup "no result" return values */ *target_portp = NULL; /* open scsi_vhci if not already done */ if (scsi_vhci_fd == -1) { scsi_vhci_fd = open("/devices/scsi_vhci:devctl", O_RDONLY); if (scsi_vhci_fd == -1) return (0); /* failure */ } /* * Perform ioctl for -> 'target-port' translation. * * NOTE: it is legimite for this ioctl to fail for transports * that use mpxio, but don't set a 'target-port' pathinfo property. * On failure we return the the "" as the target port string. */ bzero(&ioc, sizeof (sv_iocdata_t)); ioc.buf_elem = pid; ioc.addr = target_port; if (ioctl(scsi_vhci_fd, SCSI_VHCI_GET_TARGET_LONGNAME, &ioc) < 0) { (void) snprintf(target_port, sizeof (target_port), "%d", pid); } *target_portp = safe_strdup(target_port); return (1); /* success */ } /* * Find/create a disk_list entry for the given kstat name. * The basic format of a kstat name is * * "[..][,]". * * The is a decimal number. The "..", * which describes mpxio path stat information, and "," parts are * optional. The consists of the letter 't' followed by a decimal number. * When available, we use the to find the 'target-port' via ioctls to * the scsi_vhci driver. */ disk_list_t * lookup_ks_name(char *ks_name, int want_devid) { char *pidp; /* ".... */ char *part; /* ",partition... */ char *initiator; /* ".... */ char *p; int len; char driver[KSTAT_STRLEN]; int instance; disk_list_t **dlhp; /* disklist head */ disk_list_t *entry; char *devpath = NULL; char *adevpath = NULL; char *devid = NULL; int pid; char *target_port = NULL; char portform[MAXPATHLEN]; /* Filter out illegal forms (like all digits) */ if (ks_name == NULL || *ks_name == '\0' || strspn(ks_name, "0123456789") == strlen(ks_name)) goto fail; /* parse ks_name to create new entry */ pidp = strchr(ks_name, '.'); /* start of "." */ initiator = strrchr(ks_name, '.'); /* start of "." */ if (pidp != NULL && pidp == initiator) /* can't have same start */ goto fail; part = strchr(ks_name, ','); /* start of "," */ p = strchr(ks_name, ':'); /* start of ":" */ if (part != NULL && p != NULL) goto fail; /* can't have both */ if (p != NULL) part = p; if (part != NULL && pidp != NULL) goto fail; /* and partition: bad */ p = (part != NULL) ? part : pidp; if (p == NULL) p = &ks_name[strlen(ks_name) - 1]; /* last char */ else p--; /* before ',' or '.' */ while ((p >= ks_name) && isdigit(*p)) p--; /* backwards over digits */ p++; /* start of instance */ if ((*p == '\0') || (*p == ',') || (*p == '.') || (*p == ':')) goto fail; /* no */ len = p - ks_name; (void) strncpy(driver, ks_name, len); driver[len] = '\0'; instance = atoi(p); if (part != NULL) part++; /* skip ',' */ /* hash by instance and search for existing entry */ dlhp = &disklist[instance & (DISKLIST_MOD - 1)]; for (entry = *dlhp; entry; entry = entry->next) { if (strcmp(entry->ks_name, ks_name) == 0) return (entry); } /* not found, translate kstat_name components and create new entry */ /* translate kstat_name dev information */ if (drvinstpart2dev(driver, instance, part, &devpath, &adevpath, want_devid ? &devid : NULL) == 0) goto fail; /* parse and translate path information */ if (pidp != NULL) { /* parse path information: ".t#." */ pidp++; /* skip '.' */ initiator++; /* skip '.' */ if (*pidp != 't' || !isdigit(pidp[1])) goto fail; /* not ".t#" */ pid = atoi(&pidp[1]); /* translate to 'target-port' */ if (drvpid2port(pid, &target_port) == 0) goto fail; /* Establish 'target-port' form. */ (void) snprintf(portform, sizeof (portform), "%s.t%s.%s", adevpath, target_port, initiator); free(target_port); free(adevpath); adevpath = strdup(portform); } /* make a new entry ... */ entry = safe_alloc(sizeof (disk_list_t)); entry->ks_name = safe_strdup(ks_name); entry->dname = devpath; entry->dsk = adevpath; entry->devidstr = devid; #ifdef DEBUG (void) printf("lookup_ks_name: new: %s %s\n", ks_name, entry->dsk ? entry->dsk : "NULL"); #endif /* DEBUG */ /* add new entry to head of hashed list */ entry->next = *dlhp; *dlhp = entry; return (entry); fail: free(devpath); free(adevpath); free(devid); #ifdef DEBUG (void) printf("lookup_ks_name: failed: %s\n", ks_name); #endif /* DEBUG */ return (NULL); } char * lookup_nfs_name(char *ks, kstat_ctl_t *kc) { uint_t minor; char *host, *path; char *cp; char *rstr = 0; size_t len; if (sscanf(ks, "nfs%u", &minor) == 1) { retry: cp = get_nfs_by_minor(minor); if (cp) { if (strchr(cp, ',') == NULL) { rstr = safe_strdup(cp); return (rstr); } host = cur_hostname(minor, kc); if (host) { if (*host) { path = cur_special(host, cp); if (path) { len = strlen(host); len += strlen(path); len += 2; rstr = safe_alloc(len); (void) snprintf(rstr, len, "%s:%s", host, path); } else { rstr = safe_strdup(cp); } } else { rstr = safe_strdup(ks); } free(host); } else { rstr = safe_strdup(cp); } } else if (nfs_tried == 0) { nfs_tried = 1; do_mnttab(); goto retry; } } return (rstr); } static char * get_nfs_by_minor(uint_t minor) { mnt_t *localnfs; localnfs = nfs; while (localnfs) { if (localnfs->minor == minor) { return (localnfs->device_name); } localnfs = localnfs->next; } return (0); } /* * Read the cur_hostname from the mntinfo kstat */ static char * cur_hostname(uint_t minor, kstat_ctl_t *kc) { kstat_t *ksp; static struct mntinfo_kstat mik; char *rstr; for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { if (ksp->ks_type != KSTAT_TYPE_RAW) continue; if (ksp->ks_instance != minor) continue; if (strcmp(ksp->ks_module, "nfs")) continue; if (strcmp(ksp->ks_name, "mntinfo")) continue; if (ksp->ks_flags & KSTAT_FLAG_INVALID) return (NULL); if (kstat_read(kc, ksp, &mik) == -1) return (NULL); rstr = safe_strdup(mik.mik_curserver); return (rstr); } return (NULL); } /* * Given the hostname of the mounted server, extract the server * mount point from the mnttab string. * * Common forms: * server1,server2,server3:/path * server1:/path,server2:/path * or a hybrid of the two */ static char * cur_special(char *hostname, char *special) { char *cp; char *path; size_t hlen = strlen(hostname); /* * find hostname in string */ again: if ((cp = strstr(special, hostname)) == NULL) return (NULL); /* * hostname must be followed by ',' or ':' */ if (cp[hlen] != ',' && cp[hlen] != ':') { special = &cp[hlen]; goto again; } /* * If hostname is followed by a ',' eat all characters until a ':' */ cp = &cp[hlen]; if (*cp == ',') { cp++; while (*cp != ':') { if (*cp == '\0') return (NULL); cp++; } } path = ++cp; /* skip ':' */ /* * path is terminated by either 0, or space or ',' */ while (*cp) { if (isspace(*cp) || *cp == ',') { *cp = '\0'; return (path); } cp++; } return (path); }