* Make it possible to set the keymap independently from the font on
the kernel cmdline. Right now setting one resets also the other.
-* add dbus call to convert snapshot ino target
+* add dbus call to convert snapshot into target
* move nss-myhostname into systemd
* add dbus call to convert snapshot into target
-* make use of TIOCVHANGUP to revoke access to tty before we spawn a getty on it
-
-* release VT before we spawn a getty on it to entirely clear scrollback buffer
- https://bugzilla.redhat.com/show_bug.cgi?id=701704
-
* move /selinux to /sys/fs/selinux
* unset cgroup agents on shutdown
* add inode stat() check to readahead to suppress preloading changed files
-* allow list of pathes in config_parse_condition_path()
+* allow list of paths in config_parse_condition_path()
* introduce dbus calls for enabling/disabling a service
* support notifications for services being enabled/disabled
-* Maybe merge nss-myhostname into systemd?
-
* GC unreferenced jobs (such as .device jobs)
* support wildcard expansion in ListenStream= and friends
* write blog stories about:
- enabling dbus services
- status update
- - you are a distro: why switch?
- /etc/sysconfig and /etc/default
- how to write socket activated services
TTY (see above). Defaults to
<filename>/dev/console</filename>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>TTYReset=</varname></term>
+ <listitem><para>Reset the terminal
+ device specified with
+ <varname>TTYPath=</varname> before and
+ after execution. Defaults to
+ <literal>no</literal>.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>TTYVHangup=</varname></term>
+ <listitem><para>Disconnect all clients
+ which have opened the terminal device
+ specified with
+ <varname>TTYPath=</varname>
+ before and after execution. Defaults
+ to
+ <literal>no</literal>.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>TTYVTDisallocate=</varname></term>
+ <listitem><para>If the the terminal
+ device specified with
+ <varname>TTYPath=</varname> is a
+ virtual console terminal try to
+ deallocate the TTY before and after
+ execution. This ensures that the
+ screen and scrollback buffer is
+ cleared. Defaults to
+ <literal>no</literal>.</para></listitem>
+ </varlistentry>
<varlistentry>
<term><varname>SyslogIdentifier=</varname></term>
<listitem><para>Sets the process name
{ 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 }, \
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;
}
}
+ 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)) {
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 ||
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) ||
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) {
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
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);
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,
{ 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" },
};
#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 }, \
{ "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 }, \
{ "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" },
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);
#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);
}
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;
}
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
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;
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;
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;
}
}
}
-int reset_terminal(int fd) {
+int reset_terminal_fd(int fd) {
struct termios termios;
int r = 0;
long arg;
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;
/* 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);
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;
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",
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);
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)
Restart=always
RestartSec=0
UtmpIdentifier=%I
+TTYPath=/dev/%I
+TTYReset=yes
+TTYVHangup=yes
+TTYVTDisallocate=yes
KillMode=process
# Unset locale for the console getty since the console has problems
Restart=always
RestartSec=0
UtmpIdentifier=%I
+TTYPath=/dev/%I
+TTYReset=yes
+TTYVHangup=yes
KillMode=process
# Some login implementations ignore SIGTERM, so we send SIGHUP