[libvirt] [PATCH 07/12] Introduce basic infrastructure for virtlockd daemon

Michal Privoznik mprivozn at redhat.com
Fri Oct 5 11:24:36 UTC 2012


On 12.09.2012 18:29, Daniel P. Berrange wrote:
> From: "Daniel P. Berrange" <berrange at 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 at 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 at 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 at 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-generic.html#INITSCRCOMCONV
> +#
> +### 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=
> 




More information about the libvir-list mailing list