[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