259 lines
11 KiB
Diff
259 lines
11 KiB
Diff
From 6e4a324574d5f1b2296799324dbdb54326078233 Mon Sep 17 00:00:00 2001
|
|
From: Lennart Poettering <lennart@poettering.net>
|
|
Date: Tue, 28 Mar 2023 11:17:44 +0200
|
|
Subject: [PATCH] notify: add support for sending fds with notification
|
|
messages
|
|
|
|
This exposes the fd passing we support via sd_pid_notify_with_fds() also
|
|
via the command line tool systemd-notify.
|
|
|
|
Conflict:Context Adaptation. fdset_new_fill adaptation.
|
|
Reference:https://github.com/systemd/systemd/commit/6e4a324574d5f1b2296799324dbdb54326078233
|
|
|
|
---
|
|
man/systemd-notify.xml | 23 +++++++++
|
|
src/notify/notify.c | 109 +++++++++++++++++++++++++++++++++++++++--
|
|
2 files changed, 128 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/man/systemd-notify.xml b/man/systemd-notify.xml
|
|
index 3fed92e..586d09e 100644
|
|
--- a/man/systemd-notify.xml
|
|
+++ b/man/systemd-notify.xml
|
|
@@ -143,6 +143,29 @@
|
|
this option set is prone to race conditions in all other cases.</para></listitem>
|
|
</varlistentry>
|
|
|
|
+ <varlistentry>
|
|
+ <term><option>--fd=</option></term>
|
|
+
|
|
+ <listitem><para>Send a file descriptor along with the notification message. This is useful when
|
|
+ invoked in services that have the <varname>FileDescriptorStoreMax=</varname> setting enabled, see
|
|
+ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
|
+ for details. The specified file descriptor must be passed to <command>systemd-notify</command> when
|
|
+ invoked. This option may be used multiple times to pass multiple file descriptors in a single
|
|
+ notification message.</para>
|
|
+
|
|
+ <para>To use this functionality from a <command>bash</command> shell, use an expression like the following:</para>
|
|
+ <programlisting>systemd-notify --fd=4 --fd=5 4</some/file 5</some/other/file</programlisting></listitem>
|
|
+ </varlistentry>
|
|
+
|
|
+ <varlistentry>
|
|
+ <term><option>--fdname=</option></term>
|
|
+
|
|
+ <listitem><para>Set a name to assign to the file descriptors passed via <option>--fd=</option> (see
|
|
+ above). This controls the <literal>FDNAME=</literal> field. This setting may only be specified once,
|
|
+ and applies to all file descriptors passed. Invoke this tool multiple times in case multiple file
|
|
+ descriptors with different file descriptor names shall be submitted.</para></listitem>
|
|
+ </varlistentry>
|
|
+
|
|
<xi:include href="standard-options.xml" xpointer="help" />
|
|
<xi:include href="standard-options.xml" xpointer="version" />
|
|
</variablelist>
|
|
diff --git a/src/notify/notify.c b/src/notify/notify.c
|
|
index 5afea7f..ad0ae89 100644
|
|
--- a/src/notify/notify.c
|
|
+++ b/src/notify/notify.c
|
|
@@ -10,6 +10,8 @@
|
|
|
|
#include "alloc-util.h"
|
|
#include "env-util.h"
|
|
+#include "fd-util.h"
|
|
+#include "fdset.h"
|
|
#include "format-util.h"
|
|
#include "log.h"
|
|
#include "main-func.h"
|
|
@@ -31,9 +33,13 @@ static gid_t arg_gid = GID_INVALID;
|
|
static bool arg_no_block = false;
|
|
static char **arg_env = NULL;
|
|
static char **arg_exec = NULL;
|
|
+static FDSet *arg_fds = NULL;
|
|
+static char *arg_fdname = NULL;
|
|
|
|
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 int help(void) {
|
|
_cleanup_free_ char *link = NULL;
|
|
@@ -55,6 +61,8 @@ static int help(void) {
|
|
" --booted Check if the system was booted up with systemd\n"
|
|
" --no-block Do not wait until operation finished\n"
|
|
" --exec Execute command line separated by ';' once done\n"
|
|
+ " --fd=FD Pass specified file descriptor with along with message\n"
|
|
+ " --fdname=NAME Name to assign to passed file descriptor(s)\n"
|
|
"\nSee the %s for details.\n",
|
|
program_invocation_short_name,
|
|
program_invocation_short_name,
|
|
@@ -96,6 +104,8 @@ static int parse_argv(int argc, char *argv[]) {
|
|
ARG_UID,
|
|
ARG_NO_BLOCK,
|
|
ARG_EXEC,
|
|
+ ARG_FD,
|
|
+ ARG_FDNAME,
|
|
};
|
|
|
|
static const struct option options[] = {
|
|
@@ -108,9 +118,12 @@ static int parse_argv(int argc, char *argv[]) {
|
|
{ "uid", required_argument, NULL, ARG_UID },
|
|
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
|
|
{ "exec", no_argument, NULL, ARG_EXEC },
|
|
+ { "fd", required_argument, NULL, ARG_FD },
|
|
+ { "fdname", required_argument, NULL, ARG_FDNAME },
|
|
{}
|
|
};
|
|
|
|
+ _cleanup_(fdset_freep) FDSet *passed = NULL;
|
|
bool do_exec = false;
|
|
int c, r, n_env;
|
|
|
|
@@ -181,6 +194,60 @@ static int parse_argv(int argc, char *argv[]) {
|
|
do_exec = true;
|
|
break;
|
|
|
|
+ case ARG_FD: {
|
|
+ _cleanup_close_ int owned_fd = -EBADF;
|
|
+ int fdnr;
|
|
+
|
|
+ r = safe_atoi(optarg, &fdnr);
|
|
+ if (r < 0)
|
|
+ return log_error_errno(r, "Failed to parse file descriptor: %s", optarg);
|
|
+ if (fdnr < 0)
|
|
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "File descriptor can't be negative: %i", fdnr);
|
|
+
|
|
+ if (!passed) {
|
|
+ /* Take possession of all passed fds */
|
|
+ r = fdset_new_fill(/* filter_cloexec= */ 0, &passed);
|
|
+ if (r < 0)
|
|
+ return log_error_errno(r, "Failed to take possession of passed file descriptors: %m");
|
|
+
|
|
+ r = fdset_cloexec(passed, true);
|
|
+ if (r < 0)
|
|
+ return log_error_errno(r, "Failed to enable O_CLOEXEC for passed file descriptors: %m");
|
|
+ }
|
|
+
|
|
+ if (fdnr < 3) {
|
|
+ /* For stdin/stdout/stderr we want to keep the fd, too, hence make a copy */
|
|
+ owned_fd = fcntl(fdnr, F_DUPFD_CLOEXEC, 3);
|
|
+ if (owned_fd < 0)
|
|
+ return log_error_errno(errno, "Failed to duplicate file descriptor: %m");
|
|
+ } else {
|
|
+ /* Otherwise, move the fd over */
|
|
+ owned_fd = fdset_remove(passed, fdnr);
|
|
+ if (owned_fd < 0)
|
|
+ return log_error_errno(owned_fd, "Specified file descriptor '%i' not passed or specified more than once: %m", fdnr);
|
|
+ }
|
|
+
|
|
+ if (!arg_fds) {
|
|
+ arg_fds = fdset_new();
|
|
+ if (!arg_fds)
|
|
+ return log_oom();
|
|
+ }
|
|
+
|
|
+ r = fdset_consume(arg_fds, TAKE_FD(owned_fd));
|
|
+ if (r < 0)
|
|
+ return log_error_errno(r, "Failed to add file descriptor to set: %m");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case ARG_FDNAME:
|
|
+ if (!fdname_is_valid(optarg))
|
|
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File descriptor name invalid: %s", optarg);
|
|
+
|
|
+ if (free_and_strdup(&arg_fdname, optarg) < 0)
|
|
+ return log_oom();
|
|
+
|
|
+ break;
|
|
+
|
|
case '?':
|
|
return -EINVAL;
|
|
|
|
@@ -193,11 +260,15 @@ static int parse_argv(int argc, char *argv[]) {
|
|
!arg_ready &&
|
|
!arg_status &&
|
|
!arg_pid &&
|
|
- !arg_booted) {
|
|
+ !arg_booted &&
|
|
+ fdset_isempty(arg_fds)) {
|
|
help();
|
|
return -EINVAL;
|
|
}
|
|
|
|
+ if (arg_fdname && fdset_isempty(arg_fds))
|
|
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No file descriptors passed, but --fdname= set, refusing.");
|
|
+
|
|
if (do_exec) {
|
|
int i;
|
|
|
|
@@ -224,13 +295,16 @@ static int parse_argv(int argc, char *argv[]) {
|
|
return log_oom();
|
|
}
|
|
|
|
+ if (!fdset_isempty(passed))
|
|
+ log_warning("Warning: %u more file descriptors passed than referenced with --fd=.", fdset_size(passed));
|
|
+
|
|
return 1;
|
|
}
|
|
|
|
static int run(int argc, char* argv[]) {
|
|
- _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL;
|
|
+ _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL, *fdn = NULL;
|
|
_cleanup_strv_free_ char **final_env = NULL;
|
|
- char* our_env[4];
|
|
+ char* our_env[6];
|
|
unsigned i = 0;
|
|
pid_t source_pid;
|
|
int r;
|
|
@@ -271,6 +345,18 @@ static int run(int argc, char* argv[]) {
|
|
our_env[i++] = cpid;
|
|
}
|
|
|
|
+ if (!fdset_isempty(arg_fds)) {
|
|
+ our_env[i++] = (char*) "FDSTORE=1";
|
|
+
|
|
+ if (arg_fdname) {
|
|
+ fdn = strjoin("FDNAME=", arg_fdname);
|
|
+ if (!fdn)
|
|
+ return log_oom();
|
|
+
|
|
+ our_env[i++] = fdn;
|
|
+ }
|
|
+ }
|
|
+
|
|
our_env[i++] = NULL;
|
|
|
|
final_env = strv_env_merge(our_env, arg_env);
|
|
@@ -307,13 +393,28 @@ static int run(int argc, char* argv[]) {
|
|
* or the service manager itself */
|
|
source_pid = 0;
|
|
}
|
|
- r = sd_pid_notify(source_pid, false, n);
|
|
+
|
|
+ if (fdset_isempty(arg_fds))
|
|
+ r = sd_pid_notify(source_pid, /* unset_environment= */ false, n);
|
|
+ else {
|
|
+ _cleanup_free_ int *a = NULL;
|
|
+ int k;
|
|
+
|
|
+ k = fdset_to_array(arg_fds, &a);
|
|
+ if (k < 0)
|
|
+ return log_error_errno(k, "Failed to convert file descriptor set to array: %m");
|
|
+
|
|
+ r = sd_pid_notify_with_fds(source_pid, /* unset_environment= */ false, n, a, k);
|
|
+
|
|
+ }
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to notify init system: %m");
|
|
if (r == 0)
|
|
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
|
"No status data could be sent: $NOTIFY_SOCKET was not set");
|
|
|
|
+ arg_fds = fdset_free(arg_fds); /* Close before we execute anything */
|
|
+
|
|
if (!arg_no_block) {
|
|
r = sd_notify_barrier(0, 5 * USEC_PER_SEC);
|
|
if (r < 0)
|
|
--
|
|
2.33.0
|
|
|