1 /* SPDX-License-Identifier: LGPL-2.1+ */
9 #include "alloc-util.h"
13 #include "exec-util.h"
19 #include "path-util.h"
21 #include "string-util.h"
25 static int here
= 0, here2
= 0, here3
= 0;
26 void *ignore_stdout_args
[] = {&here
, &here2
, &here3
};
28 /* noop handlers, just check that arguments are passed correctly */
29 static int ignore_stdout_func(int fd
, void *arg
) {
36 static int ignore_stdout_func2(int fd
, void *arg
) {
38 assert(arg
== &here2
);
43 static int ignore_stdout_func3(int fd
, void *arg
) {
45 assert(arg
== &here3
);
51 static const gather_stdout_callback_t ignore_stdout
[] = {
57 static void test_execute_directory(bool gather_stdout
) {
58 char template_lo
[] = "/tmp/test-exec-util.lo.XXXXXXX";
59 char template_hi
[] = "/tmp/test-exec-util.hi.XXXXXXX";
60 const char * dirs
[] = {template_hi
, template_lo
, NULL
};
61 const char *name
, *name2
, *name3
,
62 *overridden
, *override
,
64 *masked2
, *mask2
, /* the mask is non-executable */
65 *masked2e
, *mask2e
; /* the mask is executable */
67 log_info("/* %s (%s) */", __func__
, gather_stdout
? "gathering stdout" : "asynchronous");
69 assert_se(mkdtemp(template_lo
));
70 assert_se(mkdtemp(template_hi
));
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");
79 masked2
= strjoina(template_lo
, "/masked2");
80 mask2
= strjoina(template_hi
, "/masked2");
81 masked2e
= strjoina(template_lo
, "/masked2e");
82 mask2e
= strjoina(template_hi
, "/masked2e");
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);
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);
105 assert_se(symlink("/dev/null", mask
) == 0);
106 assert_se(touch(mask2
) == 0);
107 assert_se(touch(mask2e
) == 0);
108 assert_se(touch(name3
) >= 0);
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);
115 assert_se(chmod(masked2
, 0755) == 0);
116 assert_se(chmod(masked2e
, 0755) == 0);
117 assert_se(chmod(mask2e
, 0755) == 0);
120 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, ignore_stdout
, ignore_stdout_args
, NULL
, NULL
);
122 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, NULL
, NULL
);
124 assert_se(chdir(template_lo
) == 0);
125 assert_se(access("it_works", F_OK
) >= 0);
126 assert_se(access("failed", F_OK
) < 0);
128 assert_se(chdir(template_hi
) == 0);
129 assert_se(access("it_works2", F_OK
) >= 0);
130 assert_se(access("failed", F_OK
) < 0);
132 (void) rm_rf(template_lo
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
133 (void) rm_rf(template_hi
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
136 static 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
;
144 assert_se(mkdtemp(template_lo
));
145 assert_se(mkdtemp(template_hi
));
147 output
= strjoina(template_hi
, "/output");
149 log_info("/* %s >>%s */", __func__
, output
);
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");
160 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
161 assert_se(write_string_file(name
, t
, WRITE_STRING_FILE_CREATE
) == 0);
163 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
164 assert_se(write_string_file(name2
, t
, WRITE_STRING_FILE_CREATE
) == 0);
166 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
167 assert_se(write_string_file(name3
, t
, WRITE_STRING_FILE_CREATE
) == 0);
169 t
= strjoina("#!/bin/sh\necho OVERRIDDEN >>", output
);
170 assert_se(write_string_file(overridden
, t
, WRITE_STRING_FILE_CREATE
) == 0);
172 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
173 assert_se(write_string_file(override
, t
, WRITE_STRING_FILE_CREATE
) == 0);
175 t
= strjoina("#!/bin/sh\necho MASKED >>", output
);
176 assert_se(write_string_file(masked
, t
, WRITE_STRING_FILE_CREATE
) == 0);
178 assert_se(symlink("/dev/null", mask
) == 0);
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);
187 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, ignore_stdout
, ignore_stdout_args
, NULL
, NULL
);
189 assert_se(read_full_file(output
, &contents
, NULL
) >= 0);
190 assert_se(streq(contents
, "30-override\n80-foo\n90-bar\nlast\n"));
192 (void) rm_rf(template_lo
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
193 (void) rm_rf(template_hi
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
196 static int gather_stdout_one(int fd
, void *arg
) {
201 assert_se(read(fd
, buf
, sizeof buf
) >= 0);
204 assert_se(t
= strndup(buf
, sizeof buf
));
205 assert_se(strv_push(s
, t
) >= 0);
209 static int gather_stdout_two(int fd
, void *arg
) {
210 char ***s
= arg
, **t
;
213 assert_se(write(fd
, *t
, strlen(*t
)) == (ssize_t
) strlen(*t
));
218 static int gather_stdout_three(int fd
, void *arg
) {
222 assert_se(read(fd
, buf
, sizeof buf
- 1) > 0);
224 assert_se(*s
= strndup(buf
, sizeof buf
));
229 const gather_stdout_callback_t gather_stdout
[] = {
235 static 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
;
241 char **tmp
= NULL
; /* this is only used in the forked process, no cleanup here */
242 _cleanup_free_
char *output
= NULL
;
244 void* args
[] = {&tmp
, &tmp
, &output
};
246 assert_se(mkdtemp(template));
248 log_info("/* %s */", __func__
);
251 name
= strjoina(template, "/10-foo");
252 name2
= strjoina(template, "/20-bar");
253 name3
= strjoina(template, "/30-last");
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);
265 assert_se(chmod(name
, 0755) == 0);
266 assert_se(chmod(name2
, 0755) == 0);
267 assert_se(chmod(name3
, 0755) == 0);
269 r
= execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, gather_stdout
, args
, NULL
, NULL
);
272 log_info("got: %s", output
);
274 assert_se(streq(output
, "a\nb\nc\nd\n"));
277 static void test_environment_gathering(void) {
278 char template[] = "/tmp/test-exec-util.XXXXXXX", **p
;
279 const char *dirs
[] = {template, NULL
};
280 const char *name
, *name2
, *name3
, *old
;
283 char **tmp
= NULL
; /* this is only used in the forked process, no cleanup here */
284 _cleanup_strv_free_
char **env
= NULL
;
286 void* const args
[] = { &tmp
, &tmp
, &env
};
288 assert_se(mkdtemp(template));
290 log_info("/* %s */", __func__
);
293 name
= strjoina(template, "/10-foo");
294 name2
= strjoina(template, "/20-bar");
295 name3
= strjoina(template, "/30-last");
297 assert_se(write_string_file(name
,
300 WRITE_STRING_FILE_CREATE
) == 0);
301 assert_se(write_string_file(name2
,
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
,
310 "echo C=001\n" /* variable overwriting */
311 /* various invalid entries */
318 /* test variable assignment without newline */
319 "echo PATH=$PATH:/no/such/file", /* no newline */
320 WRITE_STRING_FILE_CREATE
) == 0);
322 assert_se(chmod(name
, 0755) == 0);
323 assert_se(chmod(name2
, 0755) == 0);
324 assert_se(chmod(name3
, 0755) == 0);
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
331 old
= getenv("PATH");
332 r
= setenv("PATH", "no-sh-built-in-path", 1);
335 r
= execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, gather_environment
, args
, NULL
, NULL
);
339 log_info("got env: \"%s\"", *p
);
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"));
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
);
352 r
= execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, gather_environment
, args
, NULL
, env
);
356 log_info("got env: \"%s\"", *p
);
358 assert_se(streq(strv_env_get(env
, "A"), "22:23:24"));
359 assert_se(streq(strv_env_get(env
, "B"), "12"));
360 assert_se(streq(strv_env_get(env
, "C"), "001"));
361 assert_se(streq(strv_env_get(env
, "PATH"), DEFAULT_PATH
":/no/such/file"));
363 /* reset environ PATH */
364 (void) setenv("PATH", old
, 1);
367 int main(int argc
, char *argv
[]) {
368 test_setup_logging(LOG_DEBUG
);
370 test_execute_directory(true);
371 test_execute_directory(false);
372 test_execution_order();
373 test_stdout_gathering();
374 test_environment_gathering();