From 9dcd43b14927fe6f9182a252213dbf182d9d8e01 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 30 Apr 2020 11:59:30 +0200 Subject: [PATCH] notify: beef up --pid= logic Prompted by the discussions on #15547. --- man/systemd-notify.xml | 13 +++++----- src/notify/notify.c | 55 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/man/systemd-notify.xml b/man/systemd-notify.xml index 89060680f5efa..4560074505020 100644 --- a/man/systemd-notify.xml +++ b/man/systemd-notify.xml @@ -84,12 +84,13 @@ - Inform the init system about the main PID of - the daemon. Takes a PID as argument. If the argument is - omitted, the PID of the process that invoked - systemd-notify is used. This is equivalent - to systemd-notify MAINPID=$PID. For details - about the semantics of this option see + Inform the service manager about the main PID of the daemon. Takes a PID as + argument. If the argument is specified as auto or omitted, the PID of the process + that invoked systemd-notify is used, except if that's the service manager. If the + argument is specified as self, the PID of the systemd-notify + command itself is used, and if parent is specified the calling process' PID is + used — even if it is the service manager. This is equivalent to systemd-notify + MAINPID=$PID. For details about the semantics of this option see sd_notify3. diff --git a/src/notify/notify.c b/src/notify/notify.c index a76f3376951f8..6c2dedd53fd6f 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -54,6 +54,26 @@ static int help(void) { return 0; } +static pid_t manager_pid(void) { + const char *e; + pid_t pid; + int r; + + /* If we run as a service managed by systemd --user the $MANAGERPID environment variable points to + * the service manager's PID. */ + e = getenv("MANAGERPID"); + if (!e) + return 0; + + r = parse_pid(e, &pid); + if (r < 0) { + log_warning_errno(r, "$MANAGERPID is set to an invalid PID, ignoring: %s", e); + return 0; + } + + return pid; +} + static int parse_argv(int argc, char *argv[]) { enum { @@ -96,13 +116,24 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_PID: + if (isempty(optarg) || streq(optarg, "auto")) { + arg_pid = getppid(); - if (optarg) { - if (parse_pid(optarg, &arg_pid) < 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Failed to parse PID %s.", optarg); - } else + if (arg_pid <= 1 || + arg_pid == manager_pid()) /* Don't send from PID 1 or the service + * manager's PID (which might be distinct from + * 1, if we are a --user instance), that'd just + * be confusing for the service manager */ + arg_pid = getpid(); + } else if (streq(optarg, "parent")) arg_pid = getppid(); + else if (streq(optarg, "self")) + arg_pid = getpid(); + else { + r = parse_pid(optarg, &arg_pid); + if (r < 0) + return log_error_errno(r, "Failed to parse PID %s.", optarg); + } break; @@ -151,6 +182,7 @@ static int run(int argc, char* argv[]) { _cleanup_strv_free_ char **final_env = NULL; char* our_env[4]; unsigned i = 0; + pid_t source_pid; int r; log_show_color(true); @@ -207,7 +239,18 @@ static int run(int argc, char* argv[]) { setreuid(arg_uid, (uid_t) -1) < 0) return log_error_errno(errno, "Failed to change UID: %m"); - r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n); + if (arg_pid > 0) + source_pid = arg_pid; + else { + /* Pretend the message originates from our parent, given that we are typically called from a + * shell script, i.e. we are not the main process of a service but only a child of it. */ + source_pid = getppid(); + if (source_pid <= 1 || + source_pid == manager_pid()) /* safety check: don't claim we'd send anything from PID 1 + * or the service manager itself */ + source_pid = 0; + } + r = sd_pid_notify(source_pid, false, n); if (r < 0) return log_error_errno(r, "Failed to notify init system: %m"); if (r == 0)