]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: optionally create LOGIN_PROCESS or USER_PROCESS utmp entries
authorLennart Poettering <lennart@poettering.net>
Sun, 23 Aug 2015 11:14:04 +0000 (13:14 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 24 Aug 2015 20:46:45 +0000 (22:46 +0200)
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
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/shared/utmp-wtmp.c
src/shared/utmp-wtmp.h

index 8fd75d274e634aee9de1796cd37526e3a24f6ce3..cb11199ad3f4c38952a157718a420a5e0e7118b4 100644 (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">
 
         <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
         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>
 
index a9f7971cdea3680b671175ffe9f21cccdc410612..21d2b79678bc340cc9863f5065922580c98f1846 100644 (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),
index 3820165241cb1c4e233cc33f5dc05ccdadd0727a..28eeeaad182f912f24b1d5007024190cb3cceaf5 100644 (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);
index f5d5c1dee72c94c10f8a720d82e9fb945167b20f..5d46bf154cad32d5d9351bf1693426f0b5b82d4a 100644 (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_;
index aae81c80cbc50d55281fff5235fb7d140dfbdcd2..60b97722dbf9ca289aec80bba5653709ab7ca733 100644 (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')
index e1e3a5ffb72903f666980976ff240065302c07bb..b3bf8bdb404c89f9d025bd7361f372e8ed68b3b0 100644 (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,
index ce10d03c3fa9176e6fa3b9cf954fd3b6632b9f89..fcca2b0221b772b1c1cd34e87f46e161316e6cad 100644 (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);
index 8f66df771894b51de7f253722923b6ea7e79fe98..63f1e4ca6fdb02775e23691fe70de1d896ab0cdb 100644 (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) {
index 5d26ba6fb1d0f19dc6f6d07070f0edfa72c39617..e0ceb873ac2a3f3859aa73f51b13a3be7efc13ab 100644 (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(