libvirt/src/logging/log_daemon.c
Andrea Bolognani a1c793ec57 daemons: Support --timeout 0
When using systemd we want to take advantage of socket activation
instead of keeping daemons running all the time, so we default to
shutting them down after two minutes of inactivity.

At the same time, we want it to be possible for the admin to opt
out of this behavior and disable timeouts entirely. A very natural
way to do so would be to specify a zero-length timeout, but that's
currently not accepted by the command line parser. Address that.

Signed-off-by: Andrea Bolognani <abologna@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2020-04-03 11:50:27 +02:00

1000 lines
30 KiB
C

/*
* 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/>.
*/
#include <config.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <getopt.h>
#include "log_daemon.h"
#include "log_daemon_config.h"
#include "admin/admin_server_dispatch.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 "virgettext.h"
#include "virdaemon.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;
virLogHandlerPtr handler;
};
virLogDaemonPtr logDaemon = NULL;
static bool execRestart;
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->handler);
virMutexDestroy(&logd->lock);
virObjectUnref(logd->dmn);
VIR_FREE(logd);
}
static void
virLogDaemonInhibitor(bool inhibit, void *opaque)
{
virLogDaemonPtr dmn = opaque;
/* virtlogd uses inhibition only to stop session daemon being killed after
* the specified timeout, for the system daemon this is taken care of by
* libvirtd and the dependencies between the services. */
if (virNetDaemonIsPrivileged(dmn->dmn))
return;
if (inhibit)
virNetDaemonAddShutdownInhibition(dmn->dmn);
else
virNetDaemonRemoveShutdownInhibition(dmn->dmn);
}
static virLogDaemonPtr
virLogDaemonNew(virLogDaemonConfigPtr config, bool privileged)
{
virLogDaemonPtr logd;
virNetServerPtr srv = NULL;
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->dmn = virNetDaemonNew()))
goto error;
if (!(srv = virNetServerNew("virtlogd", 1,
0, 0, 0, config->max_clients,
config->max_clients, -1, 0,
virLogDaemonClientNew,
virLogDaemonClientPreExecRestart,
virLogDaemonClientFree,
(void*)(intptr_t)(privileged ? 0x1 : 0x0))))
goto error;
if (virNetDaemonAddServer(logd->dmn, srv) < 0)
goto error;
virObjectUnref(srv);
srv = NULL;
if (!(srv = virNetServerNew("admin", 1,
0, 0, 0, config->admin_max_clients,
config->admin_max_clients, -1, 0,
remoteAdmClientNew,
remoteAdmClientPreExecRestart,
remoteAdmClientFree,
logd->dmn)))
goto error;
if (virNetDaemonAddServer(logd->dmn, srv) < 0)
goto error;
virObjectUnref(srv);
srv = NULL;
if (!(logd->handler = virLogHandlerNew(privileged,
config->max_size,
config->max_backups,
virLogDaemonInhibitor,
logd)))
goto error;
return logd;
error:
virObjectUnref(srv);
virLogDaemonFree(logd);
return NULL;
}
virLogHandlerPtr
virLogDaemonGetHandler(virLogDaemonPtr dmn)
{
return dmn->handler;
}
static virNetServerPtr
virLogDaemonNewServerPostExecRestart(virNetDaemonPtr dmn,
const char *name,
virJSONValuePtr object,
void *opaque)
{
if (STREQ(name, "virtlogd")) {
return virNetServerNewPostExecRestart(object,
name,
virLogDaemonClientNew,
virLogDaemonClientNewPostExecRestart,
virLogDaemonClientPreExecRestart,
virLogDaemonClientFree,
opaque);
} else if (STREQ(name, "admin")) {
return virNetServerNewPostExecRestart(object,
name,
remoteAdmClientNew,
remoteAdmClientNewPostExecRestart,
remoteAdmClientPreExecRestart,
remoteAdmClientFree,
dmn);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected server name '%s' during restart"),
name);
return NULL;
}
}
static virLogDaemonPtr
virLogDaemonNewPostExecRestart(virJSONValuePtr object, bool privileged,
virLogDaemonConfigPtr config)
{
virLogDaemonPtr logd;
virJSONValuePtr child;
const char *serverNames[] = { "virtlogd" };
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,
G_N_ELEMENTS(serverNames),
serverNames,
virLogDaemonNewServerPostExecRestart,
(void*)(intptr_t)(privileged ? 0x1 : 0x0))))
goto error;
if (!(child = virJSONValueObjectGet(object, "handler"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Malformed daemon data from JSON file"));
goto error;
}
if (!(logd->handler = virLogHandlerNewPostExecRestart(child,
privileged,
config->max_size,
config->max_backups,
virLogDaemonInhibitor,
logd)))
goto error;
return logd;
error:
virLogDaemonFree(logd);
return NULL;
}
static void
virLogDaemonErrorHandler(void *opaque G_GNUC_UNUSED,
virErrorPtr err G_GNUC_UNUSED)
{
/* Don't do anything, since logging infrastructure already
* took care of reporting the error */
}
/* 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 G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED)
{
virNetDaemonQuit(dmn);
}
static void
virLogDaemonExecRestartHandler(virNetDaemonPtr dmn,
siginfo_t *sig G_GNUC_UNUSED,
void *opaque G_GNUC_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 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;
}
}
/* there's no closing handshake in the logging protocol */
virNetServerClientSetQuietEOF(client);
return priv;
error:
virLogDaemonClientFree(priv);
return NULL;
}
static void *
virLogDaemonClientNewPostExecRestart(virNetServerClientPtr client,
virJSONValuePtr object G_GNUC_UNUSED,
void *opaque)
{
virLogDaemonClientPtr priv = virLogDaemonClientNew(client, opaque);
if (!priv)
return NULL;
return priv;
}
static virJSONValuePtr
virLogDaemonClientPreExecRestart(virNetServerClientPtr client G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED)
{
virJSONValuePtr object = virJSONValueNewObject();
return object;
}
static int
virLogDaemonExecRestartStatePath(bool privileged,
char **state_file)
{
if (privileged) {
*state_file = g_strdup(RUNSTATEDIR "/virtlogd-restart-exec.json");
} else {
g_autofree char *rundir = NULL;
mode_t old_umask;
rundir = virGetUserRuntimeDirectory();
old_umask = umask(077);
if (virFileMakePath(rundir) < 0) {
umask(old_umask);
return -1;
}
umask(old_umask);
*state_file = g_strdup_printf("%s/virtlogd-restart-exec.json", rundir);
}
return 0;
}
static char *
virLogDaemonGetExecRestartMagic(void)
{
char *ret;
ret = g_strdup_printf("%lld", (long long int)getpid());
return ret;
}
static int
virLogDaemonPostExecRestart(const char *state_file,
const char *pid_file,
int *pid_file_fd,
bool privileged,
virLogDaemonConfigPtr config)
{
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,
config)))
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 = virJSONValueNewObject();
char *magic;
virHashKeyValuePairPtr pairs = NULL;
VIR_DEBUG("Running pre-restart exec");
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 (!(child = virLogHandlerPreExecRestart(logDaemon->handler)))
goto cleanup;
if (virJSONValueObjectAppend(object, "handler", child) < 0) {
virJSONValueFree(child);
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 log 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/libvirt/virtlogd-sock\n"
"\n"
" PID file (unless overridden by -p):\n"
" %s/virtlogd.pid\n"
"\n"),
SYSCONFDIR,
RUNSTATEDIR,
RUNSTATEDIR);
} 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"));
}
}
int main(int argc, char **argv) {
virNetServerPtr logSrv = NULL;
virNetServerPtr adminSrv = NULL;
virNetServerProgramPtr logProgram = NULL;
virNetServerProgramPtr adminProgram = 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;
char *admin_sock_file = NULL;
int timeout = 0; /* -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 (virGettextInitialize() < 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, "df: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);
pid_file = g_strdup(optarg);
break;
case 'f':
VIR_FREE(remote_config_file);
remote_config_file = g_strdup(optarg);
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);
}
}
virFileActivateDirOverrideForProg(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) {
VIR_ERROR(_("Can't load config file: %s: %s"),
virGetLastErrorMessage(), remote_config_file);
exit(EXIT_FAILURE);
}
virDaemonSetupLogging("virtlogd",
config->log_level,
config->log_filters,
config->log_outputs,
privileged,
verbose,
godaemon);
if (!pid_file &&
virPidFileConstructPath(privileged,
RUNSTATEDIR,
"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 (virDaemonUnixSocketPaths("virtlogd",
privileged,
NULL,
&sock_file,
NULL,
&admin_sock_file) < 0) {
VIR_ERROR(_("Can't determine socket paths"));
exit(EXIT_FAILURE);
}
VIR_DEBUG("Decided on socket paths '%s' and '%s'",
sock_file, admin_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) {
run_dir = g_strdup(RUNSTATEDIR "/libvirt");
} else {
run_dir = virGetUserRuntimeDirectory();
}
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) {
VIR_ERROR(_("unable to create rundir %s: %s"), run_dir,
g_strerror(errno));
ret = VIR_DAEMON_ERR_RUNDIR;
umask(old_umask);
goto cleanup;
}
umask(old_umask);
if ((rv = virLogDaemonPostExecRestart(state_file,
pid_file,
&pid_file_fd,
privileged,
config)) < 0) {
ret = VIR_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) {
g_autoptr(virSystemdActivation) act = NULL;
virSystemdActivationMap actmap[] = {
{ .name = "virtlogd.socket", .family = AF_UNIX, .path = sock_file },
{ .name = "virtlogd-admin.socket", .family = AF_UNIX, .path = admin_sock_file },
};
if (godaemon) {
if (chdir("/") < 0) {
VIR_ERROR(_("cannot change to root directory: %s"),
g_strerror(errno));
goto cleanup;
}
if ((statuswrite = virDaemonForkIntoBackground(argv[0])) < 0) {
VIR_ERROR(_("Failed to fork as daemon: %s"),
g_strerror(errno));
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_DAEMON_ERR_PIDFILE;
goto cleanup;
}
if (!(logDaemon = virLogDaemonNew(config, privileged))) {
ret = VIR_DAEMON_ERR_INIT;
goto cleanup;
}
if (virSystemdGetActivation(actmap,
G_N_ELEMENTS(actmap),
&act) < 0) {
ret = VIR_DAEMON_ERR_NETWORK;
goto cleanup;
}
logSrv = virNetDaemonGetServer(logDaemon->dmn, "virtlogd");
adminSrv = virNetDaemonGetServer(logDaemon->dmn, "admin");
if (virNetServerAddServiceUNIX(logSrv,
act, "virtlogd.socket",
sock_file, 0700, 0, 0,
NULL,
false, 0, 1) < 0) {
ret = VIR_DAEMON_ERR_NETWORK;
goto cleanup;
}
if (virNetServerAddServiceUNIX(adminSrv,
act, "virtlogd-admin.socket",
admin_sock_file, 0700, 0, 0,
NULL,
false, 0, 1) < 0) {
ret = VIR_DAEMON_ERR_NETWORK;
goto cleanup;
}
if (act &&
virSystemdActivationComplete(act) < 0) {
ret = VIR_DAEMON_ERR_NETWORK;
goto cleanup;
}
} else {
logSrv = virNetDaemonGetServer(logDaemon->dmn, "virtlogd");
/* If exec-restarting from old virtlogd, we won't have an
* admin server present */
if (virNetDaemonHasServer(logDaemon->dmn, "admin"))
adminSrv = virNetDaemonGetServer(logDaemon->dmn, "admin");
}
if (timeout > 0) {
VIR_DEBUG("Registering shutdown timeout %d", timeout);
virNetDaemonAutoShutdown(logDaemon->dmn,
timeout);
}
if ((virLogDaemonSetupSignals(logDaemon->dmn)) < 0) {
ret = VIR_DAEMON_ERR_SIGNAL;
goto cleanup;
}
if (!(logProgram = virNetServerProgramNew(VIR_LOG_MANAGER_PROTOCOL_PROGRAM,
VIR_LOG_MANAGER_PROTOCOL_PROGRAM_VERSION,
virLogManagerProtocolProcs,
virLogManagerProtocolNProcs))) {
ret = VIR_DAEMON_ERR_INIT;
goto cleanup;
}
if (virNetServerAddProgram(logSrv, logProgram) < 0) {
ret = VIR_DAEMON_ERR_INIT;
goto cleanup;
}
if (adminSrv != NULL) {
if (!(adminProgram = virNetServerProgramNew(ADMIN_PROGRAM,
ADMIN_PROTOCOL_VERSION,
adminProcs,
adminNProcs))) {
ret = VIR_DAEMON_ERR_INIT;
goto cleanup;
}
if (virNetServerAddProgram(adminSrv, adminProgram) < 0) {
ret = VIR_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 */
virNetDaemonUpdateServices(logDaemon->dmn, true);
virNetDaemonRun(logDaemon->dmn);
if (execRestart &&
virLogDaemonPreExecRestart(state_file,
logDaemon->dmn,
argv) < 0)
ret = VIR_DAEMON_ERR_REEXEC;
else
ret = 0;
cleanup:
virObjectUnref(logProgram);
virObjectUnref(adminProgram);
virObjectUnref(logSrv);
virObjectUnref(adminSrv);
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(admin_sock_file);
VIR_FREE(state_file);
VIR_FREE(run_dir);
VIR_FREE(remote_config_file);
virLogDaemonConfigFree(config);
return ret;
}