]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-path.c
Merge pull request #17549 from yuwata/tiny-fixes
[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");
27 char **test_path;
a7f7d1bd 28 Manager *tmp = NULL;
bc999297
RC
29 int r;
30
31 assert_se(m);
32
64ad9e08 33 r = enter_cgroup_subroot(NULL);
317bb217
ZJS
34 if (r == -ENOMEDIUM)
35 return log_tests_skipped("cgroupfs not available");
8c759b33 36
e8112e67 37 r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &tmp);
5eecb103 38 if (manager_errno_skip_test(r))
730d989a 39 return log_tests_skipped_errno(r, "manager_new");
bc999297
RC
40 assert_se(r >= 0);
41 assert_se(manager_startup(tmp, NULL, NULL) >= 0);
42
43 STRV_FOREACH(test_path, tests_path) {
c6878637
LP
44 _cleanup_free_ char *p = NULL;
45
605405c6 46 p = strjoin("/tmp/test-path_", *test_path);
c6878637
LP
47 assert_se(p);
48
49 (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
bc999297
RC
50 }
51
52 *m = tmp;
53
54 return 0;
55}
56
57static void shutdown_test(Manager *m) {
58 assert_se(m);
59
60 manager_free(m);
61}
62
708961c7 63static Service *service_for_path(Manager *m, Path *path, const char *service_name) {
bc999297
RC
64 _cleanup_free_ char *tmp = NULL;
65 Unit *service_unit = NULL;
bc999297
RC
66
67 assert_se(m);
708961c7 68 assert_se(path);
bc999297
RC
69
70 if (!service_name) {
708961c7 71 assert_se(tmp = strreplace(UNIT(path)->id, ".path", ".service"));
bc999297
RC
72 service_unit = manager_get_unit(m, tmp);
73 } else
74 service_unit = manager_get_unit(m, service_name);
75 assert_se(service_unit);
708961c7
MC
76
77 return SERVICE(service_unit);
78}
79
fcb7138c
ZJS
80static int _check_states(unsigned line,
81 Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) {
708961c7
MC
82 assert_se(m);
83 assert_se(service);
bc999297 84
500727c2 85 usec_t end = now(CLOCK_MONOTONIC) + 30 * USEC_PER_SEC;
708961c7 86
75edb0b0
ZJS
87 while (path->state != path_state || service->state != service_state ||
88 path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS) {
bc999297 89
500727c2 90 assert_se(sd_event_run(m->event, 100 * USEC_PER_MSEC) >= 0);
bc999297 91
75edb0b0
ZJS
92 usec_t n = now(CLOCK_MONOTONIC);
93 log_info("line %u: %s: state = %s; result = %s (left: %" PRIi64 ")",
94 line,
95 UNIT(path)->id,
96 path_state_to_string(path->state),
97 path_result_to_string(path->result),
98 end - n);
99 log_info("line %u: %s: state = %s; result = %s",
100 line,
101 UNIT(service)->id,
102 service_state_to_string(service->state),
103 service_result_to_string(service->result));
104
fcb7138c 105 if (service->state == SERVICE_FAILED &&
16e925c6
ZJS
106 service->main_exec_status.status == EXIT_CGROUP &&
107 !ci_environment())
fcb7138c
ZJS
108 /* On a general purpose system we may fail to start the service for reasons which are
109 * not under our control: permission limits, resource exhaustion, etc. Let's skip the
16e925c6 110 * test in those cases. On developer machines we require proper setup. */
fcb7138c
ZJS
111 return log_notice_errno(SYNTHETIC_ERRNO(ECANCELED),
112 "Failed to start service %s, aborting test: %s/%s",
113 UNIT(service)->id,
114 service_state_to_string(service->state),
115 service_result_to_string(service->result));
116
75edb0b0 117 if (n >= end) {
708961c7 118 log_error("Test timeout when testing %s", UNIT(path)->id);
bc999297
RC
119 exit(EXIT_FAILURE);
120 }
121 }
fcb7138c
ZJS
122
123 return 0;
bc999297 124}
75edb0b0 125#define check_states(...) _check_states(__LINE__, __VA_ARGS__)
bc999297
RC
126
127static void test_path_exists(Manager *m) {
128 const char *test_path = "/tmp/test-path_exists";
129 Unit *unit = NULL;
708961c7
MC
130 Path *path = NULL;
131 Service *service = NULL;
bc999297
RC
132
133 assert_se(m);
134
ba412430 135 assert_se(manager_load_startable_unit_or_warn(m, "path-exists.path", NULL, &unit) >= 0);
708961c7
MC
136
137 path = PATH(unit);
138 service = service_for_path(m, path, NULL);
139
bd7989a3 140 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
141 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
142 return;
bc999297
RC
143
144 assert_se(touch(test_path) >= 0);
fcb7138c
ZJS
145 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
146 return;
708961c7
MC
147
148 /* Service restarts if file still exists */
149 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
150 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
151 return;
708961c7
MC
152
153 assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
154 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
155 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
156 return;
bc999297 157
708961c7 158 assert_se(unit_stop(unit) >= 0);
bc999297
RC
159}
160
161static void test_path_existsglob(Manager *m) {
162 const char *test_path = "/tmp/test-path_existsglobFOOBAR";
163 Unit *unit = NULL;
708961c7
MC
164 Path *path = NULL;
165 Service *service = NULL;
bc999297
RC
166
167 assert_se(m);
708961c7 168
ba412430 169 assert_se(manager_load_startable_unit_or_warn(m, "path-existsglob.path", NULL, &unit) >= 0);
708961c7
MC
170
171 path = PATH(unit);
172 service = service_for_path(m, path, NULL);
173
bd7989a3 174 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
175 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
176 return;
bc999297
RC
177
178 assert_se(touch(test_path) >= 0);
fcb7138c
ZJS
179 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
180 return;
708961c7
MC
181
182 /* Service restarts if file still exists */
183 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
184 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
185 return;
bc999297 186
708961c7
MC
187 assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
188 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
189 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
190 return;
708961c7
MC
191
192 assert_se(unit_stop(unit) >= 0);
bc999297
RC
193}
194
195static void test_path_changed(Manager *m) {
196 const char *test_path = "/tmp/test-path_changed";
197 FILE *f;
198 Unit *unit = NULL;
708961c7
MC
199 Path *path = NULL;
200 Service *service = NULL;
bc999297
RC
201
202 assert_se(m);
203
ba412430 204 assert_se(manager_load_startable_unit_or_warn(m, "path-changed.path", NULL, &unit) >= 0);
708961c7
MC
205
206 path = PATH(unit);
207 service = service_for_path(m, path, NULL);
208
bd7989a3 209 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
210 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
211 return;
708961c7
MC
212
213 assert_se(touch(test_path) >= 0);
fcb7138c
ZJS
214 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
215 return;
708961c7
MC
216
217 /* Service does not restart if file still exists */
218 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
219 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
220 return;
bc999297
RC
221
222 f = fopen(test_path, "w");
223 assert_se(f);
224 fclose(f);
225
fcb7138c
ZJS
226 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
227 return;
708961c7
MC
228
229 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
230 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
231 return;
708961c7
MC
232
233 (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
234 assert_se(unit_stop(unit) >= 0);
bc999297
RC
235}
236
237static void test_path_modified(Manager *m) {
238 _cleanup_fclose_ FILE *f = NULL;
239 const char *test_path = "/tmp/test-path_modified";
240 Unit *unit = NULL;
708961c7
MC
241 Path *path = NULL;
242 Service *service = NULL;
bc999297
RC
243
244 assert_se(m);
245
ba412430 246 assert_se(manager_load_startable_unit_or_warn(m, "path-modified.path", NULL, &unit) >= 0);
708961c7
MC
247
248 path = PATH(unit);
249 service = service_for_path(m, path, NULL);
250
bd7989a3 251 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
252 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
253 return;
708961c7
MC
254
255 assert_se(touch(test_path) >= 0);
fcb7138c
ZJS
256 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
257 return;
708961c7
MC
258
259 /* Service does not restart if file still exists */
260 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
261 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
262 return;
bc999297
RC
263
264 f = fopen(test_path, "w");
265 assert_se(f);
266 fputs("test", f);
267
fcb7138c
ZJS
268 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
269 return;
708961c7
MC
270
271 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
272 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
273 return;
708961c7
MC
274
275 (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
276 assert_se(unit_stop(unit) >= 0);
bc999297
RC
277}
278
279static void test_path_unit(Manager *m) {
280 const char *test_path = "/tmp/test-path_unit";
281 Unit *unit = NULL;
708961c7
MC
282 Path *path = NULL;
283 Service *service = NULL;
bc999297
RC
284
285 assert_se(m);
286
ba412430 287 assert_se(manager_load_startable_unit_or_warn(m, "path-unit.path", NULL, &unit) >= 0);
708961c7
MC
288
289 path = PATH(unit);
290 service = service_for_path(m, path, "path-mycustomunit.service");
291
bd7989a3 292 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
293 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
294 return;
bc999297
RC
295
296 assert_se(touch(test_path) >= 0);
fcb7138c
ZJS
297 if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
298 return;
708961c7
MC
299
300 assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
301 assert_se(unit_stop(UNIT(service)) >= 0);
fcb7138c
ZJS
302 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
303 return;
bc999297 304
708961c7 305 assert_se(unit_stop(unit) >= 0);
bc999297
RC
306}
307
308static void test_path_directorynotempty(Manager *m) {
309 const char *test_path = "/tmp/test-path_directorynotempty/";
310 Unit *unit = NULL;
708961c7
MC
311 Path *path = NULL;
312 Service *service = NULL;
bc999297
RC
313
314 assert_se(m);
315
708961c7
MC
316 assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0);
317
318 path = PATH(unit);
319 service = service_for_path(m, path, NULL);
320
bc999297
RC
321 assert_se(access(test_path, F_OK) < 0);
322
bd7989a3 323 assert_se(unit_start(unit) >= 0);
fcb7138c
ZJS
324 if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
325 return;
bc999297
RC
326
327 /* MakeDirectory default to no */
328 assert_se(access(test_path, F_OK) < 0);
329
330 assert_se(mkdir_p(test_path, 0755) >= 0);
63c372cb 331 assert_se(touch(strjoina(test_path, "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}