]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/terminal-util.c
build-sys: add missing makefile symlink
[thirdparty/systemd.git] / src / basic / terminal-util.c
CommitLineData
288a74cc
RC
1/***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
07630cea
LP
20#include <assert.h>
21#include <fcntl.h>
22#include <linux/kd.h>
23#include <linux/tiocl.h>
24#include <linux/vt.h>
25#include <poll.h>
26#include <signal.h>
288a74cc 27#include <sys/ioctl.h>
288a74cc 28#include <sys/stat.h>
07630cea 29#include <sys/types.h>
288a74cc 30#include <termios.h>
288a74cc 31#include <time.h>
07630cea 32#include <unistd.h>
288a74cc 33
3ffd4af2 34#include "fd-util.h"
288a74cc 35#include "fileio.h"
c004493c 36#include "io-util.h"
288a74cc 37#include "path-util.h"
07630cea
LP
38#include "process-util.h"
39#include "string-util.h"
3ffd4af2 40#include "terminal-util.h"
07630cea
LP
41#include "time-util.h"
42#include "util.h"
288a74cc
RC
43
44static volatile unsigned cached_columns = 0;
45static volatile unsigned cached_lines = 0;
46
47int chvt(int vt) {
48 _cleanup_close_ int fd;
49
0a8b555c 50 fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
288a74cc
RC
51 if (fd < 0)
52 return -errno;
53
b9e74c39 54 if (vt <= 0) {
288a74cc
RC
55 int tiocl[2] = {
56 TIOCL_GETKMSGREDIRECT,
57 0
58 };
59
60 if (ioctl(fd, TIOCLINUX, tiocl) < 0)
61 return -errno;
62
63 vt = tiocl[0] <= 0 ? 1 : tiocl[0];
64 }
65
66 if (ioctl(fd, VT_ACTIVATE, vt) < 0)
67 return -errno;
68
69 return 0;
70}
71
72int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
73 struct termios old_termios, new_termios;
74 char c, line[LINE_MAX];
75
76 assert(f);
77 assert(ret);
78
79 if (tcgetattr(fileno(f), &old_termios) >= 0) {
80 new_termios = old_termios;
81
82 new_termios.c_lflag &= ~ICANON;
83 new_termios.c_cc[VMIN] = 1;
84 new_termios.c_cc[VTIME] = 0;
85
86 if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
87 size_t k;
88
89 if (t != USEC_INFINITY) {
90 if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
91 tcsetattr(fileno(f), TCSADRAIN, &old_termios);
92 return -ETIMEDOUT;
93 }
94 }
95
96 k = fread(&c, 1, 1, f);
97
98 tcsetattr(fileno(f), TCSADRAIN, &old_termios);
99
100 if (k <= 0)
101 return -EIO;
102
103 if (need_nl)
104 *need_nl = c != '\n';
105
106 *ret = c;
107 return 0;
108 }
109 }
110
111 if (t != USEC_INFINITY) {
112 if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
113 return -ETIMEDOUT;
114 }
115
116 errno = 0;
117 if (!fgets(line, sizeof(line), f))
118 return errno ? -errno : -EIO;
119
120 truncate_nl(line);
121
122 if (strlen(line) != 1)
123 return -EBADMSG;
124
125 if (need_nl)
126 *need_nl = false;
127
128 *ret = line[0];
129 return 0;
130}
131
132int ask_char(char *ret, const char *replies, const char *text, ...) {
133 int r;
134
135 assert(ret);
136 assert(replies);
137 assert(text);
138
139 for (;;) {
140 va_list ap;
141 char c;
142 bool need_nl = true;
143
144 if (on_tty())
1fc464f6 145 fputs(ANSI_HIGHLIGHT, stdout);
288a74cc
RC
146
147 va_start(ap, text);
148 vprintf(text, ap);
149 va_end(ap);
150
151 if (on_tty())
1fc464f6 152 fputs(ANSI_NORMAL, stdout);
288a74cc
RC
153
154 fflush(stdout);
155
156 r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl);
157 if (r < 0) {
158
159 if (r == -EBADMSG) {
160 puts("Bad input, please try again.");
161 continue;
162 }
163
164 putchar('\n');
165 return r;
166 }
167
168 if (need_nl)
169 putchar('\n');
170
171 if (strchr(replies, c)) {
172 *ret = c;
173 return 0;
174 }
175
176 puts("Read unexpected character, please try again.");
177 }
178}
179
180int ask_string(char **ret, const char *text, ...) {
181 assert(ret);
182 assert(text);
183
184 for (;;) {
185 char line[LINE_MAX];
186 va_list ap;
187
188 if (on_tty())
1fc464f6 189 fputs(ANSI_HIGHLIGHT, stdout);
288a74cc
RC
190
191 va_start(ap, text);
192 vprintf(text, ap);
193 va_end(ap);
194
195 if (on_tty())
1fc464f6 196 fputs(ANSI_NORMAL, stdout);
288a74cc
RC
197
198 fflush(stdout);
199
200 errno = 0;
201 if (!fgets(line, sizeof(line), stdin))
202 return errno ? -errno : -EIO;
203
204 if (!endswith(line, "\n"))
205 putchar('\n');
206 else {
207 char *s;
208
209 if (isempty(line))
210 continue;
211
212 truncate_nl(line);
213 s = strdup(line);
214 if (!s)
215 return -ENOMEM;
216
217 *ret = s;
218 return 0;
219 }
220 }
221}
222
223int reset_terminal_fd(int fd, bool switch_to_text) {
224 struct termios termios;
225 int r = 0;
226
227 /* Set terminal to some sane defaults */
228
229 assert(fd >= 0);
230
231 /* We leave locked terminal attributes untouched, so that
232 * Plymouth may set whatever it wants to set, and we don't
233 * interfere with that. */
234
235 /* Disable exclusive mode, just in case */
7d927c9a 236 (void) ioctl(fd, TIOCNXCL);
288a74cc
RC
237
238 /* Switch to text mode */
239 if (switch_to_text)
7d927c9a 240 (void) ioctl(fd, KDSETMODE, KD_TEXT);
288a74cc
RC
241
242 /* Enable console unicode mode */
7d927c9a 243 (void) ioctl(fd, KDSKBMODE, K_UNICODE);
288a74cc
RC
244
245 if (tcgetattr(fd, &termios) < 0) {
246 r = -errno;
247 goto finish;
248 }
249
250 /* We only reset the stuff that matters to the software. How
251 * hardware is set up we don't touch assuming that somebody
252 * else will do that for us */
253
254 termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
255 termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
256 termios.c_oflag |= ONLCR;
257 termios.c_cflag |= CREAD;
258 termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
259
260 termios.c_cc[VINTR] = 03; /* ^C */
261 termios.c_cc[VQUIT] = 034; /* ^\ */
262 termios.c_cc[VERASE] = 0177;
263 termios.c_cc[VKILL] = 025; /* ^X */
264 termios.c_cc[VEOF] = 04; /* ^D */
265 termios.c_cc[VSTART] = 021; /* ^Q */
266 termios.c_cc[VSTOP] = 023; /* ^S */
267 termios.c_cc[VSUSP] = 032; /* ^Z */
268 termios.c_cc[VLNEXT] = 026; /* ^V */
269 termios.c_cc[VWERASE] = 027; /* ^W */
270 termios.c_cc[VREPRINT] = 022; /* ^R */
271 termios.c_cc[VEOL] = 0;
272 termios.c_cc[VEOL2] = 0;
273
274 termios.c_cc[VTIME] = 0;
275 termios.c_cc[VMIN] = 1;
276
277 if (tcsetattr(fd, TCSANOW, &termios) < 0)
278 r = -errno;
279
280finish:
281 /* Just in case, flush all crap out */
7d927c9a 282 (void) tcflush(fd, TCIOFLUSH);
288a74cc
RC
283
284 return r;
285}
286
287int reset_terminal(const char *name) {
288 _cleanup_close_ int fd = -1;
289
0a8b555c
LP
290 /* We open the terminal with O_NONBLOCK here, to ensure we
291 * don't block on carrier if this is a terminal with carrier
292 * configured. */
293
294 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
288a74cc
RC
295 if (fd < 0)
296 return fd;
297
298 return reset_terminal_fd(fd, true);
299}
300
301int open_terminal(const char *name, int mode) {
302 int fd, r;
303 unsigned c = 0;
304
305 /*
306 * If a TTY is in the process of being closed opening it might
307 * cause EIO. This is horribly awful, but unlikely to be
308 * changed in the kernel. Hence we work around this problem by
309 * retrying a couple of times.
310 *
311 * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
312 */
313
35bdab77
LP
314 if (mode & O_CREAT)
315 return -EINVAL;
288a74cc
RC
316
317 for (;;) {
318 fd = open(name, mode, 0);
319 if (fd >= 0)
320 break;
321
322 if (errno != EIO)
323 return -errno;
324
325 /* Max 1s in total */
326 if (c >= 20)
327 return -errno;
328
329 usleep(50 * USEC_PER_MSEC);
330 c++;
331 }
332
333 r = isatty(fd);
334 if (r < 0) {
335 safe_close(fd);
336 return -errno;
337 }
338
339 if (!r) {
340 safe_close(fd);
341 return -ENOTTY;
342 }
343
344 return fd;
345}
346
347int acquire_terminal(
348 const char *name,
349 bool fail,
350 bool force,
351 bool ignore_tiocstty_eperm,
352 usec_t timeout) {
353
354 int fd = -1, notify = -1, r = 0, wd = -1;
355 usec_t ts = 0;
356
357 assert(name);
358
359 /* We use inotify to be notified when the tty is closed. We
360 * create the watch before checking if we can actually acquire
361 * it, so that we don't lose any event.
362 *
363 * Note: strictly speaking this actually watches for the
364 * device being closed, it does *not* really watch whether a
365 * tty loses its controlling process. However, unless some
366 * rogue process uses TIOCNOTTY on /dev/tty *after* closing
367 * its tty otherwise this will not become a problem. As long
368 * as the administrator makes sure not configure any service
369 * on the same tty as an untrusted user this should not be a
370 * problem. (Which he probably should not do anyway.) */
371
372 if (timeout != USEC_INFINITY)
373 ts = now(CLOCK_MONOTONIC);
374
375 if (!fail && !force) {
376 notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
377 if (notify < 0) {
378 r = -errno;
379 goto fail;
380 }
381
382 wd = inotify_add_watch(notify, name, IN_CLOSE);
383 if (wd < 0) {
384 r = -errno;
385 goto fail;
386 }
387 }
388
389 for (;;) {
390 struct sigaction sa_old, sa_new = {
391 .sa_handler = SIG_IGN,
392 .sa_flags = SA_RESTART,
393 };
394
395 if (notify >= 0) {
396 r = flush_fd(notify);
397 if (r < 0)
398 goto fail;
399 }
400
401 /* We pass here O_NOCTTY only so that we can check the return
402 * value TIOCSCTTY and have a reliable way to figure out if we
403 * successfully became the controlling process of the tty */
404 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
405 if (fd < 0)
406 return fd;
407
408 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
409 * if we already own the tty. */
410 assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
411
412 /* First, try to get the tty */
413 if (ioctl(fd, TIOCSCTTY, force) < 0)
414 r = -errno;
415
416 assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
417
418 /* Sometimes it makes sense to ignore TIOCSCTTY
419 * returning EPERM, i.e. when very likely we already
420 * are have this controlling terminal. */
421 if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
422 r = 0;
423
7d927c9a 424 if (r < 0 && (force || fail || r != -EPERM))
288a74cc 425 goto fail;
288a74cc
RC
426
427 if (r >= 0)
428 break;
429
430 assert(!fail);
431 assert(!force);
432 assert(notify >= 0);
433
434 for (;;) {
435 union inotify_event_buffer buffer;
436 struct inotify_event *e;
437 ssize_t l;
438
439 if (timeout != USEC_INFINITY) {
440 usec_t n;
441
442 n = now(CLOCK_MONOTONIC);
443 if (ts + timeout < n) {
444 r = -ETIMEDOUT;
445 goto fail;
446 }
447
448 r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
449 if (r < 0)
450 goto fail;
451
452 if (r == 0) {
453 r = -ETIMEDOUT;
454 goto fail;
455 }
456 }
457
458 l = read(notify, &buffer, sizeof(buffer));
459 if (l < 0) {
460 if (errno == EINTR || errno == EAGAIN)
461 continue;
462
463 r = -errno;
464 goto fail;
465 }
466
467 FOREACH_INOTIFY_EVENT(e, buffer, l) {
468 if (e->wd != wd || !(e->mask & IN_CLOSE)) {
469 r = -EIO;
470 goto fail;
471 }
472 }
473
474 break;
475 }
476
477 /* We close the tty fd here since if the old session
478 * ended our handle will be dead. It's important that
479 * we do this after sleeping, so that we don't enter
480 * an endless loop. */
481 fd = safe_close(fd);
482 }
483
484 safe_close(notify);
485
288a74cc
RC
486 return fd;
487
488fail:
489 safe_close(fd);
490 safe_close(notify);
491
492 return r;
493}
494
495int release_terminal(void) {
496 static const struct sigaction sa_new = {
497 .sa_handler = SIG_IGN,
498 .sa_flags = SA_RESTART,
499 };
500
501 _cleanup_close_ int fd = -1;
502 struct sigaction sa_old;
503 int r = 0;
504
0a8b555c 505 fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
288a74cc
RC
506 if (fd < 0)
507 return -errno;
508
509 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
510 * by our own TIOCNOTTY */
511 assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
512
513 if (ioctl(fd, TIOCNOTTY) < 0)
514 r = -errno;
515
516 assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
517
518 return r;
519}
520
521int terminal_vhangup_fd(int fd) {
522 assert(fd >= 0);
523
524 if (ioctl(fd, TIOCVHANGUP) < 0)
525 return -errno;
526
527 return 0;
528}
529
530int terminal_vhangup(const char *name) {
531 _cleanup_close_ int fd;
532
0a8b555c 533 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
288a74cc
RC
534 if (fd < 0)
535 return fd;
536
537 return terminal_vhangup_fd(fd);
538}
539
540int vt_disallocate(const char *name) {
1ba23931 541 _cleanup_close_ int fd = -1;
288a74cc 542 unsigned u;
1ba23931 543 int r;
288a74cc
RC
544
545 /* Deallocate the VT if possible. If not possible
546 * (i.e. because it is the active one), at least clear it
547 * entirely (including the scrollback buffer) */
548
549 if (!startswith(name, "/dev/"))
550 return -EINVAL;
551
552 if (!tty_is_vc(name)) {
553 /* So this is not a VT. I guess we cannot deallocate
554 * it then. But let's at least clear the screen */
555
556 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
557 if (fd < 0)
558 return fd;
559
560 loop_write(fd,
561 "\033[r" /* clear scrolling region */
562 "\033[H" /* move home */
563 "\033[2J", /* clear screen */
564 10, false);
288a74cc
RC
565 return 0;
566 }
567
568 if (!startswith(name, "/dev/tty"))
569 return -EINVAL;
570
571 r = safe_atou(name+8, &u);
572 if (r < 0)
573 return r;
574
575 if (u <= 0)
576 return -EINVAL;
577
578 /* Try to deallocate */
0a8b555c 579 fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
288a74cc
RC
580 if (fd < 0)
581 return fd;
582
583 r = ioctl(fd, VT_DISALLOCATE, u);
1ba23931 584 fd = safe_close(fd);
288a74cc
RC
585
586 if (r >= 0)
587 return 0;
588
589 if (errno != EBUSY)
590 return -errno;
591
592 /* Couldn't deallocate, so let's clear it fully with
593 * scrollback */
594 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
595 if (fd < 0)
596 return fd;
597
598 loop_write(fd,
599 "\033[r" /* clear scrolling region */
600 "\033[H" /* move home */
601 "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
602 10, false);
288a74cc
RC
603 return 0;
604}
605
288a74cc
RC
606int make_console_stdio(void) {
607 int fd, r;
608
609 /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
610
611 fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY);
612 if (fd < 0)
613 return log_error_errno(fd, "Failed to acquire terminal: %m");
614
3d18b167
LP
615 r = reset_terminal_fd(fd, true);
616 if (r < 0)
617 log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
618
288a74cc
RC
619 r = make_stdio(fd);
620 if (r < 0)
621 return log_error_errno(r, "Failed to duplicate terminal fd: %m");
622
623 return 0;
624}
625
626int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
627 static const char status_indent[] = " "; /* "[" STATUS "] " */
628 _cleanup_free_ char *s = NULL;
629 _cleanup_close_ int fd = -1;
630 struct iovec iovec[6] = {};
631 int n = 0;
632 static bool prev_ephemeral;
633
634 assert(format);
635
636 /* This is independent of logging, as status messages are
637 * optional and go exclusively to the console. */
638
639 if (vasprintf(&s, format, ap) < 0)
640 return log_oom();
641
642 fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
643 if (fd < 0)
644 return fd;
645
646 if (ellipse) {
647 char *e;
648 size_t emax, sl;
649 int c;
650
651 c = fd_columns(fd);
652 if (c <= 0)
653 c = 80;
654
655 sl = status ? sizeof(status_indent)-1 : 0;
656
657 emax = c - sl - 1;
658 if (emax < 3)
659 emax = 3;
660
661 e = ellipsize(s, emax, 50);
662 if (e) {
663 free(s);
664 s = e;
665 }
666 }
667
668 if (prev_ephemeral)
669 IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
670 prev_ephemeral = ephemeral;
671
672 if (status) {
673 if (!isempty(status)) {
674 IOVEC_SET_STRING(iovec[n++], "[");
675 IOVEC_SET_STRING(iovec[n++], status);
676 IOVEC_SET_STRING(iovec[n++], "] ");
677 } else
678 IOVEC_SET_STRING(iovec[n++], status_indent);
679 }
680
681 IOVEC_SET_STRING(iovec[n++], s);
682 if (!ephemeral)
683 IOVEC_SET_STRING(iovec[n++], "\n");
684
685 if (writev(fd, iovec, n) < 0)
686 return -errno;
687
688 return 0;
689}
690
691int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
692 va_list ap;
693 int r;
694
695 assert(format);
696
697 va_start(ap, format);
698 r = status_vprintf(status, ellipse, ephemeral, format, ap);
699 va_end(ap);
700
701 return r;
702}
703
704bool tty_is_vc(const char *tty) {
705 assert(tty);
706
707 return vtnr_from_tty(tty) >= 0;
708}
709
710bool tty_is_console(const char *tty) {
711 assert(tty);
712
713 if (startswith(tty, "/dev/"))
714 tty += 5;
715
716 return streq(tty, "console");
717}
718
719int vtnr_from_tty(const char *tty) {
720 int i, r;
721
722 assert(tty);
723
724 if (startswith(tty, "/dev/"))
725 tty += 5;
726
727 if (!startswith(tty, "tty") )
728 return -EINVAL;
729
730 if (tty[3] < '0' || tty[3] > '9')
731 return -EINVAL;
732
733 r = safe_atoi(tty+3, &i);
734 if (r < 0)
735 return r;
736
737 if (i < 0 || i > 63)
738 return -EINVAL;
739
740 return i;
741}
742
743char *resolve_dev_console(char **active) {
744 char *tty;
745
746 /* Resolve where /dev/console is pointing to, if /sys is actually ours
747 * (i.e. not read-only-mounted which is a sign for container setups) */
748
749 if (path_is_read_only_fs("/sys") > 0)
750 return NULL;
751
752 if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
753 return NULL;
754
755 /* If multiple log outputs are configured the last one is what
756 * /dev/console points to */
757 tty = strrchr(*active, ' ');
758 if (tty)
759 tty++;
760 else
761 tty = *active;
762
763 if (streq(tty, "tty0")) {
764 char *tmp;
765
766 /* Get the active VC (e.g. tty1) */
767 if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
768 free(*active);
769 tty = *active = tmp;
770 }
771 }
772
773 return tty;
774}
775
776bool tty_is_vc_resolve(const char *tty) {
777 _cleanup_free_ char *active = NULL;
778
779 assert(tty);
780
781 if (startswith(tty, "/dev/"))
782 tty += 5;
783
784 if (streq(tty, "console")) {
785 tty = resolve_dev_console(&active);
786 if (!tty)
787 return false;
788 }
789
790 return tty_is_vc(tty);
791}
792
793const char *default_term_for_tty(const char *tty) {
794 assert(tty);
795
796 return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
797}
798
799int fd_columns(int fd) {
800 struct winsize ws = {};
801
802 if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
803 return -errno;
804
805 if (ws.ws_col <= 0)
806 return -EIO;
807
808 return ws.ws_col;
809}
810
811unsigned columns(void) {
812 const char *e;
813 int c;
814
815 if (_likely_(cached_columns > 0))
816 return cached_columns;
817
818 c = 0;
819 e = getenv("COLUMNS");
820 if (e)
821 (void) safe_atoi(e, &c);
822
823 if (c <= 0)
824 c = fd_columns(STDOUT_FILENO);
825
826 if (c <= 0)
827 c = 80;
828
829 cached_columns = c;
830 return cached_columns;
831}
832
833int fd_lines(int fd) {
834 struct winsize ws = {};
835
836 if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
837 return -errno;
838
839 if (ws.ws_row <= 0)
840 return -EIO;
841
842 return ws.ws_row;
843}
844
845unsigned lines(void) {
846 const char *e;
847 int l;
848
849 if (_likely_(cached_lines > 0))
850 return cached_lines;
851
852 l = 0;
853 e = getenv("LINES");
854 if (e)
855 (void) safe_atoi(e, &l);
856
857 if (l <= 0)
858 l = fd_lines(STDOUT_FILENO);
859
860 if (l <= 0)
861 l = 24;
862
863 cached_lines = l;
864 return cached_lines;
865}
866
867/* intended to be used as a SIGWINCH sighandler */
868void columns_lines_cache_reset(int signum) {
869 cached_columns = 0;
870 cached_lines = 0;
871}
872
873bool on_tty(void) {
874 static int cached_on_tty = -1;
875
876 if (_unlikely_(cached_on_tty < 0))
877 cached_on_tty = isatty(STDOUT_FILENO) > 0;
878
879 return cached_on_tty;
880}
881
882int make_stdio(int fd) {
883 int r, s, t;
884
885 assert(fd >= 0);
886
887 r = dup2(fd, STDIN_FILENO);
888 s = dup2(fd, STDOUT_FILENO);
889 t = dup2(fd, STDERR_FILENO);
890
891 if (fd >= 3)
892 safe_close(fd);
893
894 if (r < 0 || s < 0 || t < 0)
895 return -errno;
896
897 /* Explicitly unset O_CLOEXEC, since if fd was < 3, then
898 * dup2() was a NOP and the bit hence possibly set. */
899 fd_cloexec(STDIN_FILENO, false);
900 fd_cloexec(STDOUT_FILENO, false);
901 fd_cloexec(STDERR_FILENO, false);
902
903 return 0;
904}
905
906int make_null_stdio(void) {
907 int null_fd;
908
909 null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
910 if (null_fd < 0)
911 return -errno;
912
913 return make_stdio(null_fd);
914}
915
916int getttyname_malloc(int fd, char **ret) {
917 size_t l = 100;
918 int r;
919
920 assert(fd >= 0);
921 assert(ret);
922
923 for (;;) {
924 char path[l];
925
926 r = ttyname_r(fd, path, sizeof(path));
927 if (r == 0) {
928 const char *p;
929 char *c;
930
931 p = startswith(path, "/dev/");
932 c = strdup(p ?: path);
933 if (!c)
934 return -ENOMEM;
935
936 *ret = c;
937 return 0;
938 }
939
940 if (r != ERANGE)
941 return -r;
942
943 l *= 2;
944 }
945
946 return 0;
947}
948
949int getttyname_harder(int fd, char **r) {
950 int k;
951 char *s = NULL;
952
953 k = getttyname_malloc(fd, &s);
954 if (k < 0)
955 return k;
956
957 if (streq(s, "tty")) {
958 free(s);
959 return get_ctty(0, NULL, r);
960 }
961
962 *r = s;
963 return 0;
964}
965
966int get_ctty_devnr(pid_t pid, dev_t *d) {
967 int r;
968 _cleanup_free_ char *line = NULL;
969 const char *p;
970 unsigned long ttynr;
971
972 assert(pid >= 0);
973
974 p = procfs_file_alloca(pid, "stat");
975 r = read_one_line_file(p, &line);
976 if (r < 0)
977 return r;
978
979 p = strrchr(line, ')');
980 if (!p)
981 return -EIO;
982
983 p++;
984
985 if (sscanf(p, " "
986 "%*c " /* state */
987 "%*d " /* ppid */
988 "%*d " /* pgrp */
989 "%*d " /* session */
990 "%lu ", /* ttynr */
991 &ttynr) != 1)
992 return -EIO;
993
994 if (major(ttynr) == 0 && minor(ttynr) == 0)
cfeaa44a 995 return -ENXIO;
288a74cc
RC
996
997 if (d)
998 *d = (dev_t) ttynr;
999
1000 return 0;
1001}
1002
1003int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
1004 char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
1005 _cleanup_free_ char *s = NULL;
1006 const char *p;
1007 dev_t devnr;
1008 int k;
1009
1010 assert(r);
1011
1012 k = get_ctty_devnr(pid, &devnr);
1013 if (k < 0)
1014 return k;
1015
1016 sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
1017
1018 k = readlink_malloc(fn, &s);
1019 if (k < 0) {
1020
1021 if (k != -ENOENT)
1022 return k;
1023
1024 /* This is an ugly hack */
1025 if (major(devnr) == 136) {
1026 if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
1027 return -ENOMEM;
1028 } else {
1029 /* Probably something like the ptys which have no
1030 * symlink in /dev/char. Let's return something
1031 * vaguely useful. */
1032
1033 b = strdup(fn + 5);
1034 if (!b)
1035 return -ENOMEM;
1036 }
1037 } else {
1038 if (startswith(s, "/dev/"))
1039 p = s + 5;
1040 else if (startswith(s, "../"))
1041 p = s + 3;
1042 else
1043 p = s;
1044
1045 b = strdup(p);
1046 if (!b)
1047 return -ENOMEM;
1048 }
1049
1050 *r = b;
1051 if (_devnr)
1052 *_devnr = devnr;
1053
1054 return 0;
1055}
a07c35c3 1056
66cb2fde
LP
1057int ptsname_malloc(int fd, char **ret) {
1058 size_t l = 100;
1059
1060 assert(fd >= 0);
1061 assert(ret);
1062
1063 for (;;) {
1064 char *c;
1065
1066 c = new(char, l);
1067 if (!c)
1068 return -ENOMEM;
1069
1070 if (ptsname_r(fd, c, l) == 0) {
1071 *ret = c;
1072 return 0;
1073 }
1074 if (errno != ERANGE) {
1075 free(c);
1076 return -errno;
1077 }
1078
1079 free(c);
1080 l *= 2;
1081 }
1082}
1083
a07c35c3
LP
1084int ptsname_namespace(int pty, char **ret) {
1085 int no = -1, r;
1086
1087 /* Like ptsname(), but doesn't assume that the path is
1088 * accessible in the local namespace. */
1089
1090 r = ioctl(pty, TIOCGPTN, &no);
1091 if (r < 0)
1092 return -errno;
1093
1094 if (no < 0)
1095 return -EIO;
1096
1097 if (asprintf(ret, "/dev/pts/%i", no) < 0)
1098 return -ENOMEM;
1099
1100 return 0;
1101}
66cb2fde
LP
1102
1103int openpt_in_namespace(pid_t pid, int flags) {
1104 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
1105 _cleanup_close_pair_ int pair[2] = { -1, -1 };
1106 siginfo_t si;
1107 pid_t child;
1108 int r;
1109
1110 assert(pid > 0);
1111
1112 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
1113 if (r < 0)
1114 return r;
1115
1116 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1117 return -errno;
1118
1119 child = fork();
1120 if (child < 0)
1121 return -errno;
1122
1123 if (child == 0) {
1124 int master;
1125
1126 pair[0] = safe_close(pair[0]);
1127
1128 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
1129 if (r < 0)
1130 _exit(EXIT_FAILURE);
1131
1132 master = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
1133 if (master < 0)
1134 _exit(EXIT_FAILURE);
1135
1136 if (unlockpt(master) < 0)
1137 _exit(EXIT_FAILURE);
1138
1139 if (send_one_fd(pair[1], master, 0) < 0)
1140 _exit(EXIT_FAILURE);
1141
1142 _exit(EXIT_SUCCESS);
1143 }
1144
1145 pair[1] = safe_close(pair[1]);
1146
1147 r = wait_for_terminate(child, &si);
1148 if (r < 0)
1149 return r;
1150 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
1151 return -EIO;
1152
1153 return receive_one_fd(pair[0], 0);
1154}
40e1f4ea
LP
1155
1156int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
1157 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
1158 _cleanup_close_pair_ int pair[2] = { -1, -1 };
1159 siginfo_t si;
1160 pid_t child;
1161 int r;
1162
1163 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
1164 if (r < 0)
1165 return r;
1166
1167 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1168 return -errno;
1169
1170 child = fork();
1171 if (child < 0)
1172 return -errno;
1173
1174 if (child == 0) {
1175 int master;
1176
1177 pair[0] = safe_close(pair[0]);
1178
1179 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
1180 if (r < 0)
1181 _exit(EXIT_FAILURE);
1182
1183 master = open_terminal(name, mode|O_NOCTTY|O_CLOEXEC);
1184 if (master < 0)
1185 _exit(EXIT_FAILURE);
1186
1187 if (send_one_fd(pair[1], master, 0) < 0)
1188 _exit(EXIT_FAILURE);
1189
1190 _exit(EXIT_SUCCESS);
1191 }
1192
1193 pair[1] = safe_close(pair[1]);
1194
1195 r = wait_for_terminate(child, &si);
1196 if (r < 0)
1197 return r;
1198 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
1199 return -EIO;
1200
1201 return receive_one_fd(pair[0], 0);
1202}