| 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 | } |