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