Introduce basic infrastructure for virtlockd daemon

The virtlockd daemon will maintain locks on behalf of libvirtd.
There are two reasons for it to be separate

 - Avoid risk of other libvirtd threads accidentally
   releasing fcntl() locks by opening + closing a file
   that is locked
 - Ensure locks can be preserved across libvirtd restarts.
   virtlockd will need to be able to re-exec itself while
   maintaining locks. This is simpler to achieve if its
   sole job is maintaining locks

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrange 2012-08-02 20:06:50 +01:00
parent f199f75e9b
commit c57e3d8994
11 changed files with 1322 additions and 4 deletions

2
.gitignore vendored
View File

@ -123,6 +123,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

6
cfg.mk
View File

@ -655,6 +655,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)";; \
@ -743,7 +745,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$$
@ -776,7 +778,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$$)

View File

@ -1662,9 +1662,11 @@ fi
%else
%{_sysconfdir}/rc.d/init.d/libvirtd
%{_sysconfdir}/rc.d/init.d/libvirt-guests
%{_sysconfdir}/rc.d/init.d/virtlockd
%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
@ -1731,6 +1733,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
@ -1764,6 +1770,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*

View File

@ -48,6 +48,8 @@ src/interface/interface_backend_udev.c
src/internal.h
src/libvirt.c
src/libvirt-qemu.c
src/locking/lock_daemon.c
src/locking/lock_daemon_config.c
src/locking/lock_driver_sanlock.c
src/locking/lock_manager.c
src/locking/sanlock_helper.c

View File

@ -148,6 +148,13 @@ LOCK_DRIVER_SANLOCK_SOURCES = \
LOCK_DRIVER_SANLOCK_HELPER_SOURCES = \
locking/sanlock_helper.c
LOCK_DAEMON_SOURCES = \
locking/lock_daemon.h \
locking/lock_daemon.c \
locking/lock_daemon_config.h \
locking/lock_daemon_config.c \
$(NULL)
NETDEV_CONF_SOURCES = \
conf/netdev_bandwidth_conf.h conf/netdev_bandwidth_conf.c \
conf/netdev_vport_profile_conf.h conf/netdev_vport_profile_conf.c \
@ -1510,6 +1517,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
@ -1747,7 +1824,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"
@ -1796,7 +1877,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" ||:

838
src/locking/lock_daemon.c Normal file
View File

@ -0,0 +1,838 @@
/*
* lock_daemon.c: lock management daemon
*
* Copyright (C) 2006-2012 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <getopt.h>
#include <stdlib.h>
#include <locale.h>
#include "lock_daemon.h"
#include "lock_daemon_config.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
struct _virLockDaemon {
virMutex lock;
virNetServerPtr srv;
};
virLockDaemonPtr lockDaemon = NULL;
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(bool privileged)
{
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,
false, NULL,
virLockDaemonClientNew,
NULL,
virLockDaemonClientFree,
(void*)(intptr_t)(privileged ? 0x1 : 0x0))))
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
virLockDaemonPidFilePath(bool privileged,
char **pidfile)
{
if (privileged) {
if (!(*pidfile = strdup(LOCALSTATEDIR "/run/virtlockd.pid")))
goto no_memory;
} else {
char *rundir = NULL;
mode_t old_umask;
if (!(rundir = virGetUserRuntimeDirectory()))
goto error;
old_umask = umask(077);
if (virFileMakePath(rundir) < 0) {
umask(old_umask);
goto error;
}
umask(old_umask);
if (virAsprintf(pidfile, "%s/virtlockd.pid", rundir) < 0) {
VIR_FREE(rundir);
goto no_memory;
}
VIR_FREE(rundir);
}
return 0;
no_memory:
virReportOOMError();
error:
return -1;
}
static int
virLockDaemonUnixSocketPaths(bool privileged,
char **sockfile)
{
if (privileged) {
if (!(*sockfile = strdup(LOCALSTATEDIR "/run/libvirt/virtlockd-sock")))
goto no_memory;
} else {
char *rundir = NULL;
mode_t old_umask;
if (!(rundir = virGetUserRuntimeDirectory()))
goto error;
old_umask = umask(077);
if (virFileMakePath(rundir) < 0) {
umask(old_umask);
goto error;
}
umask(old_umask);
if (virAsprintf(sockfile, "%s/virtlockd-sock", rundir) < 0) {
VIR_FREE(rundir);
goto no_memory;
}
VIR_FREE(rundir);
}
return 0;
no_memory:
virReportOOMError();
error:
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 the logfile libvirtd.log,
* but if verbose or error debugging is asked for then also output
* informational and debug messages. Default size if 64 kB.
*/
static int
virLockDaemonSetupLogging(virLockDaemonConfigPtr config,
bool privileged,
bool verbose,
bool godaemon)
{
virLogReset();
/*
* 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.
*/
if (config->log_level != 0)
virLogSetDefaultPriority(config->log_level);
virLogSetFromEnv();
virLogSetBufferSize(config->log_buffer_size);
if (virLogGetNbFilters() == 0)
virLogParseFilters(config->log_filters);
if (virLogGetNbOutputs() == 0)
virLogParseOutputs(config->log_outputs);
/*
* If no defined outputs, and either running
* as daemon or not on a tty, then first try
* to direct it to the systemd journal
* (if it exists)....
*/
if (virLogGetNbOutputs() == 0 &&
(godaemon || !isatty(STDIN_FILENO))) {
char *tmp;
if (access("/run/systemd/journal/socket", W_OK) >= 0) {
if (virAsprintf(&tmp, "%d:journald", virLogGetDefaultPriority()) < 0)
goto no_memory;
virLogParseOutputs(tmp);
VIR_FREE(tmp);
}
}
/*
* otherwise direct to libvirtd.log when running
* as daemon. Otherwise the default output is stderr.
*/
if (virLogGetNbOutputs() == 0) {
char *tmp = NULL;
if (godaemon) {
if (privileged) {
if (virAsprintf(&tmp, "%d:file:%s/log/libvirt/virtlockd.log",
virLogGetDefaultPriority(),
LOCALSTATEDIR) == -1)
goto no_memory;
} else {
char *logdir = virGetUserCacheDirectory();
mode_t old_umask;
if (!logdir)
goto error;
old_umask = umask(077);
if (virFileMakePath(logdir) < 0) {
umask(old_umask);
goto error;
}
umask(old_umask);
if (virAsprintf(&tmp, "%d:file:%s/virtlockd.log",
virLogGetDefaultPriority(), logdir) == -1) {
VIR_FREE(logdir);
goto no_memory;
}
VIR_FREE(logdir);
}
} else {
if (virAsprintf(&tmp, "%d:stderr", virLogGetDefaultPriority()) < 0)
goto no_memory;
}
virLogParseOutputs(tmp);
VIR_FREE(tmp);
}
/*
* Command line override for --verbose
*/
if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO))
virLogSetDefaultPriority(VIR_LOG_INFO);
return 0;
no_memory:
virReportOOMError();
error:
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)
{
virLockDaemonClientPtr priv;
uid_t clientuid;
gid_t clientgid;
bool privileged = opaque != NULL;
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) {
virReportError(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) {
virReportError(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, bool privileged)
{
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"
" -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"), argv0);
if (privileged) {
fprintf(stderr,
_("\n"
" Default paths:\n"
"\n"
" Configuration file (unless overridden by -f):\n"
" %s/libvirt/virtlockd.conf\n"
"\n"
" Sockets:\n"
" %s/run/libvirt/virtlockd-sock\n"
"\n"
" PID file (unless overridden by -p):\n"
" %s/run/virtlockd.pid\n"
"\n"),
SYSCONFDIR,
LOCALSTATEDIR,
LOCALSTATEDIR);
} else {
fprintf(stderr, "%s",
_("\n"
" Default paths:\n"
"\n"
" Configuration file (unless overridden by -f):\n"
" $XDG_CONFIG_HOME/libvirt/virtlockd.conf\n"
"\n"
" Sockets:\n"
" $XDG_RUNTIME_DIR/libvirt/virtlockd-sock\n"
"\n"
" PID file:\n"
" $XDG_RUNTIME_DIR/libvirt/virtlockd.pid\n"
"\n"));
}
}
enum {
OPT_VERSION = 129
};
#define MAX_LISTEN 5
int main(int argc, char **argv) {
char *remote_config_file = NULL;
int statuswrite = -1;
int ret = 1;
int verbose = 0;
int godaemon = 0;
char *run_dir = NULL;
char *pid_file = NULL;
int pid_file_fd = -1;
char *sock_file = NULL;
bool implicit_conf = false;
mode_t old_umask;
bool privileged = false;
virLockDaemonConfigPtr config = NULL;
struct option opts[] = {
{ "verbose", no_argument, &verbose, 1},
{ "daemon", no_argument, &godaemon, 1},
{ "config", required_argument, NULL, 'f'},
{ "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);
}
while (1) {
int optidx = 0;
int c;
c = getopt_long(argc, argv, "ldf:p:t:v", opts, &optidx);
if (c == -1) {
break;
}
switch (c) {
case 0:
/* Got one of the flags */
break;
case 'v':
verbose = 1;
break;
case 'd':
godaemon = 1;
break;
case 'p':
VIR_FREE(pid_file);
if (!(pid_file = strdup(optarg)))
exit(EXIT_FAILURE);
break;
case 'f':
VIR_FREE(remote_config_file);
if (!(remote_config_file = strdup(optarg)))
exit(EXIT_FAILURE);
break;
case OPT_VERSION:
virLockDaemonVersion(argv[0]);
return 0;
case '?':
virLockDaemonUsage(argv[0], privileged);
return 2;
default:
fprintf(stderr, _("%s: internal error: unknown flag: %c\n"),
argv[0], c);
exit(EXIT_FAILURE);
}
}
if (!(config = virLockDaemonConfigNew(privileged))) {
VIR_ERROR(_("Can't create initial configuration"));
exit(EXIT_FAILURE);
}
/* No explicit config, so try and find a default one */
if (remote_config_file == NULL) {
implicit_conf = true;
if (virLockDaemonConfigFilePath(privileged,
&remote_config_file) < 0) {
VIR_ERROR(_("Can't determine config path"));
exit(EXIT_FAILURE);
}
}
/* Read the config file if it exists*/
if (remote_config_file &&
virLockDaemonConfigLoadFile(config, remote_config_file, implicit_conf) < 0) {
virErrorPtr err = virGetLastError();
if (err && err->message)
VIR_ERROR(_("Can't load config file: %s: %s"),
err->message, remote_config_file);
else
VIR_ERROR(_("Can't load config file: %s"), remote_config_file);
exit(EXIT_FAILURE);
}
if (virLockDaemonSetupLogging(config, privileged, verbose, godaemon) < 0) {
VIR_ERROR(_("Can't initialize logging"));
exit(EXIT_FAILURE);
}
if (!pid_file &&
virLockDaemonPidFilePath(privileged,
&pid_file) < 0) {
VIR_ERROR(_("Can't determine pid file path."));
exit(EXIT_FAILURE);
}
VIR_DEBUG("Decided on pid file path '%s'", NULLSTR(pid_file));
if (virLockDaemonUnixSocketPaths(privileged,
&sock_file) < 0) {
VIR_ERROR(_("Can't determine socket paths"));
exit(EXIT_FAILURE);
}
VIR_DEBUG("Decided on socket paths '%s'",
sock_file);
if (godaemon) {
char ebuf[1024];
if (chdir("/") < 0) {
VIR_ERROR(_("cannot change to root directory: %s"),
virStrerror(errno, ebuf, sizeof(ebuf)));
goto cleanup;
}
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 (privileged) {
run_dir = strdup(LOCALSTATEDIR "/run/libvirt");
} else {
run_dir = virGetUserRuntimeDirectory();
if (!run_dir) {
VIR_ERROR(_("Can't determine user directory"));
goto cleanup;
}
}
if (!run_dir) {
virReportOOMError();
goto cleanup;
}
if (privileged)
old_umask = umask(022);
else
old_umask = umask(077);
VIR_DEBUG("Ensuring run dir '%s' exists", run_dir);
if (virFileMakePath(run_dir) < 0) {
char ebuf[1024];
VIR_ERROR(_("unable to create rundir %s: %s"), run_dir,
virStrerror(errno, ebuf, sizeof(ebuf)));
ret = VIR_LOCK_DAEMON_ERR_RUNDIR;
goto cleanup;
}
umask(old_umask);
/* 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;
}
if (!(lockDaemon = virLockDaemonNew(privileged))) {
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(run_dir);
return ret;
}

43
src/locking/lock_daemon.h Normal file
View File

@ -0,0 +1,43 @@
/*
* lock_daemon.h: lock management daemon
*
* Copyright (C) 2006-2012 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#ifndef __VIR_LOCK_DAEMON_H__
# define __VIR_LOCK_DAEMON_H__
# include "virlockspace.h"
# include "threads.h"
typedef struct _virLockDaemon virLockDaemon;
typedef virLockDaemon *virLockDaemonPtr;
typedef struct _virLockDaemonClient virLockDaemonClient;
typedef virLockDaemonClient *virLockDaemonClientPtr;
struct _virLockDaemonClient {
virMutex lock;
pid_t clientPid;
};
extern virLockDaemonPtr lockDaemon;
#endif /* __VIR_LOCK_DAEMON_H__ */

View File

@ -0,0 +1,193 @@
/*
* lock_daemon_config.h: virtlockd config file handling
*
* Copyright (C) 2006-2012 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include "lock_daemon_config.h"
#include "conf.h"
#include "memory.h"
#include "virterror_internal.h"
#include "logging.h"
#include "rpc/virnetserver.h"
#include "configmake.h"
#define VIR_FROM_THIS VIR_FROM_CONF
/* A helper function used by each of the following macros. */
static int
checkType(virConfValuePtr p, const char *filename,
const char *key, virConfType required_type)
{
if (p->type != required_type) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("remoteReadConfigFile: %s: %s: invalid type:"
" got %s; expected %s"), filename, key,
virConfTypeName(p->type),
virConfTypeName(required_type));
return -1;
}
return 0;
}
/* If there is no config data for the key, #var_name, then do nothing.
If there is valid data of type VIR_CONF_STRING, and strdup succeeds,
store the result in var_name. Otherwise, (i.e. invalid type, or strdup
failure), give a diagnostic and "goto" the cleanup-and-fail label. */
#define GET_CONF_STR(conf, filename, var_name) \
do { \
virConfValuePtr p = virConfGetValue(conf, #var_name); \
if (p) { \
if (checkType(p, filename, #var_name, VIR_CONF_STRING) < 0) \
goto error; \
VIR_FREE(data->var_name); \
if (!(data->var_name = strdup(p->str))) { \
virReportOOMError(); \
goto error; \
} \
} \
} while (0)
/* Like GET_CONF_STR, but for integral values. */
#define GET_CONF_INT(conf, filename, var_name) \
do { \
virConfValuePtr p = virConfGetValue(conf, #var_name); \
if (p) { \
if (checkType(p, filename, #var_name, VIR_CONF_LONG) < 0) \
goto error; \
data->var_name = p->l; \
} \
} while (0)
int
virLockDaemonConfigFilePath(bool privileged, char **configfile)
{
if (privileged) {
if (!(*configfile = strdup(SYSCONFDIR "/libvirt/virtlockd.conf")))
goto no_memory;
} else {
char *configdir = NULL;
if (!(configdir = virGetUserConfigDirectory()))
goto error;
if (virAsprintf(configfile, "%s/virtlockd.conf", configdir) < 0) {
VIR_FREE(configdir);
goto no_memory;
}
VIR_FREE(configdir);
}
return 0;
no_memory:
virReportOOMError();
error:
return -1;
}
virLockDaemonConfigPtr
virLockDaemonConfigNew(bool privileged ATTRIBUTE_UNUSED)
{
virLockDaemonConfigPtr data;
if (VIR_ALLOC(data) < 0) {
virReportOOMError();
return NULL;
}
data->log_buffer_size = 64;
return data;
}
void
virLockDaemonConfigFree(virLockDaemonConfigPtr data)
{
if (!data)
return;
VIR_FREE(data->log_filters);
VIR_FREE(data->log_outputs);
VIR_FREE(data);
}
static int
virLockDaemonConfigLoadOptions(virLockDaemonConfigPtr data,
const char *filename,
virConfPtr conf)
{
GET_CONF_INT(conf, filename, log_level);
GET_CONF_STR(conf, filename, log_filters);
GET_CONF_STR(conf, filename, log_outputs);
GET_CONF_INT(conf, filename, log_buffer_size);
return 0;
error:
return -1;
}
/* Read the config file if it exists.
* Only used in the remote case, hence the name.
*/
int
virLockDaemonConfigLoadFile(virLockDaemonConfigPtr data,
const char *filename,
bool allow_missing)
{
virConfPtr conf;
int ret;
if (allow_missing &&
access(filename, R_OK) == -1 &&
errno == ENOENT)
return 0;
conf = virConfReadFile(filename, 0);
if (!conf)
return -1;
ret = virLockDaemonConfigLoadOptions(data, filename, conf);
virConfFree(conf);
return ret;
}
int virLockDaemonConfigLoadData(virLockDaemonConfigPtr data,
const char *filename,
const char *filedata)
{
virConfPtr conf;
int ret;
conf = virConfReadMem(filedata, strlen(filedata), 0);
if (!conf)
return -1;
ret = virLockDaemonConfigLoadOptions(data, filename, conf);
virConfFree(conf);
return ret;
}

View File

@ -0,0 +1,50 @@
/*
* lock_daemon_config.h: virtlockd config file handling
*
* Copyright (C) 2006-2012 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#ifndef __VIR_LOCK_DAEMON_CONFIG_H__
# define __VIR_LOCK_DAEMON_CONFIG_H__
# include "internal.h"
typedef struct _virLockDaemonConfig virLockDaemonConfig;
typedef virLockDaemonConfig *virLockDaemonConfigPtr;
struct _virLockDaemonConfig {
int log_level;
char *log_filters;
char *log_outputs;
int log_buffer_size;
};
int virLockDaemonConfigFilePath(bool privileged, char **configfile);
virLockDaemonConfigPtr virLockDaemonConfigNew(bool privileged);
void virLockDaemonConfigFree(virLockDaemonConfigPtr data);
int virLockDaemonConfigLoadFile(virLockDaemonConfigPtr data,
const char *filename,
bool allow_missing);
int virLockDaemonConfigLoadData(virLockDaemonConfigPtr data,
const char *filename,
const char *filedata);
#endif /* __LIBVIRTD_CONFIG_H__ */

View File

@ -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

View File

@ -0,0 +1,3 @@
#
# Pass extra arguments to virtlockd
#VIRTLOCKD_ARGS=