1 /* -*- coding: utf-8 -*-
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 */
6 
7 #include "config.h"
8 
9 #include <sys/wait.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <paths.h>
14 
15 #if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
16 #include <spawn.h>
17 #endif
18 
19 // ..:: environment access fixer - begin ::..
20 #ifdef HAVE_NSGETENVIRON
21 #include <crt_externs.h>
22 #else
23 extern char **environ;
24 #endif
25 
26 char **get_environ() {
27 #ifdef HAVE_NSGETENVIRON
28     return *_NSGetEnviron();
29 #else
30     return environ;
31 #endif
32 }
33 // ..:: environment access fixer - end ::..
34 
35 // ..:: test fixtures - begin ::..
36 static char const *cwd = NULL;
37 static FILE *fd = NULL;
38 static int need_comma = 0;
39 
40 void expected_out_open(const char *expected) {
41     cwd = getcwd(NULL, 0);
42     fd = fopen(expected, "w");
43     if (!fd) {
44         perror("fopen");
45         exit(EXIT_FAILURE);
46     }
47     fprintf(fd, "[\n");
48     need_comma = 0;
49 }
50 
51 void expected_out_close() {
52     fprintf(fd, "]\n");
53     fclose(fd);
54     fd = NULL;
55 
56     free((void *)cwd);
57     cwd = NULL;
58 }
59 
60 void expected_out(const char *file) {
61     if (need_comma)
62         fprintf(fd, ",\n");
63     else
64         need_comma = 1;
65 
66     fprintf(fd, "{\n");
67     fprintf(fd, "  \"directory\": \"%s\",\n", cwd);
68     fprintf(fd, "  \"command\": \"cc -c %s\",\n", file);
69     fprintf(fd, "  \"file\": \"%s/%s\"\n", cwd, file);
70     fprintf(fd, "}\n");
71 }
72 
73 void create_source(char *file) {
74     FILE *fd = fopen(file, "w");
75     if (!fd) {
76         perror("fopen");
77         exit(EXIT_FAILURE);
78     }
79     fprintf(fd, "typedef int score;\n");
80     fclose(fd);
81 }
82 
83 typedef void (*exec_fun)();
84 
85 void wait_for(pid_t child) {
86     int status;
87     if (-1 == waitpid(child, &status, 0)) {
88         perror("wait");
89         exit(EXIT_FAILURE);
90     }
91     if (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE) {
92         fprintf(stderr, "children process has non zero exit code\n");
93         exit(EXIT_FAILURE);
94     }
95 }
96 
97 #define FORK(FUNC)                                                             \
98     {                                                                          \
99         pid_t child = fork();                                                  \
100         if (-1 == child) {                                                     \
101             perror("fork");                                                    \
102             exit(EXIT_FAILURE);                                                \
103         } else if (0 == child) {                                               \
104             FUNC fprintf(stderr, "children process failed to exec\n");         \
105             exit(EXIT_FAILURE);                                                \
106         } else {                                                               \
107             wait_for(child);                                                   \
108         }                                                                      \
109     }
110 // ..:: test fixtures - end ::..
111 
112 #ifdef HAVE_EXECV
113 void call_execv() {
114     char *const file = "execv.c";
115     char *const compiler = "/usr/bin/cc";
116     char *const argv[] = {"cc", "-c", file, 0};
117 
118     expected_out(file);
119     create_source(file);
120 
121     FORK(execv(compiler, argv);)
122 }
123 #endif
124 
125 #ifdef HAVE_EXECVE
126 void call_execve() {
127     char *const file = "execve.c";
128     char *const compiler = "/usr/bin/cc";
129     char *const argv[] = {compiler, "-c", file, 0};
130     char *const envp[] = {"THIS=THAT", 0};
131 
132     expected_out(file);
133     create_source(file);
134 
135     FORK(execve(compiler, argv, envp);)
136 }
137 #endif
138 
139 #ifdef HAVE_EXECVP
140 void call_execvp() {
141     char *const file = "execvp.c";
142     char *const compiler = "cc";
143     char *const argv[] = {compiler, "-c", file, 0};
144 
145     expected_out(file);
146     create_source(file);
147 
148     FORK(execvp(compiler, argv);)
149 }
150 #endif
151 
152 #ifdef HAVE_EXECVP2
153 void call_execvP() {
154     char *const file = "execv_p.c";
155     char *const compiler = "cc";
156     char *const argv[] = {compiler, "-c", file, 0};
157 
158     expected_out(file);
159     create_source(file);
160 
161     FORK(execvP(compiler, _PATH_DEFPATH, argv);)
162 }
163 #endif
164 
165 #ifdef HAVE_EXECVPE
166 void call_execvpe() {
167     char *const file = "execvpe.c";
168     char *const compiler = "cc";
169     char *const argv[] = {"/usr/bin/cc", "-c", file, 0};
170     char *const envp[] = {"THIS=THAT", 0};
171 
172     expected_out(file);
173     create_source(file);
174 
175     FORK(execvpe(compiler, argv, envp);)
176 }
177 #endif
178 
179 #ifdef HAVE_EXECT
180 void call_exect() {
181     char *const file = "exect.c";
182     char *const compiler = "/usr/bin/cc";
183     char *const argv[] = {compiler, "-c", file, 0};
184     char *const envp[] = {"THIS=THAT", 0};
185 
186     expected_out(file);
187     create_source(file);
188 
189     FORK(exect(compiler, argv, envp);)
190 }
191 #endif
192 
193 #ifdef HAVE_EXECL
194 void call_execl() {
195     char *const file = "execl.c";
196     char *const compiler = "/usr/bin/cc";
197 
198     expected_out(file);
199     create_source(file);
200 
201     FORK(execl(compiler, "cc", "-c", file, (char *)0);)
202 }
203 #endif
204 
205 #ifdef HAVE_EXECLP
206 void call_execlp() {
207     char *const file = "execlp.c";
208     char *const compiler = "cc";
209 
210     expected_out(file);
211     create_source(file);
212 
213     FORK(execlp(compiler, compiler, "-c", file, (char *)0);)
214 }
215 #endif
216 
217 #ifdef HAVE_EXECLE
218 void call_execle() {
219     char *const file = "execle.c";
220     char *const compiler = "/usr/bin/cc";
221     char *const envp[] = {"THIS=THAT", 0};
222 
223     expected_out(file);
224     create_source(file);
225 
226     FORK(execle(compiler, compiler, "-c", file, (char *)0, envp);)
227 }
228 #endif
229 
230 #ifdef HAVE_POSIX_SPAWN
231 void call_posix_spawn() {
232     char *const file = "posix_spawn.c";
233     char *const compiler = "cc";
234     char *const argv[] = {compiler, "-c", file, 0};
235 
236     expected_out(file);
237     create_source(file);
238 
239     pid_t child;
240     if (0 != posix_spawn(&child, "/usr/bin/cc", 0, 0, argv, get_environ())) {
241         perror("posix_spawn");
242         exit(EXIT_FAILURE);
243     }
244     wait_for(child);
245 }
246 #endif
247 
248 #ifdef HAVE_POSIX_SPAWNP
249 void call_posix_spawnp() {
250     char *const file = "posix_spawnp.c";
251     char *const compiler = "cc";
252     char *const argv[] = {compiler, "-c", file, 0};
253 
254     expected_out(file);
255     create_source(file);
256 
257     pid_t child;
258     if (0 != posix_spawnp(&child, "cc", 0, 0, argv, get_environ())) {
259         perror("posix_spawnp");
260         exit(EXIT_FAILURE);
261     }
262     wait_for(child);
263 }
264 #endif
265 
266 int main(int argc, char *const argv[]) {
267     if (argc != 2)
268         exit(EXIT_FAILURE);
269 
270     expected_out_open(argv[1]);
271 #ifdef HAVE_EXECV
272     call_execv();
273 #endif
274 #ifdef HAVE_EXECVE
275     call_execve();
276 #endif
277 #ifdef HAVE_EXECVP
278     call_execvp();
279 #endif
280 #ifdef HAVE_EXECVP2
281     call_execvP();
282 #endif
283 #ifdef HAVE_EXECVPE
284     call_execvpe();
285 #endif
286 #ifdef HAVE_EXECT
287     call_exect();
288 #endif
289 #ifdef HAVE_EXECL
290     call_execl();
291 #endif
292 #ifdef HAVE_EXECLP
293     call_execlp();
294 #endif
295 #ifdef HAVE_EXECLE
296     call_execle();
297 #endif
298 #ifdef HAVE_POSIX_SPAWN
299     call_posix_spawn();
300 #endif
301 #ifdef HAVE_POSIX_SPAWNP
302     call_posix_spawnp();
303 #endif
304     expected_out_close();
305     return 0;
306 }
307