The earlier patches didn't change libvirtd behaviour (except for
kvm/qemu not being teared down on an unexpected daemon crash), all vms
were still being shutoff at daemon shutdown. This patch now adds the
code to read back the vm status after on daemon startup and reconnects
all running vms found in stateDir which are left over from a daemon
restart or crash and also disables shutting down of VMs when libvirt
quits.
---
src/qemu_driver.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 158 insertions(+), 7 deletions(-)
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index d8b87e4..57a396c 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -183,6 +183,46 @@ qemudAutostartConfigs(struct qemud_driver *driver) {
}
+static int
+qemudGetProcFD(pid_t pid, int fdnum)
+{
+ int fd;
+
+#ifdef __linux__
+ char *path = NULL;
+
+ if (!asprintf(&path, "/proc/%d/fd/%d", pid, fdnum)) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+ return -1;
+ }
+
+ if((fd = open(path, O_RDONLY)) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Unable to open %s"), path);
+ return -1;
+ }
+ if (qemudSetCloseExec(fd) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Unable to set close-on-exec flag on %s"), path);
+ goto error;
+ }
+
+ if (qemudSetNonBlock(fd) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Unable to put %s into non-blocking mode"), path);
+ goto error;
+ }
+
+ VIR_FREE(path);
+ return fd;
+error:
+ VIR_FREE(path);
+ close(fd);
+#endif
+ return -1;
+}
+
+
/**
* qemudRemoveDomainStatus
*
@@ -220,6 +260,104 @@ cleanup:
}
+static int qemudOpenMonitor(virConnectPtr conn,
+ virDomainObjPtr vm,
+ const char *monitor,
+ int reconnect);
+
+/**
+ * qemudReconnectVMs
+ *
+ * Reconnect running vms to the daemon process
+ */
+static int
+qemudReconnectVMs(struct qemud_driver *driver)
+{
+ int i;
+
+ for (i = 0 ; i < qemu_driver->domains.count ; i++) {
+ virDomainObjPtr vm = qemu_driver->domains.objs[i];
+ qemudDomainStatusPtr status = NULL;
+ char *config = NULL;
+ int rc;
+
+ virDomainObjLock(vm);
+ /* Read pid */
+ if ((rc = virFileReadPid(driver->stateDir, vm->def->name,
&vm->pid)) == 0)
+ DEBUG("Found pid %d for '%s'", vm->pid,
vm->def->name);
+ else
+ goto next;
+
+ if ((config = virDomainConfigFile(NULL,
+ driver->stateDir,
+ vm->def->name)) == NULL) {
+ qemudLog(QEMUD_ERR, _("Failed to read domain status for %s\n"),
+ vm->def->name);
+ goto next_error;
+ }
+
+ status = qemudDomainStatusParseFile(NULL, driver->caps, config, 0);
+ if (status) {
+ vm->newDef = vm->def;
+ vm->def = status->def;
+ } else {
+ qemudLog(QEMUD_ERR, _("Failed to parse domain status for %s\n"),
+ vm->def->name);
+ goto next_error;
+ }
+
+ if ((rc = qemudOpenMonitor(NULL, vm, status->monitorpath, 1)) != 0) {
+ qemudLog(QEMUD_ERR, _("Failed to reconnect monitor for %s: %d\n"),
+ vm->def->name, rc);
+ goto next_error;
+ } else
+ vm->monitorpath = status->monitorpath;
+
+ vm->stdin_fd = qemudGetProcFD(vm->pid, 0);
+ vm->stdout_fd = qemudGetProcFD(vm->pid, 1);
+ vm->stderr_fd = qemudGetProcFD(vm->pid, 2);
+ if (vm->stdin_fd == -1 || vm->stdout_fd == -1 || vm->stderr_fd == -1)
+ goto next_error;
+
+ if (((vm->stdout_watch = virEventAddHandle(vm->stdout_fd,
+ VIR_EVENT_HANDLE_READABLE |
+ VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP,
+ qemudDispatchVMEvent,
+ driver, NULL)) < 0) ||
+ ((vm->stderr_watch = virEventAddHandle(vm->stderr_fd,
+ VIR_EVENT_HANDLE_READABLE |
+ VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP,
+ qemudDispatchVMEvent,
+ driver, NULL)) < 0)) {
+
+ qemudLog(QEMUD_ERR, _("Failed to reconnect to stdout/stderr\n"));
+ goto next_error;
+ }
+
+ if (vm->def->id >= driver->nextvmid)
+ driver->nextvmid = vm->def->id + 1;
+
+ vm->state = status->state;
+ goto next;
+
+next_error:
+ /* we failed to reconnect the vm so remove it's traces */
+ vm->def->id = -1;
+ qemudRemoveDomainStatus(NULL, driver, vm);
+ virDomainDefFree(vm->def);
+ vm->def = vm->newDef;
+ vm->newDef = NULL;
+next:
+ virDomainObjUnlock(vm);
+ VIR_FREE(status);
+ VIR_FREE(config);
+ }
+ return 0;
+}
+
+
/**
* qemudStartup:
*
@@ -316,6 +454,7 @@ qemudStartup(void) {
qemu_driver->autostartDir,
NULL, NULL) < 0)
goto error;
+ qemudReconnectVMs(qemu_driver);
qemudAutostartConfigs(qemu_driver);
qemuDriverUnlock(qemu_driver);
@@ -418,11 +557,14 @@ qemudShutdown(void) {
/* shutdown active VMs */
for (i = 0 ; i < qemu_driver->domains.count ; i++) {
+ /* FIXME: don't shutdown VMs on daemon shutdown for now */
+#if 0
virDomainObjPtr dom = qemu_driver->domains.objs[i];
virDomainObjLock(dom);
if (virDomainIsActive(dom))
qemudShutdownVMDaemon(NULL, qemu_driver, dom);
virDomainObjUnlock(dom);
+#endif
}
virDomainObjListFree(&qemu_driver->domains);
@@ -543,7 +685,8 @@ qemudCheckMonitorPrompt(virConnectPtr conn ATTRIBUTE_UNUSED,
static int qemudOpenMonitor(virConnectPtr conn,
virDomainObjPtr vm,
- const char *monitor) {
+ const char *monitor,
+ int reconnect) {
int monfd;
char buf[1024];
int ret = -1;
@@ -564,11 +707,19 @@ static int qemudOpenMonitor(virConnectPtr conn,
goto error;
}
- ret = qemudReadMonitorOutput(conn,
- vm, monfd,
- buf, sizeof(buf),
- qemudCheckMonitorPrompt,
- "monitor", 10000);
+ if (!reconnect) {
+ ret = qemudReadMonitorOutput(conn,
+ vm, monfd,
+ buf, sizeof(buf),
+ qemudCheckMonitorPrompt,
+ "monitor", 10000);
+ } else {
+ vm->monitor = monfd;
+ ret = 0;
+ }
+
+ if (ret != 0)
+ goto error;
if (!(vm->monitorpath = strdup(monitor))) {
qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
@@ -669,7 +820,7 @@ qemudFindCharDevicePTYs(virConnectPtr conn,
}
/* Got them all, so now open the monitor console */
- ret = qemudOpenMonitor(conn, vm, monitor);
+ ret = qemudOpenMonitor(conn, vm, monitor, 0);
cleanup:
VIR_FREE(monitor);
--
1.6.0.2