forked from containers/conmon
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathctr_stdio.c
More file actions
165 lines (143 loc) · 4.72 KB
/
ctr_stdio.c
File metadata and controls
165 lines (143 loc) · 4.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include "ctr_stdio.h"
#include "globals.h"
#include "config.h"
#include "conn_sock.h"
#include "utils.h"
#include "ctr_logging.h"
#include "cli.h"
#include <stdbool.h>
#include <sys/socket.h>
static gboolean tty_hup_timeout_scheduled = false;
static bool read_stdio(int fd, stdpipe_t pipe, gboolean *eof);
static void drain_log_buffers(stdpipe_t pipe);
static gboolean tty_hup_timeout_cb(G_GNUC_UNUSED gpointer user_data);
gboolean stdio_cb(int fd, GIOCondition condition, gpointer user_data)
{
stdpipe_t pipe = GPOINTER_TO_INT(user_data);
gboolean read_eof = FALSE;
gboolean has_input = (condition & G_IO_IN) != 0;
gboolean 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 main this means there
are no open workers 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;
}
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)) {
/* There exists a case that the process has already exited
* and we know about it (because we checked our child processes)
* but we needed to run the main_loop to catch all the rest of the output
* (specifically, when we are exec, but not terminal)
* In this case, after both the stderr and stdout pipes have closed
* we should quit the loop. Otherwise, conmon will hang forever
* waiting for container_exit_cb that will never be called.
*/
if (pipe == STDOUT_PIPE) {
mainfd_stdout = -1;
if (container_status >= 0 && mainfd_stderr < 0) {
g_main_loop_quit(main_loop);
}
}
if (pipe == STDERR_PIPE) {
mainfd_stderr = -1;
if (container_status >= 0 && mainfd_stdout < 0) {
g_main_loop_quit(main_loop);
}
}
close(fd);
return G_SOURCE_REMOVE;
}
return G_SOURCE_CONTINUE;
}
void drain_stdio()
{
if (mainfd_stdout != -1) {
g_unix_set_fd_nonblocking(mainfd_stdout, TRUE, NULL);
while (read_stdio(mainfd_stdout, STDOUT_PIPE, NULL))
;
}
drain_log_buffers(STDOUT_PIPE);
if (mainfd_stderr != -1) {
g_unix_set_fd_nonblocking(mainfd_stderr, TRUE, NULL);
while (read_stdio(mainfd_stderr, STDERR_PIPE, NULL))
;
}
drain_log_buffers(STDERR_PIPE);
}
/* the journald log writer is buffering partial lines so that whole log lines are emitted
* to the journal as a unit. this flushes those buffers */
static void drain_log_buffers(stdpipe_t pipe)
{
/* We pass a single byte buffer because write_to_logs expects that there is one
byte of capacity beyond the buflen that we specify */
char buf[1];
write_to_logs(pipe, buf, 0);
}
static bool read_stdio(int fd, stdpipe_t pipe, gboolean *eof)
{
/* We use two extra bytes. One at the start, which we don't read into, instead
we use that for marking the pipe when we write to the attached socket.
One at the end to guarantee a null-terminated buffer for journald logging*/
char real_buf[STDIO_BUF_SIZE + 2];
char *buf = real_buf + 1;
ssize_t num_read = 0;
if (eof)
*eof = false;
num_read = read(fd, buf, STDIO_BUF_SIZE);
if (num_read == 0) {
if (eof)
*eof = true;
return false;
} else if (num_read < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// Non-blocking mode - no data available, stop draining
return false;
}
/* Ignore EIO if fd is a tty, since this can happen when the tty is closed
while we are reading from it. */
if (errno == EIO && isatty(fd)) {
if (eof)
*eof = true;
return false;
}
nwarnf("stdio_input read failed: %m");
return false;
} else {
// Always null terminate the buffer, just in case.
buf[num_read] = '\0';
bool written = write_to_logs(pipe, buf, num_read);
if (!written)
return false;
real_buf[0] = pipe;
write_back_to_remote_consoles(real_buf, num_read + 1);
return true;
}
}
static gboolean tty_hup_timeout_cb(G_GNUC_UNUSED gpointer user_data)
{
tty_hup_timeout_scheduled = false;
g_unix_fd_add(mainfd_stdout, G_IO_IN, stdio_cb, GINT_TO_POINTER(STDOUT_PIPE));
return G_SOURCE_REMOVE;
}