chiark / gitweb /
1520dd13ac2d84c25b1e291117bc8fc488fdfc4b
[elogind.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 #if 0 /// UNNEEDED by elogind
276 static void test_environment_gathering(void) {
277         char template[] = "/tmp/test-exec-util.XXXXXXX", **p;
278         const char *dirs[] = {template, NULL};
279         const char *name, *name2, *name3;
280         int r;
281
282         char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
283         _cleanup_strv_free_ char **env = NULL;
284
285         void* const args[] = { &tmp, &tmp, &env };
286
287         assert_se(mkdtemp(template));
288
289         log_info("/* %s */", __func__);
290
291         /* write files */
292         name = strjoina(template, "/10-foo");
293         name2 = strjoina(template, "/20-bar");
294         name3 = strjoina(template, "/30-last");
295
296         assert_se(write_string_file(name,
297                                     "#!/bin/sh\n"
298                                     "echo A=23\n",
299                                     WRITE_STRING_FILE_CREATE) == 0);
300         assert_se(write_string_file(name2,
301                                     "#!/bin/sh\n"
302                                     "echo A=22:$A\n\n\n",            /* substitution from previous generator */
303                                     WRITE_STRING_FILE_CREATE) == 0);
304         assert_se(write_string_file(name3,
305                                     "#!/bin/sh\n"
306                                     "echo A=$A:24\n"
307                                     "echo B=12\n"
308                                     "echo C=000\n"
309                                     "echo C=001\n"                    /* variable overwriting */
310                                      /* various invalid entries */
311                                     "echo unset A\n"
312                                     "echo unset A=\n"
313                                     "echo unset A=B\n"
314                                     "echo unset \n"
315                                     "echo A B=C\n"
316                                     "echo A\n"
317                                     /* test variable assignment without newline */
318                                     "echo PATH=$PATH:/no/such/file",   /* no newline */
319                                     WRITE_STRING_FILE_CREATE) == 0);
320
321         assert_se(chmod(name, 0755) == 0);
322         assert_se(chmod(name2, 0755) == 0);
323         assert_se(chmod(name3, 0755) == 0);
324
325         r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL);
326         assert_se(r >= 0);
327
328         STRV_FOREACH(p, env)
329                 log_info("got env: \"%s\"", *p);
330
331         assert_se(streq(strv_env_get(env, "A"), "22:23:24"));
332         assert_se(streq(strv_env_get(env, "B"), "12"));
333         assert_se(streq(strv_env_get(env, "C"), "001"));
334         assert_se(endswith(strv_env_get(env, "PATH"), ":/no/such/file"));
335 }
336 #endif // 0
337
338 int main(int argc, char *argv[]) {
339         log_set_max_level(LOG_DEBUG);
340         log_parse_environment();
341         log_open();
342
343         test_execute_directory(true);
344         test_execute_directory(false);
345         test_execution_order();
346         test_stdout_gathering();
347 #if 0 /// UNNEEDED by elogind
348         test_environment_gathering();
349 #endif // 0
350
351         return 0;
352 }