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