]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-exec-util.c
userdb: suppress creation of empty userdb dirs
[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)
66b2d758 120 execute_directories("test", dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
c6e47247 121 else
66b2d758 122 execute_directories("test", 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
66b2d758
ZJS
192 execute_directories(__func__,
193 dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL,
194 EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
c6e47247
ZJS
195
196 assert_se(read_full_file(output, &contents, NULL) >= 0);
c79e88b3 197 ASSERT_STREQ(contents, "30-override\n80-foo\n90-bar\nlast\n");
c6e47247
ZJS
198}
199
200static int gather_stdout_one(int fd, void *arg) {
201 char ***s = arg, *t;
202 char buf[128] = {};
203
204 assert_se(s);
205 assert_se(read(fd, buf, sizeof buf) >= 0);
206 safe_close(fd);
207
208 assert_se(t = strndup(buf, sizeof buf));
209 assert_se(strv_push(s, t) >= 0);
210
211 return 0;
212}
213static int gather_stdout_two(int fd, void *arg) {
de010b0b 214 char ***s = arg;
c6e47247
ZJS
215
216 STRV_FOREACH(t, *s)
217 assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t));
218 safe_close(fd);
219
220 return 0;
221}
222static int gather_stdout_three(int fd, void *arg) {
223 char **s = arg;
224 char buf[128] = {};
225
226 assert_se(read(fd, buf, sizeof buf - 1) > 0);
227 safe_close(fd);
228 assert_se(*s = strndup(buf, sizeof buf));
229
230 return 0;
231}
232
1a735f9b 233const gather_stdout_callback_t gather_stdouts[] = {
c6e47247
ZJS
234 gather_stdout_one,
235 gather_stdout_two,
236 gather_stdout_three,
237};
238
4f7452a8 239TEST(stdout_gathering) {
84e8602d 240 _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
c6e47247
ZJS
241 const char *name, *name2, *name3;
242 int r;
243
244 char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
245 _cleanup_free_ char *output = NULL;
246
247 void* args[] = {&tmp, &tmp, &output};
248
84e8602d
YW
249 assert_se(mkdtemp_malloc("/tmp/test-exec-util.XXXXXXX", &tmpdir) >= 0);
250
251 const char *dirs[] = { tmpdir, NULL };
c6e47247 252
c6e47247 253 /* write files */
84e8602d
YW
254 name = strjoina(tmpdir, "/10-foo");
255 name2 = strjoina(tmpdir, "/20-bar");
256 name3 = strjoina(tmpdir, "/30-last");
c6e47247
ZJS
257
258 assert_se(write_string_file(name,
259 "#!/bin/sh\necho a\necho b\necho c\n",
260 WRITE_STRING_FILE_CREATE) == 0);
261 assert_se(write_string_file(name2,
262 "#!/bin/sh\necho d\n",
263 WRITE_STRING_FILE_CREATE) == 0);
264 assert_se(write_string_file(name3,
265 "#!/bin/sh\nsleep 1",
266 WRITE_STRING_FILE_CREATE) == 0);
267
268 assert_se(chmod(name, 0755) == 0);
269 assert_se(chmod(name2, 0755) == 0);
270 assert_se(chmod(name3, 0755) == 0);
271
3c14dc61
TM
272 if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
273 return;
274
66b2d758
ZJS
275 r = execute_directories(__func__,
276 dirs, DEFAULT_TIMEOUT_USEC, gather_stdouts, args, NULL, NULL,
1a735f9b 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
66b2d758
ZJS
343 r = execute_directories(__func__,
344 dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, NULL,
345 EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
78ec1bb4
DJL
346 assert_se(r >= 0);
347
348 STRV_FOREACH(p, env)
349 log_info("got env: \"%s\"", *p);
350
c79e88b3
IK
351 ASSERT_STREQ(strv_env_get(env, "A"), "22:23:24");
352 ASSERT_STREQ(strv_env_get(env, "B"), "12");
353 ASSERT_STREQ(strv_env_get(env, "C"), "001");
354 ASSERT_STREQ(strv_env_get(env, "PATH"), "no-sh-built-in-path:/no/such/file");
78ec1bb4 355
0f36a4c8 356 /* Now retest with some "default" path passed. */
78ec1bb4 357 env = strv_free(env);
0f36a4c8 358 env = strv_new("PATH=" DEFAULT_PATH_WITHOUT_SBIN);
bea1a013 359 assert_se(env);
78ec1bb4 360
66b2d758
ZJS
361 r = execute_directories(__func__,
362 dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, env,
363 EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
3303d1b2
ZJS
364 assert_se(r >= 0);
365
366 STRV_FOREACH(p, env)
367 log_info("got env: \"%s\"", *p);
368
c79e88b3
IK
369 ASSERT_STREQ(strv_env_get(env, "A"), "22:23:24");
370 ASSERT_STREQ(strv_env_get(env, "B"), "12");
371 ASSERT_STREQ(strv_env_get(env, "C"), "001");
0f36a4c8 372 ASSERT_STREQ(strv_env_get(env, "PATH"), DEFAULT_PATH_WITHOUT_SBIN ":/no/such/file");
78ec1bb4
DJL
373
374 /* reset environ PATH */
063f9f0d 375 assert_se(set_unset_env("PATH", old, true) == 0);
3303d1b2
ZJS
376}
377
4f7452a8 378TEST(error_catching) {
84e8602d 379 _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
4b05f0c9
MK
380 const char *name, *name2, *name3;
381 int r;
382
84e8602d
YW
383 assert_se(mkdtemp_malloc("/tmp/test-exec-util.XXXXXXX", &tmpdir) >= 0);
384
385 const char *dirs[] = { tmpdir, NULL };
4b05f0c9 386
4b05f0c9 387 /* write files */
84e8602d
YW
388 name = strjoina(tmpdir, "/10-foo");
389 name2 = strjoina(tmpdir, "/20-bar");
390 name3 = strjoina(tmpdir, "/30-last");
4b05f0c9
MK
391
392 assert_se(write_string_file(name,
393 "#!/bin/sh\necho a\necho b\necho c\n",
394 WRITE_STRING_FILE_CREATE) == 0);
395 assert_se(write_string_file(name2,
396 "#!/bin/sh\nexit 42\n",
397 WRITE_STRING_FILE_CREATE) == 0);
398 assert_se(write_string_file(name3,
399 "#!/bin/sh\nexit 12",
400 WRITE_STRING_FILE_CREATE) == 0);
401
402 assert_se(chmod(name, 0755) == 0);
403 assert_se(chmod(name2, 0755) == 0);
404 assert_se(chmod(name3, 0755) == 0);
405
3c14dc61
TM
406 if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
407 return;
408
66b2d758
ZJS
409 r = execute_directories(__func__,
410 dirs, DEFAULT_TIMEOUT_USEC,
8e39ba3e
MY
411 /* callbacks = */ NULL, /* callback_args = */ NULL,
412 /* argv = */ NULL, /* envp = */ NULL, /* flags = */ 0);
4b05f0c9
MK
413
414 /* we should exit with the error code of the first script that failed */
415 assert_se(r == 42);
416}
417
4f7452a8 418TEST(exec_command_flags_from_strv) {
b3d59367
AZ
419 ExecCommandFlags flags = 0;
420 char **valid_strv = STRV_MAKE("no-env-expand", "no-setuid", "ignore-failure");
421 char **invalid_strv = STRV_MAKE("no-env-expand", "no-setuid", "nonexistent-option", "ignore-failure");
422 int r;
423
424 r = exec_command_flags_from_strv(valid_strv, &flags);
425
426 assert_se(r == 0);
427 assert_se(FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND));
428 assert_se(FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID));
429 assert_se(FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE));
b3d59367
AZ
430 assert_se(!FLAGS_SET(flags, EXEC_COMMAND_FULLY_PRIVILEGED));
431
432 r = exec_command_flags_from_strv(invalid_strv, &flags);
433
434 assert_se(r == -EINVAL);
435}
436
4f7452a8 437TEST(exec_command_flags_to_strv) {
05c754bc 438 _cleanup_strv_free_ char **opts = NULL;
b3d59367 439
00a415fc
LP
440 ASSERT_OK(exec_command_flags_to_strv(EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_IGNORE_FAILURE, &opts));
441 assert_se(strv_equal(opts, STRV_MAKE("ignore-failure", "no-env-expand")));
b3d59367 442
05c754bc 443 opts = strv_free(opts);
b3d59367 444
05c754bc
MY
445 ASSERT_OK(exec_command_flags_to_strv(0, &opts));
446 assert_se(strv_isempty(opts));
b3d59367 447
05c754bc 448 opts = strv_free(opts);
b3d59367 449
05c754bc 450 ASSERT_ERROR(exec_command_flags_to_strv(1U << 20, &opts), EINVAL);
b3d59367
AZ
451}
452
4f7452a8 453DEFINE_TEST_MAIN(LOG_DEBUG);