@@ -42,24 +42,31 @@ static _Noreturn void handle_vchan_error(const char *op)
42
42
/*
43
43
* Closing the file descriptors:
44
44
*
45
- * - If this is a single socket used for both stdin and stdout, call shutdown()
46
- * to indicate that no more data will be sent or received.
47
- * - Otherwise, restore the blocking status of the FD.
45
+ * - If this file descriptor was inherited _and_ is not used for both stdin
46
+ * and stdout, restore the blocking status of the FD and then close it.
47
+ * - Otherwise, call shutdown() to indicate that no more data will be sent or
48
+ * received in the given direction.
48
49
*/
49
- static void close_stdio (int fd , int other_fd , int direction ) {
50
+ static bool close_stdio (int fd , int direction , bool single_socket ) {
50
51
if (fd == -1 )
51
- return ;
52
- if (fd == other_fd ) {
53
- /* Do not close the FD, as it is still in use. */
54
- /* ENOTCONN can happen with TCP and is harmless */
55
- if (shutdown (fd , direction ) == -1 && errno != ENOTSOCK && errno != ENOTCONN )
56
- PERROR ("shutdown" );
57
- return ;
58
- }
59
-
60
- if (fd < 3 )
52
+ return false;
53
+ if (single_socket || fd > 2 ) {
54
+ if (shutdown (fd , direction ) == -1 ) {
55
+ if (errno == ENOTSOCK && !single_socket ) {
56
+ close (fd );
57
+ return true;
58
+ } else if (errno != ENOTCONN ) {
59
+ // ENOTCONN can happen with TCP and is harmless
60
+ PERROR ("shutdown" );
61
+ }
62
+ }
63
+ return false;
64
+ } else {
65
+ /* Close the file descriptor */
61
66
set_block (fd );
62
- close (fd );
67
+ close (fd );
68
+ return true;
69
+ }
63
70
}
64
71
65
72
static void close_stderr (int fd ) {
@@ -96,6 +103,10 @@ int process_io(const struct process_io_request *req) {
96
103
97
104
pid_t local_status = -1 ;
98
105
pid_t remote_status = -1 ;
106
+ /* Saved version of stdin_fd. If we get SIGHUP,
107
+ * this replaces stdout_fd. Set to -1 only if stdin_fd
108
+ * is closed, not merely shut down. */
109
+ int saved_stdin_fd = stdin_fd ;
99
110
int stdout_msg_type = is_service ? MSG_DATA_STDOUT : MSG_DATA_STDIN ;
100
111
int ret ;
101
112
struct pollfd fds [FD_NUM ];
@@ -116,22 +127,34 @@ int process_io(const struct process_io_request *req) {
116
127
sigprocmask (SIG_BLOCK , & pollmask , NULL );
117
128
sigemptyset (& pollmask );
118
129
130
+ /* Invariants:
131
+ *
132
+ * - if stdin_fd == stdout_fd, then use_stdio_socket is true.
133
+ * - if use_stdio_socket is true, then either:
134
+ * - stdin_fd == stdout_fd right now.
135
+ * - stdin_fd == stdout_fd at some point in the past,
136
+ * and one of them is currently -1.
137
+ */
138
+ bool use_stdio_socket = stdin_fd == stdout_fd ;
119
139
set_nonblock (stdin_fd );
120
- if (stdout_fd != stdin_fd )
140
+ if (! use_stdio_socket )
121
141
set_nonblock (stdout_fd );
122
142
if (stderr_fd >= 0 ) {
123
143
assert (is_service ); // if this is a client, stderr_fd is *always* -1
124
144
set_nonblock (stderr_fd );
125
145
}
126
146
127
147
/* Convenience macros that eliminate a ton of error-prone boilerplate */
128
- #define close_stdin () do { \
129
- close_stdio(stdin_fd, stdout_fd, SHUT_WR); \
130
- stdin_fd = -1; \
148
+ #define close_stdin () do { \
149
+ /* if stdin_fd was actually closed, set saved_stdin_fd \
150
+ * to -1 to avoid use-after-close */ \
151
+ if (close_stdio(stdin_fd, SHUT_WR, use_stdio_socket)) \
152
+ saved_stdin_fd = -1; \
153
+ stdin_fd = -1; \
131
154
} while (0)
132
- #define close_stdout () do { \
133
- close_stdio(stdout_fd, stdin_fd, SHUT_RD ); \
134
- stdout_fd = -1; \
155
+ #define close_stdout () do { \
156
+ close_stdio(stdout_fd, SHUT_RD, use_stdio_socket ); \
157
+ stdout_fd = -1; \
135
158
} while (0)
136
159
#pragma GCC poison close_stdio
137
160
@@ -187,9 +210,10 @@ int process_io(const struct process_io_request *req) {
187
210
}
188
211
189
212
/* child signaled desire to use single socket for both stdin and stdout */
190
- if (sigusr1 && * sigusr1 && stdin_fd != stdout_fd ) {
213
+ if (sigusr1 && * sigusr1 && ! use_stdio_socket ) {
191
214
close_stdout ();
192
- stdout_fd = stdin_fd ;
215
+ stdout_fd = saved_stdin_fd ;
216
+ use_stdio_socket = true;
193
217
* sigusr1 = 0 ;
194
218
}
195
219
0 commit comments