diff options
author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2016-09-14 14:07:20 -0300 |
---|---|---|
committer | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2016-09-20 17:18:15 -0300 |
commit | 706e6749de9667f3f3763743a294d28f895f4fa9 (patch) | |
tree | 7494003744445d24f80373451027548d74607ff7 /nptl/tst-exec5.c | |
parent | aea5c83461dac53b8619b7bf2ef1fb348ecb4ef1 (diff) | |
download | glibc-706e6749de9667f3f3763743a294d28f895f4fa9.zip glibc-706e6749de9667f3f3763743a294d28f895f4fa9.tar.gz glibc-706e6749de9667f3f3763743a294d28f895f4fa9.tar.bz2 |
posix: Correctly enable/disable cancellation on Linux posix_spawn
This patch correctly enable and disable asynchronous cancellation on
Linux posix_spawn. Current code invert the logic by enabling and
disabling instead. It also adds a new test to check if posix_spawn
is not a cancellation entrypoint.
Checked on x86_64, i686, powerpc64le, and aarch64.
* nptl/Makefile (tests): Add tst-exec5.
* nptl/tst-exec5.c: New file.
* sysdeps/unix/sysv/linux/spawni.c (__spawni): Correctly enable and disable
asynchronous cancellation.
Diffstat (limited to 'nptl/tst-exec5.c')
-rw-r--r-- | nptl/tst-exec5.c | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/nptl/tst-exec5.c b/nptl/tst-exec5.c new file mode 100644 index 0000000..a2e7e94 --- /dev/null +++ b/nptl/tst-exec5.c @@ -0,0 +1,176 @@ +/* Check if posix_spawn does not act as a cancellation entrypoint. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <paths.h> +#include <pthread.h> +#include <signal.h> +#include <spawn.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> + +static int do_test (void); +#define TEST_FUNCTION do_test () +#include <test-skeleton.c> + +static pthread_barrier_t b; + +static pid_t pid; +static int pipefd[2]; + +static void * +tf (void *arg) +{ + xpthread_barrier_wait (&b); + + posix_spawn_file_actions_t a; + if (posix_spawn_file_actions_init (&a) != 0) + { + puts ("error: spawn_file_actions_init failed"); + exit (1); + } + + if (posix_spawn_file_actions_adddup2 (&a, pipefd[1], STDOUT_FILENO) != 0) + { + puts ("error: spawn_file_actions_adddup2 failed"); + exit (1); + } + + if (posix_spawn_file_actions_addclose (&a, pipefd[0]) != 0) + { + puts ("error: spawn_file_actions_addclose"); + exit (1); + } + + char *argv[] = { (char *) _PATH_BSHELL, (char *) "-c", (char *) "echo $$", + NULL }; + if (posix_spawn (&pid, _PATH_BSHELL, &a, NULL, argv, NULL) != 0) + { + puts ("error: spawn failed"); + exit (1); + } + + return NULL; +} + + +static int +do_test (void) +{ + /* The test basically pipe a 'echo $$' created by a thread with a + cancellation pending. It then checks if the thread is not cancelled, + the process is created and if the output is the expected one. */ + + if (pipe (pipefd) != 0) + { + puts ("error: pipe failed"); + exit (1); + } + + /* Not interested in knowing when the pipe is closed. */ + if (sigignore (SIGPIPE) != 0) + { + puts ("error: sigignore failed"); + exit (1); + } + + /* To synchronize with the thread. */ + if (pthread_barrier_init (&b, NULL, 2) != 0) + { + puts ("error: pthread_barrier_init failed"); + exit (1); + } + + pthread_t th = xpthread_create (NULL, &tf, NULL); + + if (pthread_cancel (th) != 0) + { + puts ("error: pthread_cancel failed"); + return 1; + } + + xpthread_barrier_wait (&b); + + if (xpthread_join (th) == PTHREAD_CANCELED) + { + puts ("error: thread cancelled"); + exit (1); + } + + close (pipefd[1]); + + /* The global 'pid' should be set by thread posix_spawn calling. Check + below if it was executed correctly and with expected output. */ + + char buf[64]; + ssize_t n; + bool seen_pid = false; + while (TEMP_FAILURE_RETRY ((n = read (pipefd[0], buf, sizeof (buf)))) > 0) + { + /* We only expect to read the PID. */ + char *endp; + long int rpid = strtol (buf, &endp, 10); + + if (*endp != '\n') + { + printf ("error: didn't parse whole line: \"%s\"\n", buf); + exit (1); + } + if (endp == buf) + { + puts ("error: read empty line"); + exit (1); + } + + if (rpid != pid) + { + printf ("error: found \"%s\", expected PID %ld\n", buf, + (long int) pid); + exit (1); + } + + if (seen_pid) + { + puts ("error: found more than one PID line"); + exit (1); + } + + seen_pid = true; + } + + close (pipefd[0]); + + int status; + int err = waitpid (pid, &status, 0); + if (err != pid) + { + puts ("errnor: waitpid failed"); + exit (1); + } + + if (!seen_pid) + { + puts ("error: didn't get PID"); + exit (1); + } + + return 0; +} |