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