]> ruderich.org/simon Gitweb - coloredstderr/coloredstderr.git/blob - tests/example_exec.c
Hook execvpe(), a GNU extension.
[coloredstderr/coloredstderr.git] / tests / example_exec.c
1 /*
2  * Test execve() and exec*() functions.
3  *
4  * Copyright (C) 2013  Simon Ruderich
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21
22 /* For execvpe(). */
23 #define _GNU_SOURCE
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include "../src/compiler.h"
31
32
33 #define MAGIC "@RUN_"
34 #define MAGIC_LENGTH (strlen(MAGIC))
35
36 #define SETUP_RUN(number) \
37         sprintf(argv0, "%s" MAGIC "%03d", argv[0], (number))
38
39
40 extern char **environ;
41
42
43 static int find_magic_run_number(char *argv0, int *number) {
44     size_t length = strlen(argv0);
45     if (length <= MAGIC_LENGTH + 3) {
46         return 0;
47     }
48     /* Check if the string ends with "@RUN_YYY" where YYY should be a number
49      * (not checked). */
50     argv0 += length - MAGIC_LENGTH - 3;
51     if (strncmp(argv0, MAGIC, MAGIC_LENGTH)) {
52         return 0;
53     }
54     *number = atoi(argv0 + MAGIC_LENGTH);
55
56     /* Strip off "@RUN_YYY". */
57     argv0[0] = 0;
58     return 1;
59 }
60
61 static void dump(char *argv[]) {
62     size_t i;
63
64     i = 0;
65     while (*argv) {
66         printf("argv[%zu] = |%s|\n", i++, *argv++);
67     }
68     i = 0;
69     char **env = environ;
70     while (*env) {
71         /* Skip LD_PRELOAD which contains system dependent values. */
72         if (strncmp(*env, "LD_PRELOAD=", 11)) {
73             printf("environ[%zu] = |%s|\n", i, *env);
74         }
75         i++;
76         env++;
77     }
78     printf("\n");
79     /* When not writing to stdout (e.g. redirection while testing), glibc is
80      * buffering heavily. Flush to display the output before the exec*(). */
81     fflush(stdout);
82 }
83
84
85 int main(int argc unused, char **argv) {
86     char argv0[strlen(argv[0]) + MAGIC_LENGTH + 3 + 1];
87
88     char *old_ldpreload = getenv("LD_PRELOAD");
89     size_t ldpreload_length = strlen("LD_PRELOAD=") + 1;
90     if (old_ldpreload) {
91         ldpreload_length += strlen(old_ldpreload);
92     }
93     char ldpreload[ldpreload_length];
94     strcpy(ldpreload, "LD_PRELOAD=");
95     if (old_ldpreload) {
96         strcat(ldpreload, old_ldpreload);
97     }
98
99     int run;
100
101     /* First call. */
102     if (!find_magic_run_number(argv[0], &run)) {
103         SETUP_RUN(1);
104
105         char *args[] = { argv0, NULL };
106         char *envp[] = { ldpreload, NULL };
107
108         execve(argv[0], args, envp);
109         return EXIT_FAILURE;
110     }
111
112     /* Following calls, each testing an exec*() feature. */
113
114     dump(argv);
115     SETUP_RUN(run + 1);
116
117     int skip = run - 1;
118
119
120     /* Check that we update/insert the environment correctly. */
121
122     if (!skip--) {
123         printf("\nCHECKING COLORING.\n\n");
124         fflush(stdout);
125
126         dup2(2, 3);
127
128         char *args[] = { argv0, NULL };
129         char *envp[] = { ldpreload, NULL };
130
131         execve(argv[0], args, envp);
132         return EXIT_FAILURE;
133     } else if (!skip--) {
134         dup2(2, 4);
135
136         execl(argv[0], argv0, NULL);
137         return EXIT_FAILURE;
138     } else if (!skip--) {
139         dup2(2, 5);
140
141         execlp(argv[0], argv0, NULL);
142         return EXIT_FAILURE;
143     } else if (!skip--) {
144         dup2(2, 6);
145
146         char *envp[] = { ldpreload, NULL };
147
148         execle(argv[0], argv0, NULL, envp);
149         return EXIT_FAILURE;
150     } else if (!skip--) {
151         dup2(2, 7);
152
153         /* Test closing a few descriptors. */
154         close(5);
155         close(6);
156
157         char *args[] = { argv0, NULL };
158
159         execv(argv[0], args);
160         return EXIT_FAILURE;
161     } else if (!skip--) {
162         dup2(2, 8);
163
164         /* And a few more. */
165         close(7);
166         close(4);
167
168         char *args[] = { argv0, NULL };
169
170         execvp(argv[0], args);
171         return EXIT_FAILURE;
172     } else {
173         close(3);
174         close(8);
175     }
176
177
178     /* And make sure our hooks don't change the behaviour. */
179
180     /* execve(2) */
181     if (!skip--) {
182         printf("\nCHECKING TRANSPARENCY.\n\n");
183         fflush(stdout);
184
185         char *args[] = { argv0, NULL };
186         char *envp[] = { ldpreload, NULL };
187
188         execve(argv[0], args, envp);
189         return EXIT_FAILURE;
190     } else if (!skip--) {
191         char *args[] = { argv0, NULL };
192         char *envp[] = { "TEST=42", ldpreload, NULL };
193
194         execve(argv[0], args, envp);
195         return EXIT_FAILURE;
196     } else if (!skip--) {
197         char *args[] = { argv0, "foo", "bar", NULL };
198         char *envp[] = { "TEST=43", "FOO=", ldpreload, NULL };
199
200         execve(argv[0], args, envp);
201         return EXIT_FAILURE;
202
203     /* execl(3) */
204     } else if (!skip--) {
205         setenv("TEST", "44", 1);
206
207         execl(argv[0], argv0, NULL);
208         return EXIT_FAILURE;
209     } else if (!skip--) {
210         setenv("TEST", "45", 1);
211
212         execl(argv[0], argv0, "foo", "bar", NULL);
213         return EXIT_FAILURE;
214
215     /* execp(3), but not testing the p(ath) part */
216     } else if (!skip--) {
217         setenv("TEST", "46", 1);
218
219         execlp(argv[0], argv0, NULL);
220         return EXIT_FAILURE;
221     } else if (!skip--) {
222         setenv("TEST", "47", 1);
223
224         execlp(argv[0], argv0, "foo", "bar", NULL);
225         return EXIT_FAILURE;
226
227     /* execle(3) */
228     } else if (!skip--) {
229         char *envp[] = { ldpreload, NULL };
230
231         execle(argv[0], argv0, NULL, envp);
232         return EXIT_FAILURE;
233     } else if (!skip--) {
234         char *envp[] = { "TEST=48", ldpreload, NULL };
235
236         execle(argv[0], argv0, NULL, envp);
237         return EXIT_FAILURE;
238     } else if (!skip--) {
239         char *envp[] = { "TEST=49", "FOO=", ldpreload, NULL };
240
241         execle(argv[0], argv0, "foo", "bar", NULL, envp);
242         return EXIT_FAILURE;
243
244     /* execv(3) */
245     } else if (!skip--) {
246         setenv("TEST", "50", 1);
247
248         char *args[] = { argv0, NULL };
249
250         execv(argv[0], args);
251         return EXIT_FAILURE;
252     } else if (!skip--) {
253         setenv("TEST", "51", 1);
254
255         char *args[] = { argv0, "foo", "bar", NULL };
256
257         execv(argv[0], args);
258         return EXIT_FAILURE;
259
260     /* execvp(3), but not testing the p(ath) part */
261     } else if (!skip--) {
262         setenv("TEST", "52", 1);
263
264         char *args[] = { argv0, NULL };
265
266         execvp(argv[0], args);
267         return EXIT_FAILURE;
268     } else if (!skip--) {
269         setenv("TEST", "53", 1);
270
271         char *args[] = { argv0, "foo", "bar", NULL };
272
273         execvp(argv[0], args);
274         return EXIT_FAILURE;
275
276 #ifdef HAVE_EXECVPE
277     /* execvpe(3), but not testing the p(ath) part */
278     } else if (!skip--) {
279         char *args[] = { argv0, NULL };
280         char *envp[] = { "TEST=54", ldpreload, NULL };
281
282         execvpe(argv[0], args, envp);
283         return EXIT_FAILURE;
284     } else if (!skip--) {
285         char *args[] = { argv0, "foo", "bar", NULL };
286         char *envp[] = { "TEST=55", ldpreload, NULL };
287
288         execvpe(argv[0], args, envp);
289         return EXIT_FAILURE;
290 #else
291     /* Fake output to let the test pass. */
292     } else if (!skip--) {
293         puts("argv[0] = |./example_exec|");
294         puts("environ[0] = |TEST=54|");
295         puts("environ[2] = |COLORED_STDERR_FDS=2,|");
296         puts("");
297         puts("argv[0] = |./example_exec|");
298         puts("argv[1] = |foo|");
299         puts("argv[2] = |bar|");
300         puts("environ[0] = |TEST=55|");
301         puts("environ[2] = |COLORED_STDERR_FDS=2,|");
302         puts("");
303 #endif
304     }
305
306     printf("Done.\n");
307     return EXIT_SUCCESS;
308 }