mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-08 14:29:56 +00:00
690efb960f
Use 'g_clear_pointer(&ptr, g_hash_table_unref)' instead. In few instances it allows us to also remove explicit clearing of pointers. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com> Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
979 lines
23 KiB
C
979 lines
23 KiB
C
/*
|
|
* virnetdaemon.c
|
|
*
|
|
* Copyright (C) 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 "virnetdaemon.h"
|
|
#include "virlog.h"
|
|
#include "viralloc.h"
|
|
#include "virerror.h"
|
|
#include "virthread.h"
|
|
#include "virthreadpool.h"
|
|
#include "virutil.h"
|
|
#include "virfile.h"
|
|
#include "virnetserver.h"
|
|
#include "virgdbus.h"
|
|
#include "virhash.h"
|
|
#include "virstring.h"
|
|
#include "virsystemd.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_RPC
|
|
|
|
VIR_LOG_INIT("rpc.netdaemon");
|
|
|
|
#ifndef WIN32
|
|
typedef struct _virNetDaemonSignal virNetDaemonSignal;
|
|
struct _virNetDaemonSignal {
|
|
struct sigaction oldaction;
|
|
int signum;
|
|
virNetDaemonSignalFunc func;
|
|
void *opaque;
|
|
};
|
|
#endif /* !WIN32 */
|
|
|
|
struct _virNetDaemon {
|
|
virObjectLockable parent;
|
|
|
|
bool privileged;
|
|
|
|
#ifndef WIN32
|
|
size_t nsignals;
|
|
virNetDaemonSignal **signals;
|
|
int sigread;
|
|
int sigwrite;
|
|
int sigwatch;
|
|
#endif /* !WIN32 */
|
|
|
|
GHashTable *servers;
|
|
virJSONValue *srvObject;
|
|
|
|
virNetDaemonShutdownCallback shutdownPrepareCb;
|
|
virNetDaemonShutdownCallback shutdownWaitCb;
|
|
virThread *stateStopThread;
|
|
int finishTimer;
|
|
bool quit;
|
|
bool finished;
|
|
bool graceful;
|
|
bool execRestart;
|
|
|
|
unsigned int autoShutdownTimeout;
|
|
size_t autoShutdownInhibitions;
|
|
int autoShutdownInhibitFd;
|
|
};
|
|
|
|
|
|
static virClass *virNetDaemonClass;
|
|
|
|
static int
|
|
daemonServerClose(void *payload,
|
|
const char *key G_GNUC_UNUSED,
|
|
void *opaque G_GNUC_UNUSED);
|
|
|
|
static void
|
|
virNetDaemonDispose(void *obj)
|
|
{
|
|
virNetDaemon *dmn = obj;
|
|
#ifndef WIN32
|
|
size_t i;
|
|
|
|
for (i = 0; i < dmn->nsignals; i++) {
|
|
sigaction(dmn->signals[i]->signum, &dmn->signals[i]->oldaction, NULL);
|
|
g_free(dmn->signals[i]);
|
|
}
|
|
g_free(dmn->signals);
|
|
VIR_FORCE_CLOSE(dmn->sigread);
|
|
VIR_FORCE_CLOSE(dmn->sigwrite);
|
|
if (dmn->sigwatch > 0)
|
|
virEventRemoveHandle(dmn->sigwatch);
|
|
#endif /* !WIN32 */
|
|
|
|
VIR_FORCE_CLOSE(dmn->autoShutdownInhibitFd);
|
|
g_free(dmn->stateStopThread);
|
|
|
|
g_clear_pointer(&dmn->servers, g_hash_table_unref);
|
|
|
|
virJSONValueFree(dmn->srvObject);
|
|
}
|
|
|
|
static int
|
|
virNetDaemonOnceInit(void)
|
|
{
|
|
if (!VIR_CLASS_NEW(virNetDaemon, virClassForObjectLockable()))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virNetDaemon);
|
|
|
|
|
|
virNetDaemon *
|
|
virNetDaemonNew(void)
|
|
{
|
|
virNetDaemon *dmn;
|
|
#ifndef WIN32
|
|
struct sigaction sig_action;
|
|
#endif /* !WIN32 */
|
|
|
|
if (virNetDaemonInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(dmn = virObjectLockableNew(virNetDaemonClass)))
|
|
return NULL;
|
|
|
|
dmn->servers = virHashNew(virObjectFreeHashData);
|
|
|
|
#ifndef WIN32
|
|
dmn->sigwrite = dmn->sigread = -1;
|
|
#endif /* !WIN32 */
|
|
|
|
dmn->privileged = geteuid() == 0;
|
|
dmn->autoShutdownInhibitFd = -1;
|
|
|
|
if (virEventRegisterDefaultImpl() < 0)
|
|
goto error;
|
|
|
|
#ifndef WIN32
|
|
memset(&sig_action, 0, sizeof(sig_action));
|
|
sig_action.sa_handler = SIG_IGN;
|
|
sigaction(SIGPIPE, &sig_action, NULL);
|
|
#endif /* !WIN32 */
|
|
|
|
return dmn;
|
|
|
|
error:
|
|
virObjectUnref(dmn);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int
|
|
virNetDaemonAddServer(virNetDaemon *dmn,
|
|
virNetServer *srv)
|
|
{
|
|
int ret = -1;
|
|
const char *serverName = virNetServerGetName(srv);
|
|
|
|
virObjectLock(dmn);
|
|
|
|
if (virHashAddEntry(dmn->servers, serverName, srv) < 0)
|
|
goto cleanup;
|
|
|
|
virObjectRef(srv);
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virObjectUnlock(dmn);
|
|
return ret;
|
|
}
|
|
|
|
|
|
virNetServer *
|
|
virNetDaemonGetServer(virNetDaemon *dmn,
|
|
const char *serverName)
|
|
{
|
|
virNetServer *srv = NULL;
|
|
|
|
virObjectLock(dmn);
|
|
srv = virObjectRef(virHashLookup(dmn->servers, serverName));
|
|
virObjectUnlock(dmn);
|
|
|
|
if (!srv) {
|
|
virReportError(VIR_ERR_NO_SERVER,
|
|
_("No server named '%s'"), serverName);
|
|
}
|
|
|
|
return srv;
|
|
}
|
|
|
|
bool
|
|
virNetDaemonHasServer(virNetDaemon *dmn,
|
|
const char *serverName)
|
|
{
|
|
void *ent;
|
|
|
|
virObjectLock(dmn);
|
|
ent = virHashLookup(dmn->servers, serverName);
|
|
virObjectUnlock(dmn);
|
|
|
|
return ent != NULL;
|
|
}
|
|
|
|
|
|
struct collectData {
|
|
virNetServer ***servers;
|
|
size_t nservers;
|
|
};
|
|
|
|
|
|
static int
|
|
collectServers(void *payload,
|
|
const char *name G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virNetServer *srv = virObjectRef(payload);
|
|
struct collectData *data = opaque;
|
|
|
|
if (!srv)
|
|
return -1;
|
|
|
|
VIR_APPEND_ELEMENT(*data->servers, data->nservers, srv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns number of names allocated in *servers, on error sets
|
|
* *servers to NULL and returns -1. List of *servers must be free()d,
|
|
* but not the items in it (similarly to virHashGetItems).
|
|
*/
|
|
ssize_t
|
|
virNetDaemonGetServers(virNetDaemon *dmn,
|
|
virNetServer ***servers)
|
|
{
|
|
struct collectData data = { servers, 0 };
|
|
ssize_t ret = -1;
|
|
|
|
*servers = NULL;
|
|
|
|
virObjectLock(dmn);
|
|
|
|
if (virHashForEach(dmn->servers, collectServers, &data) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Cannot get all servers from daemon"));
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = data.nservers;
|
|
|
|
cleanup:
|
|
if (ret < 0)
|
|
virObjectListFreeCount(*servers, data.nservers);
|
|
virObjectUnlock(dmn);
|
|
return ret;
|
|
}
|
|
|
|
|
|
struct virNetDaemonServerData {
|
|
virNetDaemon *dmn;
|
|
virNetDaemonNewServerPostExecRestart cb;
|
|
void *opaque;
|
|
};
|
|
|
|
static int
|
|
virNetDaemonServerIterator(const char *key,
|
|
virJSONValue *value,
|
|
void *opaque)
|
|
{
|
|
struct virNetDaemonServerData *data = opaque;
|
|
virNetServer *srv;
|
|
|
|
VIR_DEBUG("Creating server '%s'", key);
|
|
srv = data->cb(data->dmn, key, value, data->opaque);
|
|
if (!srv)
|
|
return -1;
|
|
|
|
if (virHashAddEntry(data->dmn->servers, key, srv) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
virNetDaemon *
|
|
virNetDaemonNewPostExecRestart(virJSONValue *object,
|
|
size_t nDefServerNames,
|
|
const char **defServerNames,
|
|
virNetDaemonNewServerPostExecRestart cb,
|
|
void *opaque)
|
|
{
|
|
virNetDaemon *dmn = NULL;
|
|
virJSONValue *servers = virJSONValueObjectGet(object, "servers");
|
|
bool new_version = virJSONValueObjectHasKey(object, "servers");
|
|
|
|
if (!(dmn = virNetDaemonNew()))
|
|
goto error;
|
|
|
|
if (new_version && !servers) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Malformed servers data in JSON document"));
|
|
goto error;
|
|
}
|
|
|
|
if (!new_version) {
|
|
virNetServer *srv;
|
|
|
|
if (nDefServerNames < 1) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("No default server names provided"));
|
|
goto error;
|
|
}
|
|
|
|
VIR_DEBUG("No 'servers' data, creating default '%s' name", defServerNames[0]);
|
|
|
|
srv = cb(dmn, defServerNames[0], object, opaque);
|
|
|
|
if (virHashAddEntry(dmn->servers, defServerNames[0], srv) < 0)
|
|
goto error;
|
|
} else if (virJSONValueIsArray(servers)) {
|
|
size_t i;
|
|
size_t n = virJSONValueArraySize(servers);
|
|
if (n > nDefServerNames) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Server count %zd greater than default name count %zu"),
|
|
n, nDefServerNames);
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
virNetServer *srv;
|
|
virJSONValue *value = virJSONValueArrayGet(servers, i);
|
|
|
|
VIR_DEBUG("Creating server '%s'", defServerNames[i]);
|
|
srv = cb(dmn, defServerNames[i], value, opaque);
|
|
if (!srv)
|
|
goto error;
|
|
|
|
if (virHashAddEntry(dmn->servers, defServerNames[i], srv) < 0) {
|
|
virObjectUnref(srv);
|
|
goto error;
|
|
}
|
|
}
|
|
} else {
|
|
struct virNetDaemonServerData data = {
|
|
dmn,
|
|
cb,
|
|
opaque,
|
|
};
|
|
if (virJSONValueObjectForeachKeyValue(servers,
|
|
virNetDaemonServerIterator,
|
|
&data) < 0)
|
|
goto error;
|
|
}
|
|
|
|
return dmn;
|
|
|
|
error:
|
|
virObjectUnref(dmn);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
virJSONValue *
|
|
virNetDaemonPreExecRestart(virNetDaemon *dmn)
|
|
{
|
|
size_t i = 0;
|
|
g_autoptr(virJSONValue) object = virJSONValueNewObject();
|
|
g_autoptr(virJSONValue) srvObj = virJSONValueNewObject();
|
|
g_autofree virHashKeyValuePair *srvArray = NULL;
|
|
|
|
virObjectLock(dmn);
|
|
|
|
if (!(srvArray = virHashGetItems(dmn->servers, NULL, true)))
|
|
goto error;
|
|
|
|
for (i = 0; srvArray[i].key; i++) {
|
|
virNetServer *server = virHashLookup(dmn->servers, srvArray[i].key);
|
|
g_autoptr(virJSONValue) srvJSON = NULL;
|
|
|
|
if (!server)
|
|
goto error;
|
|
|
|
srvJSON = virNetServerPreExecRestart(server);
|
|
if (!srvJSON)
|
|
goto error;
|
|
|
|
if (virJSONValueObjectAppend(srvObj, srvArray[i].key, &srvJSON) < 0)
|
|
goto error;
|
|
}
|
|
|
|
virObjectUnlock(dmn);
|
|
|
|
if (virJSONValueObjectAppend(object, "servers", &srvObj) < 0)
|
|
return NULL;
|
|
|
|
return g_steal_pointer(&object);
|
|
|
|
error:
|
|
virObjectUnlock(dmn);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool
|
|
virNetDaemonIsPrivileged(virNetDaemon *dmn)
|
|
{
|
|
bool priv;
|
|
virObjectLock(dmn);
|
|
priv = dmn->privileged;
|
|
virObjectUnlock(dmn);
|
|
return priv;
|
|
}
|
|
|
|
|
|
void
|
|
virNetDaemonAutoShutdown(virNetDaemon *dmn,
|
|
unsigned int timeout)
|
|
{
|
|
virObjectLock(dmn);
|
|
|
|
dmn->autoShutdownTimeout = timeout;
|
|
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
|
|
#ifdef G_OS_UNIX
|
|
/* As per: https://www.freedesktop.org/wiki/Software/systemd/inhibit */
|
|
static void
|
|
virNetDaemonCallInhibit(virNetDaemon *dmn,
|
|
const char *what,
|
|
const char *who,
|
|
const char *why,
|
|
const char *mode)
|
|
{
|
|
g_autoptr(GVariant) reply = NULL;
|
|
g_autoptr(GUnixFDList) replyFD = NULL;
|
|
g_autoptr(GVariant) message = NULL;
|
|
GDBusConnection *systemBus;
|
|
int fd;
|
|
int rc;
|
|
|
|
VIR_DEBUG("dmn=%p what=%s who=%s why=%s mode=%s",
|
|
dmn, NULLSTR(what), NULLSTR(who), NULLSTR(why), NULLSTR(mode));
|
|
|
|
if (virSystemdHasLogind() < 0)
|
|
return;
|
|
|
|
if (!(systemBus = virGDBusGetSystemBus()))
|
|
return;
|
|
|
|
message = g_variant_new("(ssss)", what, who, why, mode);
|
|
|
|
rc = virGDBusCallMethodWithFD(systemBus,
|
|
&reply,
|
|
G_VARIANT_TYPE("(h)"),
|
|
&replyFD,
|
|
NULL,
|
|
"org.freedesktop.login1",
|
|
"/org/freedesktop/login1",
|
|
"org.freedesktop.login1.Manager",
|
|
"Inhibit",
|
|
message,
|
|
NULL);
|
|
|
|
if (rc < 0)
|
|
return;
|
|
|
|
if (g_unix_fd_list_get_length(replyFD) <= 0)
|
|
return;
|
|
|
|
fd = g_unix_fd_list_get(replyFD, 0, NULL);
|
|
if (fd < 0)
|
|
return;
|
|
|
|
if (dmn->autoShutdownInhibitions) {
|
|
dmn->autoShutdownInhibitFd = fd;
|
|
VIR_DEBUG("Got inhibit FD %d", fd);
|
|
} else {
|
|
/* We stopped the last VM since we made the inhibit call */
|
|
VIR_DEBUG("Closing inhibit FD %d", fd);
|
|
VIR_FORCE_CLOSE(fd);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void
|
|
virNetDaemonAddShutdownInhibition(virNetDaemon *dmn)
|
|
{
|
|
virObjectLock(dmn);
|
|
dmn->autoShutdownInhibitions++;
|
|
|
|
VIR_DEBUG("dmn=%p inhibitions=%zu", dmn, dmn->autoShutdownInhibitions);
|
|
|
|
#ifdef G_OS_UNIX
|
|
if (dmn->autoShutdownInhibitions == 1)
|
|
virNetDaemonCallInhibit(dmn,
|
|
"shutdown",
|
|
_("Libvirt"),
|
|
_("Virtual machines need to be saved"),
|
|
"delay");
|
|
#endif
|
|
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
|
|
void
|
|
virNetDaemonRemoveShutdownInhibition(virNetDaemon *dmn)
|
|
{
|
|
virObjectLock(dmn);
|
|
dmn->autoShutdownInhibitions--;
|
|
|
|
VIR_DEBUG("dmn=%p inhibitions=%zu", dmn, dmn->autoShutdownInhibitions);
|
|
|
|
if (dmn->autoShutdownInhibitions == 0) {
|
|
VIR_DEBUG("Closing inhibit FD %d", dmn->autoShutdownInhibitFd);
|
|
VIR_FORCE_CLOSE(dmn->autoShutdownInhibitFd);
|
|
}
|
|
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
|
|
#ifndef WIN32
|
|
static sig_atomic_t sigErrors;
|
|
static int sigLastErrno;
|
|
static int sigWrite = -1;
|
|
|
|
static void
|
|
virNetDaemonSignalHandler(int sig, siginfo_t * siginfo,
|
|
void* context G_GNUC_UNUSED)
|
|
{
|
|
int origerrno;
|
|
int r;
|
|
siginfo_t tmp;
|
|
|
|
if (SA_SIGINFO)
|
|
tmp = *siginfo;
|
|
else
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
|
|
/* set the sig num in the struct */
|
|
tmp.si_signo = sig;
|
|
|
|
origerrno = errno;
|
|
r = safewrite(sigWrite, &tmp, sizeof(tmp));
|
|
if (r == -1) {
|
|
sigErrors++;
|
|
sigLastErrno = errno;
|
|
}
|
|
errno = origerrno;
|
|
}
|
|
|
|
static void
|
|
virNetDaemonSignalEvent(int watch,
|
|
int fd G_GNUC_UNUSED,
|
|
int events G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virNetDaemon *dmn = opaque;
|
|
siginfo_t siginfo;
|
|
size_t i;
|
|
|
|
virObjectLock(dmn);
|
|
|
|
if (saferead(dmn->sigread, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) {
|
|
virReportSystemError(errno, "%s",
|
|
_("Failed to read from signal pipe"));
|
|
virEventRemoveHandle(watch);
|
|
dmn->sigwatch = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0; i < dmn->nsignals; i++) {
|
|
if (siginfo.si_signo == dmn->signals[i]->signum) {
|
|
virNetDaemonSignalFunc func = dmn->signals[i]->func;
|
|
void *funcopaque = dmn->signals[i]->opaque;
|
|
virObjectUnlock(dmn);
|
|
func(dmn, &siginfo, funcopaque);
|
|
return;
|
|
}
|
|
}
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unexpected signal received: %d"), siginfo.si_signo);
|
|
|
|
cleanup:
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
static int
|
|
virNetDaemonSignalSetup(virNetDaemon *dmn)
|
|
{
|
|
int fds[2] = { -1, -1 };
|
|
|
|
if (dmn->sigwrite != -1)
|
|
return 0;
|
|
|
|
if (virPipeNonBlock(fds) < 0)
|
|
return -1;
|
|
|
|
if ((dmn->sigwatch = virEventAddHandle(fds[0],
|
|
VIR_EVENT_HANDLE_READABLE,
|
|
virNetDaemonSignalEvent,
|
|
dmn, NULL)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Failed to add signal handle watch"));
|
|
goto error;
|
|
}
|
|
|
|
dmn->sigread = fds[0];
|
|
dmn->sigwrite = fds[1];
|
|
sigWrite = fds[1];
|
|
|
|
return 0;
|
|
|
|
error:
|
|
VIR_FORCE_CLOSE(fds[0]);
|
|
VIR_FORCE_CLOSE(fds[1]);
|
|
return -1;
|
|
}
|
|
|
|
|
|
int
|
|
virNetDaemonAddSignalHandler(virNetDaemon *dmn,
|
|
int signum,
|
|
virNetDaemonSignalFunc func,
|
|
void *opaque)
|
|
{
|
|
virNetDaemonSignal *sigdata = NULL;
|
|
struct sigaction sig_action;
|
|
|
|
virObjectLock(dmn);
|
|
|
|
if (virNetDaemonSignalSetup(dmn) < 0)
|
|
goto error;
|
|
|
|
VIR_EXPAND_N(dmn->signals, dmn->nsignals, 1);
|
|
|
|
sigdata = g_new0(virNetDaemonSignal, 1);
|
|
|
|
sigdata->signum = signum;
|
|
sigdata->func = func;
|
|
sigdata->opaque = opaque;
|
|
|
|
memset(&sig_action, 0, sizeof(sig_action));
|
|
sig_action.sa_sigaction = virNetDaemonSignalHandler;
|
|
sig_action.sa_flags = SA_SIGINFO;
|
|
sigemptyset(&sig_action.sa_mask);
|
|
|
|
sigaction(signum, &sig_action, &sigdata->oldaction);
|
|
|
|
dmn->signals[dmn->nsignals-1] = sigdata;
|
|
|
|
virObjectUnlock(dmn);
|
|
return 0;
|
|
|
|
error:
|
|
VIR_FREE(sigdata);
|
|
virObjectUnlock(dmn);
|
|
return -1;
|
|
}
|
|
|
|
#else /* WIN32 */
|
|
|
|
int
|
|
virNetDaemonAddSignalHandler(virNetDaemon *dmn G_GNUC_UNUSED,
|
|
int signum G_GNUC_UNUSED,
|
|
virNetDaemonSignalFunc func G_GNUC_UNUSED,
|
|
void *opaque G_GNUC_UNUSED)
|
|
{
|
|
virReportSystemError(ENOSYS, "%s",
|
|
_("Signal handling not available on this platform"));
|
|
return -1;
|
|
}
|
|
|
|
#endif /* WIN32 */
|
|
|
|
|
|
static void
|
|
virNetDaemonAutoShutdownTimer(int timerid G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virNetDaemon *dmn = opaque;
|
|
|
|
virObjectLock(dmn);
|
|
|
|
if (!dmn->autoShutdownInhibitions) {
|
|
VIR_DEBUG("Automatic shutdown triggered");
|
|
dmn->quit = true;
|
|
}
|
|
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
static int
|
|
daemonServerUpdateServices(void *payload,
|
|
const char *key G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
bool *enable = opaque;
|
|
virNetServer *srv = payload;
|
|
|
|
virNetServerUpdateServices(srv, *enable);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
virNetDaemonUpdateServices(virNetDaemon *dmn,
|
|
bool enabled)
|
|
{
|
|
virObjectLock(dmn);
|
|
virHashForEach(dmn->servers, daemonServerUpdateServices, &enabled);
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
static int
|
|
daemonServerProcessClients(void *payload,
|
|
const char *key G_GNUC_UNUSED,
|
|
void *opaque G_GNUC_UNUSED)
|
|
{
|
|
virNetServer *srv = payload;
|
|
|
|
virNetServerProcessClients(srv);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
daemonServerShutdownWait(void *payload,
|
|
const char *key G_GNUC_UNUSED,
|
|
void *opaque G_GNUC_UNUSED)
|
|
{
|
|
virNetServer *srv = payload;
|
|
|
|
virNetServerShutdownWait(srv);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
daemonShutdownWait(void *opaque)
|
|
{
|
|
virNetDaemon *dmn = opaque;
|
|
bool graceful = false;
|
|
|
|
virHashForEach(dmn->servers, daemonServerShutdownWait, NULL);
|
|
if (dmn->shutdownWaitCb && dmn->shutdownWaitCb() < 0)
|
|
goto finish;
|
|
|
|
if (dmn->stateStopThread)
|
|
virThreadJoin(dmn->stateStopThread);
|
|
|
|
graceful = true;
|
|
|
|
finish:
|
|
virObjectLock(dmn);
|
|
dmn->graceful = graceful;
|
|
virEventUpdateTimeout(dmn->finishTimer, 0);
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
static void
|
|
virNetDaemonFinishTimer(int timerid G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virNetDaemon *dmn = opaque;
|
|
|
|
virObjectLock(dmn);
|
|
dmn->finished = true;
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
void
|
|
virNetDaemonRun(virNetDaemon *dmn)
|
|
{
|
|
int timerid = -1;
|
|
bool timerActive = false;
|
|
virThread shutdownThread;
|
|
|
|
virObjectLock(dmn);
|
|
|
|
if (dmn->srvObject) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Not all servers restored, cannot run server"));
|
|
goto cleanup;
|
|
}
|
|
|
|
dmn->quit = false;
|
|
dmn->finishTimer = -1;
|
|
dmn->finished = false;
|
|
dmn->graceful = false;
|
|
|
|
if (dmn->autoShutdownTimeout &&
|
|
(timerid = virEventAddTimeout(-1,
|
|
virNetDaemonAutoShutdownTimer,
|
|
dmn, NULL)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Failed to register shutdown timeout"));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* We are accepting connections now. Notify systemd
|
|
* so it can start dependent services. */
|
|
virSystemdNotifyStartup();
|
|
|
|
VIR_DEBUG("dmn=%p quit=%d", dmn, dmn->quit);
|
|
while (!dmn->finished) {
|
|
/* A shutdown timeout is specified, so check
|
|
* if any drivers have active state, if not
|
|
* shutdown after timeout seconds
|
|
*/
|
|
if (dmn->autoShutdownTimeout) {
|
|
if (timerActive) {
|
|
if (virNetDaemonHasClients(dmn)) {
|
|
VIR_DEBUG("Deactivating shutdown timer %d", timerid);
|
|
virEventUpdateTimeout(timerid, -1);
|
|
timerActive = false;
|
|
}
|
|
} else {
|
|
if (!virNetDaemonHasClients(dmn)) {
|
|
VIR_DEBUG("Activating shutdown timer %d", timerid);
|
|
virEventUpdateTimeout(timerid,
|
|
dmn->autoShutdownTimeout * 1000);
|
|
timerActive = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
virObjectUnlock(dmn);
|
|
if (virEventRunDefaultImpl() < 0) {
|
|
virObjectLock(dmn);
|
|
VIR_DEBUG("Loop iteration error, exiting");
|
|
break;
|
|
}
|
|
virObjectLock(dmn);
|
|
|
|
virHashForEach(dmn->servers, daemonServerProcessClients, NULL);
|
|
|
|
/* don't shutdown services when performing an exec-restart */
|
|
if (dmn->quit && dmn->execRestart)
|
|
goto cleanup;
|
|
|
|
if (dmn->quit && dmn->finishTimer == -1) {
|
|
virHashForEach(dmn->servers, daemonServerClose, NULL);
|
|
if (dmn->shutdownPrepareCb && dmn->shutdownPrepareCb() < 0)
|
|
break;
|
|
|
|
if ((dmn->finishTimer = virEventAddTimeout(30 * 1000,
|
|
virNetDaemonFinishTimer,
|
|
dmn, NULL)) < 0) {
|
|
VIR_WARN("Failed to register finish timer.");
|
|
break;
|
|
}
|
|
|
|
if (virThreadCreateFull(&shutdownThread, true, daemonShutdownWait,
|
|
"daemon-shutdown", false, dmn) < 0) {
|
|
VIR_WARN("Failed to register join thread.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dmn->graceful) {
|
|
virThreadJoin(&shutdownThread);
|
|
} else {
|
|
VIR_WARN("Make forcefull daemon shutdown");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
cleanup:
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
|
|
void
|
|
virNetDaemonSetStateStopWorkerThread(virNetDaemon *dmn,
|
|
virThread **thr)
|
|
{
|
|
virObjectLock(dmn);
|
|
|
|
VIR_DEBUG("Setting state stop worker thread on dmn=%p to thr=%p", dmn, thr);
|
|
dmn->stateStopThread = g_steal_pointer(thr);
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
|
|
void
|
|
virNetDaemonQuit(virNetDaemon *dmn)
|
|
{
|
|
virObjectLock(dmn);
|
|
|
|
VIR_DEBUG("Quit requested %p", dmn);
|
|
dmn->quit = true;
|
|
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
|
|
void
|
|
virNetDaemonQuitExecRestart(virNetDaemon *dmn)
|
|
{
|
|
virObjectLock(dmn);
|
|
|
|
VIR_DEBUG("Exec-restart requested %p", dmn);
|
|
dmn->quit = true;
|
|
dmn->execRestart = true;
|
|
|
|
virObjectUnlock(dmn);
|
|
}
|
|
|
|
|
|
static int
|
|
daemonServerClose(void *payload,
|
|
const char *key G_GNUC_UNUSED,
|
|
void *opaque G_GNUC_UNUSED)
|
|
{
|
|
virNetServer *srv = payload;
|
|
|
|
virNetServerClose(srv);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
daemonServerHasClients(void *payload,
|
|
const char *key G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
bool *clients = opaque;
|
|
virNetServer *srv = payload;
|
|
|
|
if (virNetServerHasClients(srv))
|
|
*clients = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
virNetDaemonHasClients(virNetDaemon *dmn)
|
|
{
|
|
bool ret = false;
|
|
|
|
virHashForEach(dmn->servers, daemonServerHasClients, &ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
virNetDaemonSetShutdownCallbacks(virNetDaemon *dmn,
|
|
virNetDaemonShutdownCallback prepareCb,
|
|
virNetDaemonShutdownCallback waitCb)
|
|
{
|
|
virObjectLock(dmn);
|
|
|
|
dmn->shutdownPrepareCb = prepareCb;
|
|
dmn->shutdownWaitCb = waitCb;
|
|
|
|
virObjectUnlock(dmn);
|
|
}
|