Merge pull request #634 from alexlarsson/conmon-no-numstdio-fds
conmon: Change how we detect container exit
This commit is contained in:
commit
39a5203a1d
1 changed files with 112 additions and 53 deletions
165
conmon/conmon.c
165
conmon/conmon.c
|
@ -468,11 +468,11 @@ static void end_argv(GPtrArray *argv_array)
|
||||||
/* Global state */
|
/* Global state */
|
||||||
|
|
||||||
static int runtime_status = -1;
|
static int runtime_status = -1;
|
||||||
|
static int container_status = -1;
|
||||||
|
|
||||||
static int masterfd_stdin = -1;
|
static int masterfd_stdin = -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;
|
|
||||||
|
|
||||||
/* Used for attach */
|
/* Used for attach */
|
||||||
static int conn_sock = -1;
|
static int conn_sock = -1;
|
||||||
|
@ -504,52 +504,103 @@ static void conn_sock_shutdown(int how)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean stdio_cb(int fd, GIOCondition condition, gpointer user_data)
|
static gboolean stdio_cb(int fd, GIOCondition condition, gpointer user_data);
|
||||||
|
|
||||||
|
static gboolean tty_hup_timeout_scheduled = false;
|
||||||
|
|
||||||
|
static gboolean tty_hup_timeout_cb (G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
tty_hup_timeout_scheduled = false;
|
||||||
|
g_unix_fd_add (masterfd_stdout, G_IO_IN, stdio_cb, GINT_TO_POINTER(STDOUT_PIPE));
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool read_stdio(int fd, stdpipe_t pipe, bool *eof)
|
||||||
{
|
{
|
||||||
#define STDIO_BUF_SIZE 8192 /* Sync with redirectResponseToOutputStreams() */
|
#define STDIO_BUF_SIZE 8192 /* Sync with redirectResponseToOutputStreams() */
|
||||||
/* We use one extra byte at the start, which we don't read into, instead
|
/* We use one extra byte at the start, which we don't read into, instead
|
||||||
we use that for marking the pipe when we write to the attached socket */
|
we use that for marking the pipe when we write to the attached socket */
|
||||||
char real_buf[STDIO_BUF_SIZE + 1];
|
char real_buf[STDIO_BUF_SIZE + 1];
|
||||||
char *buf = real_buf + 1;
|
char *buf = real_buf + 1;
|
||||||
stdpipe_t pipe = GPOINTER_TO_INT(user_data);
|
|
||||||
ssize_t num_read = 0;
|
ssize_t num_read = 0;
|
||||||
|
|
||||||
if ((condition & G_IO_IN) != 0) {
|
if (eof)
|
||||||
num_read = read(fd, buf, BUF_SIZE);
|
*eof = false;
|
||||||
if (num_read < 0) {
|
|
||||||
nwarn("stdio_input read failed %s", strerror(errno));
|
num_read = read(fd, buf, STDIO_BUF_SIZE);
|
||||||
|
if (num_read == 0) {
|
||||||
|
if (eof)
|
||||||
|
*eof = true;
|
||||||
|
return false;
|
||||||
|
} else if (num_read < 0) {
|
||||||
|
nwarn("stdio_input read failed %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (write_k8s_log(log_fd, pipe, buf, num_read) < 0) {
|
||||||
|
nwarn("write_k8s_log failed");
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_read > 0) {
|
real_buf[0] = pipe;
|
||||||
if (write_k8s_log(log_fd, pipe, buf, num_read) < 0) {
|
if (conn_sock_writable && write_all(conn_sock, real_buf, num_read+1) < 0) {
|
||||||
nwarn("write_k8s_log failed");
|
nwarn("Failed to write to socket");
|
||||||
return G_SOURCE_CONTINUE;
|
conn_sock_shutdown(SHUT_WR);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
real_buf[0] = pipe;
|
static gboolean stdio_cb(int fd, GIOCondition condition, gpointer user_data)
|
||||||
if (conn_sock_writable && write_all(conn_sock, real_buf, num_read+1) < 0) {
|
{
|
||||||
nwarn("Failed to write to socket");
|
stdpipe_t pipe = GPOINTER_TO_INT(user_data);
|
||||||
conn_sock_shutdown(SHUT_WR);
|
bool read_eof = false;
|
||||||
}
|
bool has_input = (condition & G_IO_IN) != 0;
|
||||||
|
bool has_hup = (condition & G_IO_HUP) != 0;
|
||||||
|
|
||||||
|
/* When we get here, condition can be G_IO_IN and/or G_IO_HUP.
|
||||||
|
IN means there is some data to read.
|
||||||
|
HUP means the other side closed the fd. In the case of a pine
|
||||||
|
this in final, and we will never get more data. However, in the
|
||||||
|
terminal case this just means that nobody has the terminal
|
||||||
|
open at this point, and this can be change whenever someone
|
||||||
|
opens the tty */
|
||||||
|
|
||||||
|
/* Read any data before handling hup */
|
||||||
|
if (has_input) {
|
||||||
|
read_stdio(fd, pipe, &read_eof);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_hup && opt_terminal && pipe == STDOUT_PIPE) {
|
||||||
|
/* We got a HUP from the terminal master this means there
|
||||||
|
are no open slaves ptys atm, and we will get a lot
|
||||||
|
of wakeups until we have one, switch to polling
|
||||||
|
mode. */
|
||||||
|
|
||||||
|
/* If we read some data this cycle, wait one more, maybe there
|
||||||
|
is more in the buffer before we handle the hup */
|
||||||
|
if (has_input && !read_eof) {
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tty_hup_timeout_scheduled) {
|
||||||
|
g_timeout_add (100, tty_hup_timeout_cb, NULL);
|
||||||
|
}
|
||||||
|
tty_hup_timeout_scheduled = true;
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End of input */
|
if (read_eof || (has_hup && !has_input)) {
|
||||||
if (pipe == STDOUT_PIPE)
|
/* End of input */
|
||||||
masterfd_stdout = -1;
|
if (pipe == STDOUT_PIPE)
|
||||||
if (pipe == STDERR_PIPE)
|
masterfd_stdout = -1;
|
||||||
masterfd_stderr = -1;
|
if (pipe == STDERR_PIPE)
|
||||||
num_stdio_fds--;
|
masterfd_stderr = -1;
|
||||||
if (num_stdio_fds == 0) {
|
|
||||||
ninfo ("No more stdio, killing main loop");
|
close (fd);
|
||||||
g_main_loop_quit (main_loop);
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
close (fd);
|
return G_SOURCE_CONTINUE;
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean timeout_cb (G_GNUC_UNUSED gpointer user_data)
|
static gboolean timeout_cb (G_GNUC_UNUSED gpointer user_data)
|
||||||
|
@ -755,6 +806,14 @@ runtime_exit_cb (G_GNUC_UNUSED GPid pid, int status, G_GNUC_UNUSED gpointer user
|
||||||
g_main_loop_quit (main_loop);
|
g_main_loop_quit (main_loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
container_exit_cb (G_GNUC_UNUSED GPid pid, int status, G_GNUC_UNUSED gpointer user_data)
|
||||||
|
{
|
||||||
|
ninfo("container %d exited with status %d\n", pid, status);
|
||||||
|
container_status = status;
|
||||||
|
g_main_loop_quit (main_loop);
|
||||||
|
}
|
||||||
|
|
||||||
static void write_sync_fd(int sync_pipe_fd, int res, const char *message)
|
static void write_sync_fd(int sync_pipe_fd, int res, const char *message)
|
||||||
{
|
{
|
||||||
_cleanup_free_ char *escaped_message = NULL;
|
_cleanup_free_ char *escaped_message = NULL;
|
||||||
|
@ -893,10 +952,10 @@ static void setup_terminal_control_fifo()
|
||||||
ninfo("terminal_ctrl_fd: %d", terminal_ctrl_fd);
|
ninfo("terminal_ctrl_fd: %d", terminal_ctrl_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setup_oom_handling(int cpid)
|
static void setup_oom_handling(int container_pid)
|
||||||
{
|
{
|
||||||
/* Setup OOM notification for container process */
|
/* Setup OOM notification for container process */
|
||||||
_cleanup_free_ char *memory_cgroup_path = process_cgroup_subsystem_path(cpid, "memory");
|
_cleanup_free_ char *memory_cgroup_path = process_cgroup_subsystem_path(container_pid, "memory");
|
||||||
_cleanup_close_ int cfd = -1;
|
_cleanup_close_ int cfd = -1;
|
||||||
int ofd = -1; /* Not closed */
|
int ofd = -1; /* Not closed */
|
||||||
if (!memory_cgroup_path) {
|
if (!memory_cgroup_path) {
|
||||||
|
@ -932,9 +991,8 @@ int main(int argc, char *argv[])
|
||||||
_cleanup_free_ char *csname = NULL;
|
_cleanup_free_ char *csname = NULL;
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
_cleanup_free_ char *contents = NULL;
|
_cleanup_free_ char *contents = NULL;
|
||||||
int cpid = -1;
|
int container_pid = -1;
|
||||||
int status;
|
pid_t main_pid, create_pid;
|
||||||
pid_t pid, main_pid, create_pid;
|
|
||||||
/* Used for !terminal cases. */
|
/* Used for !terminal cases. */
|
||||||
int slavefd_stdin = -1;
|
int slavefd_stdin = -1;
|
||||||
int slavefd_stdout = -1;
|
int slavefd_stdout = -1;
|
||||||
|
@ -1205,8 +1263,8 @@ int main(int argc, char *argv[])
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
cpid = atoi(contents);
|
container_pid = atoi(contents);
|
||||||
ninfo("container PID: %d", cpid);
|
ninfo("container PID: %d", container_pid);
|
||||||
|
|
||||||
/* Setup endpoint for attach */
|
/* Setup endpoint for attach */
|
||||||
_cleanup_free_ char *attach_symlink_dir_path = NULL;
|
_cleanup_free_ char *attach_symlink_dir_path = NULL;
|
||||||
|
@ -1220,45 +1278,46 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
/* Send the container pid back to parent */
|
/* Send the container pid back to parent */
|
||||||
if (!opt_exec) {
|
if (!opt_exec) {
|
||||||
write_sync_fd(sync_pipe_fd, cpid, NULL);
|
write_sync_fd(sync_pipe_fd, container_pid, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_oom_handling(cpid);
|
setup_oom_handling(container_pid);
|
||||||
|
|
||||||
if (masterfd_stdout >= 0) {
|
if (masterfd_stdout >= 0) {
|
||||||
g_unix_fd_add (masterfd_stdout, G_IO_IN, stdio_cb, GINT_TO_POINTER(STDOUT_PIPE));
|
g_unix_fd_add (masterfd_stdout, G_IO_IN, stdio_cb, GINT_TO_POINTER(STDOUT_PIPE));
|
||||||
num_stdio_fds++;
|
|
||||||
}
|
}
|
||||||
if (masterfd_stderr >= 0) {
|
if (masterfd_stderr >= 0) {
|
||||||
g_unix_fd_add (masterfd_stderr, G_IO_IN, stdio_cb, GINT_TO_POINTER(STDERR_PIPE));
|
g_unix_fd_add (masterfd_stderr, G_IO_IN, stdio_cb, GINT_TO_POINTER(STDERR_PIPE));
|
||||||
num_stdio_fds++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt_timeout > 0) {
|
if (opt_timeout > 0) {
|
||||||
g_timeout_add_seconds (opt_timeout, timeout_cb, NULL);
|
g_timeout_add_seconds (opt_timeout, timeout_cb, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_child_watch_add (container_pid, container_exit_cb, NULL);
|
||||||
|
|
||||||
g_main_loop_run (main_loop);
|
g_main_loop_run (main_loop);
|
||||||
|
|
||||||
|
/* Drain stdout and stderr */
|
||||||
|
if (masterfd_stdout != -1) {
|
||||||
|
g_unix_set_fd_nonblocking(masterfd_stdout, TRUE, NULL);
|
||||||
|
while (read_stdio(masterfd_stdout, STDOUT_PIPE, NULL))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
if (masterfd_stderr != -1) {
|
||||||
|
g_unix_set_fd_nonblocking(masterfd_stderr, TRUE, NULL);
|
||||||
|
while (read_stdio(masterfd_stderr, STDERR_PIPE, NULL))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
int exit_status = -1;
|
int exit_status = -1;
|
||||||
const char *exit_message = NULL;
|
const char *exit_message = NULL;
|
||||||
|
|
||||||
if (timed_out) {
|
if (timed_out) {
|
||||||
kill(cpid, SIGKILL);
|
kill(container_pid, SIGKILL);
|
||||||
exit_message = "command timed out";
|
exit_message = "command timed out";
|
||||||
} else {
|
} else {
|
||||||
/* Wait for the container process and record its exit code */
|
exit_status = WEXITSTATUS(container_status);
|
||||||
while ((pid = waitpid(-1, &status, 0)) > 0) {
|
|
||||||
int child_exit_status = WEXITSTATUS(status);
|
|
||||||
|
|
||||||
printf("PID %d exited with status %d\n", pid, child_exit_status);
|
|
||||||
if (pid == cpid) {
|
|
||||||
exit_status = child_exit_status;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pid == -1) {
|
|
||||||
exit_message = "runtime didn't create a child";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opt_exec) {
|
if (!opt_exec) {
|
||||||
|
|
Loading…
Reference in a new issue