chiark / gitweb /
tree-wide: drop copyright headers from frequent contributors
[elogind.git] / src / test / test-exec-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <string.h>
5 #include <sys/stat.h>
6 #include <sys/wait.h>
7 #include <unistd.h>
8
9 #include "alloc-util.h"
10 #include "copy.h"
11 #include "def.h"
12 #include "env-util.h"
13 #include "exec-util.h"
14 #include "fd-util.h"
15 #include "fileio.h"
16 #include "fs-util.h"
17 #include "log.h"
18 #include "macro.h"
19 #include "rm-rf.h"
20 #include "string-util.h"
21 #include "strv.h"
22
23 static int here = 0, here2 = 0, here3 = 0;
24 void *ignore_stdout_args[] = {&here, &here2, &here3};
25
26 /* noop handlers, just check that arguments are passed correctly */
27 static int ignore_stdout_func(int fd, void *arg) {
28         assert(fd >= 0);
29         assert(arg == &here);
30         safe_close(fd);
31
32         return 0;
33 }
34 static int ignore_stdout_func2(int fd, void *arg) {
35         assert(fd >= 0);
36         assert(arg == &here2);
37         safe_close(fd);
38
39         return 0;
40 }
41 static int ignore_stdout_func3(int fd, void *arg) {
42         assert(fd >= 0);
43         assert(arg == &here3);
44         safe_close(fd);
45
46         return 0;
47 }
48
49 static const gather_stdout_callback_t ignore_stdout[] = {
50         ignore_stdout_func,
51         ignore_stdout_func2,
52         ignore_stdout_func3,
53 };
54
55 static void test_execute_directory(bool gather_stdout) {
56         char template_lo[] = "/tmp/test-exec-util.lo.XXXXXXX";
57         char template_hi[] = "/tmp/test-exec-util.hi.XXXXXXX";
58         const char * dirs[] = {template_hi, template_lo, NULL};
59         const char *name, *name2, *name3,
60                 *overridden, *override,
61                 *masked, *mask,
62                 *masked2, *mask2,   /* the mask is non-executable */
63                 *masked2e, *mask2e; /* the mask is executable */
64
65         log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous");
66
67         assert_se(mkdtemp(template_lo));
68         assert_se(mkdtemp(template_hi));
69
70         name = strjoina(template_lo, "/script");
71         name2 = strjoina(template_hi, "/script2");
72         name3 = strjoina(template_lo, "/useless");
73         overridden = strjoina(template_lo, "/overridden");
74         override = strjoina(template_hi, "/overridden");
75         masked = strjoina(template_lo, "/masked");
76         mask = strjoina(template_hi, "/masked");
77         masked2 = strjoina(template_lo, "/masked2");
78         mask2 = strjoina(template_hi, "/masked2");
79         masked2e = strjoina(template_lo, "/masked2e");
80         mask2e = strjoina(template_hi, "/masked2e");
81
82         assert_se(write_string_file(name,
83                                     "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works",
84                                     WRITE_STRING_FILE_CREATE) == 0);
85         assert_se(write_string_file(name2,
86                                     "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2",
87                                     WRITE_STRING_FILE_CREATE) == 0);
88         assert_se(write_string_file(overridden,
89                                     "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
90                                     WRITE_STRING_FILE_CREATE) == 0);
91         assert_se(write_string_file(override,
92                                     "#!/bin/sh\necho 'Executing '$0",
93                                     WRITE_STRING_FILE_CREATE) == 0);
94         assert_se(write_string_file(masked,
95                                     "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
96                                     WRITE_STRING_FILE_CREATE) == 0);
97         assert_se(write_string_file(masked2,
98                                     "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
99                                     WRITE_STRING_FILE_CREATE) == 0);
100         assert_se(write_string_file(masked2e,
101                                     "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
102                                     WRITE_STRING_FILE_CREATE) == 0);
103         assert_se(symlink("/dev/null", mask) == 0);
104         assert_se(touch(mask2) == 0);
105         assert_se(touch(mask2e) == 0);
106         assert_se(touch(name3) >= 0);
107
108         assert_se(chmod(name, 0755) == 0);
109         assert_se(chmod(name2, 0755) == 0);
110         assert_se(chmod(overridden, 0755) == 0);
111         assert_se(chmod(override, 0755) == 0);
112         assert_se(chmod(masked, 0755) == 0);
113         assert_se(chmod(masked2, 0755) == 0);
114         assert_se(chmod(masked2e, 0755) == 0);
115         assert_se(chmod(mask2e, 0755) == 0);
116
117         if (gather_stdout)
118                 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
119         else
120                 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL);
121
122         assert_se(chdir(template_lo) == 0);
123         assert_se(access("it_works", F_OK) >= 0);
124         assert_se(access("failed", F_OK) < 0);
125
126         assert_se(chdir(template_hi) == 0);
127         assert_se(access("it_works2", F_OK) >= 0);
128         assert_se(access("failed", F_OK) < 0);
129
130         (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
131         (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
132 }
133
134 static void test_execution_order(void) {
135         char template_lo[] = "/tmp/test-exec-util-lo.XXXXXXX";
136         char template_hi[] = "/tmp/test-exec-util-hi.XXXXXXX";
137         const char *dirs[] = {template_hi, template_lo, NULL};
138         const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
139         const char *output, *t;
140         _cleanup_free_ char *contents = NULL;
141
142         assert_se(mkdtemp(template_lo));
143         assert_se(mkdtemp(template_hi));
144
145         output = strjoina(template_hi, "/output");
146
147         log_info("/* %s >>%s */", __func__, output);
148
149         /* write files in "random" order */
150         name2 = strjoina(template_lo, "/90-bar");
151         name = strjoina(template_hi, "/80-foo");
152         name3 = strjoina(template_lo, "/last");
153         overridden = strjoina(template_lo, "/30-override");
154         override = strjoina(template_hi, "/30-override");
155         masked = strjoina(template_lo, "/10-masked");
156         mask = strjoina(template_hi, "/10-masked");
157
158         t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
159         assert_se(write_string_file(name, t, WRITE_STRING_FILE_CREATE) == 0);
160
161         t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
162         assert_se(write_string_file(name2, t, WRITE_STRING_FILE_CREATE) == 0);
163
164         t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
165         assert_se(write_string_file(name3, t, WRITE_STRING_FILE_CREATE) == 0);
166
167         t = strjoina("#!/bin/sh\necho OVERRIDDEN >>", output);
168         assert_se(write_string_file(overridden, t, WRITE_STRING_FILE_CREATE) == 0);
169
170         t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
171         assert_se(write_string_file(override, t, WRITE_STRING_FILE_CREATE) == 0);
172
173         t = strjoina("#!/bin/sh\necho MASKED >>", output);
174         assert_se(write_string_file(masked, t, WRITE_STRING_FILE_CREATE) == 0);
175
176         assert_se(symlink("/dev/null", mask) == 0);
177
178         assert_se(chmod(name, 0755) == 0);
179         assert_se(chmod(name2, 0755) == 0);
180         assert_se(chmod(name3, 0755) == 0);
181         assert_se(chmod(overridden, 0755) == 0);
182         assert_se(chmod(override, 0755) == 0);
183         assert_se(chmod(masked, 0755) == 0);
184
185         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
186
187         assert_se(read_full_file(output, &contents, NULL) >= 0);
188         assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n"));
189
190         (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
191         (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
192 }
193
194 static int gather_stdout_one(int fd, void *arg) {
195         char ***s = arg, *t;
196         char buf[128] = {};
197
198         assert_se(s);
199         assert_se(read(fd, buf, sizeof buf) >= 0);
200         safe_close(fd);
201
202         assert_se(t = strndup(buf, sizeof buf));
203         assert_se(strv_push(s, t) >= 0);
204
205         return 0;
206 }
207 static int gather_stdout_two(int fd, void *arg) {
208         char ***s = arg, **t;
209
210         STRV_FOREACH(t, *s)
211                 assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t));
212         safe_close(fd);
213
214         return 0;
215 }
216 static int gather_stdout_three(int fd, void *arg) {
217         char **s = arg;
218         char buf[128] = {};
219
220         assert_se(read(fd, buf, sizeof buf - 1) > 0);
221         safe_close(fd);
222         assert_se(*s = strndup(buf, sizeof buf));
223
224         return 0;
225 }
226
227 const gather_stdout_callback_t gather_stdout[] = {
228         gather_stdout_one,
229         gather_stdout_two,
230         gather_stdout_three,
231 };
232
233 static void test_stdout_gathering(void) {
234         char template[] = "/tmp/test-exec-util.XXXXXXX";
235         const char *dirs[] = {template, NULL};
236         const char *name, *name2, *name3;
237         int r;
238
239         char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
240         _cleanup_free_ char *output = NULL;
241
242         void* args[] = {&tmp, &tmp, &output};
243
244         assert_se(mkdtemp(template));
245
246         log_info("/* %s */", __func__);
247
248         /* write files */
249         name = strjoina(template, "/10-foo");
250         name2 = strjoina(template, "/20-bar");
251         name3 = strjoina(template, "/30-last");
252
253         assert_se(write_string_file(name,
254                                     "#!/bin/sh\necho a\necho b\necho c\n",
255                                     WRITE_STRING_FILE_CREATE) == 0);
256         assert_se(write_string_file(name2,
257                                     "#!/bin/sh\necho d\n",
258                                     WRITE_STRING_FILE_CREATE) == 0);
259         assert_se(write_string_file(name3,
260                                     "#!/bin/sh\nsleep 1",
261                                     WRITE_STRING_FILE_CREATE) == 0);
262
263         assert_se(chmod(name, 0755) == 0);
264         assert_se(chmod(name2, 0755) == 0);
265         assert_se(chmod(name3, 0755) == 0);
266
267         r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL);
268         assert_se(r >= 0);
269
270         log_info("got: %s", output);
271
272         assert_se(streq(output, "a\nb\nc\nd\n"));
273 }
274
275 #if 0 /// UNNEEDED by elogind
276 static void test_environment_gathering(void) {
277         char template[] = "/tmp/test-exec-util.XXXXXXX", **p;
278         const char *dirs[] = {template, NULL};
279         const char *name, *name2, *name3;
280         int r;
281
282         char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
283         _cleanup_strv_free_ char **env = NULL;
284
285         void* const args[] = { &tmp, &tmp, &env };
286
287         assert_se(mkdtemp(template));
288
289         log_info("/* %s */", __func__);
290
291         /* write files */
292         name = strjoina(template, "/10-foo");
293         name2 = strjoina(template, "/20-bar");
294         name3 = strjoina(template, "/30-last");
295
296         assert_se(write_string_file(name,
297                                     "#!/bin/sh\n"
298                                     "echo A=23\n",
299                                     WRITE_STRING_FILE_CREATE) == 0);
300         assert_se(write_string_file(name2,
301                                     "#!/bin/sh\n"
302                                     "echo A=22:$A\n\n\n",            /* substitution from previous generator */
303                                     WRITE_STRING_FILE_CREATE) == 0);
304         assert_se(write_string_file(name3,
305                                     "#!/bin/sh\n"
306                                     "echo A=$A:24\n"
307                                     "echo B=12\n"
308                                     "echo C=000\n"
309                                     "echo C=001\n"                    /* variable overwriting */
310                                      /* various invalid entries */
311                                     "echo unset A\n"
312                                     "echo unset A=\n"
313                                     "echo unset A=B\n"
314                                     "echo unset \n"
315                                     "echo A B=C\n"
316                                     "echo A\n"
317                                     /* test variable assignment without newline */
318                                     "echo PATH=$PATH:/no/such/file",   /* no newline */
319                                     WRITE_STRING_FILE_CREATE) == 0);
320
321         assert_se(chmod(name, 0755) == 0);
322         assert_se(chmod(name2, 0755) == 0);
323         assert_se(chmod(name3, 0755) == 0);
324
325         r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
326         assert_se(r >= 0);
327
328         STRV_FOREACH(p, env)
329                 log_info("got env: \"%s\"", *p);
330
331         assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
332         assert_se(streq(strv_env_get(env, "B"), "12"));
333         assert_se(streq(strv_env_get(env, "C"), "001"));
334         assert_se(endswith(strv_env_get(env, "PATH"), ":/no/such/file"));
335 }
336 #endif // 0
337
338 int main(int argc, char *argv[]) {
339         log_set_max_level(LOG_DEBUG);
340         log_parse_environment();
341         log_open();
342
343         test_execute_directory(true);
344         test_execute_directory(false);
345         test_execution_order();
346         test_stdout_gathering();
347 #if 0 /// UNNEEDED by elogind
348         test_environment_gathering();
349 #endif // 0
350
351         return 0;
352 }