Add support for multiple consoles in LXC

Currently the LXC controller only supports setup of a single
text console. This is wired up to the container init's stdio,
as well as /dev/console and /dev/tty1. Extending support for
multiple consoles, means wiring up additional PTYs to /dev/tty2,
/dev/tty3, etc, etc. The LXC controller is passed multiple open
file handles, one for each console requested.

* src/lxc/lxc_container.c, src/lxc/lxc_container.h: Wire up
  all the /dev/ttyN links required to symlink to /dev/pts/NN
* src/lxc/lxc_container.h: Open more container side /dev/pts/NN
  devices, and adapt event loop to handle I/O from all consoles
* src/lxc/lxc_driver.c: Setup multiple host side PTYs
This commit is contained in:
Daniel P. Berrange 2011-10-20 09:44:31 +01:00
parent 86b53e59d8
commit 0f31f7b794
4 changed files with 192 additions and 119 deletions

View File

@ -94,7 +94,8 @@ struct __lxc_child_argv {
unsigned int nveths; unsigned int nveths;
char **veths; char **veths;
int monitor; int monitor;
char *ttyPath; char **ttyPaths;
size_t nttyPaths;
int handshakefd; int handshakefd;
}; };
@ -526,9 +527,9 @@ static int lxcContainerMountDevFS(virDomainFSDefPtr root)
return rc; return rc;
} }
static int lxcContainerPopulateDevices(void) static int lxcContainerPopulateDevices(char **ttyPaths, size_t nttyPaths)
{ {
int i; size_t i;
const struct { const struct {
int maj; int maj;
int min; int min;
@ -570,21 +571,28 @@ static int lxcContainerPopulateDevices(void)
} }
} }
/* XXX we should allow multiple consoles per container for (i = 0 ; i < nttyPaths ; i++) {
* for tty2, tty3, etc, but the domain XML does not char *tty;
* handle this yet if (virAsprintf(&tty, "/dev/tty%zu", i+1) < 0) {
*/ virReportOOMError();
if (symlink("/dev/pts/0", "/dev/tty1") < 0) {
virReportSystemError(errno, "%s",
_("Failed to symlink /dev/pts/0 to /dev/tty1"));
return -1; return -1;
} }
if (symlink("/dev/pts/0", "/dev/console") < 0) { if (symlink(ttyPaths[i], tty) < 0) {
virReportSystemError(errno, "%s", VIR_FREE(tty);
_("Failed to symlink /dev/pts/0 to /dev/console")); virReportSystemError(errno,
_("Failed to symlink %s to %s"),
ttyPaths[i], tty);
return -1; return -1;
} }
VIR_FREE(tty);
if (i == 0 &&
symlink(ttyPaths[i], "/dev/console") < 0) {
virReportSystemError(errno,
_("Failed to symlink %s to /dev/console"),
ttyPaths[i]);
return -1;
}
}
return 0; return 0;
} }
@ -1043,7 +1051,9 @@ cleanup:
* this is based on this thread http://lkml.org/lkml/2008/3/5/29 * this is based on this thread http://lkml.org/lkml/2008/3/5/29
*/ */
static int lxcContainerSetupPivotRoot(virDomainDefPtr vmDef, static int lxcContainerSetupPivotRoot(virDomainDefPtr vmDef,
virDomainFSDefPtr root) virDomainFSDefPtr root,
char **ttyPaths,
size_t nttyPaths)
{ {
/* Gives us a private root, leaving all parent OS mounts on /.oldroot */ /* Gives us a private root, leaving all parent OS mounts on /.oldroot */
if (lxcContainerPivotRoot(root) < 0) if (lxcContainerPivotRoot(root) < 0)
@ -1058,7 +1068,7 @@ static int lxcContainerSetupPivotRoot(virDomainDefPtr vmDef,
return -1; return -1;
/* Populates device nodes in /dev/ */ /* Populates device nodes in /dev/ */
if (lxcContainerPopulateDevices() < 0) if (lxcContainerPopulateDevices(ttyPaths, nttyPaths) < 0)
return -1; return -1;
/* Sets up any non-root mounts from guest config */ /* Sets up any non-root mounts from guest config */
@ -1102,10 +1112,12 @@ static int lxcContainerSetupExtraMounts(virDomainDefPtr vmDef)
} }
static int lxcContainerSetupMounts(virDomainDefPtr vmDef, static int lxcContainerSetupMounts(virDomainDefPtr vmDef,
virDomainFSDefPtr root) virDomainFSDefPtr root,
char **ttyPaths,
size_t nttyPaths)
{ {
if (root) if (root)
return lxcContainerSetupPivotRoot(vmDef, root); return lxcContainerSetupPivotRoot(vmDef, root, ttyPaths, nttyPaths);
else else
return lxcContainerSetupExtraMounts(vmDef); return lxcContainerSetupExtraMounts(vmDef);
} }
@ -1189,17 +1201,25 @@ static int lxcContainerChild( void *data )
root = virDomainGetRootFilesystem(vmDef); root = virDomainGetRootFilesystem(vmDef);
if (argv->nttyPaths) {
if (root) { if (root) {
if (virAsprintf(&ttyPath, "%s%s", root->src, argv->ttyPath) < 0) { if (virAsprintf(&ttyPath, "%s%s", root->src, argv->ttyPaths[0]) < 0) {
virReportOOMError(); virReportOOMError();
goto cleanup; goto cleanup;
} }
} else { } else {
if (!(ttyPath = strdup(argv->ttyPath))) { if (!(ttyPath = strdup(argv->ttyPaths[0]))) {
virReportOOMError(); virReportOOMError();
goto cleanup; goto cleanup;
} }
} }
} else {
if (!(ttyPath = strdup("/dev/null"))) {
virReportOOMError();
goto cleanup;
}
}
VIR_DEBUG("Container TTY path: %s", ttyPath); VIR_DEBUG("Container TTY path: %s", ttyPath);
ttyfd = open(ttyPath, O_RDWR|O_NOCTTY); ttyfd = open(ttyPath, O_RDWR|O_NOCTTY);
@ -1210,7 +1230,7 @@ static int lxcContainerChild( void *data )
goto cleanup; goto cleanup;
} }
if (lxcContainerSetupMounts(vmDef, root) < 0) if (lxcContainerSetupMounts(vmDef, root, argv->ttyPaths, argv->nttyPaths) < 0)
goto cleanup; goto cleanup;
if (!virFileExists(vmDef->os.init)) { if (!virFileExists(vmDef->os.init)) {
@ -1314,14 +1334,15 @@ int lxcContainerStart(virDomainDefPtr def,
char **veths, char **veths,
int control, int control,
int handshakefd, int handshakefd,
char *ttyPath) char **ttyPaths,
size_t nttyPaths)
{ {
pid_t pid; pid_t pid;
int cflags; int cflags;
int stacksize = getpagesize() * 4; int stacksize = getpagesize() * 4;
char *stack, *stacktop; char *stack, *stacktop;
lxc_child_argv_t args = { def, nveths, veths, control, ttyPath, lxc_child_argv_t args = { def, nveths, veths, control,
handshakefd}; ttyPaths, nttyPaths, handshakefd};
/* allocate a stack for the container */ /* allocate a stack for the container */
if (VIR_ALLOC_N(stack, stacksize) < 0) { if (VIR_ALLOC_N(stack, stacksize) < 0) {

View File

@ -53,7 +53,8 @@ int lxcContainerStart(virDomainDefPtr def,
char **veths, char **veths,
int control, int control,
int handshakefd, int handshakefd,
char *ttyPath); char **ttyPaths,
size_t nttyPaths);
int lxcContainerAvailable(int features); int lxcContainerAvailable(int features);

View File

@ -797,20 +797,19 @@ error:
*/ */
static int lxcControllerMain(int serverFd, static int lxcControllerMain(int serverFd,
int clientFd, int clientFd,
int hostFd, int *hostFds,
int contFd, int *contFds,
size_t nFds,
pid_t container) pid_t container)
{ {
struct lxcConsole console = { struct lxcConsole *consoles;
.hostFd = hostFd,
.contFd = contFd,
};
struct lxcMonitor monitor = { struct lxcMonitor monitor = {
.serverFd = serverFd, .serverFd = serverFd,
.clientFd = clientFd, .clientFd = clientFd,
}; };
virErrorPtr err; virErrorPtr err;
int rc = -1; int rc = -1;
size_t i;
if (virMutexInit(&lock) < 0) if (virMutexInit(&lock) < 0)
goto cleanup2; goto cleanup2;
@ -837,8 +836,8 @@ static int lxcControllerMain(int serverFd,
goto cleanup; goto cleanup;
} }
VIR_DEBUG("serverFd=%d clientFd=%d hostFd=%d contFd=%d", VIR_DEBUG("serverFd=%d clientFd=%d",
serverFd, clientFd, hostFd, contFd); serverFd, clientFd);
virResetLastError(); virResetLastError();
if ((monitor.serverWatch = virEventAddHandle(monitor.serverFd, if ((monitor.serverWatch = virEventAddHandle(monitor.serverFd,
@ -862,25 +861,35 @@ static int lxcControllerMain(int serverFd,
goto cleanup; goto cleanup;
} }
if ((console.hostWatch = virEventAddHandle(console.hostFd, if (VIR_ALLOC_N(consoles, nFds) < 0) {
virReportOOMError();
goto cleanup;
}
for (i = 0 ; i < nFds ; i++) {
consoles[i].hostFd = hostFds[i];
consoles[i].contFd = contFds[i];
if ((consoles[i].hostWatch = virEventAddHandle(consoles[i].hostFd,
VIR_EVENT_HANDLE_READABLE, VIR_EVENT_HANDLE_READABLE,
lxcConsoleIO, lxcConsoleIO,
&console, &consoles[i],
NULL)) < 0) { NULL)) < 0) {
lxcError(VIR_ERR_INTERNAL_ERROR, "%s", lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to watch host console PTY")); _("Unable to watch host console PTY"));
goto cleanup; goto cleanup;
} }
if ((console.contWatch = virEventAddHandle(console.contFd, if ((consoles[i].contWatch = virEventAddHandle(consoles[i].contFd,
VIR_EVENT_HANDLE_READABLE, VIR_EVENT_HANDLE_READABLE,
lxcConsoleIO, lxcConsoleIO,
&console, &consoles[i],
NULL)) < 0) { NULL)) < 0) {
lxcError(VIR_ERR_INTERNAL_ERROR, "%s", lxcError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to watch host console PTY")); _("Unable to watch host console PTY"));
goto cleanup; goto cleanup;
} }
}
virMutexLock(&lock); virMutexLock(&lock);
while (!quit) { while (!quit) {
@ -899,10 +908,9 @@ cleanup:
virMutexDestroy(&lock); virMutexDestroy(&lock);
signal(SIGCHLD, SIG_DFL); signal(SIGCHLD, SIG_DFL);
cleanup2: cleanup2:
VIR_FORCE_CLOSE(console.hostFd);
VIR_FORCE_CLOSE(console.contFd);
VIR_FORCE_CLOSE(monitor.serverFd); VIR_FORCE_CLOSE(monitor.serverFd);
VIR_FORCE_CLOSE(monitor.clientFd); VIR_FORCE_CLOSE(monitor.clientFd);
VIR_FREE(consoles);
return rc; return rc;
} }
@ -1027,14 +1035,15 @@ lxcControllerRun(virDomainDefPtr def,
char **veths, char **veths,
int monitor, int monitor,
int client, int client,
int appPty, int *ttyFDs,
size_t nttyFDs,
int handshakefd) int handshakefd)
{ {
int rc = -1; int rc = -1;
int control[2] = { -1, -1}; int control[2] = { -1, -1};
int containerhandshake[2] = { -1, -1 }; int containerhandshake[2] = { -1, -1 };
int containerPty = -1; int *containerTtyFDs = NULL;
char *containerPtyPath = NULL; char **containerTtyPaths = NULL;
pid_t container = -1; pid_t container = -1;
virDomainFSDefPtr root; virDomainFSDefPtr root;
char *devpts = NULL; char *devpts = NULL;
@ -1043,6 +1052,15 @@ lxcControllerRun(virDomainDefPtr def,
int *loopDevs = NULL; int *loopDevs = NULL;
size_t i; size_t i;
if (VIR_ALLOC_N(containerTtyFDs, nttyFDs) < 0) {
virReportOOMError();
goto cleanup;
}
if (VIR_ALLOC_N(containerTtyPaths, nttyFDs) < 0) {
virReportOOMError();
goto cleanup;
}
if (socketpair(PF_UNIX, SOCK_STREAM, 0, control) < 0) { if (socketpair(PF_UNIX, SOCK_STREAM, 0, control) < 0) {
virReportSystemError(errno, "%s", virReportSystemError(errno, "%s",
_("sockpair failed")); _("sockpair failed"));
@ -1133,25 +1151,35 @@ lxcControllerRun(virDomainDefPtr def,
VIR_WARN("Kernel does not support private devpts, using shared devpts"); VIR_WARN("Kernel does not support private devpts, using shared devpts");
VIR_FREE(devptmx); VIR_FREE(devptmx);
} }
} else {
if (nttyFDs != -1) {
lxcError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Expected exactly one TTY fd"));
goto cleanup;
}
} }
for (i = 0 ; i < nttyFDs ; i++) {
if (devptmx) { if (devptmx) {
VIR_DEBUG("Opening tty on private %s", devptmx); VIR_DEBUG("Opening tty on private %s", devptmx);
if (lxcCreateTty(devptmx, &containerPty, &containerPtyPath) < 0) { if (lxcCreateTty(devptmx,
&containerTtyFDs[i],
&containerTtyPaths[i]) < 0) {
virReportSystemError(errno, "%s", virReportSystemError(errno, "%s",
_("Failed to allocate tty")); _("Failed to allocate tty"));
goto cleanup; goto cleanup;
} }
} else { } else {
VIR_DEBUG("Opening tty on shared /dev/ptmx"); VIR_DEBUG("Opening tty on shared /dev/ptmx");
if (virFileOpenTty(&containerPty, if (virFileOpenTty(&containerTtyFDs[i],
&containerPtyPath, &containerTtyPaths[i],
0) < 0) { 0) < 0) {
virReportSystemError(errno, "%s", virReportSystemError(errno, "%s",
_("Failed to allocate tty")); _("Failed to allocate tty"));
goto cleanup; goto cleanup;
} }
} }
}
if (lxcSetPersonality(def) < 0) if (lxcSetPersonality(def) < 0)
goto cleanup; goto cleanup;
@ -1161,7 +1189,8 @@ lxcControllerRun(virDomainDefPtr def,
veths, veths,
control[1], control[1],
containerhandshake[1], containerhandshake[1],
containerPtyPath)) < 0) containerTtyPaths,
nttyFDs)) < 0)
goto cleanup; goto cleanup;
VIR_FORCE_CLOSE(control[1]); VIR_FORCE_CLOSE(control[1]);
VIR_FORCE_CLOSE(containerhandshake[1]); VIR_FORCE_CLOSE(containerhandshake[1]);
@ -1200,33 +1229,41 @@ lxcControllerRun(virDomainDefPtr def,
VIR_FORCE_CLOSE(handshakefd); VIR_FORCE_CLOSE(handshakefd);
if (virSetBlocking(monitor, false) < 0 || if (virSetBlocking(monitor, false) < 0 ||
virSetBlocking(client, false) < 0 || virSetBlocking(client, false) < 0) {
virSetBlocking(appPty, false) < 0 ||
virSetBlocking(containerPty, false) < 0) {
virReportSystemError(errno, "%s", virReportSystemError(errno, "%s",
_("Unable to set file descriptor non blocking")); _("Unable to set file descriptor non blocking"));
goto cleanup; goto cleanup;
} }
for (i = 0 ; i < nttyFDs ; i++) {
if (virSetBlocking(ttyFDs[i], false) < 0 ||
virSetBlocking(containerTtyFDs[i], false) < 0) {
virReportSystemError(errno, "%s",
_("Unable to set file descriptor non blocking"));
goto cleanup;
}
}
rc = lxcControllerMain(monitor, client, appPty, containerPty, container); rc = lxcControllerMain(monitor, client, ttyFDs, containerTtyFDs, nttyFDs, container);
monitor = client = appPty = containerPty = -1; monitor = client = -1;
cleanup: cleanup:
VIR_FREE(devptmx); VIR_FREE(devptmx);
VIR_FREE(devpts); VIR_FREE(devpts);
VIR_FORCE_CLOSE(control[0]); VIR_FORCE_CLOSE(control[0]);
VIR_FORCE_CLOSE(control[1]); VIR_FORCE_CLOSE(control[1]);
VIR_FREE(containerPtyPath);
VIR_FORCE_CLOSE(containerPty);
VIR_FORCE_CLOSE(handshakefd); VIR_FORCE_CLOSE(handshakefd);
VIR_FORCE_CLOSE(containerhandshake[0]); VIR_FORCE_CLOSE(containerhandshake[0]);
VIR_FORCE_CLOSE(containerhandshake[1]); VIR_FORCE_CLOSE(containerhandshake[1]);
if (loopDevs) { for (i = 0 ; i < nttyFDs ; i++)
VIR_FREE(containerTtyPaths[i]);
VIR_FREE(containerTtyPaths);
for (i = 0 ; i < nttyFDs ; i++)
VIR_FORCE_CLOSE(containerTtyFDs[i]);
VIR_FREE(containerTtyFDs);
for (i = 0 ; i < nloopDevs ; i++) for (i = 0 ; i < nloopDevs ; i++)
VIR_FORCE_CLOSE(loopDevs[i]); VIR_FORCE_CLOSE(loopDevs[i]);
}
VIR_FREE(loopDevs); VIR_FREE(loopDevs);
if (container > 1) { if (container > 1) {
@ -1250,7 +1287,6 @@ int main(int argc, char *argv[])
int nveths = 0; int nveths = 0;
char **veths = NULL; char **veths = NULL;
int monitor = -1; int monitor = -1;
int appPty = -1;
int handshakefd = -1; int handshakefd = -1;
int bg = 0; int bg = 0;
virCapsPtr caps = NULL; virCapsPtr caps = NULL;
@ -1266,6 +1302,8 @@ int main(int argc, char *argv[])
{ "help", 0, NULL, 'h' }, { "help", 0, NULL, 'h' },
{ 0, 0, 0, 0 }, { 0, 0, 0, 0 },
}; };
int *ttyFDs = NULL;
size_t nttyFDs = 0;
if (setlocale(LC_ALL, "") == NULL || if (setlocale(LC_ALL, "") == NULL ||
bindtextdomain(PACKAGE, LOCALEDIR) == NULL || bindtextdomain(PACKAGE, LOCALEDIR) == NULL ||
@ -1307,7 +1345,11 @@ int main(int argc, char *argv[])
break; break;
case 'c': case 'c':
if (virStrToLong_i(optarg, NULL, 10, &appPty) < 0) { if (VIR_REALLOC_N(ttyFDs, nttyFDs + 1) < 0) {
virReportOOMError();
goto cleanup;
}
if (virStrToLong_i(optarg, NULL, 10, &ttyFDs[nttyFDs++]) < 0) {
fprintf(stderr, "malformed --console argument '%s'", optarg); fprintf(stderr, "malformed --console argument '%s'", optarg);
goto cleanup; goto cleanup;
} }
@ -1345,11 +1387,6 @@ int main(int argc, char *argv[])
goto cleanup; goto cleanup;
} }
if (appPty < 0) {
fprintf(stderr, "%s: missing --console argument for container PTY\n", argv[0]);
goto cleanup;
}
if (handshakefd < 0) { if (handshakefd < 0) {
fprintf(stderr, "%s: missing --handshake argument for container PTY\n", fprintf(stderr, "%s: missing --handshake argument for container PTY\n",
argv[0]); argv[0]);
@ -1429,8 +1466,8 @@ int main(int argc, char *argv[])
goto cleanup; goto cleanup;
} }
rc = lxcControllerRun(def, nveths, veths, monitor, client, appPty, rc = lxcControllerRun(def, nveths, veths, monitor, client,
handshakefd); ttyFDs, nttyFDs, handshakefd);
cleanup: cleanup:

View File

@ -1447,11 +1447,12 @@ lxcBuildControllerCmd(lxc_driver_t *driver,
virDomainObjPtr vm, virDomainObjPtr vm,
int nveths, int nveths,
char **veths, char **veths,
int appPty, int *ttyFDs,
size_t nttyFDs,
int logfile, int logfile,
int handshakefd) int handshakefd)
{ {
int i; size_t i;
char *filterstr; char *filterstr;
char *outputstr; char *outputstr;
virCommandPtr cmd; virCommandPtr cmd;
@ -1492,8 +1493,12 @@ lxcBuildControllerCmd(lxc_driver_t *driver,
virLogGetDefaultPriority()); virLogGetDefaultPriority());
} }
virCommandAddArgList(cmd, "--name", vm->def->name, "--console", NULL); virCommandAddArgList(cmd, "--name", vm->def->name, NULL);
virCommandAddArgFormat(cmd, "%d", appPty); for (i = 0 ; i < nttyFDs ; i++) {
virCommandAddArg(cmd, "--console");
virCommandAddArgFormat(cmd, "%d", ttyFDs[i]);
virCommandPreserveFD(cmd, ttyFDs[i]);
}
virCommandAddArg(cmd, "--handshake"); virCommandAddArg(cmd, "--handshake");
virCommandAddArgFormat(cmd, "%d", handshakefd); virCommandAddArgFormat(cmd, "%d", handshakefd);
virCommandAddArg(cmd, "--background"); virCommandAddArg(cmd, "--background");
@ -1518,7 +1523,6 @@ lxcBuildControllerCmd(lxc_driver_t *driver,
goto cleanup; goto cleanup;
} }
virCommandPreserveFD(cmd, appPty);
virCommandPreserveFD(cmd, handshakefd); virCommandPreserveFD(cmd, handshakefd);
virCommandSetOutputFD(cmd, &logfile); virCommandSetOutputFD(cmd, &logfile);
virCommandSetErrorFD(cmd, &logfile); virCommandSetErrorFD(cmd, &logfile);
@ -1621,9 +1625,9 @@ static int lxcVmStart(virConnectPtr conn,
virDomainRunningReason reason) virDomainRunningReason reason)
{ {
int rc = -1, r; int rc = -1, r;
unsigned int i; size_t nttyFDs = 0;
int parentTty; int *ttyFDs = NULL;
char *parentTtyPath = NULL; size_t i;
char *logfile = NULL; char *logfile = NULL;
int logfd = -1; int logfd = -1;
unsigned int nveths = 0; unsigned int nveths = 0;
@ -1674,26 +1678,34 @@ static int lxcVmStart(virConnectPtr conn,
return -1; return -1;
} }
/* open parent tty */ /* Here we open all the PTYs we need on the host OS side.
if (virFileOpenTty(&parentTty, &parentTtyPath, 1) < 0) { * The LXC controller will open the guest OS side PTYs
* and forward I/O between them.
*/
nttyFDs = vm->def->nconsoles;
if (VIR_ALLOC_N(ttyFDs, nttyFDs) < 0) {
virReportOOMError();
goto cleanup;
}
for (i = 0 ; i < vm->def->nconsoles ; i++)
ttyFDs[i] = -1;
for (i = 0 ; i < vm->def->nconsoles ; i++) {
char *ttyPath;
if (vm->def->consoles[i]->source.type != VIR_DOMAIN_CHR_TYPE_PTY) {
lxcError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Only PTY console types are supported"));
goto cleanup;
}
if (virFileOpenTty(&ttyFDs[i], &ttyPath, 1) < 0) {
virReportSystemError(errno, "%s", virReportSystemError(errno, "%s",
_("Failed to allocate tty")); _("Failed to allocate tty"));
goto cleanup; goto cleanup;
} }
if (vm->def->nconsoles) {
if (vm->def->nconsoles > 1) { VIR_FREE(vm->def->consoles[i]->source.data.file.path);
lxcError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", vm->def->consoles[i]->source.data.file.path = ttyPath;
_("Only one console supported"));
goto cleanup;
}
if (vm->def->consoles[0]->source.type == VIR_DOMAIN_CHR_TYPE_PTY) {
VIR_FREE(vm->def->consoles[0]->source.data.file.path);
vm->def->consoles[0]->source.data.file.path = parentTtyPath;
} else {
VIR_FREE(parentTtyPath);
}
} else {
VIR_FREE(parentTtyPath);
} }
if (lxcSetupInterfaces(conn, vm->def, &nveths, &veths) != 0) if (lxcSetupInterfaces(conn, vm->def, &nveths, &veths) != 0)
@ -1720,7 +1732,8 @@ static int lxcVmStart(virConnectPtr conn,
if (!(cmd = lxcBuildControllerCmd(driver, if (!(cmd = lxcBuildControllerCmd(driver,
vm, vm,
nveths, veths, nveths, veths,
parentTty, logfd, handshakefds[1]))) ttyFDs, nttyFDs,
logfd, handshakefds[1])))
goto cleanup; goto cleanup;
/* Log timestamp */ /* Log timestamp */
@ -1822,7 +1835,8 @@ cleanup:
VIR_FORCE_CLOSE(priv->monitor); VIR_FORCE_CLOSE(priv->monitor);
virDomainConfVMNWFilterTeardown(vm); virDomainConfVMNWFilterTeardown(vm);
} }
VIR_FORCE_CLOSE(parentTty); for (i = 0 ; i < nttyFDs ; i++)
VIR_FORCE_CLOSE(ttyFDs[i]);
VIR_FORCE_CLOSE(handshakefds[0]); VIR_FORCE_CLOSE(handshakefds[0]);
VIR_FORCE_CLOSE(handshakefds[1]); VIR_FORCE_CLOSE(handshakefds[1]);
VIR_FREE(logfile); VIR_FREE(logfile);