/* * uml_driver.c: core driver methods for managing UML guests * * Copyright (C) 2006, 2007, 2008 Red Hat, Inc. * Copyright (C) 2006-2008 Daniel P. Berrange * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Daniel P. Berrange */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_NUMACTL #include #endif #include "uml_driver.h" #include "uml_conf.h" #include "event.h" #include "buf.h" #include "util.h" #include "nodeinfo.h" #include "stats_linux.h" #include "capabilities.h" #include "memory.h" #include "uuid.h" #include "domain_conf.h" #include "datatypes.h" /* For storing short-lived temporary files. */ #define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt" static int umlShutdown(void); /* umlDebug statements should be changed to use this macro instead. */ #define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__) #define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg) #define umlLog(level, msg...) fprintf(stderr, msg) static int umlOpenMonitor(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr vm); static int umlReadPidFile(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr vm); static int umlSetCloseExec(int fd) { int flags; if ((flags = fcntl(fd, F_GETFD)) < 0) goto error; flags |= FD_CLOEXEC; if ((fcntl(fd, F_SETFD, flags)) < 0) goto error; return 0; error: umlLog(UML_ERR, "%s", _("Failed to set close-on-exec file descriptor flag\n")); return -1; } static int umlStartVMDaemon(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr vm); static void umlShutdownVMDaemon(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr vm); static int umlMonitorCommand (virConnectPtr conn, const struct uml_driver *driver, const virDomainObjPtr vm, const char *cmd, char **reply); static struct uml_driver *uml_driver = NULL; static void umlAutostartConfigs(struct uml_driver *driver) { unsigned int i; for (i = 0 ; i < driver->domains.count ; i++) { if (driver->domains.objs[i]->autostart && !virDomainIsActive(driver->domains.objs[i]) && umlStartVMDaemon(NULL, driver, driver->domains.objs[i]) < 0) { virErrorPtr err = virGetLastError(); umlLog(UML_ERR, _("Failed to autostart VM '%s': %s\n"), driver->domains.objs[i]->def->name, err->message); } } } static int umlIdentifyOneChrPTY(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr dom, virDomainChrDefPtr def, const char *dev) { char *cmd; char *res = NULL; int retries = 0; if (asprintf(&cmd, "config %s%d", dev, def->dstPort) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); return -1; } requery: umlMonitorCommand(NULL, driver, dom, cmd, &res); if (STRPREFIX(res, "pts:")) { VIR_FREE(def->data.file.path); if ((def->data.file.path = strdup(res + 4)) == NULL) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); VIR_FREE(res); VIR_FREE(cmd); return -1; } } else if (STRPREFIX(res, "pts")) { /* It can take a while to startup, so retry for upto 5 seconds */ /* XXX should do this in a better non-blocking way somehow ...perhaps register a timer */ if (retries++ < 50) { usleep(1000*10); goto requery; } } VIR_FREE(cmd); VIR_FREE(res); return 0; } static int umlIdentifyChrPTY(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr dom) { int i; if (dom->def->console && dom->def->console->type == VIR_DOMAIN_CHR_TYPE_PTY) if (umlIdentifyOneChrPTY(conn, driver, dom, dom->def->console, "con") < 0) return -1; for (i = 0 ; i < dom->def->nserials; i++) if (dom->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PTY && umlIdentifyOneChrPTY(conn, driver, dom, dom->def->serials[i], "ssl") < 0) return -1; return 0; } static void umlInotifyEvent(int watch, int fd, int events ATTRIBUTE_UNUSED, void *data) { char buf[1024]; struct inotify_event *e; int got; char *tmp, *name; struct uml_driver *driver = data; virDomainObjPtr dom; if (watch != driver->inotifyWatch) return; reread: got = read(fd, buf, sizeof(buf)); if (got == -1) { if (errno == EINTR) goto reread; return; } tmp = buf; while (got) { if (got < sizeof(struct inotify_event)) return; /* bad */ e = (struct inotify_event *)tmp; tmp += sizeof(struct inotify_event); got -= sizeof(struct inotify_event); if (got < e->len) return; tmp += e->len; got -= e->len; name = (char *)&(e->name); dom = virDomainFindByName(&driver->domains, name); if (!dom) { continue; } if (e->mask & IN_DELETE) { if (!virDomainIsActive(dom)) { continue; } dom->def->id = -1; dom->pid = -1; if (dom->newDef) { virDomainDefFree(dom->def); dom->def = dom->newDef; } dom->state = VIR_DOMAIN_SHUTOFF; } else if (e->mask & (IN_CREATE | IN_MODIFY)) { if (virDomainIsActive(dom)) { continue; } if (umlReadPidFile(NULL, driver, dom) < 0) { continue; } dom->def->id = driver->nextvmid++; dom->state = VIR_DOMAIN_RUNNING; if (umlOpenMonitor(NULL, driver, dom) < 0) umlShutdownVMDaemon(NULL, driver, dom); if (umlIdentifyChrPTY(NULL, driver, dom) < 0) umlShutdownVMDaemon(NULL, driver, dom); } } } /** * umlStartup: * * Initialization function for the Uml daemon */ static int umlStartup(void) { uid_t uid = geteuid(); struct passwd *pw; char *base = NULL; char driverConf[PATH_MAX]; if (VIR_ALLOC(uml_driver) < 0) return -1; /* Don't have a dom0 so start from 1 */ uml_driver->nextvmid = 1; if (!(pw = getpwuid(uid))) { umlLog(UML_ERR, _("Failed to find user record for uid '%d': %s\n"), uid, strerror(errno)); goto out_nouid; } if (!uid) { if (asprintf(¨_driver->logDir, "%s/log/libvirt/uml", LOCAL_STATE_DIR) == -1) goto out_of_memory; if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) goto out_of_memory; } else { if (asprintf(¨_driver->logDir, "%s/.libvirt/uml/log", pw->pw_dir) == -1) goto out_of_memory; if (asprintf (&base, "%s/.libvirt", pw->pw_dir) == -1) goto out_of_memory; } if (asprintf (¨_driver->monitorDir, "%s/.uml", pw->pw_dir) == -1) goto out_of_memory; /* Configuration paths are either ~/.libvirt/uml/... (session) or * /etc/libvirt/uml/... (system). */ if (snprintf (driverConf, sizeof(driverConf), "%s/uml.conf", base) == -1) goto out_of_memory; driverConf[sizeof(driverConf)-1] = '\0'; if (asprintf (¨_driver->configDir, "%s/uml", base) == -1) goto out_of_memory; if (asprintf (¨_driver->autostartDir, "%s/uml/autostart", base) == -1) goto out_of_memory; VIR_FREE(base); if ((uml_driver->caps = umlCapsInit()) == NULL) goto out_of_memory; if ((uml_driver->inotifyFD = inotify_init()) < 0) { umlLog(UML_ERR, "%s", _("cannot initialize inotify")); goto out_nouid; } if (virFileMakePath(uml_driver->monitorDir) < 0) { umlLog(UML_ERR, _("Failed to create monitor directory %s: %s"), uml_driver->monitorDir, strerror(errno)); umlShutdown(); return -1; } if ((uml_driver->inotifyWatch = inotify_add_watch(uml_driver->inotifyFD, uml_driver->monitorDir, IN_CREATE | IN_MODIFY | IN_DELETE)) < 0) { umlShutdown(); return -1; } if (virEventAddHandle(uml_driver->inotifyFD, POLLIN, umlInotifyEvent, uml_driver, NULL) < 0) { umlShutdown(); return -1; } if (virDomainLoadAllConfigs(NULL, uml_driver->caps, ¨_driver->domains, uml_driver->configDir, uml_driver->autostartDir, NULL, NULL) < 0) { umlShutdown(); return -1; } umlAutostartConfigs(uml_driver); return 0; out_of_memory: umlLog (UML_ERR, "%s", _("umlStartup: out of memory\n")); out_nouid: VIR_FREE(base); VIR_FREE(uml_driver); return -1; } /** * umlReload: * * Function to restart the Uml daemon, it will recheck the configuration * files and update its state and the networking */ static int umlReload(void) { if (!uml_driver) return 0; virDomainLoadAllConfigs(NULL, uml_driver->caps, ¨_driver->domains, uml_driver->configDir, uml_driver->autostartDir, NULL, NULL); umlAutostartConfigs(uml_driver); return 0; } /** * umlActive: * * Checks if the Uml daemon is active, i.e. has an active domain or * an active network * * Returns 1 if active, 0 otherwise */ static int umlActive(void) { unsigned int i; if (!uml_driver) return 0; for (i = 0 ; i < uml_driver->domains.count ; i++) if (virDomainIsActive(uml_driver->domains.objs[i])) return 1; /* Otherwise we're happy to deal with a shutdown */ return 0; } /** * umlShutdown: * * Shutdown the Uml daemon, it will stop all active domains and networks */ static int umlShutdown(void) { unsigned int i; if (!uml_driver) return -1; virEventRemoveHandle(uml_driver->inotifyWatch); close(uml_driver->inotifyFD); virCapabilitiesFree(uml_driver->caps); /* shutdown active VMs */ for (i = 0 ; i < uml_driver->domains.count ; i++) { virDomainObjPtr dom = uml_driver->domains.objs[i]; if (virDomainIsActive(dom)) umlShutdownVMDaemon(NULL, uml_driver, dom); if (!dom->persistent) virDomainRemoveInactive(¨_driver->domains, dom); } virDomainObjListFree(¨_driver->domains); VIR_FREE(uml_driver->logDir); VIR_FREE(uml_driver->configDir); VIR_FREE(uml_driver->autostartDir); VIR_FREE(uml_driver->monitorDir); if (uml_driver->brctl) brShutdown(uml_driver->brctl); VIR_FREE(uml_driver); return 0; } static int umlReadPidFile(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr vm) { int rc = -1; FILE *file; char *pidfile = NULL; int retries = 0; vm->pid = -1; if (asprintf(&pidfile, "%s/%s/pid", driver->monitorDir, vm->def->name) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); return -1; } reopen: if (!(file = fopen(pidfile, "r"))) { if (errno == ENOENT && retries++ < 50) { usleep(1000 * 100); goto reopen; } goto cleanup; } if (fscanf(file, "%d", &vm->pid) != 1) { errno = EINVAL; goto cleanup; } if (fclose(file) < 0) goto cleanup; rc = 0; cleanup: if (rc != 0) umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("failed to read pid: %s: %s"), pidfile, strerror(errno)); VIR_FREE(pidfile); return rc; } static int umlMonitorAddress(virConnectPtr conn, const struct uml_driver *driver, virDomainObjPtr vm, struct sockaddr_un *addr) { char *sockname; if (asprintf(&sockname, "%s/%s/mconsole", driver->monitorDir, vm->def->name) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); return -1; } memset(addr, 0, sizeof *addr); addr->sun_family = AF_UNIX; strncpy(addr->sun_path, sockname, sizeof(addr->sun_path)-1); NUL_TERMINATE(addr->sun_path); VIR_FREE(sockname); return 0; } static int umlOpenMonitor(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr vm) { struct sockaddr_un addr; struct stat sb; int retries = 0; if (umlMonitorAddress(conn, driver, vm, &addr) < 0) return -1; restat: if (stat(addr.sun_path, &sb) < 0) { if (errno == ENOENT && retries < 50) { usleep(1000 * 100); goto restat; } return -1; } if ((vm->monitor = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot open socket %s"), strerror(errno)); return -1; } memset(addr.sun_path, 0, sizeof addr.sun_path); sprintf(addr.sun_path + 1, "%u", getpid()); if (bind(vm->monitor, (struct sockaddr *)&addr, sizeof addr) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot bind socket %s"), strerror(errno)); close(vm->monitor); vm->monitor = -1; return -1; } return 0; } #define MONITOR_MAGIC 0xcafebabe #define MONITOR_BUFLEN 512 #define MONITOR_VERSION 2 struct monitor_request { uint32_t magic; uint32_t version; uint32_t length; char data[MONITOR_BUFLEN]; }; struct monitor_response { uint32_t error; uint32_t extra; uint32_t length; char data[MONITOR_BUFLEN]; }; static int umlMonitorCommand(virConnectPtr conn, const struct uml_driver *driver, const virDomainObjPtr vm, const char *cmd, char **reply) { struct monitor_request req; struct monitor_response res; char *retdata = NULL; int retlen = 0, ret = 0; struct sockaddr_un addr; unsigned int addrlen; *reply = NULL; if (umlMonitorAddress(conn, driver, vm, &addr) < 0) return -1; memset(&req, 0, sizeof(req)); req.magic = MONITOR_MAGIC; req.version = MONITOR_VERSION; req.length = strlen(cmd); if (req.length > (MONITOR_BUFLEN-1)) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot send too long command %s: %s"), cmd, strerror(EINVAL)); return -1; } strncpy(req.data, cmd, req.length); req.data[req.length] = '\0'; if (sendto(vm->monitor, &req, sizeof req, 0, (struct sockaddr *)&addr, sizeof addr) != (sizeof req)) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot send command %s: %s"), cmd, strerror(errno)); return -1; } do { addrlen = sizeof(addr); if (recvfrom(vm->monitor, &res, sizeof res, 0, (struct sockaddr *)&addr, &addrlen) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot read reply %s: %s"), cmd, strerror(errno)); goto error; } if (VIR_REALLOC_N(retdata, retlen + res.length) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); goto error; } memcpy(retdata + retlen, res.data, res.length); retlen += res.length - 1; retdata[retlen] = '\0'; if (res.error) ret = -1; } while (res.extra); *reply = retdata; return ret; error: VIR_FREE(retdata); return -1; } static int umlStartVMDaemon(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr vm) { const char **argv = NULL, **tmp; const char **progenv = NULL; int i, ret, pid; char *logfile; int logfd = -1; struct stat sb; int *tapfds = NULL; int ntapfds = 0; fd_set keepfd; FD_ZERO(&keepfd); if (virDomainIsActive(vm)) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("VM is already active")); return -1; } if (!vm->def->os.kernel) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("no kernel specified")); return -1; } /* Make sure the binary we are about to try exec'ing exists. * Technically we could catch the exec() failure, but that's * in a sub-process so its hard to feed back a useful error */ if (stat(vm->def->os.kernel, &sb) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("Cannot find UML kernel %s: %s"), vm->def->os.kernel, strerror(errno)); return -1; } if (virFileMakePath(driver->logDir) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot create log directory %s"), driver->logDir); return -1; } if (asprintf(&logfile, "%s/%s.log", driver->logDir, vm->def->name) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL); return -1; } if ((logfd = open(logfile, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("failed to create logfile %s: %s"), logfile, strerror(errno)); VIR_FREE(logfile); return -1; } VIR_FREE(logfile); if (umlSetCloseExec(logfd) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("Unable to set VM logfile close-on-exec flag %s"), strerror(errno)); close(logfd); return -1; } if (umlBuildCommandLine(conn, driver, vm, &argv, &progenv, &tapfds, &ntapfds) < 0) { close(logfd); return -1; } tmp = progenv; while (*tmp) { if (safewrite(logfd, *tmp, strlen(*tmp)) < 0) umlLog(UML_WARN, _("Unable to write envv to logfile %d: %s\n"), errno, strerror(errno)); if (safewrite(logfd, " ", 1) < 0) umlLog(UML_WARN, _("Unable to write envv to logfile %d: %s\n"), errno, strerror(errno)); tmp++; } tmp = argv; while (*tmp) { if (safewrite(logfd, *tmp, strlen(*tmp)) < 0) umlLog(UML_WARN, _("Unable to write argv to logfile %d: %s\n"), errno, strerror(errno)); if (safewrite(logfd, " ", 1) < 0) umlLog(UML_WARN, _("Unable to write argv to logfile %d: %s\n"), errno, strerror(errno)); tmp++; } if (safewrite(logfd, "\n", 1) < 0) umlLog(UML_WARN, _("Unable to write argv to logfile %d: %s\n"), errno, strerror(errno)); vm->monitor = -1; vm->stdin_fd = -1; vm->stdout_fd = vm->stderr_fd = logfd; for (i = 0 ; i < ntapfds ; i++) FD_SET(tapfds[i], &keepfd); ret = virExec(conn, argv, progenv, &keepfd, &pid, vm->stdin_fd, &vm->stdout_fd, &vm->stderr_fd, VIR_EXEC_DAEMON); close(logfd); /* Cleanup intermediate proces */ if (waitpid(pid, NULL, 0) != pid) umlLog(UML_WARN, _("failed to wait on process: %d: %s\n"), pid, strerror(errno)); for (i = 0 ; argv[i] ; i++) VIR_FREE(argv[i]); VIR_FREE(argv); for (i = 0 ; progenv[i] ; i++) VIR_FREE(progenv[i]); VIR_FREE(progenv); if (tapfds) { for (i = 0 ; i < ntapfds ; i++) { close(tapfds[i]); } VIR_FREE(tapfds); } /* NB we don't mark it running here - we do that async with inotify */ return ret; } static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, struct uml_driver *driver ATTRIBUTE_UNUSED, virDomainObjPtr vm) { int ret; if (!virDomainIsActive(vm) || vm->pid <= 1) return; kill(vm->pid, SIGTERM); if (vm->monitor != -1) close(vm->monitor); vm->monitor = -1; if ((ret = waitpid(vm->pid, NULL, 0)) != vm->pid) { umlLog(UML_WARN, _("Got unexpected pid %d != %d\n"), ret, vm->pid); } vm->pid = -1; vm->def->id = -1; vm->state = VIR_DOMAIN_SHUTOFF; VIR_FREE(vm->vcpupids); vm->nvcpupids = 0; if (vm->newDef) { virDomainDefFree(vm->def); vm->def = vm->newDef; vm->def->id = -1; vm->newDef = NULL; } } static virDrvOpenStatus umlOpen(virConnectPtr conn, virConnectAuthPtr auth ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED) { uid_t uid = getuid(); if (uml_driver == NULL) goto decline; if (conn->uri != NULL) { if (conn->uri->scheme == NULL || conn->uri->path == NULL) goto decline; if (STRNEQ (conn->uri->scheme, "uml")) goto decline; if (uid != 0) { if (STRNEQ (conn->uri->path, "/session")) goto decline; } else { /* root */ if (STRNEQ (conn->uri->path, "/system") && STRNEQ (conn->uri->path, "/session")) goto decline; } } else { conn->uri = xmlParseURI(uid ? "uml:///session" : "uml:///system"); if (!conn->uri) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,NULL); return VIR_DRV_OPEN_ERROR; } } conn->privateData = uml_driver; return VIR_DRV_OPEN_SUCCESS; decline: return VIR_DRV_OPEN_DECLINED; } static int umlClose(virConnectPtr conn) { /*struct uml_driver *driver = (struct uml_driver *)conn->privateData;*/ conn->privateData = NULL; return 0; } static const char *umlGetType(virConnectPtr conn ATTRIBUTE_UNUSED) { return "UML"; } static int umlGetNodeInfo(virConnectPtr conn, virNodeInfoPtr nodeinfo) { return virNodeInfoPopulate(conn, nodeinfo); } static char *umlGetCapabilities(virConnectPtr conn) { struct uml_driver *driver = (struct uml_driver *)conn->privateData; char *xml; if ((xml = virCapabilitiesFormatXML(driver->caps)) == NULL) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "%s", _("failed to allocate space for capabilities support")); return NULL; } return xml; } #if HAVE_NUMACTL static int umlNodeGetCellsFreeMemory(virConnectPtr conn, unsigned long long *freeMems, int startCell, int maxCells) { int n, lastCell, numCells; if (numa_available() < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s", _("NUMA not supported on this host")); return -1; } lastCell = startCell + maxCells - 1; if (lastCell > numa_max_node()) lastCell = numa_max_node(); for (numCells = 0, n = startCell ; n <= lastCell ; n++) { long long mem; if (numa_node_size64(n, &mem) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to query NUMA free memory")); return -1; } freeMems[numCells++] = mem; } return numCells; } static unsigned long long umlNodeGetFreeMemory (virConnectPtr conn) { unsigned long long freeMem = 0; int n; if (numa_available() < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s", _("NUMA not supported on this host")); return -1; } for (n = 0 ; n <= numa_max_node() ; n++) { long long mem; if (numa_node_size64(n, &mem) < 0) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to query NUMA free memory")); return -1; } freeMem += mem; } return freeMem; } #endif static int umlGetProcessInfo(unsigned long long *cpuTime, int pid) { char proc[PATH_MAX]; FILE *pidinfo; unsigned long long usertime, systime; if (snprintf(proc, sizeof(proc), "/proc/%d/stat", pid) >= (int)sizeof(proc)) { return -1; } if (!(pidinfo = fopen(proc, "r"))) { /*printf("cannot read pid info");*/ /* VM probably shut down, so fake 0 */ *cpuTime = 0; return 0; } if (fscanf(pidinfo, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu", &usertime, &systime) != 2) { umlDebug("not enough arg"); return -1; } /* We got jiffies * We want nanoseconds * _SC_CLK_TCK is jiffies per second * So calulate thus.... */ *cpuTime = 1000ull * 1000ull * 1000ull * (usertime + systime) / (unsigned long long)sysconf(_SC_CLK_TCK); umlDebug("Got %llu %llu %llu", usertime, systime, *cpuTime); fclose(pidinfo); return 0; } static virDomainPtr umlDomainLookupByID(virConnectPtr conn, int id) { struct uml_driver *driver = (struct uml_driver *)conn->privateData; virDomainObjPtr vm = virDomainFindByID(&driver->domains, id); virDomainPtr dom; if (!vm) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_DOMAIN, NULL); return NULL; } dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; return dom; } static virDomainPtr umlDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid) { struct uml_driver *driver = (struct uml_driver *)conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, uuid); virDomainPtr dom; if (!vm) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_DOMAIN, NULL); return NULL; } dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; return dom; } static virDomainPtr umlDomainLookupByName(virConnectPtr conn, const char *name) { struct uml_driver *driver = (struct uml_driver *)conn->privateData; virDomainObjPtr vm = virDomainFindByName(&driver->domains, name); virDomainPtr dom; if (!vm) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_DOMAIN, NULL); return NULL; } dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; return dom; } static int umlGetVersion(virConnectPtr conn, unsigned long *version) { struct utsname ut; int major, minor, micro; uname(&ut); if (sscanf(ut.release, "%u.%u.%u", &major, &minor, µ) != 3) { umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot parse version %s"), ut.release); return -1; } *version = uml_driver->umlVersion; return 0; } static char * umlGetHostname (virConnectPtr conn) { int r; char hostname[HOST_NAME_MAX+1], *str; r = gethostname (hostname, HOST_NAME_MAX+1); if (r == -1) { umlReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR, "%s", strerror (errno)); return NULL; } /* Caller frees this string. */ str = strdup (hostname); if (str == NULL) { umlReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR, "%s", strerror (errno)); return NULL; } return str; } static int umlListDomains(virConnectPtr conn, int *ids, int nids) { struct uml_driver *driver = (struct uml_driver *)conn->privateData; int got = 0, i; for (i = 0 ; i < driver->domains.count && got < nids ; i++) if (virDomainIsActive(driver->domains.objs[i])) ids[got++] = driver->domains.objs[i]->def->id; return got; } static int umlNumDomains(virConnectPtr conn) { struct uml_driver *driver = (struct uml_driver *)conn->privateData; int n = 0, i; for (i = 0 ; i < driver->domains.count ; i++) if (virDomainIsActive(driver->domains.objs[i])) n++; return n; } static virDomainPtr umlDomainCreate(virConnectPtr conn, const char *xml, unsigned int flags ATTRIBUTE_UNUSED) { virDomainDefPtr def; virDomainObjPtr vm; virDomainPtr dom; struct uml_driver *driver = (struct uml_driver *)conn->privateData; if (!(def = virDomainDefParseString(conn, driver->caps, xml))) return NULL; vm = virDomainFindByName(&driver->domains, def->name); if (vm) { umlReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("domain '%s' is already defined"), def->name); virDomainDefFree(def); return NULL; } vm = virDomainFindByUUID(&driver->domains, def->uuid); if (vm) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(def->uuid, uuidstr); umlReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, _("domain with uuid '%s' is already defined"), uuidstr); virDomainDefFree(def); return NULL; } if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) { virDomainDefFree(def); return NULL; } if (umlStartVMDaemon(conn, driver, vm) < 0) { virDomainRemoveInactive(&driver->domains, vm); return NULL; } dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; return dom; } static int umlDomainShutdown(virDomainPtr dom) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByID(&driver->domains, dom->id); char* info; if (!vm) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, _("no domain with matching id %d"), dom->id); return -1; } #if 0 if (umlMonitorCommand(driver, vm, "system_powerdown", &info) < 0) { umlReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("shutdown operation failed")); return -1; } #endif VIR_FREE(info); return 0; } static int umlDomainDestroy(virDomainPtr dom) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByID(&driver->domains, dom->id); if (!vm) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, _("no domain with matching id %d"), dom->id); return -1; } umlShutdownVMDaemon(dom->conn, driver, vm); if (!vm->persistent) virDomainRemoveInactive(&driver->domains, vm); return 0; } static char *umlDomainGetOSType(virDomainPtr dom) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); char *type; if (!vm) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, "%s", _("no domain with matching uuid")); return NULL; } if (!(type = strdup(vm->def->os.type))) { umlReportError(dom->conn, dom, NULL, VIR_ERR_NO_MEMORY, "%s", _("failed to allocate space for ostype")); return NULL; } return type; } /* Returns max memory in kb, 0 if error */ static unsigned long umlDomainGetMaxMemory(virDomainPtr dom) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(dom->uuid, uuidstr); umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, _("no domain with matching uuid '%s'"), uuidstr); return 0; } return vm->def->maxmem; } static int umlDomainSetMaxMemory(virDomainPtr dom, unsigned long newmax) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(dom->uuid, uuidstr); umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, _("no domain with matching uuid '%s'"), uuidstr); return -1; } if (newmax < vm->def->memory) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, "%s", _("cannot set max memory lower than current memory")); return -1; } vm->def->maxmem = newmax; return 0; } static int umlDomainSetMemory(virDomainPtr dom, unsigned long newmem) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(dom->uuid, uuidstr); umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, _("no domain with matching uuid '%s'"), uuidstr); return -1; } if (virDomainIsActive(vm)) { umlReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT, "%s", _("cannot set memory of an active domain")); return -1; } if (newmem > vm->def->maxmem) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, "%s", _("cannot set memory higher than max memory")); return -1; } vm->def->memory = newmem; return 0; } static int umlDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, "%s", _("no domain with matching uuid")); return -1; } info->state = vm->state; if (!virDomainIsActive(vm)) { info->cpuTime = 0; } else { if (umlGetProcessInfo(&(info->cpuTime), vm->pid) < 0) { umlReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, ("cannot read cputime for domain")); return -1; } } info->maxMem = vm->def->maxmem; info->memory = vm->def->memory; info->nrVirtCpu = vm->def->vcpus; return 0; } static char *umlDomainDumpXML(virDomainPtr dom, int flags ATTRIBUTE_UNUSED) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, "%s", _("no domain with matching uuid")); return NULL; } return virDomainDefFormat(dom->conn, (flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef ? vm->newDef : vm->def, flags); } static int umlListDefinedDomains(virConnectPtr conn, char **const names, int nnames) { struct uml_driver *driver = (struct uml_driver *)conn->privateData; int got = 0, i; for (i = 0 ; i < driver->domains.count && got < nnames ; i++) { if (!virDomainIsActive(driver->domains.objs[i])) { if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) { umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "%s", _("failed to allocate space for VM name string")); goto cleanup; } } } return got; cleanup: for (i = 0 ; i < got ; i++) VIR_FREE(names[i]); return -1; } static int umlNumDefinedDomains(virConnectPtr conn) { struct uml_driver *driver = (struct uml_driver *)conn->privateData; int n = 0, i; for (i = 0 ; i < driver->domains.count ; i++) if (!virDomainIsActive(driver->domains.objs[i])) n++; return n; } static int umlDomainStart(virDomainPtr dom) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, "%s", _("no domain with matching uuid")); return -1; } return umlStartVMDaemon(dom->conn, driver, vm); } static virDomainPtr umlDomainDefine(virConnectPtr conn, const char *xml) { struct uml_driver *driver = (struct uml_driver *)conn->privateData; virDomainDefPtr def; virDomainObjPtr vm; virDomainPtr dom; if (!(def = virDomainDefParseString(conn, driver->caps, xml))) return NULL; if (!(vm = virDomainAssignDef(conn, &driver->domains, def))) { virDomainDefFree(def); return NULL; } vm->persistent = 1; if (virDomainSaveConfig(conn, driver->configDir, vm->newDef ? vm->newDef : vm->def) < 0) { virDomainRemoveInactive(&driver->domains, vm); return NULL; } dom = virGetDomain(conn, vm->def->name, vm->def->uuid); if (dom) dom->id = vm->def->id; return dom; } static int umlDomainUndefine(virDomainPtr dom) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, "%s", _("no domain with matching uuid")); return -1; } if (virDomainIsActive(vm)) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot delete active domain")); return -1; } if (!vm->persistent) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot undefine transient domain")); return -1; } if (virDomainDeleteConfig(dom->conn, driver->configDir, driver->autostartDir, vm) < 0) return -1; virDomainRemoveInactive(&driver->domains, vm); return 0; } static int umlDomainGetAutostart(virDomainPtr dom, int *autostart) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); if (!vm) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, "%s", _("no domain with matching uuid")); return -1; } *autostart = vm->autostart; return 0; } static int umlDomainSetAutostart(virDomainPtr dom, int autostart) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); char *configFile = NULL, *autostartLink = NULL; int ret = -1; if (!vm) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, "%s", _("no domain with matching uuid")); return -1; } if (!vm->persistent) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("cannot set autostart for transient domain")); return -1; } autostart = (autostart != 0); if (vm->autostart == autostart) return 0; if ((configFile = virDomainConfigFile(dom->conn, driver->configDir, vm->def->name)) == NULL) goto cleanup; if ((autostartLink = virDomainConfigFile(dom->conn, driver->autostartDir, vm->def->name)) == NULL) goto cleanup; if (autostart) { int err; if ((err = virFileMakePath(driver->autostartDir))) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, _("cannot create autostart directory %s: %s"), driver->autostartDir, strerror(err)); goto cleanup; } if (symlink(configFile, autostartLink) < 0) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, _("Failed to create symlink '%s to '%s': %s"), autostartLink, configFile, strerror(errno)); goto cleanup; } } else { if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, _("Failed to delete symlink '%s': %s"), autostartLink, strerror(errno)); goto cleanup; } } vm->autostart = autostart; ret = 0; cleanup: VIR_FREE(configFile); VIR_FREE(autostartLink); return ret; } static int umlDomainBlockPeek (virDomainPtr dom, const char *path, unsigned long long offset, size_t size, void *buffer, unsigned int flags ATTRIBUTE_UNUSED) { struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData; virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid); int fd, ret = -1, i; if (!vm) { umlReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, _("no domain with matching uuid")); return -1; } if (!path || path[0] == '\0') { umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, _("NULL or empty path")); return -1; } /* Check the path belongs to this domain. */ for (i = 0 ; i < vm->def->ndisks ; i++) { if (vm->def->disks[i]->src != NULL && STREQ (vm->def->disks[i]->src, path)) goto found; } umlReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, _("invalid path")); return -1; found: /* The path is correct, now try to open it and get its size. */ fd = open (path, O_RDONLY); if (fd == -1) { umlReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR, "%s", strerror (errno)); goto done; } /* Seek and read. */ /* NB. Because we configure with AC_SYS_LARGEFILE, off_t should * be 64 bits on all platforms. */ if (lseek (fd, offset, SEEK_SET) == (off_t) -1 || saferead (fd, buffer, size) == (ssize_t) -1) { umlReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR, "%s", strerror (errno)); goto done; } ret = 0; done: if (fd >= 0) close (fd); return ret; } static virDriver umlDriver = { VIR_DRV_UML, "UML", LIBVIR_VERSION_NUMBER, umlOpen, /* open */ umlClose, /* close */ NULL, /* supports_feature */ umlGetType, /* type */ umlGetVersion, /* version */ umlGetHostname, /* hostname */ NULL, /* URI */ NULL, /* getMaxVcpus */ umlGetNodeInfo, /* nodeGetInfo */ umlGetCapabilities, /* getCapabilities */ umlListDomains, /* listDomains */ umlNumDomains, /* numOfDomains */ umlDomainCreate, /* domainCreateXML */ umlDomainLookupByID, /* domainLookupByID */ umlDomainLookupByUUID, /* domainLookupByUUID */ umlDomainLookupByName, /* domainLookupByName */ NULL, /* domainSuspend */ NULL, /* domainResume */ umlDomainShutdown, /* domainShutdown */ NULL, /* domainReboot */ umlDomainDestroy, /* domainDestroy */ umlDomainGetOSType, /* domainGetOSType */ umlDomainGetMaxMemory, /* domainGetMaxMemory */ umlDomainSetMaxMemory, /* domainSetMaxMemory */ umlDomainSetMemory, /* domainSetMemory */ umlDomainGetInfo, /* domainGetInfo */ NULL, /* domainSave */ NULL, /* domainRestore */ NULL, /* domainCoreDump */ NULL, /* domainSetVcpus */ NULL, /* domainPinVcpu */ NULL, /* domainGetVcpus */ NULL, /* domainGetMaxVcpus */ umlDomainDumpXML, /* domainDumpXML */ umlListDefinedDomains, /* listDomains */ umlNumDefinedDomains, /* numOfDomains */ umlDomainStart, /* domainCreate */ umlDomainDefine, /* domainDefineXML */ umlDomainUndefine, /* domainUndefine */ NULL, /* domainAttachDevice */ NULL, /* domainDetachDevice */ umlDomainGetAutostart, /* domainGetAutostart */ umlDomainSetAutostart, /* domainSetAutostart */ NULL, /* domainGetSchedulerType */ NULL, /* domainGetSchedulerParameters */ NULL, /* domainSetSchedulerParameters */ NULL, /* domainMigratePrepare */ NULL, /* domainMigratePerform */ NULL, /* domainMigrateFinish */ NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ umlDomainBlockPeek, /* domainBlockPeek */ NULL, /* domainMemoryPeek */ #if HAVE_NUMACTL umlNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ umlNodeGetFreeMemory, /* getFreeMemory */ #else NULL, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ #endif NULL, /* domainEventRegister */ NULL, /* domainEventUnregister */ NULL, /* domainMigratePrepare2 */ NULL, /* domainMigrateFinish2 */ }; static virStateDriver umlStateDriver = { .initialize = umlStartup, .cleanup = umlShutdown, .reload = umlReload, .active = umlActive, }; int umlRegister(void) { virRegisterDriver(¨Driver); virRegisterStateDriver(¨StateDriver); return 0; }