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
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "alloc-util.h"
32 #include "exec-util.h"
39 #include "string-util.h"
42 static int here
= 0, here2
= 0, here3
= 0;
43 void *ignore_stdout_args
[] = {&here
, &here2
, &here3
};
45 /* noop handlers, just check that arguments are passed correctly */
46 static int ignore_stdout_func(int fd
, void *arg
) {
53 static int ignore_stdout_func2(int fd
, void *arg
) {
55 assert(arg
== &here2
);
60 static int ignore_stdout_func3(int fd
, void *arg
) {
62 assert(arg
== &here3
);
68 static const gather_stdout_callback_t ignore_stdout
[] = {
74 static void test_execute_directory(bool gather_stdout
) {
75 char template_lo
[] = "/tmp/test-exec-util.lo.XXXXXXX";
76 char template_hi
[] = "/tmp/test-exec-util.hi.XXXXXXX";
77 const char * dirs
[] = {template_hi
, template_lo
, NULL
};
78 const char *name
, *name2
, *name3
,
79 *overridden
, *override
,
81 *masked2
, *mask2
, /* the mask is non-executable */
82 *masked2e
, *mask2e
; /* the mask is executable */
84 log_info("/* %s (%s) */", __func__
, gather_stdout
? "gathering stdout" : "asynchronous");
86 assert_se(mkdtemp(template_lo
));
87 assert_se(mkdtemp(template_hi
));
89 name
= strjoina(template_lo
, "/script");
90 name2
= strjoina(template_hi
, "/script2");
91 name3
= strjoina(template_lo
, "/useless");
92 overridden
= strjoina(template_lo
, "/overridden");
93 override
= strjoina(template_hi
, "/overridden");
94 masked
= strjoina(template_lo
, "/masked");
95 mask
= strjoina(template_hi
, "/masked");
96 masked2
= strjoina(template_lo
, "/masked2");
97 mask2
= strjoina(template_hi
, "/masked2");
98 masked2e
= strjoina(template_lo
, "/masked2e");
99 mask2e
= strjoina(template_hi
, "/masked2e");
101 assert_se(write_string_file(name
,
102 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works",
103 WRITE_STRING_FILE_CREATE
) == 0);
104 assert_se(write_string_file(name2
,
105 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2",
106 WRITE_STRING_FILE_CREATE
) == 0);
107 assert_se(write_string_file(overridden
,
108 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
109 WRITE_STRING_FILE_CREATE
) == 0);
110 assert_se(write_string_file(override
,
111 "#!/bin/sh\necho 'Executing '$0",
112 WRITE_STRING_FILE_CREATE
) == 0);
113 assert_se(write_string_file(masked
,
114 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
115 WRITE_STRING_FILE_CREATE
) == 0);
116 assert_se(write_string_file(masked2
,
117 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
118 WRITE_STRING_FILE_CREATE
) == 0);
119 assert_se(write_string_file(masked2e
,
120 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
121 WRITE_STRING_FILE_CREATE
) == 0);
122 assert_se(symlink("/dev/null", mask
) == 0);
123 assert_se(touch(mask2
) == 0);
124 assert_se(touch(mask2e
) == 0);
125 assert_se(touch(name3
) >= 0);
127 assert_se(chmod(name
, 0755) == 0);
128 assert_se(chmod(name2
, 0755) == 0);
129 assert_se(chmod(overridden
, 0755) == 0);
130 assert_se(chmod(override
, 0755) == 0);
131 assert_se(chmod(masked
, 0755) == 0);
132 assert_se(chmod(masked2
, 0755) == 0);
133 assert_se(chmod(masked2e
, 0755) == 0);
134 assert_se(chmod(mask2e
, 0755) == 0);
137 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, ignore_stdout
, ignore_stdout_args
, NULL
);
139 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, NULL
);
141 assert_se(chdir(template_lo
) == 0);
142 assert_se(access("it_works", F_OK
) >= 0);
143 assert_se(access("failed", F_OK
) < 0);
145 assert_se(chdir(template_hi
) == 0);
146 assert_se(access("it_works2", F_OK
) >= 0);
147 assert_se(access("failed", F_OK
) < 0);
149 (void) rm_rf(template_lo
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
150 (void) rm_rf(template_hi
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
153 static void test_execution_order(void) {
154 char template_lo
[] = "/tmp/test-exec-util-lo.XXXXXXX";
155 char template_hi
[] = "/tmp/test-exec-util-hi.XXXXXXX";
156 const char *dirs
[] = {template_hi
, template_lo
, NULL
};
157 const char *name
, *name2
, *name3
, *overridden
, *override
, *masked
, *mask
;
158 const char *output
, *t
;
159 _cleanup_free_
char *contents
= NULL
;
161 assert_se(mkdtemp(template_lo
));
162 assert_se(mkdtemp(template_hi
));
164 output
= strjoina(template_hi
, "/output");
166 log_info("/* %s >>%s */", __func__
, output
);
168 /* write files in "random" order */
169 name2
= strjoina(template_lo
, "/90-bar");
170 name
= strjoina(template_hi
, "/80-foo");
171 name3
= strjoina(template_lo
, "/last");
172 overridden
= strjoina(template_lo
, "/30-override");
173 override
= strjoina(template_hi
, "/30-override");
174 masked
= strjoina(template_lo
, "/10-masked");
175 mask
= strjoina(template_hi
, "/10-masked");
177 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
178 assert_se(write_string_file(name
, t
, WRITE_STRING_FILE_CREATE
) == 0);
180 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
181 assert_se(write_string_file(name2
, t
, WRITE_STRING_FILE_CREATE
) == 0);
183 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
184 assert_se(write_string_file(name3
, t
, WRITE_STRING_FILE_CREATE
) == 0);
186 t
= strjoina("#!/bin/sh\necho OVERRIDDEN >>", output
);
187 assert_se(write_string_file(overridden
, t
, WRITE_STRING_FILE_CREATE
) == 0);
189 t
= strjoina("#!/bin/sh\necho $(basename $0) >>", output
);
190 assert_se(write_string_file(override
, t
, WRITE_STRING_FILE_CREATE
) == 0);
192 t
= strjoina("#!/bin/sh\necho MASKED >>", output
);
193 assert_se(write_string_file(masked
, t
, WRITE_STRING_FILE_CREATE
) == 0);
195 assert_se(symlink("/dev/null", mask
) == 0);
197 assert_se(chmod(name
, 0755) == 0);
198 assert_se(chmod(name2
, 0755) == 0);
199 assert_se(chmod(name3
, 0755) == 0);
200 assert_se(chmod(overridden
, 0755) == 0);
201 assert_se(chmod(override
, 0755) == 0);
202 assert_se(chmod(masked
, 0755) == 0);
204 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, ignore_stdout
, ignore_stdout_args
, NULL
);
206 assert_se(read_full_file(output
, &contents
, NULL
) >= 0);
207 assert_se(streq(contents
, "30-override\n80-foo\n90-bar\nlast\n"));
209 (void) rm_rf(template_lo
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
210 (void) rm_rf(template_hi
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
213 static int gather_stdout_one(int fd
, void *arg
) {
218 assert_se(read(fd
, buf
, sizeof buf
) >= 0);
221 assert_se(t
= strndup(buf
, sizeof buf
));
222 assert_se(strv_push(s
, t
) >= 0);
226 static int gather_stdout_two(int fd
, void *arg
) {
227 char ***s
= arg
, **t
;
230 assert_se(write(fd
, *t
, strlen(*t
)) == (ssize_t
) strlen(*t
));
235 static int gather_stdout_three(int fd
, void *arg
) {
239 assert_se(read(fd
, buf
, sizeof buf
- 1) > 0);
241 assert_se(*s
= strndup(buf
, sizeof buf
));
246 const gather_stdout_callback_t gather_stdout
[] = {
253 static void test_stdout_gathering(void) {
254 char template[] = "/tmp/test-exec-util.XXXXXXX";
255 const char *dirs
[] = {template, NULL
};
256 const char *name
, *name2
, *name3
;
259 char **tmp
= NULL
; /* this is only used in the forked process, no cleanup here */
260 _cleanup_free_
char *output
= NULL
;
262 void* args
[] = {&tmp
, &tmp
, &output
};
264 assert_se(mkdtemp(template));
266 log_info("/* %s */", __func__
);
269 name
= strjoina(template, "/10-foo");
270 name2
= strjoina(template, "/20-bar");
271 name3
= strjoina(template, "/30-last");
273 assert_se(write_string_file(name
,
274 "#!/bin/sh\necho a\necho b\necho c\n",
275 WRITE_STRING_FILE_CREATE
) == 0);
276 assert_se(write_string_file(name2
,
277 "#!/bin/sh\necho d\n",
278 WRITE_STRING_FILE_CREATE
) == 0);
279 assert_se(write_string_file(name3
,
280 "#!/bin/sh\nsleep 1",
281 WRITE_STRING_FILE_CREATE
) == 0);
283 assert_se(chmod(name
, 0755) == 0);
284 assert_se(chmod(name2
, 0755) == 0);
285 assert_se(chmod(name3
, 0755) == 0);
287 r
= execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, gather_stdout
, args
, NULL
);
290 log_info("got: %s", output
);
292 assert_se(streq(output
, "a\nb\nc\nd\n"));
295 static void test_environment_gathering(void) {
296 char template[] = "/tmp/test-exec-util.XXXXXXX", **p
;
297 const char *dirs
[] = {template, NULL
};
298 const char *name
, *name2
, *name3
;
301 char **tmp
= NULL
; /* this is only used in the forked process, no cleanup here */
302 _cleanup_strv_free_
char **env
= NULL
;
304 void* const args
[] = { &tmp
, &tmp
, &env
};
306 assert_se(mkdtemp(template));
308 log_info("/* %s */", __func__
);
311 name
= strjoina(template, "/10-foo");
312 name2
= strjoina(template, "/20-bar");
313 name3
= strjoina(template, "/30-last");
315 assert_se(write_string_file(name
,
318 WRITE_STRING_FILE_CREATE
) == 0);
319 assert_se(write_string_file(name2
,
321 "echo A=22:$A\n\n\n", /* substitution from previous generator */
322 WRITE_STRING_FILE_CREATE
) == 0);
323 assert_se(write_string_file(name3
,
328 "echo C=001\n" /* variable overwriting */
329 /* various invalid entries */
336 /* test variable assignment without newline */
337 "echo PATH=$PATH:/no/such/file", /* no newline */
338 WRITE_STRING_FILE_CREATE
) == 0);
340 assert_se(chmod(name
, 0755) == 0);
341 assert_se(chmod(name2
, 0755) == 0);
342 assert_se(chmod(name3
, 0755) == 0);
344 r
= execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, gather_environment
, args
, NULL
);
348 log_info("got env: \"%s\"", *p
);
350 assert_se(streq(strv_env_get(env
, "A"), "22:23:24"));
351 assert_se(streq(strv_env_get(env
, "B"), "12"));
352 assert_se(streq(strv_env_get(env
, "C"), "001"));
353 assert_se(endswith(strv_env_get(env
, "PATH"), ":/no/such/file"));
356 int main(int argc
, char *argv
[]) {
357 log_set_max_level(LOG_DEBUG
);
358 log_parse_environment();
361 test_execute_directory(true);
362 test_execute_directory(false);
363 test_execution_order();
364 test_stdout_gathering();
365 test_environment_gathering();