test-subprocess.c (dovecot-2.3.16) | : | test-subprocess.c (dovecot-2.3.17) | ||
---|---|---|---|---|
/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ | /* Copyright (c) 2020 Dovecot authors, see the included COPYING file */ | |||
#include "lib.h" | #include "lib.h" | |||
#include "lib-signals.h" | #include "lib-signals.h" | |||
#include "hostpid.h" | #include "hostpid.h" | |||
#include "array.h" | #include "array.h" | |||
#include "ioloop.h" | #include "ioloop.h" | |||
#include "sleep.h" | ||||
#include "test-common.h" | #include "test-common.h" | |||
#include "test-subprocess.h" | #include "test-subprocess.h" | |||
#include <sys/types.h> | #include <sys/types.h> | |||
#include <sys/wait.h> | #include <sys/wait.h> | |||
#include <unistd.h> | #include <unistd.h> | |||
struct test_subprocess { | struct test_subprocess { | |||
pid_t pid; | pid_t pid; | |||
}; | }; | |||
volatile sig_atomic_t test_subprocess_is_child = 0; | volatile sig_atomic_t test_subprocess_is_child = 0; | |||
static bool test_subprocess_lib_init = FALSE; | static bool test_subprocess_lib_init = FALSE; | |||
static volatile bool test_subprocess_notification_signal_received[SIGUSR1 + 1]; | ||||
static struct event *test_subprocess_event = NULL; | static struct event *test_subprocess_event = NULL; | |||
static ARRAY(struct test_subprocess *) test_subprocesses = ARRAY_INIT; | static ARRAY(struct test_subprocess *) test_subprocesses = ARRAY_INIT; | |||
static void (*test_subprocess_cleanup_callback)(void) = NULL; | static void (*test_subprocess_cleanup_callback)(void) = NULL; | |||
static void | static void | |||
test_subprocess_signal(const siginfo_t *si ATTR_UNUSED, | test_subprocess_signal(const siginfo_t *si ATTR_UNUSED, | |||
void *context ATTR_UNUSED) | void *context ATTR_UNUSED) | |||
{ | { | |||
io_loop_stop(current_ioloop); | io_loop_stop(current_ioloop); | |||
} | } | |||
skipping to change at line 87 | skipping to change at line 89 | |||
test_exit((test_has_failed() ? 1 : 0)); | test_exit((test_has_failed() ? 1 : 0)); | |||
} | } | |||
#undef test_subprocess_fork | #undef test_subprocess_fork | |||
void test_subprocess_fork(int (*func)(void *context), void *context, | void test_subprocess_fork(int (*func)(void *context), void *context, | |||
bool continue_test) | bool continue_test) | |||
{ | { | |||
struct test_subprocess *subprocess; | struct test_subprocess *subprocess; | |||
subprocess = i_new(struct test_subprocess, 1); | subprocess = i_new(struct test_subprocess, 1); | |||
array_push_back(&test_subprocesses, &subprocess); | ||||
lib_signals_ioloop_detach(); | lib_signals_ioloop_detach(); | |||
/* avoid races: fork the child process with test_subprocess_is_child | ||||
set to 1 in case it immediately receives a signal. */ | ||||
test_subprocess_is_child = 1; | ||||
if ((subprocess->pid = fork()) == (pid_t)-1) | if ((subprocess->pid = fork()) == (pid_t)-1) | |||
i_fatal("test: sub-process: fork() failed: %m"); | i_fatal("test: sub-process: fork() failed: %m"); | |||
if (subprocess->pid == 0) { | if (subprocess->pid == 0) { | |||
test_subprocess_is_child = 1; | ||||
test_subprocess_free_all(); | test_subprocess_free_all(); | |||
test_subprocess_child(func, context, continue_test); | test_subprocess_child(func, context, continue_test); | |||
i_unreached(); | i_unreached(); | |||
} | } | |||
test_subprocess_is_child = 0; | ||||
array_push_back(&test_subprocesses, &subprocess); | ||||
lib_signals_ioloop_attach(); | lib_signals_ioloop_attach(); | |||
} | } | |||
static void test_subprocess_verify_exit_status(int status) | static void test_subprocess_verify_exit_status(int status) | |||
{ | { | |||
test_out_quiet("sub-process ended properly", | test_out_quiet("sub-process ended properly", | |||
WIFEXITED(status) && WEXITSTATUS(status) == 0); | WIFEXITED(status) && WEXITSTATUS(status) == 0); | |||
if (WIFEXITED(status)) { | if (WIFEXITED(status)) { | |||
if (WEXITSTATUS(status) != 0) { | if (WEXITSTATUS(status) != 0) { | |||
e_warning(test_subprocess_event, | e_warning(test_subprocess_event, | |||
skipping to change at line 131 | skipping to change at line 136 | |||
WSTOPSIG(status)); | WSTOPSIG(status)); | |||
} else { | } else { | |||
e_warning(test_subprocess_event, | e_warning(test_subprocess_event, | |||
"Sub-process terminated abnormally with status %d", | "Sub-process terminated abnormally with status %d", | |||
status); | status); | |||
} | } | |||
} | } | |||
static void test_subprocess_kill_forced(struct test_subprocess *subp) | static void test_subprocess_kill_forced(struct test_subprocess *subp) | |||
{ | { | |||
i_assert(subp->pid > 0); | ||||
(void)kill(subp->pid, SIGKILL); | (void)kill(subp->pid, SIGKILL); | |||
(void)waitpid(subp->pid, NULL, 0); | (void)waitpid(subp->pid, NULL, 0); | |||
} | } | |||
void test_subprocess_kill_all(unsigned int timeout_secs) | void test_subprocess_kill_all(unsigned int timeout_secs) | |||
{ | { | |||
struct test_subprocess **subps; | struct test_subprocess **subps; | |||
unsigned int subps_count, subps_left, i; | unsigned int subps_count, subps_left, i; | |||
subps = array_get_modifiable(&test_subprocesses, &subps_count); | subps = array_get_modifiable(&test_subprocesses, &subps_count); | |||
skipping to change at line 284 | skipping to change at line 290 | |||
int signo = si->si_signo; | int signo = si->si_signo; | |||
if (terminating != 0) | if (terminating != 0) | |||
raise(signo); | raise(signo); | |||
terminating = 1; | terminating = 1; | |||
/* Perform important cleanups */ | /* Perform important cleanups */ | |||
test_subprocess_cleanup(); | test_subprocess_cleanup(); | |||
(void)signal(signo, SIG_DFL); | (void)signal(signo, SIG_DFL); | |||
raise(signo); | if (signo == SIGTERM) | |||
_exit(0); | ||||
else | ||||
raise(signo); | ||||
} | } | |||
static void test_atexit(void) | static void test_atexit(void) | |||
{ | { | |||
/* NOTICE: This is also called by children, so be careful. */ | /* NOTICE: This is also called by children, so be careful. */ | |||
/* Perform important cleanups */ | /* Perform important cleanups */ | |||
test_subprocess_cleanup(); | test_subprocess_cleanup(); | |||
} | } | |||
void test_subprocess_set_cleanup_callback(void (*callback)(void)) | void test_subprocess_set_cleanup_callback(void (*callback)(void)) | |||
{ | { | |||
test_subprocess_cleanup_callback = callback; | test_subprocess_cleanup_callback = callback; | |||
} | } | |||
void test_subprocess_notify_signal_send(int signo, pid_t pid) | ||||
{ | ||||
if (kill(pid, signo) < 0) | ||||
i_fatal("kill(%ld, SIGHUP) failed: %m", (long)pid); | ||||
} | ||||
void test_subprocess_notify_signal_send_parent(int signo) | ||||
{ | ||||
test_subprocess_notify_signal_send(signo, getppid()); | ||||
} | ||||
void test_subprocess_notify_signal_reset(int signo) | ||||
{ | ||||
i_assert(signo >= 0 && | ||||
(unsigned int)signo < N_ELEMENTS(test_subprocess_notification_si | ||||
gnal_received)); | ||||
test_subprocess_notification_signal_received[signo] = FALSE; | ||||
} | ||||
void test_subprocess_notify_signal_wait(int signo, unsigned int timeout_msecs) | ||||
{ | ||||
unsigned int i, count = timeout_msecs / 10; | ||||
for (i = 0; i < count; i++) { | ||||
if (test_subprocess_notification_signal_received[signo]) | ||||
return; | ||||
i_sleep_msecs(10); | ||||
} | ||||
i_fatal("Didn't receive wait notification signal from server"); | ||||
} | ||||
static void | ||||
test_subprocess_notification_signal(const siginfo_t *si, | ||||
void *context ATTR_UNUSED) | ||||
{ | ||||
int signo = si->si_signo; | ||||
i_assert(signo >= 0 && | ||||
(unsigned int)signo < N_ELEMENTS(test_subprocess_notification_si | ||||
gnal_received)); | ||||
test_subprocess_notification_signal_received[signo] = TRUE; | ||||
} | ||||
void test_subprocesses_init(bool debug) | void test_subprocesses_init(bool debug) | |||
{ | { | |||
if (!lib_is_initialized()) { | if (!lib_is_initialized()) { | |||
lib_init(); | lib_init(); | |||
test_subprocess_lib_init = TRUE; | test_subprocess_lib_init = TRUE; | |||
} | } | |||
lib_signals_init(); | lib_signals_init(); | |||
atexit(test_atexit); | atexit(test_atexit); | |||
lib_signals_ignore(SIGPIPE, TRUE); | lib_signals_ignore(SIGPIPE, TRUE); | |||
lib_signals_set_handler(SIGALRM, 0, test_subprocess_alarm, NULL); | lib_signals_set_handler(SIGALRM, 0, test_subprocess_alarm, NULL); | |||
lib_signals_set_handler(SIGTERM, 0, test_subprocess_terminate, NULL); | lib_signals_set_handler(SIGTERM, 0, test_subprocess_terminate, NULL); | |||
lib_signals_set_handler(SIGQUIT, 0, test_subprocess_terminate, NULL); | lib_signals_set_handler(SIGQUIT, 0, test_subprocess_terminate, NULL); | |||
lib_signals_set_handler(SIGINT, 0, test_subprocess_terminate, NULL); | lib_signals_set_handler(SIGINT, 0, test_subprocess_terminate, NULL); | |||
lib_signals_set_handler(SIGSEGV, 0, test_subprocess_terminate, NULL); | lib_signals_set_handler(SIGSEGV, 0, test_subprocess_terminate, NULL); | |||
lib_signals_set_handler(SIGABRT, 0, test_subprocess_terminate, NULL); | lib_signals_set_handler(SIGABRT, 0, test_subprocess_terminate, NULL); | |||
lib_signals_set_handler(SIGHUP, LIBSIG_FLAG_RESTART, | ||||
test_subprocess_notification_signal, NULL); | ||||
lib_signals_set_handler(SIGUSR1, LIBSIG_FLAG_RESTART, | ||||
test_subprocess_notification_signal, NULL); | ||||
i_array_init(&test_subprocesses, 8); | i_array_init(&test_subprocesses, 8); | |||
test_subprocess_event = event_create(NULL); | test_subprocess_event = event_create(NULL); | |||
event_set_forced_debug(test_subprocess_event, debug); | event_set_forced_debug(test_subprocess_event, debug); | |||
event_set_append_log_prefix(test_subprocess_event, "test: "); | event_set_append_log_prefix(test_subprocess_event, "test: "); | |||
} | } | |||
void test_subprocesses_deinit(void) | void test_subprocesses_deinit(void) | |||
{ | { | |||
End of changes. 11 change blocks. | ||||
3 lines changed or deleted | 59 lines changed or added |