[Libvir] [PATCH] lxc: start domain
Daniel Veillard
veillard at redhat.com
Thu Mar 27 13:05:41 UTC 2008
On Wed, Mar 26, 2008 at 11:20:59PM -0700, Dave Leskovec wrote:
> This is a repost of patch four in the last series I posted. It contains the
> start container support. I've made some changes corresponding to Dan B's patch
> moving the lxc driver under libvirtd. I removed the isolation forks and cleaned
> up the status handling and PID storing.
General comment over the patch is that I prefer to see all function with
an header comment, it's a bit painful, but helps graps the context when an
error arise and someone who don't know the code try to sort it out. That's
not a blocker for commits but better for long term maintainance :-)
[...]
> +/* Functions */
> +static int lxcExecContainerInit(lxc_vm_def_t *vmDef)
> +{
> + int rc = -1;
> + char* execString;
> + int execStringLen = strlen(vmDef->init) + 1 + 5;
> +
> + if(NULL == (execString = calloc(execStringLen, sizeof(char)))) {
An error should really be reported here.
> + goto error_out;
> + }
> +
> + strcpy(execString, "exec ");
> + strcat(execString, vmDef->init);
Hum, it seems there is an off by one allocation error, you don't allocate
the space for the terminating 0
> + execl("/bin/sh", "sh", "-c", execString, (char*)NULL);
> + DEBUG("execl failed: %s", strerror(errno));
> +
> +error_out:
> + exit(rc);
> +}
[...]
> +static int lxcTtyForward(int fd1, int fd2, int *loopFlag, int pollmsecs)
> +{
> + 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 (!(*loopFlag)) {
> + if ((rc = poll(fds, numFds, pollmsecs)) <= 0) {
> + if(*loopFlag) {
> + goto cleanup;
> + }
> +
> + if ((0 == rc) || (errno == EINTR) || (errno == EAGAIN)) {
> + continue;
> + }
> +
> + DEBUG("poll returned error: %s", strerror(errno));
> + goto cleanup;
> + }
> +
> + for (i = 0; i < numFds; ++i) {
> + if (!fds[i].revents) {
> + continue;
> + }
> +
> + if (fds[i].revents & POLLIN) {
> + saferead(fds[i].fd, buf, 1);
> + if (1 < numFds) {
> + safewrite(fds[i ^ 1].fd, buf, 1);
> + }
> +
So if only one fd is given we discard all data read, and if 2 fds are given
all data from one is forwarded to the other, right ?
> +static int exitChildLoop;
> +static void lxcExecChildHandler(int sig ATTRIBUTE_UNUSED,
> + siginfo_t *signalInfo,
> + void *context ATTRIBUTE_UNUSED)
> +{
> + DEBUG("lxcExecChildHandler signal from %d\n", signalInfo->si_pid);
> +
> + if (signalInfo->si_pid == initPid) {
> + exitChildLoop = 1;
> + } else {
> + waitpid(signalInfo->si_pid, NULL, WNOHANG);
> + }
> +
> +}
> +
> +static int lxcExecWithTty(lxc_vm_t *vm)
> +{
> + int rc = -1;
> + lxc_vm_def_t *vmDef = vm->def;
> + int ttymaster = -1;
> + int ttyslave = -1;
> + struct sigaction sigAction;
> + sigset_t sigMask;
> + int childStatus;
> +
> + if (lxcSetupContainerTty(&ttymaster, &ttyslave) < 0) {
> + goto exit_with_error;
> + }
> +
> + sigAction.sa_sigaction = lxcExecChildHandler;
> + sigfillset(&sigMask);
> + sigAction.sa_mask = sigMask;
> + sigAction.sa_flags = SA_SIGINFO;
> + if (0 != sigaction(SIGCHLD, &sigAction, NULL)) {
> + DEBUG("sigaction failed: %s\n", strerror(errno));
> + goto exit_with_error;
> + }
> +
> + exitChildLoop = 0;
> + if ((initPid = fork()) == 0) {
> + if(lxcSetContainerStdio(ttyslave) < 0) {
> + exitChildLoop = 1;
> + goto exit_with_error;
> + }
> +
> + lxcExecContainerInit(vmDef);
> + /* this function will not return. if it fails, it will exit */
> + }
> +
> + close(ttyslave);
> + lxcTtyForward(ttymaster, vm->parentTty,
> + &exitChildLoop, 100);
> +
> + DEBUG("child waiting on pid %d", initPid);
> + waitpid(initPid, &childStatus, 0);
> + rc = WEXITSTATUS(childStatus);
> + DEBUG("container exited with rc: %d", rc);
> +
> +exit_with_error:
> + exit(rc);
> +}
So this is forked for each container created, right ?
[...]
> Index: b/src/lxc_container.h
ok
> Index: b/src/lxc_driver.c
> ===================================================================
> --- a/src/lxc_driver.c 2008-03-21 11:46:11.000000000 -0700
> +++ b/src/lxc_driver.c 2008-03-24 16:46:48.000000000 -0700
> @@ -25,14 +25,17 @@
[...]
> +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->pid = clone(lxcChild, stacktop, flags, (void *)vm);
> +
> + DEBUG("clone() returned, %d", vm->pid);
> +
> + if (vm->pid < 0) {
> + lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
> + _("clone() failed, %s"), strerror(errno));
> + goto error_exit;
> + }
> +
> + vm->def->id = vm->pid;
> + lxcSaveConfig(NULL, driver, vm, vm->def);
We should make sure at some point taht there is some kind of locking
mechanism when creating those config files, no ?
> + rc = 0;
> +
> +error_exit:
> + return rc;
> +}
[...]
> +static int lxcVmStart(virConnectPtr conn,
> + lxc_driver_t * driver,
> + lxc_vm_t * vm)
> +{
> + int rc = -1;
> + lxc_vm_def_t *vmDef = vm->def;
> +
> + /* open tty for the container */
> + if(lxcSetupTtyTunnel(conn, vmDef, &vm->parentTty) < 0) {
> + goto cleanup;
> + }
> +
> + rc = lxcStartContainer(conn, driver, vm);
> +
> + if(rc == 0) {
> + vm->state = VIR_DOMAIN_RUNNING;
> + driver->ninactivevms--;
> + driver->nactivevms++;
> + }
> +
> +cleanup:
> + return rc;
> +}
[...]
Hum, now that config names are saved based on domain names, wouldn't
it be better to use a name based lookup ? Less precise but more direct
[...]
>
> static int lxcStartup(void)
> {
> @@ -459,7 +666,7 @@
> NULL, /* getCapabilities */
> lxcListDomains, /* listDomains */
> lxcNumDomains, /* numOfDomains */
> - NULL/*lxcDomainCreateLinux*/, /* domainCreateLinux */
> + lxcDomainCreateAndStart, /* domainCreateLinux */
> lxcDomainLookupByID, /* domainLookupByID */
> lxcDomainLookupByUUID, /* domainLookupByUUID */
> lxcDomainLookupByName, /* domainLookupByName */
> @@ -483,7 +690,7 @@
> lxcDomainDumpXML, /* domainDumpXML */
> lxcListDefinedDomains, /* listDefinedDomains */
> lxcNumDefinedDomains, /* numOfDefinedDomains */
> - NULL, /* domainCreate */
> + lxcDomainStart, /* domainCreate */
> lxcDomainDefine, /* domainDefineXML */
> lxcDomainUndefine, /* domainUndefine */
> NULL, /* domainAttachDevice */
Looks fine overall. I wonder if 1 fork/clone per container can't be
reduced to a single process doing the fd processing for all the containers
running. But that's probably harder, more fragile and for little gain.
Daniel
--
Red Hat Virtualization group http://redhat.com/virtualization/
Daniel Veillard | virtualization library http://libvirt.org/
veillard at redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/
http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/
More information about the libvir-list
mailing list