tests: Exercise g_fdwalk_set_cloexec() and g_closefrom()

Signed-off-by: Simon McVittie <smcv@collabora.com>
This commit is contained in:
Simon McVittie 2024-02-05 20:29:47 +00:00
parent a7d5a6b69a
commit f4afed90e6

View File

@ -36,6 +36,187 @@
#include "testutils.h"
static void
async_signal_safe_message (const char *message)
{
if (write (2, message, strlen (message)) < 0 ||
write (2, "\n", 1) < 0)
{
/* ignore: not much we can do */
}
}
static void
test_closefrom (void)
{
/* Enough file descriptors to be confident that we're operating on
* all of them */
const int N_FDS = 20;
int *fds;
int fd;
int i;
pid_t child;
int wait_status;
/* The loop that populates @fds with pipes assumes this */
g_assert (N_FDS % 2 == 0);
g_test_summary ("Test g_closefrom(), g_fdwalk_set_cloexec()");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3247");
for (fd = 0; fd <= 2; fd++)
{
int flags;
g_assert_no_errno ((flags = fcntl (fd, F_GETFD)));
g_assert_no_errno (fcntl (fd, F_SETFD, flags & ~FD_CLOEXEC));
}
fds = g_new0 (int, N_FDS);
for (i = 0; i < N_FDS; i += 2)
{
GError *error = NULL;
int pipefd[2];
int res;
/* Intentionally neither O_CLOEXEC nor FD_CLOEXEC */
res = g_unix_open_pipe (pipefd, 0, &error);
g_assert (res);
g_assert_no_error (error);
g_clear_error (&error);
fds[i] = pipefd[0];
fds[i + 1] = pipefd[1];
}
child = fork ();
/* Child process exits with status = 100 + the first wrong fd,
* or 0 if all were correct */
if (child == 0)
{
for (i = 0; i < N_FDS; i++)
{
int flags = fcntl (fds[i], F_GETFD);
if (flags == -1)
{
async_signal_safe_message ("fd should not have been closed");
_exit (100 + fds[i]);
}
if (flags & FD_CLOEXEC)
{
async_signal_safe_message ("fd should not have been close-on-exec yet");
_exit (100 + fds[i]);
}
}
g_fdwalk_set_cloexec (3);
for (i = 0; i < N_FDS; i++)
{
int flags = fcntl (fds[i], F_GETFD);
if (flags == -1)
{
async_signal_safe_message ("fd should not have been closed");
_exit (100 + fds[i]);
}
if (!(flags & FD_CLOEXEC))
{
async_signal_safe_message ("fd should have been close-on-exec");
_exit (100 + fds[i]);
}
}
g_closefrom (3);
for (fd = 0; fd <= 2; fd++)
{
int flags = fcntl (fd, F_GETFD);
if (flags == -1)
{
async_signal_safe_message ("fd should not have been closed");
_exit (100 + fd);
}
if (flags & FD_CLOEXEC)
{
async_signal_safe_message ("fd should not have been close-on-exec");
_exit (100 + fd);
}
}
for (i = 0; i < N_FDS; i++)
{
if (fcntl (fds[i], F_GETFD) != -1 || errno != EBADF)
{
async_signal_safe_message ("fd should have been closed");
_exit (100 + fds[i]);
}
}
_exit (0);
}
g_assert_no_errno (waitpid (child, &wait_status, 0));
if (WIFEXITED (wait_status))
{
int exit_status = WEXITSTATUS (wait_status);
if (exit_status != 0)
g_test_fail_printf ("File descriptor %d in incorrect state", exit_status - 100);
}
else
{
g_test_fail_printf ("Unexpected wait status %d", wait_status);
}
for (i = 0; i < N_FDS; i++)
{
GError *error = NULL;
g_close (fds[i], &error);
g_assert_no_error (error);
g_clear_error (&error);
}
g_free (fds);
if (g_test_undefined ())
{
g_test_trap_subprocess ("/glib-unix/closefrom/subprocess/einval",
0, G_TEST_SUBPROCESS_DEFAULT);
g_test_trap_assert_passed ();
}
}
static void
test_closefrom_subprocess_einval (void)
{
int res;
int errsv;
g_log_set_always_fatal (G_LOG_FATAL_MASK);
g_log_set_fatal_mask ("GLib", G_LOG_FATAL_MASK);
errno = 0;
res = g_closefrom (-1);
errsv = errno;
g_assert_cmpint (res, ==, -1);
g_assert_cmpint (errsv, ==, EINVAL);
errno = 0;
res = g_fdwalk_set_cloexec (-42);
errsv = errno;
g_assert_cmpint (res, ==, -1);
g_assert_cmpint (errsv, ==, EINVAL);
}
static void
test_pipe (void)
{
@ -613,6 +794,9 @@ main (int argc,
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/glib-unix/closefrom", test_closefrom);
g_test_add_func ("/glib-unix/closefrom/subprocess/einval",
test_closefrom_subprocess_einval);
g_test_add_func ("/glib-unix/pipe", test_pipe);
g_test_add_func ("/glib-unix/pipe/fd-cloexec", test_pipe_fd_cloexec);
g_test_add_func ("/glib-unix/pipe-stdio-overwrite", test_pipe_stdio_overwrite);