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(&parameter, &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", &timestamp_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, &timestamp);
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