1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
6 Copyright 2013 Thomas H.P. Andersen
15 #include "alloc-util.h"
19 #include "exec-util.h"
26 #include "string-util.h"
29 static int here
= 0, here2
= 0, here3
= 0;
30 void *ignore_stdout_args
[] = {&here
, &here2
, &here3
};
32 /* noop handlers, just check that arguments are passed correctly */
33 static int ignore_stdout_func(int fd
, void *arg
) {
40 static int ignore_stdout_func2(int fd
, void *arg
) {
42 assert(arg
== &here2
);
47 static int ignore_stdout_func3(int fd
, void *arg
) {
49 assert(arg
== &here3
);
55 static const gather_stdout_callback_t ignore_stdout
[] = {
61 static void test_execute_directory(bool gather_stdout
) {
62 char template_lo
[] = "/tmp/test-exec-util.lo.XXXXXXX";
63 char template_hi
[] = "/tmp/test-exec-util.hi.XXXXXXX";
64 const char * dirs
[] = {template_hi
, template_lo
, NULL
};
65 const char *name
, *name2
, *name3
,
66 *overridden
, *override
,
68 *masked2
, *mask2
, /* the mask is non-executable */
69 *masked2e
, *mask2e
; /* the mask is executable */
71 log_info("/* %s (%s) */", __func__
, gather_stdout
? "gathering stdout" : "asynchronous");
73 assert_se(mkdtemp(template_lo
));
74 assert_se(mkdtemp(template_hi
));
76 name
= strjoina(template_lo
, "/script");
77 name2
= strjoina(template_hi
, "/script2");
78 name3
= strjoina(template_lo
, "/useless");
79 overridden
= strjoina(template_lo
, "/overridden");
80 override
= strjoina(template_hi
, "/overridden");
81 masked
= strjoina(template_lo
, "/masked");
82 mask
= strjoina(template_hi
, "/masked");
83 masked2
= strjoina(template_lo
, "/masked2");
84 mask2
= strjoina(template_hi
, "/masked2");
85 masked2e
= strjoina(template_lo
, "/masked2e");
86 mask2e
= strjoina(template_hi
, "/masked2e");
88 assert_se(write_string_file(name
,
89 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works",
90 WRITE_STRING_FILE_CREATE
) == 0);
91 assert_se(write_string_file(name2
,
92 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2",
93 WRITE_STRING_FILE_CREATE
) == 0);
94 assert_se(write_string_file(overridden
,
95 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
96 WRITE_STRING_FILE_CREATE
) == 0);
97 assert_se(write_string_file(override
,
98 "#!/bin/sh\necho 'Executing '$0",
99 WRITE_STRING_FILE_CREATE
) == 0);
100 assert_se(write_string_file(masked
,
101 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
102 WRITE_STRING_FILE_CREATE
) == 0);
103 assert_se(write_string_file(masked2
,
104 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
105 WRITE_STRING_FILE_CREATE
) == 0);
106 assert_se(write_string_file(masked2e
,
107 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
108 WRITE_STRING_FILE_CREATE
) == 0);
109 assert_se(symlink("/dev/null", mask
) == 0);
110 assert_se(touch(mask2
) == 0);
111 assert_se(touch(mask2e
) == 0);
112 assert_se(touch(name3
) >= 0);
114 assert_se(chmod(name
, 0755) == 0);
115 assert_se(chmod(name2
, 0755) == 0);
116 assert_se(chmod(overridden
, 0755) == 0);
117 assert_se(chmod(override
, 0755) == 0);
118 assert_se(chmod(masked
, 0755) == 0);
119 assert_se(chmod(masked2
, 0755) == 0);
120 assert_se(chmod(masked2e
, 0755) == 0);
121 assert_se(chmod(mask2e
, 0755) == 0);
124 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, ignore_stdout
, ignore_stdout_args
, NULL
);
126 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, NULL
);
128 assert_se(chdir(template_lo
) == 0);
129 assert_se(access("it_works", F_OK
) >= 0);
130 assert_se(access("failed", F_OK
) < 0);
132 assert_se(chdir(template_hi
) == 0);
133 assert_se(access("it_works2", F_OK
) >= 0);
134 assert_se(access("failed", F_OK
) < 0);
136 (void) rm_rf(template_lo
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
137 (void) rm_rf(template_hi
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
140 static void test_execution_order(void) {
141 char template_lo
[] = "/tmp/test-exec-util-lo.XXXXXXX";
142 char template_hi
[] = "/tmp/test-exec-util-hi.XXXXXXX";
143 const char *dirs
[] = {template_hi
, template_lo
, NULL
};
144 const char *name
, *name2
, *name3
, *overridden
, *override
, *masked
, *mask
;
145 const char *output
, *t
;
146 _cleanup_free_
char *contents
= NULL
;
148 assert_se(mkdtemp(template_lo
));
149 assert_se(mkdtemp(template_hi
));
151 output
= strjoina(template_hi
, "/output");
153 log_info("/* %s >>%s */", __func__
, output
);
155 /* write files in "random" order */
156 name2
= strjoina(template_lo
, "/90-bar");
157 name
= strjoina(template_hi
, "/80-foo");
158 name3
= strjoina(template_lo
, "/last");
159 overridden
= strjoina(template_lo
, "/30-override");
160 override
= strjoina(template_hi
, "/30-override");
161 masked
= strjoina(template_lo
, "/10-masked");
162 mask
= strjoina(template_hi
, "/10-masked");
164 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
165 assert_se(write_string_file(name
, t
, WRITE_STRING_FILE_CREATE
) == 0);
167 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
168 assert_se(write_string_file(name2
, t
, WRITE_STRING_FILE_CREATE
) == 0);
170 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
171 assert_se(write_string_file(name3
, t
, WRITE_STRING_FILE_CREATE
) == 0);
173 t
= strjoina("#!/bin/sh\necho OVERRIDDEN >>", output
);
174 assert_se(write_string_file(overridden
, t
, WRITE_STRING_FILE_CREATE
) == 0);
176 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
177 assert_se(write_string_file(override
, t
, WRITE_STRING_FILE_CREATE
) == 0);
179 t
= strjoina("#!/bin/sh\necho MASKED >>", output
);
180 assert_se(write_string_file(masked
, t
, WRITE_STRING_FILE_CREATE
) == 0);
182 assert_se(symlink("/dev/null", mask
) == 0);
184 assert_se(chmod(name
, 0755) == 0);
185 assert_se(chmod(name2
, 0755) == 0);
186 assert_se(chmod(name3
, 0755) == 0);
187 assert_se(chmod(overridden
, 0755) == 0);
188 assert_se(chmod(override
, 0755) == 0);
189 assert_se(chmod(masked
, 0755) == 0);
191 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, ignore_stdout
, ignore_stdout_args
, NULL
);
193 assert_se(read_full_file(output
, &contents
, NULL
) >= 0);
194 assert_se(streq(contents
, "30-override\n80-foo\n90-bar\nlast\n"));
196 (void) rm_rf(template_lo
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
197 (void) rm_rf(template_hi
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
200 static int gather_stdout_one(int fd
, void *arg
) {
205 assert_se(read(fd
, buf
, sizeof buf
) >= 0);
208 assert_se(t
= strndup(buf
, sizeof buf
));
209 assert_se(strv_push(s
, t
) >= 0);
213 static int gather_stdout_two(int fd
, void *arg
) {
214 char ***s
= arg
, **t
;
217 assert_se(write(fd
, *t
, strlen(*t
)) == (ssize_t
) strlen(*t
));
222 static int gather_stdout_three(int fd
, void *arg
) {
226 assert_se(read(fd
, buf
, sizeof buf
- 1) > 0);
228 assert_se(*s
= strndup(buf
, sizeof buf
));
233 const gather_stdout_callback_t gather_stdout
[] = {
240 static void test_stdout_gathering(void) {
241 char template[] = "/tmp/test-exec-util.XXXXXXX";
242 const char *dirs
[] = {template, NULL
};
243 const char *name
, *name2
, *name3
;
246 char **tmp
= NULL
; /* this is only used in the forked process, no cleanup here */
247 _cleanup_free_
char *output
= NULL
;
249 void* args
[] = {&tmp
, &tmp
, &output
};
251 assert_se(mkdtemp(template));
253 log_info("/* %s */", __func__
);
256 name
= strjoina(template, "/10-foo");
257 name2
= strjoina(template, "/20-bar");
258 name3
= strjoina(template, "/30-last");
260 assert_se(write_string_file(name
,
261 "#!/bin/sh\necho a\necho b\necho c\n",
262 WRITE_STRING_FILE_CREATE
) == 0);
263 assert_se(write_string_file(name2
,
264 "#!/bin/sh\necho d\n",
265 WRITE_STRING_FILE_CREATE
) == 0);
266 assert_se(write_string_file(name3
,
267 "#!/bin/sh\nsleep 1",
268 WRITE_STRING_FILE_CREATE
) == 0);
270 assert_se(chmod(name
, 0755) == 0);
271 assert_se(chmod(name2
, 0755) == 0);
272 assert_se(chmod(name3
, 0755) == 0);
274 r
= execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, gather_stdout
, args
, NULL
);
277 log_info("got: %s", output
);
279 assert_se(streq(output
, "a\nb\nc\nd\n"));
282 static void test_environment_gathering(void) {
283 char template[] = "/tmp/test-exec-util.XXXXXXX", **p
;
284 const char *dirs
[] = {template, NULL
};
285 const char *name
, *name2
, *name3
;
288 char **tmp
= NULL
; /* this is only used in the forked process, no cleanup here */
289 _cleanup_strv_free_
char **env
= NULL
;
291 void* const args
[] = { &tmp
, &tmp
, &env
};
293 assert_se(mkdtemp(template));
295 log_info("/* %s */", __func__
);
298 name
= strjoina(template, "/10-foo");
299 name2
= strjoina(template, "/20-bar");
300 name3
= strjoina(template, "/30-last");
302 assert_se(write_string_file(name
,
305 WRITE_STRING_FILE_CREATE
) == 0);
306 assert_se(write_string_file(name2
,
308 "echo A=22:$A\n\n\n", /* substitution from previous generator */
309 WRITE_STRING_FILE_CREATE
) == 0);
310 assert_se(write_string_file(name3
,
315 "echo C=001\n" /* variable overwriting */
316 /* various invalid entries */
323 /* test variable assignment without newline */
324 "echo PATH=$PATH:/no/such/file", /* no newline */
325 WRITE_STRING_FILE_CREATE
) == 0);
327 assert_se(chmod(name
, 0755) == 0);
328 assert_se(chmod(name2
, 0755) == 0);
329 assert_se(chmod(name3
, 0755) == 0);
331 r
= execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, gather_environment
, args
, NULL
);
335 log_info("got env: \"%s\"", *p
);
337 assert_se(streq(strv_env_get(env
, "A"), "22:23:24"));
338 assert_se(streq(strv_env_get(env
, "B"), "12"));
339 assert_se(streq(strv_env_get(env
, "C"), "001"));
340 assert_se(endswith(strv_env_get(env
, "PATH"), ":/no/such/file"));
343 int main(int argc
, char *argv
[]) {
344 log_set_max_level(LOG_DEBUG
);
345 log_parse_environment();
348 test_execute_directory(true);
349 test_execute_directory(false);
350 test_execution_order();
351 test_stdout_gathering();
352 test_environment_gathering();