[libvirt] [PATCH 11/12] Add support for re-exec() of virtlockd upon SIGUSR1
Michal Privoznik
mprivozn at redhat.com
Fri Oct 5 11:24:20 UTC 2012
On 12.09.2012 18:29, Daniel P. Berrange wrote:
> From: "Daniel P. Berrange" <berrange at 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 at 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);
>
More information about the libvir-list
mailing list