]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/pty.c
util: rework rm_rf() logic
[thirdparty/systemd.git] / src / shared / pty.c
CommitLineData
a47d1dfd
DH
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22/*
23 * PTY
24 * A PTY object represents a single PTY connection between a master and a
25 * child. The child process is fork()ed so the caller controls what program
26 * will be run.
27 *
28 * Programs like /bin/login tend to perform a vhangup() on their TTY
29 * before running the login procedure. This also causes the pty master
30 * to get a EPOLLHUP event as long as no client has the TTY opened.
31 * This means, we cannot use the TTY connection as reliable way to track
32 * the client. Instead, we _must_ rely on the PID of the client to track
33 * them.
34 * However, this has the side effect that if the client forks and the
35 * parent exits, we loose them and restart the client. But this seems to
36 * be the expected behavior so we implement it here.
37 *
38 * Unfortunately, epoll always polls for EPOLLHUP so as long as the
39 * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep.
40 * This gets worse if the client closes the TTY but doesn't exit.
41 * Therefore, the fd must be edge-triggered in the epoll-set so we
42 * only get the events once they change.
43 */
44
45#include <errno.h>
46#include <fcntl.h>
a47d1dfd
DH
47#include <signal.h>
48#include <stdbool.h>
49#include <stdint.h>
a47d1dfd 50#include <stdlib.h>
a47d1dfd 51#include <sys/epoll.h>
a47d1dfd 52#include <sys/ioctl.h>
a47d1dfd
DH
53#include <sys/uio.h>
54#include <sys/wait.h>
55#include <termios.h>
56#include <unistd.h>
57
58#include "barrier.h"
59#include "macro.h"
60#include "pty.h"
61#include "ring.h"
62#include "util.h"
63
48fed5c5 64#define PTY_BUFSIZE 4096
a47d1dfd
DH
65
66enum {
67 PTY_ROLE_UNKNOWN,
68 PTY_ROLE_PARENT,
69 PTY_ROLE_CHILD,
70};
71
72struct Pty {
73 unsigned long ref;
74 Barrier barrier;
75 int fd;
76 pid_t child;
77 sd_event_source *fd_source;
78 sd_event_source *child_source;
79
80 char in_buf[PTY_BUFSIZE];
81 Ring out_buf;
82
83 pty_event_t event_fn;
84 void *event_fn_userdata;
85
86 bool needs_requeue : 1;
87 unsigned int role : 2;
88};
89
90int pty_new(Pty **out) {
91 _pty_unref_ Pty *pty = NULL;
92 int r;
93
94 assert_return(out, -EINVAL);
95
96 pty = new0(Pty, 1);
97 if (!pty)
98 return -ENOMEM;
99
100 pty->ref = 1;
101 pty->fd = -1;
7566e267 102 pty->barrier = (Barrier) BARRIER_NULL;
a47d1dfd
DH
103
104 pty->fd = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK);
105 if (pty->fd < 0)
106 return -errno;
107
108 /*
109 * The slave-node is initialized to uid/gid of the caller of
110 * posix_openpt(). Only if devpts is mounted with fixed uid/gid this is
111 * skipped. In that case, grantpt() can overwrite these, but then you
112 * have to be root to use chown() (or a pt_chown helper has to be
113 * present). In those cases grantpt() really does something,
114 * otherwise it's a no-op. We call grantpt() here to try supporting
115 * those cases, even though no-one uses that, I guess. If you need other
116 * access-rights, set them yourself after this call returns (no, this is
117 * not racy, it looks racy, but races regarding your own UID are never
118 * important as an attacker could ptrace you; and the slave-pty is also
119 * still locked).
120 */
121 r = grantpt(pty->fd);
122 if (r < 0)
123 return -errno;
124
7566e267 125 r = barrier_create(&pty->barrier);
a47d1dfd
DH
126 if (r < 0)
127 return r;
128
129 *out = pty;
130 pty = NULL;
131 return 0;
132}
133
134Pty *pty_ref(Pty *pty) {
135 if (!pty || pty->ref < 1)
136 return NULL;
137
138 ++pty->ref;
139 return pty;
140}
141
142Pty *pty_unref(Pty *pty) {
143 if (!pty || pty->ref < 1 || --pty->ref > 0)
144 return NULL;
145
146 pty_close(pty);
147 pty->child_source = sd_event_source_unref(pty->child_source);
148 barrier_destroy(&pty->barrier);
149 ring_clear(&pty->out_buf);
150 free(pty);
151
152 return NULL;
153}
154
155Barrier *pty_get_barrier(Pty *pty) {
156 assert(pty);
157 return &pty->barrier;
158}
159
160bool pty_is_unknown(Pty *pty) {
161 return pty && pty->role == PTY_ROLE_UNKNOWN;
162}
163
164bool pty_is_parent(Pty *pty) {
165 return pty && pty->role == PTY_ROLE_PARENT;
166}
167
168bool pty_is_child(Pty *pty) {
169 return pty && pty->role == PTY_ROLE_CHILD;
170}
171
172bool pty_has_child(Pty *pty) {
173 return pty_is_parent(pty) && pty->child > 0;
174}
175
176pid_t pty_get_child(Pty *pty) {
177 return pty_has_child(pty) ? pty->child : -ECHILD;
178}
179
180bool pty_is_open(Pty *pty) {
181 return pty && pty->fd >= 0;
182}
183
184int pty_get_fd(Pty *pty) {
185 assert_return(pty, -EINVAL);
186
187 return pty_is_open(pty) ? pty->fd : -EPIPE;
188}
189
190int pty_make_child(Pty *pty) {
611b312b 191 _cleanup_free_ char *slave_name = NULL;
a47d1dfd
DH
192 int r, fd;
193
194 assert_return(pty, -EINVAL);
195 assert_return(pty_is_unknown(pty), -EALREADY);
196
611b312b 197 r = ptsname_malloc(pty->fd, &slave_name);
a47d1dfd
DH
198 if (r < 0)
199 return -errno;
200
201 fd = open(slave_name, O_RDWR | O_CLOEXEC | O_NOCTTY);
202 if (fd < 0)
203 return -errno;
204
205 safe_close(pty->fd);
206 pty->fd = fd;
207 pty->child = getpid();
208 pty->role = PTY_ROLE_CHILD;
209 barrier_set_role(&pty->barrier, BARRIER_CHILD);
210
211 return 0;
212}
213
214int pty_make_parent(Pty *pty, pid_t child) {
215 assert_return(pty, -EINVAL);
216 assert_return(pty_is_unknown(pty), -EALREADY);
217
218 pty->child = child;
219 pty->role = PTY_ROLE_PARENT;
220
221 return 0;
222}
223
224int pty_unlock(Pty *pty) {
225 assert_return(pty, -EINVAL);
226 assert_return(pty_is_unknown(pty) || pty_is_parent(pty), -EINVAL);
227 assert_return(pty_is_open(pty), -ENODEV);
228
229 return unlockpt(pty->fd) < 0 ? -errno : 0;
230}
231
232int pty_setup_child(Pty *pty) {
233 struct termios attr;
234 pid_t pid;
235 int r;
236
237 assert_return(pty, -EINVAL);
238 assert_return(pty_is_child(pty), -EINVAL);
239 assert_return(pty_is_open(pty), -EALREADY);
240
241 r = sigprocmask_many(SIG_SETMASK, -1);
242 if (r < 0)
243 return r;
244
245 r = reset_all_signal_handlers();
246 if (r < 0)
247 return r;
248
249 pid = setsid();
250 if (pid < 0 && errno != EPERM)
251 return -errno;
252
253 r = ioctl(pty->fd, TIOCSCTTY, 0);
254 if (r < 0)
255 return -errno;
256
257 r = tcgetattr(pty->fd, &attr);
258 if (r < 0)
259 return -errno;
260
261 /* erase character should be normal backspace, PLEASEEE! */
262 attr.c_cc[VERASE] = 010;
263 /* always set UTF8 flag */
264 attr.c_iflag |= IUTF8;
265
266 r = tcsetattr(pty->fd, TCSANOW, &attr);
267 if (r < 0)
268 return -errno;
269
270 if (dup2(pty->fd, STDIN_FILENO) != STDIN_FILENO ||
271 dup2(pty->fd, STDOUT_FILENO) != STDOUT_FILENO ||
272 dup2(pty->fd, STDERR_FILENO) != STDERR_FILENO)
273 return -errno;
274
275 /* only close FD if it's not a std-fd */
276 pty->fd = (pty->fd > 2) ? safe_close(pty->fd) : -1;
277
278 return 0;
279}
280
281void pty_close(Pty *pty) {
282 if (!pty_is_open(pty))
283 return;
284
285 pty->fd_source = sd_event_source_unref(pty->fd_source);
286 pty->fd = safe_close(pty->fd);
287}
288
289/*
290 * Drain input-queue and dispatch data via the event-handler. Returns <0 on
291 * error, 0 if queue is empty and 1 if we couldn't empty the input queue fast
292 * enough and there's still data left.
293 */
294static int pty_dispatch_read(Pty *pty) {
295 unsigned int i;
296 ssize_t len;
297 int r;
298
299 /*
300 * We're edge-triggered, means we need to read the whole queue. This,
301 * however, might cause us to stall if the writer is faster than we
48fed5c5
DH
302 * are. Therefore, try reading as much as 8 times (32KiB) and only
303 * bail out then.
a47d1dfd
DH
304 */
305
48fed5c5 306 for (i = 0; i < 8; ++i) {
a47d1dfd
DH
307 len = read(pty->fd, pty->in_buf, sizeof(pty->in_buf) - 1);
308 if (len < 0) {
309 if (errno == EINTR)
310 continue;
311
312 return (errno == EAGAIN) ? 0 : -errno;
313 } else if (len == 0) {
314 continue;
315 }
316
317 /* set terminating zero for debugging safety */
318 pty->in_buf[len] = 0;
319 r = pty->event_fn(pty, pty->event_fn_userdata, PTY_DATA, pty->in_buf, len);
320 if (r < 0)
321 return r;
322 }
323
324 /* still data left, make sure we're queued again */
325 pty->needs_requeue = true;
326
327 return 1;
328}
329
330/*
331 * Drain output-queue by writing data to the pty. Returns <0 on error, 0 if the
332 * output queue is empty now and 1 if we couldn't empty the output queue fast
333 * enough and there's still data left.
334 */
335static int pty_dispatch_write(Pty *pty) {
336 struct iovec vec[2];
337 unsigned int i;
338 ssize_t len;
339 size_t num;
340
341 /*
342 * Same as pty_dispatch_read(), we're edge-triggered so we need to call
343 * write() until either all data is written or it returns EAGAIN. We
344 * call it twice and if it still writes successfully, we reschedule.
345 */
346
347 for (i = 0; i < 2; ++i) {
348 num = ring_peek(&pty->out_buf, vec);
349 if (num < 1)
350 return 0;
351
352 len = writev(pty->fd, vec, (int)num);
353 if (len < 0) {
354 if (errno == EINTR)
355 continue;
356
357 return (errno == EAGAIN) ? 1 : -errno;
358 } else if (len == 0) {
359 continue;
360 }
361
362 ring_pull(&pty->out_buf, (size_t)len);
363 }
364
365 /* still data left, make sure we're queued again */
366 if (ring_get_size(&pty->out_buf) > 0) {
367 pty->needs_requeue = true;
368 return 1;
369 }
370
371 return 0;
372}
373
374static int pty_fd_fn(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
375 Pty *pty = userdata;
376 int r_hup = 0, r_write = 0, r_read = 0, r;
377
378 /*
379 * Whenever we encounter I/O errors, we have to make sure to drain the
380 * input queue first, before we handle any HUP. A child might send us
381 * a message and immediately close the queue. We must not handle the
382 * HUP first or we loose data.
383 * Therefore, if we read a message successfully, we always return
384 * success and wait for the next event-loop iteration. Furthermore,
385 * whenever there is a write-error, we must try reading from the input
386 * queue even if EPOLLIN is not set. The input might have arrived in
387 * between epoll_wait() and write(). Therefore, write-errors are only
388 * ever handled if the input-queue is empty. In all other cases they
389 * are ignored until either reading fails or the input queue is empty.
390 */
391
392 if (revents & (EPOLLHUP | EPOLLERR))
393 r_hup = -EPIPE;
394
395 if (revents & EPOLLOUT)
396 r_write = pty_dispatch_write(pty);
397
398 /* Awesome! Kernel signals HUP without IN but queues are not empty.. */
399 if ((revents & EPOLLIN) || r_hup < 0 || r_write < 0) {
400 r_read = pty_dispatch_read(pty);
401 if (r_read > 0)
402 return 0; /* still data left to fetch next round */
403 }
404
405 if (r_hup < 0 || r_write < 0 || r_read < 0) {
406 /* PTY closed and input-queue drained */
407 pty_close(pty);
408 r = pty->event_fn(pty, pty->event_fn_userdata, PTY_HUP, NULL, 0);
409 if (r < 0)
410 return r;
411 }
412
413 return 0;
414}
415
416static int pty_fd_prepare_fn(sd_event_source *source, void *userdata) {
417 Pty *pty = userdata;
418 int r;
419
420 if (pty->needs_requeue) {
421 /*
422 * We're edge-triggered. In case we couldn't handle all events
423 * or in case new write-data is queued, we set needs_requeue.
424 * Before going asleep, we set the io-events *again*. sd-event
425 * notices that we're edge-triggered and forwards the call to
426 * the kernel even if the events didn't change. The kernel will
427 * check the events and re-queue us on the ready queue in case
428 * an event is pending.
429 */
430 r = sd_event_source_set_io_events(source, EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET);
431 if (r >= 0)
432 pty->needs_requeue = false;
433 }
434
435 return 0;
436}
437
438static int pty_child_fn(sd_event_source *source, const siginfo_t *si, void *userdata) {
439 Pty *pty = userdata;
440 int r;
441
442 pty->child = 0;
443
444 r = pty->event_fn(pty, pty->event_fn_userdata, PTY_CHILD, si, sizeof(*si));
445 if (r < 0)
446 return r;
447
448 return 0;
449}
450
451int pty_attach_event(Pty *pty, sd_event *event, pty_event_t event_fn, void *event_fn_userdata) {
452 int r;
453
454 assert_return(pty, -EINVAL);
455 assert_return(event, -EINVAL);
456 assert_return(event_fn, -EINVAL);
457 assert_return(pty_is_parent(pty), -EINVAL);
458
459 pty_detach_event(pty);
460
461 if (pty_is_open(pty)) {
462 r = sd_event_add_io(event,
463 &pty->fd_source,
464 pty->fd,
465 EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET,
466 pty_fd_fn,
467 pty);
468 if (r < 0)
469 goto error;
470
471 r = sd_event_source_set_prepare(pty->fd_source, pty_fd_prepare_fn);
472 if (r < 0)
473 goto error;
474 }
475
476 if (pty_has_child(pty)) {
477 r = sd_event_add_child(event,
478 &pty->child_source,
479 pty->child,
480 WEXITED,
481 pty_child_fn,
482 pty);
483 if (r < 0)
484 goto error;
485 }
486
487 pty->event_fn = event_fn;
488 pty->event_fn_userdata = event_fn_userdata;
489
490 return 0;
491
492error:
493 pty_detach_event(pty);
494 return r;
495}
496
497void pty_detach_event(Pty *pty) {
498 if (!pty)
499 return;
500
501 pty->child_source = sd_event_source_unref(pty->child_source);
502 pty->fd_source = sd_event_source_unref(pty->fd_source);
503 pty->event_fn = NULL;
504 pty->event_fn_userdata = NULL;
505}
506
507int pty_write(Pty *pty, const void *buf, size_t size) {
508 bool was_empty;
509 int r;
510
511 assert_return(pty, -EINVAL);
512 assert_return(pty_is_open(pty), -ENODEV);
513 assert_return(pty_is_parent(pty), -ENODEV);
514
515 if (size < 1)
516 return 0;
517
518 /*
519 * Push @buf[0..@size] into the output ring-buffer. In case the
520 * ring-buffer wasn't empty beforehand, we're already waiting for
521 * EPOLLOUT and we're done. If it was empty, we have to re-queue the
522 * FD for EPOLLOUT as we're edge-triggered and wouldn't get any new
523 * EPOLLOUT event.
524 */
525
526 was_empty = ring_get_size(&pty->out_buf) < 1;
527
528 r = ring_push(&pty->out_buf, buf, size);
529 if (r < 0)
530 return r;
531
532 if (was_empty)
533 pty->needs_requeue = true;
534
535 return 0;
536}
537
538int pty_signal(Pty *pty, int sig) {
539 assert_return(pty, -EINVAL);
540 assert_return(pty_is_open(pty), -ENODEV);
541 assert_return(pty_is_parent(pty), -ENODEV);
542
543 return ioctl(pty->fd, TIOCSIG, sig) < 0 ? -errno : 0;
544}
545
546int pty_resize(Pty *pty, unsigned short term_width, unsigned short term_height) {
aa0fff7f
LP
547 struct winsize ws = {
548 .ws_col = term_width,
549 .ws_row = term_height,
550 };
a47d1dfd
DH
551
552 assert_return(pty, -EINVAL);
553 assert_return(pty_is_open(pty), -ENODEV);
554 assert_return(pty_is_parent(pty), -ENODEV);
555
a47d1dfd
DH
556 /*
557 * This will send SIGWINCH to the pty slave foreground process group.
558 * We will also get one, but we don't need it.
559 */
560 return ioctl(pty->fd, TIOCSWINSZ, &ws) < 0 ? -errno : 0;
561}
562
563pid_t pty_fork(Pty **out, sd_event *event, pty_event_t event_fn, void *event_fn_userdata, unsigned short initial_term_width, unsigned short initial_term_height) {
564 _pty_unref_ Pty *pty = NULL;
565 int r;
566 pid_t pid;
567
568 assert_return(out, -EINVAL);
569 assert_return((event && event_fn) || (!event && !event_fn), -EINVAL);
570
571 r = pty_new(&pty);
572 if (r < 0)
573 return r;
574
575 r = pty_unlock(pty);
576 if (r < 0)
577 return r;
578
579 pid = fork();
580 if (pid < 0)
581 return -errno;
582
583 if (pid == 0) {
584 /* child */
585
586 r = pty_make_child(pty);
587 if (r < 0)
588 _exit(-r);
589
590 r = pty_setup_child(pty);
591 if (r < 0)
592 _exit(-r);
593
594 /* sync with parent */
595 if (!barrier_place_and_sync(&pty->barrier))
596 _exit(1);
597
598 /* fallthrough and return the child's PTY object */
599 } else {
600 /* parent */
601
602 r = pty_make_parent(pty, pid);
603 if (r < 0)
604 goto parent_error;
605
606 r = pty_resize(pty, initial_term_width, initial_term_height);
607 if (r < 0)
608 goto parent_error;
609
610 if (event) {
611 r = pty_attach_event(pty, event, event_fn, event_fn_userdata);
612 if (r < 0)
613 goto parent_error;
614 }
615
616 /* sync with child */
617 if (!barrier_place_and_sync(&pty->barrier)) {
618 r = -ECHILD;
619 goto parent_error;
620 }
621
622 /* fallthrough and return the parent's PTY object */
623 }
624
625 *out = pty;
626 pty = NULL;
627 return pid;
628
629parent_error:
630 barrier_abort(&pty->barrier);
631 waitpid(pty->child, NULL, 0);
632 pty->child = 0;
633 return r;
634}