From 023a4f67011f24d4b085995a4a3a02661c4794a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2015 13:14:04 +0200 Subject: [PATCH] core: optionally create LOGIN_PROCESS or USER_PROCESS utmp entries When generating utmp/wtmp entries, optionally add both LOGIN_PROCESS and INIT_PROCESS entries or even all three of LOGIN_PROCESS, INIT_PROCESS and USER_PROCESS entries, instead of just a single INIT_PROCESS entry. With this change systemd may be used to not only invoke a getty directly in a SysV-compliant way but alternatively also a login(1) implementation or even forego getty and login entirely, and invoke arbitrary shells in a way that they appear in who(1) or w(1). This is preparation for a later commit that adds a "machinectl shell" operation to invoke a shell in a container, in a way that is compatible with who(1) and w(1). --- man/systemd.exec.xml | 41 +++++++++++++++++++++++++-- src/core/dbus-execute.c | 3 ++ src/core/execute.c | 15 +++++++++- src/core/execute.h | 12 ++++++++ src/core/load-fragment-gperf.gperf.m4 | 1 + src/core/load-fragment.c | 2 ++ src/core/load-fragment.h | 1 + src/shared/utmp-wtmp.c | 24 ++++++++++++++-- src/shared/utmp-wtmp.h | 4 +-- 9 files changed, 95 insertions(+), 8 deletions(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 8fd75d274e6..cb11199ad3f 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1,3 +1,4 @@ + @@ -911,10 +912,16 @@ UtmpIdentifier= Takes a four character identifier string for - an utmp/wtmp entry for this service. This should only be set - for services such as getty implementations + an utmp5 + and wtmp entry for this service. This should only be + set for services such as getty + implementations (such as agetty8) where utmp/wtmp entries must be created and cleared before and - after execution. If the configured string is longer than four + after execution, or for services that shall be executed as if + they were run by a getty process (see + below). If the configured string is longer than four characters, it is truncated and the terminal four characters are used. This setting interprets %I style string replacements. This setting is unset by default, i.e. no @@ -922,6 +929,34 @@ service. + + UtmpMode= + + Takes one of init, + login or user. If + UtmpIdentifier= is set, controls which + type of utmp5/wtmp + entries for this service are generated. This setting has no + effect unless UtmpIdentifier= is set + too. If init is set, only an + INIT_PROCESS entry is generated and the + invoked process must implement a getty + compatible utmp/wtmp logic. If login is + set, first an INIT_PROCESS entry, + followed by an LOGIN_PROCESS entry is + generated. In this case the invoked process must implement a + login1-compatible + utmp/wtmp logic. If user is set, first an + INIT_PROCESS entry, then a + LOGIN_PROCESS entry and finally an + USER_PROCESS entry is generated. In this + case the invoked process may be any process that is suitable + to be run as session leader. Defaults to + init. + + SELinuxContext= diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index a9f7971cdea..21d2b79678b 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -46,6 +46,8 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutp static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode); + static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome); static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem); @@ -653,6 +655,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UtmpMode", "s", property_get_exec_utmp_mode, offsetof(ExecContext, utmp_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SmackProcessLabel", "(bs)", property_get_smack_process_label, 0, SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/core/execute.c b/src/core/execute.c index 3820165241c..28eeeaad182 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #ifdef HAVE_PAM @@ -1504,7 +1505,11 @@ static int exec_child( } if (context->utmp_id) - utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path); + utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path, + context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS : + context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS : + USER_PROCESS, + username ? "root" : context->user); if (context->user && is_terminal_input(context->std_input)) { r = chown_terminal(STDIN_FILENO, uid); @@ -2968,3 +2973,11 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); + +static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = { + [EXEC_UTMP_INIT] = "init", + [EXEC_UTMP_LOGIN] = "login", + [EXEC_UTMP_USER] = "user", +}; + +DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode); diff --git a/src/core/execute.h b/src/core/execute.h index f5d5c1dee72..5d46bf154ca 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -38,6 +38,14 @@ typedef struct ExecParameters ExecParameters; #include "namespace.h" #include "bus-endpoint.h" +typedef enum ExecUtmpMode { + EXEC_UTMP_INIT, + EXEC_UTMP_LOGIN, + EXEC_UTMP_USER, + _EXEC_UTMP_MODE_MAX, + _EXEC_UTMP_MODE_INVALID, +} ExecUtmpMode; + typedef enum ExecInput { EXEC_INPUT_NULL, EXEC_INPUT_TTY, @@ -131,6 +139,7 @@ struct ExecContext { char *pam_name; char *utmp_id; + ExecUtmpMode utmp_mode; bool selinux_context_ignore; char *selinux_context; @@ -265,3 +274,6 @@ ExecOutput exec_output_from_string(const char *s) _pure_; const char* exec_input_to_string(ExecInput i) _const_; ExecInput exec_input_from_string(const char *s) _pure_; + +const char* exec_utmp_mode_to_string(ExecUtmpMode i) _const_; +ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index aae81c80cbc..60b97722dbf 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -91,6 +91,7 @@ m4_ifdef(`HAVE_PAM', `$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') $1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe) $1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id) +$1.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof($1, exec_context.utmp_mode) m4_ifdef(`HAVE_SELINUX', `$1.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof($1, exec_context)', `$1.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e1e3a5ffb72..b3bf8bdb404 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1142,6 +1142,8 @@ int config_parse_sysv_priority(const char *unit, } #endif +DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); + DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); int config_parse_kill_signal(const char *unit, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index ce10d03c3fa..fcca2b0221b 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -104,6 +104,7 @@ int config_parse_cpu_quota(const char *unit, const char *filename, unsigned line int config_parse_protect_home(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_protect_system(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index 8f66df77189..63f1e4ca6fd 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -204,12 +204,13 @@ _pure_ static const char *sanitize_id(const char *id) { return id + l - sizeof(((struct utmpx*) NULL)->ut_id); } -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { struct utmpx store = { .ut_type = INIT_PROCESS, .ut_pid = pid, .ut_session = sid, }; + int r; assert(id); @@ -221,7 +222,26 @@ int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line if (line) strncpy(store.ut_line, basename(line), sizeof(store.ut_line)); - return write_entry_both(&store); + r = write_entry_both(&store); + if (r < 0) + return r; + + if (ut_type == LOGIN_PROCESS || ut_type == USER_PROCESS) { + store.ut_type = LOGIN_PROCESS; + r = write_entry_both(&store); + if (r < 0) + return r; + } + + if (ut_type == USER_PROCESS) { + store.ut_type = USER_PROCESS; + strncpy(store.ut_user, user, sizeof(store.ut_user)-1); + r = write_entry_both(&store); + if (r < 0) + return r; + } + + return 0; } int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h index 5d26ba6fb1d..e0ceb873ac2 100644 --- a/src/shared/utmp-wtmp.h +++ b/src/shared/utmp-wtmp.h @@ -31,7 +31,7 @@ int utmp_put_reboot(usec_t timestamp); int utmp_put_runlevel(int runlevel, int previous); int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line); +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user); int utmp_wall( const char *message, @@ -57,7 +57,7 @@ static inline int utmp_put_runlevel(int runlevel, int previous) { static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { return 0; } -static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { +static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { return 0; } static inline int utmp_wall( -- 2.39.2