On 11/12/2015 12:18 PM, Daniel P. Berrange wrote:
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
Full disclosure - the aspects of Makefiles, cfg files, spec files, etc.
- not my area of expertise... Looks like things were copied correctly
though and it does build... Whether it builds on all platforms for all
strange variants of make - I'll leave to existing build processes...
[...]
Hopefully some assumptions can be made regarding how much of this is
copied from lockd ;-)
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>
+ */
[...]
+static void
+virLogDaemonFree(virLogDaemonPtr logd)
+{
+ if (!logd)
+ return;
Should there be a virMutexDestroy(logd->lock); ? If so, it's also
missing from lockd
+
+ virObjectUnref(logd->srv);
+ virObjectUnref(logd->dmn);
+
+ VIR_FREE(logd);
+}
+
+
[...]
+
+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);
VIR_FREE(rundir); - I see this is true in lockd too...
+ 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);
VIR_FREE(logdir); - same in lockd
+ 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;
+}
+
+
+
[...]
+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:
Could use virLogDaemonClientFree()
+ virMutexDestroy(&priv->lock);
+ VIR_FREE(priv);
+ return NULL;
+}
+
+
[...]
+
+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);
s/lock/log/
+
+ 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);
^
Is 'l' valid? Same for lockd BTW
+
+ 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) {
^
Extra space
+ 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) {
^
Extra space (repeats a couple times...))
+ 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) {
extra space
+ 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;
should we umask(old_umask) here?
+ 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);
+
+
+
There's an extra line here.
/* 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);
+}
[...]
Admittedly I know very little about the log_protocol.x and other
src/logging/* files from here... I do note that test_virtlogd.aug.in
doesn't have 'max_clients', although virtlogd.aug has a reference. I
also note there's a log_buffer_size listed, but no corresponding element
in the virLogDaemonConfig (or of course read of such element).
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 ]
't' timeout?
'V' version (yes, I see --version)
+
+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>
+
't' 'timeout'...
+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
ACK - with a couple of minor adjustments.
John