LCOV - code coverage report
Current view: top level - basic - terminal-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 132 708 18.6 %
Date: 2019-08-23 13:36:53 Functions: 16 46 34.8 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 77 530 14.5 %

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

Generated by: LCOV version 1.14