Copy the virtlockd codebase across to form the initial virlogd
code. Simple search & replace of s/lock/log/ and gut the remote
protocol & dispatcher. This gives us a daemon that starts up
and listens for connections, but does nothing with them.
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
---
.gitignore | 7 +
cfg.mk | 4 +-
include/libvirt/virterror.h | 1 +
libvirt.spec.in | 24 +-
po/POTFILES.in | 2 +
src/Makefile.am | 169 +++++-
src/logging/log_daemon.c | 1177 +++++++++++++++++++++++++++++++++++++
src/logging/log_daemon.h | 42 ++
src/logging/log_daemon_config.c | 203 +++++++
src/logging/log_daemon_config.h | 50 ++
src/logging/log_daemon_dispatch.c | 37 ++
src/logging/log_daemon_dispatch.h | 31 +
src/logging/log_protocol.x | 22 +
src/logging/test_virtlogd.aug.in | 12 +
src/logging/virtlogd.aug | 45 ++
src/logging/virtlogd.conf | 67 +++
src/logging/virtlogd.init.in | 94 +++
src/logging/virtlogd.pod.in | 162 +++++
src/logging/virtlogd.service.in | 17 +
src/logging/virtlogd.socket.in | 8 +
src/logging/virtlogd.sysconf | 3 +
src/util/virerror.c | 1 +
22 files changed, 2156 insertions(+), 22 deletions(-)
create mode 100644 src/logging/log_daemon.c
create mode 100644 src/logging/log_daemon.h
create mode 100644 src/logging/log_daemon_config.c
create mode 100644 src/logging/log_daemon_config.h
create mode 100644 src/logging/log_daemon_dispatch.c
create mode 100644 src/logging/log_daemon_dispatch.h
create mode 100644 src/logging/log_protocol.x
create mode 100644 src/logging/test_virtlogd.aug.in
create mode 100644 src/logging/virtlogd.aug
create mode 100644 src/logging/virtlogd.conf
create mode 100644 src/logging/virtlogd.init.in
create mode 100644 src/logging/virtlogd.pod.in
create mode 100644 src/logging/virtlogd.service.in
create mode 100644 src/logging/virtlogd.socket.in
create mode 100644 src/logging/virtlogd.sysconf
diff --git a/.gitignore b/.gitignore
index 2d52a8f..7f3e253 100644
--- a/.gitignore
+++ b/.gitignore
@@ -137,6 +137,8 @@
/src/locking/qemu-lockd.conf
/src/locking/qemu-sanlock.conf
/src/locking/test_libvirt_sanlock.aug
+/src/logging/log_daemon_dispatch_stubs.h
+/src/logging/log_protocol.[ch]
/src/lxc/lxc_controller_dispatch.h
/src/lxc/lxc_monitor_dispatch.h
/src/lxc/lxc_monitor_protocol.c
@@ -150,12 +152,17 @@
/src/rpc/virnetprotocol.[ch]
/src/test_libvirt*.aug
/src/test_virtlockd.aug
+/src/test_virtlogd.aug
/src/util/virkeymaps.h
/src/virt-aa-helper
/src/virtlockd
/src/virtlockd.8
/src/virtlockd.8.in
/src/virtlockd.init
+/src/virtlogd
+/src/virtlogd.8
+/src/virtlogd.8.in
+/src/virtlogd.init
/tests/*.log
/tests/*.pid
/tests/*.trs
diff --git a/cfg.mk b/cfg.mk
index db513be..2a23b33 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -1112,7 +1112,7 @@ $(srcdir)/src/admin/admin_client.h:
$(srcdir)/src/admin/admin_protocol.x
# List all syntax-check exemptions:
exclude_file_name_regexp--sc_avoid_strcase = ^tools/vsh\.h$$
-_src1=libvirt-stream|fdstream|qemu/qemu_monitor|util/(vircommand|virfile)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller|locking/lock_daemon
+_src1=libvirt-stream|fdstream|qemu/qemu_monitor|util/(vircommand|virfile)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller|locking/lock_daemon|logging/log_daemon
_test1=shunloadtest|virnettlscontexttest|virnettlssessiontest|vircgroupmock
exclude_file_name_regexp--sc_avoid_write = \
^(src/($(_src1))|daemon/libvirtd|tools/virsh-console|tests/($(_test1)))\.c$$
@@ -1147,7 +1147,7 @@ exclude_file_name_regexp--sc_prohibit_close = \
exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = \
(^tests/(qemuhelp|nodeinfo|virpcitest)data/|\.diff$$)
-_src2=src/(util/vircommand|libvirt|lxc/lxc_controller|locking/lock_daemon)
+_src2=src/(util/vircommand|libvirt|lxc/lxc_controller|locking/lock_daemon|logging/log_daemon)
exclude_file_name_regexp--sc_prohibit_fork_wrappers = \
(^($(_src2)|tests/testutils|daemon/libvirtd)\.c$$)
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index f716cb9..0539e48 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -127,6 +127,7 @@ typedef enum {
VIR_FROM_POLKIT = 60, /* Error from polkit code */
VIR_FROM_THREAD = 61, /* Error from thread utils */
VIR_FROM_ADMIN = 62, /* Error from admin backend */
+ VIR_FROM_LOGGING = 63, /* Error from log manager */
# ifdef VIR_ENUM_SENTINELS
VIR_ERR_DOMAIN_LAST
diff --git a/libvirt.spec.in b/libvirt.spec.in
index ac46da5..8420d11 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -1690,12 +1690,13 @@ exit 0
%if %{with_systemd}
%if %{with_systemd_macros}
- %systemd_post virtlockd.socket libvirtd.service libvirtd.socket
+ %systemd_post virtlockd.socket virtlogd.socket libvirtd.service
libvirtd.socket
%else
if [ $1 -eq 1 ] ; then
# Initial installation
/bin/systemctl enable \
virtlockd.socket \
+ virtlogd.socket \
libvirtd.service >/dev/null 2>&1 || :
fi
%endif
@@ -1711,24 +1712,29 @@ fi
%endif
/sbin/chkconfig --add libvirtd
+/sbin/chkconfig --add virtlogd
/sbin/chkconfig --add virtlockd
%endif
%preun daemon
%if %{with_systemd}
%if %{with_systemd_macros}
- %systemd_preun libvirtd.socket libvirtd.service virtlockd.socket
virtlockd.service
+ %systemd_preun libvirtd.socket libvirtd.service virtlogd.socket
virtlogd.service virtlockd.socket virtlockd.service
%else
if [ $1 -eq 0 ] ; then
# Package removal, not upgrade
/bin/systemctl --no-reload disable \
libvirtd.socket \
libvirtd.service \
+ virtlogd.socket \
+ virtlogd.service \
virtlockd.socket \
virtlockd.service > /dev/null 2>&1 || :
/bin/systemctl stop \
libvirtd.socket \
libvirtd.service \
+ virtlogd.socket \
+ virtlogd.service \
virtlockd.socket \
virtlockd.service > /dev/null 2>&1 || :
fi
@@ -1737,6 +1743,8 @@ fi
if [ $1 = 0 ]; then
/sbin/service libvirtd stop 1>/dev/null 2>&1
/sbin/chkconfig --del libvirtd
+ /sbin/service virtlogd stop 1>/dev/null 2>&1
+ /sbin/chkconfig --del virtlogd
/sbin/service virtlockd stop 1>/dev/null 2>&1
/sbin/chkconfig --del virtlockd
fi
@@ -1747,11 +1755,13 @@ fi
/bin/systemctl daemon-reload >/dev/null 2>&1 || :
if [ $1 -ge 1 ] ; then
/bin/systemctl reload-or-try-restart virtlockd.service >/dev/null 2>&1 ||
:
+ /bin/systemctl reload-or-try-restart virtlogd.service >/dev/null 2>&1 || :
/bin/systemctl try-restart libvirtd.service >/dev/null 2>&1 || :
fi
%else
if [ $1 -ge 1 ]; then
/sbin/service virtlockd reload > /dev/null 2>&1 || :
+ /sbin/service virtlogd reload > /dev/null 2>&1 || :
/sbin/service libvirtd condrestart > /dev/null 2>&1
fi
%endif
@@ -1761,6 +1771,7 @@ fi
%triggerpostun daemon -- libvirt-daemon < 1.2.1
if [ "$1" -ge "1" ]; then
/sbin/service virtlockd reload > /dev/null 2>&1 || :
+ /sbin/service virtlogd reload > /dev/null 2>&1 || :
/sbin/service libvirtd condrestart > /dev/null 2>&1
fi
%endif
@@ -1917,16 +1928,21 @@ exit 0
%if %{with_systemd}
%{_unitdir}/libvirtd.service
%{_unitdir}/libvirtd.socket
+%{_unitdir}/virtlogd.service
+%{_unitdir}/virtlogd.socket
%{_unitdir}/virtlockd.service
%{_unitdir}/virtlockd.socket
%else
%{_sysconfdir}/rc.d/init.d/libvirtd
+%{_sysconfdir}/rc.d/init.d/virtlogd
%{_sysconfdir}/rc.d/init.d/virtlockd
%endif
%doc daemon/libvirtd.upstart
%config(noreplace) %{_sysconfdir}/sysconfig/libvirtd
+%config(noreplace) %{_sysconfdir}/sysconfig/virtlogd
%config(noreplace) %{_sysconfdir}/sysconfig/virtlockd
%config(noreplace) %{_sysconfdir}/libvirt/libvirtd.conf
+%config(noreplace) %{_sysconfdir}/libvirt/virtlogd.conf
%config(noreplace) %{_sysconfdir}/libvirt/virtlockd.conf
%if 0%{?fedora} || 0%{?rhel} >= 6
%config(noreplace) %{_prefix}/lib/sysctl.d/60-libvirtd.conf
@@ -1948,6 +1964,8 @@ exit 0
%{_datadir}/augeas/lenses/libvirtd.aug
%{_datadir}/augeas/lenses/tests/test_libvirtd.aug
+%{_datadir}/augeas/lenses/virtlogd.aug
+%{_datadir}/augeas/lenses/tests/test_virtlogd.aug
%{_datadir}/augeas/lenses/virtlockd.aug
%{_datadir}/augeas/lenses/tests/test_virtlockd.aug
%{_datadir}/augeas/lenses/libvirt_lockd.aug
@@ -1974,9 +1992,11 @@ exit 0
%endif
%attr(0755, root, root) %{_sbindir}/libvirtd
+%attr(0755, root, root) %{_sbindir}/virtlogd
%attr(0755, root, root) %{_sbindir}/virtlockd
%{_mandir}/man8/libvirtd.8*
+%{_mandir}/man8/virtlogd.8*
%{_mandir}/man8/virtlockd.8*
%if ! %{with_driver_modules}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 401ac6f..33bc258 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -78,6 +78,8 @@ src/locking/lock_driver_lockd.c
src/locking/lock_driver_sanlock.c
src/locking/lock_manager.c
src/locking/sanlock_helper.c
+src/logging/log_daemon.c
+src/logging/log_daemon_config.c
src/lxc/lxc_cgroup.c
src/lxc/lxc_fuse.c
src/lxc/lxc_hostdev.c
diff --git a/src/Makefile.am b/src/Makefile.am
index ee082ec..3323bd1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -263,6 +263,41 @@ locking/lock_daemon_dispatch_stubs.h: $(LOCK_PROTOCOL) \
$(LOCK_PROTOCOL) > $(srcdir)/locking/lock_daemon_dispatch_stubs.h
+LOG_PROTOCOL_GENERATED = \
+ logging/log_protocol.h \
+ logging/log_protocol.c \
+ $(NULL)
+
+LOG_PROTOCOL = $(srcdir)/logging/log_protocol.x
+EXTRA_DIST += $(LOG_PROTOCOL) \
+ $(LOG_PROTOCOL_GENERATED)
+BUILT_SOURCES += $(LOG_PROTOCOL_GENERATED)
+MAINTAINERCLEANFILES += $(LOG_PROTOCOL_GENERATED)
+
+LOG_DAEMON_GENERATED = \
+ logging/log_daemon_dispatch_stubs.h
+ $(NULL)
+
+BUILT_SOURCES += $(LOG_DAEMON_GENERATED)
+EXTRA_DIST += $(LOG_DAEMON_GENERATED)
+MAINTAINERCLEANFILES += $(LOG_DAEMON_GENERATED)
+
+LOG_DAEMON_SOURCES = \
+ logging/log_daemon.h \
+ logging/log_daemon.c \
+ logging/log_daemon_config.h \
+ logging/log_daemon_config.c \
+ logging/log_daemon_dispatch.c \
+ logging/log_daemon_dispatch.h \
+ $(NULL)
+
+logging/log_daemon_dispatch_stubs.h: $(LOG_PROTOCOL) \
+ $(srcdir)/rpc/gendispatch.pl Makefile.am
+ $(AM_V_GEN)perl -w $(srcdir)/rpc/gendispatch.pl --mode=server \
+ virLogManagerProtocol VIR_LOG_MANAGER_PROTOCOL \
+ $(LOG_PROTOCOL) > $(srcdir)/logging/log_daemon_dispatch_stubs.h
+
+
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 \
@@ -1838,7 +1873,8 @@ check-local: check-augeas
$(NULL)
check-augeas: check-augeas-qemu check-augeas-lxc check-augeas-sanlock \
- check-augeas-lockd check-augeas-virtlockd check-augeas-libxl
+ check-augeas-lockd check-augeas-virtlockd check-augeas-libxl \
+ check-augeas-virtlogd
AUG_GENTEST = $(PERL) $(top_srcdir)/build-aux/augeas-gentest.pl
EXTRA_DIST += $(top_srcdir)/build-aux/augeas-gentest.pl
@@ -1921,6 +1957,15 @@ else ! WITH_LIBXL
check-augeas-libxl:
endif ! WITH_LIBXL
+test_virtlogd.aug: logging/test_virtlogd.aug.in \
+ logging/virtlogd.conf $(AUG_GENTEST)
+ $(AM_V_GEN)$(AUG_GENTEST) $(srcdir)/logging/virtlogd.conf $< $@
+
+check-augeas-virtlogd: test_virtlogd.aug
+ $(AM_V_GEN)if test -x '$(AUGPARSE)'; then \
+ '$(AUGPARSE)' -I $(srcdir)/logging test_virtlogd.aug; \
+ fi
+
#
# Build our version script. This is composed of three parts:
#
@@ -2345,7 +2390,7 @@ locking/%-lockd.conf: $(srcdir)/locking/lockd.conf
cp $< $@
-sbin_PROGRAMS = virtlockd
+sbin_PROGRAMS = virtlockd virtlogd
virtlockd_SOURCES = \
$(LOCK_DAEMON_SOURCES) \
@@ -2374,40 +2419,79 @@ if WITH_DTRACE_PROBES
virtlockd_LDADD += libvirt_probes.lo
endif WITH_DTRACE_PROBES
+
+virtlogd_SOURCES = \
+ $(LOG_DAEMON_SOURCES) \
+ $(LOG_PROTOCOL_GENERATED) \
+ $(LOG_DAEMON_GENERATED) \
+ $(NULL)
+virtlogd_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(PIE_CFLAGS) \
+ $(XDR_CFLAGS) \
+ $(NULL)
+virtlogd_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ $(PIE_LDFLAGS) \
+ $(CYGWIN_EXTRA_LDFLAGS) \
+ $(MINGW_EXTRA_LDFLAGS) \
+ $(NULL)
+virtlogd_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
+virtlogd_LDADD += libvirt_probes.lo
+endif WITH_DTRACE_PROBES
+
else ! WITH_LIBVIRTD
EXTRA_DIST += $(LOCK_DAEMON_SOURCES) \
- $(LOCK_DRIVER_LOCKD_SOURCES)
+ $(LOCK_DRIVER_LOCKD_SOURCES) \
+ $(LOG_DAEMON_SOURCES)
endif ! WITH_LIBVIRTD
-EXTRA_DIST += locking/virtlockd.sysconf \
+EXTRA_DIST += \
+ locking/virtlockd.sysconf \
locking/lockd.conf \
locking/libvirt_lockd.aug \
- locking/test_libvirt_lockd.aug.in
+ locking/test_libvirt_lockd.aug.in \
+ logging/virtlogd.sysconf \
+ logging/libvirt_logd.aug \
+ logging/test_libvirt_logd.aug.in
install-sysconfig:
$(MKDIR_P) $(DESTDIR)$(sysconfdir)/sysconfig
$(INSTALL_DATA) $(srcdir)/locking/virtlockd.sysconf \
$(DESTDIR)$(sysconfdir)/sysconfig/virtlockd
+ $(INSTALL_DATA) $(srcdir)/logging/virtlogd.sysconf \
+ $(DESTDIR)$(sysconfdir)/sysconfig/virtlogd
uninstall-sysconfig:
+ rm -f $(DESTDIR)$(sysconfdir)/sysconfig/virtlogd
rm -f $(DESTDIR)$(sysconfdir)/sysconfig/virtlockd
rmdir $(DESTDIR)$(sysconfdir)/sysconfig || :
-EXTRA_DIST += locking/virtlockd.init.in
+EXTRA_DIST += locking/virtlockd.init.in logging/virtlogd.init.in
if WITH_LIBVIRTD
if LIBVIRT_INIT_SCRIPT_RED_HAT
-install-init:: virtlockd.init install-sysconfig
+install-init:: virtlockd.init virtlogd.init install-sysconfig
$(MKDIR_P) $(DESTDIR)$(sysconfdir)/rc.d/init.d
$(INSTALL_SCRIPT) virtlockd.init \
$(DESTDIR)$(sysconfdir)/rc.d/init.d/virtlockd
+ $(INSTALL_SCRIPT) virtlogd.init \
+ $(DESTDIR)$(sysconfdir)/rc.d/init.d/virtlogd
uninstall-init:: uninstall-sysconfig
rm -f $(DESTDIR)$(sysconfdir)/rc.d/init.d/virtlockd
+ rm -f $(DESTDIR)$(sysconfdir)/rc.d/init.d/virtlogd
rmdir $(DESTDIR)$(sysconfdir)/rc.d/init.d || :
-BUILT_SOURCES += virtlockd.init
-DISTCLEANFILES += virtlockd.init
+BUILT_SOURCES += virtlockd.init virtlogd.init
+DISTCLEANFILES += virtlockd.init virtlogd.init
else ! LIBVIRT_INIT_SCRIPT_RED_HAT
install-init::
uninstall-init::
@@ -2426,6 +2510,15 @@ virtlockd.init: locking/virtlockd.init.in
$(top_builddir)/config.status
chmod a+x $@-t && \
mv $@-t $@
+virtlogd.init: logging/virtlogd.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 $@
+
POD2MAN = pod2man -c "Virtualization Support" \
-r "$(PACKAGE)-$(VERSION)" -s 8
@@ -2440,17 +2533,29 @@ virtlockd.8: $(srcdir)/virtlockd.8.in
< $< > $@-t && \
mv $@-t $@
+$(srcdir)/virtlogd.8.in: logging/virtlogd.pod.in $(top_srcdir)/configure.ac
+ $(AM_V_GEN)$(POD2MAN) --name VIRTLOGD $< $@ \
+ && if grep 'POD ERROR' $@ ; then rm $@; exit 1; fi
+
+virtlogd.8: $(srcdir)/virtlogd.8.in
+ $(AM_V_GEN)sed \
+ -e 's|[@]sysconfdir[@]|$(sysconfdir)|g' \
+ -e 's|[@]localstatedir[@]|$(localstatedir)|g' \
+ < $< > $@-t && \
+ mv $@-t $@
+
if WITH_LIBVIRTD
-man8_MANS = virtlockd.8
+man8_MANS = virtlockd.8 virtlogd.8
-conf_DATA += locking/virtlockd.conf
+conf_DATA += locking/virtlockd.conf logging/virtlogd.conf
-augeas_DATA += locking/virtlockd.aug
-augeastest_DATA += test_virtlockd.aug
+augeas_DATA += locking/virtlockd.aug logging/virtlogd.aug
+augeastest_DATA += test_virtlockd.aug test_virtlogd.aug
endif WITH_LIBVIRTD
-CLEANFILES += test_virtlockd.aug virtlockd.8
-MAINTAINERCLEANFILES += $(srcdir)/virtlockd.8.in
+CLEANFILES += test_virtlockd.aug virtlockd.8 \
+ test_virtlogd.aug virtlogd.8
+MAINTAINERCLEANFILES += $(srcdir)/virtlockd.8.in $(srcdir)/virtlogd.8.in
EXTRA_DIST += \
locking/virtlockd.service.in \
@@ -2460,6 +2565,13 @@ EXTRA_DIST += \
locking/virtlockd.aug \
locking/virtlockd.conf \
locking/test_virtlockd.aug.in \
+ logging/virtlogd.service.in \
+ logging/virtlogd.socket.in \
+ logging/virtlogd.pod.in \
+ virtlogd.8.in \
+ logging/virtlogd.aug \
+ logging/virtlogd.conf \
+ logging/test_virtlogd.aug.in \
$(NULL)
@@ -2468,19 +2580,28 @@ if LIBVIRT_INIT_SCRIPT_SYSTEMD
SYSTEMD_UNIT_DIR = $(prefix)/lib/systemd/system
-BUILT_SOURCES += virtlockd.service virtlockd.socket
-DISTCLEANFILES += virtlockd.service virtlockd.socket
+BUILT_SOURCES += virtlockd.service virtlockd.socket \
+ virtlogd.service virtlogd.socket
+DISTCLEANFILES += virtlockd.service virtlockd.socket \
+ virtlogd.service virtlogd.socket
-install-systemd: virtlockd.service virtlockd.socket install-sysconfig
+install-systemd: virtlockd.service virtlockd.socket \
+ virtlogd.service virtlogd.socket install-sysconfig
$(MKDIR_P) $(DESTDIR)$(SYSTEMD_UNIT_DIR)
$(INSTALL_DATA) virtlockd.service \
$(DESTDIR)$(SYSTEMD_UNIT_DIR)/
$(INSTALL_DATA) virtlockd.socket \
$(DESTDIR)$(SYSTEMD_UNIT_DIR)/
+ $(INSTALL_DATA) virtlogd.service \
+ $(DESTDIR)$(SYSTEMD_UNIT_DIR)/
+ $(INSTALL_DATA) virtlogd.socket \
+ $(DESTDIR)$(SYSTEMD_UNIT_DIR)/
uninstall-systemd: uninstall-sysconfig
rm -f $(DESTDIR)$(SYSTEMD_UNIT_DIR)/virtlockd.service \
$(DESTDIR)$(SYSTEMD_UNIT_DIR)/virtlockd.socket
+ rm -f $(DESTDIR)$(SYSTEMD_UNIT_DIR)/virtlogd.service \
+ $(DESTDIR)$(SYSTEMD_UNIT_DIR)/virtlogd.socket
rmdir $(DESTDIR)$(SYSTEMD_UNIT_DIR) || :
else ! LIBVIRT_INIT_SCRIPT_SYSTEMD
install-systemd:
@@ -2503,6 +2624,18 @@ virtlockd.socket: locking/virtlockd.socket.in
$(top_builddir)/config.status
< $< > $@-t && \
mv $@-t $@
+virtlogd.service: logging/virtlogd.service.in $(top_builddir)/config.status
+ $(AM_V_GEN)sed \
+ -e 's|[@]sbindir[@]|$(sbindir)|g' \
+ < $< > $@-t && \
+ mv $@-t $@
+
+virtlogd.socket: logging/virtlogd.socket.in $(top_builddir)/config.status
+ $(AM_V_GEN)sed \
+ -e 's|[@]localstatedir[@]|$(localstatedir)|g' \
+ < $< > $@-t && \
+ mv $@-t $@
+
if WITH_SANLOCK
lockdriver_LTLIBRARIES += sanlock.la
diff --git a/src/logging/log_daemon.c b/src/logging/log_daemon.c
new file mode 100644
index 0000000..184076c
--- /dev/null
+++ b/src/logging/log_daemon.c
@@ -0,0 +1,1177 @@
+/*
+ * log_daemon.c: log management daemon
+ *
+ * Copyright (C) 2006-2015 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 "log_daemon.h"
+#include "log_daemon_config.h"
+#include "virutil.h"
+#include "virfile.h"
+#include "virpidfile.h"
+#include "virprocess.h"
+#include "virerror.h"
+#include "virlog.h"
+#include "viralloc.h"
+#include "virconf.h"
+#include "rpc/virnetdaemon.h"
+#include "virrandom.h"
+#include "virhash.h"
+#include "viruuid.h"
+#include "virstring.h"
+
+#include "log_daemon_dispatch.h"
+#include "log_protocol.h"
+
+#include "configmake.h"
+
+#define VIR_FROM_THIS VIR_FROM_LOGGING
+
+VIR_LOG_INIT("logging.log_daemon");
+
+struct _virLogDaemon {
+ virMutex lock;
+ virNetDaemonPtr dmn;
+ virNetServerPtr srv;
+};
+
+virLogDaemonPtr logDaemon = NULL;
+
+static bool execRestart;
+
+enum {
+ VIR_LOG_DAEMON_ERR_NONE = 0,
+ VIR_LOG_DAEMON_ERR_PIDFILE,
+ VIR_LOG_DAEMON_ERR_RUNDIR,
+ VIR_LOG_DAEMON_ERR_INIT,
+ VIR_LOG_DAEMON_ERR_SIGNAL,
+ VIR_LOG_DAEMON_ERR_PRIVS,
+ VIR_LOG_DAEMON_ERR_NETWORK,
+ VIR_LOG_DAEMON_ERR_CONFIG,
+ VIR_LOG_DAEMON_ERR_HOOKS,
+ VIR_LOG_DAEMON_ERR_REEXEC,
+
+ VIR_LOG_DAEMON_ERR_LAST
+};
+
+VIR_ENUM_DECL(virDaemonErr)
+VIR_ENUM_IMPL(virDaemonErr, VIR_LOG_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",
+ "Unable to re-execute daemon");
+
+static void *
+virLogDaemonClientNew(virNetServerClientPtr client,
+ void *opaque);
+static void
+virLogDaemonClientFree(void *opaque);
+
+static void *
+virLogDaemonClientNewPostExecRestart(virNetServerClientPtr client,
+ virJSONValuePtr object,
+ void *opaque);
+static virJSONValuePtr
+virLogDaemonClientPreExecRestart(virNetServerClientPtr client,
+ void *opaque);
+
+static void
+virLogDaemonFree(virLogDaemonPtr logd)
+{
+ if (!logd)
+ return;
+
+ virObjectUnref(logd->srv);
+ virObjectUnref(logd->dmn);
+
+ VIR_FREE(logd);
+}
+
+
+static virLogDaemonPtr
+virLogDaemonNew(virLogDaemonConfigPtr config, bool privileged)
+{
+ virLogDaemonPtr logd;
+
+ if (VIR_ALLOC(logd) < 0)
+ return NULL;
+
+ if (virMutexInit(&logd->lock) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to initialize mutex"));
+ VIR_FREE(logd);
+ return NULL;
+ }
+
+ if (!(logd->srv = virNetServerNew(1, 1, 0, config->max_clients,
+ config->max_clients, -1, 0,
+ NULL,
+ virLogDaemonClientNew,
+ virLogDaemonClientPreExecRestart,
+ virLogDaemonClientFree,
+ (void*)(intptr_t)(privileged ? 0x1 : 0x0))))
+ goto error;
+
+ if (!(logd->dmn = virNetDaemonNew()) ||
+ virNetDaemonAddServer(logd->dmn, logd->srv) < 0)
+ goto error;
+
+ return logd;
+
+ error:
+ virLogDaemonFree(logd);
+ return NULL;
+}
+
+
+static virLogDaemonPtr
+virLogDaemonNewPostExecRestart(virJSONValuePtr object, bool privileged)
+{
+ virLogDaemonPtr logd;
+ virJSONValuePtr child;
+
+ if (VIR_ALLOC(logd) < 0)
+ return NULL;
+
+ if (virMutexInit(&logd->lock) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to initialize mutex"));
+ VIR_FREE(logd);
+ return NULL;
+ }
+
+ if (!(child = virJSONValueObjectGet(object, "daemon"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Malformed daemon data from JSON file"));
+ goto error;
+ }
+
+ if (!(logd->dmn = virNetDaemonNewPostExecRestart(child)))
+ goto error;
+
+ if (!(logd->srv = virNetDaemonAddServerPostExec(logd->dmn,
+ virLogDaemonClientNew,
+
virLogDaemonClientNewPostExecRestart,
+ virLogDaemonClientPreExecRestart,
+ virLogDaemonClientFree,
+ (void*)(intptr_t)(privileged ? 0x1 :
0x0))))
+ goto error;
+
+ return logd;
+
+ error:
+ virLogDaemonFree(logd);
+ return NULL;
+}
+
+
+static int
+virLogDaemonForkIntoBackground(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
+virLogDaemonUnixSocketPaths(bool privileged,
+ char **sockfile)
+{
+ if (privileged) {
+ if (VIR_STRDUP(*sockfile, LOCALSTATEDIR "/run/libvirt/virtlogd-sock")
< 0)
+ goto error;
+ } 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/virtlogd-sock", rundir) < 0) {
+ VIR_FREE(rundir);
+ goto error;
+ }
+
+ VIR_FREE(rundir);
+ }
+ return 0;
+
+ error:
+ return -1;
+}
+
+
+static void
+virLogDaemonErrorHandler(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
+virLogDaemonSetupLogging(virLogDaemonConfigPtr 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();
+
+ 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 error;
+ 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/virtlogd.log",
+ virLogGetDefaultPriority(),
+ LOCALSTATEDIR) == -1)
+ goto error;
+ } 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/virtlogd.log",
+ virLogGetDefaultPriority(), logdir) == -1) {
+ VIR_FREE(logdir);
+ goto error;
+ }
+ VIR_FREE(logdir);
+ }
+ } else {
+ if (virAsprintf(&tmp, "%d:stderr", virLogGetDefaultPriority())
< 0)
+ goto error;
+ }
+ virLogParseOutputs(tmp);
+ VIR_FREE(tmp);
+ }
+
+ /*
+ * Command line override for --verbose
+ */
+ if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO))
+ virLogSetDefaultPriority(VIR_LOG_INFO);
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+
+
+/* Display version information. */
+static void
+virLogDaemonVersion(const char *argv0)
+{
+ printf("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION);
+}
+
+static void
+virLogDaemonShutdownHandler(virNetDaemonPtr dmn,
+ siginfo_t *sig ATTRIBUTE_UNUSED,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ virNetDaemonQuit(dmn);
+}
+
+static void
+virLogDaemonExecRestartHandler(virNetDaemonPtr dmn,
+ siginfo_t *sig ATTRIBUTE_UNUSED,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ execRestart = true;
+ virNetDaemonQuit(dmn);
+}
+
+static int
+virLogDaemonSetupSignals(virNetDaemonPtr dmn)
+{
+ if (virNetDaemonAddSignalHandler(dmn, SIGINT, virLogDaemonShutdownHandler, NULL) <
0)
+ return -1;
+ if (virNetDaemonAddSignalHandler(dmn, SIGQUIT, virLogDaemonShutdownHandler, NULL)
< 0)
+ return -1;
+ if (virNetDaemonAddSignalHandler(dmn, SIGTERM, virLogDaemonShutdownHandler, NULL)
< 0)
+ return -1;
+ if (virNetDaemonAddSignalHandler(dmn, SIGUSR1, virLogDaemonExecRestartHandler, NULL)
< 0)
+ return -1;
+ return 0;
+}
+
+
+static int
+virLogDaemonSetupNetworkingSystemD(virNetServerPtr srv)
+{
+ virNetServerServicePtr svc;
+ unsigned int nfds;
+
+ if ((nfds = virGetListenFDs()) == 0)
+ return 0;
+ if (nfds > 1)
+ VIR_DEBUG("Too many (%d) file descriptors from systemd", nfds);
+ nfds = 1;
+
+ /* Systemd passes FDs, starting immediately after stderr,
+ * so the first FD we'll get is '3'. */
+ if (!(svc = virNetServerServiceNewFD(3, 0,
+#if WITH_GNUTLS
+ NULL,
+#endif
+ false, 0, 1)))
+ return -1;
+
+ if (virNetServerAddService(srv, svc, NULL) < 0) {
+ virObjectUnref(svc);
+ return -1;
+ }
+ return 1;
+}
+
+
+static int
+virLogDaemonSetupNetworkingNative(virNetServerPtr srv, const char *sock_path)
+{
+ virNetServerServicePtr svc;
+
+ VIR_DEBUG("Setting up networking natively");
+
+ if (!(svc = virNetServerServiceNewUNIX(sock_path, 0700, 0, 0,
+#if WITH_GNUTLS
+ NULL,
+#endif
+ false, 0, 1)))
+ return -1;
+
+ if (virNetServerAddService(srv, svc, NULL) < 0) {
+ virObjectUnref(svc);
+ return -1;
+ }
+ return 0;
+}
+
+
+static void
+virLogDaemonClientFree(void *opaque)
+{
+ virLogDaemonClientPtr 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 *
+virLogDaemonClientNew(virNetServerClientPtr client,
+ void *opaque)
+{
+ virLogDaemonClientPtr priv;
+ uid_t clientuid;
+ gid_t clientgid;
+ unsigned long long timestamp;
+ bool privileged = opaque != NULL;
+
+ if (VIR_ALLOC(priv) < 0)
+ return NULL;
+
+ if (virMutexInit(&priv->lock) < 0) {
+ VIR_FREE(priv);
+ virReportSystemError(errno, "%s", _("unable to init
mutex"));
+ return NULL;
+ }
+
+ if (virNetServerClientGetUNIXIdentity(client,
+ &clientuid,
+ &clientgid,
+ &priv->clientPid,
+ ×tamp) < 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) {
+ virReportRestrictedError(_("Disallowing client %llu with uid
%llu"),
+ (unsigned long long)priv->clientPid,
+ (unsigned long long)clientuid);
+ goto error;
+ }
+ } else {
+ if (clientuid != 0) {
+ virReportRestrictedError(_("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 *
+virLogDaemonClientNewPostExecRestart(virNetServerClientPtr client,
+ virJSONValuePtr object ATTRIBUTE_UNUSED,
+ void *opaque)
+{
+ virLogDaemonClientPtr priv = virLogDaemonClientNew(client, opaque);
+
+ if (!priv)
+ return NULL;
+
+ return priv;
+}
+
+
+static virJSONValuePtr
+virLogDaemonClientPreExecRestart(virNetServerClientPtr client ATTRIBUTE_UNUSED,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ virJSONValuePtr object = virJSONValueNewObject();
+
+ if (!object)
+ return NULL;
+
+ return object;
+}
+
+
+static int
+virLogDaemonExecRestartStatePath(bool privileged,
+ char **state_file)
+{
+ if (privileged) {
+ if (VIR_STRDUP(*state_file, LOCALSTATEDIR
"/run/virtlogd-restart-exec.json") < 0)
+ goto error;
+ } else {
+ char *rundir = NULL;
+ mode_t old_umask;
+
+ if (!(rundir = virGetUserRuntimeDirectory()))
+ goto error;
+
+ old_umask = umask(077);
+ if (virFileMakePath(rundir) < 0) {
+ umask(old_umask);
+ VIR_FREE(rundir);
+ goto error;
+ }
+ umask(old_umask);
+
+ if (virAsprintf(state_file, "%s/virtlogd-restart-exec.json", rundir)
< 0) {
+ VIR_FREE(rundir);
+ goto error;
+ }
+
+ VIR_FREE(rundir);
+ }
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+
+static char *
+virLogDaemonGetExecRestartMagic(void)
+{
+ char *ret;
+
+ ignore_value(virAsprintf(&ret, "%lld", (long long int)getpid()));
+ return ret;
+}
+
+
+static int
+virLogDaemonPostExecRestart(const char *state_file,
+ const char *pid_file,
+ int *pid_file_fd,
+ bool privileged)
+{
+ const char *gotmagic;
+ char *wantmagic = NULL;
+ int ret = -1;
+ char *state = NULL;
+ virJSONValuePtr object = NULL;
+
+ VIR_DEBUG("Running post-restart exec");
+
+ if (!virFileExists(state_file)) {
+ VIR_DEBUG("No restart state file %s present",
+ state_file);
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (virFileReadAll(state_file,
+ 1024 * 1024 * 10, /* 10 MB */
+ &state) < 0)
+ goto cleanup;
+
+ VIR_DEBUG("Loading state %s", state);
+
+ if (!(object = virJSONValueFromString(state)))
+ goto cleanup;
+
+ gotmagic = virJSONValueObjectGetString(object, "magic");
+ if (!gotmagic) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Missing magic data in JSON document"));
+ goto cleanup;
+ }
+
+ if (!(wantmagic = virLogDaemonGetExecRestartMagic()))
+ goto cleanup;
+
+ if (STRNEQ(gotmagic, wantmagic)) {
+ VIR_WARN("Found restart exec file with old magic %s vs wanted %s",
+ gotmagic, wantmagic);
+ ret = 0;
+ goto cleanup;
+ }
+
+ /* Re-claim PID file now as we will not be daemonizing */
+ if (pid_file &&
+ (*pid_file_fd = virPidFileAcquirePath(pid_file, false, getpid())) < 0)
+ goto cleanup;
+
+ if (!(logDaemon = virLogDaemonNewPostExecRestart(object, privileged)))
+ goto cleanup;
+
+ ret = 1;
+
+ cleanup:
+ unlink(state_file);
+ VIR_FREE(wantmagic);
+ VIR_FREE(state);
+ virJSONValueFree(object);
+ return ret;
+}
+
+
+static int
+virLogDaemonPreExecRestart(const char *state_file,
+ virNetDaemonPtr dmn,
+ char **argv)
+{
+ virJSONValuePtr child;
+ char *state = NULL;
+ int ret = -1;
+ virJSONValuePtr object;
+ char *magic;
+ virHashKeyValuePairPtr pairs = NULL;
+
+ VIR_DEBUG("Running pre-restart exec");
+
+ if (!(object = virJSONValueNewObject()))
+ goto cleanup;
+
+ if (!(child = virNetDaemonPreExecRestart(dmn)))
+ goto cleanup;
+
+ if (virJSONValueObjectAppend(object, "daemon", child) < 0) {
+ virJSONValueFree(child);
+ goto cleanup;
+ }
+
+ if (!(magic = virLogDaemonGetExecRestartMagic()))
+ goto cleanup;
+
+ if (virJSONValueObjectAppendString(object, "magic", magic) < 0) {
+ VIR_FREE(magic);
+ goto cleanup;
+ }
+
+ if (!(state = virJSONValueToString(object, true)))
+ goto cleanup;
+
+ VIR_DEBUG("Saving state %s", state);
+
+ if (virFileWriteStr(state_file,
+ state, 0700) < 0) {
+ virReportSystemError(errno,
+ _("Unable to save state file %s"),
+ state_file);
+ goto cleanup;
+ }
+
+ if (execvp(argv[0], argv) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Unable to restart self"));
+ goto cleanup;
+ }
+
+ abort(); /* This should be impossible to reach */
+
+ cleanup:
+ VIR_FREE(pairs);
+ VIR_FREE(state);
+ virJSONValueFree(object);
+ return ret;
+}
+
+
+static void
+virLogDaemonUsage(const char *argv0, bool privileged)
+{
+ fprintf(stderr,
+ _("\n"
+ "Usage:\n"
+ " %s [options]\n"
+ "\n"
+ "Options:\n"
+ " -h | --help Display program help:\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"
+ " -V | --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/virtlogd.conf\n"
+ "\n"
+ " Sockets:\n"
+ " %s/run/libvirt/virtlogd-sock\n"
+ "\n"
+ " PID file (unless overridden by -p):\n"
+ " %s/run/virtlogd.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/virtlogd.conf\n"
+ "\n"
+ " Sockets:\n"
+ " $XDG_RUNTIME_DIR/libvirt/virtlogd-sock\n"
+ "\n"
+ " PID file:\n"
+ " $XDG_RUNTIME_DIR/libvirt/virtlogd.pid\n"
+ "\n"));
+ }
+}
+
+#define MAX_LISTEN 5
+int main(int argc, char **argv) {
+ virNetServerProgramPtr logProgram = NULL;
+ 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;
+ int timeout = -1; /* -t: Shutdown timeout */
+ char *state_file = NULL;
+ bool implicit_conf = false;
+ mode_t old_umask;
+ bool privileged = false;
+ virLogDaemonConfigPtr config = NULL;
+ int rv;
+
+ struct option opts[] = {
+ { "verbose", no_argument, &verbose, 'v'},
+ { "daemon", no_argument, &godaemon, 'd'},
+ { "config", required_argument, NULL, 'f'},
+ { "timeout", required_argument, NULL, 't'},
+ { "pid-file", required_argument, NULL, 'p'},
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ {0, 0, 0, 0}
+ };
+
+ privileged = geteuid() == 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;
+ char *tmp;
+
+ c = getopt_long(argc, argv, "ldf:p:t:vVh", 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 't':
+ if (virStrToLong_i(optarg, &tmp, 10, &timeout) != 0
+ || timeout <= 0
+ /* Ensure that we can multiply by 1000 without overflowing. */
+ || timeout > INT_MAX / 1000) {
+ VIR_ERROR(_("Invalid value for timeout"));
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+ case 'p':
+ VIR_FREE(pid_file);
+ if (VIR_STRDUP_QUIET(pid_file, optarg) < 0)
+ goto no_memory;
+ break;
+
+ case 'f':
+ VIR_FREE(remote_config_file);
+ if (VIR_STRDUP_QUIET(remote_config_file, optarg) < 0)
+ goto no_memory;
+ break;
+
+ case 'V':
+ virLogDaemonVersion(argv[0]);
+ exit(EXIT_SUCCESS);
+
+ case 'h':
+ virLogDaemonUsage(argv[0], privileged);
+ exit(EXIT_SUCCESS);
+
+ case '?':
+ default:
+ virLogDaemonUsage(argv[0], privileged);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ virFileActivateDirOverride(argv[0]);
+
+ if (!(config = virLogDaemonConfigNew(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 (virLogDaemonConfigFilePath(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 &&
+ virLogDaemonConfigLoadFile(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 (virLogDaemonSetupLogging(config, privileged, verbose, godaemon) < 0) {
+ VIR_ERROR(_("Can't initialize logging"));
+ exit(EXIT_FAILURE);
+ }
+
+ if (!pid_file &&
+ virPidFileConstructPath(privileged,
+ LOCALSTATEDIR,
+ "virtlogd",
+ &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 (virLogDaemonUnixSocketPaths(privileged,
+ &sock_file) < 0) {
+ VIR_ERROR(_("Can't determine socket paths"));
+ exit(EXIT_FAILURE);
+ }
+ VIR_DEBUG("Decided on socket paths '%s'",
+ sock_file);
+
+ if (virLogDaemonExecRestartStatePath(privileged,
+ &state_file) < 0) {
+ VIR_ERROR(_("Can't determine restart state file path"));
+ exit(EXIT_FAILURE);
+ }
+ VIR_DEBUG("Decided on restart state file path '%s'",
+ state_file);
+
+ /* Ensure the rundir exists (on tmpfs on some systems) */
+ if (privileged) {
+ if (VIR_STRDUP_QUIET(run_dir, LOCALSTATEDIR "/run/libvirt") < 0)
+ goto no_memory;
+ } else {
+ if (!(run_dir = virGetUserRuntimeDirectory())) {
+ VIR_ERROR(_("Can't determine user directory"));
+ 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_LOG_DAEMON_ERR_RUNDIR;
+ goto cleanup;
+ }
+ umask(old_umask);
+
+ if ((rv = virLogDaemonPostExecRestart(state_file,
+ pid_file,
+ &pid_file_fd,
+ privileged)) < 0) {
+ ret = VIR_LOG_DAEMON_ERR_INIT;
+ goto cleanup;
+ }
+
+ /* rv == 1, means we setup everything from saved state,
+ * so only (possibly) daemonize and setup stuff from
+ * scratch if rv == 0
+ */
+ if (rv == 0) {
+ 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 = virLogDaemonForkIntoBackground(argv[0])) < 0) {
+ VIR_ERROR(_("Failed to fork as daemon: %s"),
+ virStrerror(errno, ebuf, sizeof(ebuf)));
+ goto cleanup;
+ }
+ }
+
+ /* If we have a pidfile set, claim it now, exiting if already taken */
+ if ((pid_file_fd = virPidFileAcquirePath(pid_file, false, getpid())) < 0) {
+ ret = VIR_LOG_DAEMON_ERR_PIDFILE;
+ goto cleanup;
+ }
+
+ if (!(logDaemon = virLogDaemonNew(config, privileged))) {
+ ret = VIR_LOG_DAEMON_ERR_INIT;
+ goto cleanup;
+ }
+
+ if ((rv = virLogDaemonSetupNetworkingSystemD(logDaemon->srv)) < 0) {
+ ret = VIR_LOG_DAEMON_ERR_NETWORK;
+ goto cleanup;
+ }
+
+ /* Only do this, if systemd did not pass a FD */
+ if (rv == 0 &&
+ virLogDaemonSetupNetworkingNative(logDaemon->srv, sock_file) < 0) {
+ ret = VIR_LOG_DAEMON_ERR_NETWORK;
+ goto cleanup;
+ }
+ }
+
+ if (timeout != -1) {
+ VIR_DEBUG("Registering shutdown timeout %d", timeout);
+ virNetDaemonAutoShutdown(logDaemon->dmn,
+ timeout);
+ }
+
+ if ((virLogDaemonSetupSignals(logDaemon->dmn)) < 0) {
+ ret = VIR_LOG_DAEMON_ERR_SIGNAL;
+ goto cleanup;
+ }
+
+ if (!(logProgram = virNetServerProgramNew(VIR_LOG_MANAGER_PROTOCOL_PROGRAM,
+ VIR_LOG_MANAGER_PROTOCOL_PROGRAM_VERSION,
+ virLogManagerProtocolProcs,
+ virLogManagerProtocolNProcs))) {
+ ret = VIR_LOG_DAEMON_ERR_INIT;
+ goto cleanup;
+ }
+ if (virNetServerAddProgram(logDaemon->srv, logProgram) < 0) {
+ ret = VIR_LOG_DAEMON_ERR_INIT;
+ goto cleanup;
+ }
+
+ /* Disable error func, now logging is setup */
+ virSetErrorFunc(NULL, virLogDaemonErrorHandler);
+
+
+ /* 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(logDaemon->srv, true);
+ virNetDaemonRun(logDaemon->dmn);
+
+ if (execRestart &&
+ virLogDaemonPreExecRestart(state_file,
+ logDaemon->dmn,
+ argv) < 0)
+ ret = VIR_LOG_DAEMON_ERR_REEXEC;
+ else
+ ret = 0;
+
+ cleanup:
+ virObjectUnref(logProgram);
+ virLogDaemonFree(logDaemon);
+ 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_file);
+ VIR_FREE(run_dir);
+ return ret;
+
+ no_memory:
+ VIR_ERROR(_("Can't allocate memory"));
+ exit(EXIT_FAILURE);
+}
diff --git a/src/logging/log_daemon.h b/src/logging/log_daemon.h
new file mode 100644
index 0000000..a153160
--- /dev/null
+++ b/src/logging/log_daemon.h
@@ -0,0 +1,42 @@
+/*
+ * log_daemon.h: log management daemon
+ *
+ * Copyright (C) 2006-2015 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_LOG_DAEMON_H__
+# define __VIR_LOG_DAEMON_H__
+
+# include "virthread.h"
+
+typedef struct _virLogDaemon virLogDaemon;
+typedef virLogDaemon *virLogDaemonPtr;
+
+typedef struct _virLogDaemonClient virLogDaemonClient;
+typedef virLogDaemonClient *virLogDaemonClientPtr;
+
+struct _virLogDaemonClient {
+ virMutex lock;
+
+ pid_t clientPid;
+};
+
+extern virLogDaemonPtr logDaemon;
+
+#endif /* __VIR_LOG_DAEMON_H__ */
diff --git a/src/logging/log_daemon_config.c b/src/logging/log_daemon_config.c
new file mode 100644
index 0000000..98d4c89
--- /dev/null
+++ b/src/logging/log_daemon_config.c
@@ -0,0 +1,203 @@
+/*
+ * log_daemon_config.c: virtlogd config file handling
+ *
+ * Copyright (C) 2006-2015 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(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include "log_daemon_config.h"
+#include "virconf.h"
+#include "viralloc.h"
+#include "virerror.h"
+#include "virlog.h"
+#include "rpc/virnetserver.h"
+#include "configmake.h"
+#include "virstring.h"
+#include "virutil.h"
+
+#define VIR_FROM_THIS VIR_FROM_CONF
+
+VIR_LOG_INIT("logging.log_daemon_config");
+
+
+/* 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,
+ virConfTypeToString(p->type),
+ virConfTypeToString(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 VIR_STRDUP succeeds,
+ store the result in var_name. Otherwise, (i.e. invalid type, or VIR_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 (VIR_STRDUP(data->var_name, p->str) < 0) \
+ goto error; \
+ } \
+ } while (0)
+
+/* Like GET_CONF_STR, but for signed integer values. */
+#define GET_CONF_INT(conf, filename, var_name) \
+ do { \
+ virConfValuePtr p = virConfGetValue(conf, #var_name); \
+ if (p) { \
+ if (p->type != VIR_CONF_ULONG && \
+ checkType(p, filename, #var_name, VIR_CONF_LONG) < 0) \
+ goto error; \
+ data->var_name = p->l; \
+ } \
+ } while (0)
+
+/* Like GET_CONF_STR, but for unsigned integer values. */
+#define GET_CONF_UINT(conf, filename, var_name) \
+ do { \
+ virConfValuePtr p = virConfGetValue(conf, #var_name); \
+ if (p) { \
+ if (checkType(p, filename, #var_name, VIR_CONF_ULONG) < 0) \
+ goto error; \
+ data->var_name = p->l; \
+ } \
+ } while (0)
+
+int
+virLogDaemonConfigFilePath(bool privileged, char **configfile)
+{
+ if (privileged) {
+ if (VIR_STRDUP(*configfile, SYSCONFDIR "/libvirt/virtlogd.conf") <
0)
+ goto error;
+ } else {
+ char *configdir = NULL;
+
+ if (!(configdir = virGetUserConfigDirectory()))
+ goto error;
+
+ if (virAsprintf(configfile, "%s/virtlogd.conf", configdir) < 0) {
+ VIR_FREE(configdir);
+ goto error;
+ }
+ VIR_FREE(configdir);
+ }
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+
+virLogDaemonConfigPtr
+virLogDaemonConfigNew(bool privileged ATTRIBUTE_UNUSED)
+{
+ virLogDaemonConfigPtr data;
+
+ if (VIR_ALLOC(data) < 0)
+ return NULL;
+
+ data->max_clients = 1024;
+
+ return data;
+}
+
+void
+virLogDaemonConfigFree(virLogDaemonConfigPtr data)
+{
+ if (!data)
+ return;
+
+ VIR_FREE(data->log_filters);
+ VIR_FREE(data->log_outputs);
+
+ VIR_FREE(data);
+}
+
+static int
+virLogDaemonConfigLoadOptions(virLogDaemonConfigPtr data,
+ const char *filename,
+ virConfPtr conf)
+{
+ GET_CONF_UINT(conf, filename, log_level);
+ GET_CONF_STR(conf, filename, log_filters);
+ GET_CONF_STR(conf, filename, log_outputs);
+ GET_CONF_UINT(conf, filename, max_clients);
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+
+/* Read the config file if it exists.
+ * Only used in the remote case, hence the name.
+ */
+int
+virLogDaemonConfigLoadFile(virLogDaemonConfigPtr 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 = virLogDaemonConfigLoadOptions(data, filename, conf);
+ virConfFree(conf);
+ return ret;
+}
+
+int virLogDaemonConfigLoadData(virLogDaemonConfigPtr data,
+ const char *filename,
+ const char *filedata)
+{
+ virConfPtr conf;
+ int ret;
+
+ conf = virConfReadMem(filedata, strlen(filedata), 0);
+ if (!conf)
+ return -1;
+
+ ret = virLogDaemonConfigLoadOptions(data, filename, conf);
+ virConfFree(conf);
+ return ret;
+}
diff --git a/src/logging/log_daemon_config.h b/src/logging/log_daemon_config.h
new file mode 100644
index 0000000..24cc631
--- /dev/null
+++ b/src/logging/log_daemon_config.h
@@ -0,0 +1,50 @@
+/*
+ * log_daemon_config.h: virtlogd config file handling
+ *
+ * Copyright (C) 2006-2015 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(a)redhat.com>
+ */
+
+#ifndef __VIR_LOG_DAEMON_CONFIG_H__
+# define __VIR_LOG_DAEMON_CONFIG_H__
+
+# include "internal.h"
+
+typedef struct _virLogDaemonConfig virLogDaemonConfig;
+typedef virLogDaemonConfig *virLogDaemonConfigPtr;
+
+struct _virLogDaemonConfig {
+ int log_level;
+ char *log_filters;
+ char *log_outputs;
+ int max_clients;
+};
+
+
+int virLogDaemonConfigFilePath(bool privileged, char **configfile);
+virLogDaemonConfigPtr virLogDaemonConfigNew(bool privileged);
+void virLogDaemonConfigFree(virLogDaemonConfigPtr data);
+int virLogDaemonConfigLoadFile(virLogDaemonConfigPtr data,
+ const char *filename,
+ bool allow_missing);
+int virLogDaemonConfigLoadData(virLogDaemonConfigPtr data,
+ const char *filename,
+ const char *filedata);
+
+#endif /* __LIBVIRTD_CONFIG_H__ */
diff --git a/src/logging/log_daemon_dispatch.c b/src/logging/log_daemon_dispatch.c
new file mode 100644
index 0000000..98df178
--- /dev/null
+++ b/src/logging/log_daemon_dispatch.c
@@ -0,0 +1,37 @@
+/*
+ * log_daemon_dispatch.c: log management daemon dispatch
+ *
+ * Copyright (C) 2006-2015 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 "rpc/virnetserver.h"
+#include "rpc/virnetserverclient.h"
+#include "virlog.h"
+#include "virstring.h"
+#include "log_daemon.h"
+#include "log_protocol.h"
+#include "virerror.h"
+
+#define VIR_FROM_THIS VIR_FROM_RPC
+
+VIR_LOG_INIT("logging.log_daemon_dispatch");
+
+#include "log_daemon_dispatch_stubs.h"
diff --git a/src/logging/log_daemon_dispatch.h b/src/logging/log_daemon_dispatch.h
new file mode 100644
index 0000000..af3e3b4
--- /dev/null
+++ b/src/logging/log_daemon_dispatch.h
@@ -0,0 +1,31 @@
+/*
+ * log_daemon_dispatch.h: log management daemon dispatch
+ *
+ * Copyright (C) 2006-2015 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_LOG_DAEMON_DISPATCH_H__
+# define __VIR_LOG_DAEMON_DISPATCH_H__
+
+# include "rpc/virnetserverprogram.h"
+
+extern virNetServerProgramProc virLogManagerProtocolProcs[];
+extern size_t virLogManagerProtocolNProcs;
+
+#endif /* __VIR_LOG_DAEMON_DISPATCH_H__ */
diff --git a/src/logging/log_protocol.x b/src/logging/log_protocol.x
new file mode 100644
index 0000000..9b8fa41
--- /dev/null
+++ b/src/logging/log_protocol.x
@@ -0,0 +1,22 @@
+/* -*- c -*-
+ */
+
+%#include "internal.h"
+
+typedef opaque virLogManagerProtocolUUID[VIR_UUID_BUFLEN];
+
+/* Length of long, but not unbounded, strings.
+ * This is an arbitrary limit designed to stop the decoder from trying
+ * to allocate unbounded amounts of memory when fed with a bad message.
+ */
+const VIR_LOG_MANAGER_PROTOCOL_STRING_MAX = 65536;
+
+/* A long string, which may NOT be NULL. */
+typedef string
virLogManagerProtocolNonNullString<VIR_LOG_MANAGER_PROTOCOL_STRING_MAX>;
+
+/* A long string, which may be NULL. */
+typedef virLogManagerProtocolNonNullString *virLogManagerProtocolString;
+
+/* Define the program number, protocol version and procedure numbers here. */
+const VIR_LOG_MANAGER_PROTOCOL_PROGRAM = 0x87539319;
+const VIR_LOG_MANAGER_PROTOCOL_PROGRAM_VERSION = 1;
diff --git a/src/logging/test_virtlogd.aug.in b/src/logging/test_virtlogd.aug.in
new file mode 100644
index 0000000..cc659d2
--- /dev/null
+++ b/src/logging/test_virtlogd.aug.in
@@ -0,0 +1,12 @@
+module Test_virtlogd =
+ let conf = "log_level = 3
+log_filters=\"3:remote 4:event\"
+log_outputs=\"3:syslog:libvirtd\"
+log_buffer_size = 64
+"
+
+ test Virtlogd.lns get conf =
+ { "log_level" = "3" }
+ { "log_filters" = "3:remote 4:event" }
+ { "log_outputs" = "3:syslog:libvirtd" }
+ { "log_buffer_size" = "64" }
diff --git a/src/logging/virtlogd.aug b/src/logging/virtlogd.aug
new file mode 100644
index 0000000..eefba5b
--- /dev/null
+++ b/src/logging/virtlogd.aug
@@ -0,0 +1,45 @@
+(* /etc/libvirt/virtlogd.conf *)
+
+module Virtlogd =
+ autoload xfm
+
+ let eol = del /[ \t]*\n/ "\n"
+ let value_sep = del /[ \t]*=[ \t]*/ " = "
+ let indent = del /[ \t]*/ ""
+
+ let array_sep = del /,[ \t\n]*/ ", "
+ let array_start = del /\[[ \t\n]*/ "[ "
+ let array_end = del /\]/ "]"
+
+ let str_val = del /\"/ "\"" . store /[^\"]*/ . del /\"/
"\""
+ let bool_val = store /0|1/
+ let int_val = store /[0-9]+/
+ let str_array_element = [ seq "el" . str_val ] . del /[ \t\n]*/
""
+ let str_array_val = counter "el" . array_start . ( str_array_element . (
array_sep . str_array_element ) * ) ? . array_end
+
+ let str_entry (kw:string) = [ key kw . value_sep . str_val ]
+ let bool_entry (kw:string) = [ key kw . value_sep . bool_val ]
+ let int_entry (kw:string) = [ key kw . value_sep . int_val ]
+ let str_array_entry (kw:string) = [ key kw . value_sep . str_array_val ]
+
+
+ (* Config entry grouped by function - same order as example config *)
+ let logging_entry = int_entry "log_level"
+ | str_entry "log_filters"
+ | str_entry "log_outputs"
+ | int_entry "log_buffer_size"
+ | int_entry "max_clients"
+
+ (* Each enty in the config is one of the following three ... *)
+ let entry = logging_entry
+ let comment = [ label "#comment" . del /#[ \t]*/ "# " . store
/([^ \t\n][^\n]*)?/ . del /\n/ "\n" ]
+ let empty = [ label "#empty" . eol ]
+
+ let record = indent . entry . eol
+
+ let lns = ( record | comment | empty ) *
+
+ let filter = incl "/etc/libvirt/virtlogd.conf"
+ . Util.stdexcl
+
+ let xfm = transform lns filter
diff --git a/src/logging/virtlogd.conf b/src/logging/virtlogd.conf
new file mode 100644
index 0000000..609f67a
--- /dev/null
+++ b/src/logging/virtlogd.conf
@@ -0,0 +1,67 @@
+# Master virtlogd daemon configuration file
+#
+
+#################################################################
+#
+# Logging controls
+#
+
+# Logging level: 4 errors, 3 warnings, 2 information, 1 debug
+# basically 1 will log everything possible
+#log_level = 3
+
+# Logging filters:
+# A filter allows to select a different logging level for a given category
+# of logs
+# The format for a filter is one of:
+# x:name
+# x:+name
+# where name is a string which is matched against source file name,
+# e.g., "remote", "qemu", or "util/json", the optional
"+" prefix
+# tells libvirt to log stack trace for each message matching name,
+# and x is the minimal level where matching messages should be logged:
+# 1: DEBUG
+# 2: INFO
+# 3: WARNING
+# 4: ERROR
+#
+# Multiple filter can be defined in a single @filters, they just need to be
+# separated by spaces.
+#
+# e.g. to only get warning or errors from the remote layer and only errors
+# from the event layer:
+#log_filters="3:remote 4:event"
+
+# Logging outputs:
+# An output is one of the places to save logging information
+# The format for an output can be:
+# x:stderr
+# output goes to stderr
+# x:syslog:name
+# use syslog for the output and use the given name as the ident
+# x:file:file_path
+# output to a file, with the given filepath
+# In all case the x prefix is the minimal level, acting as a filter
+# 1: DEBUG
+# 2: INFO
+# 3: WARNING
+# 4: ERROR
+#
+# Multiple output can be defined, they just need to be separated by spaces.
+# e.g. to log all warnings and errors to syslog under the virtlogd ident:
+#log_outputs="3:syslog:virtlogd"
+#
+
+# Log debug buffer size:
+#
+# This configuration option is no longer used, since the global
+# log buffer functionality has been removed. Please configure
+# suitable log_outputs/log_filters settings to obtain logs.
+#log_buffer_size = 64
+
+# The maximum number of concurrent client connections to allow
+# over all sockets combined.
+# Each running virtual machine will require one open connection
+# to virtlogd. So 'max_clients' will affect how many VMs can
+# be run on a host
+#max_clients = 1024
diff --git a/src/logging/virtlogd.init.in b/src/logging/virtlogd.init.in
new file mode 100644
index 0000000..1408236
--- /dev/null
+++ b/src/logging/virtlogd.init.in
@@ -0,0 +1,94 @@
+#!/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: virtlogd
+# Default-Start:
+# Default-Stop: 0 1 2 3 4 5 6
+# Short-Description: virtual machine log manager
+# Description: This is a daemon for managing logs
+# of virtual machine consoles
+### END INIT INFO
+
+# the following is chkconfig init header
+#
+# virtlogd: virtual machine log manager
+#
+# chkconfig: - 96 04
+# description: This is a daemon for managing logs \
+# of virtual machine consoles
+#
+# processname: virtlogd
+# pidfile: @localstatedir(a)/run/virtlogd.pid
+#
+
+# Source function library.
+. @sysconfdir(a)/rc.d/init.d/functions
+
+SERVICE=virtlogd
+PROCESS=virtlogd
+PIDFILE=@localstatedir(a)/run/$SERVICE.pid
+
+VIRTLOGD_ARGS=
+
+test -f @sysconfdir@/sysconfig/virtlogd && . @sysconfdir@/sysconfig/virtlogd
+
+RETVAL=0
+
+start() {
+ echo -n $"Starting $SERVICE daemon: "
+ daemon --pidfile $PIDFILE --check $SERVICE $PROCESS --daemon $VIRTLOGD_ARGS
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && touch @localstatedir@/log/subsys/$SERVICE
+}
+
+stop() {
+ echo -n $"Stopping $SERVICE daemon: "
+
+ killproc -p $PIDFILE $PROCESS
+ RETVAL=$?
+ echo
+ if [ $RETVAL -eq 0 ]; then
+ rm -f @localstatedir@/log/subsys/$SERVICE
+ rm -f $PIDFILE
+ fi
+}
+
+restart() {
+ stop
+ start
+}
+
+reload() {
+ echo -n $"Reloading $SERVICE configuration: "
+
+ killproc -p $PIDFILE $PROCESS -USR1
+ 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@/log/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/logging/virtlogd.pod.in b/src/logging/virtlogd.pod.in
new file mode 100644
index 0000000..bba7714
--- /dev/null
+++ b/src/logging/virtlogd.pod.in
@@ -0,0 +1,162 @@
+=head1 NAME
+
+virtlogd - libvirt log management daemon
+
+=head1 SYNOPSIS
+
+B<virtlogd> [ -dv ] [ -f config_file ] [ -p pid_file ]
+
+B<virtlogd> --version
+
+=head1 DESCRIPTION
+
+The B<virtlogd> program is a server side daemon component of the libvirt
+virtualization management system that is used to manage logs from virtual
+machine consoles.
+
+This daemon is not used directly by libvirt client applications, rather it
+is called on their behalf by B<libvirtd>. By maintaining the logs in a
+standalone daemon, the main libvirtd daemon can be restarted without risk
+of losing logs. The B<virtlogd> daemon has the ability to re-exec()
+itself upon receiving SIGUSR1, to allow live upgrades without downtime.
+
+The virtlogd daemon listens for requests on a local Unix domain socket.
+
+=head1 OPTIONS
+
+=over
+
+=item B<-h, --help>
+
+Display command line help usage then exit.
+
+=item B<-d, --daemon>
+
+Run as a daemon and write PID file.
+
+=item B<-f, --config> I<FILE>
+
+Use this configuration file, overriding the default value.
+
+=item B<-p, --pid-file> I<FILE>
+
+Use this name for the PID file, overriding the default value.
+
+=item B<-v, --verbose>
+
+Enable output of verbose messages.
+
+=item B<-V, --version>
+
+Display version information then exit.
+
+=back
+
+=head1 SIGNALS
+
+On receipt of B<SIGUSR1> virtlogd will re-exec() its binary, while
+maintaining all current logs and clients. This allows for live
+upgrades of the virtlogd service.
+
+=head1 FILES
+
+=head2 When run as B<root>.
+
+=over
+
+=item F<SYSCONFDIR/virtlogd.conf>
+
+The default configuration file used by virtlogd, unless overridden on the
+command line using the B<-f>|B<--config> option.
+
+=item F<LOCALSTATEDIR/run/libvirt/virtlogd-sock>
+
+The sockets libvirtd will use.
+
+=item F<LOCALSTATEDIR/run/virtlogd.pid>
+
+The PID file to use, unless overridden by the B<-p>|B<--pid-file> option.
+
+=back
+
+=head2 When run as B<non-root>.
+
+=over
+
+=item F<$XDG_CONFIG_HOME/virtlogd.conf>
+
+The default configuration file used by libvirtd, unless overridden on the
+command line using the B<-f>|B<--config> option.
+
+=item F<$XDG_RUNTIME_DIR/libvirt/virtlogd-sock>
+
+The socket libvirtd will use.
+
+=item F<$XDG_RUNTIME_DIR/libvirt/virtlogd.pid>
+
+The PID file to use, unless overridden by the B<-p>|B<--pid-file> option.
+
+=item If $XDG_CONFIG_HOME is not set in your environment, libvirtd will use
F<$HOME/.config>
+
+=item If $XDG_RUNTIME_DIR is not set in your environment, libvirtd will use
F<$HOME/.cache>
+
+=back
+
+=head1 EXAMPLES
+
+To retrieve the version of virtlogd:
+
+ # virtlogd --version
+ virtlogd (libvirt) 1.1.1
+ #
+
+To start virtlogd, instructing it to daemonize and create a PID file:
+
+ # virtlogd -d
+ # ls -la LOCALSTATEDIR/run/virtlogd.pid
+ -rw-r--r-- 1 root root 6 Jul 9 02:40 LOCALSTATEDIR/run/virtlogd.pid
+ #
+
+=head1 BUGS
+
+Please report all bugs you discover. This should be done via either:
+
+=over
+
+=item a) the mailing list
+
+L<http://libvirt.org/contact.html>
+
+=item or,
+
+B<>
+
+=item b) the bug tracker
+
+L<http://libvirt.org/bugs.html>
+
+=item Alternatively, you may report bugs to your software distributor / vendor.
+
+=back
+
+=head1 AUTHORS
+
+Please refer to the AUTHORS file distributed with libvirt.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006-2015 Red Hat, Inc., and the authors listed in the
+libvirt AUTHORS file.
+
+=head1 LICENSE
+
+virtlogd is distributed under the terms of the GNU LGPL v2.1+.
+This is free software; see the source for copying conditions. There
+is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE
+
+=head1 SEE ALSO
+
+L<libvirtd(8)>,
L<http://www.libvirt.org/>
+
+=cut
diff --git a/src/logging/virtlogd.service.in b/src/logging/virtlogd.service.in
new file mode 100644
index 0000000..a264d3a
--- /dev/null
+++ b/src/logging/virtlogd.service.in
@@ -0,0 +1,17 @@
+[Unit]
+Description=Virtual machine log manager
+Requires=virtlogd.socket
+Documentation=man:virtlogd(8)
+Documentation=http://libvirt.org
+
+[Service]
+EnvironmentFile=-/etc/sysconfig/virtlogd
+ExecStart=@sbindir@/virtlogd $VIRTLOGD_ARGS
+ExecReload=/bin/kill -USR1 $MAINPID
+# Loosing the logs is a really bad thing that will
+# cause the machine to be fenced (rebooted), so make
+# sure we discourage OOM killer
+OOMScoreAdjust=-900
+
+[Install]
+Also=virtlogd.socket
diff --git a/src/logging/virtlogd.socket.in b/src/logging/virtlogd.socket.in
new file mode 100644
index 0000000..724976d
--- /dev/null
+++ b/src/logging/virtlogd.socket.in
@@ -0,0 +1,8 @@
+[Unit]
+Description=Virtual machine log manager socket
+
+[Socket]
+ListenStream=@localstatedir@/run/libvirt/virtlogd-sock
+
+[Install]
+WantedBy=sockets.target
diff --git a/src/logging/virtlogd.sysconf b/src/logging/virtlogd.sysconf
new file mode 100644
index 0000000..5886f35
--- /dev/null
+++ b/src/logging/virtlogd.sysconf
@@ -0,0 +1,3 @@
+#
+# Pass extra arguments to virtlogd
+#VIRTLOGD_ARGS=
diff --git a/src/util/virerror.c b/src/util/virerror.c
index 6dc05f4..098211a 100644
--- a/src/util/virerror.c
+++ b/src/util/virerror.c
@@ -134,6 +134,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
"Polkit", /* 60 */
"Thread jobs",
"Admin Interface",
+ "Log Manager",
)
--
2.5.0