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