1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <fnmatch.h>
6 #include <limits.h>
7 #include <stdlib.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <sys/utsname.h>
11 #include <time.h>
12 #include <unistd.h>
13
14 #include "sd-id128.h"
15
16 #include "alloc-util.h"
17 #include "apparmor-util.h"
18 #include "architecture.h"
19 #include "audit-util.h"
20 #include "blockdev-util.h"
21 #include "cap-list.h"
22 #include "cgroup-util.h"
23 #include "condition.h"
24 #include "cpu-set-util.h"
25 #include "efi-api.h"
26 #include "env-file.h"
27 #include "env-util.h"
28 #include "extract-word.h"
29 #include "fd-util.h"
30 #include "fileio.h"
31 #include "fs-util.h"
32 #include "glob-util.h"
33 #include "hostname-util.h"
34 #include "ima-util.h"
35 #include "limits-util.h"
36 #include "list.h"
37 #include "macro.h"
38 #include "mountpoint-util.h"
39 #include "os-util.h"
40 #include "parse-util.h"
41 #include "path-util.h"
42 #include "percent-util.h"
43 #include "proc-cmdline.h"
44 #include "process-util.h"
45 #include "psi-util.h"
46 #include "selinux-util.h"
47 #include "smack-util.h"
48 #include "special.h"
49 #include "stat-util.h"
50 #include "string-table.h"
51 #include "string-util.h"
52 #include "tomoyo-util.h"
53 #include "tpm2-util.h"
54 #include "udev-util.h"
55 #include "uid-alloc-range.h"
56 #include "user-util.h"
57 #include "virt.h"
58
condition_new(ConditionType type,const char * parameter,bool trigger,bool negate)59 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
60 Condition *c;
61
62 assert(type >= 0);
63 assert(type < _CONDITION_TYPE_MAX);
64 assert(parameter);
65
66 c = new(Condition, 1);
67 if (!c)
68 return NULL;
69
70 *c = (Condition) {
71 .type = type,
72 .trigger = trigger,
73 .negate = negate,
74 };
75
76 if (parameter) {
77 c->parameter = strdup(parameter);
78 if (!c->parameter)
79 return mfree(c);
80 }
81
82 return c;
83 }
84
condition_free(Condition * c)85 Condition* condition_free(Condition *c) {
86 assert(c);
87
88 free(c->parameter);
89 return mfree(c);
90 }
91
condition_free_list_type(Condition * head,ConditionType type)92 Condition* condition_free_list_type(Condition *head, ConditionType type) {
93 LIST_FOREACH(conditions, c, head)
94 if (type < 0 || c->type == type) {
95 LIST_REMOVE(conditions, head, c);
96 condition_free(c);
97 }
98
99 assert(type >= 0 || !head);
100 return head;
101 }
102
condition_test_kernel_command_line(Condition * c,char ** env)103 static int condition_test_kernel_command_line(Condition *c, char **env) {
104 _cleanup_free_ char *line = NULL;
105 const char *p;
106 bool equal;
107 int r;
108
109 assert(c);
110 assert(c->parameter);
111 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
112
113 r = proc_cmdline(&line);
114 if (r < 0)
115 return r;
116
117 equal = strchr(c->parameter, '=');
118
119 for (p = line;;) {
120 _cleanup_free_ char *word = NULL;
121 bool found;
122
123 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
124 if (r < 0)
125 return r;
126 if (r == 0)
127 break;
128
129 if (equal)
130 found = streq(word, c->parameter);
131 else {
132 const char *f;
133
134 f = startswith(word, c->parameter);
135 found = f && IN_SET(*f, 0, '=');
136 }
137
138 if (found)
139 return true;
140 }
141
142 return false;
143 }
144
145 typedef enum {
146 /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
147 * should be listed first. */
148 ORDER_LOWER_OR_EQUAL,
149 ORDER_GREATER_OR_EQUAL,
150 ORDER_LOWER,
151 ORDER_GREATER,
152 ORDER_EQUAL,
153 ORDER_UNEQUAL,
154 _ORDER_MAX,
155 _ORDER_INVALID = -EINVAL,
156 } OrderOperator;
157
parse_order(const char ** s)158 static OrderOperator parse_order(const char **s) {
159
160 static const char *const prefix[_ORDER_MAX] = {
161 [ORDER_LOWER_OR_EQUAL] = "<=",
162 [ORDER_GREATER_OR_EQUAL] = ">=",
163 [ORDER_LOWER] = "<",
164 [ORDER_GREATER] = ">",
165 [ORDER_EQUAL] = "=",
166 [ORDER_UNEQUAL] = "!=",
167 };
168
169 OrderOperator i;
170
171 for (i = 0; i < _ORDER_MAX; i++) {
172 const char *e;
173
174 e = startswith(*s, prefix[i]);
175 if (e) {
176 *s = e;
177 return i;
178 }
179 }
180
181 return _ORDER_INVALID;
182 }
183
test_order(int k,OrderOperator p)184 static bool test_order(int k, OrderOperator p) {
185
186 switch (p) {
187
188 case ORDER_LOWER:
189 return k < 0;
190
191 case ORDER_LOWER_OR_EQUAL:
192 return k <= 0;
193
194 case ORDER_EQUAL:
195 return k == 0;
196
197 case ORDER_UNEQUAL:
198 return k != 0;
199
200 case ORDER_GREATER_OR_EQUAL:
201 return k >= 0;
202
203 case ORDER_GREATER:
204 return k > 0;
205
206 default:
207 assert_not_reached();
208
209 }
210 }
211
condition_test_kernel_version(Condition * c,char ** env)212 static int condition_test_kernel_version(Condition *c, char **env) {
213 OrderOperator order;
214 struct utsname u;
215 const char *p;
216 bool first = true;
217
218 assert(c);
219 assert(c->parameter);
220 assert(c->type == CONDITION_KERNEL_VERSION);
221
222 assert_se(uname(&u) >= 0);
223
224 p = c->parameter;
225
226 for (;;) {
227 _cleanup_free_ char *word = NULL;
228 const char *s;
229 int r;
230
231 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
232 if (r < 0)
233 return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
234 if (r == 0)
235 break;
236
237 s = strstrip(word);
238 order = parse_order(&s);
239 if (order >= 0) {
240 s += strspn(s, WHITESPACE);
241 if (isempty(s)) {
242 if (first) {
243 /* For backwards compatibility, allow whitespace between the operator and
244 * value, without quoting, but only in the first expression. */
245 word = mfree(word);
246 r = extract_first_word(&p, &word, NULL, 0);
247 if (r < 0)
248 return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
249 if (r == 0)
250 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
251 s = word;
252 } else
253 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
254 }
255
256 r = test_order(strverscmp_improved(u.release, s), order);
257 } else
258 /* No prefix? Then treat as glob string */
259 r = fnmatch(s, u.release, 0) == 0;
260
261 if (r == 0)
262 return false;
263
264 first = false;
265 }
266
267 return true;
268 }
269
condition_test_osrelease(Condition * c,char ** env)270 static int condition_test_osrelease(Condition *c, char **env) {
271 const char *parameter = c->parameter;
272 int r;
273
274 assert(c);
275 assert(c->parameter);
276 assert(c->type == CONDITION_OS_RELEASE);
277
278 for (;;) {
279 _cleanup_free_ char *key = NULL, *condition = NULL, *actual_value = NULL;
280 OrderOperator order;
281 const char *word;
282 bool matches;
283
284 r = extract_first_word(¶meter, &condition, NULL, EXTRACT_UNQUOTE);
285 if (r < 0)
286 return log_debug_errno(r, "Failed to parse parameter: %m");
287 if (r == 0)
288 break;
289
290 /* parse_order() needs the string to start with the comparators */
291 word = condition;
292 r = extract_first_word(&word, &key, "!<=>", EXTRACT_RETAIN_SEPARATORS);
293 if (r < 0)
294 return log_debug_errno(r, "Failed to parse parameter: %m");
295 /* The os-release spec mandates env-var-like key names */
296 if (r == 0 || isempty(word) || !env_name_is_valid(key))
297 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
298 "Failed to parse parameter, key/value format expected: %m");
299
300 /* Do not allow whitespace after the separator, as that's not a valid os-release format */
301 order = parse_order(&word);
302 if (order < 0 || isempty(word) || strchr(WHITESPACE, *word) != NULL)
303 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
304 "Failed to parse parameter, key/value format expected: %m");
305
306 r = parse_os_release(NULL, key, &actual_value);
307 if (r < 0)
308 return log_debug_errno(r, "Failed to parse os-release: %m");
309
310 /* Might not be comparing versions, so do exact string matching */
311 if (order == ORDER_EQUAL)
312 matches = streq_ptr(actual_value, word);
313 else if (order == ORDER_UNEQUAL)
314 matches = !streq_ptr(actual_value, word);
315 else
316 matches = test_order(strverscmp_improved(actual_value, word), order);
317
318 if (!matches)
319 return false;
320 }
321
322 return true;
323 }
324
condition_test_memory(Condition * c,char ** env)325 static int condition_test_memory(Condition *c, char **env) {
326 OrderOperator order;
327 uint64_t m, k;
328 const char *p;
329 int r;
330
331 assert(c);
332 assert(c->parameter);
333 assert(c->type == CONDITION_MEMORY);
334
335 m = physical_memory();
336
337 p = c->parameter;
338 order = parse_order(&p);
339 if (order < 0)
340 order = ORDER_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
341
342 r = safe_atou64(p, &k);
343 if (r < 0)
344 return log_debug_errno(r, "Failed to parse size: %m");
345
346 return test_order(CMP(m, k), order);
347 }
348
condition_test_cpus(Condition * c,char ** env)349 static int condition_test_cpus(Condition *c, char **env) {
350 OrderOperator order;
351 const char *p;
352 unsigned k;
353 int r, n;
354
355 assert(c);
356 assert(c->parameter);
357 assert(c->type == CONDITION_CPUS);
358
359 n = cpus_in_affinity_mask();
360 if (n < 0)
361 return log_debug_errno(n, "Failed to determine CPUs in affinity mask: %m");
362
363 p = c->parameter;
364 order = parse_order(&p);
365 if (order < 0)
366 order = ORDER_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
367
368 r = safe_atou(p, &k);
369 if (r < 0)
370 return log_debug_errno(r, "Failed to parse number of CPUs: %m");
371
372 return test_order(CMP((unsigned) n, k), order);
373 }
374
condition_test_user(Condition * c,char ** env)375 static int condition_test_user(Condition *c, char **env) {
376 uid_t id;
377 int r;
378 _cleanup_free_ char *username = NULL;
379 const char *u;
380
381 assert(c);
382 assert(c->parameter);
383 assert(c->type == CONDITION_USER);
384
385 r = parse_uid(c->parameter, &id);
386 if (r >= 0)
387 return id == getuid() || id == geteuid();
388
389 if (streq("@system", c->parameter))
390 return uid_is_system(getuid()) || uid_is_system(geteuid());
391
392 username = getusername_malloc();
393 if (!username)
394 return -ENOMEM;
395
396 if (streq(username, c->parameter))
397 return 1;
398
399 if (getpid_cached() == 1)
400 return streq(c->parameter, "root");
401
402 u = c->parameter;
403 r = get_user_creds(&u, &id, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
404 if (r < 0)
405 return 0;
406
407 return id == getuid() || id == geteuid();
408 }
409
condition_test_control_group_controller(Condition * c,char ** env)410 static int condition_test_control_group_controller(Condition *c, char **env) {
411 int r;
412 CGroupMask system_mask, wanted_mask = 0;
413
414 assert(c);
415 assert(c->parameter);
416 assert(c->type == CONDITION_CONTROL_GROUP_CONTROLLER);
417
418 if (streq(c->parameter, "v2"))
419 return cg_all_unified();
420 if (streq(c->parameter, "v1")) {
421 r = cg_all_unified();
422 if (r < 0)
423 return r;
424 return !r;
425 }
426
427 r = cg_mask_supported(&system_mask);
428 if (r < 0)
429 return log_debug_errno(r, "Failed to determine supported controllers: %m");
430
431 r = cg_mask_from_string(c->parameter, &wanted_mask);
432 if (r < 0 || wanted_mask <= 0) {
433 /* This won't catch the case that we have an unknown controller
434 * mixed in with valid ones -- these are only assessed on the
435 * validity of the valid controllers found. */
436 log_debug("Failed to parse cgroup string: %s", c->parameter);
437 return 1;
438 }
439
440 return FLAGS_SET(system_mask, wanted_mask);
441 }
442
condition_test_group(Condition * c,char ** env)443 static int condition_test_group(Condition *c, char **env) {
444 gid_t id;
445 int r;
446
447 assert(c);
448 assert(c->parameter);
449 assert(c->type == CONDITION_GROUP);
450
451 r = parse_gid(c->parameter, &id);
452 if (r >= 0)
453 return in_gid(id);
454
455 /* Avoid any NSS lookups if we are PID1 */
456 if (getpid_cached() == 1)
457 return streq(c->parameter, "root");
458
459 return in_group(c->parameter) > 0;
460 }
461
condition_test_virtualization(Condition * c,char ** env)462 static int condition_test_virtualization(Condition *c, char **env) {
463 Virtualization v;
464 int b;
465
466 assert(c);
467 assert(c->parameter);
468 assert(c->type == CONDITION_VIRTUALIZATION);
469
470 if (streq(c->parameter, "private-users"))
471 return running_in_userns();
472
473 v = detect_virtualization();
474 if (v < 0)
475 return v;
476
477 /* First, compare with yes/no */
478 b = parse_boolean(c->parameter);
479 if (b >= 0)
480 return b == (v != VIRTUALIZATION_NONE);
481
482 /* Then, compare categorization */
483 if (streq(c->parameter, "vm"))
484 return VIRTUALIZATION_IS_VM(v);
485
486 if (streq(c->parameter, "container"))
487 return VIRTUALIZATION_IS_CONTAINER(v);
488
489 /* Finally compare id */
490 return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
491 }
492
condition_test_architecture(Condition * c,char ** env)493 static int condition_test_architecture(Condition *c, char **env) {
494 Architecture a, b;
495
496 assert(c);
497 assert(c->parameter);
498 assert(c->type == CONDITION_ARCHITECTURE);
499
500 a = uname_architecture();
501 if (a < 0)
502 return a;
503
504 if (streq(c->parameter, "native"))
505 b = native_architecture();
506 else {
507 b = architecture_from_string(c->parameter);
508 if (b < 0) /* unknown architecture? Then it's definitely not ours */
509 return false;
510 }
511
512 return a == b;
513 }
514
515 #define DTCOMPAT_FILE "/proc/device-tree/compatible"
condition_test_firmware_devicetree_compatible(const char * dtcarg)516 static int condition_test_firmware_devicetree_compatible(const char *dtcarg) {
517 int r;
518 _cleanup_free_ char *dtcompat = NULL;
519 _cleanup_strv_free_ char **dtcompatlist = NULL;
520 size_t size;
521
522 r = read_full_virtual_file(DTCOMPAT_FILE, &dtcompat, &size);
523 if (r < 0) {
524 /* if the path doesn't exist it is incompatible */
525 if (r != -ENOENT)
526 log_debug_errno(r, "Failed to open() '%s', assuming machine is incompatible: %m", DTCOMPAT_FILE);
527 return false;
528 }
529
530 /* Not sure this can happen, but play safe. */
531 if (size == 0) {
532 log_debug("%s has zero length, assuming machine is incompatible", DTCOMPAT_FILE);
533 return false;
534 }
535
536 /* /proc/device-tree/compatible consists of one or more strings, each ending in '\0'.
537 * So the last character in dtcompat must be a '\0'. */
538 if (dtcompat[size - 1] != '\0') {
539 log_debug("%s is in an unknown format, assuming machine is incompatible", DTCOMPAT_FILE);
540 return false;
541 }
542
543 dtcompatlist = strv_parse_nulstr(dtcompat, size);
544 if (!dtcompatlist)
545 return -ENOMEM;
546
547 return strv_contains(dtcompatlist, dtcarg);
548 }
549
condition_test_firmware(Condition * c,char ** env)550 static int condition_test_firmware(Condition *c, char **env) {
551 sd_char *dtc;
552
553 assert(c);
554 assert(c->parameter);
555 assert(c->type == CONDITION_FIRMWARE);
556
557 if (streq(c->parameter, "device-tree")) {
558 if (access("/sys/firmware/device-tree/", F_OK) < 0) {
559 if (errno != ENOENT)
560 log_debug_errno(errno, "Unexpected error when checking for /sys/firmware/device-tree/: %m");
561 return false;
562 } else
563 return true;
564 } else if ((dtc = startswith(c->parameter, "device-tree-compatible("))) {
565 _cleanup_free_ char *dtcarg = NULL;
566 char *end;
567
568 end = strchr(dtc, ')');
569 if (!end || *(end + 1) != '\0') {
570 log_debug("Malformed Firmware condition \"%s\"", c->parameter);
571 return false;
572 }
573
574 dtcarg = strndup(dtc, end - dtc);
575 if (!dtcarg)
576 return -ENOMEM;
577
578 return condition_test_firmware_devicetree_compatible(dtcarg);
579 } else if (streq(c->parameter, "uefi"))
580 return is_efi_boot();
581 else {
582 log_debug("Unsupported Firmware condition \"%s\"", c->parameter);
583 return false;
584 }
585 }
586
condition_test_host(Condition * c,char ** env)587 static int condition_test_host(Condition *c, char **env) {
588 _cleanup_free_ char *h = NULL;
589 sd_id128_t x, y;
590 int r;
591
592 assert(c);
593 assert(c->parameter);
594 assert(c->type == CONDITION_HOST);
595
596 if (sd_id128_from_string(c->parameter, &x) >= 0) {
597
598 r = sd_id128_get_machine(&y);
599 if (r < 0)
600 return r;
601
602 return sd_id128_equal(x, y);
603 }
604
605 h = gethostname_malloc();
606 if (!h)
607 return -ENOMEM;
608
609 return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
610 }
611
condition_test_ac_power(Condition * c,char ** env)612 static int condition_test_ac_power(Condition *c, char **env) {
613 int r;
614
615 assert(c);
616 assert(c->parameter);
617 assert(c->type == CONDITION_AC_POWER);
618
619 r = parse_boolean(c->parameter);
620 if (r < 0)
621 return r;
622
623 return (on_ac_power() != 0) == !!r;
624 }
625
has_tpm2(void)626 static int has_tpm2(void) {
627 /* Checks whether the system has at least one TPM2 resource manager device, i.e. at least one "tpmrm"
628 * class device. Alternatively, we are also happy if the firmware reports support (this is to cover
629 * for cases where we simply haven't loaded the driver for it yet, i.e. during early boot where we
630 * very likely want to use this condition check).
631 *
632 * Note that we don't check if we ourselves are built with TPM2 support here! */
633
634 return (tpm2_support() & (TPM2_SUPPORT_DRIVER|TPM2_SUPPORT_FIRMWARE)) != 0;
635 }
636
condition_test_security(Condition * c,char ** env)637 static int condition_test_security(Condition *c, char **env) {
638 assert(c);
639 assert(c->parameter);
640 assert(c->type == CONDITION_SECURITY);
641
642 if (streq(c->parameter, "selinux"))
643 return mac_selinux_use();
644 if (streq(c->parameter, "smack"))
645 return mac_smack_use();
646 if (streq(c->parameter, "apparmor"))
647 return mac_apparmor_use();
648 if (streq(c->parameter, "audit"))
649 return use_audit();
650 if (streq(c->parameter, "ima"))
651 return use_ima();
652 if (streq(c->parameter, "tomoyo"))
653 return mac_tomoyo_use();
654 if (streq(c->parameter, "uefi-secureboot"))
655 return is_efi_secure_boot();
656 if (streq(c->parameter, "tpm2"))
657 return has_tpm2();
658
659 return false;
660 }
661
condition_test_capability(Condition * c,char ** env)662 static int condition_test_capability(Condition *c, char **env) {
663 unsigned long long capabilities = (unsigned long long) -1;
664 _cleanup_fclose_ FILE *f = NULL;
665 int value, r;
666
667 assert(c);
668 assert(c->parameter);
669 assert(c->type == CONDITION_CAPABILITY);
670
671 /* If it's an invalid capability, we don't have it */
672 value = capability_from_name(c->parameter);
673 if (value < 0)
674 return -EINVAL;
675
676 /* If it's a valid capability we default to assume
677 * that we have it */
678
679 f = fopen("/proc/self/status", "re");
680 if (!f)
681 return -errno;
682
683 for (;;) {
684 _cleanup_free_ char *line = NULL;
685 const char *p;
686
687 r = read_line(f, LONG_LINE_MAX, &line);
688 if (r < 0)
689 return r;
690 if (r == 0)
691 break;
692
693 p = startswith(line, "CapBnd:");
694 if (p) {
695 if (sscanf(line+7, "%llx", &capabilities) != 1)
696 return -EIO;
697
698 break;
699 }
700 }
701
702 return !!(capabilities & (1ULL << value));
703 }
704
condition_test_needs_update(Condition * c,char ** env)705 static int condition_test_needs_update(Condition *c, char **env) {
706 struct stat usr, other;
707 const char *p;
708 bool b;
709 int r;
710
711 assert(c);
712 assert(c->parameter);
713 assert(c->type == CONDITION_NEEDS_UPDATE);
714
715 r = proc_cmdline_get_bool("systemd.condition-needs-update", &b);
716 if (r < 0)
717 log_debug_errno(r, "Failed to parse systemd.condition-needs-update= kernel command line argument, ignoring: %m");
718 if (r > 0)
719 return b;
720
721 if (in_initrd()) {
722 log_debug("We are in an initrd, not doing any updates.");
723 return false;
724 }
725
726 if (!path_is_absolute(c->parameter)) {
727 log_debug("Specified condition parameter '%s' is not absolute, assuming an update is needed.", c->parameter);
728 return true;
729 }
730
731 /* If the file system is read-only we shouldn't suggest an update */
732 r = path_is_read_only_fs(c->parameter);
733 if (r < 0)
734 log_debug_errno(r, "Failed to determine if '%s' is read-only, ignoring: %m", c->parameter);
735 if (r > 0)
736 return false;
737
738 /* Any other failure means we should allow the condition to be true, so that we rather invoke too
739 * many update tools than too few. */
740
741 p = strjoina(c->parameter, "/.updated");
742 if (lstat(p, &other) < 0) {
743 if (errno != ENOENT)
744 log_debug_errno(errno, "Failed to stat() '%s', assuming an update is needed: %m", p);
745 return true;
746 }
747
748 if (lstat("/usr/", &usr) < 0) {
749 log_debug_errno(errno, "Failed to stat() /usr/, assuming an update is needed: %m");
750 return true;
751 }
752
753 /*
754 * First, compare seconds as they are always accurate...
755 */
756 if (usr.st_mtim.tv_sec != other.st_mtim.tv_sec)
757 return usr.st_mtim.tv_sec > other.st_mtim.tv_sec;
758
759 /*
760 * ...then compare nanoseconds.
761 *
762 * A false positive is only possible when /usr's nanoseconds > 0
763 * (otherwise /usr cannot be strictly newer than the target file)
764 * AND the target file's nanoseconds == 0
765 * (otherwise the filesystem supports nsec timestamps, see stat(2)).
766 */
767 if (usr.st_mtim.tv_nsec == 0 || other.st_mtim.tv_nsec > 0)
768 return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec;
769
770 _cleanup_free_ char *timestamp_str = NULL;
771 r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", ×tamp_str);
772 if (r < 0) {
773 log_debug_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
774 return true;
775 }
776 if (isempty(timestamp_str)) {
777 log_debug("No data in timestamp file '%s', using mtime.", p);
778 return true;
779 }
780
781 uint64_t timestamp;
782 r = safe_atou64(timestamp_str, ×tamp);
783 if (r < 0) {
784 log_debug_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p);
785 return true;
786 }
787
788 return timespec_load_nsec(&usr.st_mtim) > timestamp;
789 }
790
condition_test_first_boot(Condition * c,char ** env)791 static int condition_test_first_boot(Condition *c, char **env) {
792 int r, q;
793 bool b;
794
795 assert(c);
796 assert(c->parameter);
797 assert(c->type == CONDITION_FIRST_BOOT);
798
799 r = proc_cmdline_get_bool("systemd.condition-first-boot", &b);
800 if (r < 0)
801 log_debug_errno(r, "Failed to parse systemd.condition-first-boot= kernel command line argument, ignoring: %m");
802 if (r > 0)
803 return b == !!r;
804
805 r = parse_boolean(c->parameter);
806 if (r < 0)
807 return r;
808
809 q = access("/run/systemd/first-boot", F_OK);
810 if (q < 0 && errno != ENOENT)
811 log_debug_errno(errno, "Failed to check if /run/systemd/first-boot exists, ignoring: %m");
812
813 return (q >= 0) == !!r;
814 }
815
condition_test_environment(Condition * c,char ** env)816 static int condition_test_environment(Condition *c, char **env) {
817 bool equal;
818
819 assert(c);
820 assert(c->parameter);
821 assert(c->type == CONDITION_ENVIRONMENT);
822
823 equal = strchr(c->parameter, '=');
824
825 STRV_FOREACH(i, env) {
826 bool found;
827
828 if (equal)
829 found = streq(c->parameter, *i);
830 else {
831 const char *f;
832
833 f = startswith(*i, c->parameter);
834 found = f && IN_SET(*f, 0, '=');
835 }
836
837 if (found)
838 return true;
839 }
840
841 return false;
842 }
843
condition_test_path_exists(Condition * c,char ** env)844 static int condition_test_path_exists(Condition *c, char **env) {
845 assert(c);
846 assert(c->parameter);
847 assert(c->type == CONDITION_PATH_EXISTS);
848
849 return access(c->parameter, F_OK) >= 0;
850 }
851
condition_test_path_exists_glob(Condition * c,char ** env)852 static int condition_test_path_exists_glob(Condition *c, char **env) {
853 assert(c);
854 assert(c->parameter);
855 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
856
857 return glob_exists(c->parameter) > 0;
858 }
859
condition_test_path_is_directory(Condition * c,char ** env)860 static int condition_test_path_is_directory(Condition *c, char **env) {
861 assert(c);
862 assert(c->parameter);
863 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
864
865 return is_dir(c->parameter, true) > 0;
866 }
867
condition_test_path_is_symbolic_link(Condition * c,char ** env)868 static int condition_test_path_is_symbolic_link(Condition *c, char **env) {
869 assert(c);
870 assert(c->parameter);
871 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
872
873 return is_symlink(c->parameter) > 0;
874 }
875
condition_test_path_is_mount_point(Condition * c,char ** env)876 static int condition_test_path_is_mount_point(Condition *c, char **env) {
877 assert(c);
878 assert(c->parameter);
879 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
880
881 return path_is_mount_point(c->parameter, NULL, AT_SYMLINK_FOLLOW) > 0;
882 }
883
condition_test_path_is_read_write(Condition * c,char ** env)884 static int condition_test_path_is_read_write(Condition *c, char **env) {
885 int r;
886
887 assert(c);
888 assert(c->parameter);
889 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
890
891 r = path_is_read_only_fs(c->parameter);
892
893 return r <= 0 && r != -ENOENT;
894 }
895
condition_test_cpufeature(Condition * c,char ** env)896 static int condition_test_cpufeature(Condition *c, char **env) {
897 assert(c);
898 assert(c->parameter);
899 assert(c->type == CONDITION_CPU_FEATURE);
900
901 return has_cpu_with_flag(ascii_strlower(c->parameter));
902 }
903
condition_test_path_is_encrypted(Condition * c,char ** env)904 static int condition_test_path_is_encrypted(Condition *c, char **env) {
905 int r;
906
907 assert(c);
908 assert(c->parameter);
909 assert(c->type == CONDITION_PATH_IS_ENCRYPTED);
910
911 r = path_is_encrypted(c->parameter);
912 if (r < 0 && r != -ENOENT)
913 log_debug_errno(r, "Failed to determine if '%s' is encrypted: %m", c->parameter);
914
915 return r > 0;
916 }
917
condition_test_directory_not_empty(Condition * c,char ** env)918 static int condition_test_directory_not_empty(Condition *c, char **env) {
919 int r;
920
921 assert(c);
922 assert(c->parameter);
923 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
924
925 r = dir_is_empty(c->parameter, /* ignore_hidden_or_backup= */ true);
926 return r <= 0 && !IN_SET(r, -ENOENT, -ENOTDIR);
927 }
928
condition_test_file_not_empty(Condition * c,char ** env)929 static int condition_test_file_not_empty(Condition *c, char **env) {
930 struct stat st;
931
932 assert(c);
933 assert(c->parameter);
934 assert(c->type == CONDITION_FILE_NOT_EMPTY);
935
936 return (stat(c->parameter, &st) >= 0 &&
937 S_ISREG(st.st_mode) &&
938 st.st_size > 0);
939 }
940
condition_test_file_is_executable(Condition * c,char ** env)941 static int condition_test_file_is_executable(Condition *c, char **env) {
942 struct stat st;
943
944 assert(c);
945 assert(c->parameter);
946 assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
947
948 return (stat(c->parameter, &st) >= 0 &&
949 S_ISREG(st.st_mode) &&
950 (st.st_mode & 0111));
951 }
952
condition_test_psi(Condition * c,char ** env)953 static int condition_test_psi(Condition *c, char **env) {
954 _cleanup_free_ char *first = NULL, *second = NULL, *third = NULL, *fourth = NULL, *pressure_path = NULL;
955 const char *p, *value, *pressure_type;
956 loadavg_t *current, limit;
957 ResourcePressure pressure;
958 int r;
959
960 assert(c);
961 assert(c->parameter);
962 assert(IN_SET(c->type, CONDITION_MEMORY_PRESSURE, CONDITION_CPU_PRESSURE, CONDITION_IO_PRESSURE));
963
964 if (!is_pressure_supported()) {
965 log_debug("Pressure Stall Information (PSI) is not supported, skipping.");
966 return 1;
967 }
968
969 pressure_type = c->type == CONDITION_MEMORY_PRESSURE ? "memory" :
970 c->type == CONDITION_CPU_PRESSURE ? "cpu" :
971 "io";
972
973 p = c->parameter;
974 r = extract_many_words(&p, ":", 0, &first, &second, NULL);
975 if (r <= 0)
976 return log_debug_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s: %m", c->parameter);
977 /* If only one parameter is passed, then we look at the global system pressure rather than a specific cgroup. */
978 if (r == 1) {
979 pressure_path = path_join("/proc/pressure", pressure_type);
980 if (!pressure_path)
981 return log_oom_debug();
982
983 value = first;
984 } else {
985 const char *controller = strjoina(pressure_type, ".pressure");
986 _cleanup_free_ char *slice_path = NULL, *root_scope = NULL;
987 CGroupMask mask, required_mask;
988 char *slice, *e;
989
990 required_mask = c->type == CONDITION_MEMORY_PRESSURE ? CGROUP_MASK_MEMORY :
991 c->type == CONDITION_CPU_PRESSURE ? CGROUP_MASK_CPU :
992 CGROUP_MASK_IO;
993
994 slice = strstrip(first);
995 if (!slice)
996 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s: %m", c->parameter);
997
998 r = cg_all_unified();
999 if (r < 0)
1000 return log_debug_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
1001 if (r == 0) {
1002 log_debug("PSI condition check requires the unified cgroups hierarchy, skipping.");
1003 return 1;
1004 }
1005
1006 r = cg_mask_supported(&mask);
1007 if (r < 0)
1008 return log_debug_errno(r, "Failed to get supported cgroup controllers: %m");
1009
1010 if (!FLAGS_SET(mask, required_mask)) {
1011 log_debug("Cgroup %s controller not available, skipping PSI condition check.", pressure_type);
1012 return 1;
1013 }
1014
1015 r = cg_slice_to_path(slice, &slice_path);
1016 if (r < 0)
1017 return log_debug_errno(r, "Cannot determine slice \"%s\" cgroup path: %m", slice);
1018
1019 /* We might be running under the user manager, so get the root path and prefix it accordingly. */
1020 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &root_scope);
1021 if (r < 0)
1022 return log_debug_errno(r, "Failed to get root cgroup path: %m");
1023
1024 /* Drop init.scope, we want the parent. We could get an empty or / path, but that's fine,
1025 * just skip it in that case. */
1026 e = endswith(root_scope, "/" SPECIAL_INIT_SCOPE);
1027 if (e)
1028 *e = 0;
1029 if (!empty_or_root(root_scope)) {
1030 _cleanup_free_ char *slice_joined = NULL;
1031
1032 slice_joined = path_join(root_scope, slice_path);
1033 if (!slice_joined)
1034 return log_oom_debug();
1035
1036 free_and_replace(slice_path, slice_joined);
1037 }
1038
1039 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, slice_path, controller, &pressure_path);
1040 if (r < 0)
1041 return log_debug_errno(r, "Error getting cgroup pressure path from %s: %m", slice_path);
1042
1043 value = second;
1044 }
1045
1046 /* If a value including a specific timespan (in the intervals allowed by the kernel),
1047 * parse it, otherwise we assume just a plain percentage that will be checked if it is
1048 * smaller or equal to the current pressure average over 5 minutes. */
1049 r = extract_many_words(&value, "/", 0, &third, &fourth, NULL);
1050 if (r <= 0)
1051 return log_debug_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s: %m", c->parameter);
1052 if (r == 1)
1053 current = &pressure.avg300;
1054 else {
1055 const char *timespan;
1056
1057 timespan = skip_leading_chars(fourth, NULL);
1058 if (!timespan)
1059 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s: %m", c->parameter);
1060
1061 if (startswith(timespan, "10sec"))
1062 current = &pressure.avg10;
1063 else if (startswith(timespan, "1min"))
1064 current = &pressure.avg60;
1065 else if (startswith(timespan, "5min"))
1066 current = &pressure.avg300;
1067 else
1068 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s: %m", c->parameter);
1069 }
1070
1071 value = strstrip(third);
1072 if (!value)
1073 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s: %m", c->parameter);
1074
1075 r = parse_permyriad(value);
1076 if (r < 0)
1077 return log_debug_errno(r, "Failed to parse permyriad: %s", c->parameter);
1078
1079 r = store_loadavg_fixed_point(r / 100LU, r % 100LU, &limit);
1080 if (r < 0)
1081 return log_debug_errno(r, "Failed to parse loadavg: %s", c->parameter);
1082
1083 r = read_resource_pressure(pressure_path, PRESSURE_TYPE_FULL, &pressure);
1084 if (r == -ENODATA) /* cpu.pressure 'full' was added recently, fall back to 'some'. */
1085 r = read_resource_pressure(pressure_path, PRESSURE_TYPE_SOME, &pressure);
1086 if (r == -ENOENT) {
1087 /* We already checked that /proc/pressure exists, so this means we were given a cgroup
1088 * that doesn't exist or doesn't exist any longer. */
1089 log_debug("\"%s\" not found, skipping PSI check.", pressure_path);
1090 return 1;
1091 }
1092 if (r < 0)
1093 return log_debug_errno(r, "Error parsing pressure from %s: %m", pressure_path);
1094
1095 return *current <= limit;
1096 }
1097
condition_test(Condition * c,char ** env)1098 int condition_test(Condition *c, char **env) {
1099
1100 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c, char **env) = {
1101 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
1102 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
1103 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
1104 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
1105 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
1106 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
1107 [CONDITION_PATH_IS_ENCRYPTED] = condition_test_path_is_encrypted,
1108 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
1109 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
1110 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
1111 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
1112 [CONDITION_KERNEL_VERSION] = condition_test_kernel_version,
1113 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
1114 [CONDITION_SECURITY] = condition_test_security,
1115 [CONDITION_CAPABILITY] = condition_test_capability,
1116 [CONDITION_HOST] = condition_test_host,
1117 [CONDITION_AC_POWER] = condition_test_ac_power,
1118 [CONDITION_ARCHITECTURE] = condition_test_architecture,
1119 [CONDITION_FIRMWARE] = condition_test_firmware,
1120 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
1121 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
1122 [CONDITION_USER] = condition_test_user,
1123 [CONDITION_GROUP] = condition_test_group,
1124 [CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller,
1125 [CONDITION_CPUS] = condition_test_cpus,
1126 [CONDITION_MEMORY] = condition_test_memory,
1127 [CONDITION_ENVIRONMENT] = condition_test_environment,
1128 [CONDITION_CPU_FEATURE] = condition_test_cpufeature,
1129 [CONDITION_OS_RELEASE] = condition_test_osrelease,
1130 [CONDITION_MEMORY_PRESSURE] = condition_test_psi,
1131 [CONDITION_CPU_PRESSURE] = condition_test_psi,
1132 [CONDITION_IO_PRESSURE] = condition_test_psi,
1133 };
1134
1135 int r, b;
1136
1137 assert(c);
1138 assert(c->type >= 0);
1139 assert(c->type < _CONDITION_TYPE_MAX);
1140
1141 r = condition_tests[c->type](c, env);
1142 if (r < 0) {
1143 c->result = CONDITION_ERROR;
1144 return r;
1145 }
1146
1147 b = (r > 0) == !c->negate;
1148 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
1149 return b;
1150 }
1151
condition_test_list(Condition * first,char ** env,condition_to_string_t to_string,condition_test_logger_t logger,void * userdata)1152 bool condition_test_list(
1153 Condition *first,
1154 char **env,
1155 condition_to_string_t to_string,
1156 condition_test_logger_t logger,
1157 void *userdata) {
1158
1159 int triggered = -1;
1160
1161 assert(!!logger == !!to_string);
1162
1163 /* If the condition list is empty, then it is true */
1164 if (!first)
1165 return true;
1166
1167 /* Otherwise, if all of the non-trigger conditions apply and
1168 * if any of the trigger conditions apply (unless there are
1169 * none) we return true */
1170 LIST_FOREACH(conditions, c, first) {
1171 int r;
1172
1173 r = condition_test(c, env);
1174
1175 if (logger) {
1176 if (r < 0)
1177 logger(userdata, LOG_WARNING, r, PROJECT_FILE, __LINE__, __func__,
1178 "Couldn't determine result for %s=%s%s%s, assuming failed: %m",
1179 to_string(c->type),
1180 c->trigger ? "|" : "",
1181 c->negate ? "!" : "",
1182 c->parameter);
1183 else
1184 logger(userdata, LOG_DEBUG, 0, PROJECT_FILE, __LINE__, __func__,
1185 "%s=%s%s%s %s.",
1186 to_string(c->type),
1187 c->trigger ? "|" : "",
1188 c->negate ? "!" : "",
1189 c->parameter,
1190 condition_result_to_string(c->result));
1191 }
1192
1193 if (!c->trigger && r <= 0)
1194 return false;
1195
1196 if (c->trigger && triggered <= 0)
1197 triggered = r > 0;
1198 }
1199
1200 return triggered != 0;
1201 }
1202
condition_dump(Condition * c,FILE * f,const char * prefix,condition_to_string_t to_string)1203 void condition_dump(Condition *c, FILE *f, const char *prefix, condition_to_string_t to_string) {
1204 assert(c);
1205 assert(f);
1206 assert(to_string);
1207
1208 prefix = strempty(prefix);
1209
1210 fprintf(f,
1211 "%s\t%s: %s%s%s %s\n",
1212 prefix,
1213 to_string(c->type),
1214 c->trigger ? "|" : "",
1215 c->negate ? "!" : "",
1216 c->parameter,
1217 condition_result_to_string(c->result));
1218 }
1219
condition_dump_list(Condition * first,FILE * f,const char * prefix,condition_to_string_t to_string)1220 void condition_dump_list(Condition *first, FILE *f, const char *prefix, condition_to_string_t to_string) {
1221 LIST_FOREACH(conditions, c, first)
1222 condition_dump(c, f, prefix, to_string);
1223 }
1224
1225 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
1226 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
1227 [CONDITION_FIRMWARE] = "ConditionFirmware",
1228 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
1229 [CONDITION_HOST] = "ConditionHost",
1230 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
1231 [CONDITION_KERNEL_VERSION] = "ConditionKernelVersion",
1232 [CONDITION_SECURITY] = "ConditionSecurity",
1233 [CONDITION_CAPABILITY] = "ConditionCapability",
1234 [CONDITION_AC_POWER] = "ConditionACPower",
1235 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
1236 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
1237 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
1238 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
1239 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
1240 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
1241 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
1242 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
1243 [CONDITION_PATH_IS_ENCRYPTED] = "ConditionPathIsEncrypted",
1244 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
1245 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
1246 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
1247 [CONDITION_USER] = "ConditionUser",
1248 [CONDITION_GROUP] = "ConditionGroup",
1249 [CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController",
1250 [CONDITION_CPUS] = "ConditionCPUs",
1251 [CONDITION_MEMORY] = "ConditionMemory",
1252 [CONDITION_ENVIRONMENT] = "ConditionEnvironment",
1253 [CONDITION_CPU_FEATURE] = "ConditionCPUFeature",
1254 [CONDITION_OS_RELEASE] = "ConditionOSRelease",
1255 [CONDITION_MEMORY_PRESSURE] = "ConditionMemoryPressure",
1256 [CONDITION_CPU_PRESSURE] = "ConditionCPUPressure",
1257 [CONDITION_IO_PRESSURE] = "ConditionIOPressure",
1258 };
1259
1260 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
1261
1262 static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
1263 [CONDITION_ARCHITECTURE] = "AssertArchitecture",
1264 [CONDITION_FIRMWARE] = "AssertFirmware",
1265 [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
1266 [CONDITION_HOST] = "AssertHost",
1267 [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
1268 [CONDITION_KERNEL_VERSION] = "AssertKernelVersion",
1269 [CONDITION_SECURITY] = "AssertSecurity",
1270 [CONDITION_CAPABILITY] = "AssertCapability",
1271 [CONDITION_AC_POWER] = "AssertACPower",
1272 [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
1273 [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
1274 [CONDITION_PATH_EXISTS] = "AssertPathExists",
1275 [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
1276 [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
1277 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
1278 [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
1279 [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
1280 [CONDITION_PATH_IS_ENCRYPTED] = "AssertPathIsEncrypted",
1281 [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
1282 [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
1283 [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
1284 [CONDITION_USER] = "AssertUser",
1285 [CONDITION_GROUP] = "AssertGroup",
1286 [CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController",
1287 [CONDITION_CPUS] = "AssertCPUs",
1288 [CONDITION_MEMORY] = "AssertMemory",
1289 [CONDITION_ENVIRONMENT] = "AssertEnvironment",
1290 [CONDITION_CPU_FEATURE] = "AssertCPUFeature",
1291 [CONDITION_OS_RELEASE] = "AssertOSRelease",
1292 [CONDITION_MEMORY_PRESSURE] = "AssertMemoryPressure",
1293 [CONDITION_CPU_PRESSURE] = "AssertCPUPressure",
1294 [CONDITION_IO_PRESSURE] = "AssertIOPressure",
1295 };
1296
1297 DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
1298
1299 static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
1300 [CONDITION_UNTESTED] = "untested",
1301 [CONDITION_SUCCEEDED] = "succeeded",
1302 [CONDITION_FAILED] = "failed",
1303 [CONDITION_ERROR] = "error",
1304 };
1305
1306 DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);
1307