1 /* Check if posix_spawn does not act as a cancellation entrypoint.
2    Copyright (C) 2016-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <errno.h>
20 #include <paths.h>
21 #include <pthread.h>
22 #include <signal.h>
23 #include <spawn.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <sys/wait.h>
29 
30 static int do_test (void);
31 #define TEST_FUNCTION do_test ()
32 #include <test-skeleton.c>
33 
34 static pthread_barrier_t b;
35 
36 static pid_t pid;
37 static int pipefd[2];
38 
39 static void *
tf(void * arg)40 tf (void *arg)
41 {
42   xpthread_barrier_wait (&b);
43 
44   posix_spawn_file_actions_t a;
45   if (posix_spawn_file_actions_init (&a) != 0)
46     {
47       puts ("error: spawn_file_actions_init failed");
48       exit (1);
49     }
50 
51   if (posix_spawn_file_actions_adddup2 (&a, pipefd[1], STDOUT_FILENO) != 0)
52     {
53       puts ("error: spawn_file_actions_adddup2 failed");
54       exit (1);
55     }
56 
57   if (posix_spawn_file_actions_addclose (&a, pipefd[0]) != 0)
58     {
59       puts ("error: spawn_file_actions_addclose");
60       exit (1);
61     }
62 
63   char *argv[] = { (char *) _PATH_BSHELL, (char *) "-c", (char *) "echo $$",
64 		   NULL };
65   if (posix_spawn (&pid, _PATH_BSHELL, &a, NULL, argv, NULL) != 0)
66     {
67       puts ("error: spawn failed");
68       exit (1);
69     }
70 
71   return NULL;
72 }
73 
74 
75 static int
do_test(void)76 do_test (void)
77 {
78   /* The test basically pipe a 'echo $$' created by a thread with a
79      cancellation pending.  It then checks if the thread is not cancelled,
80      the process is created and if the output is the expected one.  */
81 
82   if (pipe (pipefd) != 0)
83     {
84       puts ("error: pipe failed");
85       exit (1);
86     }
87 
88   /* Not interested in knowing when the pipe is closed.  */
89   xsignal (SIGPIPE, SIG_IGN);
90 
91   /* To synchronize with the thread.  */
92   if (pthread_barrier_init (&b, NULL, 2) != 0)
93     {
94       puts ("error: pthread_barrier_init failed");
95       exit (1);
96     }
97 
98   pthread_t th = xpthread_create (NULL, &tf, NULL);
99 
100   if (pthread_cancel (th) != 0)
101     {
102       puts ("error: pthread_cancel failed");
103       return 1;
104     }
105 
106   xpthread_barrier_wait (&b);
107 
108   if (xpthread_join (th) == PTHREAD_CANCELED)
109     {
110       puts ("error: thread cancelled");
111       exit (1);
112     }
113 
114   close (pipefd[1]);
115 
116   /* The global 'pid' should be set by thread posix_spawn calling.  Check
117      below if it was executed correctly and with expected output.  */
118 
119   char buf[64];
120   ssize_t n;
121   bool seen_pid = false;
122   while (TEMP_FAILURE_RETRY ((n = read (pipefd[0], buf, sizeof (buf)))) > 0)
123     {
124       /* We only expect to read the PID.  */
125       char *endp;
126       long int rpid = strtol (buf, &endp, 10);
127 
128       if (*endp != '\n')
129 	{
130 	  printf ("error: didn't parse whole line: \"%s\"\n", buf);
131 	  exit (1);
132 	}
133       if (endp == buf)
134 	{
135 	  puts ("error: read empty line");
136 	  exit (1);
137 	}
138 
139       if (rpid != pid)
140 	{
141 	  printf ("error: found \"%s\", expected PID %ld\n", buf,
142 		 (long int) pid);
143 	  exit (1);
144 	}
145 
146       if (seen_pid)
147 	{
148 	  puts ("error: found more than one PID line");
149 	  exit (1);
150 	}
151 
152       seen_pid = true;
153     }
154 
155   close (pipefd[0]);
156 
157   int status;
158   int err = waitpid (pid, &status, 0);
159   if (err != pid)
160     {
161       puts ("errnor: waitpid failed");
162       exit (1);
163     }
164 
165   if (!seen_pid)
166     {
167       puts ("error: didn't get PID");
168       exit (1);
169     }
170 
171   return 0;
172 }
173