File: | build-scan/../src/shared/ptyfwd.c |
Warning: | line 411, column 32 Potential leak of memory pointed to by 'f' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
2 | ||||
3 | #include <errno(*__errno_location ()).h> | |||
4 | #include <limits.h> | |||
5 | #include <signal.h> | |||
6 | #include <stddef.h> | |||
7 | #include <stdint.h> | |||
8 | #include <stdlib.h> | |||
9 | #include <string.h> | |||
10 | #include <sys/epoll.h> | |||
11 | #include <sys/ioctl.h> | |||
12 | #include <sys/time.h> | |||
13 | #include <termios.h> | |||
14 | #include <unistd.h> | |||
15 | ||||
16 | #include "sd-event.h" | |||
17 | ||||
18 | #include "alloc-util.h" | |||
19 | #include "fd-util.h" | |||
20 | #include "log.h" | |||
21 | #include "macro.h" | |||
22 | #include "ptyfwd.h" | |||
23 | #include "time-util.h" | |||
24 | ||||
25 | struct PTYForward { | |||
26 | sd_event *event; | |||
27 | ||||
28 | int master; | |||
29 | ||||
30 | PTYForwardFlags flags; | |||
31 | ||||
32 | sd_event_source *stdin_event_source; | |||
33 | sd_event_source *stdout_event_source; | |||
34 | sd_event_source *master_event_source; | |||
35 | ||||
36 | sd_event_source *sigwinch_event_source; | |||
37 | ||||
38 | struct termios saved_stdin_attr; | |||
39 | struct termios saved_stdout_attr; | |||
40 | ||||
41 | bool_Bool saved_stdin:1; | |||
42 | bool_Bool saved_stdout:1; | |||
43 | ||||
44 | bool_Bool stdin_readable:1; | |||
45 | bool_Bool stdin_hangup:1; | |||
46 | bool_Bool stdout_writable:1; | |||
47 | bool_Bool stdout_hangup:1; | |||
48 | bool_Bool master_readable:1; | |||
49 | bool_Bool master_writable:1; | |||
50 | bool_Bool master_hangup:1; | |||
51 | ||||
52 | bool_Bool read_from_master:1; | |||
53 | ||||
54 | bool_Bool done:1; | |||
55 | bool_Bool drain:1; | |||
56 | ||||
57 | bool_Bool last_char_set:1; | |||
58 | char last_char; | |||
59 | ||||
60 | char in_buffer[LINE_MAX2048], out_buffer[LINE_MAX2048]; | |||
61 | size_t in_buffer_full, out_buffer_full; | |||
62 | ||||
63 | usec_t escape_timestamp; | |||
64 | unsigned escape_counter; | |||
65 | ||||
66 | PTYForwardHandler handler; | |||
67 | void *userdata; | |||
68 | }; | |||
69 | ||||
70 | #define ESCAPE_USEC(1*((usec_t) 1000000ULL)) (1*USEC_PER_SEC((usec_t) 1000000ULL)) | |||
71 | ||||
72 | static void pty_forward_disconnect(PTYForward *f) { | |||
73 | ||||
74 | if (f) { | |||
75 | f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); | |||
76 | f->stdout_event_source = sd_event_source_unref(f->stdout_event_source); | |||
77 | ||||
78 | f->master_event_source = sd_event_source_unref(f->master_event_source); | |||
79 | f->sigwinch_event_source = sd_event_source_unref(f->sigwinch_event_source); | |||
80 | f->event = sd_event_unref(f->event); | |||
81 | ||||
82 | if (f->saved_stdout) | |||
83 | tcsetattr(STDOUT_FILENO1, TCSANOW0, &f->saved_stdout_attr); | |||
84 | if (f->saved_stdin) | |||
85 | tcsetattr(STDIN_FILENO0, TCSANOW0, &f->saved_stdin_attr); | |||
86 | ||||
87 | f->saved_stdout = f->saved_stdin = false0; | |||
88 | } | |||
89 | ||||
90 | /* STDIN/STDOUT should not be nonblocking normally, so let's unconditionally reset it */ | |||
91 | fd_nonblock(STDIN_FILENO0, false0); | |||
92 | fd_nonblock(STDOUT_FILENO1, false0); | |||
93 | } | |||
94 | ||||
95 | static int pty_forward_done(PTYForward *f, int rcode) { | |||
96 | _cleanup_(sd_event_unrefp)__attribute__((cleanup(sd_event_unrefp))) sd_event *e = NULL((void*)0); | |||
97 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 97, __PRETTY_FUNCTION__ ); } while (0); | |||
98 | ||||
99 | if (f->done) | |||
100 | return 0; | |||
101 | ||||
102 | e = sd_event_ref(f->event); | |||
103 | ||||
104 | f->done = true1; | |||
105 | pty_forward_disconnect(f); | |||
106 | ||||
107 | if (f->handler) | |||
108 | return f->handler(f, rcode, f->userdata); | |||
109 | else | |||
110 | return sd_event_exit(e, rcode < 0 ? EXIT_FAILURE1 : rcode); | |||
111 | } | |||
112 | ||||
113 | static bool_Bool look_for_escape(PTYForward *f, const char *buffer, size_t n) { | |||
114 | const char *p; | |||
115 | ||||
116 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 116, __PRETTY_FUNCTION__ ); } while (0); | |||
117 | assert(buffer)do { if ((__builtin_expect(!!(!(buffer)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("buffer"), "../src/shared/ptyfwd.c", 117 , __PRETTY_FUNCTION__); } while (0); | |||
118 | assert(n > 0)do { if ((__builtin_expect(!!(!(n > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n > 0"), "../src/shared/ptyfwd.c", 118 , __PRETTY_FUNCTION__); } while (0); | |||
119 | ||||
120 | for (p = buffer; p < buffer + n; p++) { | |||
121 | ||||
122 | /* Check for ^] */ | |||
123 | if (*p == 0x1D) { | |||
124 | usec_t nw = now(CLOCK_MONOTONIC1); | |||
125 | ||||
126 | if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC(1*((usec_t) 1000000ULL))) { | |||
127 | f->escape_timestamp = nw; | |||
128 | f->escape_counter = 1; | |||
129 | } else { | |||
130 | (f->escape_counter)++; | |||
131 | ||||
132 | if (f->escape_counter >= 3) | |||
133 | return true1; | |||
134 | } | |||
135 | } else { | |||
136 | f->escape_timestamp = 0; | |||
137 | f->escape_counter = 0; | |||
138 | } | |||
139 | } | |||
140 | ||||
141 | return false0; | |||
142 | } | |||
143 | ||||
144 | static bool_Bool ignore_vhangup(PTYForward *f) { | |||
145 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 145, __PRETTY_FUNCTION__ ); } while (0); | |||
146 | ||||
147 | if (f->flags & PTY_FORWARD_IGNORE_VHANGUP) | |||
148 | return true1; | |||
149 | ||||
150 | if ((f->flags & PTY_FORWARD_IGNORE_INITIAL_VHANGUP) && !f->read_from_master) | |||
151 | return true1; | |||
152 | ||||
153 | return false0; | |||
154 | } | |||
155 | ||||
156 | static bool_Bool drained(PTYForward *f) { | |||
157 | int q = 0; | |||
158 | ||||
159 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 159, __PRETTY_FUNCTION__ ); } while (0); | |||
160 | ||||
161 | if (f->out_buffer_full > 0) | |||
162 | return false0; | |||
163 | ||||
164 | if (f->master_readable) | |||
165 | return false0; | |||
166 | ||||
167 | if (ioctl(f->master, TIOCINQ0x541B, &q) < 0) | |||
168 | log_debug_errno(errno, "TIOCINQ failed on master: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/ptyfwd.c", 168, __func__, "TIOCINQ failed on master: %m") : -abs(_e); }); | |||
169 | else if (q > 0) | |||
170 | return false0; | |||
171 | ||||
172 | if (ioctl(f->master, TIOCOUTQ0x5411, &q) < 0) | |||
173 | log_debug_errno(errno, "TIOCOUTQ failed on master: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/ptyfwd.c", 173, __func__, "TIOCOUTQ failed on master: %m") : -abs(_e); }); | |||
174 | else if (q > 0) | |||
175 | return false0; | |||
176 | ||||
177 | return true1; | |||
178 | } | |||
179 | ||||
180 | static int shovel(PTYForward *f) { | |||
181 | ssize_t k; | |||
182 | ||||
183 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 183, __PRETTY_FUNCTION__ ); } while (0); | |||
184 | ||||
185 | while ((f->stdin_readable && f->in_buffer_full <= 0) || | |||
186 | (f->master_writable && f->in_buffer_full > 0) || | |||
187 | (f->master_readable && f->out_buffer_full <= 0) || | |||
188 | (f->stdout_writable && f->out_buffer_full > 0)) { | |||
189 | ||||
190 | if (f->stdin_readable && f->in_buffer_full < LINE_MAX2048) { | |||
191 | ||||
192 | k = read(STDIN_FILENO0, f->in_buffer + f->in_buffer_full, LINE_MAX2048 - f->in_buffer_full); | |||
193 | if (k < 0) { | |||
194 | ||||
195 | if (errno(*__errno_location ()) == EAGAIN11) | |||
196 | f->stdin_readable = false0; | |||
197 | else if (IN_SET(errno, EIO, EPIPE, ECONNRESET)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){5, 32, 104})/sizeof(int)]; switch((*__errno_location ())) { case 5: case 32: case 104: _found = 1; break; default : break; } _found; })) { | |||
198 | f->stdin_readable = false0; | |||
199 | f->stdin_hangup = true1; | |||
200 | ||||
201 | f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); | |||
202 | } else { | |||
203 | log_error_errno(errno, "read(): %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/ptyfwd.c", 203, __func__, "read(): %m") : -abs(_e); }); | |||
204 | return pty_forward_done(f, -errno(*__errno_location ())); | |||
205 | } | |||
206 | } else if (k == 0) { | |||
207 | /* EOF on stdin */ | |||
208 | f->stdin_readable = false0; | |||
209 | f->stdin_hangup = true1; | |||
210 | ||||
211 | f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); | |||
212 | } else { | |||
213 | /* Check if ^] has been pressed three times within one second. If we get this we quite | |||
214 | * immediately. */ | |||
215 | if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k)) | |||
216 | return pty_forward_done(f, -ECANCELED125); | |||
217 | ||||
218 | f->in_buffer_full += (size_t) k; | |||
219 | } | |||
220 | } | |||
221 | ||||
222 | if (f->master_writable && f->in_buffer_full > 0) { | |||
223 | ||||
224 | k = write(f->master, f->in_buffer, f->in_buffer_full); | |||
225 | if (k < 0) { | |||
226 | ||||
227 | if (IN_SET(errno, EAGAIN, EIO)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){11, 5})/sizeof(int)]; switch((*__errno_location ())) { case 11: case 5: _found = 1; break; default: break; } _found; })) | |||
228 | f->master_writable = false0; | |||
229 | else if (IN_SET(errno, EPIPE, ECONNRESET)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){32, 104})/sizeof(int)]; switch((*__errno_location ())) { case 32: case 104: _found = 1; break; default: break; } _found; })) { | |||
230 | f->master_writable = f->master_readable = false0; | |||
231 | f->master_hangup = true1; | |||
232 | ||||
233 | f->master_event_source = sd_event_source_unref(f->master_event_source); | |||
234 | } else { | |||
235 | log_error_errno(errno, "write(): %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/ptyfwd.c", 235, __func__, "write(): %m") : -abs(_e); }); | |||
236 | return pty_forward_done(f, -errno(*__errno_location ())); | |||
237 | } | |||
238 | } else { | |||
239 | assert(f->in_buffer_full >= (size_t) k)do { if ((__builtin_expect(!!(!(f->in_buffer_full >= (size_t ) k)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("f->in_buffer_full >= (size_t) k" ), "../src/shared/ptyfwd.c", 239, __PRETTY_FUNCTION__); } while (0); | |||
240 | memmove(f->in_buffer, f->in_buffer + k, f->in_buffer_full - k); | |||
241 | f->in_buffer_full -= k; | |||
242 | } | |||
243 | } | |||
244 | ||||
245 | if (f->master_readable && f->out_buffer_full < LINE_MAX2048) { | |||
246 | ||||
247 | k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX2048 - f->out_buffer_full); | |||
248 | if (k < 0) { | |||
249 | ||||
250 | /* Note that EIO on the master device | |||
251 | * might be caused by vhangup() or | |||
252 | * temporary closing of everything on | |||
253 | * the other side, we treat it like | |||
254 | * EAGAIN here and try again, unless | |||
255 | * ignore_vhangup is off. */ | |||
256 | ||||
257 | if (errno(*__errno_location ()) == EAGAIN11 || (errno(*__errno_location ()) == EIO5 && ignore_vhangup(f))) | |||
258 | f->master_readable = false0; | |||
259 | else if (IN_SET(errno, EPIPE, ECONNRESET, EIO)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){32, 104, 5})/sizeof(int)]; switch((*__errno_location ())) { case 32: case 104: case 5: _found = 1; break; default : break; } _found; })) { | |||
260 | f->master_readable = f->master_writable = false0; | |||
261 | f->master_hangup = true1; | |||
262 | ||||
263 | f->master_event_source = sd_event_source_unref(f->master_event_source); | |||
264 | } else { | |||
265 | log_error_errno(errno, "read(): %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/ptyfwd.c", 265, __func__, "read(): %m") : -abs(_e); }); | |||
266 | return pty_forward_done(f, -errno(*__errno_location ())); | |||
267 | } | |||
268 | } else { | |||
269 | f->read_from_master = true1; | |||
270 | f->out_buffer_full += (size_t) k; | |||
271 | } | |||
272 | } | |||
273 | ||||
274 | if (f->stdout_writable && f->out_buffer_full > 0) { | |||
275 | ||||
276 | k = write(STDOUT_FILENO1, f->out_buffer, f->out_buffer_full); | |||
277 | if (k < 0) { | |||
278 | ||||
279 | if (errno(*__errno_location ()) == EAGAIN11) | |||
280 | f->stdout_writable = false0; | |||
281 | else if (IN_SET(errno, EIO, EPIPE, ECONNRESET)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){5, 32, 104})/sizeof(int)]; switch((*__errno_location ())) { case 5: case 32: case 104: _found = 1; break; default : break; } _found; })) { | |||
282 | f->stdout_writable = false0; | |||
283 | f->stdout_hangup = true1; | |||
284 | f->stdout_event_source = sd_event_source_unref(f->stdout_event_source); | |||
285 | } else { | |||
286 | log_error_errno(errno, "write(): %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/ptyfwd.c", 286, __func__, "write(): %m") : -abs(_e); }); | |||
287 | return pty_forward_done(f, -errno(*__errno_location ())); | |||
288 | } | |||
289 | ||||
290 | } else { | |||
291 | ||||
292 | if (k > 0) { | |||
293 | f->last_char = f->out_buffer[k-1]; | |||
294 | f->last_char_set = true1; | |||
295 | } | |||
296 | ||||
297 | assert(f->out_buffer_full >= (size_t) k)do { if ((__builtin_expect(!!(!(f->out_buffer_full >= ( size_t) k)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ( "f->out_buffer_full >= (size_t) k"), "../src/shared/ptyfwd.c" , 297, __PRETTY_FUNCTION__); } while (0); | |||
298 | memmove(f->out_buffer, f->out_buffer + k, f->out_buffer_full - k); | |||
299 | f->out_buffer_full -= k; | |||
300 | } | |||
301 | } | |||
302 | } | |||
303 | ||||
304 | if (f->stdin_hangup || f->stdout_hangup || f->master_hangup) { | |||
305 | /* Exit the loop if any side hung up and if there's | |||
306 | * nothing more to write or nothing we could write. */ | |||
307 | ||||
308 | if ((f->out_buffer_full <= 0 || f->stdout_hangup) && | |||
309 | (f->in_buffer_full <= 0 || f->master_hangup)) | |||
310 | return pty_forward_done(f, 0); | |||
311 | } | |||
312 | ||||
313 | /* If we were asked to drain, and there's nothing more to handle from the master, then call the callback | |||
314 | * too. */ | |||
315 | if (f->drain && drained(f)) | |||
316 | return pty_forward_done(f, 0); | |||
317 | ||||
318 | return 0; | |||
319 | } | |||
320 | ||||
321 | static int on_master_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { | |||
322 | PTYForward *f = userdata; | |||
323 | ||||
324 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 324, __PRETTY_FUNCTION__ ); } while (0); | |||
325 | assert(e)do { if ((__builtin_expect(!!(!(e)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("e"), "../src/shared/ptyfwd.c", 325, __PRETTY_FUNCTION__ ); } while (0); | |||
326 | assert(e == f->master_event_source)do { if ((__builtin_expect(!!(!(e == f->master_event_source )),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("e == f->master_event_source" ), "../src/shared/ptyfwd.c", 326, __PRETTY_FUNCTION__); } while (0); | |||
327 | assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/shared/ptyfwd.c", 327, __PRETTY_FUNCTION__); } while (0); | |||
328 | assert(fd == f->master)do { if ((__builtin_expect(!!(!(fd == f->master)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd == f->master"), "../src/shared/ptyfwd.c" , 328, __PRETTY_FUNCTION__); } while (0); | |||
329 | ||||
330 | if (revents & (EPOLLINEPOLLIN|EPOLLHUPEPOLLHUP)) | |||
331 | f->master_readable = true1; | |||
332 | ||||
333 | if (revents & (EPOLLOUTEPOLLOUT|EPOLLHUPEPOLLHUP)) | |||
334 | f->master_writable = true1; | |||
335 | ||||
336 | return shovel(f); | |||
337 | } | |||
338 | ||||
339 | static int on_stdin_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { | |||
340 | PTYForward *f = userdata; | |||
341 | ||||
342 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 342, __PRETTY_FUNCTION__ ); } while (0); | |||
343 | assert(e)do { if ((__builtin_expect(!!(!(e)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("e"), "../src/shared/ptyfwd.c", 343, __PRETTY_FUNCTION__ ); } while (0); | |||
344 | assert(e == f->stdin_event_source)do { if ((__builtin_expect(!!(!(e == f->stdin_event_source )),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("e == f->stdin_event_source" ), "../src/shared/ptyfwd.c", 344, __PRETTY_FUNCTION__); } while (0); | |||
345 | assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/shared/ptyfwd.c", 345, __PRETTY_FUNCTION__); } while (0); | |||
346 | assert(fd == STDIN_FILENO)do { if ((__builtin_expect(!!(!(fd == 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd == STDIN_FILENO"), "../src/shared/ptyfwd.c" , 346, __PRETTY_FUNCTION__); } while (0); | |||
347 | ||||
348 | if (revents & (EPOLLINEPOLLIN|EPOLLHUPEPOLLHUP)) | |||
349 | f->stdin_readable = true1; | |||
350 | ||||
351 | return shovel(f); | |||
352 | } | |||
353 | ||||
354 | static int on_stdout_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { | |||
355 | PTYForward *f = userdata; | |||
356 | ||||
357 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 357, __PRETTY_FUNCTION__ ); } while (0); | |||
358 | assert(e)do { if ((__builtin_expect(!!(!(e)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("e"), "../src/shared/ptyfwd.c", 358, __PRETTY_FUNCTION__ ); } while (0); | |||
359 | assert(e == f->stdout_event_source)do { if ((__builtin_expect(!!(!(e == f->stdout_event_source )),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("e == f->stdout_event_source" ), "../src/shared/ptyfwd.c", 359, __PRETTY_FUNCTION__); } while (0); | |||
360 | assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/shared/ptyfwd.c", 360, __PRETTY_FUNCTION__); } while (0); | |||
361 | assert(fd == STDOUT_FILENO)do { if ((__builtin_expect(!!(!(fd == 1)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd == STDOUT_FILENO"), "../src/shared/ptyfwd.c" , 361, __PRETTY_FUNCTION__); } while (0); | |||
362 | ||||
363 | if (revents & (EPOLLOUTEPOLLOUT|EPOLLHUPEPOLLHUP)) | |||
364 | f->stdout_writable = true1; | |||
365 | ||||
366 | return shovel(f); | |||
367 | } | |||
368 | ||||
369 | static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *si, void *userdata) { | |||
370 | PTYForward *f = userdata; | |||
371 | struct winsize ws; | |||
372 | ||||
373 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 373, __PRETTY_FUNCTION__ ); } while (0); | |||
374 | assert(e)do { if ((__builtin_expect(!!(!(e)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("e"), "../src/shared/ptyfwd.c", 374, __PRETTY_FUNCTION__ ); } while (0); | |||
375 | assert(e == f->sigwinch_event_source)do { if ((__builtin_expect(!!(!(e == f->sigwinch_event_source )),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("e == f->sigwinch_event_source" ), "../src/shared/ptyfwd.c", 375, __PRETTY_FUNCTION__); } while (0); | |||
376 | ||||
377 | /* The window size changed, let's forward that. */ | |||
378 | if (ioctl(STDOUT_FILENO1, TIOCGWINSZ0x5413, &ws) >= 0) | |||
379 | (void) ioctl(f->master, TIOCSWINSZ0x5414, &ws); | |||
380 | ||||
381 | return 0; | |||
382 | } | |||
383 | ||||
384 | int pty_forward_new( | |||
385 | sd_event *event, | |||
386 | int master, | |||
387 | PTYForwardFlags flags, | |||
388 | PTYForward **ret) { | |||
389 | ||||
390 | _cleanup_(pty_forward_freep)__attribute__((cleanup(pty_forward_freep))) PTYForward *f = NULL((void*)0); | |||
391 | struct winsize ws; | |||
392 | int r; | |||
393 | ||||
394 | f = new0(PTYForward, 1)((PTYForward*) calloc((1), sizeof(PTYForward))); | |||
| ||||
395 | if (!f) | |||
396 | return -ENOMEM12; | |||
397 | ||||
398 | f->flags = flags; | |||
399 | ||||
400 | if (event) | |||
401 | f->event = sd_event_ref(event); | |||
402 | else { | |||
403 | r = sd_event_default(&f->event); | |||
404 | if (r < 0) | |||
405 | return r; | |||
406 | } | |||
407 | ||||
408 | if (!(flags & PTY_FORWARD_READ_ONLY)) { | |||
409 | r = fd_nonblock(STDIN_FILENO0, true1); | |||
410 | if (r < 0) | |||
411 | return r; | |||
| ||||
412 | ||||
413 | r = fd_nonblock(STDOUT_FILENO1, true1); | |||
414 | if (r < 0) | |||
415 | return r; | |||
416 | } | |||
417 | ||||
418 | r = fd_nonblock(master, true1); | |||
419 | if (r < 0) | |||
420 | return r; | |||
421 | ||||
422 | f->master = master; | |||
423 | ||||
424 | if (ioctl(STDOUT_FILENO1, TIOCGWINSZ0x5413, &ws) >= 0) | |||
425 | (void) ioctl(master, TIOCSWINSZ0x5414, &ws); | |||
426 | ||||
427 | if (!(flags & PTY_FORWARD_READ_ONLY)) { | |||
428 | if (tcgetattr(STDIN_FILENO0, &f->saved_stdin_attr) >= 0) { | |||
429 | struct termios raw_stdin_attr; | |||
430 | ||||
431 | f->saved_stdin = true1; | |||
432 | ||||
433 | raw_stdin_attr = f->saved_stdin_attr; | |||
434 | cfmakeraw(&raw_stdin_attr); | |||
435 | raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag; | |||
436 | tcsetattr(STDIN_FILENO0, TCSANOW0, &raw_stdin_attr); | |||
437 | } | |||
438 | ||||
439 | if (tcgetattr(STDOUT_FILENO1, &f->saved_stdout_attr) >= 0) { | |||
440 | struct termios raw_stdout_attr; | |||
441 | ||||
442 | f->saved_stdout = true1; | |||
443 | ||||
444 | raw_stdout_attr = f->saved_stdout_attr; | |||
445 | cfmakeraw(&raw_stdout_attr); | |||
446 | raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag; | |||
447 | raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag; | |||
448 | tcsetattr(STDOUT_FILENO1, TCSANOW0, &raw_stdout_attr); | |||
449 | } | |||
450 | ||||
451 | r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO0, EPOLLINEPOLLIN|EPOLLETEPOLLET, on_stdin_event, f); | |||
452 | if (r < 0 && r != -EPERM1) | |||
453 | return r; | |||
454 | ||||
455 | if (r >= 0) | |||
456 | (void) sd_event_source_set_description(f->stdin_event_source, "ptyfwd-stdin"); | |||
457 | } | |||
458 | ||||
459 | r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO1, EPOLLOUTEPOLLOUT|EPOLLETEPOLLET, on_stdout_event, f); | |||
460 | if (r == -EPERM1) | |||
461 | /* stdout without epoll support. Likely redirected to regular file. */ | |||
462 | f->stdout_writable = true1; | |||
463 | else if (r < 0) | |||
464 | return r; | |||
465 | else | |||
466 | (void) sd_event_source_set_description(f->stdout_event_source, "ptyfwd-stdout"); | |||
467 | ||||
468 | r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLINEPOLLIN|EPOLLOUTEPOLLOUT|EPOLLETEPOLLET, on_master_event, f); | |||
469 | if (r < 0) | |||
470 | return r; | |||
471 | ||||
472 | (void) sd_event_source_set_description(f->master_event_source, "ptyfwd-master"); | |||
473 | ||||
474 | r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH28, on_sigwinch_event, f); | |||
475 | if (r < 0) | |||
476 | return r; | |||
477 | ||||
478 | (void) sd_event_source_set_description(f->sigwinch_event_source, "ptyfwd-sigwinch"); | |||
479 | ||||
480 | *ret = TAKE_PTR(f)({ typeof(f) _ptr_ = (f); (f) = ((void*)0); _ptr_; }); | |||
481 | ||||
482 | return 0; | |||
483 | } | |||
484 | ||||
485 | PTYForward *pty_forward_free(PTYForward *f) { | |||
486 | pty_forward_disconnect(f); | |||
487 | return mfree(f); | |||
488 | } | |||
489 | ||||
490 | int pty_forward_get_last_char(PTYForward *f, char *ch) { | |||
491 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 491, __PRETTY_FUNCTION__ ); } while (0); | |||
492 | assert(ch)do { if ((__builtin_expect(!!(!(ch)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ch"), "../src/shared/ptyfwd.c", 492, __PRETTY_FUNCTION__ ); } while (0); | |||
493 | ||||
494 | if (!f->last_char_set) | |||
495 | return -ENXIO6; | |||
496 | ||||
497 | *ch = f->last_char; | |||
498 | return 0; | |||
499 | } | |||
500 | ||||
501 | int pty_forward_set_ignore_vhangup(PTYForward *f, bool_Bool b) { | |||
502 | int r; | |||
503 | ||||
504 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 504, __PRETTY_FUNCTION__ ); } while (0); | |||
505 | ||||
506 | if (!!(f->flags & PTY_FORWARD_IGNORE_VHANGUP) == b) | |||
507 | return 0; | |||
508 | ||||
509 | SET_FLAG(f->flags, PTY_FORWARD_IGNORE_VHANGUP, b)(f->flags) = (b) ? ((f->flags) | (PTY_FORWARD_IGNORE_VHANGUP )) : ((f->flags) & ~(PTY_FORWARD_IGNORE_VHANGUP)); | |||
510 | ||||
511 | if (!ignore_vhangup(f)) { | |||
512 | ||||
513 | /* We shall now react to vhangup()s? Let's check | |||
514 | * immediately if we might be in one */ | |||
515 | ||||
516 | f->master_readable = true1; | |||
517 | r = shovel(f); | |||
518 | if (r < 0) | |||
519 | return r; | |||
520 | } | |||
521 | ||||
522 | return 0; | |||
523 | } | |||
524 | ||||
525 | bool_Bool pty_forward_get_ignore_vhangup(PTYForward *f) { | |||
526 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 526, __PRETTY_FUNCTION__ ); } while (0); | |||
527 | ||||
528 | return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP); | |||
529 | } | |||
530 | ||||
531 | bool_Bool pty_forward_is_done(PTYForward *f) { | |||
532 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 532, __PRETTY_FUNCTION__ ); } while (0); | |||
533 | ||||
534 | return f->done; | |||
535 | } | |||
536 | ||||
537 | void pty_forward_set_handler(PTYForward *f, PTYForwardHandler cb, void *userdata) { | |||
538 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 538, __PRETTY_FUNCTION__ ); } while (0); | |||
539 | ||||
540 | f->handler = cb; | |||
541 | f->userdata = userdata; | |||
542 | } | |||
543 | ||||
544 | bool_Bool pty_forward_drain(PTYForward *f) { | |||
545 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 545, __PRETTY_FUNCTION__ ); } while (0); | |||
546 | ||||
547 | /* Starts draining the forwarder. Specifically: | |||
548 | * | |||
549 | * - Returns true if there are no unprocessed bytes from the pty, false otherwise | |||
550 | * | |||
551 | * - Makes sure the handler function is called the next time the number of unprocessed bytes hits zero | |||
552 | */ | |||
553 | ||||
554 | f->drain = true1; | |||
555 | return drained(f); | |||
556 | } | |||
557 | ||||
558 | int pty_forward_set_priority(PTYForward *f, int64_t priority) { | |||
559 | int r; | |||
560 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/shared/ptyfwd.c", 560, __PRETTY_FUNCTION__ ); } while (0); | |||
561 | ||||
562 | r = sd_event_source_set_priority(f->stdin_event_source, priority); | |||
563 | if (r < 0) | |||
564 | return r; | |||
565 | ||||
566 | r = sd_event_source_set_priority(f->stdout_event_source, priority); | |||
567 | if (r < 0) | |||
568 | return r; | |||
569 | ||||
570 | r = sd_event_source_set_priority(f->master_event_source, priority); | |||
571 | if (r < 0) | |||
572 | return r; | |||
573 | ||||
574 | r = sd_event_source_set_priority(f->sigwinch_event_source, priority); | |||
575 | if (r < 0) | |||
576 | return r; | |||
577 | ||||
578 | return 0; | |||
579 | } |