---
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