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