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