chiark / gitweb /
terminal-util: modernize get_kernel_consoles() a bit
[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 ***ret) {
719         _cleanup_strv_free_ char **l = NULL;
720         _cleanup_free_ char *line = NULL;
721         const char *p;
722         int r;
723
724         assert(ret);
725
726         /* If we /sys is mounted read-only this means we are running in some kind of container environment. In that
727          * case /sys would reflect the host system, not us, hence ignore the data we can read from it. */
728         if (path_is_read_only_fs("/sys") > 0)
729                 goto fallback;
730
731         r = read_one_line_file("/sys/class/tty/console/active", &line);
732         if (r < 0)
733                 return r;
734
735         p = line;
736         for (;;) {
737                 _cleanup_free_ char *tty = NULL;
738                 char *path;
739
740                 r = extract_first_word(&p, &tty, NULL, 0);
741                 if (r < 0)
742                         return r;
743                 if (r == 0)
744                         break;
745
746                 if (streq(tty, "tty0")) {
747                         tty = mfree(tty);
748                         r = read_one_line_file("/sys/class/tty/tty0/active", &tty);
749                         if (r < 0)
750                                 return r;
751                 }
752
753                 path = strappend("/dev/", tty);
754                 if (!path)
755                         return -ENOMEM;
756
757                 if (access(path, F_OK) < 0) {
758                         log_debug_errno(errno, "Console device %s is not accessible, skipping: %m", path);
759                         free(path);
760                         continue;
761                 }
762
763                 r = strv_consume(&l, path);
764                 if (r < 0)
765                         return r;
766         }
767
768         if (strv_isempty(l)) {
769                 log_debug("No devices found for system console");
770                 goto fallback;
771         }
772
773         *ret = l;
774         l = NULL;
775
776         return 0;
777
778 fallback:
779         r = strv_extend(&l, "/dev/console");
780         if (r < 0)
781                 return r;
782
783         *ret = l;
784         l = NULL;
785
786         return 0;
787 }
788
789 bool tty_is_vc_resolve(const char *tty) {
790         _cleanup_free_ char *active = NULL;
791
792         assert(tty);
793
794         tty = skip_dev_prefix(tty);
795
796         if (streq(tty, "console")) {
797                 tty = resolve_dev_console(&active);
798                 if (!tty)
799                         return false;
800         }
801
802         return tty_is_vc(tty);
803 }
804
805 const char *default_term_for_tty(const char *tty) {
806         return tty && tty_is_vc_resolve(tty) ? "linux" : "vt220";
807 }
808 #endif // 0
809
810 int fd_columns(int fd) {
811         struct winsize ws = {};
812
813         if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
814                 return -errno;
815
816         if (ws.ws_col <= 0)
817                 return -EIO;
818
819         return ws.ws_col;
820 }
821
822 unsigned columns(void) {
823         const char *e;
824         int c;
825
826         if (cached_columns > 0)
827                 return cached_columns;
828
829         c = 0;
830         e = getenv("COLUMNS");
831         if (e)
832                 (void) safe_atoi(e, &c);
833
834         if (c <= 0)
835                 c = fd_columns(STDOUT_FILENO);
836
837         if (c <= 0)
838                 c = 80;
839
840         cached_columns = c;
841         return cached_columns;
842 }
843
844 int fd_lines(int fd) {
845         struct winsize ws = {};
846
847         if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
848                 return -errno;
849
850         if (ws.ws_row <= 0)
851                 return -EIO;
852
853         return ws.ws_row;
854 }
855
856 unsigned lines(void) {
857         const char *e;
858         int l;
859
860         if (cached_lines > 0)
861                 return cached_lines;
862
863         l = 0;
864         e = getenv("LINES");
865         if (e)
866                 (void) safe_atoi(e, &l);
867
868         if (l <= 0)
869                 l = fd_lines(STDOUT_FILENO);
870
871         if (l <= 0)
872                 l = 24;
873
874         cached_lines = l;
875         return cached_lines;
876 }
877
878 /* intended to be used as a SIGWINCH sighandler */
879 #if 0 /// UNNEEDED by elogind
880 void columns_lines_cache_reset(int signum) {
881         cached_columns = 0;
882         cached_lines = 0;
883 }
884 #endif // 0
885
886 void reset_terminal_feature_caches(void) {
887         cached_columns = 0;
888         cached_lines = 0;
889
890         cached_colors_enabled = -1;
891         cached_underline_enabled = -1;
892         cached_on_tty = -1;
893 }
894
895 bool on_tty(void) {
896         if (cached_on_tty < 0)
897                 cached_on_tty = isatty(STDOUT_FILENO) > 0;
898
899         return cached_on_tty;
900 }
901
902 int make_stdio(int fd) {
903         int r = 0;
904
905         assert(fd >= 0);
906
907         if (dup2(fd, STDIN_FILENO) < 0)
908                 r = -errno;
909         if (dup2(fd, STDOUT_FILENO) < 0 && r >= 0)
910                 r = -errno;
911         if (dup2(fd, STDERR_FILENO) < 0 && r >= 0)
912                 r = -errno;
913
914         if (fd >= 3)
915                 safe_close(fd);
916
917         /* Explicitly unset O_CLOEXEC, since if fd was < 3, then dup2() was a NOP and the bit hence possibly set. */
918         stdio_unset_cloexec();
919
920         return r;
921 }
922
923 int make_null_stdio(void) {
924         int null_fd, r;
925
926         null_fd = open("/dev/null", O_RDWR|O_NOCTTY|O_CLOEXEC);
927         if (null_fd < 0)
928                 return -errno;
929
930         r = make_stdio(null_fd);
931
932         reset_terminal_feature_caches();
933
934         return r;
935 }
936
937 int getttyname_malloc(int fd, char **ret) {
938         size_t l = 100;
939         int r;
940
941         assert(fd >= 0);
942         assert(ret);
943
944         for (;;) {
945                 char path[l];
946
947                 r = ttyname_r(fd, path, sizeof(path));
948                 if (r == 0) {
949                         char *c;
950
951                         c = strdup(skip_dev_prefix(path));
952                         if (!c)
953                                 return -ENOMEM;
954
955                         *ret = c;
956                         return 0;
957                 }
958
959                 if (r != ERANGE)
960                         return -r;
961
962                 l *= 2;
963         }
964
965         return 0;
966 }
967
968 int getttyname_harder(int fd, char **r) {
969         int k;
970         char *s = NULL;
971
972         k = getttyname_malloc(fd, &s);
973         if (k < 0)
974                 return k;
975
976         if (streq(s, "tty")) {
977                 free(s);
978                 return get_ctty(0, NULL, r);
979         }
980
981         *r = s;
982         return 0;
983 }
984
985 int get_ctty_devnr(pid_t pid, dev_t *d) {
986         int r;
987         _cleanup_free_ char *line = NULL;
988         const char *p;
989         unsigned long ttynr;
990
991         assert(pid >= 0);
992
993         p = procfs_file_alloca(pid, "stat");
994         r = read_one_line_file(p, &line);
995         if (r < 0)
996                 return r;
997
998         p = strrchr(line, ')');
999         if (!p)
1000                 return -EIO;
1001
1002         p++;
1003
1004         if (sscanf(p, " "
1005                    "%*c "  /* state */
1006                    "%*d "  /* ppid */
1007                    "%*d "  /* pgrp */
1008                    "%*d "  /* session */
1009                    "%lu ", /* ttynr */
1010                    &ttynr) != 1)
1011                 return -EIO;
1012
1013         if (major(ttynr) == 0 && minor(ttynr) == 0)
1014                 return -ENXIO;
1015
1016         if (d)
1017                 *d = (dev_t) ttynr;
1018
1019         return 0;
1020 }
1021
1022 int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
1023         char fn[STRLEN("/dev/char/") + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
1024         _cleanup_free_ char *s = NULL;
1025         const char *p;
1026         dev_t devnr;
1027         int k;
1028
1029         assert(r);
1030
1031         k = get_ctty_devnr(pid, &devnr);
1032         if (k < 0)
1033                 return k;
1034
1035         sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
1036
1037         k = readlink_malloc(fn, &s);
1038         if (k < 0) {
1039
1040                 if (k != -ENOENT)
1041                         return k;
1042
1043                 /* This is an ugly hack */
1044                 if (major(devnr) == 136) {
1045                         if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
1046                                 return -ENOMEM;
1047                 } else {
1048                         /* Probably something like the ptys which have no
1049                          * symlink in /dev/char. Let's return something
1050                          * vaguely useful. */
1051
1052                         b = strdup(fn + 5);
1053                         if (!b)
1054                                 return -ENOMEM;
1055                 }
1056         } else {
1057                 if (startswith(s, "/dev/"))
1058                         p = s + 5;
1059                 else if (startswith(s, "../"))
1060                         p = s + 3;
1061                 else
1062                         p = s;
1063
1064                 b = strdup(p);
1065                 if (!b)
1066                         return -ENOMEM;
1067         }
1068
1069         *r = b;
1070         if (_devnr)
1071                 *_devnr = devnr;
1072
1073         return 0;
1074 }
1075
1076 #if 0 /// UNNEEDED by elogind
1077 int ptsname_malloc(int fd, char **ret) {
1078         size_t l = 100;
1079
1080         assert(fd >= 0);
1081         assert(ret);
1082
1083         for (;;) {
1084                 char *c;
1085
1086                 c = new(char, l);
1087                 if (!c)
1088                         return -ENOMEM;
1089
1090                 if (ptsname_r(fd, c, l) == 0) {
1091                         *ret = c;
1092                         return 0;
1093                 }
1094                 if (errno != ERANGE) {
1095                         free(c);
1096                         return -errno;
1097                 }
1098
1099                 free(c);
1100                 l *= 2;
1101         }
1102 }
1103
1104 int ptsname_namespace(int pty, char **ret) {
1105         int no = -1, r;
1106
1107         /* Like ptsname(), but doesn't assume that the path is
1108          * accessible in the local namespace. */
1109
1110         r = ioctl(pty, TIOCGPTN, &no);
1111         if (r < 0)
1112                 return -errno;
1113
1114         if (no < 0)
1115                 return -EIO;
1116
1117         if (asprintf(ret, "/dev/pts/%i", no) < 0)
1118                 return -ENOMEM;
1119
1120         return 0;
1121 }
1122
1123 int openpt_in_namespace(pid_t pid, int flags) {
1124         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
1125         _cleanup_close_pair_ int pair[2] = { -1, -1 };
1126         pid_t child;
1127         int r;
1128
1129         assert(pid > 0);
1130
1131         r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
1132         if (r < 0)
1133                 return r;
1134
1135         if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1136                 return -errno;
1137
1138         r = safe_fork("(sd-openpt)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
1139         if (r < 0)
1140                 return r;
1141         if (r == 0) {
1142                 int master;
1143
1144                 pair[0] = safe_close(pair[0]);
1145
1146                 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
1147                 if (r < 0)
1148                         _exit(EXIT_FAILURE);
1149
1150                 master = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
1151                 if (master < 0)
1152                         _exit(EXIT_FAILURE);
1153
1154                 if (unlockpt(master) < 0)
1155                         _exit(EXIT_FAILURE);
1156
1157                 if (send_one_fd(pair[1], master, 0) < 0)
1158                         _exit(EXIT_FAILURE);
1159
1160                 _exit(EXIT_SUCCESS);
1161         }
1162
1163         pair[1] = safe_close(pair[1]);
1164
1165         r = wait_for_terminate_and_check("(sd-openpt)", child, 0);
1166         if (r < 0)
1167                 return r;
1168         if (r != EXIT_SUCCESS)
1169                 return -EIO;
1170
1171         return receive_one_fd(pair[0], 0);
1172 }
1173
1174 int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
1175         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
1176         _cleanup_close_pair_ int pair[2] = { -1, -1 };
1177         pid_t child;
1178         int r;
1179
1180         r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
1181         if (r < 0)
1182                 return r;
1183
1184         if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1185                 return -errno;
1186
1187         r = safe_fork("(sd-terminal)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
1188         if (r < 0)
1189                 return r;
1190         if (r == 0) {
1191                 int master;
1192
1193                 pair[0] = safe_close(pair[0]);
1194
1195                 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
1196                 if (r < 0)
1197                         _exit(EXIT_FAILURE);
1198
1199                 master = open_terminal(name, mode|O_NOCTTY|O_CLOEXEC);
1200                 if (master < 0)
1201                         _exit(EXIT_FAILURE);
1202
1203                 if (send_one_fd(pair[1], master, 0) < 0)
1204                         _exit(EXIT_FAILURE);
1205
1206                 _exit(EXIT_SUCCESS);
1207         }
1208
1209         pair[1] = safe_close(pair[1]);
1210
1211         r = wait_for_terminate_and_check("(sd-terminal)", child, 0);
1212         if (r < 0)
1213                 return r;
1214         if (r != EXIT_SUCCESS)
1215                 return -EIO;
1216
1217         return receive_one_fd(pair[0], 0);
1218 }
1219 #endif // 0
1220
1221 static bool getenv_terminal_is_dumb(void) {
1222         const char *e;
1223
1224         e = getenv("TERM");
1225         if (!e)
1226                 return true;
1227
1228         return streq(e, "dumb");
1229 }
1230
1231 bool terminal_is_dumb(void) {
1232         if (!on_tty())
1233                 return true;
1234
1235         return getenv_terminal_is_dumb();
1236 }
1237
1238 bool colors_enabled(void) {
1239
1240         /* Returns true if colors are considered supported on our stdout. For that we check $SYSTEMD_COLORS first
1241          * (which is the explicit way to turn off/on colors). If that didn't work we turn off colors unless we are on a
1242          * 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
1243          * we are PID 1 then we do not check whether we are connected to a TTY, because we don't keep /dev/console open
1244          * continously due to fear of SAK, and hence things are a bit weird. */
1245
1246         if (cached_colors_enabled < 0) {
1247 #if 0 /// elogind does not allow such forcing, and we are never init!
1248                 int val;
1249
1250                 val = getenv_bool("SYSTEMD_COLORS");
1251                 if (val >= 0)
1252                         cached_colors_enabled = val;
1253                 else if (getpid_cached() == 1)
1254                         /* PID1 outputs to the console without holding it open all the time */
1255                         cached_colors_enabled = !getenv_terminal_is_dumb();
1256                 else
1257 #endif // 0
1258                         cached_colors_enabled = !terminal_is_dumb();
1259         }
1260
1261         return cached_colors_enabled;
1262 }
1263
1264 bool dev_console_colors_enabled(void) {
1265         _cleanup_free_ char *s = NULL;
1266         int b;
1267
1268         /* Returns true if we assume that color is supported on /dev/console.
1269          *
1270          * For that we first check if we explicitly got told to use colors or not, by checking $SYSTEMD_COLORS. If that
1271          * didn't tell us anything we check whether PID 1 has $TERM set, and if not whether $TERM is set on the kernel
1272          * command line. If we find $TERM set we assume color if it's not set to "dumb", similar to regular
1273          * colors_enabled() operates. */
1274
1275         b = getenv_bool("SYSTEMD_COLORS");
1276         if (b >= 0)
1277                 return b;
1278
1279         if (getenv_for_pid(1, "TERM", &s) <= 0)
1280                 (void) proc_cmdline_get_key("TERM", 0, &s);
1281
1282         return !streq_ptr(s, "dumb");
1283 }
1284
1285 bool underline_enabled(void) {
1286
1287         if (cached_underline_enabled < 0) {
1288
1289                 /* The Linux console doesn't support underlining, turn it off, but only there. */
1290
1291                 if (colors_enabled())
1292                         cached_underline_enabled = !streq_ptr(getenv("TERM"), "linux");
1293                 else
1294                         cached_underline_enabled = false;
1295         }
1296
1297         return cached_underline_enabled;
1298 }
1299
1300 int vt_default_utf8(void) {
1301         _cleanup_free_ char *b = NULL;
1302         int r;
1303
1304         /* Read the default VT UTF8 setting from the kernel */
1305
1306         r = read_one_line_file("/sys/module/vt/parameters/default_utf8", &b);
1307         if (r < 0)
1308                 return r;
1309
1310         return parse_boolean(b);
1311 }
1312
1313 int vt_reset_keyboard(int fd) {
1314         int kb;
1315
1316         /* If we can't read the default, then default to unicode. It's 2017 after all. */
1317         kb = vt_default_utf8() != 0 ? K_UNICODE : K_XLATE;
1318
1319         if (ioctl(fd, KDSKBMODE, kb) < 0)
1320                 return -errno;
1321
1322         return 0;
1323 }