Merge pull request #34755 from YHNdnzj/soft-reboot-generator-cmdline

core/manager: pass soft-reboot count to generators
This commit is contained in:
Yu Watanabe
2024-10-15 10:48:31 +09:00
committed by GitHub
10 changed files with 161 additions and 126 deletions

View File

@@ -163,6 +163,15 @@
<xi:include href="version-info.xml" xpointer="v251"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>$SYSTEMD_SOFT_REBOOTS_COUNT</varname></term>
<listitem><para>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.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>$SYSTEMD_FIRST_BOOT</varname></term>

View File

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

View File

@@ -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,
@@ -4078,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)
@@ -4117,17 +4126,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 +4148,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 +4180,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);

View File

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

View File

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

View File

@@ -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,35 +417,33 @@ 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] = {
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 */

View File

@@ -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,6 +22,28 @@ typedef enum {
EXEC_DIR_WARN_WORLD_WRITABLE = 1 << 4, /* Warn if world writable files are found */
} ExecDirFlags;
int execute_strv(
const char *name,
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],
char *argv[],
char *envp[],
ExecDirFlags flags);
int execute_directories(
const char * const *directories,
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void * const callback_args[_STDOUT_CONSUME_MAX],
char *argv[],
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,
@@ -32,31 +53,9 @@ typedef enum ExecCommandFlags {
_EXEC_COMMAND_FLAGS_INVALID = -EINVAL,
} ExecCommandFlags;
int execute_strv(
const char *name,
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],
char *argv[],
char *envp[],
ExecDirFlags flags);
int execute_directories(
const char* const* directories,
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void* const callback_args[_STDOUT_CONSUME_MAX],
char *argv[],
char *envp[],
ExecDirFlags flags);
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);

View File

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

View File

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

View File

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