|
|
65878a |
From 49bfaaa65107a9d79fbb1276e44fd4e3c98b0e9c Mon Sep 17 00:00:00 2001
|
|
|
65878a |
From: Lennart Poettering <lennart@poettering.net>
|
|
|
65878a |
Date: Fri, 7 Feb 2014 11:58:25 +0100
|
|
|
65878a |
Subject: [PATCH] core: allow PIDs to be watched by two units at the same time
|
|
|
65878a |
|
|
|
65878a |
In some cases it is interesting to map a PID to two units at the same
|
|
|
65878a |
time. For example, when a user logs in via a getty, which is reexeced to
|
|
|
65878a |
/sbin/login that binary will be explicitly referenced as main pid of the
|
|
|
65878a |
getty service, as well as implicitly referenced as part of the session
|
|
|
65878a |
scope.
|
|
|
65878a |
|
|
|
65878a |
Conflicts:
|
|
|
65878a |
src/core/manager.c
|
|
|
65878a |
src/core/manager.h
|
|
|
65878a |
---
|
|
|
65878a |
src/core/manager.c | 201 ++++++++++++++++++++++++++++++-----------------------
|
|
|
65878a |
src/core/manager.h | 9 ++-
|
|
|
65878a |
src/core/unit.c | 28 ++++++--
|
|
|
65878a |
3 files changed, 142 insertions(+), 96 deletions(-)
|
|
|
65878a |
|
|
|
65878a |
diff --git a/src/core/manager.c b/src/core/manager.c
|
|
|
65878a |
index db5094f..2829c95 100644
|
|
|
65878a |
--- a/src/core/manager.c
|
|
|
65878a |
+++ b/src/core/manager.c
|
|
|
65878a |
@@ -525,7 +525,10 @@ int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) {
|
|
|
65878a |
if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
|
|
|
65878a |
goto fail;
|
|
|
65878a |
|
|
|
65878a |
- if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func)))
|
|
|
65878a |
+ if (!(m->watch_pids1 = hashmap_new(trivial_hash_func, trivial_compare_func)))
|
|
|
65878a |
+ goto fail;
|
|
|
65878a |
+
|
|
|
65878a |
+ if (!(m->watch_pids2 = hashmap_new(trivial_hash_func, trivial_compare_func)))
|
|
|
65878a |
goto fail;
|
|
|
65878a |
|
|
|
65878a |
m->cgroup_unit = hashmap_new(string_hash_func, string_compare_func);
|
|
|
65878a |
@@ -740,7 +743,8 @@ void manager_free(Manager *m) {
|
|
|
65878a |
|
|
|
65878a |
hashmap_free(m->units);
|
|
|
65878a |
hashmap_free(m->jobs);
|
|
|
65878a |
- hashmap_free(m->watch_pids);
|
|
|
65878a |
+ hashmap_free(m->watch_pids1);
|
|
|
65878a |
+ hashmap_free(m->watch_pids2);
|
|
|
65878a |
hashmap_free(m->watch_bus);
|
|
|
65878a |
|
|
|
65878a |
if (m->epoll_fd >= 0)
|
|
|
65878a |
@@ -1247,6 +1251,26 @@ unsigned manager_dispatch_dbus_queue(Manager *m) {
|
|
|
65878a |
return n;
|
|
|
65878a |
}
|
|
|
65878a |
|
|
|
65878a |
+static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *buf, size_t n) {
|
|
|
65878a |
+ _cleanup_strv_free_ char **tags = NULL;
|
|
|
65878a |
+
|
|
|
65878a |
+ assert(m);
|
|
|
65878a |
+ assert(u);
|
|
|
65878a |
+ assert(buf);
|
|
|
65878a |
+ assert(n > 0);
|
|
|
65878a |
+
|
|
|
65878a |
+ tags = strv_split(buf, "\n\r");
|
|
|
65878a |
+ if (!tags) {
|
|
|
65878a |
+ log_oom();
|
|
|
65878a |
+ return;
|
|
|
65878a |
+ }
|
|
|
65878a |
+
|
|
|
65878a |
+ log_debug_unit(u->id, "Got notification message for unit %s", u->id);
|
|
|
65878a |
+
|
|
|
65878a |
+ if (UNIT_VTABLE(u)->notify_message)
|
|
|
65878a |
+ UNIT_VTABLE(u)->notify_message(u, pid, tags);
|
|
|
65878a |
+}
|
|
|
65878a |
+
|
|
|
65878a |
static int manager_process_notify_fd(Manager *m) {
|
|
|
65878a |
ssize_t n;
|
|
|
65878a |
|
|
|
65878a |
@@ -1259,6 +1283,8 @@ static int manager_process_notify_fd(Manager *m) {
|
|
|
65878a |
.iov_len = sizeof(buf)-1,
|
|
|
65878a |
};
|
|
|
65878a |
|
|
|
65878a |
+ bool found = false;
|
|
|
65878a |
+
|
|
|
65878a |
union {
|
|
|
65878a |
struct cmsghdr cmsghdr;
|
|
|
65878a |
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
|
|
|
65878a |
@@ -1272,7 +1298,6 @@ static int manager_process_notify_fd(Manager *m) {
|
|
|
65878a |
};
|
|
|
65878a |
struct ucred *ucred;
|
|
|
65878a |
Unit *u;
|
|
|
65878a |
- _cleanup_strv_free_ char **tags = NULL;
|
|
|
65878a |
|
|
|
65878a |
n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT);
|
|
|
65878a |
if (n <= 0) {
|
|
|
65878a |
@@ -1295,105 +1320,105 @@ static int manager_process_notify_fd(Manager *m) {
|
|
|
65878a |
|
|
|
65878a |
ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
|
|
|
65878a |
|
|
|
65878a |
- u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid));
|
|
|
65878a |
- if (!u) {
|
|
|
65878a |
- u = manager_get_unit_by_pid(m, ucred->pid);
|
|
|
65878a |
- if (!u) {
|
|
|
65878a |
- log_warning("Cannot find unit for notify message of PID %lu.", (unsigned long) ucred->pid);
|
|
|
65878a |
- continue;
|
|
|
65878a |
- }
|
|
|
65878a |
- }
|
|
|
65878a |
-
|
|
|
65878a |
assert((size_t) n < sizeof(buf));
|
|
|
65878a |
buf[n] = 0;
|
|
|
65878a |
- tags = strv_split(buf, "\n\r");
|
|
|
65878a |
- if (!tags)
|
|
|
65878a |
- return log_oom();
|
|
|
65878a |
-
|
|
|
65878a |
- log_debug_unit(u->id, "Got notification message for unit %s", u->id);
|
|
|
65878a |
-
|
|
|
65878a |
- if (UNIT_VTABLE(u)->notify_message)
|
|
|
65878a |
- UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags);
|
|
|
65878a |
- }
|
|
|
65878a |
-
|
|
|
65878a |
- return 0;
|
|
|
65878a |
-}
|
|
|
65878a |
-
|
|
|
65878a |
-static int manager_dispatch_sigchld(Manager *m) {
|
|
|
65878a |
- assert(m);
|
|
|
65878a |
-
|
|
|
65878a |
- for (;;) {
|
|
|
65878a |
- siginfo_t si = {};
|
|
|
65878a |
- Unit *u;
|
|
|
65878a |
- int r;
|
|
|
65878a |
-
|
|
|
65878a |
- /* First we call waitd() for a PID and do not reap the
|
|
|
65878a |
- * zombie. That way we can still access /proc/$PID for
|
|
|
65878a |
- * it while it is a zombie. */
|
|
|
65878a |
- if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
|
|
|
65878a |
-
|
|
|
65878a |
- if (errno == ECHILD)
|
|
|
65878a |
- break;
|
|
|
65878a |
|
|
|
65878a |
- if (errno == EINTR)
|
|
|
65878a |
- continue;
|
|
|
65878a |
-
|
|
|
65878a |
- return -errno;
|
|
|
65878a |
+ u = manager_get_unit_by_pid(m, ucred->pid);
|
|
|
65878a |
+ if (u) {
|
|
|
65878a |
+ manager_invoke_notify_message(m, u, ucred->pid, buf, n);
|
|
|
65878a |
+ found = true;
|
|
|
65878a |
}
|
|
|
65878a |
|
|
|
65878a |
- if (si.si_pid <= 0)
|
|
|
65878a |
- break;
|
|
|
65878a |
-
|
|
|
65878a |
- if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
|
|
|
65878a |
- _cleanup_free_ char *name = NULL;
|
|
|
65878a |
-
|
|
|
65878a |
- get_process_comm(si.si_pid, &name);
|
|
|
65878a |
- log_debug("Got SIGCHLD for process %lu (%s)", (unsigned long) si.si_pid, strna(name));
|
|
|
65878a |
+ u = hashmap_get(m->watch_pids1, LONG_TO_PTR(ucred->pid));
|
|
|
65878a |
+ if (u) {
|
|
|
65878a |
+ manager_invoke_notify_message(m, u, ucred->pid, buf, n);
|
|
|
65878a |
+ found = true;
|
|
|
65878a |
}
|
|
|
65878a |
|
|
|
65878a |
- /* Let's flush any message the dying child might still
|
|
|
65878a |
- * have queued for us. This ensures that the process
|
|
|
65878a |
- * still exists in /proc so that we can figure out
|
|
|
65878a |
- * which cgroup and hence unit it belongs to. */
|
|
|
65878a |
- r = manager_process_notify_fd(m);
|
|
|
65878a |
- if (r < 0)
|
|
|
65878a |
- return r;
|
|
|
65878a |
-
|
|
|
65878a |
- /* And now figure out the unit this belongs to */
|
|
|
65878a |
- u = hashmap_get(m->watch_pids, LONG_TO_PTR(si.si_pid));
|
|
|
65878a |
- if (!u)
|
|
|
65878a |
- u = manager_get_unit_by_pid(m, si.si_pid);
|
|
|
65878a |
-
|
|
|
65878a |
- /* And now, we actually reap the zombie. */
|
|
|
65878a |
- if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
|
|
|
65878a |
- if (errno == EINTR)
|
|
|
65878a |
- continue;
|
|
|
65878a |
-
|
|
|
65878a |
- return -errno;
|
|
|
65878a |
+ u = hashmap_get(m->watch_pids2, LONG_TO_PTR(ucred->pid));
|
|
|
65878a |
+ if (u) {
|
|
|
65878a |
+ manager_invoke_notify_message(m, u, ucred->pid, buf, n);
|
|
|
65878a |
+ found = true;
|
|
|
65878a |
}
|
|
|
65878a |
|
|
|
65878a |
- if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED)
|
|
|
65878a |
- continue;
|
|
|
65878a |
+ if (!found)
|
|
|
65878a |
+ log_warning("Cannot find unit for notify message of PID %lu.",(long unsigned) ucred->pid);
|
|
|
65878a |
+ }
|
|
|
65878a |
|
|
|
65878a |
- log_debug("Child %lu died (code=%s, status=%i/%s)",
|
|
|
65878a |
- (long unsigned) si.si_pid,
|
|
|
65878a |
- sigchld_code_to_string(si.si_code),
|
|
|
65878a |
- si.si_status,
|
|
|
65878a |
- strna(si.si_code == CLD_EXITED
|
|
|
65878a |
- ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL)
|
|
|
65878a |
- : signal_to_string(si.si_status)));
|
|
|
65878a |
+ return 0;
|
|
|
65878a |
+}
|
|
|
65878a |
|
|
|
65878a |
- if (!u)
|
|
|
65878a |
- continue;
|
|
|
65878a |
+static void invoke_sigchld_event(Manager *m, Unit *u, siginfo_t *si) {
|
|
|
65878a |
+ assert(m);
|
|
|
65878a |
+ assert(u);
|
|
|
65878a |
+ assert(si);
|
|
|
65878a |
|
|
|
65878a |
- log_debug_unit(u->id,
|
|
|
65878a |
- "Child %lu belongs to %s", (long unsigned) si.si_pid, u->id);
|
|
|
65878a |
+ log_debug_unit(u->id, "Child %lu belongs to %s",(long unsigned) si->si_pid, u->id);
|
|
|
65878a |
|
|
|
65878a |
- unit_unwatch_pid(u, si.si_pid);
|
|
|
65878a |
- UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status);
|
|
|
65878a |
- }
|
|
|
65878a |
+ unit_unwatch_pid(u, si->si_pid);
|
|
|
65878a |
+ UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
|
|
|
65878a |
+}
|
|
|
65878a |
|
|
|
65878a |
- return 0;
|
|
|
65878a |
+static int manager_dispatch_sigchld(Manager *m) {
|
|
|
65878a |
+ assert(m);
|
|
|
65878a |
+
|
|
|
65878a |
+ for (;;) {
|
|
|
65878a |
+ siginfo_t si = {};
|
|
|
65878a |
+
|
|
|
65878a |
+ /* First we call waitd() for a PID and do not reap the
|
|
|
65878a |
+ * zombie. That way we can still access /proc/$PID for
|
|
|
65878a |
+ * it while it is a zombie. */
|
|
|
65878a |
+ if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
|
|
|
65878a |
+
|
|
|
65878a |
+ if (errno == ECHILD)
|
|
|
65878a |
+ break;
|
|
|
65878a |
+
|
|
|
65878a |
+ if (errno == EINTR)
|
|
|
65878a |
+ continue;
|
|
|
65878a |
+
|
|
|
65878a |
+ return -errno;
|
|
|
65878a |
+ }
|
|
|
65878a |
+
|
|
|
65878a |
+ if (si.si_pid <= 0)
|
|
|
65878a |
+ break;
|
|
|
65878a |
+
|
|
|
65878a |
+ if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
|
|
|
65878a |
+ _cleanup_free_ char *name = NULL;
|
|
|
65878a |
+ Unit *u;
|
|
|
65878a |
+
|
|
|
65878a |
+ get_process_comm(si.si_pid, &name);
|
|
|
65878a |
+
|
|
|
65878a |
+ log_debug("Child %lu (%s) died (code=%s, status=%i/%s)",
|
|
|
65878a |
+ (long unsigned) si.si_pid, strna(name),
|
|
|
65878a |
+ sigchld_code_to_string(si.si_code),
|
|
|
65878a |
+ si.si_status,
|
|
|
65878a |
+ strna(si.si_code == CLD_EXITED
|
|
|
65878a |
+ ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL)
|
|
|
65878a |
+ : signal_to_string(si.si_status)));
|
|
|
65878a |
+
|
|
|
65878a |
+ /* And now figure out the unit this belongs
|
|
|
65878a |
+ * to, it might be multiple... */
|
|
|
65878a |
+ u = manager_get_unit_by_pid(m, si.si_pid);
|
|
|
65878a |
+ if (u)
|
|
|
65878a |
+ invoke_sigchld_event(m, u, &si);
|
|
|
65878a |
+ u = hashmap_get(m->watch_pids1, LONG_TO_PTR(si.si_pid));
|
|
|
65878a |
+ if (u)
|
|
|
65878a |
+ invoke_sigchld_event(m, u, &si);
|
|
|
65878a |
+ u = hashmap_get(m->watch_pids2, LONG_TO_PTR(si.si_pid));
|
|
|
65878a |
+ if (u)
|
|
|
65878a |
+ invoke_sigchld_event(m, u, &si);
|
|
|
65878a |
+ }
|
|
|
65878a |
+
|
|
|
65878a |
+ /* And now, we actually reap the zombie. */
|
|
|
65878a |
+ if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
|
|
|
65878a |
+ if (errno == EINTR)
|
|
|
65878a |
+ continue;
|
|
|
65878a |
+
|
|
|
65878a |
+ return -errno;
|
|
|
65878a |
+ }
|
|
|
65878a |
+ }
|
|
|
65878a |
+
|
|
|
65878a |
+ return 0;
|
|
|
65878a |
}
|
|
|
65878a |
|
|
|
65878a |
static int manager_start_target(Manager *m, const char *name, JobMode mode) {
|
|
|
65878a |
diff --git a/src/core/manager.h b/src/core/manager.h
|
|
|
65878a |
index ee42c5e..0133ea5 100644
|
|
|
65878a |
--- a/src/core/manager.h
|
|
|
65878a |
+++ b/src/core/manager.h
|
|
|
65878a |
@@ -125,7 +125,14 @@ struct Manager {
|
|
|
65878a |
/* Units that should be realized */
|
|
|
65878a |
LIST_HEAD(Unit, cgroup_queue);
|
|
|
65878a |
|
|
|
65878a |
- Hashmap *watch_pids; /* pid => Unit object n:1 */
|
|
|
65878a |
+ /* We use two hash tables here, since the same PID might be
|
|
|
65878a |
+ * watched by two different units: once the unit that forked
|
|
|
65878a |
+ * it off, and possibly a different unit to which it was
|
|
|
65878a |
+ * joined as cgroup member. Since we know that it is either
|
|
|
65878a |
+ * one or two units for each PID we just use to hashmaps
|
|
|
65878a |
+ * here. */
|
|
|
65878a |
+ Hashmap *watch_pids1; /* pid => Unit object n:1 */
|
|
|
65878a |
+ Hashmap *watch_pids2; /* pid => Unit object n:1 */
|
|
|
65878a |
|
|
|
65878a |
char *notify_socket;
|
|
|
65878a |
|
|
|
65878a |
diff --git a/src/core/unit.c b/src/core/unit.c
|
|
|
65878a |
index 0332094..a510eb2 100644
|
|
|
65878a |
--- a/src/core/unit.c
|
|
|
65878a |
+++ b/src/core/unit.c
|
|
|
65878a |
@@ -1665,16 +1665,27 @@ int unit_watch_pid(Unit *u, pid_t pid) {
|
|
|
65878a |
assert(u);
|
|
|
65878a |
assert(pid >= 1);
|
|
|
65878a |
|
|
|
65878a |
+ /* Watch a specific PID. We only support one or two units
|
|
|
65878a |
+ * watching each PID for now, not more. */
|
|
|
65878a |
+
|
|
|
65878a |
+ r = hashmap_ensure_allocated(&u->manager->watch_pids1, trivial_hash_func, trivial_compare_func);
|
|
|
65878a |
+ if (r < 0)
|
|
|
65878a |
+ return r;
|
|
|
65878a |
+
|
|
|
65878a |
r = set_ensure_allocated(&u->pids, trivial_hash_func, trivial_compare_func);
|
|
|
65878a |
if (r < 0)
|
|
|
65878a |
return r;
|
|
|
65878a |
|
|
|
65878a |
- /* Watch a specific PID. We only support one unit watching
|
|
|
65878a |
- * each PID for now. */
|
|
|
65878a |
+ r = hashmap_put(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
|
|
|
65878a |
+ if (r == -EEXIST) {
|
|
|
65878a |
+ r = hashmap_ensure_allocated(&u->manager->watch_pids2, trivial_hash_func, trivial_compare_func);
|
|
|
65878a |
+ if (r < 0)
|
|
|
65878a |
+ return r;
|
|
|
65878a |
|
|
|
65878a |
- r = set_put(u->pids, LONG_TO_PTR(pid));
|
|
|
65878a |
+ r = hashmap_put(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
|
|
|
65878a |
+ }
|
|
|
65878a |
|
|
|
65878a |
- q = hashmap_put(u->manager->watch_pids, LONG_TO_PTR(pid), u);
|
|
|
65878a |
+ q = set_put(u->pids, LONG_TO_PTR(pid));
|
|
|
65878a |
if (q < 0)
|
|
|
65878a |
return q;
|
|
|
65878a |
|
|
|
65878a |
@@ -1685,7 +1696,8 @@ void unit_unwatch_pid(Unit *u, pid_t pid) {
|
|
|
65878a |
assert(u);
|
|
|
65878a |
assert(pid >= 1);
|
|
|
65878a |
|
|
|
65878a |
- hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u);
|
|
|
65878a |
+ hashmap_remove_value(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
|
|
|
65878a |
+ hashmap_remove_value(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
|
|
|
65878a |
set_remove(u->pids, LONG_TO_PTR(pid));
|
|
|
65878a |
}
|
|
|
65878a |
|
|
|
65878a |
@@ -1758,8 +1770,10 @@ void unit_unwatch_all_pids(Unit *u) {
|
|
|
65878a |
|
|
|
65878a |
assert(u);
|
|
|
65878a |
|
|
|
65878a |
- SET_FOREACH(e, u->pids, i)
|
|
|
65878a |
- hashmap_remove_value(u->manager->watch_pids, e, u);
|
|
|
65878a |
+ SET_FOREACH(e, u->pids, i) {
|
|
|
65878a |
+ hashmap_remove_value(u->manager->watch_pids1, e, u);
|
|
|
65878a |
+ hashmap_remove_value(u->manager->watch_pids2, e, u);
|
|
|
65878a |
+ }
|
|
|
65878a |
|
|
|
65878a |
set_free(u->pids);
|
|
|
65878a |
u->pids = NULL;
|