-
Notifications
You must be signed in to change notification settings - Fork 128
/
Copy pathctr_stdio.c
142 lines (121 loc) · 3.94 KB
/
ctr_stdio.c
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
#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 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))
;
}
if (mainfd_stderr != -1) {
g_unix_set_fd_nonblocking(mainfd_stderr, TRUE, NULL);
while (read_stdio(mainfd_stderr, STDERR_PIPE, NULL))
;
}
return;
}
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) {
nwarnf("stdio_input read failed %s", strerror(errno));
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 written;
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;
}