From f85c1874594a92caed396cf91603072d2951b21b Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Tue, 19 Jul 2022 22:19:51 -0500 Subject: [PATCH] Feature: Add missing signal functions f_signal_action(), f_signal_pause(), and f_signal_suspend(). The f_signal_pause() just calls pause() and returns F_none. The return value of pause() is meaningless if I am understanding the man pages correctly. It should always return -1 and then sets errno despite this not being an actual error. --- level_0/f_signal/c/signal.c | 45 +++++++++ level_0/f_signal/c/signal.h | 60 +++++++++++ level_0/f_signal/data/build/settings-mocks | 3 + level_0/f_signal/data/build/settings-tests | 2 +- level_0/f_signal/tests/unit/c/mock-signal.c | 31 ++++++ level_0/f_signal/tests/unit/c/mock-signal.h | 3 + level_0/f_signal/tests/unit/c/test-signal-action.c | 111 +++++++++++++++++++++ level_0/f_signal/tests/unit/c/test-signal-action.h | 34 +++++++ level_0/f_signal/tests/unit/c/test-signal-pause.c | 19 ++++ level_0/f_signal/tests/unit/c/test-signal-pause.h | 20 ++++ .../f_signal/tests/unit/c/test-signal-suspend.c | 65 ++++++++++++ .../f_signal/tests/unit/c/test-signal-suspend.h | 34 +++++++ level_0/f_signal/tests/unit/c/test-signal.c | 11 ++ level_0/f_signal/tests/unit/c/test-signal.h | 3 + 14 files changed, 440 insertions(+), 1 deletion(-) create mode 100644 level_0/f_signal/tests/unit/c/test-signal-action.c create mode 100644 level_0/f_signal/tests/unit/c/test-signal-action.h create mode 100644 level_0/f_signal/tests/unit/c/test-signal-pause.c create mode 100644 level_0/f_signal/tests/unit/c/test-signal-pause.h create mode 100644 level_0/f_signal/tests/unit/c/test-signal-suspend.c create mode 100644 level_0/f_signal/tests/unit/c/test-signal-suspend.h diff --git a/level_0/f_signal/c/signal.c b/level_0/f_signal/c/signal.c index 664d4b3..06d9e76 100644 --- a/level_0/f_signal/c/signal.c +++ b/level_0/f_signal/c/signal.c @@ -4,6 +4,23 @@ extern "C" { #endif +#ifndef _di_f_signal_action_ + f_status_t f_signal_action(const f_signal_t signal, const struct sigaction * const action, struct sigaction *previous) { + #ifndef _di_level_0_parameter_checking_ + if (!action && !previous) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (sigaction(signal.id, action, previous) == -1) { + if (errno == EFAULT) return F_status_set_error(F_buffer); + if (errno == EINVAL) return F_status_set_error(F_parameter); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // _di_f_signal_action_ + #ifndef _di_f_signal_close_ f_status_t f_signal_close(f_signal_t * const signal) { #ifndef _di_level_0_parameter_checking_ @@ -71,6 +88,15 @@ extern "C" { } #endif // _di_f_signal_open_ +#ifndef _di_f_signal_pause_ + f_status_t f_signal_pause(void) { + + pause(); + + return F_none; + } +#endif // _di_f_signal_pause_ + #ifndef _di_f_signal_queue_ f_status_t f_signal_queue(const pid_t id, const int signal, const union sigval value) { @@ -244,6 +270,25 @@ extern "C" { } #endif // _di_f_signal_set_has_ +#ifndef _di_f_signal_suspend_ + f_status_t f_signal_suspend(const sigset_t * const mask) { + #ifndef _di_level_0_parameter_checking_ + if (!mask) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + const int result = sigsuspend(mask); + + if (result == -1) { + if (errno == EFAULT) return F_status_set_error(F_buffer); + if (errno == EINTR) return F_status_set_error(F_interrupt); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // _di_f_signal_suspend_ + #ifndef _di_f_signal_wait_ f_status_t f_signal_wait(const sigset_t * const set, siginfo_t * const information) { diff --git a/level_0/f_signal/c/signal.h b/level_0/f_signal/c/signal.h index 6285271..c868866 100644 --- a/level_0/f_signal/c/signal.h +++ b/level_0/f_signal/c/signal.h @@ -29,6 +29,33 @@ extern "C" { #endif /** + * Get or set the signal action handlers. + * + * @param signal + * The signal settings. + * @param action + * (optional) The signal action to use. + * Set to NULL to not use. + * Both action and previous may not be NULL. + * @param previous + * (optional) The previous signal action. + * Set to NULL to not use. + * + * @return + * F_none on success but no signal found. + * + * F_buffer (with error bit) if the buffer is invalid (action or previous point to invalid memory). + * F_parameter (with error bit) if a parameter is invalid. + * + * F_failure (with error bit) for any other error. + * + * @see sigaction() + */ +#ifndef _di_f_signal_action_ + extern f_status_t f_signal_action(const f_signal_t signal, const struct sigaction * const action, struct sigaction *previous); +#endif // _di_f_signal_action_ + +/** * Close an open signal descriptor. * * The signal.id is set to 0 on success. @@ -39,6 +66,7 @@ extern "C" { * @return * F_none on success. * F_data_not on success, but no descriptor was provided to close. + * * F_descriptor (with error bit) if id is an invalid descriptor. * F_filesystem_quota_block (with error bit) if file system's disk blocks or inodes are exhausted. * F_input_output (with error bit) if an I/O error occurred. @@ -109,6 +137,18 @@ extern "C" { #endif // _di_f_signal_open_ /** + * Pause the current process until a signal is received. + * + * @return + * The received signal. + * + * @see pause() + */ +#ifndef _di_f_signal_pause_ + extern f_status_t f_signal_pause(void); +#endif // _di_f_signal_pause_ + +/** * Send the signal and value to the given process. * * @param id @@ -297,6 +337,26 @@ extern "C" { #endif // _di_f_signal_set_has_ /** + * Suspend the current process until one of the provided signals is received. + * + * @param mask + * The signal mask. + * + * @return + * F_none on success but no signal found. + * + * F_buffer (with error bit) if the mask is pointing to invalid memory. + * F_interrupt (with error bit) when program received an interrupt signal, halting operation. + * + * F_failure (with error bit) for any other error. + * + * @see sigsuspend() + */ +#ifndef _di_f_signal_suspend_ + extern f_status_t f_signal_suspend(const sigset_t * const mask); +#endif // _di_f_signal_suspend_ + +/** * Wait until any signal in a set of signals is received. * * @param set diff --git a/level_0/f_signal/data/build/settings-mocks b/level_0/f_signal/data/build/settings-mocks index 7d51975..7d16e7e 100644 --- a/level_0/f_signal/data/build/settings-mocks +++ b/level_0/f_signal/data/build/settings-mocks @@ -60,8 +60,10 @@ flags_library -fPIC # Inject mocks. flags -Wl,--wrap=close flags -Wl,--wrap=kill +flags -Wl,--wrap=pause flags -Wl,--wrap=poll flags -Wl,--wrap=read +flags -Wl,--wrap=sigaction flags -Wl,--wrap=sigaddset flags -Wl,--wrap=sigdelset flags -Wl,--wrap=sigemptyset @@ -70,5 +72,6 @@ flags -Wl,--wrap=sigismember flags -Wl,--wrap=signalfd flags -Wl,--wrap=sigprocmask flags -Wl,--wrap=sigqueue +flags -Wl,--wrap=sigsuspend flags -Wl,--wrap=sigtimedwait flags -Wl,--wrap=sigwaitinfo diff --git a/level_0/f_signal/data/build/settings-tests b/level_0/f_signal/data/build/settings-tests index bc5af73..81937d1 100644 --- a/level_0/f_signal/data/build/settings-tests +++ b/level_0/f_signal/data/build/settings-tests @@ -25,7 +25,7 @@ build_language c build_libraries -lc -lcmocka build_libraries-individual -lf_memory -lf_string -lf_signal -build_sources_program test-signal-close.c test-signal-mask.c test-signal-open.c test-signal-queue.c test-signal-read.c test-signal-send.c test-signal-set_add.c test-signal-set_delete.c test-signal-set_empty.c test-signal-set_fill.c test-signal-set_has.c test-signal-wait.c test-signal-wait_until.c +build_sources_program test-signal-action.c test-signal-close.c test-signal-mask.c test-signal-open.c test-signal-pause.c test-signal-queue.c test-signal-read.c test-signal-send.c test-signal-set_add.c test-signal-set_delete.c test-signal-set_empty.c test-signal-set_fill.c test-signal-set_has.c test-signal-suspend.c test-signal-wait.c test-signal-wait_until.c build_sources_program test-signal.c build_script no diff --git a/level_0/f_signal/tests/unit/c/mock-signal.c b/level_0/f_signal/tests/unit/c/mock-signal.c index 6bf8978..c6eb63b 100644 --- a/level_0/f_signal/tests/unit/c/mock-signal.c +++ b/level_0/f_signal/tests/unit/c/mock-signal.c @@ -4,6 +4,19 @@ extern "C" { #endif +int __wrap_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { + + const bool failure = mock_type(bool); + + if (failure) { + errno = mock_type(int); + + return -1; + } + + return 0; +} + int __wrap_close(int fd) { const bool failure = mock_type(bool); @@ -30,6 +43,11 @@ int __wrap_kill(pid_t pid, int sig) { return 0; } +int __wrap_pause(void) { + + return -1; +} + int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout) { const bool failure = mock_type(bool); @@ -162,6 +180,19 @@ int __wrap_sigqueue(pid_t pid, int sig, const union sigval value) { return 0; } +int __wrap_sigsuspend(const sigset_t *mask) { + + const bool failure = mock_type(bool); + + if (failure) { + errno = mock_type(int); + + return -1; + } + + return 0; +} + int __wrap_sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout) { const bool failure = mock_type(bool); diff --git a/level_0/f_signal/tests/unit/c/mock-signal.h b/level_0/f_signal/tests/unit/c/mock-signal.h index 2472577..cd27025 100644 --- a/level_0/f_signal/tests/unit/c/mock-signal.h +++ b/level_0/f_signal/tests/unit/c/mock-signal.h @@ -28,8 +28,10 @@ extern "C" { const static int mock_errno_generic = 32767; +extern int __wrap_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); extern int __wrap_close(int fd); extern int __wrap_kill(pid_t pid, int sig); +extern int __wrap_pause(void); extern int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout); extern ssize_t __wrap_read(int fd, void *buf, size_t count); extern int __wrap_sigaddset(sigset_t *set, int signum); @@ -40,6 +42,7 @@ extern int __wrap_sigismember(const sigset_t *set, int signum); extern int __wrap_signalfd(int fd, const sigset_t *mask, int flags); extern int __wrap_sigprocmask(int how, const sigset_t *set, sigset_t *oldset); extern int __wrap_sigqueue(pid_t pid, int sig, const union sigval value); +extern int __wrap_sigsuspend(const sigset_t *mask); extern int __wrap_sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout); extern int __wrap_sigwaitinfo(const sigset_t *set, siginfo_t *info); diff --git a/level_0/f_signal/tests/unit/c/test-signal-action.c b/level_0/f_signal/tests/unit/c/test-signal-action.c new file mode 100644 index 0000000..5e8ffab --- /dev/null +++ b/level_0/f_signal/tests/unit/c/test-signal-action.c @@ -0,0 +1,111 @@ +#include "test-signal.h" +#include "test-signal-action.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_signal_action__fails(void **state) { + + f_signal_t signal = f_signal_t_initialize; + + struct sigaction action; + struct sigaction previous; + + memset(&action, 0, sizeof(struct sigaction)); + memset(&previous, 0, sizeof(struct sigaction)); + + { + int errnos[] = { + EFAULT, + EINVAL, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_buffer, + F_parameter, + F_failure, + }; + + for (int i = 0; i < 3; ++i) { + + will_return(__wrap_sigaction, true); + will_return(__wrap_sigaction, errnos[i]); + + const f_status_t status = f_signal_action(signal, &action, 0); + + assert_int_equal(status, F_status_set_error(statuss[i])); + } // for + + for (int i = 0; i < 3; ++i) { + + will_return(__wrap_sigaction, true); + will_return(__wrap_sigaction, errnos[i]); + + const f_status_t status = f_signal_action(signal, 0, &previous); + + assert_int_equal(status, F_status_set_error(statuss[i])); + } // for + + for (int i = 0; i < 3; ++i) { + + will_return(__wrap_sigaction, true); + will_return(__wrap_sigaction, errnos[i]); + + const f_status_t status = f_signal_action(signal, &action, &previous); + + assert_int_equal(status, F_status_set_error(statuss[i])); + } // for + } +} + +void test__f_signal_action__parameter_checking(void **state) { + + f_signal_t signal = f_signal_t_initialize; + + { + const f_status_t status = f_signal_action(signal, 0, 0); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } +} + +void test__f_signal_action__works(void **state) { + + f_signal_t signal = f_signal_t_initialize; + + struct sigaction action; + struct sigaction previous; + + memset(&action, 0, sizeof(struct sigaction)); + memset(&previous, 0, sizeof(struct sigaction)); + + { + will_return(__wrap_sigaction, false); + + const f_status_t status = f_signal_action(signal, &action, 0); + + assert_int_equal(status, F_none); + } + + { + will_return(__wrap_sigaction, false); + + const f_status_t status = f_signal_action(signal, 0, &previous); + + assert_int_equal(status, F_none); + } + + { + will_return(__wrap_sigaction, false); + + const f_status_t status = f_signal_action(signal, &action, &previous); + + assert_int_equal(status, F_none); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_signal/tests/unit/c/test-signal-action.h b/level_0/f_signal/tests/unit/c/test-signal-action.h new file mode 100644 index 0000000..23eda75 --- /dev/null +++ b/level_0/f_signal/tests/unit/c/test-signal-action.h @@ -0,0 +1,34 @@ +/** + * FLL - Level 0 + * + * Project: Signal + * API Version: 0.6 + * Licenses: lgpl-2.1-or-later + * + * Test the signal project. + */ +#ifndef _TEST__F_signal_action_h +#define _TEST__F_signal_action_h + +/** + * Test that function fails. + * + * @see f_signal_action() + */ +extern void test__f_signal_action__fails(void **state); + +/** + * Test that parameter checking works as expected. + * + * @see f_signal_action() + */ +extern void test__f_signal_action__parameter_checking(void **state); + +/** + * Test that function works. + * + * @see f_signal_action() + */ +extern void test__f_signal_action__works(void **state); + +#endif // _TEST__F_signal_action_h diff --git a/level_0/f_signal/tests/unit/c/test-signal-pause.c b/level_0/f_signal/tests/unit/c/test-signal-pause.c new file mode 100644 index 0000000..582616b --- /dev/null +++ b/level_0/f_signal/tests/unit/c/test-signal-pause.c @@ -0,0 +1,19 @@ +#include "test-signal.h" +#include "test-signal-pause.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_signal_pause__works(void **state) { + + { + const f_status_t status = f_signal_pause(); + + assert_int_equal(status, F_none); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_signal/tests/unit/c/test-signal-pause.h b/level_0/f_signal/tests/unit/c/test-signal-pause.h new file mode 100644 index 0000000..3a4fd73 --- /dev/null +++ b/level_0/f_signal/tests/unit/c/test-signal-pause.h @@ -0,0 +1,20 @@ +/** + * FLL - Level 0 + * + * Project: Signal + * API Version: 0.6 + * Licenses: lgpl-2.1-or-later + * + * Test the signal project. + */ +#ifndef _TEST__F_signal_pause_h +#define _TEST__F_signal_pause_h + +/** + * Test that function works. + * + * @see f_signal_pause() + */ +extern void test__f_signal_pause__works(void **state); + +#endif // _TEST__F_signal_pause_h diff --git a/level_0/f_signal/tests/unit/c/test-signal-suspend.c b/level_0/f_signal/tests/unit/c/test-signal-suspend.c new file mode 100644 index 0000000..232a465 --- /dev/null +++ b/level_0/f_signal/tests/unit/c/test-signal-suspend.c @@ -0,0 +1,65 @@ +#include "test-signal.h" +#include "test-signal-suspend.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_signal_suspend__fails(void **state) { + + sigset_t set; + + memset(&set, 0, sizeof(sigset_t)); + + { + int errnos[] = { + EFAULT, + EINTR, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_buffer, + F_interrupt, + F_failure, + }; + + for (int i = 0; i < 3; ++i) { + + will_return(__wrap_sigsuspend, true); + will_return(__wrap_sigsuspend, errnos[i]); + + const f_status_t status = f_signal_suspend(&set); + + assert_int_equal(status, F_status_set_error(statuss[i])); + } // for + } +} + +void test__f_signal_suspend__parameter_checking(void **state) { + + { + const f_status_t status = f_signal_suspend(0); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } +} + +void test__f_signal_suspend__works(void **state) { + + { + sigset_t set; + + memset(&set, 0, sizeof(sigset_t)); + + will_return(__wrap_sigsuspend, false); + + const f_status_t status = f_signal_suspend(&set); + + assert_int_equal(status, F_none); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_signal/tests/unit/c/test-signal-suspend.h b/level_0/f_signal/tests/unit/c/test-signal-suspend.h new file mode 100644 index 0000000..80c0f05 --- /dev/null +++ b/level_0/f_signal/tests/unit/c/test-signal-suspend.h @@ -0,0 +1,34 @@ +/** + * FLL - Level 0 + * + * Project: Signal + * API Version: 0.6 + * Licenses: lgpl-2.1-or-later + * + * Test the signal project. + */ +#ifndef _TEST__F_signal_suspend_h +#define _TEST__F_signal_suspend_h + +/** + * Test that function fails. + * + * @see f_signal_suspend() + */ +extern void test__f_signal_suspend__fails(void **state); + +/** + * Test that parameter checking works as expected. + * + * @see f_signal_suspend() + */ +extern void test__f_signal_suspend__parameter_checking(void **state); + +/** + * Test that function works. + * + * @see f_signal_suspend() + */ +extern void test__f_signal_suspend__works(void **state); + +#endif // _TEST__F_signal_suspend_h diff --git a/level_0/f_signal/tests/unit/c/test-signal.c b/level_0/f_signal/tests/unit/c/test-signal.c index c49b5de..6e61a62 100644 --- a/level_0/f_signal/tests/unit/c/test-signal.c +++ b/level_0/f_signal/tests/unit/c/test-signal.c @@ -19,6 +19,9 @@ int setdown(void **state) { int main(void) { const struct CMUnitTest tests[] = { + cmocka_unit_test(test__f_signal_action__fails), + cmocka_unit_test(test__f_signal_action__works), + cmocka_unit_test(test__f_signal_close__fails), cmocka_unit_test(test__f_signal_close__works), @@ -28,6 +31,8 @@ int main(void) { cmocka_unit_test(test__f_signal_open__fails), cmocka_unit_test(test__f_signal_open__works), + cmocka_unit_test(test__f_signal_pause__works), + cmocka_unit_test(test__f_signal_queue__fails), cmocka_unit_test(test__f_signal_queue__works), @@ -52,6 +57,9 @@ int main(void) { cmocka_unit_test(test__f_signal_set_has__fails), cmocka_unit_test(test__f_signal_set_has__works), + cmocka_unit_test(test__f_signal_suspend__fails), + cmocka_unit_test(test__f_signal_suspend__works), + cmocka_unit_test(test__f_signal_wait__fails), cmocka_unit_test(test__f_signal_wait__works), @@ -59,9 +67,11 @@ int main(void) { cmocka_unit_test(test__f_signal_wait_until__works), #ifndef _di_level_0_parameter_checking_ + cmocka_unit_test(test__f_signal_action__parameter_checking), cmocka_unit_test(test__f_signal_close__parameter_checking), cmocka_unit_test(test__f_signal_mask__parameter_checking), cmocka_unit_test(test__f_signal_open__parameter_checking), + // f_signal_pause() doesn't use parameter checking. // f_signal_queue() doesn't use parameter checking. cmocka_unit_test(test__f_signal_read__parameter_checking), // f_signal_send() doesn't use parameter checking. @@ -70,6 +80,7 @@ int main(void) { cmocka_unit_test(test__f_signal_set_empty__parameter_checking), cmocka_unit_test(test__f_signal_set_fill__parameter_checking), cmocka_unit_test(test__f_signal_set_has__parameter_checking), + cmocka_unit_test(test__f_signal_suspend__parameter_checking), // f_signal_wait() doesn't use parameter checking. // f_signal_wait_until() doesn't use parameter checking. #endif // _di_level_0_parameter_checking_ diff --git a/level_0/f_signal/tests/unit/c/test-signal.h b/level_0/f_signal/tests/unit/c/test-signal.h index 51b7023..b37e83e 100644 --- a/level_0/f_signal/tests/unit/c/test-signal.h +++ b/level_0/f_signal/tests/unit/c/test-signal.h @@ -26,9 +26,11 @@ #include "mock-signal.h" // Test includes. +#include "test-signal-action.h" #include "test-signal-close.h" #include "test-signal-mask.h" #include "test-signal-open.h" +#include "test-signal-pause.h" #include "test-signal-queue.h" #include "test-signal-read.h" #include "test-signal-send.h" @@ -37,6 +39,7 @@ #include "test-signal-set_empty.h" #include "test-signal-set_fill.h" #include "test-signal-set_has.h" +#include "test-signal-suspend.h" #include "test-signal-wait.h" #include "test-signal-wait_until.h" -- 1.8.3.1