4fbe94
From 79e9566ec0a61d887ab63f17192dbd71aae36ee0 Mon Sep 17 00:00:00 2001
4fbe94
From: Franck Bui <fbui@suse.com>
4fbe94
Date: Mon, 18 Mar 2019 20:59:36 +0100
4fbe94
Subject: [PATCH] core: reduce the number of stalled PIDs from the watched
4fbe94
 processes list when possible
4fbe94
MIME-Version: 1.0
4fbe94
Content-Type: text/plain; charset=UTF-8
4fbe94
Content-Transfer-Encoding: 8bit
4fbe94
4fbe94
Some PIDs can remain in the watched list even though their processes have
4fbe94
exited since a long time. It can easily happen if the main process of a forking
4fbe94
service manages to spawn a child before the control process exits for example.
4fbe94
4fbe94
However when a pid is about to be mapped to a unit by calling unit_watch_pid(),
4fbe94
the caller usually knows if the pid should belong to this unit exclusively: if
4fbe94
we just forked() off a child, then we can be sure that its PID is otherwise
4fbe94
unused. In this case we take this opportunity to remove any stalled PIDs from
4fbe94
the watched process list.
4fbe94
4fbe94
If we learnt about a PID in any other form (for example via PID file, via
4fbe94
searching, MAINPID= and so on), then we can't assume anything.
4fbe94
4fbe94
Thanks Renaud Métrich for backporting this to RHEL.
4fbe94
Resolves: #1744972
4fbe94
---
4fbe94
 src/core/cgroup.c         |  2 +-
4fbe94
 src/core/dbus-scope.c     |  2 +-
4fbe94
 src/core/manager.c        | 10 ++++++++++
4fbe94
 src/core/manager.h        |  2 ++
4fbe94
 src/core/mount.c          |  5 ++---
4fbe94
 src/core/service.c        | 16 ++++++++--------
4fbe94
 src/core/socket.c         |  7 +++----
4fbe94
 src/core/swap.c           |  5 ++---
4fbe94
 src/core/unit.c           |  8 +++++++-
4fbe94
 src/core/unit.h           |  2 +-
4fbe94
 src/test/test-watch-pid.c | 12 ++++++------
4fbe94
 11 files changed, 43 insertions(+), 28 deletions(-)
4fbe94
4fbe94
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
4fbe94
index b7ed07e65b..76eafdc082 100644
4fbe94
--- a/src/core/cgroup.c
4fbe94
+++ b/src/core/cgroup.c
4fbe94
@@ -1926,7 +1926,7 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) {
4fbe94
                 pid_t pid;
4fbe94
 
4fbe94
                 while ((r = cg_read_pid(f, &pid)) > 0) {
4fbe94
-                        r = unit_watch_pid(u, pid);
4fbe94
+                        r = unit_watch_pid(u, pid, false);
4fbe94
                         if (r < 0 && ret >= 0)
4fbe94
                                 ret = r;
4fbe94
                 }
4fbe94
diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c
4fbe94
index 6725f62794..0bbf64fff1 100644
4fbe94
--- a/src/core/dbus-scope.c
4fbe94
+++ b/src/core/dbus-scope.c
4fbe94
@@ -106,7 +106,7 @@ static int bus_scope_set_transient_property(
4fbe94
                                 return r;
4fbe94
 
4fbe94
                         if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
4fbe94
-                                r = unit_watch_pid(UNIT(s), pid);
4fbe94
+                                r = unit_watch_pid(UNIT(s), pid, false);
4fbe94
                                 if (r < 0 && r != -EEXIST)
4fbe94
                                         return r;
4fbe94
                         }
4fbe94
diff --git a/src/core/manager.c b/src/core/manager.c
4fbe94
index c83e296cf3..0eae7d46fb 100644
4fbe94
--- a/src/core/manager.c
4fbe94
+++ b/src/core/manager.c
4fbe94
@@ -2044,6 +2044,16 @@ void manager_clear_jobs(Manager *m) {
4fbe94
                 job_finish_and_invalidate(j, JOB_CANCELED, false, false);
4fbe94
 }
4fbe94
 
4fbe94
+void manager_unwatch_pid(Manager *m, pid_t pid) {
4fbe94
+        assert(m);
4fbe94
+
4fbe94
+        /* First let's drop the unit keyed as "pid". */
4fbe94
+        (void) hashmap_remove(m->watch_pids, PID_TO_PTR(pid));
4fbe94
+
4fbe94
+        /* Then, let's also drop the array keyed by -pid. */
4fbe94
+        free(hashmap_remove(m->watch_pids, PID_TO_PTR(-pid)));
4fbe94
+}
4fbe94
+
4fbe94
 static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) {
4fbe94
         Manager *m = userdata;
4fbe94
         Job *j;
4fbe94
diff --git a/src/core/manager.h b/src/core/manager.h
4fbe94
index c7f4d66ecd..fa47952d24 100644
4fbe94
--- a/src/core/manager.h
4fbe94
+++ b/src/core/manager.h
4fbe94
@@ -406,6 +406,8 @@ int manager_get_dump_string(Manager *m, char **ret);
4fbe94
 
4fbe94
 void manager_clear_jobs(Manager *m);
4fbe94
 
4fbe94
+void manager_unwatch_pid(Manager *m, pid_t pid);
4fbe94
+
4fbe94
 unsigned manager_dispatch_load_queue(Manager *m);
4fbe94
 
4fbe94
 int manager_environment_add(Manager *m, char **minus, char **plus);
4fbe94
diff --git a/src/core/mount.c b/src/core/mount.c
4fbe94
index 2ac04e3874..5878814b1b 100644
4fbe94
--- a/src/core/mount.c
4fbe94
+++ b/src/core/mount.c
4fbe94
@@ -677,7 +677,7 @@ static int mount_coldplug(Unit *u) {
4fbe94
             pid_is_unwaited(m->control_pid) &&
4fbe94
             MOUNT_STATE_WITH_PROCESS(new_state)) {
4fbe94
 
4fbe94
-                r = unit_watch_pid(UNIT(m), m->control_pid);
4fbe94
+                r = unit_watch_pid(UNIT(m), m->control_pid, false);
4fbe94
                 if (r < 0)
4fbe94
                         return r;
4fbe94
 
4fbe94
@@ -781,9 +781,8 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
4fbe94
         if (r < 0)
4fbe94
                 return r;
4fbe94
 
4fbe94
-        r = unit_watch_pid(UNIT(m), pid);
4fbe94
+        r = unit_watch_pid(UNIT(m), pid, true);
4fbe94
         if (r < 0)
4fbe94
-                /* FIXME: we need to do something here */
4fbe94
                 return r;
4fbe94
 
4fbe94
         *_pid = pid;
4fbe94
diff --git a/src/core/service.c b/src/core/service.c
4fbe94
index 614ba05d89..310838a5f6 100644
4fbe94
--- a/src/core/service.c
4fbe94
+++ b/src/core/service.c
4fbe94
@@ -974,7 +974,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
4fbe94
         if (r < 0)
4fbe94
                 return r;
4fbe94
 
4fbe94
-        r = unit_watch_pid(UNIT(s), pid);
4fbe94
+        r = unit_watch_pid(UNIT(s), pid, false);
4fbe94
         if (r < 0) /* FIXME: we need to do something here */
4fbe94
                 return log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid);
4fbe94
 
4fbe94
@@ -1004,7 +1004,7 @@ static void service_search_main_pid(Service *s) {
4fbe94
         if (service_set_main_pid(s, pid) < 0)
4fbe94
                 return;
4fbe94
 
4fbe94
-        r = unit_watch_pid(UNIT(s), pid);
4fbe94
+        r = unit_watch_pid(UNIT(s), pid, false);
4fbe94
         if (r < 0)
4fbe94
                 /* FIXME: we need to do something here */
4fbe94
                 log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid);
4fbe94
@@ -1135,7 +1135,7 @@ static int service_coldplug(Unit *u) {
4fbe94
                     SERVICE_RUNNING, SERVICE_RELOAD,
4fbe94
                     SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
4fbe94
                     SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
4fbe94
-                r = unit_watch_pid(UNIT(s), s->main_pid);
4fbe94
+                r = unit_watch_pid(UNIT(s), s->main_pid, false);
4fbe94
                 if (r < 0)
4fbe94
                         return r;
4fbe94
         }
4fbe94
@@ -1147,7 +1147,7 @@ static int service_coldplug(Unit *u) {
4fbe94
                    SERVICE_RELOAD,
4fbe94
                    SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
4fbe94
                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
4fbe94
-                r = unit_watch_pid(UNIT(s), s->control_pid);
4fbe94
+                r = unit_watch_pid(UNIT(s), s->control_pid, false);
4fbe94
                 if (r < 0)
4fbe94
                         return r;
4fbe94
         }
4fbe94
@@ -1545,8 +1545,8 @@ static int service_spawn(
4fbe94
         s->exec_fd_event_source = TAKE_PTR(exec_fd_source);
4fbe94
         s->exec_fd_hot = false;
4fbe94
 
4fbe94
-        r = unit_watch_pid(UNIT(s), pid);
4fbe94
-        if (r < 0) /* FIXME: we need to do something here */
4fbe94
+        r = unit_watch_pid(UNIT(s), pid, true);
4fbe94
+        if (r < 0)
4fbe94
                 return r;
4fbe94
 
4fbe94
         *_pid = pid;
4fbe94
@@ -3643,7 +3643,7 @@ static void service_notify_message(
4fbe94
                         }
4fbe94
                         if (r > 0) {
4fbe94
                                 service_set_main_pid(s, new_main_pid);
4fbe94
-                                unit_watch_pid(UNIT(s), new_main_pid);
4fbe94
+                                unit_watch_pid(UNIT(s), new_main_pid, false);
4fbe94
                                 notify_dbus = true;
4fbe94
                         }
4fbe94
                 }
4fbe94
@@ -3858,7 +3858,7 @@ static void service_bus_name_owner_change(
4fbe94
                         log_unit_debug(u, "D-Bus name %s is now owned by process " PID_FMT, name, pid);
4fbe94
 
4fbe94
                         service_set_main_pid(s, pid);
4fbe94
-                        unit_watch_pid(UNIT(s), pid);
4fbe94
+                        unit_watch_pid(UNIT(s), pid, false);
4fbe94
                 }
4fbe94
         }
4fbe94
 }
4fbe94
diff --git a/src/core/socket.c b/src/core/socket.c
4fbe94
index d488c64e91..b034549634 100644
4fbe94
--- a/src/core/socket.c
4fbe94
+++ b/src/core/socket.c
4fbe94
@@ -1816,7 +1816,7 @@ static int socket_coldplug(Unit *u) {
4fbe94
                    SOCKET_FINAL_SIGTERM,
4fbe94
                    SOCKET_FINAL_SIGKILL)) {
4fbe94
 
4fbe94
-                r = unit_watch_pid(UNIT(s), s->control_pid);
4fbe94
+                r = unit_watch_pid(UNIT(s), s->control_pid, false);
4fbe94
                 if (r < 0)
4fbe94
                         return r;
4fbe94
 
4fbe94
@@ -1902,9 +1902,8 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
4fbe94
         if (r < 0)
4fbe94
                 return r;
4fbe94
 
4fbe94
-        r = unit_watch_pid(UNIT(s), pid);
4fbe94
+        r = unit_watch_pid(UNIT(s), pid, true);
4fbe94
         if (r < 0)
4fbe94
-                /* FIXME: we need to do something here */
4fbe94
                 return r;
4fbe94
 
4fbe94
         *_pid = pid;
4fbe94
@@ -1973,7 +1972,7 @@ static int socket_chown(Socket *s, pid_t *_pid) {
4fbe94
                 _exit(EXIT_SUCCESS);
4fbe94
         }
4fbe94
 
4fbe94
-        r = unit_watch_pid(UNIT(s), pid);
4fbe94
+        r = unit_watch_pid(UNIT(s), pid, true);
4fbe94
         if (r < 0)
4fbe94
                 goto fail;
4fbe94
 
4fbe94
diff --git a/src/core/swap.c b/src/core/swap.c
4fbe94
index b644753a1c..e717dbb54a 100644
4fbe94
--- a/src/core/swap.c
4fbe94
+++ b/src/core/swap.c
4fbe94
@@ -531,7 +531,7 @@ static int swap_coldplug(Unit *u) {
4fbe94
             pid_is_unwaited(s->control_pid) &&
4fbe94
             SWAP_STATE_WITH_PROCESS(new_state)) {
4fbe94
 
4fbe94
-                r = unit_watch_pid(UNIT(s), s->control_pid);
4fbe94
+                r = unit_watch_pid(UNIT(s), s->control_pid, false);
4fbe94
                 if (r < 0)
4fbe94
                         return r;
4fbe94
 
4fbe94
@@ -636,9 +636,8 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
4fbe94
         if (r < 0)
4fbe94
                 goto fail;
4fbe94
 
4fbe94
-        r = unit_watch_pid(UNIT(s), pid);
4fbe94
+        r = unit_watch_pid(UNIT(s), pid, true);
4fbe94
         if (r < 0)
4fbe94
-                /* FIXME: we need to do something here */
4fbe94
                 goto fail;
4fbe94
 
4fbe94
         *_pid = pid;
4fbe94
diff --git a/src/core/unit.c b/src/core/unit.c
4fbe94
index d298afb0d4..b0b1c77ef7 100644
4fbe94
--- a/src/core/unit.c
4fbe94
+++ b/src/core/unit.c
4fbe94
@@ -2500,7 +2500,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
4fbe94
         unit_add_to_gc_queue(u);
4fbe94
 }
4fbe94
 
4fbe94
-int unit_watch_pid(Unit *u, pid_t pid) {
4fbe94
+int unit_watch_pid(Unit *u, pid_t pid, bool exclusive) {
4fbe94
         int r;
4fbe94
 
4fbe94
         assert(u);
4fbe94
@@ -2508,6 +2508,12 @@ int unit_watch_pid(Unit *u, pid_t pid) {
4fbe94
 
4fbe94
         /* Watch a specific PID */
4fbe94
 
4fbe94
+        /* Caller might be sure that this PID belongs to this unit only. Let's take this
4fbe94
+         * opportunity to remove any stalled references to this PID as they can be created
4fbe94
+         * easily (when watching a process which is not our direct child). */
4fbe94
+        if (exclusive)
4fbe94
+                manager_unwatch_pid(u->manager, pid);
4fbe94
+
4fbe94
         r = set_ensure_allocated(&u->pids, NULL);
4fbe94
         if (r < 0)
4fbe94
                 return r;
4fbe94
diff --git a/src/core/unit.h b/src/core/unit.h
4fbe94
index e1a60da244..68cc1869e4 100644
4fbe94
--- a/src/core/unit.h
4fbe94
+++ b/src/core/unit.h
4fbe94
@@ -655,7 +655,7 @@ typedef enum UnitNotifyFlags {
4fbe94
 
4fbe94
 void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags);
4fbe94
 
4fbe94
-int unit_watch_pid(Unit *u, pid_t pid);
4fbe94
+int unit_watch_pid(Unit *u, pid_t pid, bool exclusive);
4fbe94
 void unit_unwatch_pid(Unit *u, pid_t pid);
4fbe94
 void unit_unwatch_all_pids(Unit *u);
4fbe94
 
4fbe94
diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c
4fbe94
index cb43b35bc5..8c70175aed 100644
4fbe94
--- a/src/test/test-watch-pid.c
4fbe94
+++ b/src/test/test-watch-pid.c
4fbe94
@@ -49,25 +49,25 @@ int main(int argc, char *argv[]) {
4fbe94
         assert_se(hashmap_isempty(m->watch_pids));
4fbe94
         assert_se(manager_get_unit_by_pid(m, 4711) == NULL);
4fbe94
 
4fbe94
-        assert_se(unit_watch_pid(a, 4711) >= 0);
4fbe94
+        assert_se(unit_watch_pid(a, 4711, false) >= 0);
4fbe94
         assert_se(manager_get_unit_by_pid(m, 4711) == a);
4fbe94
 
4fbe94
-        assert_se(unit_watch_pid(a, 4711) >= 0);
4fbe94
+        assert_se(unit_watch_pid(a, 4711, false) >= 0);
4fbe94
         assert_se(manager_get_unit_by_pid(m, 4711) == a);
4fbe94
 
4fbe94
-        assert_se(unit_watch_pid(b, 4711) >= 0);
4fbe94
+        assert_se(unit_watch_pid(b, 4711, false) >= 0);
4fbe94
         u = manager_get_unit_by_pid(m, 4711);
4fbe94
         assert_se(u == a || u == b);
4fbe94
 
4fbe94
-        assert_se(unit_watch_pid(b, 4711) >= 0);
4fbe94
+        assert_se(unit_watch_pid(b, 4711, false) >= 0);
4fbe94
         u = manager_get_unit_by_pid(m, 4711);
4fbe94
         assert_se(u == a || u == b);
4fbe94
 
4fbe94
-        assert_se(unit_watch_pid(c, 4711) >= 0);
4fbe94
+        assert_se(unit_watch_pid(c, 4711, false) >= 0);
4fbe94
         u = manager_get_unit_by_pid(m, 4711);
4fbe94
         assert_se(u == a || u == b || u == c);
4fbe94
 
4fbe94
-        assert_se(unit_watch_pid(c, 4711) >= 0);
4fbe94
+        assert_se(unit_watch_pid(c, 4711, false) >= 0);
4fbe94
         u = manager_get_unit_by_pid(m, 4711);
4fbe94
         assert_se(u == a || u == b || u == c);
4fbe94