+Wed Aug 13 11:48:36 BST 2008 Daniel Berrange <berrange@redhat.com>
+
+ * 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 <berrange@redhat.com>
+
+ 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 <berrange@redhat.com>
* src/lxc_conf.h, src/lxc_conf.c, src/lxc_container.h,
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.
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,
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;
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 */
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;
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;
lxc_vm_t *vms;
int nactivevms;
int ninactivevms;
- char* configDir;
- char* stateDir;
+ char *configDir;
+ char *stateDir;
+ char *logDir;
int have_netns;
};
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,
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;
};
*
* 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;
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;
*
* 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:
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 */
/**
* lxcContainerStart:
- * @conn: pointer to connection
* @driver: pointer to driver structure
* @vm: pointer to virtual machine structure
*
*
* 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)
{
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;
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;
}
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);
#ifdef WITH_LXC
#include <sys/epoll.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
#include <unistd.h>
+#include <paths.h>
+#include <fcntl.h>
+#include <signal.h>
#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:
*
* 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;
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 */
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
#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 */
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
-#include <termios.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
#include <unistd.h>
#include <wait.h>
+#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__)
vm->configFile[0] = '\0';
- lxcDeleteTtyPidFile(vm);
-
lxcRemoveInactiveVM(driver, vm);
return 0;
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
* 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) {
bridge = virNetworkGetBridgeName(network);
virNetworkFree(network);
-
} else {
bridge = net->txName;
}
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,
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,
}
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;
}
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);
}
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;
}
*/
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:
*/
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)
static int lxcStartup(void)
{
uid_t uid = getuid();
+ lxc_vm_t *vm;
/* Check that the user is root */
if (0 != uid) {
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;
}
{
VIR_FREE(driver->configDir);
VIR_FREE(driver->stateDir);
+ VIR_FREE(driver->logDir);
VIR_FREE(driver);
}
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 = {
lxcShutdown,
NULL, /* reload */
lxcActive,
- lxcSigHandler
+ NULL,
};
int lxcRegister(void)
#include <sys/wait.h>
#endif
#include <string.h>
+#if HAVE_TERMIOS_H
+#include <termios.h>
+#endif
#include "c-ctype.h"
#ifdef HAVE_PATHS_H
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.
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,