diff --git a/man/systemd-binfmt.service.xml b/man/systemd-binfmt.service.xml index 27e34195af..41a8247c5f 100644 --- a/man/systemd-binfmt.service.xml +++ b/man/systemd-binfmt.service.xml @@ -41,6 +41,14 @@ Options + + + + If passed, instead of registering configured binary formats in the kernel, the + reverse operation is executed: all currently registered binary formats are unregistered from the + kernel. + + diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c index 7ff844c78c..06aee22dc0 100644 --- a/src/binfmt/binfmt.c +++ b/src/binfmt/binfmt.c @@ -10,6 +10,7 @@ #include #include "alloc-util.h" +#include "binfmt-util.h" #include "conf-files.h" #include "def.h" #include "fd-util.h" @@ -24,6 +25,7 @@ static bool arg_cat_config = false; static PagerFlags arg_pager_flags = 0; +static bool arg_unregister = false; static int delete_rule(const char *rule) { _cleanup_free_ char *x = NULL, *fn = NULL; @@ -32,18 +34,17 @@ static int delete_rule(const char *rule) { assert(rule); assert(rule[0]); - x = strdup(rule); + e = strchrnul(rule + 1, rule[0]); + x = strndup(rule + 1, e - rule - 1); if (!x) return log_oom(); - e = strchrnul(x+1, x[0]); - *e = 0; - - if (!filename_is_valid(x + 1)) + if (!filename_is_valid(x) || + STR_IN_SET(x, "register", "status")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Rule file name '%s' is not valid, refusing.", x + 1); + "Rule file name '%s' is not valid, refusing.", x); - fn = path_join("/proc/sys/fs/binfmt_misc", x+1); + fn = path_join("/proc/sys/fs/binfmt_misc", x); if (!fn) return log_oom(); @@ -116,6 +117,7 @@ static int help(void) { " --version Show package version\n" " --cat-config Show configuration files\n" " --no-pager Do not pipe output into a pager\n" + " --unregister Unregister all existing entries\n" "\nSee the %s for details.\n" , program_invocation_short_name , link @@ -129,6 +131,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERSION = 0x100, ARG_CAT_CONFIG, ARG_NO_PAGER, + ARG_UNREGISTER, }; static const struct option options[] = { @@ -136,6 +139,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "cat-config", no_argument, NULL, ARG_CAT_CONFIG }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "unregister", no_argument, NULL, ARG_UNREGISTER }, {} }; @@ -162,6 +166,10 @@ static int parse_argv(int argc, char *argv[]) { arg_pager_flags |= PAGER_DISABLE; break; + case ARG_UNREGISTER: + arg_unregister = true; + break; + case '?': return -EINVAL; @@ -169,9 +177,9 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (arg_cat_config && argc > optind) + if ((arg_unregister || arg_cat_config) && argc > optind) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Positional arguments are not allowed with --cat-config"); + "Positional arguments are not allowed with --cat-config or --unregister"); return 1; } @@ -189,6 +197,9 @@ static int run(int argc, char *argv[]) { r = 0; + if (arg_unregister) + return disable_binfmt(); + if (argc > optind) { int i; diff --git a/src/shared/binfmt-util.c b/src/shared/binfmt-util.c new file mode 100644 index 0000000000..0229726b42 --- /dev/null +++ b/src/shared/binfmt-util.c @@ -0,0 +1,33 @@ +#include +#include +#include + +#include "binfmt-util.h" +#include "fileio.h" +#include "missing_magic.h" +#include "stat-util.h" + +int disable_binfmt(void) { + int r; + + /* Flush out all rules. This is important during shutdown to cover for rules using "F", since those + * might pin a file and thus block us from unmounting stuff cleanly. + * + * We are a bit careful here, since binfmt_misc might still be an autofs which we don't want to + * trigger. */ + + r = path_is_fs_type("/proc/sys/fs/binfmt_misc", BINFMTFS_MAGIC); + if (r == 0 || r == -ENOENT) { + log_debug("binfmt_misc is not mounted, not detaching entries."); + return 0; + } + if (r < 0) + return log_warning_errno(r, "Failed to determine whether binfmt_misc is mounted: %m"); + + r = write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_warning_errno(r, "Failed to unregister binfmt_misc entries: %m"); + + log_debug("Unregistered all remaining binfmt_misc entries."); + return 0; +} diff --git a/src/shared/binfmt-util.h b/src/shared/binfmt-util.h new file mode 100644 index 0000000000..70feaad314 --- /dev/null +++ b/src/shared/binfmt-util.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +int disable_binfmt(void); diff --git a/src/shared/meson.build b/src/shared/meson.build index 483148492c..e608ea8a1d 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -12,6 +12,8 @@ shared_sources = files(''' barrier.h base-filesystem.c base-filesystem.h + binfmt-util.c + binfmt-util.h bitmap.c bitmap.h blkid-util.h diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index f2ad23d055..be8ae75e04 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -16,6 +16,7 @@ #include "alloc-util.h" #include "async.h" +#include "binfmt-util.h" #include "cgroup-setup.h" #include "cgroup-util.h" #include "def.h" @@ -386,6 +387,7 @@ int main(int argc, char *argv[]) { sync_with_progress(); disable_coredumps(); + disable_binfmt(); log_info("Sending SIGTERM to remaining processes..."); broadcast_signal(SIGTERM, true, true, arg_timeout); diff --git a/units/systemd-binfmt.service.in b/units/systemd-binfmt.service.in index 0c0f26451b..e54e95e11d 100644 --- a/units/systemd-binfmt.service.in +++ b/units/systemd-binfmt.service.in @@ -28,4 +28,5 @@ ConditionDirectoryNotEmpty=|/run/binfmt.d Type=oneshot RemainAfterExit=yes ExecStart=@rootlibexecdir@/systemd-binfmt +ExecStop=@rootlibexecdir@/systemd-binfmt --unregister TimeoutSec=90s