]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/terminal-util.c
conf-files: include the expected suffix in the 'unexpected suffix' debug message.
[thirdparty/systemd.git] / src / basic / terminal-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
288a74cc 2
07630cea 3#include <fcntl.h>
23b27b39 4#include <linux/kd.h>
3fc2a440 5#include <linux/magic.h>
23b27b39
LP
6#include <linux/tiocl.h>
7#include <linux/vt.h>
8#include <poll.h>
9#include <signal.h>
11c3a366 10#include <stdlib.h>
11c3a366 11#include <sys/inotify.h>
23b27b39 12#include <sys/ioctl.h>
11c3a366 13#include <sys/sysmacros.h>
288a74cc 14#include <termios.h>
0c15577a 15#include <time.h>
07630cea 16#include <unistd.h>
288a74cc 17
b5efdb8a 18#include "alloc-util.h"
b7120388 19#include "ansi-color.h"
dffbe1d1 20#include "chase.h"
7176f06c 21#include "devnum-util.h"
0c15577a 22#include "errno-util.h"
6553db60 23#include "extract-word.h"
3ffd4af2 24#include "fd-util.h"
288a74cc 25#include "fileio.h"
f4f15635 26#include "fs-util.h"
63e9c383 27#include "hexdecoct.h"
9e5fd717 28#include "inotify-util.h"
c004493c 29#include "io-util.h"
93cc7779 30#include "log.h"
0cb8e3d1 31#include "namespace-util.h"
6bedfcbb 32#include "parse-util.h"
c2b32159
LP
33#include "path-util.h"
34#include "proc-cmdline.h"
07630cea 35#include "process-util.h"
0bd72176 36#include "signal-util.h"
2583fbea 37#include "socket-util.h"
8fcde012 38#include "stat-util.h"
11f3c130 39#include "stdio-util.h"
07630cea 40#include "string-util.h"
6af62124 41#include "strv.h"
3ffd4af2 42#include "terminal-util.h"
07630cea 43#include "time-util.h"
e4a08721 44#include "utf8.h"
288a74cc 45
5321b957
ZJS
46/* How much to wait for a reply to a terminal sequence */
47#define CONSOLE_REPLY_WAIT_USEC (333 * USEC_PER_MSEC)
48
288a74cc
RC
49static volatile unsigned cached_columns = 0;
50static volatile unsigned cached_lines = 0;
51
c6063244 52static volatile int cached_on_tty = -1;
197dd3a9 53static volatile int cached_on_dev_null = -1;
c6063244 54
76270f5c
MY
55bool isatty_safe(int fd) {
56 assert(fd >= 0);
57
58 if (isatty(fd))
59 return true;
60
1b24357c
LP
61 /* Linux/glibc returns EIO for hung up TTY on isatty(). Which is wrong, the thing doesn't stop being
62 * a TTY after all, just because it is temporarily hung up. Let's work around this here, until this
63 * is fixed in glibc. See: https://sourceware.org/bugzilla/show_bug.cgi?id=32103 */
64 if (errno == EIO)
65 return true;
66
76270f5c
MY
67 /* Be resilient if we're working on stdio, since they're set up by parent process. */
68 assert(errno != EBADF || IN_SET(fd, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO));
69
70 return false;
71}
72
288a74cc 73int chvt(int vt) {
254d1313 74 _cleanup_close_ int fd = -EBADF;
288a74cc 75
0295642d
LP
76 /* Switch to the specified vt number. If the VT is specified <= 0 switch to the VT the kernel log messages go,
77 * if that's configured. */
78
0a8b555c 79 fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
288a74cc 80 if (fd < 0)
4c8c499e 81 return fd;
288a74cc 82
b9e74c39 83 if (vt <= 0) {
288a74cc
RC
84 int tiocl[2] = {
85 TIOCL_GETKMSGREDIRECT,
86 0
87 };
88
89 if (ioctl(fd, TIOCLINUX, tiocl) < 0)
90 return -errno;
91
92 vt = tiocl[0] <= 0 ? 1 : tiocl[0];
93 }
94
7c248223 95 return RET_NERRNO(ioctl(fd, VT_ACTIVATE, vt));
288a74cc
RC
96}
97
8fcd8576 98int read_one_char(FILE *f, char *ret, usec_t t, bool echo, bool *need_nl) {
715bcf36
LP
99 _cleanup_free_ char *line = NULL;
100 struct termios old_termios;
14f594b9 101 int r, fd;
288a74cc 102
288a74cc
RC
103 assert(ret);
104
8fcd8576
LP
105 if (!f)
106 f = stdin;
107
14f594b9
LP
108 /* If this is a terminal, then switch canonical mode off, so that we can read a single
109 * character. (Note that fmemopen() streams do not have an fd associated with them, let's handle that
8fcd8576
LP
110 * nicely.) If 'echo' is false we'll also disable ECHO mode so that the pressed key is not made
111 * visible to the user. */
14f594b9
LP
112 fd = fileno(f);
113 if (fd >= 0 && tcgetattr(fd, &old_termios) >= 0) {
715bcf36 114 struct termios new_termios = old_termios;
288a74cc 115
8fcd8576 116 new_termios.c_lflag &= ~(ICANON|(echo ? 0 : ECHO));
288a74cc
RC
117 new_termios.c_cc[VMIN] = 1;
118 new_termios.c_cc[VTIME] = 0;
119
f789b17e 120 if (tcsetattr(fd, TCSANOW, &new_termios) >= 0) {
03a7dbea 121 char c;
288a74cc
RC
122
123 if (t != USEC_INFINITY) {
14f594b9 124 if (fd_wait_for_event(fd, POLLIN, t) <= 0) {
f789b17e 125 (void) tcsetattr(fd, TCSANOW, &old_termios);
288a74cc
RC
126 return -ETIMEDOUT;
127 }
128 }
129
03a7dbea 130 r = safe_fgetc(f, &c);
f789b17e 131 (void) tcsetattr(fd, TCSANOW, &old_termios);
d3f9790c
LP
132 if (r < 0)
133 return r;
03a7dbea
LP
134 if (r == 0)
135 return -EIO;
288a74cc
RC
136
137 if (need_nl)
138 *need_nl = c != '\n';
139
140 *ret = c;
141 return 0;
142 }
143 }
144
978e7d16 145 if (t != USEC_INFINITY && fd >= 0) {
14f594b9
LP
146 /* Let's wait the specified amount of time for input. When we have no fd we skip this, under
147 * the assumption that this is an fmemopen() stream or so where waiting doesn't make sense
148 * anyway, as the data is either already in the stream or cannot possible be placed there
149 * while we access the stream */
150
151 if (fd_wait_for_event(fd, POLLIN, t) <= 0)
288a74cc
RC
152 return -ETIMEDOUT;
153 }
154
715bcf36 155 /* If this is not a terminal, then read a full line instead */
288a74cc 156
715bcf36
LP
157 r = read_line(f, 16, &line); /* longer than necessary, to eat up UTF-8 chars/vt100 key sequences */
158 if (r < 0)
159 return r;
160 if (r == 0)
161 return -EIO;
288a74cc
RC
162
163 if (strlen(line) != 1)
164 return -EBADMSG;
165
166 if (need_nl)
167 *need_nl = false;
168
169 *ret = line[0];
170 return 0;
171}
172
3c670f89
FB
173#define DEFAULT_ASK_REFRESH_USEC (2*USEC_PER_SEC)
174
175int ask_char(char *ret, const char *replies, const char *fmt, ...) {
288a74cc
RC
176 int r;
177
178 assert(ret);
179 assert(replies);
3c670f89 180 assert(fmt);
288a74cc
RC
181
182 for (;;) {
183 va_list ap;
184 char c;
185 bool need_nl = true;
186
25e4608b 187 fputs(ansi_highlight(), stdout);
288a74cc 188
3c670f89
FB
189 putchar('\r');
190
191 va_start(ap, fmt);
192 vprintf(fmt, ap);
288a74cc
RC
193 va_end(ap);
194
25e4608b 195 fputs(ansi_normal(), stdout);
288a74cc
RC
196
197 fflush(stdout);
198
8fcd8576 199 r = read_one_char(stdin, &c, DEFAULT_ASK_REFRESH_USEC, /* echo= */ true, &need_nl);
288a74cc
RC
200 if (r < 0) {
201
3c670f89
FB
202 if (r == -ETIMEDOUT)
203 continue;
204
288a74cc
RC
205 if (r == -EBADMSG) {
206 puts("Bad input, please try again.");
207 continue;
208 }
209
210 putchar('\n');
211 return r;
212 }
213
214 if (need_nl)
215 putchar('\n');
216
217 if (strchr(replies, c)) {
218 *ret = c;
219 return 0;
220 }
221
222 puts("Read unexpected character, please try again.");
223 }
224}
225
94a2b1cd
LP
226typedef enum CompletionResult{
227 COMPLETION_ALREADY, /* the input string is already complete */
228 COMPLETION_FULL, /* completed the input string to be complete now */
229 COMPLETION_PARTIAL, /* completed the input string so that is still incomplete */
230 COMPLETION_NONE, /* found no matching completion */
231 _COMPLETION_RESULT_MAX,
232 _COMPLETION_RESULT_INVALID = -EINVAL,
233 _COMPLETION_RESULT_ERRNO_MAX = -ERRNO_MAX,
234} CompletionResult;
235
236static CompletionResult pick_completion(const char *string, char *const*completions, char **ret) {
237 _cleanup_free_ char *found = NULL;
238 bool partial = false;
239
240 string = strempty(string);
241
242 STRV_FOREACH(c, completions) {
243
244 /* Ignore entries that are not actually completions */
245 if (!startswith(*c, string))
246 continue;
247
248 /* Store first completion that matches */
249 if (!found) {
250 found = strdup(*c);
251 if (!found)
252 return -ENOMEM;
253
254 continue;
255 }
256
257 /* If there's another completion that works truncate the one we already found by common
258 * prefix */
259 size_t n = str_common_prefix(found, *c);
260 if (n == SIZE_MAX)
261 continue;
262
263 found[n] = 0;
264 partial = true;
265 }
266
267 *ret = TAKE_PTR(found);
268
269 if (!*ret)
270 return COMPLETION_NONE;
271 if (partial)
272 return COMPLETION_PARTIAL;
273
274 return streq(string, *ret) ? COMPLETION_ALREADY : COMPLETION_FULL;
275}
276
277static void clear_by_backspace(size_t n) {
278 /* Erase the specified number of character cells backwards on the terminal */
279 for (size_t i = 0; i < n; i++)
280 fputs("\b \b", stdout);
281}
282
283int ask_string_full(
284 char **ret,
285 GetCompletionsCallback get_completions,
286 void *userdata,
287 const char *text, ...) {
288
03d94294 289 va_list ap;
715bcf36
LP
290 int r;
291
288a74cc
RC
292 assert(ret);
293 assert(text);
294
94a2b1cd 295 /* Output the prompt */
25e4608b 296 fputs(ansi_highlight(), stdout);
03d94294
ZJS
297 va_start(ap, text);
298 vprintf(text, ap);
299 va_end(ap);
25e4608b 300 fputs(ansi_normal(), stdout);
03d94294 301 fflush(stdout);
288a74cc 302
94a2b1cd
LP
303 _cleanup_free_ char *string = NULL;
304 size_t n = 0;
305
306 /* Do interactive logic only if stdin + stdout are connected to the same place. And yes, we could use
307 * STDIN_FILENO and STDOUT_FILENO here, but let's be overly correct for once, after all libc allows
308 * swapping out stdin/stdout. */
309 int fd_input = fileno(stdin);
310 int fd_output = fileno(stdout);
311 if (fd_input < 0 || fd_output < 0 || same_fd(fd_input, fd_output) <= 0)
312 goto fallback;
313
314 /* Try to disable echo, which also tells us if this even is a terminal */
315 struct termios old_termios;
316 if (tcgetattr(fd_input, &old_termios) < 0)
317 goto fallback;
318
319 struct termios new_termios = old_termios;
320 termios_disable_echo(&new_termios);
f789b17e 321 if (tcsetattr(fd_input, TCSANOW, &new_termios) < 0)
94a2b1cd
LP
322 return -errno;
323
324 for (;;) {
325 int c = fgetc(stdin);
326
327 /* On EOF or NUL, end the request, don't output anything anymore */
328 if (IN_SET(c, EOF, 0))
329 break;
330
331 /* On Return also end the request, but make this visible */
332 if (IN_SET(c, '\n', '\r')) {
333 fputc('\n', stdout);
334 break;
335 }
336
337 if (c == '\t') {
338 /* Tab */
339
340 _cleanup_strv_free_ char **completions = NULL;
341 if (get_completions) {
342 r = get_completions(string, &completions, userdata);
343 if (r < 0)
344 goto fail;
345 }
346
347 _cleanup_free_ char *new_string = NULL;
348 CompletionResult cr = pick_completion(string, completions, &new_string);
349 if (cr < 0) {
350 r = cr;
351 goto fail;
352 }
353 if (IN_SET(cr, COMPLETION_PARTIAL, COMPLETION_FULL)) {
354 /* Output the new suffix we learned */
355 fputs(ASSERT_PTR(startswith(new_string, strempty(string))), stdout);
356
357 /* And update the whole string */
358 free_and_replace(string, new_string);
359 n = strlen(string);
360 }
361 if (cr == COMPLETION_NONE)
362 fputc('\a', stdout); /* BEL */
363
364 if (IN_SET(cr, COMPLETION_PARTIAL, COMPLETION_ALREADY)) {
365 /* If this worked only partially, or if the user hit TAB even though we were
366 * complete already, then show the remaining options (in the latter case just
367 * the one). */
368 fputc('\n', stdout);
369
370 _cleanup_strv_free_ char **filtered = strv_filter_prefix(completions, string);
371 if (!filtered) {
372 r = -ENOMEM;
373 goto fail;
374 }
375
376 r = show_menu(filtered,
377 /* n_columns= */ SIZE_MAX,
378 /* column_width= */ SIZE_MAX,
379 /* ellipsize_percentage= */ 0,
380 /* grey_prefix=*/ string,
381 /* with_numbers= */ false);
382 if (r < 0)
383 goto fail;
384
385 /* Show the prompt again */
386 fputs(ansi_highlight(), stdout);
387 va_start(ap, text);
388 vprintf(text, ap);
389 va_end(ap);
390 fputs(ansi_normal(), stdout);
391 fputs(string, stdout);
392 }
393
394 } else if (IN_SET(c, '\b', 127)) {
395 /* Backspace */
396
397 if (n == 0)
398 fputc('\a', stdout); /* BEL */
399 else {
400 size_t m = utf8_last_length(string, n);
401
402 char *e = string + n - m;
403 clear_by_backspace(utf8_console_width(e));
404
405 *e = 0;
406 n -= m;
407 }
408
409 } else if (c == 21) {
410 /* Ctrl-u → erase all input */
411
412 clear_by_backspace(utf8_console_width(string));
a6eb2296
YW
413 if (string)
414 string[n = 0] = 0;
415 else
416 assert(n == 0);
94a2b1cd
LP
417
418 } else if (c == 4) {
419 /* Ctrl-d → cancel this field input */
420
421 r = -ECANCELED;
422 goto fail;
423
424 } else if (char_is_cc(c) || n >= LINE_MAX)
425 /* refuse control characters and too long strings */
426 fputc('\a', stdout); /* BEL */
427 else {
428 /* Regular char */
429
430 if (!GREEDY_REALLOC(string, n+2)) {
431 r = -ENOMEM;
432 goto fail;
433 }
434
435 string[n++] = (char) c;
436 string[n] = 0;
437
438 fputc(c, stdout);
439 }
440
441 fflush(stdout);
442 }
443
f789b17e 444 if (tcsetattr(fd_input, TCSANOW, &old_termios) < 0)
94a2b1cd
LP
445 return -errno;
446
447 if (!string) {
448 string = strdup("");
449 if (!string)
450 return -ENOMEM;
451 }
452
453 *ret = TAKE_PTR(string);
454 return 0;
455
456fail:
f789b17e 457 (void) tcsetattr(fd_input, TCSANOW, &old_termios);
94a2b1cd
LP
458 return r;
459
460fallback:
461 /* A simple fallback without TTY magic */
462 r = read_line(stdin, LONG_LINE_MAX, &string);
03d94294
ZJS
463 if (r < 0)
464 return r;
465 if (r == 0)
466 return -EIO;
288a74cc 467
94a2b1cd 468 *ret = TAKE_PTR(string);
03d94294 469 return 0;
288a74cc
RC
470}
471
91ea3dcf 472bool any_key_to_proceed(void) {
91ea3dcf 473
8fcd8576
LP
474 /* Insert a new line here as well as to when the user inputs, as this is also used during the boot up
475 * sequence when status messages may be interleaved with the current program output. This ensures
476 * that the status messages aren't appended on the same line as this message. */
91ea3dcf 477
8fcd8576
LP
478 fputc('\n', stdout);
479 fputs(ansi_highlight_magenta(), stdout);
480 fputs("-- Press any key to proceed --", stdout);
481 fputs(ansi_normal(), stdout);
787904d0 482 fputc('\n', stdout);
8fcd8576 483 fflush(stdout);
91ea3dcf 484
8fcd8576
LP
485 char key = 0;
486 (void) read_one_char(stdin, &key, USEC_INFINITY, /* echo= */ false, /* need_nl= */ NULL);
487
8fcd8576
LP
488 fputc('\n', stdout);
489 fflush(stdout);
91ea3dcf
MF
490
491 return key != 'q';
492}
493
b6478aa1
LP
494static size_t widest_list_element(char *const*l) {
495 size_t w = 0;
496
497 /* Returns the largest console width of all elements in 'l' */
498
499 STRV_FOREACH(i, l)
500 w = MAX(w, utf8_console_width(*i));
501
502 return w;
503}
504
505int show_menu(char **x,
506 size_t n_columns,
507 size_t column_width,
508 unsigned ellipsize_percentage,
509 const char *grey_prefix,
510 bool with_numbers) {
ec75a254
DDM
511
512 assert(n_columns > 0);
513
b6478aa1
LP
514 if (n_columns == SIZE_MAX)
515 n_columns = 3;
516
517 if (column_width == SIZE_MAX) {
518 size_t widest = widest_list_element(x);
519
520 /* If not specified, derive column width from screen width */
521 size_t column_max = (columns()-1) / n_columns;
522
523 /* Subtract room for numbers */
524 if (with_numbers && column_max > 6)
525 column_max -= 6;
ec75a254 526
b6478aa1
LP
527 /* If columns would get too tight let's make this a linear list instead. */
528 if (column_max < 10 && widest > 10) {
529 n_columns = 1;
530 column_max = columns()-1;
531
532 if (with_numbers && column_max > 6)
533 column_max -= 6;
534 }
535
536 column_width = CLAMP(widest+1, 10U, column_max);
537 }
538
539 size_t n = strv_length(x);
540 size_t per_column = DIV_ROUND_UP(n, n_columns);
541
542 size_t break_lines = lines();
ec75a254
DDM
543 if (break_lines > 2)
544 break_lines--;
545
546 /* The first page gets two extra lines, since we want to show
547 * a title */
b6478aa1 548 size_t break_modulo = break_lines;
ec75a254
DDM
549 if (break_modulo > 3)
550 break_modulo -= 3;
551
b6478aa1 552 for (size_t i = 0; i < per_column; i++) {
ec75a254 553
b6478aa1 554 for (size_t j = 0; j < n_columns; j++) {
ec75a254
DDM
555 _cleanup_free_ char *e = NULL;
556
557 if (j * per_column + i >= n)
558 break;
559
b6478aa1 560 e = ellipsize(x[j * per_column + i], column_width, ellipsize_percentage);
ec75a254 561 if (!e)
b6478aa1
LP
562 return -ENOMEM;
563
564 if (with_numbers)
565 printf("%s%4zu)%s ",
566 ansi_grey(),
567 j * per_column + i + 1,
568 ansi_normal());
569
570 if (grey_prefix && startswith(e, grey_prefix)) {
571 size_t k = MIN(strlen(grey_prefix), column_width);
572 printf("%s%.*s%s",
573 ansi_grey(),
574 (int) k, e,
575 ansi_normal());
576 printf("%-*s",
577 (int) (column_width - k), e+k);
578 } else
579 printf("%-*s", (int) column_width, e);
ec75a254
DDM
580 }
581
582 putchar('\n');
583
584 /* on the first screen we reserve 2 extra lines for the title */
8fcd8576 585 if (i % break_lines == break_modulo)
ec75a254
DDM
586 if (!any_key_to_proceed())
587 return 0;
ec75a254
DDM
588 }
589
590 return 0;
591}
592
288a74cc 593int open_terminal(const char *name, int mode) {
254d1313 594 _cleanup_close_ int fd = -EBADF;
288a74cc
RC
595
596 /*
4768529f
LP
597 * If a TTY is in the process of being closed opening it might cause EIO. This is horribly awful, but
598 * unlikely to be changed in the kernel. Hence we work around this problem by retrying a couple of
599 * times.
288a74cc
RC
600 *
601 * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
602 */
603
fbd2679f 604 assert((mode & (O_CREAT|O_PATH|O_DIRECTORY|O_TMPFILE)) == 0);
288a74cc 605
fbd2679f 606 for (unsigned c = 0;; c++) {
288a74cc
RC
607 fd = open(name, mode, 0);
608 if (fd >= 0)
609 break;
610
611 if (errno != EIO)
612 return -errno;
613
614 /* Max 1s in total */
615 if (c >= 20)
fbd2679f 616 return -EIO;
288a74cc 617
4251512e 618 (void) usleep_safe(50 * USEC_PER_MSEC);
288a74cc
RC
619 }
620
dd9c8da8 621 if (!isatty_safe(fd))
aae47bf7 622 return -ENOTTY;
288a74cc 623
4768529f 624 return TAKE_FD(fd);
288a74cc
RC
625}
626
627int acquire_terminal(
628 const char *name,
8854d795 629 AcquireTerminalFlags flags,
288a74cc
RC
630 usec_t timeout) {
631
254d1313 632 _cleanup_close_ int notify = -EBADF, fd = -EBADF;
8854d795
LP
633 usec_t ts = USEC_INFINITY;
634 int r, wd = -1;
288a74cc
RC
635
636 assert(name);
789f4f7e
LP
637
638 AcquireTerminalFlags mode = flags & _ACQUIRE_TERMINAL_MODE_MASK;
639 assert(IN_SET(mode, ACQUIRE_TERMINAL_TRY, ACQUIRE_TERMINAL_FORCE, ACQUIRE_TERMINAL_WAIT));
640 assert(mode == ACQUIRE_TERMINAL_WAIT || !FLAGS_SET(flags, ACQUIRE_TERMINAL_WATCH_SIGTERM));
288a74cc 641
8854d795
LP
642 /* We use inotify to be notified when the tty is closed. We create the watch before checking if we can actually
643 * acquire it, so that we don't lose any event.
288a74cc 644 *
8854d795
LP
645 * Note: strictly speaking this actually watches for the device being closed, it does *not* really watch
646 * whether a tty loses its controlling process. However, unless some rogue process uses TIOCNOTTY on /dev/tty
f95dbcc2
ZJS
647 * *after* closing its tty otherwise this will not become a problem. As long as the administrator makes sure to
648 * not configure any service on the same tty as an untrusted user this should not be a problem. (Which they
8854d795
LP
649 * probably should not do anyway.) */
650
789f4f7e
LP
651 if (mode == ACQUIRE_TERMINAL_WAIT) {
652 notify = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
8854d795
LP
653 if (notify < 0)
654 return -errno;
288a74cc
RC
655
656 wd = inotify_add_watch(notify, name, IN_CLOSE);
8854d795
LP
657 if (wd < 0)
658 return -errno;
659
660 if (timeout != USEC_INFINITY)
661 ts = now(CLOCK_MONOTONIC);
288a74cc
RC
662 }
663
789f4f7e
LP
664 /* If we are called with ACQUIRE_TERMINAL_WATCH_SIGTERM we'll unblock SIGTERM during ppoll() temporarily */
665 sigset_t poll_ss;
666 assert_se(sigprocmask(SIG_SETMASK, /* newset= */ NULL, &poll_ss) >= 0);
667 if (flags & ACQUIRE_TERMINAL_WATCH_SIGTERM) {
668 assert_se(sigismember(&poll_ss, SIGTERM) > 0);
669 assert_se(sigdelset(&poll_ss, SIGTERM) >= 0);
670 }
671
288a74cc 672 for (;;) {
288a74cc
RC
673 if (notify >= 0) {
674 r = flush_fd(notify);
675 if (r < 0)
8854d795 676 return r;
288a74cc
RC
677 }
678
8854d795
LP
679 /* We pass here O_NOCTTY only so that we can check the return value TIOCSCTTY and have a reliable way
680 * to figure out if we successfully became the controlling process of the tty */
288a74cc
RC
681 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
682 if (fd < 0)
683 return fd;
684
8854d795 685 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed if we already own the tty. */
0bd72176
LP
686 struct sigaction sa_old;
687 assert_se(sigaction(SIGHUP, &sigaction_ignore, &sa_old) >= 0);
288a74cc
RC
688
689 /* First, try to get the tty */
789f4f7e 690 r = RET_NERRNO(ioctl(fd, TIOCSCTTY, mode == ACQUIRE_TERMINAL_FORCE));
288a74cc 691
8854d795 692 /* Reset signal handler to old value */
0bd72176 693 assert_se(sigaction(SIGHUP, &sa_old, NULL) >= 0);
288a74cc 694
8854d795
LP
695 /* Success? Exit the loop now! */
696 if (r >= 0)
697 break;
288a74cc 698
8854d795
LP
699 /* Any failure besides -EPERM? Fail, regardless of the mode. */
700 if (r != -EPERM)
701 return r;
288a74cc 702
8854d795
LP
703 if (flags & ACQUIRE_TERMINAL_PERMISSIVE) /* If we are in permissive mode, then EPERM is fine, turn this
704 * into a success. Note that EPERM is also returned if we
705 * already are the owner of the TTY. */
288a74cc
RC
706 break;
707
789f4f7e 708 if (mode != ACQUIRE_TERMINAL_WAIT) /* If we are in TRY or FORCE mode, then propagate EPERM as EPERM */
8854d795
LP
709 return r;
710
288a74cc 711 assert(notify >= 0);
8854d795 712 assert(wd >= 0);
288a74cc
RC
713
714 for (;;) {
789f4f7e
LP
715 usec_t left;
716 if (timeout == USEC_INFINITY)
717 left = USEC_INFINITY;
718 else {
8854d795
LP
719 assert(ts != USEC_INFINITY);
720
789f4f7e 721 usec_t n = usec_sub_unsigned(now(CLOCK_MONOTONIC), ts);
496db330 722 if (n >= timeout)
8854d795 723 return -ETIMEDOUT;
288a74cc 724
789f4f7e 725 left = timeout - n;
288a74cc
RC
726 }
727
789f4f7e
LP
728 r = ppoll_usec_full(
729 &(struct pollfd) {
730 .fd = notify,
731 .events = POLLIN,
732 },
fa6f4484 733 /* n_fds = */ 1,
789f4f7e
LP
734 left,
735 &poll_ss);
736 if (r < 0)
737 return r;
738 if (r == 0)
739 return -ETIMEDOUT;
740
741 union inotify_event_buffer buffer;
742 ssize_t l;
288a74cc
RC
743 l = read(notify, &buffer, sizeof(buffer));
744 if (l < 0) {
8add30a0 745 if (ERRNO_IS_TRANSIENT(errno))
288a74cc
RC
746 continue;
747
8854d795 748 return -errno;
288a74cc
RC
749 }
750
751 FOREACH_INOTIFY_EVENT(e, buffer, l) {
8854d795
LP
752 if (e->mask & IN_Q_OVERFLOW) /* If we hit an inotify queue overflow, simply check if the terminal is up for grabs now. */
753 break;
754
755 if (e->wd != wd || !(e->mask & IN_CLOSE)) /* Safety checks */
756 return -EIO;
288a74cc
RC
757 }
758
759 break;
760 }
761
8854d795
LP
762 /* We close the tty fd here since if the old session ended our handle will be dead. It's important that
763 * we do this after sleeping, so that we don't enter an endless loop. */
288a74cc
RC
764 fd = safe_close(fd);
765 }
766
c10d6bdb 767 return TAKE_FD(fd);
288a74cc
RC
768}
769
770int release_terminal(void) {
254d1313 771 _cleanup_close_ int fd = -EBADF;
87964ec7 772 int r;
288a74cc 773
0a8b555c 774 fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
288a74cc
RC
775 if (fd < 0)
776 return -errno;
777
778 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
779 * by our own TIOCNOTTY */
0bd72176
LP
780 struct sigaction sa_old;
781 assert_se(sigaction(SIGHUP, &sigaction_ignore, &sa_old) >= 0);
288a74cc 782
7c248223 783 r = RET_NERRNO(ioctl(fd, TIOCNOTTY));
288a74cc 784
0bd72176 785 assert_se(sigaction(SIGHUP, &sa_old, NULL) >= 0);
288a74cc
RC
786
787 return r;
788}
789
1de12823
MY
790int terminal_new_session(void) {
791
792 /* Make us the new session leader, and set stdin tty to be our controlling terminal.
793 *
794 * Why stdin? Well, the ctty logic is relevant for signal delivery mostly, i.e. if people hit C-c
795 * or the line is hung up. Such events are basically just a form of input, via a side channel
796 * (that side channel being signal delivery, i.e. SIGINT, SIGHUP et al). Hence we focus on input,
797 * not output here. */
798
799 if (!isatty_safe(STDIN_FILENO))
800 return -ENXIO;
801
802 (void) setsid();
803 return RET_NERRNO(ioctl(STDIN_FILENO, TIOCSCTTY, 0));
804}
805
0c15577a
DDM
806void terminal_detach_session(void) {
807 (void) setsid();
808 (void) release_terminal();
809}
810
288a74cc
RC
811int terminal_vhangup_fd(int fd) {
812 assert(fd >= 0);
7c248223 813 return RET_NERRNO(ioctl(fd, TIOCVHANGUP));
288a74cc
RC
814}
815
bc3477fd
DDM
816int terminal_vhangup(const char *tty) {
817 _cleanup_close_ int fd = -EBADF;
818
819 assert(tty);
820
821 fd = open_terminal(tty, O_RDWR|O_NOCTTY|O_CLOEXEC);
822 if (fd < 0)
823 return fd;
824
825 return terminal_vhangup_fd(fd);
826}
827
45d785df
LP
828int vt_disallocate(const char *tty_path) {
829 assert(tty_path);
288a74cc 830
45d785df
LP
831 /* Deallocate the VT if possible. If not possible (i.e. because it is the active one), at least clear
832 * it entirely (including the scrollback buffer). */
288a74cc 833
45d785df
LP
834 int ttynr = vtnr_from_tty(tty_path);
835 if (ttynr > 0) {
836 _cleanup_close_ int fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
ba5d26cc
ZJS
837 if (fd < 0)
838 return fd;
288a74cc 839
45d785df
LP
840 /* Try to deallocate */
841 if (ioctl(fd, VT_DISALLOCATE, ttynr) >= 0)
ba5d26cc
ZJS
842 return 0;
843 if (errno != EBUSY)
844 return -errno;
845 }
288a74cc 846
45d785df
LP
847 /* So this is not a VT (in which case we cannot deallocate it), or we failed to deallocate. Let's at
848 * least clear the screen. */
288a74cc 849
45d785df 850 _cleanup_close_ int fd2 = open_terminal(tty_path, O_WRONLY|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
ba5d26cc
ZJS
851 if (fd2 < 0)
852 return fd2;
288a74cc 853
45d785df
LP
854 return loop_write_full(fd2,
855 "\033[r" /* clear scrolling region */
856 "\033[H" /* move home */
857 "\033[3J" /* clear screen including scrollback, requires Linux 2.6.40 */
858 "\033c", /* reset to initial state */
859 SIZE_MAX,
860 100 * USEC_PER_MSEC);
288a74cc
RC
861}
862
b61c015a
LP
863static int vt_default_utf8(void) {
864 _cleanup_free_ char *b = NULL;
865 int r;
866
867 /* Read the default VT UTF8 setting from the kernel */
868
869 r = read_one_line_file("/sys/module/vt/parameters/default_utf8", &b);
870 if (r < 0)
871 return r;
872
873 return parse_boolean(b);
874}
875
876static int vt_reset_keyboard(int fd) {
af1d3a6d 877 int r, kb;
b61c015a 878
af1d3a6d 879 assert(fd >= 0);
b61c015a 880
af1d3a6d
LP
881 /* If we can't read the default, then default to Unicode. It's 2024 after all. */
882 r = vt_default_utf8();
883 if (r < 0)
884 log_debug_errno(r, "Failed to determine kernel VT UTF-8 mode, assuming enabled: %m");
885
886 kb = vt_default_utf8() != 0 ? K_UNICODE : K_XLATE;
b61c015a
LP
887 return RET_NERRNO(ioctl(fd, KDSKBMODE, kb));
888}
889
e2216800
LP
890static int terminal_reset_ioctl(int fd, bool switch_to_text) {
891 struct termios termios;
892 int r;
893
894 /* Set terminal to some sane defaults */
895
896 assert(fd >= 0);
897
898 /* We leave locked terminal attributes untouched, so that Plymouth may set whatever it wants to set,
899 * and we don't interfere with that. */
900
901 /* Disable exclusive mode, just in case */
902 if (ioctl(fd, TIOCNXCL) < 0)
903 log_debug_errno(errno, "TIOCNXCL ioctl failed on TTY, ignoring: %m");
904
905 /* Switch to text mode */
906 if (switch_to_text)
907 if (ioctl(fd, KDSETMODE, KD_TEXT) < 0)
908 log_debug_errno(errno, "KDSETMODE ioctl for switching to text mode failed on TTY, ignoring: %m");
909
910 /* Set default keyboard mode */
911 r = vt_reset_keyboard(fd);
912 if (r < 0)
913 log_debug_errno(r, "Failed to reset VT keyboard, ignoring: %m");
914
915 if (tcgetattr(fd, &termios) < 0) {
916 r = log_debug_errno(errno, "Failed to get terminal parameters: %m");
917 goto finish;
918 }
919
920 /* We only reset the stuff that matters to the software. How
921 * hardware is set up we don't touch assuming that somebody
922 * else will do that for us */
923
924 termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
925 termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
926 termios.c_oflag |= ONLCR | OPOST;
927 termios.c_cflag |= CREAD;
928 termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE;
929
930 termios.c_cc[VINTR] = 03; /* ^C */
931 termios.c_cc[VQUIT] = 034; /* ^\ */
932 termios.c_cc[VERASE] = 0177;
933 termios.c_cc[VKILL] = 025; /* ^X */
934 termios.c_cc[VEOF] = 04; /* ^D */
935 termios.c_cc[VSTART] = 021; /* ^Q */
936 termios.c_cc[VSTOP] = 023; /* ^S */
937 termios.c_cc[VSUSP] = 032; /* ^Z */
938 termios.c_cc[VLNEXT] = 026; /* ^V */
939 termios.c_cc[VWERASE] = 027; /* ^W */
940 termios.c_cc[VREPRINT] = 022; /* ^R */
941 termios.c_cc[VEOL] = 0;
942 termios.c_cc[VEOL2] = 0;
943
944 termios.c_cc[VTIME] = 0;
945 termios.c_cc[VMIN] = 1;
946
947 r = RET_NERRNO(tcsetattr(fd, TCSANOW, &termios));
948 if (r < 0)
949 log_debug_errno(r, "Failed to set terminal parameters: %m");
950
951finish:
952 /* Just in case, flush all crap out */
953 (void) tcflush(fd, TCIOFLUSH);
954
955 return r;
956}
957
3d97db8f 958int terminal_reset_ansi_seq(int fd) {
e2216800
LP
959 int r, k;
960
961 assert(fd >= 0);
962
963 if (getenv_terminal_is_dumb())
964 return 0;
965
966 r = fd_nonblock(fd, true);
967 if (r < 0)
968 return log_debug_errno(r, "Failed to set terminal to non-blocking mode: %m");
969
970 k = loop_write_full(fd,
971 "\033[!p" /* soft terminal reset */
972 "\033]104\007" /* reset colors */
4efd46c4
LP
973 "\033[?7h" /* enable line-wrapping */
974 "\033[1G" /* place cursor at beginning of current line */
975 "\033[0J", /* erase till end of screen */
e2216800 976 SIZE_MAX,
524e1240 977 100 * USEC_PER_MSEC);
e2216800 978 if (k < 0)
524e1240 979 log_debug_errno(k, "Failed to reset terminal through ANSI sequences: %m");
e2216800
LP
980
981 if (r > 0) {
982 r = fd_nonblock(fd, false);
983 if (r < 0)
984 log_debug_errno(r, "Failed to set terminal back to blocking mode: %m");
985 }
986
987 return k < 0 ? k : r;
988}
989
2736295d
LP
990void reset_dev_console_fd(int fd, bool switch_to_text) {
991 int r;
992
993 assert(fd >= 0);
994
2cd19499
LP
995 _cleanup_close_ int lock_fd = lock_dev_console();
996 if (lock_fd < 0)
997 log_debug_errno(lock_fd, "Failed to lock /dev/console, ignoring: %m");
998
5a4e5417 999 r = terminal_reset_ioctl(fd, switch_to_text);
2736295d
LP
1000 if (r < 0)
1001 log_warning_errno(r, "Failed to reset /dev/console, ignoring: %m");
1002
1003 unsigned rows, cols;
1004 r = proc_cmdline_tty_size("/dev/console", &rows, &cols);
1005 if (r < 0)
1006 log_warning_errno(r, "Failed to get /dev/console size, ignoring: %m");
1007 else if (r > 0) {
1008 r = terminal_set_size_fd(fd, NULL, rows, cols);
1009 if (r < 0)
1010 log_warning_errno(r, "Failed to set configured terminal size on /dev/console, ignoring: %m");
1011 } else
1012 (void) terminal_fix_size(fd, fd);
1013
1014 r = terminal_reset_ansi_seq(fd);
1015 if (r < 0)
1016 log_warning_errno(r, "Failed to reset /dev/console using ANSI sequences, ignoring: %m");
1017}
1018
4a24cc85
LP
1019int lock_dev_console(void) {
1020 _cleanup_close_ int fd = -EBADF;
1021 int r;
1022
1023 /* NB: We do not use O_NOFOLLOW here, because some container managers might place a symlink to some
1024 * pty in /dev/console, in which case it should be fine to lock the target TTY. */
1025 fd = open_terminal("/dev/console", O_RDONLY|O_CLOEXEC|O_NOCTTY);
1026 if (fd < 0)
1027 return fd;
1028
1029 r = lock_generic(fd, LOCK_BSD, LOCK_EX);
1030 if (r < 0)
1031 return r;
1032
1033 return TAKE_FD(fd);
1034}
1035
288a74cc
RC
1036int make_console_stdio(void) {
1037 int fd, r;
1038
9281e703
LP
1039 /* Make /dev/console the controlling terminal and stdin/stdout/stderr, if we can. If we can't use
1040 * /dev/null instead. This is particularly useful if /dev/console is turned off, e.g. if console=null
1041 * is specified on the kernel command line. */
288a74cc 1042
8854d795 1043 fd = acquire_terminal("/dev/console", ACQUIRE_TERMINAL_FORCE|ACQUIRE_TERMINAL_PERMISSIVE, USEC_INFINITY);
9281e703
LP
1044 if (fd < 0) {
1045 log_warning_errno(fd, "Failed to acquire terminal, using /dev/null stdin/stdout/stderr instead: %m");
288a74cc 1046
9281e703
LP
1047 r = make_null_stdio();
1048 if (r < 0)
1049 return log_error_errno(r, "Failed to make /dev/null stdin/stdout/stderr: %m");
3d18b167 1050
9281e703 1051 } else {
2736295d 1052 reset_dev_console_fd(fd, /* switch_to_text= */ true);
963e25c2 1053
9281e703
LP
1054 r = rearrange_stdio(fd, fd, fd); /* This invalidates 'fd' both on success and on failure. */
1055 if (r < 0)
1056 return log_error_errno(r, "Failed to make terminal stdin/stdout/stderr: %m");
1057 }
c6063244 1058
9281e703 1059 reset_terminal_feature_caches();
288a74cc
RC
1060 return 0;
1061}
1062
b27f7916 1063static int vtnr_from_tty_raw(const char *tty, unsigned *ret) {
288a74cc
RC
1064 assert(tty);
1065
b27f7916 1066 tty = skip_dev_prefix(tty);
288a74cc 1067
b27f7916
MY
1068 const char *e = startswith(tty, "tty");
1069 if (!e)
1070 return -EINVAL;
288a74cc 1071
b27f7916 1072 return safe_atou(e, ret);
288a74cc
RC
1073}
1074
1075int vtnr_from_tty(const char *tty) {
b27f7916 1076 unsigned u;
57e55f93 1077 int r;
288a74cc
RC
1078
1079 assert(tty);
1080
b27f7916 1081 r = vtnr_from_tty_raw(tty, &u);
288a74cc
RC
1082 if (r < 0)
1083 return r;
57e55f93
LP
1084 if (!vtnr_is_valid(u))
1085 return -ERANGE;
288a74cc 1086
57e55f93 1087 return (int) u;
288a74cc
RC
1088}
1089
b27f7916
MY
1090bool tty_is_vc(const char *tty) {
1091 assert(tty);
1092
1093 /* NB: for >= 0 values no range check is conducted here, on the assumption that the caller will
1094 * either extract vtnr through vtnr_from_tty() later where ERANGE would be reported, or doesn't care
79ef0374 1095 * about whether it's strictly valid, but only asking "does this fall into the vt category?", for which
b27f7916
MY
1096 * "yes" seems to be a better answer. */
1097
1098 return vtnr_from_tty_raw(tty, /* ret = */ NULL) >= 0;
1099}
1100
1101bool tty_is_console(const char *tty) {
1102 assert(tty);
1103
1104 return streq(skip_dev_prefix(tty), "console");
1105}
1106
845be16f 1107int resolve_dev_console(char **ret) {
7b912648 1108 int r;
288a74cc 1109
7b912648
LP
1110 assert(ret);
1111
dffbe1d1
LP
1112 /* Resolve where /dev/console is pointing to. If /dev/console is a symlink (like in container
1113 * managers), we'll just resolve the symlink. If it's a real device node, we'll use if
1114 * /sys/class/tty/tty0/active, but only if /sys/ is actually ours (i.e. not read-only-mounted which
1115 * is a sign for container setups). */
288a74cc 1116
dffbe1d1 1117 _cleanup_free_ char *chased = NULL;
39f46860 1118 r = chase("/dev/console", /* root= */ NULL, /* flags= */ 0, &chased, /* ret_fd= */ NULL);
dffbe1d1
LP
1119 if (r < 0)
1120 return r;
1121 if (!path_equal(chased, "/dev/console")) {
1122 *ret = TAKE_PTR(chased);
1123 return 0;
1124 }
1125
1126 r = path_is_read_only_fs("/sys");
1127 if (r < 0)
1128 return r;
1129 if (r > 0)
7b912648 1130 return -ENOMEDIUM;
288a74cc 1131
dffbe1d1 1132 _cleanup_free_ char *active = NULL;
7b912648
LP
1133 r = read_one_line_file("/sys/class/tty/console/active", &active);
1134 if (r < 0)
1135 return r;
288a74cc 1136
7b912648 1137 /* If multiple log outputs are configured the last one is what /dev/console points to */
dffbe1d1 1138 const char *tty = strrchr(active, ' ');
288a74cc
RC
1139 if (tty)
1140 tty++;
1141 else
7b912648 1142 tty = active;
288a74cc
RC
1143
1144 if (streq(tty, "tty0")) {
7b912648 1145 active = mfree(active);
288a74cc
RC
1146
1147 /* Get the active VC (e.g. tty1) */
7b912648
LP
1148 r = read_one_line_file("/sys/class/tty/tty0/active", &active);
1149 if (r < 0)
1150 return r;
1151
1152 tty = active;
1153 }
1154
c7f7d87d
DDM
1155 _cleanup_free_ char *path = NULL;
1156 path = path_join("/dev", tty);
1157 if (!path)
1158 return -ENOMEM;
288a74cc 1159
c7f7d87d 1160 *ret = TAKE_PTR(path);
7b912648 1161 return 0;
288a74cc
RC
1162}
1163
bef41af2
LP
1164int get_kernel_consoles(char ***ret) {
1165 _cleanup_strv_free_ char **l = NULL;
6af62124 1166 _cleanup_free_ char *line = NULL;
6af62124
WF
1167 int r;
1168
bef41af2
LP
1169 assert(ret);
1170
daf13202
MY
1171 /* If /sys/ is mounted read-only this means we are running in some kind of container environment.
1172 * In that case /sys/ would reflect the host system, not us, hence ignore the data we can read from it. */
bef41af2
LP
1173 if (path_is_read_only_fs("/sys") > 0)
1174 goto fallback;
6af62124
WF
1175
1176 r = read_one_line_file("/sys/class/tty/console/active", &line);
1177 if (r < 0)
1178 return r;
1179
daf13202 1180 for (const char *p = line;;) {
6abdec98 1181 _cleanup_free_ char *tty = NULL, *path = NULL;
6af62124 1182
bef41af2 1183 r = extract_first_word(&p, &tty, NULL, 0);
6af62124
WF
1184 if (r < 0)
1185 return r;
1186 if (r == 0)
1187 break;
1188
1189 if (streq(tty, "tty0")) {
1190 tty = mfree(tty);
1191 r = read_one_line_file("/sys/class/tty/tty0/active", &tty);
1192 if (r < 0)
1193 return r;
1194 }
1195
6abdec98 1196 path = path_join("/dev", tty);
6af62124
WF
1197 if (!path)
1198 return -ENOMEM;
1199
1200 if (access(path, F_OK) < 0) {
1201 log_debug_errno(errno, "Console device %s is not accessible, skipping: %m", path);
6af62124
WF
1202 continue;
1203 }
1204
6abdec98 1205 r = strv_consume(&l, TAKE_PTR(path));
6af62124
WF
1206 if (r < 0)
1207 return r;
1208 }
1209
bef41af2 1210 if (strv_isempty(l)) {
6af62124 1211 log_debug("No devices found for system console");
bef41af2 1212 goto fallback;
6af62124
WF
1213 }
1214
ae2a15bc 1215 *ret = TAKE_PTR(l);
daf13202 1216 return strv_length(*ret);
bef41af2
LP
1217
1218fallback:
1219 r = strv_extend(&l, "/dev/console");
1220 if (r < 0)
1221 return r;
1222
ae2a15bc 1223 *ret = TAKE_PTR(l);
6af62124
WF
1224 return 0;
1225}
1226
288a74cc 1227bool tty_is_vc_resolve(const char *tty) {
7b912648 1228 _cleanup_free_ char *resolved = NULL;
288a74cc
RC
1229
1230 assert(tty);
1231
c7f7d87d 1232 if (streq(skip_dev_prefix(tty), "console")) {
7b912648 1233 if (resolve_dev_console(&resolved) < 0)
288a74cc 1234 return false;
7b912648
LP
1235
1236 tty = resolved;
288a74cc
RC
1237 }
1238
1239 return tty_is_vc(tty);
1240}
1241
288a74cc
RC
1242int fd_columns(int fd) {
1243 struct winsize ws = {};
1244
14f594b9
LP
1245 if (fd < 0)
1246 return -EBADF;
1247
288a74cc
RC
1248 if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
1249 return -errno;
1250
1251 if (ws.ws_col <= 0)
0ea4198f 1252 return -ENODATA; /* some tty types come up with invalid row/column initially, return a recognizable error for that */
288a74cc
RC
1253
1254 return ws.ws_col;
1255}
1256
e8139b15
LP
1257int getenv_columns(void) {
1258 int r;
1259
1260 const char *e = getenv("COLUMNS");
1261 if (!e)
1262 return -ENXIO;
1263
1264 unsigned c;
1265 r = safe_atou_bounded(e, 1, USHRT_MAX, &c);
1266 if (r < 0)
1267 return r;
1268
1269 return (int) c;
1270}
1271
288a74cc 1272unsigned columns(void) {
288a74cc 1273
c6063244 1274 if (cached_columns > 0)
288a74cc
RC
1275 return cached_columns;
1276
e8139b15
LP
1277 int c = getenv_columns();
1278 if (c < 0) {
288a74cc 1279 c = fd_columns(STDOUT_FILENO);
e8139b15 1280 if (c < 0)
d09a7135
LP
1281 c = 80;
1282 }
288a74cc 1283
e8139b15
LP
1284 assert(c > 0);
1285
288a74cc
RC
1286 cached_columns = c;
1287 return cached_columns;
1288}
1289
1290int fd_lines(int fd) {
1291 struct winsize ws = {};
1292
14f594b9
LP
1293 if (fd < 0)
1294 return -EBADF;
1295
288a74cc
RC
1296 if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
1297 return -errno;
1298
1299 if (ws.ws_row <= 0)
0ea4198f 1300 return -ENODATA; /* some tty types come up with invalid row/column initially, return a recognizable error for that */
288a74cc
RC
1301
1302 return ws.ws_row;
1303}
1304
1305unsigned lines(void) {
1306 const char *e;
1307 int l;
1308
c6063244 1309 if (cached_lines > 0)
288a74cc
RC
1310 return cached_lines;
1311
1312 l = 0;
1313 e = getenv("LINES");
1314 if (e)
1315 (void) safe_atoi(e, &l);
1316
d09a7135 1317 if (l <= 0 || l > USHRT_MAX) {
288a74cc 1318 l = fd_lines(STDOUT_FILENO);
d09a7135
LP
1319 if (l <= 0)
1320 l = 24;
1321 }
288a74cc
RC
1322
1323 cached_lines = l;
1324 return cached_lines;
1325}
1326
51462135
DDM
1327int terminal_set_size_fd(int fd, const char *ident, unsigned rows, unsigned cols) {
1328 struct winsize ws;
1329
061b4458
LP
1330 assert(fd >= 0);
1331
1332 if (!ident)
1333 ident = "TTY";
1334
51462135
DDM
1335 if (rows == UINT_MAX && cols == UINT_MAX)
1336 return 0;
1337
1338 if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
1339 return log_debug_errno(errno,
1340 "TIOCGWINSZ ioctl for getting %s size failed, not setting terminal size: %m",
061b4458 1341 ident);
51462135
DDM
1342
1343 if (rows == UINT_MAX)
1344 rows = ws.ws_row;
1345 else if (rows > USHRT_MAX)
1346 rows = USHRT_MAX;
1347
1348 if (cols == UINT_MAX)
1349 cols = ws.ws_col;
1350 else if (cols > USHRT_MAX)
1351 cols = USHRT_MAX;
1352
1353 if (rows == ws.ws_row && cols == ws.ws_col)
1354 return 0;
1355
1356 ws.ws_row = rows;
1357 ws.ws_col = cols;
1358
1359 if (ioctl(fd, TIOCSWINSZ, &ws) < 0)
061b4458 1360 return log_debug_errno(errno, "TIOCSWINSZ ioctl for setting %s size failed: %m", ident);
51462135
DDM
1361
1362 return 0;
1363}
1364
29f5a5ae
DDM
1365int proc_cmdline_tty_size(const char *tty, unsigned *ret_rows, unsigned *ret_cols) {
1366 _cleanup_free_ char *rowskey = NULL, *rowsvalue = NULL, *colskey = NULL, *colsvalue = NULL;
1367 unsigned rows = UINT_MAX, cols = UINT_MAX;
1368 int r;
1369
1370 assert(tty);
1371
1372 if (!ret_rows && !ret_cols)
1373 return 0;
1374
1375 tty = skip_dev_prefix(tty);
7036dd8b
MY
1376 if (path_startswith(tty, "pts/"))
1377 return -EMEDIUMTYPE;
29f5a5ae 1378 if (!in_charset(tty, ALPHANUMERICAL))
7036dd8b
MY
1379 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
1380 "TTY name '%s' contains non-alphanumeric characters, not searching kernel cmdline for size.", tty);
29f5a5ae
DDM
1381
1382 rowskey = strjoin("systemd.tty.rows.", tty);
1383 if (!rowskey)
1384 return -ENOMEM;
1385
1386 colskey = strjoin("systemd.tty.columns.", tty);
1387 if (!colskey)
1388 return -ENOMEM;
1389
1390 r = proc_cmdline_get_key_many(/* flags = */ 0,
1391 rowskey, &rowsvalue,
1392 colskey, &colsvalue);
1393 if (r < 0)
1394 return log_debug_errno(r, "Failed to read TTY size of %s from kernel cmdline: %m", tty);
1395
1396 if (rowsvalue) {
1397 r = safe_atou(rowsvalue, &rows);
1398 if (r < 0)
1399 return log_debug_errno(r, "Failed to parse %s=%s: %m", rowskey, rowsvalue);
1400 }
1401
1402 if (colsvalue) {
1403 r = safe_atou(colsvalue, &cols);
1404 if (r < 0)
1405 return log_debug_errno(r, "Failed to parse %s=%s: %m", colskey, colsvalue);
1406 }
1407
1408 if (ret_rows)
1409 *ret_rows = rows;
1410 if (ret_cols)
1411 *ret_cols = cols;
1412
f6927e3c 1413 return rows != UINT_MAX || cols != UINT_MAX;
29f5a5ae
DDM
1414}
1415
288a74cc
RC
1416/* intended to be used as a SIGWINCH sighandler */
1417void columns_lines_cache_reset(int signum) {
1418 cached_columns = 0;
1419 cached_lines = 0;
1420}
1421
c6063244
LP
1422void reset_terminal_feature_caches(void) {
1423 cached_columns = 0;
1424 cached_lines = 0;
1425
c6063244 1426 cached_on_tty = -1;
197dd3a9 1427 cached_on_dev_null = -1;
7227d5bf
DDM
1428
1429 reset_ansi_feature_caches();
c6063244 1430}
288a74cc 1431
c6063244 1432bool on_tty(void) {
8cd0356e
LP
1433
1434 /* We check both stdout and stderr, so that situations where pipes on the shell are used are reliably
1435 * recognized, regardless if only the output or the errors are piped to some place. Since on_tty() is generally
1436 * used to default to a safer, non-interactive, non-color mode of operation it's probably good to be defensive
1437 * here, and check for both. Note that we don't check for STDIN_FILENO, because it should fine to use fancy
1438 * terminal functionality when outputting stuff, even if the input is piped to us. */
1439
c6063244 1440 if (cached_on_tty < 0)
8cd0356e 1441 cached_on_tty =
300b7e76
LP
1442 isatty_safe(STDOUT_FILENO) &&
1443 isatty_safe(STDERR_FILENO);
288a74cc
RC
1444
1445 return cached_on_tty;
1446}
1447
288a74cc 1448int getttyname_malloc(int fd, char **ret) {
454318d3 1449 char path[PATH_MAX]; /* PATH_MAX is counted *with* the trailing NUL byte */
288a74cc
RC
1450 int r;
1451
1452 assert(fd >= 0);
1453 assert(ret);
1454
30222f4b
ZJS
1455 r = ttyname_r(fd, path, sizeof path); /* positive error */
1456 assert(r >= 0);
1457 if (r == ERANGE)
1458 return -ENAMETOOLONG;
1459 if (r > 0)
1460 return -r;
288a74cc 1461
454318d3 1462 return strdup_to(ret, skip_dev_prefix(path));
288a74cc
RC
1463}
1464
f171decd
LP
1465int getttyname_harder(int fd, char **ret) {
1466 _cleanup_free_ char *s = NULL;
1467 int r;
288a74cc 1468
f171decd
LP
1469 r = getttyname_malloc(fd, &s);
1470 if (r < 0)
1471 return r;
288a74cc 1472
f171decd
LP
1473 if (streq(s, "tty"))
1474 return get_ctty(0, NULL, ret);
288a74cc 1475
f171decd 1476 *ret = TAKE_PTR(s);
288a74cc
RC
1477 return 0;
1478}
1479
ac508b11 1480int get_ctty_devnr(pid_t pid, dev_t *ret) {
288a74cc 1481 _cleanup_free_ char *line = NULL;
288a74cc 1482 unsigned long ttynr;
ac508b11
LP
1483 const char *p;
1484 int r;
288a74cc
RC
1485
1486 assert(pid >= 0);
1487
1488 p = procfs_file_alloca(pid, "stat");
1489 r = read_one_line_file(p, &line);
1490 if (r < 0)
1491 return r;
1492
1493 p = strrchr(line, ')');
1494 if (!p)
1495 return -EIO;
1496
1497 p++;
1498
1499 if (sscanf(p, " "
1500 "%*c " /* state */
1501 "%*d " /* ppid */
1502 "%*d " /* pgrp */
1503 "%*d " /* session */
1504 "%lu ", /* ttynr */
1505 &ttynr) != 1)
1506 return -EIO;
1507
d80e2a1e 1508 if (devnum_is_zero(ttynr))
cfeaa44a 1509 return -ENXIO;
288a74cc 1510
ac508b11
LP
1511 if (ret)
1512 *ret = (dev_t) ttynr;
288a74cc
RC
1513
1514 return 0;
1515}
1516
54b22b26 1517int get_ctty(pid_t pid, dev_t *ret_devnr, char **ret) {
11f3c130
LP
1518 char pty[STRLEN("/dev/pts/") + DECIMAL_STR_MAX(dev_t) + 1];
1519 _cleanup_free_ char *buf = NULL;
1520 const char *fn = NULL, *w;
288a74cc 1521 dev_t devnr;
54b22b26 1522 int r;
288a74cc 1523
54b22b26
LP
1524 r = get_ctty_devnr(pid, &devnr);
1525 if (r < 0)
1526 return r;
288a74cc 1527
11f3c130 1528 r = device_path_make_canonical(S_IFCHR, devnr, &buf);
54b22b26 1529 if (r < 0) {
11f3c130
LP
1530 struct stat st;
1531
54b22b26
LP
1532 if (r != -ENOENT) /* No symlink for this in /dev/char/? */
1533 return r;
288a74cc 1534
11f3c130
LP
1535 /* Maybe this is PTY? PTY devices are not listed in /dev/char/, as they don't follow the
1536 * Linux device model and hence device_path_make_canonical() doesn't work for them. Let's
1537 * assume this is a PTY for a moment, and check if the device node this would then map to in
1538 * /dev/pts/ matches the one we are looking for. This way we don't have to hardcode the major
1539 * number (which is 136 btw), but we still rely on the fact that PTY numbers map directly to
1540 * the minor number of the pty. */
1541 xsprintf(pty, "/dev/pts/%u", minor(devnr));
1542
1543 if (stat(pty, &st) < 0) {
1544 if (errno != ENOENT)
1545 return -errno;
54b22b26 1546
11f3c130
LP
1547 } else if (S_ISCHR(st.st_mode) && devnr == st.st_rdev) /* Bingo! */
1548 fn = pty;
288a74cc 1549
11f3c130
LP
1550 if (!fn) {
1551 /* Doesn't exist, or not a PTY? Probably something similar to the PTYs which have no
1552 * symlink in /dev/char/. Let's return something vaguely useful. */
1553 r = device_path_make_major_minor(S_IFCHR, devnr, &buf);
54b22b26
LP
1554 if (r < 0)
1555 return r;
11f3c130
LP
1556
1557 fn = buf;
288a74cc 1558 }
11f3c130
LP
1559 } else
1560 fn = buf;
288a74cc 1561
11f3c130
LP
1562 w = path_startswith(fn, "/dev/");
1563 if (!w)
1564 return -EINVAL;
54b22b26 1565
11f3c130 1566 if (ret) {
4f77ddca
ZJS
1567 r = strdup_to(ret, w);
1568 if (r < 0)
1569 return r;
11f3c130 1570 }
54b22b26
LP
1571
1572 if (ret_devnr)
1573 *ret_devnr = devnr;
288a74cc
RC
1574
1575 return 0;
1576}
a07c35c3 1577
66cb2fde 1578int ptsname_malloc(int fd, char **ret) {
66cb2fde
LP
1579 assert(fd >= 0);
1580 assert(ret);
1581
deedd5c2
MY
1582 for (size_t l = 50;;) {
1583 _cleanup_free_ char *c = NULL;
66cb2fde
LP
1584
1585 c = new(char, l);
1586 if (!c)
1587 return -ENOMEM;
1588
deedd5c2
MY
1589 if (ptsname_r(fd, c, l) >= 0) {
1590 *ret = TAKE_PTR(c);
66cb2fde
LP
1591 return 0;
1592 }
deedd5c2 1593 if (errno != ERANGE)
66cb2fde 1594 return -errno;
1fd4c4ed 1595
deedd5c2 1596 if (!MUL_ASSIGN_SAFE(&l, 2))
1fd4c4ed 1597 return -ENOMEM;
66cb2fde
LP
1598 }
1599}
1600
fbd2679f 1601int openpt_allocate(int flags, char **ret_peer_path) {
254d1313 1602 _cleanup_close_ int fd = -EBADF;
ae1d13db
FB
1603 int r;
1604
1605 fd = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
1606 if (fd < 0)
1607 return -errno;
1608
fbd2679f
LP
1609 _cleanup_free_ char *p = NULL;
1610 if (ret_peer_path) {
ae1d13db
FB
1611 r = ptsname_malloc(fd, &p);
1612 if (r < 0)
1613 return r;
1614
1615 if (!path_startswith(p, "/dev/pts/"))
1616 return -EINVAL;
1617 }
1618
1619 if (unlockpt(fd) < 0)
1620 return -errno;
1621
fbd2679f
LP
1622 if (ret_peer_path)
1623 *ret_peer_path = TAKE_PTR(p);
ae1d13db
FB
1624
1625 return TAKE_FD(fd);
1626}
1627
1628static int ptsname_namespace(int pty, char **ret) {
fbd2679f
LP
1629 int no = -1;
1630
1631 assert(pty >= 0);
1632 assert(ret);
a07c35c3
LP
1633
1634 /* Like ptsname(), but doesn't assume that the path is
1635 * accessible in the local namespace. */
1636
fbd2679f 1637 if (ioctl(pty, TIOCGPTN, &no) < 0)
a07c35c3
LP
1638 return -errno;
1639
1640 if (no < 0)
1641 return -EIO;
1642
1643 if (asprintf(ret, "/dev/pts/%i", no) < 0)
1644 return -ENOMEM;
1645
1646 return 0;
1647}
66cb2fde 1648
b25430de
LP
1649int openpt_allocate_in_namespace(
1650 const PidRef *pidref,
1651 int flags,
1652 char **ret_peer_path) {
1653
254d1313 1654 _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, usernsfd = -EBADF, rootfd = -EBADF, fd = -EBADF;
71136404 1655 _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
66cb2fde
LP
1656 int r;
1657
b25430de 1658 r = pidref_namespace_open(pidref, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
66cb2fde
LP
1659 if (r < 0)
1660 return r;
1661
fbd2679f 1662 if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pair) < 0)
66cb2fde
LP
1663 return -errno;
1664
fbd2679f
LP
1665 r = namespace_fork(
1666 "(sd-openptns)",
1667 "(sd-openpt)",
1668 /* except_fds= */ NULL,
1669 /* n_except_fds= */ 0,
1670 FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_WAIT,
1671 pidnsfd,
1672 mntnsfd,
1673 /* netns_fd= */ -EBADF,
1674 usernsfd,
1675 rootfd,
1676 /* ret_pid= */ NULL);
4c253ed1
LP
1677 if (r < 0)
1678 return r;
1679 if (r == 0) {
66cb2fde
LP
1680 pair[0] = safe_close(pair[0]);
1681
fbd2679f 1682 fd = openpt_allocate(flags, /* ret_peer_path= */ NULL);
ae1d13db 1683 if (fd < 0)
66cb2fde
LP
1684 _exit(EXIT_FAILURE);
1685
ae1d13db 1686 if (send_one_fd(pair[1], fd, 0) < 0)
66cb2fde
LP
1687 _exit(EXIT_FAILURE);
1688
1689 _exit(EXIT_SUCCESS);
1690 }
1691
1692 pair[1] = safe_close(pair[1]);
1693
ae1d13db
FB
1694 fd = receive_one_fd(pair[0], 0);
1695 if (fd < 0)
1696 return fd;
1697
fbd2679f
LP
1698 if (ret_peer_path) {
1699 r = ptsname_namespace(fd, ret_peer_path);
ae1d13db
FB
1700 if (r < 0)
1701 return r;
1702 }
1703
1704 return TAKE_FD(fd);
66cb2fde 1705}
40e1f4ea 1706
197dd3a9
DDM
1707static bool on_dev_null(void) {
1708 struct stat dst, ost, est;
1709
1710 if (cached_on_dev_null >= 0)
1711 return cached_on_dev_null;
1712
1713 if (stat("/dev/null", &dst) < 0 || fstat(STDOUT_FILENO, &ost) < 0 || fstat(STDERR_FILENO, &est) < 0)
1714 cached_on_dev_null = false;
1715 else
1716 cached_on_dev_null = stat_inode_same(&dst, &ost) && stat_inode_same(&dst, &est);
1717
1718 return cached_on_dev_null;
1719}
1720
1b889631 1721bool getenv_terminal_is_dumb(void) {
ac96418b
LP
1722 const char *e;
1723
ac96418b
LP
1724 e = getenv("TERM");
1725 if (!e)
1726 return true;
1727
1728 return streq(e, "dumb");
1729}
1730
158fbf76 1731bool terminal_is_dumb(void) {
197dd3a9 1732 if (!on_tty() && !on_dev_null())
158fbf76
ZJS
1733 return true;
1734
1735 return getenv_terminal_is_dumb();
1736}
1737
c2b32159
LP
1738bool dev_console_colors_enabled(void) {
1739 _cleanup_free_ char *s = NULL;
c4fea19a 1740 ColorMode m;
c2b32159
LP
1741
1742 /* Returns true if we assume that color is supported on /dev/console.
1743 *
1744 * For that we first check if we explicitly got told to use colors or not, by checking $SYSTEMD_COLORS. If that
f95dbcc2
ZJS
1745 * isn't set we check whether PID 1 has $TERM set, and if not, whether TERM is set on the kernel command
1746 * line. If we find $TERM set we assume color if it's not set to "dumb", similarly to how regular
c2b32159
LP
1747 * colors_enabled() operates. */
1748
c4fea19a
MGR
1749 m = parse_systemd_colors();
1750 if (m >= 0)
1751 return m;
c2b32159 1752
c484315b
ZJS
1753 if (getenv("NO_COLOR"))
1754 return false;
1755
c2b32159
LP
1756 if (getenv_for_pid(1, "TERM", &s) <= 0)
1757 (void) proc_cmdline_get_key("TERM", 0, &s);
1758
1759 return !streq_ptr(s, "dumb");
1760}
1761
6179ede1 1762int vt_restore(int fd) {
d3f818fe 1763
6179ede1
FB
1764 static const struct vt_mode mode = {
1765 .mode = VT_AUTO,
1766 };
d3f818fe
MY
1767
1768 int r, ret = 0;
1769
1770 assert(fd >= 0);
6179ede1 1771
dd9c8da8 1772 if (!isatty_safe(fd))
7f6086d5 1773 return log_debug_errno(SYNTHETIC_ERRNO(ENOTTY), "Asked to restore the VT for an fd that does not refer to a terminal.");
e60a4a3c 1774
1802d5f2 1775 if (ioctl(fd, KDSETMODE, KD_TEXT) < 0)
d3f818fe 1776 RET_GATHER(ret, log_debug_errno(errno, "Failed to set VT to text mode, ignoring: %m"));
6179ede1
FB
1777
1778 r = vt_reset_keyboard(fd);
d3f818fe
MY
1779 if (r < 0)
1780 RET_GATHER(ret, log_debug_errno(r, "Failed to reset keyboard mode, ignoring: %m"));
6179ede1 1781
d3f818fe
MY
1782 if (ioctl(fd, VT_SETMODE, &mode) < 0)
1783 RET_GATHER(ret, log_debug_errno(errno, "Failed to set VT_AUTO mode, ignoring: %m"));
6179ede1 1784
f5fbe71d 1785 r = fchmod_and_chown(fd, TTY_MODE, 0, GID_INVALID);
d3f818fe
MY
1786 if (r < 0)
1787 RET_GATHER(ret, log_debug_errno(r, "Failed to chmod()/chown() VT, ignoring: %m"));
6179ede1 1788
d3f818fe 1789 return ret;
6179ede1 1790}
27dafac9
FB
1791
1792int vt_release(int fd, bool restore) {
1793 assert(fd >= 0);
1794
1795 /* This function releases the VT by acknowledging the VT-switch signal
1796 * sent by the kernel and optionally reset the VT in text and auto
1797 * VT-switching modes. */
1798
dd9c8da8 1799 if (!isatty_safe(fd))
7f6086d5 1800 return log_debug_errno(SYNTHETIC_ERRNO(ENOTTY), "Asked to release the VT for an fd that does not refer to a terminal.");
e60a4a3c 1801
27dafac9
FB
1802 if (ioctl(fd, VT_RELDISP, 1) < 0)
1803 return -errno;
1804
1805 if (restore)
1806 return vt_restore(fd);
1807
1808 return 0;
1809}
37b8d2f6
ZJS
1810
1811void get_log_colors(int priority, const char **on, const char **off, const char **highlight) {
1812 /* Note that this will initialize output variables only when there's something to output.
5e2b0e1c 1813 * The caller must pre-initialize to "" or NULL as appropriate. */
37b8d2f6
ZJS
1814
1815 if (priority <= LOG_ERR) {
1816 if (on)
25e4608b 1817 *on = ansi_highlight_red();
37b8d2f6 1818 if (off)
bb146d23 1819 *off = ansi_normal();
37b8d2f6 1820 if (highlight)
bb146d23 1821 *highlight = ansi_highlight();
37b8d2f6 1822
0d0464d3
ZJS
1823 } else if (priority <= LOG_WARNING) {
1824 if (on)
25e4608b 1825 *on = ansi_highlight_yellow();
0d0464d3 1826 if (off)
bb146d23 1827 *off = ansi_normal();
0d0464d3 1828 if (highlight)
bb146d23 1829 *highlight = ansi_highlight();
0d0464d3 1830
37b8d2f6
ZJS
1831 } else if (priority <= LOG_NOTICE) {
1832 if (on)
bb146d23 1833 *on = ansi_highlight();
37b8d2f6 1834 if (off)
bb146d23 1835 *off = ansi_normal();
37b8d2f6 1836 if (highlight)
25e4608b 1837 *highlight = ansi_highlight_red();
37b8d2f6
ZJS
1838
1839 } else if (priority >= LOG_DEBUG) {
1840 if (on)
25e4608b 1841 *on = ansi_grey();
37b8d2f6 1842 if (off)
bb146d23 1843 *off = ansi_normal();
37b8d2f6 1844 if (highlight)
25e4608b 1845 *highlight = ansi_highlight_red();
37b8d2f6
ZJS
1846 }
1847}
fc7eb132 1848
53f0ab51 1849int terminal_set_cursor_position(int fd, unsigned row, unsigned column) {
fc7eb132
OJ
1850 assert(fd >= 0);
1851
53f0ab51 1852 char cursor_position[STRLEN("\x1B[" ";" "H") + DECIMAL_STR_MAX(unsigned) * 2 + 1];
fc7eb132
OJ
1853 xsprintf(cursor_position, "\x1B[%u;%uH", row, column);
1854
53f0ab51 1855 return loop_write(fd, cursor_position, SIZE_MAX);
fc7eb132 1856}
d02d4f83 1857
30aeab78
LP
1858static int terminal_verify_same(int input_fd, int output_fd) {
1859 assert(input_fd >= 0);
1860 assert(output_fd >= 0);
1861
1862 /* Validates that the specified fds reference the same TTY */
1863
1864 if (input_fd != output_fd) {
1865 struct stat sti;
1866 if (fstat(input_fd, &sti) < 0)
1867 return -errno;
1868
1869 if (!S_ISCHR(sti.st_mode)) /* TTYs are character devices */
1870 return -ENOTTY;
1871
1872 struct stat sto;
1873 if (fstat(output_fd, &sto) < 0)
1874 return -errno;
1875
1876 if (!S_ISCHR(sto.st_mode))
1877 return -ENOTTY;
1878
1879 if (sti.st_rdev != sto.st_rdev)
1880 return -ENOLINK;
1881 }
1882
1883 if (!isatty_safe(input_fd)) /* The check above was just for char device, but now let's ensure it's actually a tty */
1884 return -ENOTTY;
1885
1886 return 0;
1887}
1888
1889typedef enum CursorPositionState {
1890 CURSOR_TEXT,
1891 CURSOR_ESCAPE,
1892 CURSOR_ROW,
1893 CURSOR_COLUMN,
1894} CursorPositionState;
1895
1896typedef struct CursorPositionContext {
1897 CursorPositionState state;
1898 unsigned row, column;
1899} CursorPositionContext;
1900
1901static int scan_cursor_position_response(
1902 CursorPositionContext *context,
1903 const char *buf,
1904 size_t size,
1905 size_t *ret_processed) {
1906
1907 assert(context);
1908 assert(buf);
1909 assert(ret_processed);
1910
1911 for (size_t i = 0; i < size; i++) {
1912 char c = buf[i];
1913
1914 switch (context->state) {
1915
1916 case CURSOR_TEXT:
1917 context->state = c == '\x1B' ? CURSOR_ESCAPE : CURSOR_TEXT;
1918 break;
1919
1920 case CURSOR_ESCAPE:
1921 context->state = c == '[' ? CURSOR_ROW : CURSOR_TEXT;
1922 break;
1923
1924 case CURSOR_ROW:
1925 if (c == ';')
1926 context->state = context->row > 0 ? CURSOR_COLUMN : CURSOR_TEXT;
1927 else {
1928 int d = undecchar(c);
1929
1930 /* We read a decimal character, let's suffix it to the number we so far read,
1931 * but let's do an overflow check first. */
1932 if (d < 0 || context->row > (UINT_MAX-d)/10)
1933 context->state = CURSOR_TEXT;
1934 else
1935 context->row = context->row * 10 + d;
1936 }
1937 break;
1938
1939 case CURSOR_COLUMN:
1940 if (c == 'R') {
1941 if (context->column > 0) {
1942 *ret_processed = i + 1;
1943 return 1; /* success! */
1944 }
1945
1946 context->state = CURSOR_TEXT;
1947 } else {
1948 int d = undecchar(c);
1949
1950 /* As above, add the decimal character to our column number */
1951 if (d < 0 || context->column > (UINT_MAX-d)/10)
1952 context->state = CURSOR_TEXT;
1953 else
1954 context->column = context->column * 10 + d;
1955 }
1956
1957 break;
1958 }
1959
1960 /* Reset any positions we might have picked up */
1961 if (IN_SET(context->state, CURSOR_TEXT, CURSOR_ESCAPE))
1962 context->row = context->column = 0;
1963 }
1964
1965 *ret_processed = size;
1966 return 0; /* all good, but not enough data yet */
1967}
1968
1969int terminal_get_cursor_position(
1970 int input_fd,
1971 int output_fd,
1972 unsigned *ret_row,
1973 unsigned *ret_column) {
1974
1975 _cleanup_close_ int nonblock_input_fd = -EBADF;
1976 int r;
1977
1978 assert(input_fd >= 0);
1979 assert(output_fd >= 0);
1980
1981 if (terminal_is_dumb())
1982 return -EOPNOTSUPP;
1983
1984 r = terminal_verify_same(input_fd, output_fd);
1985 if (r < 0)
1986 return log_debug_errno(r, "Called with distinct input/output fds: %m");
1987
1988 struct termios old_termios;
1989 if (tcgetattr(input_fd, &old_termios) < 0)
1990 return log_debug_errno(errno, "Failed to get terminal settings: %m");
1991
1992 struct termios new_termios = old_termios;
1993 termios_disable_echo(&new_termios);
1994
1995 if (tcsetattr(input_fd, TCSANOW, &new_termios) < 0)
1996 return log_debug_errno(errno, "Failed to set new terminal settings: %m");
1997
1998 /* Request cursor position (DSR/CPR) */
1999 r = loop_write(output_fd, "\x1B[6n", SIZE_MAX);
2000 if (r < 0)
2001 goto finish;
2002
2003 /* Open a 2nd input fd, in non-blocking mode, so that we won't ever hang in read() should someone
2004 * else process the POLLIN. */
2005
2006 nonblock_input_fd = r = fd_reopen(input_fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
2007 if (r < 0)
2008 goto finish;
2009
2010 usec_t end = usec_add(now(CLOCK_MONOTONIC), CONSOLE_REPLY_WAIT_USEC);
2011 char buf[STRLEN("\x1B[1;1R")]; /* The shortest valid reply possible */
2012 size_t buf_full = 0;
2013 CursorPositionContext context = {};
2014
2015 for (bool first = true;; first = false) {
2016 if (buf_full == 0) {
2017 usec_t n = now(CLOCK_MONOTONIC);
2018 if (n >= end) {
2019 r = -EOPNOTSUPP;
2020 goto finish;
2021 }
2022
2023 r = fd_wait_for_event(nonblock_input_fd, POLLIN, usec_sub_unsigned(end, n));
2024 if (r < 0)
2025 goto finish;
2026 if (r == 0) {
2027 r = -EOPNOTSUPP;
2028 goto finish;
2029 }
2030
2031 /* On the first try, read multiple characters, i.e. the shortest valid
2032 * reply. Afterwards read byte-wise, since we don't want to read too much, and
2033 * unnecessarily drop too many characters from the input queue. */
2034 ssize_t l = read(nonblock_input_fd, buf, first ? sizeof(buf) : 1);
2035 if (l < 0) {
2036 if (errno == EAGAIN)
2037 continue;
2038
2039 r = -errno;
2040 goto finish;
2041 }
2042
2043 assert((size_t) l <= sizeof(buf));
2044 buf_full = l;
2045 }
2046
2047 size_t processed;
2048 r = scan_cursor_position_response(&context, buf, buf_full, &processed);
2049 if (r < 0)
2050 goto finish;
2051
2052 assert(processed <= buf_full);
2053 buf_full -= processed;
2054 memmove(buf, buf + processed, buf_full);
2055
2056 if (r > 0) {
2057 /* Superficial validity check */
2058 if (context.row >= 32766 || context.column >= 32766) {
2059 r = -ENODATA;
2060 goto finish;
2061 }
2062
2063 if (ret_row)
2064 *ret_row = context.row;
2065 if (ret_column)
2066 *ret_column = context.column;
2067
2068 r = 0;
2069 goto finish;
2070 }
2071 }
2072
2073finish:
e698ee57
ZJS
2074 /* We ignore failure here and in similar cases below. We already got a reply and if cleanup fails,
2075 * this doesn't change the validity of the result. */
2076 (void) tcsetattr(input_fd, TCSANOW, &old_termios);
30aeab78
LP
2077 return r;
2078}
2079
9ab703d8 2080int terminal_reset_defensive(int fd, TerminalResetFlags flags) {
cfac0908
LP
2081 int r = 0;
2082
2083 assert(fd >= 0);
5b3eaf9e 2084 assert(!FLAGS_SET(flags, TERMINAL_RESET_AVOID_ANSI_SEQ|TERMINAL_RESET_FORCE_ANSI_SEQ));
cfac0908 2085
5b3eaf9e
LP
2086 /* Resets the terminal comprehensively, i.e. via both ioctl()s and via ANSI sequences, but do so only
2087 * if $TERM is unset or set to "dumb" */
cfac0908 2088
e2216800
LP
2089 if (!isatty_safe(fd))
2090 return -ENOTTY;
2091
9ab703d8 2092 RET_GATHER(r, terminal_reset_ioctl(fd, FLAGS_SET(flags, TERMINAL_RESET_SWITCH_TO_TEXT)));
cfac0908 2093
5b3eaf9e
LP
2094 if (!FLAGS_SET(flags, TERMINAL_RESET_AVOID_ANSI_SEQ) &&
2095 (FLAGS_SET(flags, TERMINAL_RESET_FORCE_ANSI_SEQ) || !getenv_terminal_is_dumb()))
cfac0908
LP
2096 RET_GATHER(r, terminal_reset_ansi_seq(fd));
2097
2098 return r;
2099}
2100
9ab703d8 2101int terminal_reset_defensive_locked(int fd, TerminalResetFlags flags) {
2cd19499
LP
2102 assert(fd >= 0);
2103
2104 _cleanup_close_ int lock_fd = lock_dev_console();
2105 if (lock_fd < 0)
2106 log_debug_errno(lock_fd, "Failed to acquire lock for /dev/console, ignoring: %m");
2107
9ab703d8 2108 return terminal_reset_defensive(fd, flags);
2cd19499
LP
2109}
2110
d02d4f83
LP
2111void termios_disable_echo(struct termios *termios) {
2112 assert(termios);
2113
2114 termios->c_lflag &= ~(ICANON|ECHO);
2115 termios->c_cc[VMIN] = 1;
2116 termios->c_cc[VTIME] = 0;
2117}
63e9c383
LP
2118
2119typedef enum BackgroundColorState {
2120 BACKGROUND_TEXT,
2121 BACKGROUND_ESCAPE,
2122 BACKGROUND_BRACKET,
2123 BACKGROUND_FIRST_ONE,
2124 BACKGROUND_SECOND_ONE,
2125 BACKGROUND_SEMICOLON,
2126 BACKGROUND_R,
2127 BACKGROUND_G,
2128 BACKGROUND_B,
2129 BACKGROUND_RED,
2130 BACKGROUND_GREEN,
2131 BACKGROUND_BLUE,
73a72e3a 2132 BACKGROUND_STRING_TERMINATOR,
63e9c383
LP
2133} BackgroundColorState;
2134
2135typedef struct BackgroundColorContext {
2136 BackgroundColorState state;
2137 uint32_t red, green, blue;
2138 unsigned red_bits, green_bits, blue_bits;
2139} BackgroundColorContext;
2140
2141static int scan_background_color_response(
2142 BackgroundColorContext *context,
2143 const char *buf,
1df569b2
LP
2144 size_t size,
2145 size_t *ret_processed) {
63e9c383
LP
2146
2147 assert(context);
f4bdf373
ZJS
2148 assert(buf);
2149 assert(ret_processed);
63e9c383
LP
2150
2151 for (size_t i = 0; i < size; i++) {
2152 char c = buf[i];
2153
2154 switch (context->state) {
2155
2156 case BACKGROUND_TEXT:
2157 context->state = c == '\x1B' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT;
2158 break;
2159
2160 case BACKGROUND_ESCAPE:
2161 context->state = c == ']' ? BACKGROUND_BRACKET : BACKGROUND_TEXT;
2162 break;
2163
2164 case BACKGROUND_BRACKET:
2165 context->state = c == '1' ? BACKGROUND_FIRST_ONE : BACKGROUND_TEXT;
2166 break;
2167
2168 case BACKGROUND_FIRST_ONE:
2169 context->state = c == '1' ? BACKGROUND_SECOND_ONE : BACKGROUND_TEXT;
2170 break;
2171
2172 case BACKGROUND_SECOND_ONE:
2173 context->state = c == ';' ? BACKGROUND_SEMICOLON : BACKGROUND_TEXT;
2174 break;
2175
2176 case BACKGROUND_SEMICOLON:
2177 context->state = c == 'r' ? BACKGROUND_R : BACKGROUND_TEXT;
2178 break;
2179
2180 case BACKGROUND_R:
2181 context->state = c == 'g' ? BACKGROUND_G : BACKGROUND_TEXT;
2182 break;
2183
2184 case BACKGROUND_G:
2185 context->state = c == 'b' ? BACKGROUND_B : BACKGROUND_TEXT;
2186 break;
2187
2188 case BACKGROUND_B:
2189 context->state = c == ':' ? BACKGROUND_RED : BACKGROUND_TEXT;
2190 break;
2191
2192 case BACKGROUND_RED:
2193 if (c == '/')
2194 context->state = context->red_bits > 0 ? BACKGROUND_GREEN : BACKGROUND_TEXT;
2195 else {
2196 int d = unhexchar(c);
2197 if (d < 0 || context->red_bits >= sizeof(context->red)*8)
2198 context->state = BACKGROUND_TEXT;
2199 else {
2200 context->red = (context->red << 4) | d;
2201 context->red_bits += 4;
2202 }
2203 }
2204 break;
2205
2206 case BACKGROUND_GREEN:
2207 if (c == '/')
2208 context->state = context->green_bits > 0 ? BACKGROUND_BLUE : BACKGROUND_TEXT;
2209 else {
2210 int d = unhexchar(c);
2211 if (d < 0 || context->green_bits >= sizeof(context->green)*8)
2212 context->state = BACKGROUND_TEXT;
2213 else {
2214 context->green = (context->green << 4) | d;
2215 context->green_bits += 4;
2216 }
2217 }
2218 break;
2219
2220 case BACKGROUND_BLUE:
2221 if (c == '\x07') {
1df569b2 2222 if (context->blue_bits > 0) {
f4bdf373 2223 *ret_processed = i + 1;
63e9c383 2224 return 1; /* success! */
1df569b2 2225 }
63e9c383
LP
2226
2227 context->state = BACKGROUND_TEXT;
73a72e3a
SL
2228 } else if (c == '\x1b')
2229 context->state = context->blue_bits > 0 ? BACKGROUND_STRING_TERMINATOR : BACKGROUND_TEXT;
2230 else {
63e9c383
LP
2231 int d = unhexchar(c);
2232 if (d < 0 || context->blue_bits >= sizeof(context->blue)*8)
2233 context->state = BACKGROUND_TEXT;
2234 else {
2235 context->blue = (context->blue << 4) | d;
2236 context->blue_bits += 4;
2237 }
2238 }
2239 break;
73a72e3a
SL
2240
2241 case BACKGROUND_STRING_TERMINATOR:
1df569b2 2242 if (c == '\\') {
f4bdf373 2243 *ret_processed = i + 1;
73a72e3a 2244 return 1; /* success! */
1df569b2 2245 }
73a72e3a
SL
2246
2247 context->state = c == ']' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT;
2248 break;
2249
63e9c383
LP
2250 }
2251
2252 /* Reset any colors we might have picked up */
73a72e3a 2253 if (IN_SET(context->state, BACKGROUND_TEXT, BACKGROUND_ESCAPE)) {
63e9c383
LP
2254 /* reset color */
2255 context->red = context->green = context->blue = 0;
2256 context->red_bits = context->green_bits = context->blue_bits = 0;
2257 }
2258 }
2259
f4bdf373 2260 *ret_processed = size;
63e9c383
LP
2261 return 0; /* all good, but not enough data yet */
2262}
2263
2264int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue) {
2265 int r;
2266
2267 assert(ret_red);
2268 assert(ret_green);
2269 assert(ret_blue);
2270
2271 if (!colors_enabled())
2272 return -EOPNOTSUPP;
2273
445e5738
LP
2274 r = terminal_verify_same(STDIN_FILENO, STDOUT_FILENO);
2275 if (r < 0)
2276 return r;
63e9c383
LP
2277
2278 if (streq_ptr(getenv("TERM"), "linux")) {
2279 /* Linux console is black */
2280 *ret_red = *ret_green = *ret_blue = 0.0;
2281 return 0;
2282 }
2283
46f67429
ZJS
2284 /* Open a 2nd input fd, in non-blocking mode, so that we won't ever hang in read()
2285 * should someone else process the POLLIN. Do all subsequent operations on the new fd. */
2286 _cleanup_close_ int nonblock_input_fd = r = fd_reopen(STDIN_FILENO, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
2287 if (r < 0)
2288 return r;
2289
63e9c383 2290 struct termios old_termios;
46f67429 2291 if (tcgetattr(nonblock_input_fd, &old_termios) < 0)
63e9c383
LP
2292 return -errno;
2293
2294 struct termios new_termios = old_termios;
2295 termios_disable_echo(&new_termios);
2296
46f67429 2297 if (tcsetattr(nonblock_input_fd, TCSANOW, &new_termios) < 0)
63e9c383
LP
2298 return -errno;
2299
03674247 2300 r = loop_write(STDOUT_FILENO, ANSI_OSC "11;?" ANSI_ST, SIZE_MAX);
63e9c383
LP
2301 if (r < 0)
2302 goto finish;
2303
5321b957 2304 usec_t end = usec_add(now(CLOCK_MONOTONIC), CONSOLE_REPLY_WAIT_USEC);
03674247 2305 char buf[STRLEN(ANSI_OSC "11;rgb:0/0/0" ANSI_ST)]; /* shortest possible reply */
63e9c383
LP
2306 size_t buf_full = 0;
2307 BackgroundColorContext context = {};
2308
abe8e99e 2309 for (bool first = true;; first = false) {
1df569b2
LP
2310 if (buf_full == 0) {
2311 usec_t n = now(CLOCK_MONOTONIC);
1df569b2
LP
2312 if (n >= end) {
2313 r = -EOPNOTSUPP;
2314 goto finish;
2315 }
63e9c383 2316
a0c314d6 2317 r = fd_wait_for_event(nonblock_input_fd, POLLIN, usec_sub_unsigned(end, n));
1df569b2
LP
2318 if (r < 0)
2319 goto finish;
2320 if (r == 0) {
2321 r = -EOPNOTSUPP;
2322 goto finish;
2323 }
63e9c383 2324
abe8e99e
LP
2325 /* On the first try, read multiple characters, i.e. the shortest valid
2326 * reply. Afterwards read byte-wise, since we don't want to read too much, and
2327 * unnecessarily drop too many characters from the input queue. */
a0c314d6 2328 ssize_t l = read(nonblock_input_fd, buf, first ? sizeof(buf) : 1);
1df569b2 2329 if (l < 0) {
a0c314d6
LP
2330 if (errno == EAGAIN)
2331 continue;
1df569b2
LP
2332 r = -errno;
2333 goto finish;
2334 }
63e9c383 2335
1df569b2
LP
2336 assert((size_t) l <= sizeof(buf));
2337 buf_full = l;
2338 }
63e9c383 2339
1df569b2
LP
2340 size_t processed;
2341 r = scan_background_color_response(&context, buf, buf_full, &processed);
63e9c383
LP
2342 if (r < 0)
2343 goto finish;
1df569b2
LP
2344
2345 assert(processed <= buf_full);
2346 buf_full -= processed;
2347 memmove(buf, buf + processed, buf_full);
2348
63e9c383
LP
2349 if (r > 0) {
2350 assert(context.red_bits > 0);
2351 *ret_red = (double) context.red / ((UINT64_C(1) << context.red_bits) - 1);
2352 assert(context.green_bits > 0);
2353 *ret_green = (double) context.green / ((UINT64_C(1) << context.green_bits) - 1);
2354 assert(context.blue_bits > 0);
2355 *ret_blue = (double) context.blue / ((UINT64_C(1) << context.blue_bits) - 1);
2356 r = 0;
2357 goto finish;
2358 }
2359 }
2360
2361finish:
e698ee57 2362 (void) tcsetattr(nonblock_input_fd, TCSANOW, &old_termios);
63e9c383
LP
2363 return r;
2364}
3390be38 2365
3390be38
LP
2366int terminal_get_size_by_dsr(
2367 int input_fd,
2368 int output_fd,
2369 unsigned *ret_rows,
2370 unsigned *ret_columns) {
2371
13cb6641 2372 int r;
a0c314d6 2373
3390be38
LP
2374 assert(input_fd >= 0);
2375 assert(output_fd >= 0);
2376
3390be38
LP
2377 /* Tries to determine the terminal dimension by means of ANSI sequences rather than TIOCGWINSZ
2378 * ioctl(). Why bother with this? The ioctl() information is often incorrect on serial terminals
2379 * (since there's no handshake or protocol to determine the right dimensions in RS232), but since the
2380 * ANSI sequences are interpreted by the final terminal instead of an intermediary tty driver they
2381 * should be more accurate.
2382 *
2383 * Unfortunately there's no direct ANSI sequence to query terminal dimensions. But we can hack around
2384 * it: we position the cursor briefly at an absolute location very far down and very far to the
2385 * right, and then read back where we actually ended up. Because cursor locations are capped at the
2386 * terminal width/height we should then see the right values. In order to not risk integer overflows
2387 * in terminal applications we'll use INT16_MAX-1 as location to jump to — hopefully a value that is
2388 * large enough for any real-life terminals, but small enough to not overflow anything or be
2389 * recognized as a "niche" value. (Note that the dimension fields in "struct winsize" are 16bit only,
2390 * too). */
2391
2392 if (terminal_is_dumb())
2393 return -EOPNOTSUPP;
2394
2395 r = terminal_verify_same(input_fd, output_fd);
2396 if (r < 0)
2397 return log_debug_errno(r, "Called with distinct input/output fds: %m");
2398
933c6900
ZJS
2399 /* Open a 2nd input fd, in non-blocking mode, so that we won't ever hang in read()
2400 * should someone else process the POLLIN. Do all subsequent operations on the new fd. */
2401 _cleanup_close_ int nonblock_input_fd = r = fd_reopen(input_fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
2402 if (r < 0)
2403 return r;
2404
3390be38 2405 struct termios old_termios;
933c6900 2406 if (tcgetattr(nonblock_input_fd, &old_termios) < 0)
a4eb5094 2407 return log_debug_errno(errno, "Failed to get terminal settings: %m");
3390be38
LP
2408
2409 struct termios new_termios = old_termios;
2410 termios_disable_echo(&new_termios);
2411
933c6900 2412 if (tcsetattr(nonblock_input_fd, TCSANOW, &new_termios) < 0)
a4eb5094 2413 return log_debug_errno(errno, "Failed to set new terminal settings: %m");
3390be38
LP
2414
2415 unsigned saved_row = 0, saved_column = 0;
2416
2417 r = loop_write(output_fd,
2418 "\x1B[6n" /* Request cursor position (DSR/CPR) */
2419 "\x1B[32766;32766H" /* Position cursor really far to the right and to the bottom, but let's stay within the 16bit signed range */
2420 "\x1B[6n", /* Request cursor position again */
2421 SIZE_MAX);
2422 if (r < 0)
2423 goto finish;
2424
5321b957 2425 usec_t end = usec_add(now(CLOCK_MONOTONIC), CONSOLE_REPLY_WAIT_USEC);
3390be38
LP
2426 char buf[STRLEN("\x1B[1;1R")]; /* The shortest valid reply possible */
2427 size_t buf_full = 0;
2428 CursorPositionContext context = {};
2429
2430 for (bool first = true;; first = false) {
2431 if (buf_full == 0) {
2432 usec_t n = now(CLOCK_MONOTONIC);
3390be38
LP
2433 if (n >= end) {
2434 r = -EOPNOTSUPP;
2435 goto finish;
2436 }
2437
a0c314d6 2438 r = fd_wait_for_event(nonblock_input_fd, POLLIN, usec_sub_unsigned(end, n));
3390be38
LP
2439 if (r < 0)
2440 goto finish;
2441 if (r == 0) {
2442 r = -EOPNOTSUPP;
2443 goto finish;
2444 }
2445
2446 /* On the first try, read multiple characters, i.e. the shortest valid
2447 * reply. Afterwards read byte-wise, since we don't want to read too much, and
2448 * unnecessarily drop too many characters from the input queue. */
a0c314d6 2449 ssize_t l = read(nonblock_input_fd, buf, first ? sizeof(buf) : 1);
3390be38 2450 if (l < 0) {
a0c314d6
LP
2451 if (errno == EAGAIN)
2452 continue;
2453
3390be38
LP
2454 r = -errno;
2455 goto finish;
2456 }
2457
2458 assert((size_t) l <= sizeof(buf));
2459 buf_full = l;
2460 }
2461
2462 size_t processed;
2463 r = scan_cursor_position_response(&context, buf, buf_full, &processed);
2464 if (r < 0)
2465 goto finish;
2466
2467 assert(processed <= buf_full);
2468 buf_full -= processed;
2469 memmove(buf, buf + processed, buf_full);
2470
2471 if (r > 0) {
2472 if (saved_row == 0) {
2473 assert(saved_column == 0);
2474
2475 /* First sequence, this is the cursor position before we set it somewhere
2476 * into the void at the bottom right. Let's save where we are so that we can
2477 * return later. */
2478
2479 /* Superficial validity checks */
2480 if (context.row <= 0 || context.column <= 0 || context.row >= 32766 || context.column >= 32766) {
2481 r = -ENODATA;
2482 goto finish;
2483 }
2484
2485 saved_row = context.row;
2486 saved_column = context.column;
2487
2488 /* Reset state */
2489 context = (CursorPositionContext) {};
2490 } else {
2491 /* Second sequence, this is the cursor position after we set it somewhere
2492 * into the void at the bottom right. */
2493
2494 /* Superficial validity checks (no particular reason to check for < 4, it's
2495 * just a way to look for unreasonably small values) */
2496 if (context.row < 4 || context.column < 4 || context.row >= 32766 || context.column >= 32766) {
2497 r = -ENODATA;
2498 goto finish;
2499 }
2500
2501 if (ret_rows)
2502 *ret_rows = context.row;
2503 if (ret_columns)
2504 *ret_columns = context.column;
2505
2506 r = 0;
2507 goto finish;
2508 }
2509 }
2510 }
2511
2512finish:
2513 /* Restore cursor position */
2514 if (saved_row > 0 && saved_column > 0)
e698ee57
ZJS
2515 (void) terminal_set_cursor_position(output_fd, saved_row, saved_column);
2516 (void) tcsetattr(nonblock_input_fd, TCSANOW, &old_termios);
3390be38 2517
3390be38
LP
2518 return r;
2519}
63c631d7
LP
2520
2521int terminal_fix_size(int input_fd, int output_fd) {
2522 unsigned rows, columns;
2523 int r;
2524
2525 /* Tries to update the current terminal dimensions to the ones reported via ANSI sequences */
2526
2527 r = terminal_verify_same(input_fd, output_fd);
2528 if (r < 0)
2529 return r;
2530
2531 struct winsize ws = {};
2532 if (ioctl(output_fd, TIOCGWINSZ, &ws) < 0)
2533 return log_debug_errno(errno, "Failed to query terminal dimensions, ignoring: %m");
2534
2535 r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &columns);
2536 if (r < 0)
2537 return log_debug_errno(r, "Failed to acquire terminal dimensions via ANSI sequences, not adjusting terminal dimensions: %m");
2538
2539 if (ws.ws_row == rows && ws.ws_col == columns) {
2540 log_debug("Terminal dimensions reported via ANSI sequences match currently set terminal dimensions, not changing.");
2541 return 0;
2542 }
2543
2544 ws.ws_col = columns;
2545 ws.ws_row = rows;
2546
2547 if (ioctl(output_fd, TIOCSWINSZ, &ws) < 0)
2548 return log_debug_errno(errno, "Failed to update terminal dimensions, ignoring: %m");
2549
2550 log_debug("Fixed terminal dimensions to %ux%u based on ANSI sequence information.", columns, rows);
2551 return 1;
2552}
ce3a1593 2553
5321b957
ZJS
2554#define MAX_TERMINFO_LENGTH 64
2555/* python -c 'print("".join(hex(ord(i))[2:] for i in "name").upper())' */
2556#define DCS_TERMINFO_Q ANSI_DCS "+q" "6E616D65" ANSI_ST
2557/* The answer is either 0+r… (invalid) or 1+r… (OK). */
2558#define DCS_TERMINFO_R0 ANSI_DCS "0+r" ANSI_ST
2559#define DCS_TERMINFO_R1 ANSI_DCS "1+r" "6E616D65" "=" /* This is followed by Pt ST. */
2560assert_cc(STRLEN(DCS_TERMINFO_R0) <= STRLEN(DCS_TERMINFO_R1 ANSI_ST));
2561
2562static int scan_terminfo_response(
2563 const char *buf,
2564 size_t size,
2565 char **ret_name) {
2566 int r;
2567
2568 assert(buf);
2569 assert(ret_name);
2570
2571 /* Check if we have enough space for the shortest possible answer. */
2572 if (size < STRLEN(DCS_TERMINFO_R0))
2573 return -EAGAIN;
2574
2575 /* Check if the terminating sequence is present */
2576 if (memcmp(buf + size - STRLEN(ANSI_ST), ANSI_ST, STRLEN(ANSI_ST)) != 0)
2577 return -EAGAIN;
2578
2579 if (size <= STRLEN(DCS_TERMINFO_R1 ANSI_ST))
2580 return -EINVAL; /* The answer is invalid or empty */
2581
2582 if (memcmp(buf, DCS_TERMINFO_R1, STRLEN(DCS_TERMINFO_R1)) != 0)
2583 return -EINVAL; /* The answer is not valid */
2584
2585 _cleanup_free_ void *dec = NULL;
2586 size_t dec_size;
2587 r = unhexmem_full(buf + STRLEN(DCS_TERMINFO_R1), size - STRLEN(DCS_TERMINFO_R1 ANSI_ST),
2588 /* secure= */ false,
2589 &dec, &dec_size);
2590 if (r < 0)
2591 return r;
2592
2593 assert(((const char *) dec)[dec_size] == '\0'); /* unhexmem appends NUL for our convenience */
2594 if (memchr(dec, '\0', dec_size) || string_has_cc(dec, NULL) || !filename_is_valid(dec))
2595 return -EUCLEAN;
2596
2597 *ret_name = TAKE_PTR(dec);
2598 return 0;
2599}
2600
2601int terminal_get_terminfo_by_dcs(int fd, char **ret_name) {
2602 int r;
2603
2604 assert(fd >= 0);
2605 assert(ret_name);
2606
2607 /* Note: fd must be in non-blocking read-write mode! */
2608
2609 struct termios old_termios;
2610 if (tcgetattr(fd, &old_termios) < 0)
2611 return -errno;
2612
2613 struct termios new_termios = old_termios;
2614 termios_disable_echo(&new_termios);
2615
f789b17e 2616 if (tcsetattr(fd, TCSANOW, &new_termios) < 0)
5321b957
ZJS
2617 return -errno;
2618
2619 r = loop_write(fd, DCS_TERMINFO_Q, SIZE_MAX);
2620 if (r < 0)
2621 goto finish;
2622
2623 usec_t end = usec_add(now(CLOCK_MONOTONIC), CONSOLE_REPLY_WAIT_USEC);
2624 char buf[STRLEN(DCS_TERMINFO_R1) + MAX_TERMINFO_LENGTH + STRLEN(ANSI_ST)];
2625 size_t bytes = 0;
2626
2627 for (;;) {
2628 usec_t n = now(CLOCK_MONOTONIC);
2629 if (n >= end) {
2630 r = -EOPNOTSUPP;
2631 break;
2632 }
2633
2634 r = fd_wait_for_event(fd, POLLIN, usec_sub_unsigned(end, n));
2635 if (r < 0)
2636 break;
2637 if (r == 0) {
2638 r = -EOPNOTSUPP;
2639 break;
2640 }
2641
2642 /* On the first read, read multiple characters, i.e. the shortest valid reply. Afterwards
2643 * read byte by byte, since we don't want to read too much and drop characters from the input
2644 * queue. */
2645 ssize_t l = read(fd, buf + bytes, bytes == 0 ? STRLEN(DCS_TERMINFO_R0) : 1);
2646 if (l < 0) {
2647 if (errno == EAGAIN)
2648 continue;
2649 r = -errno;
2650 break;
2651 }
2652
2653 assert((size_t) l <= sizeof(buf) - bytes);
2654 bytes += l;
2655
2656 r = scan_terminfo_response(buf, bytes, ret_name);
2657 if (r != -EAGAIN)
2658 break;
2659
2660 if (bytes == sizeof(buf)) {
2661 r = -EOPNOTSUPP; /* The response has the right prefix, but we didn't find a valid
3ec92f2f 2662 * answer with a terminator in the allotted space. Something is
5321b957
ZJS
2663 * wrong, possibly some unrelated bytes got injected into the
2664 * answer. */
2665 break;
2666 }
2667 }
2668
2669finish:
f789b17e 2670 (void) tcsetattr(fd, TCSANOW, &old_termios);
5321b957
ZJS
2671 return r;
2672}
2673
e3b050a5
ZJS
2674int have_terminfo_file(const char *name) {
2675 /* This is a heuristic check if we have the file, using the directory layout used on
2676 * current Linux systems. Checks for other layouts can be added later if appropriate. */
2677 int r;
2678
2679 assert(filename_is_valid(name));
2680
2681 _cleanup_free_ char *p = path_join("/usr/share/terminfo", CHAR_TO_STR(name[0]), name);
2682 if (!p)
2683 return log_oom_debug();
2684
2685 r = RET_NERRNO(access(p, F_OK));
2686 if (r == -ENOENT)
2687 return false;
2688 if (r < 0)
2689 return r;
2690 return true;
2691}
2692
2693int query_term_for_tty(const char *tty, char **ret_term) {
2694 _cleanup_free_ char *dcs_term = NULL;
2695 int r;
2696
2697 assert(tty);
2698 assert(ret_term);
2699
2700 if (tty_is_vc_resolve(tty))
2701 return strdup_to(ret_term, "linux");
2702
2703 /* Try to query the terminal implementation that we're on. This will not work in all
2704 * cases, which is fine, since this is intended to be used as a fallback. */
2705
2706 _cleanup_close_ int tty_fd = open_terminal(tty, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
2707 if (tty_fd < 0)
2708 return log_debug_errno(tty_fd, "Failed to open %s to query terminfo: %m", tty);
2709
2710 r = terminal_get_terminfo_by_dcs(tty_fd, &dcs_term);
2711 if (r < 0)
2712 return log_debug_errno(r, "Failed to query %s for terminfo: %m", tty);
2713
2714 r = have_terminfo_file(dcs_term);
2715 if (r < 0)
2716 return log_debug_errno(r, "Failed to look for terminfo %s: %m", dcs_term);
2717 if (r == 0)
2718 return log_info_errno(SYNTHETIC_ERRNO(ENODATA),
2719 "Terminfo %s not found for %s.", dcs_term, tty);
2720
2721 *ret_term = TAKE_PTR(dcs_term);
2722 return 0;
2723}
2724
ce3a1593
LP
2725int terminal_is_pty_fd(int fd) {
2726 int r;
2727
2728 assert(fd >= 0);
2729
2730 /* Returns true if we are looking at a pty, i.e. if it's backed by the /dev/pts/ file system */
2731
2732 if (!isatty_safe(fd))
2733 return false;
2734
2735 r = is_fs_type_at(fd, NULL, DEVPTS_SUPER_MAGIC);
2736 if (r != 0)
2737 return r;
2738
2739 /* The ptmx device is weird, it exists twice, once inside and once outside devpts. To detect the
2740 * latter case, let's fire off an ioctl() that only works on ptmx devices. */
2741
2742 int v;
2743 if (ioctl(fd, TIOCGPKT, &v) < 0) {
2744 if (ERRNO_IS_NOT_SUPPORTED(errno))
2745 return false;
2746
2747 return -errno;
2748 }
2749
2750 return true;
2751}
fc9dc71a 2752
1d522f1a 2753int pty_open_peer(int fd, int mode) {
fc9dc71a
LP
2754 assert(fd >= 0);
2755
2756 /* Opens the peer PTY using the new race-free TIOCGPTPEER ioctl() (kernel 4.13).
2757 *
2758 * This is safe to be called on TTYs from other namespaces. */
2759
2760 assert((mode & (O_CREAT|O_PATH|O_DIRECTORY|O_TMPFILE)) == 0);
2761
2762 /* This replicates the EIO retry logic of open_terminal() in a modified way. */
2763 for (unsigned c = 0;; c++) {
2764 int peer_fd = ioctl(fd, TIOCGPTPEER, mode);
2765 if (peer_fd >= 0)
2766 return peer_fd;
2767
fc9dc71a
LP
2768 if (errno != EIO)
2769 return -errno;
2770
2771 /* Max 1s in total */
2772 if (c >= 20)
2773 return -EIO;
2774
2775 (void) usleep_safe(50 * USEC_PER_MSEC);
2776 }
2777}