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