1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-26 08:55:40 +03:00

Compare commits

...

18 Commits

Author SHA1 Message Date
Igor Opaniuk
d2e628b91e
Merge e2e42056fd into f7078de515 2024-10-25 20:37:05 +02:00
Yu Watanabe
f7078de515
Merge pull request #34884 from poettering/run0-disconnect-fix
run: reconnect if our dbus connection is terminated
2024-10-26 02:50:48 +09:00
Yu Watanabe
6d6048b4cb
Merge pull request #34881 from poettering/run0-ui-tweaks
run0: various UI tweaks
2024-10-26 02:49:48 +09:00
Ivan Kruglov
10a48938ef machine: operation should not send a response when 'done' callback set 2024-10-26 02:45:53 +09:00
Lennart Poettering
b58b13f1c6 test: add brief testcase for systemd-run disconnect handling 2024-10-25 17:51:04 +02:00
Lennart Poettering
c8f59296bf run: reconnect if our dbus connection is terminated
We must be prepared that systemd temporarily drops off the bus or
disconnects our direct connections (due to systemctl daemon-reexec or
so). Hence automatically reconnect when we watch the unit status, and
handle this case gracefully.

Fixes: #32906 #27204
2024-10-25 17:51:04 +02:00
Lennart Poettering
d585085f57 update TODO 2024-10-25 17:32:19 +02:00
Lennart Poettering
ff4b6a1915 run: drop "-" prefix from command line when generating unit description
Let's not confuse users with the login shell indicator and drop it from
the description. This means a run0 session will now usually show up with
a description of "[run0] /bin/bash" rather than "[run0] -/bin/bash".
2024-10-25 17:32:19 +02:00
Lennart Poettering
d9f68f48f7 run: prefix unit description with our own process name
I think we should try to communicate clearly if something is a run0
session, or a systemd-run invocation. Hence, let's initialize the
description so that the command is prefixed by
program_invocation_short_name.

Effectively this means that our run0 sessions now appear as services
with a description of "[run0] -/bin/bash"
2024-10-25 17:32:19 +02:00
Lennart Poettering
0310b2a60b run: tweak how we name our transient units
The current logic is a bit complex how systemd-run units are called. It
used to be just the unique ID of the dbus connection. Which was nice,
since its system-widely, uniquely assigned to us. But this didn't work
out well, due to direct connections to PID 1 and due to soft reboots.

We nowadays have a better ID to use though, with nicer properties: the
kernel manages a pidfd ID for every process after all, and it's globally
unique, for any process, and regardless of soft reboots. Hence use that
for naming preferably, and just keep one branch with a randomized name
as fallback.
2024-10-25 17:32:19 +02:00
Lennart Poettering
115fac3c29 run0: optionally show superhero emoji on each shell prompt
This makes use of the infra introduced in 229d4a9806 to indicate visually on each prompt that we are in superuser mode temporarily.
pick ad5de3222f userdbctl: add some basic client-side filtering
2024-10-25 17:31:06 +02:00
Lennart Poettering
9d8f5e22f8
Merge pull request #34891 from poettering/run0-pty
run0: add --pty and --pipe switches to force allocation of a pty or pipe
2024-10-25 16:25:01 +02:00
Lennart Poettering
6fb0c52295 ci: add some basic testing of the new --pty and --pipe switches 2024-10-25 14:14:26 +02:00
Lennart Poettering
edd10ab29c run0: add options to force allocation of PTY or of pipe use
Fixes: #33033
2024-10-25 14:14:26 +02:00
Lennart Poettering
988053eac3 tree-wide: use isatty_safe() everywhere 2024-10-25 14:09:38 +02:00
Lennart Poettering
a586f57eb2 update TODO 2024-10-25 13:57:44 +02:00
Igor Opaniuk
e2e42056fd efi/boot: use fallback entry in case of UEFI firmware update failure
Add support for using a fallback boot entry in case of UEFI firmware
capsule update failure [1]. The status of a firmware update is obtained from
the EFI System Resource Table (ESRT), which provides an optional mechanism
for identifying device and system firmware resources for the purposes of
targeting firmware updates to those resources.

Current implementation uses the value of LastAttemptStatus field from
ESRT, which describes the result of the last firmware update attempt for
the firmware resource entry. The field is updated each time an
UpdateCapsule() is attempted for an ESRT entry and is preserved across
reboots (non-volatile).

This can be be used in setups with support for A/B OTA updates, where
the boot firmware and Linux/RootFS are updated synchronously.

The check is activated by adding "fallback-firmware-failure" to
loader.conf

[1] https://uefi.org/specs/UEFI/2.10/23_Firmware_Update_and_Reporting.html
Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
2024-10-22 16:20:38 +02:00
Igor Opaniuk
c2327afbfe efi/boot: add support for a fallback entry
Add support for a fallback boot entry. Fallback boot entries can be used to
ensure that a system starts even if the default boot entry fails or
other unexpected events happen that can prevent to use the default boot
entry (boot firmware failure, failed A/B OTA update etc).

The EFI variable `LoaderEntryFallback` contains the fallback boot loader
entry to use.

config_is_fallback_required() expected to be extented to support possible
conditions when we should fall back to "recovery" boot entry.

Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
2024-10-22 16:20:34 +02:00
23 changed files with 522 additions and 150 deletions

8
TODO
View File

@ -129,6 +129,11 @@ Deprecations and removals:
Features:
* $LISTEN_PID, $MAINPID and $SYSTEMD_EXECPID env vars that the service manager
sets should be augmented with $LISTEN_PIDFDID, $MAINPIDFDID and
$SYSTEMD_EXECPIDFD (and similar for other env vars we might send). Also,
MAINPID= in sd_notify() should be augmented with MAINPIDFDID=, and so on.
* port copy.c over to use LabelOps for all labelling.
* port remaining getmntent() users over to libmount. There are subtle
@ -157,9 +162,6 @@ Features:
sd_event_add_child(), and then get rid of many more explicit sigprocmask()
calls.
* 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
stuff, pin and so on. Then pass that around in tpm2_seal() and tpm2_unseal().

View File

@ -102,6 +102,7 @@ Some EFI variables control the loader or exported the loaders state to the start
| EFI Variables |
|---------------|------------------------|-------------------------------|
| LoaderEntryDefault | entry identifier to select as default at bootup | non-volatile |
| LoaderEntryFallback | fallback entry identifier | non-volatile |
| LoaderConfigTimeout | timeout in seconds to show the menu | non-volatile |
| LoaderEntryOneShot | entry identifier to select at the next and only the next bootup | non-volatile |
| LoaderDeviceIdentifier | list of identifiers of the volume the loader was started from | volatile |

View File

@ -58,6 +58,11 @@ variables. All EFI variables use the vendor UUID
* The EFI variable `LoaderEntryDefault` contains the default boot loader entry
to use. It contains a NUL-terminated boot loader entry identifier.
* The EFI variable `LoaderEntryFallback` contains the fallback boot loader entry
to use. Fallback boot entries can be used to ensure that a system starts even
if the default boot entry fails or other unexpected event happened (boot firmware
failure etc.). It contains a NUL-terminated boot loader entry identifier.
* Similarly, the EFI variable `LoaderEntryOneShot` contains the default boot
loader entry to use for a single following boot. It is set by the OS in order
to request booting into a specific menu entry on the following boot. When set

View File

@ -146,8 +146,9 @@
<option>@oneshot</option> or <option>@current</option>, which correspond to the current default boot loader
entry for all future boots, the current default boot loader entry for the next boot, and the currently booted
boot loader entry. These special IDs are resolved to the current values of the EFI variables
<varname>LoaderEntryDefault</varname>, <varname>LoaderEntryOneShot</varname> and <varname>LoaderEntrySelected</varname>,
see <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink> for details.
<varname>LoaderEntryDefault</varname>, <varname>LoaderEntryFallback</varname>, <varname>LoaderEntryOneShot</varname>
and <varname>LoaderEntrySelected</varname>, see <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">
Boot Loader Specification</ulink> for details.
These special IDs are primarily useful as a quick way to persistently make the currently booted boot loader
entry the default choice, or to upgrade the default boot loader entry for the next boot to the default boot
loader entry for all future boots, but may be used for other operations too.</para>

View File

@ -192,6 +192,35 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--pty</option></term>
<term><option>--pipe</option></term>
<listitem><para>Request allocation of a pseudo TTY for the <command>run0</command> session (in case
of <option>--pty</option>), or request passing the caller's STDIO file descriptors directly through
(in case of <option>--pipe</option>). If neither switch is specified, or if both switches are
specified, the mode will be picked automatically: if standard input, standard output and standard
error output are all connected to a TTY then a pseudo TTY is allocated, otherwise the relevant file
descriptors are passed through directly.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--shell-prompt-prefix=<replaceable>STRING</replaceable></option></term>
<listitem><para>Set a shell prompt prefix string. This ultimately controls the
<varname>$SHELL_PROMPT_PREFIX</varname> environment variable for the invoked program, which is
typically imported into the shell prompt. By default if emojis are supported a superhero emoji is
shown (🦸). This default may also be changed (or turned off) by passing the
<varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname> environment variable to <varname>run0</varname>,
see below. Set to an empty string to disable shell prompt prefixing.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--machine=</option></term>
@ -256,7 +285,30 @@
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>$SHELL_PROMPT_PREFIX</varname></term>
<listitem><para>By default set to the superhero emoji (if supported), but may be overriden with the
<varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname> environment variable (see below), or the
<option>--shell-prompt-prefix=</option> switch (see above).</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist>
<para>The following variables may be passed to <command>run0</command>:</para>
<variablelist>
<varlistentry>
<term><varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname></term>
<listitem><para>If set, overrides the default shell prompt prefix that <command>run0</command> sets
for the invoked shell (the superhero emoji). Set to an empty string to disable shell prompt
prefixing.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>

View File

@ -451,6 +451,7 @@
<varlistentry>
<term><varname>LoaderEntryDefault</varname></term>
<term><varname>LoaderEntryFallback</varname></term>
<term><varname>LoaderEntryOneShot</varname></term>
<listitem><para>The identifier of the default boot loader entry. Set primarily by the OS and read by the boot

View File

@ -80,6 +80,7 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
[SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
[SPECIAL_GLYPH_GREEN_CIRCLE] = "o",
[SPECIAL_GLYPH_SUPERHERO] = "S",
},
/* UTF-8 */
@ -149,6 +150,7 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
[SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
[SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢",
[SPECIAL_GLYPH_SUPERHERO] = u8"🦸",
},
};

View File

@ -55,6 +55,7 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_YELLOW_CIRCLE,
SPECIAL_GLYPH_BLUE_CIRCLE,
SPECIAL_GLYPH_GREEN_CIRCLE,
SPECIAL_GLYPH_SUPERHERO,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;

View File

@ -1019,6 +1019,7 @@ static int remove_loader_variables(void) {
EFI_LOADER_VARIABLE(LoaderConfigTimeout),
EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot),
EFI_LOADER_VARIABLE(LoaderEntryDefault),
EFI_LOADER_VARIABLE(LoaderEntryFallback),
EFI_LOADER_VARIABLE(LoaderEntryLastBooted),
EFI_LOADER_VARIABLE(LoaderEntryOneShot),
EFI_LOADER_VARIABLE(LoaderSystemToken)) {

View File

@ -88,6 +88,11 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target
if (r < 0)
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m");
} else if (streq(arg1, "@fallback")) {
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryFallback), NULL, (void *) ret_target, ret_target_size);
if (r < 0)
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryFallback': %m");
} else if (arg1[0] == '@' && !streq(arg1, "@saved"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
else {
@ -139,6 +144,9 @@ int verb_set_efivar(int argc, char *argv[], void *userdata) {
if (streq(argv[0], "set-default")) {
variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
arg_parser = parse_loader_entry_target_arg;
} else if (streq(argv[0], "set-fallback")) {
variable = EFI_LOADER_VARIABLE(LoaderEntryFallback);
arg_parser = parse_loader_entry_target_arg;
} else if (streq(argv[0], "set-oneshot")) {
variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
arg_parser = parse_loader_entry_target_arg;

View File

@ -20,6 +20,7 @@
#include "proto/device-path.h"
#include "proto/simple-text-io.h"
#include "random-seed.h"
#include "recovery.h"
#include "sbat.h"
#include "secure-boot.h"
#include "shim.h"
@ -88,6 +89,7 @@ typedef struct {
uint64_t timeout_sec_config;
uint64_t timeout_sec_efivar;
char16_t *entry_default_config;
char16_t *entry_fallback;
char16_t *entry_default_efivar;
char16_t *entry_oneshot;
char16_t *entry_saved;
@ -97,6 +99,7 @@ typedef struct {
bool auto_poweroff;
bool auto_reboot;
bool reboot_for_bitlocker;
bool fallback_firmware_failure;
secure_boot_enroll secure_boot_enroll;
bool force_menu;
bool use_saved_entry;
@ -525,6 +528,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
if (config->entry_default_config)
printf(" default (config): %ls\n", config->entry_default_config);
if (config->entry_fallback)
printf(" default (fallback): %ls\n", config->entry_fallback);
if (config->entry_default_efivar)
printf(" default (EFI var): %ls\n", config->entry_default_efivar);
if (config->entry_oneshot)
@ -538,6 +543,7 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
printf(" auto-reboot: %ls\n", yes_no(config->auto_reboot));
printf(" beep: %ls\n", yes_no(config->beep));
printf(" reboot-for-bitlocker: %ls\n", yes_no(config->reboot_for_bitlocker));
printf(" fallback-firmware-failure: %ls\n", yes_no(config->fallback_firmware_failure));
switch (config->secure_boot_enroll) {
case ENROLL_OFF:
@ -1275,6 +1281,10 @@ static void config_defaults_load_from_file(Config *config, char *content) {
if (!parse_boolean(value, &config->reboot_for_bitlocker))
log_error("Error parsing 'reboot-for-bitlocker' config option, ignoring: %s",
value);
} else if (streq8(key, "fallback-firmware-failure")) {
if (!parse_boolean(value, &config->fallback_firmware_failure))
log_error("Error parsing 'fallback-firmware-failure' config option, ignoring: %s",
value);
} else if (streq8(key, "secure-boot-enroll")) {
if (streq8(value, "manual"))
@ -1634,8 +1644,10 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
(void) efivar_unset(MAKE_GUID_PTR(LOADER), u"LoaderEntryOneShot", EFI_VARIABLE_NON_VOLATILE);
(void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryDefault", &config->entry_default_efivar);
(void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryFallback", &config->entry_fallback);
strtolower16(config->entry_default_config);
strtolower16(config->entry_fallback);
strtolower16(config->entry_default_efivar);
strtolower16(config->entry_oneshot);
strtolower16(config->entry_saved);
@ -1777,11 +1789,29 @@ static size_t config_find_entry(Config *config, const char16_t *pattern) {
return IDX_INVALID;
}
static bool config_is_fallback_required(Config *config) {
assert(config);
if (config->fallback_firmware_failure) {
return recovery_check_firmware_failure();
}
return false;
}
static void config_select_default_entry(Config *config) {
size_t i;
assert(config);
if (config_is_fallback_required(config)) {
i = config_find_entry(config, config->entry_fallback);
if (i != IDX_INVALID) {
config->idx_default = i;
return;
}
}
i = config_find_entry(config, config->entry_oneshot);
if (i != IDX_INVALID) {
config->idx_default = i;
@ -2643,6 +2673,7 @@ static void config_free(Config *config) {
boot_entry_free(config->entries[i]);
free(config->entries);
free(config->entry_default_config);
free(config->entry_fallback);
free(config->entry_default_efivar);
free(config->entry_oneshot);
free(config->entry_saved);

View File

@ -144,6 +144,16 @@ typedef struct {
GUID_DEF(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
#define EFI_CUSTOM_MODE_ENABLE_GUID \
GUID_DEF(0xc076ec0c, 0x7028, 0x4399, 0xa0, 0x72, 0x71, 0xee, 0x5c, 0x44, 0x8b, 0x9f)
#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
GUID_DEF(0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80)
/* EFI System Resource Table (ESRT) Firmware Type Definitions */
#define ESRT_FW_TYPE_UNKNOWN 0x00000000
#define ESRT_FW_TYPE_SYSTEMFIRMWARE 0x00000001
#define ESRT_FW_TYPE_DEVICEFIRMWARE 0x00000002
#define ESRT_FW_TYPE_UEFIDRIVER 0x00000003
#define LAST_ATTEMPT_STATUS_SUCCESS 0x00000000
#define EVT_TIMER 0x80000000U
#define EVT_RUNTIME 0x40000000U
@ -458,6 +468,22 @@ typedef struct {
} *ConfigurationTable;
} EFI_SYSTEM_TABLE;
typedef struct {
EFI_GUID FwClass;
uint32_t FwType;
uint32_t FwVersion;
uint32_t LowestSupportedFwVersion;
uint32_t CapsuleFlags;
uint32_t LastAttemptVersion;
uint32_t LastAttemptStatus;
} EFI_SYSTEM_RESOURCE_ENTRY;
typedef struct {
uint32_t FwResourceCount;
uint32_t FwResourceCountMax;
uint64_t FwResourceVersion;
} EFI_SYSTEM_RESOURCE_TABLE;
extern EFI_SYSTEM_TABLE *ST;
extern EFI_BOOT_SERVICES *BS;
extern EFI_RUNTIME_SERVICES *RT;

View File

@ -267,6 +267,7 @@ libefi_sources = files(
'measure.c',
'part-discovery.c',
'pe.c',
'recovery.c',
'random-seed.c',
'secure-boot.c',
'shim.c',

23
src/boot/efi/recovery.c Normal file
View File

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "recovery.h"
#include "util.h"
#define FALLBACK_BOOT_ENTRY L"ostree-1-lmp.conf\0"
bool recovery_check_firmware_failure(void) {
EFI_SYSTEM_RESOURCE_TABLE *esrt_table = NULL;
EFI_SYSTEM_RESOURCE_ENTRY *esrt_entry = NULL;
esrt_table = find_configuration_table(MAKE_GUID_PTR(EFI_SYSTEM_RESOURCE_TABLE));
if (esrt_table) {
esrt_entry = (void *)(esrt_table + 1);
for (uint32_t i = 0; i < esrt_table->FwResourceCount; i++, esrt_entry++) {
if (esrt_entry->FwType == ESRT_FW_TYPE_SYSTEMFIRMWARE) {
return (esrt_entry->LastAttemptStatus != LAST_ATTEMPT_STATUS_SUCCESS);
}
}
}
return false;
}

7
src/boot/efi/recovery.h Normal file
View File

@ -0,0 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <efi.h>
#include "efivars-fundamental.h"
bool recovery_check_firmware_failure(void);

View File

@ -8,7 +8,7 @@
#include "operation.h"
#include "process-util.h"
static int operation_done_internal(const siginfo_t *si, Operation *o, sd_bus_error *error) {
static int read_operation_errno(const siginfo_t *si, Operation *o) {
int r;
assert(si);
@ -27,15 +27,6 @@ static int operation_done_internal(const siginfo_t *si, Operation *o, sd_bus_err
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Received unexpectedly short message when reading operation's errno");
}
if (o->done)
/* A completion routine is set for this operation, call it. */
return o->done(o, r, error);
/* The default operation when done is to simply return an error on failure or an empty success
* message on success. */
if (r < 0)
log_debug_errno(r, "Operation failed: %m");
return r;
}
@ -51,10 +42,18 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat
o->pid = 0;
r = read_operation_errno(si, o);
if (r < 0)
log_debug_errno(r, "Operation failed: %m");
/* If a completion routine (o->done) is set for this operation, call it. It sends a response, but can return an error in which case it expect us to reply.
* Otherwise, the default action is to simply return an error on failure or an empty success message on success. */
if (o->message) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (o->done)
r = o->done(o, r, &error);
r = operation_done_internal(si, o, &error);
if (r < 0) {
if (!sd_bus_error_is_set(&error))
sd_bus_error_set_errno(&error, r);
@ -62,16 +61,20 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat
r = sd_bus_reply_method_error(o->message, &error);
if (r < 0)
log_error_errno(r, "Failed to reply to dbus message: %m");
} else {
} else if (!o->done) {
/* when o->done set it's responsible for sending reply in a happy-path case */
r = sd_bus_reply_method_return(o->message, NULL);
if (r < 0)
log_error_errno(r, "Failed to reply to dbus message: %m");
}
} else if (o->link) {
r = operation_done_internal(si, o, /* error = */ NULL);
if (o->done)
r = o->done(o, r, /* error = */ NULL);
if (r < 0)
(void) sd_varlink_error_errno(o->link, r);
else
else if (!o->done)
/* when o->done set it's responsible for sending reply in a happy-path case */
(void) sd_varlink_reply(o->link, NULL);
} else
assert_not_reached();

View File

@ -290,7 +290,7 @@ static int handle_arg_console(const char *arg) {
else if (streq(arg, "passive"))
arg_console_mode = CONSOLE_PASSIVE;
else if (streq(arg, "pipe")) {
if (isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO))
if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO))
log_full(arg_quiet ? LOG_DEBUG : LOG_NOTICE,
"Console mode 'pipe' selected, but standard input/output are connected to an interactive TTY. "
"Most likely you want to use 'interactive' console mode for proper interactivity and shell job control. "
@ -298,7 +298,7 @@ static int handle_arg_console(const char *arg) {
arg_console_mode = CONSOLE_PIPE;
} else if (streq(arg, "autopipe")) {
if (isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO))
if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO))
arg_console_mode = CONSOLE_INTERACTIVE;
else
arg_console_mode = CONSOLE_PIPE;
@ -5981,7 +5981,7 @@ static int run(int argc, char *argv[]) {
umask(0022);
if (arg_console_mode < 0)
arg_console_mode = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) ?
arg_console_mode = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) ?
CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY;
if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */

View File

@ -22,6 +22,7 @@
#include "chase.h"
#include "env-util.h"
#include "escape.h"
#include "event-util.h"
#include "exec-util.h"
#include "exit-status.h"
#include "fd-util.h"
@ -85,6 +86,7 @@ static char *arg_exec_path = NULL;
static bool arg_ignore_failure = false;
static char *arg_background = NULL;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
static char *arg_shell_prompt_prefix = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@ -96,6 +98,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
STATIC_DESTRUCTOR_REGISTER(arg_shell_prompt_prefix, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@ -171,6 +174,10 @@ static int help_sudo_mode(void) {
if (r < 0)
return log_oom();
/* NB: Let's not go overboard with short options: we try to keep a modicum of compatibility with
* sudo's short switches, hence please do not introduce new short switches unless they have a roughly
* equivalent purpose on sudo. Use long options for everything private to run0. */
printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
"\n%sElevate privileges interactively.%s\n\n"
" -h --help Show this help\n"
@ -188,6 +195,9 @@ static int help_sudo_mode(void) {
" -D --chdir=PATH Set working directory\n"
" --setenv=NAME[=VALUE] Set environment variable\n"
" --background=COLOR Set ANSI color for background\n"
" --pty Request allocation of a pseudo TTY for stdio\n"
" --pipe Request direct pipe for stdio\n"
" --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@ -674,7 +684,7 @@ static int parse_argv(int argc, char *argv[]) {
/* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully
* to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell
* pipeline, but we are neatly interactive with tty-level isolation otherwise. */
arg_stdio = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ?
arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ?
ARG_STDIO_PTY :
ARG_STDIO_DIRECT;
@ -770,6 +780,9 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
ARG_NICE,
ARG_SETENV,
ARG_BACKGROUND,
ARG_PTY,
ARG_PIPE,
ARG_SHELL_PROMPT_PREFIX,
};
/* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
@ -791,6 +804,9 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
{ "chdir", required_argument, NULL, 'D' },
{ "setenv", required_argument, NULL, ARG_SETENV },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "pty", no_argument, NULL, ARG_PTY },
{ "pipe", no_argument, NULL, ARG_PIPE },
{ "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX },
{},
};
@ -883,6 +899,26 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
break;
case ARG_PTY:
if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */
arg_stdio = ARG_STDIO_AUTO;
else
arg_stdio = ARG_STDIO_PTY;
break;
case ARG_PIPE:
if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */
arg_stdio = ARG_STDIO_AUTO;
else
arg_stdio = ARG_STDIO_DIRECT;
break;
case ARG_SHELL_PROMPT_PREFIX:
r = free_and_strdup_warn(&arg_shell_prompt_prefix, optarg);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
@ -913,7 +949,9 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
arg_wait = true;
arg_aggressive_gc = true;
arg_stdio = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
if (IN_SET(arg_stdio, ARG_STDIO_NONE, ARG_STDIO_AUTO))
arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
arg_expand_environment = false;
arg_send_sighup = true;
@ -993,6 +1031,25 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m");
}
if (!arg_shell_prompt_prefix) {
const char *e = secure_getenv("SYSTEMD_RUN_SHELL_PROMPT_PREFIX");
if (e) {
arg_shell_prompt_prefix = strdup(e);
if (!arg_shell_prompt_prefix)
return log_oom();
} else if (emoji_enabled()) {
arg_shell_prompt_prefix = strjoin(special_glyph(SPECIAL_GLYPH_SUPERHERO), " ");
if (!arg_shell_prompt_prefix)
return log_oom();
}
}
if (!isempty(arg_shell_prompt_prefix)) {
r = strv_env_assign(&arg_environment, "SHELL_PROMPT_PREFIX", arg_shell_prompt_prefix);
if (r < 0)
return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m");
}
return 1;
}
@ -1181,7 +1238,7 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
if (r < 0)
return bus_log_create_error(r);
send_term = isatty_safe(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO);
send_term = isatty_safe(STDIN_FILENO) || isatty_safe(STDOUT_FILENO) || isatty_safe(STDERR_FILENO);
}
if (send_term) {
@ -1345,78 +1402,75 @@ static int transient_timer_set_properties(sd_bus_message *m) {
return 0;
}
static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
unsigned soft_reboots_count = 0;
const char *unique, *id;
char *p;
static int make_unit_name(UnitType t, char **ret) {
int r;
assert(bus);
assert(t >= 0);
assert(t < _UNIT_TYPE_MAX);
assert(ret);
r = sd_bus_get_unique_name(bus, &unique);
/* Preferably use our PID + pidfd ID as identifier, if available. It's a boot time unique identifier
* managed by the kernel. Unfortunately only new kernels support this, hence we keep some fallback
* logic in place. */
_cleanup_(pidref_done) PidRef self = PIDREF_NULL;
r = pidref_set_self(&self);
if (r < 0)
return log_error_errno(r, "Failed to get reference to my own process: %m");
r = pidref_acquire_pidfd_id(&self);
if (r < 0) {
log_debug_errno(r, "Failed to acquire pidfd ID of myself, defaulting to randomized unit name: %m");
/* We couldn't get the pidfd id. In that case, just pick a random uuid as name */
sd_id128_t rnd;
/* We couldn't get the unique name, which is a pretty
* common case if we are connected to systemd
* directly. In that case, just pick a random uuid as
* name */
r = sd_id128_randomize(&rnd);
if (r < 0)
return log_error_errno(r, "Failed to generate random run unit name: %m");
if (asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t)) < 0)
r = asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t));
} else
r = asprintf(ret, "run-p" PID_FMT "-i%" PRIu64 ".%s", self.pid, self.fd_id, unit_type_to_string(t));
if (r < 0)
return log_oom();
return 0;
}
}
/* We managed to get the unique name, then let's use that to name our transient units. */
static int connect_bus(sd_bus **ret) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
id = startswith(unique, ":1."); /* let' strip the usual prefix */
if (!id)
id = startswith(unique, ":"); /* the spec only requires things to start with a colon, hence
* let's add a generic fallback for that. */
if (!id)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unique name %s has unexpected format.",
unique);
assert(ret);
/* The unique D-Bus names are actually unique per D-Bus instance, so on soft-reboot they will wrap
* and start over since the D-Bus broker is restarted. If there's a failed unit left behind that
* hasn't been garbage collected, we'll conflict. Append the soft-reboot counter to avoid clashing. */
if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = bus_get_property_trivial(
bus, bus_systemd_mgr, "SoftRebootsCount", &error, 'u', &soft_reboots_count);
/* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the
* limited direct connection */
if (arg_wait ||
arg_stdio != ARG_STDIO_NONE ||
(arg_runtime_scope == RUNTIME_SCOPE_USER && !IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)))
r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
else
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);
if (r < 0)
log_debug_errno(r,
"Failed to get SoftRebootsCount property, ignoring: %s",
bus_error_message(&error, r));
}
return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
if (soft_reboots_count > 0) {
if (asprintf(&p, "run-u%s-s%u.%s", id, soft_reboots_count, unit_type_to_string(t)) < 0)
return log_oom();
} else {
p = strjoin("run-u", id, ".", unit_type_to_string(t));
if (!p)
return log_oom();
}
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
*ret = p;
*ret = TAKE_PTR(bus);
return 0;
}
typedef struct RunContext {
sd_bus *bus;
sd_event *event;
PTYForward *forward;
sd_bus_slot *match;
char *service;
char *bus_path;
/* Bus objects */
sd_bus *bus;
sd_bus_slot *match_properties_changed;
sd_bus_slot *match_disconnected;
sd_event_source *retry_timer;
/* Current state of the unit */
char *active_state;
@ -1437,16 +1491,72 @@ typedef struct RunContext {
uint32_t exit_status;
} RunContext;
static void run_context_free(RunContext *c) {
static int run_context_update(RunContext *c);
static int run_context_attach_bus(RunContext *c, sd_bus *bus);
static void run_context_detach_bus(RunContext *c);
static int run_context_reconnect(RunContext *c);
static void run_context_done(RunContext *c) {
assert(c);
run_context_detach_bus(c);
c->retry_timer = sd_event_source_disable_unref(c->retry_timer);
c->forward = pty_forward_free(c->forward);
c->match = sd_bus_slot_unref(c->match);
c->bus = sd_bus_unref(c->bus);
c->event = sd_event_unref(c->event);
free(c->active_state);
free(c->result);
free(c->bus_path);
free(c->service);
}
static int on_retry_timer(sd_event_source *s, uint64_t usec, void *userdata) {
RunContext *c = ASSERT_PTR(userdata);
c->retry_timer = sd_event_source_disable_unref(c->retry_timer);
return run_context_reconnect(c);
}
static int run_context_reconnect(RunContext *c) {
int r;
assert(c);
run_context_detach_bus(c);
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
r = connect_bus(&bus);
if (r < 0) {
log_warning_errno(r, "Failed to reconnect, retrying in 2s: %m");
r = event_reset_time_relative(
c->event,
&c->retry_timer,
CLOCK_MONOTONIC,
2 * USEC_PER_SEC, /* accuracy= */ 0,
on_retry_timer, c,
SD_EVENT_PRIORITY_NORMAL,
"retry-timeout",
/* force_reset= */ false);
if (r < 0) {
(void) sd_event_exit(c->event, EXIT_FAILURE);
return log_error_errno(r, "Failed to install retry timer: %m");
}
return 0;
}
r = run_context_attach_bus(c, bus);
if (r < 0) {
(void) sd_event_exit(c->event, EXIT_FAILURE);
return r;
}
log_info("Reconnected to bus.");
return run_context_update(c);
}
static void run_context_check_done(RunContext *c) {
@ -1454,16 +1564,13 @@ static void run_context_check_done(RunContext *c) {
assert(c);
if (c->match)
done = STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job;
else
done = true;
if (c->forward && !pty_forward_is_done(c->forward) && done) /* If the service is gone, it's time to drain the output */
done = pty_forward_drain(c->forward);
if (done)
sd_event_exit(c->event, EXIT_SUCCESS);
(void) sd_event_exit(c->event, EXIT_SUCCESS);
}
static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
@ -1480,7 +1587,7 @@ static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_er
return 0;
}
static int run_context_update(RunContext *c, const char *path) {
static int run_context_update(RunContext *c) {
static const struct bus_properties_map map[] = {
{ "ActiveState", "s", NULL, offsetof(RunContext, active_state) },
@ -1503,16 +1610,35 @@ static int run_context_update(RunContext *c, const char *path) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
r = bus_map_all_properties(c->bus,
assert(c);
assert(c->bus);
r = bus_map_all_properties(
c->bus,
"org.freedesktop.systemd1",
path,
c->bus_path,
map,
BUS_MAP_STRDUP,
&error,
NULL,
c);
if (r < 0) {
sd_event_exit(c->event, EXIT_FAILURE);
/* If this is a connection error, then try to reconnect. This might be because the service
* manager is being restarted. Handle this gracefully. */
if (sd_bus_error_has_names(
&error,
SD_BUS_ERROR_NO_REPLY,
SD_BUS_ERROR_DISCONNECTED,
SD_BUS_ERROR_TIMED_OUT,
SD_BUS_ERROR_SERVICE_UNKNOWN,
SD_BUS_ERROR_NAME_HAS_NO_OWNER)) {
log_info("Bus call failed due to connection problems. Trying to reconnect...");
/* Not propagating error, because we handled it already, by reconnecting. */
return run_context_reconnect(c);
}
(void) sd_event_exit(c->event, EXIT_FAILURE);
return log_error_errno(r, "Failed to query unit state: %s", bus_error_message(&error, r));
}
@ -1521,11 +1647,67 @@ static int run_context_update(RunContext *c, const char *path) {
}
static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
RunContext *c = ASSERT_PTR(userdata);
return run_context_update(ASSERT_PTR(userdata));
}
assert(m);
static int on_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
/* If our connection gets terminated, then try to reconnect. This might be because the service
* manager is being restarted. Handle this gracefully. */
log_info("Got disconnected from bus connection. Trying to reconnect...");
return run_context_reconnect(ASSERT_PTR(userdata));
}
return run_context_update(c, sd_bus_message_get_path(m));
static int run_context_attach_bus(RunContext *c, sd_bus *bus) {
int r;
assert(c);
assert(bus);
assert(!c->bus);
assert(!c->match_properties_changed);
assert(!c->match_disconnected);
c->bus = sd_bus_ref(bus);
r = sd_bus_match_signal_async(
c->bus,
&c->match_properties_changed,
"org.freedesktop.systemd1",
c->bus_path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
on_properties_changed, NULL, c);
if (r < 0)
return log_error_errno(r, "Failed to request PropertiesChanged signal match: %m");
r = sd_bus_match_signal_async(
bus,
&c->match_disconnected,
"org.freedesktop.DBus.Local",
/* path= */ NULL,
"org.freedesktop.DBus.Local",
"Disconnected",
on_disconnected, NULL, c);
if (r < 0)
return log_error_errno(r, "Failed to request Disconnected signal match: %m");
r = sd_bus_attach_event(c->bus, c->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
return 0;
}
static void run_context_detach_bus(RunContext *c) {
assert(c);
if (c->bus) {
(void) sd_bus_detach_event(c->bus);
c->bus = sd_bus_unref(c->bus);
}
c->match_properties_changed = sd_bus_slot_unref(c->match_properties_changed);
c->match_disconnected = sd_bus_slot_unref(c->match_disconnected);
}
static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
@ -1541,7 +1723,7 @@ static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
/* If --wait is specified, we'll only exit the pty forwarding, but will continue to wait
* for the service to end. If the user hits ^C we'll exit too. */
} else if (rcode < 0) {
sd_event_exit(c->event, EXIT_FAILURE);
(void) sd_event_exit(c->event, EXIT_FAILURE);
return log_error_errno(rcode, "Error on PTY forwarding logic: %m");
}
@ -1818,7 +2000,7 @@ static int start_transient_service(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
} else {
r = make_unit_name(bus, UNIT_SERVICE, &service);
r = make_unit_name(UNIT_SERVICE, &service);
if (r < 0)
return r;
}
@ -1860,7 +2042,7 @@ static int start_transient_service(sd_bus *bus) {
}
if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
_cleanup_(run_context_free) RunContext c = {
_cleanup_(run_context_done) RunContext c = {
.cpu_usage_nsec = NSEC_INFINITY,
.memory_peak = UINT64_MAX,
.memory_swap_peak = UINT64_MAX,
@ -1871,14 +2053,19 @@ static int start_transient_service(sd_bus *bus) {
.inactive_exit_usec = USEC_INFINITY,
.inactive_enter_usec = USEC_INFINITY,
};
_cleanup_free_ char *path = NULL;
c.bus = sd_bus_ref(bus);
r = sd_event_default(&c.event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
c.service = strdup(service);
if (!c.service)
return log_oom();
c.bus_path = unit_dbus_path_from_name(service);
if (!c.bus_path)
return log_oom();
if (master >= 0) {
(void) sd_event_set_signal_exit(c.event, true);
@ -1900,26 +2087,11 @@ static int start_transient_service(sd_bus *bus) {
set_window_title(c.forward);
}
path = unit_dbus_path_from_name(service);
if (!path)
return log_oom();
r = sd_bus_match_signal_async(
bus,
&c.match,
"org.freedesktop.systemd1",
path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
on_properties_changed, NULL, &c);
r = run_context_attach_bus(&c, bus);
if (r < 0)
return log_error_errno(r, "Failed to request properties changed signal match: %m");
return r;
r = sd_bus_attach_event(bus, c.event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
r = run_context_update(&c, path);
r = run_context_update(&c);
if (r < 0)
return r;
@ -2033,7 +2205,7 @@ static int start_transient_scope(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to mangle scope name: %m");
} else {
r = make_unit_name(bus, UNIT_SCOPE, &scope);
r = make_unit_name(UNIT_SCOPE, &scope);
if (r < 0)
return r;
}
@ -2332,7 +2504,7 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
break;
}
} else {
r = make_unit_name(bus, UNIT_SERVICE, &service);
r = make_unit_name(UNIT_SERVICE, &service);
if (r < 0)
return r;
@ -2411,16 +2583,30 @@ static int run(int argc, char* argv[]) {
}
if (!arg_description) {
char *t;
_cleanup_free_ char *t = NULL;
if (strv_isempty(arg_cmdline))
t = strdup(arg_unit);
else
else if (startswith(arg_cmdline[0], "-")) {
/* Drop the login shell marker from the command line when generating the description,
* in order to minimize user confusion. */
_cleanup_strv_free_ char **l = strv_copy(arg_cmdline);
if (!l)
return log_oom();
r = free_and_strdup_warn(l + 0, l[0] + 1);
if (r < 0)
return r;
t = quote_command_line(l, SHELL_ESCAPE_EMPTY);
} else
t = quote_command_line(arg_cmdline, SHELL_ESCAPE_EMPTY);
if (!t)
return log_oom();
free_and_replace(arg_description, t);
arg_description = strjoin("[", program_invocation_short_name, "] ", t);
if (!arg_description)
return log_oom();
}
/* For backward compatibility reasons env var expansion is disabled by default for scopes, and
@ -2435,18 +2621,9 @@ static int run(int argc, char* argv[]) {
" Use --expand-environment=yes/no to explicitly control it as needed.");
}
/* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the
* limited direct connection */
if (arg_wait ||
arg_stdio != ARG_STDIO_NONE ||
(arg_runtime_scope == RUNTIME_SCOPE_USER && !IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)))
r = bus_connect_transport(arg_transport, arg_host, arg_runtime_scope, &bus);
else
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);
r = connect_bus(&bus);
if (r < 0)
return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
(void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
return r;
if (arg_scope)
return start_transient_scope(bus);

View File

@ -1394,6 +1394,12 @@ static int boot_load_efi_entry_pointers(BootConfig *config, bool skip_efivars) {
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA))
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\", ignoring: %m");
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryFallback), &config->entry_fallback);
if (r == -ENOMEM)
return log_oom();
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA))
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryFallback\", ignoring: %m");
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntrySelected), &config->entry_selected);
if (r == -ENOMEM)
return log_oom();

View File

@ -71,6 +71,7 @@ typedef struct BootConfig {
char *entry_oneshot;
char *entry_default;
char *entry_fallback;
char *entry_selected;
BootEntry *entries;

View File

@ -82,7 +82,7 @@ TEST(keymaps) {
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
TEST(dump_special_glyphs) {
assert_cc(SPECIAL_GLYPH_GREEN_CIRCLE + 1 == _SPECIAL_GLYPH_MAX);
assert_cc(SPECIAL_GLYPH_SUPERHERO + 1 == _SPECIAL_GLYPH_MAX);
log_info("is_locale_utf8: %s", yes_no(is_locale_utf8()));
@ -133,6 +133,7 @@ TEST(dump_special_glyphs) {
dump_glyph(SPECIAL_GLYPH_YELLOW_CIRCLE);
dump_glyph(SPECIAL_GLYPH_BLUE_CIRCLE);
dump_glyph(SPECIAL_GLYPH_GREEN_CIRCLE);
dump_glyph(SPECIAL_GLYPH_SUPERHERO);
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -261,4 +261,17 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
assert_eq "$(run0 -D / pwd)" "/"
assert_eq "$(run0 --user=testuser pwd)" "/home/testuser"
assert_eq "$(run0 -D / --user=testuser pwd)" "/"
# Verify that all combinations of --pty/--pipe come to the sam results
assert_eq "$(run0 echo -n foo)" "foo"
assert_eq "$(run0 --pty echo -n foo)" "foo"
assert_eq "$(run0 --pipe echo -n foo)" "foo"
assert_eq "$(run0 --pipe --pty echo -n foo)" "foo"
# Validate when we invoke run0 without a tty, that depending on --pty it either allocates a tty or not
assert_neq "$(run0 --pty tty < /dev/null)" "not a tty"
assert_eq "$(run0 --pipe tty < /dev/null)" "not a tty"
fi
# Tests whether intermediate disconnects corrupt us (modified testcase from https://github.com/systemd/systemd/issues/27204)
assert_rc "37" systemd-run --unit=disconnecttest --wait --pipe --user -M testuser@.host bash -ec 'systemctl --user daemon-reexec; sleep 3; exit 37'

View File

@ -39,6 +39,15 @@ assert_eq() {(
fi
)}
assert_neq() {(
set +ex
if [[ "${1?}" = "${2?}" ]]; then
echo "FAIL: not expected: '$2' actual: '$1'" >&2
exit 1
fi
)}
assert_le() {(
set +ex