1 /* Tests for spawn.
2    Copyright (C) 2000-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 <stdio.h>
20 #include <getopt.h>
21 #include <errno.h>
22 #include <error.h>
23 #include <fcntl.h>
24 #include <spawn.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/param.h>
28 
29 #include <support/check.h>
30 #include <support/xunistd.h>
31 #include <support/temp_file.h>
32 #include <support/support.h>
33 
34 
35 /* Nonzero if the program gets called via `exec'.  */
36 static int restart;
37 
38 
39 #define CMDLINE_OPTIONS \
40   { "restart", no_argument, &restart, 1 },
41 
42 /* Name of the temporary files.  */
43 static char *name1;
44 static char *name2;
45 static char *name3;
46 static char *name5;
47 
48 /* Descriptors for the temporary files.  */
49 static int temp_fd1 = -1;
50 static int temp_fd2 = -1;
51 static int temp_fd3 = -1;
52 static int temp_fd5 = -1;
53 
54 /* The contents of our files.  */
55 static const char fd1string[] = "This file should get closed";
56 static const char fd2string[] = "This file should stay opened";
57 static const char fd3string[] = "This file will be opened";
58 static const char fd5string[] = "This file should stay opened (O_CLOEXEC)";
59 
60 
61 /* We have a preparation function.  */
62 static void
do_prepare(int argc,char * argv[])63 do_prepare (int argc, char *argv[])
64 {
65   /* We must not open any files in the restart case.  */
66   if (restart)
67     return;
68 
69   TEST_VERIFY_EXIT ((temp_fd1 = create_temp_file ("spawn", &name1)) != -1);
70   TEST_VERIFY_EXIT ((temp_fd2 = create_temp_file ("spawn", &name2)) != -1);
71   TEST_VERIFY_EXIT ((temp_fd3 = create_temp_file ("spawn", &name3)) != -1);
72   TEST_VERIFY_EXIT ((temp_fd5 = create_temp_file ("spawn", &name5)) != -1);
73 
74   int flags;
75   TEST_VERIFY_EXIT ((flags = fcntl (temp_fd5, F_GETFD, &flags)) != -1);
76   TEST_COMPARE (fcntl (temp_fd5, F_SETFD, flags | FD_CLOEXEC), 0);
77 }
78 #define PREPARE do_prepare
79 
80 
81 static int
handle_restart(const char * fd1s,const char * fd2s,const char * fd3s,const char * fd4s,const char * name,const char * fd5s)82 handle_restart (const char *fd1s, const char *fd2s, const char *fd3s,
83 		const char *fd4s, const char *name, const char *fd5s)
84 {
85   char buf[100];
86   int fd1;
87   int fd2;
88   int fd3;
89   int fd4;
90   int fd5;
91 
92   /* First get the descriptors.  */
93   fd1 = atol (fd1s);
94   fd2 = atol (fd2s);
95   fd3 = atol (fd3s);
96   fd4 = atol (fd4s);
97   fd5 = atol (fd5s);
98 
99   /* Sanity check.  */
100   TEST_VERIFY_EXIT (fd1 != fd2);
101   TEST_VERIFY_EXIT (fd1 != fd3);
102   TEST_VERIFY_EXIT (fd1 != fd4);
103   TEST_VERIFY_EXIT (fd2 != fd3);
104   TEST_VERIFY_EXIT (fd2 != fd4);
105   TEST_VERIFY_EXIT (fd3 != fd4);
106   TEST_VERIFY_EXIT (fd4 != fd5);
107 
108   /* First the easy part: read from the file descriptor which is
109      supposed to be open.  */
110   TEST_COMPARE (xlseek (fd2, 0, SEEK_CUR), strlen (fd2string));
111   /* The duped descriptor must have the same position.  */
112   TEST_COMPARE (xlseek (fd4, 0, SEEK_CUR), strlen (fd2string));
113   TEST_COMPARE (xlseek (fd2, 0, SEEK_SET), 0);
114   TEST_COMPARE (xlseek (fd4, 0, SEEK_CUR), 0);
115   TEST_COMPARE (read (fd2, buf, sizeof buf), strlen (fd2string));
116   TEST_COMPARE_BLOB (fd2string, strlen (fd2string), buf, strlen (fd2string));
117 
118   /* Now read from the third file.  */
119   TEST_COMPARE (read (fd3, buf, sizeof buf), strlen (fd3string));
120   TEST_COMPARE_BLOB (fd3string, strlen (fd3string), buf, strlen (fd3string));
121   /* Try to write to the file.  This should not be allowed.  */
122   TEST_COMPARE (write (fd3, "boo!", 4), -1);
123   TEST_COMPARE (errno, EBADF);
124 
125   /* Now try to read the first file.  First make sure it is not opened.  */
126   TEST_COMPARE (lseek (fd1, 0, SEEK_CUR), (off_t) -1);
127   TEST_COMPARE (errno, EBADF);
128 
129   /* Now open the file and read it.  */
130   fd1 = xopen (name, O_RDONLY, 0600);
131 
132   TEST_COMPARE (read (fd1, buf, sizeof buf), strlen (fd1string));
133   TEST_COMPARE_BLOB (fd1string, strlen (fd1string), buf, strlen (fd1string));
134 
135   TEST_COMPARE (xlseek (fd5, 0, SEEK_SET), 0);
136   TEST_COMPARE (read (fd5, buf, sizeof buf), strlen (fd5string));
137   TEST_COMPARE_BLOB (fd5string, strlen (fd5string), buf, strlen (fd5string));
138 
139   return 0;
140 }
141 
142 
143 static int
do_test(int argc,char * argv[])144 do_test (int argc, char *argv[])
145 {
146   pid_t pid;
147   int fd4;
148   int status;
149   posix_spawn_file_actions_t actions;
150   char fd1name[18];
151   char fd2name[18];
152   char fd3name[18];
153   char fd4name[18];
154   char fd5name[18];
155   char *name3_copy;
156   char *spargv[13];
157   int i;
158 
159   /* We must have
160      - one or four parameters left if called initially
161        + path for ld.so		optional
162        + "--library-path"	optional
163        + the library path	optional
164        + the application name
165      - six parameters left if called through re-execution
166        + file descriptor number which is supposed to be closed
167        + the open file descriptor
168        + the newly opened file descriptor
169        + the duped second descriptor
170        + the name of the closed descriptor
171        + the duped fourth file descriptor which O_CLOEXEC should be
172 	 remove by adddup2.
173   */
174   if (argc != (restart ? 7 : 2) && argc != (restart ? 7 : 5))
175     FAIL_EXIT1 ("wrong number of arguments (%d)", argc);
176 
177   if (restart)
178     return handle_restart (argv[1], argv[2], argv[3], argv[4], argv[5],
179 			   argv[6]);
180 
181   /* Prepare the test.  We are creating four files: two which file descriptor
182      will be marked with FD_CLOEXEC, another which is not.  */
183 
184   /* Write something in the files.  */
185   xwrite (temp_fd1, fd1string, strlen (fd1string));
186   xwrite (temp_fd2, fd2string, strlen (fd2string));
187   xwrite (temp_fd3, fd3string, strlen (fd3string));
188   xwrite (temp_fd5, fd5string, strlen (fd5string));
189 
190   /* Close the third file.  It'll be opened by `spawn'.  */
191   xclose (temp_fd3);
192 
193   /* Tell `spawn' what to do.  */
194   TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0);
195   /* Close `temp_fd1'.  */
196   TEST_COMPARE (posix_spawn_file_actions_addclose (&actions, temp_fd1), 0);
197   /* We want to open the third file.  */
198   name3_copy = xstrdup (name3);
199   TEST_COMPARE (posix_spawn_file_actions_addopen (&actions, temp_fd3,
200 						  name3_copy,
201 						  O_RDONLY, 0666),
202 		0);
203   /* Overwrite the name to check that a copy has been made.  */
204   memset (name3_copy, 'X', strlen (name3_copy));
205 
206   /* We dup the second descriptor.  */
207   fd4 = MAX (2, MAX (temp_fd1, MAX (temp_fd2, MAX (temp_fd3, temp_fd5)))) + 1;
208   TEST_COMPARE (posix_spawn_file_actions_adddup2 (&actions, temp_fd2, fd4),
209 	        0);
210 
211   /* We clear the O_CLOEXEC on fourth descriptor, so it should be
212      stay open on child.  */
213   TEST_COMPARE (posix_spawn_file_actions_adddup2 (&actions, temp_fd5,
214 						  temp_fd5),
215 		0);
216 
217   /* Now spawn the process.  */
218   snprintf (fd1name, sizeof fd1name, "%d", temp_fd1);
219   snprintf (fd2name, sizeof fd2name, "%d", temp_fd2);
220   snprintf (fd3name, sizeof fd3name, "%d", temp_fd3);
221   snprintf (fd4name, sizeof fd4name, "%d", fd4);
222   snprintf (fd5name, sizeof fd5name, "%d", temp_fd5);
223 
224   for (i = 0; i < (argc == (restart ? 7 : 5) ? 4 : 1); i++)
225     spargv[i] = argv[i + 1];
226   spargv[i++] = (char *) "--direct";
227   spargv[i++] = (char *) "--restart";
228   spargv[i++] = fd1name;
229   spargv[i++] = fd2name;
230   spargv[i++] = fd3name;
231   spargv[i++] = fd4name;
232   spargv[i++] = name1;
233   spargv[i++] = fd5name;
234   spargv[i] = NULL;
235 
236   TEST_COMPARE (posix_spawn (&pid, argv[1], &actions, NULL, spargv, environ),
237 		0);
238 
239   /* Wait for the children.  */
240   TEST_COMPARE (xwaitpid (pid, &status, 0), pid);
241   TEST_VERIFY (WIFEXITED (status));
242   TEST_VERIFY (!WIFSIGNALED (status));
243   TEST_COMPARE (WEXITSTATUS (status), 0);
244 
245   /* Same test but with a NULL pid argument.  */
246   TEST_COMPARE (posix_spawn (NULL, argv[1], &actions, NULL, spargv, environ),
247 		0);
248 
249   /* Cleanup.  */
250   TEST_COMPARE (posix_spawn_file_actions_destroy (&actions), 0);
251   free (name3_copy);
252 
253   /* Wait for the children.  */
254   xwaitpid (-1, &status, 0);
255   TEST_VERIFY (WIFEXITED (status));
256   TEST_VERIFY (!WIFSIGNALED (status));
257   TEST_COMPARE (WEXITSTATUS (status), 0);
258 
259   return 0;
260 }
261 
262 #define TEST_FUNCTION_ARGV do_test
263 #include <support/test-driver.c>
264