diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c index 131fd3ba00..b06b5ce774 100644 --- a/src/basic/signal-util.c +++ b/src/basic/signal-util.c @@ -253,3 +253,38 @@ int signal_is_blocked(int sig) { return r; } + +int pop_pending_signal_internal(int sig, ...) { + sigset_t ss; + va_list ap; + int r; + + if (sig < 0) /* Empty list? */ + return -EINVAL; + + if (sigemptyset(&ss) < 0) + return -errno; + + /* Add first signal (if the signal is zero, we'll silently skip it, to make it easiert to build + * parameter lists where some element are sometimes off, similar to how sigset_add_many_ap() handles + * this.) */ + if (sig > 0 && sigaddset(&ss, sig) < 0) + return -errno; + + /* Add all other signals */ + va_start(ap, sig); + r = sigset_add_many_ap(&ss, ap); + va_end(ap); + if (r < 0) + return r; + + r = sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 }); + if (r < 0) { + if (errno == EAGAIN) + return 0; + + return -errno; + } + + return r; /* Returns the signal popped */ +} diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h index 37271d7a68..36372c19bd 100644 --- a/src/basic/signal-util.h +++ b/src/basic/signal-util.h @@ -62,3 +62,6 @@ static inline const char* signal_to_string_with_check(int n) { } int signal_is_blocked(int sig); + +int pop_pending_signal_internal(int sig, ...); +#define pop_pending_signal(...) pop_pending_signal_internal(__VA_ARGS__, -1) diff --git a/src/shared/copy.c b/src/shared/copy.c index 04f99a2d6f..65d75a15d5 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -24,6 +24,7 @@ #include "nulstr-util.h" #include "rm-rf.h" #include "selinux-util.h" +#include "signal-util.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" @@ -87,22 +88,6 @@ static int fd_is_nonblock_pipe(int fd) { return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE; } -static int sigint_pending(void) { - sigset_t ss; - - assert_se(sigemptyset(&ss) >= 0); - assert_se(sigaddset(&ss, SIGINT) >= 0); - - if (sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 }) < 0) { - if (errno == EAGAIN) - return false; - - return -errno; - } - - return true; -} - int copy_bytes_full( int fdf, int fdt, uint64_t max_bytes, @@ -192,7 +177,7 @@ int copy_bytes_full( return 1; /* return > 0 if we hit the max_bytes limit */ if (FLAGS_SET(copy_flags, COPY_SIGINT)) { - r = sigint_pending(); + r = pop_pending_signal(SIGINT); if (r < 0) return r; if (r > 0) @@ -861,7 +846,7 @@ static int fd_copy_directory( continue; if (FLAGS_SET(copy_flags, COPY_SIGINT)) { - r = sigint_pending(); + r = pop_pending_signal(SIGINT); if (r < 0) return r; if (r > 0) diff --git a/src/test/test-signal-util.c b/src/test/test-signal-util.c index 76ab9b8ad2..0586775902 100644 --- a/src/test/test-signal-util.c +++ b/src/test/test-signal-util.c @@ -133,11 +133,50 @@ static void test_ignore_signals(void) { assert_se(default_signals(SIGINT, SIGUSR1, SIGUSR2, SIGTERM, SIGPIPE) >= 0); } +static void test_pop_pending_signal(void) { + + assert_se(signal_is_blocked(SIGUSR1) == 0); + assert_se(signal_is_blocked(SIGUSR2) == 0); + assert_se(pop_pending_signal(SIGUSR1) == 0); + assert_se(pop_pending_signal(SIGUSR2) == 0); + + { + BLOCK_SIGNALS(SIGUSR1, SIGUSR2); + + assert_se(signal_is_blocked(SIGUSR1) > 0); + assert_se(signal_is_blocked(SIGUSR2) > 0); + + assert_se(pop_pending_signal(SIGUSR1) == 0); + assert_se(pop_pending_signal(SIGUSR2) == 0); + + assert_se(raise(SIGUSR1) >= 0); + + assert_se(pop_pending_signal(SIGUSR2) == 0); + assert_se(pop_pending_signal(SIGUSR1) == SIGUSR1); + assert_se(pop_pending_signal(SIGUSR1) == 0); + + assert_se(raise(SIGUSR1) >= 0); + assert_se(raise(SIGUSR2) >= 0); + + assert_cc(SIGUSR1 < SIGUSR2); + + assert_se(pop_pending_signal(SIGUSR1, SIGUSR2) == SIGUSR1); + assert_se(pop_pending_signal(SIGUSR1, SIGUSR2) == SIGUSR2); + assert_se(pop_pending_signal(SIGUSR1, SIGUSR2) == 0); + } + + assert_se(signal_is_blocked(SIGUSR1) == 0); + assert_se(signal_is_blocked(SIGUSR2) == 0); + assert_se(pop_pending_signal(SIGUSR1) == 0); + assert_se(pop_pending_signal(SIGUSR2) == 0); +} + int main(int argc, char *argv[]) { test_rt_signals(); test_signal_from_string(); test_block_signals(); test_ignore_signals(); + test_pop_pending_signal(); return 0; }