#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/dkio.h>
#include <sys/dktp/fdisk.h>
#include <sys/mnttab.h>
#include <sys/mntent.h>
#include <sys/sysmacros.h>
#include <sys/mkdev.h>
#include <sys/vfs.h>
#include <nfs/nfs.h>
#include <nfs/nfs_clnt.h>
#include <kstat.h>
#include <ctype.h>
#include <dirent.h>
#include <libdevinfo.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <devid.h>
#include <sys/scsi/adapters/scsi_vhci.h>
#include "dsr.h"
#include "statcommon.h"
static di_node_t di_root;
static di_dim_t di_dim;
static int scsi_vhci_fd = -1;
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
static disk_list_t *disklist[DISKLIST_MOD];
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 *);
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;
}
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;
if (devpathp != NULL)
*devpathp = NULL;
if (adevpathp != NULL)
*adevpathp = NULL;
if (devidp != NULL)
*devidp = NULL;
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 {
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);
if (devpathp != NULL)
*devpathp = safe_strdup(devpath);
if (adevpathp != NULL) {
char *a;
a = strrchr(devpath, '/');
if (a == NULL) {
free(devpath);
return (0);
}
a++;
if (part == NULL && mm->minor_isdisk) {
char *s;
if ((s = strrchr(a, 's')) == NULL &&
(s = strrchr(a, 'p')) == NULL) {
free(devpath);
return (0);
}
*s = '\0';
}
*adevpathp = safe_strdup(a);
}
if (devidp != NULL) {
if (di_root == DI_NODE_NIL)
di_root = di_init("/", DINFOCACHE);
if (di_root != NULL) {
devicespath = di_dim_path_devices(di_dim,
driver, instance, NULL);
if (devicespath) {
node = di_lookup_node(di_root, devicespath);
free(devicespath);
if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
DEVID_PROP_NAME, &devid) != -1)
*devidp = devid;
}
}
}
free(devpath);
return (1);
}
static int
drvpid2port(uint_t pid, char **target_portp)
{
sv_iocdata_t ioc;
char target_port[MAXNAMELEN];
*target_portp = NULL;
if (scsi_vhci_fd == -1) {
scsi_vhci_fd = open("/devices/scsi_vhci:devctl", O_RDONLY);
if (scsi_vhci_fd == -1)
return (0);
}
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);
}
disk_list_t *
lookup_ks_name(char *ks_name, int want_devid)
{
char *pidp;
char *part;
char *initiator;
char *p;
int len;
char driver[KSTAT_STRLEN];
int instance;
disk_list_t **dlhp;
disk_list_t *entry;
char *devpath = NULL;
char *adevpath = NULL;
char *devid = NULL;
int pid;
char *target_port = NULL;
char portform[MAXPATHLEN];
if (ks_name == NULL || *ks_name == '\0' ||
strspn(ks_name, "0123456789") == strlen(ks_name))
goto fail;
pidp = strchr(ks_name, '.');
initiator = strrchr(ks_name, '.');
if (pidp != NULL && pidp == initiator)
goto fail;
part = strchr(ks_name, ',');
p = strchr(ks_name, ':');
if (part != NULL && p != NULL)
goto fail;
if (p != NULL)
part = p;
if (part != NULL && pidp != NULL)
goto fail;
p = (part != NULL) ? part : pidp;
if (p == NULL)
p = &ks_name[strlen(ks_name) - 1];
else
p--;
while ((p >= ks_name) && isdigit(*p))
p--;
p++;
if ((*p == '\0') || (*p == ',') || (*p == '.') || (*p == ':'))
goto fail;
len = p - ks_name;
(void) strncpy(driver, ks_name, len);
driver[len] = '\0';
instance = atoi(p);
if (part != NULL)
part++;
dlhp = &disklist[instance & (DISKLIST_MOD - 1)];
for (entry = *dlhp; entry; entry = entry->next) {
if (strcmp(entry->ks_name, ks_name) == 0)
return (entry);
}
if (drvinstpart2dev(driver, instance, part,
&devpath, &adevpath, want_devid ? &devid : NULL) == 0)
goto fail;
if (pidp != NULL) {
pidp++;
initiator++;
if (*pidp != 't' || !isdigit(pidp[1]))
goto fail;
pid = atoi(&pidp[1]);
if (drvpid2port(pid, &target_port) == 0)
goto fail;
(void) snprintf(portform, sizeof (portform),
"%s.t%s.%s", adevpath, target_port, initiator);
free(target_port);
free(adevpath);
adevpath = strdup(portform);
}
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
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
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);
}
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);
}
static char *
cur_special(char *hostname, char *special)
{
char *cp;
char *path;
size_t hlen = strlen(hostname);
again:
if ((cp = strstr(special, hostname)) == NULL)
return (NULL);
if (cp[hlen] != ',' && cp[hlen] != ':') {
special = &cp[hlen];
goto again;
}
cp = &cp[hlen];
if (*cp == ',') {
cp++;
while (*cp != ':') {
if (*cp == '\0')
return (NULL);
cp++;
}
}
path = ++cp;
while (*cp) {
if (isspace(*cp) || *cp == ',') {
*cp = '\0';
return (path);
}
cp++;
}
return (path);
}