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