summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2015-08-23 13:14:04 +0200
committerLennart Poettering <lennart@poettering.net>2015-08-24 22:46:45 +0200
commit023a4f67011f24d4b085995a4a3a02661c4794a2 (patch)
treec513fa8120c95cb04b19db69d56a4af8a1b8a129
parent53496ca9adc383bd8aa28e8f75f1b21c2cc82b05 (diff)
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).
-rw-r--r--man/systemd.exec.xml41
-rw-r--r--src/core/dbus-execute.c3
-rw-r--r--src/core/execute.c15
-rw-r--r--src/core/execute.h12
-rw-r--r--src/core/load-fragment-gperf.gperf.m41
-rw-r--r--src/core/load-fragment.c2
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/shared/utmp-wtmp.c24
-rw-r--r--src/shared/utmp-wtmp.h4
9 files changed, 95 insertions, 8 deletions
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 8fd75d274e..cb11199ad3 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1,3 +1,4 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
@@ -911,10 +912,16 @@
<term><varname>UtmpIdentifier=</varname></term>
<listitem><para>Takes a four character identifier string for
- an utmp/wtmp entry for this service. This should only be set
- for services such as <command>getty</command> implementations
+ an <citerefentry
+ project='man-pages'><refentrytitle>utmp</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ and wtmp entry for this service. This should only be
+ set for services such as <command>getty</command>
+ implementations (such as <citerefentry
+ project='die-net'><refentrytitle>agetty</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
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 <command>getty</command> 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
@@ -923,6 +930,34 @@
</varlistentry>
<varlistentry>
+ <term><varname>UtmpMode=</varname></term>
+
+ <listitem><para>Takes one of <literal>init</literal>,
+ <literal>login</literal> or <literal>user</literal>. If
+ <varname>UtmpIdentifier=</varname> is set, controls which
+ type of <citerefentry
+ project='man-pages'><refentrytitle>utmp</refentrytitle><manvolnum>5</manvolnum></citerefentry>/wtmp
+ entries for this service are generated. This setting has no
+ effect unless <varname>UtmpIdentifier=</varname> is set
+ too. If <literal>init</literal> is set, only an
+ <constant>INIT_PROCESS</constant> entry is generated and the
+ invoked process must implement a <command>getty</command>
+ compatible utmp/wtmp logic. If <literal>login</literal> is
+ set, first an <constant>INIT_PROCESS</constant> entry,
+ followed by an <constant>LOGIN_PROCESS</constant> entry is
+ generated. In this case the invoked process must implement a
+ <citerefentry
+ project='die-net'><refentrytitle>login</refentrytitle><manvolnum>1</manvolnum></citerefentry>-compatible
+ utmp/wtmp logic. If <literal>user</literal> is set, first an
+ <constant>INIT_PROCESS</constant> entry, then a
+ <constant>LOGIN_PROCESS</constant> entry and finally an
+ <constant>USER_PROCESS</constant> entry is generated. In this
+ case the invoked process may be any process that is suitable
+ to be run as session leader. Defaults to
+ <literal>init</literal>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>SELinuxContext=</varname></term>
<listitem><para>Set the SELinux security context of the
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index a9f7971cde..21d2b79678 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 3820165241..28eeeaad18 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -31,6 +31,7 @@
#include <grp.h>
#include <poll.h>
#include <glob.h>
+#include <utmpx.h>
#include <sys/personality.h>
#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 f5d5c1dee7..5d46bf154c 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 aae81c80cb..60b97722db 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 e1e3a5ffb7..b3bf8bdb40 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 ce10d03c3f..fcca2b0221 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 8f66df7718..63f1e4ca6f 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 5d26ba6fb1..e0ceb873ac 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(