]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-exec-util.c
Tighten checking for variable validity
[thirdparty/systemd.git] / src / test / test-exec-util.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5 Copyright 2013 Thomas H.P. Andersen
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <string.h>
23 #include <sys/stat.h>
24 #include <sys/wait.h>
25 #include <unistd.h>
26
27 #include "alloc-util.h"
28 #include "copy.h"
29 #include "def.h"
30 #include "env-util.h"
31 #include "exec-util.h"
32 #include "fd-util.h"
33 #include "fileio.h"
34 #include "fs-util.h"
35 #include "log.h"
36 #include "macro.h"
37 #include "rm-rf.h"
38 #include "string-util.h"
39 #include "strv.h"
40
41 static int here = 0, here2 = 0, here3 = 0;
42 void *ignore_stdout_args[] = {&here, &here2, &here3};
43
44 /* noop handlers, just check that arguments are passed correctly */
45 static int ignore_stdout_func(int fd, void *arg) {
46 assert(fd >= 0);
47 assert(arg == &here);
48 safe_close(fd);
49
50 return 0;
51 }
52 static int ignore_stdout_func2(int fd, void *arg) {
53 assert(fd >= 0);
54 assert(arg == &here2);
55 safe_close(fd);
56
57 return 0;
58 }
59 static int ignore_stdout_func3(int fd, void *arg) {
60 assert(fd >= 0);
61 assert(arg == &here3);
62 safe_close(fd);
63
64 return 0;
65 }
66
67 static const gather_stdout_callback_t ignore_stdout[] = {
68 ignore_stdout_func,
69 ignore_stdout_func2,
70 ignore_stdout_func3,
71 };
72
73 static void test_execute_directory(bool gather_stdout) {
74 char template_lo[] = "/tmp/test-exec-util.XXXXXXX";
75 char template_hi[] = "/tmp/test-exec-util.XXXXXXX";
76 const char * dirs[] = {template_hi, template_lo, NULL};
77 const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
78
79 log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous");
80
81 assert_se(mkdtemp(template_lo));
82 assert_se(mkdtemp(template_hi));
83
84 name = strjoina(template_lo, "/script");
85 name2 = strjoina(template_hi, "/script2");
86 name3 = strjoina(template_lo, "/useless");
87 overridden = strjoina(template_lo, "/overridden");
88 override = strjoina(template_hi, "/overridden");
89 masked = strjoina(template_lo, "/masked");
90 mask = strjoina(template_hi, "/masked");
91
92 assert_se(write_string_file(name,
93 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works",
94 WRITE_STRING_FILE_CREATE) == 0);
95 assert_se(write_string_file(name2,
96 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2",
97 WRITE_STRING_FILE_CREATE) == 0);
98 assert_se(write_string_file(overridden,
99 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
100 WRITE_STRING_FILE_CREATE) == 0);
101 assert_se(write_string_file(override,
102 "#!/bin/sh\necho 'Executing '$0",
103 WRITE_STRING_FILE_CREATE) == 0);
104 assert_se(write_string_file(masked,
105 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
106 WRITE_STRING_FILE_CREATE) == 0);
107 assert_se(symlink("/dev/null", mask) == 0);
108 assert_se(touch(name3) >= 0);
109
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
116 if (gather_stdout)
117 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
118 else
119 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL);
120
121 assert_se(chdir(template_lo) == 0);
122 assert_se(access("it_works", F_OK) >= 0);
123 assert_se(access("failed", F_OK) < 0);
124
125 assert_se(chdir(template_hi) == 0);
126 assert_se(access("it_works2", F_OK) >= 0);
127 assert_se(access("failed", F_OK) < 0);
128
129 (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
130 (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
131 }
132
133 static void test_execution_order(void) {
134 char template_lo[] = "/tmp/test-exec-util-lo.XXXXXXX";
135 char template_hi[] = "/tmp/test-exec-util-hi.XXXXXXX";
136 const char *dirs[] = {template_hi, template_lo, NULL};
137 const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
138 const char *output, *t;
139 _cleanup_free_ char *contents = NULL;
140
141 assert_se(mkdtemp(template_lo));
142 assert_se(mkdtemp(template_hi));
143
144 output = strjoina(template_hi, "/output");
145
146 log_info("/* %s >>%s */", __func__, output);
147
148 /* write files in "random" order */
149 name2 = strjoina(template_lo, "/90-bar");
150 name = strjoina(template_hi, "/80-foo");
151 name3 = strjoina(template_lo, "/last");
152 overridden = strjoina(template_lo, "/30-override");
153 override = strjoina(template_hi, "/30-override");
154 masked = strjoina(template_lo, "/10-masked");
155 mask = strjoina(template_hi, "/10-masked");
156
157 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
158 assert_se(write_string_file(name, t, WRITE_STRING_FILE_CREATE) == 0);
159
160 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
161 assert_se(write_string_file(name2, t, WRITE_STRING_FILE_CREATE) == 0);
162
163 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
164 assert_se(write_string_file(name3, t, WRITE_STRING_FILE_CREATE) == 0);
165
166 t = strjoina("#!/bin/sh\necho OVERRIDDEN >>", output);
167 assert_se(write_string_file(overridden, t, WRITE_STRING_FILE_CREATE) == 0);
168
169 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
170 assert_se(write_string_file(override, t, WRITE_STRING_FILE_CREATE) == 0);
171
172 t = strjoina("#!/bin/sh\necho MASKED >>", output);
173 assert_se(write_string_file(masked, t, WRITE_STRING_FILE_CREATE) == 0);
174
175 assert_se(symlink("/dev/null", mask) == 0);
176
177 assert_se(chmod(name, 0755) == 0);
178 assert_se(chmod(name2, 0755) == 0);
179 assert_se(chmod(name3, 0755) == 0);
180 assert_se(chmod(overridden, 0755) == 0);
181 assert_se(chmod(override, 0755) == 0);
182 assert_se(chmod(masked, 0755) == 0);
183
184 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
185
186 assert_se(read_full_file(output, &contents, NULL) >= 0);
187 assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n"));
188
189 (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
190 (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
191 }
192
193 static int gather_stdout_one(int fd, void *arg) {
194 char ***s = arg, *t;
195 char buf[128] = {};
196
197 assert_se(s);
198 assert_se(read(fd, buf, sizeof buf) >= 0);
199 safe_close(fd);
200
201 assert_se(t = strndup(buf, sizeof buf));
202 assert_se(strv_push(s, t) >= 0);
203
204 return 0;
205 }
206 static int gather_stdout_two(int fd, void *arg) {
207 char ***s = arg, **t;
208
209 STRV_FOREACH(t, *s)
210 assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t));
211 safe_close(fd);
212
213 return 0;
214 }
215 static int gather_stdout_three(int fd, void *arg) {
216 char **s = arg;
217 char buf[128] = {};
218
219 assert_se(read(fd, buf, sizeof buf - 1) > 0);
220 safe_close(fd);
221 assert_se(*s = strndup(buf, sizeof buf));
222
223 return 0;
224 }
225
226 const gather_stdout_callback_t const gather_stdout[] = {
227 gather_stdout_one,
228 gather_stdout_two,
229 gather_stdout_three,
230 };
231
232
233 static void test_stdout_gathering(void) {
234 char template[] = "/tmp/test-exec-util.XXXXXXX";
235 const char *dirs[] = {template, NULL};
236 const char *name, *name2, *name3;
237 int r;
238
239 char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
240 _cleanup_free_ char *output = NULL;
241
242 void* args[] = {&tmp, &tmp, &output};
243
244 assert_se(mkdtemp(template));
245
246 log_info("/* %s */", __func__);
247
248 /* write files */
249 name = strjoina(template, "/10-foo");
250 name2 = strjoina(template, "/20-bar");
251 name3 = strjoina(template, "/30-last");
252
253 assert_se(write_string_file(name,
254 "#!/bin/sh\necho a\necho b\necho c\n",
255 WRITE_STRING_FILE_CREATE) == 0);
256 assert_se(write_string_file(name2,
257 "#!/bin/sh\necho d\n",
258 WRITE_STRING_FILE_CREATE) == 0);
259 assert_se(write_string_file(name3,
260 "#!/bin/sh\nsleep 1",
261 WRITE_STRING_FILE_CREATE) == 0);
262
263 assert_se(chmod(name, 0755) == 0);
264 assert_se(chmod(name2, 0755) == 0);
265 assert_se(chmod(name3, 0755) == 0);
266
267 r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL);
268 assert_se(r >= 0);
269
270 log_info("got: %s", output);
271
272 assert_se(streq(output, "a\nb\nc\nd\n"));
273 }
274
275 static void test_environment_gathering(void) {
276 char template[] = "/tmp/test-exec-util.XXXXXXX", **p;
277 const char *dirs[] = {template, NULL};
278 const char *name, *name2, *name3;
279 int r;
280
281 char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
282 _cleanup_strv_free_ char **env = NULL;
283
284 void* const args[] = { &tmp, &tmp, &env };
285
286 assert_se(mkdtemp(template));
287
288 log_info("/* %s */", __func__);
289
290 /* write files */
291 name = strjoina(template, "/10-foo");
292 name2 = strjoina(template, "/20-bar");
293 name3 = strjoina(template, "/30-last");
294
295 assert_se(write_string_file(name,
296 "#!/bin/sh\n"
297 "echo A=23\n",
298 WRITE_STRING_FILE_CREATE) == 0);
299 assert_se(write_string_file(name2,
300 "#!/bin/sh\n"
301 "echo A=22:$A\n\n\n", /* substitution from previous generator */
302 WRITE_STRING_FILE_CREATE) == 0);
303 assert_se(write_string_file(name3,
304 "#!/bin/sh\n"
305 "echo A=$A:24\n"
306 "echo B=12\n"
307 "echo C=000\n"
308 "echo C=001\n" /* variable overwriting */
309 /* various invalid entries */
310 "echo unset A\n"
311 "echo unset A=\n"
312 "echo unset A=B\n"
313 "echo unset \n"
314 "echo A B=C\n"
315 "echo A\n"
316 /* test variable assignment without newline */
317 "echo PATH=$PATH:/no/such/file", /* no newline */
318 WRITE_STRING_FILE_CREATE) == 0);
319
320 assert_se(chmod(name, 0755) == 0);
321 assert_se(chmod(name2, 0755) == 0);
322 assert_se(chmod(name3, 0755) == 0);
323
324 r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
325 assert_se(r >= 0);
326
327 STRV_FOREACH(p, env)
328 log_info("got env: \"%s\"", *p);
329
330 assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
331 assert_se(streq(strv_env_get(env, "B"), "12"));
332 assert_se(streq(strv_env_get(env, "C"), "001"));
333 assert_se(endswith(strv_env_get(env, "PATH"), ":/no/such/file"));
334 }
335
336 int main(int argc, char *argv[]) {
337 log_set_max_level(LOG_DEBUG);
338 log_parse_environment();
339 log_open();
340
341 test_execute_directory(true);
342 test_execute_directory(false);
343 test_execution_order();
344 test_stdout_gathering();
345 test_environment_gathering();
346
347 return 0;
348 }