diff --git a/ChangeLog b/ChangeLog index 5b62492627..fa63ff8e78 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Tue Jan 20 16:48:00 CET 2009 Daniel Veillard + + * src/domain_conf.h src/qemu_driver.c: use monitor fd for QEmu/KVM + domain shutdown and read saved vm status on libvirtd startup, + last 2 patches from Guido Günther finishing up the surviving the + libvirt daemon restart + Tue Jan 20 16:35:24 CET 2009 Daniel Veillard * docs/apibuild.py: fix the parser with another Win32 keyword diff --git a/src/domain_conf.h b/src/domain_conf.h index 45b3e10cf4..c236a66ce4 100644 --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -465,6 +465,7 @@ struct _virDomainObj { int stderr_fd; int stderr_watch; int monitor; + int monitor_watch; char *monitorpath; int monitorWatch; int logfile; diff --git a/src/qemu_driver.c b/src/qemu_driver.c index 06f444bdeb..cde65a9903 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -181,6 +181,45 @@ qemudLogFD(virConnectPtr conn, const char* logDir, const char* name) } +static int +qemudLogReadFD(virConnectPtr conn, const char* logDir, const char* name, off_t pos) +{ + char logfile[PATH_MAX]; + mode_t logmode = O_RDONLY; + int ret, fd = -1; + + if ((ret = snprintf(logfile, sizeof(logfile), "%s/%s.log", logDir, name)) + < 0 || ret >= sizeof(logfile)) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to build logfile name %s/%s.log"), + logDir, name); + return -1; + } + + + if ((fd = open(logfile, logmode)) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to create logfile %s: %s"), + logfile, strerror(errno)); + return -1; + } + if (qemudSetCloseExec(fd) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Unable to set VM logfile close-on-exec flag: %s"), + strerror(errno)); + close(fd); + return -1; + } + if (lseek(fd, pos, SEEK_SET) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Unable to seek to %lld in %s: %s"), + pos, logfile, strerror(errno)); + close(fd); + } + return fd; +} + + static void qemudAutostartConfigs(struct qemud_driver *driver) { unsigned int i; @@ -258,6 +297,84 @@ cleanup: } +static int qemudOpenMonitor(virConnectPtr conn, + struct qemud_driver* driver, + 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 < driver->domains.count ; i++) { + virDomainObjPtr vm = driver->domains.objs[i]; + qemudDomainStatusPtr status = NULL; + char *config = NULL; + int rc; + + virDomainObjLock(vm); + 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, driver, 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; + + if((vm->logfile = qemudLogFD(NULL, driver->logDir, vm->def->name)) < 0) + return -1; + + 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: * @@ -357,6 +474,7 @@ qemudStartup(void) { qemu_driver->autostartDir, NULL, NULL) < 0) goto error; + qemudReconnectVMs(qemu_driver); qemudAutostartConfigs(qemu_driver); qemuDriverUnlock(qemu_driver); @@ -449,7 +567,6 @@ qemudActive(void) { */ static int qemudShutdown(void) { - unsigned int i; if (!qemu_driver) return -1; @@ -457,15 +574,6 @@ qemudShutdown(void) { qemuDriverLock(qemu_driver); virCapabilitiesFree(qemu_driver->caps); - /* shutdown active VMs */ - for (i = 0 ; i < qemu_driver->domains.count ; i++) { - virDomainObjPtr dom = qemu_driver->domains.objs[i]; - virDomainObjLock(dom); - if (virDomainIsActive(dom)) - qemudShutdownVMDaemon(NULL, qemu_driver, dom); - virDomainObjUnlock(dom); - } - virDomainObjListFree(&qemu_driver->domains); VIR_FREE(qemu_driver->logDir); @@ -516,11 +624,7 @@ qemudReadMonitorOutput(virConnectPtr conn, int ret; ret = read(fd, buf+got, buflen-got-1); - if (ret == 0) { - qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("QEMU quit during %s startup\n%s"), what, buf); - return -1; - } + if (ret < 0) { struct pollfd pfd = { .fd = fd, .events = POLLIN }; if (errno == EINTR) @@ -584,8 +688,10 @@ qemudCheckMonitorPrompt(virConnectPtr conn ATTRIBUTE_UNUSED, } static int qemudOpenMonitor(virConnectPtr conn, + struct qemud_driver* driver, virDomainObjPtr vm, - const char *monitor) { + const char *monitor, + int reconnect) { int monfd; char buf[1024]; int ret = -1; @@ -606,11 +712,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, @@ -618,6 +732,12 @@ static int qemudOpenMonitor(virConnectPtr conn, goto error; } + if ((vm->monitor_watch = virEventAddHandle(vm->monitor, 0, + qemudDispatchVMEvent, + driver, NULL)) < 0) + goto error; + + /* Keep monitor open upon success */ if (ret == 0) return ret; @@ -677,6 +797,7 @@ qemudFindCharDevicePTYs(virConnectPtr conn, const char *output, int fd ATTRIBUTE_UNUSED) { + struct qemud_driver* driver = conn->privateData; char *monitor = NULL; size_t offset = 0; int ret, i; @@ -711,7 +832,7 @@ qemudFindCharDevicePTYs(virConnectPtr conn, } /* Got them all, so now open the monitor console */ - ret = qemudOpenMonitor(conn, vm, monitor); + ret = qemudOpenMonitor(conn, driver, vm, monitor, 0); cleanup: VIR_FREE(monitor); @@ -719,21 +840,23 @@ cleanup: } static int qemudWaitForMonitor(virConnectPtr conn, - virDomainObjPtr vm) { + struct qemud_driver* driver, + virDomainObjPtr vm, off_t pos) +{ char buf[1024]; /* Plenty of space to get startup greeting */ - int ret = qemudReadMonitorOutput(conn, - vm, vm->stderr_fd, - buf, sizeof(buf), - qemudFindCharDevicePTYs, - "console", 3000); + int logfd; + int ret; - buf[sizeof(buf)-1] = '\0'; + if ((logfd = qemudLogReadFD(conn, driver->logDir, vm->def->name, pos)) + < 0) + return logfd; - if (safewrite(vm->logfile, buf, strlen(buf)) < 0) { - /* Log, but ignore failures to write logfile for VM */ - qemudLog(QEMUD_WARN, _("Unable to log VM console data: %s\n"), + ret = qemudReadMonitorOutput(conn, vm, logfd, buf, sizeof(buf), + qemudFindCharDevicePTYs, + "console", 3000); + if (close(logfd) < 0) + qemudLog(QEMUD_WARN, _("Unable to close logfile: %s\n"), strerror(errno)); - } return ret; } @@ -942,6 +1065,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, fd_set keepfd; const char *emulator; pid_t child; + int pos = -1; FD_ZERO(&keepfd); @@ -1034,14 +1158,15 @@ static int qemudStartVMDaemon(virConnectPtr conn, qemudLog(QEMUD_WARN, _("Unable to write argv to logfile %d: %s\n"), errno, strerror(errno)); - vm->stdout_fd = -1; - vm->stderr_fd = -1; + if ((pos = lseek(vm->logfile, 0, SEEK_END)) < 0) + qemudLog(QEMUD_WARN, _("Unable to seek to end of logfile %d: %s\n"), + errno, strerror(errno)); for (i = 0 ; i < ntapfds ; i++) FD_SET(tapfds[i], &keepfd); ret = virExec(conn, argv, progenv, &keepfd, &child, - vm->stdin_fd, &vm->stdout_fd, &vm->stderr_fd, + vm->stdin_fd, &vm->logfile, &vm->logfile, VIR_EXEC_NONBLOCK | VIR_EXEC_DAEMON); /* wait for qemu process to to show up */ @@ -1078,19 +1203,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, } if (ret == 0) { - 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) || - (qemudWaitForMonitor(conn, vm) < 0) || + if ((qemudWaitForMonitor(conn, driver, vm, pos) < 0) || (qemudDetectVcpuPIDs(conn, vm) < 0) || (qemudInitCpus(conn, vm, migrateFrom) < 0)) { qemudShutdownVMDaemon(conn, driver, vm); @@ -1102,32 +1215,6 @@ static int qemudStartVMDaemon(virConnectPtr conn, return ret; } -static int qemudVMData(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, int fd) { - char buf[4096]; - if (vm->pid < 0) - return 0; - - for (;;) { - int ret = read(fd, buf, sizeof(buf)-1); - if (ret < 0) { - if (errno == EAGAIN) - return 0; - return -1; - } - if (ret == 0) { - return 0; - } - buf[ret] = '\0'; - - if (safewrite(vm->logfile, buf, ret) < 0) { - /* Log, but ignore failures to write logfile for VM */ - qemudLog(QEMUD_WARN, _("Unable to log VM console data: %s\n"), - strerror(errno)); - } - } -} - static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, struct qemud_driver *driver, virDomainObjPtr vm) { @@ -1141,22 +1228,14 @@ static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, qemudLog(QEMUD_ERROR, _("Failed to send SIGTERM to %s (%d): %s\n"), vm->def->name, vm->pid, strerror(errno)); - qemudVMData(driver, vm, vm->stdout_fd); - qemudVMData(driver, vm, vm->stderr_fd); - - virEventRemoveHandle(vm->stdout_watch); - virEventRemoveHandle(vm->stderr_watch); + virEventRemoveHandle(vm->monitor_watch); if (close(vm->logfile) < 0) qemudLog(QEMUD_WARN, _("Unable to close logfile %d: %s\n"), errno, strerror(errno)); - close(vm->stdout_fd); - close(vm->stderr_fd); if (vm->monitor != -1) close(vm->monitor); vm->logfile = -1; - vm->stdout_fd = -1; - vm->stderr_fd = -1; vm->monitor = -1; /* shut it off for sure */ @@ -1191,8 +1270,7 @@ qemudDispatchVMEvent(int watch, int fd, int events, void *opaque) { virDomainObjPtr tmpvm = driver->domains.objs[i]; virDomainObjLock(tmpvm); if (virDomainIsActive(tmpvm) && - (tmpvm->stdout_watch == watch || - tmpvm->stderr_watch == watch)) { + tmpvm->monitor_watch == watch) { vm = tmpvm; break; } @@ -1202,15 +1280,15 @@ qemudDispatchVMEvent(int watch, int fd, int events, void *opaque) { if (!vm) goto cleanup; - if (vm->stdout_fd != fd && - vm->stderr_fd != fd) { + if (vm->monitor != fd) { failed = 1; } else { - if (events & VIR_EVENT_HANDLE_READABLE) { - if (qemudVMData(driver, vm, fd) < 0) - failed = 1; - } else { + if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) quit = 1; + else { + qemudLog(QEMUD_ERROR, _("unhandled fd event %d for %s"), + events, vm->def->name); + failed = 1; } }