diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 77045e6aa8e..f48285d3d52 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -742,6 +742,44 @@ int pidref_get_ppid(const PidRef *pidref, pid_t *ret) { return 0; } +int pidref_get_ppid_as_pidref(const PidRef *pidref, PidRef *ret) { + pid_t ppid; + int r; + + assert(ret); + + r = pidref_get_ppid(pidref, &ppid); + if (r < 0) + return r; + + for (unsigned attempt = 0; attempt < 16; attempt++) { + _cleanup_(pidref_done) PidRef parent = PIDREF_NULL; + + r = pidref_set_pid(&parent, ppid); + if (r < 0) + return r; + + /* If we have a pidfd of the original PID, let's verify that the process we acquired really + * is the parent still */ + if (pidref->fd >= 0) { + r = pidref_get_ppid(pidref, &ppid); + if (r < 0) + return r; + + /* Did the PPID change since we queried it? if so we might have pinned the wrong + * process, if its PID got reused by now. Let's try again */ + if (parent.pid != ppid) + continue; + } + + *ret = TAKE_PIDREF(parent); + return 0; + } + + /* Give up after 16 tries */ + return -ENOTRECOVERABLE; +} + int pid_get_start_time(pid_t pid, usec_t *ret) { _cleanup_free_ char *line = NULL; const char *p; diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 8864472af64..d4d7d87c069 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -54,6 +54,7 @@ int get_process_root(pid_t pid, char **ret); int get_process_environ(pid_t pid, char **ret); int pid_get_ppid(pid_t pid, pid_t *ret); int pidref_get_ppid(const PidRef *pidref, pid_t *ret); +int pidref_get_ppid_as_pidref(const PidRef *pidref, PidRef *ret); int pid_get_start_time(pid_t pid, usec_t *ret); int pidref_get_start_time(const PidRef *pid, usec_t *ret); int get_process_umask(pid_t pid, mode_t *ret); diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 5a6bcdffcc0..12bac656e20 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -848,6 +848,29 @@ TEST(pid_get_ppid) { pid = ppid; } + + /* the same via pidref */ + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + ASSERT_OK(pidref_set_self(&pidref)); + for (;;) { + _cleanup_free_ char *c1 = NULL, *c2 = NULL; + _cleanup_(pidref_done) PidRef parent = PIDREF_NULL; + r = pidref_get_ppid_as_pidref(&pidref, &parent); + if (r == -EADDRNOTAVAIL) { + log_info("No further parent PID"); + break; + } + + ASSERT_OK(r); + + ASSERT_OK(pidref_get_cmdline(&pidref, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c1)); + ASSERT_OK(pidref_get_cmdline(&parent, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &c2)); + + log_info("Parent of " PID_FMT " (%s) is " PID_FMT " (%s).", pidref.pid, c1, parent.pid, c2); + + pidref_done(&pidref); + pidref = TAKE_PIDREF(parent); + } } TEST(set_oom_score_adjust) {