From e472238525b5f34a57e1830dc9de82b55f0cbd99 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Fri, 11 Oct 2024 18:04:17 +0200 Subject: [PATCH 1/7] basic/stat-util: use xopenat() where appropriate --- src/basic/stat-util.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index f05e66d80d0..2181ee2df51 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -157,25 +157,9 @@ int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) struct dirent *buf; size_t m; - if (path) { - assert(dir_fd >= 0 || dir_fd == AT_FDCWD); - - fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (fd < 0) - return -errno; - } else if (dir_fd == AT_FDCWD) { - fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (fd < 0) - return -errno; - } else { - /* Note that DUPing is not enough, as the internal pointer would still be shared and moved - * getedents64(). */ - assert(dir_fd >= 0); - - fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (fd < 0) - return fd; - } + fd = xopenat(dir_fd, path, O_DIRECTORY|O_CLOEXEC); + if (fd < 0) + return fd; /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." + * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. If From 8e39ba3e5a73b7e84dc8766f9583ff43cb6a28eb Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Fri, 11 Oct 2024 17:53:34 +0200 Subject: [PATCH 2/7] shared/exec-util: minor rearrangement, drop unused EXEC_DIR_NONE --- src/shared/exec-util.c | 12 ++++++------ src/shared/exec-util.h | 25 ++++++++++++------------- src/test/test-exec-util.c | 4 +++- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c index 628e777da17..cdd05cfda2b 100644 --- a/src/shared/exec-util.c +++ b/src/shared/exec-util.c @@ -438,6 +438,12 @@ static int gather_environment_consume(int fd, void *arg) { return r; } +const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX] = { + gather_environment_generate, + gather_environment_collect, + gather_environment_consume, +}; + int exec_command_flags_from_strv(char * const *ex_opts, ExecCommandFlags *ret) { ExecCommandFlags flags = 0; @@ -478,12 +484,6 @@ int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ret) { return 0; } -const gather_stdout_callback_t gather_environment[] = { - gather_environment_generate, - gather_environment_collect, - gather_environment_consume, -}; - static const char* const exec_command_strings[] = { "ignore-failure", /* EXEC_COMMAND_IGNORE_FAILURE */ "privileged", /* EXEC_COMMAND_FULLY_PRIVILEGED */ diff --git a/src/shared/exec-util.h b/src/shared/exec-util.h index 4565ddbee08..7658085b258 100644 --- a/src/shared/exec-util.h +++ b/src/shared/exec-util.h @@ -14,8 +14,7 @@ enum { _STDOUT_CONSUME_MAX, }; -typedef enum { - EXEC_DIR_NONE = 0, /* No execdir flags */ +typedef enum ExecDirFlags { EXEC_DIR_PARALLEL = 1 << 0, /* Execute scripts in parallel, if possible */ EXEC_DIR_IGNORE_ERRORS = 1 << 1, /* Ignore non-zero exit status of scripts */ EXEC_DIR_SET_SYSTEMD_EXEC_PID = 1 << 2, /* Set $SYSTEMD_EXEC_PID environment variable */ @@ -23,15 +22,6 @@ typedef enum { EXEC_DIR_WARN_WORLD_WRITABLE = 1 << 4, /* Warn if world writable files are found */ } ExecDirFlags; -typedef enum ExecCommandFlags { - EXEC_COMMAND_IGNORE_FAILURE = 1 << 0, - EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1, - EXEC_COMMAND_NO_SETUID = 1 << 2, - EXEC_COMMAND_AMBIENT_MAGIC = 1 << 3, - EXEC_COMMAND_NO_ENV_EXPAND = 1 << 4, - _EXEC_COMMAND_FLAGS_INVALID = -EINVAL, -} ExecCommandFlags; - int execute_strv( const char *name, char* const* paths, @@ -52,11 +42,20 @@ int execute_directories( char *envp[], ExecDirFlags flags); +extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX]; + +typedef enum ExecCommandFlags { + EXEC_COMMAND_IGNORE_FAILURE = 1 << 0, + EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1, + EXEC_COMMAND_NO_SETUID = 1 << 2, + EXEC_COMMAND_AMBIENT_MAGIC = 1 << 3, + EXEC_COMMAND_NO_ENV_EXPAND = 1 << 4, + _EXEC_COMMAND_FLAGS_INVALID = -EINVAL, +} ExecCommandFlags; + int exec_command_flags_from_strv(char * const *ex_opts, ExecCommandFlags *ret); int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ret); -extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX]; - const char* exec_command_flags_to_string(ExecCommandFlags i); ExecCommandFlags exec_command_flags_from_string(const char *s); diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c index 301e02db0b8..8ccede252a5 100644 --- a/src/test/test-exec-util.c +++ b/src/test/test-exec-util.c @@ -402,7 +402,9 @@ TEST(error_catching) { if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) return; - r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL, EXEC_DIR_NONE); + r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, + /* callbacks = */ NULL, /* callback_args = */ NULL, + /* argv = */ NULL, /* envp = */ NULL, /* flags = */ 0); /* we should exit with the error code of the first script that failed */ assert_se(r == 42); From f5dc74de2e882c5cdacca7932efc01c2673f940d Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Mon, 14 Oct 2024 18:13:35 +0200 Subject: [PATCH 3/7] shared/exec-util: modernize execute_strv() and friends a bit do_spawn() is also called during execute_strv(), so rename "direxec" to "exec-inner". --- src/shared/exec-util.c | 88 +++++++++++++++++++++--------------------- src/shared/exec-util.h | 8 ++-- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c index cdd05cfda2b..8435c4f1180 100644 --- a/src/shared/exec-util.c +++ b/src/shared/exec-util.c @@ -50,13 +50,13 @@ static int do_spawn( assert(ret_pid); if (null_or_empty_path(path) > 0) { - log_debug("%s is empty (a mask).", path); + log_debug("%s is masked, skipping.", path); return 0; } pid_t pid; r = safe_fork_full( - "(direxec)", + "(exec-inner)", (const int[]) { STDIN_FILENO, stdout_fd < 0 ? STDOUT_FILENO : stdout_fd, STDERR_FILENO }, /* except_fds= */ NULL, /* n_except_fds= */ 0, FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REARRANGE_STDIO|FORK_CLOSE_ALL_FDS, @@ -89,11 +89,11 @@ static int do_spawn( } static int do_execute( - char* const* paths, + char * const *paths, const char *root, usec_t timeout, gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX], - void* const callback_args[_STDOUT_CONSUME_MAX], + void * const callback_args[_STDOUT_CONSUME_MAX], int output_fd, char *argv[], char *envp[], @@ -103,12 +103,15 @@ static int do_execute( bool parallel_execution; int r; - /* We fork this all off from a child process so that we can somewhat cleanly make - * use of SIGALRM to set a time limit. + /* We fork this all off from a child process so that we can somewhat cleanly make use of SIGALRM + * to set a time limit. * - * We attempt to perform parallel execution if configured by the user, however - * if `callbacks` is nonnull, execution must be serial. + * We attempt to perform parallel execution if configured by the user, however if `callbacks` is nonnull, + * execution must be serial. */ + + assert(!strv_isempty(paths)); + parallel_execution = FLAGS_SET(flags, EXEC_DIR_PARALLEL) && !callbacks; if (parallel_execution) { @@ -218,13 +221,12 @@ static int do_execute( while (!hashmap_isempty(pids)) { _cleanup_free_ char *t = NULL; pid_t pid; + void *p; - pid = PTR_TO_PID(hashmap_first_key(pids)); + t = ASSERT_PTR(hashmap_steal_first_key_and_value(pids, &p)); + pid = PTR_TO_PID(p); assert(pid > 0); - t = hashmap_remove(pids, PID_TO_PTR(pid)); - assert(t); - r = wait_for_terminate_and_check(t, pid, WAIT_LOG); if (r < 0) return r; @@ -237,11 +239,11 @@ static int do_execute( int execute_strv( const char *name, - char* const* paths, + char * const *paths, const char *root, usec_t timeout, gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX], - void* const callback_args[_STDOUT_CONSUME_MAX], + void * const callback_args[_STDOUT_CONSUME_MAX], char *argv[], char *envp[], ExecDirFlags flags) { @@ -257,10 +259,10 @@ int execute_strv( if (callbacks) { assert(name); - assert(callback_args); assert(callbacks[STDOUT_GENERATE]); assert(callbacks[STDOUT_COLLECT]); assert(callbacks[STDOUT_CONSUME]); + assert(callback_args); fd = open_serialization_fd(name); if (fd < 0) @@ -298,10 +300,10 @@ int execute_strv( } int execute_directories( - const char* const* directories, + const char * const *directories, usec_t timeout, gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX], - void* const callback_args[_STDOUT_CONSUME_MAX], + void * const callback_args[_STDOUT_CONSUME_MAX], char *argv[], char *envp[], ExecDirFlags flags) { @@ -310,7 +312,7 @@ int execute_directories( _cleanup_free_ char *name = NULL; int r; - assert(!strv_isempty((char**) directories)); + assert(!strv_isempty((char* const*) directories)); r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, directories); if (r < 0) @@ -327,7 +329,7 @@ int execute_directories( return log_error_errno(r, "Failed to extract file name from '%s': %m", directories[0]); } - return execute_strv(name, paths, NULL, timeout, callbacks, callback_args, argv, envp, flags); + return execute_strv(name, paths, /* root = */ NULL, timeout, callbacks, callback_args, argv, envp, flags); } static int gather_environment_generate(int fd, void *arg) { @@ -336,12 +338,14 @@ static int gather_environment_generate(int fd, void *arg) { _cleanup_strv_free_ char **new = NULL; int r; - /* Read a series of VAR=value assignments from fd, use them to update the list of - * variables in env. Also update the exported environment. + /* Read a series of VAR=value assignments from fd, use them to update the list of variables in env. + * Also update the exported environment. * * fd is always consumed, even on error. */ + assert(fd >= 0); + f = fdopen(fd, "r"); if (!f) { safe_close(fd); @@ -362,7 +366,7 @@ static int gather_environment_generate(int fd, void *arg) { if (r < 0) return r; - if (setenv(*x, *y, true) < 0) + if (setenv(*x, *y, /* overwrite = */ true) < 0) return -errno; } @@ -370,12 +374,14 @@ static int gather_environment_generate(int fd, void *arg) { } static int gather_environment_collect(int fd, void *arg) { - _cleanup_fclose_ FILE *f = NULL; char ***env = ASSERT_PTR(arg); + _cleanup_fclose_ FILE *f = NULL; int r; /* Write out a series of env=cescape(VAR=value) assignments to fd. */ + assert(fd >= 0); + f = fdopen(fd, "w"); if (!f) { safe_close(fd); @@ -394,12 +400,14 @@ static int gather_environment_collect(int fd, void *arg) { } static int gather_environment_consume(int fd, void *arg) { - _cleanup_fclose_ FILE *f = NULL; char ***env = ASSERT_PTR(arg); - int r = 0; + _cleanup_fclose_ FILE *f = NULL; + int r, ret = 0; /* Read a series of env=cescape(VAR=value) assignments from fd into env. */ + assert(fd >= 0); + f = fdopen(fd, "r"); if (!f) { safe_close(fd); @@ -409,33 +417,25 @@ static int gather_environment_consume(int fd, void *arg) { for (;;) { _cleanup_free_ char *line = NULL; const char *v; - int k; - k = read_line(f, LONG_LINE_MAX, &line); - if (k < 0) - return k; - if (k == 0) - break; + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + return ret; v = startswith(line, "env="); if (!v) { - log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line); - if (r == 0) - r = -EINVAL; - + RET_GATHER(ret, log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "Serialization line unexpectedly didn't start with \"env=\", ignoring: %s", + line)); continue; } - k = deserialize_environment(v, env); - if (k < 0) { - log_debug_errno(k, "Invalid serialization line \"%s\": %m", line); - - if (r == 0) - r = k; - } + r = deserialize_environment(v, env); + if (r < 0) + RET_GATHER(ret, log_debug_errno(r, "Failed to deserialize line \"%s\": %m", line)); } - - return r; } const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX] = { diff --git a/src/shared/exec-util.h b/src/shared/exec-util.h index 7658085b258..ca7d30d45e0 100644 --- a/src/shared/exec-util.h +++ b/src/shared/exec-util.h @@ -24,20 +24,20 @@ typedef enum ExecDirFlags { int execute_strv( const char *name, - char* const* paths, + char * const *paths, const char *root, usec_t timeout, gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX], - void* const callback_args[_STDOUT_CONSUME_MAX], + void * const callback_args[_STDOUT_CONSUME_MAX], char *argv[], char *envp[], ExecDirFlags flags); int execute_directories( - const char* const* directories, + const char * const *directories, usec_t timeout, gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX], - void* const callback_args[_STDOUT_CONSUME_MAX], + void * const callback_args[_STDOUT_CONSUME_MAX], char *argv[], char *envp[], ExecDirFlags flags); From de41622bf39cfa01c8d79e2898b15e8a9bbc89ab Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Fri, 11 Oct 2024 17:57:06 +0200 Subject: [PATCH 4/7] core/manager: minor cleanup for generator_path_any() and friends --- src/core/manager.c | 58 +++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/src/core/manager.c b/src/core/manager.c index 456ad46135b..373f4d66e7b 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -4007,30 +4007,26 @@ void manager_send_reloading(Manager *m) { m->ready_sent = false; } -static bool generator_path_any(const char* const* paths) { - bool found = false; +static bool generator_path_any(char * const *paths) { - /* Optimize by skipping the whole process by not creating output directories - * if no generators are found. */ - STRV_FOREACH(path, paths) - if (access(*path, F_OK) == 0) - found = true; - else if (errno != ENOENT) - log_warning_errno(errno, "Failed to open generator directory %s: %m", *path); + /* Optimize by skipping the whole process by not creating output directories if no generators are found. */ - return found; + STRV_FOREACH(i, paths) { + if (access(*i, F_OK) >= 0) + return true; + if (errno != ENOENT) + log_warning_errno(errno, "Failed to check if generator dir '%s' exists, assuming not: %m", *i); + } + + return false; } static int manager_run_environment_generators(Manager *m) { - char **tmp = NULL; /* this is only used in the forked process, no cleanup here */ _cleanup_strv_free_ char **paths = NULL; - void* args[] = { - [STDOUT_GENERATE] = &tmp, - [STDOUT_COLLECT] = &tmp, - [STDOUT_CONSUME] = &m->transient_environment, - }; int r; + assert(m); + if (MANAGER_IS_TEST_RUN(m) && !(m->test_run_flags & MANAGER_TEST_RUN_ENV_GENERATORS)) return 0; @@ -4038,9 +4034,16 @@ static int manager_run_environment_generators(Manager *m) { if (!paths) return log_oom(); - if (!generator_path_any((const char* const*) paths)) + if (!generator_path_any(paths)) return 0; + char **tmp = NULL; /* this is only used in the forked process, no cleanup here */ + void *args[_STDOUT_CONSUME_MAX] = { + [STDOUT_GENERATE] = &tmp, + [STDOUT_COLLECT] = &tmp, + [STDOUT_CONSUME] = &m->transient_environment, + }; + WITH_UMASK(0022) r = execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, m->transient_environment, @@ -4117,17 +4120,12 @@ static int build_generator_environment(Manager *m, char ***ret) { return 0; } -static int manager_execute_generators(Manager *m, char **paths, bool remount_ro) { +static int manager_execute_generators(Manager *m, char * const *paths, bool remount_ro) { _cleanup_strv_free_ char **ge = NULL; - const char *argv[] = { - NULL, /* Leave this empty, execute_directory() will fill something in */ - m->lookup_paths.generator, - m->lookup_paths.generator_early, - m->lookup_paths.generator_late, - NULL, - }; int r; + assert(m); + r = build_generator_environment(m, &ge); if (r < 0) return log_error_errno(r, "Failed to build generator environment: %m"); @@ -4144,6 +4142,14 @@ static int manager_execute_generators(Manager *m, char **paths, bool remount_ro) log_warning_errno(r, "Read-only bind remount failed, ignoring: %m"); } + const char *argv[] = { + NULL, /* Leave this empty, execute_directory() will fill something in */ + m->lookup_paths.generator, + m->lookup_paths.generator_early, + m->lookup_paths.generator_late, + NULL, + }; + BLOCK_WITH_UMASK(0022); return execute_directories( (const char* const*) paths, @@ -4168,7 +4174,7 @@ static int manager_run_generators(Manager *m) { if (!paths) return log_oom(); - if (!generator_path_any((const char* const*) paths)) + if (!generator_path_any(paths)) return 0; r = lookup_paths_mkdir_generator(&m->lookup_paths); From 86eb3a8fdd4d11ddf3155743b05d3b2a2044009d Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 28 Sep 2024 15:54:42 +0200 Subject: [PATCH 5/7] core/manager: pass soft-reboot count to generators soft-reboot allows switching into a different root/installation, i.e. potentially invalidate settings from kernel cmdline and such. Let's hence inform generators about soft-reboots. --- man/systemd.generator.xml | 9 +++++++++ src/core/manager.c | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/man/systemd.generator.xml b/man/systemd.generator.xml index ae4c2c5c0b3..1a9b5d16530 100644 --- a/man/systemd.generator.xml +++ b/man/systemd.generator.xml @@ -163,6 +163,15 @@ + + $SYSTEMD_SOFT_REBOOTS_COUNT + + If the system has soft-rebooted, this variable is set to the count of soft-reboots. + This environment variable is only set for system generators. + + + + $SYSTEMD_FIRST_BOOT diff --git a/src/core/manager.c b/src/core/manager.c index 373f4d66e7b..b9ea79a7ada 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -4081,6 +4081,12 @@ static int build_generator_environment(Manager *m, char ***ret) { if (r < 0) return r; + if (m->soft_reboots_count > 0) { + r = strv_env_assignf(&nl, "SYSTEMD_SOFT_REBOOTS_COUNT", "%u", m->soft_reboots_count); + if (r < 0) + return r; + } + if (m->first_boot >= 0) { r = strv_env_assign(&nl, "SYSTEMD_FIRST_BOOT", one_zero(m->first_boot)); if (r < 0) From 1cfe9737cf2973c9e3b27de79099ae897cea54c1 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Mon, 30 Sep 2024 21:21:53 +0200 Subject: [PATCH 6/7] gpt-auto: use RET_GATHER at one more place --- src/gpt-auto-generator/gpt-auto-generator.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index d69a16af813..6f0d7c877a0 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -955,7 +955,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat } static int run(const char *dest, const char *dest_early, const char *dest_late) { - int r, k; + int r; assert_se(arg_dest = dest_late); @@ -975,12 +975,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) if (arg_root_enabled) r = add_root_mount(); + else + r = 0; - if (!in_initrd()) { - k = add_mounts(); - if (r >= 0) - r = k; - } + if (!in_initrd()) + RET_GATHER(r, add_mounts()); return r; } From 70ae9dc4f66aa6d0ee4d487f65bc2fdf0027cc4b Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Mon, 14 Oct 2024 18:31:14 +0200 Subject: [PATCH 7/7] hibernate-resume-generator: don't initiate resume if soft-rebooted This is just paranoia, to ensure that we don't accidentally initiate resume if the initrd is entered through soft-reboot rather than the initial one for booting up. --- .../hibernate-resume-generator.c | 5 ++++ src/shared/generator.c | 23 +++++++++++++++++++ src/shared/generator.h | 2 ++ 3 files changed, 30 insertions(+) diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c index 01684283942..2b175342c14 100644 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ b/src/hibernate-resume/hibernate-resume-generator.c @@ -107,6 +107,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) return 0; } + if (generator_soft_rebooted()) { + log_debug("Running in an initrd entered through soft-reboot, not initiating resume."); + return 0; + } + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); diff --git a/src/shared/generator.c b/src/shared/generator.c index bff44cfc15d..b3e57770aa7 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -16,6 +16,7 @@ #include "macro.h" #include "mkdir-label.h" #include "mountpoint-util.h" +#include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "special.h" @@ -974,3 +975,25 @@ void log_setup_generator(void) { log_parse_environment(); log_open(); } + +bool generator_soft_rebooted(void) { + static int cached = -1; + int r; + + if (cached >= 0) + return cached; + + const char *e = secure_getenv("SYSTEMD_SOFT_REBOOTS_COUNT"); + if (!e) + return (cached = false); + + unsigned u; + + r = safe_atou(e, &u); + if (r < 0) { + log_debug_errno(r, "Failed to parse $SYSTEMD_SOFT_REBOOTS_COUNT, assuming the system hasn't soft-rebooted: %m"); + return (cached = false); + } + + return (cached = u > 0); +} diff --git a/src/shared/generator.h b/src/shared/generator.h index baf1dafca3f..f07d7d6b3ae 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -100,6 +100,8 @@ int generator_enable_remount_fs_service(const char *dir); void log_setup_generator(void); +bool generator_soft_rebooted(void); + /* Similar to DEFINE_MAIN_FUNCTION, but initializes logging and assigns positional arguments. */ #define DEFINE_MAIN_GENERATOR_FUNCTION(impl) \ _DEFINE_MAIN_FUNCTION( \