]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-path.c
strv: make iterator in STRV_FOREACH() declaread in the loop
[thirdparty/systemd.git] / src / test / test-path.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
bc999297 2
bc999297 3#include <stdbool.h>
edb3ca0d
FB
4#include <sys/stat.h>
5#include <sys/types.h>
bc999297 6
b5efdb8a 7#include "alloc-util.h"
57b7a260 8#include "all-units.h"
f4f15635
LP
9#include "fd-util.h"
10#include "fs-util.h"
bc999297 11#include "macro.h"
07630cea 12#include "manager.h"
bc999297 13#include "mkdir.h"
55890a40 14#include "path-util.h"
c6878637 15#include "rm-rf.h"
07630cea
LP
16#include "string-util.h"
17#include "strv.h"
d2120590 18#include "tests.h"
07630cea
LP
19#include "unit.h"
20#include "util.h"
bc999297
RC
21
22typedef void (*test_function_t)(Manager *m);
23
24static int setup_test(Manager **m) {
25 char **tests_path = STRV_MAKE("exists", "existsglobFOOBAR", "changed", "modified", "unit",
26 "directorynotempty", "makedirectory");
a7f7d1bd 27 Manager *tmp = NULL;
bc999297
RC
28 int r;
29
30 assert_se(m);
31
64ad9e08 32 r = enter_cgroup_subroot(NULL);
317bb217
ZJS
33 if (r == -ENOMEDIUM)
34 return log_tests_skipped("cgroupfs not available");
8c759b33 35
e8112e67 36 r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &tmp);
5eecb103 37 if (manager_errno_skip_test(r))
730d989a 38 return log_tests_skipped_errno(r, "manager_new");
bc999297 39 assert_se(r >= 0);
2a7cf953 40 assert_se(manager_startup(tmp, NULL, NULL, NULL) >= 0);
bc999297
RC
41
42 STRV_FOREACH(test_path, tests_path) {
c6878637
LP
43 _cleanup_free_ char *p = NULL;
44
605405c6 45 p = strjoin("/tmp/test-path_", *test_path);
c6878637
LP
46 assert_se(p);
47
48 (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
bc999297
RC
49 }
50
51 *m = tmp;
52
53 return 0;
54}
55
56static void shutdown_test(Manager *m) {
57 assert_se(m);
58
59 manager_free(m);
60}
61
708961c7 62static Service *service_for_path(Manager *m, Path *path, const char *service_name) {
bc999297
RC
63 _cleanup_free_ char *tmp = NULL;
64 Unit *service_unit = NULL;
bc999297
RC
65
66 assert_se(m);
708961c7 67 assert_se(path);
bc999297
RC
68
69 if (!service_name) {
708961c7 70 assert_se(tmp = strreplace(UNIT(path)->id, ".path", ".service"));
bc999297
RC
71 service_unit = manager_get_unit(m, tmp);
72 } else
73 service_unit = manager_get_unit(m, service_name);
74 assert_se(service_unit);
708961c7
MC
75
76 return SERVICE(service_unit);
77}
78
fcb7138c
ZJS
79static int _check_states(unsigned line,
80 Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) {
708961c7
MC
81 assert_se(m);
82 assert_se(service);
bc999297 83
500727c2 84 usec_t end = now(CLOCK_MONOTONIC) + 30 * USEC_PER_SEC;
708961c7 85
75edb0b0
ZJS
86 while (path->state != path_state || service->state != service_state ||
87 path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS) {
bc999297 88
500727c2 89 assert_se(sd_event_run(m->event, 100 * USEC_PER_MSEC) >= 0);
bc999297 90
75edb0b0
ZJS
91 usec_t n = now(CLOCK_MONOTONIC);
92 log_info("line %u: %s: state = %s; result = %s (left: %" PRIi64 ")",
93 line,
94 UNIT(path)->id,
95 path_state_to_string(path->state),
96 path_result_to_string(path->result),
97 end - n);
98 log_info("line %u: %s: state = %s; result = %s",
99 line,
100 UNIT(service)->id,
101 service_state_to_string(service->state),
102 service_result_to_string(service->result));
103
fcb7138c 104 if (service->state == SERVICE_FAILED &&
16e925c6
ZJS
105 service->main_exec_status.status == EXIT_CGROUP &&
106 !ci_environment())
fcb7138c
ZJS
107 /* On a general purpose system we may fail to start the service for reasons which are
108 * not under our control: permission limits, resource exhaustion, etc. Let's skip the
16e925c6 109 * test in those cases. On developer machines we require proper setup. */
fcb7138c
ZJS
110 return log_notice_errno(SYNTHETIC_ERRNO(ECANCELED),
111 "Failed to start service %s, aborting test: %s/%s",
112 UNIT(service)->id,
113 service_state_to_string(service->state),
114 service_result_to_string(service->result));
115
75edb0b0 116 if (n >= end) {
708961c7 117 log_error("Test timeout when testing %s", UNIT(path)->id);
bc999297
RC
118 exit(EXIT_FAILURE);
119 }
120 }
fcb7138c
ZJS
121
122 return 0;
bc999297 123}
75edb0b0 124#define check_states(...) _check_states(__LINE__, __VA_ARGS__)
bc999297
RC
125
126static void test_path_exists(Manager *m) {
127 const char *test_path = "/tmp/test-path_exists";
128 Unit *unit = NULL;
708961c7
MC
129 Path *path = NULL;
130 Service *service = NULL;
bc999297
RC
131
132 assert_se(m);
133
ba412430 134 assert_se(manager_load_startable_unit_or_warn(m, "path-exists.path", NULL, &unit) >= 0);
708961c7
MC
135
136 path = PATH(unit);
137 service = service_for_path(m, path, NULL);
138
bd7989a3 139 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
140 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
141 return;
bc999297
RC
142
143 assert_se(touch(test_path) >= 0);
fcb7138c
ZJS
144 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
145 return;
708961c7
MC
146
147 /* Service restarts if file still exists */
148 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
149 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
150 return;
708961c7
MC
151
152 assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
153 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
154 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
155 return;
bc999297 156
708961c7 157 assert_se(unit_stop(unit) >= 0);
bc999297
RC
158}
159
160static void test_path_existsglob(Manager *m) {
161 const char *test_path = "/tmp/test-path_existsglobFOOBAR";
162 Unit *unit = NULL;
708961c7
MC
163 Path *path = NULL;
164 Service *service = NULL;
bc999297
RC
165
166 assert_se(m);
708961c7 167
ba412430 168 assert_se(manager_load_startable_unit_or_warn(m, "path-existsglob.path", NULL, &unit) >= 0);
708961c7
MC
169
170 path = PATH(unit);
171 service = service_for_path(m, path, NULL);
172
bd7989a3 173 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
174 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
175 return;
bc999297
RC
176
177 assert_se(touch(test_path) >= 0);
fcb7138c
ZJS
178 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
179 return;
708961c7
MC
180
181 /* Service restarts if file still exists */
182 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
183 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
184 return;
bc999297 185
708961c7
MC
186 assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
187 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
188 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
189 return;
708961c7
MC
190
191 assert_se(unit_stop(unit) >= 0);
bc999297
RC
192}
193
194static void test_path_changed(Manager *m) {
195 const char *test_path = "/tmp/test-path_changed";
196 FILE *f;
197 Unit *unit = NULL;
708961c7
MC
198 Path *path = NULL;
199 Service *service = NULL;
bc999297
RC
200
201 assert_se(m);
202
ba412430 203 assert_se(manager_load_startable_unit_or_warn(m, "path-changed.path", NULL, &unit) >= 0);
708961c7
MC
204
205 path = PATH(unit);
206 service = service_for_path(m, path, NULL);
207
bd7989a3 208 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
209 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
210 return;
708961c7
MC
211
212 assert_se(touch(test_path) >= 0);
fcb7138c
ZJS
213 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
214 return;
708961c7
MC
215
216 /* Service does not restart if file still exists */
217 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
218 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
219 return;
bc999297
RC
220
221 f = fopen(test_path, "w");
222 assert_se(f);
223 fclose(f);
224
fcb7138c
ZJS
225 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
226 return;
708961c7
MC
227
228 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
229 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
230 return;
708961c7
MC
231
232 (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
233 assert_se(unit_stop(unit) >= 0);
bc999297
RC
234}
235
236static void test_path_modified(Manager *m) {
237 _cleanup_fclose_ FILE *f = NULL;
238 const char *test_path = "/tmp/test-path_modified";
239 Unit *unit = NULL;
708961c7
MC
240 Path *path = NULL;
241 Service *service = NULL;
bc999297
RC
242
243 assert_se(m);
244
ba412430 245 assert_se(manager_load_startable_unit_or_warn(m, "path-modified.path", NULL, &unit) >= 0);
708961c7
MC
246
247 path = PATH(unit);
248 service = service_for_path(m, path, NULL);
249
bd7989a3 250 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
251 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
252 return;
708961c7
MC
253
254 assert_se(touch(test_path) >= 0);
fcb7138c
ZJS
255 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
256 return;
708961c7
MC
257
258 /* Service does not restart if file still exists */
259 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
260 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
261 return;
bc999297
RC
262
263 f = fopen(test_path, "w");
264 assert_se(f);
265 fputs("test", f);
266
fcb7138c
ZJS
267 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
268 return;
708961c7
MC
269
270 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
271 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
272 return;
708961c7
MC
273
274 (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
275 assert_se(unit_stop(unit) >= 0);
bc999297
RC
276}
277
278static void test_path_unit(Manager *m) {
279 const char *test_path = "/tmp/test-path_unit";
280 Unit *unit = NULL;
708961c7
MC
281 Path *path = NULL;
282 Service *service = NULL;
bc999297
RC
283
284 assert_se(m);
285
ba412430 286 assert_se(manager_load_startable_unit_or_warn(m, "path-unit.path", NULL, &unit) >= 0);
708961c7
MC
287
288 path = PATH(unit);
289 service = service_for_path(m, path, "path-mycustomunit.service");
290
bd7989a3 291 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
292 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
293 return;
bc999297
RC
294
295 assert_se(touch(test_path) >= 0);
fcb7138c
ZJS
296 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
297 return;
708961c7
MC
298
299 assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
300 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
301 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
302 return;
bc999297 303
708961c7 304 assert_se(unit_stop(unit) >= 0);
bc999297
RC
305}
306
307static void test_path_directorynotempty(Manager *m) {
4150584e 308 const char *test_file, *test_path = "/tmp/test-path_directorynotempty/";
bc999297 309 Unit *unit = NULL;
708961c7
MC
310 Path *path = NULL;
311 Service *service = NULL;
bc999297
RC
312
313 assert_se(m);
314
708961c7
MC
315 assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0);
316
317 path = PATH(unit);
318 service = service_for_path(m, path, NULL);
319
bc999297
RC
320 assert_se(access(test_path, F_OK) < 0);
321
bd7989a3 322 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
323 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
324 return;
bc999297
RC
325
326 /* MakeDirectory default to no */
327 assert_se(access(test_path, F_OK) < 0);
328
329 assert_se(mkdir_p(test_path, 0755) >= 0);
4150584e
YW
330 test_file = strjoina(test_path, "test_file");
331 assert_se(touch(test_file) >= 0);
fcb7138c
ZJS
332 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
333 return;
708961c7
MC
334
335 /* Service restarts if directory is still not empty */
336 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
337 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
338 return;
708961c7
MC
339
340 assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
341 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
342 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
343 return;
bc999297 344
708961c7 345 assert_se(unit_stop(unit) >= 0);
bc999297
RC
346}
347
348static void test_path_makedirectory_directorymode(Manager *m) {
349 const char *test_path = "/tmp/test-path_makedirectory/";
350 Unit *unit = NULL;
351 struct stat s;
352
353 assert_se(m);
354
708961c7
MC
355 assert_se(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit) >= 0);
356
bc999297
RC
357 assert_se(access(test_path, F_OK) < 0);
358
bd7989a3 359 assert_se(unit_start(unit) >= 0);
bc999297
RC
360
361 /* Check if the directory has been created */
362 assert_se(access(test_path, F_OK) >= 0);
363
364 /* Check the mode we specified with DirectoryMode=0744 */
365 assert_se(stat(test_path, &s) >= 0);
366 assert_se((s.st_mode & S_IRWXU) == 0700);
367 assert_se((s.st_mode & S_IRWXG) == 0040);
368 assert_se((s.st_mode & S_IRWXO) == 0004);
369
bd7989a3 370 assert_se(unit_stop(unit) >= 0);
c6878637 371 (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
bc999297
RC
372}
373
374int main(int argc, char *argv[]) {
d2120590 375 static const test_function_t tests[] = {
bc999297
RC
376 test_path_exists,
377 test_path_existsglob,
378 test_path_changed,
379 test_path_modified,
380 test_path_unit,
381 test_path_directorynotempty,
382 test_path_makedirectory_directorymode,
383 NULL,
384 };
d2120590 385
55890a40 386 _cleanup_free_ char *test_path = NULL;
e3643b00 387 _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
bc999297 388
edb3ca0d
FB
389 umask(022);
390
6d7c4033 391 test_setup_logging(LOG_INFO);
bc999297 392
7b432953 393 assert_se(get_testdata_dir("test-path", &test_path) >= 0);
55890a40 394 assert_se(set_unit_path(test_path) >= 0);
3e29e810 395 assert_se(runtime_dir = setup_fake_runtime_dir());
bc999297 396
c87d0661 397 for (const test_function_t *test = tests; *test; test++) {
e3643b00 398 Manager *m = NULL;
bc999297
RC
399 int r;
400
401 /* We create a clean environment for each test */
402 r = setup_test(&m);
317bb217
ZJS
403 if (r != 0)
404 return r;
bc999297
RC
405
406 (*test)(m);
407
408 shutdown_test(m);
409 }
410
411 return 0;
412}