[libvirt] [PATCH 10/14] Add a 'fcntl' lock driver which uses the simple lock database

Daniel P. Berrange berrange at redhat.com
Thu Jul 7 14:17:28 UTC 2011


From: "Daniel P. Berrange" <berrange at redhat.com>

The 'fcntl' lock driver is a implementation of the lock manager
driver API, which will be linked directly into the virtlockd
process. It will use the simple lock database added in the
previous commit to acquire/release locks for leases or disks
associated with virtual machines.

* src/locking/lock_driver_fcntl.c, src/locking/lock_driver_fcntl.h:
  Add fcntl lock driver implementation
---
 bootstrap.conf                  |    1 +
 src/locking/lock_driver_fcntl.c |  929 +++++++++++++++++++++++++++++++++++++++
 src/locking/lock_driver_fcntl.h |    4 +
 3 files changed, 934 insertions(+), 0 deletions(-)
 create mode 100644 src/locking/lock_driver_fcntl.c
 create mode 100644 src/locking/lock_driver_fcntl.h

diff --git a/bootstrap.conf b/bootstrap.conf
index 581d60b..f78d167 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -34,6 +34,7 @@ connect
 configmake
 count-one-bits
 crypto/md5
+crypto/sha256
 dirname-lgpl
 fcntl-h
 fnmatch
diff --git a/src/locking/lock_driver_fcntl.c b/src/locking/lock_driver_fcntl.c
new file mode 100644
index 0000000..904be45
--- /dev/null
+++ b/src/locking/lock_driver_fcntl.c
@@ -0,0 +1,929 @@
+/*
+ * lock_driver_fcntl.c: A lock driver using fcntl()
+ *
+ * Copyright (C) 2010-2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ */
+
+#include <config.h>
+
+#include <stdbool.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "lock_driver_fcntl.h"
+#include "lock_database.h"
+#include "memory.h"
+#include "logging.h"
+#include "uuid.h"
+#include "util.h"
+#include "files.h"
+#include "threads.h"
+#include "virterror_internal.h"
+#include "conf.h"
+#include "configmake.h"
+#include "sha256.h"
+#include "hash.h"
+
+#define VIR_FROM_THIS VIR_FROM_LOCKING
+
+#define virLockError(code, ...)                                   \
+    virReportErrorHelper(VIR_FROM_THIS, code, __FILE__,           \
+                         __FUNCTION__, __LINE__, __VA_ARGS__)
+
+typedef struct _virLockManagerFcntlResource virLockManagerFcntlResource;
+typedef virLockManagerFcntlResource *virLockManagerFcntlResourcePtr;
+
+typedef struct _virLockManagerFcntlObject virLockManagerFcntlObject;
+typedef virLockManagerFcntlObject *virLockManagerFcntlObjectPtr;
+
+typedef struct _virLockManagerFcntlPrivate virLockManagerFcntlPrivate;
+typedef virLockManagerFcntlPrivate *virLockManagerFcntlPrivatePtr;
+
+#define VIR_LOCK_FCNTL_LOCKSPACE_FILES_NAME \
+    "org.libvirt.lockd.files"
+
+struct _virLockManagerFcntlResource {
+    char *path;
+    char *lockspace;
+    char *key;
+    bool shared;
+    bool active;
+};
+
+struct _virLockManagerFcntlObject {
+    char *hashKey;
+
+    int refs;
+    bool dead; /* True if the primary client has gone */
+
+    char *name;
+    unsigned char uuid[VIR_UUID_BUFLEN];
+    pid_t pid;
+
+    /* Resources currently held by the object */
+    size_t nresources;
+    virLockManagerFcntlResourcePtr *resources;
+};
+
+struct _virLockManagerFcntlPrivate {
+    pid_t pid;
+    virLockManagerFcntlObjectPtr object;
+
+    /* Resources added, but yet to be acquired/released */
+    size_t nresources;
+    virLockManagerFcntlResourcePtr *resources;
+};
+
+struct _virLockManagerFcntlDriver {
+    virMutex lock;
+    char *autoDiskLeasePath;
+    virLockDatabasePtr lockDB;
+    virHashTablePtr objects;
+} *driver;
+
+
+static char *virLockManagerFcntlNewHash(const char *key)
+{
+    char buf[SHA256_DIGEST_SIZE];
+    char *sum;
+    const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
+                           '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+    size_t i;
+
+    if (!(sha256_buffer(key, strlen(key), buf)))
+        return NULL;
+
+    if (VIR_ALLOC_N(sum, (SHA256_DIGEST_SIZE * 2) + 1) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    for (i = 0 ; i < SHA256_DIGEST_SIZE ; i++) {
+        sum[i*2] = hex[(buf[i] >> 4) & 0xf];
+        sum[(i*2)+1] = hex[buf[i] & 0xf];
+    }
+    sum[(SHA256_DIGEST_SIZE*2)] = '\0';
+
+    return sum;
+}
+
+
+
+static void virLockManagerFcntlResourceFree(virLockManagerFcntlResourcePtr res)
+{
+    if (!res)
+        return;
+
+    VIR_FREE(res->path);
+    VIR_FREE(res->lockspace);
+    VIR_FREE(res->key);
+
+    VIR_FREE(res);
+}
+
+
+static void virLockManagerFcntlObjectFree(virLockManagerFcntlObjectPtr object)
+{
+    size_t i;
+
+    if (!object)
+        return;
+
+    object->refs--;
+    if (object->refs)
+        return;
+
+    VIR_FREE(object->hashKey);
+    VIR_FREE(object->name);
+    if (object->resources) {
+        for (i = 0 ; i < object->nresources ; i++)
+            virLockManagerFcntlResourceFree(object->resources[i]);
+        VIR_FREE(object->resources);
+    }
+
+    VIR_FREE(object);
+}
+
+
+static void virLockManagerFcntlPrivateFree(virLockManagerFcntlPrivatePtr priv)
+{
+    size_t i;
+
+    if (!priv)
+        return;
+
+    virLockManagerFcntlObjectFree(priv->object);
+
+    if (priv->resources) {
+        for (i = 0 ; i < priv->nresources ; i++)
+            virLockManagerFcntlResourceFree(priv->resources[i]);
+        VIR_FREE(priv->resources);
+    }
+
+    VIR_FREE(priv);
+}
+
+
+static virLockManagerFcntlObjectPtr
+virLockManagerFcntlObjectNew(const char *name,
+                             unsigned char *uuid,
+                             pid_t pid)
+{
+    virLockManagerFcntlObjectPtr obj;
+
+    if (VIR_ALLOC(obj) < 0)
+        goto no_memory;
+
+    if (!(obj->name = strdup(name)))
+        goto no_memory;
+    memcpy(obj->uuid, uuid, VIR_UUID_BUFLEN);
+    obj->pid = pid;
+    obj->refs = 1;
+
+    return obj;
+
+no_memory:
+    virReportOOMError();
+    virLockManagerFcntlObjectFree(obj);
+    return NULL;
+}
+
+static virLockManagerFcntlPrivatePtr
+virLockManagerFcntlPrivateNewAttach(virLockManagerFcntlObjectPtr object,
+                                    pid_t clientPid)
+{
+    virLockManagerFcntlPrivatePtr priv;
+
+    if (VIR_ALLOC(priv) < 0)
+        goto no_memory;
+
+    object->refs++;
+    priv->object = object;
+    priv->pid = clientPid;
+
+    return priv;
+
+no_memory:
+    virReportOOMError();
+    return NULL;
+}
+
+
+static virLockManagerFcntlPrivatePtr
+virLockManagerFcntlPrivateNew(const char *name,
+                              unsigned char *uuid,
+                              pid_t pid,
+                              pid_t clientPid)
+{
+    virLockManagerFcntlPrivatePtr priv;
+
+    if (VIR_ALLOC(priv) < 0)
+        goto no_memory;
+
+    if (!(priv->object = virLockManagerFcntlObjectNew(name, uuid, pid)))
+        goto error;
+    priv->pid = clientPid;
+
+    return priv;
+
+no_memory:
+    virReportOOMError();
+error:
+    VIR_FREE(priv);
+    return NULL;
+}
+
+
+static virLockManagerFcntlResourcePtr
+virLockManagerFcntlResourceNew(const char *path,
+                               const char *lockspace,
+                               const char *key,
+                               bool hashKey,
+                               bool shared)
+{
+    virLockManagerFcntlResourcePtr res;
+
+    if (VIR_ALLOC(res) < 0)
+        goto no_memory;
+
+    if (!(res->path = strdup(path)))
+        goto no_memory;
+    if (!(res->lockspace = strdup(lockspace)))
+        goto no_memory;
+    if (hashKey) {
+        if (!(res->key = virLockManagerFcntlNewHash(key)))
+            goto error;
+    } else {
+        if (!(res->key = strdup(key)))
+            goto no_memory;
+    }
+
+    res->shared = shared;
+
+    return res;
+
+no_memory:
+    virReportOOMError();
+error:
+    virLockManagerFcntlResourceFree(res);
+    return NULL;
+}
+
+
+#if 0
+/*
+ * sanlock plugin for the libvirt virLockManager API
+ */
+static int virLockManagerFcntlLoadConfig(const char *configFile)
+{
+    virConfPtr conf;
+    virConfValuePtr p;
+
+    if (access(configFile, R_OK) == -1) {
+        if (errno != ENOENT) {
+            virReportSystemError(errno,
+                                 _("Unable to access config file %s"),
+                                 configFile);
+            return -1;
+        }
+        return 0;
+    }
+
+    if (!(conf = virConfReadFile(configFile, 0)))
+        return -1;
+
+# define CHECK_TYPE(name,typ) if (p && p->type != (typ)) {              \
+        virLockError(VIR_ERR_INTERNAL_ERROR,                            \
+                     "%s: %s: expected type " #typ,                     \
+                     configFile, (name));                               \
+        virConfFree(conf);                                              \
+        return -1;                                                      \
+    }
+
+    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;
+        }
+    }
+
+    virConfFree(conf);
+    return 0;
+}
+#endif
+
+static char *virLockManagerFcntlPath(bool privileged)
+{
+    char *path;
+    if (privileged) {
+        if (!(path = strdup(LOCALSTATEDIR "/lib/libvirt/lockd"))) {
+            virReportOOMError();
+            return NULL;
+        }
+    } else {
+        char *userdir;
+        if (!(userdir = virGetUserDirectory(geteuid())))
+            return NULL;
+
+        if (virAsprintf(&path, "%s/.libvirt/lockd", userdir) < 0) {
+            virReportOOMError();
+        }
+        VIR_FREE(userdir);
+    }
+    return path;
+}
+
+
+
+static int virLockManagerFcntlInit(unsigned int version ATTRIBUTE_UNUSED,
+                                   const char *configFile ATTRIBUTE_UNUSED,
+                                   unsigned int flags ATTRIBUTE_UNUSED)
+{
+    unsigned char hostuuid[VIR_UUID_BUFLEN];
+    char *hostname = virGetHostname(NULL);
+    if (!hostname)
+        return -1;
+
+    if (driver) {
+        virLockError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Driver already initialized"));
+        return -1;
+    }
+
+    if (VIR_ALLOC(driver) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    if (virMutexInit(&driver->lock) < 0) {
+        virLockError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Unable to initialize driver mutex"));
+        VIR_FREE(driver);
+        return -1;
+    }
+
+    if (!(driver->lockDB = virLockDatabaseNew(hostuuid, hostname)))
+        goto error;
+
+    if (!(driver->autoDiskLeasePath = virLockManagerFcntlPath(getuid() == 0))) {
+        virReportOOMError();
+        goto error;
+    }
+
+    if (!(driver->objects = virHashCreate(10, NULL)))
+        goto error;
+
+#if 0
+    if (virLockManagerFcntlLoadConfig(configFile) < 0)
+        goto error;
+#endif
+
+    /* A standard lockspace to which we map virtual disk files */
+    if (virLockDatabaseCreateLockspace(driver->lockDB,
+                                       driver->autoDiskLeasePath,
+                                       VIR_LOCK_FCNTL_LOCKSPACE_FILES_NAME,
+                                       true) < 0)
+        goto error;
+
+    VIR_FREE(hostname);
+    return 0;
+
+error:
+    virLockDatabaseFree(driver->lockDB);
+    driver->lockDB = NULL;
+    VIR_FREE(driver->autoDiskLeasePath);
+    VIR_FREE(hostname);
+    return -1;
+}
+
+static int virLockManagerFcntlDeinit(void)
+{
+    virLockDatabaseFree(driver->lockDB);
+    VIR_FREE(driver->autoDiskLeasePath);
+    virMutexDestroy(&driver->lock);
+    VIR_FREE(driver);
+    return 0;
+}
+
+
+static char *virLockManagerFcntlObjectName(unsigned char *uuid,
+                                           pid_t pid)
+{
+    char *ret;
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+    virUUIDFormat(uuid, uuidstr);
+
+    if (virAsprintf(&ret, "%s-%llu", uuidstr, (long long unsigned)pid) < 0)
+        return NULL;
+    return ret;
+}
+
+
+static int virLockManagerFcntlNew(virLockManagerPtr lock,
+                                  unsigned int type,
+                                  size_t nparams,
+                                  virLockManagerParamPtr params,
+                                  unsigned int flags)
+{
+    virLockManagerFcntlPrivatePtr priv = NULL;
+    char *name = NULL;
+    pid_t pid = 0;
+    pid_t clientPid = 0;
+    unsigned char *uuid = NULL;
+    size_t i;
+    char *objectName = NULL;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+
+    virMutexLock(&driver->lock);
+
+    if (type != VIR_LOCK_MANAGER_OBJECT_TYPE_DOMAIN)
+        goto cleanup;
+
+    for (i = 0 ; i < nparams ; i++) {
+        virLockManagerParamPtr param = &params[i];
+        if (STREQ(param->key, "uuid")) {
+            uuid = param->value.uuid;
+        } else if (STREQ(param->key, "name")) {
+            name = param->value.str;
+        } else if (STREQ(param->key, "pid")) {
+            pid = param->value.ui;
+        } else if (STREQ(param->key, "client-pid")) {
+            clientPid = param->value.ui;
+        }
+    }
+
+    if (!name) {
+        virLockError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Missing 'name' parameter for object lock"));
+        goto cleanup;
+    }
+
+    if (!pid) {
+        virLockError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Missing 'pid' parameter for object lock"));
+        goto cleanup;
+    }
+
+    if (!clientPid) {
+        virLockError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Missing 'client-pid' parameter for object lock"));
+        goto cleanup;
+    }
+
+    if (!uuid) {
+        virLockError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Missing 'uuid' parameter for object lock"));
+        goto cleanup;
+    }
+
+    if (!virUUIDIsValid(uuid)) {
+        virLockError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Malformed 'uuid' parameter for object lock"));
+        goto cleanup;
+    }
+
+    objectName = virLockManagerFcntlObjectName(uuid, pid);
+    if (clientPid == pid) {
+        if (!(priv = virLockManagerFcntlPrivateNew(name, uuid, pid, clientPid)))
+            goto cleanup;
+
+        if (virHashAddEntry(driver->objects, objectName, priv->object) < 0)
+            goto cleanup;
+
+        priv->object->hashKey = objectName;
+        objectName = NULL;
+    } else {
+        virLockManagerFcntlObjectPtr object = virHashLookup(driver->objects, objectName);
+
+        if (!object) {
+            virLockError(VIR_ERR_INTERNAL_ERROR,
+                         _("No active connection for object %s (%s)"),
+                         objectName, name);
+            goto cleanup;
+        }
+
+        if (!(priv = virLockManagerFcntlPrivateNewAttach(object, clientPid)))
+            goto cleanup;
+    }
+
+    lock->privateData = priv;
+
+    ret = 0;
+
+cleanup:
+    if (ret != 0)
+        virLockManagerFcntlPrivateFree(priv);
+    VIR_FREE(objectName);
+    virMutexUnlock(&driver->lock);
+    return ret;
+}
+
+
+static int virLockManagerFcntlAddResource(virLockManagerPtr lock,
+                                          unsigned int type,
+                                          const char *name,
+                                          size_t nparams,
+                                          virLockManagerParamPtr params,
+                                          unsigned int flags)
+{
+    virLockManagerFcntlPrivatePtr priv = lock->privateData;
+    virLockManagerFcntlResourcePtr res;
+    const char *lockspace = NULL;
+    const char *path = NULL;
+    bool hashName = false;
+    size_t i;
+    int ret = 0;
+
+    virCheckFlags(VIR_LOCK_MANAGER_RESOURCE_SHARED |
+                  VIR_LOCK_MANAGER_RESOURCE_READONLY, -1);
+
+    if ((flags & VIR_LOCK_MANAGER_RESOURCE_READONLY) &&
+        (flags & VIR_LOCK_MANAGER_RESOURCE_SHARED)) {
+        virLockError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                     _("Lease flags cannot request 'shared' and 'readonly' at the same time"));
+        return -1;
+    }
+
+    if (flags & VIR_LOCK_MANAGER_RESOURCE_READONLY)
+        return 0;
+
+    virMutexLock(&driver->lock);
+
+    switch (type) {
+    case VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK:
+        hashName = true;
+        path = driver->autoDiskLeasePath;
+        lockspace = VIR_LOCK_FCNTL_LOCKSPACE_FILES_NAME;
+        break;
+
+    case VIR_LOCK_MANAGER_RESOURCE_TYPE_LEASE:
+        for (i = 0 ; i < nparams ; i++) {
+            virLockManagerParamPtr param = &params[i];
+            if (STREQ(param->key, "path")) {
+                path = param->value.str;
+            } else if (STREQ(param->key, "lockspace")) {
+                lockspace = param->value.str;
+            } else if (STREQ(param->key, "offset")) {
+                if (param->value.ul) {
+                    virLockError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                                 _("lease offset must be zero"));
+                    goto cleanup;
+                }
+            } else {
+                virLockError(VIR_ERR_CONFIG_UNSUPPORTED,
+                             _("Unexpected parameter %s"),
+                             param->key);
+                goto cleanup;
+            }
+        }
+        break;
+
+    default:
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Unsupported resource type %d"),
+                     type);
+        goto cleanup;
+    }
+
+    if (!lockspace) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Missing lockspace for resource %s"), name);
+        goto cleanup;
+    }
+
+    if (!path) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("Missing lock path for resource %s"), name);
+        goto cleanup;
+    }
+
+    if (priv->object->dead) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("The object connection for %s has died"),
+                     priv->object->name);
+        goto cleanup;
+    }
+
+    if (!(res = virLockManagerFcntlResourceNew(path, lockspace, name, hashName,
+                                               flags & VIR_LOCK_MANAGER_RESOURCE_SHARED)))
+        goto cleanup;
+
+    if (VIR_EXPAND_N(priv->resources, priv->nresources, 1) < 0) {
+        virLockManagerFcntlResourceFree(res);
+        goto cleanup;
+    }
+    priv->resources[priv->nresources-1] = res;
+
+    ret = 0;
+
+cleanup:
+    virMutexUnlock(&driver->lock);
+    return ret;
+}
+
+
+static int virLockManagerFcntlAcquire(virLockManagerPtr lock,
+                                      const char *state ATTRIBUTE_UNUSED,
+                                      unsigned int flags,
+                                      int *fd ATTRIBUTE_UNUSED)
+{
+    virLockManagerFcntlPrivatePtr priv = lock->privateData;
+    size_t i = 0;
+    int ret = -1;
+
+    virCheckFlags(VIR_LOCK_MANAGER_ACQUIRE_RESTRICT |
+                  VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY, -1);
+
+    if (flags & VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY)
+        return 0;
+
+    virMutexLock(&driver->lock);
+
+    if (priv->object->dead) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("The object connection for %s has died"),
+                     priv->object->name);
+        goto cleanup;
+    }
+
+    if (VIR_EXPAND_N(priv->object->resources,
+                     priv->object->nresources,
+                     priv->nresources) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    for (i = 0 ; i < priv->nresources ; i++) {
+        if (virLockDatabaseAcquireLease(driver->lockDB,
+                                        priv->resources[i]->path,
+                                        priv->resources[i]->lockspace,
+                                        priv->resources[i]->key,
+                                        priv->resources[i]->shared) < 0) {
+            i--;
+            goto error;
+        }
+        priv->resources[i]->active = true;
+    }
+
+    for (i = 0 ; i < priv->nresources ; i++) {
+        priv->object->resources[priv->object->nresources-priv->nresources+i]
+            = priv->resources[i];
+    }
+    VIR_FREE(priv->resources);
+    priv->nresources = 0;
+
+    ret = 0;
+
+cleanup:
+    virMutexUnlock(&driver->lock);
+    return ret;
+
+error:
+    for (i = 0 ; i < priv->nresources ; i++) {
+        if (priv->resources[i]->active &&
+            virLockDatabaseReleaseLease(driver->lockDB,
+                                        priv->resources[i]->path,
+                                        priv->resources[i]->lockspace,
+                                        priv->resources[i]->key,
+                                        priv->resources[i]->shared) < 0)
+            VIR_WARN("Cannot release resource after failed lock");
+        priv->resources[i]->active = false;
+    }
+    VIR_SHRINK_N(priv->object->resources,
+                 priv->object->nresources,
+                 priv->nresources);
+    goto cleanup;
+}
+
+
+static int virLockManagerFcntlRelease(virLockManagerPtr lock,
+                                      char **state,
+                                      unsigned int flags)
+{
+    virLockManagerFcntlPrivatePtr priv = lock->privateData;
+    size_t i;
+    size_t j;
+    int ret = -1;
+    bool failed = false;
+    int *indexes;
+
+    virCheckFlags(0, -1);
+
+    *state = NULL;
+
+    virMutexLock(&driver->lock);
+
+    if (priv->object->dead) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("The object connection for %s has died"),
+                     priv->object->name);
+        goto cleanup;
+    }
+
+    if (VIR_ALLOC_N(indexes, priv->nresources) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    /* Make sure the resources requested for release are in fact
+     * currently held by this object
+     */
+    for (i = 0 ; i < priv->nresources ; i++) {
+        bool match = false;
+        for (j = 0 ; j < priv->object->nresources ; j++) {
+            if (STREQ(priv->resources[i]->path,
+                      priv->object->resources[j]->path) &&
+                STREQ(priv->resources[i]->lockspace,
+                      priv->object->resources[j]->lockspace) &&
+                STREQ(priv->resources[i]->key,
+                      priv->object->resources[j]->key)) {
+                match = true;
+                indexes[i] = j;
+                break;
+            }
+        }
+
+        if (!match) {
+            virLockError(VIR_ERR_INTERNAL_ERROR,
+                         _("Lease %s in lockspace %s at %s is not held by this object"),
+                         priv->resources[i]->key,
+                         priv->resources[i]->lockspace,
+                         priv->resources[i]->path);
+            VIR_FREE(indexes);
+            return -1;
+        }
+    }
+
+
+    /*
+     * Acquire all the new leases
+     */
+    for (i = 0 ; i < priv->nresources ; i++) {
+        if (virLockDatabaseReleaseLease(driver->lockDB,
+                                        priv->resources[i]->path,
+                                        priv->resources[i]->lockspace,
+                                        priv->resources[i]->key,
+                                        priv->resources[i]->shared) < 0) {
+            failed = true;
+        } else {
+            priv->object->resources[indexes[i]]->active = false;
+        }
+    }
+
+    /*
+     * Move the newly held resources to the object resource list
+     */
+    for (i = 0, j = 0 ; i < priv->object->nresources ; i++) {
+        if (priv->object->resources[i]->active) {
+            priv->object->resources[j] = priv->object->resources[i];
+            j++;
+        } else {
+            virLockManagerFcntlResourceFree(priv->object->resources[i]);
+        }
+    }
+    VIR_SHRINK_N(priv->object->resources,
+                 priv->object->nresources,
+                 j);
+
+    VIR_FREE(indexes);
+
+    ret = failed ? -1 : 0;
+
+cleanup:
+    virMutexUnlock(&driver->lock);
+    return ret;
+}
+
+
+static int virLockManagerFcntlInquire(virLockManagerPtr lock ATTRIBUTE_UNUSED,
+                                      char **state,
+                                      unsigned int flags)
+{
+    virLockManagerFcntlPrivatePtr priv = lock->privateData;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+
+    *state = NULL;
+
+    virMutexLock(&driver->lock);
+
+    if (priv->object->dead) {
+        virLockError(VIR_ERR_INTERNAL_ERROR,
+                     _("The object connection for %s has died"),
+                     priv->object->name);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    virMutexUnlock(&driver->lock);
+    return ret;
+}
+
+
+static void virLockManagerFcntlFree(virLockManagerPtr lock)
+{
+    virLockManagerFcntlPrivatePtr priv = lock->privateData;
+
+    /*
+     * If the primary object PID closed its connection,
+     * we need to make sure it is really dead and release
+     * its resources
+     */
+    if (priv->pid == priv->object->pid) {
+        size_t i;
+
+        VIR_DEBUG("Killing off PID %llu hash key %s",
+                  (unsigned long long)priv->pid, priv->object->hashKey);
+
+        virHashRemoveEntry(driver->objects, priv->object->hashKey);
+
+        /*
+         * Kill off all held leases
+         */
+        for (i = 0 ; i < priv->object->nresources ; i++) {
+            if (virLockDatabaseReleaseLease(driver->lockDB,
+                                            priv->object->resources[i]->path,
+                                            priv->object->resources[i]->lockspace,
+                                            priv->object->resources[i]->key,
+                                            priv->object->resources[i]->shared) < 0) {
+                VIR_WARN("Unable to release resource %s in %s at %s",
+                         priv->object->resources[i]->key,
+                         priv->object->resources[i]->lockspace,
+                         priv->object->resources[i]->path);
+            }
+        }
+
+        /* This loop sends SIGTERM, then waits a few iterations
+         * (1.6 seconds) to see if it dies. If still alive then
+         * it does SIGKILL, and waits a few more iterations (1.6
+         * seconds more) to confirm that it has really gone.
+         */
+        for (i = 0 ; i < 15 ; i++) {
+            int signum;
+            if (i == 0)
+                signum = SIGTERM;
+            else if (i == 8)
+                signum = SIGKILL;
+            else
+                signum = 0; /* Just check for existence */
+
+            if (virKillProcess(priv->object->pid, signum) < 0) {
+                if (errno != ESRCH) {
+                    char ebuf[1024];
+                    VIR_WARN("Failed to kill process %d %s",
+                             priv->object->pid, virStrerror(errno, ebuf, sizeof ebuf));
+                }
+                break;
+            }
+
+            usleep(200 * 1000);
+        }
+
+        priv->object->dead = true;
+    }
+
+    virLockManagerFcntlPrivateFree(priv);
+}
+
+virLockDriver virLockDriverFcntl =
+{
+    .version = VIR_LOCK_MANAGER_VERSION,
+
+    .drvInit = virLockManagerFcntlInit,
+    .drvDeinit = virLockManagerFcntlDeinit,
+
+    .drvNew = virLockManagerFcntlNew,
+    .drvFree = virLockManagerFcntlFree,
+
+    .drvAddResource = virLockManagerFcntlAddResource,
+
+    .drvAcquire = virLockManagerFcntlAcquire,
+    .drvRelease = virLockManagerFcntlRelease,
+
+    .drvInquire = virLockManagerFcntlInquire,
+};
diff --git a/src/locking/lock_driver_fcntl.h b/src/locking/lock_driver_fcntl.h
new file mode 100644
index 0000000..c539fe1
--- /dev/null
+++ b/src/locking/lock_driver_fcntl.h
@@ -0,0 +1,4 @@
+
+#include "lock_driver.h"
+
+extern virLockDriver virLockDriverFcntl;
-- 
1.7.6




More information about the libvir-list mailing list