From 763dacda97ab2f4890a6fc43dd0f052183ca072b Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 13 Aug 2008 10:52:15 +0000 Subject: [PATCH] Make LXC I/O controller process a parent of the container process --- ChangeLog | 21 ++ configure.in | 2 +- src/lxc_conf.c | 195 +------------- src/lxc_conf.h | 12 +- src/lxc_container.c | 35 +-- src/lxc_container.h | 8 +- src/lxc_controller.c | 335 +++++++++++++++++++++-- src/lxc_controller.h | 10 +- src/lxc_driver.c | 613 ++++++++++++++++++++----------------------- src/util.c | 166 ++++++++++++ src/util.h | 12 + 11 files changed, 854 insertions(+), 555 deletions(-) diff --git a/ChangeLog b/ChangeLog index 88f206bf8a..4b2883daf7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +Wed Aug 13 11:48:36 BST 2008 Daniel Berrange + + * configure.in: Add check for termios.h + * src/util.h, src/util.c: Add virFileOpenTty and helpers + for creating/deleting/reading PID files + * src/lxc_conf.h, src/lxc_conf.c, src/lxc_container.c, + src/lxc_container.h, src/lxc_controller.c, + src/lxc_controller.h, src/lxc_driver.c: Re-arrange + container launch process so that the I/O helper is + a direct parent of the container process. Daemonize + container so it survives restarts of libvirtd. + +Wed Aug 13 11:23:36 BST 2008 Daniel Berrange + + Re-arrange code between LXC driver files + * src/lxc_container.c, src/lxc_container.h, + src/lxc_controller.h, src/lxc_container.c, + src/lxc_driver.c: Move code for I/O handling into + a seprate lxc_controller module, and move code for + creating containers into lcx_container module. + Wed Aug 13 10:55:36 BST 2008 Daniel Berrange * src/lxc_conf.h, src/lxc_conf.c, src/lxc_container.h, diff --git a/configure.in b/configure.in index 14dd776562..0513a72afa 100644 --- a/configure.in +++ b/configure.in @@ -68,7 +68,7 @@ dnl Availability of various common functions (non-fatal if missing). AC_CHECK_FUNCS([cfmakeraw regexec uname sched_getaffinity]) dnl Availability of various common headers (non-fatal if missing). -AC_CHECK_HEADERS([pwd.h paths.h sys/syslimits.h sys/utsname.h sys/wait.h winsock2.h sched.h]) +AC_CHECK_HEADERS([pwd.h paths.h sys/syslimits.h sys/utsname.h sys/wait.h winsock2.h sched.h termios.h]) dnl Where are the XDR functions? dnl If portablexdr is installed, prefer that. diff --git a/src/lxc_conf.c b/src/lxc_conf.c index 226df1889f..875651eaed 100644 --- a/src/lxc_conf.c +++ b/src/lxc_conf.c @@ -833,25 +833,24 @@ static lxc_vm_t * lxcLoadConfig(lxc_driver_t *driver, strncpy(vm->configFileBase, file, PATH_MAX); vm->configFile[PATH_MAX-1] = '\0'; - if (lxcLoadTtyPid(driver, vm) < 0) { - DEBUG0("failed to load tty pid"); - } - return vm; } int lxcLoadDriverConfig(lxc_driver_t *driver) { /* Set the container configuration directory */ - driver->configDir = strdup(SYSCONF_DIR "/libvirt/lxc"); - if (NULL == driver->configDir) { - lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, "configDir"); - return -1; - } - - driver->stateDir = strdup(LOCAL_STATE_DIR "/run/libvirt/lxc"); + if ((driver->configDir = strdup(SYSCONF_DIR "/libvirt/lxc")) == NULL) + goto no_memory; + if ((driver->stateDir = strdup(LOCAL_STATE_DIR "/run/libvirt/lxc")) == NULL) + goto no_memory; + if ((driver->logDir = strdup(LOCAL_STATE_DIR "/log/libvirt/lxc")) == NULL) + goto no_memory; return 0; + +no_memory: + lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, "configDir"); + return -1; } int lxcLoadContainerConfigFile(lxc_driver_t *driver, @@ -1012,9 +1011,7 @@ void lxcFreeVMDef(lxc_vm_def_t *vmdef) curNet = vmdef->nets; while (curNet) { nextNet = curNet->next; - printf("Freeing %s:%s\n", curNet->parentVeth, curNet->containerVeth); VIR_FREE(curNet->parentVeth); - VIR_FREE(curNet->containerVeth); VIR_FREE(curNet->txName); VIR_FREE(curNet); curNet = nextNet; @@ -1106,176 +1103,4 @@ int lxcDeleteConfig(virConnectPtr conn, return 0; } -/** - * lxcStoreTtyPid: - * @driver: pointer to driver - * @vm: Ptr to VM - * - * Stores the pid of the tty forward process contained in vm->pid - * LOCAL_STATE_DIR/run/libvirt/lxc/{container_name}.pid - * - * Returns 0 on success or -1 in case of error - */ -int lxcStoreTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm) -{ - int rc = -1; - int fd; - FILE *file = NULL; - - if (vm->ttyPidFile[0] == 0x00) { - if ((rc = virFileMakePath(driver->stateDir))) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create lxc state directory %s: %s"), - driver->stateDir, strerror(rc)); - goto error_out; - } - - if (virFileBuildPath(driver->stateDir, vm->def->name, ".pid", - vm->ttyPidFile, PATH_MAX) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot construct tty pid file path")); - goto error_out; - } - } - - if ((fd = open(vm->ttyPidFile, - O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR)) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create tty pid file %s: %s"), - vm->ttyPidFile, strerror(errno)); - goto error_out; - } - - if (!(file = fdopen(fd, "w"))) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot fdopen tty pid file %s: %s"), - vm->ttyPidFile, strerror(errno)); - - if (close(fd) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to close tty pid file %s: %s"), - vm->ttyPidFile, strerror(errno)); - } - - goto error_out; - } - - if (fprintf(file, "%d", vm->pid) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot write tty pid file %s: %s"), - vm->ttyPidFile, strerror(errno)); - - goto fclose_error_out; - } - - rc = 0; - -fclose_error_out: - if (fclose(file) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to close tty pid file %s: %s"), - vm->ttyPidFile, strerror(errno)); - } - -error_out: - return rc; -} - -/** - * lxcLoadTtyPid: - * @driver: pointer to driver - * @vm: Ptr to VM - * - * Loads the pid of the tty forward process from the pid file. - * LOCAL_STATE_DIR/run/libvirt/lxc/{container_name}.pid - * - * Returns - * > 0 - pid of tty process - * 0 - no tty pid file - * -1 - error - */ -int lxcLoadTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm) -{ - int rc = -1; - FILE *file; - - if (vm->ttyPidFile[0] == 0x00) { - if ((rc = virFileMakePath(driver->stateDir))) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot create lxc state directory %s: %s"), - driver->stateDir, strerror(rc)); - goto cleanup; - } - - if (virFileBuildPath(driver->stateDir, vm->def->name, ".pid", - vm->ttyPidFile, PATH_MAX) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot construct tty pid file path")); - goto cleanup; - } - } - - if (!(file = fopen(vm->ttyPidFile, "r"))) { - if (ENOENT == errno) { - rc = 0; - goto cleanup; - } - - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot open tty pid file %s: %s"), - vm->ttyPidFile, strerror(errno)); - goto cleanup; - } - - if (fscanf(file, "%d", &(vm->pid)) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot read tty pid file %s: %s"), - vm->ttyPidFile, strerror(errno)); - goto cleanup; - } - - if (fclose(file) < 0) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to close tty pid file %s: %s"), - vm->ttyPidFile, strerror(errno)); - goto cleanup; - } - - rc = vm->pid; - - cleanup: - return rc; -} - -/** - * lxcDeleteTtyPid: - * @vm: Ptr to VM - * - * Unlinks the tty pid file for the vm - * LOCAL_STATE_DIR/run/libvirt/lxc/{container_name}.pid - * - * Returns on 0 success or -1 in case of error - */ -int lxcDeleteTtyPidFile(const lxc_vm_t *vm) -{ - if (vm->ttyPidFile[0] == 0x00) { - goto no_file; - } - - if (unlink(vm->ttyPidFile) < 0) { - if (errno == ENOENT) { - goto no_file; - } - - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot remove ttyPidFile %s: %s"), vm->ttyPidFile, - strerror(errno)); - return -1; - } - -no_file: - return 0; -} - #endif /* WITH_LXC */ diff --git a/src/lxc_conf.h b/src/lxc_conf.h index be21560ed8..b3fe86b364 100644 --- a/src/lxc_conf.h +++ b/src/lxc_conf.h @@ -46,7 +46,6 @@ typedef struct __lxc_net_def lxc_net_def_t; struct __lxc_net_def { int type; char *parentVeth; /* veth device in parent namespace */ - char *containerVeth; /* veth device in container namespace */ char *txName; /* bridge or network name */ lxc_net_def_t *next; @@ -87,12 +86,11 @@ typedef struct __lxc_vm lxc_vm_t; struct __lxc_vm { int pid; int state; + int monitor; char configFile[PATH_MAX]; char configFileBase[PATH_MAX]; - char ttyPidFile[PATH_MAX]; - lxc_vm_def_t *def; lxc_vm_t *next; @@ -103,8 +101,9 @@ struct __lxc_driver { lxc_vm_t *vms; int nactivevms; int ninactivevms; - char* configDir; - char* stateDir; + char *configDir; + char *stateDir; + char *logDir; int have_netns; }; @@ -154,9 +153,6 @@ int lxcDeleteConfig(virConnectPtr conn, lxc_driver_t *driver, const char *configFile, const char *name); -int lxcStoreTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm); -int lxcLoadTtyPid(const lxc_driver_t *driver, lxc_vm_t *vm); -int lxcDeleteTtyPidFile(const lxc_vm_t *vm); void lxcError(virConnectPtr conn, virDomainPtr dom, diff --git a/src/lxc_container.c b/src/lxc_container.c index 510a113054..36ab654928 100644 --- a/src/lxc_container.c +++ b/src/lxc_container.c @@ -69,6 +69,8 @@ typedef char lxc_message_t; typedef struct __lxc_child_argv lxc_child_argv_t; struct __lxc_child_argv { lxc_vm_def_t *config; + unsigned int nveths; + char **veths; int monitor; char *ttyPath; }; @@ -171,8 +173,7 @@ error_out: * * Returns 0 on success or -1 in case of error */ -int lxcContainerSendContinue(virConnectPtr conn, - int control) +int lxcContainerSendContinue(int control) { int rc = -1; lxc_message_t msg = LXC_CONTINUE_MSG; @@ -180,7 +181,7 @@ int lxcContainerSendContinue(virConnectPtr conn, writeCount = safewrite(control, &msg, sizeof(msg)); if (writeCount != sizeof(msg)) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("unable to send container continue message: %s"), strerror(errno)); goto error_out; @@ -230,21 +231,22 @@ static int lxcContainerWaitForContinue(int control) * * Returns 0 on success or nonzero in case of error */ -static int lxcContainerEnableInterfaces(const lxc_vm_def_t *def) +static int lxcContainerEnableInterfaces(unsigned int nveths, + char **veths) { int rc = 0; - const lxc_net_def_t *net; + unsigned int i; - for (net = def->nets; net; net = net->next) { - DEBUG("Enabling %s", net->containerVeth); - rc = vethInterfaceUpOrDown(net->containerVeth, 1); + for (i = 0 ; i < nveths ; i++) { + DEBUG("Enabling %s", veths[i]); + rc = vethInterfaceUpOrDown(veths[i], 1); if (0 != rc) { goto error_out; } } /* enable lo device only if there were other net devices */ - if (def->nets) + if (veths) rc = vethInterfaceUpOrDown("lo", 1); error_out: @@ -311,7 +313,7 @@ static int lxcContainerChild( void *data ) return -1; /* enable interfaces */ - if (lxcContainerEnableInterfaces(vmDef) < 0) + if (lxcContainerEnableInterfaces(argv->nveths, argv->veths) < 0) return -1; /* this function will only return if an error occured */ @@ -320,7 +322,6 @@ static int lxcContainerChild( void *data ) /** * lxcContainerStart: - * @conn: pointer to connection * @driver: pointer to driver structure * @vm: pointer to virtual machine structure * @@ -328,8 +329,9 @@ static int lxcContainerChild( void *data ) * * Returns PID of container on success or -1 in case of error */ -int lxcContainerStart(virConnectPtr conn, - lxc_vm_def_t *def, +int lxcContainerStart(lxc_vm_def_t *def, + unsigned int nveths, + char **veths, int control, char *ttyPath) { @@ -337,12 +339,11 @@ int lxcContainerStart(virConnectPtr conn, int flags; int stacksize = getpagesize() * 4; char *stack, *stacktop; - lxc_child_argv_t args = { def, control, ttyPath }; + lxc_child_argv_t args = { def, nveths, veths, control, ttyPath }; /* allocate a stack for the container */ if (VIR_ALLOC_N(stack, stacksize) < 0) { - lxcError(conn, NULL, VIR_ERR_NO_MEMORY, - _("unable to allocate container stack")); + lxcError(NULL, NULL, VIR_ERR_NO_MEMORY, NULL); return -1; } stacktop = stack + stacksize; @@ -357,7 +358,7 @@ int lxcContainerStart(virConnectPtr conn, DEBUG("clone() returned, %d", pid); if (pid < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("clone() failed, %s"), strerror(errno)); return -1; } diff --git a/src/lxc_container.h b/src/lxc_container.h index 715753ec38..1b492a69ed 100644 --- a/src/lxc_container.h +++ b/src/lxc_container.h @@ -32,11 +32,11 @@ enum { LXC_CONTAINER_FEATURE_NET = (1 << 0), }; -int lxcContainerSendContinue(virConnectPtr conn, - int control); +int lxcContainerSendContinue(int control); -int lxcContainerStart(virConnectPtr conn, - lxc_vm_def_t *def, +int lxcContainerStart(lxc_vm_def_t *def, + unsigned int nveths, + char **veths, int control, char *ttyPath); diff --git a/src/lxc_controller.c b/src/lxc_controller.c index 09af605791..b2c07182a8 100644 --- a/src/lxc_controller.c +++ b/src/lxc_controller.c @@ -26,16 +26,27 @@ #ifdef WITH_LXC #include +#include +#include #include +#include +#include +#include #include "internal.h" #include "util.h" #include "lxc_conf.h" +#include "lxc_container.h" #include "lxc_controller.h" +#include "veth.h" +#include "memory.h" +#include "util.h" #define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__) +#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg) + /** * lxcFdForward: @@ -91,7 +102,10 @@ typedef struct _lxcTtyForwardFd_t { * * Returns 0 on success or -1 in case of error */ -int lxcControllerMain(int appPty, int contPty) +static int lxcControllerMain(int monitor, + int client, + int appPty, + int contPty) { int rc = -1; int epollFd; @@ -120,41 +134,77 @@ int lxcControllerMain(int appPty, int contPty) memset(&epollEvent, 0x00, sizeof(epollEvent)); epollEvent.events = EPOLLIN|EPOLLET; /* edge triggered */ epollEvent.data.fd = appPty; - epollEvent.data.u32 = 0; /* fdArray position */ if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, appPty, &epollEvent)) { lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("epoll_ctl(appPty) failed: %s"), strerror(errno)); goto cleanup; } epollEvent.data.fd = contPty; - epollEvent.data.u32 = 1; /* fdArray position */ if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, contPty, &epollEvent)) { lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("epoll_ctl(contPty) failed: %s"), strerror(errno)); goto cleanup; } + epollEvent.events = EPOLLIN; + epollEvent.data.fd = monitor; + if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, monitor, &epollEvent)) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + goto cleanup; + } + + epollEvent.events = EPOLLHUP; + epollEvent.data.fd = client; + if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &epollEvent)) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + goto cleanup; + } + while (1) { /* if active fd's, return if no events, else wait forever */ timeout = (numActive > 0) ? 0 : -1; numEvents = epoll_wait(epollFd, &epollEvent, 1, timeout); - if (0 < numEvents) { - if (epollEvent.events & EPOLLIN) { - curFdOff = epollEvent.data.u32; - if (!fdArray[curFdOff].active) { - fdArray[curFdOff].active = 1; - ++numActive; + if (numEvents > 0) { + if (epollEvent.data.fd == monitor) { + int fd = accept(monitor, NULL, 0); + if (client != -1) { /* Already connected, so kick new one out */ + close(fd); + continue; } - - } else if (epollEvent.events & EPOLLHUP) { - DEBUG("EPOLLHUP from fd %d", epollEvent.data.fd); - continue; + client = fd; + epollEvent.events = EPOLLHUP; + epollEvent.data.fd = client; + if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, client, &epollEvent)) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + goto cleanup; + } + } else if (client != -1 && epollEvent.data.fd == client) { + if (0 > epoll_ctl(epollFd, EPOLL_CTL_DEL, client, &epollEvent)) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("epoll_ctl(contPty) failed: %s"), strerror(errno)); + goto cleanup; + } + close(client); + client = -1; } else { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("error event %d"), epollEvent.events); - goto cleanup; + if (epollEvent.events & EPOLLIN) { + curFdOff = epollEvent.data.fd == appPty ? 0 : 1; + if (!fdArray[curFdOff].active) { + fdArray[curFdOff].active = 1; + ++numActive; + } + } else if (epollEvent.events & EPOLLHUP) { + DEBUG("EPOLLHUP from fd %d", epollEvent.data.fd); + continue; + } else { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("error event %d"), epollEvent.events); + goto cleanup; + } } - } else if (0 == numEvents) { if (2 == numActive) { /* both fds active, toggle between the two */ @@ -202,4 +252,255 @@ cleanup: return rc; } + + +/** + * lxcControllerMoveInterfaces + * @nveths: number of interfaces + * @veths: interface names + * @container: pid of container + * + * Moves network interfaces into a container's namespace + * + * Returns 0 on success or -1 in case of error + */ +static int lxcControllerMoveInterfaces(unsigned int nveths, + char **veths, + pid_t container) +{ + unsigned int i; + for (i = 0 ; i < nveths ; i++) + if (moveInterfaceToNetNs(veths[i], container) < 0) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to move interface %s to ns %d"), + veths[i], container); + return -1; + } + + return 0; +} + + +/** + * lxcCleanupInterfaces: + * @conn: pointer to connection + * @vm: pointer to virtual machine structure + * + * Cleans up the container interfaces by deleting the veth device pairs. + * + * Returns 0 on success or -1 in case of error + */ +static int lxcControllerCleanupInterfaces(unsigned int nveths, + char **veths) +{ + unsigned int i; + for (i = 0 ; i < nveths ; i++) + if (vethDelete(veths[i]) < 0) + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to delete veth: %s"), veths[i]); + /* will continue to try to cleanup any other interfaces */ + + return 0; +} + + +static int +lxcControllerRun(const char *stateDir, + lxc_vm_def_t *def, + unsigned int nveths, + char **veths, + int monitor, + int client, + int appPty) +{ + int rc = -1; + int control[2] = { -1, -1}; + int containerPty; + char *containerPtyPath; + pid_t container = -1; + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, control) < 0) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("sockpair failed: %s"), strerror(errno)); + goto cleanup; + } + + if (virFileOpenTty(&containerPty, + &containerPtyPath, + 0) < 0) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to allocate tty: %s"), strerror(errno)); + goto cleanup; + } + + if ((container = lxcContainerStart(def, + nveths, + veths, + control[1], + containerPtyPath)) < 0) + goto cleanup; + close(control[1]); + control[1] = -1; + + if (lxcControllerMoveInterfaces(nveths, veths, container) < 0) + goto cleanup; + + if (lxcContainerSendContinue(control[0]) < 0) + goto cleanup; + + rc = lxcControllerMain(monitor, client, appPty, containerPty); + +cleanup: + if (control[0] != -1) + close(control[0]); + if (control[1] != -1) + close(control[1]); + VIR_FREE(containerPtyPath); + if (containerPty != -1) + close(containerPty); + + kill(container, SIGTERM); + waitpid(container, NULL, 0); + lxcControllerCleanupInterfaces(nveths, veths); + virFileDeletePid(stateDir, def->name); + return rc; +} + + +int lxcControllerStart(const char *stateDir, + lxc_vm_def_t *def, + unsigned int nveths, + char **veths, + int monitor, + int appPty, + int logfd) +{ + pid_t pid; + int rc; + int status, null; + int open_max, i; + int client; + struct sigaction sig_action; + + if ((pid = fork()) < 0) + return -1; + + if (pid > 0) { + /* Original caller waits for first child to exit */ + while (1) { + rc = waitpid(pid, &status, 0); + if (rc < 0) { + if (errno == EINTR) + continue; + return -1; + } + if (rc != pid) { + fprintf(stderr, + _("Unexpected pid %d != %d from waitpid\n"), + rc, pid); + return -1; + } + if (WIFEXITED(status) && + WEXITSTATUS(status) == 0) + return 0; + else { + fprintf(stderr, + _("Unexpected status %d from pid %d\n"), + status, pid); + return -1; + } + } + } + + /* First child is running here */ + + /* Clobber all libvirtd's signal handlers so they + * don't affect us + */ + sig_action.sa_handler = SIG_DFL; + sig_action.sa_flags = 0; + sigemptyset(&sig_action.sa_mask); + + sigaction(SIGHUP, &sig_action, NULL); + sigaction(SIGINT, &sig_action, NULL); + sigaction(SIGQUIT, &sig_action, NULL); + sigaction(SIGTERM, &sig_action, NULL); + sigaction(SIGCHLD, &sig_action, NULL); + + sig_action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sig_action, NULL); + + + /* Don't hold onto any cwd we inherit from libvirtd either */ + if (chdir("/") < 0) { + fprintf(stderr, _("Unable to change to root dir: %s\n"), + strerror(errno)); + _exit(-1); + } + + if (setsid() < 0) { + fprintf(stderr, _("Unable to become session leader: %s\n"), + strerror(errno)); + _exit(-1); + } + + if ((null = open(_PATH_DEVNULL, O_RDONLY)) < 0) { + fprintf(stderr, _("Unable to open %s: %s\n"), + _PATH_DEVNULL, strerror(errno)); + _exit(-1); + } + + open_max = sysconf (_SC_OPEN_MAX); + for (i = 0; i < open_max; i++) + if (i != appPty && + i != monitor && + i != logfd && + i != null) + close(i); + + if (dup2(null, STDIN_FILENO) < 0 || + dup2(logfd, STDOUT_FILENO) < 0 || + dup2(logfd, STDERR_FILENO) < 0) { + fprintf(stderr, _("Unable to redirect stdio: %s\n"), + strerror(errno)); + _exit(-1); + } + + close(null); + close(logfd); + + /* Now fork the real controller process */ + if ((pid = fork()) < 0) { + fprintf(stderr, _("Unable to fork controller: %s\n"), + strerror(errno)); + _exit(-1); + } + + if (pid > 0) { + if ((rc = virFileWritePid(stateDir, def->name, pid)) != 0) { + fprintf(stderr, _("Unable to write pid file: %s\n"), + strerror(rc)); + _exit(-1); + } + /* First child now exits, allowing originall caller to + * complete their waitpid & continue */ + _exit(0); + } + + /* This is real controller running finally... */ + + /* Accept initial client which is the libvirtd daemon */ + if ((client = accept(monitor, NULL, 0))) { + fprintf(stderr, _("Failed connection from LXC driver: %s\n"), + strerror(errno)); + _exit(-1); + } + + /* Controlling libvirtd LXC driver now knows + what our PID is, and is able to cleanup after + us from now on */ + _exit(lxcControllerRun(stateDir, def, nveths, veths, monitor, client, appPty)); +} + + #endif diff --git a/src/lxc_controller.h b/src/lxc_controller.h index edaf53ada7..d390dc2761 100644 --- a/src/lxc_controller.h +++ b/src/lxc_controller.h @@ -26,7 +26,15 @@ #ifdef WITH_LXC -int lxcControllerMain(int appPty, int contPty); +#include "lxc_conf.h" + +int lxcControllerStart(const char *stateDir, + lxc_vm_def_t *def, + unsigned int nveths, + char **veths, + int monitor, + int appPty, + int logfd); #endif /* WITH_LXC */ diff --git a/src/lxc_driver.c b/src/lxc_driver.c index 4eb9aba1c7..515bfd76d6 100644 --- a/src/lxc_driver.c +++ b/src/lxc_driver.c @@ -31,22 +31,23 @@ #include #include #include -#include +#include +#include +#include #include #include +#include "internal.h" #include "lxc_conf.h" #include "lxc_container.h" #include "lxc_driver.h" #include "lxc_controller.h" -#include "driver.h" -#include "internal.h" #include "memory.h" #include "util.h" -#include "memory.h" #include "bridge.h" -#include "qemu_conf.h" #include "veth.h" +#include "event.h" + /* debug macros */ #define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__) @@ -284,8 +285,6 @@ static int lxcDomainUndefine(virDomainPtr dom) vm->configFile[0] = '\0'; - lxcDeleteTtyPidFile(vm); - lxcRemoveInactiveVM(driver, vm); return 0; @@ -339,10 +338,60 @@ static char *lxcDomainDumpXML(virDomainPtr dom, return lxcGenerateXML(dom->conn, driver, vm, vm->def); } + +/** + * lxcVmCleanup: + * @vm: Ptr to VM to clean up + * + * waitpid() on the container process. kill and wait the tty process + * This is called by both lxcDomainDestroy and lxcSigHandler when a + * container exits. + * + * Returns 0 on success or -1 in case of error + */ +static int lxcVMCleanup(virConnectPtr conn, + lxc_driver_t *driver, + lxc_vm_t * vm) +{ + int rc = -1; + int waitRc; + int childStatus = -1; + + while (((waitRc = waitpid(vm->pid, &childStatus, 0)) == -1) && + errno == EINTR) + ; /* empty */ + + if ((waitRc != vm->pid) && (errno != ECHILD)) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("waitpid failed to wait for container %d: %d %s"), + vm->pid, waitRc, strerror(errno)); + } + + rc = 0; + + if (WIFEXITED(childStatus)) { + rc = WEXITSTATUS(childStatus); + DEBUG("container exited with rc: %d", rc); + } + + virEventRemoveHandle(vm->monitor); + close(vm->monitor); + + virFileDeletePid(driver->stateDir, vm->def->name); + + vm->state = VIR_DOMAIN_SHUTOFF; + vm->pid = -1; + vm->def->id = -1; + vm->monitor = -1; + driver->nactivevms--; + driver->ninactivevms++; + + return rc; +} + /** * lxcSetupInterfaces: - * @conn: pointer to connection - * @vm: pointer to virtual machine structure + * @def: pointer to virtual machine structure * * Sets up the container interfaces by creating the veth device pairs and * attaching the parent end to the appropriate bridge. The container end @@ -351,24 +400,21 @@ static char *lxcDomainDumpXML(virDomainPtr dom, * Returns 0 on success or -1 in case of error */ static int lxcSetupInterfaces(virConnectPtr conn, - lxc_vm_t *vm) + lxc_vm_def_t *def, + unsigned int *nveths, + char ***veths) { int rc = -1; - lxc_driver_t *driver = conn->privateData; - struct qemud_driver *networkDriver = - (struct qemud_driver *)(conn->networkPrivateData); - lxc_net_def_t *net = vm->def->nets; - char* bridge; + lxc_net_def_t *net; + char *bridge = NULL; char parentVeth[PATH_MAX] = ""; char containerVeth[PATH_MAX] = ""; + brControl *brctl = NULL; - if ((vm->def->nets != NULL) && (driver->have_netns == 0)) { - lxcError(conn, NULL, VIR_ERR_NO_SUPPORT, - _("System lacks NETNS support")); + if (brInit(&brctl) != 0) return -1; - } - for (net = vm->def->nets; net; net = net->next) { + for (net = def->nets; net; net = net->next) { if (LXC_NET_NETWORK == net->type) { virNetworkPtr network = virNetworkLookupByName(conn, net->txName); if (!network) { @@ -378,7 +424,6 @@ static int lxcSetupInterfaces(virConnectPtr conn, bridge = virNetworkGetBridgeName(network); virNetworkFree(network); - } else { bridge = net->txName; } @@ -394,9 +439,6 @@ static int lxcSetupInterfaces(virConnectPtr conn, if (NULL != net->parentVeth) { strcpy(parentVeth, net->parentVeth); } - if (NULL != net->containerVeth) { - strcpy(containerVeth, net->containerVeth); - } DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth); if (0 != (rc = vethCreate(parentVeth, PATH_MAX, containerVeth, PATH_MAX))) { lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, @@ -406,24 +448,18 @@ static int lxcSetupInterfaces(virConnectPtr conn, if (NULL == net->parentVeth) { net->parentVeth = strdup(parentVeth); } - if (NULL == net->containerVeth) { - net->containerVeth = strdup(containerVeth); - } - - if ((NULL == net->parentVeth) || (NULL == net->containerVeth)) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to allocate veth names")); + if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0) + goto error_exit; + if (((*veths)[(*nveths)++] = strdup(containerVeth)) == NULL) goto error_exit; - } - if (!(networkDriver->brctl) && (rc = brInit(&(networkDriver->brctl)))) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("cannot initialize bridge support: %s"), - strerror(rc)); + if (NULL == net->parentVeth) { + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to allocate veth names")); goto error_exit; } - if (0 != (rc = brAddInterface(networkDriver->brctl, bridge, parentVeth))) { + if (0 != (rc = brAddInterface(brctl, bridge, parentVeth))) { lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, _("failed to add %s device to %s: %s"), parentVeth, @@ -433,7 +469,7 @@ static int lxcSetupInterfaces(virConnectPtr conn, } if (0 != (rc = vethInterfaceUpOrDown(parentVeth, 1))) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, _("failed to enable parent ns veth device: %d"), rc); goto error_exit; } @@ -443,136 +479,144 @@ static int lxcSetupInterfaces(virConnectPtr conn, rc = 0; error_exit: + brShutdown(brctl); return rc; } -/** - * lxcMoveInterfacesToNetNs: - * @conn: pointer to connection - * @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 lxcMoveInterfacesToNetNs(virConnectPtr conn, - const lxc_vm_t *vm) +static int lxcMonitorServer(virConnectPtr conn, + lxc_driver_t * driver, + lxc_vm_t *vm) { - int rc = -1; - lxc_net_def_t *net; + char *sockpath = NULL; + int fd; + struct sockaddr_un addr; - for (net = vm->def->nets; net; net = net->next) { - if (0 != moveInterfaceToNetNs(net->containerVeth, vm->def->id)) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to move interface %s to ns %d"), - net->containerVeth, vm->def->id); - goto error_exit; - } + if (asprintf(&sockpath, "%s/%s.sock", + driver->stateDir, vm->def->name) < 0) { + lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL); + return -1; } - rc = 0; - -error_exit: - return rc; -} + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to create server socket: %s"), + strerror(errno)); + goto error; + } -/** - * lxcCleanupInterfaces: - * @conn: pointer to connection - * @vm: pointer to virtual machine structure - * - * Cleans up the container interfaces by deleting the veth device pairs. - * - * Returns 0 on success or -1 in case of error - */ -static int lxcCleanupInterfaces(const lxc_vm_t *vm) -{ - int rc = -1; - lxc_net_def_t *net; + unlink(sockpath); + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); - for (net = vm->def->nets; net; net = net->next) { - if (0 != (rc = vethDelete(net->parentVeth))) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("failed to delete veth: %s"), net->parentVeth); - /* will continue to try to cleanup any other interfaces */ - } + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to bind server socket: %s"), + strerror(errno)); + goto error; + } + if (listen(fd, 30 /* backlog */ ) < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("failed to listen server socket: %s"), + strerror(errno)); + goto error; + return (-1); } - return 0; -} + VIR_FREE(sockpath); + return fd; +error: + VIR_FREE(sockpath); + if (fd != -1) + close(fd); + return -1; +} -/** - * lxcOpenTty: - * @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 lxcOpenTty(virConnectPtr conn, - int *ttymaster, - char **ttyName, - int rawmode) +static int lxcMonitorClient(virConnectPtr conn, + lxc_driver_t * driver, + lxc_vm_t *vm) { - int rc = -1; + char *sockpath = NULL; + int fd; + struct sockaddr_un addr; - *ttymaster = posix_openpt(O_RDWR|O_NOCTTY|O_NONBLOCK); - if (*ttymaster < 0) { + if (asprintf(&sockpath, "%s/%s.sock", + driver->stateDir, vm->def->name) < 0) { + lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL); + return -1; + } + + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("posix_openpt failed: %s"), strerror(errno)); - goto cleanup; + _("failed to create client socket: %s"), + strerror(errno)); + goto error; } - if (unlockpt(*ttymaster) < 0) { + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); + + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("unlockpt failed: %s"), strerror(errno)); - goto cleanup; + _("failed to connect to client socket: %s"), + strerror(errno)); + goto error; } - if (rawmode) { - struct termios ttyAttr; - if (tcgetattr(*ttymaster, &ttyAttr) < 0) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - "tcgetattr() failed: %s", strerror(errno)); - goto cleanup; - } + VIR_FREE(sockpath); + return fd; - cfmakeraw(&ttyAttr); +error: + VIR_FREE(sockpath); + if (fd != -1) + close(fd); + return -1; +} + + +static int lxcVmTerminate(virConnectPtr conn, + lxc_driver_t *driver, + lxc_vm_t *vm, + int signum) +{ + if (signum == 0) + signum = SIGINT; - if (tcsetattr(*ttymaster, TCSADRAIN, &ttyAttr) < 0) { + if (kill(vm->pid, signum) < 0) { + if (errno != ESRCH) { lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - "tcsetattr failed: %s", strerror(errno)); - goto cleanup; + _("failed to kill pid %d: %s"), + vm->pid, strerror(errno)); + return -1; } } - if (ttyName) { - char tempTtyName[PATH_MAX]; - if (0 != ptsname_r(*ttymaster, tempTtyName, sizeof(tempTtyName))) { - lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("ptsname_r failed: %s"), strerror(errno)); - goto cleanup; - } + vm->state = VIR_DOMAIN_SHUTDOWN; - if ((*ttyName = strdup(tempTtyName)) == NULL) { - lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL); - goto cleanup; - } - } + return lxcVMCleanup(conn, driver, vm); +} - rc = 0; +static void lxcMonitorEvent(int fd, + int events ATTRIBUTE_UNUSED, + void *data) +{ + lxc_driver_t *driver = data; + lxc_vm_t *vm = driver->vms; -cleanup: - if (rc != 0 && - *ttymaster != -1) { - close(*ttymaster); + while (vm) { + if (vm->monitor == fd) + break; + vm = vm->next; + } + if (!vm) { + virEventRemoveHandle(fd); + return; } - return rc; + if (lxcVmTerminate(NULL, driver, vm, SIGINT) < 0) + virEventRemoveHandle(fd); } @@ -591,80 +635,106 @@ static int lxcVmStart(virConnectPtr conn, lxc_vm_t * vm) { int rc = -1; - int sockpair[2] = { -1, -1 }; - int containerTty, parentTty; - char *containerTtyPath = NULL; + unsigned int i; + int monitor; + int parentTty; + char *logfile = NULL; + int logfd = -1; + unsigned int nveths = 0; + char **veths = NULL; + + if (virFileMakePath(driver->logDir) < 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("cannot create log directory %s: %s"), + driver->logDir, strerror(rc)); + return -1; + } - /* open parent tty */ - VIR_FREE(vm->def->tty); - if (lxcOpenTty(conn, &parentTty, &vm->def->tty, 1) < 0) { - goto cleanup; + if (asprintf(&logfile, "%s/%s.log", + driver->logDir, vm->def->name) < 0) { + lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL); + return -1; } - /* open container tty */ - if (lxcOpenTty(conn, &containerTty, &containerTtyPath, 0) < 0) { + if ((monitor = lxcMonitorServer(conn, driver, vm)) < 0) goto cleanup; - } - /* fork process to handle the tty io forwarding */ - if ((vm->pid = fork()) < 0) { + /* open parent tty */ + VIR_FREE(vm->def->tty); + if (virFileOpenTty(&parentTty, &vm->def->tty, 1) < 0) { lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("unable to fork tty forwarding process: %s"), + _("failed to allocate tty: %s"), strerror(errno)); goto cleanup; } - if (vm->pid == 0) { - /* child process calls forward routine */ - lxcControllerMain(parentTty, containerTty); - } - - if (lxcStoreTtyPid(driver, vm)) { - DEBUG0("unable to store tty pid"); - } - - close(parentTty); - close(containerTty); - - if (0 != (rc = lxcSetupInterfaces(conn, vm))) { + if (lxcSetupInterfaces(conn, vm->def, &nveths, &veths) != 0) goto cleanup; - } - /* create a socket pair to send continue message to the container once */ - /* we've completed the post clone configuration */ - if (0 != socketpair(PF_UNIX, SOCK_STREAM, 0, sockpair)) { + if ((logfd = open(logfile, O_WRONLY | O_TRUNC | O_CREAT, + S_IRUSR|S_IWUSR)) < 0) { lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, - _("sockpair failed: %s"), strerror(errno)); + _("failed to open %s: %s"), logfile, + strerror(errno)); goto cleanup; } - /* check this rc */ - - vm->def->id = lxcContainerStart(conn, - vm->def, - sockpair[1], - containerTtyPath); - if (vm->def->id == -1) + if (lxcControllerStart(driver->stateDir, + vm->def, nveths, veths, + monitor, parentTty, logfd) < 0) goto cleanup; - lxcSaveConfig(conn, driver, vm, vm->def); - - rc = lxcMoveInterfacesToNetNs(conn, vm); - if (rc != 0) + /* Close the server side of the monitor, now owned + * by the controller process */ + close(monitor); + monitor = -1; + + /* Connect to the controller as a client *first* because + * this will block until the child has written their + * pid file out to disk */ + if ((vm->monitor = lxcMonitorClient(conn, driver, vm)) < 0) goto cleanup; - rc = lxcContainerSendContinue(conn, sockpair[0]); - if (rc != 0) + /* And get its pid */ + if ((rc = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) != 0) { + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, + _("Failed to read pid file %s/%s.pid: %s"), + driver->stateDir, vm->def->name, strerror(rc)); + rc = -1; goto cleanup; + } + vm->def->id = vm->pid; vm->state = VIR_DOMAIN_RUNNING; driver->ninactivevms--; driver->nactivevms++; -cleanup: - if (sockpair[0] != -1) close(sockpair[0]); - if (sockpair[1] != -1) close(sockpair[1]); - VIR_FREE(containerTtyPath); + if (virEventAddHandle(vm->monitor, + POLLERR | POLLHUP, + lxcMonitorEvent, + driver) < 0) { + lxcVmTerminate(conn, driver, vm, 0); + goto cleanup; + } + rc = 0; + +cleanup: + for (i = 0 ; i < nveths ; i++) { + if (rc != 0) + vethDelete(veths[i]); + VIR_FREE(veths[i]); + } + if (monitor != -1) + close(monitor); + if (rc != 0 && vm->monitor != -1) { + close(vm->monitor); + vm->monitor = -1; + } + if (parentTty != -1) + close(parentTty); + if (logfd != -1) + close(logfd); + VIR_FREE(logfile); return rc; } @@ -752,105 +822,18 @@ return_point: */ 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; - } + return -1; } - vm->state = VIR_DOMAIN_SHUTDOWN; - - rc = 0; - -error_out: - return rc; + return lxcVmTerminate(dom->conn, driver, vm, 0); } -/** - * lxcVmCleanup: - * @vm: Ptr to VM to clean up - * - * waitpid() on the container process. kill and wait the tty process - * This is called by boh lxcDomainDestroy and lxcSigHandler when a - * container exits. - * - * Returns 0 on success or -1 in case of error - */ -static int lxcVMCleanup(lxc_driver_t *driver, lxc_vm_t * vm) -{ - int rc = -1; - int waitRc; - int childStatus = -1; - - /* if this fails, we'll continue. it will report any errors */ - lxcCleanupInterfaces(vm); - - while (((waitRc = waitpid(vm->def->id, &childStatus, 0)) == -1) && - errno == EINTR); - - if ((waitRc != vm->def->id) && (errno != ECHILD)) { - lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, - _("waitpid failed to wait for container %d: %d %s"), - vm->def->id, waitRc, strerror(errno)); - goto kill_tty; - } - - rc = 0; - - if (WIFEXITED(childStatus)) { - rc = WEXITSTATUS(childStatus); - DEBUG("container exited with rc: %d", rc); - } - -kill_tty: - if (2 > vm->pid) { - DEBUG("not killing tty process with pid %d", vm->pid); - goto tty_error_out; - } - - if (0 > (kill(vm->pid, SIGKILL))) { - if (ESRCH != errno) { - lxcError(NULL, NULL, 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) && (errno != ECHILD)) { - lxcError(NULL, NULL, 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; - lxcDeleteTtyPidFile(vm); - vm->def->id = -1; - driver->nactivevms--; - driver->ninactivevms++; - lxcSaveConfig(NULL, driver, vm, vm->def); - - return rc; - } /** * lxcDomainDestroy: @@ -862,31 +845,16 @@ tty_error_out: */ static int lxcDomainDestroy(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, SIGKILL))) { - if (ESRCH != errno) { - lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR, - _("sending SIGKILL failed: %s"), strerror(errno)); - - goto error_out; - } + return -1; } - vm->state = VIR_DOMAIN_SHUTDOWN; - - rc = lxcVMCleanup(driver, vm); - -error_out: - return rc; + return lxcVmTerminate(dom->conn, driver, vm, SIGKILL); } static int lxcCheckNetNsSupport(void) @@ -907,6 +875,7 @@ static int lxcCheckNetNsSupport(void) static int lxcStartup(void) { uid_t uid = getuid(); + lxc_vm_t *vm; /* Check that the user is root */ if (0 != uid) { @@ -935,6 +904,36 @@ static int lxcStartup(void) return -1; } + vm = lxc_driver->vms; + while (vm) { + int rc; + if ((vm->monitor = lxcMonitorClient(NULL, lxc_driver, vm)) < 0) { + vm = vm->next; + continue; + } + + /* Read pid from controller */ + if ((rc = virFileReadPid(lxc_driver->stateDir, vm->def->name, &vm->pid)) != 0) { + close(vm->monitor); + vm->monitor = -1; + vm = vm->next; + continue; + } + + if (vm->pid != 0) { + vm->def->id = vm->pid; + vm->state = VIR_DOMAIN_RUNNING; + lxc_driver->ninactivevms--; + lxc_driver->nactivevms++; + } else { + vm->def->id = -1; + close(vm->monitor); + vm->monitor = -1; + } + + vm = vm->next; + } + return 0; } @@ -942,6 +941,7 @@ static void lxcFreeDriver(lxc_driver_t *driver) { VIR_FREE(driver->configDir); VIR_FREE(driver->stateDir); + VIR_FREE(driver->logDir); VIR_FREE(driver); } @@ -978,37 +978,6 @@ lxcActive(void) { return 0; } -/** - * lxcSigHandler: - * @siginfo: Pointer to siginfo_t structure - * - * Handles signals received by libvirtd. Currently this is used to - * catch SIGCHLD from an exiting container. - * - * Returns 0 on success or -1 in case of error - */ -static int lxcSigHandler(siginfo_t *siginfo) -{ - int rc = -1; - lxc_vm_t *vm; - - if (siginfo->si_signo == SIGCHLD) { - vm = lxcFindVMByID(lxc_driver, siginfo->si_pid); - - if (NULL == vm) { - DEBUG("Ignoring SIGCHLD from non-container process %d\n", - siginfo->si_pid); - goto cleanup; - } - - rc = lxcVMCleanup(lxc_driver, vm); - - } - -cleanup: - return rc; -} - /* Function Tables */ static virDriver lxcDriver = { @@ -1079,7 +1048,7 @@ static virStateDriver lxcStateDriver = { lxcShutdown, NULL, /* reload */ lxcActive, - lxcSigHandler + NULL, }; int lxcRegister(void) diff --git a/src/util.c b/src/util.c index fe701cf382..a4ae78a4e0 100644 --- a/src/util.c +++ b/src/util.c @@ -37,6 +37,9 @@ #include #endif #include +#if HAVE_TERMIOS_H +#include +#endif #include "c-ctype.h" #ifdef HAVE_PATHS_H @@ -472,6 +475,169 @@ int virFileBuildPath(const char *dir, return 0; } + +#ifdef __linux__ +int virFileOpenTty(int *ttymaster, + char **ttyName, + int rawmode) +{ + int rc = -1; + + if ((*ttymaster = posix_openpt(O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0) + goto cleanup; + + if (unlockpt(*ttymaster) < 0) + goto cleanup; + + if (grantpt(*ttymaster) < 0) + goto cleanup; + + if (rawmode) { + struct termios ttyAttr; + if (tcgetattr(*ttymaster, &ttyAttr) < 0) + goto cleanup; + + cfmakeraw(&ttyAttr); + + if (tcsetattr(*ttymaster, TCSADRAIN, &ttyAttr) < 0) + goto cleanup; + } + + if (ttyName) { + char tempTtyName[PATH_MAX]; + if (ptsname_r(*ttymaster, tempTtyName, sizeof(tempTtyName)) < 0) + goto cleanup; + + if ((*ttyName = strdup(tempTtyName)) == NULL) { + errno = ENOMEM; + goto cleanup; + } + } + + rc = 0; + +cleanup: + if (rc != 0 && + *ttymaster != -1) { + close(*ttymaster); + } + + return rc; + +} +#else +int virFileOpenTty(int *ttymaster ATTRIBUTE_UNUSED, + char **ttyName ATTRIBUTE_UNUSED, + int rawmode ATTRIBUTE_UNUSED) +{ + return -1; +} +#endif + + +int virFileWritePid(const char *dir, + const char *name, + pid_t pid) +{ + int rc; + int fd; + FILE *file = NULL; + char *pidfile = NULL; + + if ((rc = virFileMakePath(dir))) + goto cleanup; + + if (asprintf(&pidfile, "%s/%s.pid", dir, name) < 0) { + rc = ENOMEM; + goto cleanup; + } + + if ((fd = open(pidfile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR)) < 0) { + rc = errno; + goto cleanup; + } + + if (!(file = fdopen(fd, "w"))) { + rc = errno; + close(fd); + goto cleanup; + } + + if (fprintf(file, "%d", pid) < 0) { + rc = errno; + goto cleanup; + } + + rc = 0; + +cleanup: + if (file && + fclose(file) < 0) { + rc = errno; + } + + VIR_FREE(pidfile); + return rc; +} + +int virFileReadPid(const char *dir, + const char *name, + pid_t *pid) +{ + int rc; + FILE *file; + char *pidfile = NULL; + *pid = 0; + if (asprintf(&pidfile, "%s/%s.pid", dir, name) < 0) { + rc = ENOMEM; + goto cleanup; + } + + if (!(file = fopen(pidfile, "r"))) { + rc = errno; + goto cleanup; + } + + if (fscanf(file, "%d", pid) != 1) { + rc = EINVAL; + goto cleanup; + } + + if (fclose(file) < 0) { + rc = errno; + goto cleanup; + } + + rc = 0; + + cleanup: + VIR_FREE(pidfile); + return rc; +} + +int virFileDeletePid(const char *dir, + const char *name) +{ + int rc = 0; + char *pidfile = NULL; + + if (asprintf(&pidfile, "%s/%s.pid", dir, name) < 0) { + rc = errno; + goto cleanup; + } + + if (unlink(pidfile) < 0 && errno != ENOENT) + rc = errno; + +cleanup: + VIR_FREE(pidfile); + return rc; +} + + + /* Like strtol, but produce an "int" result, and check more carefully. Return 0 upon success; return -1 to indicate failure. When END_PTR is NULL, the byte after the final valid digit must be NUL. diff --git a/src/util.h b/src/util.h index 16f79c26e4..4452a297d2 100644 --- a/src/util.h +++ b/src/util.h @@ -58,6 +58,18 @@ int virFileBuildPath(const char *dir, char *buf, unsigned int buflen); +int virFileOpenTty(int *ttymaster, + char **ttyName, + int rawmode); + +int virFileWritePid(const char *dir, + const char *name, + pid_t pid); +int virFileReadPid(const char *dir, + const char *name, + pid_t *pid); +int virFileDeletePid(const char *dir, + const char *name); int __virStrToLong_i(char const *s, char **end_ptr, -- GitLab