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