pidref: Add pidref_wait_for_terminate_full()
We can use this to replace wait_for_terminate_with_timeout(). Because we poll a pidfd, we don't need to block SIGCHLD anymore in umount.c.
This commit is contained in:
+36
-18
@@ -1,5 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <poll.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -8,12 +9,14 @@
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "hash-funcs.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "parse-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
#include "siphash24.h"
|
||||
#include "time-util.h"
|
||||
|
||||
int pidref_acquire_pidfd_id(PidRef *pidref) {
|
||||
int r;
|
||||
@@ -449,7 +452,8 @@ bool pidref_is_self(PidRef *pidref) {
|
||||
return pidref->fd_id == self_id;
|
||||
}
|
||||
|
||||
int pidref_wait(PidRef *pidref, siginfo_t *ret, int options) {
|
||||
int pidref_wait_for_terminate_full(PidRef *pidref, usec_t timeout, siginfo_t *ret) {
|
||||
siginfo_t si = {};
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pidref))
|
||||
@@ -461,13 +465,37 @@ int pidref_wait(PidRef *pidref, siginfo_t *ret, int options) {
|
||||
if (pidref->pid == 1 || pidref_is_self(pidref))
|
||||
return -ECHILD;
|
||||
|
||||
siginfo_t si = {};
|
||||
if (pidref->fd >= 0)
|
||||
r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
|
||||
else
|
||||
r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (timeout != USEC_INFINITY && pidref->fd < 0)
|
||||
return -ENOMEDIUM;
|
||||
|
||||
usec_t ts = timeout == USEC_INFINITY ? USEC_INFINITY : now(CLOCK_MONOTONIC) + timeout;
|
||||
|
||||
for (;;) {
|
||||
if (timeout != USEC_INFINITY) {
|
||||
usec_t left = usec_sub_unsigned(ts, now(CLOCK_MONOTONIC));
|
||||
if (left == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
r = fd_wait_for_event(pidref->fd, POLLIN, left);
|
||||
if (r == 0)
|
||||
return -ETIMEDOUT;
|
||||
if (r == -EINTR)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (pidref->fd >= 0)
|
||||
r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, WEXITED));
|
||||
else
|
||||
r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, WEXITED));
|
||||
if (r == -EINTR)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
*ret = si;
|
||||
@@ -475,16 +503,6 @@ int pidref_wait(PidRef *pidref, siginfo_t *ret, int options) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidref_wait_for_terminate(PidRef *pidref, siginfo_t *ret) {
|
||||
int r;
|
||||
|
||||
for (;;) {
|
||||
r = pidref_wait(pidref, ret, WEXITED);
|
||||
if (r != -EINTR)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
bool pidref_is_automatic(const PidRef *pidref) {
|
||||
return pidref && pid_is_automatic(pidref->pid);
|
||||
}
|
||||
|
||||
+12
-2
@@ -96,8 +96,10 @@ int pidref_kill(const PidRef *pidref, int sig);
|
||||
int pidref_kill_and_sigcont(const PidRef *pidref, int sig);
|
||||
int pidref_sigqueue(const PidRef *pidref, int sig, int value);
|
||||
|
||||
int pidref_wait(PidRef *pidref, siginfo_t *ret, int options);
|
||||
int pidref_wait_for_terminate(PidRef *pidref, siginfo_t *ret);
|
||||
int pidref_wait_for_terminate_full(PidRef *pidref, usec_t timeout, siginfo_t *ret);
|
||||
static inline int pidref_wait_for_terminate(PidRef *pidref, siginfo_t *ret) {
|
||||
return pidref_wait_for_terminate_full(pidref, USEC_INFINITY, ret);
|
||||
}
|
||||
|
||||
static inline void pidref_done_sigkill_wait(PidRef *pidref) {
|
||||
if (!pidref_is_set(pidref))
|
||||
@@ -108,6 +110,14 @@ static inline void pidref_done_sigkill_wait(PidRef *pidref) {
|
||||
pidref_done(pidref);
|
||||
}
|
||||
|
||||
static inline void pidref_done_sigkill_nowait(PidRef *pidref) {
|
||||
if (!pidref_is_set(pidref))
|
||||
return;
|
||||
|
||||
(void) pidref_kill(pidref, SIGKILL);
|
||||
pidref_done(pidref);
|
||||
}
|
||||
|
||||
int pidref_verify(const PidRef *pidref);
|
||||
|
||||
#define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)
|
||||
|
||||
@@ -912,69 +912,6 @@ int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
|
||||
return pidref_wait_for_terminate_and_check(name, &PIDREF_MAKE_FROM_PID(pid), flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return values:
|
||||
*
|
||||
* < 0 : wait_for_terminate_with_timeout() failed to get the state of the process, the process timed out, the process
|
||||
* was terminated by a signal, or failed for an unknown reason.
|
||||
*
|
||||
* >=0 : The process terminated normally with no failures.
|
||||
*
|
||||
* Success is indicated by a return value of zero, a timeout is indicated by ETIMEDOUT, and all other child failure
|
||||
* states are indicated by error is indicated by a non-zero value.
|
||||
*
|
||||
* This call assumes SIGCHLD has been blocked already, in particular before the child to wait for has been forked off
|
||||
* to remain entirely race-free.
|
||||
*/
|
||||
int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
|
||||
sigset_t mask;
|
||||
int r;
|
||||
usec_t until;
|
||||
|
||||
assert_se(sigemptyset(&mask) == 0);
|
||||
assert_se(sigaddset(&mask, SIGCHLD) == 0);
|
||||
|
||||
/* Drop into a sigtimewait-based timeout. Waiting for the
|
||||
* pid to exit. */
|
||||
until = usec_add(now(CLOCK_MONOTONIC), timeout);
|
||||
for (;;) {
|
||||
usec_t n;
|
||||
siginfo_t status = {};
|
||||
|
||||
n = now(CLOCK_MONOTONIC);
|
||||
if (n >= until)
|
||||
break;
|
||||
|
||||
r = RET_NERRNO(sigtimedwait(&mask, NULL, TIMESPEC_STORE(until - n)));
|
||||
/* Assuming we woke due to the child exiting. */
|
||||
if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
|
||||
if (status.si_pid == pid) {
|
||||
/* This is the correct child. */
|
||||
if (status.si_code == CLD_EXITED)
|
||||
return status.si_status == 0 ? 0 : -EPROTO;
|
||||
else
|
||||
return -EPROTO;
|
||||
}
|
||||
}
|
||||
/* Not the child, check for errors and proceed appropriately */
|
||||
if (r < 0) {
|
||||
switch (r) {
|
||||
case -EAGAIN:
|
||||
/* Timed out, child is likely hung. */
|
||||
return -ETIMEDOUT;
|
||||
case -EINTR:
|
||||
/* Received a different signal and should retry */
|
||||
continue;
|
||||
default:
|
||||
/* Return any unexpected errors */
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
void sigkill_wait(pid_t pid) {
|
||||
assert(pid > 1);
|
||||
|
||||
|
||||
@@ -73,8 +73,6 @@ typedef enum WaitFlags {
|
||||
int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFlags flags);
|
||||
int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags);
|
||||
|
||||
int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
|
||||
|
||||
void sigkill_wait(pid_t pid);
|
||||
void sigkill_waitp(pid_t *pid);
|
||||
void sigterm_wait(pid_t pid);
|
||||
|
||||
Reference in New Issue
Block a user