diff --git a/TODO b/TODO
index d2eeed503f0..83bce330d76 100644
--- a/TODO
+++ b/TODO
@@ -130,14 +130,8 @@ Deprecations and removals:
Features:
-* introduce a new system credential "shell.prompt.extra" or so that may contain
- a string to include in $PS1. Inherit it down into gettys/logins and suchlike.
- And provide an /etc/profile.d/*.sh drop-in which reads the credential if set,
- and suffixes $PS1. Usecase: for wsl-like environments use this to include
- additional information in prompts that is inherited from the execution
- context. For example include 📦 emoji via this mechanism in the prompt. Or,
- in run0 include a ☢️ emoji in the prompt. Or in mkosi include a 👷 emoji in
- the prompt. You get the idea.
+* maybe set shell.prompt.prefix credential in run0 to some warning emoji,
+ i.e. ⚠️ or ☢️ or ⚡ or 👊 or 🧑🔧 or so.
* introduce new structure Tpm2CombinedPolicy, that combines the various TPm2
policy bits into one structure, i.e. public key info, pcr masks, pcrlock
diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml
index 40a4564a22c..c55cdee894a 100644
--- a/man/pam_systemd.xml
+++ b/man/pam_systemd.xml
@@ -268,6 +268,21 @@
+
+ $SHELL_PROMPT_PREFIX
+ $SHELL_PROMPT_SUFFIX
+ $SHELL_WELCOME
+
+ These environment variables are initialized from the service credentials
+ shell.prompt.prefix, shell.prompt.suffix and
+ shell.welcome if set. They are passed to the invoked session processes, where they
+ are imported into any shell prompt (specifically $SHELL_PROMPT_PREFIX is added as
+ prefix to $PS1, and $SHELL_PROMPT_SUFFIX as suffix) or printed
+ on screen when a shell first initializes.
+
+
+
+
The following environment variables are read by the module and may be used by the PAM service to pass
diff --git a/man/systemd.system-credentials.xml b/man/systemd.system-credentials.xml
index f8c27d04acc..7962bb257ab 100644
--- a/man/systemd.system-credentials.xml
+++ b/man/systemd.system-credentials.xml
@@ -334,6 +334,27 @@
+
+ shell.prompt.prefix
+ shell.prompt.suffix
+
+ Defines strings to prefix and suffix any interactive UNIX shell prompt with. For details see
+ pam_systemd8.
+
+
+
+
+
+
+ shell.welcome
+
+ Define a string to print when an interactive UNIX shell initializes. For details see
+ pam_systemd8.
+
+
+
+
+
system.machine_id
diff --git a/meson.build b/meson.build
index c26302d25ca..80e0047281b 100644
--- a/meson.build
+++ b/meson.build
@@ -188,7 +188,7 @@ mimepackagesdir = prefixdir / 'share/mime/packages'
configfiledir = get_option('configfiledir')
if configfiledir == ''
- configfiledir= sysconfdir
+ configfiledir = sysconfdir
endif
pkgconfigfiledir = configfiledir / 'systemd'
@@ -228,6 +228,13 @@ if libcryptsetup_plugins_dir == ''
libcryptsetup_plugins_dir = libdir / 'cryptsetup'
endif
+shellprofiledir = get_option('shellprofiledir')
+if shellprofiledir == ''
+ shellprofiledir = sysconfdir / 'profile.d'
+endif
+conf.set10('LINK_SHELL_EXTRA_DROPIN', shellprofiledir != 'no' and not shellprofiledir.startswith('/usr/'))
+conf.set('SHELLPROFILEDIR', shellprofiledir, description : 'shell profile directory')
+
memory_accounting_default = get_option('memory-accounting-default')
status_unit_format_default = get_option('status-unit-format-default')
if status_unit_format_default == 'auto'
@@ -257,6 +264,7 @@ conf.set_quoted('RANDOM_SEED_DIR', randomseeddir)
conf.set_quoted('RC_LOCAL_PATH', get_option('rc-local'))
conf.set_quoted('SSHCONFDIR', sshconfdir)
conf.set_quoted('SSHDCONFDIR', sshdconfdir)
+conf.set_quoted('SHELLPROFILEDIR', shellprofiledir)
conf.set_quoted('SYSCONF_DIR', sysconfdir)
conf.set_quoted('SYSCTL_DIR', sysctldir)
conf.set_quoted('SYSTEMCTL_BINARY_PATH', bindir / 'systemctl')
@@ -2701,6 +2709,7 @@ subdir('man')
subdir('modprobe.d')
subdir('network')
subdir('presets')
+subdir('profile.d')
subdir('shell-completion/bash')
subdir('shell-completion/zsh')
subdir('sysctl.d')
@@ -2923,6 +2932,7 @@ summary({
'ssh server privilege separation directory' : sshdprivsepdir,
'ssh client configuration directory' : sshconfdir,
'libcryptsetup plugins directory' : libcryptsetup_plugins_dir,
+ 'Shell profile directory' : shellprofiledir,
'RPM macros directory' : rpmmacrosdir,
'modprobe.d directory' : modprobedir,
'D-Bus policy directory' : dbuspolicydir,
diff --git a/meson_options.txt b/meson_options.txt
index 185aa85a8de..e90debdd3e0 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -225,6 +225,8 @@ option('sshdprivsepdir', type : 'string',
description : 'directory for SSH privilege separation ["no" disables]', value : '/run/sshd')
option('libcryptsetup-plugins-dir', type : 'string',
description : 'directory for libcryptsetup plugins')
+option('shellprofiledir', type : 'string',
+ description : 'directory for Shell profile drop-ins ["no" disables]')
option('docdir', type : 'string',
description : 'documentation directory')
option('install-sysconfdir', type : 'combo', choices : ['true', 'no-samples', 'false'], value : 'true',
diff --git a/profile.d/70-systemd-shell-extra.sh b/profile.d/70-systemd-shell-extra.sh
new file mode 100644
index 00000000000..70be3341b95
--- /dev/null
+++ b/profile.d/70-systemd-shell-extra.sh
@@ -0,0 +1,25 @@
+# shellcheck shell=sh
+
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+# Import the additional shell prompt prefix and suffix strings into $PS1, and
+# show the shell welcome string. These can be provisioned as system or service
+# credentials shell.prompt.prefix, shell.prompt.suffix and shell.welcome, and
+# are propagated into these environment variables by pam_systemd(8).
+
+if [ -n "$SHELL_PROMPT_PREFIX" ]; then
+ PS1="$SHELL_PROMPT_PREFIX$PS1"
+fi
+
+if [ -n "$SHELL_PROMPT_SUFFIX" ]; then
+ PS1="$PS1$SHELL_PROMPT_SUFFIX"
+fi
+
+if [ -n "$SHELL_WELCOME" ]; then
+ printf '%b\n' "$SHELL_WELCOME"
+fi
diff --git a/profile.d/meson.build b/profile.d/meson.build
new file mode 100644
index 00000000000..b87dc18eaa5
--- /dev/null
+++ b/profile.d/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+install_data('70-systemd-shell-extra.sh', install_dir : shellprofiledir.startswith('/usr/') ? shellprofiledir : libexecdir / 'profile.d')
+
+if conf.get('LINK_SHELL_EXTRA_DROPIN') == 1
+ install_emptydir(shellprofiledir)
+
+ meson.add_install_script(sh, '-c',
+ ln_s.format(libexecdir / 'profile.d' / '70-systemd-shell-extra.sh', shellprofiledir / '70-systemd-shell-extra.sh'))
+endif
diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c
index 40721ebfd9f..77b23d54dbd 100644
--- a/src/login/pam_systemd.c
+++ b/src/login/pam_systemd.c
@@ -27,6 +27,7 @@
#include "cap-list.h"
#include "capability-util.h"
#include "cgroup-setup.h"
+#include "creds-util.h"
#include "devnum-util.h"
#include "errno-util.h"
#include "fd-util.h"
@@ -567,6 +568,31 @@ static int update_environment(pam_handle_t *handle, const char *key, const char
return PAM_SUCCESS;
}
+static int propagate_credential_to_environment(pam_handle_t *handle, const char *credential, const char *varname) {
+ int r;
+
+ assert(handle);
+ assert(credential);
+ assert(varname);
+
+ _cleanup_free_ char *value = NULL;
+
+ /* Read a service credential, and propagate it into an environment variable */
+
+ r = read_credential(credential, (void**) &value, /* ret_size= */ NULL);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read credential '%s', ignoring: %m", credential);
+ return PAM_SUCCESS;
+ }
+
+ r = pam_misc_setenv(handle, varname, value, 0);
+ if (r != PAM_SUCCESS)
+ return pam_syslog_pam_error(handle, LOG_ERR, r,
+ "Failed to set environment variable %s: @PAMERR@", varname);
+
+ return PAM_SUCCESS;
+}
+
static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) {
struct stat st;
@@ -1192,6 +1218,19 @@ _public_ PAM_EXTERN int pam_sm_open_session(
if (r != PAM_SUCCESS)
return r;
+ static const char *const propagate[] = {
+ "shell.prompt.prefix", "SHELL_PROMPT_PREFIX",
+ "shell.prompt.suffix", "SHELL_PROMPT_SUFFIX",
+ "shell.welcome", "SHELL_WELCOME",
+ NULL
+ };
+
+ STRV_FOREACH_PAIR(k, v, propagate) {
+ r = propagate_credential_to_environment(handle, *k, *v);
+ if (r != PAM_SUCCESS)
+ return r;
+ }
+
if (vtnr > 0) {
char buf[DECIMAL_STR_MAX(vtnr)];
sprintf(buf, "%u", vtnr);
diff --git a/tmpfiles.d/20-systemd-shell-extra.conf.in b/tmpfiles.d/20-systemd-shell-extra.conf.in
new file mode 100644
index 00000000000..8ebe83dd697
--- /dev/null
+++ b/tmpfiles.d/20-systemd-shell-extra.conf.in
@@ -0,0 +1,12 @@
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+# See tmpfiles.d(5) for details
+
+{% if LINK_SHELL_EXTRA_DROPIN %}
+L {{SHELLPROFILEDIR}}/70-systemd-shell-extra.sh - - - - {{LIBEXECDIR}}/profile.d/70-systemd-shell-extra.sh
+{% endif %}
diff --git a/tmpfiles.d/meson.build b/tmpfiles.d/meson.build
index bec24ac7b4d..8d05abcfc14 100644
--- a/tmpfiles.d/meson.build
+++ b/tmpfiles.d/meson.build
@@ -36,6 +36,7 @@ in_files = [['etc.conf', ''],
['var.conf', ''],
['20-systemd-userdb.conf', 'ENABLE_SSH_USERDB_CONFIG'],
['20-systemd-ssh-generator.conf', 'ENABLE_SSH_PROXY_CONFIG'],
+ ['20-systemd-shell-extra.conf', 'LINK_SHELL_EXTRA_DROPIN'],
]
foreach pair : in_files
diff --git a/units/console-getty.service.in b/units/console-getty.service.in
index 575a7770c9d..33e6368db15 100644
--- a/units/console-getty.service.in
+++ b/units/console-getty.service.in
@@ -40,6 +40,7 @@ ImportCredential=tty.console.agetty.*:agetty.
ImportCredential=tty.console.login.*:login.
ImportCredential=agetty.*
ImportCredential=login.*
+ImportCredential=shell.*
[Install]
WantedBy=getty.target
diff --git a/units/container-getty@.service.in b/units/container-getty@.service.in
index 6bee309bf5a..7573532d6dd 100644
--- a/units/container-getty@.service.in
+++ b/units/container-getty@.service.in
@@ -46,3 +46,4 @@ ImportCredential=tty.container.%I.agetty.*:agetty.
ImportCredential=tty.container.%I.login.*:login.
ImportCredential=agetty.*
ImportCredential=login.*
+ImportCredential=shell.*
diff --git a/units/getty@.service.in b/units/getty@.service.in
index 3ea95e1ee4f..f30bba406d5 100644
--- a/units/getty@.service.in
+++ b/units/getty@.service.in
@@ -56,6 +56,7 @@ ImportCredential=tty.virtual.%I.agetty.*:agetty.
ImportCredential=tty.virtual.%I.login.*:login.
ImportCredential=agetty.*
ImportCredential=login.*
+ImportCredential=shell.*
# Unset locale for the console getty since the console has problems
# displaying some internationalized messages.
diff --git a/units/serial-getty@.service.in b/units/serial-getty@.service.in
index a071241ef90..20a5eb2754f 100644
--- a/units/serial-getty@.service.in
+++ b/units/serial-getty@.service.in
@@ -50,6 +50,7 @@ ImportCredential=tty.serial.%I.agetty.*:agetty.
ImportCredential=tty.serial.%I.login.*:login.
ImportCredential=agetty.*
ImportCredential=login.*
+ImportCredential=shell.*
[Install]
WantedBy=getty.target