/* * 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. */ #pragma ident "%Z%%M% %I% %E% SMI" #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 "dsr.h" #include "statcommon.h" /* disk/tape info */ static di_node_t di_root; /* for devid */ static di_dim_t di_dim; /* for /dev names */ 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 char md_minor_name[MAXPATHLEN]; static minor_match_t mm_md = {md_minor_name, 0}; static minor_match_t *mma_disk_tape[] = {&mm_disk, &mm_tape, NULL}; static minor_match_t *mma_md[] = {&mm_md, NULL}; static char *mdsetno2name(int setno); #define DISKLIST_MOD 256 /* ^2 instunit 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 drvinstunit2dev(). */ 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, instunit) device: return zero on failure. * * NOTE: Failure of drvinstunit2dev 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 drvinstunit2dev(char *driver, int instunit, char **devpathp, char **adevpathp, char **devidp, int *isdiskp) { int instance; minor_match_t **mma; minor_match_t *mm; char *devpath; char *devid; char *a, *s; int mdsetno; char *mdsetname = NULL; char amdsetname[MAXPATHLEN]; char *devicespath; di_node_t node; /* setup "no result" return values */ if (devpathp) *devpathp = NULL; if (adevpathp) *adevpathp = NULL; if (devidp) *devidp = NULL; if (isdiskp) *isdiskp = 0; /* take snapshot if not established */ if (di_dim == NULL) { di_dim = di_dim_init(); if (di_dim == NULL) return (0); } /* * Determine if 'instunit' is an 'instance' or 'unit' based on the * 'driver'. The current code only detects 'md' metadevice 'units', * and defaults to 'instance' for everything else. * * For a metadevice, 'driver' is either "md" or "/md". */ s = strstr(driver, "/md"); if ((strcmp(driver, "md") == 0) || (s && isdigit(*driver) && (strcmp(s, "/md") == 0))) { /* * "md" unit: Special case translation of "md" kstat names. * For the local set the kstat name is "md", and for * a shared set the kstat name is "/md": we map * these to the minor paths "/pseudo/md@0:,blk" and * "/pseudo/md@0:,,blk" respectively. */ if (isdigit(*driver)) { mdsetno = atoi(driver); /* convert setno to setname */ mdsetname = mdsetno2name(mdsetno); } else mdsetno = 0; driver = "md"; instance = 0; mma = mma_md; /* metadevice dynamic minor */ (void) snprintf(md_minor_name, sizeof (md_minor_name), "%d,%d,blk", mdsetno, instunit); } else { instance = instunit; mma = mma_disk_tape; /* disk/tape minors */ } /* Try to find a minor_match that works */ for (mm = *mma++; mm; 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) /* devpath */ *devpathp = safe_strdup(devpath); if (adevpathp) { /* abbreviated devpath */ if (mm->minor_isdisk) { /* * For disks we return the last component (with * trailing "s#" or "p#" stripped off for disks). * For example for devpath of "/dev/dsk/c0t0d0s0" the * abbreviated devpath would be "c0t0d0". */ a = strrchr(devpath, '/'); if (a == NULL) { free(devpath); return (0); } a++; s = strrchr(a, 's'); if (s == NULL) { s = strrchr(a, 'p'); if (s == NULL) { free(devpath); return (0); } } /* don't include slice information in devpath */ *s = '\0'; } else { /* * remove "/dev/", and "/dsk/", from 'devpath' (like * "/dev/md/dsk/d0") to form the abbreviated devpath * (like "md/d0"). */ if ((s = strstr(devpath, "/dev/")) != NULL) (void) strcpy(s + 1, s + 5); if ((s = strstr(devpath, "/dsk/")) != NULL) (void) strcpy(s + 1, s + 5); /* * If we have an mdsetname, convert abbreviated setno * notation (like "md/shared/1/d0" to abbreviated * setname notation (like "md/red/d0"). */ if (mdsetname) { a = strrchr(devpath, '/'); (void) snprintf(amdsetname, sizeof (amdsetname), "md/%s%s", mdsetname, a); free(mdsetname); a = amdsetname; } else { if (*devpath == '/') a = devpath + 1; else a = devpath; } } *adevpathp = safe_strdup(a); } if (devidp) { /* lookup the devid */ /* take snapshots if not established */ if (di_root == DI_NODE_NIL) { di_root = di_init("/", DINFOCACHE); } if (di_root) { /* 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; } } } if (isdiskp) *isdiskp = mm->minor_isdisk; free(devpath); return (1); /* success */ } /* * Find/create a disk_list entry for "" given a kstat name. * The basic format of a kstat name is ",". The * is a base10 number, and the "," part is optional. * * NOTE: In the case of non-local metadevices, the format of "" in * a kstat name is acutally "/md". */ disk_list_t * lookup_ks_name(char *ks_name, int want_devid) { char *p; int len; char driver[MAXNAMELEN]; int instunit; disk_list_t **dlhp; /* disklist head */ disk_list_t *entry; char *devpath; char *adevpath = NULL; char *devid = NULL; int isdisk; /* * Extract and from kstat name. * Filter out illegal forms (like all digits). */ if ((ks_name == NULL) || (*ks_name == 0) || (strspn(ks_name, "0123456789") == strlen(ks_name))) return (NULL); p = strrchr(ks_name, ','); /* start of ",partition" */ if (p == NULL) p = &ks_name[strlen(ks_name) - 1]; /* last char */ else p--; /* before ",partition" */ while ((p >= ks_name) && isdigit(*p)) p--; /* backwards over digits */ p++; /* start of instunit */ if ((*p == '\0') || (*p == ',')) return (NULL); /* no */ len = p - ks_name; (void) strncpy(driver, ks_name, len); driver[len] = '\0'; instunit = atoi(p); /* hash and search for existing disklist entry */ dlhp = &disklist[instunit & (DISKLIST_MOD - 1)]; for (entry = *dlhp; entry; entry = entry->next) { if ((strcmp(entry->dtype, driver) == 0) && (entry->dnum == instunit)) { return (entry); } } /* not found, try to get dev information */ if (drvinstunit2dev(driver, instunit, &devpath, &adevpath, want_devid ? &devid : NULL, &isdisk) == 0) { return (NULL); } /* and make a new disklist entry ... */ entry = safe_alloc(sizeof (disk_list_t)); entry->dtype = safe_strdup(driver); entry->dnum = instunit; entry->dname = devpath; entry->dsk = adevpath; entry->devidstr = devid; entry->flags = 0; if (isdisk) { entry->flags |= SLICES_OK; #if defined(__i386) entry->flags |= PARTITIONS_OK; #endif } entry->seen = 0; /* add new entry to head of instunit hashed list */ entry->next = *dlhp; *dlhp = entry; return (entry); } /* * Convert metadevice setno to setname by looking in /dev/md for symlinks * that point to "shared/setno" - the name of such a symlink is the setname. * The caller is responsible for freeing the returned string. */ static char * mdsetno2name(int setno) { char setlink[MAXPATHLEN + 1]; char link[MAXPATHLEN + 1]; char path[MAXPATHLEN + 1]; char *p; DIR *dirp; struct dirent *dp; size_t len; char *mdsetname = NULL; /* we are looking for a link to setlink */ (void) snprintf(setlink, MAXPATHLEN, "shared/%d", setno); /* in the directory /dev/md */ (void) strcpy(path, "/dev/md/"); p = path + strlen(path); dirp = opendir(path); if (dirp == NULL) return (NULL); /* loop through /dev/md directory entries */ while ((dp = readdir(dirp)) != NULL) { /* doing a readlink of entry (fails for non-symlinks) */ *p = '\0'; (void) strcpy(p, dp->d_name); if ((len = readlink(path, link, MAXPATHLEN)) == (size_t)-1) continue; /* and looking for a link to setlink */ link[len] = '\0'; if (strcmp(setlink, link)) continue; /* found- name of link is the setname */ mdsetname = safe_strdup(dp->d_name); break; } (void) closedir(dirp); return (mdsetname); } 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 == NULL) return (NULL); cp++; } } path = ++cp; /* skip ':' */ /* * path is terminated by either 0, or space or ',' */ while (*cp) { if (isspace(*cp) || *cp == ',') { *cp = NULL; return (path); } cp++; } return (path); }