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