[libvirt] [PATCH 2/3] Refactor storage code to use common functions for LU discovery for all SCSI transports.

David Allan dallan at redhat.com
Mon Mar 2 19:41:12 UTC 2009


---
 src/storage_backend_iscsi.c |  327 +---------------------
 src/storage_backend_scsi.c  |  649 ++++++++++++++++++++-----------------------
 src/storage_backend_scsi.h  |   22 +-
 3 files changed, 319 insertions(+), 679 deletions(-)

diff --git a/src/storage_backend_iscsi.c b/src/storage_backend_iscsi.c
index d5b10e5..1b2dfd0 100644
--- a/src/storage_backend_iscsi.c
+++ b/src/storage_backend_iscsi.c
@@ -35,6 +35,7 @@
 #include <dirent.h>
 
 #include "virterror_internal.h"
+#include "storage_backend_scsi.h"
 #include "storage_backend_iscsi.h"
 #include "util.h"
 #include "memory.h"
@@ -169,145 +170,6 @@ virStorageBackendISCSIConnection(virConnectPtr conn,
     return 0;
 }
 
-static int
-virStorageBackendISCSINewLun(virConnectPtr conn, virStoragePoolObjPtr pool,
-                             unsigned int lun, const char *dev)
-{
-    virStorageVolDefPtr vol;
-    int fd = -1;
-    char *devpath = NULL;
-    int opentries = 0;
-
-    if (VIR_ALLOC(vol) < 0) {
-        virReportOOMError(conn);
-        goto cleanup;
-    }
-
-    vol->type = VIR_STORAGE_VOL_BLOCK;
-
-    if (virAsprintf(&(vol->name), "lun-%d", lun) < 0) {
-        virReportOOMError(conn);
-        goto cleanup;
-    }
-
-    if (virAsprintf(&devpath, "/dev/%s", dev) < 0) {
-        virReportOOMError(conn);
-        goto cleanup;
-    }
-
-    /* It can take a little while between logging into the ISCSI
-     * server and udev creating the /dev nodes, so if we get ENOENT
-     * we must retry a few times - they should eventually appear.
-     * We currently wait for upto 5 seconds. Is this good enough ?
-     * Perhaps not on a very heavily loaded system Any other
-     * options... ?
-     */
- reopen:
-    if ((fd = open(devpath, O_RDONLY)) < 0) {
-        opentries++;
-        if (errno == ENOENT && opentries < 50) {
-            usleep(100 * 1000);
-            goto reopen;
-        }
-        virReportSystemError(conn, errno,
-                             _("cannot open '%s'"),
-                             devpath);
-        goto cleanup;
-    }
-
-    /* Now figure out the stable path
-     *
-     * XXX this method is O(N) because it scans the pool target
-     * dir every time its run. Should figure out a more efficient
-     * way of doing this...
-     */
-    if ((vol->target.path = virStorageBackendStablePath(conn,
-                                                        pool,
-                                                        devpath)) == NULL)
-        goto cleanup;
-
-    VIR_FREE(devpath);
-
-    if (virStorageBackendUpdateVolTargetInfoFD(conn,
-                                               &vol->target,
-                                               fd,
-                                               &vol->allocation,
-                                               &vol->capacity) < 0)
-        goto cleanup;
-
-    /* XXX use unique iSCSI id instead */
-    vol->key = strdup(vol->target.path);
-    if (vol->key == NULL) {
-        virReportOOMError(conn);
-        goto cleanup;
-    }
-
-
-    pool->def->capacity += vol->capacity;
-    pool->def->allocation += vol->allocation;
-
-    if (VIR_REALLOC_N(pool->volumes.objs,
-                      pool->volumes.count+1) < 0) {
-        virReportOOMError(conn);
-        goto cleanup;
-    }
-    pool->volumes.objs[pool->volumes.count++] = vol;
-
-    close(fd);
-
-    return 0;
-
- cleanup:
-    if (fd != -1) close(fd);
-    VIR_FREE(devpath);
-    virStorageVolDefFree(vol);
-    return -1;
-}
-
-static int notdotdir(const struct dirent *dir)
-{
-    return !(STREQLEN(dir->d_name, ".", 1) || STREQLEN(dir->d_name, "..", 2));
-}
-
-/* Function to check if the type file in the given sysfs_path is a
- * Direct-Access device (i.e. type 0).  Return -1 on failure, 0 if not
- * a Direct-Access device, and 1 if a Direct-Access device
- */
-static int directAccessDevice(const char *sysfs_path)
-{
-    char typestr[3];
-    char *gottype, *p;
-    FILE *typefile;
-    int type;
-
-    typefile = fopen(sysfs_path, "r");
-    if (typefile == NULL) {
-        /* there was no type file; that doesn't seem right */
-        return -1;
-    }
-    gottype = fgets(typestr, 3, typefile);
-    fclose(typefile);
-
-    if (gottype == NULL) {
-        /* we couldn't read the type file; have to give up */
-        return -1;
-    }
-
-    /* we don't actually care about p, but if you pass NULL and the last
-     * character is not \0, virStrToLong_i complains
-     */
-    if (virStrToLong_i(typestr, &p, 10, &type) < 0) {
-        /* Hm, type wasn't an integer; seems strange */
-        return -1;
-    }
-
-    if (type != 0) {
-        /* saw a device other than Direct-Access */
-        return 0;
-    }
-
-    return 1;
-}
 
 static int
 virStorageBackendISCSIFindLUNs(virConnectPtr conn,
@@ -315,196 +177,17 @@ virStorageBackendISCSIFindLUNs(virConnectPtr conn,
                                const char *session)
 {
     char sysfs_path[PATH_MAX];
-    uint32_t host, bus, target, lun;
-    DIR *sysdir;
-    struct dirent *sys_dirent;
-    struct dirent **namelist;
-    int i, n, tries, retval, directaccess;
-    char *block, *scsidev, *block2;
-
-    retval = 0;
-    block = NULL;
-    scsidev = NULL;
+    int retval = 0;
 
     snprintf(sysfs_path, PATH_MAX,
              "/sys/class/iscsi_session/session%s/device", session);
 
-    sysdir = opendir(sysfs_path);
-    if (sysdir == NULL) {
+    if (virStorageBackendSCSIFindTargets(conn, pool, sysfs_path) < 0) {
         virReportSystemError(conn, errno,
-                             _("Failed to opendir sysfs path '%s'"),
+                             _("Failed to get target list for path '%s'"),
                              sysfs_path);
-        return -1;
+        retval = -1;
     }
-    while ((sys_dirent = readdir(sysdir))) {
-        /* double-negative, so we can use the same function for scandir below */
-        if (!notdotdir(sys_dirent))
-            continue;
-
-        if (STREQLEN(sys_dirent->d_name, "target", 6)) {
-            if (sscanf(sys_dirent->d_name, "target%u:%u:%u",
-                       &host, &bus, &target) != 3) {
-                virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                                      _("Failed to parse target from sysfs path %s/%s"),
-                                      sysfs_path, sys_dirent->d_name);
-                closedir(sysdir);
-                return -1;
-            }
-            break;
-        }
-    }
-    closedir(sysdir);
-
-    /* we now have the host, bus, and target; let's scan for LUNs */
-    snprintf(sysfs_path, PATH_MAX,
-             "/sys/class/iscsi_session/session%s/device/target%u:%u:%u",
-             session, host, bus, target);
-
-    n = scandir(sysfs_path, &namelist, notdotdir, versionsort);
-    if (n <= 0) {
-        /* we didn't find any reasonable entries; return failure */
-        virReportSystemError(conn, errno,
-                             _("Failed to find any LUNs for session '%s'"),
-                             session);
-        return -1;
-    }
-
-    for (i=0; i<n; i++) {
-        block = NULL;
-        scsidev = NULL;
-
-        if (sscanf(namelist[i]->d_name, "%u:%u:%u:%u\n",
-                   &host, &bus, &target, &lun) != 4)
-            continue;
-
-        /* we found a LUN */
-        /* Note, however, that just finding a LUN doesn't mean it is
-         * actually useful to us.  There are a few different types of
-         * LUNs, enumerated in the linux kernel in
-         * drivers/scsi/scsi.c:scsi_device_types[].  Luckily, these device
-         * types form part of the ABI between the kernel and userland, so
-         * are unlikely to change.  For now, we ignore everything that isn't
-         * type 0; that is, a Direct-Access device
-         */
-        snprintf(sysfs_path, PATH_MAX,
-                 "/sys/bus/scsi/devices/%u:%u:%u:%u/type",
-                 host, bus, target, lun);
-
-        directaccess = directAccessDevice(sysfs_path);
-        if (directaccess < 0) {
-            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                                  _("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"),
-                                  host, bus, target, lun);
-            retval = -1;
-            goto namelist_cleanup;
-        }
-        else if (directaccess == 0) {
-            /* not a direct-access device; skip */
-            continue;
-        }
-        /* implicit else if (access == 1); Direct-Access device */
-
-        /* It might take some time for the
-         * /sys/bus/scsi/devices/host:bus:target:lun/block{:sda,/sda}
-         * link to show up; wait up to 5 seconds for it, then give up
-         */
-        tries = 0;
-        while (block == NULL && tries < 50) {
-            snprintf(sysfs_path, PATH_MAX, "/sys/bus/scsi/devices/%u:%u:%u:%u",
-                     host, bus, target, lun);
-
-            sysdir = opendir(sysfs_path);
-            if (sysdir == NULL) {
-                virReportSystemError(conn, errno,
-                                     _("Failed to opendir sysfs path '%s'"),
-                                     sysfs_path);
-                retval = -1;
-                goto namelist_cleanup;
-            }
-            while ((sys_dirent = readdir(sysdir))) {
-                if (!notdotdir(sys_dirent))
-                    continue;
-                if (STREQLEN(sys_dirent->d_name, "block", 5)) {
-                    block = strdup(sys_dirent->d_name);
-                    break;
-                }
-            }
-            closedir(sysdir);
-            tries++;
-            if (block == NULL)
-                 usleep(100 * 1000);
-        }
-
-        if (block == NULL) {
-            /* we couldn't find the device link for this device; fail */
-            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                                  _("Failed to find device link for lun %d"),
-                                  lun);
-            retval = -1;
-            goto namelist_cleanup;
-        }
-
-        if (strlen(block) == 5) {
-            /* OK, this is exactly "block"; must be new-style */
-            snprintf(sysfs_path, PATH_MAX,
-                     "/sys/bus/scsi/devices/%u:%u:%u:%u/block",
-                     host, bus, target, lun);
-            sysdir = opendir(sysfs_path);
-            if (sysdir == NULL) {
-                virReportSystemError(conn, errno,
-                                     _("Failed to opendir sysfs path '%s'"),
-                                     sysfs_path);
-                retval = -1;
-                goto namelist_cleanup;
-            }
-            while ((sys_dirent = readdir(sysdir))) {
-                if (!notdotdir(sys_dirent))
-                    continue;
-
-                scsidev = strdup(sys_dirent->d_name);
-                break;
-            }
-            closedir(sysdir);
-        }
-        else {
-            /* old-style; just parse out the sd */
-            block2 = strrchr(block, ':');
-            if (block2 == NULL) {
-                /* Hm, wasn't what we were expecting; have to give up */
-                virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                                      _("Failed to parse block path %s"),
-                                      block);
-                retval = -1;
-                goto namelist_cleanup;
-            }
-            block2++;
-            scsidev = strdup(block2);
-        }
-        if (scsidev == NULL) {
-            virReportOOMError(conn);
-            retval = -1;
-            goto namelist_cleanup;
-        }
-
-        retval = virStorageBackendISCSINewLun(conn, pool, lun, scsidev);
-        if (retval < 0)
-            break;
-        VIR_FREE(scsidev);
-        VIR_FREE(block);
-    }
-
-namelist_cleanup:
-    /* we call these VIR_FREE here to make sure we don't leak memory on
-     * error cases; in the success case, these are already freed but NULL,
-     * which should be fine
-     */
-    VIR_FREE(scsidev);
-    VIR_FREE(block);
-
-    for (i=0; i<n; i++)
-        VIR_FREE(namelist[i]);
-
-    VIR_FREE(namelist);
 
     return retval;
 }
diff --git a/src/storage_backend_scsi.c b/src/storage_backend_scsi.c
index 236d894..3752bf2 100644
--- a/src/storage_backend_scsi.c
+++ b/src/storage_backend_scsi.c
@@ -23,10 +23,10 @@
 
 #include <config.h>
 
-#include <c-ctype.h>
 #include <unistd.h>
 #include <stdio.h>
-#include <hal/libhal.h>
+#include <dirent.h>
+#include <fcntl.h>
 
 #include "virterror_internal.h"
 #include "storage_backend_scsi.h"
@@ -34,152 +34,91 @@
 
 #define VIR_FROM_THIS VIR_FROM_STORAGE
 
-#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host/"
-#define LINUX_SYSFS_SCSI_HOST_POSTFIX "/device"
 
-
-/**
- * virStorageBackendSCSIMakeHostDevice:
- * @conn: report errors agains
- * @pool: pool to resolve
- * @buf: pre-allocated buffer to fill with path name
- * @buflen: size of buffer
- *
- * Resolve a HBA name to HBA device  path
- *
- * Given a pool object configured for a SCSI HBA, resolve the HBA name
- * into the canonical sysfs path for the HBA device. This can then be
- * used to lookup the device in HAL.
- *
- * Returns 0 on success, -1 on failure
- */
-static int virStorageBackendSCSIMakeHostDevice(virConnectPtr conn,
-                                               virStoragePoolObjPtr pool,
-                                               char *buf,
-                                               size_t buflen)
+static int
+virStorageBackendSCSIRefreshPool(virConnectPtr conn,
+                                  virStoragePoolObjPtr pool)
 {
-    char *dev = NULL;
-    char *tmp = pool->def->source.adapter;
-    char relLink[PATH_MAX], absLink[PATH_MAX];
-    ssize_t len;
-
-    if (!tmp) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("Missing adapter name for pool"));
-        return -1;
-    }
+    char sysfs_path[PATH_MAX];
+    int ret = 0;
 
-    /* Sanity check host adapter name - should only be 0-9, a-z.
-     * Anything else is bogus & so we reject it, to prevent them
-     * making us read arbitrary paths on host
-     */
-    while (*tmp) {
-        if (!c_isalnum(*tmp)) {
-            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                                  _("Invalid character in pool adapter name '%s'"),
-                                  pool->def->source.adapter);
-            return -1;
-        }
-        tmp++;
-    }
+    pool->def->allocation = pool->def->capacity = pool->def->available = 0;
 
-    /*
-     * First get the class based path eg
-     *
-     *   /sys/class/scsi_host/host1/device
-     */
-    if ((dev = malloc(sizeof(LINUX_SYSFS_SCSI_HOST_PREFIX) +
-                      strlen(pool->def->source.adapter) +
-                      sizeof(LINUX_SYSFS_SCSI_HOST_POSTFIX) +
-                      1)) == NULL) {
-        virReportOOMError(conn);
-        return -1;
-    }
+    virStorageBackendWaitForDevices(conn);
 
-    strcpy(dev, LINUX_SYSFS_SCSI_HOST_PREFIX);
-    strcat(dev, pool->def->source.adapter);
-    strcat(dev, LINUX_SYSFS_SCSI_HOST_POSTFIX);
+    snprintf(sysfs_path, PATH_MAX, "%s/%s/%s",
+             LINUX_SYSFS_SCSI_HOST_PREFIX,
+             pool->def->source.adapter,
+             LINUX_SYSFS_SCSI_HOST_POSTFIX);
 
-    /*
-     * Now resolve the class based path symlink to the real
-     * device path, which is likely under PCI bus hierarchy
-     * and is the path tracked by HAL
-     */
-    /* Readlink does not null terminate, so we reserve one byte */
-    if ((len = readlink(dev, relLink, sizeof(relLink)-1)) < 0) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("cannot find SCSI host adapter '%s' at '%s'"),
-                              pool->def->source.adapter, dev);
-        free(dev);
-        return -1;
+    if (virStorageBackendSCSIFindTargets(conn, pool, sysfs_path) < 0) {
+        virReportSystemError(conn, errno,
+                             _("Failed to get target list for path '%s'"),
+                             sysfs_path);
+        ret = -1;
     }
-    relLink[len] = '\0';
+
+    return ret;
+}
 
 
-    /*
-     * The symlink is relative, so now we have to turn it
-     * into a absolute path
-     */
-    if ((tmp = realloc(dev,
-                       sizeof(LINUX_SYSFS_SCSI_HOST_PREFIX) +
-                       strlen(pool->def->source.adapter) +
-                       1 +
-                       strlen(relLink) +
-                       1)) == NULL) {
-        virReportOOMError(conn);
-        free(dev);
+static int
+notdotdir(const struct dirent *dir)
+{
+    return !(STREQLEN(dir->d_name, ".", 1) || STREQLEN(dir->d_name, "..", 2));
+}
+
+
+/* Function to check if the type file in the given sysfs_path is a
+ * Direct-Access device (i.e. type 0).  Return -1 on failure, 0 if not
+ * a Direct-Access device, and 1 if a Direct-Access device
+ */
+static int
+directAccessDevice(const char *sysfs_path)
+{
+    char typestr[3];
+    char *gottype, *p;
+    FILE *typefile;
+    int type;
+
+    typefile = fopen(sysfs_path, "r");
+    if (typefile == NULL) {
+        /* there was no type file; that doesn't seem right */
         return -1;
     }
-    dev = tmp;
+    gottype = fgets(typestr, 3, typefile);
+    fclose(typefile);
 
-    strcpy(dev, LINUX_SYSFS_SCSI_HOST_PREFIX);
-    strcat(dev, pool->def->source.adapter);
-    strcat(dev, "/");
-    strcat(dev, relLink);
+    if (gottype == NULL) {
+        /* we couldn't read the type file; have to give up */
+        return -1;
+    }
 
-    /*
-     * And finally canonicalize the absolute path to remove the
-     * copious  ..  components
+    /* we don't actually care about p, but if you pass NULL and the last
+     * character is not \0, virStrToLong_i complains
      */
-    if (!realpath(dev, absLink)) {
-        char ebuf[1024];
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("cannot canonicalize link: %s"),
-                              virStrerror(errno, ebuf, sizeof ebuf));
-        free(dev);
+    if (virStrToLong_i(typestr, &p, 10, &type) < 0) {
+        /* Hm, type wasn't an integer; seems strange */
         return -1;
     }
-    free(dev);
 
-    if (strlen(absLink) >= buflen) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("link too long for buffer"));
-        return -1;
+    if (type != 0) {
+        /* saw a device other than Direct-Access */
+        return 0;
     }
-    strcpy(buf, absLink);
 
-    return 0;
+    return 1;
 }
 
 
-/**
- * virStorageBackendSCSICreateVol
- *
- * Allocate a virStorageVolDef object for the specified
- * metadata and hook it into the pool
- *
- * Returns 0 on success, -1 on failure
- */
 static int
-virStorageBackendSCSICreateVol(virConnectPtr conn,
-                               virStoragePoolObjPtr pool,
-                               const char *name,
-                               const char *path,
-                               const char *key,
-                               unsigned long long size)
+virStorageBackendSCSINewLun(virConnectPtr conn, virStoragePoolObjPtr pool,
+                            unsigned int lun, const char *dev)
 {
     virStorageVolDefPtr vol;
-    char *tmppath;
+    int fd = -1;
+    char *devpath = NULL;
+    int opentries = 0;
 
     if (VIR_ALLOC(vol) < 0) {
         virReportOOMError(conn);
@@ -188,18 +127,36 @@ virStorageBackendSCSICreateVol(virConnectPtr conn,
 
     vol->type = VIR_STORAGE_VOL_BLOCK;
 
-    tmppath = strdup(path);
-    vol->name = strdup(name);
-    vol->key = strdup(key);
-    vol->allocation = vol->capacity = size;
+    if (virAsprintf(&(vol->name), "lun-%d", lun) < 0) {
+        virReportOOMError(conn);
+        goto cleanup;
+    }
 
-    if ((vol->name == NULL) ||
-        (vol->key == NULL) ||
-        (tmppath == NULL)) {
+    if (virAsprintf(&devpath, "/dev/%s", dev) < 0) {
         virReportOOMError(conn);
         goto cleanup;
     }
 
+    /* For SAN storage it can take a little while between getting
+     * access to the array and udev creating the /dev nodes, so if we
+     * get ENOENT we must retry a few times - they should eventually
+     * appear.  We currently wait for upto 5 seconds. Is this good
+     * enough ?  Perhaps not on a very heavily loaded system Any other
+     * options... ?
+     */
+ reopen:
+    if ((fd = open(devpath, O_RDONLY)) < 0) {
+        opentries++;
+        if (errno == ENOENT && opentries < 50) {
+            usleep(100 * 1000);
+            goto reopen;
+        }
+        virReportSystemError(conn, errno,
+                             _("cannot open '%s'"),
+                             devpath);
+        goto cleanup;
+    }
+
     /* Now figure out the stable path
      *
      * XXX this method is O(N) because it scans the pool target
@@ -208,15 +165,25 @@ virStorageBackendSCSICreateVol(virConnectPtr conn,
      */
     if ((vol->target.path = virStorageBackendStablePath(conn,
                                                         pool,
-                                                        tmppath)) == NULL)
+                                                        devpath)) == NULL)
         goto cleanup;
 
-    if (tmppath != vol->target.path)
-        free(tmppath);
-    tmppath = NULL;
+    VIR_FREE(devpath);
+
+    if (virStorageBackendUpdateVolTargetInfoFD(conn,
+                                               &vol->target,
+                                               fd,
+                                               &vol->allocation,
+                                               &vol->capacity) < 0)
+        goto cleanup;
 
-    if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0)
+    /* XXX use unique iSCSI id instead */
+    vol->key = strdup(vol->target.path);
+    if (vol->key == NULL) {
+        virReportOOMError(conn);
         goto cleanup;
+    }
+
 
     pool->def->capacity += vol->capacity;
     pool->def->allocation += vol->allocation;
@@ -224,248 +191,244 @@ virStorageBackendSCSICreateVol(virConnectPtr conn,
     if (VIR_REALLOC_N(pool->volumes.objs,
                       pool->volumes.count+1) < 0) {
         virReportOOMError(conn);
-        virStorageVolDefFree(vol);
         goto cleanup;
     }
     pool->volumes.objs[pool->volumes.count++] = vol;
 
+    close(fd);
+
     return 0;
 
-cleanup:
-    free(tmppath);
+ cleanup:
+    if (fd != -1) close(fd);
+    VIR_FREE(devpath);
     virStorageVolDefFree(vol);
     return -1;
 }
 
 
-/**
- * virStorageBackendSCSIAddLUN:
- *
- * @conn: connection to report errors against
- * @pool: pool to register volume with
- * @ctx: HAL context
- * @devname: HAL device name for LUN
- *
- * Given a HAL device supported 'block' and 'storage' capabilities
- * register it as a volume in the pool
- *
- * Returns 0 on success, -1 on error
- */
 static int
-virStorageBackendSCSIAddLUN(virConnectPtr conn,
-                            virStoragePoolObjPtr pool,
-                            LibHalContext *ctx,
-                            const char *devname)
+virStorageBackendSCSIFindLUNs(virConnectPtr conn,
+                              virStoragePoolObjPtr pool,
+                              uint32_t host,
+                              uint32_t bus,
+                              uint32_t target)
 {
-    char **strdevs = NULL;
-    int numstrdevs = 0;
-    char *dev = NULL, *key = NULL;
-    unsigned long long size;
-    int host, bus, target, lun;
-    int n = -1, i;
-    int ret = -1;
-    char name[100];
-    DBusError derr = DBUS_ERROR_INIT;
-
-    if ((strdevs = libhal_manager_find_device_string_match(ctx,
-                                                           "info.parent",
-                                                           devname,
-                                                           &numstrdevs,
-                                                           &derr)) == NULL) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("HAL failed to lookup LUNs for '%s': %s"),
-                              devname, derr.message);
-        goto cleanup;
-    }
-    for (i = 0 ; i < numstrdevs && n == -1 ; i++) {
-        char *cat = libhal_device_get_property_string(ctx,
-                                                      strdevs[0],
-                                                      "info.category",
-                                                      NULL);
-        /* XXX derr */
-        if (cat != NULL) {
-            if (STREQ(cat, "storage"))
-                n = i;
-            free(cat);
-        }
-    }
-    /* No storage vol for device - probably a removable vol so ignore */
-    if (n == -1) {
-        ret = 0;
-        goto cleanup;
-    }
-
-#define HAL_GET_PROPERTY(path, name, err, type, value)                  \
-    (value) = libhal_device_get_property_ ## type(ctx, (path), (name), (err)); \
-    if (dbus_error_is_set((err))) {                                     \
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,             \
-                              "unable to lookup '%s' property on %s: %s", \
-                              (name), (path), derr.message);            \
-        goto cleanup;                                                   \
+    char sysfs_path[PATH_MAX];
+    int i, n, tries, retval, directaccess;
+    unsigned int tmphost, tmpbus, tmptarget, tmplun;
+    DIR *sysdir;
+    char *block, *block2;
+    struct dirent *sys_dirent;
+    struct dirent **namelist;
+    char *scsidev;
+
+    retval = 0;
+
+    /* we now have the host, bus, and target; let's scan for LUNs */
+    snprintf(sysfs_path, PATH_MAX,
+             "/sys/bus/scsi/devices/target%u:%u:%u",
+             host, bus, target);
+
+    n = scandir(sysfs_path, &namelist, notdotdir, versionsort);
+    if (n <= 0) {
+        /* we didn't find any reasonable entries; return failure */
+        virReportSystemError(conn, errno,
+                             _("Failed to find any LUNs for host %d bus %d target %d"),
+                             host, bus, target);
+        return -1;
     }
 
-    /* These are props of the physical device */
-    HAL_GET_PROPERTY(devname, "scsi.host", &derr, int, host);
-    HAL_GET_PROPERTY(devname, "scsi.bus", &derr, int, bus);
-    HAL_GET_PROPERTY(devname, "scsi.target", &derr, int, target);
-    HAL_GET_PROPERTY(devname, "scsi.lun", &derr, int, lun);
-
-    /* These are props of the logic device */
-    HAL_GET_PROPERTY(strdevs[0], "block.device", &derr, string, dev);
-    /*
-     * XXX storage.serial is not actually unique if they have
-     * multipath on the fibre channel adapter
-     */
-    HAL_GET_PROPERTY(strdevs[0], "storage.serial", &derr, string, key);
-    HAL_GET_PROPERTY(strdevs[0], "storage.size", &derr, uint64, size);
-
-#undef HAL_GET_PROPERTY
-
-    snprintf(name, sizeof(name), "%d:%d:%d:%d", host, bus, target, lun);
-    name[sizeof(name)-1] = '\0';
-
-    if (virStorageBackendSCSICreateVol(conn, pool, name, dev, key, size) < 0)
-        goto cleanup;
-    ret = 0;
-
-cleanup:
-    for (i = 0 ; strdevs && i < numstrdevs ; i++)
-        free(strdevs[i]);
-    free(strdevs);
-    free(dev);
-    free(key);
-    if (dbus_error_is_set(&derr))
-        dbus_error_free(&derr);
-    return ret;
-}
+    for (i = 0 ; i < n ; i++) {
+        block = NULL;
+        scsidev = NULL;
+
+        if (sscanf(namelist[i]->d_name, "%u:%u:%u:%u\n",
+                   &tmphost, &tmpbus, &tmptarget, &tmplun) != 4)
+            continue;
+
+        /* we found a LUN */
+        /* Note, however, that just finding a LUN doesn't mean it is
+         * actually useful to us.  There are a few different types of
+         * LUNs, enumerated in the linux kernel in
+         * drivers/scsi/scsi.c:scsi_device_types[].  Luckily, these device
+         * types form part of the ABI between the kernel and userland, so
+         * are unlikely to change.  For now, we ignore everything that isn't
+         * type 0; that is, a Direct-Access device
+         */
+        snprintf(sysfs_path, PATH_MAX,
+                 "/sys/bus/scsi/devices/%u:%u:%u:%u/type",
+                 tmphost, tmpbus, tmptarget, tmplun);
+
+        directaccess = directAccessDevice(sysfs_path);
+        if (directaccess < 0) {
+            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  _("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"),
+                                  tmphost, tmpbus, tmptarget, tmplun);
+            retval = -1;
+            goto namelist_cleanup;
+        }
+        else if (directaccess == 0) {
+            /* not a direct-access device; skip */
+            continue;
+        }
+        /* implicit else if (access == 1); Direct-Access device */
+
+        /* It might take some time for the
+         * /sys/bus/scsi/devices/host:bus:target:lun/block{:sda,/sda}
+         * link to show up; wait up to 5 seconds for it, then give up
+         */
+        tries = 0;
+        while (block == NULL && tries < 50) {
+            snprintf(sysfs_path, PATH_MAX, "/sys/bus/scsi/devices/%u:%u:%u:%u",
+                     tmphost, tmpbus, tmptarget, tmplun);
+
+            sysdir = opendir(sysfs_path);
+            if (sysdir == NULL) {
+                virReportSystemError(conn, errno,
+                                     _("Failed to opendir sysfs path '%s'"),
+                                     sysfs_path);
+                retval = -1;
+                goto namelist_cleanup;
+            }
+            while ((sys_dirent = readdir(sysdir))) {
+                if (!notdotdir(sys_dirent))
+                    continue;
+                if (STREQLEN(sys_dirent->d_name, "block", 5)) {
+                    block = strdup(sys_dirent->d_name);
+                    break;
+                }
+            }
+            closedir(sysdir);
+            tries++;
+            if (block == NULL)
+                 usleep(100 * 1000);
+        }
 
+        if (block == NULL) {
+            /* we couldn't find the device link for this device; fail */
+            virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  _("Failed to find device link for lun %d"),
+                                  tmplun);
+            retval = -1;
+            goto namelist_cleanup;
+        }
 
-/**
- * virStorageBackendSCSIRefreshPool:
- *
- * Query HAL for all storage devices associated with a specific
- * SCSI HBA.
- *
- * XXX, currently we only support regular devices in /dev/,
- * or /dev/disk/by-XXX.  In future we also need to be able to
- * map to multipath devices setup under /dev/mpath/XXXX.
- */
-static int
-virStorageBackendSCSIRefreshPool(virConnectPtr conn,
-                                 virStoragePoolObjPtr pool)
-{
-    char hostdevice[PATH_MAX];
-    LibHalContext *ctx = NULL;
-    DBusConnection *sysbus = NULL;
-    DBusError derr = DBUS_ERROR_INIT;
-    char **hbadevs = NULL, **blkdevs = NULL;
-    int numhbadevs = 0, numblkdevs = 0;
-    int i, ret = -1;
-
-    /* Get the canonical sysfs path for the HBA device */
-    if (virStorageBackendSCSIMakeHostDevice(conn, pool,
-                                            hostdevice, sizeof(hostdevice)) < 0)
-        goto cleanup;
+        if (strlen(block) == 5) {
+            /* OK, this is exactly "block"; must be new-style */
+            snprintf(sysfs_path, PATH_MAX,
+                     "/sys/bus/scsi/devices/%u:%u:%u:%u/block",
+                     tmphost, tmpbus, tmptarget, tmplun);
+            sysdir = opendir(sysfs_path);
+            if (sysdir == NULL) {
+                virReportSystemError(conn, errno,
+                                     _("Failed to opendir sysfs path '%s'"),
+                                     sysfs_path);
+                retval = -1;
+                goto namelist_cleanup;
+            }
+            while ((sys_dirent = readdir(sysdir))) {
+                if (!notdotdir(sys_dirent))
+                    continue;
+
+                scsidev = strdup(sys_dirent->d_name);
+                break;
+            }
+            closedir(sysdir);
+        }
+        else {
+            /* old-style; just parse out the sd */
+            block2 = strrchr(block, ':');
+            if (block2 == NULL) {
+                /* Hm, wasn't what we were expecting; have to give up */
+                virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                      _("Failed to parse block path %s"),
+                                      block);
+                retval = -1;
+                goto namelist_cleanup;
+            }
+            block2++;
+            scsidev = strdup(block2);
+        }
+        if (scsidev == NULL) {
+            virReportOOMError(conn);
+            retval = -1;
+            goto namelist_cleanup;
+        }
 
-    if ((ctx = libhal_ctx_new()) == NULL) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("failed to allocate HAL context"));
-        goto cleanup;
+        retval = virStorageBackendSCSINewLun(conn, pool, tmplun, scsidev);
+        if (retval < 0)
+            break;
+        VIR_FREE(scsidev);
+        VIR_FREE(block);
     }
 
-    sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &derr);
-    if (sysbus == NULL) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("failed to get dbus system bus: '%s'"),
-                              derr.message);
-        goto cleanup;
-    }
+namelist_cleanup:
+    /* we call these VIR_FREE here to make sure we don't leak memory on
+     * error cases; in the success case, these are already freed but NULL,
+     * which should be fine
+     */
+    VIR_FREE(scsidev);
+    VIR_FREE(block);
 
-    if (!libhal_ctx_set_dbus_connection(ctx, sysbus)) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("failed to set HAL dbus connection"));
-        goto cleanup;
+    for (i = 0 ; i < n ; i++) {
+        VIR_FREE(namelist[i]);
     }
 
+    VIR_FREE(namelist);
 
-    if (!libhal_ctx_init(ctx, &derr)) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("failed to initialize HAL context: '%s'"),
-                              derr.message);
-        goto cleanup;
-    }
+    return retval;
+}
 
-    if ((hbadevs = libhal_manager_find_device_string_match(ctx,
-                                                           "linux.sysfs_path",
-                                                           hostdevice,
-                                                           &numhbadevs,
-                                                           &derr)) == NULL) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("HAL failed to lookup device '%s': %s"),
-                              hostdevice, derr.message);
-        goto cleanup;
-    }
 
-    if (numhbadevs != 1) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("unexpected number of HBA devices from HAL "
-                                "(expected 1, got %d) when looking up device "
-                                "'%s'"), numhbadevs, hostdevice);
-        goto cleanup;
+int
+virStorageBackendSCSIFindTargets(virConnectPtr conn,
+                                 virStoragePoolObjPtr pool,
+                                 const char *sysfs_path)
+{
+    uint32_t host, bus, target;
+    DIR *sysdir;
+    struct dirent *sys_dirent;
+    int retval = 0;
+
+    sysdir = opendir(sysfs_path);
+    if (sysdir == NULL) {
+        virReportSystemError(conn, errno,
+                             _("Failed to opendir sysfs path '%s'"),
+                             sysfs_path);
+        retval = -1;
+        goto out;
     }
 
-    if ((blkdevs = libhal_manager_find_device_string_match(ctx,
-                                                           "info.parent",
-                                                           hbadevs[0],
-                                                           &numblkdevs,
-                                                           &derr)) == NULL) {
-        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                              _("failed to lookup LUNs for %s with HAL: %s"),
-                              hbadevs[0], derr.message);
-        goto cleanup;
+    while ((sys_dirent = readdir(sysdir))) {
+        /* double-negative, so we can use the same function for scandir below */
+        if (!notdotdir(sys_dirent))
+            continue;
+
+        if (STREQLEN(sys_dirent->d_name, "target", 6)) {
+            if (sscanf(sys_dirent->d_name, "target%u:%u:%u",
+                       &host, &bus, &target) != 3) {
+                virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                      _("Failed to parse target from sysfs path %s/%s"),
+                                      sysfs_path, sys_dirent->d_name);
+                closedir(sysdir);
+                retval = -1;
+                goto out;
+            }
+            /* There may be more than one target on a host, so we
+             * continue to check after finding one. */
+            virStorageBackendSCSIFindLUNs(conn, pool, host, bus, target);
+        }
     }
+    closedir(sysdir);
 
-    for (i = 0 ; i < numblkdevs ; i++)
-        virStorageBackendSCSIAddLUN(conn,
-                                    pool,
-                                    ctx,
-                                    blkdevs[i]);
-
-    ret = 0;
-
-cleanup:
-    for (i = 0 ; hbadevs && i < numhbadevs ; i++)
-        free(hbadevs[i]);
-    free(hbadevs);
-    for (i = 0 ; blkdevs && i < numblkdevs ; i++)
-        free(blkdevs[i]);
-    free(blkdevs);
-    libhal_ctx_shutdown(ctx, NULL);
-    libhal_ctx_free(ctx);
-    if (sysbus)
-        dbus_connection_unref(sysbus);
-    if (dbus_error_is_set(&derr))
-        dbus_error_free(&derr);
-    return ret;
+out:
+    return retval;
 }
 
 
-
 virStorageBackend virStorageBackendSCSI = {
     .type = VIR_STORAGE_POOL_SCSI,
+
     .refreshPool = virStorageBackendSCSIRefreshPool,
 };
-
-/*
- * vim: set tabstop=4:
- * vim: set shiftwidth=4:
- * vim: set expandtab:
- */
-/*
- * Local variables:
- *  indent-tabs-mode: nil
- *  c-indent-level: 4
- *  c-basic-offset: 4
- *  tab-width: 4
- * End:
- */
diff --git a/src/storage_backend_scsi.h b/src/storage_backend_scsi.h
index c912269..022efef 100644
--- a/src/storage_backend_scsi.h
+++ b/src/storage_backend_scsi.h
@@ -26,20 +26,14 @@
 
 #include "storage_backend.h"
 
+#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host"
+#define LINUX_SYSFS_SCSI_HOST_POSTFIX "device"
+
 extern virStorageBackend virStorageBackendSCSI;
 
-#endif /* __VIR_STORAGE_BACKEND_SCSI_H__ */
+int
+virStorageBackendSCSIFindTargets(virConnectPtr conn,
+                                 virStoragePoolObjPtr pool,
+                                 const char *sysfs_path);
 
-/*
- * vim: set tabstop=4:
- * vim: set shiftwidth=4:
- * vim: set expandtab:
- */
-/*
- * Local variables:
- *  indent-tabs-mode: nil
- *  c-indent-level: 4
- *  c-basic-offset: 4
- *  tab-width: 4
- * End:
- */
+#endif /* __VIR_STORAGE_BACKEND_SCSI_H__ */
-- 
1.6.0.6




More information about the libvir-list mailing list