summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2011-05-18 01:07:31 +0200
committerLennart Poettering <lennart@poettering.net>2011-05-18 01:07:36 +0200
commit6ea832a20700f5282c08c70f38422c6ab290a0b5 (patch)
treebcd9a7705c87f3828adf70938ec59cdcfe368e72 /src
parent9131f660eedb29d18a29e6efff49c485e683c56c (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')
-rw-r--r--src/dbus-execute.h3
-rw-r--r--src/execute.c35
-rw-r--r--src/execute.h7
-rw-r--r--src/load-fragment.c49
-rw-r--r--src/main.c2
-rw-r--r--src/missing.h4
-rw-r--r--src/mount.c2
-rw-r--r--src/service.c4
-rw-r--r--src/socket.c2
-rw-r--r--src/swap.c2
-rw-r--r--src/util.c138
-rw-r--r--src/util.h9
12 files changed, 236 insertions, 21 deletions
diff --git a/src/dbus-execute.h b/src/dbus-execute.h
index ed66390d0b..bf3160b07a 100644
--- a/src/dbus-execute.h
+++ b/src/dbus-execute.h
@@ -128,6 +128,9 @@
{ interface, "StandardOutput", bus_execute_append_output, "s", &(context).std_output }, \
{ interface, "StandardError", bus_execute_append_output, "s", &(context).std_error }, \
{ interface, "TTYPath", bus_property_append_string, "s", (context).tty_path }, \
+ { interface, "TTYReset", bus_property_append_bool, "b", &(context).tty_reset }, \
+ { interface, "TTYVHangup", bus_property_append_bool, "b", &(context).tty_vhangup }, \
+ { interface, "TTYVTDisallocate", bus_property_append_bool, "b", &(context).tty_vt_disallocate }, \
{ interface, "SyslogPriority", bus_property_append_int, "i", &(context).syslog_priority }, \
{ interface, "SyslogIdentifier", bus_property_append_string, "s", (context).syslog_identifier }, \
{ interface, "SyslogLevelPrefix", bus_property_append_bool, "b", &(context).syslog_level_prefix }, \
diff --git a/src/execute.c b/src/execute.c
index 745dcfcdb8..a62f9dbbc6 100644
--- a/src/execute.c
+++ b/src/execute.c
@@ -140,6 +140,19 @@ static const char *tty_path(const ExecContext *context) {
return "/dev/console";
}
+void exec_context_tty_reset(const ExecContext *context) {
+ assert(context);
+
+ if (context->tty_vhangup)
+ terminal_vhangup(tty_path(context));
+
+ if (context->tty_reset)
+ reset_terminal(tty_path(context));
+
+ if (context->tty_vt_disallocate && context->tty_path)
+ vt_disallocate(context->tty_path);
+}
+
static int open_null_as(int flags, int nfd) {
int fd, r;
@@ -1089,6 +1102,8 @@ int exec_spawn(ExecCommand *command,
}
}
+ exec_context_tty_reset(context);
+
/* We skip the confirmation step if we shall not apply the TTY */
if (confirm_spawn &&
(!is_terminal_input(context->std_input) || apply_tty_stdin)) {
@@ -1700,8 +1715,14 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
if (c->tty_path)
fprintf(f,
- "%sTTYPath: %s\n",
- prefix, c->tty_path);
+ "%sTTYPath: %s\n"
+ "%sTTYReset: %s\n"
+ "%sTTYVHangup: %s\n"
+ "%sTTYVTDisallocate: %s\n",
+ prefix, c->tty_path,
+ prefix, yes_no(c->tty_reset),
+ prefix, yes_no(c->tty_vhangup),
+ prefix, yes_no(c->tty_vt_disallocate));
if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG ||
c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
@@ -1802,7 +1823,7 @@ void exec_status_start(ExecStatus *s, pid_t pid) {
dual_timestamp_get(&s->start_timestamp);
}
-void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status, const char *utmp_id) {
+void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status) {
assert(s);
if ((s->pid && s->pid != pid) ||
@@ -1815,8 +1836,12 @@ void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status, const char
s->code = code;
s->status = status;
- if (utmp_id)
- utmp_put_dead_process(utmp_id, pid, code, status);
+ if (context) {
+ if (context->utmp_id)
+ utmp_put_dead_process(context->utmp_id, pid, code, status);
+
+ exec_context_tty_reset(context);
+ }
}
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) {
diff --git a/src/execute.h b/src/execute.h
index 208fe4ad5e..4ed79f0d8b 100644
--- a/src/execute.h
+++ b/src/execute.h
@@ -123,6 +123,10 @@ struct ExecContext {
char *tty_path;
+ bool tty_reset;
+ bool tty_vhangup;
+ bool tty_vt_disallocate;
+
/* Since resolving these names might might involve socket
* connections and we don't want to deadlock ourselves these
* names are resolved on execution only and in the child
@@ -198,11 +202,12 @@ int exec_command_set(ExecCommand *c, const char *path, ...);
void exec_context_init(ExecContext *c);
void exec_context_done(ExecContext *c);
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
+void exec_context_tty_reset(const ExecContext *context);
int exec_context_load_environment(const ExecContext *c, char ***l);
void exec_status_start(ExecStatus *s, pid_t pid);
-void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status, const char *utmp_id);
+void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status);
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix);
const char* exec_output_to_string(ExecOutput i);
diff --git a/src/load-fragment.c b/src/load-fragment.c
index 7c39d238f6..321214ef46 100644
--- a/src/load-fragment.c
+++ b/src/load-fragment.c
@@ -188,6 +188,43 @@ static int config_parse_string_printf(
return 0;
}
+static int config_parse_path_printf(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = userdata;
+ char **s = data;
+ char *k;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(s);
+ assert(u);
+
+ if (!(k = unit_full_printf(u, rvalue)))
+ return -ENOMEM;
+
+ if (!path_is_absolute(k)) {
+ log_error("[%s:%u] Not an absolute path: %s", filename, line, k);
+ free(k);
+ return -EINVAL;
+ }
+
+ path_kill_slashes(k);
+
+ free(*s);
+ *s = k;
+
+ return 0;
+}
+
static int config_parse_listen(
const char *filename,
unsigned line,
@@ -1719,6 +1756,7 @@ static void dump_items(FILE *f, const ConfigItem *items) {
{ config_parse_bool, "BOOLEAN" },
{ config_parse_string, "STRING" },
{ config_parse_path, "PATH" },
+ { config_parse_path_printf, "PATH" },
{ config_parse_strv, "STRING [...]" },
{ config_parse_nice, "NICE" },
{ config_parse_oom_score_adjust, "OOMSCOREADJUST" },
@@ -1812,8 +1850,8 @@ static int load_from_path(Unit *u, const char *path) {
};
#define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \
- { "WorkingDirectory", config_parse_path, 0, &(context).working_directory, section }, \
- { "RootDirectory", config_parse_path, 0, &(context).root_directory, section }, \
+ { "WorkingDirectory", config_parse_path_printf, 0, &(context).working_directory, section }, \
+ { "RootDirectory", config_parse_path_printf, 0, &(context).root_directory, section }, \
{ "User", config_parse_string_printf, 0, &(context).user, section }, \
{ "Group", config_parse_string_printf, 0, &(context).group, section }, \
{ "SupplementaryGroups", config_parse_strv, 0, &(context).supplementary_groups, section }, \
@@ -1831,7 +1869,10 @@ static int load_from_path(Unit *u, const char *path) {
{ "StandardInput", config_parse_input, 0, &(context).std_input, section }, \
{ "StandardOutput", config_parse_output, 0, &(context).std_output, section }, \
{ "StandardError", config_parse_output, 0, &(context).std_error, section }, \
- { "TTYPath", config_parse_path, 0, &(context).tty_path, section }, \
+ { "TTYPath", config_parse_path_printf, 0, &(context).tty_path, section }, \
+ { "TTYReset", config_parse_bool, 0, &(context).tty_reset, section }, \
+ { "TTYVHangup", config_parse_bool, 0, &(context).tty_vhangup, section }, \
+ { "TTYVTDisallocate", config_parse_bool, 0, &(context).tty_vt_disallocate, section }, \
{ "SyslogIdentifier", config_parse_string_printf, 0, &(context).syslog_identifier, section }, \
{ "SyslogFacility", config_parse_facility, 0, &(context).syslog_priority, section }, \
{ "SyslogLevel", config_parse_level, 0, &(context).syslog_priority, section }, \
@@ -1899,7 +1940,7 @@ static int load_from_path(Unit *u, const char *path) {
{ "ConditionSecurity", config_parse_condition_string, CONDITION_SECURITY, u, "Unit" },
{ "ConditionNull", config_parse_condition_null, 0, u, "Unit" },
- { "PIDFile", config_parse_path, 0, &u->service.pid_file, "Service" },
+ { "PIDFile", config_parse_path_printf, 0, &u->service.pid_file, "Service" },
{ "ExecStartPre", config_parse_exec, 0, u->service.exec_command+SERVICE_EXEC_START_PRE, "Service" },
{ "ExecStart", config_parse_exec, 0, u->service.exec_command+SERVICE_EXEC_START, "Service" },
{ "ExecStartPost", config_parse_exec, 0, u->service.exec_command+SERVICE_EXEC_START_POST, "Service" },
diff --git a/src/main.c b/src/main.c
index b43d8eca91..52b3031fbe 100644
--- a/src/main.c
+++ b/src/main.c
@@ -203,7 +203,7 @@ static int console_setup(bool do_reset) {
return -tty_fd;
}
- if ((r = reset_terminal(tty_fd)) < 0)
+ if ((r = reset_terminal_fd(tty_fd)) < 0)
log_error("Failed to reset /dev/console: %s", strerror(-r));
close_nointr_nofail(tty_fd);
diff --git a/src/missing.h b/src/missing.h
index f1dbb398e0..02b31b7434 100644
--- a/src/missing.h
+++ b/src/missing.h
@@ -76,6 +76,10 @@
#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */
#endif
+#ifndef TIOCVHANGUP
+#define TIOCVHANGUP 0x5437
+#endif
+
static inline int pivot_root(const char *new_root, const char *put_old) {
return syscall(SYS_pivot_root, new_root, put_old);
}
diff --git a/src/mount.c b/src/mount.c
index 4b300364af..423cf434a4 100644
--- a/src/mount.c
+++ b/src/mount.c
@@ -1212,7 +1212,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->failure = m->failure || !success;
if (m->control_command) {
- exec_status_exit(&m->control_command->exec_status, pid, code, status, m->exec_context.utmp_id);
+ exec_status_exit(&m->control_command->exec_status, &m->exec_context, pid, code, status);
m->control_command = NULL;
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
}
diff --git a/src/service.c b/src/service.c
index f826754152..d59c4cbad8 100644
--- a/src/service.c
+++ b/src/service.c
@@ -2571,7 +2571,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
if (s->main_pid == pid) {
s->main_pid = 0;
- exec_status_exit(&s->main_exec_status, pid, code, status, s->exec_context.utmp_id);
+ exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status);
/* If this is not a forking service than the main
* process got started and hence we copy the exit
@@ -2650,7 +2650,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
if (s->control_command) {
- exec_status_exit(&s->control_command->exec_status, pid, code, status, s->exec_context.utmp_id);
+ exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
if (s->control_command->ignore)
success = true;
diff --git a/src/socket.c b/src/socket.c
index 2b9362db77..6c935c4201 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -1808,7 +1808,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
success = is_clean_exit(code, status);
if (s->control_command) {
- exec_status_exit(&s->control_command->exec_status, pid, code, status, s->exec_context.utmp_id);
+ exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
if (s->control_command->ignore)
success = true;
diff --git a/src/swap.c b/src/swap.c
index ef001a98bf..04df5854ad 100644
--- a/src/swap.c
+++ b/src/swap.c
@@ -940,7 +940,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->failure = s->failure || !success;
if (s->control_command) {
- exec_status_exit(&s->control_command->exec_status, pid, code, status, s->exec_context.utmp_id);
+ exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
s->control_command = NULL;
s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
}
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",
diff --git a/src/util.h b/src/util.h
index 79e98be20b..04049f7d92 100644
--- a/src/util.h
+++ b/src/util.h
@@ -315,7 +315,9 @@ int chvt(int vt);
int read_one_char(FILE *f, char *ret, bool *need_nl);
int ask(char *ret, const char *replies, const char *text, ...);
-int reset_terminal(int fd);
+int reset_terminal_fd(int fd);
+int reset_terminal(const char *name);
+
int open_terminal(const char *name, int mode);
int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm);
int release_terminal(void);
@@ -411,6 +413,11 @@ char* hostname_cleanup(char *s);
char* strshorten(char *s, size_t l);
+int terminal_vhangup_fd(int fd);
+int terminal_vhangup(const char *name);
+
+int vt_disallocate(const char *name);
+
#define NULSTR_FOREACH(i, l) \
for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)