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