From 9ae41a71ac457994b7ca975e9eec7c3fc13ac101 Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Thu, 10 Apr 2008 07:30:52 +0000 Subject: [PATCH] * src/Makefile.am src/lxc_conf.c src/lxc_conf.h src/lxc_container.c src/lxc_container.h src/lxc_driver.c: Applied second set of linux container patches from Dave Leskovec adding start/stop of domains Daniel --- ChangeLog | 7 + src/Makefile.am | 1 + src/lxc_conf.c | 1 + src/lxc_conf.h | 2 + src/lxc_container.c | 229 ++++++++++++++++++ src/lxc_container.h | 44 ++++ src/lxc_driver.c | 551 +++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 831 insertions(+), 4 deletions(-) create mode 100644 src/lxc_container.c create mode 100644 src/lxc_container.h diff --git a/ChangeLog b/ChangeLog index 2c94b1fed3..67e7f3ea22 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Thu Apr 10 09:28:31 CEST 2008 Daniel Veillard + + * src/Makefile.am src/lxc_conf.c src/lxc_conf.h src/lxc_container.c + src/lxc_container.h src/lxc_driver.c: Applied second set of + linux container patches from Dave Leskovec adding start/stop + of domains + Wed Apr 9 16:08:00 BST 2008 Richard W.M. Jones Add virsh.pod to EXTRA_DIST diff --git a/src/Makefile.am b/src/Makefile.am index 9bcc936c07..35b0cece1b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -61,6 +61,7 @@ CLIENT_SOURCES = \ openvz_driver.c openvz_driver.h \ lxc_driver.c lxc_driver.h \ lxc_conf.c lxc_conf.h \ + lxc_container.c lxc_container.h \ nodeinfo.h nodeinfo.c \ storage_conf.h storage_conf.c \ storage_driver.h storage_driver.c \ diff --git a/src/lxc_conf.c b/src/lxc_conf.c index 8c6f5096ee..325a1bf090 100644 --- a/src/lxc_conf.c +++ b/src/lxc_conf.c @@ -819,6 +819,7 @@ void lxcFreeVMs(lxc_vm_t *vms) void lxcFreeVM(lxc_vm_t *vm) { lxcFreeVMDef(vm->def); + free(vm->containerTty); free(vm); } diff --git a/src/lxc_conf.h b/src/lxc_conf.h index a0f1737b84..92dec390bb 100644 --- a/src/lxc_conf.h +++ b/src/lxc_conf.h @@ -72,6 +72,8 @@ struct __lxc_vm { char configFileBase[PATH_MAX]; int parentTty; + int containerTtyFd; + char *containerTty; lxc_vm_def_t *def; diff --git a/src/lxc_container.c b/src/lxc_container.c new file mode 100644 index 0000000000..d02c23b827 --- /dev/null +++ b/src/lxc_container.c @@ -0,0 +1,229 @@ +/* + * Copyright IBM Corp. 2008 + * + * lxc_container.c: file description + * + * Authors: + * David L. Leskovec + * + * 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 + */ + +#include + +#ifdef WITH_LXC + +#include +#include +#include +#include +#include +#include + +#include "lxc_container.h" +#include "lxc_conf.h" +#include "util.h" + +#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__) +#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg) + +/** + * lxcExecContainerInit: + * @vmDef: Ptr to vm definition structure + * + * Exec the container init string. The container init will replace then + * be running in the current process + * + * Returns 0 on success or -1 in case of error + */ +static int lxcExecContainerInit(const lxc_vm_def_t *vmDef) +{ + int rc = -1; + char* execString; + size_t execStringLen = strlen(vmDef->init) + 1 + 5; + + if (NULL == (execString = calloc(execStringLen, sizeof(char)))) { + lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, + _("failed to calloc memory for init string: %s"), + strerror(errno)); + goto error_out; + } + + strcpy(execString, "exec "); + strcat(execString, vmDef->init); + + execl("/bin/sh", "sh", "-c", execString, (char*)NULL); + lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, + _("execl failed to exec init: %s"), strerror(errno)); + +error_out: + exit(rc); +} + +/** + * lxcSetContainerStdio: + * @ttyName: Name of tty to set as the container console + * + * Sets the given tty as the primary conosole for the container as well as + * stdout, stdin and stderr. + * + * Returns 0 on success or -1 in case of error + */ +static int lxcSetContainerStdio(const char *ttyName) +{ + int rc = -1; + int ttyfd; + + if (setsid() < 0) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("setsid failed: %s"), strerror(errno)); + goto error_out; + } + + ttyfd = open(ttyName, O_RDWR|O_NOCTTY); + if (ttyfd < 0) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("open(%s) failed: %s"), ttyName, strerror(errno)); + goto error_out; + } + + if (ioctl(ttyfd, TIOCSCTTY, NULL) < 0) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("ioctl(TIOCSTTY) failed: %s"), strerror(errno)); + goto cleanup; + } + + close(0); close(1); close(2); + + if (dup2(ttyfd, 0) < 0) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("dup2(stdin) failed: %s"), strerror(errno)); + goto cleanup; + } + + if (dup2(ttyfd, 1) < 0) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("dup2(stdout) failed: %s"), strerror(errno)); + goto cleanup; + } + + if (dup2(ttyfd, 2) < 0) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("dup2(stderr) failed: %s"), strerror(errno)); + goto cleanup; + } + + rc = 0; + +cleanup: + close(ttyfd); + +error_out: + return rc; +} + +/** + * lxcExecWithTty: + * @vm: Ptr to vm structure + * + * Sets container console and stdio and then execs container init + * + * Returns 0 on success or -1 in case of error + */ +static int lxcExecWithTty(lxc_vm_t *vm) +{ + int rc = -1; + lxc_vm_def_t *vmDef = vm->def; + + if(lxcSetContainerStdio(vm->containerTty) < 0) { + goto exit_with_error; + } + + lxcExecContainerInit(vmDef); + +exit_with_error: + exit(rc); +} + +/** + * lxcChild: + * @argv: Pointer to container arguments + * + * This function is run in the process clone()'d in lxcStartContainer. + * Perform a number of container setup tasks: + * Setup container file system + * mount container /proca + * Then exec's the container init + * + * Returns 0 on success or -1 in case of error + */ +int lxcChild( void *argv ) +{ + int rc = -1; + lxc_vm_t *vm = (lxc_vm_t *)argv; + lxc_vm_def_t *vmDef = vm->def; + lxc_mount_t *curMount; + int i; + + if (NULL == vmDef) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("lxcChild() passed invalid vm definition")); + goto cleanup; + } + + /* handle the bind mounts first before doing anything else that may */ + /* then access those mounted dirs */ + curMount = vmDef->mounts; + for (i = 0; curMount; curMount = curMount->next) { + rc = mount(curMount->source, + curMount->target, + NULL, + MS_BIND, + NULL); + if (0 != rc) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to mount %s at %s for container: %s"), + curMount->source, curMount->target, strerror(errno)); + goto cleanup; + } + } + + /* mount /proc */ + rc = mount("lxcproc", "/proc", "proc", 0, NULL); + if (0 != rc) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to mount /proc for container: %s"), + strerror(errno)); + goto cleanup; + } + + rc = lxcExecWithTty(vm); + /* this function will only return if an error occured */ + +cleanup: + return rc; +} + +#endif /* WITH_LXC */ + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ + diff --git a/src/lxc_container.h b/src/lxc_container.h new file mode 100644 index 0000000000..d8deecfdad --- /dev/null +++ b/src/lxc_container.h @@ -0,0 +1,44 @@ +/* + * Copyright IBM Corp. 2008 + * + * lxc_container.h: header file for fcns run inside container + * + * Authors: + * David L. Leskovec + * + * 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 + */ + +#ifndef LXC_CONTAINER_H +#define LXC_CONTAINER_H + +#ifdef WITH_LXC + +/* Function declarations */ +int lxcChild( void *argv ); + +#endif /* LXC_DRIVER_H */ + +#endif /* LXC_CONTAINER_H */ + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ + diff --git a/src/lxc_driver.c b/src/lxc_driver.c index a4e77859fa..db597e1b5a 100644 --- a/src/lxc_driver.c +++ b/src/lxc_driver.c @@ -25,17 +25,22 @@ #ifdef WITH_LXC +#include +#include #include #include #include #include +#include #include #include #include "lxc_conf.h" +#include "lxc_container.h" #include "lxc_driver.h" #include "driver.h" #include "internal.h" +#include "util.h" /* debug macros */ #define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__) @@ -375,6 +380,544 @@ static char *lxcDomainDumpXML(virDomainPtr dom, return lxcGenerateXML(dom->conn, driver, vm, vm->def); } +/** + * lxcStartContainer: + * @conn: pointer to connection + * @driver: pointer to driver structure + * @vm: pointer to virtual machine structure + * + * Starts a container process by calling clone() with the namespace flags + * + * Returns 0 on success or -1 in case of error + */ +static int lxcStartContainer(virConnectPtr conn, + lxc_driver_t* driver, + lxc_vm_t *vm) +{ + int rc = -1; + int flags; + int stacksize = getpagesize() * 4; + void *stack, *stacktop; + + /* allocate a stack for the container */ + stack = malloc(stacksize); + if (!stack) { + lxcError(conn, NULL, VIR_ERR_NO_MEMORY, + _("unable to allocate container stack")); + goto error_exit; + } + stacktop = (char*)stack + stacksize; + + flags = CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWUSER|CLONE_NEWIPC|SIGCHLD; + + vm->def->id = clone(lxcChild, stacktop, flags, (void *)vm); + + DEBUG("clone() returned, %d", vm->def->id); + + if (vm->def->id < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("clone() failed, %s"), strerror(errno)); + goto error_exit; + } + + lxcSaveConfig(NULL, driver, vm, vm->def); + + rc = 0; + +error_exit: + return rc; +} + +/** + * lxcPutTtyInRawMode: + * @conn: pointer to connection + * @ttyDev: file descriptor for tty + * + * Sets tty attributes via cfmakeraw() + * + * Returns 0 on success or -1 in case of error + */ +static int lxcPutTtyInRawMode(virConnectPtr conn, int ttyDev) +{ + int rc = -1; + + struct termios ttyAttr; + + if (tcgetattr(ttyDev, &ttyAttr) < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + "tcgetattr() failed: %s", strerror(errno)); + goto cleanup; + } + + cfmakeraw(&ttyAttr); + + if (tcsetattr(ttyDev, TCSADRAIN, &ttyAttr) < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + "tcsetattr failed: %s", strerror(errno)); + goto cleanup; + } + + rc = 0; + +cleanup: + return rc; +} + +/** + * lxcSetupTtyTunnel: + * @conn: pointer to connection + * @vmDef: pointer to virtual machine definition structure + * @ttyDev: pointer to int. On success will be set to fd for master + * end of tty + * + * Opens and configures the parent side tty + * + * Returns 0 on success or -1 in case of error + */ +static int lxcSetupTtyTunnel(virConnectPtr conn, + lxc_vm_def_t *vmDef, + int* ttyDev) +{ + int rc = -1; + char *ptsStr; + + if (0 < strlen(vmDef->tty)) { + *ttyDev = open(vmDef->tty, O_RDWR|O_NOCTTY|O_NONBLOCK); + if (*ttyDev < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + "open() tty failed: %s", strerror(errno)); + goto setup_complete; + } + + rc = grantpt(*ttyDev); + if (rc < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + "grantpt() failed: %s", strerror(errno)); + goto setup_complete; + } + + rc = unlockpt(*ttyDev); + if (rc < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + "unlockpt() failed: %s", strerror(errno)); + goto setup_complete; + } + + /* get the name and print it to stdout */ + ptsStr = ptsname(*ttyDev); + if (ptsStr == NULL) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + "ptsname() failed"); + goto setup_complete; + } + /* This value needs to be stored in the container configuration file */ + if (STRNEQ(ptsStr, vmDef->tty)) { + strcpy(vmDef->tty, ptsStr); + } + + /* Enter raw mode, so all characters are passed directly to child */ + if (lxcPutTtyInRawMode(conn, *ttyDev) < 0) { + goto setup_complete; + } + + } else { + *ttyDev = -1; + } + + rc = 0; + +setup_complete: + if((0 != rc) && (*ttyDev > 0)) { + close(*ttyDev); + } + + return rc; +} + +/** + * lxcSetupContainerTty: + * @conn: pointer to connection + * @ttymaster: pointer to int. On success, set to fd for master end + * @ttyName: On success, will point to string slave end of tty. Caller + * must free when done (such as in lxcFreeVM). + * + * Opens and configures container tty. + * + * Returns 0 on success or -1 in case of error + */ +static int lxcSetupContainerTty(virConnectPtr conn, + int *ttymaster, + char **ttyName) +{ + int rc = -1; + char tempTtyName[PATH_MAX]; + + *ttymaster = posix_openpt(O_RDWR|O_NOCTTY); + if (*ttymaster < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("posix_openpt failed: %s"), strerror(errno)); + goto cleanup; + } + + if (unlockpt(*ttymaster) < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("unlockpt failed: %s"), strerror(errno)); + goto cleanup; + } + + if (0 != ptsname_r(*ttymaster, tempTtyName, sizeof(tempTtyName))) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("ptsname_r failed: %s"), strerror(errno)); + goto cleanup; + } + + *ttyName = malloc(sizeof(char) * (strlen(tempTtyName) + 1)); + if (NULL == ttyName) { + lxcError(conn, NULL, VIR_ERR_NO_MEMORY, + _("unable to allocate container name string")); + goto cleanup; + } + + strcpy(*ttyName, tempTtyName); + + rc = 0; + +cleanup: + if (0 != rc) { + if (-1 != *ttymaster) { + close(*ttymaster); + } + } + + return rc; +} + +/** + * lxcTtyForward: + * @fd1: Open fd + * @fd1: Open fd + * + * Forwards traffic between fds. Data read from fd1 will be written to fd2 + * Data read from fd2 will be written to fd1. This process loops forever. + * + * Returns 0 on success or -1 in case of error + */ +static int lxcTtyForward(int fd1, int fd2) +{ + int rc = -1; + int i; + char buf[2]; + struct pollfd fds[2]; + int numFds = 0; + + if (0 <= fd1) { + fds[numFds].fd = fd1; + fds[numFds].events = POLLIN; + ++numFds; + } + + if (0 <= fd2) { + fds[numFds].fd = fd2; + fds[numFds].events = POLLIN; + ++numFds; + } + + if (0 == numFds) { + DEBUG0("No fds to monitor, return"); + goto cleanup; + } + + while (1) { + if ((rc = poll(fds, numFds, -1)) <= 0) { + + if ((0 == rc) || (errno == EINTR) || (errno == EAGAIN)) { + continue; + } + + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("poll returned error: %s"), strerror(errno)); + goto cleanup; + } + + for (i = 0; i < numFds; ++i) { + if (!fds[i].revents) { + continue; + } + + if (fds[i].revents & POLLIN) { + if (1 != (saferead(fds[i].fd, buf, 1))) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("read of fd %d failed: %s"), i, + strerror(errno)); + goto cleanup; + } + + if (1 < numFds) { + if (1 != (safewrite(fds[i ^ 1].fd, buf, 1))) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("write to fd %d failed: %s"), i, + strerror(errno)); + goto cleanup; + } + + } + + } + + } + + } + + rc = 0; + +cleanup: + return rc; +} + +/** + * lxcVmStart: + * @conn: pointer to connection + * @driver: pointer to driver structure + * @vm: pointer to virtual machine structure + * + * Starts a vm + * + * Returns 0 on success or -1 in case of error + */ +static int lxcVmStart(virConnectPtr conn, + lxc_driver_t * driver, + lxc_vm_t * vm) +{ + int rc = -1; + lxc_vm_def_t *vmDef = vm->def; + + /* open parent tty */ + if (lxcSetupTtyTunnel(conn, vmDef, &vm->parentTty) < 0) { + goto cleanup; + } + + /* open container tty */ + if (lxcSetupContainerTty(conn, &(vm->containerTtyFd), &(vm->containerTty)) < 0) { + goto cleanup; + } + + /* fork process to handle the tty io forwarding */ + if ((vm->pid = fork()) < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("unable to fork tty forwarding process: %s"), + strerror(errno)); + goto cleanup; + } + + if (vm->pid == 0) { + /* child process calls forward routine */ + lxcTtyForward(vm->parentTty, vm->containerTtyFd); + } + + close(vm->parentTty); + close(vm->containerTtyFd); + + rc = lxcStartContainer(conn, driver, vm); + + if (rc == 0) { + vm->state = VIR_DOMAIN_RUNNING; + driver->ninactivevms--; + driver->nactivevms++; + } + +cleanup: + return rc; +} + +/** + * lxcDomainStart: + * @dom: domain to start + * + * Looks up domain and starts it. + * + * Returns 0 on success or -1 in case of error + */ +static int lxcDomainStart(virDomainPtr dom) +{ + int rc = -1; + virConnectPtr conn = dom->conn; + lxc_driver_t *driver = (lxc_driver_t *)(conn->privateData); + lxc_vm_t *vm = lxcFindVMByName(driver, dom->name); + + if (!vm) { + lxcError(conn, dom, VIR_ERR_INVALID_DOMAIN, + "no domain with uuid"); + goto cleanup; + } + + rc = lxcVmStart(conn, driver, vm); + +cleanup: + return rc; +} + +/** + * lxcDomainCreateAndStart: + * @conn: pointer to connection + * @xml: XML definition of domain + * @flags: Unused + * + * Creates a domain based on xml and starts it + * + * Returns 0 on success or -1 in case of error + */ +static virDomainPtr +lxcDomainCreateAndStart(virConnectPtr conn, + const char *xml, + unsigned int flags ATTRIBUTE_UNUSED) { + lxc_driver_t *driver = (lxc_driver_t *)conn->privateData; + lxc_vm_t *vm; + lxc_vm_def_t *def; + virDomainPtr dom = NULL; + + if (!(def = lxcParseVMDef(conn, xml, NULL))) { + goto return_point; + } + + if (!(vm = lxcAssignVMDef(conn, driver, def))) { + lxcFreeVMDef(def); + goto return_point; + } + + if (lxcSaveVMDef(conn, driver, vm, def) < 0) { + lxcRemoveInactiveVM(driver, vm); + return NULL; + } + + if (lxcVmStart(conn, driver, vm) < 0) { + lxcRemoveInactiveVM(driver, vm); + goto return_point; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) { + dom->id = vm->def->id; + } + +return_point: + return dom; +} + +/** + * lxcDomainShutdown: + * @dom: Ptr to domain to shutdown + * + * Sends SIGINT to container root process to request it to shutdown + * + * Returns 0 on success or -1 in case of error + */ +static int lxcDomainShutdown(virDomainPtr dom) +{ + int rc = -1; + lxc_driver_t *driver = (lxc_driver_t*)dom->conn->privateData; + lxc_vm_t *vm = lxcFindVMByID(driver, dom->id); + + if (!vm) { + lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN, + _("no domain with id %d"), dom->id); + goto error_out; + } + + if (0 > (kill(vm->def->id, SIGINT))) { + if (ESRCH != errno) { + lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR, + _("sending SIGTERM failed: %s"), strerror(errno)); + + goto error_out; + } + } + + vm->state = VIR_DOMAIN_SHUTDOWN; + + rc = 0; + +error_out: + return rc; +} + +/** + * lxcDomainDestroy: + * @dom: Ptr to domain to destroy + * + * Sends SIGKILL to container root process to terminate the container + * + * Returns 0 on success or -1 in case of error + */ +static int lxcDomainDestroy(virDomainPtr dom) +{ + int rc = -1; + int waitRc; + lxc_driver_t *driver = (lxc_driver_t*)dom->conn->privateData; + lxc_vm_t *vm = lxcFindVMByID(driver, dom->id); + int childStatus; + + if (!vm) { + lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN, + _("no domain with id %d"), dom->id); + goto error_out; + } + + if (0 > (kill(vm->def->id, SIGKILL))) { + if (ESRCH != errno) { + lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR, + _("sending SIGKILL failed: %s"), strerror(errno)); + + goto error_out; + } + } + + vm->state = VIR_DOMAIN_SHUTDOWN; + + while (((waitRc = waitpid(vm->def->id, &childStatus, 0)) == -1) && + errno == EINTR); + + if (waitRc != vm->def->id) { + lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR, + _("waitpid failed to wait for container %d: %d %s"), + vm->def->id, waitRc, strerror(errno)); + goto kill_tty; + } + + rc = WEXITSTATUS(childStatus); + DEBUG("container exited with rc: %d", rc); + +kill_tty: + if (0 > (kill(vm->pid, SIGKILL))) { + if (ESRCH != errno) { + lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR, + _("sending SIGKILL to tty process failed: %s"), + strerror(errno)); + + goto tty_error_out; + } + } + + while (((waitRc = waitpid(vm->pid, &childStatus, 0)) == -1) && + errno == EINTR); + + if (waitRc != vm->pid) { + lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR, + _("waitpid failed to wait for tty %d: %d %s"), + vm->pid, waitRc, strerror(errno)); + } + +tty_error_out: + vm->state = VIR_DOMAIN_SHUTOFF; + vm->pid = -1; + vm->def->id = -1; + driver->nactivevms--; + driver->ninactivevms++; + + rc = 0; + +error_out: + return rc; +} static int lxcStartup(void) { @@ -469,15 +1012,15 @@ static virDriver lxcDriver = { NULL, /* getCapabilities */ lxcListDomains, /* listDomains */ lxcNumDomains, /* numOfDomains */ - NULL/*lxcDomainCreateLinux*/, /* domainCreateLinux */ + lxcDomainCreateAndStart, /* domainCreateLinux */ lxcDomainLookupByID, /* domainLookupByID */ lxcDomainLookupByUUID, /* domainLookupByUUID */ lxcDomainLookupByName, /* domainLookupByName */ NULL, /* domainSuspend */ NULL, /* domainResume */ - NULL, /* domainShutdown */ + lxcDomainShutdown, /* domainShutdown */ NULL, /* domainReboot */ - NULL, /* domainDestroy */ + lxcDomainDestroy, /* domainDestroy */ lxcGetOSType, /* domainGetOSType */ NULL, /* domainGetMaxMemory */ NULL, /* domainSetMaxMemory */ @@ -493,7 +1036,7 @@ static virDriver lxcDriver = { lxcDomainDumpXML, /* domainDumpXML */ lxcListDefinedDomains, /* listDefinedDomains */ lxcNumDefinedDomains, /* numOfDefinedDomains */ - NULL, /* domainCreate */ + lxcDomainStart, /* domainCreate */ lxcDomainDefine, /* domainDefineXML */ lxcDomainUndefine, /* domainUndefine */ NULL, /* domainAttachDevice */