On 12.09.2012 18:29, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange(a)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(a)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(a)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(a)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-...
+#
+### 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=