Add a simple handshake with the lxc_controller process so we can detect
process startup failures. We do this by adding a new --handshake cli arg
to lxc_controller for passing a file descriptor. If the process fails to
launch, we scrape all output from the logfile and report it to the user.
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/lxc/lxc_container.c | 2 +-
src/lxc/lxc_container.h | 1 +
src/lxc/lxc_controller.c | 33 +++++++++++++-
src/lxc/lxc_driver.c | 107 +++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 137 insertions(+), 6 deletions(-)
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c
index 00add94..ff90842 100644
--- a/src/lxc/lxc_container.c
+++ b/src/lxc/lxc_container.c
@@ -213,7 +213,7 @@ error_out:
*
* Returns 0 on success or -1 in case of error
*/
-static int lxcContainerWaitForContinue(int control)
+int lxcContainerWaitForContinue(int control)
{
lxc_message_t msg;
int readLen;
diff --git a/src/lxc/lxc_container.h b/src/lxc/lxc_container.h
index 5e08d45..a3e457e 100644
--- a/src/lxc/lxc_container.h
+++ b/src/lxc/lxc_container.h
@@ -46,6 +46,7 @@ enum {
# define LXC_DEV_MAJ_PTY 136
int lxcContainerSendContinue(int control);
+int lxcContainerWaitForContinue(int control);
int lxcContainerStart(virDomainDefPtr def,
unsigned int nveths,
diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index cef4b58..5bf8ee3 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -612,7 +612,8 @@ lxcControllerRun(virDomainDefPtr def,
char **veths,
int monitor,
int client,
- int appPty)
+ int appPty,
+ int handshakefd)
{
int rc = -1;
int control[2] = { -1, -1};
@@ -742,6 +743,13 @@ lxcControllerRun(virDomainDefPtr def,
if (lxcControllerClearCapabilities() < 0)
goto cleanup;
+ if (lxcContainerSendContinue(handshakefd) < 0) {
+ virReportSystemError(errno, "%s",
+ _("error sending continue signal to parent"));
+ goto cleanup;
+ }
+ VIR_FORCE_CLOSE(handshakefd);
+
rc = lxcControllerMain(monitor, client, appPty, containerPty, container);
cleanup:
@@ -751,6 +759,7 @@ cleanup:
VIR_FORCE_CLOSE(control[1]);
VIR_FREE(containerPtyPath);
VIR_FORCE_CLOSE(containerPty);
+ VIR_FORCE_CLOSE(handshakefd);
if (container > 1) {
int status;
@@ -774,6 +783,7 @@ int main(int argc, char *argv[])
char **veths = NULL;
int monitor = -1;
int appPty = -1;
+ int handshakefd = -1;
int bg = 0;
virCapsPtr caps = NULL;
virDomainDefPtr def = NULL;
@@ -784,6 +794,7 @@ int main(int argc, char *argv[])
{ "name", 1, NULL, 'n' },
{ "veth", 1, NULL, 'v' },
{ "console", 1, NULL, 'c' },
+ { "handshakefd", 1, NULL, 's' },
{ "help", 0, NULL, 'h' },
{ 0, 0, 0, 0 },
};
@@ -798,7 +809,7 @@ int main(int argc, char *argv[])
while (1) {
int c;
- c = getopt_long(argc, argv, "dn:v:m:c:h",
+ c = getopt_long(argc, argv, "dn:v:m:c:s:h",
options, NULL);
if (c == -1)
@@ -834,6 +845,14 @@ int main(int argc, char *argv[])
}
break;
+ case 's':
+ if (virStrToLong_i(optarg, NULL, 10, &handshakefd) < 0) {
+ fprintf(stderr, "malformed --handshakefd argument
'%s'",
+ optarg);
+ goto cleanup;
+ }
+ break;
+
case 'h':
case '?':
fprintf(stderr, "\n");
@@ -845,6 +864,7 @@ int main(int argc, char *argv[])
fprintf(stderr, " -n NAME, --name NAME\n");
fprintf(stderr, " -c FD, --console FD\n");
fprintf(stderr, " -v VETH, --veth VETH\n");
+ fprintf(stderr, " -s FD, --handshakefd FD\n");
fprintf(stderr, " -h, --help\n");
fprintf(stderr, "\n");
goto cleanup;
@@ -862,6 +882,12 @@ int main(int argc, char *argv[])
goto cleanup;
}
+ if (handshakefd < 0) {
+ fprintf(stderr, "%s: missing --handshake argument for container
PTY\n",
+ argv[0]);
+ goto cleanup;
+ }
+
if (getuid() != 0) {
fprintf(stderr, "%s: must be run as the 'root' user\n",
argv[0]);
goto cleanup;
@@ -932,7 +958,8 @@ int main(int argc, char *argv[])
goto cleanup;
}
- rc = lxcControllerRun(def, nveths, veths, monitor, client, appPty);
+ rc = lxcControllerRun(def, nveths, veths, monitor, client, appPty,
+ handshakefd);
cleanup:
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index 99f94ff..755de70 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -1287,7 +1287,8 @@ lxcBuildControllerCmd(lxc_driver_t *driver,
int nveths,
char **veths,
int appPty,
- int logfile)
+ int logfile,
+ int handshakefd)
{
int i;
char *filterstr;
@@ -1332,6 +1333,8 @@ lxcBuildControllerCmd(lxc_driver_t *driver,
virCommandAddArgList(cmd, "--name", vm->def->name,
"--console", NULL);
virCommandAddArgFormat(cmd, "%d", appPty);
+ virCommandAddArg(cmd, "--handshake");
+ virCommandAddArgFormat(cmd, "%d", handshakefd);
virCommandAddArg(cmd, "--background");
for (i = 0 ; i < nveths ; i++) {
@@ -1355,6 +1358,7 @@ lxcBuildControllerCmd(lxc_driver_t *driver,
}
virCommandPreserveFD(cmd, appPty);
+ virCommandPreserveFD(cmd, handshakefd);
virCommandSetOutputFD(cmd, &logfile);
virCommandSetErrorFD(cmd, &logfile);
@@ -1364,6 +1368,78 @@ cleanup:
return NULL;
}
+static int
+lxcReadLogOutput(virDomainObjPtr vm,
+ char *logfile,
+ off_t pos,
+ char *buf,
+ size_t buflen)
+{
+ int fd;
+ off_t off;
+ int whence;
+ int got = 0, ret = -1;
+ int retries = 10;
+
+ if ((fd = open(logfile, O_RDONLY)) < 0) {
+ virReportSystemError(errno, _("failed to open logfile %s"),
+ logfile);
+ goto cleanup;
+ }
+
+ if (pos < 0) {
+ off = 0;
+ whence = SEEK_END;
+ } else {
+ off = pos;
+ whence = SEEK_SET;
+ }
+
+ if (lseek(fd, off, whence) < 0) {
+ if (whence == SEEK_END)
+ virReportSystemError(errno,
+ _("unable to seek to end of log for %s"),
+ logfile);
+ else
+ virReportSystemError(errno,
+ _("unable to seek to %lld from start for
%s"),
+ (long long)off, logfile);
+ goto cleanup;
+ }
+
+ while (retries) {
+ ssize_t bytes;
+ int isdead = 0;
+
+ if (kill(vm->pid, 0) == -1 && errno == ESRCH)
+ isdead = 1;
+
+ /* Any failures should be detected before we read the log, so we
+ * always have something useful to report on failure. */
+ bytes = saferead(fd, buf+got, buflen-got-1);
+ if (bytes < 0) {
+ virReportSystemError(errno, "%s",
+ _("Failure while reading guest log output"));
+ goto cleanup;
+ }
+
+ got += bytes;
+ buf[got] = '\0';
+
+ if ((got == buflen-1) || isdead) {
+ break;
+ }
+
+ usleep(100*1000);
+ retries--;
+ }
+
+
+ ret = got;
+cleanup:
+ VIR_FORCE_CLOSE(fd);
+ return ret;
+}
/**
* lxcVmStart:
@@ -1389,6 +1465,7 @@ static int lxcVmStart(virConnectPtr conn,
int logfd = -1;
unsigned int nveths = 0;
char **veths = NULL;
+ int handshakefds[2] = { -1, -1 };
off_t pos = -1;
char ebuf[1024];
char *timestamp;
@@ -1462,10 +1539,16 @@ static int lxcVmStart(virConnectPtr conn,
goto cleanup;
}
+ if (pipe(handshakefds) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Unable to create pipe"));
+ goto cleanup;
+ }
+
if (!(cmd = lxcBuildControllerCmd(driver,
vm,
nveths, veths,
- parentTty, logfd)))
+ parentTty, logfd, handshakefds[1])))
goto cleanup;
/* Log timestamp */
@@ -1489,6 +1572,12 @@ static int lxcVmStart(virConnectPtr conn,
if (virCommandRun(cmd, NULL) < 0)
goto cleanup;
+ if (VIR_CLOSE(handshakefds[1]) < 0) {
+ virReportSystemError(errno, "%s", _("could not close handshake
fd"));
+ goto cleanup;
+ }
+ handshakefds[1] = -1;
+
/* Connect to the controller as a client *first* because
* this will block until the child has written their
* pid file out to disk */
@@ -1506,6 +1595,18 @@ static int lxcVmStart(virConnectPtr conn,
vm->def->id = vm->pid;
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
+ if (lxcContainerWaitForContinue(handshakefds[0]) < 0) {
+ char out[1024];
+
+ if (!(lxcReadLogOutput(vm, logfile, pos, out, 1024) < 0)) {
+ lxcError(VIR_ERR_INTERNAL_ERROR,
+ _("guest failed to start: %s"), out);
+ }
+
+ lxcVmTerminate(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
+ goto cleanup;
+ }
+
if ((priv->monitorWatch = virEventAddHandle(
priv->monitor,
VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP,
@@ -1545,6 +1646,8 @@ cleanup:
if (rc != 0)
VIR_FORCE_CLOSE(priv->monitor);
VIR_FORCE_CLOSE(parentTty);
+ VIR_FORCE_CLOSE(handshakefds[0]);
+ VIR_FORCE_CLOSE(handshakefds[1]);
VIR_FREE(logfile);
return rc;
}
--
1.7.4.4