Blame SOURCES/0232-core-reduce-the-number-of-stalled-PIDs-from-the-watc.patch

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