]>
Commit | Line | Data |
---|---|---|
6954895c KZ |
1 | /* |
2 | * This is pseudo-terminal container for child process where parent creates a | |
3 | * proxy between the current std{in,out,etrr} and the child's pty. Advantages: | |
4 | * | |
5 | * - child has no access to parent's terminal (e.g. su --pty) | |
6 | * - parent can log all traffic between user and child's terminall (e.g. script(1)) | |
7 | * - it's possible to start commands on terminal although parent has no terminal | |
8 | * | |
9 | * This code is in the public domain; do with it what you wish. | |
10 | * | |
11 | * Written by Karel Zak <kzak@redhat.com> in Jul 2019 | |
12 | */ | |
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <pty.h> | |
16 | #include <poll.h> | |
17 | #include <sys/signalfd.h> | |
18 | #include <paths.h> | |
19 | #include <sys/types.h> | |
20 | #include <sys/wait.h> | |
21 | ||
22 | #include "c.h" | |
23 | #include "all-io.h" | |
24 | #include "ttyutils.h" | |
25 | #include "pty-session.h" | |
4d5b2fed | 26 | #include "monotonic.h" |
6954895c KZ |
27 | #include "debug.h" |
28 | ||
29 | static UL_DEBUG_DEFINE_MASK(ulpty); | |
30 | UL_DEBUG_DEFINE_MASKNAMES(ulpty) = UL_DEBUG_EMPTY_MASKNAMES; | |
31 | ||
32 | #define ULPTY_DEBUG_INIT (1 << 1) | |
33 | #define ULPTY_DEBUG_SETUP (1 << 2) | |
34 | #define ULPTY_DEBUG_SIG (1 << 3) | |
35 | #define ULPTY_DEBUG_IO (1 << 4) | |
36 | #define ULPTY_DEBUG_DONE (1 << 5) | |
37 | #define ULPTY_DEBUG_ALL 0xFFFF | |
38 | ||
39 | #define DBG(m, x) __UL_DBG(ulpty, ULPTY_DEBUG_, m, x) | |
40 | #define ON_DBG(m, x) __UL_DBG_CALL(ulpty, ULPTY_DEBUG_, m, x) | |
41 | ||
42 | #define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulpty) | |
43 | #include "debugobj.h" | |
44 | ||
45 | void ul_pty_init_debug(int mask) | |
46 | { | |
47 | if (ulpty_debug_mask) | |
48 | return; | |
49 | __UL_INIT_DEBUG_FROM_ENV(ulpty, ULPTY_DEBUG_, mask, ULPTY_DEBUG); | |
50 | } | |
51 | ||
52 | struct ul_pty *ul_new_pty(int is_stdin_tty) | |
53 | { | |
54 | struct ul_pty *pty = calloc(1, sizeof(*pty)); | |
55 | ||
56 | if (!pty) | |
57 | return NULL; | |
58 | ||
59 | DBG(SETUP, ul_debugobj(pty, "alloc handler")); | |
60 | pty->isterm = is_stdin_tty; | |
61 | pty->master = -1; | |
62 | pty->slave = -1; | |
63 | pty->sigfd = -1; | |
64 | pty->child = (pid_t) -1; | |
65 | ||
66 | return pty; | |
67 | } | |
68 | ||
ab61a038 | 69 | void ul_free_pty(struct ul_pty *pty) |
6954895c | 70 | { |
ab61a038 | 71 | free(pty); |
6954895c KZ |
72 | } |
73 | ||
1eee1acb | 74 | void ul_pty_slave_echo(struct ul_pty *pty, int enable) |
4169bcb7 KZ |
75 | { |
76 | assert(pty); | |
1eee1acb | 77 | pty->slave_echo = enable ? 1 : 0; |
4169bcb7 | 78 | } |
ab61a038 | 79 | |
6954895c KZ |
80 | int ul_pty_get_delivered_signal(struct ul_pty *pty) |
81 | { | |
82 | assert(pty); | |
83 | return pty->delivered_signal; | |
84 | } | |
85 | ||
86 | struct ul_pty_callbacks *ul_pty_get_callbacks(struct ul_pty *pty) | |
87 | { | |
88 | assert(pty); | |
89 | return &pty->callbacks; | |
90 | } | |
91 | ||
92 | void ul_pty_set_callback_data(struct ul_pty *pty, void *data) | |
93 | { | |
94 | assert(pty); | |
95 | pty->callback_data = data; | |
96 | } | |
97 | ||
98 | void ul_pty_set_child(struct ul_pty *pty, pid_t child) | |
99 | { | |
100 | assert(pty); | |
101 | pty->child = child; | |
102 | } | |
103 | ||
4d5b2fed KZ |
104 | int ul_pty_get_childfd(struct ul_pty *pty) |
105 | { | |
106 | assert(pty); | |
107 | return pty->master; | |
108 | } | |
109 | ||
bdd43357 KZ |
110 | pid_t ul_pty_get_child(struct ul_pty *pty) |
111 | { | |
112 | assert(pty); | |
113 | return pty->child; | |
114 | } | |
115 | ||
6954895c KZ |
116 | /* it's active when signals are redurected to sigfd */ |
117 | int ul_pty_is_running(struct ul_pty *pty) | |
118 | { | |
119 | assert(pty); | |
120 | return pty->sigfd >= 0; | |
121 | } | |
122 | ||
4d5b2fed KZ |
123 | void ul_pty_set_mainloop_time(struct ul_pty *pty, struct timeval *tv) |
124 | { | |
125 | assert(pty); | |
126 | if (!tv) { | |
127 | DBG(IO, ul_debugobj(pty, "mainloop time: clear")); | |
128 | timerclear(&pty->next_callback_time); | |
129 | } else { | |
130 | pty->next_callback_time.tv_sec = tv->tv_sec; | |
131 | pty->next_callback_time.tv_usec = tv->tv_usec; | |
132 | DBG(IO, ul_debugobj(pty, "mainloop time: %ld.%06ld", tv->tv_sec, tv->tv_usec)); | |
133 | } | |
134 | } | |
135 | ||
b1154c4e KZ |
136 | static void pty_signals_cleanup(struct ul_pty *pty) |
137 | { | |
138 | if (pty->sigfd != -1) | |
139 | close(pty->sigfd); | |
140 | pty->sigfd = -1; | |
141 | ||
142 | /* restore original setting */ | |
143 | sigprocmask(SIG_SETMASK, &pty->orgsig, NULL); | |
144 | } | |
145 | ||
6954895c KZ |
146 | /* call me before fork() */ |
147 | int ul_pty_setup(struct ul_pty *pty) | |
148 | { | |
149 | struct termios slave_attrs; | |
b1154c4e KZ |
150 | sigset_t ourset; |
151 | int rc = 0; | |
152 | ||
153 | assert(pty->sigfd == -1); | |
6954895c | 154 | |
ab61a038 KZ |
155 | /* save the current signals setting */ |
156 | sigprocmask(0, NULL, &pty->orgsig); | |
157 | ||
6954895c KZ |
158 | if (pty->isterm) { |
159 | DBG(SETUP, ul_debugobj(pty, "create for terminal")); | |
160 | ||
161 | /* original setting of the current terminal */ | |
b1154c4e KZ |
162 | if (tcgetattr(STDIN_FILENO, &pty->stdin_attrs) != 0) { |
163 | rc = -errno; | |
164 | goto done; | |
165 | } | |
6954895c KZ |
166 | ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&pty->win); |
167 | /* create master+slave */ | |
168 | rc = openpty(&pty->master, &pty->slave, NULL, &pty->stdin_attrs, &pty->win); | |
b1154c4e KZ |
169 | if (rc) |
170 | goto done; | |
6954895c KZ |
171 | |
172 | /* set the current terminal to raw mode; pty_cleanup() reverses this change on exit */ | |
173 | slave_attrs = pty->stdin_attrs; | |
174 | cfmakeraw(&slave_attrs); | |
1eee1acb KZ |
175 | |
176 | if (pty->slave_echo) | |
177 | slave_attrs.c_lflag |= ECHO; | |
178 | else | |
179 | slave_attrs.c_lflag &= ~ECHO; | |
180 | ||
6954895c KZ |
181 | tcsetattr(STDIN_FILENO, TCSANOW, &slave_attrs); |
182 | } else { | |
183 | DBG(SETUP, ul_debugobj(pty, "create for non-terminal")); | |
6954895c | 184 | |
1eee1acb KZ |
185 | rc = openpty(&pty->master, &pty->slave, NULL, NULL, NULL); |
186 | if (rc) | |
b1154c4e | 187 | goto done; |
1eee1acb KZ |
188 | |
189 | tcgetattr(pty->slave, &slave_attrs); | |
190 | ||
191 | if (pty->slave_echo) | |
192 | slave_attrs.c_lflag |= ECHO; | |
193 | else | |
194 | slave_attrs.c_lflag &= ~ECHO; | |
195 | ||
196 | tcsetattr(pty->slave, TCSANOW, &slave_attrs); | |
b1154c4e KZ |
197 | } |
198 | ||
199 | sigfillset(&ourset); | |
200 | if (sigprocmask(SIG_BLOCK, &ourset, NULL)) { | |
201 | rc = -errno; | |
202 | goto done; | |
6954895c KZ |
203 | } |
204 | ||
b1154c4e KZ |
205 | sigemptyset(&ourset); |
206 | sigaddset(&ourset, SIGCHLD); | |
207 | sigaddset(&ourset, SIGWINCH); | |
208 | sigaddset(&ourset, SIGALRM); | |
209 | sigaddset(&ourset, SIGTERM); | |
210 | sigaddset(&ourset, SIGINT); | |
211 | sigaddset(&ourset, SIGQUIT); | |
212 | ||
7727be1a KZ |
213 | if (pty->callbacks.flush_logs) |
214 | sigaddset(&ourset, SIGUSR1); | |
215 | ||
b1154c4e KZ |
216 | if ((pty->sigfd = signalfd(-1, &ourset, SFD_CLOEXEC)) < 0) |
217 | rc = -errno; | |
218 | done: | |
219 | if (rc) | |
220 | ul_pty_cleanup(pty); | |
221 | ||
6954895c KZ |
222 | DBG(SETUP, ul_debugobj(pty, "pty setup done [master=%d, slave=%d, rc=%d]", |
223 | pty->master, pty->slave, rc)); | |
224 | return rc; | |
225 | } | |
226 | ||
227 | /* cleanup in parent process */ | |
228 | void ul_pty_cleanup(struct ul_pty *pty) | |
229 | { | |
230 | struct termios rtt; | |
231 | ||
b1154c4e KZ |
232 | pty_signals_cleanup(pty); |
233 | ||
6954895c KZ |
234 | if (pty->master == -1 || !pty->isterm) |
235 | return; | |
236 | ||
237 | DBG(DONE, ul_debugobj(pty, "cleanup")); | |
238 | rtt = pty->stdin_attrs; | |
239 | tcsetattr(STDIN_FILENO, TCSADRAIN, &rtt); | |
240 | } | |
241 | ||
242 | /* call me in child process */ | |
243 | void ul_pty_init_slave(struct ul_pty *pty) | |
244 | { | |
245 | DBG(SETUP, ul_debugobj(pty, "initialize slave")); | |
246 | ||
247 | setsid(); | |
248 | ||
249 | ioctl(pty->slave, TIOCSCTTY, 1); | |
250 | close(pty->master); | |
251 | ||
252 | dup2(pty->slave, STDIN_FILENO); | |
253 | dup2(pty->slave, STDOUT_FILENO); | |
254 | dup2(pty->slave, STDERR_FILENO); | |
255 | ||
256 | close(pty->slave); | |
257 | ||
258 | if (pty->sigfd >= 0) | |
259 | close(pty->sigfd); | |
260 | ||
261 | pty->slave = -1; | |
262 | pty->master = -1; | |
263 | pty->sigfd = -1; | |
264 | ||
265 | sigprocmask(SIG_SETMASK, &pty->orgsig, NULL); | |
266 | ||
267 | DBG(SETUP, ul_debugobj(pty, "... initialize slave done")); | |
268 | } | |
269 | ||
270 | static int write_output(char *obuf, ssize_t bytes) | |
271 | { | |
272 | DBG(IO, ul_debug(" writing output")); | |
273 | ||
274 | if (write_all(STDOUT_FILENO, obuf, bytes)) { | |
275 | DBG(IO, ul_debug(" writing output *failed*")); | |
276 | return -errno; | |
277 | } | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
4d5b2fed | 282 | static int write_to_child(struct ul_pty *pty, char *buf, size_t bufsz) |
6954895c KZ |
283 | { |
284 | return write_all(pty->master, buf, bufsz); | |
285 | } | |
286 | ||
287 | /* | |
288 | * The pty is usually faster than shell, so it's a good idea to wait until | |
289 | * the previous message has been already read by shell from slave before we | |
290 | * write to master. This is necessary especially for EOF situation when we can | |
291 | * send EOF to master before shell is fully initialized, to workaround this | |
292 | * problem we wait until slave is empty. For example: | |
293 | * | |
294 | * echo "date" | su --pty | |
295 | * | |
296 | * Unfortunately, the child (usually shell) can ignore stdin at all, so we | |
297 | * don't wait forever to avoid dead locks... | |
298 | * | |
299 | * Note that su --pty is primarily designed for interactive sessions as it | |
300 | * maintains master+slave tty stuff within the session. Use pipe to write to | |
301 | * pty and assume non-interactive (tee-like) behavior is NOT well supported. | |
302 | */ | |
95d255a8 | 303 | void ul_pty_write_eof_to_child(struct ul_pty *pty) |
6954895c KZ |
304 | { |
305 | unsigned int tries = 0; | |
306 | struct pollfd fds[] = { | |
307 | { .fd = pty->slave, .events = POLLIN } | |
308 | }; | |
309 | char c = DEF_EOF; | |
310 | ||
311 | DBG(IO, ul_debugobj(pty, " waiting for empty slave")); | |
312 | while (poll(fds, 1, 10) == 1 && tries < 8) { | |
313 | DBG(IO, ul_debugobj(pty, " slave is not empty")); | |
314 | xusleep(250000); | |
315 | tries++; | |
316 | } | |
317 | if (tries < 8) | |
318 | DBG(IO, ul_debugobj(pty, " slave is empty now")); | |
319 | ||
320 | DBG(IO, ul_debugobj(pty, " sending EOF to master")); | |
321 | write_to_child(pty, &c, sizeof(char)); | |
322 | } | |
323 | ||
4d5b2fed KZ |
324 | static int mainloop_callback(struct ul_pty *pty) |
325 | { | |
f896aef3 KZ |
326 | int rc; |
327 | ||
4d5b2fed KZ |
328 | if (!pty->callbacks.mainloop) |
329 | return 0; | |
330 | ||
331 | DBG(IO, ul_debugobj(pty, "calling mainloop callback")); | |
f896aef3 KZ |
332 | rc = pty->callbacks.mainloop(pty->callback_data); |
333 | ||
334 | DBG(IO, ul_debugobj(pty, " callback done [rc=%d]", rc)); | |
335 | return rc; | |
4d5b2fed KZ |
336 | } |
337 | ||
6954895c KZ |
338 | static int handle_io(struct ul_pty *pty, int fd, int *eof) |
339 | { | |
340 | char buf[BUFSIZ]; | |
341 | ssize_t bytes; | |
4f7f723b | 342 | int rc = 0; |
6954895c | 343 | |
c4bacbd1 | 344 | DBG(IO, ul_debugobj(pty, " handle I/O on fd=%d", fd)); |
6954895c KZ |
345 | *eof = 0; |
346 | ||
347 | /* read from active FD */ | |
348 | bytes = read(fd, buf, sizeof(buf)); | |
349 | if (bytes < 0) { | |
350 | if (errno == EAGAIN || errno == EINTR) | |
351 | return 0; | |
352 | return -errno; | |
353 | } | |
354 | ||
355 | if (bytes == 0) { | |
356 | *eof = 1; | |
357 | return 0; | |
358 | } | |
359 | ||
360 | /* from stdin (user) to command */ | |
361 | if (fd == STDIN_FILENO) { | |
362 | DBG(IO, ul_debugobj(pty, " stdin --> master %zd bytes", bytes)); | |
363 | ||
364 | if (write_to_child(pty, buf, bytes)) | |
365 | return -errno; | |
366 | ||
367 | /* without sync write_output() will write both input & | |
368 | * shell output that looks like double echoing */ | |
369 | fdatasync(pty->master); | |
370 | ||
371 | /* from command (master) to stdout */ | |
372 | } else if (fd == pty->master) { | |
373 | DBG(IO, ul_debugobj(pty, " master --> stdout %zd bytes", bytes)); | |
374 | write_output(buf, bytes); | |
375 | } | |
376 | ||
4f7f723b KZ |
377 | if (pty->callbacks.log_stream_activity) |
378 | rc = pty->callbacks.log_stream_activity( | |
379 | pty->callback_data, fd, buf, bytes); | |
380 | ||
381 | return rc; | |
6954895c KZ |
382 | } |
383 | ||
bdd43357 KZ |
384 | void ul_pty_wait_for_child(struct ul_pty *pty) |
385 | { | |
386 | int status; | |
387 | pid_t pid; | |
388 | int options = 0; | |
389 | ||
390 | if (pty->child == (pid_t) -1) | |
391 | return; | |
392 | ||
125314c0 | 393 | DBG(SIG, ul_debug("waiting for child [child=%d]", (int) pty->child)); |
bdd43357 KZ |
394 | |
395 | if (ul_pty_is_running(pty)) { | |
396 | /* wait for specific child */ | |
397 | options = WNOHANG; | |
398 | for (;;) { | |
399 | pid = waitpid(pty->child, &status, options); | |
125314c0 | 400 | DBG(SIG, ul_debug(" waitpid done [rc=%d]", (int) pid)); |
bdd43357 KZ |
401 | if (pid != (pid_t) - 1) { |
402 | if (pty->callbacks.child_die) | |
403 | pty->callbacks.child_die( | |
404 | pty->callback_data, | |
405 | pty->child, status); | |
406 | ul_pty_set_child(pty, (pid_t) -1); | |
407 | } else | |
408 | break; | |
409 | } | |
410 | } else { | |
411 | /* final wait */ | |
412 | while ((pid = wait3(&status, options, NULL)) > 0) { | |
125314c0 | 413 | DBG(SIG, ul_debug(" wait3 done [rc=%d]", (int) pid)); |
bdd43357 KZ |
414 | if (pid == pty->child) { |
415 | if (pty->callbacks.child_die) | |
416 | pty->callbacks.child_die( | |
417 | pty->callback_data, | |
418 | pty->child, status); | |
419 | ul_pty_set_child(pty, (pid_t) -1); | |
420 | } | |
421 | } | |
422 | } | |
423 | } | |
424 | ||
6954895c KZ |
425 | static int handle_signal(struct ul_pty *pty, int fd) |
426 | { | |
427 | struct signalfd_siginfo info; | |
428 | ssize_t bytes; | |
4f7f723b | 429 | int rc = 0; |
6954895c | 430 | |
c4bacbd1 | 431 | DBG(SIG, ul_debugobj(pty, " handle signal on fd=%d", fd)); |
6954895c KZ |
432 | |
433 | bytes = read(fd, &info, sizeof(info)); | |
434 | if (bytes != sizeof(info)) { | |
435 | if (bytes < 0 && (errno == EAGAIN || errno == EINTR)) | |
436 | return 0; | |
437 | return -errno; | |
438 | } | |
439 | ||
440 | switch (info.ssi_signo) { | |
441 | case SIGCHLD: | |
442 | DBG(SIG, ul_debugobj(pty, " get signal SIGCHLD")); | |
443 | ||
444 | if (info.ssi_code == CLD_EXITED | |
445 | || info.ssi_code == CLD_KILLED | |
bdd43357 KZ |
446 | || info.ssi_code == CLD_DUMPED) { |
447 | ||
448 | if (pty->callbacks.child_wait) | |
449 | pty->callbacks.child_wait(pty->callback_data, | |
450 | pty->child); | |
451 | else | |
452 | ul_pty_wait_for_child(pty); | |
6954895c | 453 | |
bdd43357 KZ |
454 | } else if (info.ssi_status == SIGSTOP && pty->child > 0) |
455 | pty->callbacks.child_sigstop(pty->callback_data, | |
456 | pty->child); | |
6954895c | 457 | |
984082fa | 458 | if (pty->child <= 0) { |
125314c0 | 459 | DBG(SIG, ul_debugobj(pty, " no child, setting leaving timeout")); |
6954895c | 460 | pty->poll_timeout = 10; |
984082fa KZ |
461 | timerclear(&pty->next_callback_time); |
462 | } | |
6954895c KZ |
463 | return 0; |
464 | case SIGWINCH: | |
465 | DBG(SIG, ul_debugobj(pty, " get signal SIGWINCH")); | |
466 | if (pty->isterm) { | |
467 | ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&pty->win); | |
468 | ioctl(pty->slave, TIOCSWINSZ, (char *)&pty->win); | |
4f7f723b KZ |
469 | |
470 | if (pty->callbacks.log_signal) | |
471 | rc = pty->callbacks.log_signal(pty->callback_data, | |
472 | &info, (void *) &pty->win); | |
6954895c KZ |
473 | } |
474 | break; | |
475 | case SIGTERM: | |
476 | /* fallthrough */ | |
477 | case SIGINT: | |
478 | /* fallthrough */ | |
479 | case SIGQUIT: | |
480 | DBG(SIG, ul_debugobj(pty, " get signal SIG{TERM,INT,QUIT}")); | |
481 | pty->delivered_signal = info.ssi_signo; | |
482 | /* Child termination is going to generate SIGCHILD (see above) */ | |
483 | if (pty->child > 0) | |
484 | kill(pty->child, SIGTERM); | |
4f7f723b KZ |
485 | |
486 | if (pty->callbacks.log_signal) | |
487 | rc = pty->callbacks.log_signal(pty->callback_data, | |
488 | &info, (void *) &pty->win); | |
6954895c | 489 | break; |
7727be1a KZ |
490 | case SIGUSR1: |
491 | DBG(SIG, ul_debugobj(pty, " get signal SIGUSR1")); | |
492 | if (pty->callbacks.flush_logs) | |
493 | rc = pty->callbacks.flush_logs(pty->callback_data); | |
494 | break; | |
6954895c KZ |
495 | default: |
496 | abort(); | |
497 | } | |
498 | ||
4f7f723b | 499 | return rc; |
6954895c KZ |
500 | } |
501 | ||
502 | /* loop in parent */ | |
503 | int ul_pty_proxy_master(struct ul_pty *pty) | |
504 | { | |
6954895c KZ |
505 | int rc = 0, ret, eof = 0; |
506 | enum { | |
507 | POLLFD_SIGNAL = 0, | |
508 | POLLFD_MASTER, | |
509 | POLLFD_STDIN | |
510 | ||
511 | }; | |
512 | struct pollfd pfd[] = { | |
513 | [POLLFD_SIGNAL] = { .fd = -1, .events = POLLIN | POLLERR | POLLHUP }, | |
514 | [POLLFD_MASTER] = { .fd = pty->master, .events = POLLIN | POLLERR | POLLHUP }, | |
515 | [POLLFD_STDIN] = { .fd = STDIN_FILENO, .events = POLLIN | POLLERR | POLLHUP } | |
516 | }; | |
517 | ||
518 | /* We use signalfd and standard signals by handlers are blocked | |
519 | * at all | |
520 | */ | |
b1154c4e | 521 | assert(pty->sigfd >= 0); |
6954895c KZ |
522 | |
523 | pfd[POLLFD_SIGNAL].fd = pty->sigfd; | |
524 | pty->poll_timeout = -1; | |
525 | ||
526 | while (!pty->delivered_signal) { | |
527 | size_t i; | |
4d5b2fed KZ |
528 | int errsv, timeout; |
529 | ||
530 | DBG(IO, ul_debugobj(pty, "--poll() loop--")); | |
531 | ||
532 | /* note, callback usually updates @next_callback_time */ | |
533 | if (timerisset(&pty->next_callback_time)) { | |
534 | struct timeval now; | |
535 | ||
536 | DBG(IO, ul_debugobj(pty, " callback requested")); | |
537 | gettime_monotonic(&now); | |
538 | if (timercmp(&now, &pty->next_callback_time, >)) { | |
539 | rc = mainloop_callback(pty); | |
540 | if (rc) | |
541 | break; | |
542 | } | |
543 | } | |
544 | ||
545 | /* set timeout */ | |
546 | if (timerisset(&pty->next_callback_time)) { | |
547 | struct timeval now, rest; | |
548 | ||
549 | gettime_monotonic(&now); | |
550 | timersub(&pty->next_callback_time, &now, &rest); | |
551 | timeout = (rest.tv_sec * 1000) + (rest.tv_usec / 1000); | |
552 | } else | |
553 | timeout = pty->poll_timeout; | |
6954895c | 554 | |
4d5b2fed KZ |
555 | /* wait for input, signal or timeout */ |
556 | DBG(IO, ul_debugobj(pty, "calling poll() [timeout=%dms]", timeout)); | |
557 | ret = poll(pfd, ARRAY_SIZE(pfd), timeout); | |
6954895c | 558 | |
6954895c KZ |
559 | errsv = errno; |
560 | DBG(IO, ul_debugobj(pty, "poll() rc=%d", ret)); | |
561 | ||
4d5b2fed | 562 | /* error */ |
6954895c KZ |
563 | if (ret < 0) { |
564 | if (errsv == EAGAIN) | |
565 | continue; | |
566 | rc = -errno; | |
567 | break; | |
568 | } | |
4d5b2fed KZ |
569 | |
570 | /* timeout */ | |
6954895c | 571 | if (ret == 0) { |
4d5b2fed KZ |
572 | if (timerisset(&pty->next_callback_time)) { |
573 | rc = mainloop_callback(pty); | |
574 | if (rc == 0) | |
575 | continue; | |
576 | } else | |
577 | rc = 0; | |
578 | ||
984082fa | 579 | DBG(IO, ul_debugobj(pty, "leaving poll() loop [timeout=%d, rc=%d]", timeout, rc)); |
6954895c KZ |
580 | break; |
581 | } | |
4d5b2fed | 582 | /* event */ |
6954895c KZ |
583 | for (i = 0; i < ARRAY_SIZE(pfd); i++) { |
584 | rc = 0; | |
585 | ||
586 | if (pfd[i].revents == 0) | |
587 | continue; | |
588 | ||
f896aef3 | 589 | DBG(IO, ul_debugobj(pty, " active pfd[%s].fd=%d %s %s %s %s", |
6954895c KZ |
590 | i == POLLFD_STDIN ? "stdin" : |
591 | i == POLLFD_MASTER ? "master" : | |
592 | i == POLLFD_SIGNAL ? "signal" : "???", | |
593 | pfd[i].fd, | |
594 | pfd[i].revents & POLLIN ? "POLLIN" : "", | |
595 | pfd[i].revents & POLLHUP ? "POLLHUP" : "", | |
f896aef3 KZ |
596 | pfd[i].revents & POLLERR ? "POLLERR" : "", |
597 | pfd[i].revents & POLLNVAL ? "POLLNVAL" : "")); | |
598 | ||
6954895c KZ |
599 | switch (i) { |
600 | case POLLFD_STDIN: | |
601 | case POLLFD_MASTER: | |
602 | /* data */ | |
603 | if (pfd[i].revents & POLLIN) | |
604 | rc = handle_io(pty, pfd[i].fd, &eof); | |
605 | /* EOF maybe detected by two ways: | |
606 | * A) poll() return POLLHUP event after close() | |
f896aef3 KZ |
607 | * B) read() returns 0 (no data) |
608 | * | |
609 | * POLLNVAL means that fd is closed. | |
610 | */ | |
611 | if ((pfd[i].revents & POLLHUP) || (pfd[i].revents & POLLNVAL) || eof) { | |
6954895c KZ |
612 | DBG(IO, ul_debugobj(pty, " ignore FD")); |
613 | pfd[i].fd = -1; | |
614 | if (i == POLLFD_STDIN) { | |
95d255a8 | 615 | ul_pty_write_eof_to_child(pty); |
6954895c KZ |
616 | DBG(IO, ul_debugobj(pty, " ignore STDIN")); |
617 | } | |
618 | } | |
619 | continue; | |
620 | case POLLFD_SIGNAL: | |
621 | rc = handle_signal(pty, pfd[i].fd); | |
622 | break; | |
623 | } | |
624 | if (rc) | |
625 | break; | |
626 | } | |
627 | } | |
628 | ||
b1154c4e | 629 | pty_signals_cleanup(pty); |
6954895c KZ |
630 | |
631 | DBG(IO, ul_debug("poll() done [signal=%d, rc=%d]", pty->delivered_signal, rc)); | |
632 | return rc; | |
633 | } | |
634 | ||
635 | #ifdef TEST_PROGRAM_PTY | |
636 | /* | |
637 | * $ make test_pty | |
638 | * $ ./test_pty | |
639 | * | |
640 | * ... and see for example tty(1) or "ps afu" | |
641 | */ | |
bdd43357 | 642 | static void child_sigstop(void *data __attribute__((__unused__)), pid_t child) |
6954895c | 643 | { |
6954895c | 644 | kill(getpid(), SIGSTOP); |
bdd43357 | 645 | kill(child, SIGCONT); |
6954895c KZ |
646 | } |
647 | ||
648 | int main(int argc, char *argv[]) | |
649 | { | |
6954895c KZ |
650 | struct ul_pty_callbacks *cb; |
651 | const char *shell, *command = NULL, *shname = NULL; | |
652 | int caught_signal = 0; | |
bdd43357 | 653 | pid_t child; |
8cf448e9 | 654 | struct ul_pty *pty; |
6954895c KZ |
655 | |
656 | shell = getenv("SHELL"); | |
657 | if (shell == NULL) | |
658 | shell = _PATH_BSHELL; | |
659 | if (argc == 2) | |
660 | command = argv[1]; | |
661 | ||
662 | ul_pty_init_debug(0); | |
663 | ||
8cf448e9 KZ |
664 | pty = ul_new_pty(isatty(STDIN_FILENO)); |
665 | if (!pty) | |
6954895c KZ |
666 | err(EXIT_FAILURE, "failed to allocate PTY handler"); |
667 | ||
8cf448e9 | 668 | cb = ul_pty_get_callbacks(pty); |
6954895c KZ |
669 | cb->child_sigstop = child_sigstop; |
670 | ||
8cf448e9 | 671 | if (ul_pty_setup(pty)) |
6954895c KZ |
672 | err(EXIT_FAILURE, "failed to create pseudo-terminal"); |
673 | ||
674 | fflush(stdout); /* ??? */ | |
675 | ||
bdd43357 | 676 | switch ((int) (child = fork())) { |
6954895c | 677 | case -1: /* error */ |
8cf448e9 | 678 | ul_pty_cleanup(pty); |
6954895c KZ |
679 | err(EXIT_FAILURE, "cannot create child process"); |
680 | break; | |
681 | ||
682 | case 0: /* child */ | |
8cf448e9 | 683 | ul_pty_init_slave(pty); |
6954895c KZ |
684 | |
685 | signal(SIGTERM, SIG_DFL); /* because /etc/csh.login */ | |
686 | ||
687 | shname = strrchr(shell, '/'); | |
688 | shname = shname ? shname + 1 : shell; | |
689 | ||
690 | if (command) | |
691 | execl(shell, shname, "-c", command, NULL); | |
692 | else | |
693 | execl(shell, shname, "-i", NULL); | |
694 | err(EXIT_FAILURE, "failed to execute %s", shell); | |
695 | break; | |
696 | ||
697 | default: | |
698 | break; | |
699 | } | |
700 | ||
701 | /* parent */ | |
8cf448e9 | 702 | ul_pty_set_child(pty, child); |
6954895c KZ |
703 | |
704 | /* this is the main loop */ | |
8cf448e9 | 705 | ul_pty_proxy_master(pty); |
6954895c KZ |
706 | |
707 | /* all done; cleanup and kill */ | |
8cf448e9 | 708 | caught_signal = ul_pty_get_delivered_signal(pty); |
6954895c | 709 | |
8cf448e9 KZ |
710 | if (!caught_signal && ul_pty_get_child(pty) != (pid_t)-1) |
711 | ul_pty_wait_for_child(pty); /* final wait */ | |
6954895c | 712 | |
8cf448e9 | 713 | if (caught_signal && ul_pty_get_child(pty) != (pid_t)-1) { |
6954895c | 714 | fprintf(stderr, "\nSession terminated, killing shell..."); |
bdd43357 | 715 | kill(child, SIGTERM); |
6954895c | 716 | sleep(2); |
bdd43357 | 717 | kill(child, SIGKILL); |
6954895c KZ |
718 | fprintf(stderr, " ...killed.\n"); |
719 | } | |
720 | ||
8cf448e9 KZ |
721 | ul_pty_cleanup(pty); |
722 | ul_free_pty(pty); | |
6954895c KZ |
723 | return EXIT_SUCCESS; |
724 | } | |
725 | ||
726 | #endif /* TEST_PROGRAM */ | |
727 |