]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-exec-util.c
bus-unit-util: extend the bus call timeout for UnitFreezer
[thirdparty/systemd.git] / src / test / test-exec-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
89711996
ZJS
2
3#include <errno.h>
89711996
ZJS
4#include <sys/stat.h>
5#include <sys/wait.h>
6#include <unistd.h>
7
c6e47247
ZJS
8#include "alloc-util.h"
9#include "copy.h"
28db6fbf 10#include "constants.h"
3303d1b2 11#include "env-util.h"
89711996 12#include "exec-util.h"
c6e47247 13#include "fd-util.h"
89711996
ZJS
14#include "fileio.h"
15#include "fs-util.h"
16#include "log.h"
17#include "macro.h"
78ec1bb4 18#include "path-util.h"
89711996
ZJS
19#include "rm-rf.h"
20#include "string-util.h"
c6e47247 21#include "strv.h"
6d7c4033 22#include "tests.h"
84e8602d 23#include "tmpfile-util.h"
89711996 24
c6e47247 25static int here = 0, here2 = 0, here3 = 0;
9b1c5610 26static void *ignore_stdout_args[] = { &here, &here2, &here3 };
c6e47247
ZJS
27
28/* noop handlers, just check that arguments are passed correctly */
29static int ignore_stdout_func(int fd, void *arg) {
f21b863e
YW
30 assert_se(fd >= 0);
31 assert_se(arg == &here);
c6e47247
ZJS
32 safe_close(fd);
33
34 return 0;
35}
36static int ignore_stdout_func2(int fd, void *arg) {
f21b863e
YW
37 assert_se(fd >= 0);
38 assert_se(arg == &here2);
c6e47247
ZJS
39 safe_close(fd);
40
41 return 0;
42}
43static int ignore_stdout_func3(int fd, void *arg) {
f21b863e
YW
44 assert_se(fd >= 0);
45 assert_se(arg == &here3);
c6e47247
ZJS
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
4f7452a8 57static void test_execute_directory_one(bool gather_stdout) {
84e8602d 58 _cleanup_(rm_rf_physical_and_freep) char *tmp_lo = NULL, *tmp_hi = NULL;
f66137fb
ZJS
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 */
89711996 64
c6e47247
ZJS
65 log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous");
66
84e8602d
YW
67 assert_se(mkdtemp_malloc("/tmp/test-exec-util.lo.XXXXXXX", &tmp_lo) >= 0);
68 assert_se(mkdtemp_malloc("/tmp/test-exec-util.hi.XXXXXXX", &tmp_hi) >= 0);
69
70 const char * dirs[] = { tmp_hi, tmp_lo, NULL };
71
72 name = strjoina(tmp_lo, "/script");
73 name2 = strjoina(tmp_hi, "/script2");
74 name3 = strjoina(tmp_lo, "/useless");
75 overridden = strjoina(tmp_lo, "/overridden");
76 override = strjoina(tmp_hi, "/overridden");
77 masked = strjoina(tmp_lo, "/masked");
78 mask = strjoina(tmp_hi, "/masked");
79 masked2 = strjoina(tmp_lo, "/masked2");
80 mask2 = strjoina(tmp_hi, "/masked2");
81 masked2e = strjoina(tmp_lo, "/masked2e");
82 mask2e = strjoina(tmp_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
3c14dc61
TM
119 if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
120 return;
121
c6e47247 122 if (gather_stdout)
4b05f0c9 123 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
c6e47247 124 else
4b05f0c9 125 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
89711996 126
84e8602d 127 assert_se(chdir(tmp_lo) == 0);
89711996
ZJS
128 assert_se(access("it_works", F_OK) >= 0);
129 assert_se(access("failed", F_OK) < 0);
130
84e8602d 131 assert_se(chdir(tmp_hi) == 0);
89711996
ZJS
132 assert_se(access("it_works2", F_OK) >= 0);
133 assert_se(access("failed", F_OK) < 0);
89711996
ZJS
134}
135
4f7452a8
JJ
136TEST(execute_directory) {
137 test_execute_directory_one(true);
138 test_execute_directory_one(false);
139}
140
141TEST(execution_order) {
84e8602d 142 _cleanup_(rm_rf_physical_and_freep) char *tmp_lo = NULL, *tmp_hi = NULL;
c6e47247
ZJS
143 const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
144 const char *output, *t;
145 _cleanup_free_ char *contents = NULL;
146
84e8602d
YW
147 assert_se(mkdtemp_malloc("/tmp/test-exec-util-lo.XXXXXXX", &tmp_lo) >= 0);
148 assert_se(mkdtemp_malloc("/tmp/test-exec-util-hi.XXXXXXX", &tmp_hi) >= 0);
149
150 const char *dirs[] = { tmp_hi, tmp_lo, NULL };
c6e47247 151
84e8602d 152 output = strjoina(tmp_hi, "/output");
c6e47247
ZJS
153
154 log_info("/* %s >>%s */", __func__, output);
155
156 /* write files in "random" order */
84e8602d
YW
157 name2 = strjoina(tmp_lo, "/90-bar");
158 name = strjoina(tmp_hi, "/80-foo");
159 name3 = strjoina(tmp_lo, "/last");
160 overridden = strjoina(tmp_lo, "/30-override");
161 override = strjoina(tmp_hi, "/30-override");
162 masked = strjoina(tmp_lo, "/10-masked");
163 mask = strjoina(tmp_hi, "/10-masked");
c6e47247
ZJS
164
165 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
166 assert_se(write_string_file(name, t, WRITE_STRING_FILE_CREATE) == 0);
167
168 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
169 assert_se(write_string_file(name2, t, WRITE_STRING_FILE_CREATE) == 0);
170
171 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
172 assert_se(write_string_file(name3, t, WRITE_STRING_FILE_CREATE) == 0);
173
174 t = strjoina("#!/bin/sh\necho OVERRIDDEN >>", output);
175 assert_se(write_string_file(overridden, t, WRITE_STRING_FILE_CREATE) == 0);
176
177 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
178 assert_se(write_string_file(override, t, WRITE_STRING_FILE_CREATE) == 0);
179
180 t = strjoina("#!/bin/sh\necho MASKED >>", output);
181 assert_se(write_string_file(masked, t, WRITE_STRING_FILE_CREATE) == 0);
182
183 assert_se(symlink("/dev/null", mask) == 0);
184
185 assert_se(chmod(name, 0755) == 0);
186 assert_se(chmod(name2, 0755) == 0);
187 assert_se(chmod(name3, 0755) == 0);
188 assert_se(chmod(overridden, 0755) == 0);
189 assert_se(chmod(override, 0755) == 0);
190 assert_se(chmod(masked, 0755) == 0);
191
3c14dc61
TM
192 if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
193 return;
194
4b05f0c9 195 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
c6e47247
ZJS
196
197 assert_se(read_full_file(output, &contents, NULL) >= 0);
c79e88b3 198 ASSERT_STREQ(contents, "30-override\n80-foo\n90-bar\nlast\n");
c6e47247
ZJS
199}
200
201static int gather_stdout_one(int fd, void *arg) {
202 char ***s = arg, *t;
203 char buf[128] = {};
204
205 assert_se(s);
206 assert_se(read(fd, buf, sizeof buf) >= 0);
207 safe_close(fd);
208
209 assert_se(t = strndup(buf, sizeof buf));
210 assert_se(strv_push(s, t) >= 0);
211
212 return 0;
213}
214static int gather_stdout_two(int fd, void *arg) {
de010b0b 215 char ***s = arg;
c6e47247
ZJS
216
217 STRV_FOREACH(t, *s)
218 assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t));
219 safe_close(fd);
220
221 return 0;
222}
223static int gather_stdout_three(int fd, void *arg) {
224 char **s = arg;
225 char buf[128] = {};
226
227 assert_se(read(fd, buf, sizeof buf - 1) > 0);
228 safe_close(fd);
229 assert_se(*s = strndup(buf, sizeof buf));
230
231 return 0;
232}
233
1a735f9b 234const gather_stdout_callback_t gather_stdouts[] = {
c6e47247
ZJS
235 gather_stdout_one,
236 gather_stdout_two,
237 gather_stdout_three,
238};
239
4f7452a8 240TEST(stdout_gathering) {
84e8602d 241 _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
c6e47247
ZJS
242 const char *name, *name2, *name3;
243 int r;
244
245 char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
246 _cleanup_free_ char *output = NULL;
247
248 void* args[] = {&tmp, &tmp, &output};
249
84e8602d
YW
250 assert_se(mkdtemp_malloc("/tmp/test-exec-util.XXXXXXX", &tmpdir) >= 0);
251
252 const char *dirs[] = { tmpdir, NULL };
c6e47247 253
c6e47247 254 /* write files */
84e8602d
YW
255 name = strjoina(tmpdir, "/10-foo");
256 name2 = strjoina(tmpdir, "/20-bar");
257 name3 = strjoina(tmpdir, "/30-last");
c6e47247
ZJS
258
259 assert_se(write_string_file(name,
260 "#!/bin/sh\necho a\necho b\necho c\n",
261 WRITE_STRING_FILE_CREATE) == 0);
262 assert_se(write_string_file(name2,
263 "#!/bin/sh\necho d\n",
264 WRITE_STRING_FILE_CREATE) == 0);
265 assert_se(write_string_file(name3,
266 "#!/bin/sh\nsleep 1",
267 WRITE_STRING_FILE_CREATE) == 0);
268
269 assert_se(chmod(name, 0755) == 0);
270 assert_se(chmod(name2, 0755) == 0);
271 assert_se(chmod(name3, 0755) == 0);
272
3c14dc61
TM
273 if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
274 return;
275
1a735f9b
ZJS
276 r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdouts, args, NULL, NULL,
277 EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
c6e47247
ZJS
278 assert_se(r >= 0);
279
280 log_info("got: %s", output);
281
c79e88b3 282 ASSERT_STREQ(output, "a\nb\nc\nd\n");
c6e47247
ZJS
283}
284
4f7452a8 285TEST(environment_gathering) {
84e8602d 286 _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
78ec1bb4 287 const char *name, *name2, *name3, *old;
3303d1b2
ZJS
288 int r;
289
290 char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
291 _cleanup_strv_free_ char **env = NULL;
292
293 void* const args[] = { &tmp, &tmp, &env };
294
84e8602d
YW
295 assert_se(mkdtemp_malloc("/tmp/test-exec-util.XXXXXXX", &tmpdir) >= 0);
296
297 const char *dirs[] = { tmpdir, NULL };
3303d1b2 298
3303d1b2 299 /* write files */
84e8602d
YW
300 name = strjoina(tmpdir, "/10-foo");
301 name2 = strjoina(tmpdir, "/20-bar");
302 name3 = strjoina(tmpdir, "/30-last");
3303d1b2
ZJS
303
304 assert_se(write_string_file(name,
305 "#!/bin/sh\n"
306 "echo A=23\n",
307 WRITE_STRING_FILE_CREATE) == 0);
308 assert_se(write_string_file(name2,
309 "#!/bin/sh\n"
310 "echo A=22:$A\n\n\n", /* substitution from previous generator */
311 WRITE_STRING_FILE_CREATE) == 0);
312 assert_se(write_string_file(name3,
313 "#!/bin/sh\n"
314 "echo A=$A:24\n"
315 "echo B=12\n"
316 "echo C=000\n"
184d1904
ZJS
317 "echo C=001\n" /* variable overwriting */
318 /* various invalid entries */
319 "echo unset A\n"
320 "echo unset A=\n"
321 "echo unset A=B\n"
322 "echo unset \n"
323 "echo A B=C\n"
324 "echo A\n"
325 /* test variable assignment without newline */
326 "echo PATH=$PATH:/no/such/file", /* no newline */
3303d1b2
ZJS
327 WRITE_STRING_FILE_CREATE) == 0);
328
329 assert_se(chmod(name, 0755) == 0);
330 assert_se(chmod(name2, 0755) == 0);
331 assert_se(chmod(name3, 0755) == 0);
332
32e27670 333 /* When booting in containers or without initrd there might not be any PATH in the environment and if
0f36a4c8 334 * there is no PATH /bin/sh built-in PATH may leak and override systemd's default path which is not
32e27670 335 * good. Force our own PATH in environment, to prevent expansion of sh built-in $PATH */
78ec1bb4
DJL
336 old = getenv("PATH");
337 r = setenv("PATH", "no-sh-built-in-path", 1);
338 assert_se(r >= 0);
339
3c14dc61
TM
340 if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
341 return;
342
4b05f0c9 343 r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
78ec1bb4
DJL
344 assert_se(r >= 0);
345
346 STRV_FOREACH(p, env)
347 log_info("got env: \"%s\"", *p);
348
c79e88b3
IK
349 ASSERT_STREQ(strv_env_get(env, "A"), "22:23:24");
350 ASSERT_STREQ(strv_env_get(env, "B"), "12");
351 ASSERT_STREQ(strv_env_get(env, "C"), "001");
352 ASSERT_STREQ(strv_env_get(env, "PATH"), "no-sh-built-in-path:/no/such/file");
78ec1bb4 353
0f36a4c8 354 /* Now retest with some "default" path passed. */
78ec1bb4 355 env = strv_free(env);
0f36a4c8 356 env = strv_new("PATH=" DEFAULT_PATH_WITHOUT_SBIN);
bea1a013 357 assert_se(env);
78ec1bb4 358
4b05f0c9 359 r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, env, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
3303d1b2
ZJS
360 assert_se(r >= 0);
361
362 STRV_FOREACH(p, env)
363 log_info("got env: \"%s\"", *p);
364
c79e88b3
IK
365 ASSERT_STREQ(strv_env_get(env, "A"), "22:23:24");
366 ASSERT_STREQ(strv_env_get(env, "B"), "12");
367 ASSERT_STREQ(strv_env_get(env, "C"), "001");
0f36a4c8 368 ASSERT_STREQ(strv_env_get(env, "PATH"), DEFAULT_PATH_WITHOUT_SBIN ":/no/such/file");
78ec1bb4
DJL
369
370 /* reset environ PATH */
063f9f0d 371 assert_se(set_unset_env("PATH", old, true) == 0);
3303d1b2
ZJS
372}
373
4f7452a8 374TEST(error_catching) {
84e8602d 375 _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
4b05f0c9
MK
376 const char *name, *name2, *name3;
377 int r;
378
84e8602d
YW
379 assert_se(mkdtemp_malloc("/tmp/test-exec-util.XXXXXXX", &tmpdir) >= 0);
380
381 const char *dirs[] = { tmpdir, NULL };
4b05f0c9 382
4b05f0c9 383 /* write files */
84e8602d
YW
384 name = strjoina(tmpdir, "/10-foo");
385 name2 = strjoina(tmpdir, "/20-bar");
386 name3 = strjoina(tmpdir, "/30-last");
4b05f0c9
MK
387
388 assert_se(write_string_file(name,
389 "#!/bin/sh\necho a\necho b\necho c\n",
390 WRITE_STRING_FILE_CREATE) == 0);
391 assert_se(write_string_file(name2,
392 "#!/bin/sh\nexit 42\n",
393 WRITE_STRING_FILE_CREATE) == 0);
394 assert_se(write_string_file(name3,
395 "#!/bin/sh\nexit 12",
396 WRITE_STRING_FILE_CREATE) == 0);
397
398 assert_se(chmod(name, 0755) == 0);
399 assert_se(chmod(name2, 0755) == 0);
400 assert_se(chmod(name3, 0755) == 0);
401
3c14dc61
TM
402 if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
403 return;
404
4b05f0c9
MK
405 r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL, EXEC_DIR_NONE);
406
407 /* we should exit with the error code of the first script that failed */
408 assert_se(r == 42);
409}
410
4f7452a8 411TEST(exec_command_flags_from_strv) {
b3d59367
AZ
412 ExecCommandFlags flags = 0;
413 char **valid_strv = STRV_MAKE("no-env-expand", "no-setuid", "ignore-failure");
414 char **invalid_strv = STRV_MAKE("no-env-expand", "no-setuid", "nonexistent-option", "ignore-failure");
415 int r;
416
417 r = exec_command_flags_from_strv(valid_strv, &flags);
418
419 assert_se(r == 0);
420 assert_se(FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND));
421 assert_se(FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID));
422 assert_se(FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE));
423 assert_se(!FLAGS_SET(flags, EXEC_COMMAND_AMBIENT_MAGIC));
424 assert_se(!FLAGS_SET(flags, EXEC_COMMAND_FULLY_PRIVILEGED));
425
426 r = exec_command_flags_from_strv(invalid_strv, &flags);
427
428 assert_se(r == -EINVAL);
429}
430
4f7452a8 431TEST(exec_command_flags_to_strv) {
b3d59367
AZ
432 _cleanup_strv_free_ char **opts = NULL, **empty_opts = NULL, **invalid_opts = NULL;
433 ExecCommandFlags flags = 0;
434 int r;
435
436 flags |= (EXEC_COMMAND_AMBIENT_MAGIC|EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_IGNORE_FAILURE);
437
438 r = exec_command_flags_to_strv(flags, &opts);
439
440 assert_se(r == 0);
441 assert_se(strv_equal(opts, STRV_MAKE("ignore-failure", "ambient", "no-env-expand")));
442
443 r = exec_command_flags_to_strv(0, &empty_opts);
444
445 assert_se(r == 0);
446 assert_se(strv_equal(empty_opts, STRV_MAKE_EMPTY));
447
448 flags = _EXEC_COMMAND_FLAGS_INVALID;
449
450 r = exec_command_flags_to_strv(flags, &invalid_opts);
451
452 assert_se(r == -EINVAL);
453}
454
4f7452a8 455DEFINE_TEST_MAIN(LOG_DEBUG);