]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
notify: beef up --pid= logic
authorLennart Poettering <lennart@poettering.net>
Thu, 30 Apr 2020 09:59:30 +0000 (11:59 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 30 Apr 2020 16:36:05 +0000 (18:36 +0200)
Prompted by the discussions on #15547.

man/systemd-notify.xml
src/notify/notify.c

index 89060680f5efa1132446b4442d4fbb4a01b1ecea..4560074505020a3023bc2f9efdf7eec89a8fd324 100644 (file)
       <varlistentry>
         <term><option>--pid=</option></term>
 
-        <listitem><para>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
-        <command>systemd-notify</command> is used. This is equivalent
-        to <command>systemd-notify MAINPID=$PID</command>. For details
-        about the semantics of this option see
+        <listitem><para>Inform the service manager about the main PID of the daemon. Takes a PID as
+        argument. If the argument is specified as <literal>auto</literal> or omitted, the PID of the process
+        that invoked <command>systemd-notify</command> is used, except if that's the service manager. If the
+        argument is specified as <literal>self</literal>, the PID of the <command>systemd-notify</command>
+        command itself is used, and if <literal>parent</literal> is specified the calling process' PID is
+        used — even if it is the service manager. This is equivalent to <command>systemd-notify
+        MAINPID=$PID</command>. For details about the semantics of this option see
         <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
       </varlistentry>
 
index a76f3376951f8eabfbf204f94e3fa5dcd4893358..6c2dedd53fd6f8e9dceec01b2246356736368fbc 100644 (file)
@@ -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)