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