[libvirt] [PATCH v2 02/13] Import stripped down virtlockd code as basis of virtlogd

Daniel P. Berrange berrange at redhat.com
Thu Nov 12 17:18:59 UTC 2015


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 at 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 at redhat.com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <locale.h>
+
+
+#include "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,
+                                          &timestamp) < 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 at 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 at 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 at 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 at 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 at 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-generic.html#INITSCRCOMCONV
+#
+### 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@/run/virtlogd.pid
+#
+
+# Source function library.
+. @sysconfdir@/rc.d/init.d/functions
+
+SERVICE=virtlogd
+PROCESS=virtlogd
+PIDFILE=@localstatedir@/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




More information about the libvir-list mailing list