diff --git a/src/remote/remote_daemon.c b/src/remote/remote_daemon.c index 1aa9bfc0d2..2ac4f6cd2e 100644 --- a/src/remote/remote_daemon.c +++ b/src/remote/remote_daemon.c @@ -1193,6 +1193,9 @@ int main(int argc, char **argv) { #endif /* Run event loop. */ + virNetDaemonSetShutdownCallbacks(dmn, + virStateShutdownPrepare, + virStateShutdownWait); virNetDaemonRun(dmn); ret = 0; @@ -1201,9 +1204,6 @@ int main(int argc, char **argv) { 0, "shutdown", NULL, NULL); cleanup: - /* Keep cleanup order in inverse order of startup */ - virNetDaemonClose(dmn); - virNetlinkEventServiceStopAll(); if (driversInitialized) { diff --git a/src/rpc/virnetdaemon.c b/src/rpc/virnetdaemon.c index 5e3a41d19c..c81685c0f3 100644 --- a/src/rpc/virnetdaemon.c +++ b/src/rpc/virnetdaemon.c @@ -71,7 +71,10 @@ struct _virNetDaemon { virNetDaemonShutdownCallback shutdownPrepareCb; virNetDaemonShutdownCallback shutdownWaitCb; + int finishTimer; bool quit; + bool finished; + bool graceful; unsigned int autoShutdownTimeout; size_t autoShutdownInhibitions; @@ -82,6 +85,11 @@ struct _virNetDaemon { static virClassPtr virNetDaemonClass; +static int +daemonServerClose(void *payload, + const void *key G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED); + static void virNetDaemonDispose(void *obj) { @@ -798,11 +806,53 @@ daemonServerProcessClients(void *payload, return 0; } +static int +daemonServerShutdownWait(void *payload, + const void *key G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + virNetServerPtr srv = payload; + + virNetServerShutdownWait(srv); + return 0; +} + +static void +daemonShutdownWait(void *opaque) +{ + virNetDaemonPtr dmn = opaque; + bool graceful = false; + + virHashForEach(dmn->servers, daemonServerShutdownWait, NULL); + if (dmn->shutdownWaitCb && dmn->shutdownWaitCb() < 0) + goto finish; + + graceful = true; + + finish: + virObjectLock(dmn); + dmn->graceful = graceful; + virEventUpdateTimeout(dmn->finishTimer, 0); + virObjectUnlock(dmn); +} + +static void +virNetDaemonFinishTimer(int timerid G_GNUC_UNUSED, + void *opaque) +{ + virNetDaemonPtr dmn = opaque; + + virObjectLock(dmn); + dmn->finished = true; + virObjectUnlock(dmn); +} + void virNetDaemonRun(virNetDaemonPtr dmn) { int timerid = -1; bool timerActive = false; + virThread shutdownThread; virObjectLock(dmn); @@ -813,6 +863,9 @@ virNetDaemonRun(virNetDaemonPtr dmn) } dmn->quit = false; + dmn->finishTimer = -1; + dmn->finished = false; + dmn->graceful = false; if (dmn->autoShutdownTimeout && (timerid = virEventAddTimeout(-1, @@ -828,7 +881,7 @@ virNetDaemonRun(virNetDaemonPtr dmn) virSystemdNotifyStartup(); VIR_DEBUG("dmn=%p quit=%d", dmn, dmn->quit); - while (!dmn->quit) { + while (!dmn->finished) { /* A shutdown timeout is specified, so check * if any drivers have active state, if not * shutdown after timeout seconds @@ -859,6 +912,32 @@ virNetDaemonRun(virNetDaemonPtr dmn) virObjectLock(dmn); virHashForEach(dmn->servers, daemonServerProcessClients, NULL); + + 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: