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