#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <sanlock.h>
#include <sanlock_resource.h>
+#include <sanlock_direct.h>
+#include <sanlock_admin.h>
#include "lock_driver.h"
#include "logging.h"
#include "memory.h"
#include "util.h"
#include "files.h"
+#include "md5.h"
+#include "conf.h"
+
+#include "configmake.h"
#define VIR_FROM_THIS VIR_FROM_LOCKING
__FUNCTION__, __LINE__, __VA_ARGS__)
+#define VIR_LOCK_MANAGER_SANLOCK_AUTO_DISK_LOCKSPACE "__LIBVIRT__DISKS__"
+
typedef struct _virLockManagerSanlockDriver virLockManagerSanlockDriver;
typedef virLockManagerSanlockDriver *virLockManagerSanlockDriverPtr;
struct _virLockManagerSanlockDriver {
bool requireLeaseForDisks;
+ int hostID;
+ bool autoDiskLease;
+ char *autoDiskLeasePath;
};
static virLockManagerSanlockDriver *driver = NULL;
return -1; \
}
+ p = virConfGetValue(conf, "auto_disk_leases");
+ CHECK_TYPE("auto_disk_leases", VIR_CONF_LONG);
+ if (p) driver->autoDiskLease = p->l;
+
+ p = virConfGetValue(conf, "disk_lease_dir");
+ CHECK_TYPE("disk_lease_dir", VIR_CONF_STRING);
+ if (p && p->str) {
+ VIR_FREE(driver->autoDiskLeasePath);
+ if (!(driver->autoDiskLeasePath = strdup(p->str))) {
+ virReportOOMError();
+ virConfFree(conf);
+ return -1;
+ }
+ }
+
+ p = virConfGetValue(conf, "host_id");
+ CHECK_TYPE("host_id", VIR_CONF_LONG);
+ if (p) driver->hostID = p->l;
+
p = virConfGetValue(conf, "require_lease_for_disks");
CHECK_TYPE("require_lease_for_disks", VIR_CONF_LONG);
if (p)
driver->requireLeaseForDisks = p->l;
+ else
+ driver->requireLeaseForDisks = !driver->autoDiskLease;
virConfFree(conf);
return 0;
}
+static int virLockManagerSanlockSetupLockspace(void)
+{
+ int fd = -1;
+ struct stat st;
+ int rv;
+ struct sanlk_lockspace ls;
+ char *path = NULL;
+
+ if (virAsprintf(&path, "%s/%s",
+ driver->autoDiskLeasePath,
+ VIR_LOCK_MANAGER_SANLOCK_AUTO_DISK_LOCKSPACE) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+ memcpy(ls.name, VIR_LOCK_MANAGER_SANLOCK_AUTO_DISK_LOCKSPACE, SANLK_NAME_LEN);
+ ls.host_id = 0; /* Doesn't matter for initialization */
+ ls.flags = 0;
+ if (!virStrcpy(ls.host_id_disk.path, path, SANLK_NAME_LEN)) {
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Lockspace path '%s' exceeded %d characters"),
+ path, SANLK_NAME_LEN);
+ goto error;
+ }
+ ls.host_id_disk.offset = 0;
+
+ /* Stage 1: Ensure the lockspace file exists on disk, has
+ * space allocated for it and is initialized with lease
+ */
+ if (stat(path, &st) < 0) {
+ VIR_DEBUG("Lockspace %s does not yet exist", path);
+ if ((fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
+ if (errno != EEXIST) {
+ virReportSystemError(errno,
+ _("Unable to create lockspace %s"),
+ path);
+ goto error;
+ }
+ VIR_DEBUG("Someone else just created lockspace %s", path);
+ } else {
+ if ((rv = sanlock_direct_align(&ls.host_id_disk)) < 0) {
+ if (rv <= -200)
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to query sector size %s: error %d"),
+ path, rv);
+ else
+ virReportSystemError(-rv,
+ _("Unable to query sector size %s"),
+ path);
+ goto error_unlink;
+ }
+
+ /*
+ * Pre allocate enough data for 1 block of leases at preferred alignment
+ */
+ if (safezero(fd, 0, 0, rv) < 0) {
+ virReportSystemError(errno,
+ _("Unable to allocate lockspace %s"),
+ path);
+ goto error_unlink;
+ }
+
+ if (VIR_CLOSE(fd) < 0) {
+ virReportSystemError(errno,
+ _("Unable to save lockspace %s"),
+ path);
+ goto error_unlink;
+ }
+
+ if ((rv = sanlock_direct_init(&ls, NULL, 0, 0, 0)) < 0) {
+ if (rv <= -200)
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to initialize lockspace %s: error %d"),
+ path, rv);
+ else
+ virReportSystemError(-rv,
+ _("Unable to initialize lockspace %s"),
+ path);
+ goto error_unlink;
+ }
+ VIR_DEBUG("Lockspace %s has been initialized", path);
+ }
+ }
+
+ ls.host_id = driver->hostID;
+ /* Stage 2: Try to register the lockspace with the daemon.
+ * If the lockspace is already registered, we should get EEXIST back
+ * in which case we can just carry on with life
+ */
+ if ((rv = sanlock_add_lockspace(&ls, 0)) < 0) {
+ if (-rv != EEXIST) {
+ if (rv <= -200)
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to add lockspace %s: error %d"),
+ path, rv);
+ else
+ virReportSystemError(-rv,
+ _("Unable to add lockspace %s"),
+ path);
+ return -1;
+ } else {
+ VIR_DEBUG("Lockspace %s is already registered", path);
+ }
+ } else {
+ VIR_DEBUG("Lockspace %s has been registered", path);
+ }
+
+ return 0;
+
+error_unlink:
+ if (path)
+ unlink(path);
+error:
+ VIR_FORCE_CLOSE(fd);
+ VIR_FREE(path);
+ return -1;
+}
+
+
+static int virLockManagerSanlockDeinit(void);
static int virLockManagerSanlockInit(unsigned int version,
const char *configFile,
unsigned int flags)
}
driver->requireLeaseForDisks = true;
+ driver->hostID = 0;
+ driver->autoDiskLease = false;
+ if (!(driver->autoDiskLeasePath = strdup(LOCALSTATEDIR "/lib/libvirt/sanlock"))) {
+ VIR_FREE(driver);
+ virReportOOMError();
+ goto error;
+ }
if (virLockManagerSanlockLoadConfig(configFile) < 0)
- return -1;
+ goto error;
+
+ if (driver->autoDiskLease && !driver->hostID) {
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Automatic disk lease mode enabled, but no host ID is set"));
+ goto error;
+ }
+
+ if (virLockManagerSanlockSetupLockspace() < 0)
+ goto error;
return 0;
+
+error:
+ virLockManagerSanlockDeinit();
+ return -1;
}
static int virLockManagerSanlockDeinit(void)
if (!driver)
return 0;
- virLockError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Unloading sanlock plugin is forbidden"));
- return -1;
+ VIR_FREE(driver->autoDiskLeasePath);
+ VIR_FREE(driver);
+
+ return 0;
}
lock->privateData = NULL;
}
-static int virLockManagerSanlockAddResource(virLockManagerPtr lock,
- unsigned int type,
- const char *name,
- size_t nparams,
- virLockManagerParamPtr params,
- unsigned int flags)
+
+static const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+static int virLockManagerSanlockDiskLeaseName(const char *path,
+ char *str,
+ size_t strbuflen)
{
- virLockManagerSanlockPrivatePtr priv = lock->privateData;
- struct sanlk_resource *res;
+ unsigned char buf[MD5_DIGEST_SIZE];
int i;
- virCheckFlags(VIR_LOCK_MANAGER_RESOURCE_READONLY |
- VIR_LOCK_MANAGER_RESOURCE_SHARED, -1);
-
- if (priv->res_count == SANLK_MAX_RESOURCES) {
+ if (strbuflen < ((MD5_DIGEST_SIZE * 2) + 1)) {
virLockError(VIR_ERR_INTERNAL_ERROR,
- _("Too many resources %d for object"),
- SANLK_MAX_RESOURCES);
+ _("String length too small to store md5 checksum"));
return -1;
}
- if (type == VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK) {
- if (!(flags & (VIR_LOCK_MANAGER_RESOURCE_SHARED |
- VIR_LOCK_MANAGER_RESOURCE_READONLY)))
- priv->hasRWDisks = true;
- return 0;
- }
-
- if (type != VIR_LOCK_MANAGER_RESOURCE_TYPE_LEASE)
- return 0;
-
- if (flags & VIR_LOCK_MANAGER_RESOURCE_READONLY) {
- virLockError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("Readonly leases are not supported"));
+ if (!(md5_buffer(path, strlen(path), buf))) {
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to compute md5 checksum"));
return -1;
}
- if (flags & VIR_LOCK_MANAGER_RESOURCE_SHARED) {
- virLockError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("Sharable leases are not supported"));
- return -1;
+
+ for (i = 0 ; i < MD5_DIGEST_SIZE ; i++) {
+ str[i*2] = hex[(buf[i] >> 4) & 0xf];
+ str[(i*2)+1] = hex[buf[i] & 0xf];
}
+ str[(MD5_DIGEST_SIZE*2)+1] = '\0';
+ return 0;
+}
+
+static int virLockManagerSanlockAddLease(virLockManagerPtr lock,
+ const char *name,
+ size_t nparams,
+ virLockManagerParamPtr params)
+{
+ virLockManagerSanlockPrivatePtr priv = lock->privateData;
+ int ret = -1;
+ struct sanlk_resource *res = NULL;
+ int i;
if (VIR_ALLOC_VAR(res, struct sanlk_disk, 1) < 0) {
virReportOOMError();
- return -1;
+ goto cleanup;
}
res->num_disks = 1;
virLockError(VIR_ERR_INTERNAL_ERROR,
_("Resource name '%s' exceeds %d characters"),
name, SANLK_NAME_LEN);
- goto error;
+ goto cleanup;
}
for (i = 0; i < nparams; i++) {
virLockError(VIR_ERR_INTERNAL_ERROR,
_("Lease path '%s' exceeds %d characters"),
params[i].value.str, SANLK_PATH_LEN);
- goto error;
+ goto cleanup;
}
} else if (STREQ(params[i].key, "offset")) {
res->disks[0].offset = params[i].value.ul;
virLockError(VIR_ERR_INTERNAL_ERROR,
_("Resource lockspace '%s' exceeds %d characters"),
params[i].value.str, SANLK_NAME_LEN);
- goto error;
+ goto cleanup;
}
}
}
priv->res_args[priv->res_count] = res;
priv->res_count++;
+
+ ret = 0;
+
+cleanup:
+ if (ret == -1)
+ VIR_FREE(res);
+ return ret;
+}
+
+
+
+
+static int virLockManagerSanlockAddDisk(virLockManagerPtr lock,
+ const char *name,
+ size_t nparams,
+ virLockManagerParamPtr params ATTRIBUTE_UNUSED)
+{
+ virLockManagerSanlockPrivatePtr priv = lock->privateData;
+ int ret = -1;
+ struct sanlk_resource *res = NULL;
+ char *path = NULL;
+
+ if (nparams) {
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Unexpected lock parameters for disk resource"));
+ return -1;
+ }
+
+ if (VIR_ALLOC_VAR(res, struct sanlk_disk, 1) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ res->num_disks = 1;
+ if (virLockManagerSanlockDiskLeaseName(name, res->name, SANLK_NAME_LEN) < 0)
+ goto cleanup;
+
+ if (virAsprintf(&path, "%s/%s",
+ driver->autoDiskLeasePath, res->name) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ if (!virStrcpy(res->disks[0].path, path, SANLK_PATH_LEN)) {
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Lease path '%s' exceeds %d characters"),
+ path, SANLK_PATH_LEN);
+ goto cleanup;
+ }
+
+ if (!virStrcpy(res->lockspace_name,
+ VIR_LOCK_MANAGER_SANLOCK_AUTO_DISK_LOCKSPACE,
+ SANLK_NAME_LEN)) {
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Resource lockspace '%s' exceeds %d characters"),
+ VIR_LOCK_MANAGER_SANLOCK_AUTO_DISK_LOCKSPACE, SANLK_NAME_LEN);
+ goto cleanup;
+ }
+
+ priv->res_args[priv->res_count] = res;
+ priv->res_count++;
+
+ ret = 0;
+
+cleanup:
+ if (ret == -1)
+ VIR_FREE(res);
+ VIR_FREE(path);
+ return ret;
+}
+
+
+static int virLockManagerSanlockCreateLease(struct sanlk_resource *res)
+{
+ int fd = -1;
+ struct stat st;
+ int rv;
+
+ if (stat(res->disks[0].path, &st) < 0) {
+ VIR_DEBUG("Lockspace %s does not yet exist", res->disks[0].path);
+ if ((fd = open(res->disks[0].path, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
+ if (errno != EEXIST) {
+ virReportSystemError(errno,
+ _("Unable to create lockspace %s"),
+ res->disks[0].path);
+ return -1;
+ }
+ VIR_DEBUG("Someone else just created lockspace %s", res->disks[0].path);
+ } else {
+ if ((rv = sanlock_direct_align(&res->disks[0])) < 0) {
+ if (rv <= -200)
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to query sector size %s: error %d"),
+ res->disks[0].path, rv);
+ else
+ virReportSystemError(-rv,
+ _("Unable to query sector size %s"),
+ res->disks[0].path);
+ goto error_unlink;
+ }
+
+ /*
+ * Pre allocate enough data for 1 block of leases at preferred alignment
+ */
+ if (safezero(fd, 0, 0, rv) < 0) {
+ virReportSystemError(errno,
+ _("Unable to allocate lease %s"),
+ res->disks[0].path);
+ goto error_unlink;
+ }
+
+ if (VIR_CLOSE(fd) < 0) {
+ virReportSystemError(errno,
+ _("Unable to save lease %s"),
+ res->disks[0].path);
+ goto error_unlink;
+ }
+
+ if ((rv = sanlock_direct_init(NULL, res, 0, 0, 0)) < 0) {
+ if (rv <= -200)
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to initialize lease %s: error %d"),
+ res->disks[0].path, rv);
+ else
+ virReportSystemError(-rv,
+ _("Unable to initialize lease %s"),
+ res->disks[0].path);
+ goto error_unlink;
+ }
+ VIR_DEBUG("Lease %s has been initialized", res->disks[0].path);
+ }
+ }
+
return 0;
-error:
- VIR_FREE(res);
+error_unlink:
+ unlink(res->disks[0].path);
+ VIR_FORCE_CLOSE(fd);
return -1;
}
+
+static int virLockManagerSanlockAddResource(virLockManagerPtr lock,
+ unsigned int type,
+ const char *name,
+ size_t nparams,
+ virLockManagerParamPtr params,
+ unsigned int flags)
+{
+ virLockManagerSanlockPrivatePtr priv = lock->privateData;
+
+ virCheckFlags(VIR_LOCK_MANAGER_RESOURCE_READONLY |
+ VIR_LOCK_MANAGER_RESOURCE_SHARED, -1);
+
+ if (priv->res_count == SANLK_MAX_RESOURCES) {
+ virLockError(VIR_ERR_INTERNAL_ERROR,
+ _("Too many resources %d for object"),
+ SANLK_MAX_RESOURCES);
+ return -1;
+ }
+
+ if (flags & VIR_LOCK_MANAGER_RESOURCE_READONLY) {
+ virLockError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Readonly leases are not supported"));
+ return -1;
+ }
+ if (flags & VIR_LOCK_MANAGER_RESOURCE_SHARED) {
+ virLockError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Sharable leases are not supported"));
+ return -1;
+ }
+
+ switch (type) {
+ case VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK:
+ if (driver->autoDiskLease) {
+ if (virLockManagerSanlockAddDisk(lock, name, nparams, params) < 0)
+ return -1;
+
+ if (virLockManagerSanlockCreateLease(priv->res_args[priv->res_count-1]) < 0)
+ return -1;
+ } else {
+ if (!(flags & (VIR_LOCK_MANAGER_RESOURCE_SHARED |
+ VIR_LOCK_MANAGER_RESOURCE_READONLY)))
+ priv->hasRWDisks = true;
+ /* Ignore disk resources without error */
+ }
+ break;
+
+ case VIR_LOCK_MANAGER_RESOURCE_TYPE_LEASE:
+ if (virLockManagerSanlockAddLease(lock, name, nparams, params) < 0)
+ return -1;
+ break;
+
+ default:
+ /* Ignore other resources, without error */
+ break;
+ }
+
+ return 0;
+}
+
static int virLockManagerSanlockAcquire(virLockManagerPtr lock,
const char *state,
unsigned int flags,