/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "time-util.h" #include "ioloop.h" #include "lib-signals.h" #include #include struct test_context_delayed { bool timed_out:1; bool signal_handled:1; }; static void kill_timeout(struct test_context_delayed *tctx ATTR_UNUSED) { if (kill(getpid(), SIGALRM) < 0) i_fatal("Failed to send signal: %m"); } static void test_timeout(struct test_context_delayed *tctx) { tctx->timed_out = TRUE; io_loop_stop(current_ioloop); } static void signal_handler_delayed(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { struct test_context_delayed *tctx = (struct test_context_delayed *)context; tctx->signal_handled = TRUE; io_loop_stop(current_ioloop); } static void test_lib_signals_delayed(void) { struct test_context_delayed tctx; struct timeout *to_kill, *to_test; struct ioloop *ioloop; test_begin("lib-signals delayed - init lib-signals first"); i_zero(&tctx); lib_signals_init(); lib_signals_set_handler(SIGALRM, LIBSIG_FLAGS_SAFE | LIBSIG_FLAG_IOLOOP_AUTOMOVE, signal_handler_delayed, &tctx); ioloop = io_loop_create(); to_kill = timeout_add_short(200, kill_timeout, &tctx); to_test = timeout_add_short(400, test_timeout, &tctx); io_loop_run(ioloop); timeout_remove(&to_kill); timeout_remove(&to_test); io_loop_destroy(&ioloop); lib_signals_deinit(); test_assert(!tctx.timed_out); test_assert(tctx.signal_handled); test_end(); test_begin("lib-signals delayed - init ioloop first"); i_zero(&tctx); ioloop = io_loop_create(); lib_signals_init(); lib_signals_set_handler(SIGALRM, LIBSIG_FLAGS_SAFE | LIBSIG_FLAG_IOLOOP_AUTOMOVE, signal_handler_delayed, &tctx); to_kill = timeout_add_short(200, kill_timeout, &tctx); to_test = timeout_add_short(400, test_timeout, &tctx); io_loop_run(ioloop); timeout_remove(&to_kill); timeout_remove(&to_test); lib_signals_deinit(); io_loop_destroy(&ioloop); test_assert(!tctx.timed_out); test_assert(tctx.signal_handled); test_end(); } static void test_lib_signals_delayed_nested_ioloop(void) { struct test_context_delayed tctx; struct timeout *to_kill, *to_test; struct ioloop *ioloop1, *ioloop2; test_begin("lib-signals delayed in nested ioloop"); i_zero(&tctx); lib_signals_init(); lib_signals_set_handler(SIGALRM, LIBSIG_FLAGS_SAFE | LIBSIG_FLAG_IOLOOP_AUTOMOVE, signal_handler_delayed, &tctx); /* briefly run outer ioloop */ ioloop1 = io_loop_create(); to_test = timeout_add_short(100, test_timeout, &tctx); io_loop_run(ioloop1); timeout_remove(&to_test); test_assert(tctx.timed_out); test_assert(!tctx.signal_handled); tctx.timed_out = FALSE; /* run inner ioloop, which triggers the signal */ ioloop2 = io_loop_create(); to_kill = timeout_add_short(200, kill_timeout, &tctx); to_test = timeout_add_short(400, test_timeout, &tctx); io_loop_run(ioloop2); timeout_remove(&to_kill); timeout_remove(&to_test); io_loop_destroy(&ioloop2); io_loop_destroy(&ioloop1); lib_signals_deinit(); test_assert(!tctx.timed_out); test_assert(tctx.signal_handled); test_end(); } static void test_lib_signals_delayed_no_ioloop_automove(void) { struct test_context_delayed tctx; struct timeout *to_kill, *to_test; struct ioloop *ioloop1, *ioloop2; test_begin("lib-signals delayed with NO_IOLOOP_AUTOMOVE - unmoved"); i_zero(&tctx); ioloop1 = io_loop_create(); lib_signals_init(); lib_signals_set_handler(SIGALRM, LIBSIG_FLAGS_SAFE, signal_handler_delayed, &tctx); /* briefly run outer ioloop */ to_test = timeout_add_short(100, test_timeout, &tctx); io_loop_run(ioloop1); timeout_remove(&to_test); test_assert(tctx.timed_out); test_assert(!tctx.signal_handled); tctx.timed_out = FALSE; /* run inner ioloop, which triggers the signal but musn't handle it */ ioloop2 = io_loop_create(); to_kill = timeout_add_short(200, kill_timeout, &tctx); to_test = timeout_add_short(400, test_timeout, &tctx); io_loop_run(ioloop2); test_assert(tctx.timed_out); test_assert(!tctx.signal_handled); tctx.timed_out = FALSE; timeout_remove(&to_kill); timeout_remove(&to_test); io_loop_destroy(&ioloop2); /* run outer ioloop once more */ to_test = timeout_add_short(100, test_timeout, &tctx); io_loop_run(ioloop1); timeout_remove(&to_test); lib_signals_deinit(); io_loop_destroy(&ioloop1); test_assert(!tctx.timed_out); test_assert(tctx.signal_handled); test_end(); test_begin("lib-signals delayed with NO_IOLOOP_AUTOMOVE - moved"); i_zero(&tctx); ioloop1 = io_loop_create(); lib_signals_init(); lib_signals_set_handler(SIGALRM, LIBSIG_FLAGS_SAFE, signal_handler_delayed, &tctx); /* briefly run outer ioloop */ to_test = timeout_add_short(100, test_timeout, &tctx); io_loop_run(ioloop1); timeout_remove(&to_test); test_assert(tctx.timed_out); test_assert(!tctx.signal_handled); tctx.timed_out = FALSE; /* run inner ioloop, which triggers the signal */ ioloop2 = io_loop_create(); lib_signals_switch_ioloop(SIGALRM, signal_handler_delayed, &tctx); to_kill = timeout_add_short(200, kill_timeout, &tctx); to_test = timeout_add_short(400, test_timeout, &tctx); io_loop_run(ioloop2); test_assert(!tctx.timed_out); test_assert(tctx.signal_handled); timeout_remove(&to_kill); timeout_remove(&to_test); io_loop_destroy(&ioloop2); lib_signals_deinit(); io_loop_destroy(&ioloop1); test_end(); } void test_lib_signals(void) { test_lib_signals_delayed(); test_lib_signals_delayed_nested_ioloop(); test_lib_signals_delayed_no_ioloop_automove(); }