/*
* lock_daemon.c: lock 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
* .
*/
#include
#include
#include
#include
#include
#include
#include "lock_daemon.h"
#include "lock_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 "rpc/virnetserver.h"
#include "virrandom.h"
#include "virhash.h"
#include "viruuid.h"
#include "virstring.h"
#include "virgettext.h"
#include "virdaemon.h"
#include "locking/lock_daemon_dispatch.h"
#include "locking/lock_protocol.h"
#include "configmake.h"
#define VIR_FROM_THIS VIR_FROM_LOCKING
VIR_LOG_INIT("locking.lock_daemon");
struct _virLockDaemon {
GMutex lock;
virNetDaemonPtr dmn;
virHashTablePtr lockspaces;
virLockSpacePtr defaultLockspace;
};
virLockDaemonPtr lockDaemon = NULL;
static bool execRestart;
static void *
virLockDaemonClientNew(virNetServerClientPtr client,
void *opaque);
static void
virLockDaemonClientFree(void *opaque);
static void *
virLockDaemonClientNewPostExecRestart(virNetServerClientPtr client,
virJSONValuePtr object,
void *opaque);
static virJSONValuePtr
virLockDaemonClientPreExecRestart(virNetServerClientPtr client,
void *opaque);
static void
virLockDaemonFree(virLockDaemonPtr lockd)
{
if (!lockd)
return;
g_mutex_clear(&lockd->lock);
virObjectUnref(lockd->dmn);
virHashFree(lockd->lockspaces);
virLockSpaceFree(lockd->defaultLockspace);
VIR_FREE(lockd);
}
static inline void
virLockDaemonLock(virLockDaemonPtr lockd)
{
g_mutex_lock(&lockd->lock);
}
static inline void
virLockDaemonUnlock(virLockDaemonPtr lockd)
{
g_mutex_unlock(&lockd->lock);
}
static void virLockDaemonLockSpaceDataFree(void *data)
{
virLockSpaceFree(data);
}
static virLockDaemonPtr
virLockDaemonNew(virLockDaemonConfigPtr config, bool privileged)
{
virLockDaemonPtr lockd;
virNetServerPtr srv = NULL;
lockd = g_new0(virLockDaemon, 1);
g_mutex_init(&lockd->lock);
if (!(lockd->dmn = virNetDaemonNew()))
goto error;
if (!(srv = virNetServerNew("virtlockd", 1,
0, 0, 0, config->max_clients,
config->max_clients, -1, 0,
virLockDaemonClientNew,
virLockDaemonClientPreExecRestart,
virLockDaemonClientFree,
(void*)(intptr_t)(privileged ? 0x1 : 0x0))))
goto error;
if (virNetDaemonAddServer(lockd->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,
lockd->dmn)))
goto error;
if (virNetDaemonAddServer(lockd->dmn, srv) < 0)
goto error;
virObjectUnref(srv);
srv = NULL;
if (!(lockd->lockspaces = virHashNew(virLockDaemonLockSpaceDataFree)))
goto error;
if (!(lockd->defaultLockspace = virLockSpaceNew(NULL)))
goto error;
return lockd;
error:
virObjectUnref(srv);
virLockDaemonFree(lockd);
return NULL;
}
static virNetServerPtr
virLockDaemonNewServerPostExecRestart(virNetDaemonPtr dmn G_GNUC_UNUSED,
const char *name,
virJSONValuePtr object,
void *opaque)
{
if (STREQ(name, "virtlockd")) {
return virNetServerNewPostExecRestart(object,
name,
virLockDaemonClientNew,
virLockDaemonClientNewPostExecRestart,
virLockDaemonClientPreExecRestart,
virLockDaemonClientFree,
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 virLockDaemonPtr
virLockDaemonNewPostExecRestart(virJSONValuePtr object, bool privileged)
{
virLockDaemonPtr lockd;
virJSONValuePtr child;
virJSONValuePtr lockspaces;
size_t i;
const char *serverNames[] = { "virtlockd" };
lockd = g_new0(virLockDaemon, 1);
g_mutex_init(&lockd->lock);
if (!(lockd->lockspaces = virHashNew(virLockDaemonLockSpaceDataFree)))
goto error;
if (!(child = virJSONValueObjectGet(object, "defaultLockspace"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing defaultLockspace data from JSON file"));
goto error;
}
if (!(lockd->defaultLockspace =
virLockSpaceNewPostExecRestart(child)))
goto error;
if (!(lockspaces = virJSONValueObjectGet(object, "lockspaces"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing lockspaces data from JSON file"));
goto error;
}
if (!virJSONValueIsArray(lockspaces)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Malformed lockspaces array"));
goto error;
}
for (i = 0; i < virJSONValueArraySize(lockspaces); i++) {
virLockSpacePtr lockspace;
child = virJSONValueArrayGet(lockspaces, i);
if (!(lockspace = virLockSpaceNewPostExecRestart(child)))
goto error;
if (virHashAddEntry(lockd->lockspaces,
virLockSpaceGetDirectory(lockspace),
lockspace) < 0) {
virLockSpaceFree(lockspace);
}
}
if (virJSONValueObjectHasKey(object, "daemon")) {
if (!(child = virJSONValueObjectGet(object, "daemon"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Malformed daemon data from JSON file"));
goto error;
}
} else {
if (!(child = virJSONValueObjectGet(object, "server"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing server data from JSON file"));
goto error;
}
}
if (!(lockd->dmn = virNetDaemonNewPostExecRestart(child,
G_N_ELEMENTS(serverNames),
serverNames,
virLockDaemonNewServerPostExecRestart,
(void*)(intptr_t)(privileged ? 0x1 : 0x0))))
goto error;
return lockd;
error:
virLockDaemonFree(lockd);
return NULL;
}
int virLockDaemonAddLockSpace(virLockDaemonPtr lockd,
const char *path,
virLockSpacePtr lockspace)
{
int ret;
virLockDaemonLock(lockd);
ret = virHashAddEntry(lockd->lockspaces, path, lockspace);
virLockDaemonUnlock(lockd);
return ret;
}
virLockSpacePtr virLockDaemonFindLockSpace(virLockDaemonPtr lockd,
const char *path)
{
virLockSpacePtr lockspace;
virLockDaemonLock(lockd);
if (path && STRNEQ(path, ""))
lockspace = virHashLookup(lockd->lockspaces, path);
else
lockspace = lockd->defaultLockspace;
virLockDaemonUnlock(lockd);
return lockspace;
}
static void
virLockDaemonErrorHandler(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
virLockDaemonVersion(const char *argv0)
{
printf("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION);
}
static void
virLockDaemonShutdownHandler(virNetDaemonPtr dmn,
siginfo_t *sig G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED)
{
virNetDaemonQuit(dmn);
}
static void
virLockDaemonExecRestartHandler(virNetDaemonPtr dmn,
siginfo_t *sig G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED)
{
execRestart = true;
virNetDaemonQuit(dmn);
}
static int
virLockDaemonSetupSignals(virNetDaemonPtr dmn)
{
if (virNetDaemonAddSignalHandler(dmn, SIGINT, virLockDaemonShutdownHandler, NULL) < 0)
return -1;
if (virNetDaemonAddSignalHandler(dmn, SIGQUIT, virLockDaemonShutdownHandler, NULL) < 0)
return -1;
if (virNetDaemonAddSignalHandler(dmn, SIGTERM, virLockDaemonShutdownHandler, NULL) < 0)
return -1;
if (virNetDaemonAddSignalHandler(dmn, SIGUSR1, virLockDaemonExecRestartHandler, NULL) < 0)
return -1;
return 0;
}
struct virLockDaemonClientReleaseData {
virLockDaemonClientPtr client;
bool hadSomeLeases;
bool gotError;
};
static int
virLockDaemonClientReleaseLockspace(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virLockSpacePtr lockspace = payload;
struct virLockDaemonClientReleaseData *data = opaque;
int rc;
rc = virLockSpaceReleaseResourcesForOwner(lockspace,
data->client->clientPid);
if (rc > 0)
data->hadSomeLeases = true;
else if (rc < 0)
data->gotError = true;
return 0;
}
static void
virLockDaemonClientFree(void *opaque)
{
virLockDaemonClientPtr priv = opaque;
if (!priv)
return;
VIR_DEBUG("priv=%p client=%lld owner=%lld",
priv,
(unsigned long long)priv->clientPid,
(unsigned long long)priv->ownerPid);
/* If client & owner match, this is the lock holder */
if (priv->clientPid == priv->ownerPid) {
size_t i;
struct virLockDaemonClientReleaseData data = {
.client = priv, .hadSomeLeases = false, .gotError = false
};
/* Release all locks associated with this
* owner in all lockspaces */
virLockDaemonLock(lockDaemon);
virHashForEach(lockDaemon->lockspaces,
virLockDaemonClientReleaseLockspace,
&data);
virLockDaemonClientReleaseLockspace(lockDaemon->defaultLockspace,
"",
&data);
virLockDaemonUnlock(lockDaemon);
/* If the client had some active leases when it
* closed the connection, we must kill it off
* to make sure it doesn't do nasty stuff */
if (data.gotError || data.hadSomeLeases) {
for (i = 0; i < 15; i++) {
int signum;
if (i == 0)
signum = SIGTERM;
else if (i == 8)
signum = SIGKILL;
else
signum = 0;
if (priv->clientPid != 0 && virProcessKill(priv->clientPid, signum) < 0) {
if (errno == ESRCH)
break;
VIR_WARN("Failed to kill off pid %lld",
(unsigned long long)priv->clientPid);
}
g_usleep(200 * 1000);
}
}
}
g_mutex_clear(&priv->lock);
VIR_FREE(priv->ownerName);
VIR_FREE(priv);
}
static void *
virLockDaemonClientNew(virNetServerClientPtr client,
void *opaque)
{
virLockDaemonClientPtr priv;
uid_t clientuid;
gid_t clientgid;
unsigned long long timestamp;
bool privileged = opaque != NULL;
priv = g_new0(virLockDaemonClient, 1);
g_mutex_init(&priv->lock);
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;
}
}
/* there's no closing handshake in the locking protocol */
virNetServerClientSetQuietEOF(client);
return priv;
error:
g_mutex_clear(&priv->lock);
VIR_FREE(priv);
return NULL;
}
static void *
virLockDaemonClientNewPostExecRestart(virNetServerClientPtr client,
virJSONValuePtr object,
void *opaque)
{
virLockDaemonClientPtr priv = virLockDaemonClientNew(client, opaque);
unsigned int ownerPid;
const char *ownerUUID;
const char *ownerName;
if (!priv)
return NULL;
if (virJSONValueObjectGetBoolean(object, "restricted", &priv->restricted) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing restricted data in JSON document"));
goto error;
}
if (virJSONValueObjectGetNumberUint(object, "ownerPid", &ownerPid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing ownerPid data in JSON document"));
goto error;
}
priv->ownerPid = (pid_t)ownerPid;
if (virJSONValueObjectGetNumberUint(object, "ownerId", &priv->ownerId) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing ownerId data in JSON document"));
goto error;
}
if (!(ownerName = virJSONValueObjectGetString(object, "ownerName"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing ownerName data in JSON document"));
goto error;
}
priv->ownerName = g_strdup(ownerName);
if (!(ownerUUID = virJSONValueObjectGetString(object, "ownerUUID"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing ownerUUID data in JSON document"));
goto error;
}
if (virUUIDParse(ownerUUID, priv->ownerUUID) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing ownerUUID data in JSON document"));
goto error;
}
return priv;
error:
virLockDaemonClientFree(priv);
return NULL;
}
static virJSONValuePtr
virLockDaemonClientPreExecRestart(virNetServerClientPtr client G_GNUC_UNUSED,
void *opaque)
{
virLockDaemonClientPtr priv = opaque;
virJSONValuePtr object = virJSONValueNewObject();
char uuidstr[VIR_UUID_STRING_BUFLEN];
if (virJSONValueObjectAppendBoolean(object, "restricted", priv->restricted) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot set restricted data in JSON document"));
goto error;
}
if (virJSONValueObjectAppendNumberUint(object, "ownerPid", priv->ownerPid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot set ownerPid data in JSON document"));
goto error;
}
if (virJSONValueObjectAppendNumberUint(object, "ownerId", priv->ownerId) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot set ownerId data in JSON document"));
goto error;
}
if (virJSONValueObjectAppendString(object, "ownerName", priv->ownerName) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot set ownerName data in JSON document"));
goto error;
}
virUUIDFormat(priv->ownerUUID, uuidstr);
if (virJSONValueObjectAppendString(object, "ownerUUID", uuidstr) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot set ownerUUID data in JSON document"));
goto error;
}
return object;
error:
virJSONValueFree(object);
return NULL;
}
static int
virLockDaemonExecRestartStatePath(bool privileged,
char **state_file)
{
if (privileged) {
*state_file = g_strdup(RUNSTATEDIR "/virtlockd-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/virtlockd-restart-exec.json", rundir);
}
return 0;
}
static char *
virLockDaemonGetExecRestartMagic(void)
{
char *ret;
ret = g_strdup_printf("%lld", (long long int)getpid());
return ret;
}
static int
virLockDaemonPostExecRestart(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 = virLockDaemonGetExecRestartMagic()))
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 (!(lockDaemon = virLockDaemonNewPostExecRestart(object, privileged)))
goto cleanup;
ret = 1;
cleanup:
unlink(state_file);
VIR_FREE(wantmagic);
VIR_FREE(state);
virJSONValueFree(object);
return ret;
}
static int
virLockDaemonPreExecRestart(const char *state_file,
virNetDaemonPtr dmn,
char **argv)
{
virJSONValuePtr child;
char *state = NULL;
virJSONValuePtr object = virJSONValueNewObject();
char *magic;
virHashKeyValuePairPtr pairs = NULL, tmp;
virJSONValuePtr lockspaces;
VIR_DEBUG("Running pre-restart exec");
if (!(child = virNetDaemonPreExecRestart(dmn)))
goto cleanup;
if (virJSONValueObjectAppend(object, "daemon", child) < 0) {
virJSONValueFree(child);
goto cleanup;
}
if (!(child = virLockSpacePreExecRestart(lockDaemon->defaultLockspace)))
goto cleanup;
if (virJSONValueObjectAppend(object, "defaultLockspace", child) < 0) {
virJSONValueFree(child);
goto cleanup;
}
lockspaces = virJSONValueNewArray();
if (virJSONValueObjectAppend(object, "lockspaces", lockspaces) < 0) {
virJSONValueFree(lockspaces);
goto cleanup;
}
tmp = pairs = virHashGetItems(lockDaemon->lockspaces, NULL);
while (tmp && tmp->key) {
virLockSpacePtr lockspace = (virLockSpacePtr)tmp->value;
if (!(child = virLockSpacePreExecRestart(lockspace)))
goto cleanup;
if (virJSONValueArrayAppend(lockspaces, child) < 0) {
virJSONValueFree(child);
goto cleanup;
}
tmp++;
}
if (!(magic = virLockDaemonGetExecRestartMagic()))
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 -1;
}
static void
virLockDaemonUsage(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 Exit after timeout period.\n"
" -f | --config Configuration file.\n"
" -V | --version Display version information.\n"
" -p | --pid-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/virtlockd.conf\n"
"\n"
" Sockets:\n"
" %s/libvirt/virtlockd-sock\n"
"\n"
" PID file (unless overridden by -p):\n"
" %s/virtlockd.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/virtlockd.conf\n"
"\n"
" Sockets:\n"
" $XDG_RUNTIME_DIR/libvirt/virtlockd-sock\n"
"\n"
" PID file:\n"
" $XDG_RUNTIME_DIR/libvirt/virtlockd.pid\n"
"\n"));
}
}
int main(int argc, char **argv) {
virNetServerPtr lockSrv = NULL;
virNetServerPtr adminSrv = NULL;
virNetServerProgramPtr lockProgram = 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;
virLockDaemonConfigPtr 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':
virLockDaemonVersion(argv[0]);
exit(EXIT_SUCCESS);
case 'h':
virLockDaemonUsage(argv[0], privileged);
exit(EXIT_SUCCESS);
case '?':
default:
virLockDaemonUsage(argv[0], privileged);
exit(EXIT_FAILURE);
}
}
virFileActivateDirOverrideForProg(argv[0]);
if (!(config = virLockDaemonConfigNew(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 (virLockDaemonConfigFilePath(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 &&
virLockDaemonConfigLoadFile(config, remote_config_file, implicit_conf) < 0) {
VIR_ERROR(_("Can't load config file: %s: %s"),
virGetLastErrorMessage(), remote_config_file);
exit(EXIT_FAILURE);
}
VIR_FREE(remote_config_file);
virDaemonSetupLogging("virtlockd",
config->log_level,
config->log_filters,
config->log_outputs,
privileged,
verbose,
godaemon);
if (!pid_file &&
virPidFileConstructPath(privileged,
RUNSTATEDIR,
"virtlockd",
&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("virtlockd",
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 (virLockDaemonExecRestartStatePath(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 = virLockDaemonPostExecRestart(state_file,
pid_file,
&pid_file_fd,
privileged)) < 0) {
ret = VIR_DAEMON_ERR_INIT;
goto cleanup;
}
/* rv == 1 means we successfully restored from the saved internal state
* (but still need to add @lockProgram into @srv). rv == 0 means that no
* saved state is present, therefore initialize from scratch here. */
if (rv == 0) {
g_autoptr(virSystemdActivation) act = NULL;
virSystemdActivationMap actmap[] = {
{ .name = "virtlockd.socket", .family = AF_UNIX, .path = sock_file },
{ .name = "virtlockd-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 (!(lockDaemon = virLockDaemonNew(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;
}
lockSrv = virNetDaemonGetServer(lockDaemon->dmn, "virtlockd");
adminSrv = virNetDaemonGetServer(lockDaemon->dmn, "admin");
if (virNetServerAddServiceUNIX(lockSrv,
act, "virtlockd.socket",
sock_file, 0700, 0, 0,
NULL,
false, 0, 1) < 0) {
ret = VIR_DAEMON_ERR_NETWORK;
goto cleanup;
}
if (virNetServerAddServiceUNIX(adminSrv,
act, "virtlockd-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 {
lockSrv = virNetDaemonGetServer(lockDaemon->dmn, "virtlockd");
/* If exec-restarting from old virtlockd, we won't have an
* admin server present */
if (virNetDaemonHasServer(lockDaemon->dmn, "admin"))
adminSrv = virNetDaemonGetServer(lockDaemon->dmn, "admin");
}
if (timeout > 0) {
VIR_DEBUG("Registering shutdown timeout %d", timeout);
virNetDaemonAutoShutdown(lockDaemon->dmn,
timeout);
}
if ((virLockDaemonSetupSignals(lockDaemon->dmn)) < 0) {
ret = VIR_DAEMON_ERR_SIGNAL;
goto cleanup;
}
if (!(lockProgram = virNetServerProgramNew(VIR_LOCK_SPACE_PROTOCOL_PROGRAM,
VIR_LOCK_SPACE_PROTOCOL_PROGRAM_VERSION,
virLockSpaceProtocolProcs,
virLockSpaceProtocolNProcs))) {
ret = VIR_DAEMON_ERR_INIT;
goto cleanup;
}
if (virNetServerAddProgram(lockSrv, lockProgram) < 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, virLockDaemonErrorHandler);
/* 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(lockDaemon->dmn, true);
virNetDaemonRun(lockDaemon->dmn);
if (execRestart &&
virLockDaemonPreExecRestart(state_file,
lockDaemon->dmn,
argv) < 0)
ret = VIR_DAEMON_ERR_REEXEC;
else
ret = 0;
cleanup:
virObjectUnref(lockProgram);
virObjectUnref(adminProgram);
virObjectUnref(lockSrv);
virObjectUnref(adminSrv);
virLockDaemonFree(lockDaemon);
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);
virLockDaemonConfigFree(config);
return ret;
}