]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-exec-util.c
basic/exec-util: add support for synchronous (ordered) execution
[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 "exec-util.h"
31 #include "fd-util.h"
32 #include "fileio.h"
33 #include "fs-util.h"
34 #include "log.h"
35 #include "macro.h"
36 #include "rm-rf.h"
37 #include "string-util.h"
38 #include "strv.h"
39
40 static int here = 0, here2 = 0, here3 = 0;
41 void *ignore_stdout_args[] = {&here, &here2, &here3};
42
43 /* noop handlers, just check that arguments are passed correctly */
44 static int ignore_stdout_func(int fd, void *arg) {
45 assert(fd >= 0);
46 assert(arg == &here);
47 safe_close(fd);
48
49 return 0;
50 }
51 static int ignore_stdout_func2(int fd, void *arg) {
52 assert(fd >= 0);
53 assert(arg == &here2);
54 safe_close(fd);
55
56 return 0;
57 }
58 static int ignore_stdout_func3(int fd, void *arg) {
59 assert(fd >= 0);
60 assert(arg == &here3);
61 safe_close(fd);
62
63 return 0;
64 }
65
66 static const gather_stdout_callback_t ignore_stdout[] = {
67 ignore_stdout_func,
68 ignore_stdout_func2,
69 ignore_stdout_func3,
70 };
71
72 static void test_execute_directory(bool gather_stdout) {
73 char template_lo[] = "/tmp/test-exec-util.XXXXXXX";
74 char template_hi[] = "/tmp/test-exec-util.XXXXXXX";
75 const char * dirs[] = {template_hi, template_lo, NULL};
76 const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
77
78 log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous");
79
80 assert_se(mkdtemp(template_lo));
81 assert_se(mkdtemp(template_hi));
82
83 name = strjoina(template_lo, "/script");
84 name2 = strjoina(template_hi, "/script2");
85 name3 = strjoina(template_lo, "/useless");
86 overridden = strjoina(template_lo, "/overridden");
87 override = strjoina(template_hi, "/overridden");
88 masked = strjoina(template_lo, "/masked");
89 mask = strjoina(template_hi, "/masked");
90
91 assert_se(write_string_file(name,
92 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works",
93 WRITE_STRING_FILE_CREATE) == 0);
94 assert_se(write_string_file(name2,
95 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2",
96 WRITE_STRING_FILE_CREATE) == 0);
97 assert_se(write_string_file(overridden,
98 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
99 WRITE_STRING_FILE_CREATE) == 0);
100 assert_se(write_string_file(override,
101 "#!/bin/sh\necho 'Executing '$0",
102 WRITE_STRING_FILE_CREATE) == 0);
103 assert_se(write_string_file(masked,
104 "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed",
105 WRITE_STRING_FILE_CREATE) == 0);
106 assert_se(symlink("/dev/null", mask) == 0);
107 assert_se(touch(name3) >= 0);
108
109 assert_se(chmod(name, 0755) == 0);
110 assert_se(chmod(name2, 0755) == 0);
111 assert_se(chmod(overridden, 0755) == 0);
112 assert_se(chmod(override, 0755) == 0);
113 assert_se(chmod(masked, 0755) == 0);
114
115 if (gather_stdout)
116 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
117 else
118 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL);
119
120 assert_se(chdir(template_lo) == 0);
121 assert_se(access("it_works", F_OK) >= 0);
122 assert_se(access("failed", F_OK) < 0);
123
124 assert_se(chdir(template_hi) == 0);
125 assert_se(access("it_works2", F_OK) >= 0);
126 assert_se(access("failed", F_OK) < 0);
127
128 (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
129 (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
130 }
131
132 static void test_execution_order(void) {
133 char template_lo[] = "/tmp/test-exec-util-lo.XXXXXXX";
134 char template_hi[] = "/tmp/test-exec-util-hi.XXXXXXX";
135 const char *dirs[] = {template_hi, template_lo, NULL};
136 const char *name, *name2, *name3, *overridden, *override, *masked, *mask;
137 const char *output, *t;
138 _cleanup_free_ char *contents = NULL;
139
140 assert_se(mkdtemp(template_lo));
141 assert_se(mkdtemp(template_hi));
142
143 output = strjoina(template_hi, "/output");
144
145 log_info("/* %s >>%s */", __func__, output);
146
147 /* write files in "random" order */
148 name2 = strjoina(template_lo, "/90-bar");
149 name = strjoina(template_hi, "/80-foo");
150 name3 = strjoina(template_lo, "/last");
151 overridden = strjoina(template_lo, "/30-override");
152 override = strjoina(template_hi, "/30-override");
153 masked = strjoina(template_lo, "/10-masked");
154 mask = strjoina(template_hi, "/10-masked");
155
156 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
157 assert_se(write_string_file(name, t, WRITE_STRING_FILE_CREATE) == 0);
158
159 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
160 assert_se(write_string_file(name2, t, WRITE_STRING_FILE_CREATE) == 0);
161
162 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
163 assert_se(write_string_file(name3, t, WRITE_STRING_FILE_CREATE) == 0);
164
165 t = strjoina("#!/bin/sh\necho OVERRIDDEN >>", output);
166 assert_se(write_string_file(overridden, t, WRITE_STRING_FILE_CREATE) == 0);
167
168 t = strjoina("#!/bin/sh\necho $(basename $0) >>", output);
169 assert_se(write_string_file(override, t, WRITE_STRING_FILE_CREATE) == 0);
170
171 t = strjoina("#!/bin/sh\necho MASKED >>", output);
172 assert_se(write_string_file(masked, t, WRITE_STRING_FILE_CREATE) == 0);
173
174 assert_se(symlink("/dev/null", mask) == 0);
175
176 assert_se(chmod(name, 0755) == 0);
177 assert_se(chmod(name2, 0755) == 0);
178 assert_se(chmod(name3, 0755) == 0);
179 assert_se(chmod(overridden, 0755) == 0);
180 assert_se(chmod(override, 0755) == 0);
181 assert_se(chmod(masked, 0755) == 0);
182
183 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL);
184
185 assert_se(read_full_file(output, &contents, NULL) >= 0);
186 assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n"));
187
188 (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL);
189 (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
190 }
191
192 static int gather_stdout_one(int fd, void *arg) {
193 char ***s = arg, *t;
194 char buf[128] = {};
195
196 assert_se(s);
197 assert_se(read(fd, buf, sizeof buf) >= 0);
198 safe_close(fd);
199
200 assert_se(t = strndup(buf, sizeof buf));
201 assert_se(strv_push(s, t) >= 0);
202
203 return 0;
204 }
205 static int gather_stdout_two(int fd, void *arg) {
206 char ***s = arg, **t;
207
208 STRV_FOREACH(t, *s)
209 assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t));
210 safe_close(fd);
211
212 return 0;
213 }
214 static int gather_stdout_three(int fd, void *arg) {
215 char **s = arg;
216 char buf[128] = {};
217
218 assert_se(read(fd, buf, sizeof buf - 1) > 0);
219 safe_close(fd);
220 assert_se(*s = strndup(buf, sizeof buf));
221
222 return 0;
223 }
224
225 const gather_stdout_callback_t const gather_stdout[] = {
226 gather_stdout_one,
227 gather_stdout_two,
228 gather_stdout_three,
229 };
230
231
232 static void test_stdout_gathering(void) {
233 char template[] = "/tmp/test-exec-util.XXXXXXX";
234 const char *dirs[] = {template, NULL};
235 const char *name, *name2, *name3;
236 int r;
237
238 char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
239 _cleanup_free_ char *output = NULL;
240
241 void* args[] = {&tmp, &tmp, &output};
242
243 assert_se(mkdtemp(template));
244
245 log_info("/* %s */", __func__);
246
247 /* write files */
248 name = strjoina(template, "/10-foo");
249 name2 = strjoina(template, "/20-bar");
250 name3 = strjoina(template, "/30-last");
251
252 assert_se(write_string_file(name,
253 "#!/bin/sh\necho a\necho b\necho c\n",
254 WRITE_STRING_FILE_CREATE) == 0);
255 assert_se(write_string_file(name2,
256 "#!/bin/sh\necho d\n",
257 WRITE_STRING_FILE_CREATE) == 0);
258 assert_se(write_string_file(name3,
259 "#!/bin/sh\nsleep 1",
260 WRITE_STRING_FILE_CREATE) == 0);
261
262 assert_se(chmod(name, 0755) == 0);
263 assert_se(chmod(name2, 0755) == 0);
264 assert_se(chmod(name3, 0755) == 0);
265
266 r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL);
267 assert_se(r >= 0);
268
269 log_info("got: %s", output);
270
271 assert_se(streq(output, "a\nb\nc\nd\n"));
272 }
273
274 int main(int argc, char *argv[]) {
275 log_set_max_level(LOG_DEBUG);
276 log_parse_environment();
277 log_open();
278
279 test_execute_directory(true);
280 test_execute_directory(false);
281 test_execution_order();
282 test_stdout_gathering();
283
284 return 0;
285 }