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