conmon: Handle runc exiting before passing terminal fd

We don't want to block on accepting the terminal fd, because then
we can't detect if runc died before calling out to pass the terminal
fd. To handle this we spin the glib mainloop listening to both the
terminal accept fd and a child pid watch.

Signed-off-by: Alexander Larsson <alexl@redhat.com>
This commit is contained in:
Alexander Larsson 2017-06-08 17:57:15 +02:00
parent 4494d82cfe
commit fcac68bf27

View file

@ -437,6 +437,8 @@ static char *escape_json_string(const char *str)
/* Global state */ /* Global state */
static int runtime_status = -1;
static int masterfd_stdout = -1; static int masterfd_stdout = -1;
static int masterfd_stderr = -1; static int masterfd_stderr = -1;
static int num_stdio_fds = 0; static int num_stdio_fds = 0;
@ -633,9 +635,49 @@ static gboolean ctrl_cb(int fd, GIOCondition condition, G_GNUC_UNUSED gpointer u
return G_SOURCE_CONTINUE; return G_SOURCE_CONTINUE;
} }
static gboolean terminal_accept_cb(int fd, G_GNUC_UNUSED GIOCondition condition, G_GNUC_UNUSED gpointer user_data)
{
const char *csname = user_data;
struct file_t console;
int connfd = -1;
ninfo("about to accept from csfd: %d", fd);
connfd = accept4(fd, NULL, NULL, SOCK_CLOEXEC);
if (connfd < 0)
pexit("Failed to accept console-socket connection");
/* Not accepting anything else. */
close(fd);
unlink(csname);
/* We exit if this fails. */
ninfo("about to recvfd from connfd: %d", connfd);
console = recvfd(connfd);
ninfo("console = {.name = '%s'; .fd = %d}", console.name, console.fd);
free(console.name);
/* We only have a single fd for both pipes, so we just treat it as
* stdout. stderr is ignored. */
masterfd_stdout = console.fd;
masterfd_stderr = -1;
/* Clean up everything */
close(connfd);
return G_SOURCE_CONTINUE;
}
static void
runtime_exit_cb (GPid pid, int status, G_GNUC_UNUSED gpointer user_data)
{
runtime_status = status;
g_main_loop_quit (main_loop);
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ret, runtime_status; int ret;
char cwd[PATH_MAX]; char cwd[PATH_MAX];
char default_pid_file[PATH_MAX]; char default_pid_file[PATH_MAX];
char attach_sock_path[PATH_MAX]; char attach_sock_path[PATH_MAX];
@ -854,50 +896,22 @@ int main(int argc, char *argv[])
close(slavefd_stdout); close(slavefd_stdout);
close(slavefd_stderr); close(slavefd_stderr);
/* Get the console fd. */
/*
* FIXME: If runc fails to start a container, we won't bail because we're
* busy waiting for requests. The solution probably involves
* epoll(2) and a signalfd(2). This causes a lot of issues.
*/
if (terminal) {
struct file_t console;
int connfd = -1;
ninfo("about to accept from csfd: %d", csfd);
connfd = accept4(csfd, NULL, NULL, SOCK_CLOEXEC);
if (connfd < 0)
pexit("Failed to accept console-socket connection");
/* Not accepting anything else. */
close(csfd);
unlink(csname);
/* We exit if this fails. */
ninfo("about to recvfd from connfd: %d", connfd);
console = recvfd(connfd);
ninfo("console = {.name = '%s'; .fd = %d}", console.name, console.fd);
free(console.name);
/* We only have a single fd for both pipes, so we just treat it as
* stdout. stderr is ignored. */
masterfd_stdout = console.fd;
masterfd_stderr = -1;
/* Clean up everything */
close(connfd);
}
ninfo("about to waitpid: %d", create_pid); ninfo("about to waitpid: %d", create_pid);
if (terminal) {
/* Wait for our create child to exit with the return code. */ guint terminal_watch = g_unix_fd_add (csfd, G_IO_IN, terminal_accept_cb, csname);
if (waitpid(create_pid, &runtime_status, 0) < 0) { g_child_watch_add (create_pid, runtime_exit_cb, NULL);
int old_errno = errno; g_main_loop_run (main_loop);
kill(create_pid, SIGKILL); g_source_remove (terminal_watch);
errno = old_errno; } else {
pexit("Failed to wait for `runtime %s`", exec ? "exec" : "create"); /* Wait for our create child to exit with the return code. */
if (waitpid(create_pid, &runtime_status, 0) < 0) {
int old_errno = errno;
kill(create_pid, SIGKILL);
errno = old_errno;
pexit("Failed to wait for `runtime %s`", exec ? "exec" : "create");
}
} }
if (!WIFEXITED(runtime_status) || WEXITSTATUS(runtime_status) != 0) { if (!WIFEXITED(runtime_status) || WEXITSTATUS(runtime_status) != 0) {
if (sync_pipe_fd > 0 && !exec) { if (sync_pipe_fd > 0 && !exec) {
if (terminal) { if (terminal) {
@ -932,6 +946,9 @@ int main(int argc, char *argv[])
nexit("Failed to create container: exit status %d", WEXITSTATUS(runtime_status)); nexit("Failed to create container: exit status %d", WEXITSTATUS(runtime_status));
} }
if (terminal && masterfd_stdout == -1)
pexit("Runtime did not set up terminal");
/* Read the pid so we can wait for the process to exit */ /* Read the pid so we can wait for the process to exit */
g_file_get_contents(pid_file, &contents, NULL, &err); g_file_get_contents(pid_file, &contents, NULL, &err);
if (err) { if (err) {