]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-exec-util.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / test / test-exec-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
89711996
ZJS
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6 Copyright 2013 Thomas H.P. Andersen
7
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.
12
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.
17
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/>.
20***/
21
22#include <errno.h>
23#include <string.h>
24#include <sys/stat.h>
25#include <sys/wait.h>
26#include <unistd.h>
27
c6e47247
ZJS
28#include "alloc-util.h"
29#include "copy.h"
89711996 30#include "def.h"
3303d1b2 31#include "env-util.h"
89711996 32#include "exec-util.h"
c6e47247 33#include "fd-util.h"
89711996
ZJS
34#include "fileio.h"
35#include "fs-util.h"
36#include "log.h"
37#include "macro.h"
38#include "rm-rf.h"
39#include "string-util.h"
c6e47247 40#include "strv.h"
89711996 41
c6e47247
ZJS
42static int here = 0, here2 = 0, here3 = 0;
43void *ignore_stdout_args[] = {&here, &here2, &here3};
44
45/* noop handlers, just check that arguments are passed correctly */
46static int ignore_stdout_func(int fd, void *arg) {
47 assert(fd >= 0);
48 assert(arg == &here);
49 safe_close(fd);
50
51 return 0;
52}
53static int ignore_stdout_func2(int fd, void *arg) {
54 assert(fd >= 0);
55 assert(arg == &here2);
56 safe_close(fd);
57
58 return 0;
59}
60static int ignore_stdout_func3(int fd, void *arg) {
61 assert(fd >= 0);
62 assert(arg == &here3);
63 safe_close(fd);
64
65 return 0;
66}
67
68static const gather_stdout_callback_t ignore_stdout[] = {
69 ignore_stdout_func,
70 ignore_stdout_func2,
71 ignore_stdout_func3,
72};
73
74static void test_execute_directory(bool gather_stdout) {
f66137fb
ZJS
75 char template_lo[] = "/tmp/test-exec-util.lo.XXXXXXX";
76 char template_hi[] = "/tmp/test-exec-util.hi.XXXXXXX";
89711996 77 const char * dirs[] = {template_hi, template_lo, NULL};
f66137fb
ZJS
78 const char *name, *name2, *name3,
79 *overridden, *override,
80 *masked, *mask,
81 *masked2, *mask2, /* the mask is non-executable */
82 *masked2e, *mask2e; /* the mask is executable */
89711996 83
c6e47247
ZJS
84 log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous");
85
89711996
ZJS
86 assert_se(mkdtemp(template_lo));
87 assert_se(mkdtemp(template_hi));
88
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");
f66137fb
ZJS
96 masked2 = strjoina(template_lo, "/masked2");
97 mask2 = strjoina(template_hi, "/masked2");
98 masked2e = strjoina(template_lo, "/masked2e");
99 mask2e = strjoina(template_hi, "/masked2e");
89711996 100
c6e47247
ZJS
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);
f66137fb
ZJS
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);
89711996 122 assert_se(symlink("/dev/null", mask) == 0);
f66137fb
ZJS
123 assert_se(touch(mask2) == 0);
124 assert_se(touch(mask2e) == 0);
c6e47247
ZJS
125 assert_se(touch(name3) >= 0);
126
89711996
ZJS
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);
f66137fb
ZJS
132 assert_se(chmod(masked2, 0755) == 0);
133 assert_se(chmod(masked2e, 0755) == 0);
134 assert_se(chmod(mask2e, 0755) == 0);
89711996 135
c6e47247
ZJS
136 if (gather_stdout)
137 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
138 else
139 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL);
89711996
ZJS
140
141 assert_se(chdir(template_lo) == 0);
142 assert_se(access("it_works", F_OK) >= 0);
143 assert_se(access("failed", F_OK) < 0);
144
145 assert_se(chdir(template_hi) == 0);
146 assert_se(access("it_works2", F_OK) >= 0);
147 assert_se(access("failed", F_OK) < 0);
148
149 (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
150 (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
151}
152
c6e47247
ZJS
153static 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;
160
161 assert_se(mkdtemp(template_lo));
162 assert_se(mkdtemp(template_hi));
163
164 output = strjoina(template_hi, "/output");
165
166 log_info("/* %s >>%s */", __func__, output);
167
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");
176
177 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
178 assert_se(write_string_file(name, t, WRITE_STRING_FILE_CREATE) == 0);
179
180 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
181 assert_se(write_string_file(name2, t, WRITE_STRING_FILE_CREATE) == 0);
182
183 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
184 assert_se(write_string_file(name3, t, WRITE_STRING_FILE_CREATE) == 0);
185
186 t = strjoina("#!/bin/sh\necho OVERRIDDEN >>", output);
187 assert_se(write_string_file(overridden, t, WRITE_STRING_FILE_CREATE) == 0);
188
189 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
190 assert_se(write_string_file(override, t, WRITE_STRING_FILE_CREATE) == 0);
191
192 t = strjoina("#!/bin/sh\necho MASKED >>", output);
193 assert_se(write_string_file(masked, t, WRITE_STRING_FILE_CREATE) == 0);
194
195 assert_se(symlink("/dev/null", mask) == 0);
196
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);
203
204 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
205
206 assert_se(read_full_file(output, &contents, NULL) >= 0);
207 assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n"));
208
209 (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
210 (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
211}
212
213static int gather_stdout_one(int fd, void *arg) {
214 char ***s = arg, *t;
215 char buf[128] = {};
216
217 assert_se(s);
218 assert_se(read(fd, buf, sizeof buf) >= 0);
219 safe_close(fd);
220
221 assert_se(t = strndup(buf, sizeof buf));
222 assert_se(strv_push(s, t) >= 0);
223
224 return 0;
225}
226static int gather_stdout_two(int fd, void *arg) {
227 char ***s = arg, **t;
228
229 STRV_FOREACH(t, *s)
230 assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t));
231 safe_close(fd);
232
233 return 0;
234}
235static int gather_stdout_three(int fd, void *arg) {
236 char **s = arg;
237 char buf[128] = {};
238
239 assert_se(read(fd, buf, sizeof buf - 1) > 0);
240 safe_close(fd);
241 assert_se(*s = strndup(buf, sizeof buf));
242
243 return 0;
244}
245
4fa3993b 246const gather_stdout_callback_t gather_stdout[] = {
c6e47247
ZJS
247 gather_stdout_one,
248 gather_stdout_two,
249 gather_stdout_three,
250};
251
252
253static 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;
257 int r;
258
259 char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
260 _cleanup_free_ char *output = NULL;
261
262 void* args[] = {&tmp, &tmp, &output};
263
264 assert_se(mkdtemp(template));
265
266 log_info("/* %s */", __func__);
267
268 /* write files */
269 name = strjoina(template, "/10-foo");
270 name2 = strjoina(template, "/20-bar");
271 name3 = strjoina(template, "/30-last");
272
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);
282
283 assert_se(chmod(name, 0755) == 0);
284 assert_se(chmod(name2, 0755) == 0);
285 assert_se(chmod(name3, 0755) == 0);
286
287 r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL);
288 assert_se(r >= 0);
289
290 log_info("got: %s", output);
291
292 assert_se(streq(output, "a\nb\nc\nd\n"));
293}
294
3303d1b2
ZJS
295static 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;
299 int r;
300
301 char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
302 _cleanup_strv_free_ char **env = NULL;
303
304 void* const args[] = { &tmp, &tmp, &env };
305
306 assert_se(mkdtemp(template));
307
308 log_info("/* %s */", __func__);
309
310 /* write files */
311 name = strjoina(template, "/10-foo");
312 name2 = strjoina(template, "/20-bar");
313 name3 = strjoina(template, "/30-last");
314
315 assert_se(write_string_file(name,
316 "#!/bin/sh\n"
317 "echo A=23\n",
318 WRITE_STRING_FILE_CREATE) == 0);
319 assert_se(write_string_file(name2,
320 "#!/bin/sh\n"
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,
324 "#!/bin/sh\n"
325 "echo A=$A:24\n"
326 "echo B=12\n"
327 "echo C=000\n"
184d1904
ZJS
328 "echo C=001\n" /* variable overwriting */
329 /* various invalid entries */
330 "echo unset A\n"
331 "echo unset A=\n"
332 "echo unset A=B\n"
333 "echo unset \n"
334 "echo A B=C\n"
335 "echo A\n"
336 /* test variable assignment without newline */
337 "echo PATH=$PATH:/no/such/file", /* no newline */
3303d1b2
ZJS
338 WRITE_STRING_FILE_CREATE) == 0);
339
340 assert_se(chmod(name, 0755) == 0);
341 assert_se(chmod(name2, 0755) == 0);
342 assert_se(chmod(name3, 0755) == 0);
343
344 r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
345 assert_se(r >= 0);
346
347 STRV_FOREACH(p, env)
348 log_info("got env: \"%s\"", *p);
349
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"));
354}
355
89711996 356int main(int argc, char *argv[]) {
c6e47247 357 log_set_max_level(LOG_DEBUG);
89711996
ZJS
358 log_parse_environment();
359 log_open();
360
c6e47247
ZJS
361 test_execute_directory(true);
362 test_execute_directory(false);
363 test_execution_order();
364 test_stdout_gathering();
3303d1b2 365 test_environment_gathering();
89711996
ZJS
366
367 return 0;
368}