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