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).
This commit is contained in:
Lennart Poettering 2015-08-23 13:14:04 +02:00
parent 53496ca9ad
commit 023a4f6701
9 changed files with 95 additions and 8 deletions

View File

@ -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
@ -922,6 +929,34 @@
service.</para></listitem>
</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>

View File

@ -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),

View File

@ -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);

View File

@ -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_;

View File

@ -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')

View File

@ -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,

View File

@ -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);

View File

@ -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) {

View File

@ -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(