[libvirt] [PATCH 00/12]

An update of https://www.redhat.com/archives/libvir-list/2012-August/msg00618.html Mostly this is a rebase to latest GIT. Alot of the questions raised with review last time bogged down on whether the APIs were design in the right way. I think I clarified their intended usage, so hopefully we can do code review this time :-) There are still a few more patches to add on to this - Serialize the tx/rx message queues so we don't loose partially sent/received messages across re-exec() - Make fcntl driver more configurable in its behaviour - Add support for leases based on LVM UUID and SCSI WWN

From: "Daniel P. Berrange" <berrange@redhat.com> The previously introduced virFile{Lock,Unlock} APIs provide a way to acquire/release fcntl() locks on individual files. For unknown reason though, the POSIX spec says that fcntl() locks are released when *any* file handle referring to the same path is closed. In the following sequence threadA: fd1 = open("foo") threadB: fd2 = open("foo") threadA: virFileLock(fd1) threadB: virFileLock(fd2) threadB: close(fd2) you'd expect threadA to come out holding a lock on 'foo', and indeed it does hold a lock for a very short time. Unfortunately when threadB does close(fd2) this releases the lock associated with fd1. For the current libvirt use case for virFileLock - pidfiles - this doesn't matter since the lock is acquired at startup while single threaded an never released until exit. To provide a more generally useful API though, it is necessary to introduce a slightly higher level abstraction, which is to be referred to as a "lockspace". This is to be provided by a virLockSpacePtr object in src/util/virlockspace.{c,h}. The core idea is that the lockspace keeps track of what files are already open+locked. This means that when a 2nd thread comes along and tries to acquire a lock, it doesn't end up opening and closing a new FD. The lockspace just checks the current list of held locks and immediately returns VIR_ERR_RESOURCE_BUSY. NB, the API as it stands is designed on the basis that the files being locked are not being otherwise opened and used by the application code. One approach to using this API is to acquire locks based on a hash of the filepath. eg to lock /var/lib/libvirt/images/foo.img the application might do virLockSpacePtr lockspace = virLockSpaceNew("/var/lib/libvirt/imagelocks"); lockname = md5sum("/var/lib/libvirt/images/foo.img"); virLockSpaceAcquireLock(lockspace, lockname); NB, in this example, the caller should ensure that the path is canonicalized before calculating the checksum. It is also possible to do locks directly on resources by using a NULL lockspace directory and then using the file path as the lock name eg virLockSpacePtr lockspace = virLockSpaceNew(NULL); virLockSpaceAcquireLock(lockspace, "/var/lib/libvirt/images/foo.img"); This is only safe to do though if no other part of the process will be opening the files. This will be the case when this code is used inside the soon-to-be-reposted virlockd daemon Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 1 + include/libvirt/virterror.h | 4 +- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 11 + src/util/virlockspace.c | 558 ++++++++++++++++++++++++++++++++++++++++++++ src/util/virlockspace.h | 58 +++++ src/util/virterror.c | 9 +- tests/Makefile.am | 7 +- tests/virlockspacetest.c | 363 ++++++++++++++++++++++++++++ 10 files changed, 1010 insertions(+), 3 deletions(-) create mode 100644 src/util/virlockspace.c create mode 100644 src/util/virlockspace.h create mode 100644 tests/virlockspacetest.c diff --git a/.gitignore b/.gitignore index d998f0e..7919f74 100644 --- a/.gitignore +++ b/.gitignore @@ -161,6 +161,7 @@ /tests/virdrivermoduletest /tests/virhashtest /tests/virkeyfiletest +/tests/virlockspacetest /tests/virnet*test /tests/virshtest /tests/virtimetest diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 5140c38..98c2cb2 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -112,7 +112,8 @@ typedef enum { VIR_FROM_PARALLELS = 48, /* Error from Parallels */ VIR_FROM_DEVICE = 49, /* Error from Device */ - VIR_FROM_SSH = 50, /* Error from libssh2 connection transport */ + VIR_FROM_SSH = 50, /* Error from libssh2 connection transport */ + VIR_FROM_LOCKSPACE = 51, /* Error from lockspace */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST @@ -285,6 +286,7 @@ typedef enum { VIR_ERR_SSH = 85, /* error in ssh transport driver */ VIR_ERR_AGENT_UNRESPONSIVE = 86, /* guest agent is unresponsive, not running or not usable */ + VIR_ERR_RESOURCE_BUSY = 87, /* resource is already in use */ } virErrorNumber; /** diff --git a/po/POTFILES.in b/po/POTFILES.in index 7a91eb4..c5f4cf7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -148,6 +148,7 @@ src/util/virdbus.c src/util/virfile.c src/util/virhash.c src/util/virkeyfile.c +src/util/virlockspace.c src/util/virnetdev.c src/util/virnetdevbridge.c src/util/virnetdevmacvlan.c diff --git a/src/Makefile.am b/src/Makefile.am index 9f27fcf..6860e7f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -96,6 +96,7 @@ UTIL_SOURCES = \ util/virkeycode.c util/virkeycode.h \ util/virkeyfile.c util/virkeyfile.h \ util/virkeymaps.h \ + util/virlockspace.c util/virlockspace.h \ util/virmacaddr.h util/virmacaddr.c \ util/virnetdev.h util/virnetdev.c \ util/virnetdevbandwidth.h util/virnetdevbandwidth.c \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0494e1f..58a9520 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1321,6 +1321,17 @@ virKeyFileHasGroup; virKeyFileGetValueString; +# virlockspace.h +virLockSpaceAcquireResource; +virLockSpaceCreateResource; +virLockSpaceDeleteResource; +virLockSpaceFree; +virLockSpaceGetDirectory; +virLockSpaceNew; +virLockSpaceReleaseResource; +virLockSpaceReleaseResourcesForOwner; + + # virmacaddr.h virMacAddrCmp; virMacAddrCmpRaw; diff --git a/src/util/virlockspace.c b/src/util/virlockspace.c new file mode 100644 index 0000000..611592a --- /dev/null +++ b/src/util/virlockspace.c @@ -0,0 +1,558 @@ +/* + * virlockspace.c: simple file based lockspaces + * + * Copyright (C) 2012 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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include "virlockspace.h" +#include "logging.h" +#include "memory.h" +#include "virterror_internal.h" +#include "util.h" +#include "virfile.h" +#include "virhash.h" +#include "threads.h" + +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + +#define VIR_FROM_THIS VIR_FROM_LOCKSPACE + +typedef struct _virLockSpaceResource virLockSpaceResource; +typedef virLockSpaceResource *virLockSpaceResourcePtr; + +struct _virLockSpaceResource { + char *name; + char *path; + int fd; + bool lockHeld; + unsigned int flags; + size_t nOwners; + pid_t *owners; +}; + +struct _virLockSpace { + char *dir; + virMutex lock; + + virHashTablePtr resources; +}; + + +static char *virLockSpaceGetResourcePath(virLockSpacePtr lockspace, + const char *resname) +{ + char *ret; + if (lockspace->dir) { + if (virAsprintf(&ret, "%s/%s", lockspace->dir, resname) < 0) { + virReportOOMError(); + return NULL; + } + } else { + if (!(ret = strdup(resname))) { + virReportOOMError(); + return NULL; + } + } + + return ret; +} + + +static void virLockSpaceResourceFree(virLockSpaceResourcePtr res) +{ + if (!res) + return; + + if (res->lockHeld && + (res->flags & VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE)) { + if (res->flags & VIR_LOCK_SPACE_ACQUIRE_SHARED) { + /* We must upgrade to an exclusive lock to ensure + * no one else still has it before trying to delete */ + if (virFileLock(res->fd, false, 0, 1) < 0) { + VIR_DEBUG("Could not upgrade shared lease to exclusive, not deleting"); + } else { + if (unlink(res->path) < 0 && + errno != ENOENT) { + char ebuf[1024]; + VIR_WARN("Failed to unlink resource %s: %s", + res->path, virStrerror(errno, ebuf, sizeof(ebuf))); + } + } + } else { + if (unlink(res->path) < 0 && + errno != ENOENT) { + char ebuf[1024]; + VIR_WARN("Failed to unlink resource %s: %s", + res->path, virStrerror(errno, ebuf, sizeof(ebuf))); + } + } + } + + VIR_FORCE_CLOSE(res->fd); + VIR_FREE(res->path); + VIR_FREE(res->name); + VIR_FREE(res); +} + + +static virLockSpaceResourcePtr +virLockSpaceResourceNew(virLockSpacePtr lockspace, + const char *resname, + unsigned int flags, + pid_t owner) +{ + virLockSpaceResourcePtr res; + bool shared = !!(flags & VIR_LOCK_SPACE_ACQUIRE_SHARED); + + if (VIR_ALLOC(res) < 0) + return NULL; + + res->fd = -1; + res->flags = flags; + + if (!(res->name = strdup(resname))) + goto no_memory; + + if (!(res->path = virLockSpaceGetResourcePath(lockspace, resname))) + goto no_memory; + + if (flags & VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) { + while (1) { + struct stat a, b; + if ((res->fd = open(res->path, O_RDWR|O_CREAT, 0600)) < 0) { + virReportSystemError(errno, + _("Unable to open/create resource %s"), + res->path); + goto error; + } + + if (virSetCloseExec(res->fd) < 0) { + virReportSystemError(errno, + _("Failed to set close-on-exec flag '%s'"), + res->path); + goto error; + } + + if (fstat(res->fd, &b) < 0) { + virReportSystemError(errno, + _("Unable to check status of pid file '%s'"), + res->path); + goto error; + } + + if (virFileLock(res->fd, shared, 0, 1) < 0) { + if (errno == EACCES || errno == EAGAIN) { + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is locked"), + resname); + } else { + virReportSystemError(errno, + _("Unable to acquire lock on '%s'"), + res->path); + } + goto error; + } + + /* Now make sure the pidfile we locked is the same + * one that now exists on the filesystem + */ + if (stat(res->path, &a) < 0) { + char ebuf[1024] ATTRIBUTE_UNUSED; + VIR_DEBUG("Resource '%s' disappeared: %s", + res->path, virStrerror(errno, ebuf, sizeof(ebuf))); + VIR_FORCE_CLOSE(res->fd); + /* Someone else must be racing with us, so try again */ + continue; + } + + if (a.st_ino == b.st_ino) + break; + + VIR_DEBUG("Resource '%s' was recreated", res->path); + VIR_FORCE_CLOSE(res->fd); + /* Someone else must be racing with us, so try again */ + } + } else { + if ((res->fd = open(res->path, O_RDWR)) < 0) { + virReportSystemError(errno, + _("Unable to open resource %s"), + res->path); + goto error; + } + + if (virSetCloseExec(res->fd) < 0) { + virReportSystemError(errno, + _("Failed to set close-on-exec flag '%s'"), + res->path); + goto error; + } + + if (virFileLock(res->fd, !!(flags & VIR_LOCK_SPACE_ACQUIRE_SHARED), 0, 0) < 0) { + if (errno == EACCES || errno == EAGAIN) { + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is locked"), + resname); + } else { + virReportSystemError(errno, + _("Unable to acquire lock on '%s'"), + res->path); + } + goto error; + } + } + res->lockHeld = true; + + if (VIR_EXPAND_N(res->owners, res->nOwners, 1) < 0) + goto no_memory; + + res->owners[res->nOwners-1] = owner; + + return res; + +no_memory: + virReportOOMError(); +error: + virLockSpaceResourceFree(res); + return NULL; +} + + +static void virLockSpaceResourceDataFree(void *opaque, const void *name ATTRIBUTE_UNUSED) +{ + virLockSpaceResourcePtr res = opaque; + virLockSpaceResourceFree(res); +} + + +virLockSpacePtr virLockSpaceNew(const char *directory) +{ + virLockSpacePtr lockspace; + + VIR_DEBUG("directory=%s", NULLSTR(directory)); + + if (VIR_ALLOC(lockspace) < 0) + return NULL; + + if (virMutexInit(&lockspace->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to initialize lockspace mutex")); + VIR_FREE(lockspace); + return NULL; + } + + if (directory && + !(lockspace->dir = strdup(directory))) + goto no_memory; + + if (!(lockspace->resources = virHashCreate(10, + virLockSpaceResourceDataFree))) + goto error; + + if (directory) { + if (virFileExists(directory)) { + if (!virFileIsDir(directory)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Lockspace location %s exists, but is not a directory"), + directory); + goto error; + } + } else { + if (virFileMakePathWithMode(directory, 0700) < 0) { + virReportSystemError(errno, + _("Unable to create lockspace %s"), + directory); + goto error; + } + } + } + + return lockspace; + +no_memory: + virReportOOMError(); +error: + virLockSpaceFree(lockspace); + return NULL; +} + + +void virLockSpaceFree(virLockSpacePtr lockspace) +{ + if (!lockspace) + return; + + virHashFree(lockspace->resources); + VIR_FREE(lockspace->dir); + virMutexDestroy(&lockspace->lock); + VIR_FREE(lockspace); +} + + +const char *virLockSpaceGetDirectory(virLockSpacePtr lockspace) +{ + return lockspace->dir; +} + + +int virLockSpaceCreateResource(virLockSpacePtr lockspace, + const char *resname) +{ + int ret = -1; + char *respath = NULL; + virLockSpaceResourcePtr res; + + VIR_DEBUG("lockspace=%p resname=%s", lockspace, resname); + + virMutexLock(&lockspace->lock); + + if ((res = virHashLookup(lockspace->resources, resname))) { + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is locked"), + resname); + goto cleanup; + } + + if (!(respath = virLockSpaceGetResourcePath(lockspace, resname))) + goto cleanup; + + if (virFileTouch(respath, 0600) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virMutexUnlock(&lockspace->lock); + VIR_FREE(respath); + return ret; +} + + +int virLockSpaceDeleteResource(virLockSpacePtr lockspace, + const char *resname) +{ + int ret = -1; + char *respath = NULL; + virLockSpaceResourcePtr res; + + VIR_DEBUG("lockspace=%p resname=%s", lockspace, resname); + + virMutexLock(&lockspace->lock); + + if ((res = virHashLookup(lockspace->resources, resname))) { + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is locked"), + resname); + goto cleanup; + } + + if (!(respath = virLockSpaceGetResourcePath(lockspace, resname))) + goto cleanup; + + if (unlink(respath) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to delete lockspace resource %s"), + respath); + goto cleanup; + } + + ret = 0; + +cleanup: + virMutexUnlock(&lockspace->lock); + VIR_FREE(respath); + return ret; +} + + +int virLockSpaceAcquireResource(virLockSpacePtr lockspace, + const char *resname, + pid_t owner, + unsigned int flags) +{ + int ret = -1; + virLockSpaceResourcePtr res; + + VIR_DEBUG("lockspace=%p resname=%s flags=%x owner=%lld", + lockspace, resname, flags, (unsigned long long)owner); + + virCheckFlags(VIR_LOCK_SPACE_ACQUIRE_SHARED | + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE, -1); + + virMutexLock(&lockspace->lock); + + if ((res = virHashLookup(lockspace->resources, resname))) { + if ((res->flags & VIR_LOCK_SPACE_ACQUIRE_SHARED) && + (flags & VIR_LOCK_SPACE_ACQUIRE_SHARED)) { + + if (VIR_EXPAND_N(res->owners, res->nOwners, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + res->owners[res->nOwners-1] = owner; + + goto done; + } + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is locked"), + resname); + goto cleanup; + } + + if (!(res = virLockSpaceResourceNew(lockspace, resname, flags, owner))) + goto cleanup; + + if (virHashAddEntry(lockspace->resources, resname, res) < 0) { + virLockSpaceResourceFree(res); + goto cleanup; + } + +done: + ret = 0; + +cleanup: + virMutexUnlock(&lockspace->lock); + return ret; +} + + +int virLockSpaceReleaseResource(virLockSpacePtr lockspace, + const char *resname, + pid_t owner) +{ + int ret = -1; + virLockSpaceResourcePtr res; + size_t i; + + VIR_DEBUG("lockspace=%p resname=%s owner=%lld", + lockspace, resname, (unsigned long long)owner); + + virMutexLock(&lockspace->lock); + + if (!(res = virHashLookup(lockspace->resources, resname))) { + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is not locked"), + resname); + goto cleanup; + } + + for (i = 0 ; i < res->nOwners ; i++) { + if (res->owners[i] == owner) { + break; + } + } + + if (i == res->nOwners) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("owner %lld does not hold the resource lock"), + (unsigned long long)owner); + goto cleanup; + } + + if (i < (res->nOwners - 1)) + memmove(res->owners + i, + res->owners + i + 1, + (res->nOwners - i - 1) * sizeof(res->owners[0])); + VIR_SHRINK_N(res->owners, res->nOwners, 1); + + if ((res->nOwners == 0) && + virHashRemoveEntry(lockspace->resources, resname) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virMutexUnlock(&lockspace->lock); + return ret; +} + + +struct virLockSpaceRemoveData { + pid_t owner; + size_t count; +}; + + +static int +virLockSpaceRemoveResourcesForOwner(const void *payload, + const void *name ATTRIBUTE_UNUSED, + const void *opaque) +{ + virLockSpaceResourcePtr res = (virLockSpaceResourcePtr)payload; + struct virLockSpaceRemoveData *data = (struct virLockSpaceRemoveData *)opaque; + size_t i; + + VIR_DEBUG("res %s owner %lld", res->name, (unsigned long long)data->owner); + + for (i = 0 ; i < res->nOwners ; i++) { + if (res->owners[i] == data->owner) { + break; + } + } + + if (i == res->nOwners) + return 0; + + data->count++; + + if (i < (res->nOwners - 1)) + memmove(res->owners + i, + res->owners + i + 1, + (res->nOwners - i - 1) * sizeof(res->owners[0])); + VIR_SHRINK_N(res->owners, res->nOwners, 1); + + if (res->nOwners) { + VIR_DEBUG("Other shared owners remain"); + return 0; + } + + VIR_DEBUG("No more owners, remove it"); + return 1; +} + + +int virLockSpaceReleaseResourcesForOwner(virLockSpacePtr lockspace, + pid_t owner) +{ + int ret = 0; + struct virLockSpaceRemoveData data = { + owner, 0 + }; + + VIR_DEBUG("lockspace=%p owner=%lld", lockspace, (unsigned long long)owner); + + virMutexLock(&lockspace->lock); + + if (virHashRemoveSet(lockspace->resources, + virLockSpaceRemoveResourcesForOwner, + &data) < 0) + goto error; + + ret = data.count; + + virMutexUnlock(&lockspace->lock); + return ret; + +error: + virMutexUnlock(&lockspace->lock); + return -1; +} diff --git a/src/util/virlockspace.h b/src/util/virlockspace.h new file mode 100644 index 0000000..bd8f91c --- /dev/null +++ b/src/util/virlockspace.h @@ -0,0 +1,58 @@ +/* + * virlockspace.h: simple file based lockspaces + * + * Copyright (C) 2012 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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __VIR_LOCK_SPACE_H__ +# define __VIR_LOCK_SPACE_H__ + +# include "internal.h" + +typedef struct _virLockSpace virLockSpace; +typedef virLockSpace *virLockSpacePtr; + +virLockSpacePtr virLockSpaceNew(const char *directory); + +void virLockSpaceFree(virLockSpacePtr lockspace); + +const char *virLockSpaceGetDirectory(virLockSpacePtr lockspace); + +int virLockSpaceCreateResource(virLockSpacePtr lockspace, + const char *resname); +int virLockSpaceDeleteResource(virLockSpacePtr lockspace, + const char *resname); + +typedef enum { + VIR_LOCK_SPACE_ACQUIRE_SHARED = (1 << 0), + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE = (1 << 1), +} virLockSpaceAcquireFlags; + +int virLockSpaceAcquireResource(virLockSpacePtr lockspace, + const char *resname, + pid_t owner, + unsigned int flags); + +int virLockSpaceReleaseResource(virLockSpacePtr lockspace, + const char *resname, + pid_t owner); + +int virLockSpaceReleaseResourcesForOwner(virLockSpacePtr lockspace, + pid_t owner); + +#endif /* __VIR_LOCK_SPACE_H__ */ diff --git a/src/util/virterror.c b/src/util/virterror.c index 7caa69e..6fade3b 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -115,7 +115,8 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "Parallels Cloud Server", "Device Config", - "SSH transport layer" /* 50 */ + "SSH transport layer", /* 50 */ + "Lock Space", ) @@ -1206,6 +1207,12 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("Guest agent is not responding: %s"); break; + case VIR_ERR_RESOURCE_BUSY: + if (info == NULL) + errmsg = _("resource busy"); + else + errmsg = _("resource busy %s"); + break; } return errmsg; } diff --git a/tests/Makefile.am b/tests/Makefile.am index bec89e2..bcc2163 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -92,7 +92,7 @@ test_programs = virshtest sockettest \ viratomictest \ utiltest virnettlscontexttest shunloadtest \ virtimetest viruritest virkeyfiletest \ - virauthconfigtest + virauthconfigtest virlockspacetest if WITH_SECDRIVER_SELINUX test_programs += securityselinuxtest @@ -534,6 +534,11 @@ virtimetest_SOURCES = \ virtimetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) virtimetest_LDADD = $(LDADDS) +virlockspacetest_SOURCES = \ + virlockspacetest.c testutils.h testutils.c +virlockspacetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) +virlockspacetest_LDADD = $(LDADDS) + viruritest_SOURCES = \ viruritest.c testutils.h testutils.c viruritest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) diff --git a/tests/virlockspacetest.c b/tests/virlockspacetest.c new file mode 100644 index 0000000..ee58f95 --- /dev/null +++ b/tests/virlockspacetest.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <stdlib.h> +#include <signal.h> +#include <sys/stat.h> + +#include "testutils.h" +#include "util.h" +#include "virterror_internal.h" +#include "memory.h" +#include "logging.h" + +#include "virlockspace.h" + +#define VIR_FROM_THIS VIR_FROM_RPC + +#define LOCKSPACE_DIR abs_builddir "/virlockspacedata" + +static int testLockSpaceCreate(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLifecycle(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, "foo") < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLockExcl(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, "foo") < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), 0) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), 0) == 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") == 0) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLockExclAuto(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, "foo") < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLockShr(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, "foo") < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_SHARED) < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), 0) == 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_SHARED) < 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") == 0) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") == 0) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLockShrAuto(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, "foo") < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_SHARED | + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) == 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_SHARED | + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLockPath(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(NULL); + + mkdir(LOCKSPACE_DIR, 0700); + + if (virLockSpaceCreateResource(lockspace, LOCKSPACE_DIR "/foo") < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, LOCKSPACE_DIR "/foo", geteuid(), 0) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, LOCKSPACE_DIR "/foo", geteuid(), 0) == 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, LOCKSPACE_DIR "/foo") == 0) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, LOCKSPACE_DIR "/foo", geteuid()) < 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, LOCKSPACE_DIR "/foo") < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + + +static int +mymain(void) +{ + int ret = 0; + + signal(SIGPIPE, SIG_IGN); + + if (virtTestRun("Lockspace creation", 1, testLockSpaceCreate, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res lifecycle", 1, testLockSpaceResourceLifecycle, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res lock excl", 1, testLockSpaceResourceLockExcl, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res lock shr", 1, testLockSpaceResourceLockShr, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res lock excl auto", 1, testLockSpaceResourceLockExclAuto, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res lock shr auto", 1, testLockSpaceResourceLockShrAuto, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res full path", 1, testLockSpaceResourceLockPath, NULL) < 0) + ret = -1; + + return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN(mymain) -- 1.7.11.2

On 12.09.2012 18:28, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The previously introduced virFile{Lock,Unlock} APIs provide a way to acquire/release fcntl() locks on individual files. For unknown reason though, the POSIX spec says that fcntl() locks are released when *any* file handle referring to the same path is closed. In the following sequence
threadA: fd1 = open("foo") threadB: fd2 = open("foo") threadA: virFileLock(fd1) threadB: virFileLock(fd2) threadB: close(fd2)
you'd expect threadA to come out holding a lock on 'foo', and indeed it does hold a lock for a very short time. Unfortunately when threadB does close(fd2) this releases the lock associated with fd1. For the current libvirt use case for virFileLock - pidfiles - this doesn't matter since the lock is acquired at startup while single threaded an never released until exit.
To provide a more generally useful API though, it is necessary to introduce a slightly higher level abstraction, which is to be referred to as a "lockspace". This is to be provided by a virLockSpacePtr object in src/util/virlockspace.{c,h}. The core idea is that the lockspace keeps track of what files are already open+locked. This means that when a 2nd thread comes along and tries to acquire a lock, it doesn't end up opening and closing a new FD. The lockspace just checks the current list of held locks and immediately returns VIR_ERR_RESOURCE_BUSY.
NB, the API as it stands is designed on the basis that the files being locked are not being otherwise opened and used by the application code. One approach to using this API is to acquire locks based on a hash of the filepath.
eg to lock /var/lib/libvirt/images/foo.img the application might do
virLockSpacePtr lockspace = virLockSpaceNew("/var/lib/libvirt/imagelocks"); lockname = md5sum("/var/lib/libvirt/images/foo.img"); virLockSpaceAcquireLock(lockspace, lockname);
NB, in this example, the caller should ensure that the path is canonicalized before calculating the checksum.
It is also possible to do locks directly on resources by using a NULL lockspace directory and then using the file path as the lock name eg
virLockSpacePtr lockspace = virLockSpaceNew(NULL); virLockSpaceAcquireLock(lockspace, "/var/lib/libvirt/images/foo.img");
This is only safe to do though if no other part of the process will be opening the files. This will be the case when this code is used inside the soon-to-be-reposted virlockd daemon
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 1 + include/libvirt/virterror.h | 4 +- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 11 + src/util/virlockspace.c | 558 ++++++++++++++++++++++++++++++++++++++++++++ src/util/virlockspace.h | 58 +++++ src/util/virterror.c | 9 +- tests/Makefile.am | 7 +- tests/virlockspacetest.c | 363 ++++++++++++++++++++++++++++ 10 files changed, 1010 insertions(+), 3 deletions(-) create mode 100644 src/util/virlockspace.c create mode 100644 src/util/virlockspace.h create mode 100644 tests/virlockspacetest.c
ACK but see my comments below.
diff --git a/.gitignore b/.gitignore index d998f0e..7919f74 100644 --- a/.gitignore +++ b/.gitignore @@ -161,6 +161,7 @@ /tests/virdrivermoduletest /tests/virhashtest /tests/virkeyfiletest +/tests/virlockspacetest /tests/virnet*test /tests/virshtest /tests/virtimetest diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 5140c38..98c2cb2 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -112,7 +112,8 @@ typedef enum { VIR_FROM_PARALLELS = 48, /* Error from Parallels */ VIR_FROM_DEVICE = 49, /* Error from Device */
- VIR_FROM_SSH = 50, /* Error from libssh2 connection transport */ + VIR_FROM_SSH = 50, /* Error from libssh2 connection transport */ + VIR_FROM_LOCKSPACE = 51, /* Error from lockspace */
# ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST @@ -285,6 +286,7 @@ typedef enum { VIR_ERR_SSH = 85, /* error in ssh transport driver */ VIR_ERR_AGENT_UNRESPONSIVE = 86, /* guest agent is unresponsive, not running or not usable */ + VIR_ERR_RESOURCE_BUSY = 87, /* resource is already in use */ } virErrorNumber;
/** diff --git a/po/POTFILES.in b/po/POTFILES.in index 7a91eb4..c5f4cf7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -148,6 +148,7 @@ src/util/virdbus.c src/util/virfile.c src/util/virhash.c src/util/virkeyfile.c +src/util/virlockspace.c src/util/virnetdev.c src/util/virnetdevbridge.c src/util/virnetdevmacvlan.c diff --git a/src/Makefile.am b/src/Makefile.am index 9f27fcf..6860e7f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -96,6 +96,7 @@ UTIL_SOURCES = \ util/virkeycode.c util/virkeycode.h \ util/virkeyfile.c util/virkeyfile.h \ util/virkeymaps.h \ + util/virlockspace.c util/virlockspace.h \ util/virmacaddr.h util/virmacaddr.c \ util/virnetdev.h util/virnetdev.c \ util/virnetdevbandwidth.h util/virnetdevbandwidth.c \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0494e1f..58a9520 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1321,6 +1321,17 @@ virKeyFileHasGroup; virKeyFileGetValueString;
+# virlockspace.h +virLockSpaceAcquireResource; +virLockSpaceCreateResource; +virLockSpaceDeleteResource; +virLockSpaceFree; +virLockSpaceGetDirectory; +virLockSpaceNew; +virLockSpaceReleaseResource; +virLockSpaceReleaseResourcesForOwner; + + # virmacaddr.h virMacAddrCmp; virMacAddrCmpRaw; diff --git a/src/util/virlockspace.c b/src/util/virlockspace.c new file mode 100644 index 0000000..611592a --- /dev/null +++ b/src/util/virlockspace.c @@ -0,0 +1,558 @@ +/* + * virlockspace.c: simple file based lockspaces + * + * Copyright (C) 2012 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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include "virlockspace.h" +#include "logging.h" +#include "memory.h" +#include "virterror_internal.h" +#include "util.h" +#include "virfile.h" +#include "virhash.h" +#include "threads.h" + +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + +#define VIR_FROM_THIS VIR_FROM_LOCKSPACE + +typedef struct _virLockSpaceResource virLockSpaceResource; +typedef virLockSpaceResource *virLockSpaceResourcePtr; + +struct _virLockSpaceResource { + char *name; + char *path; + int fd; + bool lockHeld; + unsigned int flags; + size_t nOwners; + pid_t *owners; +}; + +struct _virLockSpace { + char *dir; + virMutex lock; + + virHashTablePtr resources; +}; + + +static char *virLockSpaceGetResourcePath(virLockSpacePtr lockspace, + const char *resname) +{ + char *ret; + if (lockspace->dir) { + if (virAsprintf(&ret, "%s/%s", lockspace->dir, resname) < 0) { + virReportOOMError(); + return NULL; + } + } else { + if (!(ret = strdup(resname))) { + virReportOOMError(); + return NULL; + } + } + + return ret; +} + + +static void virLockSpaceResourceFree(virLockSpaceResourcePtr res) +{ + if (!res) + return; + + if (res->lockHeld && + (res->flags & VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE)) { + if (res->flags & VIR_LOCK_SPACE_ACQUIRE_SHARED) { + /* We must upgrade to an exclusive lock to ensure + * no one else still has it before trying to delete */ + if (virFileLock(res->fd, false, 0, 1) < 0) { + VIR_DEBUG("Could not upgrade shared lease to exclusive, not deleting"); + } else { + if (unlink(res->path) < 0 && + errno != ENOENT) { + char ebuf[1024]; + VIR_WARN("Failed to unlink resource %s: %s", + res->path, virStrerror(errno, ebuf, sizeof(ebuf))); + } + } + } else { + if (unlink(res->path) < 0 && + errno != ENOENT) { + char ebuf[1024]; + VIR_WARN("Failed to unlink resource %s: %s", + res->path, virStrerror(errno, ebuf, sizeof(ebuf))); + } + } + } + + VIR_FORCE_CLOSE(res->fd); + VIR_FREE(res->path); + VIR_FREE(res->name); + VIR_FREE(res); +} + + +static virLockSpaceResourcePtr +virLockSpaceResourceNew(virLockSpacePtr lockspace, + const char *resname, + unsigned int flags, + pid_t owner) +{ + virLockSpaceResourcePtr res; + bool shared = !!(flags & VIR_LOCK_SPACE_ACQUIRE_SHARED); + + if (VIR_ALLOC(res) < 0) + return NULL; + + res->fd = -1; + res->flags = flags; + + if (!(res->name = strdup(resname))) + goto no_memory; + + if (!(res->path = virLockSpaceGetResourcePath(lockspace, resname))) + goto no_memory; + + if (flags & VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) { + while (1) { + struct stat a, b; + if ((res->fd = open(res->path, O_RDWR|O_CREAT, 0600)) < 0) { + virReportSystemError(errno, + _("Unable to open/create resource %s"), + res->path); + goto error; + } + + if (virSetCloseExec(res->fd) < 0) { + virReportSystemError(errno, + _("Failed to set close-on-exec flag '%s'"), + res->path); + goto error; + } + + if (fstat(res->fd, &b) < 0) { + virReportSystemError(errno, + _("Unable to check status of pid file '%s'"), + res->path); + goto error; + } + + if (virFileLock(res->fd, shared, 0, 1) < 0) { + if (errno == EACCES || errno == EAGAIN) { + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is locked"), + resname); + } else { + virReportSystemError(errno, + _("Unable to acquire lock on '%s'"), + res->path); + } + goto error; + } + + /* Now make sure the pidfile we locked is the same + * one that now exists on the filesystem + */ + if (stat(res->path, &a) < 0) { + char ebuf[1024] ATTRIBUTE_UNUSED; + VIR_DEBUG("Resource '%s' disappeared: %s", + res->path, virStrerror(errno, ebuf, sizeof(ebuf))); + VIR_FORCE_CLOSE(res->fd); + /* Someone else must be racing with us, so try again */ + continue; + } + + if (a.st_ino == b.st_ino) + break; + + VIR_DEBUG("Resource '%s' was recreated", res->path); + VIR_FORCE_CLOSE(res->fd); + /* Someone else must be racing with us, so try again */ + } + } else { + if ((res->fd = open(res->path, O_RDWR)) < 0) { + virReportSystemError(errno, + _("Unable to open resource %s"), + res->path); + goto error; + } + + if (virSetCloseExec(res->fd) < 0) { + virReportSystemError(errno, + _("Failed to set close-on-exec flag '%s'"), + res->path); + goto error; + } + + if (virFileLock(res->fd, !!(flags & VIR_LOCK_SPACE_ACQUIRE_SHARED), 0, 0) < 0) {
I think you can s/!!(...)/shared/
+ if (errno == EACCES || errno == EAGAIN) { + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is locked"), + resname); + } else { + virReportSystemError(errno, + _("Unable to acquire lock on '%s'"), + res->path); + } + goto error; + } + } + res->lockHeld = true; + + if (VIR_EXPAND_N(res->owners, res->nOwners, 1) < 0) + goto no_memory; + + res->owners[res->nOwners-1] = owner; + + return res; + +no_memory: + virReportOOMError(); +error: + virLockSpaceResourceFree(res); + return NULL; +} + + +static void virLockSpaceResourceDataFree(void *opaque, const void *name ATTRIBUTE_UNUSED) +{ + virLockSpaceResourcePtr res = opaque; + virLockSpaceResourceFree(res); +} + + +virLockSpacePtr virLockSpaceNew(const char *directory) +{ + virLockSpacePtr lockspace; + + VIR_DEBUG("directory=%s", NULLSTR(directory)); + + if (VIR_ALLOC(lockspace) < 0) + return NULL; + + if (virMutexInit(&lockspace->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to initialize lockspace mutex")); + VIR_FREE(lockspace); + return NULL; + } + + if (directory && + !(lockspace->dir = strdup(directory))) + goto no_memory; + + if (!(lockspace->resources = virHashCreate(10, + virLockSpaceResourceDataFree)))
10 is a magic constant esp. when used in subsequent patches. I think you can #define VIR_LOCK_SPACE_RESOURCE_HASH_TABLE_SIZE 10 or something.
+ goto error; + + if (directory) { + if (virFileExists(directory)) { + if (!virFileIsDir(directory)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Lockspace location %s exists, but is not a directory"), + directory); + goto error; + } + } else { + if (virFileMakePathWithMode(directory, 0700) < 0) { + virReportSystemError(errno, + _("Unable to create lockspace %s"), + directory); + goto error; + } + } + } + + return lockspace; + +no_memory: + virReportOOMError(); +error: + virLockSpaceFree(lockspace); + return NULL; +} + + +void virLockSpaceFree(virLockSpacePtr lockspace) +{ + if (!lockspace) + return; + + virHashFree(lockspace->resources); + VIR_FREE(lockspace->dir); + virMutexDestroy(&lockspace->lock); + VIR_FREE(lockspace); +} + + +const char *virLockSpaceGetDirectory(virLockSpacePtr lockspace) +{ + return lockspace->dir; +} + + +int virLockSpaceCreateResource(virLockSpacePtr lockspace, + const char *resname) +{ + int ret = -1; + char *respath = NULL; + virLockSpaceResourcePtr res; + + VIR_DEBUG("lockspace=%p resname=%s", lockspace, resname); + + virMutexLock(&lockspace->lock); + + if ((res = virHashLookup(lockspace->resources, resname))) { + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is locked"), + resname); + goto cleanup; + } + + if (!(respath = virLockSpaceGetResourcePath(lockspace, resname))) + goto cleanup; + + if (virFileTouch(respath, 0600) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virMutexUnlock(&lockspace->lock); + VIR_FREE(respath); + return ret; +} + + +int virLockSpaceDeleteResource(virLockSpacePtr lockspace, + const char *resname) +{ + int ret = -1; + char *respath = NULL; + virLockSpaceResourcePtr res; + + VIR_DEBUG("lockspace=%p resname=%s", lockspace, resname); + + virMutexLock(&lockspace->lock); + + if ((res = virHashLookup(lockspace->resources, resname))) { + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is locked"), + resname); + goto cleanup; + } + + if (!(respath = virLockSpaceGetResourcePath(lockspace, resname))) + goto cleanup; + + if (unlink(respath) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to delete lockspace resource %s"), + respath); + goto cleanup; + } + + ret = 0; + +cleanup: + virMutexUnlock(&lockspace->lock); + VIR_FREE(respath); + return ret; +} + + +int virLockSpaceAcquireResource(virLockSpacePtr lockspace, + const char *resname, + pid_t owner, + unsigned int flags) +{ + int ret = -1; + virLockSpaceResourcePtr res; + + VIR_DEBUG("lockspace=%p resname=%s flags=%x owner=%lld", + lockspace, resname, flags, (unsigned long long)owner); + + virCheckFlags(VIR_LOCK_SPACE_ACQUIRE_SHARED | + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE, -1); + + virMutexLock(&lockspace->lock); + + if ((res = virHashLookup(lockspace->resources, resname))) { + if ((res->flags & VIR_LOCK_SPACE_ACQUIRE_SHARED) && + (flags & VIR_LOCK_SPACE_ACQUIRE_SHARED)) { + + if (VIR_EXPAND_N(res->owners, res->nOwners, 1) < 0) { + virReportOOMError(); + goto cleanup; + } + res->owners[res->nOwners-1] = owner; + + goto done; + } + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is locked"), + resname); + goto cleanup; + } + + if (!(res = virLockSpaceResourceNew(lockspace, resname, flags, owner))) + goto cleanup; + + if (virHashAddEntry(lockspace->resources, resname, res) < 0) { + virLockSpaceResourceFree(res); + goto cleanup; + } + +done: + ret = 0; + +cleanup: + virMutexUnlock(&lockspace->lock); + return ret; +} + + +int virLockSpaceReleaseResource(virLockSpacePtr lockspace, + const char *resname, + pid_t owner) +{ + int ret = -1; + virLockSpaceResourcePtr res; + size_t i; + + VIR_DEBUG("lockspace=%p resname=%s owner=%lld", + lockspace, resname, (unsigned long long)owner); + + virMutexLock(&lockspace->lock); + + if (!(res = virHashLookup(lockspace->resources, resname))) { + virReportError(VIR_ERR_RESOURCE_BUSY, + _("Lockspace resource '%s' is not locked"), + resname); + goto cleanup; + } + + for (i = 0 ; i < res->nOwners ; i++) { + if (res->owners[i] == owner) { + break; + } + } + + if (i == res->nOwners) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("owner %lld does not hold the resource lock"), + (unsigned long long)owner); + goto cleanup; + } + + if (i < (res->nOwners - 1)) + memmove(res->owners + i, + res->owners + i + 1, + (res->nOwners - i - 1) * sizeof(res->owners[0])); + VIR_SHRINK_N(res->owners, res->nOwners, 1); + + if ((res->nOwners == 0) && + virHashRemoveEntry(lockspace->resources, resname) < 0) + goto cleanup; + + ret = 0; + +cleanup: + virMutexUnlock(&lockspace->lock); + return ret; +} + + +struct virLockSpaceRemoveData { + pid_t owner; + size_t count; +}; + + +static int +virLockSpaceRemoveResourcesForOwner(const void *payload, + const void *name ATTRIBUTE_UNUSED, + const void *opaque) +{ + virLockSpaceResourcePtr res = (virLockSpaceResourcePtr)payload; + struct virLockSpaceRemoveData *data = (struct virLockSpaceRemoveData *)opaque; + size_t i; + + VIR_DEBUG("res %s owner %lld", res->name, (unsigned long long)data->owner); + + for (i = 0 ; i < res->nOwners ; i++) { + if (res->owners[i] == data->owner) { + break; + } + } + + if (i == res->nOwners) + return 0; + + data->count++; + + if (i < (res->nOwners - 1)) + memmove(res->owners + i, + res->owners + i + 1, + (res->nOwners - i - 1) * sizeof(res->owners[0])); + VIR_SHRINK_N(res->owners, res->nOwners, 1); + + if (res->nOwners) { + VIR_DEBUG("Other shared owners remain"); + return 0; + } + + VIR_DEBUG("No more owners, remove it"); + return 1; +} + + +int virLockSpaceReleaseResourcesForOwner(virLockSpacePtr lockspace, + pid_t owner) +{ + int ret = 0; + struct virLockSpaceRemoveData data = { + owner, 0 + }; + + VIR_DEBUG("lockspace=%p owner=%lld", lockspace, (unsigned long long)owner); + + virMutexLock(&lockspace->lock); + + if (virHashRemoveSet(lockspace->resources, + virLockSpaceRemoveResourcesForOwner, + &data) < 0) + goto error; + + ret = data.count; + + virMutexUnlock(&lockspace->lock); + return ret; + +error: + virMutexUnlock(&lockspace->lock); + return -1; +} diff --git a/src/util/virlockspace.h b/src/util/virlockspace.h new file mode 100644 index 0000000..bd8f91c --- /dev/null +++ b/src/util/virlockspace.h @@ -0,0 +1,58 @@ +/* + * virlockspace.h: simple file based lockspaces + * + * Copyright (C) 2012 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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __VIR_LOCK_SPACE_H__ +# define __VIR_LOCK_SPACE_H__ + +# include "internal.h" + +typedef struct _virLockSpace virLockSpace; +typedef virLockSpace *virLockSpacePtr; + +virLockSpacePtr virLockSpaceNew(const char *directory); + +void virLockSpaceFree(virLockSpacePtr lockspace); + +const char *virLockSpaceGetDirectory(virLockSpacePtr lockspace); + +int virLockSpaceCreateResource(virLockSpacePtr lockspace, + const char *resname); +int virLockSpaceDeleteResource(virLockSpacePtr lockspace, + const char *resname); + +typedef enum { + VIR_LOCK_SPACE_ACQUIRE_SHARED = (1 << 0), + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE = (1 << 1), +} virLockSpaceAcquireFlags; + +int virLockSpaceAcquireResource(virLockSpacePtr lockspace, + const char *resname, + pid_t owner, + unsigned int flags); + +int virLockSpaceReleaseResource(virLockSpacePtr lockspace, + const char *resname, + pid_t owner); + +int virLockSpaceReleaseResourcesForOwner(virLockSpacePtr lockspace, + pid_t owner); + +#endif /* __VIR_LOCK_SPACE_H__ */ diff --git a/src/util/virterror.c b/src/util/virterror.c index 7caa69e..6fade3b 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -115,7 +115,8 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "Parallels Cloud Server", "Device Config",
- "SSH transport layer" /* 50 */ + "SSH transport layer", /* 50 */ + "Lock Space", )
@@ -1206,6 +1207,12 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("Guest agent is not responding: %s"); break; + case VIR_ERR_RESOURCE_BUSY: + if (info == NULL) + errmsg = _("resource busy"); + else + errmsg = _("resource busy %s"); + break; } return errmsg; } diff --git a/tests/Makefile.am b/tests/Makefile.am index bec89e2..bcc2163 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -92,7 +92,7 @@ test_programs = virshtest sockettest \ viratomictest \ utiltest virnettlscontexttest shunloadtest \ virtimetest viruritest virkeyfiletest \ - virauthconfigtest + virauthconfigtest virlockspacetest
if WITH_SECDRIVER_SELINUX test_programs += securityselinuxtest @@ -534,6 +534,11 @@ virtimetest_SOURCES = \ virtimetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) virtimetest_LDADD = $(LDADDS)
+virlockspacetest_SOURCES = \ + virlockspacetest.c testutils.h testutils.c +virlockspacetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) +virlockspacetest_LDADD = $(LDADDS) + viruritest_SOURCES = \ viruritest.c testutils.h testutils.c viruritest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS) diff --git a/tests/virlockspacetest.c b/tests/virlockspacetest.c new file mode 100644 index 0000000..ee58f95 --- /dev/null +++ b/tests/virlockspacetest.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <stdlib.h> +#include <signal.h> +#include <sys/stat.h> + +#include "testutils.h" +#include "util.h" +#include "virterror_internal.h" +#include "memory.h" +#include "logging.h" + +#include "virlockspace.h" + +#define VIR_FROM_THIS VIR_FROM_RPC + +#define LOCKSPACE_DIR abs_builddir "/virlockspacedata" + +static int testLockSpaceCreate(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLifecycle(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, "foo") < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLockExcl(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, "foo") < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), 0) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), 0) == 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") == 0) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLockExclAuto(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, "foo") < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLockShr(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, "foo") < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_SHARED) < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), 0) == 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_SHARED) < 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") == 0) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") == 0) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, "foo") < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLockShrAuto(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(LOCKSPACE_DIR); + + if (!virFileIsDir(LOCKSPACE_DIR)) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, "foo") < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_SHARED | + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) == 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, "foo", geteuid(), + VIR_LOCK_SPACE_ACQUIRE_SHARED | + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, "foo", geteuid()) < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + +static int testLockSpaceResourceLockPath(const void *args ATTRIBUTE_UNUSED) +{ + virLockSpacePtr lockspace; + int ret = -1; + + rmdir(LOCKSPACE_DIR); + + lockspace = virLockSpaceNew(NULL); + + mkdir(LOCKSPACE_DIR, 0700); + + if (virLockSpaceCreateResource(lockspace, LOCKSPACE_DIR "/foo") < 0) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, LOCKSPACE_DIR "/foo", geteuid(), 0) < 0) + goto cleanup; + + if (!virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + if (virLockSpaceAcquireResource(lockspace, LOCKSPACE_DIR "/foo", geteuid(), 0) == 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, LOCKSPACE_DIR "/foo") == 0) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, LOCKSPACE_DIR "/foo", geteuid()) < 0) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, LOCKSPACE_DIR "/foo") < 0) + goto cleanup; + + if (virFileExists(LOCKSPACE_DIR "/foo")) + goto cleanup; + + ret = 0; + +cleanup: + virLockSpaceFree(lockspace); + rmdir(LOCKSPACE_DIR); + return ret; +} + + + +static int +mymain(void) +{ + int ret = 0; + + signal(SIGPIPE, SIG_IGN); + + if (virtTestRun("Lockspace creation", 1, testLockSpaceCreate, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res lifecycle", 1, testLockSpaceResourceLifecycle, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res lock excl", 1, testLockSpaceResourceLockExcl, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res lock shr", 1, testLockSpaceResourceLockShr, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res lock excl auto", 1, testLockSpaceResourceLockExclAuto, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res lock shr auto", 1, testLockSpaceResourceLockShrAuto, NULL) < 0) + ret = -1; + + if (virtTestRun("Lockspace res full path", 1, testLockSpaceResourceLockPath, NULL) < 0) + ret = -1; + + return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN(mymain)

From: "Daniel P. Berrange" <berrange@redhat.com> Add two new APIs virLockSpaceNewPostExecRestart and virLockSpacePreExecRestart which allow a virLockSpacePtr object to be created from a JSON object and saved to a JSON object, for the purposes of re-exec'ing a process. As well as saving the state in JSON format, the second method will disable the O_CLOEXEC flag so that the open file descriptors are preserved across the process re-exec() Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 2 + src/util/virlockspace.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virlockspace.h | 4 + 3 files changed, 242 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 58a9520..6c94584 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1328,6 +1328,8 @@ virLockSpaceDeleteResource; virLockSpaceFree; virLockSpaceGetDirectory; virLockSpaceNew; +virLockSpaceNewPostExecRestart; +virLockSpacePreExecRestart; virLockSpaceReleaseResource; virLockSpaceReleaseResourcesForOwner; diff --git a/src/util/virlockspace.c b/src/util/virlockspace.c index 611592a..786513f 100644 --- a/src/util/virlockspace.c +++ b/src/util/virlockspace.c @@ -295,6 +295,242 @@ error: } + +virLockSpacePtr virLockSpaceNewPostExecRestart(virJSONValuePtr object) +{ + virLockSpacePtr lockspace; + virJSONValuePtr resources; + int n; + size_t i; + + VIR_DEBUG("object=%p", object); + + if (VIR_ALLOC(lockspace) < 0) + return NULL; + + if (virMutexInit(&lockspace->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to initialize lockspace mutex")); + VIR_FREE(lockspace); + return NULL; + } + + if (!(lockspace->resources = virHashCreate(10, + virLockSpaceResourceDataFree))) + goto error; + + if (virJSONValueObjectHasKey(object, "directory")) { + const char *dir = virJSONValueObjectGetString(object, "directory"); + if (!(lockspace->dir = strdup(dir))) { + virReportOOMError(); + goto error; + } + } + + if (!(resources = virJSONValueObjectGet(object, "resources"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resources value in JSON document")); + goto error; + } + + if ((n = virJSONValueArraySize(resources)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed resources value in JSON document")); + goto error; + } + + for (i = 0 ; i < n ; i++) { + virJSONValuePtr child = virJSONValueArrayGet(resources, i); + virLockSpaceResourcePtr res; + const char *tmp; + virJSONValuePtr owners; + size_t j; + int m; + + if (VIR_ALLOC(res) < 0) { + virReportOOMError(); + goto error; + } + res->fd = -1; + + if (!(tmp = virJSONValueObjectGetString(child, "name"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource name in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + if (!(res->name = strdup(tmp))) { + virReportOOMError(); + virLockSpaceResourceFree(res); + goto error; + } + + if (!(tmp = virJSONValueObjectGetString(child, "path"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource path in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + if (!(res->path = strdup(tmp))) { + virReportOOMError(); + virLockSpaceResourceFree(res); + goto error; + } + if (virJSONValueObjectGetNumberInt(child, "fd", &res->fd) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource fd in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + if (virSetInherit(res->fd, false) < 0) { + virReportSystemError(errno, "%s", + _("Cannot enable close-on-exec flag")); + goto error; + } + if (virJSONValueObjectGetBoolean(child, "lockHeld", &res->lockHeld) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource lockHeld in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if (virJSONValueObjectGetNumberUint(child, "flags", &res->flags) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource flags in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if (!(owners = virJSONValueObjectGet(child, "owners"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource owners in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if ((m = virJSONValueArraySize(owners)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed owners value in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + for (j = 0 ; j < m ; j++) { + unsigned long long int owner; + virJSONValuePtr ownerval = virJSONValueArrayGet(owners, j); + + if (virJSONValueGetNumberUlong(ownerval, &owner) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed owner value in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if (VIR_EXPAND_N(res->owners, res->nOwners, 1) < 0) { + virReportOOMError(); + virLockSpaceResourceFree(res); + goto error; + } + + res->owners[res->nOwners-1] = (pid_t)owner; + } + + if (virHashAddEntry(lockspace->resources, res->name, res) < 0) { + virLockSpaceResourceFree(res); + goto error; + } + } + + return lockspace; + +error: + virLockSpaceFree(lockspace); + return NULL; +} + + +virJSONValuePtr virLockSpacePreExecRestart(virLockSpacePtr lockspace) +{ + virJSONValuePtr object = virJSONValueNewObject(); + virJSONValuePtr resources; + virHashKeyValuePairPtr pairs = NULL, tmp; + + if (!object) + return NULL; + + virMutexLock(&lockspace->lock); + + if (lockspace->dir && + virJSONValueObjectAppendString(object, "directory", lockspace->dir) < 0) + goto error; + + if (!(resources = virJSONValueNewArray())) + goto error; + + if (virJSONValueObjectAppend(object, "resources", resources) < 0) { + virJSONValueFree(resources); + goto error; + } + + tmp = pairs = virHashGetItems(lockspace->resources, NULL); + while (tmp && tmp->value) { + virLockSpaceResourcePtr res = (virLockSpaceResourcePtr)tmp->value; + virJSONValuePtr child = virJSONValueNewObject(); + virJSONValuePtr owners = NULL; + size_t i; + + if (virJSONValueArrayAppend(resources, child) < 0) { + virJSONValueFree(child); + goto error; + } + + if (virJSONValueObjectAppendString(child, "name", res->name) < 0 || + virJSONValueObjectAppendString(child, "path", res->path) < 0 || + virJSONValueObjectAppendNumberInt(child, "fd", res->fd) < 0 || + virJSONValueObjectAppendBoolean(child, "lockHeld", res->lockHeld) < 0 || + virJSONValueObjectAppendNumberUint(child, "flags", res->flags) < 0) + goto error; + + if (virSetInherit(res->fd, true) < 0) { + virReportSystemError(errno, "%s", + _("Cannot disable close-on-exec flag")); + goto error; + } + + if (!(owners = virJSONValueNewArray())) + goto error; + + if (virJSONValueObjectAppend(child, "owners", owners) < 0) { + virJSONValueFree(owners); + goto error; + } + + for (i = 0 ; i < res->nOwners ; i++) { + virJSONValuePtr owner = virJSONValueNewNumberUlong(res->owners[i]); + if (!owner) + goto error; + + if (virJSONValueArrayAppend(owners, owner) < 0) { + virJSONValueFree(owner); + goto error; + } + } + + tmp++; + } + VIR_FREE(pairs); + + virMutexUnlock(&lockspace->lock); + return object; + + error: + VIR_FREE(pairs); + virJSONValueFree(object); + virMutexUnlock(&lockspace->lock); + return NULL; +} + + void virLockSpaceFree(virLockSpacePtr lockspace) { if (!lockspace) diff --git a/src/util/virlockspace.h b/src/util/virlockspace.h index bd8f91c..9c5128b 100644 --- a/src/util/virlockspace.h +++ b/src/util/virlockspace.h @@ -23,11 +23,15 @@ # define __VIR_LOCK_SPACE_H__ # include "internal.h" +# include "json.h" typedef struct _virLockSpace virLockSpace; typedef virLockSpace *virLockSpacePtr; virLockSpacePtr virLockSpaceNew(const char *directory); +virLockSpacePtr virLockSpaceNewPostExecRestart(virJSONValuePtr object); + +virJSONValuePtr virLockSpacePreExecRestart(virLockSpacePtr lockspace); void virLockSpaceFree(virLockSpacePtr lockspace); -- 1.7.11.2

On 12.09.2012 18:28, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add two new APIs virLockSpaceNewPostExecRestart and virLockSpacePreExecRestart which allow a virLockSpacePtr object to be created from a JSON object and saved to a JSON object, for the purposes of re-exec'ing a process.
As well as saving the state in JSON format, the second method will disable the O_CLOEXEC flag so that the open file descriptors are preserved across the process re-exec()
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 2 + src/util/virlockspace.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virlockspace.h | 4 + 3 files changed, 242 insertions(+)
ACK but see my comments below
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 58a9520..6c94584 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1328,6 +1328,8 @@ virLockSpaceDeleteResource; virLockSpaceFree; virLockSpaceGetDirectory; virLockSpaceNew; +virLockSpaceNewPostExecRestart; +virLockSpacePreExecRestart; virLockSpaceReleaseResource; virLockSpaceReleaseResourcesForOwner;
diff --git a/src/util/virlockspace.c b/src/util/virlockspace.c index 611592a..786513f 100644 --- a/src/util/virlockspace.c +++ b/src/util/virlockspace.c @@ -295,6 +295,242 @@ error: }
+ +virLockSpacePtr virLockSpaceNewPostExecRestart(virJSONValuePtr object) +{ + virLockSpacePtr lockspace; + virJSONValuePtr resources; + int n; + size_t i; + + VIR_DEBUG("object=%p", object); + + if (VIR_ALLOC(lockspace) < 0) + return NULL; + + if (virMutexInit(&lockspace->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to initialize lockspace mutex")); + VIR_FREE(lockspace); + return NULL; + } + + if (!(lockspace->resources = virHashCreate(10, + virLockSpaceResourceDataFree)))
s/10/VIR_LOCK_SPACE_RESOURCE_HASH_TABLE_SIZE/ or the name you invent
+ goto error; + + if (virJSONValueObjectHasKey(object, "directory")) { + const char *dir = virJSONValueObjectGetString(object, "directory"); + if (!(lockspace->dir = strdup(dir))) { + virReportOOMError(); + goto error; + } + } + + if (!(resources = virJSONValueObjectGet(object, "resources"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resources value in JSON document")); + goto error; + } + + if ((n = virJSONValueArraySize(resources)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed resources value in JSON document")); + goto error; + } + + for (i = 0 ; i < n ; i++) { + virJSONValuePtr child = virJSONValueArrayGet(resources, i); + virLockSpaceResourcePtr res; + const char *tmp; + virJSONValuePtr owners; + size_t j; + int m; + + if (VIR_ALLOC(res) < 0) { + virReportOOMError(); + goto error; + } + res->fd = -1;
This is useless ...
+ + if (!(tmp = virJSONValueObjectGetString(child, "name"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource name in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + if (!(res->name = strdup(tmp))) { + virReportOOMError(); + virLockSpaceResourceFree(res); + goto error; + } + + if (!(tmp = virJSONValueObjectGetString(child, "path"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource path in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + if (!(res->path = strdup(tmp))) { + virReportOOMError(); + virLockSpaceResourceFree(res); + goto error; + } + if (virJSONValueObjectGetNumberInt(child, "fd", &res->fd) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource fd in JSON document")); + virLockSpaceResourceFree(res); + goto error; + }
... since we require 'fd' attribute anyway.
+ if (virSetInherit(res->fd, false) < 0) { + virReportSystemError(errno, "%s", + _("Cannot enable close-on-exec flag")); + goto error; + } + if (virJSONValueObjectGetBoolean(child, "lockHeld", &res->lockHeld) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource lockHeld in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if (virJSONValueObjectGetNumberUint(child, "flags", &res->flags) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource flags in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if (!(owners = virJSONValueObjectGet(child, "owners"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource owners in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if ((m = virJSONValueArraySize(owners)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed owners value in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } +
Since we already know the res->owners size, wouldn't it be better to allocate it here instead of ...
+ for (j = 0 ; j < m ; j++) { + unsigned long long int owner; + virJSONValuePtr ownerval = virJSONValueArrayGet(owners, j); + + if (virJSONValueGetNumberUlong(ownerval, &owner) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed owner value in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if (VIR_EXPAND_N(res->owners, res->nOwners, 1) < 0) { + virReportOOMError(); + virLockSpaceResourceFree(res); + goto error;
... doing this?
+ } + + res->owners[res->nOwners-1] = (pid_t)owner; + } + + if (virHashAddEntry(lockspace->resources, res->name, res) < 0) { + virLockSpaceResourceFree(res); + goto error; + } + } + + return lockspace; + +error: + virLockSpaceFree(lockspace); + return NULL; +} + + +virJSONValuePtr virLockSpacePreExecRestart(virLockSpacePtr lockspace) +{ + virJSONValuePtr object = virJSONValueNewObject(); + virJSONValuePtr resources; + virHashKeyValuePairPtr pairs = NULL, tmp; + + if (!object) + return NULL; + + virMutexLock(&lockspace->lock); + + if (lockspace->dir && + virJSONValueObjectAppendString(object, "directory", lockspace->dir) < 0) + goto error; + + if (!(resources = virJSONValueNewArray())) + goto error; + + if (virJSONValueObjectAppend(object, "resources", resources) < 0) { + virJSONValueFree(resources); + goto error; + } + + tmp = pairs = virHashGetItems(lockspace->resources, NULL); + while (tmp && tmp->value) { + virLockSpaceResourcePtr res = (virLockSpaceResourcePtr)tmp->value; + virJSONValuePtr child = virJSONValueNewObject(); + virJSONValuePtr owners = NULL; + size_t i; + + if (virJSONValueArrayAppend(resources, child) < 0) { + virJSONValueFree(child); + goto error; + } + + if (virJSONValueObjectAppendString(child, "name", res->name) < 0 || + virJSONValueObjectAppendString(child, "path", res->path) < 0 || + virJSONValueObjectAppendNumberInt(child, "fd", res->fd) < 0 || + virJSONValueObjectAppendBoolean(child, "lockHeld", res->lockHeld) < 0 || + virJSONValueObjectAppendNumberUint(child, "flags", res->flags) < 0) + goto error; + + if (virSetInherit(res->fd, true) < 0) { + virReportSystemError(errno, "%s", + _("Cannot disable close-on-exec flag")); + goto error; + } + + if (!(owners = virJSONValueNewArray())) + goto error; + + if (virJSONValueObjectAppend(child, "owners", owners) < 0) { + virJSONValueFree(owners); + goto error; + } + + for (i = 0 ; i < res->nOwners ; i++) { + virJSONValuePtr owner = virJSONValueNewNumberUlong(res->owners[i]); + if (!owner) + goto error; + + if (virJSONValueArrayAppend(owners, owner) < 0) { + virJSONValueFree(owner); + goto error; + } + } + + tmp++; + } + VIR_FREE(pairs); + + virMutexUnlock(&lockspace->lock); + return object; + + error: + VIR_FREE(pairs); + virJSONValueFree(object); + virMutexUnlock(&lockspace->lock); + return NULL; +} + + void virLockSpaceFree(virLockSpacePtr lockspace) { if (!lockspace) diff --git a/src/util/virlockspace.h b/src/util/virlockspace.h index bd8f91c..9c5128b 100644 --- a/src/util/virlockspace.h +++ b/src/util/virlockspace.h @@ -23,11 +23,15 @@ # define __VIR_LOCK_SPACE_H__
# include "internal.h" +# include "json.h"
typedef struct _virLockSpace virLockSpace; typedef virLockSpace *virLockSpacePtr;
virLockSpacePtr virLockSpaceNew(const char *directory); +virLockSpacePtr virLockSpaceNewPostExecRestart(virJSONValuePtr object); + +virJSONValuePtr virLockSpacePreExecRestart(virLockSpacePtr lockspace);
void virLockSpaceFree(virLockSpacePtr lockspace);

On Fri, Oct 05, 2012 at 01:25:51PM +0200, Michal Privoznik wrote:
On 12.09.2012 18:28, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add two new APIs virLockSpaceNewPostExecRestart and virLockSpacePreExecRestart which allow a virLockSpacePtr object to be created from a JSON object and saved to a JSON object, for the purposes of re-exec'ing a process.
As well as saving the state in JSON format, the second method will disable the O_CLOEXEC flag so that the open file descriptors are preserved across the process re-exec()
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 2 + src/util/virlockspace.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virlockspace.h | 4 + 3 files changed, 242 insertions(+)
ACK but see my comments below
+ for (i = 0 ; i < n ; i++) { + virJSONValuePtr child = virJSONValueArrayGet(resources, i); + virLockSpaceResourcePtr res; + const char *tmp; + virJSONValuePtr owners; + size_t j; + int m; + + if (VIR_ALLOC(res) < 0) { + virReportOOMError(); + goto error; + } + res->fd = -1;
This is useless ...
+ + if (!(tmp = virJSONValueObjectGetString(child, "name"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource name in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + if (!(res->name = strdup(tmp))) { + virReportOOMError(); + virLockSpaceResourceFree(res); + goto error; + } + + if (!(tmp = virJSONValueObjectGetString(child, "path"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource path in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + if (!(res->path = strdup(tmp))) { + virReportOOMError(); + virLockSpaceResourceFree(res); + goto error; + } + if (virJSONValueObjectGetNumberInt(child, "fd", &res->fd) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource fd in JSON document")); + virLockSpaceResourceFree(res); + goto error; + }
... since we require 'fd' attribute anyway.
It is not useless actually. The virLockSpaceResourceFree() method will do VIR_FORCE_CLOSE(res->fd); So if we were to hit the error paths before we read the 'fd' attribute, we'll end up doing VIR_FORCE_CLOSE(0) which is stdin. We can't rely on init-to-zero for file descriptor fields in structs - you must always initialize them to -1 explicitly.
+ if (virSetInherit(res->fd, false) < 0) { + virReportSystemError(errno, "%s", + _("Cannot enable close-on-exec flag")); + goto error; + } + if (virJSONValueObjectGetBoolean(child, "lockHeld", &res->lockHeld) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource lockHeld in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if (virJSONValueObjectGetNumberUint(child, "flags", &res->flags) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource flags in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if (!(owners = virJSONValueObjectGet(child, "owners"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing resource owners in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if ((m = virJSONValueArraySize(owners)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed owners value in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } +
Since we already know the res->owners size, wouldn't it be better to allocate it here instead of ...
+ for (j = 0 ; j < m ; j++) { + unsigned long long int owner; + virJSONValuePtr ownerval = virJSONValueArrayGet(owners, j); + + if (virJSONValueGetNumberUlong(ownerval, &owner) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed owner value in JSON document")); + virLockSpaceResourceFree(res); + goto error; + } + + if (VIR_EXPAND_N(res->owners, res->nOwners, 1) < 0) { + virReportOOMError(); + virLockSpaceResourceFree(res); + goto error;
... doing this?
Yes, we can do that. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

From: "Daniel P. Berrange" <berrange@redhat.com> Add two new APIs virNetSocketNewPostExecRestart and virNetSocketPreExecRestart which allow a virNetSocketPtr object to be created from a JSON object and saved to a JSON object, for the purpose of re-exec'ing a process. As well as saving the state in JSON format, the second method will disable the O_CLOEXEC flag so that the open file descriptors are preserved across the process re-exec() Since it is not possible to serialize SASL or TLS encryption state, an error will be raised if attempting to perform serialization on non-raw sockets Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 2 + src/rpc/virnetsocket.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetsocket.h | 6 +++ 3 files changed, 116 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6c94584..73e89e0 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1633,6 +1633,8 @@ virNetSocketNewConnectUNIX; virNetSocketNewListenFD; virNetSocketNewListenTCP; virNetSocketNewListenUNIX; +virNetSocketNewPostExecRestart; +virNetSocketPreExecRestart; virNetSocketRead; virNetSocketRecvFD; virNetSocketRemoteAddrString; diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index 5a48300..6bff170 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -881,6 +881,114 @@ int virNetSocketNewConnectExternal(const char **cmdargv, } +virNetSocketPtr virNetSocketNewPostExecRestart(virJSONValuePtr object) +{ + virSocketAddr localAddr; + virSocketAddr remoteAddr; + int fd, thepid, errfd; + bool isClient; + + if (virJSONValueObjectGetNumberInt(object, "fd", &fd) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing fd data in JSON document")); + return NULL; + } + + if (virJSONValueObjectGetNumberInt(object, "pid", &thepid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing pid data in JSON document")); + return NULL; + } + + if (virJSONValueObjectGetNumberInt(object, "errfd", &errfd) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing errfd data in JSON document")); + return NULL; + } + if (virJSONValueObjectGetBoolean(object, "isClient", &isClient) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing isClient data in JSON document")); + return NULL; + } + + memset(&localAddr, 0, sizeof(localAddr)); + memset(&remoteAddr, 0, sizeof(remoteAddr)); + + remoteAddr.len = sizeof(remoteAddr.data.stor); + if (getsockname(fd, &remoteAddr.data.sa, &remoteAddr.len) < 0) { + virReportSystemError(errno, "%s", _("Unable to get peer socket name")); + return NULL; + } + + localAddr.len = sizeof(localAddr.data.stor); + if (getsockname(fd, &localAddr.data.sa, &localAddr.len) < 0) { + virReportSystemError(errno, "%s", _("Unable to get local socket name")); + return NULL; + } + + return virNetSocketNew(&localAddr, &remoteAddr, + isClient, fd, errfd, thepid); +} + + +virJSONValuePtr virNetSocketPreExecRestart(virNetSocketPtr sock) +{ + virJSONValuePtr object = NULL; + + virMutexLock(&sock->lock); + +#if HAVE_SASL + if (sock->saslSession) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Unable to save socket state when SASL session is active")); + goto error; + } +#endif + if (sock->tlsSession) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Unable to save socket state when TLS session is active")); + goto error; + } + + if (!(object = virJSONValueNewObject())) + goto error; + + if (virJSONValueObjectAppendNumberInt(object, "fd", sock->fd) < 0) + goto error; + + if (virJSONValueObjectAppendNumberInt(object, "errfd", sock->errfd) < 0) + goto error; + + if (virJSONValueObjectAppendNumberInt(object, "pid", sock->pid) < 0) + goto error; + + if (virJSONValueObjectAppendBoolean(object, "isClient", sock->client) < 0) + goto error; + + if (virSetInherit(sock->fd, true) < 0) { + virReportSystemError(errno, + _("Cannot disable close-on-exec flag on socket %d"), + sock->fd); + goto error; + } + if (sock->errfd != -1 && + virSetInherit(sock->errfd, true) < 0) { + virReportSystemError(errno, + _("Cannot disable close-on-exec flag on pipe %d"), + sock->errfd); + goto error; + } + + virMutexUnlock(&sock->lock); + return object; + +error: + virMutexUnlock(&sock->lock); + virJSONValueFree(object); + return NULL; +} + + void virNetSocketDispose(void *obj) { virNetSocketPtr sock = obj; diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h index 75b66c5..604103b 100644 --- a/src/rpc/virnetsocket.h +++ b/src/rpc/virnetsocket.h @@ -31,6 +31,7 @@ # ifdef HAVE_SASL # include "virnetsaslcontext.h" # endif +# include "json.h" typedef struct _virNetSocket virNetSocket; typedef virNetSocket *virNetSocketPtr; @@ -93,6 +94,11 @@ int virNetSocketNewConnectLibSSH2(const char *host, int virNetSocketNewConnectExternal(const char **cmdargv, virNetSocketPtr *addr); + +virNetSocketPtr virNetSocketNewPostExecRestart(virJSONValuePtr object); + +virJSONValuePtr virNetSocketPreExecRestart(virNetSocketPtr sock); + int virNetSocketGetFD(virNetSocketPtr sock); int virNetSocketDupFD(virNetSocketPtr sock, bool cloexec); -- 1.7.11.2

On 12.09.2012 18:28, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add two new APIs virNetSocketNewPostExecRestart and virNetSocketPreExecRestart which allow a virNetSocketPtr object to be created from a JSON object and saved to a JSON object, for the purpose of re-exec'ing a process.
As well as saving the state in JSON format, the second method will disable the O_CLOEXEC flag so that the open file descriptors are preserved across the process re-exec()
Since it is not possible to serialize SASL or TLS encryption state, an error will be raised if attempting to perform serialization on non-raw sockets
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 2 + src/rpc/virnetsocket.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetsocket.h | 6 +++ 3 files changed, 116 insertions(+)
ACK
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6c94584..73e89e0 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1633,6 +1633,8 @@ virNetSocketNewConnectUNIX; virNetSocketNewListenFD; virNetSocketNewListenTCP; virNetSocketNewListenUNIX; +virNetSocketNewPostExecRestart; +virNetSocketPreExecRestart; virNetSocketRead; virNetSocketRecvFD; virNetSocketRemoteAddrString; diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index 5a48300..6bff170 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -881,6 +881,114 @@ int virNetSocketNewConnectExternal(const char **cmdargv, }
+virNetSocketPtr virNetSocketNewPostExecRestart(virJSONValuePtr object) +{ + virSocketAddr localAddr; + virSocketAddr remoteAddr; + int fd, thepid, errfd; + bool isClient; + + if (virJSONValueObjectGetNumberInt(object, "fd", &fd) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing fd data in JSON document")); + return NULL; + } + + if (virJSONValueObjectGetNumberInt(object, "pid", &thepid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing pid data in JSON document")); + return NULL; + } + + if (virJSONValueObjectGetNumberInt(object, "errfd", &errfd) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing errfd data in JSON document")); + return NULL; + } + if (virJSONValueObjectGetBoolean(object, "isClient", &isClient) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing isClient data in JSON document")); + return NULL; + } + + memset(&localAddr, 0, sizeof(localAddr)); + memset(&remoteAddr, 0, sizeof(remoteAddr)); + + remoteAddr.len = sizeof(remoteAddr.data.stor); + if (getsockname(fd, &remoteAddr.data.sa, &remoteAddr.len) < 0) { + virReportSystemError(errno, "%s", _("Unable to get peer socket name")); + return NULL; + } + + localAddr.len = sizeof(localAddr.data.stor); + if (getsockname(fd, &localAddr.data.sa, &localAddr.len) < 0) { + virReportSystemError(errno, "%s", _("Unable to get local socket name")); + return NULL; + } + + return virNetSocketNew(&localAddr, &remoteAddr, + isClient, fd, errfd, thepid); +} + + +virJSONValuePtr virNetSocketPreExecRestart(virNetSocketPtr sock) +{ + virJSONValuePtr object = NULL; + + virMutexLock(&sock->lock); + +#if HAVE_SASL + if (sock->saslSession) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Unable to save socket state when SASL session is active")); + goto error; + } +#endif + if (sock->tlsSession) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Unable to save socket state when TLS session is active")); + goto error; + } + + if (!(object = virJSONValueNewObject())) + goto error; + + if (virJSONValueObjectAppendNumberInt(object, "fd", sock->fd) < 0) + goto error; + + if (virJSONValueObjectAppendNumberInt(object, "errfd", sock->errfd) < 0) + goto error; + + if (virJSONValueObjectAppendNumberInt(object, "pid", sock->pid) < 0) + goto error; + + if (virJSONValueObjectAppendBoolean(object, "isClient", sock->client) < 0) + goto error; + + if (virSetInherit(sock->fd, true) < 0) { + virReportSystemError(errno, + _("Cannot disable close-on-exec flag on socket %d"), + sock->fd); + goto error; + } + if (sock->errfd != -1 && + virSetInherit(sock->errfd, true) < 0) { + virReportSystemError(errno, + _("Cannot disable close-on-exec flag on pipe %d"), + sock->errfd); + goto error; + } + + virMutexUnlock(&sock->lock); + return object; + +error: + virMutexUnlock(&sock->lock); + virJSONValueFree(object); + return NULL; +} + + void virNetSocketDispose(void *obj) { virNetSocketPtr sock = obj; diff --git a/src/rpc/virnetsocket.h b/src/rpc/virnetsocket.h index 75b66c5..604103b 100644 --- a/src/rpc/virnetsocket.h +++ b/src/rpc/virnetsocket.h @@ -31,6 +31,7 @@ # ifdef HAVE_SASL # include "virnetsaslcontext.h" # endif +# include "json.h"
typedef struct _virNetSocket virNetSocket; typedef virNetSocket *virNetSocketPtr; @@ -93,6 +94,11 @@ int virNetSocketNewConnectLibSSH2(const char *host, int virNetSocketNewConnectExternal(const char **cmdargv, virNetSocketPtr *addr);
+ +virNetSocketPtr virNetSocketNewPostExecRestart(virJSONValuePtr object); + +virJSONValuePtr virNetSocketPreExecRestart(virNetSocketPtr sock); + int virNetSocketGetFD(virNetSocketPtr sock); int virNetSocketDupFD(virNetSocketPtr sock, bool cloexec);

From: "Daniel P. Berrange" <berrange@redhat.com> Add two new APIs virNetServerServiceNewPostExecRestart and virNetServerServicePreExecRestart which allow a virNetServerServicePtr object to be created from a JSON object and saved to a JSON object, for the purpose of re-exec'ing a process. This includes serialization of the listening sockets associated with the service Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- po/POTFILES.in | 1 + src/libvirt_private.syms | 2 + src/rpc/virnetserverservice.c | 124 ++++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetserverservice.h | 4 ++ 4 files changed, 131 insertions(+) diff --git a/po/POTFILES.in b/po/POTFILES.in index c5f4cf7..e8101a4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -101,6 +101,7 @@ src/rpc/virnetserver.c src/rpc/virnetserverclient.c src/rpc/virnetservermdns.c src/rpc/virnetserverprogram.c +src/rpc/virnetserverservice.c src/rpc/virnetsshsession.c src/rpc/virnettlscontext.c src/secret/secret_driver.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 73e89e0..b847f46 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1604,8 +1604,10 @@ virNetServerServiceGetPort; virNetServerServiceGetTLSContext; virNetServerServiceIsReadonly; virNetServerServiceNewFD; +virNetServerServiceNewPostExecRestart; virNetServerServiceNewTCP; virNetServerServiceNewUNIX; +virNetServerServicePreExecRestart; virNetServerServiceSetDispatcher; virNetServerServiceToggle; diff --git a/src/rpc/virnetserverservice.c b/src/rpc/virnetserverservice.c index 53ff503..c31bff3 100644 --- a/src/rpc/virnetserverservice.c +++ b/src/rpc/virnetserverservice.c @@ -250,6 +250,130 @@ error: } +virNetServerServicePtr virNetServerServiceNewPostExecRestart(virJSONValuePtr object) +{ + virNetServerServicePtr svc; + virJSONValuePtr socks; + size_t i; + int n; + + if (virNetServerServiceInitialize() < 0) + return NULL; + + if (!(svc = virObjectNew(virNetServerServiceClass))) + return NULL; + + if (virJSONValueObjectGetNumberInt(object, "auth", &svc->auth) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing auth field in JSON state document")); + goto error; + } + if (virJSONValueObjectGetBoolean(object, "readonly", &svc->readonly) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing readonly field in JSON state document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "nrequests_client_max", + (unsigned int *)&svc->nrequests_client_max) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing nrequests_client_max field in JSON state document")); + goto error; + } + + if (!(socks = virJSONValueObjectGet(object, "socks"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing socks field in JSON state document")); + goto error; + } + + if ((n = virJSONValueArraySize(socks)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("socks field in JSON was not an array")); + goto error; + } + + for (i = 0 ; i < n ; i++) { + virJSONValuePtr child = virJSONValueArrayGet(socks, i); + virNetSocketPtr sock; + + if (!(sock = virNetSocketNewPostExecRestart(child))) { + virObjectUnref(sock); + goto error; + } + + if (VIR_EXPAND_N(svc->socks, svc->nsocks, 1) < 0) { + virReportOOMError(); + virObjectUnref(sock); + goto error; + } + + svc->socks[svc->nsocks-1] = sock; + + /* IO callback is initially disabled, until we're ready + * to deal with incoming clients */ + virObjectRef(svc); + if (virNetSocketAddIOCallback(sock, + 0, + virNetServerServiceAccept, + svc, + virObjectFreeCallback) < 0) { + virObjectUnref(svc); + virObjectUnref(sock); + goto error; + } + } + + return svc; + +error: + virObjectUnref(svc); + return NULL; +} + + +virJSONValuePtr virNetServerServicePreExecRestart(virNetServerServicePtr svc) +{ + virJSONValuePtr object = virJSONValueNewObject(); + virJSONValuePtr socks; + size_t i; + + if (!object) + return NULL; + + if (!(socks = virJSONValueNewArray())) + goto error; + + if (virJSONValueObjectAppendNumberInt(object, "auth", svc->auth) < 0) + goto error; + if (virJSONValueObjectAppendBoolean(object, "readonly", svc->readonly) < 0) + goto error; + if (virJSONValueObjectAppendNumberInt(object, "nrequests_client_max", svc->nrequests_client_max) < 0) + goto error; + + if (virJSONValueObjectAppend(object, "socks", socks) < 0) { + virJSONValueFree(socks); + goto error; + } + + for (i = 0 ; i < svc->nsocks ; i++) { + virJSONValuePtr child; + if (!(child = virNetSocketPreExecRestart(svc->socks[i]))) + goto error; + + if (virJSONValueArrayAppend(socks, child) < 0) { + virJSONValueFree(child); + goto error; + } + } + + return object; + +error: + virJSONValueFree(object); + return NULL; +} + + int virNetServerServiceGetPort(virNetServerServicePtr svc) { /* We're assuming if there are multiple sockets diff --git a/src/rpc/virnetserverservice.h b/src/rpc/virnetserverservice.h index 48f49e7..344d20c 100644 --- a/src/rpc/virnetserverservice.h +++ b/src/rpc/virnetserverservice.h @@ -56,6 +56,10 @@ virNetServerServicePtr virNetServerServiceNewFD(int fd, size_t nrequests_client_max, virNetTLSContextPtr tls); +virNetServerServicePtr virNetServerServiceNewPostExecRestart(virJSONValuePtr object); + +virJSONValuePtr virNetServerServicePreExecRestart(virNetServerServicePtr service); + int virNetServerServiceGetPort(virNetServerServicePtr svc); int virNetServerServiceGetAuth(virNetServerServicePtr svc); -- 1.7.11.2

On 12.09.2012 18:28, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add two new APIs virNetServerServiceNewPostExecRestart and virNetServerServicePreExecRestart which allow a virNetServerServicePtr object to be created from a JSON object and saved to a JSON object, for the purpose of re-exec'ing a process.
This includes serialization of the listening sockets associated with the service
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- po/POTFILES.in | 1 + src/libvirt_private.syms | 2 + src/rpc/virnetserverservice.c | 124 ++++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetserverservice.h | 4 ++ 4 files changed, 131 insertions(+)
ACK but see my comments below
diff --git a/po/POTFILES.in b/po/POTFILES.in index c5f4cf7..e8101a4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -101,6 +101,7 @@ src/rpc/virnetserver.c src/rpc/virnetserverclient.c src/rpc/virnetservermdns.c src/rpc/virnetserverprogram.c +src/rpc/virnetserverservice.c src/rpc/virnetsshsession.c src/rpc/virnettlscontext.c src/secret/secret_driver.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 73e89e0..b847f46 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1604,8 +1604,10 @@ virNetServerServiceGetPort; virNetServerServiceGetTLSContext; virNetServerServiceIsReadonly; virNetServerServiceNewFD; +virNetServerServiceNewPostExecRestart; virNetServerServiceNewTCP; virNetServerServiceNewUNIX; +virNetServerServicePreExecRestart; virNetServerServiceSetDispatcher; virNetServerServiceToggle;
diff --git a/src/rpc/virnetserverservice.c b/src/rpc/virnetserverservice.c index 53ff503..c31bff3 100644 --- a/src/rpc/virnetserverservice.c +++ b/src/rpc/virnetserverservice.c @@ -250,6 +250,130 @@ error: }
+virNetServerServicePtr virNetServerServiceNewPostExecRestart(virJSONValuePtr object) +{ + virNetServerServicePtr svc; + virJSONValuePtr socks; + size_t i; + int n; + + if (virNetServerServiceInitialize() < 0) + return NULL; + + if (!(svc = virObjectNew(virNetServerServiceClass))) + return NULL; + + if (virJSONValueObjectGetNumberInt(object, "auth", &svc->auth) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing auth field in JSON state document")); + goto error; + } + if (virJSONValueObjectGetBoolean(object, "readonly", &svc->readonly) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing readonly field in JSON state document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "nrequests_client_max", + (unsigned int *)&svc->nrequests_client_max) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing nrequests_client_max field in JSON state document")); + goto error; + } + + if (!(socks = virJSONValueObjectGet(object, "socks"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing socks field in JSON state document")); + goto error; + } + + if ((n = virJSONValueArraySize(socks)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("socks field in JSON was not an array")); + goto error; + } + + for (i = 0 ; i < n ; i++) { + virJSONValuePtr child = virJSONValueArrayGet(socks, i); + virNetSocketPtr sock; + + if (!(sock = virNetSocketNewPostExecRestart(child))) { + virObjectUnref(sock); + goto error; + } + + if (VIR_EXPAND_N(svc->socks, svc->nsocks, 1) < 0) { + virReportOOMError(); + virObjectUnref(sock); + goto error; + }
Again, it's better to alloc mem at one if we know the desired size.
+ + svc->socks[svc->nsocks-1] = sock; + + /* IO callback is initially disabled, until we're ready + * to deal with incoming clients */ + virObjectRef(svc); + if (virNetSocketAddIOCallback(sock, + 0, + virNetServerServiceAccept, + svc, + virObjectFreeCallback) < 0) { + virObjectUnref(svc); + virObjectUnref(sock); + goto error; + } + } + + return svc; + +error: + virObjectUnref(svc); + return NULL; +} + + +virJSONValuePtr virNetServerServicePreExecRestart(virNetServerServicePtr svc) +{ + virJSONValuePtr object = virJSONValueNewObject(); + virJSONValuePtr socks; + size_t i; + + if (!object) + return NULL; + + if (!(socks = virJSONValueNewArray())) + goto error; + + if (virJSONValueObjectAppendNumberInt(object, "auth", svc->auth) < 0) + goto error; + if (virJSONValueObjectAppendBoolean(object, "readonly", svc->readonly) < 0) + goto error; + if (virJSONValueObjectAppendNumberInt(object, "nrequests_client_max", svc->nrequests_client_max) < 0)
This should be AppendNumberUInt()
+ goto error; + + if (virJSONValueObjectAppend(object, "socks", socks) < 0) { + virJSONValueFree(socks); + goto error; + } + + for (i = 0 ; i < svc->nsocks ; i++) { + virJSONValuePtr child; + if (!(child = virNetSocketPreExecRestart(svc->socks[i]))) + goto error; + + if (virJSONValueArrayAppend(socks, child) < 0) { + virJSONValueFree(child); + goto error; + } + } + + return object; + +error: + virJSONValueFree(object); + return NULL; +} + + int virNetServerServiceGetPort(virNetServerServicePtr svc) { /* We're assuming if there are multiple sockets diff --git a/src/rpc/virnetserverservice.h b/src/rpc/virnetserverservice.h index 48f49e7..344d20c 100644 --- a/src/rpc/virnetserverservice.h +++ b/src/rpc/virnetserverservice.h @@ -56,6 +56,10 @@ virNetServerServicePtr virNetServerServiceNewFD(int fd, size_t nrequests_client_max, virNetTLSContextPtr tls);
+virNetServerServicePtr virNetServerServiceNewPostExecRestart(virJSONValuePtr object); + +virJSONValuePtr virNetServerServicePreExecRestart(virNetServerServicePtr service); + int virNetServerServiceGetPort(virNetServerServicePtr svc);
int virNetServerServiceGetAuth(virNetServerServicePtr svc);

From: "Daniel P. Berrange" <berrange@redhat.com> Add two new APIs virNetServerClientNewPostExecRestart and virNetServerClientPreExecRestart which allow a virNetServerClientPtr object to be created from a JSON object and saved to a JSON object, for the purpose of re-exec'ing a process. This includes serialization of the connected socket associated with the client Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/libvirtd.c | 1 + src/libvirt_private.syms | 2 + src/lxc/lxc_controller.c | 1 + src/rpc/virnetserver.c | 4 ++ src/rpc/virnetserver.h | 1 + src/rpc/virnetserverclient.c | 135 +++++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetserverclient.h | 15 +++++ 7 files changed, 159 insertions(+) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 6973df6..5696073 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -1204,6 +1204,7 @@ int main(int argc, char **argv) { !!config->keepalive_required, config->mdns_adv ? config->mdns_name : NULL, remoteClientInitHook, + NULL, remoteClientFreeFunc, NULL))) { ret = VIR_DAEMON_ERR_INIT; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b847f46..004a8da 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1560,6 +1560,8 @@ virNetServerClientIsSecure; virNetServerClientLocalAddrString; virNetServerClientNeedAuth; virNetServerClientNew; +virNetServerClientNewPostExecRestart; +virNetServerClientPreExecRestart; virNetServerClientRemoteAddrString; virNetServerClientRemoveFilter; virNetServerClientSendMessage; diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c index e5aea11..74bf7ac 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -616,6 +616,7 @@ static int virLXCControllerSetupServer(virLXCControllerPtr ctrl) -1, 0, false, NULL, virLXCControllerClientPrivateNew, + NULL, virLXCControllerClientPrivateFree, ctrl))) goto error; diff --git a/src/rpc/virnetserver.c b/src/rpc/virnetserver.c index 0a6ecdc..902c34e 100644 --- a/src/rpc/virnetserver.c +++ b/src/rpc/virnetserver.c @@ -105,6 +105,7 @@ struct _virNetServer { void *autoShutdownOpaque; virNetServerClientPrivNew clientPrivNew; + virNetServerClientPrivPreExecRestart clientPrivPreExecRestart; virFreeCallback clientPrivFree; void *clientPrivOpaque; }; @@ -312,6 +313,7 @@ static int virNetServerDispatchNewClient(virNetServerServicePtr svc, virNetServerServiceGetMaxRequests(svc), virNetServerServiceGetTLSContext(svc), srv->clientPrivNew, + srv->clientPrivPreExecRestart, srv->clientPrivFree, srv->clientPrivOpaque))) return -1; @@ -363,6 +365,7 @@ virNetServerPtr virNetServerNew(size_t min_workers, bool keepaliveRequired, const char *mdnsGroupName, virNetServerClientPrivNew clientPrivNew, + virNetServerClientPrivPreExecRestart clientPrivPreExecRestart, virFreeCallback clientPrivFree, void *clientPrivOpaque) { @@ -388,6 +391,7 @@ virNetServerPtr virNetServerNew(size_t min_workers, srv->keepaliveRequired = keepaliveRequired; srv->sigwrite = srv->sigread = -1; srv->clientPrivNew = clientPrivNew; + srv->clientPrivPreExecRestart = clientPrivPreExecRestart; srv->clientPrivFree = clientPrivFree; srv->clientPrivOpaque = clientPrivOpaque; srv->privileged = geteuid() == 0 ? true : false; diff --git a/src/rpc/virnetserver.h b/src/rpc/virnetserver.h index 9188072..778b069 100644 --- a/src/rpc/virnetserver.h +++ b/src/rpc/virnetserver.h @@ -41,6 +41,7 @@ virNetServerPtr virNetServerNew(size_t min_workers, bool keepaliveRequired, const char *mdnsGroupName, virNetServerClientPrivNew clientPrivNew, + virNetServerClientPrivPreExecRestart clientPrivPreExecRestart, virFreeCallback clientPrivFree, void *clientPrivOpaque); diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index acd2b4d..661242a 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -98,6 +98,7 @@ struct _virNetServerClient void *privateData; virFreeCallback privateDataFreeFunc; + virNetServerClientPrivPreExecRestart privateDataPreExecRestart; virNetServerClientCloseFunc privateDataCloseFunc; virKeepAlivePtr keepalive; @@ -395,6 +396,7 @@ virNetServerClientPtr virNetServerClientNew(virNetSocketPtr sock, size_t nrequests_max, virNetTLSContextPtr tls, virNetServerClientPrivNew privNew, + virNetServerClientPrivPreExecRestart privPreExecRestart, virFreeCallback privFree, void *privOpaque) { @@ -411,12 +413,145 @@ virNetServerClientPtr virNetServerClientNew(virNetSocketPtr sock, return NULL; } client->privateDataFreeFunc = privFree; + client->privateDataPreExecRestart = privPreExecRestart; } return client; } +virNetServerClientPtr virNetServerClientNewPostExecRestart(virJSONValuePtr object, + virNetServerClientPrivNewPostExecRestart privNew, + virNetServerClientPrivPreExecRestart privPreExecRestart, + virFreeCallback privFree, + void *privOpaque) +{ + virJSONValuePtr child; + virNetServerClientPtr client = NULL; + virNetSocketPtr sock; + const char *identity = NULL; + int auth; + bool readonly; + unsigned int nrequests_max; + + if (virJSONValueObjectGetNumberInt(object, "auth", &auth) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing auth field in JSON state document")); + return NULL; + } + if (virJSONValueObjectGetBoolean(object, "readonly", &readonly) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing readonly field in JSON state document")); + return NULL; + } + if (virJSONValueObjectGetNumberUint(object, "nrequests_max", + (unsigned int *)&nrequests_max) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing nrequests_client_max field in JSON state document")); + return NULL; + } + if (virJSONValueObjectHasKey(object, "identity") && + (!(identity = virJSONValueObjectGetString(object, "identity")))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing identity field in JSON state document")); + return NULL; + } + + if (!(child = virJSONValueObjectGet(object, "sock"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing sock field in JSON state document")); + return NULL; + } + + if (!(sock = virNetSocketNewPostExecRestart(child))) { + virObjectUnref(sock); + return NULL; + } + + if (!(client = virNetServerClientNewInternal(sock, + auth, + readonly, + nrequests_max, + NULL))) { + virObjectUnref(sock); + return NULL; + } + virObjectUnref(sock); + + if (identity && + virNetServerClientSetIdentity(client, identity) < 0) + goto error; + + if (privNew) { + if (!(child = virJSONValueObjectGet(object, "privateData"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing privateData field in JSON state document")); + goto error; + } + if (!(client->privateData = privNew(client, child, privOpaque))) { + goto error; + } + client->privateDataFreeFunc = privFree; + client->privateDataPreExecRestart = privPreExecRestart; + } + + + return client; + +error: + virObjectUnref(client); + return NULL; +} + + +virJSONValuePtr virNetServerClientPreExecRestart(virNetServerClientPtr client) +{ + virJSONValuePtr object = virJSONValueNewObject(); + virJSONValuePtr child; + + if (!object) + return NULL; + + virNetServerClientLock(client); + + if (virJSONValueObjectAppendNumberInt(object, "auth", client->auth) < 0) + goto error; + if (virJSONValueObjectAppendBoolean(object, "readonly", client->readonly) < 0) + goto error; + if (virJSONValueObjectAppendNumberUint(object, "nrequests_max", client->nrequests_max) < 0) + goto error; + + if (client->identity && + virJSONValueObjectAppendString(object, "identity", client->identity) < 0) + goto error; + + if (!(child = virNetSocketPreExecRestart(client->sock))) + goto error; + + if (virJSONValueObjectAppend(object, "sock", child) < 0) { + virJSONValueFree(child); + goto error; + } + + if (client->privateData && client->privateDataPreExecRestart && + !(child = client->privateDataPreExecRestart(client, client->privateData))) + goto error; + + if (virJSONValueObjectAppend(object, "privateData", child) < 0) { + virJSONValueFree(child); + goto error; + } + + virNetServerClientUnlock(client); + return object; + +error: + virNetServerClientUnlock(client); + virJSONValueFree(object); + return NULL; +} + + int virNetServerClientGetAuth(virNetServerClientPtr client) { int auth; diff --git a/src/rpc/virnetserverclient.h b/src/rpc/virnetserverclient.h index f950c61..2a2413b 100644 --- a/src/rpc/virnetserverclient.h +++ b/src/rpc/virnetserverclient.h @@ -27,6 +27,7 @@ # include "virnetsocket.h" # include "virnetmessage.h" # include "virobject.h" +# include "json.h" typedef struct _virNetServerClient virNetServerClient; typedef virNetServerClient *virNetServerClientPtr; @@ -39,6 +40,11 @@ typedef int (*virNetServerClientFilterFunc)(virNetServerClientPtr client, virNetMessagePtr msg, void *opaque); +typedef virJSONValuePtr (*virNetServerClientPrivPreExecRestart)(virNetServerClientPtr client, + void *data); +typedef void *(*virNetServerClientPrivNewPostExecRestart)(virNetServerClientPtr client, + virJSONValuePtr object, + void *opaque); typedef void *(*virNetServerClientPrivNew)(virNetServerClientPtr client, void *opaque); @@ -48,9 +54,18 @@ virNetServerClientPtr virNetServerClientNew(virNetSocketPtr sock, size_t nrequests_max, virNetTLSContextPtr tls, virNetServerClientPrivNew privNew, + virNetServerClientPrivPreExecRestart privPreExecRestart, virFreeCallback privFree, void *privOpaque); +virNetServerClientPtr virNetServerClientNewPostExecRestart(virJSONValuePtr object, + virNetServerClientPrivNewPostExecRestart privNew, + virNetServerClientPrivPreExecRestart privPreExecRestart, + virFreeCallback privFree, + void *privOpaque); + +virJSONValuePtr virNetServerClientPreExecRestart(virNetServerClientPtr client); + int virNetServerClientAddFilter(virNetServerClientPtr client, virNetServerClientFilterFunc func, void *opaque); -- 1.7.11.2

On 12.09.2012 18:28, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add two new APIs virNetServerClientNewPostExecRestart and virNetServerClientPreExecRestart which allow a virNetServerClientPtr object to be created from a JSON object and saved to a JSON object, for the purpose of re-exec'ing a process.
This includes serialization of the connected socket associated with the client
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/libvirtd.c | 1 + src/libvirt_private.syms | 2 + src/lxc/lxc_controller.c | 1 + src/rpc/virnetserver.c | 4 ++ src/rpc/virnetserver.h | 1 + src/rpc/virnetserverclient.c | 135 +++++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetserverclient.h | 15 +++++ 7 files changed, 159 insertions(+)
ACK
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 6973df6..5696073 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -1204,6 +1204,7 @@ int main(int argc, char **argv) { !!config->keepalive_required, config->mdns_adv ? config->mdns_name : NULL, remoteClientInitHook, + NULL, remoteClientFreeFunc, NULL))) { ret = VIR_DAEMON_ERR_INIT; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b847f46..004a8da 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1560,6 +1560,8 @@ virNetServerClientIsSecure; virNetServerClientLocalAddrString; virNetServerClientNeedAuth; virNetServerClientNew; +virNetServerClientNewPostExecRestart; +virNetServerClientPreExecRestart; virNetServerClientRemoteAddrString; virNetServerClientRemoveFilter; virNetServerClientSendMessage; diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c index e5aea11..74bf7ac 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -616,6 +616,7 @@ static int virLXCControllerSetupServer(virLXCControllerPtr ctrl) -1, 0, false, NULL, virLXCControllerClientPrivateNew, + NULL, virLXCControllerClientPrivateFree, ctrl))) goto error; diff --git a/src/rpc/virnetserver.c b/src/rpc/virnetserver.c index 0a6ecdc..902c34e 100644 --- a/src/rpc/virnetserver.c +++ b/src/rpc/virnetserver.c @@ -105,6 +105,7 @@ struct _virNetServer { void *autoShutdownOpaque;
virNetServerClientPrivNew clientPrivNew; + virNetServerClientPrivPreExecRestart clientPrivPreExecRestart; virFreeCallback clientPrivFree; void *clientPrivOpaque; }; @@ -312,6 +313,7 @@ static int virNetServerDispatchNewClient(virNetServerServicePtr svc, virNetServerServiceGetMaxRequests(svc), virNetServerServiceGetTLSContext(svc), srv->clientPrivNew, + srv->clientPrivPreExecRestart, srv->clientPrivFree, srv->clientPrivOpaque))) return -1; @@ -363,6 +365,7 @@ virNetServerPtr virNetServerNew(size_t min_workers, bool keepaliveRequired, const char *mdnsGroupName, virNetServerClientPrivNew clientPrivNew, + virNetServerClientPrivPreExecRestart clientPrivPreExecRestart, virFreeCallback clientPrivFree, void *clientPrivOpaque) { @@ -388,6 +391,7 @@ virNetServerPtr virNetServerNew(size_t min_workers, srv->keepaliveRequired = keepaliveRequired; srv->sigwrite = srv->sigread = -1; srv->clientPrivNew = clientPrivNew; + srv->clientPrivPreExecRestart = clientPrivPreExecRestart; srv->clientPrivFree = clientPrivFree; srv->clientPrivOpaque = clientPrivOpaque; srv->privileged = geteuid() == 0 ? true : false; diff --git a/src/rpc/virnetserver.h b/src/rpc/virnetserver.h index 9188072..778b069 100644 --- a/src/rpc/virnetserver.h +++ b/src/rpc/virnetserver.h @@ -41,6 +41,7 @@ virNetServerPtr virNetServerNew(size_t min_workers, bool keepaliveRequired, const char *mdnsGroupName, virNetServerClientPrivNew clientPrivNew, + virNetServerClientPrivPreExecRestart clientPrivPreExecRestart, virFreeCallback clientPrivFree, void *clientPrivOpaque);
diff --git a/src/rpc/virnetserverclient.c b/src/rpc/virnetserverclient.c index acd2b4d..661242a 100644 --- a/src/rpc/virnetserverclient.c +++ b/src/rpc/virnetserverclient.c @@ -98,6 +98,7 @@ struct _virNetServerClient
void *privateData; virFreeCallback privateDataFreeFunc; + virNetServerClientPrivPreExecRestart privateDataPreExecRestart; virNetServerClientCloseFunc privateDataCloseFunc;
virKeepAlivePtr keepalive; @@ -395,6 +396,7 @@ virNetServerClientPtr virNetServerClientNew(virNetSocketPtr sock, size_t nrequests_max, virNetTLSContextPtr tls, virNetServerClientPrivNew privNew, + virNetServerClientPrivPreExecRestart privPreExecRestart, virFreeCallback privFree, void *privOpaque) { @@ -411,12 +413,145 @@ virNetServerClientPtr virNetServerClientNew(virNetSocketPtr sock, return NULL; } client->privateDataFreeFunc = privFree; + client->privateDataPreExecRestart = privPreExecRestart; }
return client; }
+virNetServerClientPtr virNetServerClientNewPostExecRestart(virJSONValuePtr object, + virNetServerClientPrivNewPostExecRestart privNew, + virNetServerClientPrivPreExecRestart privPreExecRestart, + virFreeCallback privFree, + void *privOpaque) +{ + virJSONValuePtr child; + virNetServerClientPtr client = NULL; + virNetSocketPtr sock; + const char *identity = NULL; + int auth; + bool readonly; + unsigned int nrequests_max; + + if (virJSONValueObjectGetNumberInt(object, "auth", &auth) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing auth field in JSON state document")); + return NULL; + } + if (virJSONValueObjectGetBoolean(object, "readonly", &readonly) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing readonly field in JSON state document")); + return NULL; + } + if (virJSONValueObjectGetNumberUint(object, "nrequests_max", + (unsigned int *)&nrequests_max) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing nrequests_client_max field in JSON state document")); + return NULL; + } + if (virJSONValueObjectHasKey(object, "identity") && + (!(identity = virJSONValueObjectGetString(object, "identity")))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing identity field in JSON state document")); + return NULL; + } + + if (!(child = virJSONValueObjectGet(object, "sock"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing sock field in JSON state document")); + return NULL; + } + + if (!(sock = virNetSocketNewPostExecRestart(child))) { + virObjectUnref(sock); + return NULL; + } + + if (!(client = virNetServerClientNewInternal(sock, + auth, + readonly, + nrequests_max, + NULL))) { + virObjectUnref(sock); + return NULL; + } + virObjectUnref(sock); + + if (identity && + virNetServerClientSetIdentity(client, identity) < 0) + goto error; + + if (privNew) { + if (!(child = virJSONValueObjectGet(object, "privateData"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing privateData field in JSON state document")); + goto error; + } + if (!(client->privateData = privNew(client, child, privOpaque))) { + goto error; + } + client->privateDataFreeFunc = privFree; + client->privateDataPreExecRestart = privPreExecRestart; + } + + + return client; + +error: + virObjectUnref(client); + return NULL; +} + + +virJSONValuePtr virNetServerClientPreExecRestart(virNetServerClientPtr client) +{ + virJSONValuePtr object = virJSONValueNewObject(); + virJSONValuePtr child; + + if (!object) + return NULL; + + virNetServerClientLock(client); + + if (virJSONValueObjectAppendNumberInt(object, "auth", client->auth) < 0) + goto error; + if (virJSONValueObjectAppendBoolean(object, "readonly", client->readonly) < 0) + goto error; + if (virJSONValueObjectAppendNumberUint(object, "nrequests_max", client->nrequests_max) < 0) + goto error; + + if (client->identity && + virJSONValueObjectAppendString(object, "identity", client->identity) < 0) + goto error; + + if (!(child = virNetSocketPreExecRestart(client->sock))) + goto error; + + if (virJSONValueObjectAppend(object, "sock", child) < 0) { + virJSONValueFree(child); + goto error; + } + + if (client->privateData && client->privateDataPreExecRestart && + !(child = client->privateDataPreExecRestart(client, client->privateData))) + goto error; + + if (virJSONValueObjectAppend(object, "privateData", child) < 0) { + virJSONValueFree(child); + goto error; + } + + virNetServerClientUnlock(client); + return object; + +error: + virNetServerClientUnlock(client); + virJSONValueFree(object); + return NULL; +} + + int virNetServerClientGetAuth(virNetServerClientPtr client) { int auth; diff --git a/src/rpc/virnetserverclient.h b/src/rpc/virnetserverclient.h index f950c61..2a2413b 100644 --- a/src/rpc/virnetserverclient.h +++ b/src/rpc/virnetserverclient.h @@ -27,6 +27,7 @@ # include "virnetsocket.h" # include "virnetmessage.h" # include "virobject.h" +# include "json.h"
typedef struct _virNetServerClient virNetServerClient; typedef virNetServerClient *virNetServerClientPtr; @@ -39,6 +40,11 @@ typedef int (*virNetServerClientFilterFunc)(virNetServerClientPtr client, virNetMessagePtr msg, void *opaque);
+typedef virJSONValuePtr (*virNetServerClientPrivPreExecRestart)(virNetServerClientPtr client, + void *data); +typedef void *(*virNetServerClientPrivNewPostExecRestart)(virNetServerClientPtr client, + virJSONValuePtr object, + void *opaque); typedef void *(*virNetServerClientPrivNew)(virNetServerClientPtr client, void *opaque);
@@ -48,9 +54,18 @@ virNetServerClientPtr virNetServerClientNew(virNetSocketPtr sock, size_t nrequests_max, virNetTLSContextPtr tls, virNetServerClientPrivNew privNew, + virNetServerClientPrivPreExecRestart privPreExecRestart, virFreeCallback privFree, void *privOpaque);
+virNetServerClientPtr virNetServerClientNewPostExecRestart(virJSONValuePtr object, + virNetServerClientPrivNewPostExecRestart privNew, + virNetServerClientPrivPreExecRestart privPreExecRestart, + virFreeCallback privFree, + void *privOpaque); + +virJSONValuePtr virNetServerClientPreExecRestart(virNetServerClientPtr client); + int virNetServerClientAddFilter(virNetServerClientPtr client, virNetServerClientFilterFunc func, void *opaque);

From: "Daniel P. Berrange" <berrange@redhat.com> Add two new APIs virNetServerNewPostExecRestart and virNetServerPreExecRestart which allow a virNetServerPtr object to be created from a JSON object and saved to a JSON object, for the purpose of re-exec'ing a process. This includes serialization of all registered services and clients Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 2 + src/rpc/virnetserver.c | 252 +++++++++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetserver.h | 10 ++ 3 files changed, 264 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 004a8da..338c75e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1534,6 +1534,8 @@ virNetServerClose; virNetServerIsPrivileged; virNetServerKeepAliveRequired; virNetServerNew; +virNetServerNewPostExecRestart; +virNetServerPreExecRestart; virNetServerQuit; virNetServerRun; virNetServerSetTLSContext; diff --git a/src/rpc/virnetserver.c b/src/rpc/virnetserver.c index 902c34e..92e0264 100644 --- a/src/rpc/virnetserver.c +++ b/src/rpc/virnetserver.c @@ -447,6 +447,258 @@ error: } +virNetServerPtr virNetServerNewPostExecRestart(virJSONValuePtr object, + virNetServerClientPrivNew clientPrivNew, + virNetServerClientPrivNewPostExecRestart clientPrivNewPostExecRestart, + virNetServerClientPrivPreExecRestart clientPrivPreExecRestart, + virFreeCallback clientPrivFree, + void *clientPrivOpaque) +{ + virNetServerPtr srv = NULL; + virJSONValuePtr clients; + virJSONValuePtr services; + size_t i; + int n; + unsigned int min_workers; + unsigned int max_workers; + unsigned int priority_workers; + unsigned int max_clients; + unsigned int keepaliveInterval; + unsigned int keepaliveCount; + bool keepaliveRequired; + const char *mdnsGroupName = NULL; + + if (virJSONValueObjectGetNumberUint(object, "min_workers", &min_workers) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing min_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "max_workers", &max_workers) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing max_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "priority_workers", &priority_workers) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing priority_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "max_clients", &max_clients) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing max_clients data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "keepaliveInterval", &keepaliveInterval) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing keepaliveInterval data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "keepaliveCount", &keepaliveCount) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing keepaliveCount data in JSON document")); + goto error; + } + if (virJSONValueObjectGetBoolean(object, "keepaliveRequired", &keepaliveRequired) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing keepaliveRequired data in JSON document")); + goto error; + } + + if (virJSONValueObjectHasKey(object, "mdnsGroupName") && + (!(mdnsGroupName = virJSONValueObjectGetString(object, "mdnsGroupName")))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed mdnsGroupName data in JSON document")); + goto error; + } + + if (!(srv = virNetServerNew(min_workers, max_clients, + priority_workers, max_clients, + keepaliveInterval, keepaliveCount, + keepaliveRequired, mdnsGroupName, + clientPrivNew, clientPrivPreExecRestart, + clientPrivFree, clientPrivOpaque))) + goto error; + + if (!(services = virJSONValueObjectGet(object, "services"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing services data in JSON document")); + goto error; + } + + n = virJSONValueArraySize(services); + if (n < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed services data in JSON document")); + goto error; + } + + for (i = 0 ; i < n ; i++) { + virNetServerServicePtr service; + virJSONValuePtr child = virJSONValueArrayGet(services, i); + if (!child) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing service data in JSON document")); + goto error; + } + + if (!(service = virNetServerServiceNewPostExecRestart(child))) + goto error; + + /* XXX mdns entry names ? */ + if (virNetServerAddService(srv, service, NULL) < 0) { + virObjectUnref(service); + goto error; + } + } + + + if (!(clients = virJSONValueObjectGet(object, "clients"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing clients data in JSON document")); + goto error; + } + + n = virJSONValueArraySize(clients); + if (n < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed clients data in JSON document")); + goto error; + } + + for (i = 0 ; i < n ; i++) { + virNetServerClientPtr client; + virJSONValuePtr child = virJSONValueArrayGet(clients, i); + if (!child) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing client data in JSON document")); + goto error; + } + + if (!(client = virNetServerClientNewPostExecRestart(child, + clientPrivNewPostExecRestart, + clientPrivPreExecRestart, + clientPrivFree, + clientPrivOpaque))) + goto error; + + if (virNetServerAddClient(srv, client) < 0) { + virObjectUnref(client); + goto error; + } + virObjectUnref(client); + } + + return srv; + +error: + virObjectUnref(srv); + return NULL; +} + + +virJSONValuePtr virNetServerPreExecRestart(virNetServerPtr srv) +{ + virJSONValuePtr object; + virJSONValuePtr clients; + virJSONValuePtr services; + size_t i; + + virMutexLock(&srv->lock); + + if (!(object = virJSONValueNewObject())) + goto error; + + if (virJSONValueObjectAppendNumberUint(object, "min_workers", + virThreadPoolGetMinWorkers(srv->workers)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set min_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "max_workers", + virThreadPoolGetMaxWorkers(srv->workers)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set max_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "priority_workers", + virThreadPoolGetPriorityWorkers(srv->workers)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set priority_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "max_clients", srv->nclients_max) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set max_clients data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "keepaliveInterval", srv->keepaliveInterval) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set keepaliveInterval data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "keepaliveCount", srv->keepaliveCount) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set keepaliveCount data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendBoolean(object, "keepaliveRequired", srv->keepaliveRequired) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set keepaliveRequired data in JSON document")); + goto error; + } + + if (srv->mdnsGroupName && + virJSONValueObjectAppendString(object, "mdnsGroupName", srv->mdnsGroupName) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set mdnsGroupName data in JSON document")); + goto error; + } + + services = virJSONValueNewArray(); + if (virJSONValueObjectAppend(object, "services", services) < 0) { + virJSONValueFree(services); + goto error; + } + + for (i = 0 ; i < srv->nservices ; i++) { + virJSONValuePtr child; + if (!(child = virNetServerServicePreExecRestart(srv->services[i]))) + goto error; + + if (virJSONValueArrayAppend(services, child) < 0) { + virJSONValueFree(child); + goto error; + } + } + + clients = virJSONValueNewArray(); + if (virJSONValueObjectAppend(object, "clients", clients) < 0) { + virJSONValueFree(clients); + goto error; + } + + for (i = 0 ; i < srv->nclients ; i++) { + virJSONValuePtr child; + if (!(child = virNetServerClientPreExecRestart(srv->clients[i]))) + goto error; + + if (virJSONValueArrayAppend(clients, child) < 0) { + virJSONValueFree(child); + goto error; + } + } + + virMutexUnlock(&srv->lock); + + return object; + +error: + virJSONValueFree(object); + virMutexUnlock(&srv->lock); + return NULL; +} + + bool virNetServerIsPrivileged(virNetServerPtr srv) { bool priv; diff --git a/src/rpc/virnetserver.h b/src/rpc/virnetserver.h index 778b069..a5cbcd3 100644 --- a/src/rpc/virnetserver.h +++ b/src/rpc/virnetserver.h @@ -31,6 +31,7 @@ # include "virnetserverclient.h" # include "virnetserverservice.h" # include "virobject.h" +# include "json.h" virNetServerPtr virNetServerNew(size_t min_workers, size_t max_workers, @@ -45,6 +46,15 @@ virNetServerPtr virNetServerNew(size_t min_workers, virFreeCallback clientPrivFree, void *clientPrivOpaque); +virNetServerPtr virNetServerNewPostExecRestart(virJSONValuePtr object, + virNetServerClientPrivNew clientPrivNew, + virNetServerClientPrivNewPostExecRestart clientPrivNewPostExecRestart, + virNetServerClientPrivPreExecRestart clientPrivPreExecRestart, + virFreeCallback clientPrivFree, + void *clientPrivOpaque); + +virJSONValuePtr virNetServerPreExecRestart(virNetServerPtr srv); + typedef int (*virNetServerAutoShutdownFunc)(virNetServerPtr srv, void *opaque); bool virNetServerIsPrivileged(virNetServerPtr srv); -- 1.7.11.2

On 12.09.2012 18:28, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Add two new APIs virNetServerNewPostExecRestart and virNetServerPreExecRestart which allow a virNetServerPtr object to be created from a JSON object and saved to a JSON object, for the purpose of re-exec'ing a process.
This includes serialization of all registered services and clients
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/libvirt_private.syms | 2 + src/rpc/virnetserver.c | 252 +++++++++++++++++++++++++++++++++++++++++++++++ src/rpc/virnetserver.h | 10 ++ 3 files changed, 264 insertions(+)
ACK
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 004a8da..338c75e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1534,6 +1534,8 @@ virNetServerClose; virNetServerIsPrivileged; virNetServerKeepAliveRequired; virNetServerNew; +virNetServerNewPostExecRestart; +virNetServerPreExecRestart; virNetServerQuit; virNetServerRun; virNetServerSetTLSContext; diff --git a/src/rpc/virnetserver.c b/src/rpc/virnetserver.c index 902c34e..92e0264 100644 --- a/src/rpc/virnetserver.c +++ b/src/rpc/virnetserver.c @@ -447,6 +447,258 @@ error: }
+virNetServerPtr virNetServerNewPostExecRestart(virJSONValuePtr object, + virNetServerClientPrivNew clientPrivNew, + virNetServerClientPrivNewPostExecRestart clientPrivNewPostExecRestart, + virNetServerClientPrivPreExecRestart clientPrivPreExecRestart, + virFreeCallback clientPrivFree, + void *clientPrivOpaque) +{ + virNetServerPtr srv = NULL; + virJSONValuePtr clients; + virJSONValuePtr services; + size_t i; + int n; + unsigned int min_workers; + unsigned int max_workers; + unsigned int priority_workers; + unsigned int max_clients; + unsigned int keepaliveInterval; + unsigned int keepaliveCount; + bool keepaliveRequired; + const char *mdnsGroupName = NULL; + + if (virJSONValueObjectGetNumberUint(object, "min_workers", &min_workers) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing min_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "max_workers", &max_workers) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing max_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "priority_workers", &priority_workers) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing priority_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "max_clients", &max_clients) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing max_clients data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "keepaliveInterval", &keepaliveInterval) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing keepaliveInterval data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "keepaliveCount", &keepaliveCount) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing keepaliveCount data in JSON document")); + goto error; + } + if (virJSONValueObjectGetBoolean(object, "keepaliveRequired", &keepaliveRequired) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing keepaliveRequired data in JSON document")); + goto error; + } + + if (virJSONValueObjectHasKey(object, "mdnsGroupName") && + (!(mdnsGroupName = virJSONValueObjectGetString(object, "mdnsGroupName")))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed mdnsGroupName data in JSON document")); + goto error; + } + + if (!(srv = virNetServerNew(min_workers, max_clients, + priority_workers, max_clients, + keepaliveInterval, keepaliveCount, + keepaliveRequired, mdnsGroupName, + clientPrivNew, clientPrivPreExecRestart, + clientPrivFree, clientPrivOpaque))) + goto error; + + if (!(services = virJSONValueObjectGet(object, "services"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing services data in JSON document")); + goto error; + } + + n = virJSONValueArraySize(services); + if (n < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed services data in JSON document")); + goto error; + } + + for (i = 0 ; i < n ; i++) { + virNetServerServicePtr service; + virJSONValuePtr child = virJSONValueArrayGet(services, i); + if (!child) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing service data in JSON document")); + goto error; + } + + if (!(service = virNetServerServiceNewPostExecRestart(child))) + goto error; + + /* XXX mdns entry names ? */ + if (virNetServerAddService(srv, service, NULL) < 0) { + virObjectUnref(service); + goto error; + } + } + + + if (!(clients = virJSONValueObjectGet(object, "clients"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing clients data in JSON document")); + goto error; + } + + n = virJSONValueArraySize(clients); + if (n < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed clients data in JSON document")); + goto error; + } + + for (i = 0 ; i < n ; i++) { + virNetServerClientPtr client; + virJSONValuePtr child = virJSONValueArrayGet(clients, i); + if (!child) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing client data in JSON document")); + goto error; + } + + if (!(client = virNetServerClientNewPostExecRestart(child, + clientPrivNewPostExecRestart, + clientPrivPreExecRestart, + clientPrivFree, + clientPrivOpaque))) + goto error; + + if (virNetServerAddClient(srv, client) < 0) { + virObjectUnref(client); + goto error; + } + virObjectUnref(client); + } + + return srv; + +error: + virObjectUnref(srv); + return NULL; +} + + +virJSONValuePtr virNetServerPreExecRestart(virNetServerPtr srv) +{ + virJSONValuePtr object; + virJSONValuePtr clients; + virJSONValuePtr services; + size_t i; + + virMutexLock(&srv->lock); + + if (!(object = virJSONValueNewObject())) + goto error; + + if (virJSONValueObjectAppendNumberUint(object, "min_workers", + virThreadPoolGetMinWorkers(srv->workers)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set min_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "max_workers", + virThreadPoolGetMaxWorkers(srv->workers)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set max_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "priority_workers", + virThreadPoolGetPriorityWorkers(srv->workers)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set priority_workers data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "max_clients", srv->nclients_max) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set max_clients data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "keepaliveInterval", srv->keepaliveInterval) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set keepaliveInterval data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "keepaliveCount", srv->keepaliveCount) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set keepaliveCount data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendBoolean(object, "keepaliveRequired", srv->keepaliveRequired) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set keepaliveRequired data in JSON document")); + goto error; + } + + if (srv->mdnsGroupName && + virJSONValueObjectAppendString(object, "mdnsGroupName", srv->mdnsGroupName) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set mdnsGroupName data in JSON document")); + goto error; + } + + services = virJSONValueNewArray(); + if (virJSONValueObjectAppend(object, "services", services) < 0) { + virJSONValueFree(services); + goto error; + } + + for (i = 0 ; i < srv->nservices ; i++) { + virJSONValuePtr child; + if (!(child = virNetServerServicePreExecRestart(srv->services[i]))) + goto error; + + if (virJSONValueArrayAppend(services, child) < 0) { + virJSONValueFree(child); + goto error; + } + } + + clients = virJSONValueNewArray(); + if (virJSONValueObjectAppend(object, "clients", clients) < 0) { + virJSONValueFree(clients); + goto error; + } + + for (i = 0 ; i < srv->nclients ; i++) { + virJSONValuePtr child; + if (!(child = virNetServerClientPreExecRestart(srv->clients[i]))) + goto error; + + if (virJSONValueArrayAppend(clients, child) < 0) { + virJSONValueFree(child); + goto error; + } + } + + virMutexUnlock(&srv->lock); + + return object; + +error: + virJSONValueFree(object); + virMutexUnlock(&srv->lock); + return NULL; +} + + bool virNetServerIsPrivileged(virNetServerPtr srv) { bool priv; diff --git a/src/rpc/virnetserver.h b/src/rpc/virnetserver.h index 778b069..a5cbcd3 100644 --- a/src/rpc/virnetserver.h +++ b/src/rpc/virnetserver.h @@ -31,6 +31,7 @@ # include "virnetserverclient.h" # include "virnetserverservice.h" # include "virobject.h" +# include "json.h"
virNetServerPtr virNetServerNew(size_t min_workers, size_t max_workers, @@ -45,6 +46,15 @@ virNetServerPtr virNetServerNew(size_t min_workers, virFreeCallback clientPrivFree, void *clientPrivOpaque);
+virNetServerPtr virNetServerNewPostExecRestart(virJSONValuePtr object, + virNetServerClientPrivNew clientPrivNew, + virNetServerClientPrivNewPostExecRestart clientPrivNewPostExecRestart, + virNetServerClientPrivPreExecRestart clientPrivPreExecRestart, + virFreeCallback clientPrivFree, + void *clientPrivOpaque); + +virJSONValuePtr virNetServerPreExecRestart(virNetServerPtr srv); + typedef int (*virNetServerAutoShutdownFunc)(virNetServerPtr srv, void *opaque);
bool virNetServerIsPrivileged(virNetServerPtr srv);

From: "Daniel P. Berrange" <berrange@redhat.com> The virtlockd daemon will maintain locks on behalf of libvirtd. There are two reasons for it to be separate - Avoid risk of other libvirtd threads accidentally releasing fcntl() locks by opening + closing a file that is locked - Ensure locks can be preserved across libvirtd restarts. virtlockd will need to be able to re-exec itself while maintaining locks. This is simpler to achieve if its sole job is maintaining locks Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 2 + cfg.mk | 6 +- libvirt.spec.in | 7 + po/POTFILES.in | 1 + src/Makefile.am | 86 ++++- src/locking/lock_daemon.c | 750 ++++++++++++++++++++++++++++++++++++++++++ src/locking/lock_daemon.h | 43 +++ src/locking/virtlockd.init.in | 93 ++++++ src/locking/virtlockd.sysconf | 3 + 9 files changed, 987 insertions(+), 4 deletions(-) create mode 100644 src/locking/lock_daemon.c create mode 100644 src/locking/lock_daemon.h create mode 100644 src/locking/virtlockd.init.in create mode 100644 src/locking/virtlockd.sysconf diff --git a/.gitignore b/.gitignore index 7919f74..619d481 100644 --- a/.gitignore +++ b/.gitignore @@ -118,6 +118,8 @@ /src/test_libvirt*.aug /src/util/virkeymaps.h /src/virt-aa-helper +/src/virtlockd +/src/virtlockd.init /tests/*.log /tests/*.pid /tests/*xml2*test diff --git a/cfg.mk b/cfg.mk index bca363c..087bd74 100644 --- a/cfg.mk +++ b/cfg.mk @@ -641,6 +641,8 @@ sc_prohibit_cross_inclusion: @for dir in $(cross_dirs); do \ case $$dir in \ util/) safe="util";; \ + locking/) \ + safe="($$dir|util|conf|rpc)";; \ cpu/ | locking/ | network/ | rpc/ | security/) \ safe="($$dir|util|conf)";; \ xenapi/ | xenxs/ ) safe="($$dir|util|conf|xen)";; \ @@ -734,7 +736,7 @@ $(srcdir)/src/remote/remote_client_bodies.h: $(srcdir)/src/remote/remote_protoco # List all syntax-check exemptions: exclude_file_name_regexp--sc_avoid_strcase = ^tools/virsh\.h$$ -_src1=libvirt|fdstream|qemu/qemu_monitor|util/(command|util)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller +_src1=libvirt|fdstream|qemu/qemu_monitor|util/(command|util)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller|locking/lock_daemon exclude_file_name_regexp--sc_avoid_write = \ ^(src/($(_src1))|daemon/libvirtd|tools/console|tests/(shunload|virnettlscontext)test)\.c$$ @@ -767,7 +769,7 @@ exclude_file_name_regexp--sc_prohibit_close = \ exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = \ (^tests/(qemuhelp|nodeinfo)data/|\.(gif|ico|png|diff)$$) -_src2=src/(util/command|libvirt|lxc/lxc_controller) +_src2=src/(util/command|libvirt|lxc/lxc_controller|locking/lock_daemon) exclude_file_name_regexp--sc_prohibit_fork_wrappers = \ (^($(_src2)|tests/testutils|daemon/libvirtd)\.c$$) diff --git a/libvirt.spec.in b/libvirt.spec.in index 8c4c08d..69d8c58 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1562,11 +1562,13 @@ fi %dir %attr(0700, root, root) %{_sysconfdir}/libvirt/nwfilter/ %{_sysconfdir}/rc.d/init.d/libvirtd +%{_sysconfdir}/rc.d/init.d/virtlockd %if %{with_systemd} %{_unitdir}/libvirtd.service %endif %doc daemon/libvirtd.upstart %config(noreplace) %{_sysconfdir}/sysconfig/libvirtd +%config(noreplace) %{_sysconfdir}/sysconfig/virtlockd %config(noreplace) %{_sysconfdir}/libvirt/libvirtd.conf %if 0%{?fedora} >= 14 || 0%{?rhel} >= 6 %config(noreplace) %{_sysconfdir}/sysctl.d/libvirtd @@ -1630,6 +1632,10 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd %dir %attr(0755, root, root) %{_localstatedir}/lib/libvirt/dnsmasq/ %endif +%if %{with_libvirtd} +%dir %attr(0755, root, root) %{_libdir}/libvirt/lock-driver +%endif + %if %{with_qemu} %{_datadir}/augeas/lenses/libvirtd_qemu.aug %{_datadir}/augeas/lenses/tests/test_libvirtd_qemu.aug @@ -1663,6 +1669,7 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd %attr(0755, root, root) %{_libexecdir}/libvirt_iohelper %attr(0755, root, root) %{_sbindir}/libvirtd +%attr(0755, root, root) %{_sbindir}/virtlockd %{_mandir}/man8/libvirtd.8* diff --git a/po/POTFILES.in b/po/POTFILES.in index e8101a4..78f71f5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -44,6 +44,7 @@ src/interface/netcf_driver.c src/internal.h src/libvirt.c src/libvirt-qemu.c +src/locking/lock_daemon.c src/locking/lock_driver_sanlock.c src/locking/lock_manager.c src/lxc/lxc_cgroup.c diff --git a/src/Makefile.am b/src/Makefile.am index 6860e7f..a60a772 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -142,6 +142,10 @@ DRIVER_SOURCES = \ LOCK_DRIVER_SANLOCK_SOURCES = \ locking/lock_driver_sanlock.c +LOCK_DAEMON_SOURCES = \ + locking/lock_daemon.h \ + locking/lock_daemon.c \ + $(NULL) NETDEV_CONF_SOURCES = \ conf/netdev_bandwidth_conf.h conf/netdev_bandwidth_conf.c \ @@ -1475,6 +1479,76 @@ libvirt_qemu_la_CFLAGS = $(AM_CFLAGS) libvirt_qemu_la_LIBADD = libvirt.la $(CYGWIN_EXTRA_LIBADD) EXTRA_DIST += $(LIBVIRT_QEMU_SYMBOL_FILE) +if WITH_LIBVIRTD +sbin_PROGRAMS = virtlockd + +virtlockd_SOURCES = $(LOCK_DAEMON_SOURCES) +virtlockd_CFLAGS = \ + $(AM_CFLAGS) \ + $(NULL) +virtlockd_LDFLAGS = \ + $(AM_LDFLAGS) \ + $(CYGWIN_EXTRA_LDFLAGS) \ + $(MINGW_EXTRA_LDFLAGS) \ + $(NULL) +virtlockd_LDADD = \ + libvirt-net-rpc-server.la \ + libvirt-net-rpc.la \ + libvirt_util.la \ + ../gnulib/lib/libgnu.la \ + $(CYGWIN_EXTRA_LIBADD) \ + $(NULL) +if WITH_DTRACE_PROBES +virtlockd_LDADD += libvirt_probes.lo +endif + +else +EXTRA_DIST += $(LOCK_DAEMON_SOURCES) +endif + +EXTRA_DIST += locking/virtlockd.sysconf + +install-sysconfig: + mkdir -p $(DESTDIR)$(sysconfdir)/sysconfig + $(INSTALL_DATA) $(srcdir)/locking/virtlockd.sysconf \ + $(DESTDIR)$(sysconfdir)/sysconfig/virtlockd + +uninstall-sysconfig: + rm -f $(DESTDIR)$(sysconfdir)/sysconfig/virtlockd + +EXTRA_DIST += locking/virtlockd.init.in + +if WITH_LIBVIRTD +if LIBVIRT_INIT_SCRIPT_RED_HAT +install-init:: virtlockd.init install-sysconfig + mkdir -p $(DESTDIR)$(sysconfdir)/rc.d/init.d + $(INSTALL_SCRIPT) virtlockd.init \ + $(DESTDIR)$(sysconfdir)/rc.d/init.d/virtlockd + +uninstall-init:: uninstall-sysconfig + rm -f $(DESTDIR)$(sysconfdir)/rc.d/init.d/libvirtd + +BUILT_SOURCES += virtlockd.init +else +install-init:: +uninstall-init:: +endif +else +install-init:: +uninstall-init:: +endif + +virtlockd.init: locking/virtlockd.init.in $(top_builddir)/config.status + $(AM_V_GEN)sed \ + -e "s!::localstatedir::!$(localstatedir)!g" \ + -e "s!::sbindir::!$(sbindir)!g" \ + -e "s!::sysconfdir::!$(sysconfdir)!g" \ + < $< > $@-t && \ + chmod a+x $@-t && \ + mv $@-t $@ + + + if HAVE_SANLOCK lockdriverdir = $(libdir)/libvirt/lock-driver lockdriver_LTLIBRARIES = sanlock.la @@ -1693,7 +1767,11 @@ endif endif EXTRA_DIST += $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES) -install-data-local: +install-data-local: install-init +if WITH_LIBVIRTD + $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd" + $(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/lockd" +endif $(MKDIR_P) "$(DESTDIR)$(localstatedir)/cache/libvirt" $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/images" $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/filesystems" @@ -1739,7 +1817,11 @@ if WITH_NETWORK $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart/default.xml endif -uninstall-local:: +uninstall-local:: uninstall-init +if WITH_LIBVIRTD + rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd" ||: + rmdir "$(DESTDIR)$(localstatedir)/run/libvirt/lockd" ||: +endif rmdir "$(DESTDIR)$(localstatedir)/cache/libvirt" ||: rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/images" ||: rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/filesystems" ||: diff --git a/src/locking/lock_daemon.c b/src/locking/lock_daemon.c new file mode 100644 index 0000000..287ad8c --- /dev/null +++ b/src/locking/lock_daemon.c @@ -0,0 +1,750 @@ +/* + * lock_daemon.c: lock management daemon + * + * Copyright (C) 2006-2012 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <unistd.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <getopt.h> +#include <stdlib.h> +#include <locale.h> + + +#include "lock_daemon.h" +#include "util.h" +#include "virfile.h" +#include "virpidfile.h" +#include "virterror_internal.h" +#include "logging.h" +#include "memory.h" +#include "conf.h" +#include "rpc/virnetserver.h" +#include "virrandom.h" +#include "virhash.h" + +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_LOCKING + +#define virLockError(code, ...) \ + virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +struct _virLockDaemon { + virMutex lock; + virNetServerPtr srv; +}; + +virLockDaemonPtr lockDaemon = NULL; + +static bool privileged; + +enum { + VIR_LOCK_DAEMON_ERR_NONE = 0, + VIR_LOCK_DAEMON_ERR_PIDFILE, + VIR_LOCK_DAEMON_ERR_RUNDIR, + VIR_LOCK_DAEMON_ERR_INIT, + VIR_LOCK_DAEMON_ERR_SIGNAL, + VIR_LOCK_DAEMON_ERR_PRIVS, + VIR_LOCK_DAEMON_ERR_NETWORK, + VIR_LOCK_DAEMON_ERR_CONFIG, + VIR_LOCK_DAEMON_ERR_HOOKS, + + VIR_LOCK_DAEMON_ERR_LAST +}; + +VIR_ENUM_DECL(virDaemonErr) +VIR_ENUM_IMPL(virDaemonErr, VIR_LOCK_DAEMON_ERR_LAST, + "Initialization successful", + "Unable to obtain pidfile", + "Unable to create rundir", + "Unable to initialize libvirt", + "Unable to setup signal handlers", + "Unable to drop privileges", + "Unable to initialize network sockets", + "Unable to load configuration file", + "Unable to look for hook scripts"); + +static void * +virLockDaemonClientNew(virNetServerClientPtr client, + void *opaque); +static void +virLockDaemonClientFree(void *opaque); + +static void +virLockDaemonFree(virLockDaemonPtr lockd) +{ + if (!lockd) + return; + + virObjectUnref(lockd->srv); + + VIR_FREE(lockd); +} + + +static virLockDaemonPtr +virLockDaemonNew(void) +{ + virLockDaemonPtr lockd; + + if (VIR_ALLOC(lockd) < 0) { + virReportOOMError(); + return NULL; + } + + if (virMutexInit(&lockd->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to initialize mutex")); + VIR_FREE(lockd); + return NULL; + } + + if (!(lockd->srv = virNetServerNew(1, 1, 0, 20, + -1, 0, + NULL, NULL, + virLockDaemonClientNew, + NULL, + virLockDaemonClientFree, + NULL))) + goto error; + + return lockd; + +error: + virLockDaemonFree(lockd); + return NULL; +} + + +static int +virLockDaemonForkIntoBackground(const char *argv0) +{ + int statuspipe[2]; + if (pipe(statuspipe) < 0) + return -1; + + pid_t pid = fork(); + switch (pid) { + case 0: + { + int stdinfd = -1; + int stdoutfd = -1; + int nextpid; + + VIR_FORCE_CLOSE(statuspipe[0]); + + if ((stdinfd = open("/dev/null", O_RDONLY)) < 0) + goto cleanup; + if ((stdoutfd = open("/dev/null", O_WRONLY)) < 0) + goto cleanup; + if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO) + goto cleanup; + if (VIR_CLOSE(stdinfd) < 0) + goto cleanup; + if (VIR_CLOSE(stdoutfd) < 0) + goto cleanup; + + if (setsid() < 0) + goto cleanup; + + nextpid = fork(); + switch (nextpid) { + case 0: + return statuspipe[1]; + case -1: + return -1; + default: + _exit(0); + } + + cleanup: + VIR_FORCE_CLOSE(stdoutfd); + VIR_FORCE_CLOSE(stdinfd); + return -1; + + } + + case -1: + return -1; + + default: + { + int got, exitstatus = 0; + int ret; + char status; + + VIR_FORCE_CLOSE(statuspipe[1]); + + /* We wait to make sure the first child forked successfully */ + if ((got = waitpid(pid, &exitstatus, 0)) < 0 || + got != pid || + exitstatus != 0) { + return -1; + } + + /* Now block until the second child initializes successfully */ + again: + ret = read(statuspipe[0], &status, 1); + if (ret == -1 && errno == EINTR) + goto again; + + if (ret == 1 && status != 0) { + fprintf(stderr, + _("%s: error: %s. Check /var/log/messages or run without " + "--daemon for more info.\n"), argv0, + virDaemonErrTypeToString(status)); + } + _exit(ret == 1 && status == 0 ? 0 : 1); + } + } +} + + +static int +virLockDaemonMakePaths(char **basedir, + char **statedir, + char **pidfile, + char **sockfile) +{ + char *userdir = NULL; + + *basedir = *statedir = *pidfile = *sockfile = NULL; + + if (privileged) { + if (!(*basedir = strdup(LOCALSTATEDIR "/run/libvirt"))) + goto no_memory; + } else { + if (!(userdir = virGetUserDirectory())) + goto error; + + if (virAsprintf(basedir, "%s/.libvirt", userdir) < 0) + goto no_memory; + } + + if (virAsprintf(statedir, "%s/virtlockd", *basedir) < 0) + goto no_memory; + if (virAsprintf(pidfile, "%s/virtlockd.pid", *statedir) < 0) + goto no_memory; + if (virAsprintf(sockfile, "%s/virtlockd.sock", *statedir) < 0) + goto no_memory; + + VIR_FREE(userdir); + return 0; + +no_memory: + VIR_FREE(*basedir); + VIR_FREE(*statedir); + VIR_FREE(*pidfile); + VIR_FREE(*sockfile); +error: + VIR_FREE(userdir); + return -1; +} + +static void +virLockDaemonErrorHandler(void *opaque ATTRIBUTE_UNUSED, + virErrorPtr err ATTRIBUTE_UNUSED) +{ + /* Don't do anything, since logging infrastructure already + * took care of reporting the error */ +} + + +/* + * Set up the logging environment + * By default if daemonized all errors go to syslog and the logging + * is also saved onto the logfile libvird.log, but if verbose or error + * debugging is asked for then output informations or debug. + */ +static int +virLockDaemonSetLogging(virConfPtr conf ATTRIBUTE_UNUSED, + const char *filename ATTRIBUTE_UNUSED, + int godaemon, int verbose) +{ + //int log_level = 0; + char *log_filters = NULL; + char *log_outputs = NULL; + int ret = -1; + + virLogReset(); +#if 0 + /* + * Libvirtd's order of precedence is: + * cmdline > environment > config + * + * In order to achieve this, we must process configuration in + * different order for the log level versus the filters and + * outputs. Because filters and outputs append, we have to look at + * the environment first and then only check the config file if + * there was no result from the environment. The default output is + * then applied only if there was no setting from either of the + * first two. Because we don't have a way to determine if the log + * level has been set, we must process variables in the opposite + * order, each one overriding the previous. + */ + GET_CONF_INT (conf, filename, log_level); + if (log_level != 0) + virLogSetDefaultPriority(log_level); + + if (virLogGetNbFilters() == 0) { + GET_CONF_STR (conf, filename, log_filters); + virLogParseFilters(log_filters); + } + + if (virLogGetNbOutputs() == 0) { + GET_CONF_STR (conf, filename, log_outputs); + virLogParseOutputs(log_outputs); + } +#endif + + virLogSetFromEnv(); + + /* + * If no defined outputs, then direct to syslog when running + * as daemon. Otherwise the default output is stderr. + */ + if (virLogGetNbOutputs() == 0) { + char *tmp = NULL; + if (godaemon) { + if (virAsprintf (&tmp, "%d:syslog:libvirtd", + virLogGetDefaultPriority()) < 0) + goto free_and_fail; + } else { + if (virAsprintf (&tmp, "%d:stderr", + virLogGetDefaultPriority()) < 0) + goto free_and_fail; + } + virLogParseOutputs(tmp); + VIR_FREE(tmp); + } + + /* + * Command line override for --verbose + */ + if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO)) + virLogSetDefaultPriority(VIR_LOG_INFO); + + ret = 0; + +free_and_fail: + VIR_FREE(log_filters); + VIR_FREE(log_outputs); + return ret; +} + +/* Read the config file if it exists. + * Only used in the remote case, hence the name. + */ +static int +virLockDaemonReadConfigFile(const char *filename, + int godaemon, int verbose) +{ + virConfPtr conf; + + if (!(conf = virConfReadFile (filename, 0))) + goto error; + + if (virLockDaemonSetLogging(conf, filename, godaemon, verbose) < 0) + goto error; + + virConfFree (conf); + return 0; + +error: + virConfFree (conf); + + return -1; +} + +/* Display version information. */ +static void + virLockDaemonVersion(const char *argv0) +{ + printf ("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION); +} + +static void +virLockDaemonShutdownHandler(virNetServerPtr srv, + siginfo_t *sig ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + virNetServerQuit(srv); +} + +static int +virLockDaemonSetupSignals(virNetServerPtr srv) +{ + if (virNetServerAddSignalHandler(srv, SIGINT, virLockDaemonShutdownHandler, NULL) < 0) + return -1; + if (virNetServerAddSignalHandler(srv, SIGQUIT, virLockDaemonShutdownHandler, NULL) < 0) + return -1; + if (virNetServerAddSignalHandler(srv, SIGTERM, virLockDaemonShutdownHandler, NULL) < 0) + return -1; + return 0; +} + +static int +virLockDaemonSetupNetworking(virNetServerPtr srv, const char *sock_path) +{ + virNetServerServicePtr svc; + + VIR_DEBUG("Setting up networking natively"); + + if (!(svc = virNetServerServiceNewUNIX(sock_path, 0700, 0, 0, false, 1, NULL))) + return -1; + + if (virNetServerAddService(srv, svc, NULL) < 0) { + virObjectUnref(svc); + return -1; + } + return 0; +} + + +static void +virLockDaemonClientFree(void *opaque) +{ + virLockDaemonClientPtr priv = opaque; + + if (!priv) + return; + + VIR_DEBUG("priv=%p client=%lld", + priv, + (unsigned long long)priv->clientPid); + + virMutexDestroy(&priv->lock); + VIR_FREE(priv); +} + + +static void * +virLockDaemonClientNew(virNetServerClientPtr client, + void *opaque ATTRIBUTE_UNUSED) +{ + virLockDaemonClientPtr priv; + uid_t clientuid; + gid_t clientgid; + + if (VIR_ALLOC(priv) < 0) { + virReportOOMError(); + return NULL; + } + + if (virMutexInit(&priv->lock) < 0) { + VIR_FREE(priv); + virReportOOMError(); + return NULL; + } + + if (virNetServerClientGetUNIXIdentity(client, + &clientuid, + &clientgid, + &priv->clientPid) < 0) + goto error; + + VIR_DEBUG("New client pid %llu uid %llu", + (unsigned long long)priv->clientPid, + (unsigned long long)clientuid); + + if (!privileged) { + if (geteuid() != clientuid) { + virLockError(VIR_ERR_OPERATION_DENIED, + _("Disallowing client %llu with uid %llu"), + (unsigned long long)priv->clientPid, + (unsigned long long)clientuid); + + goto error; + } + } else { + if (clientuid != 0) { + virLockError(VIR_ERR_OPERATION_DENIED, + _("Disallowing client %llu with uid %llu"), + (unsigned long long)priv->clientPid, + (unsigned long long)clientuid); + goto error; + } + } + + return priv; + +error: + virMutexDestroy(&priv->lock); + VIR_FREE(priv); + return NULL; +} + + +static void +virLockDaemonUsage(const char *argv0) +{ + fprintf (stderr, + _("\n\ +Usage:\n\ + %s [options]\n\ +\n\ +Options:\n\ + -v | --verbose Verbose messages.\n\ + -d | --daemon Run as a daemon & write PID file.\n\ + -t | --timeout <secs> Exit after timeout period.\n\ + -f | --config <file> Configuration file.\n\ + | --version Display version information.\n\ + -p | --pid-file <file> Change name of PID file.\n\ +\n\ +libvirt lock management daemon:\n\ +\n\ + Default paths:\n\ +\n\ + Configuration file (unless overridden by -f):\n\ + %s/libvirt/libvirtd.conf\n\ +\n\ + Sockets (as root):\n\ + %s/run/virtlockd/virtlockd.sock\n\ +\n\ + Sockets (as non-root):\n\ + $HOME/.libvirt/virtlockd/virtlockd.sock (in UNIX abstract namespace)\n\ +\n\ + Default PID file (as root):\ + %s/run/vitlockd/virtlockd.pid\n\ +\n\ + Default PID file (as non-root):\ + $HOME/.libvirt/virtlockd/virtlockd.pid\n\ +\n"), + argv0, + SYSCONFDIR, + LOCALSTATEDIR, + LOCALSTATEDIR); +} + +enum { + OPT_VERSION = 129 +}; + +#define MAX_LISTEN 5 +int main(int argc, char **argv) { + const char *remote_config_file = NULL; + int statuswrite = -1; + int ret = 1; + int verbose = 0; + int godaemon = 0; + int timeout = 0; + char *base_dir = NULL; + char *state_dir = NULL; + char *pid_file = NULL; + int pid_file_fd = -1; + char *sock_file = NULL; + + struct option opts[] = { + { "verbose", no_argument, &verbose, 1}, + { "daemon", no_argument, &godaemon, 1}, + { "config", required_argument, NULL, 'f'}, + { "timeout", required_argument, NULL, 't'}, + { "pid-file", required_argument, NULL, 'p'}, + { "version", no_argument, NULL, OPT_VERSION }, + { "help", no_argument, NULL, '?' }, + {0, 0, 0, 0} + }; + + privileged = getuid() == 0; + + if (setlocale (LC_ALL, "") == NULL || + bindtextdomain (PACKAGE, LOCALEDIR) == NULL || + textdomain(PACKAGE) == NULL || + virThreadInitialize() < 0 || + virErrorInitialize() < 0) { + fprintf(stderr, _("%s: initialization failed\n"), argv[0]); + exit(EXIT_FAILURE); + } + + if (virLockDaemonMakePaths(&base_dir, &state_dir, + &pid_file, &sock_file) < 0) + exit(EXIT_FAILURE); + + while (1) { + int optidx = 0; + int c; + char *tmp; + + c = getopt_long(argc, argv, "ldf:p:t:v", opts, &optidx); + + if (c == -1) { + break; + } + + switch (c) { + case 0: + /* Got one of the flags */ + break; + case 'v': + verbose = 1; + break; + case 'd': + godaemon = 1; + break; + + case 't': + if (virStrToLong_i(optarg, &tmp, 10, &timeout) != 0 + || timeout <= 0 + /* Ensure that we can multiply by 1000 without overflowing. */ + || timeout > INT_MAX / 1000) + timeout = -1; + break; + + case 'p': + VIR_FREE(pid_file); + if (!(pid_file = strdup(optarg))) + exit(EXIT_FAILURE); + break; + + case 'f': + remote_config_file = optarg; + break; + + case OPT_VERSION: + virLockDaemonVersion(argv[0]); + return 0; + + case '?': + virLockDaemonUsage(argv[0]); + return 2; + + default: + fprintf (stderr, _("%s: internal error: unknown flag: %c\n"), + argv[0], c); + exit (EXIT_FAILURE); + } + } + + if (remote_config_file == NULL) { + static const char *default_config_file + = SYSCONFDIR "/libvirt/virtlockd.conf"; + remote_config_file = + (access(default_config_file, R_OK) == 0 + ? default_config_file + : "/dev/null"); + } + + if (godaemon) { + char ebuf[1024]; + if ((statuswrite = virLockDaemonForkIntoBackground(argv[0])) < 0) { + VIR_ERROR(_("Failed to fork as daemon: %s"), + virStrerror(errno, ebuf, sizeof(ebuf))); + goto cleanup; + } + } + + /* Ensure the rundir exists (on tmpfs on some systems) */ + if (mkdir(base_dir, 0755)) { + if (errno != EEXIST) { + char ebuf[1024]; + VIR_ERROR(_("unable to create rundir %s: %s"), base_dir, + virStrerror(errno, ebuf, sizeof(ebuf))); + ret = VIR_LOCK_DAEMON_ERR_RUNDIR; + goto cleanup; + } + } + if (mkdir(state_dir, 0700)) { + if (errno != EEXIST) { + char ebuf[1024]; + VIR_ERROR(_("unable to create rundir %s: %s"), state_dir, + virStrerror(errno, ebuf, sizeof(ebuf))); + ret = VIR_LOCK_DAEMON_ERR_RUNDIR; + goto cleanup; + } + } + + /* If we have a pidfile set, claim it now, exiting if already taken */ + if ((pid_file_fd = virPidFileAcquirePath(pid_file, getpid())) < 0) { + ret = VIR_LOCK_DAEMON_ERR_PIDFILE; + goto cleanup; + } + + /* Read the config file (if it exists). */ + if (virLockDaemonReadConfigFile(remote_config_file, godaemon, verbose) < 0) { + ret = VIR_LOCK_DAEMON_ERR_CONFIG; + goto cleanup; + } + + + if (!(lockDaemon = virLockDaemonNew())) { + ret = VIR_LOCK_DAEMON_ERR_INIT; + goto cleanup; + } + + if (virLockDaemonSetupNetworking(lockDaemon->srv, sock_file) < 0) { + ret = VIR_LOCK_DAEMON_ERR_NETWORK; + goto cleanup; + } + + if ((virLockDaemonSetupSignals(lockDaemon->srv)) < 0) { + ret = VIR_LOCK_DAEMON_ERR_SIGNAL; + goto cleanup; + } + + /* Disable error func, now logging is setup */ + virSetErrorFunc(NULL, virLockDaemonErrorHandler); + + + /* Tell parent of daemon that basic initialization is complete + * In particular we're ready to accept net connections & have + * written the pidfile + */ + if (statuswrite != -1) { + char status = 0; + while (write(statuswrite, &status, 1) == -1 && + errno == EINTR) + ; + VIR_FORCE_CLOSE(statuswrite); + } + + /* Start accepting new clients from network */ + + virNetServerUpdateServices(lockDaemon->srv, true); + virNetServerRun(lockDaemon->srv); + ret = 0; + +cleanup: + virLockDaemonFree(lockDaemon); + if (statuswrite != -1) { + if (ret != 0) { + /* Tell parent of daemon what failed */ + char status = ret; + while (write(statuswrite, &status, 1) == -1 && + errno == EINTR) + ; + } + VIR_FORCE_CLOSE(statuswrite); + } + if (pid_file_fd != -1) + virPidFileReleasePath(pid_file, pid_file_fd); + VIR_FREE(pid_file); + VIR_FREE(sock_file); + VIR_FREE(state_dir); + return ret; +} diff --git a/src/locking/lock_daemon.h b/src/locking/lock_daemon.h new file mode 100644 index 0000000..7bc8c2e --- /dev/null +++ b/src/locking/lock_daemon.h @@ -0,0 +1,43 @@ +/* + * lock_daemon.h: lock management daemon + * + * Copyright (C) 2006-2012 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_LOCK_DAEMON_H__ +# define __VIR_LOCK_DAEMON_H__ + +# include "virlockspace.h" +# include "threads.h" + +typedef struct _virLockDaemon virLockDaemon; +typedef virLockDaemon *virLockDaemonPtr; + +typedef struct _virLockDaemonClient virLockDaemonClient; +typedef virLockDaemonClient *virLockDaemonClientPtr; + +struct _virLockDaemonClient { + virMutex lock; + + pid_t clientPid; +}; + +extern virLockDaemonPtr lockDaemon; + +#endif /* __VIR_LOCK_DAEMON_H__ */ diff --git a/src/locking/virtlockd.init.in b/src/locking/virtlockd.init.in new file mode 100644 index 0000000..e55cbf9 --- /dev/null +++ b/src/locking/virtlockd.init.in @@ -0,0 +1,93 @@ +#!/bin/sh + +# the following is the LSB init header see +# http://www.linux-foundation.org/spec//booksets/LSB-Core-generic/LSB-Core-gen... +# +### BEGIN INIT INFO +# Provides: virtlockd +# Default-Start: 3 4 5 +# Short-Description: virtual machine lock manager +# Description: This is a daemon for managing locks +# on virtual machine disk images +### END INIT INFO + +# the following is chkconfig init header +# +# virtlockd: virtual machine lock manager +# +# chkconfig: 345 97 03 +# description: This is a daemon for managing locks \ +# on virtual machine disk images +# +# processname: virtlockd +# pidfile: ::localstatedir::/run/libvirt/virtlockd.pid +# + +# Source function library. +. ::sysconfdir::/rc.d/init.d/functions + +SERVICE=virtlockd +PROCESS=virtlockd +PIDFILE=::localstatedir::/run/libvirt/lockd/$SERVICE.pid + +VIRTLOCKD_ARGS= + +test -f ::sysconfdir::/sysconfig/virtlockd && . ::sysconfdir::/sysconfig/virtlockd + +RETVAL=0 + +start() { + echo -n $"Starting $SERVICE daemon: " + daemon --pidfile $PIDFILE --check $SERVICE $PROCESS --daemon $VIRTLOCKD_ARGS + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch ::localstatedir::/lock/subsys/$SERVICE +} + +stop() { + echo -n $"Stopping $SERVICE daemon: " + + killproc -p $PIDFILE $PROCESS + RETVAL=$? + echo + if [ $RETVAL -eq 0 ]; then + rm -f ::localstatedir::/lock/subsys/$SERVICE + rm -f $PIDFILE + fi +} + +restart() { + stop + start +} + +reload() { + echo -n $"Reloading $SERVICE configuration: " + + killproc -p $PIDFILE $PROCESS -HUP + RETVAL=$? + echo + return $RETVAL +} + +# See how we were called. +case "$1" in + start|stop|restart|reload) + $1 + ;; + status) + status -p $PIDFILE $PROCESS + RETVAL=$? + ;; + force-reload) + reload + ;; + condrestart|try-restart) + [ -f ::localstatedir::/lock/subsys/$SERVICE ] && restart || : + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|reload|force-reload|try-restart}" + exit 2 + ;; +esac +exit $RETVAL diff --git a/src/locking/virtlockd.sysconf b/src/locking/virtlockd.sysconf new file mode 100644 index 0000000..d44dc46 --- /dev/null +++ b/src/locking/virtlockd.sysconf @@ -0,0 +1,3 @@ +# +# Pass extra arguments to virtlockd +#VIRTLOCKD_ARGS= -- 1.7.11.2

On 12.09.2012 18:29, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The virtlockd daemon will maintain locks on behalf of libvirtd. There are two reasons for it to be separate
- Avoid risk of other libvirtd threads accidentally releasing fcntl() locks by opening + closing a file that is locked - Ensure locks can be preserved across libvirtd restarts. virtlockd will need to be able to re-exec itself while maintaining locks. This is simpler to achieve if its sole job is maintaining locks
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 2 + cfg.mk | 6 +- libvirt.spec.in | 7 + po/POTFILES.in | 1 + src/Makefile.am | 86 ++++- src/locking/lock_daemon.c | 750 ++++++++++++++++++++++++++++++++++++++++++ src/locking/lock_daemon.h | 43 +++ src/locking/virtlockd.init.in | 93 ++++++ src/locking/virtlockd.sysconf | 3 + 9 files changed, 987 insertions(+), 4 deletions(-) create mode 100644 src/locking/lock_daemon.c create mode 100644 src/locking/lock_daemon.h create mode 100644 src/locking/virtlockd.init.in create mode 100644 src/locking/virtlockd.sysconf
ACK but see my comments below
diff --git a/.gitignore b/.gitignore index 7919f74..619d481 100644 --- a/.gitignore +++ b/.gitignore @@ -118,6 +118,8 @@ /src/test_libvirt*.aug /src/util/virkeymaps.h /src/virt-aa-helper +/src/virtlockd +/src/virtlockd.init /tests/*.log /tests/*.pid /tests/*xml2*test diff --git a/cfg.mk b/cfg.mk index bca363c..087bd74 100644 --- a/cfg.mk +++ b/cfg.mk @@ -641,6 +641,8 @@ sc_prohibit_cross_inclusion: @for dir in $(cross_dirs); do \ case $$dir in \ util/) safe="util";; \ + locking/) \ + safe="($$dir|util|conf|rpc)";; \ cpu/ | locking/ | network/ | rpc/ | security/) \ safe="($$dir|util|conf)";; \ xenapi/ | xenxs/ ) safe="($$dir|util|conf|xen)";; \ @@ -734,7 +736,7 @@ $(srcdir)/src/remote/remote_client_bodies.h: $(srcdir)/src/remote/remote_protoco # List all syntax-check exemptions: exclude_file_name_regexp--sc_avoid_strcase = ^tools/virsh\.h$$
-_src1=libvirt|fdstream|qemu/qemu_monitor|util/(command|util)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller +_src1=libvirt|fdstream|qemu/qemu_monitor|util/(command|util)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller|locking/lock_daemon exclude_file_name_regexp--sc_avoid_write = \ ^(src/($(_src1))|daemon/libvirtd|tools/console|tests/(shunload|virnettlscontext)test)\.c$$
@@ -767,7 +769,7 @@ exclude_file_name_regexp--sc_prohibit_close = \ exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = \ (^tests/(qemuhelp|nodeinfo)data/|\.(gif|ico|png|diff)$$)
-_src2=src/(util/command|libvirt|lxc/lxc_controller) +_src2=src/(util/command|libvirt|lxc/lxc_controller|locking/lock_daemon) exclude_file_name_regexp--sc_prohibit_fork_wrappers = \ (^($(_src2)|tests/testutils|daemon/libvirtd)\.c$$)
diff --git a/libvirt.spec.in b/libvirt.spec.in index 8c4c08d..69d8c58 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1562,11 +1562,13 @@ fi %dir %attr(0700, root, root) %{_sysconfdir}/libvirt/nwfilter/
%{_sysconfdir}/rc.d/init.d/libvirtd +%{_sysconfdir}/rc.d/init.d/virtlockd %if %{with_systemd} %{_unitdir}/libvirtd.service %endif %doc daemon/libvirtd.upstart %config(noreplace) %{_sysconfdir}/sysconfig/libvirtd +%config(noreplace) %{_sysconfdir}/sysconfig/virtlockd %config(noreplace) %{_sysconfdir}/libvirt/libvirtd.conf %if 0%{?fedora} >= 14 || 0%{?rhel} >= 6 %config(noreplace) %{_sysconfdir}/sysctl.d/libvirtd @@ -1630,6 +1632,10 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd %dir %attr(0755, root, root) %{_localstatedir}/lib/libvirt/dnsmasq/ %endif
+%if %{with_libvirtd} +%dir %attr(0755, root, root) %{_libdir}/libvirt/lock-driver +%endif + %if %{with_qemu} %{_datadir}/augeas/lenses/libvirtd_qemu.aug %{_datadir}/augeas/lenses/tests/test_libvirtd_qemu.aug @@ -1663,6 +1669,7 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/sysctl.d/libvirtd
%attr(0755, root, root) %{_libexecdir}/libvirt_iohelper %attr(0755, root, root) %{_sbindir}/libvirtd +%attr(0755, root, root) %{_sbindir}/virtlockd
%{_mandir}/man8/libvirtd.8*
diff --git a/po/POTFILES.in b/po/POTFILES.in index e8101a4..78f71f5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -44,6 +44,7 @@ src/interface/netcf_driver.c src/internal.h src/libvirt.c src/libvirt-qemu.c +src/locking/lock_daemon.c src/locking/lock_driver_sanlock.c src/locking/lock_manager.c src/lxc/lxc_cgroup.c diff --git a/src/Makefile.am b/src/Makefile.am index 6860e7f..a60a772 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -142,6 +142,10 @@ DRIVER_SOURCES = \ LOCK_DRIVER_SANLOCK_SOURCES = \ locking/lock_driver_sanlock.c
+LOCK_DAEMON_SOURCES = \ + locking/lock_daemon.h \ + locking/lock_daemon.c \ + $(NULL)
NETDEV_CONF_SOURCES = \ conf/netdev_bandwidth_conf.h conf/netdev_bandwidth_conf.c \ @@ -1475,6 +1479,76 @@ libvirt_qemu_la_CFLAGS = $(AM_CFLAGS) libvirt_qemu_la_LIBADD = libvirt.la $(CYGWIN_EXTRA_LIBADD) EXTRA_DIST += $(LIBVIRT_QEMU_SYMBOL_FILE)
+if WITH_LIBVIRTD +sbin_PROGRAMS = virtlockd + +virtlockd_SOURCES = $(LOCK_DAEMON_SOURCES) +virtlockd_CFLAGS = \ + $(AM_CFLAGS) \ + $(NULL) +virtlockd_LDFLAGS = \ + $(AM_LDFLAGS) \ + $(CYGWIN_EXTRA_LDFLAGS) \ + $(MINGW_EXTRA_LDFLAGS) \ + $(NULL) +virtlockd_LDADD = \ + libvirt-net-rpc-server.la \ + libvirt-net-rpc.la \ + libvirt_util.la \ + ../gnulib/lib/libgnu.la \ + $(CYGWIN_EXTRA_LIBADD) \ + $(NULL) +if WITH_DTRACE_PROBES +virtlockd_LDADD += libvirt_probes.lo +endif + +else +EXTRA_DIST += $(LOCK_DAEMON_SOURCES) +endif + +EXTRA_DIST += locking/virtlockd.sysconf + +install-sysconfig: + mkdir -p $(DESTDIR)$(sysconfdir)/sysconfig + $(INSTALL_DATA) $(srcdir)/locking/virtlockd.sysconf \ + $(DESTDIR)$(sysconfdir)/sysconfig/virtlockd + +uninstall-sysconfig: + rm -f $(DESTDIR)$(sysconfdir)/sysconfig/virtlockd + +EXTRA_DIST += locking/virtlockd.init.in + +if WITH_LIBVIRTD +if LIBVIRT_INIT_SCRIPT_RED_HAT +install-init:: virtlockd.init install-sysconfig + mkdir -p $(DESTDIR)$(sysconfdir)/rc.d/init.d + $(INSTALL_SCRIPT) virtlockd.init \ + $(DESTDIR)$(sysconfdir)/rc.d/init.d/virtlockd + +uninstall-init:: uninstall-sysconfig + rm -f $(DESTDIR)$(sysconfdir)/rc.d/init.d/libvirtd + +BUILT_SOURCES += virtlockd.init +else +install-init:: +uninstall-init:: +endif +else +install-init:: +uninstall-init:: +endif + +virtlockd.init: locking/virtlockd.init.in $(top_builddir)/config.status + $(AM_V_GEN)sed \ + -e "s!::localstatedir::!$(localstatedir)!g" \ + -e "s!::sbindir::!$(sbindir)!g" \ + -e "s!::sysconfdir::!$(sysconfdir)!g" \ + < $< > $@-t && \ + chmod a+x $@-t && \ + mv $@-t $@ + + + if HAVE_SANLOCK lockdriverdir = $(libdir)/libvirt/lock-driver lockdriver_LTLIBRARIES = sanlock.la @@ -1693,7 +1767,11 @@ endif endif EXTRA_DIST += $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES)
-install-data-local: +install-data-local: install-init +if WITH_LIBVIRTD + $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd" + $(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/lockd" +endif $(MKDIR_P) "$(DESTDIR)$(localstatedir)/cache/libvirt" $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/images" $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/filesystems" @@ -1739,7 +1817,11 @@ if WITH_NETWORK $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart/default.xml endif
-uninstall-local:: +uninstall-local:: uninstall-init +if WITH_LIBVIRTD + rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd" ||: + rmdir "$(DESTDIR)$(localstatedir)/run/libvirt/lockd" ||: +endif rmdir "$(DESTDIR)$(localstatedir)/cache/libvirt" ||: rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/images" ||: rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/filesystems" ||: diff --git a/src/locking/lock_daemon.c b/src/locking/lock_daemon.c new file mode 100644 index 0000000..287ad8c --- /dev/null +++ b/src/locking/lock_daemon.c @@ -0,0 +1,750 @@ +/* + * lock_daemon.c: lock management daemon + * + * Copyright (C) 2006-2012 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <unistd.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <getopt.h> +#include <stdlib.h> +#include <locale.h> + + +#include "lock_daemon.h" +#include "util.h" +#include "virfile.h" +#include "virpidfile.h" +#include "virterror_internal.h" +#include "logging.h" +#include "memory.h" +#include "conf.h" +#include "rpc/virnetserver.h" +#include "virrandom.h" +#include "virhash.h" + +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_LOCKING + +#define virLockError(code, ...) \ + virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__)
We need to drop this - as we did a while ago for the rest of the code.
+ +struct _virLockDaemon { + virMutex lock; + virNetServerPtr srv; +}; + +virLockDaemonPtr lockDaemon = NULL; + +static bool privileged; + +enum { + VIR_LOCK_DAEMON_ERR_NONE = 0, + VIR_LOCK_DAEMON_ERR_PIDFILE, + VIR_LOCK_DAEMON_ERR_RUNDIR, + VIR_LOCK_DAEMON_ERR_INIT, + VIR_LOCK_DAEMON_ERR_SIGNAL, + VIR_LOCK_DAEMON_ERR_PRIVS, + VIR_LOCK_DAEMON_ERR_NETWORK, + VIR_LOCK_DAEMON_ERR_CONFIG, + VIR_LOCK_DAEMON_ERR_HOOKS, + + VIR_LOCK_DAEMON_ERR_LAST +}; + +VIR_ENUM_DECL(virDaemonErr) +VIR_ENUM_IMPL(virDaemonErr, VIR_LOCK_DAEMON_ERR_LAST, + "Initialization successful", + "Unable to obtain pidfile", + "Unable to create rundir", + "Unable to initialize libvirt", + "Unable to setup signal handlers", + "Unable to drop privileges", + "Unable to initialize network sockets", + "Unable to load configuration file", + "Unable to look for hook scripts"); + +static void * +virLockDaemonClientNew(virNetServerClientPtr client, + void *opaque); +static void +virLockDaemonClientFree(void *opaque); + +static void +virLockDaemonFree(virLockDaemonPtr lockd) +{ + if (!lockd) + return; + + virObjectUnref(lockd->srv); + + VIR_FREE(lockd); +} + + +static virLockDaemonPtr +virLockDaemonNew(void) +{ + virLockDaemonPtr lockd; + + if (VIR_ALLOC(lockd) < 0) { + virReportOOMError(); + return NULL; + } + + if (virMutexInit(&lockd->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to initialize mutex")); + VIR_FREE(lockd); + return NULL; + } + + if (!(lockd->srv = virNetServerNew(1, 1, 0, 20, + -1, 0, + NULL, NULL,
The 1st NULL is supposed to be 'false' as it is bool keepaliveRequired;
+ virLockDaemonClientNew, + NULL, + virLockDaemonClientFree, + NULL))) + goto error; + + return lockd; + +error: + virLockDaemonFree(lockd); + return NULL; +} + + +static int +virLockDaemonForkIntoBackground(const char *argv0) +{ + int statuspipe[2]; + if (pipe(statuspipe) < 0) + return -1; + + pid_t pid = fork(); + switch (pid) { + case 0: + { + int stdinfd = -1; + int stdoutfd = -1; + int nextpid; + + VIR_FORCE_CLOSE(statuspipe[0]); + + if ((stdinfd = open("/dev/null", O_RDONLY)) < 0) + goto cleanup; + if ((stdoutfd = open("/dev/null", O_WRONLY)) < 0) + goto cleanup; + if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO) + goto cleanup; + if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO) + goto cleanup; + if (VIR_CLOSE(stdinfd) < 0) + goto cleanup; + if (VIR_CLOSE(stdoutfd) < 0) + goto cleanup; + + if (setsid() < 0) + goto cleanup; + + nextpid = fork(); + switch (nextpid) { + case 0: + return statuspipe[1]; + case -1: + return -1; + default: + _exit(0); + } + + cleanup: + VIR_FORCE_CLOSE(stdoutfd); + VIR_FORCE_CLOSE(stdinfd); + return -1; + + } + + case -1: + return -1; + + default: + { + int got, exitstatus = 0; + int ret; + char status; + + VIR_FORCE_CLOSE(statuspipe[1]); + + /* We wait to make sure the first child forked successfully */ + if ((got = waitpid(pid, &exitstatus, 0)) < 0 || + got != pid || + exitstatus != 0) { + return -1; + } + + /* Now block until the second child initializes successfully */ + again: + ret = read(statuspipe[0], &status, 1); + if (ret == -1 && errno == EINTR) + goto again; + + if (ret == 1 && status != 0) { + fprintf(stderr, + _("%s: error: %s. Check /var/log/messages or run without " + "--daemon for more info.\n"), argv0, + virDaemonErrTypeToString(status)); + } + _exit(ret == 1 && status == 0 ? 0 : 1); + } + } +} + + +static int +virLockDaemonMakePaths(char **basedir, + char **statedir, + char **pidfile, + char **sockfile) +{ + char *userdir = NULL; + + *basedir = *statedir = *pidfile = *sockfile = NULL; + + if (privileged) { + if (!(*basedir = strdup(LOCALSTATEDIR "/run/libvirt"))) + goto no_memory; + } else { + if (!(userdir = virGetUserDirectory()))
I wonder if we should use $HOME/.config here, that is virGetUserConfigDirectory().
+ goto error; + + if (virAsprintf(basedir, "%s/.libvirt", userdir) < 0) + goto no_memory; + } + + if (virAsprintf(statedir, "%s/virtlockd", *basedir) < 0) + goto no_memory; + if (virAsprintf(pidfile, "%s/virtlockd.pid", *statedir) < 0) + goto no_memory; + if (virAsprintf(sockfile, "%s/virtlockd.sock", *statedir) < 0) + goto no_memory; + + VIR_FREE(userdir); + return 0; + +no_memory: + VIR_FREE(*basedir); + VIR_FREE(*statedir); + VIR_FREE(*pidfile); + VIR_FREE(*sockfile); +error: + VIR_FREE(userdir); + return -1; +} + +static void +virLockDaemonErrorHandler(void *opaque ATTRIBUTE_UNUSED, + virErrorPtr err ATTRIBUTE_UNUSED) +{ + /* Don't do anything, since logging infrastructure already + * took care of reporting the error */ +} + + +/* + * Set up the logging environment + * By default if daemonized all errors go to syslog and the logging + * is also saved onto the logfile libvird.log, but if verbose or error + * debugging is asked for then output informations or debug. + */ +static int +virLockDaemonSetLogging(virConfPtr conf ATTRIBUTE_UNUSED, + const char *filename ATTRIBUTE_UNUSED, + int godaemon, int verbose) +{ + //int log_level = 0; + char *log_filters = NULL; + char *log_outputs = NULL; + int ret = -1; + + virLogReset(); +#if 0 + /* + * Libvirtd's order of precedence is: + * cmdline > environment > config + * + * In order to achieve this, we must process configuration in + * different order for the log level versus the filters and + * outputs. Because filters and outputs append, we have to look at + * the environment first and then only check the config file if + * there was no result from the environment. The default output is + * then applied only if there was no setting from either of the + * first two. Because we don't have a way to determine if the log + * level has been set, we must process variables in the opposite + * order, each one overriding the previous. + */ + GET_CONF_INT (conf, filename, log_level); + if (log_level != 0) + virLogSetDefaultPriority(log_level); + + if (virLogGetNbFilters() == 0) { + GET_CONF_STR (conf, filename, log_filters); + virLogParseFilters(log_filters); + } + + if (virLogGetNbOutputs() == 0) { + GET_CONF_STR (conf, filename, log_outputs); + virLogParseOutputs(log_outputs); + } +#endif + + virLogSetFromEnv(); + + /* + * If no defined outputs, then direct to syslog when running + * as daemon. Otherwise the default output is stderr. + */ + if (virLogGetNbOutputs() == 0) { + char *tmp = NULL; + if (godaemon) { + if (virAsprintf (&tmp, "%d:syslog:libvirtd", + virLogGetDefaultPriority()) < 0) + goto free_and_fail; + } else { + if (virAsprintf (&tmp, "%d:stderr", + virLogGetDefaultPriority()) < 0) + goto free_and_fail; + } + virLogParseOutputs(tmp); + VIR_FREE(tmp); + } + + /* + * Command line override for --verbose + */ + if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO)) + virLogSetDefaultPriority(VIR_LOG_INFO); + + ret = 0; + +free_and_fail: + VIR_FREE(log_filters); + VIR_FREE(log_outputs); + return ret; +} + +/* Read the config file if it exists. + * Only used in the remote case, hence the name. + */ +static int +virLockDaemonReadConfigFile(const char *filename, + int godaemon, int verbose) +{ + virConfPtr conf; + + if (!(conf = virConfReadFile (filename, 0))) + goto error; + + if (virLockDaemonSetLogging(conf, filename, godaemon, verbose) < 0) + goto error; + + virConfFree (conf); + return 0; + +error: + virConfFree (conf); + + return -1; +} + +/* Display version information. */ +static void + virLockDaemonVersion(const char *argv0) +{ + printf ("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION); +} + +static void +virLockDaemonShutdownHandler(virNetServerPtr srv, + siginfo_t *sig ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + virNetServerQuit(srv); +} + +static int +virLockDaemonSetupSignals(virNetServerPtr srv) +{ + if (virNetServerAddSignalHandler(srv, SIGINT, virLockDaemonShutdownHandler, NULL) < 0) + return -1; + if (virNetServerAddSignalHandler(srv, SIGQUIT, virLockDaemonShutdownHandler, NULL) < 0) + return -1; + if (virNetServerAddSignalHandler(srv, SIGTERM, virLockDaemonShutdownHandler, NULL) < 0) + return -1; + return 0; +} + +static int +virLockDaemonSetupNetworking(virNetServerPtr srv, const char *sock_path) +{ + virNetServerServicePtr svc; + + VIR_DEBUG("Setting up networking natively"); + + if (!(svc = virNetServerServiceNewUNIX(sock_path, 0700, 0, 0, false, 1, NULL))) + return -1; + + if (virNetServerAddService(srv, svc, NULL) < 0) { + virObjectUnref(svc); + return -1; + } + return 0; +} + + +static void +virLockDaemonClientFree(void *opaque) +{ + virLockDaemonClientPtr priv = opaque; + + if (!priv) + return; + + VIR_DEBUG("priv=%p client=%lld", + priv, + (unsigned long long)priv->clientPid); + + virMutexDestroy(&priv->lock); + VIR_FREE(priv); +} + + +static void * +virLockDaemonClientNew(virNetServerClientPtr client, + void *opaque ATTRIBUTE_UNUSED) +{ + virLockDaemonClientPtr priv; + uid_t clientuid; + gid_t clientgid; + + if (VIR_ALLOC(priv) < 0) { + virReportOOMError(); + return NULL; + } + + if (virMutexInit(&priv->lock) < 0) { + VIR_FREE(priv); + virReportOOMError(); + return NULL; + } + + if (virNetServerClientGetUNIXIdentity(client, + &clientuid, + &clientgid, + &priv->clientPid) < 0) + goto error; + + VIR_DEBUG("New client pid %llu uid %llu", + (unsigned long long)priv->clientPid, + (unsigned long long)clientuid); + + if (!privileged) { + if (geteuid() != clientuid) { + virLockError(VIR_ERR_OPERATION_DENIED, + _("Disallowing client %llu with uid %llu"), + (unsigned long long)priv->clientPid, + (unsigned long long)clientuid); + + goto error; + } + } else { + if (clientuid != 0) { + virLockError(VIR_ERR_OPERATION_DENIED, + _("Disallowing client %llu with uid %llu"), + (unsigned long long)priv->clientPid, + (unsigned long long)clientuid); + goto error; + } + } + + return priv; + +error: + virMutexDestroy(&priv->lock); + VIR_FREE(priv); + return NULL; +} + + +static void +virLockDaemonUsage(const char *argv0) +{ + fprintf (stderr, + _("\n\ +Usage:\n\ + %s [options]\n\ +\n\ +Options:\n\ + -v | --verbose Verbose messages.\n\ + -d | --daemon Run as a daemon & write PID file.\n\ + -t | --timeout <secs> Exit after timeout period.\n\ + -f | --config <file> Configuration file.\n\ + | --version Display version information.\n\ + -p | --pid-file <file> Change name of PID file.\n\ +\n\ +libvirt lock management daemon:\n\ +\n\ + Default paths:\n\ +\n\ + Configuration file (unless overridden by -f):\n\ + %s/libvirt/libvirtd.conf\n\
s/libvirtd/virtlockd/
+\n\ + Sockets (as root):\n\ + %s/run/virtlockd/virtlockd.sock\n\ +\n\ + Sockets (as non-root):\n\ + $HOME/.libvirt/virtlockd/virtlockd.sock (in UNIX abstract namespace)\n\ +\n\ + Default PID file (as root):\ + %s/run/vitlockd/virtlockd.pid\n\ +\n\ + Default PID file (as non-root):\ + $HOME/.libvirt/virtlockd/virtlockd.pid\n\ +\n"), + argv0, + SYSCONFDIR, + LOCALSTATEDIR, + LOCALSTATEDIR); +} + +enum { + OPT_VERSION = 129 +}; + +#define MAX_LISTEN 5 +int main(int argc, char **argv) { + const char *remote_config_file = NULL; + int statuswrite = -1; + int ret = 1; + int verbose = 0; + int godaemon = 0; + int timeout = 0; + char *base_dir = NULL; + char *state_dir = NULL; + char *pid_file = NULL; + int pid_file_fd = -1; + char *sock_file = NULL; + + struct option opts[] = { + { "verbose", no_argument, &verbose, 1}, + { "daemon", no_argument, &godaemon, 1}, + { "config", required_argument, NULL, 'f'}, + { "timeout", required_argument, NULL, 't'}, + { "pid-file", required_argument, NULL, 'p'}, + { "version", no_argument, NULL, OPT_VERSION }, + { "help", no_argument, NULL, '?' }, + {0, 0, 0, 0} + }; + + privileged = getuid() == 0; + + if (setlocale (LC_ALL, "") == NULL || + bindtextdomain (PACKAGE, LOCALEDIR) == NULL || + textdomain(PACKAGE) == NULL || + virThreadInitialize() < 0 || + virErrorInitialize() < 0) { + fprintf(stderr, _("%s: initialization failed\n"), argv[0]); + exit(EXIT_FAILURE); + } + + if (virLockDaemonMakePaths(&base_dir, &state_dir, + &pid_file, &sock_file) < 0) + exit(EXIT_FAILURE); + + while (1) { + int optidx = 0; + int c; + char *tmp; + + c = getopt_long(argc, argv, "ldf:p:t:v", opts, &optidx);
what's 'l' here?
+ + if (c == -1) { + break; + } + + switch (c) { + case 0: + /* Got one of the flags */ + break; + case 'v': + verbose = 1; + break; + case 'd': + godaemon = 1; + break; + + case 't': + if (virStrToLong_i(optarg, &tmp, 10, &timeout) != 0 + || timeout <= 0 + /* Ensure that we can multiply by 1000 without overflowing. */ + || timeout > INT_MAX / 1000) + timeout = -1; + break; + + case 'p': + VIR_FREE(pid_file); + if (!(pid_file = strdup(optarg))) + exit(EXIT_FAILURE); + break; + + case 'f': + remote_config_file = optarg; + break; + + case OPT_VERSION: + virLockDaemonVersion(argv[0]); + return 0; + + case '?': + virLockDaemonUsage(argv[0]); + return 2; + + default: + fprintf (stderr, _("%s: internal error: unknown flag: %c\n"), + argv[0], c); + exit (EXIT_FAILURE); + } + } + + if (remote_config_file == NULL) { + static const char *default_config_file + = SYSCONFDIR "/libvirt/virtlockd.conf"; + remote_config_file = + (access(default_config_file, R_OK) == 0 + ? default_config_file + : "/dev/null"); + } + + if (godaemon) { + char ebuf[1024]; + if ((statuswrite = virLockDaemonForkIntoBackground(argv[0])) < 0) { + VIR_ERROR(_("Failed to fork as daemon: %s"), + virStrerror(errno, ebuf, sizeof(ebuf))); + goto cleanup; + } + } + + /* Ensure the rundir exists (on tmpfs on some systems) */ + if (mkdir(base_dir, 0755)) { + if (errno != EEXIST) { + char ebuf[1024]; + VIR_ERROR(_("unable to create rundir %s: %s"), base_dir, + virStrerror(errno, ebuf, sizeof(ebuf))); + ret = VIR_LOCK_DAEMON_ERR_RUNDIR; + goto cleanup; + } + } + if (mkdir(state_dir, 0700)) { + if (errno != EEXIST) { + char ebuf[1024]; + VIR_ERROR(_("unable to create rundir %s: %s"), state_dir, + virStrerror(errno, ebuf, sizeof(ebuf))); + ret = VIR_LOCK_DAEMON_ERR_RUNDIR; + goto cleanup; + } + } + + /* If we have a pidfile set, claim it now, exiting if already taken */ + if ((pid_file_fd = virPidFileAcquirePath(pid_file, getpid())) < 0) { + ret = VIR_LOCK_DAEMON_ERR_PIDFILE; + goto cleanup; + } + + /* Read the config file (if it exists). */ + if (virLockDaemonReadConfigFile(remote_config_file, godaemon, verbose) < 0) { + ret = VIR_LOCK_DAEMON_ERR_CONFIG; + goto cleanup; + } + + + if (!(lockDaemon = virLockDaemonNew())) { + ret = VIR_LOCK_DAEMON_ERR_INIT; + goto cleanup; + } + + if (virLockDaemonSetupNetworking(lockDaemon->srv, sock_file) < 0) { + ret = VIR_LOCK_DAEMON_ERR_NETWORK; + goto cleanup; + } + + if ((virLockDaemonSetupSignals(lockDaemon->srv)) < 0) { + ret = VIR_LOCK_DAEMON_ERR_SIGNAL; + goto cleanup; + } + + /* Disable error func, now logging is setup */ + virSetErrorFunc(NULL, virLockDaemonErrorHandler); + + + /* Tell parent of daemon that basic initialization is complete + * In particular we're ready to accept net connections & have + * written the pidfile + */ + if (statuswrite != -1) { + char status = 0; + while (write(statuswrite, &status, 1) == -1 && + errno == EINTR) + ; + VIR_FORCE_CLOSE(statuswrite); + } + + /* Start accepting new clients from network */ + + virNetServerUpdateServices(lockDaemon->srv, true); + virNetServerRun(lockDaemon->srv); + ret = 0; + +cleanup: + virLockDaemonFree(lockDaemon); + if (statuswrite != -1) { + if (ret != 0) { + /* Tell parent of daemon what failed */ + char status = ret; + while (write(statuswrite, &status, 1) == -1 && + errno == EINTR) + ; + } + VIR_FORCE_CLOSE(statuswrite); + } + if (pid_file_fd != -1) + virPidFileReleasePath(pid_file, pid_file_fd); + VIR_FREE(pid_file); + VIR_FREE(sock_file); + VIR_FREE(state_dir); + return ret; +} diff --git a/src/locking/lock_daemon.h b/src/locking/lock_daemon.h new file mode 100644 index 0000000..7bc8c2e --- /dev/null +++ b/src/locking/lock_daemon.h @@ -0,0 +1,43 @@ +/* + * lock_daemon.h: lock management daemon + * + * Copyright (C) 2006-2012 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_LOCK_DAEMON_H__ +# define __VIR_LOCK_DAEMON_H__ + +# include "virlockspace.h" +# include "threads.h" + +typedef struct _virLockDaemon virLockDaemon; +typedef virLockDaemon *virLockDaemonPtr; + +typedef struct _virLockDaemonClient virLockDaemonClient; +typedef virLockDaemonClient *virLockDaemonClientPtr; + +struct _virLockDaemonClient { + virMutex lock; + + pid_t clientPid; +}; + +extern virLockDaemonPtr lockDaemon; + +#endif /* __VIR_LOCK_DAEMON_H__ */ diff --git a/src/locking/virtlockd.init.in b/src/locking/virtlockd.init.in new file mode 100644 index 0000000..e55cbf9 --- /dev/null +++ b/src/locking/virtlockd.init.in @@ -0,0 +1,93 @@ +#!/bin/sh + +# the following is the LSB init header see +# http://www.linux-foundation.org/spec//booksets/LSB-Core-generic/LSB-Core-gen... +# +### BEGIN INIT INFO +# Provides: virtlockd +# Default-Start: 3 4 5 +# Short-Description: virtual machine lock manager +# Description: This is a daemon for managing locks +# on virtual machine disk images +### END INIT INFO + +# the following is chkconfig init header +# +# virtlockd: virtual machine lock manager +# +# chkconfig: 345 97 03 +# description: This is a daemon for managing locks \ +# on virtual machine disk images +# +# processname: virtlockd +# pidfile: ::localstatedir::/run/libvirt/virtlockd.pid +# + +# Source function library. +. ::sysconfdir::/rc.d/init.d/functions + +SERVICE=virtlockd +PROCESS=virtlockd +PIDFILE=::localstatedir::/run/libvirt/lockd/$SERVICE.pid + +VIRTLOCKD_ARGS= + +test -f ::sysconfdir::/sysconfig/virtlockd && . ::sysconfdir::/sysconfig/virtlockd + +RETVAL=0 + +start() { + echo -n $"Starting $SERVICE daemon: " + daemon --pidfile $PIDFILE --check $SERVICE $PROCESS --daemon $VIRTLOCKD_ARGS + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch ::localstatedir::/lock/subsys/$SERVICE +} + +stop() { + echo -n $"Stopping $SERVICE daemon: " + + killproc -p $PIDFILE $PROCESS + RETVAL=$? + echo + if [ $RETVAL -eq 0 ]; then + rm -f ::localstatedir::/lock/subsys/$SERVICE + rm -f $PIDFILE + fi +} + +restart() { + stop + start +} + +reload() { + echo -n $"Reloading $SERVICE configuration: " + + killproc -p $PIDFILE $PROCESS -HUP + RETVAL=$? + echo + return $RETVAL +} + +# See how we were called. +case "$1" in + start|stop|restart|reload) + $1 + ;; + status) + status -p $PIDFILE $PROCESS + RETVAL=$? + ;; + force-reload) + reload + ;; + condrestart|try-restart) + [ -f ::localstatedir::/lock/subsys/$SERVICE ] && restart || : + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|reload|force-reload|try-restart}" + exit 2 + ;; +esac +exit $RETVAL diff --git a/src/locking/virtlockd.sysconf b/src/locking/virtlockd.sysconf new file mode 100644 index 0000000..d44dc46 --- /dev/null +++ b/src/locking/virtlockd.sysconf @@ -0,0 +1,3 @@ +# +# Pass extra arguments to virtlockd +#VIRTLOCKD_ARGS=

From: "Daniel P. Berrange" <berrange@redhat.com> The virtlockd daemon will be responsible for managing locks on virtual machines. Communication will be via the standard RPC infrastructure. This provides the XDR protocol definition * src/locking/lock_protocol.x: Wire protocol for virtlockd * src/Makefile.am: Include lock_protocol.[ch] in virtlockd Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 1 + cfg.mk | 3 ++ src/Makefile.am | 14 ++++++- src/locking/lock_protocol.x | 89 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/locking/lock_protocol.x diff --git a/.gitignore b/.gitignore index 619d481..c93433b 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,7 @@ /src/libvirt_*helper /src/libvirt_*probes.h /src/libvirt_lxc +/src/locking/lock_protocol.[ch] /src/locking/qemu-sanlock.conf /src/locking/test_libvirt_sanlock.aug /src/lxc/lxc_controller_dispatch.h diff --git a/cfg.mk b/cfg.mk index 087bd74..72f00b0 100644 --- a/cfg.mk +++ b/cfg.mk @@ -818,3 +818,6 @@ exclude_file_name_regexp--sc_unmarked_diagnostics = \ ^(docs/apibuild.py|tests/virt-aa-helper-test)$$ exclude_file_name_regexp--sc_size_of_brackets = cfg.mk + +exclude_file_name_regexp--sc_correct_id_types = \ + (^src/locking/lock_protocol.x$$) diff --git a/src/Makefile.am b/src/Makefile.am index a60a772..0b0367b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -142,6 +142,15 @@ DRIVER_SOURCES = \ LOCK_DRIVER_SANLOCK_SOURCES = \ locking/lock_driver_sanlock.c +LOCK_PROTOCOL_GENERATED = \ + locking/lock_protocol.h \ + locking/lock_protocol.c \ + $(NULL) + +EXTRA_DIST += locking/lock_protocol.x +BUILT_SOURCES += $(LOCK_PROTOCOL_GENERATED) +MAINTAINERCLEANFILES += $(LOCK_PROTOCOL_GENERATED) + LOCK_DAEMON_SOURCES = \ locking/lock_daemon.h \ locking/lock_daemon.c \ @@ -1482,7 +1491,10 @@ EXTRA_DIST += $(LIBVIRT_QEMU_SYMBOL_FILE) if WITH_LIBVIRTD sbin_PROGRAMS = virtlockd -virtlockd_SOURCES = $(LOCK_DAEMON_SOURCES) +virtlockd_SOURCES = \ + $(LOCK_DAEMON_SOURCES) \ + $(LOCK_PROTOCOL_GENERATED) \ + $(NULL) virtlockd_CFLAGS = \ $(AM_CFLAGS) \ $(NULL) diff --git a/src/locking/lock_protocol.x b/src/locking/lock_protocol.x new file mode 100644 index 0000000..d3f6fb1 --- /dev/null +++ b/src/locking/lock_protocol.x @@ -0,0 +1,89 @@ +/* -*- c -*- + */ + +%#include "internal.h" + +typedef opaque virLockSpaceProtocolUUID[VIR_UUID_BUFLEN]; + +/* Length of long, but not unbounded, strings. + * This is an arbitrary limit designed to stop the decoder from trying + * to allocate unbounded amounts of memory when fed with a bad message. + */ +const VIR_LOCK_SPACE_PROTOCOL_STRING_MAX = 65536; + +/* A long string, which may NOT be NULL. */ +typedef string virLockSpaceProtocolNonNullString<VIR_LOCK_SPACE_PROTOCOL_STRING_MAX>; + +/* A long string, which may be NULL. */ +typedef virLockSpaceProtocolNonNullString *virLockSpaceProtocolString; + +struct virLockSpaceProtocolOwner { + virLockSpaceProtocolUUID uuid; + virLockSpaceProtocolNonNullString name; + unsigned int id; + unsigned int pid; +}; + +struct virLockSpaceProtocolRegisterArgs { + virLockSpaceProtocolOwner owner; + unsigned int flags; +}; + +struct virLockSpaceProtocolRestrictArgs { + unsigned int flags; +}; + +struct virLockSpaceProtocolNewArgs { + virLockSpaceProtocolNonNullString path; + unsigned int flags; +}; + +struct virLockSpaceProtocolCreateResourceArgs { + virLockSpaceProtocolNonNullString path; + virLockSpaceProtocolNonNullString name; + unsigned int flags; +}; + +struct virLockSpaceProtocolDeleteResourceArgs { + virLockSpaceProtocolNonNullString path; + virLockSpaceProtocolNonNullString name; + unsigned int flags; +}; + +enum virLockSpaceProtocolAcquireResourceFlags { + VIR_LOCK_SPACE_PROTOCOL_ACQUIRE_RESOURCE_SHARED = 1, + VIR_LOCK_SPACE_PROTOCOL_ACQUIRE_RESOURCE_AUTOCREATE = 2 +}; + +struct virLockSpaceProtocolAcquireResourceArgs { + virLockSpaceProtocolNonNullString path; + virLockSpaceProtocolNonNullString name; + unsigned int flags; +}; + +struct virLockSpaceProtocolReleaseResourceArgs { + virLockSpaceProtocolNonNullString path; + virLockSpaceProtocolNonNullString name; + unsigned int flags; +}; + + +/* Define the program number, protocol version and procedure numbers here. */ +const VIR_LOCK_SPACE_PROTOCOL_PROGRAM = 0xEA7BEEF; +const VIR_LOCK_SPACE_PROTOCOL_PROGRAM_VERSION = 1; + +enum virLockSpaceProtocolProcedure { + /* Each function must have a two-word comment. The first word is + * whether remote_generator.pl handles daemon, the second whether + * it handles src/remote. Additional flags can be specified after a + * pipe. + */ + VIR_LOCK_SPACE_PROTOCOL_PROC_REGISTER = 1, /* skipgen skipgen */ + VIR_LOCK_SPACE_PROTOCOL_PROC_RESTRICT = 2, /* skipgen skipgen */ + VIR_LOCK_SPACE_PROTOCOL_PROC_NEW = 3, /* skipgen skipgen */ + VIR_LOCK_SPACE_PROTOCOL_PROC_CREATE_RESOURCE = 4, /* skipgen skipgen */ + VIR_LOCK_SPACE_PROTOCOL_PROC_DELETE_RESOURCE = 5, /* skipgen skipgen */ + + VIR_LOCK_SPACE_PROTOCOL_PROC_ACQUIRE_RESOURCE = 6, /* skipgen skipgen */ + VIR_LOCK_SPACE_PROTOCOL_PROC_RELEASE_RESOURCE = 7 /* skipgen skipgen */ +}; -- 1.7.11.2

On 12.09.2012 18:29, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The virtlockd daemon will be responsible for managing locks on virtual machines. Communication will be via the standard RPC infrastructure. This provides the XDR protocol definition
* src/locking/lock_protocol.x: Wire protocol for virtlockd * src/Makefile.am: Include lock_protocol.[ch] in virtlockd
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 1 + cfg.mk | 3 ++ src/Makefile.am | 14 ++++++- src/locking/lock_protocol.x | 89 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/locking/lock_protocol.x
ACK
diff --git a/.gitignore b/.gitignore index 619d481..c93433b 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,7 @@ /src/libvirt_*helper /src/libvirt_*probes.h /src/libvirt_lxc +/src/locking/lock_protocol.[ch] /src/locking/qemu-sanlock.conf /src/locking/test_libvirt_sanlock.aug /src/lxc/lxc_controller_dispatch.h diff --git a/cfg.mk b/cfg.mk index 087bd74..72f00b0 100644 --- a/cfg.mk +++ b/cfg.mk @@ -818,3 +818,6 @@ exclude_file_name_regexp--sc_unmarked_diagnostics = \ ^(docs/apibuild.py|tests/virt-aa-helper-test)$$
exclude_file_name_regexp--sc_size_of_brackets = cfg.mk + +exclude_file_name_regexp--sc_correct_id_types = \ + (^src/locking/lock_protocol.x$$) diff --git a/src/Makefile.am b/src/Makefile.am index a60a772..0b0367b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -142,6 +142,15 @@ DRIVER_SOURCES = \ LOCK_DRIVER_SANLOCK_SOURCES = \ locking/lock_driver_sanlock.c
+LOCK_PROTOCOL_GENERATED = \ + locking/lock_protocol.h \ + locking/lock_protocol.c \ + $(NULL) + +EXTRA_DIST += locking/lock_protocol.x +BUILT_SOURCES += $(LOCK_PROTOCOL_GENERATED) +MAINTAINERCLEANFILES += $(LOCK_PROTOCOL_GENERATED) + LOCK_DAEMON_SOURCES = \ locking/lock_daemon.h \ locking/lock_daemon.c \ @@ -1482,7 +1491,10 @@ EXTRA_DIST += $(LIBVIRT_QEMU_SYMBOL_FILE) if WITH_LIBVIRTD sbin_PROGRAMS = virtlockd
-virtlockd_SOURCES = $(LOCK_DAEMON_SOURCES) +virtlockd_SOURCES = \ + $(LOCK_DAEMON_SOURCES) \ + $(LOCK_PROTOCOL_GENERATED) \ + $(NULL) virtlockd_CFLAGS = \ $(AM_CFLAGS) \ $(NULL) diff --git a/src/locking/lock_protocol.x b/src/locking/lock_protocol.x new file mode 100644 index 0000000..d3f6fb1 --- /dev/null +++ b/src/locking/lock_protocol.x @@ -0,0 +1,89 @@ +/* -*- c -*- + */ + +%#include "internal.h" + +typedef opaque virLockSpaceProtocolUUID[VIR_UUID_BUFLEN]; + +/* Length of long, but not unbounded, strings. + * This is an arbitrary limit designed to stop the decoder from trying + * to allocate unbounded amounts of memory when fed with a bad message. + */ +const VIR_LOCK_SPACE_PROTOCOL_STRING_MAX = 65536; + +/* A long string, which may NOT be NULL. */ +typedef string virLockSpaceProtocolNonNullString<VIR_LOCK_SPACE_PROTOCOL_STRING_MAX>; + +/* A long string, which may be NULL. */ +typedef virLockSpaceProtocolNonNullString *virLockSpaceProtocolString; + +struct virLockSpaceProtocolOwner { + virLockSpaceProtocolUUID uuid; + virLockSpaceProtocolNonNullString name; + unsigned int id; + unsigned int pid; +}; + +struct virLockSpaceProtocolRegisterArgs { + virLockSpaceProtocolOwner owner; + unsigned int flags; +}; + +struct virLockSpaceProtocolRestrictArgs { + unsigned int flags; +}; + +struct virLockSpaceProtocolNewArgs { + virLockSpaceProtocolNonNullString path; + unsigned int flags; +}; + +struct virLockSpaceProtocolCreateResourceArgs { + virLockSpaceProtocolNonNullString path; + virLockSpaceProtocolNonNullString name; + unsigned int flags; +}; + +struct virLockSpaceProtocolDeleteResourceArgs { + virLockSpaceProtocolNonNullString path; + virLockSpaceProtocolNonNullString name; + unsigned int flags; +}; + +enum virLockSpaceProtocolAcquireResourceFlags { + VIR_LOCK_SPACE_PROTOCOL_ACQUIRE_RESOURCE_SHARED = 1, + VIR_LOCK_SPACE_PROTOCOL_ACQUIRE_RESOURCE_AUTOCREATE = 2 +}; + +struct virLockSpaceProtocolAcquireResourceArgs { + virLockSpaceProtocolNonNullString path; + virLockSpaceProtocolNonNullString name; + unsigned int flags; +}; + +struct virLockSpaceProtocolReleaseResourceArgs { + virLockSpaceProtocolNonNullString path; + virLockSpaceProtocolNonNullString name; + unsigned int flags; +}; + + +/* Define the program number, protocol version and procedure numbers here. */ +const VIR_LOCK_SPACE_PROTOCOL_PROGRAM = 0xEA7BEEF; +const VIR_LOCK_SPACE_PROTOCOL_PROGRAM_VERSION = 1; + +enum virLockSpaceProtocolProcedure { + /* Each function must have a two-word comment. The first word is + * whether remote_generator.pl handles daemon, the second whether + * it handles src/remote. Additional flags can be specified after a + * pipe. + */ + VIR_LOCK_SPACE_PROTOCOL_PROC_REGISTER = 1, /* skipgen skipgen */ + VIR_LOCK_SPACE_PROTOCOL_PROC_RESTRICT = 2, /* skipgen skipgen */ + VIR_LOCK_SPACE_PROTOCOL_PROC_NEW = 3, /* skipgen skipgen */ + VIR_LOCK_SPACE_PROTOCOL_PROC_CREATE_RESOURCE = 4, /* skipgen skipgen */ + VIR_LOCK_SPACE_PROTOCOL_PROC_DELETE_RESOURCE = 5, /* skipgen skipgen */ + + VIR_LOCK_SPACE_PROTOCOL_PROC_ACQUIRE_RESOURCE = 6, /* skipgen skipgen */ + VIR_LOCK_SPACE_PROTOCOL_PROC_RELEASE_RESOURCE = 7 /* skipgen skipgen */ +};

From: "Daniel P. Berrange" <berrange@redhat.com> Introduce a lock_daemon_dispatch.c file which implements the server side dispatcher the RPC APIs previously defined in the lock protocol. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 1 + po/POTFILES.in | 1 + src/Makefile.am | 14 ++ src/internal.h | 22 +++ src/locking/lock_daemon.c | 130 ++++++++++++- src/locking/lock_daemon.h | 13 ++ src/locking/lock_daemon_dispatch.c | 370 +++++++++++++++++++++++++++++++++++++ src/locking/lock_daemon_dispatch.h | 31 ++++ 8 files changed, 580 insertions(+), 2 deletions(-) create mode 100644 src/locking/lock_daemon_dispatch.c create mode 100644 src/locking/lock_daemon_dispatch.h diff --git a/.gitignore b/.gitignore index c93433b..060bc7a 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,7 @@ /src/libvirt_*probes.h /src/libvirt_lxc /src/locking/lock_protocol.[ch] +/src/locking/lock_daemon_dispatch_stubs.h /src/locking/qemu-sanlock.conf /src/locking/test_libvirt_sanlock.aug /src/lxc/lxc_controller_dispatch.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 78f71f5..6b9a7af 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -45,6 +45,7 @@ src/internal.h src/libvirt.c src/libvirt-qemu.c src/locking/lock_daemon.c +src/locking/lock_daemon_dispatch.c src/locking/lock_driver_sanlock.c src/locking/lock_manager.c src/lxc/lxc_cgroup.c diff --git a/src/Makefile.am b/src/Makefile.am index 0b0367b..e088d9b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -151,11 +151,24 @@ EXTRA_DIST += locking/lock_protocol.x BUILT_SOURCES += $(LOCK_PROTOCOL_GENERATED) MAINTAINERCLEANFILES += $(LOCK_PROTOCOL_GENERATED) +LOCK_DAEMON_GENERATED = \ + locking/lock_daemon_dispatch_stubs.h + $(NULL) + +BUILT_SOURCES += $(LOCK_DAEMON_GENERATED) +MAINTAINERCLEANFILES += $(LOCK_DAEMON_GENERATED) + LOCK_DAEMON_SOURCES = \ locking/lock_daemon.h \ locking/lock_daemon.c \ + locking/lock_daemon_dispatch.c \ + locking/lock_daemon_dispatch.h \ $(NULL) +$(srcdir)/locking/lock_daemon_dispatch_stubs.h: locking/lock_protocol.x $(srcdir)/rpc/gendispatch.pl Makefile.am + $(AM_V_GEN)perl -w $(srcdir)/rpc/gendispatch.pl -b virLockSpaceProtocol VIR_LOCK_SPACE_PROTOCOL $< > $@ + + NETDEV_CONF_SOURCES = \ conf/netdev_bandwidth_conf.h conf/netdev_bandwidth_conf.c \ conf/netdev_vport_profile_conf.h conf/netdev_vport_profile_conf.c \ @@ -1494,6 +1507,7 @@ sbin_PROGRAMS = virtlockd virtlockd_SOURCES = \ $(LOCK_DAEMON_SOURCES) \ $(LOCK_PROTOCOL_GENERATED) \ + $(LOCK_DAEMON_GENERATED) \ $(NULL) virtlockd_CFLAGS = \ $(AM_CFLAGS) \ diff --git a/src/internal.h b/src/internal.h index 8037a4a..b70df2a 100644 --- a/src/internal.h +++ b/src/internal.h @@ -236,6 +236,28 @@ } \ } while (0) +/** + * virCheckFlagsGoto: + * @supported: an OR'ed set of supported flags + * @label: label to jump to on error + * + * To avoid memory leaks this macro has to be used before any non-trivial + * code which could possibly allocate some memory. + * + * Returns nothing. Jumps to a label if unsupported flags were + * passed to it. + */ +# define virCheckFlagsGoto(supported, label) \ + do { \ + unsigned long __unsuppflags = flags & ~(supported); \ + if (__unsuppflags) { \ + virReportInvalidArg(flags, \ + _("unsupported flags (0x%lx) in function %s"), \ + __unsuppflags, __FUNCTION__); \ + goto label; \ + } \ + } while (0) + # define virCheckNonNullArgReturn(argname, retval) \ do { \ if (argname == NULL) { \ diff --git a/src/locking/lock_daemon.c b/src/locking/lock_daemon.c index 287ad8c..94b41db 100644 --- a/src/locking/lock_daemon.c +++ b/src/locking/lock_daemon.c @@ -43,6 +43,9 @@ #include "virrandom.h" #include "virhash.h" +#include "locking/lock_daemon_dispatch.h" +#include "locking/lock_protocol.h" + #include "configmake.h" #define VIR_FROM_THIS VIR_FROM_LOCKING @@ -54,6 +57,8 @@ struct _virLockDaemon { virMutex lock; virNetServerPtr srv; + virHashTablePtr lockspaces; + virLockSpacePtr defaultLockspace; }; virLockDaemonPtr lockDaemon = NULL; @@ -99,11 +104,19 @@ virLockDaemonFree(virLockDaemonPtr lockd) return; virObjectUnref(lockd->srv); + virHashFree(lockd->lockspaces); + virLockSpaceFree(lockd->defaultLockspace); VIR_FREE(lockd); } +static void virLockDaemonLockSpaceDataFree(void *data, + const void *key ATTRIBUTE_UNUSED) +{ + virLockSpaceFree(data); +} + static virLockDaemonPtr virLockDaemonNew(void) { @@ -130,6 +143,13 @@ virLockDaemonNew(void) NULL))) goto error; + if (!(lockd->lockspaces = virHashCreate(3, + virLockDaemonLockSpaceDataFree))) + goto error; + + if (!(lockd->defaultLockspace = virLockSpaceNew(NULL))) + goto error; + return lockd; error: @@ -138,6 +158,31 @@ error: } +int virLockDaemonAddLockSpace(virLockDaemonPtr lockd, + const char *path, + virLockSpacePtr lockspace) +{ + int ret; + virMutexLock(&lockd->lock); + ret = virHashAddEntry(lockd->lockspaces, path, lockspace); + virMutexUnlock(&lockd->lock); + return ret; +} + +virLockSpacePtr virLockDaemonFindLockSpace(virLockDaemonPtr lockd, + const char *path) +{ + virLockSpacePtr lockspace; + virMutexLock(&lockd->lock); + if (path && STRNEQ(path, "")) + lockspace = virHashLookup(lockd->lockspaces, path); + else + lockspace = lockd->defaultLockspace; + virMutexUnlock(&lockd->lock); + return lockspace; +} + + static int virLockDaemonForkIntoBackground(const char *argv0) { @@ -427,6 +472,30 @@ virLockDaemonSetupNetworking(virNetServerPtr srv, const char *sock_path) } +struct virLockDaemonClientReleaseData { + virLockDaemonClientPtr client; + bool hadSomeLeases; + bool gotError; +}; + +static void +virLockDaemonClientReleaseLockspace(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *opaque) +{ + virLockSpacePtr lockspace = payload; + struct virLockDaemonClientReleaseData *data = opaque; + int rc; + + rc = virLockSpaceReleaseResourcesForOwner(lockspace, + data->client->clientPid); + if (rc > 0) + data->hadSomeLeases = true; + else if (rc < 0) + data->gotError = true; +} + + static void virLockDaemonClientFree(void *opaque) { @@ -435,9 +504,52 @@ virLockDaemonClientFree(void *opaque) if (!priv) return; - VIR_DEBUG("priv=%p client=%lld", + VIR_DEBUG("priv=%p client=%lld owner=%lld", priv, - (unsigned long long)priv->clientPid); + (unsigned long long)priv->clientPid, + (unsigned long long)priv->ownerPid); + + /* If client & owner match, this is the lock holder */ + if (priv->clientPid == priv->ownerPid) { + size_t i; + struct virLockDaemonClientReleaseData data = { + priv, false, false + }; + + /* Release all locks associated with this + * owner in all lockspaces */ + virMutexLock(&lockDaemon->lock); + virHashForEach(lockDaemon->lockspaces, + virLockDaemonClientReleaseLockspace, + &data); + virLockDaemonClientReleaseLockspace(lockDaemon->defaultLockspace, + "", + &data); + virMutexUnlock(&lockDaemon->lock); + + /* If the client had some active leases when it + * closed the connection, we must kill it off + * to make sure it doesn't do nasty stuff */ + if (data.gotError || data.hadSomeLeases) { + for (i = 0 ; i < 15 ; i++) { + int signum; + if (i == 0) + signum = SIGTERM; + else if (i == 8) + signum = SIGKILL; + else + signum = 0; + if (virKillProcess(priv->clientPid, signum) < 0) { + if (errno == ESRCH) + break; + + VIR_WARN("Failed to kill off pid %lld", + (unsigned long long)priv->clientPid); + } + usleep(200 * 1000); + } + } + } virMutexDestroy(&priv->lock); VIR_FREE(priv); @@ -548,6 +660,7 @@ enum { #define MAX_LISTEN 5 int main(int argc, char **argv) { + virNetServerProgramPtr lockProgram = NULL; const char *remote_config_file = NULL; int statuswrite = -1; int ret = 1; @@ -707,6 +820,18 @@ int main(int argc, char **argv) { goto cleanup; } + if (!(lockProgram = virNetServerProgramNew(VIR_LOCK_SPACE_PROTOCOL_PROGRAM, + VIR_LOCK_SPACE_PROTOCOL_PROGRAM_VERSION, + virLockSpaceProtocolProcs, + virLockSpaceProtocolNProcs))) { + ret = VIR_LOCK_DAEMON_ERR_INIT; + goto cleanup; + } + if (virNetServerAddProgram(lockDaemon->srv, lockProgram) < 0) { + ret = VIR_LOCK_DAEMON_ERR_INIT; + goto cleanup; + } + /* Disable error func, now logging is setup */ virSetErrorFunc(NULL, virLockDaemonErrorHandler); @@ -730,6 +855,7 @@ int main(int argc, char **argv) { ret = 0; cleanup: + virObjectUnref(lockProgram); virLockDaemonFree(lockDaemon); if (statuswrite != -1) { if (ret != 0) { diff --git a/src/locking/lock_daemon.h b/src/locking/lock_daemon.h index 7bc8c2e..619f8f2 100644 --- a/src/locking/lock_daemon.h +++ b/src/locking/lock_daemon.h @@ -34,10 +34,23 @@ typedef virLockDaemonClient *virLockDaemonClientPtr; struct _virLockDaemonClient { virMutex lock; + bool restricted; + + pid_t ownerPid; + char *ownerName; + unsigned char ownerUUID[VIR_UUID_BUFLEN]; + unsigned int ownerId; pid_t clientPid; }; extern virLockDaemonPtr lockDaemon; +int virLockDaemonAddLockSpace(virLockDaemonPtr lockd, + const char *path, + virLockSpacePtr lockspace); + +virLockSpacePtr virLockDaemonFindLockSpace(virLockDaemonPtr lockd, + const char *path); + #endif /* __VIR_LOCK_DAEMON_H__ */ diff --git a/src/locking/lock_daemon_dispatch.c b/src/locking/lock_daemon_dispatch.c new file mode 100644 index 0000000..664bad5 --- /dev/null +++ b/src/locking/lock_daemon_dispatch.c @@ -0,0 +1,370 @@ +/* + * lock_daemon_dispatch.c: lock management daemon dispatch + * + * Copyright (C) 2006-2012 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "rpc/virnetserver.h" +#include "rpc/virnetserverclient.h" +#include "util.h" +#include "logging.h" + +#include "lock_daemon.h" +#include "lock_protocol.h" +#include "lock_daemon_dispatch_stubs.h" +#include "virterror_internal.h" + +#define VIR_FROM_THIS VIR_FROM_RPC + +static int +virLockSpaceProtocolDispatchAcquireResource(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolAcquireResourceArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + virLockSpacePtr lockspace; + unsigned int newFlags; + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(VIR_LOCK_SPACE_ACQUIRE_SHARED | + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + if (!(lockspace = virLockDaemonFindLockSpace(lockDaemon, args->path))) + goto cleanup; + + newFlags = 0; + if (flags & VIR_LOCK_SPACE_PROTOCOL_ACQUIRE_RESOURCE_SHARED) + newFlags |= VIR_LOCK_SPACE_ACQUIRE_SHARED; + if (flags & VIR_LOCK_SPACE_PROTOCOL_ACQUIRE_RESOURCE_AUTOCREATE) + newFlags |= VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE; + + if (virLockSpaceAcquireResource(lockspace, + args->name, + priv->ownerPid, + newFlags) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchCreateResource(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolCreateResourceArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + virLockSpacePtr lockspace; + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + if (!(lockspace = virLockDaemonFindLockSpace(lockDaemon, args->path))) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, args->name) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchDeleteResource(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolDeleteResourceArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + virLockSpacePtr lockspace; + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + if (!(lockspace = virLockDaemonFindLockSpace(lockDaemon, args->path))) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, args->name) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchNew(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolNewArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + virLockSpacePtr lockspace; + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + if (!args->path || STREQ(args->path, "")) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("the default lockspace already exists")); + goto cleanup; + } + + if ((lockspace = virLockDaemonFindLockSpace(lockDaemon, args->path))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Lockspace for path %s already exists"), + args->path); + goto cleanup; + } + virResetLastError(); + + lockspace = virLockSpaceNew(args->path); + virLockDaemonAddLockSpace(lockDaemon, args->path, lockspace); + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchRegister(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolRegisterArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have already been registered")); + goto cleanup; + } + + if (!(priv->ownerName = strdup(args->owner.name))) { + virReportOOMError(); + goto cleanup; + } + memcpy(priv->ownerUUID, args->owner.uuid, VIR_UUID_BUFLEN); + priv->ownerId = args->owner.id; + priv->ownerPid = args->owner.pid; + VIR_DEBUG("ownerName=%s ownerId=%d ownerPid=%lld", + priv->ownerName, priv->ownerId, (unsigned long long)priv->ownerPid); + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchReleaseResource(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolReleaseResourceArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + virLockSpacePtr lockspace; + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + if (!(lockspace = virLockDaemonFindLockSpace(lockDaemon, args->path))) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, + args->name, + priv->ownerPid) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchRestrict(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolRestrictArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + priv->restricted = true; + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} diff --git a/src/locking/lock_daemon_dispatch.h b/src/locking/lock_daemon_dispatch.h new file mode 100644 index 0000000..a193a58 --- /dev/null +++ b/src/locking/lock_daemon_dispatch.h @@ -0,0 +1,31 @@ +/* + * lock_daemon_dispatch.h: lock management daemon dispatch + * + * Copyright (C) 2006-2012 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_LOCK_DAEMON_DISPATCH_H__ +# define __VIR_LOCK_DAEMON_DISPATCH_H__ + +# include "rpc/virnetserverprogram.h" + +extern virNetServerProgramProc virLockSpaceProtocolProcs[]; +extern size_t virLockSpaceProtocolNProcs; + +#endif /* __VIR_LOCK_DAEMON_DISPATCH_H__ */ -- 1.7.11.2

On 12.09.2012 18:29, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
Introduce a lock_daemon_dispatch.c file which implements the server side dispatcher the RPC APIs previously defined in the lock protocol.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- .gitignore | 1 + po/POTFILES.in | 1 + src/Makefile.am | 14 ++ src/internal.h | 22 +++ src/locking/lock_daemon.c | 130 ++++++++++++- src/locking/lock_daemon.h | 13 ++ src/locking/lock_daemon_dispatch.c | 370 +++++++++++++++++++++++++++++++++++++ src/locking/lock_daemon_dispatch.h | 31 ++++ 8 files changed, 580 insertions(+), 2 deletions(-) create mode 100644 src/locking/lock_daemon_dispatch.c create mode 100644 src/locking/lock_daemon_dispatch.h
ACK but see my comments below
diff --git a/.gitignore b/.gitignore index c93433b..060bc7a 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,7 @@ /src/libvirt_*probes.h /src/libvirt_lxc /src/locking/lock_protocol.[ch] +/src/locking/lock_daemon_dispatch_stubs.h
Please order alphabetically.
/src/locking/qemu-sanlock.conf /src/locking/test_libvirt_sanlock.aug /src/lxc/lxc_controller_dispatch.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 78f71f5..6b9a7af 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -45,6 +45,7 @@ src/internal.h src/libvirt.c src/libvirt-qemu.c src/locking/lock_daemon.c +src/locking/lock_daemon_dispatch.c src/locking/lock_driver_sanlock.c src/locking/lock_manager.c src/lxc/lxc_cgroup.c diff --git a/src/Makefile.am b/src/Makefile.am index 0b0367b..e088d9b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -151,11 +151,24 @@ EXTRA_DIST += locking/lock_protocol.x BUILT_SOURCES += $(LOCK_PROTOCOL_GENERATED) MAINTAINERCLEANFILES += $(LOCK_PROTOCOL_GENERATED)
+LOCK_DAEMON_GENERATED = \ + locking/lock_daemon_dispatch_stubs.h + $(NULL) + +BUILT_SOURCES += $(LOCK_DAEMON_GENERATED) +MAINTAINERCLEANFILES += $(LOCK_DAEMON_GENERATED) + LOCK_DAEMON_SOURCES = \ locking/lock_daemon.h \ locking/lock_daemon.c \ + locking/lock_daemon_dispatch.c \ + locking/lock_daemon_dispatch.h \ $(NULL)
+$(srcdir)/locking/lock_daemon_dispatch_stubs.h: locking/lock_protocol.x $(srcdir)/rpc/gendispatch.pl Makefile.am + $(AM_V_GEN)perl -w $(srcdir)/rpc/gendispatch.pl -b virLockSpaceProtocol VIR_LOCK_SPACE_PROTOCOL $< > $@ + + NETDEV_CONF_SOURCES = \ conf/netdev_bandwidth_conf.h conf/netdev_bandwidth_conf.c \ conf/netdev_vport_profile_conf.h conf/netdev_vport_profile_conf.c \ @@ -1494,6 +1507,7 @@ sbin_PROGRAMS = virtlockd virtlockd_SOURCES = \ $(LOCK_DAEMON_SOURCES) \ $(LOCK_PROTOCOL_GENERATED) \ + $(LOCK_DAEMON_GENERATED) \ $(NULL) virtlockd_CFLAGS = \ $(AM_CFLAGS) \ diff --git a/src/internal.h b/src/internal.h index 8037a4a..b70df2a 100644 --- a/src/internal.h +++ b/src/internal.h @@ -236,6 +236,28 @@ } \ } while (0)
+/** + * virCheckFlagsGoto: + * @supported: an OR'ed set of supported flags + * @label: label to jump to on error + * + * To avoid memory leaks this macro has to be used before any non-trivial + * code which could possibly allocate some memory. + * + * Returns nothing. Jumps to a label if unsupported flags were + * passed to it. + */ +# define virCheckFlagsGoto(supported, label) \ + do { \ + unsigned long __unsuppflags = flags & ~(supported); \ + if (__unsuppflags) { \ + virReportInvalidArg(flags, \ + _("unsupported flags (0x%lx) in function %s"), \ + __unsuppflags, __FUNCTION__); \ + goto label; \ + } \ + } while (0) + # define virCheckNonNullArgReturn(argname, retval) \ do { \ if (argname == NULL) { \ diff --git a/src/locking/lock_daemon.c b/src/locking/lock_daemon.c index 287ad8c..94b41db 100644 --- a/src/locking/lock_daemon.c +++ b/src/locking/lock_daemon.c @@ -43,6 +43,9 @@ #include "virrandom.h" #include "virhash.h"
+ #include "virprocess.h"
+#include "locking/lock_daemon_dispatch.h" +#include "locking/lock_protocol.h" + #include "configmake.h"
#define VIR_FROM_THIS VIR_FROM_LOCKING @@ -54,6 +57,8 @@ struct _virLockDaemon { virMutex lock; virNetServerPtr srv; + virHashTablePtr lockspaces; + virLockSpacePtr defaultLockspace; };
virLockDaemonPtr lockDaemon = NULL; @@ -99,11 +104,19 @@ virLockDaemonFree(virLockDaemonPtr lockd) return;
virObjectUnref(lockd->srv); + virHashFree(lockd->lockspaces); + virLockSpaceFree(lockd->defaultLockspace);
VIR_FREE(lockd); }
+static void virLockDaemonLockSpaceDataFree(void *data, + const void *key ATTRIBUTE_UNUSED) +{ + virLockSpaceFree(data); +} + static virLockDaemonPtr virLockDaemonNew(void) { @@ -130,6 +143,13 @@ virLockDaemonNew(void) NULL))) goto error;
+ if (!(lockd->lockspaces = virHashCreate(3, + virLockDaemonLockSpaceDataFree)))
I suggest (as in previous patches) to substitute this magic constant with a macro.
+ goto error; + + if (!(lockd->defaultLockspace = virLockSpaceNew(NULL))) + goto error; + return lockd;
error: @@ -138,6 +158,31 @@ error: }
+int virLockDaemonAddLockSpace(virLockDaemonPtr lockd, + const char *path, + virLockSpacePtr lockspace) +{ + int ret; + virMutexLock(&lockd->lock); + ret = virHashAddEntry(lockd->lockspaces, path, lockspace); + virMutexUnlock(&lockd->lock); + return ret; +} + +virLockSpacePtr virLockDaemonFindLockSpace(virLockDaemonPtr lockd, + const char *path) +{ + virLockSpacePtr lockspace; + virMutexLock(&lockd->lock); + if (path && STRNEQ(path, "")) + lockspace = virHashLookup(lockd->lockspaces, path); + else + lockspace = lockd->defaultLockspace; + virMutexUnlock(&lockd->lock); + return lockspace; +} + + static int virLockDaemonForkIntoBackground(const char *argv0) { @@ -427,6 +472,30 @@ virLockDaemonSetupNetworking(virNetServerPtr srv, const char *sock_path) }
+struct virLockDaemonClientReleaseData { + virLockDaemonClientPtr client; + bool hadSomeLeases; + bool gotError; +}; + +static void +virLockDaemonClientReleaseLockspace(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *opaque) +{ + virLockSpacePtr lockspace = payload; + struct virLockDaemonClientReleaseData *data = opaque; + int rc; + + rc = virLockSpaceReleaseResourcesForOwner(lockspace, + data->client->clientPid); + if (rc > 0) + data->hadSomeLeases = true; + else if (rc < 0) + data->gotError = true; +} + + static void virLockDaemonClientFree(void *opaque) { @@ -435,9 +504,52 @@ virLockDaemonClientFree(void *opaque) if (!priv) return;
- VIR_DEBUG("priv=%p client=%lld", + VIR_DEBUG("priv=%p client=%lld owner=%lld", priv, - (unsigned long long)priv->clientPid); + (unsigned long long)priv->clientPid, + (unsigned long long)priv->ownerPid); + + /* If client & owner match, this is the lock holder */ + if (priv->clientPid == priv->ownerPid) { + size_t i; + struct virLockDaemonClientReleaseData data = { + priv, false, false
I think in C99 style this would be more readable.
+ }; + + /* Release all locks associated with this + * owner in all lockspaces */ + virMutexLock(&lockDaemon->lock); + virHashForEach(lockDaemon->lockspaces, + virLockDaemonClientReleaseLockspace, + &data); + virLockDaemonClientReleaseLockspace(lockDaemon->defaultLockspace, + "", + &data); + virMutexUnlock(&lockDaemon->lock); + + /* If the client had some active leases when it + * closed the connection, we must kill it off + * to make sure it doesn't do nasty stuff */ + if (data.gotError || data.hadSomeLeases) { + for (i = 0 ; i < 15 ; i++) { + int signum; + if (i == 0) + signum = SIGTERM; + else if (i == 8) + signum = SIGKILL; + else + signum = 0; + if (virKillProcess(priv->clientPid, signum) < 0) {
virKillProcess was renamed
+ if (errno == ESRCH) + break; + + VIR_WARN("Failed to kill off pid %lld", + (unsigned long long)priv->clientPid); + } + usleep(200 * 1000); + } + } + }
virMutexDestroy(&priv->lock); VIR_FREE(priv); @@ -548,6 +660,7 @@ enum {
#define MAX_LISTEN 5 int main(int argc, char **argv) { + virNetServerProgramPtr lockProgram = NULL; const char *remote_config_file = NULL; int statuswrite = -1; int ret = 1; @@ -707,6 +820,18 @@ int main(int argc, char **argv) { goto cleanup; }
+ if (!(lockProgram = virNetServerProgramNew(VIR_LOCK_SPACE_PROTOCOL_PROGRAM, + VIR_LOCK_SPACE_PROTOCOL_PROGRAM_VERSION, + virLockSpaceProtocolProcs, + virLockSpaceProtocolNProcs))) { + ret = VIR_LOCK_DAEMON_ERR_INIT; + goto cleanup; + } + if (virNetServerAddProgram(lockDaemon->srv, lockProgram) < 0) { + ret = VIR_LOCK_DAEMON_ERR_INIT; + goto cleanup; + } + /* Disable error func, now logging is setup */ virSetErrorFunc(NULL, virLockDaemonErrorHandler);
@@ -730,6 +855,7 @@ int main(int argc, char **argv) { ret = 0;
cleanup: + virObjectUnref(lockProgram); virLockDaemonFree(lockDaemon); if (statuswrite != -1) { if (ret != 0) { diff --git a/src/locking/lock_daemon.h b/src/locking/lock_daemon.h index 7bc8c2e..619f8f2 100644 --- a/src/locking/lock_daemon.h +++ b/src/locking/lock_daemon.h @@ -34,10 +34,23 @@ typedef virLockDaemonClient *virLockDaemonClientPtr;
struct _virLockDaemonClient { virMutex lock; + bool restricted; + + pid_t ownerPid; + char *ownerName; + unsigned char ownerUUID[VIR_UUID_BUFLEN]; + unsigned int ownerId;
pid_t clientPid; };
extern virLockDaemonPtr lockDaemon;
+int virLockDaemonAddLockSpace(virLockDaemonPtr lockd, + const char *path, + virLockSpacePtr lockspace); + +virLockSpacePtr virLockDaemonFindLockSpace(virLockDaemonPtr lockd, + const char *path); + #endif /* __VIR_LOCK_DAEMON_H__ */ diff --git a/src/locking/lock_daemon_dispatch.c b/src/locking/lock_daemon_dispatch.c new file mode 100644 index 0000000..664bad5 --- /dev/null +++ b/src/locking/lock_daemon_dispatch.c @@ -0,0 +1,370 @@ +/* + * lock_daemon_dispatch.c: lock management daemon dispatch + * + * Copyright (C) 2006-2012 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "rpc/virnetserver.h" +#include "rpc/virnetserverclient.h" +#include "util.h" +#include "logging.h" + +#include "lock_daemon.h" +#include "lock_protocol.h" +#include "lock_daemon_dispatch_stubs.h" +#include "virterror_internal.h" + +#define VIR_FROM_THIS VIR_FROM_RPC + +static int +virLockSpaceProtocolDispatchAcquireResource(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolAcquireResourceArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + virLockSpacePtr lockspace; + unsigned int newFlags; + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(VIR_LOCK_SPACE_ACQUIRE_SHARED | + VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + if (!(lockspace = virLockDaemonFindLockSpace(lockDaemon, args->path))) + goto cleanup; + + newFlags = 0; + if (flags & VIR_LOCK_SPACE_PROTOCOL_ACQUIRE_RESOURCE_SHARED) + newFlags |= VIR_LOCK_SPACE_ACQUIRE_SHARED; + if (flags & VIR_LOCK_SPACE_PROTOCOL_ACQUIRE_RESOURCE_AUTOCREATE) + newFlags |= VIR_LOCK_SPACE_ACQUIRE_AUTOCREATE; + + if (virLockSpaceAcquireResource(lockspace, + args->name, + priv->ownerPid, + newFlags) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchCreateResource(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolCreateResourceArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + virLockSpacePtr lockspace; + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + if (!(lockspace = virLockDaemonFindLockSpace(lockDaemon, args->path))) + goto cleanup; + + if (virLockSpaceCreateResource(lockspace, args->name) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchDeleteResource(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolDeleteResourceArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + virLockSpacePtr lockspace; + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + if (!(lockspace = virLockDaemonFindLockSpace(lockDaemon, args->path))) + goto cleanup; + + if (virLockSpaceDeleteResource(lockspace, args->name) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchNew(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolNewArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + virLockSpacePtr lockspace; + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + if (!args->path || STREQ(args->path, "")) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("the default lockspace already exists")); + goto cleanup; + } + + if ((lockspace = virLockDaemonFindLockSpace(lockDaemon, args->path))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Lockspace for path %s already exists"), + args->path); + goto cleanup; + } + virResetLastError(); + + lockspace = virLockSpaceNew(args->path); + virLockDaemonAddLockSpace(lockDaemon, args->path, lockspace); + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchRegister(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolRegisterArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have already been registered")); + goto cleanup; + } + + if (!(priv->ownerName = strdup(args->owner.name))) { + virReportOOMError(); + goto cleanup; + } + memcpy(priv->ownerUUID, args->owner.uuid, VIR_UUID_BUFLEN); + priv->ownerId = args->owner.id; + priv->ownerPid = args->owner.pid; + VIR_DEBUG("ownerName=%s ownerId=%d ownerPid=%lld", + priv->ownerName, priv->ownerId, (unsigned long long)priv->ownerPid); + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchReleaseResource(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolReleaseResourceArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + virLockSpacePtr lockspace; + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + if (!(lockspace = virLockDaemonFindLockSpace(lockDaemon, args->path))) + goto cleanup; + + if (virLockSpaceReleaseResource(lockspace, + args->name, + priv->ownerPid) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + + +static int +virLockSpaceProtocolDispatchRestrict(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + virLockSpaceProtocolRestrictArgs *args) +{ + int rv = -1; + unsigned int flags = args->flags; + virLockDaemonClientPtr priv = + virNetServerClientGetPrivateData(client); + + virMutexLock(&priv->lock); + + virCheckFlagsGoto(0, cleanup); + + if (priv->restricted) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("lock manager connection has been restricted")); + goto cleanup; + } + + if (!priv->ownerPid) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("lock owner details have not been registered")); + goto cleanup; + } + + priv->restricted = true; + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} diff --git a/src/locking/lock_daemon_dispatch.h b/src/locking/lock_daemon_dispatch.h new file mode 100644 index 0000000..a193a58 --- /dev/null +++ b/src/locking/lock_daemon_dispatch.h @@ -0,0 +1,31 @@ +/* + * lock_daemon_dispatch.h: lock management daemon dispatch + * + * Copyright (C) 2006-2012 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_LOCK_DAEMON_DISPATCH_H__ +# define __VIR_LOCK_DAEMON_DISPATCH_H__ + +# include "rpc/virnetserverprogram.h" + +extern virNetServerProgramProc virLockSpaceProtocolProcs[]; +extern size_t virLockSpaceProtocolNProcs; + +#endif /* __VIR_LOCK_DAEMON_DISPATCH_H__ */

From: "Daniel P. Berrange" <berrange@redhat.com> This enhancement virtlockd so that it can receive a pre-opened UNIX domain socket from systemd at launch time, and adds the systemd service/socket unit files * daemon/libvirtd.service.in: Require virtlockd to be running * libvirt.spec.in: Add virtlockd systemd files * src/Makefile.am: Install systemd files * src/locking/lock_daemon.c: Support socket activation * src/locking/virtlockd.service.in, src/locking/virtlockd.socket.in: systemd unit files * src/rpc/virnetserverservice.c, src/rpc/virnetserverservice.h: Add virNetServerServiceNewFD() method * src/rpc/virnetsocket.c, src/rpc/virnetsocket.h: Add virNetSocketNewListenFD method Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- libvirt.spec.in | 6 ++++ src/Makefile.am | 51 ++++++++++++++++++++++++++-- src/locking/lock_daemon.c | 73 ++++++++++++++++++++++++++++++++++++++-- src/locking/virtlockd.service.in | 13 +++++++ src/locking/virtlockd.socket.in | 8 +++++ 5 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 src/locking/virtlockd.service.in create mode 100644 src/locking/virtlockd.socket.in diff --git a/libvirt.spec.in b/libvirt.spec.in index 69d8c58..62938e5 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1428,6 +1428,7 @@ done %if %{with_systemd} if [ $1 -eq 1 ] ; then # Initial installation + /bin/systemctl enable virtlockd.socket >/dev/null 2>&1 || : /bin/systemctl enable libvirtd.service >/dev/null 2>&1 || : /bin/systemctl enable cgconfig.service >/dev/null 2>&1 || : fi @@ -1452,8 +1453,10 @@ fi %if %{with_systemd} if [ $1 -eq 0 ] ; then # Package removal, not upgrade + /bin/systemctl --no-reload disable virtlockd.socket > /dev/null 2>&1 || : /bin/systemctl --no-reload disable libvirtd.service > /dev/null 2>&1 || : /bin/systemctl stop libvirtd.service > /dev/null 2>&1 || : + /bin/systemctl stop virtlockd.service > /dev/null 2>&1 || : fi %else if [ $1 = 0 ]; then @@ -1467,6 +1470,7 @@ fi /bin/systemctl daemon-reload >/dev/null 2>&1 || : if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall + /bin/systemctl try-restart virtlockd.service >/dev/null 2>&1 || : /bin/systemctl try-restart libvirtd.service >/dev/null 2>&1 || : fi %endif @@ -1565,6 +1569,8 @@ fi %{_sysconfdir}/rc.d/init.d/virtlockd %if %{with_systemd} %{_unitdir}/libvirtd.service +%{_unitdir}/virtlockd.service +%{_unitdir}/virtlockd.socket %endif %doc daemon/libvirtd.upstart %config(noreplace) %{_sysconfdir}/sysconfig/libvirtd diff --git a/src/Makefile.am b/src/Makefile.am index e088d9b..b402297 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1575,6 +1575,53 @@ virtlockd.init: locking/virtlockd.init.in $(top_builddir)/config.status +EXTRA_DIST += locking/virtlockd.service.in locking/virtlockd.socket.in + +if WITH_LIBVIRTD +if LIBVIRT_INIT_SCRIPT_SYSTEMD + +SYSTEMD_UNIT_DIR = /lib/systemd/system + +BUILT_SOURCES += virtlockd.service virtlockd.socket + +install-systemd: virtlockd.init install-sysconfig + mkdir -p $(DESTDIR)$(SYSTEMD_UNIT_DIR) + $(INSTALL_SCRIPT) virtlockd.service \ + $(DESTDIR)$(SYSTEMD_UNIT_DIR)/ + $(INSTALL_SCRIPT) virtlockd.socket \ + $(DESTDIR)$(SYSTEMD_UNIT_DIR)/ + +uninstall-systemd: uninstall-sysconfig + rm -f $(DESTDIR)$(SYSTEMD_UNIT_DIR)/virtlockd.service + rm -f $(DESTDIR)$(SYSTEMD_UNIT_DIR)/virtlockd.socket +else +install-systemd: +uninstall-systemd: +endif +else +install-systemd: +uninstall-systemd: +endif + +virtlockd.service: locking/virtlockd.service.in $(top_builddir)/config.status + $(AM_V_GEN)sed \ + -e "s!::localstatedir::!$(localstatedir)!g" \ + -e "s!::sbindir::!$(sbindir)!g" \ + -e "s!::sysconfdir::!$(sysconfdir)!g" \ + < $< > $@-t && \ + chmod a+x $@-t && \ + mv $@-t $@ + +virtlockd.socket: locking/virtlockd.socket.in $(top_builddir)/config.status + $(AM_V_GEN)sed \ + -e "s!::localstatedir::!$(localstatedir)!g" \ + -e "s!::sbindir::!$(sbindir)!g" \ + -e "s!::sysconfdir::!$(sysconfdir)!g" \ + < $< > $@-t && \ + chmod a+x $@-t && \ + mv $@-t $@ + + if HAVE_SANLOCK lockdriverdir = $(libdir)/libvirt/lock-driver lockdriver_LTLIBRARIES = sanlock.la @@ -1793,7 +1840,7 @@ endif endif EXTRA_DIST += $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES) -install-data-local: install-init +install-data-local: install-init install-systemd if WITH_LIBVIRTD $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd" $(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/lockd" @@ -1843,7 +1890,7 @@ if WITH_NETWORK $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart/default.xml endif -uninstall-local:: uninstall-init +uninstall-local:: uninstall-init uninstall-systemd if WITH_LIBVIRTD rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd" ||: rmdir "$(DESTDIR)$(localstatedir)/run/libvirt/lockd" ||: diff --git a/src/locking/lock_daemon.c b/src/locking/lock_daemon.c index 94b41db..7bc6917 100644 --- a/src/locking/lock_daemon.c +++ b/src/locking/lock_daemon.c @@ -454,8 +454,69 @@ virLockDaemonSetupSignals(virNetServerPtr srv) return 0; } + static int -virLockDaemonSetupNetworking(virNetServerPtr srv, const char *sock_path) +virLockDaemonSetupNetworkingSystemD(virNetServerPtr srv) +{ + virNetServerServicePtr svc; + const char *pidstr; + const char *fdstr; + unsigned long long procid; + unsigned int nfds; + + if (!(pidstr = getenv("LISTEN_PID"))) { + VIR_DEBUG("No LISTEN_FDS from systemd"); + return 0; + } + + if (virStrToLong_ull(pidstr, NULL, 10, &procid) < 0) { + VIR_DEBUG("Malformed LISTEN_PID from systemd %s", pidstr); + return 0; + } + + if ((pid_t)procid != getpid()) { + VIR_DEBUG("LISTEN_PID %s is not for us %llu", + pidstr, (unsigned long long)getpid()); + return 0; + } + + if (!(fdstr = getenv("LISTEN_FDS"))) { + VIR_DEBUG("No LISTEN_FDS from systemd"); + return 0; + } + + if (virStrToLong_ui(fdstr, NULL, 10, &nfds) < 0) { + VIR_DEBUG("Malformed LISTEN_FDS from systemd %s", fdstr); + return 0; + } + + if (nfds > 1) { + VIR_DEBUG("Too many (%d) file descriptors from systemd", + nfds); + nfds = 1; + } + + unsetenv("LISTEN_PID"); + unsetenv("LISTEN_FDS"); + + if (nfds == 0) + return 0; + + /* Systemd passes FDs, starting immediately after stderr, + * so the first FD we'll get is '3'. */ + if (!(svc = virNetServerServiceNewFD(3, 0, false, 1, NULL))) + return -1; + + if (virNetServerAddService(srv, svc, NULL) < 0) { + virObjectUnref(svc); + return -1; + } + return 1; +} + + +static int +virLockDaemonSetupNetworkingNative(virNetServerPtr srv, const char *sock_path) { virNetServerServicePtr svc; @@ -672,6 +733,7 @@ int main(int argc, char **argv) { char *pid_file = NULL; int pid_file_fd = -1; char *sock_file = NULL; + int rv; struct option opts[] = { { "verbose", no_argument, &verbose, 1}, @@ -810,7 +872,14 @@ int main(int argc, char **argv) { goto cleanup; } - if (virLockDaemonSetupNetworking(lockDaemon->srv, sock_file) < 0) { + if ((rv = virLockDaemonSetupNetworkingSystemD(lockDaemon->srv)) < 0) { + ret = VIR_LOCK_DAEMON_ERR_NETWORK; + goto cleanup; + } + + /* Only do this, if systemd did not pass a FD */ + if (rv == 0 && + virLockDaemonSetupNetworkingNative(lockDaemon->srv, sock_file) < 0) { ret = VIR_LOCK_DAEMON_ERR_NETWORK; goto cleanup; } diff --git a/src/locking/virtlockd.service.in b/src/locking/virtlockd.service.in new file mode 100644 index 0000000..a9f9f93 --- /dev/null +++ b/src/locking/virtlockd.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=Virtual machine lock manager +Requires=virtlockd.socket +After=syslog.target + +[Service] +EnvironmentFile=-/etc/sysconfig/virtlockd +ExecStart=@sbindir@/virtlockd +ExecReload=/bin/kill -HUP $MAINPID +# Loosing the locks is a really bad thing that will +# cause the machine to be fenced (rebooted), so make +# sure we discourage OOM killer +OOMScoreAdjust=-900 diff --git a/src/locking/virtlockd.socket.in b/src/locking/virtlockd.socket.in new file mode 100644 index 0000000..0589a29 --- /dev/null +++ b/src/locking/virtlockd.socket.in @@ -0,0 +1,8 @@ +[Unit] +Description=Virtual machine lock manager socket + +[Socket] +ListenStream=/var/run/libvirt/virtlockd/virtlockd.sock + +[Install] +WantedBy=multi-user.target -- 1.7.11.2

On 12.09.2012 18:29, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
This enhancement virtlockd so that it can receive a pre-opened UNIX domain socket from systemd at launch time, and adds the systemd service/socket unit files
* daemon/libvirtd.service.in: Require virtlockd to be running * libvirt.spec.in: Add virtlockd systemd files * src/Makefile.am: Install systemd files * src/locking/lock_daemon.c: Support socket activation * src/locking/virtlockd.service.in, src/locking/virtlockd.socket.in: systemd unit files * src/rpc/virnetserverservice.c, src/rpc/virnetserverservice.h: Add virNetServerServiceNewFD() method * src/rpc/virnetsocket.c, src/rpc/virnetsocket.h: Add virNetSocketNewListenFD method
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- libvirt.spec.in | 6 ++++ src/Makefile.am | 51 ++++++++++++++++++++++++++-- src/locking/lock_daemon.c | 73 ++++++++++++++++++++++++++++++++++++++-- src/locking/virtlockd.service.in | 13 +++++++ src/locking/virtlockd.socket.in | 8 +++++ 5 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 src/locking/virtlockd.service.in create mode 100644 src/locking/virtlockd.socket.in
ACK
diff --git a/libvirt.spec.in b/libvirt.spec.in index 69d8c58..62938e5 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1428,6 +1428,7 @@ done %if %{with_systemd} if [ $1 -eq 1 ] ; then # Initial installation + /bin/systemctl enable virtlockd.socket >/dev/null 2>&1 || : /bin/systemctl enable libvirtd.service >/dev/null 2>&1 || : /bin/systemctl enable cgconfig.service >/dev/null 2>&1 || : fi @@ -1452,8 +1453,10 @@ fi %if %{with_systemd} if [ $1 -eq 0 ] ; then # Package removal, not upgrade + /bin/systemctl --no-reload disable virtlockd.socket > /dev/null 2>&1 || : /bin/systemctl --no-reload disable libvirtd.service > /dev/null 2>&1 || : /bin/systemctl stop libvirtd.service > /dev/null 2>&1 || : + /bin/systemctl stop virtlockd.service > /dev/null 2>&1 || : fi %else if [ $1 = 0 ]; then @@ -1467,6 +1470,7 @@ fi /bin/systemctl daemon-reload >/dev/null 2>&1 || : if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall + /bin/systemctl try-restart virtlockd.service >/dev/null 2>&1 || : /bin/systemctl try-restart libvirtd.service >/dev/null 2>&1 || : fi %endif @@ -1565,6 +1569,8 @@ fi %{_sysconfdir}/rc.d/init.d/virtlockd %if %{with_systemd} %{_unitdir}/libvirtd.service +%{_unitdir}/virtlockd.service +%{_unitdir}/virtlockd.socket %endif %doc daemon/libvirtd.upstart %config(noreplace) %{_sysconfdir}/sysconfig/libvirtd diff --git a/src/Makefile.am b/src/Makefile.am index e088d9b..b402297 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1575,6 +1575,53 @@ virtlockd.init: locking/virtlockd.init.in $(top_builddir)/config.status
+EXTRA_DIST += locking/virtlockd.service.in locking/virtlockd.socket.in + +if WITH_LIBVIRTD +if LIBVIRT_INIT_SCRIPT_SYSTEMD + +SYSTEMD_UNIT_DIR = /lib/systemd/system + +BUILT_SOURCES += virtlockd.service virtlockd.socket + +install-systemd: virtlockd.init install-sysconfig + mkdir -p $(DESTDIR)$(SYSTEMD_UNIT_DIR) + $(INSTALL_SCRIPT) virtlockd.service \ + $(DESTDIR)$(SYSTEMD_UNIT_DIR)/ + $(INSTALL_SCRIPT) virtlockd.socket \ + $(DESTDIR)$(SYSTEMD_UNIT_DIR)/ + +uninstall-systemd: uninstall-sysconfig + rm -f $(DESTDIR)$(SYSTEMD_UNIT_DIR)/virtlockd.service + rm -f $(DESTDIR)$(SYSTEMD_UNIT_DIR)/virtlockd.socket +else +install-systemd: +uninstall-systemd: +endif +else +install-systemd: +uninstall-systemd: +endif + +virtlockd.service: locking/virtlockd.service.in $(top_builddir)/config.status + $(AM_V_GEN)sed \ + -e "s!::localstatedir::!$(localstatedir)!g" \ + -e "s!::sbindir::!$(sbindir)!g" \ + -e "s!::sysconfdir::!$(sysconfdir)!g" \ + < $< > $@-t && \ + chmod a+x $@-t && \ + mv $@-t $@ + +virtlockd.socket: locking/virtlockd.socket.in $(top_builddir)/config.status + $(AM_V_GEN)sed \ + -e "s!::localstatedir::!$(localstatedir)!g" \ + -e "s!::sbindir::!$(sbindir)!g" \ + -e "s!::sysconfdir::!$(sysconfdir)!g" \ + < $< > $@-t && \ + chmod a+x $@-t && \ + mv $@-t $@ + + if HAVE_SANLOCK lockdriverdir = $(libdir)/libvirt/lock-driver lockdriver_LTLIBRARIES = sanlock.la @@ -1793,7 +1840,7 @@ endif endif EXTRA_DIST += $(SECURITY_DRIVER_APPARMOR_HELPER_SOURCES)
-install-data-local: install-init +install-data-local: install-init install-systemd if WITH_LIBVIRTD $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd" $(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/lockd" @@ -1843,7 +1890,7 @@ if WITH_NETWORK $(DESTDIR)$(sysconfdir)/libvirt/qemu/networks/autostart/default.xml endif
-uninstall-local:: uninstall-init +uninstall-local:: uninstall-init uninstall-systemd if WITH_LIBVIRTD rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/lockd" ||: rmdir "$(DESTDIR)$(localstatedir)/run/libvirt/lockd" ||: diff --git a/src/locking/lock_daemon.c b/src/locking/lock_daemon.c index 94b41db..7bc6917 100644 --- a/src/locking/lock_daemon.c +++ b/src/locking/lock_daemon.c @@ -454,8 +454,69 @@ virLockDaemonSetupSignals(virNetServerPtr srv) return 0; }
+ static int -virLockDaemonSetupNetworking(virNetServerPtr srv, const char *sock_path) +virLockDaemonSetupNetworkingSystemD(virNetServerPtr srv) +{ + virNetServerServicePtr svc; + const char *pidstr; + const char *fdstr; + unsigned long long procid; + unsigned int nfds; + + if (!(pidstr = getenv("LISTEN_PID"))) { + VIR_DEBUG("No LISTEN_FDS from systemd"); + return 0; + } + + if (virStrToLong_ull(pidstr, NULL, 10, &procid) < 0) { + VIR_DEBUG("Malformed LISTEN_PID from systemd %s", pidstr); + return 0; + } + + if ((pid_t)procid != getpid()) { + VIR_DEBUG("LISTEN_PID %s is not for us %llu", + pidstr, (unsigned long long)getpid()); + return 0; + } + + if (!(fdstr = getenv("LISTEN_FDS"))) { + VIR_DEBUG("No LISTEN_FDS from systemd"); + return 0; + } + + if (virStrToLong_ui(fdstr, NULL, 10, &nfds) < 0) { + VIR_DEBUG("Malformed LISTEN_FDS from systemd %s", fdstr); + return 0; + } + + if (nfds > 1) { + VIR_DEBUG("Too many (%d) file descriptors from systemd", + nfds); + nfds = 1; + } + + unsetenv("LISTEN_PID"); + unsetenv("LISTEN_FDS"); + + if (nfds == 0) + return 0; + + /* Systemd passes FDs, starting immediately after stderr, + * so the first FD we'll get is '3'. */ + if (!(svc = virNetServerServiceNewFD(3, 0, false, 1, NULL))) + return -1; + + if (virNetServerAddService(srv, svc, NULL) < 0) { + virObjectUnref(svc); + return -1; + } + return 1; +} + + +static int +virLockDaemonSetupNetworkingNative(virNetServerPtr srv, const char *sock_path) { virNetServerServicePtr svc;
@@ -672,6 +733,7 @@ int main(int argc, char **argv) { char *pid_file = NULL; int pid_file_fd = -1; char *sock_file = NULL; + int rv;
struct option opts[] = { { "verbose", no_argument, &verbose, 1}, @@ -810,7 +872,14 @@ int main(int argc, char **argv) { goto cleanup; }
- if (virLockDaemonSetupNetworking(lockDaemon->srv, sock_file) < 0) { + if ((rv = virLockDaemonSetupNetworkingSystemD(lockDaemon->srv)) < 0) { + ret = VIR_LOCK_DAEMON_ERR_NETWORK; + goto cleanup; + } + + /* Only do this, if systemd did not pass a FD */ + if (rv == 0 && + virLockDaemonSetupNetworkingNative(lockDaemon->srv, sock_file) < 0) { ret = VIR_LOCK_DAEMON_ERR_NETWORK; goto cleanup; } diff --git a/src/locking/virtlockd.service.in b/src/locking/virtlockd.service.in new file mode 100644 index 0000000..a9f9f93 --- /dev/null +++ b/src/locking/virtlockd.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=Virtual machine lock manager +Requires=virtlockd.socket +After=syslog.target + +[Service] +EnvironmentFile=-/etc/sysconfig/virtlockd +ExecStart=@sbindir@/virtlockd +ExecReload=/bin/kill -HUP $MAINPID +# Loosing the locks is a really bad thing that will +# cause the machine to be fenced (rebooted), so make +# sure we discourage OOM killer +OOMScoreAdjust=-900 diff --git a/src/locking/virtlockd.socket.in b/src/locking/virtlockd.socket.in new file mode 100644 index 0000000..0589a29 --- /dev/null +++ b/src/locking/virtlockd.socket.in @@ -0,0 +1,8 @@ +[Unit] +Description=Virtual machine lock manager socket + +[Socket] +ListenStream=/var/run/libvirt/virtlockd/virtlockd.sock + +[Install] +WantedBy=multi-user.target

From: "Daniel P. Berrange" <berrange@redhat.com> The virtlockd daemon maintains file locks on behalf of libvirtd and any VMs it is running. These file locks must be held for as long as any VM is running. If virtlockd itself ever quits, then it is expected that a node would be fenced/rebooted. Thus to allow for software upgrads on live systemd, virtlockd needs the ability to re-exec() itself. Upon receipt of SIGUSR1, virtlockd will save its current live state out to a file /var/run/virtlockd-restart-exec.json It then re-exec()'s itself with exactly the same argv as it originally had, and loads the state file, reconstructing any objects as appropriate. The state file contains information about all locks held and all network services and clients currently active. An example state document is { "server": { "min_workers": 1, "max_workers": 20, "priority_workers": 0, "max_clients": 20, "keepaliveInterval": 4294967295, "keepaliveCount": 0, "keepaliveRequired": false, "services": [ { "auth": 0, "readonly": false, "nrequests_client_max": 1, "socks": [ { "fd": 6, "errfd": -1, "pid": 0, "isClient": false } ] } ], "clients": [ { "auth": 0, "readonly": false, "nrequests_max": 1, "sock": { "fd": 9, "errfd": -1, "pid": 0, "isClient": true }, "privateData": { "restricted": true, "ownerPid": 1722, "ownerId": 6, "ownerName": "f18x86_64", "ownerUUID": "97586ba9-df27-9459-c806-f016c8bbd224" } }, { "auth": 0, "readonly": false, "nrequests_max": 1, "sock": { "fd": 10, "errfd": -1, "pid": 0, "isClient": true }, "privateData": { "restricted": true, "ownerPid": 1784, "ownerId": 7, "ownerName": "f16x86_64", "ownerUUID": "7b8e5e42-b875-61e9-b981-91ad8fa46979" } } ] }, "defaultLockspace": { "resources": [ { "name": "/var/lib/libvirt/images/f16x86_64.raw", "path": "/var/lib/libvirt/images/f16x86_64.raw", "fd": 14, "lockHeld": true, "flags": 0, "owners": [ 1784 ] }, { "name": "/var/lib/libvirt/images/shared.img", "path": "/var/lib/libvirt/images/shared.img", "fd": 12, "lockHeld": true, "flags": 1, "owners": [ 1722, 1784 ] }, { "name": "/var/lib/libvirt/images/f18x86_64.img", "path": "/var/lib/libvirt/images/f18x86_64.img", "fd": 11, "lockHeld": true, "flags": 0, "owners": [ 1722 ] } ] }, "lockspaces": [ ], "magic": "30199" } Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- libvirt.spec.in | 5 +- src/locking/lock_daemon.c | 417 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 408 insertions(+), 14 deletions(-) diff --git a/libvirt.spec.in b/libvirt.spec.in index 62938e5..71f838b 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1470,7 +1470,10 @@ fi /bin/systemctl daemon-reload >/dev/null 2>&1 || : if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall - /bin/systemctl try-restart virtlockd.service >/dev/null 2>&1 || : + /bin/systemctl status virtlockd.service >/dev/null 2>&1 + if [ $? = 1 ] ; then + /bin/systemctl kill --signal=USR1 virtlockd.service >/dev/null 2>&1 || : + fi /bin/systemctl try-restart libvirtd.service >/dev/null 2>&1 || : fi %endif diff --git a/src/locking/lock_daemon.c b/src/locking/lock_daemon.c index 7bc6917..ab9ca35 100644 --- a/src/locking/lock_daemon.c +++ b/src/locking/lock_daemon.c @@ -42,6 +42,7 @@ #include "rpc/virnetserver.h" #include "virrandom.h" #include "virhash.h" +#include "uuid.h" #include "locking/lock_daemon_dispatch.h" #include "locking/lock_protocol.h" @@ -64,6 +65,7 @@ struct _virLockDaemon { virLockDaemonPtr lockDaemon = NULL; static bool privileged; +static bool execRestart = false; enum { VIR_LOCK_DAEMON_ERR_NONE = 0, @@ -97,6 +99,14 @@ virLockDaemonClientNew(virNetServerClientPtr client, static void virLockDaemonClientFree(void *opaque); +static void * +virLockDaemonClientNewPostExecRestart(virNetServerClientPtr client, + virJSONValuePtr object, + void *opaque); +static virJSONValuePtr +virLockDaemonClientPreExecRestart(virNetServerClientPtr client, + void *opaque); + static void virLockDaemonFree(virLockDaemonPtr lockd) { @@ -138,7 +148,7 @@ virLockDaemonNew(void) -1, 0, NULL, NULL, virLockDaemonClientNew, - NULL, + virLockDaemonClientPreExecRestart, virLockDaemonClientFree, NULL))) goto error; @@ -158,6 +168,90 @@ error: } +static virLockDaemonPtr +virLockDaemonNewPostExecRestart(virJSONValuePtr object) +{ + virLockDaemonPtr lockd; + virJSONValuePtr child; + virJSONValuePtr lockspaces; + size_t i; + int n; + + if (VIR_ALLOC(lockd) < 0) { + virReportOOMError(); + return NULL; + } + + if (virMutexInit(&lockd->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to initialize mutex")); + VIR_FREE(lockd); + return NULL; + } + + if (!(lockd->lockspaces = virHashCreate(3, + virLockDaemonLockSpaceDataFree))) + goto error; + + if (!(child = virJSONValueObjectGet(object, "defaultLockspace"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing defaultLockspace data from JSON file")); + goto error; + } + + if (!(lockd->defaultLockspace = + virLockSpaceNewPostExecRestart(child))) + goto error; + + if (!(lockspaces = virJSONValueObjectGet(object, "lockspaces"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing lockspaces data from JSON file")); + goto error; + } + + if ((n = virJSONValueArraySize(lockspaces)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed lockspaces data from JSON file")); + goto error; + } + + for (i = 0 ; i < n ; i++) { + virLockSpacePtr lockspace; + + child = virJSONValueArrayGet(lockspaces, i); + + if (!(lockspace = virLockSpaceNewPostExecRestart(child))) + goto error; + + if (virHashAddEntry(lockd->lockspaces, + virLockSpaceGetDirectory(lockspace), + lockspace) < 0) { + virLockSpaceFree(lockspace); + } + } + + if (!(child = virJSONValueObjectGet(object, "server"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing server data from JSON file")); + goto error; + } + + if (!(lockd->srv = virNetServerNewPostExecRestart(child, + virLockDaemonClientNew, + virLockDaemonClientNewPostExecRestart, + virLockDaemonClientPreExecRestart, + virLockDaemonClientFree, + NULL))) + goto error; + + return lockd; + +error: + virLockDaemonFree(lockd); + return NULL; +} + + int virLockDaemonAddLockSpace(virLockDaemonPtr lockd, const char *path, virLockSpacePtr lockspace) @@ -442,6 +536,15 @@ virLockDaemonShutdownHandler(virNetServerPtr srv, virNetServerQuit(srv); } +static void +virLockDaemonExecRestartHandler(virNetServerPtr srv, + siginfo_t *sig ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + execRestart = true; + virNetServerQuit(srv); +} + static int virLockDaemonSetupSignals(virNetServerPtr srv) { @@ -451,6 +554,8 @@ virLockDaemonSetupSignals(virNetServerPtr srv) return -1; if (virNetServerAddSignalHandler(srv, SIGTERM, virLockDaemonShutdownHandler, NULL) < 0) return -1; + if (virNetServerAddSignalHandler(srv, SIGUSR1, virLockDaemonExecRestartHandler, NULL) < 0) + return -1; return 0; } @@ -464,6 +569,8 @@ virLockDaemonSetupNetworkingSystemD(virNetServerPtr srv) unsigned long long procid; unsigned int nfds; + VIR_DEBUG("Setting up networking from systemd"); + if (!(pidstr = getenv("LISTEN_PID"))) { VIR_DEBUG("No LISTEN_FDS from systemd"); return 0; @@ -674,6 +781,277 @@ error: } +static void * +virLockDaemonClientNewPostExecRestart(virNetServerClientPtr client, + virJSONValuePtr object, + void *opaque) +{ + virLockDaemonClientPtr priv = virLockDaemonClientNew(client, opaque); + unsigned int ownerPid; + const char *ownerUUID; + const char *ownerName; + + if (!priv) + return NULL; + + if (virJSONValueObjectGetBoolean(object, "restricted", &priv->restricted) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing restricted data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "ownerPid", &ownerPid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ownerPid data in JSON document")); + goto error; + } + priv->ownerPid = (pid_t)ownerPid; + if (virJSONValueObjectGetNumberUint(object, "ownerId", &priv->ownerId) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ownerId data in JSON document")); + goto error; + } + if (!(ownerName = virJSONValueObjectGetString(object, "ownerName"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ownerName data in JSON document")); + goto error; + } + if (!(priv->ownerName = strdup(ownerName))) { + virReportOOMError(); + goto error; + } + if (!(ownerUUID = virJSONValueObjectGetString(object, "ownerUUID"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ownerUUID data in JSON document")); + goto error; + } + if (virUUIDParse(ownerUUID, priv->ownerUUID) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ownerUUID data in JSON document")); + goto error; + } + return priv; + +error: + virLockDaemonClientFree(priv); + return NULL; +} + + +static virJSONValuePtr +virLockDaemonClientPreExecRestart(virNetServerClientPtr client ATTRIBUTE_UNUSED, + void *opaque) +{ + virLockDaemonClientPtr priv = opaque; + virJSONValuePtr object = virJSONValueNewObject(); + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (!object) + return NULL; + + if (virJSONValueObjectAppendBoolean(object, "restricted", priv->restricted) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set restricted data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "ownerPid", priv->ownerPid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set ownerPid data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "ownerId", priv->ownerId) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set ownerId data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendString(object, "ownerName", priv->ownerName) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set ownerName data in JSON document")); + goto error; + } + virUUIDFormat(priv->ownerUUID, uuidstr); + if (virJSONValueObjectAppendString(object, "ownerUUID", uuidstr) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set ownerUUID data in JSON document")); + goto error; + } + + return object; + +error: + virJSONValueFree(object); + return NULL; +} + + +#define VIR_LOCK_DAEMON_RESTART_EXEC_FILE LOCALSTATEDIR "/run/virtlockd-restart-exec.json" + +static char * +virLockDaemonGetExecRestartMagic(void) +{ + char *ret; + + if (virAsprintf(&ret, "%lld", + (long long int)getpid()) < 0) { + virReportOOMError(); + return NULL; + } + + return ret; +} + + +static int +virLockDaemonPostExecRestart(void) +{ + const char *gotmagic; + char *wantmagic = NULL; + int ret = -1; + char *state = NULL; + virJSONValuePtr object = NULL; + + VIR_DEBUG("Running post-restart exec"); + + if (!virFileExists(VIR_LOCK_DAEMON_RESTART_EXEC_FILE)) { + VIR_DEBUG("No restart file %s present", + VIR_LOCK_DAEMON_RESTART_EXEC_FILE); + ret = 0; + goto cleanup; + } + + if (virFileReadAll(VIR_LOCK_DAEMON_RESTART_EXEC_FILE, + 1024 * 1024 * 10, /* 10 MB */ + &state) < 0) + goto cleanup; + + VIR_DEBUG("Loading state %s", state); + + if (!(object = virJSONValueFromString(state))) + goto cleanup; + + gotmagic = virJSONValueObjectGetString(object, "magic"); + if (!gotmagic) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing magic data in JSON document")); + goto cleanup; + } + + if (!(wantmagic = virLockDaemonGetExecRestartMagic())) + goto cleanup; + + if (STRNEQ(gotmagic, wantmagic)) { + VIR_WARN("Found restart exec file with old magic %s vs wanted %s", + gotmagic, wantmagic); + ret = 0; + goto cleanup; + } + + if (!(lockDaemon = virLockDaemonNewPostExecRestart(object))) + goto cleanup; + + ret = 1; + +cleanup: + unlink(VIR_LOCK_DAEMON_RESTART_EXEC_FILE); + VIR_FREE(wantmagic); + VIR_FREE(state); + virJSONValueFree(object); + return ret; +} + + +static int +virLockDaemonPreExecRestart(virNetServerPtr srv, + char **argv) +{ + virJSONValuePtr child; + char *state = NULL; + int ret = -1; + virJSONValuePtr object; + char *magic; + virHashKeyValuePairPtr pairs = NULL, tmp; + virJSONValuePtr lockspaces; + + VIR_DEBUG("Running pre-restart exec"); + + if (!(object = virJSONValueNewObject())) + goto cleanup; + + if (!(child = virNetServerPreExecRestart(srv))) + goto cleanup; + + if (virJSONValueObjectAppend(object, "server", child) < 0) { + virJSONValueFree(child); + goto cleanup; + } + + if (!(child = virLockSpacePreExecRestart(lockDaemon->defaultLockspace))) + goto cleanup; + + if (virJSONValueObjectAppend(object, "defaultLockspace", child) < 0) { + virJSONValueFree(child); + goto cleanup; + } + + if (!(lockspaces = virJSONValueNewArray())) + goto cleanup; + if (virJSONValueObjectAppend(object, "lockspaces", lockspaces) < 0) { + virJSONValueFree(lockspaces); + goto cleanup; + } + + + tmp = pairs = virHashGetItems(lockDaemon->lockspaces, NULL); + while (tmp && tmp->key) { + virLockSpacePtr lockspace = (virLockSpacePtr)tmp->value; + + if (!(child = virLockSpacePreExecRestart(lockspace))) + goto cleanup; + + if (virJSONValueArrayAppend(lockspaces, child) < 0) { + virJSONValueFree(child); + goto cleanup; + } + + tmp++; + } + + if (!(magic = virLockDaemonGetExecRestartMagic())) + goto cleanup; + + if (virJSONValueObjectAppendString(object, "magic", magic) < 0) { + VIR_FREE(magic); + goto cleanup; + } + + if (!(state = virJSONValueToString(object, true))) + goto cleanup; + + VIR_DEBUG("Saving state %s", state); + + if (virFileWriteStr(VIR_LOCK_DAEMON_RESTART_EXEC_FILE, + state, 0700) < 0) { + virReportSystemError(errno, + _("Unable to save state file %s"), + VIR_LOCK_DAEMON_RESTART_EXEC_FILE); + goto cleanup; + } + + if (execv(argv[0], argv) < 0) { + virReportSystemError(errno, "%s", + _("Unable to restart self")); + goto cleanup; + } + + abort(); /* This should be impossible to reach */ + +cleanup: + VIR_FREE(pairs); + VIR_FREE(state); + virJSONValueFree(object); + return ret; +} + + static void virLockDaemonUsage(const char *argv0) { @@ -866,22 +1244,30 @@ int main(int argc, char **argv) { goto cleanup; } - - if (!(lockDaemon = virLockDaemonNew())) { + if ((rv = virLockDaemonPostExecRestart()) < 0) { ret = VIR_LOCK_DAEMON_ERR_INIT; goto cleanup; } - if ((rv = virLockDaemonSetupNetworkingSystemD(lockDaemon->srv)) < 0) { - ret = VIR_LOCK_DAEMON_ERR_NETWORK; - goto cleanup; - } + /* rv == 1, means we setup everything from saved state, + * so we only setup stuff from scratch if rv == 0 */ + if (rv == 0) { + if (!(lockDaemon = virLockDaemonNew())) { + ret = VIR_LOCK_DAEMON_ERR_INIT; + goto cleanup; + } - /* Only do this, if systemd did not pass a FD */ - if (rv == 0 && - virLockDaemonSetupNetworkingNative(lockDaemon->srv, sock_file) < 0) { - ret = VIR_LOCK_DAEMON_ERR_NETWORK; - goto cleanup; + if ((rv = virLockDaemonSetupNetworkingSystemD(lockDaemon->srv)) < 0) { + ret = VIR_LOCK_DAEMON_ERR_NETWORK; + goto cleanup; + } + + /* Only do this, if systemd did not pass a FD */ + if (rv == 0 && + virLockDaemonSetupNetworkingNative(lockDaemon->srv, sock_file) < 0) { + ret = VIR_LOCK_DAEMON_ERR_NETWORK; + goto cleanup; + } } if ((virLockDaemonSetupSignals(lockDaemon->srv)) < 0) { @@ -921,7 +1307,12 @@ int main(int argc, char **argv) { virNetServerUpdateServices(lockDaemon->srv, true); virNetServerRun(lockDaemon->srv); - ret = 0; + + if (execRestart && + virLockDaemonPreExecRestart(lockDaemon->srv, argv) < 0) + ret = -1; + else + ret = 0; cleanup: virObjectUnref(lockProgram); -- 1.7.11.2

On 12.09.2012 18:29, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
The virtlockd daemon maintains file locks on behalf of libvirtd and any VMs it is running. These file locks must be held for as long as any VM is running. If virtlockd itself ever quits, then it is expected that a node would be fenced/rebooted. Thus to allow for software upgrads on live systemd, virtlockd needs the
s/upgrads/upgrades/
ability to re-exec() itself.
Upon receipt of SIGUSR1, virtlockd will save its current live state out to a file /var/run/virtlockd-restart-exec.json It then re-exec()'s itself with exactly the same argv as it originally had, and loads the state file, reconstructing any objects as appropriate.
The state file contains information about all locks held and all network services and clients currently active. An example state document is
{ "server": { "min_workers": 1, "max_workers": 20, "priority_workers": 0, "max_clients": 20, "keepaliveInterval": 4294967295, "keepaliveCount": 0, "keepaliveRequired": false, "services": [ { "auth": 0, "readonly": false, "nrequests_client_max": 1, "socks": [ { "fd": 6, "errfd": -1, "pid": 0, "isClient": false } ] } ], "clients": [ { "auth": 0, "readonly": false, "nrequests_max": 1, "sock": { "fd": 9, "errfd": -1, "pid": 0, "isClient": true }, "privateData": { "restricted": true, "ownerPid": 1722, "ownerId": 6, "ownerName": "f18x86_64", "ownerUUID": "97586ba9-df27-9459-c806-f016c8bbd224" } }, { "auth": 0, "readonly": false, "nrequests_max": 1, "sock": { "fd": 10, "errfd": -1, "pid": 0, "isClient": true }, "privateData": { "restricted": true, "ownerPid": 1784, "ownerId": 7, "ownerName": "f16x86_64", "ownerUUID": "7b8e5e42-b875-61e9-b981-91ad8fa46979" } } ] }, "defaultLockspace": { "resources": [ { "name": "/var/lib/libvirt/images/f16x86_64.raw", "path": "/var/lib/libvirt/images/f16x86_64.raw", "fd": 14, "lockHeld": true, "flags": 0, "owners": [ 1784 ] }, { "name": "/var/lib/libvirt/images/shared.img", "path": "/var/lib/libvirt/images/shared.img", "fd": 12, "lockHeld": true, "flags": 1, "owners": [ 1722, 1784 ] }, { "name": "/var/lib/libvirt/images/f18x86_64.img", "path": "/var/lib/libvirt/images/f18x86_64.img", "fd": 11, "lockHeld": true, "flags": 0, "owners": [ 1722 ] } ] }, "lockspaces": [
], "magic": "30199" }
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- libvirt.spec.in | 5 +- src/locking/lock_daemon.c | 417 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 408 insertions(+), 14 deletions(-)
ACK with one nit
diff --git a/libvirt.spec.in b/libvirt.spec.in index 62938e5..71f838b 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1470,7 +1470,10 @@ fi /bin/systemctl daemon-reload >/dev/null 2>&1 || : if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall - /bin/systemctl try-restart virtlockd.service >/dev/null 2>&1 || : + /bin/systemctl status virtlockd.service >/dev/null 2>&1 + if [ $? = 1 ] ; then + /bin/systemctl kill --signal=USR1 virtlockd.service >/dev/null 2>&1 || : + fi /bin/systemctl try-restart libvirtd.service >/dev/null 2>&1 || : fi %endif diff --git a/src/locking/lock_daemon.c b/src/locking/lock_daemon.c index 7bc6917..ab9ca35 100644 --- a/src/locking/lock_daemon.c +++ b/src/locking/lock_daemon.c @@ -42,6 +42,7 @@ #include "rpc/virnetserver.h" #include "virrandom.h" #include "virhash.h" +#include "uuid.h"
#include "locking/lock_daemon_dispatch.h" #include "locking/lock_protocol.h" @@ -64,6 +65,7 @@ struct _virLockDaemon { virLockDaemonPtr lockDaemon = NULL;
static bool privileged; +static bool execRestart = false;
enum { VIR_LOCK_DAEMON_ERR_NONE = 0, @@ -97,6 +99,14 @@ virLockDaemonClientNew(virNetServerClientPtr client, static void virLockDaemonClientFree(void *opaque);
+static void * +virLockDaemonClientNewPostExecRestart(virNetServerClientPtr client, + virJSONValuePtr object, + void *opaque); +static virJSONValuePtr +virLockDaemonClientPreExecRestart(virNetServerClientPtr client, + void *opaque); + static void virLockDaemonFree(virLockDaemonPtr lockd) { @@ -138,7 +148,7 @@ virLockDaemonNew(void) -1, 0, NULL, NULL, virLockDaemonClientNew, - NULL, + virLockDaemonClientPreExecRestart, virLockDaemonClientFree, NULL))) goto error; @@ -158,6 +168,90 @@ error: }
+static virLockDaemonPtr +virLockDaemonNewPostExecRestart(virJSONValuePtr object) +{ + virLockDaemonPtr lockd; + virJSONValuePtr child; + virJSONValuePtr lockspaces; + size_t i; + int n; + + if (VIR_ALLOC(lockd) < 0) { + virReportOOMError(); + return NULL; + } + + if (virMutexInit(&lockd->lock) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to initialize mutex")); + VIR_FREE(lockd); + return NULL; + } + + if (!(lockd->lockspaces = virHashCreate(3, + virLockDaemonLockSpaceDataFree)))
Again, s/3/{macro}/
+ goto error; + + if (!(child = virJSONValueObjectGet(object, "defaultLockspace"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing defaultLockspace data from JSON file")); + goto error; + } + + if (!(lockd->defaultLockspace = + virLockSpaceNewPostExecRestart(child))) + goto error; + + if (!(lockspaces = virJSONValueObjectGet(object, "lockspaces"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing lockspaces data from JSON file")); + goto error; + } + + if ((n = virJSONValueArraySize(lockspaces)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Malformed lockspaces data from JSON file")); + goto error; + } + + for (i = 0 ; i < n ; i++) { + virLockSpacePtr lockspace; + + child = virJSONValueArrayGet(lockspaces, i); + + if (!(lockspace = virLockSpaceNewPostExecRestart(child))) + goto error; + + if (virHashAddEntry(lockd->lockspaces, + virLockSpaceGetDirectory(lockspace), + lockspace) < 0) { + virLockSpaceFree(lockspace); + } + } + + if (!(child = virJSONValueObjectGet(object, "server"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing server data from JSON file")); + goto error; + } + + if (!(lockd->srv = virNetServerNewPostExecRestart(child, + virLockDaemonClientNew, + virLockDaemonClientNewPostExecRestart, + virLockDaemonClientPreExecRestart, + virLockDaemonClientFree, + NULL))) + goto error; + + return lockd; + +error: + virLockDaemonFree(lockd); + return NULL; +} + + int virLockDaemonAddLockSpace(virLockDaemonPtr lockd, const char *path, virLockSpacePtr lockspace) @@ -442,6 +536,15 @@ virLockDaemonShutdownHandler(virNetServerPtr srv, virNetServerQuit(srv); }
+static void +virLockDaemonExecRestartHandler(virNetServerPtr srv, + siginfo_t *sig ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + execRestart = true; + virNetServerQuit(srv); +} + static int virLockDaemonSetupSignals(virNetServerPtr srv) { @@ -451,6 +554,8 @@ virLockDaemonSetupSignals(virNetServerPtr srv) return -1; if (virNetServerAddSignalHandler(srv, SIGTERM, virLockDaemonShutdownHandler, NULL) < 0) return -1; + if (virNetServerAddSignalHandler(srv, SIGUSR1, virLockDaemonExecRestartHandler, NULL) < 0) + return -1; return 0; }
@@ -464,6 +569,8 @@ virLockDaemonSetupNetworkingSystemD(virNetServerPtr srv) unsigned long long procid; unsigned int nfds;
+ VIR_DEBUG("Setting up networking from systemd"); + if (!(pidstr = getenv("LISTEN_PID"))) { VIR_DEBUG("No LISTEN_FDS from systemd"); return 0; @@ -674,6 +781,277 @@ error: }
+static void * +virLockDaemonClientNewPostExecRestart(virNetServerClientPtr client, + virJSONValuePtr object, + void *opaque) +{ + virLockDaemonClientPtr priv = virLockDaemonClientNew(client, opaque); + unsigned int ownerPid; + const char *ownerUUID; + const char *ownerName; + + if (!priv) + return NULL; + + if (virJSONValueObjectGetBoolean(object, "restricted", &priv->restricted) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing restricted data in JSON document")); + goto error; + } + if (virJSONValueObjectGetNumberUint(object, "ownerPid", &ownerPid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ownerPid data in JSON document")); + goto error; + } + priv->ownerPid = (pid_t)ownerPid; + if (virJSONValueObjectGetNumberUint(object, "ownerId", &priv->ownerId) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ownerId data in JSON document")); + goto error; + } + if (!(ownerName = virJSONValueObjectGetString(object, "ownerName"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ownerName data in JSON document")); + goto error; + } + if (!(priv->ownerName = strdup(ownerName))) { + virReportOOMError(); + goto error; + } + if (!(ownerUUID = virJSONValueObjectGetString(object, "ownerUUID"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ownerUUID data in JSON document")); + goto error; + } + if (virUUIDParse(ownerUUID, priv->ownerUUID) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ownerUUID data in JSON document")); + goto error; + } + return priv; + +error: + virLockDaemonClientFree(priv); + return NULL; +} + + +static virJSONValuePtr +virLockDaemonClientPreExecRestart(virNetServerClientPtr client ATTRIBUTE_UNUSED, + void *opaque) +{ + virLockDaemonClientPtr priv = opaque; + virJSONValuePtr object = virJSONValueNewObject(); + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (!object) + return NULL; + + if (virJSONValueObjectAppendBoolean(object, "restricted", priv->restricted) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set restricted data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "ownerPid", priv->ownerPid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set ownerPid data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendNumberUint(object, "ownerId", priv->ownerId) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set ownerId data in JSON document")); + goto error; + } + if (virJSONValueObjectAppendString(object, "ownerName", priv->ownerName) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set ownerName data in JSON document")); + goto error; + } + virUUIDFormat(priv->ownerUUID, uuidstr); + if (virJSONValueObjectAppendString(object, "ownerUUID", uuidstr) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot set ownerUUID data in JSON document")); + goto error; + } + + return object; + +error: + virJSONValueFree(object); + return NULL; +} + + +#define VIR_LOCK_DAEMON_RESTART_EXEC_FILE LOCALSTATEDIR "/run/virtlockd-restart-exec.json" + +static char * +virLockDaemonGetExecRestartMagic(void) +{ + char *ret; + + if (virAsprintf(&ret, "%lld", + (long long int)getpid()) < 0) { + virReportOOMError(); + return NULL; + } + + return ret; +} + + +static int +virLockDaemonPostExecRestart(void) +{ + const char *gotmagic; + char *wantmagic = NULL; + int ret = -1; + char *state = NULL; + virJSONValuePtr object = NULL; + + VIR_DEBUG("Running post-restart exec"); + + if (!virFileExists(VIR_LOCK_DAEMON_RESTART_EXEC_FILE)) { + VIR_DEBUG("No restart file %s present", + VIR_LOCK_DAEMON_RESTART_EXEC_FILE); + ret = 0; + goto cleanup; + } + + if (virFileReadAll(VIR_LOCK_DAEMON_RESTART_EXEC_FILE, + 1024 * 1024 * 10, /* 10 MB */ + &state) < 0) + goto cleanup; + + VIR_DEBUG("Loading state %s", state); + + if (!(object = virJSONValueFromString(state))) + goto cleanup; + + gotmagic = virJSONValueObjectGetString(object, "magic"); + if (!gotmagic) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing magic data in JSON document")); + goto cleanup; + } + + if (!(wantmagic = virLockDaemonGetExecRestartMagic())) + goto cleanup; + + if (STRNEQ(gotmagic, wantmagic)) { + VIR_WARN("Found restart exec file with old magic %s vs wanted %s", + gotmagic, wantmagic); + ret = 0; + goto cleanup; + } + + if (!(lockDaemon = virLockDaemonNewPostExecRestart(object))) + goto cleanup; + + ret = 1; + +cleanup: + unlink(VIR_LOCK_DAEMON_RESTART_EXEC_FILE); + VIR_FREE(wantmagic); + VIR_FREE(state); + virJSONValueFree(object); + return ret; +} + + +static int +virLockDaemonPreExecRestart(virNetServerPtr srv, + char **argv) +{ + virJSONValuePtr child; + char *state = NULL; + int ret = -1; + virJSONValuePtr object; + char *magic; + virHashKeyValuePairPtr pairs = NULL, tmp; + virJSONValuePtr lockspaces; + + VIR_DEBUG("Running pre-restart exec"); + + if (!(object = virJSONValueNewObject())) + goto cleanup; + + if (!(child = virNetServerPreExecRestart(srv))) + goto cleanup; + + if (virJSONValueObjectAppend(object, "server", child) < 0) { + virJSONValueFree(child); + goto cleanup; + } + + if (!(child = virLockSpacePreExecRestart(lockDaemon->defaultLockspace))) + goto cleanup; + + if (virJSONValueObjectAppend(object, "defaultLockspace", child) < 0) { + virJSONValueFree(child); + goto cleanup; + } + + if (!(lockspaces = virJSONValueNewArray())) + goto cleanup; + if (virJSONValueObjectAppend(object, "lockspaces", lockspaces) < 0) { + virJSONValueFree(lockspaces); + goto cleanup; + } + + + tmp = pairs = virHashGetItems(lockDaemon->lockspaces, NULL); + while (tmp && tmp->key) { + virLockSpacePtr lockspace = (virLockSpacePtr)tmp->value; + + if (!(child = virLockSpacePreExecRestart(lockspace))) + goto cleanup; + + if (virJSONValueArrayAppend(lockspaces, child) < 0) { + virJSONValueFree(child); + goto cleanup; + } + + tmp++; + } + + if (!(magic = virLockDaemonGetExecRestartMagic())) + goto cleanup; + + if (virJSONValueObjectAppendString(object, "magic", magic) < 0) { + VIR_FREE(magic); + goto cleanup; + } + + if (!(state = virJSONValueToString(object, true))) + goto cleanup; + + VIR_DEBUG("Saving state %s", state); + + if (virFileWriteStr(VIR_LOCK_DAEMON_RESTART_EXEC_FILE, + state, 0700) < 0) { + virReportSystemError(errno, + _("Unable to save state file %s"), + VIR_LOCK_DAEMON_RESTART_EXEC_FILE); + goto cleanup; + } + + if (execv(argv[0], argv) < 0) { + virReportSystemError(errno, "%s", + _("Unable to restart self")); + goto cleanup; + } + + abort(); /* This should be impossible to reach */ + +cleanup: + VIR_FREE(pairs); + VIR_FREE(state); + virJSONValueFree(object); + return ret; +} + + static void virLockDaemonUsage(const char *argv0) { @@ -866,22 +1244,30 @@ int main(int argc, char **argv) { goto cleanup; }
- - if (!(lockDaemon = virLockDaemonNew())) { + if ((rv = virLockDaemonPostExecRestart()) < 0) { ret = VIR_LOCK_DAEMON_ERR_INIT; goto cleanup; }
- if ((rv = virLockDaemonSetupNetworkingSystemD(lockDaemon->srv)) < 0) { - ret = VIR_LOCK_DAEMON_ERR_NETWORK; - goto cleanup; - } + /* rv == 1, means we setup everything from saved state, + * so we only setup stuff from scratch if rv == 0 */ + if (rv == 0) { + if (!(lockDaemon = virLockDaemonNew())) { + ret = VIR_LOCK_DAEMON_ERR_INIT; + goto cleanup; + }
- /* Only do this, if systemd did not pass a FD */ - if (rv == 0 && - virLockDaemonSetupNetworkingNative(lockDaemon->srv, sock_file) < 0) { - ret = VIR_LOCK_DAEMON_ERR_NETWORK; - goto cleanup; + if ((rv = virLockDaemonSetupNetworkingSystemD(lockDaemon->srv)) < 0) { + ret = VIR_LOCK_DAEMON_ERR_NETWORK; + goto cleanup; + } + + /* Only do this, if systemd did not pass a FD */ + if (rv == 0 && + virLockDaemonSetupNetworkingNative(lockDaemon->srv, sock_file) < 0) { + ret = VIR_LOCK_DAEMON_ERR_NETWORK; + goto cleanup; + } }
if ((virLockDaemonSetupSignals(lockDaemon->srv)) < 0) { @@ -921,7 +1307,12 @@ int main(int argc, char **argv) {
virNetServerUpdateServices(lockDaemon->srv, true); virNetServerRun(lockDaemon->srv); - ret = 0; + + if (execRestart && + virLockDaemonPreExecRestart(lockDaemon->srv, argv) < 0) + ret = -1; + else + ret = 0;
cleanup: virObjectUnref(lockProgram);

From: "Daniel P. Berrange" <berrange@redhat.com> This adds a 'lockd' lock driver which is just a client which talks to the lockd daemon to perform all locking. This will be the default lock driver for any hypervisor which needs one. * src/Makefile.am: Add lockd.so plugin * src/locking/lock_driver_lockd.c: Lockd driver impl Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 26 +- src/locking/lock_driver_lockd.c | 561 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 584 insertions(+), 4 deletions(-) create mode 100644 src/locking/lock_driver_lockd.c diff --git a/po/POTFILES.in b/po/POTFILES.in index 6b9a7af..663e37b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -46,6 +46,7 @@ src/libvirt.c src/libvirt-qemu.c src/locking/lock_daemon.c src/locking/lock_daemon_dispatch.c +src/locking/lock_driver_lockd.c src/locking/lock_driver_sanlock.c src/locking/lock_manager.c src/lxc/lxc_cgroup.c diff --git a/src/Makefile.am b/src/Makefile.am index b402297..ec5014a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -158,6 +158,10 @@ LOCK_DAEMON_GENERATED = \ BUILT_SOURCES += $(LOCK_DAEMON_GENERATED) MAINTAINERCLEANFILES += $(LOCK_DAEMON_GENERATED) +LOCK_DRIVER_LOCKD_SOURCES = \ + locking/lock_driver_lockd.c \ + $(NULL) + LOCK_DAEMON_SOURCES = \ locking/lock_daemon.h \ locking/lock_daemon.c \ @@ -1501,7 +1505,22 @@ libvirt_qemu_la_CFLAGS = $(AM_CFLAGS) libvirt_qemu_la_LIBADD = libvirt.la $(CYGWIN_EXTRA_LIBADD) EXTRA_DIST += $(LIBVIRT_QEMU_SYMBOL_FILE) +lockdriverdir = $(libdir)/libvirt/lock-driver +lockdriver_LTLIBRARIES = + if WITH_LIBVIRTD +lockdriver_LTLIBRARIES += lockd.la +lockd_la_SOURCES = \ + $(LOCK_DRIVER_LOCKD_SOURCES) \ + $(LOCK_PROTOCOL_GENERATED) \ + $(NULL) +lockd_la_CFLAGS = $(AM_CFLAGS) +lockd_la_LDFLAGS = -module -avoid-version +lockd_la_LIBADD = ../gnulib/lib/libgnu.la libvirt-net-rpc.la libvirt-net-rpc-client.la +if WITH_DTRACE_PROBES +lockd_la_LIBADD += libvirt_probes.lo +endif + sbin_PROGRAMS = virtlockd virtlockd_SOURCES = \ @@ -1529,7 +1548,8 @@ virtlockd_LDADD += libvirt_probes.lo endif else -EXTRA_DIST += $(LOCK_DAEMON_SOURCES) +EXTRA_DIST += $(LOCK_DAEMON_SOURCES) \ + $(LOCK_DRIVER_LOCKD_SOURCES) endif EXTRA_DIST += locking/virtlockd.sysconf @@ -1623,9 +1643,7 @@ virtlockd.socket: locking/virtlockd.socket.in $(top_builddir)/config.status if HAVE_SANLOCK -lockdriverdir = $(libdir)/libvirt/lock-driver -lockdriver_LTLIBRARIES = sanlock.la - +lockdriver_LTLIBRARIES += sanlock.la sanlock_la_SOURCES = $(LOCK_DRIVER_SANLOCK_SOURCES) sanlock_la_CFLAGS = $(AM_CFLAGS) sanlock_la_LDFLAGS = -module -avoid-version diff --git a/src/locking/lock_driver_lockd.c b/src/locking/lock_driver_lockd.c new file mode 100644 index 0000000..462996b --- /dev/null +++ b/src/locking/lock_driver_lockd.c @@ -0,0 +1,561 @@ +/* + * lock_driver_lockd.c: A lock driver which locks nothing + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include "lock_driver.h" +#include "memory.h" +#include "logging.h" +#include "uuid.h" +#include "util.h" +#include "virfile.h" +#include "virterror_internal.h" +#include "rpc/virnetclient.h" +#include "lock_protocol.h" +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_LOCKING + +#define virLockError(code, ...) \ + virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +typedef struct _virLockManagerLockDaemonPrivate virLockManagerLockDaemonPrivate; +typedef virLockManagerLockDaemonPrivate *virLockManagerLockDaemonPrivatePtr; + +typedef struct _virLockManagerLockDaemonResource virLockManagerLockDaemonResource; +typedef virLockManagerLockDaemonResource *virLockManagerLockDaemonResourcePtr; + +struct _virLockManagerLockDaemonResource { + char *lockspace; + char *name; + unsigned int flags; +}; + +struct _virLockManagerLockDaemonPrivate { + unsigned char uuid[VIR_UUID_BUFLEN]; + char *name; + int id; + pid_t pid; + + size_t nresources; + virLockManagerLockDaemonResourcePtr resources; +}; + + +#define VIRTLOCKD_PATH SBINDIR "/virtlockd" + +static const char * +virLockManagerLockDaemonFindDaemon(void) +{ + const char *customDaemon = getenv("VIRTLOCKD_PATH"); + + if (customDaemon) + return customDaemon; + + if (virFileIsExecutable(VIRTLOCKD_PATH)) + return VIRTLOCKD_PATH; + + return NULL; +} + +static int virLockManagerLockDaemonInit(unsigned int version, + const char *configFile, + unsigned int flags) +{ + VIR_DEBUG("version=%u configFile=%s flags=%x", version, NULLSTR(configFile), flags); + + return 0; +} + +static int virLockManagerLockDaemonDeinit(void) +{ + VIR_DEBUG(" "); + + return 0; +} + +static void virLockManagerLockDaemonFree(virLockManagerPtr lock) +{ + virLockManagerLockDaemonPrivatePtr priv = lock->privateData; + size_t i; + + if (!priv) + return; + + lock->privateData = NULL; + + for (i = 0 ; i < priv->nresources ; i++) { + VIR_FREE(priv->resources[i].lockspace); + VIR_FREE(priv->resources[i].name); + } + VIR_FREE(priv->resources); + + VIR_FREE(priv->name); + + VIR_FREE(priv); +} + + +static char *virLockManagerLockDaemonPath(bool privileged) +{ + char *path; + if (privileged) { + if (!(path = strdup(LOCALSTATEDIR "/run/libvirt/virtlockd/virtlockd.sock"))) { + virReportOOMError(); + return NULL; + } + } else { + char *userdir; + if (!(userdir = virGetUserDirectory())) + return NULL; + + if (virAsprintf(&path, "%s/.libvirt/virtlockd/virtlockd.sock", userdir) < 0) { + virReportOOMError(); + } + VIR_FREE(userdir); + } + return path; +} + + +static int virLockManagerLockDaemonNew(virLockManagerPtr lock, + unsigned int type, + size_t nparams, + virLockManagerParamPtr params, + unsigned int flags) +{ + virLockManagerLockDaemonPrivatePtr priv; + size_t i; + + virCheckFlags(VIR_LOCK_MANAGER_USES_STATE, -1); + + if (VIR_ALLOC(priv) < 0) { + virReportOOMError(); + return -1; + } + lock->privateData = priv; + + switch (type) { + case VIR_LOCK_MANAGER_OBJECT_TYPE_DOMAIN: + for (i = 0 ; i < nparams ; i++) { + if (STREQ(params[i].key, "uuid")) { + memcpy(priv->uuid, params[i].value.uuid, VIR_UUID_BUFLEN); + } else if (STREQ(params[i].key, "name")) { + if (!(priv->name = strdup(params[i].value.str))) { + virReportOOMError(); + return -1; + } + } else if (STREQ(params[i].key, "id")) { + priv->id = params[i].value.i; + } else if (STREQ(params[i].key, "pid")) { + priv->pid = params[i].value.i; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected parameter %s for object"), + params[i].key); + } + } + if (priv->id == 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ID parameter for domain object")); + return -1; + } + if (priv->pid == 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing PID parameter for domain object")); + return -1; + } + if (!priv->name) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing name parameter for domain object")); + return -1; + } + if (!virUUIDIsValid(priv->uuid)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing UUID parameter for domain object")); + return -1; + } + break; + + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown lock manager object type %d"), + type); + return -1; + } + + return 0; +} + + +static int virLockManagerLockDaemonAddResource(virLockManagerPtr lock, + unsigned int type, + const char *name, + size_t nparams, + virLockManagerParamPtr params, + unsigned int flags) +{ + virLockManagerLockDaemonPrivatePtr priv = lock->privateData; + char *newName; + char *newLockspace = NULL; + + virCheckFlags(VIR_LOCK_MANAGER_RESOURCE_READONLY | + VIR_LOCK_MANAGER_RESOURCE_SHARED, -1); + + if (flags & VIR_LOCK_MANAGER_RESOURCE_READONLY) + return 0; + + switch (type) { + case VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK: + if (params || nparams) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpected parameters for disk resource")); + return -1; + } + if (!(newLockspace = strdup(""))) { + virReportOOMError(); + return -1; + } + break; + case VIR_LOCK_MANAGER_RESOURCE_TYPE_LEASE: { + size_t i; + char *path = NULL; + char *lockspace = NULL; + for (i = 0 ; i < nparams ; i++) { + if (STREQ(params[i].key, "offset")) { + if (params[i].value.ul != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Offset must be zero for this lock manager")); + return -1; + } + } else if (STREQ(params[i].key, "lockspace")) { + lockspace = params[i].value.str; + } else if (STREQ(params[i].key, "path")) { + path = params[i].value.str; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected parameter %s for lease resource"), + params[i].key); + return -1; + } + } + if (!path || !lockspace) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing path or lockspace for lease resource")); + return -1; + } + if (virAsprintf(&newLockspace, "%s/%s", + path, lockspace) < 0) { + virReportOOMError(); + return -1; + } + } break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown lock manager object type %d"), + type); + return -1; + } + + if (!(newName = strdup(name))) + goto no_memory; + + if (VIR_EXPAND_N(priv->resources, priv->nresources, 1) < 0) + goto no_memory; + + priv->resources[priv->nresources-1].lockspace = newLockspace; + priv->resources[priv->nresources-1].name = newName; + + if (flags & VIR_LOCK_MANAGER_RESOURCE_SHARED) + priv->resources[priv->nresources-1].flags |= + VIR_LOCK_SPACE_PROTOCOL_ACQUIRE_RESOURCE_SHARED; + + return 0; + +no_memory: + virReportOOMError(); + VIR_FREE(newName); + return -1; +} + + +static int +virLockManagerLockDaemonConnectionRegister(virLockManagerPtr lock, + virNetClientPtr client, + virNetClientProgramPtr program, + int *counter) +{ + virLockManagerLockDaemonPrivatePtr priv = lock->privateData; + virLockSpaceProtocolRegisterArgs args; + int rv = -1; + + memset(&args, 0, sizeof(args)); + + args.flags = 0; + memcpy(args.owner.uuid, priv->uuid, VIR_UUID_BUFLEN); + args.owner.name = priv->name; + args.owner.id = priv->id; + args.owner.pid = priv->pid; + + if (virNetClientProgramCall(program, + client, + (*counter)++, + VIR_LOCK_SPACE_PROTOCOL_PROC_REGISTER, + 0, NULL, NULL, NULL, + (xdrproc_t)xdr_virLockSpaceProtocolRegisterArgs, (char*)&args, + (xdrproc_t)xdr_void, NULL) < 0) + goto cleanup; + + rv = 0; + +cleanup: + return rv; +} + + +static int +virLockManagerLockDaemonConnectionRestrict(virLockManagerPtr lock ATTRIBUTE_UNUSED, + virNetClientPtr client, + virNetClientProgramPtr program, + int *counter) +{ + virLockSpaceProtocolRestrictArgs args; + int rv = -1; + + memset(&args, 0, sizeof(args)); + + args.flags = 0; + + if (virNetClientProgramCall(program, + client, + (*counter)++, + VIR_LOCK_SPACE_PROTOCOL_PROC_RESTRICT, + 0, NULL, NULL, NULL, + (xdrproc_t)xdr_virLockSpaceProtocolRestrictArgs, (char*)&args, + (xdrproc_t)xdr_void, NULL) < 0) + goto cleanup; + + rv = 0; + +cleanup: + return rv; +} + + +static virNetClientPtr virLockManagerLockDaemonConnectionNew(bool privileged, + virNetClientProgramPtr *prog) +{ + virNetClientPtr client = NULL; + char *lockdpath; + const char *daemonPath = NULL; + + *prog = NULL; + + if (!(lockdpath = virLockManagerLockDaemonPath(privileged))) + goto error; + + if (!privileged) + daemonPath = virLockManagerLockDaemonFindDaemon(); + + if (!(client = virNetClientNewUNIX(lockdpath, + daemonPath != NULL, + daemonPath))) + goto error; + + if (!(*prog = virNetClientProgramNew(VIR_LOCK_SPACE_PROTOCOL_PROGRAM, + VIR_LOCK_SPACE_PROTOCOL_PROGRAM_VERSION, + NULL, + 0, + NULL))) + goto error; + + if (virNetClientAddProgram(client, *prog) < 0) + goto error; + + VIR_FREE(lockdpath); + + return client; + +error: + VIR_FREE(lockdpath); + virNetClientClose(client); + virObjectUnref(client); + virObjectUnref(*prog); + return NULL; +} + + +static virNetClientPtr +virLockManagerLockDaemonConnect(virLockManagerPtr lock, + virNetClientProgramPtr *program, + int *counter) +{ + virNetClientPtr client; + + if (!(client = virLockManagerLockDaemonConnectionNew(getuid() == 0, program))) + return NULL; + + if (virLockManagerLockDaemonConnectionRegister(lock, + client, + *program, + counter) < 0) + goto error; + + return client; + +error: + virNetClientClose(client); + virObjectUnref(client); + return NULL; +} + + +static int virLockManagerLockDaemonAcquire(virLockManagerPtr lock, + const char *state ATTRIBUTE_UNUSED, + unsigned int flags, + int *fd) +{ + virNetClientPtr client = NULL; + virNetClientProgramPtr program = NULL; + int counter = 0; + int rv = -1; + virLockManagerLockDaemonPrivatePtr priv = lock->privateData; + + virCheckFlags(VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY | + VIR_LOCK_MANAGER_ACQUIRE_RESTRICT, -1); + + if (!(client = virLockManagerLockDaemonConnect(lock, &program, &counter))) + goto cleanup; + + if (fd && + (*fd = virNetClientDupFD(client, false)) < 0) + goto cleanup; + + if (!(flags & VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY)) { + size_t i; + for (i = 0 ; i < priv->nresources ; i++) { + virLockSpaceProtocolAcquireResourceArgs args; + + memset(&args, 0, sizeof(args)); + + if (priv->resources[i].lockspace) + args.path = priv->resources[i].lockspace; + args.name = priv->resources[i].name; + args.flags = priv->resources[i].flags; + + if (virNetClientProgramCall(program, + client, + counter++, + VIR_LOCK_SPACE_PROTOCOL_PROC_ACQUIRE_RESOURCE, + 0, NULL, NULL, NULL, + (xdrproc_t)xdr_virLockSpaceProtocolAcquireResourceArgs, &args, + (xdrproc_t)xdr_void, NULL) < 0) + goto cleanup; + } + } + + if ((flags & VIR_LOCK_MANAGER_ACQUIRE_RESTRICT) && + virLockManagerLockDaemonConnectionRestrict(lock, client, program, &counter) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv != 0 && fd) + VIR_FORCE_CLOSE(*fd); + virNetClientClose(client); + virObjectUnref(client); + virObjectUnref(program); + + return rv; +} + +static int virLockManagerLockDaemonRelease(virLockManagerPtr lock, + char **state, + unsigned int flags) +{ + virNetClientPtr client = NULL; + virNetClientProgramPtr program = NULL; + int counter = 0; + virLockSpaceProtocolReleaseResourceArgs args; + int rv = -1; + + memset(&args, 0, sizeof(args)); + + if (state) + *state = NULL; + + if (!(client = virLockManagerLockDaemonConnect(lock, &program, &counter))) + goto cleanup; + + args.flags = flags; + + if (virNetClientProgramCall(program, + client, + counter++, + VIR_LOCK_SPACE_PROTOCOL_PROC_RELEASE_RESOURCE, + 0, NULL, NULL, NULL, + (xdrproc_t)xdr_virLockSpaceProtocolReleaseResourceArgs, &args, + (xdrproc_t)xdr_void, NULL) < 0) + goto cleanup; + + rv = 0; + +cleanup: + virNetClientClose(client); + virObjectUnref(client); + virObjectUnref(program); + + return rv; +} + + +static int virLockManagerLockDaemonInquire(virLockManagerPtr lock ATTRIBUTE_UNUSED, + char **state, + unsigned int flags) +{ + virCheckFlags(0, -1); + + if (state) + *state = NULL; + + return 0; +} + +virLockDriver virLockDriverImpl = +{ + .version = VIR_LOCK_MANAGER_VERSION, + .flags = 0, + + .drvInit = virLockManagerLockDaemonInit, + .drvDeinit = virLockManagerLockDaemonDeinit, + + .drvNew = virLockManagerLockDaemonNew, + .drvFree = virLockManagerLockDaemonFree, + + .drvAddResource = virLockManagerLockDaemonAddResource, + + .drvAcquire = virLockManagerLockDaemonAcquire, + .drvRelease = virLockManagerLockDaemonRelease, + + .drvInquire = virLockManagerLockDaemonInquire, +}; -- 1.7.11.2

On 12.09.2012 18:29, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
This adds a 'lockd' lock driver which is just a client which talks to the lockd daemon to perform all locking. This will be the default lock driver for any hypervisor which needs one.
* src/Makefile.am: Add lockd.so plugin * src/locking/lock_driver_lockd.c: Lockd driver impl
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- po/POTFILES.in | 1 + src/Makefile.am | 26 +- src/locking/lock_driver_lockd.c | 561 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 584 insertions(+), 4 deletions(-) create mode 100644 src/locking/lock_driver_lockd.c
ACK with one nit
diff --git a/po/POTFILES.in b/po/POTFILES.in index 6b9a7af..663e37b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -46,6 +46,7 @@ src/libvirt.c src/libvirt-qemu.c src/locking/lock_daemon.c src/locking/lock_daemon_dispatch.c +src/locking/lock_driver_lockd.c src/locking/lock_driver_sanlock.c src/locking/lock_manager.c src/lxc/lxc_cgroup.c diff --git a/src/Makefile.am b/src/Makefile.am index b402297..ec5014a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -158,6 +158,10 @@ LOCK_DAEMON_GENERATED = \ BUILT_SOURCES += $(LOCK_DAEMON_GENERATED) MAINTAINERCLEANFILES += $(LOCK_DAEMON_GENERATED)
+LOCK_DRIVER_LOCKD_SOURCES = \ + locking/lock_driver_lockd.c \ + $(NULL) + LOCK_DAEMON_SOURCES = \ locking/lock_daemon.h \ locking/lock_daemon.c \ @@ -1501,7 +1505,22 @@ libvirt_qemu_la_CFLAGS = $(AM_CFLAGS) libvirt_qemu_la_LIBADD = libvirt.la $(CYGWIN_EXTRA_LIBADD) EXTRA_DIST += $(LIBVIRT_QEMU_SYMBOL_FILE)
+lockdriverdir = $(libdir)/libvirt/lock-driver +lockdriver_LTLIBRARIES = + if WITH_LIBVIRTD +lockdriver_LTLIBRARIES += lockd.la +lockd_la_SOURCES = \ + $(LOCK_DRIVER_LOCKD_SOURCES) \ + $(LOCK_PROTOCOL_GENERATED) \ + $(NULL) +lockd_la_CFLAGS = $(AM_CFLAGS) +lockd_la_LDFLAGS = -module -avoid-version +lockd_la_LIBADD = ../gnulib/lib/libgnu.la libvirt-net-rpc.la libvirt-net-rpc-client.la +if WITH_DTRACE_PROBES +lockd_la_LIBADD += libvirt_probes.lo +endif + sbin_PROGRAMS = virtlockd
virtlockd_SOURCES = \ @@ -1529,7 +1548,8 @@ virtlockd_LDADD += libvirt_probes.lo endif
else -EXTRA_DIST += $(LOCK_DAEMON_SOURCES) +EXTRA_DIST += $(LOCK_DAEMON_SOURCES) \ + $(LOCK_DRIVER_LOCKD_SOURCES)
indentation
endif
EXTRA_DIST += locking/virtlockd.sysconf @@ -1623,9 +1643,7 @@ virtlockd.socket: locking/virtlockd.socket.in $(top_builddir)/config.status
if HAVE_SANLOCK -lockdriverdir = $(libdir)/libvirt/lock-driver -lockdriver_LTLIBRARIES = sanlock.la - +lockdriver_LTLIBRARIES += sanlock.la sanlock_la_SOURCES = $(LOCK_DRIVER_SANLOCK_SOURCES) sanlock_la_CFLAGS = $(AM_CFLAGS) sanlock_la_LDFLAGS = -module -avoid-version diff --git a/src/locking/lock_driver_lockd.c b/src/locking/lock_driver_lockd.c new file mode 100644 index 0000000..462996b --- /dev/null +++ b/src/locking/lock_driver_lockd.c @@ -0,0 +1,561 @@ +/* + * lock_driver_lockd.c: A lock driver which locks nothing + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include "lock_driver.h" +#include "memory.h" +#include "logging.h" +#include "uuid.h" +#include "util.h" +#include "virfile.h" +#include "virterror_internal.h" +#include "rpc/virnetclient.h" +#include "lock_protocol.h" +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_LOCKING + +#define virLockError(code, ...) \ + virReportErrorHelper(VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +typedef struct _virLockManagerLockDaemonPrivate virLockManagerLockDaemonPrivate; +typedef virLockManagerLockDaemonPrivate *virLockManagerLockDaemonPrivatePtr; + +typedef struct _virLockManagerLockDaemonResource virLockManagerLockDaemonResource; +typedef virLockManagerLockDaemonResource *virLockManagerLockDaemonResourcePtr; + +struct _virLockManagerLockDaemonResource { + char *lockspace; + char *name; + unsigned int flags; +}; + +struct _virLockManagerLockDaemonPrivate { + unsigned char uuid[VIR_UUID_BUFLEN]; + char *name; + int id; + pid_t pid; + + size_t nresources; + virLockManagerLockDaemonResourcePtr resources; +}; + + +#define VIRTLOCKD_PATH SBINDIR "/virtlockd" + +static const char * +virLockManagerLockDaemonFindDaemon(void) +{ + const char *customDaemon = getenv("VIRTLOCKD_PATH"); + + if (customDaemon) + return customDaemon; + + if (virFileIsExecutable(VIRTLOCKD_PATH)) + return VIRTLOCKD_PATH; + + return NULL; +} + +static int virLockManagerLockDaemonInit(unsigned int version, + const char *configFile, + unsigned int flags) +{ + VIR_DEBUG("version=%u configFile=%s flags=%x", version, NULLSTR(configFile), flags); + + return 0; +} + +static int virLockManagerLockDaemonDeinit(void) +{ + VIR_DEBUG(" "); + + return 0; +} + +static void virLockManagerLockDaemonFree(virLockManagerPtr lock) +{ + virLockManagerLockDaemonPrivatePtr priv = lock->privateData; + size_t i; + + if (!priv) + return; + + lock->privateData = NULL; + + for (i = 0 ; i < priv->nresources ; i++) { + VIR_FREE(priv->resources[i].lockspace); + VIR_FREE(priv->resources[i].name); + } + VIR_FREE(priv->resources); + + VIR_FREE(priv->name); + + VIR_FREE(priv); +} + + +static char *virLockManagerLockDaemonPath(bool privileged) +{ + char *path; + if (privileged) { + if (!(path = strdup(LOCALSTATEDIR "/run/libvirt/virtlockd/virtlockd.sock"))) { + virReportOOMError(); + return NULL; + } + } else { + char *userdir; + if (!(userdir = virGetUserDirectory())) + return NULL; + + if (virAsprintf(&path, "%s/.libvirt/virtlockd/virtlockd.sock", userdir) < 0) { + virReportOOMError(); + } + VIR_FREE(userdir); + } + return path; +} + + +static int virLockManagerLockDaemonNew(virLockManagerPtr lock, + unsigned int type, + size_t nparams, + virLockManagerParamPtr params, + unsigned int flags) +{ + virLockManagerLockDaemonPrivatePtr priv; + size_t i; + + virCheckFlags(VIR_LOCK_MANAGER_USES_STATE, -1); + + if (VIR_ALLOC(priv) < 0) { + virReportOOMError(); + return -1; + } + lock->privateData = priv; + + switch (type) { + case VIR_LOCK_MANAGER_OBJECT_TYPE_DOMAIN: + for (i = 0 ; i < nparams ; i++) { + if (STREQ(params[i].key, "uuid")) { + memcpy(priv->uuid, params[i].value.uuid, VIR_UUID_BUFLEN); + } else if (STREQ(params[i].key, "name")) { + if (!(priv->name = strdup(params[i].value.str))) { + virReportOOMError(); + return -1; + } + } else if (STREQ(params[i].key, "id")) { + priv->id = params[i].value.i; + } else if (STREQ(params[i].key, "pid")) { + priv->pid = params[i].value.i; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected parameter %s for object"), + params[i].key); + } + } + if (priv->id == 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing ID parameter for domain object")); + return -1; + } + if (priv->pid == 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing PID parameter for domain object")); + return -1; + } + if (!priv->name) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing name parameter for domain object")); + return -1; + } + if (!virUUIDIsValid(priv->uuid)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing UUID parameter for domain object")); + return -1; + } + break; + + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown lock manager object type %d"), + type); + return -1; + } + + return 0; +} + + +static int virLockManagerLockDaemonAddResource(virLockManagerPtr lock, + unsigned int type, + const char *name, + size_t nparams, + virLockManagerParamPtr params, + unsigned int flags) +{ + virLockManagerLockDaemonPrivatePtr priv = lock->privateData; + char *newName; + char *newLockspace = NULL; + + virCheckFlags(VIR_LOCK_MANAGER_RESOURCE_READONLY | + VIR_LOCK_MANAGER_RESOURCE_SHARED, -1); + + if (flags & VIR_LOCK_MANAGER_RESOURCE_READONLY) + return 0; + + switch (type) { + case VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK: + if (params || nparams) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpected parameters for disk resource")); + return -1; + } + if (!(newLockspace = strdup(""))) { + virReportOOMError(); + return -1; + } + break; + case VIR_LOCK_MANAGER_RESOURCE_TYPE_LEASE: { + size_t i; + char *path = NULL; + char *lockspace = NULL; + for (i = 0 ; i < nparams ; i++) { + if (STREQ(params[i].key, "offset")) { + if (params[i].value.ul != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Offset must be zero for this lock manager")); + return -1; + } + } else if (STREQ(params[i].key, "lockspace")) { + lockspace = params[i].value.str; + } else if (STREQ(params[i].key, "path")) { + path = params[i].value.str; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected parameter %s for lease resource"), + params[i].key); + return -1; + } + } + if (!path || !lockspace) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing path or lockspace for lease resource")); + return -1; + } + if (virAsprintf(&newLockspace, "%s/%s", + path, lockspace) < 0) { + virReportOOMError(); + return -1; + } + } break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown lock manager object type %d"), + type); + return -1; + } + + if (!(newName = strdup(name))) + goto no_memory; + + if (VIR_EXPAND_N(priv->resources, priv->nresources, 1) < 0) + goto no_memory; + + priv->resources[priv->nresources-1].lockspace = newLockspace; + priv->resources[priv->nresources-1].name = newName; + + if (flags & VIR_LOCK_MANAGER_RESOURCE_SHARED) + priv->resources[priv->nresources-1].flags |= + VIR_LOCK_SPACE_PROTOCOL_ACQUIRE_RESOURCE_SHARED; + + return 0; + +no_memory: + virReportOOMError(); + VIR_FREE(newName); + return -1; +} + + +static int +virLockManagerLockDaemonConnectionRegister(virLockManagerPtr lock, + virNetClientPtr client, + virNetClientProgramPtr program, + int *counter) +{ + virLockManagerLockDaemonPrivatePtr priv = lock->privateData; + virLockSpaceProtocolRegisterArgs args; + int rv = -1; + + memset(&args, 0, sizeof(args)); + + args.flags = 0; + memcpy(args.owner.uuid, priv->uuid, VIR_UUID_BUFLEN); + args.owner.name = priv->name; + args.owner.id = priv->id; + args.owner.pid = priv->pid; + + if (virNetClientProgramCall(program, + client, + (*counter)++, + VIR_LOCK_SPACE_PROTOCOL_PROC_REGISTER, + 0, NULL, NULL, NULL, + (xdrproc_t)xdr_virLockSpaceProtocolRegisterArgs, (char*)&args, + (xdrproc_t)xdr_void, NULL) < 0) + goto cleanup; + + rv = 0; + +cleanup: + return rv; +} + + +static int +virLockManagerLockDaemonConnectionRestrict(virLockManagerPtr lock ATTRIBUTE_UNUSED, + virNetClientPtr client, + virNetClientProgramPtr program, + int *counter) +{ + virLockSpaceProtocolRestrictArgs args; + int rv = -1; + + memset(&args, 0, sizeof(args)); + + args.flags = 0; + + if (virNetClientProgramCall(program, + client, + (*counter)++, + VIR_LOCK_SPACE_PROTOCOL_PROC_RESTRICT, + 0, NULL, NULL, NULL, + (xdrproc_t)xdr_virLockSpaceProtocolRestrictArgs, (char*)&args, + (xdrproc_t)xdr_void, NULL) < 0) + goto cleanup; + + rv = 0; + +cleanup: + return rv; +} + + +static virNetClientPtr virLockManagerLockDaemonConnectionNew(bool privileged, + virNetClientProgramPtr *prog) +{ + virNetClientPtr client = NULL; + char *lockdpath; + const char *daemonPath = NULL; + + *prog = NULL; + + if (!(lockdpath = virLockManagerLockDaemonPath(privileged))) + goto error; + + if (!privileged) + daemonPath = virLockManagerLockDaemonFindDaemon(); + + if (!(client = virNetClientNewUNIX(lockdpath, + daemonPath != NULL, + daemonPath))) + goto error; + + if (!(*prog = virNetClientProgramNew(VIR_LOCK_SPACE_PROTOCOL_PROGRAM, + VIR_LOCK_SPACE_PROTOCOL_PROGRAM_VERSION, + NULL, + 0, + NULL))) + goto error; + + if (virNetClientAddProgram(client, *prog) < 0) + goto error; + + VIR_FREE(lockdpath); + + return client; + +error: + VIR_FREE(lockdpath); + virNetClientClose(client); + virObjectUnref(client); + virObjectUnref(*prog); + return NULL; +} + + +static virNetClientPtr +virLockManagerLockDaemonConnect(virLockManagerPtr lock, + virNetClientProgramPtr *program, + int *counter) +{ + virNetClientPtr client; + + if (!(client = virLockManagerLockDaemonConnectionNew(getuid() == 0, program))) + return NULL; + + if (virLockManagerLockDaemonConnectionRegister(lock, + client, + *program, + counter) < 0) + goto error; + + return client; + +error: + virNetClientClose(client); + virObjectUnref(client); + return NULL; +} + + +static int virLockManagerLockDaemonAcquire(virLockManagerPtr lock, + const char *state ATTRIBUTE_UNUSED, + unsigned int flags, + int *fd) +{ + virNetClientPtr client = NULL; + virNetClientProgramPtr program = NULL; + int counter = 0; + int rv = -1; + virLockManagerLockDaemonPrivatePtr priv = lock->privateData; + + virCheckFlags(VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY | + VIR_LOCK_MANAGER_ACQUIRE_RESTRICT, -1); + + if (!(client = virLockManagerLockDaemonConnect(lock, &program, &counter))) + goto cleanup; + + if (fd && + (*fd = virNetClientDupFD(client, false)) < 0) + goto cleanup; + + if (!(flags & VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY)) { + size_t i; + for (i = 0 ; i < priv->nresources ; i++) { + virLockSpaceProtocolAcquireResourceArgs args; + + memset(&args, 0, sizeof(args)); + + if (priv->resources[i].lockspace) + args.path = priv->resources[i].lockspace; + args.name = priv->resources[i].name; + args.flags = priv->resources[i].flags; + + if (virNetClientProgramCall(program, + client, + counter++, + VIR_LOCK_SPACE_PROTOCOL_PROC_ACQUIRE_RESOURCE, + 0, NULL, NULL, NULL, + (xdrproc_t)xdr_virLockSpaceProtocolAcquireResourceArgs, &args, + (xdrproc_t)xdr_void, NULL) < 0) + goto cleanup; + } + } + + if ((flags & VIR_LOCK_MANAGER_ACQUIRE_RESTRICT) && + virLockManagerLockDaemonConnectionRestrict(lock, client, program, &counter) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv != 0 && fd) + VIR_FORCE_CLOSE(*fd); + virNetClientClose(client); + virObjectUnref(client); + virObjectUnref(program); + + return rv; +} + +static int virLockManagerLockDaemonRelease(virLockManagerPtr lock, + char **state, + unsigned int flags) +{ + virNetClientPtr client = NULL; + virNetClientProgramPtr program = NULL; + int counter = 0; + virLockSpaceProtocolReleaseResourceArgs args; + int rv = -1; + + memset(&args, 0, sizeof(args)); + + if (state) + *state = NULL; + + if (!(client = virLockManagerLockDaemonConnect(lock, &program, &counter))) + goto cleanup; + + args.flags = flags; + + if (virNetClientProgramCall(program, + client, + counter++, + VIR_LOCK_SPACE_PROTOCOL_PROC_RELEASE_RESOURCE, + 0, NULL, NULL, NULL, + (xdrproc_t)xdr_virLockSpaceProtocolReleaseResourceArgs, &args, + (xdrproc_t)xdr_void, NULL) < 0) + goto cleanup; + + rv = 0; + +cleanup: + virNetClientClose(client); + virObjectUnref(client); + virObjectUnref(program); + + return rv; +} + + +static int virLockManagerLockDaemonInquire(virLockManagerPtr lock ATTRIBUTE_UNUSED, + char **state, + unsigned int flags) +{ + virCheckFlags(0, -1); + + if (state) + *state = NULL; + + return 0; +} + +virLockDriver virLockDriverImpl = +{ + .version = VIR_LOCK_MANAGER_VERSION, + .flags = 0, + + .drvInit = virLockManagerLockDaemonInit, + .drvDeinit = virLockManagerLockDaemonDeinit, + + .drvNew = virLockManagerLockDaemonNew, + .drvFree = virLockManagerLockDaemonFree, + + .drvAddResource = virLockManagerLockDaemonAddResource, + + .drvAcquire = virLockManagerLockDaemonAcquire, + .drvRelease = virLockManagerLockDaemonRelease, + + .drvInquire = virLockManagerLockDaemonInquire, +};

On Wed, Sep 12, 2012 at 05:28:53PM +0100, Daniel P. Berrange wrote:
An update of
https://www.redhat.com/archives/libvir-list/2012-August/msg00618.html
Mostly this is a rebase to latest GIT. Alot of the questions raised with review last time bogged down on whether the APIs were design in the right way. I think I clarified their intended usage, so hopefully we can do code review this time :-)
There are still a few more patches to add on to this
- Serialize the tx/rx message queues so we don't loose partially sent/received messages across re-exec() - Make fcntl driver more configurable in its behaviour - Add support for leases based on LVM UUID and SCSI WWN
It would be nice to get some review of this now 0.10.2 is out of the door, so its ready for the 1.0 release of libvirt. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Daniel P. Berrange
-
Michal Privoznik