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