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