diff --git a/man/systemd-notify.xml b/man/systemd-notify.xml
index 0e0cc6cdd09..6c2c8a75a1a 100644
--- a/man/systemd-notify.xml
+++ b/man/systemd-notify.xml
@@ -240,6 +240,24 @@
+
+
+
+ Remove file descriptors from the service's file descriptor store whose names match
+ the specified case-sensitive shell-style glob. The argument is required, and an explicitly empty
+ argument is equivalent to * and removes all stored file descriptors. The pattern
+ should be quoted to prevent expansion by the invoking shell. For example,
+ --fdstore-wipe='connection-*' removes all descriptors whose names start with
+ connection-.
+
+ This is equivalent to sending FDSTOREWIPE= with the specified pattern
+ (or * when the argument is empty) via
+ sd_notify3.
+ This option may not be combined with .
+
+
+
+
diff --git a/shell-completion/bash/systemd-notify b/shell-completion/bash/systemd-notify
index 491a54d58b2..dfa7702e6cb 100644
--- a/shell-completion/bash/systemd-notify
+++ b/shell-completion/bash/systemd-notify
@@ -6,7 +6,7 @@ _systemd_notify() {
local cur prev words cword split
local i mode=notify do_exec=false after_separator=false parse_options=true
local OPTS='-h --help --version --ready --reloading --stopping --pid= --uid= --status=
- --booted --no-block --exec --fd= --fdname= --fork -q --quiet'
+ --booted --no-block --exec --fd= --fdname= --fdstore-wipe= --fork -q --quiet'
_init_completion -s || return
@@ -17,7 +17,7 @@ _systemd_notify() {
((i < cword)) && parse_options=false
continue
;;
- --uid|--status|--fd|--fdname)
+ --uid|--status|--fd|--fdname|--fdstore-wipe)
((i++))
continue
;;
@@ -64,7 +64,7 @@ _systemd_notify() {
_comp_compgen -- -u
return
;;
- --status|--fd|--fdname)
+ --status|--fd|--fdname|--fdstore-wipe)
return
;;
esac
diff --git a/shell-completion/zsh/_systemd b/shell-completion/zsh/_systemd
index f555abd06b5..2a610d08837 100644
--- a/shell-completion/zsh/_systemd
+++ b/shell-completion/zsh/_systemd
@@ -64,6 +64,7 @@ case "$service" in
'--ready[Inform the init system about service start-up completion.]' \
'--pid=[Inform the init system about the main PID of the daemon]:daemon main PID:_pids' \
'--status=[Send a free-form status string for the daemon to the init systemd]:status string:' \
+ '--fdstore-wipe=[Remove stored file descriptors by name glob]:glob:' \
'--booted[Returns 0 if the system was booted up with systemd]'
;;
systemd-tty-ask-password-agent)
diff --git a/src/notify/notify.c b/src/notify/notify.c
index 9b630ab3903..3f736fea445 100644
--- a/src/notify/notify.c
+++ b/src/notify/notify.c
@@ -47,6 +47,7 @@ static char **arg_env = NULL;
static char **arg_exec = NULL;
static FDSet *arg_fds = NULL;
static char *arg_fdname = NULL;
+static char *arg_fdstore_wipe = NULL;
static bool arg_quiet = false;
STATIC_DESTRUCTOR_REGISTER(arg_pid, pidref_done);
@@ -54,6 +55,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_env, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_exec, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_fds, fdset_freep);
STATIC_DESTRUCTOR_REGISTER(arg_fdname, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_fdstore_wipe, freep);
COMMAND(
"systemd-notify\0",
@@ -253,6 +255,19 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
break;
+ OPTION_LONG("fdstore-wipe", "GLOB", "Remove file descriptors from the store by name glob"): {
+ const char *pattern = isempty(opts.arg) ? "*" : opts.arg;
+
+ if (!fdname_glob_is_valid(pattern))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File descriptor name pattern invalid: %s", opts.arg);
+
+ r = free_and_strdup_warn(&arg_fdstore_wipe, pattern);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
OPTION_LONG("fork", NULL, "Receive notifications from child rather than sending them"):
arg_action = ACTION_FORK;
break;
@@ -265,7 +280,8 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
return introspect_cli(SD_JSON_FORMAT_OFF);
}
- bool have_env = arg_ready || arg_stopping || arg_reloading || arg_status || pidref_is_set(&arg_pid) || !fdset_isempty(arg_fds);
+ bool have_env = arg_ready || arg_stopping || arg_reloading || arg_status || pidref_is_set(&arg_pid) ||
+ !fdset_isempty(arg_fds) || arg_fdstore_wipe;
char **args = option_parser_get_args(&opts);
@@ -274,6 +290,8 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
case ACTION_NOTIFY: {
if (arg_fdname && fdset_isempty(arg_fds))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No file descriptors passed, but --fdname= used, refusing.");
+ if (arg_fdstore_wipe && !fdset_isempty(arg_fds))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--fdstore-wipe= may not be combined with --fd=, refusing.");
size_t n_arg_env;
@@ -333,7 +351,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
if (have_env && arg_action != ACTION_NOTIFY)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "--ready, --reloading, --stopping, --pid=, --status=, --fd= may not be combined with --fork or --booted, refusing.");
+ "--ready, --reloading, --stopping, --pid=, --status=, --fd=, --fdstore-wipe= may not be combined with --fork or --booted, refusing.");
*ret_args = args;
@@ -503,9 +521,9 @@ static int action_fork(char *const *_command) {
static int run(int argc, char* argv[]) {
_cleanup_free_ char *status = NULL, *main_pid = NULL, *main_pidfd_id = NULL, *msg = NULL,
- *monotonic_usec = NULL, *fdn = NULL;
+ *monotonic_usec = NULL, *fdn = NULL, *fdstore_wipe = NULL;
_cleanup_strv_free_ char **final_env = NULL;
- const char *our_env[10];
+ const char *our_env[11];
size_t i = 0;
char **args = NULL; /* unnecessary initialization to appease gcc */
int r;
@@ -571,6 +589,14 @@ static int run(int argc, char* argv[]) {
}
}
+ if (arg_fdstore_wipe) {
+ fdstore_wipe = strjoin("FDSTOREWIPE=", arg_fdstore_wipe);
+ if (!fdstore_wipe)
+ return log_oom();
+
+ our_env[i++] = fdstore_wipe;
+ }
+
if (!fdset_isempty(arg_fds)) {
our_env[i++] = "FDSTORE=1";