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 : }
|