]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
b77c08e0 | 2 | |
b77c08e0 | 3 | #include <errno.h> |
a8fbdf54 | 4 | #include <fcntl.h> |
84ac7bea | 5 | #include <fnmatch.h> |
a8fbdf54 | 6 | #include <limits.h> |
84ac7bea | 7 | #include <stdlib.h> |
b77c08e0 | 8 | #include <string.h> |
a8fbdf54 | 9 | #include <sys/stat.h> |
c465a29f | 10 | #include <sys/types.h> |
5022f08a | 11 | #include <sys/utsname.h> |
a8fbdf54 | 12 | #include <time.h> |
b77c08e0 | 13 | #include <unistd.h> |
b77c08e0 | 14 | |
d1bddcec | 15 | #include "sd-id128.h" |
84ac7bea | 16 | |
b5efdb8a | 17 | #include "alloc-util.h" |
d1bddcec | 18 | #include "apparmor-util.h" |
84ac7bea | 19 | #include "architecture.h" |
430f0182 | 20 | #include "audit-util.h" |
2822da4f | 21 | #include "cap-list.h" |
e16647c3 | 22 | #include "cgroup-util.h" |
3ffd4af2 | 23 | #include "condition.h" |
84ac7bea | 24 | #include "extract-word.h" |
3ffd4af2 | 25 | #include "fd-util.h" |
fb8b0869 | 26 | #include "fileio.h" |
7d50b32a | 27 | #include "glob-util.h" |
958b66ea | 28 | #include "hostname-util.h" |
84ac7bea | 29 | #include "ima-util.h" |
a8fbdf54 TA |
30 | #include "list.h" |
31 | #include "macro.h" | |
4349cd7c | 32 | #include "mount-util.h" |
6bedfcbb | 33 | #include "parse-util.h" |
84ac7bea | 34 | #include "path-util.h" |
4e731273 | 35 | #include "proc-cmdline.h" |
df0ff127 | 36 | #include "process-util.h" |
84ac7bea LP |
37 | #include "selinux-util.h" |
38 | #include "smack-util.h" | |
8fcde012 | 39 | #include "stat-util.h" |
8b43440b | 40 | #include "string-table.h" |
07630cea | 41 | #include "string-util.h" |
ed440f6b | 42 | #include "tomoyo-util.h" |
c465a29f | 43 | #include "user-util.h" |
84ac7bea LP |
44 | #include "util.h" |
45 | #include "virt.h" | |
b77c08e0 TG |
46 | |
47 | Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { | |
48 | Condition *c; | |
d1bddcec | 49 | int r; |
b77c08e0 | 50 | |
b80ba1da | 51 | assert(type >= 0); |
b77c08e0 | 52 | assert(type < _CONDITION_TYPE_MAX); |
8a9c6071 | 53 | assert((!parameter) == (type == CONDITION_NULL)); |
b77c08e0 TG |
54 | |
55 | c = new0(Condition, 1); | |
56 | if (!c) | |
57 | return NULL; | |
58 | ||
59 | c->type = type; | |
60 | c->trigger = trigger; | |
61 | c->negate = negate; | |
62 | ||
d1bddcec LP |
63 | r = free_and_strdup(&c->parameter, parameter); |
64 | if (r < 0) { | |
5fecf46d | 65 | return mfree(c); |
b77c08e0 TG |
66 | } |
67 | ||
68 | return c; | |
69 | } | |
70 | ||
71 | void condition_free(Condition *c) { | |
72 | assert(c); | |
73 | ||
74 | free(c->parameter); | |
75 | free(c); | |
76 | } | |
77 | ||
447021aa | 78 | Condition* condition_free_list(Condition *first) { |
b77c08e0 TG |
79 | Condition *c, *n; |
80 | ||
81 | LIST_FOREACH_SAFE(conditions, c, n, first) | |
82 | condition_free(c); | |
447021aa ZJS |
83 | |
84 | return NULL; | |
b77c08e0 TG |
85 | } |
86 | ||
a4705396 | 87 | static int condition_test_kernel_command_line(Condition *c) { |
07318c29 LP |
88 | _cleanup_free_ char *line = NULL; |
89 | const char *p; | |
b77c08e0 TG |
90 | bool equal; |
91 | int r; | |
b77c08e0 TG |
92 | |
93 | assert(c); | |
94 | assert(c->parameter); | |
95 | assert(c->type == CONDITION_KERNEL_COMMAND_LINE); | |
96 | ||
97 | r = proc_cmdline(&line); | |
98 | if (r < 0) | |
592fd144 | 99 | return r; |
b77c08e0 | 100 | |
5d904a6a | 101 | equal = strchr(c->parameter, '='); |
07318c29 | 102 | |
c58bd76a | 103 | for (p = line;;) { |
07318c29 LP |
104 | _cleanup_free_ char *word = NULL; |
105 | bool found; | |
106 | ||
12ba2c44 | 107 | r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); |
592fd144 LP |
108 | if (r < 0) |
109 | return r; | |
110 | if (r == 0) | |
a4705396 | 111 | break; |
07318c29 LP |
112 | |
113 | if (equal) | |
114 | found = streq(word, c->parameter); | |
115 | else { | |
116 | const char *f; | |
117 | ||
118 | f = startswith(word, c->parameter); | |
4c701096 | 119 | found = f && IN_SET(*f, 0, '='); |
b77c08e0 TG |
120 | } |
121 | ||
07318c29 | 122 | if (found) |
a4705396 | 123 | return true; |
b77c08e0 | 124 | } |
b77c08e0 | 125 | |
a4705396 | 126 | return false; |
b77c08e0 TG |
127 | } |
128 | ||
5022f08a | 129 | static int condition_test_kernel_version(Condition *c) { |
68c58c67 LP |
130 | enum { |
131 | /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest | |
132 | * should be listed first. */ | |
133 | LOWER_OR_EQUAL, | |
134 | GREATER_OR_EQUAL, | |
135 | LOWER, | |
136 | GREATER, | |
137 | EQUAL, | |
138 | _ORDER_MAX, | |
139 | }; | |
140 | ||
141 | static const char *const prefix[_ORDER_MAX] = { | |
142 | [LOWER_OR_EQUAL] = "<=", | |
143 | [GREATER_OR_EQUAL] = ">=", | |
144 | [LOWER] = "<", | |
145 | [GREATER] = ">", | |
146 | [EQUAL] = "=", | |
147 | }; | |
148 | const char *p = NULL; | |
5022f08a | 149 | struct utsname u; |
68c58c67 LP |
150 | size_t i; |
151 | int k; | |
5022f08a LP |
152 | |
153 | assert(c); | |
154 | assert(c->parameter); | |
155 | assert(c->type == CONDITION_KERNEL_VERSION); | |
156 | ||
157 | assert_se(uname(&u) >= 0); | |
158 | ||
68c58c67 LP |
159 | for (i = 0; i < _ORDER_MAX; i++) { |
160 | p = startswith(c->parameter, prefix[i]); | |
161 | if (p) | |
162 | break; | |
163 | } | |
164 | ||
165 | /* No prefix? Then treat as glob string */ | |
166 | if (!p) | |
167 | return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0; | |
168 | ||
169 | k = str_verscmp(u.release, skip_leading_chars(p, NULL)); | |
170 | ||
171 | switch (i) { | |
172 | ||
173 | case LOWER: | |
174 | return k < 0; | |
175 | ||
176 | case LOWER_OR_EQUAL: | |
177 | return k <= 0; | |
178 | ||
179 | case EQUAL: | |
180 | return k == 0; | |
181 | ||
182 | case GREATER_OR_EQUAL: | |
183 | return k >= 0; | |
184 | ||
185 | case GREATER: | |
186 | return k > 0; | |
187 | ||
188 | default: | |
189 | assert_not_reached("Can't compare"); | |
190 | } | |
5022f08a LP |
191 | } |
192 | ||
c465a29f FS |
193 | static int condition_test_user(Condition *c) { |
194 | uid_t id; | |
195 | int r; | |
196 | _cleanup_free_ char *username = NULL; | |
197 | const char *u; | |
198 | ||
199 | assert(c); | |
200 | assert(c->parameter); | |
201 | assert(c->type == CONDITION_USER); | |
202 | ||
203 | r = parse_uid(c->parameter, &id); | |
204 | if (r >= 0) | |
205 | return id == getuid() || id == geteuid(); | |
206 | ||
534bab66 | 207 | if (streq("@system", c->parameter)) |
ece877d4 | 208 | return uid_is_system(getuid()) || uid_is_system(geteuid()); |
534bab66 | 209 | |
c465a29f FS |
210 | username = getusername_malloc(); |
211 | if (!username) | |
212 | return -ENOMEM; | |
213 | ||
214 | if (streq(username, c->parameter)) | |
215 | return 1; | |
216 | ||
df0ff127 | 217 | if (getpid_cached() == 1) |
c465a29f FS |
218 | return streq(c->parameter, "root"); |
219 | ||
220 | u = c->parameter; | |
221 | r = get_user_creds(&u, &id, NULL, NULL, NULL); | |
222 | if (r < 0) | |
223 | return 0; | |
224 | ||
225 | return id == getuid() || id == geteuid(); | |
226 | } | |
227 | ||
e16647c3 CD |
228 | static int condition_test_control_group_controller(Condition *c) { |
229 | int r; | |
230 | CGroupMask system_mask, wanted_mask = 0; | |
231 | ||
232 | assert(c); | |
233 | assert(c->parameter); | |
234 | assert(c->type == CONDITION_CONTROL_GROUP_CONTROLLER); | |
235 | ||
236 | r = cg_mask_supported(&system_mask); | |
237 | if (r < 0) | |
238 | return log_debug_errno(r, "Failed to determine supported controllers: %m"); | |
239 | ||
240 | r = cg_mask_from_string(c->parameter, &wanted_mask); | |
241 | if (r < 0 || wanted_mask <= 0) { | |
242 | /* This won't catch the case that we have an unknown controller | |
243 | * mixed in with valid ones -- these are only assessed on the | |
244 | * validity of the valid controllers found. */ | |
245 | log_debug("Failed to parse cgroup string: %s", c->parameter); | |
246 | return 1; | |
247 | } | |
248 | ||
d94a24ca | 249 | return FLAGS_SET(system_mask, wanted_mask); |
e16647c3 CD |
250 | } |
251 | ||
c465a29f FS |
252 | static int condition_test_group(Condition *c) { |
253 | gid_t id; | |
254 | int r; | |
255 | ||
256 | assert(c); | |
257 | assert(c->parameter); | |
258 | assert(c->type == CONDITION_GROUP); | |
259 | ||
260 | r = parse_gid(c->parameter, &id); | |
261 | if (r >= 0) | |
262 | return in_gid(id); | |
263 | ||
264 | /* Avoid any NSS lookups if we are PID1 */ | |
df0ff127 | 265 | if (getpid_cached() == 1) |
c465a29f FS |
266 | return streq(c->parameter, "root"); |
267 | ||
268 | return in_group(c->parameter) > 0; | |
269 | } | |
270 | ||
a4705396 | 271 | static int condition_test_virtualization(Condition *c) { |
248fab74 | 272 | int b, v; |
b77c08e0 TG |
273 | |
274 | assert(c); | |
275 | assert(c->parameter); | |
276 | assert(c->type == CONDITION_VIRTUALIZATION); | |
277 | ||
239a5707 ZJS |
278 | if (streq(c->parameter, "private-users")) |
279 | return running_in_userns(); | |
280 | ||
75f86906 | 281 | v = detect_virtualization(); |
592fd144 LP |
282 | if (v < 0) |
283 | return v; | |
b77c08e0 TG |
284 | |
285 | /* First, compare with yes/no */ | |
286 | b = parse_boolean(c->parameter); | |
0809d774 ZJS |
287 | if (b >= 0) |
288 | return b == !!v; | |
b77c08e0 TG |
289 | |
290 | /* Then, compare categorization */ | |
0809d774 ZJS |
291 | if (streq(c->parameter, "vm")) |
292 | return VIRTUALIZATION_IS_VM(v); | |
b77c08e0 | 293 | |
0809d774 ZJS |
294 | if (streq(c->parameter, "container")) |
295 | return VIRTUALIZATION_IS_CONTAINER(v); | |
b77c08e0 TG |
296 | |
297 | /* Finally compare id */ | |
75f86906 | 298 | return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v)); |
b77c08e0 TG |
299 | } |
300 | ||
a4705396 | 301 | static int condition_test_architecture(Condition *c) { |
592fd144 | 302 | int a, b; |
099524d7 LP |
303 | |
304 | assert(c); | |
305 | assert(c->parameter); | |
306 | assert(c->type == CONDITION_ARCHITECTURE); | |
307 | ||
308 | a = uname_architecture(); | |
309 | if (a < 0) | |
592fd144 | 310 | return a; |
099524d7 LP |
311 | |
312 | if (streq(c->parameter, "native")) | |
313 | b = native_architecture(); | |
2cb62395 | 314 | else { |
099524d7 | 315 | b = architecture_from_string(c->parameter); |
2cb62395 LP |
316 | if (b < 0) /* unknown architecture? Then it's definitely not ours */ |
317 | return false; | |
318 | } | |
099524d7 | 319 | |
a4705396 | 320 | return a == b; |
099524d7 LP |
321 | } |
322 | ||
a4705396 | 323 | static int condition_test_host(Condition *c) { |
dc92e62c | 324 | _cleanup_free_ char *h = NULL; |
b77c08e0 | 325 | sd_id128_t x, y; |
b77c08e0 | 326 | int r; |
b77c08e0 TG |
327 | |
328 | assert(c); | |
329 | assert(c->parameter); | |
330 | assert(c->type == CONDITION_HOST); | |
331 | ||
332 | if (sd_id128_from_string(c->parameter, &x) >= 0) { | |
333 | ||
334 | r = sd_id128_get_machine(&y); | |
335 | if (r < 0) | |
592fd144 | 336 | return r; |
b77c08e0 | 337 | |
a4705396 | 338 | return sd_id128_equal(x, y); |
b77c08e0 TG |
339 | } |
340 | ||
341 | h = gethostname_malloc(); | |
342 | if (!h) | |
592fd144 | 343 | return -ENOMEM; |
b77c08e0 | 344 | |
a4705396 | 345 | return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0; |
b77c08e0 TG |
346 | } |
347 | ||
a4705396 | 348 | static int condition_test_ac_power(Condition *c) { |
b77c08e0 TG |
349 | int r; |
350 | ||
351 | assert(c); | |
352 | assert(c->parameter); | |
353 | assert(c->type == CONDITION_AC_POWER); | |
354 | ||
355 | r = parse_boolean(c->parameter); | |
356 | if (r < 0) | |
592fd144 | 357 | return r; |
b77c08e0 | 358 | |
a4705396 | 359 | return (on_ac_power() != 0) == !!r; |
b77c08e0 TG |
360 | } |
361 | ||
d1bddcec LP |
362 | static int condition_test_security(Condition *c) { |
363 | assert(c); | |
364 | assert(c->parameter); | |
365 | assert(c->type == CONDITION_SECURITY); | |
366 | ||
367 | if (streq(c->parameter, "selinux")) | |
6d395665 | 368 | return mac_selinux_use(); |
d1bddcec | 369 | if (streq(c->parameter, "smack")) |
a4705396 | 370 | return mac_smack_use(); |
d1bddcec | 371 | if (streq(c->parameter, "apparmor")) |
a4705396 | 372 | return mac_apparmor_use(); |
d1bddcec | 373 | if (streq(c->parameter, "audit")) |
a4705396 | 374 | return use_audit(); |
d1bddcec | 375 | if (streq(c->parameter, "ima")) |
a4705396 | 376 | return use_ima(); |
ed440f6b SL |
377 | if (streq(c->parameter, "tomoyo")) |
378 | return mac_tomoyo_use(); | |
d1bddcec | 379 | |
a4705396 | 380 | return false; |
d1bddcec LP |
381 | } |
382 | ||
383 | static int condition_test_capability(Condition *c) { | |
384 | _cleanup_fclose_ FILE *f = NULL; | |
2822da4f | 385 | int value; |
d1bddcec LP |
386 | char line[LINE_MAX]; |
387 | unsigned long long capabilities = -1; | |
388 | ||
389 | assert(c); | |
390 | assert(c->parameter); | |
391 | assert(c->type == CONDITION_CAPABILITY); | |
392 | ||
393 | /* If it's an invalid capability, we don't have it */ | |
2822da4f LP |
394 | value = capability_from_name(c->parameter); |
395 | if (value < 0) | |
d1bddcec LP |
396 | return -EINVAL; |
397 | ||
398 | /* If it's a valid capability we default to assume | |
399 | * that we have it */ | |
400 | ||
401 | f = fopen("/proc/self/status", "re"); | |
402 | if (!f) | |
403 | return -errno; | |
404 | ||
405 | while (fgets(line, sizeof(line), f)) { | |
406 | truncate_nl(line); | |
407 | ||
408 | if (startswith(line, "CapBnd:")) { | |
409 | (void) sscanf(line+7, "%llx", &capabilities); | |
410 | break; | |
411 | } | |
412 | } | |
413 | ||
a4705396 | 414 | return !!(capabilities & (1ULL << value)); |
d1bddcec LP |
415 | } |
416 | ||
417 | static int condition_test_needs_update(Condition *c) { | |
418 | const char *p; | |
419 | struct stat usr, other; | |
420 | ||
421 | assert(c); | |
422 | assert(c->parameter); | |
423 | assert(c->type == CONDITION_NEEDS_UPDATE); | |
424 | ||
425 | /* If the file system is read-only we shouldn't suggest an update */ | |
426 | if (path_is_read_only_fs(c->parameter) > 0) | |
a4705396 | 427 | return false; |
d1bddcec LP |
428 | |
429 | /* Any other failure means we should allow the condition to be true, | |
96d49011 | 430 | * so that we rather invoke too many update tools than too |
d1bddcec LP |
431 | * few. */ |
432 | ||
433 | if (!path_is_absolute(c->parameter)) | |
a4705396 | 434 | return true; |
d1bddcec | 435 | |
63c372cb | 436 | p = strjoina(c->parameter, "/.updated"); |
d1bddcec | 437 | if (lstat(p, &other) < 0) |
a4705396 | 438 | return true; |
d1bddcec LP |
439 | |
440 | if (lstat("/usr/", &usr) < 0) | |
a4705396 | 441 | return true; |
d1bddcec | 442 | |
fb8b0869 IS |
443 | /* |
444 | * First, compare seconds as they are always accurate... | |
445 | */ | |
446 | if (usr.st_mtim.tv_sec != other.st_mtim.tv_sec) | |
447 | return usr.st_mtim.tv_sec > other.st_mtim.tv_sec; | |
448 | ||
449 | /* | |
450 | * ...then compare nanoseconds. | |
451 | * | |
452 | * A false positive is only possible when /usr's nanoseconds > 0 | |
453 | * (otherwise /usr cannot be strictly newer than the target file) | |
454 | * AND the target file's nanoseconds == 0 | |
455 | * (otherwise the filesystem supports nsec timestamps, see stat(2)). | |
456 | */ | |
457 | if (usr.st_mtim.tv_nsec > 0 && other.st_mtim.tv_nsec == 0) { | |
458 | _cleanup_free_ char *timestamp_str = NULL; | |
459 | uint64_t timestamp; | |
460 | int r; | |
461 | ||
1a5a177e | 462 | r = parse_env_file(NULL, p, NULL, "TIMESTAMP_NSEC", ×tamp_str, NULL); |
fb8b0869 | 463 | if (r < 0) { |
ec2ebfd5 | 464 | log_error_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p); |
fb8b0869 IS |
465 | return true; |
466 | } else if (r == 0) { | |
467 | log_debug("No data in timestamp file '%s', using mtime", p); | |
468 | return true; | |
469 | } | |
470 | ||
471 | r = safe_atou64(timestamp_str, ×tamp); | |
472 | if (r < 0) { | |
ec2ebfd5 | 473 | log_error_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p); |
fb8b0869 IS |
474 | return true; |
475 | } | |
476 | ||
ec2ebfd5 | 477 | timespec_store(&other.st_mtim, timestamp); |
fb8b0869 IS |
478 | } |
479 | ||
480 | return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec; | |
d1bddcec LP |
481 | } |
482 | ||
483 | static int condition_test_first_boot(Condition *c) { | |
484 | int r; | |
485 | ||
486 | assert(c); | |
487 | assert(c->parameter); | |
488 | assert(c->type == CONDITION_FIRST_BOOT); | |
489 | ||
490 | r = parse_boolean(c->parameter); | |
491 | if (r < 0) | |
492 | return r; | |
493 | ||
a4705396 | 494 | return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r; |
d1bddcec LP |
495 | } |
496 | ||
497 | static int condition_test_path_exists(Condition *c) { | |
498 | assert(c); | |
499 | assert(c->parameter); | |
500 | assert(c->type == CONDITION_PATH_EXISTS); | |
501 | ||
a4705396 | 502 | return access(c->parameter, F_OK) >= 0; |
d1bddcec LP |
503 | } |
504 | ||
505 | static int condition_test_path_exists_glob(Condition *c) { | |
506 | assert(c); | |
507 | assert(c->parameter); | |
508 | assert(c->type == CONDITION_PATH_EXISTS_GLOB); | |
509 | ||
a4705396 | 510 | return glob_exists(c->parameter) > 0; |
d1bddcec LP |
511 | } |
512 | ||
513 | static int condition_test_path_is_directory(Condition *c) { | |
514 | assert(c); | |
515 | assert(c->parameter); | |
516 | assert(c->type == CONDITION_PATH_IS_DIRECTORY); | |
517 | ||
a4705396 | 518 | return is_dir(c->parameter, true) > 0; |
d1bddcec LP |
519 | } |
520 | ||
521 | static int condition_test_path_is_symbolic_link(Condition *c) { | |
522 | assert(c); | |
523 | assert(c->parameter); | |
524 | assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK); | |
525 | ||
a4705396 | 526 | return is_symlink(c->parameter) > 0; |
d1bddcec LP |
527 | } |
528 | ||
529 | static int condition_test_path_is_mount_point(Condition *c) { | |
530 | assert(c); | |
531 | assert(c->parameter); | |
532 | assert(c->type == CONDITION_PATH_IS_MOUNT_POINT); | |
533 | ||
e1873695 | 534 | return path_is_mount_point(c->parameter, NULL, AT_SYMLINK_FOLLOW) > 0; |
d1bddcec LP |
535 | } |
536 | ||
537 | static int condition_test_path_is_read_write(Condition *c) { | |
538 | assert(c); | |
539 | assert(c->parameter); | |
540 | assert(c->type == CONDITION_PATH_IS_READ_WRITE); | |
541 | ||
a4705396 | 542 | return path_is_read_only_fs(c->parameter) <= 0; |
d1bddcec LP |
543 | } |
544 | ||
545 | static int condition_test_directory_not_empty(Condition *c) { | |
546 | int r; | |
547 | ||
548 | assert(c); | |
549 | assert(c->parameter); | |
550 | assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY); | |
551 | ||
552 | r = dir_is_empty(c->parameter); | |
a4705396 | 553 | return r <= 0 && r != -ENOENT; |
d1bddcec LP |
554 | } |
555 | ||
556 | static int condition_test_file_not_empty(Condition *c) { | |
557 | struct stat st; | |
558 | ||
559 | assert(c); | |
560 | assert(c->parameter); | |
561 | assert(c->type == CONDITION_FILE_NOT_EMPTY); | |
562 | ||
563 | return (stat(c->parameter, &st) >= 0 && | |
564 | S_ISREG(st.st_mode) && | |
a4705396 | 565 | st.st_size > 0); |
d1bddcec LP |
566 | } |
567 | ||
568 | static int condition_test_file_is_executable(Condition *c) { | |
569 | struct stat st; | |
570 | ||
571 | assert(c); | |
572 | assert(c->parameter); | |
573 | assert(c->type == CONDITION_FILE_IS_EXECUTABLE); | |
574 | ||
575 | return (stat(c->parameter, &st) >= 0 && | |
576 | S_ISREG(st.st_mode) && | |
a4705396 | 577 | (st.st_mode & 0111)); |
d1bddcec LP |
578 | } |
579 | ||
580 | static int condition_test_null(Condition *c) { | |
581 | assert(c); | |
d1bddcec LP |
582 | assert(c->type == CONDITION_NULL); |
583 | ||
584 | /* Note that during parsing we already evaluate the string and | |
585 | * store it in c->negate */ | |
a4705396 | 586 | return true; |
d1bddcec LP |
587 | } |
588 | ||
589 | int condition_test(Condition *c) { | |
590 | ||
591 | static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = { | |
592 | [CONDITION_PATH_EXISTS] = condition_test_path_exists, | |
593 | [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob, | |
594 | [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory, | |
595 | [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link, | |
596 | [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point, | |
597 | [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write, | |
598 | [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty, | |
599 | [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty, | |
600 | [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable, | |
601 | [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line, | |
5022f08a | 602 | [CONDITION_KERNEL_VERSION] = condition_test_kernel_version, |
d1bddcec LP |
603 | [CONDITION_VIRTUALIZATION] = condition_test_virtualization, |
604 | [CONDITION_SECURITY] = condition_test_security, | |
605 | [CONDITION_CAPABILITY] = condition_test_capability, | |
606 | [CONDITION_HOST] = condition_test_host, | |
607 | [CONDITION_AC_POWER] = condition_test_ac_power, | |
608 | [CONDITION_ARCHITECTURE] = condition_test_architecture, | |
609 | [CONDITION_NEEDS_UPDATE] = condition_test_needs_update, | |
610 | [CONDITION_FIRST_BOOT] = condition_test_first_boot, | |
c465a29f FS |
611 | [CONDITION_USER] = condition_test_user, |
612 | [CONDITION_GROUP] = condition_test_group, | |
e16647c3 | 613 | [CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller, |
d1bddcec LP |
614 | [CONDITION_NULL] = condition_test_null, |
615 | }; | |
cc50ef13 LP |
616 | |
617 | int r, b; | |
d1bddcec LP |
618 | |
619 | assert(c); | |
620 | assert(c->type >= 0); | |
621 | assert(c->type < _CONDITION_TYPE_MAX); | |
622 | ||
a4705396 | 623 | r = condition_tests[c->type](c); |
cc50ef13 LP |
624 | if (r < 0) { |
625 | c->result = CONDITION_ERROR; | |
a4705396 | 626 | return r; |
cc50ef13 | 627 | } |
a4705396 | 628 | |
cc50ef13 LP |
629 | b = (r > 0) == !c->negate; |
630 | c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED; | |
631 | return b; | |
d1bddcec LP |
632 | } |
633 | ||
59fccdc5 | 634 | void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { |
b77c08e0 TG |
635 | assert(c); |
636 | assert(f); | |
637 | ||
ad5d4b17 | 638 | prefix = strempty(prefix); |
b77c08e0 TG |
639 | |
640 | fprintf(f, | |
641 | "%s\t%s: %s%s%s %s\n", | |
642 | prefix, | |
59fccdc5 | 643 | to_string(c->type), |
b77c08e0 TG |
644 | c->trigger ? "|" : "", |
645 | c->negate ? "!" : "", | |
646 | c->parameter, | |
cc50ef13 | 647 | condition_result_to_string(c->result)); |
b77c08e0 TG |
648 | } |
649 | ||
59fccdc5 | 650 | void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { |
b77c08e0 TG |
651 | Condition *c; |
652 | ||
653 | LIST_FOREACH(conditions, c, first) | |
59fccdc5 | 654 | condition_dump(c, f, prefix, to_string); |
b77c08e0 TG |
655 | } |
656 | ||
657 | static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { | |
651c3318 LP |
658 | [CONDITION_ARCHITECTURE] = "ConditionArchitecture", |
659 | [CONDITION_VIRTUALIZATION] = "ConditionVirtualization", | |
660 | [CONDITION_HOST] = "ConditionHost", | |
661 | [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", | |
5022f08a | 662 | [CONDITION_KERNEL_VERSION] = "ConditionKernelVersion", |
651c3318 LP |
663 | [CONDITION_SECURITY] = "ConditionSecurity", |
664 | [CONDITION_CAPABILITY] = "ConditionCapability", | |
665 | [CONDITION_AC_POWER] = "ConditionACPower", | |
666 | [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate", | |
667 | [CONDITION_FIRST_BOOT] = "ConditionFirstBoot", | |
b77c08e0 TG |
668 | [CONDITION_PATH_EXISTS] = "ConditionPathExists", |
669 | [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob", | |
670 | [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory", | |
671 | [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink", | |
672 | [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint", | |
673 | [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite", | |
674 | [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", | |
675 | [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty", | |
676 | [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable", | |
c465a29f FS |
677 | [CONDITION_USER] = "ConditionUser", |
678 | [CONDITION_GROUP] = "ConditionGroup", | |
e16647c3 | 679 | [CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController", |
b77c08e0 TG |
680 | [CONDITION_NULL] = "ConditionNull" |
681 | }; | |
682 | ||
683 | DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType); | |
cc50ef13 | 684 | |
59fccdc5 | 685 | static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { |
651c3318 LP |
686 | [CONDITION_ARCHITECTURE] = "AssertArchitecture", |
687 | [CONDITION_VIRTUALIZATION] = "AssertVirtualization", | |
688 | [CONDITION_HOST] = "AssertHost", | |
689 | [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine", | |
5022f08a | 690 | [CONDITION_KERNEL_VERSION] = "AssertKernelVersion", |
651c3318 LP |
691 | [CONDITION_SECURITY] = "AssertSecurity", |
692 | [CONDITION_CAPABILITY] = "AssertCapability", | |
693 | [CONDITION_AC_POWER] = "AssertACPower", | |
694 | [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate", | |
695 | [CONDITION_FIRST_BOOT] = "AssertFirstBoot", | |
59fccdc5 LP |
696 | [CONDITION_PATH_EXISTS] = "AssertPathExists", |
697 | [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob", | |
698 | [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory", | |
699 | [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink", | |
700 | [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint", | |
701 | [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite", | |
702 | [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty", | |
703 | [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty", | |
704 | [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable", | |
c465a29f FS |
705 | [CONDITION_USER] = "AssertUser", |
706 | [CONDITION_GROUP] = "AssertGroup", | |
e16647c3 | 707 | [CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController", |
59fccdc5 LP |
708 | [CONDITION_NULL] = "AssertNull" |
709 | }; | |
710 | ||
711 | DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType); | |
712 | ||
cc50ef13 LP |
713 | static const char* const condition_result_table[_CONDITION_RESULT_MAX] = { |
714 | [CONDITION_UNTESTED] = "untested", | |
715 | [CONDITION_SUCCEEDED] = "succeeded", | |
716 | [CONDITION_FAILED] = "failed", | |
717 | [CONDITION_ERROR] = "error", | |
718 | }; | |
719 | ||
720 | DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult); |