diff options
author | Lennart Poettering <lennart@poettering.net> | 2011-05-18 01:07:31 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2011-05-18 01:07:36 +0200 |
commit | 6ea832a20700f5282c08c70f38422c6ab290a0b5 (patch) | |
tree | bcd9a7705c87f3828adf70938ec59cdcfe368e72 /src/util.c | |
parent | 9131f660eedb29d18a29e6efff49c485e683c56c (diff) |
exec: hangup/reset/deallocate VTs in gettys
Explicitly disconnect all clients from a VT when a getty starts/finishes
(requires TIOCVHANGUP, available in 2.6.29).
Explicitly deallocate getty VTs in order to flush scrollback buffer.
Explicitly reset terminals to a defined state before spawning getty.
Diffstat (limited to 'src/util.c')
-rw-r--r-- | src/util.c | 138 |
1 files changed, 134 insertions, 4 deletions
diff --git a/src/util.c b/src/util.c index f0051ee285..14aa1f9728 100644 --- a/src/util.c +++ b/src/util.c @@ -2261,7 +2261,7 @@ int ask(char *ret, const char *replies, const char *text, ...) { } } -int reset_terminal(int fd) { +int reset_terminal_fd(int fd) { struct termios termios; int r = 0; long arg; @@ -2323,6 +2323,19 @@ finish: return r; } +int reset_terminal(const char *name) { + int fd, r; + + fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return fd; + + r = reset_terminal_fd(fd); + close_nointr_nofail(fd); + + return r; +} + int open_terminal(const char *name, int mode) { int fd, r; unsigned c = 0; @@ -2443,8 +2456,8 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst /* We pass here O_NOCTTY only so that we can check the return * value TIOCSCTTY and have a reliable way to figure out if we * successfully became the controlling process of the tty */ - if ((fd = open_terminal(name, O_RDWR|O_NOCTTY)) < 0) - return -errno; + if ((fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0) + return fd; /* First, try to get the tty */ r = ioctl(fd, TIOCSCTTY, force); @@ -2511,7 +2524,7 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst if (notify >= 0) close_nointr_nofail(notify); - if ((r = reset_terminal(fd)) < 0) + if ((r = reset_terminal_fd(fd)) < 0) log_warning("Failed to reset terminal: %s", strerror(-r)); return fd; @@ -4413,6 +4426,123 @@ char* hostname_cleanup(char *s) { return s; } +int terminal_vhangup_fd(int fd) { + if (ioctl(fd, TIOCVHANGUP) < 0) + return -errno; + + return 0; +} + +int terminal_vhangup(const char *name) { + int fd, r; + + fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return fd; + + r = terminal_vhangup_fd(fd); + close_nointr_nofail(fd); + + return r; +} + +int vt_disallocate(const char *name) { + int fd, r; + unsigned u; + int temporary_vt, temporary_fd; + char tpath[64]; + struct vt_stat vt_stat; + + /* Deallocate the VT if possible. If not possible + * (i.e. because it is the active one), at least clear it + * entirely (including the scrollback buffer) */ + + if (!tty_is_vc(name)) + return -EIO; + + if (!startswith(name, "/dev/tty")) + return -EINVAL; + + r = safe_atou(name+8, &u); + if (r < 0) + return r; + + if (u <= 0) + return -EIO; + + fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return fd; + + r = ioctl(fd, VT_DISALLOCATE, u); + if (r >= 0) { + close_nointr_nofail(fd); + return 0; + } + + if (errno != EBUSY) { + close_nointr_nofail(fd); + return -errno; + } + + if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0) { + close_nointr_nofail(fd); + return -errno; + } + + if (u != vt_stat.v_active) { + close_nointr_nofail(fd); + return -EBUSY; + } + + if (ioctl(fd, VT_OPENQRY, &temporary_vt) < 0) { + close_nointr_nofail(fd); + return -errno; + } + + if (temporary_vt <= 0) { + close_nointr_nofail(fd); + return -EIO; + } + + /* Switch to temporary VT */ + snprintf(tpath, sizeof(tpath), "/dev/tty%i", temporary_vt); + char_array_0(tpath); + temporary_fd = open_terminal(tpath, O_RDWR|O_NOCTTY|O_CLOEXEC); + ioctl(fd, VT_ACTIVATE, temporary_vt); + if (temporary_fd >= 0) + close_nointr_nofail(temporary_fd); + + /* Reopen /dev/tty0 */ + close_nointr_nofail(fd); + fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + r = -errno; + else { + /* Disallocate the real VT */ + if (ioctl(fd, VT_DISALLOCATE, u) < 0) + r = -errno; + else + r = 0; + } + + /* Recreate original VT */ + temporary_fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); + + if (temporary_fd >= 0) { + loop_write(temporary_fd, "\033[H\033[2J", 7, false); /* clear screen explicitly */ + close_nointr_nofail(temporary_fd); + } + + /* Switch back to original VT */ + if (fd >= 0) { + ioctl(fd, VT_ACTIVATE, vt_stat.v_active); + close_nointr_nofail(fd); + } + + return r; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", |