From: Zbigniew Jędrzejewski-Szmek Date: Thu, 21 May 2026 08:08:33 +0000 (+0200) Subject: manager: make systemd+executor a multicall binary X-Git-Url: http://git.ipfire.org/index.cgi?a=commitdiff_plain;p=thirdparty%2Fsystemd.git manager: make systemd+executor a multicall binary Allow systemd-executor to be compiled into a single binary. The existing -Dlink-executor-shared=true|false is extended to also allow -Dlink-executor-shared=single (*). The new mode is opt-in, to allow experimentation and introduce this smoothly. This saves a little space, but not as much as I expected: $ ls -l build/{systemd,systemd-executor} build-new/systemd -rwxr-xr-x 1 zbyszek zbyszek 631520 May 25 22:44 build/systemd -rwxr-xr-x 1 zbyszek zbyszek 670464 May 25 22:44 build/systemd-executor -rwxr-xr-x 1 zbyszek zbyszek 1214488 May 25 22:45 build-new/systemd (This is with -Dbuildtype=debugoptimized -Db_lto=true). The combined binary is slightly smaller than the sum of the separate ones, but not much. In both cases, the binaries are linked to libsystemd-core which is 10MB, so the size of the binaries themselves doesn't make much of a difference. The executor needs exec-invoke.c which is huge and not shared with anything else. Longer term, I want to allow systemd to be linked statically. In that case, having systemd-executor separate would be very painful. So the option to use a multicall binary will be necessary. Previously, we stored the resolved path to systemd-executor and used it argv[0]. I don't think this was useful. After all, normally we would use the non-resolved original path as argv[0]. So that part is dropped, and the resolved path is only logged, but "systemd-executor" is always used as argv[0]. This makes the multicall binary work reliably, no matter what the actual file name is. (*) This means that compat as the commandline level is maintained: 'meson setup build -Dlink-executor-shared=true …' works as before. Unfortunately, when using an existing build directory, meson chokes on the type change and refuses to reconfigure the directory or change the option or do anything useful. I think meson is DTWT here, but this is hard to fix. So the build directory probably needs to be recreated. --- diff --git a/meson.build b/meson.build index f03032aa2ec..f953eeab13f 100644 --- a/meson.build +++ b/meson.build @@ -78,6 +78,9 @@ conf.set10('FUZZ_USE_SIZE_LIMIT', fuzzer_build) # We'll set this to '1' for EFI builds in a different place. conf.set10('SD_BOOT', false) +link_executor_shared = get_option('link-executor-shared') +conf.set10('BUILD_EXECUTOR_SINGLE', link_executor_shared == 'single') + # Create a title-less summary section early, so it ends up first in the output. # More items are added later after they have been detected. summary({ diff --git a/meson_options.txt b/meson_options.txt index b2fddf8a34a..701272d13a0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -15,8 +15,8 @@ option('split-bin', type : 'combo', choices : ['auto', 'true', 'false'], description : 'sbin is not a symlink to bin') option('link-udev-shared', type : 'boolean', description : 'link systemd-udevd and its helpers to libsystemd-shared.so') -option('link-executor-shared', type : 'boolean', - description : 'link systemd-executor to libsystemd-shared.so and libsystemd-core.so') +option('link-executor-shared', type : 'combo', choices : ['true', 'false', 'single'], + description : 'link systemd-executor to libsystemd-shared.so and libsystemd-core.so, or into systemd') option('link-systemctl-shared', type: 'boolean', description : 'link systemctl against libsystemd-shared.so') option('link-networkd-shared', type: 'boolean', diff --git a/src/core/execute.c b/src/core/execute.c index 4a95473e46f..c2e8bc82b7d 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -479,7 +479,6 @@ int exec_spawn( assert(unit); assert(unit->manager); assert(unit->manager->executor_fd >= 0); - assert(unit->manager->executor_path); assert(command); assert(context); assert(params); @@ -580,7 +579,7 @@ int exec_spawn( /* The executor binary is pinned, to avoid compatibility problems during upgrades. */ r = posix_spawn_wrapper( FORMAT_PROC_FD_PATH(unit->manager->executor_fd), - STRV_MAKE(unit->manager->executor_path, + STRV_MAKE("systemd-executor", "--deserialize", serialization_fd_number, "--log-level", max_log_levels, "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))), diff --git a/src/core/executor.c b/src/core/executor.c index 00761c6e3f7..b227584789b 100644 --- a/src/core/executor.c +++ b/src/core/executor.c @@ -13,6 +13,7 @@ #include "exec-invoke.h" #include "execute.h" #include "execute-serialize.h" +#include "executor.h" #include "exit-status.h" #include "fd-util.h" #include "fdset.h" @@ -38,7 +39,7 @@ static int help(void) { if (r < 0) return log_oom(); - r = option_parser_get_help_table(&options); + r = option_parser_get_help_table_ns("systemd-executor", &options); if (r < 0) return r; @@ -62,11 +63,13 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - OptionParser opts = { argc, argv }; + OptionParser opts = { argc, argv, .namespace = "systemd-executor" }; FOREACH_OPTION_OR_RETURN(c, &opts) switch (c) { + OPTION_NAMESPACE("systemd-executor"): {} + OPTION_COMMON_HELP: return help(); @@ -226,7 +229,7 @@ static int run(int argc, char *argv[]) { return exit_status; } -int main(int argc, char *argv[]) { +int run_executor(int argc, char *argv[]) { int r; /* We use safe_fork() for spawning sd-pam helper process, which internally calls rename_process(). @@ -241,3 +244,7 @@ int main(int argc, char *argv[]) { return r < 0 ? EXIT_FAILURE : r; } + +#if !BUILD_EXECUTOR_SINGLE +_alias_(run_executor) main; +#endif diff --git a/src/core/executor.h b/src/core/executor.h new file mode 100644 index 00000000000..75d2abe0e54 --- /dev/null +++ b/src/core/executor.h @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +int run_executor(int argc, char *argv[]); diff --git a/src/core/main.c b/src/core/main.c index de0916476fa..6c83953f30e 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -45,6 +45,7 @@ #include "emergency-action.h" #include "env-util.h" #include "escape.h" +#include "executor.h" #include "extract-word.h" #include "fd-util.h" #include "fdset.h" @@ -1038,13 +1039,19 @@ static int parse_argv(int argc, char *argv[]) { assert(argv); int log_level_shift = getpid_cached() == 1 ? LOG_DEBUG - LOG_ERR : 0; - OptionParser opts = { argc, argv, .log_level_shift = log_level_shift }; + OptionParser opts = { + argc, argv, + .namespace = "systemd", + .log_level_shift = log_level_shift, + }; /* Note: when new options are added here, also add them to the exclusion list in proc-cmdline.c! */ FOREACH_OPTION(c, &opts) switch (c) { + OPTION_NAMESPACE("systemd"): {} + OPTION_COMMON_HELP: arg_action = ACTION_HELP; break; @@ -1279,7 +1286,7 @@ static int help(void) { _cleanup_(table_unrefp) Table *options = NULL; int r; - r = option_parser_get_help_table(&options); + r = option_parser_get_help_table_ns("systemd", &options); if (r < 0) return r; @@ -3430,7 +3437,7 @@ static int save_env(void) { return 0; } -int main(int argc, char *argv[]) { +static int run_systemd(int argc, char *argv[]) { dual_timestamp initrd_timestamp = DUAL_TIMESTAMP_NULL, userspace_timestamp = DUAL_TIMESTAMP_NULL, @@ -3911,3 +3918,11 @@ finish: reset_arguments(); return retval; } + +int main(int argc, char *argv[]) { +#if BUILD_EXECUTOR_SINGLE + if (invoked_as(argv, "executor")) + return run_executor(argc, argv); +#endif + return run_systemd(argc, argv); +} diff --git a/src/core/manager.c b/src/core/manager.c index 1ee2228c899..d89b1eae0dc 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -895,6 +895,30 @@ usec_t manager_default_timeout(RuntimeScope scope) { return scope == RUNTIME_SCOPE_SYSTEM ? DEFAULT_TIMEOUT_USEC : DEFAULT_USER_TIMEOUT_USEC; } +static int pin_executor_binary(int *ret_fd) { + _cleanup_free_ char *path = NULL; + + assert(ret_fd); + +#if BUILD_EXECUTOR_SINGLE + int r; + + r = open_and_check_executable("/proc/self/exe", /* root= */ NULL, &path, ret_fd); + if (r < 0) + return log_debug_errno(r, "Failed to pin executor binary %s: %m", "/proc/self/exe"); +#else + int fd; + + fd = pin_callout_binary(SYSTEMD_EXECUTOR_BINARY_PATH, &path); + if (fd < 0) + return log_debug_errno(fd, "Failed to pin executor binary %s: %m", SYSTEMD_EXECUTOR_BINARY_PATH); + *ret_fd = fd; +#endif + + log_debug("Using systemd-executor binary %s.", path); + return 0; +} + int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags, Manager **ret) { _cleanup_(manager_freep) Manager *m = NULL; int r; @@ -1057,11 +1081,9 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags, } if (!FLAGS_SET(test_run_flags, MANAGER_TEST_DONT_OPEN_EXECUTOR)) { - m->executor_fd = pin_callout_binary(SYSTEMD_EXECUTOR_BINARY_PATH, &m->executor_path); - if (m->executor_fd < 0) - return log_debug_errno(m->executor_fd, "Failed to pin executor binary: %m"); - - log_debug("Using systemd-executor binary from '%s'.", m->executor_path); + r = pin_executor_binary(&m->executor_fd); + if (r < 0) + return r; } /* Note that we do not set up the notify fd here. We do that after deserialization, @@ -1797,7 +1819,6 @@ Manager* manager_free(Manager *m) { safe_close(m->restrict_fsaccess_bss_map_fd); safe_close(m->executor_fd); - free(m->executor_path); return mfree(m); } diff --git a/src/core/manager.h b/src/core/manager.h index e655e168c60..304124de049 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -505,7 +505,6 @@ typedef struct Manager { /* Pin the systemd-executor binary, so that it never changes until re-exec, ensuring we don't have * serialization/deserialization compatibility issues during upgrades. */ - char *executor_path; int executor_fd; unsigned soft_reboots_count; diff --git a/src/core/meson.build b/src/core/meson.build index af50a2a4fa7..6e68a59d204 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -189,17 +189,38 @@ libcore = shared_library( install : true, install_dir : pkglibdir) -executor_libs = get_option('link-executor-shared') ? \ - [ - libcore, - libshared, - ] : [ - libc_wrapper_static, - libcore_static, - libshared_static, - libbasic_static, - libsystemd_static, +core_libs_static = [ + libc_wrapper_static, + libcore_static, + libshared_static, + libbasic_static, + libsystemd_static, +] +core_libs_shared = [ + libcore, + libshared, +] + +systemd_deps = [ + libapparmor_cflags, + libkmod_cflags, + libmount_cflags, + libseccomp_cflags, + libselinux_cflags, +] + +link_executor_shared = get_option('link-executor-shared') + +executor_libs = link_executor_shared == 'true' ? core_libs_shared : core_libs_static + +if link_executor_shared == 'single' + systemd_sources += systemd_executor_sources + systemd_deps += [ + libbpf_cflags, + libcryptsetup_cflags, + libpam_cflags, ] +endif executables += [ libexec_template + { @@ -207,33 +228,10 @@ executables += [ 'dbus' : true, 'public' : true, 'sources' : systemd_sources, - 'link_with' : [ - libcore, - libshared, - ], - 'dependencies' : [ - libapparmor_cflags, - libkmod_cflags, - libmount_cflags, - libseccomp_cflags, - libselinux_cflags, - ], - }, - libexec_template + { - 'name' : 'systemd-executor', - 'public' : true, - 'sources' : systemd_executor_sources, - 'link_with' : executor_libs, - 'dependencies' : [ - libapparmor_cflags, - libbpf_cflags, - libcryptsetup_cflags, - libmount_cflags, - libpam_cflags, - libseccomp_cflags, - libselinux_cflags, - ], + 'link_with' : core_libs_shared, + 'dependencies' : systemd_deps, }, + fuzz_template + { 'sources' : files('fuzz-unit-file.c'), 'link_with' : [ @@ -258,6 +256,31 @@ executables += [ }, ] +if link_executor_shared == 'single' + # Symlink for external callers + install_symlink('systemd-executor', + pointing_to : 'systemd', + install_dir : libexecdir) +else + executables += [ + libexec_template + { + 'name' : 'systemd-executor', + 'public' : true, + 'sources' : systemd_executor_sources, + 'link_with' : executor_libs, + 'dependencies' : [ + libapparmor_cflags, + libbpf_cflags, + libcryptsetup_cflags, + libmount_cflags, + libpam_cflags, + libseccomp_cflags, + libselinux_cflags, + ], + }, + ] +endif + in_files = [['system.conf', pkgconfigfiledir], ['user.conf', pkgconfigfiledir], ['org.freedesktop.systemd1.policy', polkitpolicydir]]