libvirt/src/logging/log_daemon.c

997 lines
29 KiB
C
Raw Normal View History

/*
* 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;
virJSONValuePtr object = virJSONValueNewObject();
char *magic;
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(state);
virJSONValueFree(object);
return -1;
}
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;
}