|
|
#include "../../unity/unity.h" |
|
|
#include <errno.h> |
|
|
#include <fcntl.h> |
|
|
#include <signal.h> |
|
|
#include <stdbool.h> |
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <sys/stat.h> |
|
|
#include <sys/types.h> |
|
|
#include <sys/wait.h> |
|
|
#include <unistd.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int fd_is_closed(int fd) |
|
|
{ |
|
|
errno = 0; |
|
|
int flags = fcntl(fd, F_GETFL); |
|
|
return (flags == -1 && errno == EBADF); |
|
|
} |
|
|
|
|
|
static int save_stdio(int *saved_in, int *saved_out) |
|
|
{ |
|
|
*saved_in = dup(STDIN_FILENO); |
|
|
if (*saved_in < 0) return -1; |
|
|
*saved_out = dup(STDOUT_FILENO); |
|
|
if (*saved_out < 0) { close(*saved_in); return -1; } |
|
|
return 0; |
|
|
} |
|
|
|
|
|
static int restore_stdio(int saved_in, int saved_out) |
|
|
{ |
|
|
int ok = 0; |
|
|
if (saved_in >= 0) { |
|
|
if (dup2(saved_in, STDIN_FILENO) < 0) ok = -1; |
|
|
close(saved_in); |
|
|
} |
|
|
if (saved_out >= 0) { |
|
|
if (dup2(saved_out, STDOUT_FILENO) < 0) ok = -1; |
|
|
close(saved_out); |
|
|
} |
|
|
|
|
|
clearerr(stdout); |
|
|
return ok; |
|
|
} |
|
|
|
|
|
static int redirect_stdout_to_temp(int *tmpfd_out) |
|
|
{ |
|
|
char tmpl[] = "/tmp/dd_cleanup_test_XXXXXX"; |
|
|
int tfd = mkstemp(tmpl); |
|
|
if (tfd < 0) |
|
|
return -1; |
|
|
|
|
|
unlink(tmpl); |
|
|
if (dup2(tfd, STDOUT_FILENO) < 0) { |
|
|
close(tfd); |
|
|
return -1; |
|
|
} |
|
|
*tmpfd_out = tfd; |
|
|
return 0; |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_cleanup_interrupted_skips_sync_and_closes(void) |
|
|
{ |
|
|
int saved_in = -1, saved_out = -1; |
|
|
TEST_ASSERT_EQUAL_INT(0, save_stdio(&saved_in, &saved_out)); |
|
|
|
|
|
int old_mask = conversions_mask; |
|
|
sig_atomic_t old_intr = interrupt_signal; |
|
|
|
|
|
conversions_mask = C_FSYNC | C_FDATASYNC; |
|
|
interrupt_signal = SIGINT; |
|
|
|
|
|
|
|
|
cleanup(); |
|
|
|
|
|
int in_closed = fd_is_closed(STDIN_FILENO); |
|
|
int out_closed = fd_is_closed(STDOUT_FILENO); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, restore_stdio(saved_in, saved_out)); |
|
|
saved_in = saved_out = -1; |
|
|
|
|
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE(in_closed, "STDIN was not closed by cleanup when interrupted"); |
|
|
TEST_ASSERT_TRUE_MESSAGE(out_closed, "STDOUT was not closed by cleanup when interrupted"); |
|
|
|
|
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE((conversions_mask & (C_FSYNC | C_FDATASYNC)) == (C_FSYNC | C_FDATASYNC), |
|
|
"synchronize_output ran despite interrupt_signal; bits were cleared"); |
|
|
|
|
|
|
|
|
conversions_mask = old_mask; |
|
|
interrupt_signal = old_intr; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void test_cleanup_calls_sync_clears_bits_and_closes(void) |
|
|
{ |
|
|
int saved_in = -1, saved_out = -1; |
|
|
TEST_ASSERT_EQUAL_INT(0, save_stdio(&saved_in, &saved_out)); |
|
|
|
|
|
int tmpfd = -1; |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, redirect_stdout_to_temp(&tmpfd), "Failed to redirect stdout to temp file"); |
|
|
|
|
|
int old_mask = conversions_mask; |
|
|
sig_atomic_t old_intr = interrupt_signal; |
|
|
|
|
|
conversions_mask = C_FDATASYNC; |
|
|
interrupt_signal = 0; |
|
|
|
|
|
cleanup(); |
|
|
|
|
|
int in_closed = fd_is_closed(STDIN_FILENO); |
|
|
int out_closed = fd_is_closed(STDOUT_FILENO); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, restore_stdio(saved_in, saved_out)); |
|
|
saved_in = saved_out = -1; |
|
|
|
|
|
|
|
|
if (tmpfd >= 0) close(tmpfd); |
|
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE(in_closed, "STDIN was not closed by cleanup"); |
|
|
TEST_ASSERT_TRUE_MESSAGE(out_closed, "STDOUT was not closed by cleanup"); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, conversions_mask & (C_FSYNC | C_FDATASYNC), |
|
|
"synchronize_output did not clear sync bits"); |
|
|
|
|
|
|
|
|
conversions_mask = old_mask; |
|
|
interrupt_signal = old_intr; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_cleanup_exit_on_sync_failure(void) |
|
|
{ |
|
|
fflush(stdout); |
|
|
fflush(stderr); |
|
|
pid_t pid = fork(); |
|
|
TEST_ASSERT_MESSAGE(pid >= 0, "fork failed"); |
|
|
|
|
|
if (pid == 0) { |
|
|
|
|
|
int fds[2]; |
|
|
if (pipe(fds) != 0) _exit(100); |
|
|
|
|
|
if (dup2(fds[1], STDOUT_FILENO) < 0) _exit(101); |
|
|
close(fds[0]); |
|
|
close(fds[1]); |
|
|
|
|
|
conversions_mask = C_FSYNC; |
|
|
interrupt_signal = 0; |
|
|
|
|
|
|
|
|
cleanup(); |
|
|
|
|
|
|
|
|
_exit(77); |
|
|
} else { |
|
|
int status = 0; |
|
|
waitpid(pid, &status, 0); |
|
|
TEST_ASSERT_TRUE_MESSAGE(WIFEXITED(status), "Child did not exit normally"); |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(EXIT_FAILURE, WEXITSTATUS(status), |
|
|
"cleanup did not exit with failure on sync failure"); |
|
|
} |
|
|
} |
|
|
|
|
|
int main(void) |
|
|
{ |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_cleanup_interrupted_skips_sync_and_closes); |
|
|
RUN_TEST(test_cleanup_calls_sync_clears_bits_and_closes); |
|
|
RUN_TEST(test_cleanup_exit_on_sync_failure); |
|
|
return UNITY_END(); |
|
|
} |