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