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