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