LCOV - code coverage report
Current view: top level - basic - terminal-util.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 104 537 19.4 %
Date: 2015-07-29 18:47:03 Functions: 11 34 32.4 %

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

Generated by: LCOV version 1.11