• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 16867927458

09 Aug 2025 02:25PM UTC coverage: 72.249% (+0.001%) from 72.248%
16867927458

push

github

yuwata
po: Translated using Weblate (Khmer (Central))

Currently translated at 16.2% (43 of 264 strings)

Co-authored-by: kanitha chim <kchim@redhat.com>
Translate-URL: https://translate.fedoraproject.org/projects/systemd/main/km/
Translation: systemd/main

302512 of 418708 relevant lines covered (72.25%)

643355.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.62
/src/shared/condition.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <fnmatch.h>
5
#include <gnu/libc-version.h>
6
#include <sys/stat.h>
7
#include <sys/utsname.h>
8
#include <time.h>
9
#include <unistd.h>
10

11
#include "sd-id128.h"
12

13
#include "alloc-util.h"
14
#include "apparmor-util.h"
15
#include "architecture.h"
16
#include "battery-util.h"
17
#include "bitfield.h"
18
#include "blockdev-util.h"
19
#include "capability-list.h"
20
#include "capability-util.h"
21
#include "cgroup-util.h"
22
#include "compare-operator.h"
23
#include "condition.h"
24
#include "confidential-virt.h"
25
#include "cpu-set-util.h"
26
#include "creds-util.h"
27
#include "efi-loader.h"
28
#include "efivars.h"
29
#include "env-file.h"
30
#include "env-util.h"
31
#include "extract-word.h"
32
#include "fd-util.h"
33
#include "fileio.h"
34
#include "fs-util.h"
35
#include "glob-util.h"
36
#include "hostname-setup.h"
37
#include "id128-util.h"
38
#include "ima-util.h"
39
#include "initrd-util.h"
40
#include "libaudit-util.h"
41
#include "limits-util.h"
42
#include "list.h"
43
#include "log.h"
44
#include "mountpoint-util.h"
45
#include "nulstr-util.h"
46
#include "os-util.h"
47
#include "parse-util.h"
48
#include "path-util.h"
49
#include "percent-util.h"
50
#include "pidref.h"
51
#include "proc-cmdline.h"
52
#include "process-util.h"
53
#include "psi-util.h"
54
#include "selinux-util.h"
55
#include "smack-util.h"
56
#include "special.h"
57
#include "stat-util.h"
58
#include "string-table.h"
59
#include "string-util.h"
60
#include "strv.h"
61
#include "time-util.h"
62
#include "tomoyo-util.h"
63
#include "tpm2-util.h"
64
#include "uid-classification.h"
65
#include "user-util.h"
66
#include "virt.h"
67

68
Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
21,050✔
69
        Condition *c;
21,050✔
70

71
        assert(type >= 0);
21,050✔
72
        assert(type < _CONDITION_TYPE_MAX);
21,050✔
73
        assert(parameter);
21,050✔
74

75
        c = new(Condition, 1);
21,050✔
76
        if (!c)
21,050✔
77
                return NULL;
78

79
        *c = (Condition) {
21,050✔
80
                .type = type,
81
                .trigger = trigger,
82
                .negate = negate,
83
        };
84

85
        if (parameter) {
21,050✔
86
                c->parameter = strdup(parameter);
21,050✔
87
                if (!c->parameter)
21,050✔
88
                        return mfree(c);
×
89
        }
90

91
        return c;
92
}
93

94
Condition* condition_free(Condition *c) {
21,050✔
95
        assert(c);
21,050✔
96

97
        free(c->parameter);
21,050✔
98
        return mfree(c);
21,050✔
99
}
100

101
Condition* condition_free_list_type(Condition *head, ConditionType type) {
171,495✔
102
        LIST_FOREACH(conditions, c, head)
192,259✔
103
                if (type < 0 || c->type == type) {
20,764✔
104
                        LIST_REMOVE(conditions, head, c);
20,764✔
105
                        condition_free(c);
20,764✔
106
                }
107

108
        assert(type >= 0 || !head);
171,495✔
109
        return head;
171,495✔
110
}
111

112
static int condition_test_kernel_command_line(Condition *c, char **env) {
158✔
113
        _cleanup_strv_free_ char **args = NULL;
158✔
114
        int r;
158✔
115

116
        assert(c);
158✔
117
        assert(c->parameter);
158✔
118
        assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
158✔
119

120
        r = proc_cmdline_strv(&args);
158✔
121
        if (r < 0)
158✔
122
                return r;
123

124
        bool equal = strchr(c->parameter, '=');
158✔
125

126
        STRV_FOREACH(word, args) {
5,692✔
127
                bool found;
5,534✔
128

129
                if (equal)
5,534✔
130
                        found = streq(*word, c->parameter);
924✔
131
                else {
132
                        const char *f;
4,610✔
133

134
                        f = startswith(*word, c->parameter);
4,610✔
135
                        found = f && IN_SET(*f, 0, '=');
4,610✔
136
                }
137

138
                if (found)
924✔
139
                        return true;
140
        }
141

142
        return false;
143
}
144

145
static int condition_test_credential(Condition *c, char **env) {
39✔
146
        int r;
39✔
147

148
        assert(c);
39✔
149
        assert(c->parameter);
39✔
150
        assert(c->type == CONDITION_CREDENTIAL);
39✔
151

152
        /* For now we'll do a very simple existence check and are happy with either a regular or an encrypted
153
         * credential. Given that we check the syntax of the argument we have the option to later maybe allow
154
         * contents checks too without breaking compatibility, but for now let's be minimalistic. */
155

156
        if (!credential_name_valid(c->parameter)) /* credentials with invalid names do not exist */
39✔
157
                return false;
158

159
        int (*gd)(const char **ret);
38✔
160
        FOREACH_ARGUMENT(gd, get_credentials_dir, get_encrypted_credentials_dir) {
111✔
161
                _cleanup_free_ char *j = NULL;
39✔
162
                const char *cd;
75✔
163

164
                r = gd(&cd);
75✔
165
                if (r == -ENXIO) /* no env var set */
75✔
166
                        continue;
36✔
167
                if (r < 0)
39✔
168
                        return r;
169

170
                j = path_join(cd, c->parameter);
39✔
171
                if (!j)
39✔
172
                        return -ENOMEM;
173

174
                r = access_nofollow(j, F_OK);
39✔
175
                if (r >= 0)
39✔
176
                        return true; /* yay! */
177
                if (r != -ENOENT)
37✔
178
                        return r;
179

180
                /* not found in this dir */
181
        }
182

183
        return false;
36✔
184
}
185

186
static int condition_test_version_cmp(const char *condition, const char *ver) {
55✔
187
        CompareOperator operator;
55✔
188
        bool first = true;
55✔
189

190
        assert(condition);
55✔
191
        assert(ver);
55✔
192

193
        for (const char *p = condition;;) {
55✔
194
                _cleanup_free_ char *word = NULL;
59✔
195
                const char *s;
86✔
196
                int r;
86✔
197

198
                r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
86✔
199
                if (r < 0)
86✔
200
                        return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
×
201
                if (r == 0)
86✔
202
                        break;
203

204
                s = strstrip(word);
59✔
205
                operator = parse_compare_operator(&s, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
59✔
206
                if (operator < 0) /* No prefix? Then treat as glob string */
59✔
207
                        operator = COMPARE_FNMATCH_EQUAL;
6✔
208

209
                s += strspn(s, WHITESPACE);
59✔
210
                if (isempty(s)) {
59✔
211
                        if (first) {
38✔
212
                                /* For backwards compatibility, allow whitespace between the operator and
213
                                 * value, without quoting, but only in the first expression. */
214
                                word = mfree(word);
36✔
215
                                r = extract_first_word(&p, &word, NULL, 0);
36✔
216
                                if (r < 0)
36✔
217
                                        return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
×
218
                                if (r == 0)
36✔
219
                                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
2✔
220
                                s = word;
34✔
221
                        } else
222
                                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
2✔
223
                }
224

225
                r = version_or_fnmatch_compare(operator, ver, s);
55✔
226
                if (r < 0)
55✔
227
                        return r;
228
                if (!r)
55✔
229
                        return false;
230

231
                first = false;
31✔
232
        }
233

234
        return true;
27✔
235
}
236

237
static int condition_test_version(Condition *c, char **env) {
57✔
238
        int r;
57✔
239

240
        assert(c);
57✔
241
        assert(c->type == CONDITION_VERSION);
57✔
242

243
        /* An empty condition is considered true. */
244
        if (isempty(c->parameter))
57✔
245
                return true;
57✔
246

247
        const char *p = c->parameter;
55✔
248
        _cleanup_free_ char *word = NULL;
55✔
249
        r = extract_first_word(&p, &word, COMPARE_OPERATOR_WITH_FNMATCH_CHARS WHITESPACE,
55✔
250
                               EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_SEPARATORS);
251
        if (r < 0)
55✔
252
                return log_debug_errno(r, "Failed to parse compare predicate \"%s\": %m", p);
×
253
        if (r == 0)
55✔
254
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing right operand in condition: %s", c->parameter);
×
255

256
        if (streq(word, "systemd"))
55✔
257
                return condition_test_version_cmp(p, STRINGIFY(PROJECT_VERSION));
21✔
258

259
        if (streq(word, "glibc"))
34✔
260
                return condition_test_version_cmp(p, gnu_get_libc_version());
6✔
261

262
        /* if no predicate has been set, default to "kernel" and use the whole parameter as condition */
263
        if (!streq(word, "kernel"))
28✔
264
                p = c->parameter;
28✔
265

266
        struct utsname u;
28✔
267
        assert_se(uname(&u) >= 0);
28✔
268
        return condition_test_version_cmp(p, u.release);
28✔
269
}
270

271
static int condition_test_osrelease(Condition *c, char **env) {
17✔
272
        int r;
17✔
273

274
        assert(c);
17✔
275
        assert(c->type == CONDITION_OS_RELEASE);
17✔
276

277
        for (const char *parameter = ASSERT_PTR(c->parameter);;) {
17✔
278
                _cleanup_free_ char *key = NULL, *condition = NULL, *actual_value = NULL;
16✔
279
                CompareOperator operator;
21✔
280
                const char *word;
21✔
281

282
                r = extract_first_word(&parameter, &condition, NULL, EXTRACT_UNQUOTE);
21✔
283
                if (r < 0)
21✔
284
                        return log_debug_errno(r, "Failed to parse parameter: %m");
×
285
                if (r == 0)
21✔
286
                        break;
287

288
                /* parse_compare_operator() needs the string to start with the comparators */
289
                word = condition;
16✔
290
                r = extract_first_word(&word, &key, COMPARE_OPERATOR_WITH_FNMATCH_CHARS, EXTRACT_RETAIN_SEPARATORS);
16✔
291
                if (r < 0)
16✔
292
                        return log_debug_errno(r, "Failed to parse parameter: %m");
×
293
                /* The os-release spec mandates env-var-like key names */
294
                if (r == 0 || isempty(word) || !env_name_is_valid(key))
27✔
295
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
5✔
296
                                        "Failed to parse parameter, key/value format expected.");
297

298
                /* Do not allow whitespace after the separator, as that's not a valid os-release format */
299
                operator = parse_compare_operator(&word, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
11✔
300
                if (operator < 0 || isempty(word) || strchr(WHITESPACE, *word) != NULL)
20✔
301
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
2✔
302
                                        "Failed to parse parameter, key/value format expected.");
303

304
                r = parse_os_release(NULL, key, &actual_value);
9✔
305
                if (r < 0)
9✔
306
                        return log_debug_errno(r, "Failed to parse os-release: %m");
×
307

308
                /* If not found, use "". This means that missing and empty assignments
309
                 * in the file have the same result. */
310
                r = version_or_fnmatch_compare(operator, strempty(actual_value), word);
14✔
311
                if (r < 0)
9✔
312
                        return r;
313
                if (!r)
9✔
314
                        return false;
315
        }
316

317
        return true;
5✔
318
}
319

320
static int condition_test_memory(Condition *c, char **env) {
36✔
321
        CompareOperator operator;
36✔
322
        uint64_t m, k;
36✔
323
        const char *p;
36✔
324
        int r;
36✔
325

326
        assert(c);
36✔
327
        assert(c->parameter);
36✔
328
        assert(c->type == CONDITION_MEMORY);
36✔
329

330
        m = physical_memory();
36✔
331

332
        p = c->parameter;
36✔
333
        operator = parse_compare_operator(&p, 0);
36✔
334
        if (operator < 0)
36✔
335
                operator = COMPARE_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
×
336

337
        r = parse_size(p, 1024, &k);
36✔
338
        if (r < 0)
36✔
339
                return log_debug_errno(r, "Failed to parse size '%s': %m", p);
×
340

341
        return test_order(CMP(m, k), operator);
36✔
342
}
343

344
static int condition_test_cpus(Condition *c, char **env) {
18✔
345
        CompareOperator operator;
18✔
346
        const char *p;
18✔
347
        unsigned k;
18✔
348
        int r, n;
18✔
349

350
        assert(c);
18✔
351
        assert(c->parameter);
18✔
352
        assert(c->type == CONDITION_CPUS);
18✔
353

354
        n = cpus_in_affinity_mask();
18✔
355
        if (n < 0)
18✔
356
                return log_debug_errno(n, "Failed to determine CPUs in affinity mask: %m");
×
357

358
        p = c->parameter;
18✔
359
        operator = parse_compare_operator(&p, 0);
18✔
360
        if (operator < 0)
18✔
361
                operator = COMPARE_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */
×
362

363
        r = safe_atou(p, &k);
18✔
364
        if (r < 0)
18✔
365
                return log_debug_errno(r, "Failed to parse number of CPUs: %m");
×
366

367
        return test_order(CMP((unsigned) n, k), operator);
18✔
368
}
369

370
static int condition_test_user(Condition *c, char **env) {
7✔
371
        uid_t id;
7✔
372
        int r;
7✔
373

374
        assert(c);
7✔
375
        assert(c->parameter);
7✔
376
        assert(c->type == CONDITION_USER);
7✔
377

378
        /* Do the quick&easy comparisons first, and only parse the UID later. */
379
        if (streq(c->parameter, "root"))
7✔
380
                return getuid() == 0 || geteuid() == 0;
7✔
381
        if (streq(c->parameter, NOBODY_USER_NAME))
6✔
382
                return getuid() == UID_NOBODY || geteuid() == UID_NOBODY;
1✔
383
        if (streq(c->parameter, "@system"))
5✔
384
                return uid_is_system(getuid()) || uid_is_system(geteuid());
1✔
385

386
        r = parse_uid(c->parameter, &id);
4✔
387
        if (r >= 0)
4✔
388
                return id == getuid() || id == geteuid();
2✔
389

390
        if (getpid_cached() == 1)  /* We already checked for "root" above, and we know that
2✔
391
                                    * PID 1 is running as root, hence we know it cannot match. */
392
                return false;
393

394
        /* getusername_malloc() may do an nss lookup, which is not allowed in PID 1. */
395
        _cleanup_free_ char *username = getusername_malloc();
4✔
396
        if (!username)
2✔
397
                return -ENOMEM;
398

399
        if (streq(username, c->parameter))
2✔
400
                return 1;
401

402
        const char *u = c->parameter;
2✔
403
        r = get_user_creds(&u, &id, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
2✔
404
        if (r < 0)
2✔
405
                return 0;
406

407
        return id == getuid() || id == geteuid();
×
408
}
409

410
static int condition_test_control_group_controller(Condition *c, char **env) {
32✔
411
        CGroupMask system_mask, wanted_mask;
32✔
412
        int r;
32✔
413

414
        assert(c);
32✔
415
        assert(c->parameter);
32✔
416
        assert(c->type == CONDITION_CONTROL_GROUP_CONTROLLER);
32✔
417

418
        if (streq(c->parameter, "v2"))
32✔
419
                return true;
32✔
420
        if (streq(c->parameter, "v1"))
31✔
421
                return false;
422

423
        r = cg_mask_supported(&system_mask);
30✔
424
        if (r < 0)
30✔
425
                return log_debug_errno(r, "Failed to determine supported controllers: %m");
×
426

427
        r = cg_mask_from_string(c->parameter, &wanted_mask);
30✔
428
        if (r < 0 || wanted_mask <= 0) {
30✔
429
                /* This won't catch the case that we have an unknown controller
430
                 * mixed in with valid ones -- these are only assessed on the
431
                 * validity of the valid controllers found. */
432
                log_debug("Failed to parse cgroup string: %s", c->parameter);
2✔
433
                return true;
2✔
434
        }
435

436
        return FLAGS_SET(system_mask, wanted_mask);
28✔
437
}
438

439
static int condition_test_group(Condition *c, char **env) {
4✔
440
        gid_t id;
4✔
441
        int r;
4✔
442

443
        assert(c);
4✔
444
        assert(c->parameter);
4✔
445
        assert(c->type == CONDITION_GROUP);
4✔
446

447
        r = parse_gid(c->parameter, &id);
4✔
448
        if (r >= 0)
4✔
449
                return in_gid(id);
2✔
450

451
        /* Avoid any NSS lookups if we are PID1 */
452
        if (getpid_cached() == 1)
2✔
453
                return streq(c->parameter, "root");
×
454

455
        return in_group(c->parameter) > 0;
2✔
456
}
457

458
static int condition_test_virtualization(Condition *c, char **env) {
1,403✔
459
        Virtualization v;
1,403✔
460
        int b;
1,403✔
461

462
        assert(c);
1,403✔
463
        assert(c->parameter);
1,403✔
464
        assert(c->type == CONDITION_VIRTUALIZATION);
1,403✔
465

466
        if (streq(c->parameter, "private-users"))
1,403✔
467
                return running_in_userns();
63✔
468

469
        v = detect_virtualization();
1,340✔
470
        if (v < 0)
1,340✔
471
                return v;
472

473
        /* First, compare with yes/no */
474
        b = parse_boolean(c->parameter);
1,340✔
475
        if (b >= 0)
1,340✔
476
                return b == (v != VIRTUALIZATION_NONE);
10✔
477

478
        /* Then, compare categorization */
479
        if (streq(c->parameter, "vm"))
1,330✔
480
                return VIRTUALIZATION_IS_VM(v);
1✔
481

482
        if (streq(c->parameter, "container"))
1,329✔
483
                return VIRTUALIZATION_IS_CONTAINER(v);
1,302✔
484

485
        /* Finally compare id */
486
        return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
27✔
487
}
488

489
static int condition_test_architecture(Condition *c, char **env) {
4✔
490
        Architecture a, b;
4✔
491

492
        assert(c);
4✔
493
        assert(c->parameter);
4✔
494
        assert(c->type == CONDITION_ARCHITECTURE);
4✔
495

496
        a = uname_architecture();
4✔
497
        if (a < 0)
4✔
498
                return a;
499

500
        if (streq(c->parameter, "native"))
4✔
501
                b = native_architecture();
502
        else {
503
                b = architecture_from_string(c->parameter);
4✔
504
                if (b < 0) /* unknown architecture? Then it's definitely not ours */
4✔
505
                        return false;
506
        }
507

508
        return a == b;
3✔
509
}
510

511
#define DTCOMPAT_FILE "/proc/device-tree/compatible"
512
static int condition_test_firmware_devicetree_compatible(const char *dtcarg) {
1✔
513
        int r;
1✔
514
        _cleanup_free_ char *dtcompat = NULL;
1✔
515
        _cleanup_strv_free_ char **dtcompatlist = NULL;
1✔
516
        size_t size;
1✔
517

518
        r = read_full_virtual_file(DTCOMPAT_FILE, &dtcompat, &size);
1✔
519
        if (r < 0) {
1✔
520
                /* if the path doesn't exist it is incompatible */
521
                if (r != -ENOENT)
1✔
522
                        log_debug_errno(r, "Failed to open '%s', assuming machine is incompatible: %m", DTCOMPAT_FILE);
×
523
                return false;
1✔
524
        }
525

526
        /* Not sure this can happen, but play safe. */
527
        if (size == 0) {
×
528
                log_debug("%s has zero length, assuming machine is incompatible", DTCOMPAT_FILE);
×
529
                return false;
×
530
        }
531

532
         /* /proc/device-tree/compatible consists of one or more strings, each ending in '\0'.
533
          * So the last character in dtcompat must be a '\0'. */
534
        if (dtcompat[size - 1] != '\0') {
×
535
                log_debug("%s is in an unknown format, assuming machine is incompatible", DTCOMPAT_FILE);
×
536
                return false;
×
537
        }
538

539
        dtcompatlist = strv_parse_nulstr(dtcompat, size);
×
540
        if (!dtcompatlist)
×
541
                return -ENOMEM;
542

543
        return strv_contains(dtcompatlist, dtcarg);
×
544
}
545

546
static int condition_test_firmware_smbios_field(const char *expression) {
17✔
547
        _cleanup_free_ char *field = NULL, *expected_value = NULL, *actual_value = NULL;
17✔
548
        CompareOperator operator;
17✔
549
        int r;
17✔
550

551
        assert(expression);
17✔
552

553
        /* Parse SMBIOS field */
554
        r = extract_first_word(&expression, &field, COMPARE_OPERATOR_WITH_FNMATCH_CHARS, EXTRACT_RETAIN_SEPARATORS);
17✔
555
        if (r < 0)
17✔
556
                return r;
557
        if (r == 0 || isempty(expression))
17✔
558
                return -EINVAL;
559

560
        /* Remove trailing spaces from SMBIOS field */
561
        delete_trailing_chars(field, WHITESPACE);
15✔
562

563
        /* Parse operator */
564
        operator = parse_compare_operator(&expression, COMPARE_ALLOW_FNMATCH|COMPARE_EQUAL_BY_STRING);
15✔
565
        if (operator < 0)
15✔
566
                return operator;
567

568
        /* Parse expected value */
569
        r = extract_first_word(&expression, &expected_value, NULL, EXTRACT_UNQUOTE);
15✔
570
        if (r < 0)
15✔
571
                return r;
572
        if (r == 0 || !isempty(expression))
30✔
573
                return -EINVAL;
574

575
        /* Read actual value from sysfs */
576
        if (!filename_is_valid(field))
12✔
577
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid SMBIOS field name.");
×
578

579
        const char *p = strjoina("/sys/class/dmi/id/", field);
60✔
580
        r = read_virtual_file(p, SIZE_MAX, &actual_value, NULL);
12✔
581
        if (r < 0) {
12✔
582
                log_debug_errno(r, "Failed to read %s: %m", p);
1✔
583
                if (r == -ENOENT)
1✔
584
                        return false;
585
                return r;
×
586
        }
587

588
        /* Remove trailing newline */
589
        delete_trailing_chars(actual_value, WHITESPACE);
11✔
590

591
        /* Finally compare actual and expected value */
592
        return version_or_fnmatch_compare(operator, actual_value, expected_value);
11✔
593
}
594

595
static int condition_test_firmware(Condition *c, char **env) {
22✔
596
        sd_char *arg;
22✔
597
        int r;
22✔
598

599
        assert(c);
22✔
600
        assert(c->parameter);
22✔
601
        assert(c->type == CONDITION_FIRMWARE);
22✔
602

603
        if (streq(c->parameter, "device-tree")) {
22✔
604
                if (access("/sys/firmware/devicetree/", F_OK) < 0) {
1✔
605
                        if (errno != ENOENT)
1✔
606
                                log_debug_errno(errno, "Unexpected error when checking for /sys/firmware/devicetree/: %m");
×
607
                        return false;
1✔
608
                } else
609
                        return true;
610
        } else if ((arg = startswith(c->parameter, "device-tree-compatible("))) {
21✔
611
                _cleanup_free_ char *dtc_arg = NULL;
1✔
612
                char *end;
1✔
613

614
                end = strrchr(arg, ')');
1✔
615
                if (!end || *(end + 1) != '\0') {
1✔
616
                        log_debug("Malformed ConditionFirmware=%s", c->parameter);
×
617
                        return false;
×
618
                }
619

620
                dtc_arg = strndup(arg, end - arg);
1✔
621
                if (!dtc_arg)
1✔
622
                        return -ENOMEM;
623

624
                return condition_test_firmware_devicetree_compatible(dtc_arg);
1✔
625
        } else if (streq(c->parameter, "uefi"))
20✔
626
                return is_efi_boot();
1✔
627
        else if ((arg = startswith(c->parameter, "smbios-field("))) {
19✔
628
                _cleanup_free_ char *smbios_arg = NULL;
18✔
629
                char *end;
18✔
630

631
                end = strrchr(arg, ')');
18✔
632
                if (!end || *(end + 1) != '\0')
18✔
633
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed ConditionFirmware=%s.", c->parameter);
1✔
634

635
                smbios_arg = strndup(arg, end - arg);
17✔
636
                if (!smbios_arg)
17✔
637
                        return log_oom_debug();
×
638

639
                r = condition_test_firmware_smbios_field(smbios_arg);
17✔
640
                if (r < 0)
17✔
641
                        return log_debug_errno(r, "Malformed ConditionFirmware=%s: %m", c->parameter);
5✔
642
                return r;
643
        } else {
644
                log_debug("Unsupported Firmware condition \"%s\"", c->parameter);
1✔
645
                return false;
1✔
646
        }
647
}
648

649
static int condition_test_host(Condition *c, char **env) {
4✔
650
        _cleanup_free_ char *h = NULL;
4✔
651
        int r;
4✔
652

653
        assert(c);
4✔
654
        assert(c->parameter);
4✔
655
        assert(c->type == CONDITION_HOST);
4✔
656

657
        sd_id128_t x;
4✔
658
        if (sd_id128_from_string(c->parameter, &x) >= 0) {
4✔
659
                static const struct {
660
                        const char *name;
661
                        int (*get_id)(sd_id128_t *ret);
662
                } table[] = {
663
                        { "machine ID",   sd_id128_get_machine },
664
                        { "boot ID",      sd_id128_get_boot    },
665
                        { "product UUID", id128_get_product    },
666
                };
667

668
                /* If this is a UUID, check if this matches the machine ID, boot ID or product UUID */
669
                FOREACH_ELEMENT(i, table) {
2✔
670
                        sd_id128_t y;
2✔
671

672
                        r = i->get_id(&y);
2✔
673
                        if (r < 0)
2✔
674
                                log_debug_errno(r, "Failed to get %s, ignoring: %m", i->name);
×
675
                        else if (sd_id128_equal(x, y))
2✔
676
                                return true;
2✔
677
                }
678

679
                /* Fall through, also allow setups where people set hostnames to UUIDs. Kinda weird, but no
680
                 * reason not to allow that */
681
        }
682

683
        h = gethostname_malloc();
2✔
684
        if (!h)
2✔
685
                return -ENOMEM;
686

687
        r = fnmatch(c->parameter, h, FNM_CASEFOLD);
2✔
688
        if (r == FNM_NOMATCH)
2✔
689
                return false;
690
        if (r != 0)
1✔
691
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "fnmatch() failed.");
×
692

693
        return true;
694
}
695

696
static int condition_test_ac_power(Condition *c, char **env) {
4✔
697
        int r;
4✔
698

699
        assert(c);
4✔
700
        assert(c->parameter);
4✔
701
        assert(c->type == CONDITION_AC_POWER);
4✔
702

703
        r = parse_boolean(c->parameter);
4✔
704
        if (r < 0)
4✔
705
                return r;
706

707
        return (on_ac_power() != 0) == !!r;
4✔
708
}
709

710
static int has_tpm2(void) {
×
711
        /* Checks whether the kernel has the TPM subsystem enabled and the firmware reports support. Note
712
         * we don't check for actual TPM devices, since we might not have loaded the driver for it yet, i.e.
713
         * during early boot where we very likely want to use this condition check).
714
         *
715
         * Note that we don't check if we ourselves are built with TPM2 support here! */
716

717
        return FLAGS_SET(tpm2_support_full(TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_FIRMWARE), TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_FIRMWARE);
×
718
}
719

720
static int condition_test_security(Condition *c, char **env) {
273✔
721
        assert(c);
273✔
722
        assert(c->parameter);
273✔
723
        assert(c->type == CONDITION_SECURITY);
273✔
724

725
        if (streq(c->parameter, "selinux"))
273✔
726
                return mac_selinux_use();
1✔
727
        if (streq(c->parameter, "smack"))
272✔
728
                return mac_smack_use();
1✔
729
        if (streq(c->parameter, "apparmor"))
271✔
730
                return mac_apparmor_use();
1✔
731
        if (streq(c->parameter, "audit"))
270✔
732
                return use_audit();
14✔
733
        if (streq(c->parameter, "ima"))
256✔
734
                return use_ima();
1✔
735
        if (streq(c->parameter, "tomoyo"))
255✔
736
                return mac_tomoyo_use();
1✔
737
        if (streq(c->parameter, "uefi-secureboot"))
254✔
738
                return is_efi_secure_boot();
1✔
739
        if (streq(c->parameter, "tpm2"))
253✔
740
                return has_tpm2();
×
741
        if (streq(c->parameter, "cvm"))
253✔
742
                return detect_confidential_virtualization() > 0;
1✔
743
        if (streq(c->parameter, "measured-uki"))
252✔
744
                return efi_measured_uki(LOG_DEBUG);
251✔
745

746
        return false;
747
}
748

749
static int condition_test_capability(Condition *c, char **env) {
625✔
750
        int r;
625✔
751

752
        assert(c);
625✔
753
        assert(c->parameter);
625✔
754
        assert(c->type == CONDITION_CAPABILITY);
625✔
755

756
        /* If it's an invalid capability, we don't have it */
757
        int value = capability_from_name(c->parameter);
625✔
758
        if (value < 0)
625✔
759
                return -EINVAL;
625✔
760

761
        CapabilityQuintet q;
625✔
762
        r = pidref_get_capability(&PIDREF_MAKE_FROM_PID(getpid_cached()), &q);
625✔
763
        if (r < 0)
625✔
764
                return r;
765

766
        return BIT_SET(q.bounding, value);
625✔
767
}
768

769
static int condition_test_needs_update(Condition *c, char **env) {
204✔
770
        struct stat usr, other;
204✔
771
        const char *p;
204✔
772
        bool b;
204✔
773
        int r;
204✔
774

775
        assert(c);
204✔
776
        assert(c->parameter);
204✔
777
        assert(c->type == CONDITION_NEEDS_UPDATE);
204✔
778

779
        r = proc_cmdline_get_bool("systemd.condition_needs_update", /* flags = */ 0, &b);
204✔
780
        if (r < 0)
204✔
781
                log_debug_errno(r, "Failed to parse systemd.condition_needs_update= kernel command line argument, ignoring: %m");
×
782
        if (r > 0)
204✔
783
                return b;
204✔
784

785
        if (in_initrd()) {
204✔
786
                log_debug("We are in an initrd, not doing any updates.");
60✔
787
                return false;
60✔
788
        }
789

790
        if (!path_is_absolute(c->parameter)) {
144✔
791
                log_debug("Specified condition parameter '%s' is not absolute, assuming an update is needed.", c->parameter);
×
792
                return true;
×
793
        }
794

795
        /* If the file system is read-only we shouldn't suggest an update */
796
        r = path_is_read_only_fs(c->parameter);
144✔
797
        if (r < 0)
144✔
798
                log_debug_errno(r, "Failed to determine if '%s' is read-only, ignoring: %m", c->parameter);
×
799
        if (r > 0)
144✔
800
                return false;
801

802
        /* Any other failure means we should allow the condition to be true, so that we rather invoke too
803
         * many update tools than too few. */
804

805
        p = strjoina(c->parameter, "/.updated");
720✔
806
        if (lstat(p, &other) < 0) {
144✔
807
                if (errno != ENOENT)
×
808
                        log_debug_errno(errno, "Failed to stat() '%s', assuming an update is needed: %m", p);
×
809
                return true;
×
810
        }
811

812
        if (lstat("/usr/", &usr) < 0) {
144✔
813
                log_debug_errno(errno, "Failed to stat() /usr/, assuming an update is needed: %m");
×
814
                return true;
×
815
        }
816

817
        /*
818
         * First, compare seconds as they are always accurate...
819
         */
820
        if (usr.st_mtim.tv_sec != other.st_mtim.tv_sec)
144✔
821
                return usr.st_mtim.tv_sec > other.st_mtim.tv_sec;
×
822

823
        /*
824
         * ...then compare nanoseconds.
825
         *
826
         * A false positive is only possible when /usr's nanoseconds > 0
827
         * (otherwise /usr cannot be strictly newer than the target file)
828
         * AND the target file's nanoseconds == 0
829
         * (otherwise the filesystem supports nsec timestamps, see stat(2)).
830
         */
831
        if (usr.st_mtim.tv_nsec == 0 || other.st_mtim.tv_nsec > 0)
144✔
832
                return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec;
144✔
833

834
        _cleanup_free_ char *timestamp_str = NULL;
×
835
        r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", &timestamp_str);
×
836
        if (r < 0) {
×
837
                log_debug_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
×
838
                return true;
×
839
        }
840
        if (isempty(timestamp_str)) {
×
841
                log_debug("No data in timestamp file '%s', using mtime.", p);
×
842
                return true;
×
843
        }
844

845
        uint64_t timestamp;
×
846
        r = safe_atou64(timestamp_str, &timestamp);
×
847
        if (r < 0) {
×
848
                log_debug_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p);
×
849
                return true;
×
850
        }
851

852
        return timespec_load_nsec(&usr.st_mtim) > timestamp;
×
853
}
854

855
static bool in_first_boot(void) {
68✔
856
        static int first_boot = -1;
68✔
857
        int r;
68✔
858

859
        if (first_boot >= 0)
68✔
860
                return first_boot;
×
861

862
        const char *e = secure_getenv("SYSTEMD_FIRST_BOOT");
68✔
863
        if (e) {
68✔
864
                r = parse_boolean(e);
×
865
                if (r < 0)
×
866
                        log_debug_errno(r, "Failed to parse $SYSTEMD_FIRST_BOOT, ignoring: %m");
×
867
                else
868
                        return (first_boot = r);
×
869
        }
870

871
        r = RET_NERRNO(access("/run/systemd/first-boot", F_OK));
68✔
872
        if (r < 0 && r != -ENOENT)
68✔
873
                log_debug_errno(r, "Failed to check if /run/systemd/first-boot exists, assuming no: %m");
×
874
        return r >= 0;
68✔
875
}
876

877
static int condition_test_first_boot(Condition *c, char **env) {
68✔
878
        int r;
68✔
879

880
        assert(c);
68✔
881
        assert(c->parameter);
68✔
882
        assert(c->type == CONDITION_FIRST_BOOT);
68✔
883

884
        // TODO: Parse c->parameter immediately when reading the config.
885
        //       Apply negation when parsing too.
886

887
        r = parse_boolean(c->parameter);
68✔
888
        if (r < 0)
68✔
889
                return r;
890

891
        return in_first_boot() == r;
68✔
892
}
893

894
static int condition_test_environment(Condition *c, char **env) {
205✔
895
        bool equal;
205✔
896

897
        assert(c);
205✔
898
        assert(c->parameter);
205✔
899
        assert(c->type == CONDITION_ENVIRONMENT);
205✔
900

901
        equal = strchr(c->parameter, '=');
205✔
902

903
        STRV_FOREACH(i, env) {
2,316✔
904
                bool found;
2,127✔
905

906
                if (equal)
2,127✔
907
                        found = streq(c->parameter, *i);
55✔
908
                else {
909
                        const char *f;
2,072✔
910

911
                        f = startswith(*i, c->parameter);
2,072✔
912
                        found = f && IN_SET(*f, 0, '=');
2,072✔
913
                }
914

915
                if (found)
55✔
916
                        return true;
917
        }
918

919
        return false;
920
}
921

922
static int condition_test_path_exists(Condition *c, char **env) {
1,050✔
923
        assert(c);
1,050✔
924
        assert(c->parameter);
1,050✔
925
        assert(c->type == CONDITION_PATH_EXISTS);
1,050✔
926

927
        return access(c->parameter, F_OK) >= 0;
1,050✔
928
}
929

930
static int condition_test_path_exists_glob(Condition *c, char **env) {
2✔
931
        assert(c);
2✔
932
        assert(c->parameter);
2✔
933
        assert(c->type == CONDITION_PATH_EXISTS_GLOB);
2✔
934

935
        return glob_exists(c->parameter) > 0;
2✔
936
}
937

938
static int condition_test_path_is_directory(Condition *c, char **env) {
1✔
939
        assert(c);
1✔
940
        assert(c->parameter);
1✔
941
        assert(c->type == CONDITION_PATH_IS_DIRECTORY);
1✔
942

943
        return is_dir(c->parameter, true) > 0;
1✔
944
}
945

946
static int condition_test_path_is_symbolic_link(Condition *c, char **env) {
17✔
947
        assert(c);
17✔
948
        assert(c->parameter);
17✔
949
        assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
17✔
950

951
        return is_symlink(c->parameter) > 0;
17✔
952
}
953

954
static int condition_test_path_is_mount_point(Condition *c, char **env) {
71✔
955
        assert(c);
71✔
956
        assert(c->parameter);
71✔
957
        assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
71✔
958

959
        return path_is_mount_point_full(c->parameter, /* root = */ NULL, AT_SYMLINK_FOLLOW) > 0;
71✔
960
}
961

962
static int condition_test_path_is_read_write(Condition *c, char **env) {
414✔
963
        int r;
414✔
964

965
        assert(c);
414✔
966
        assert(c->parameter);
414✔
967
        assert(c->type == CONDITION_PATH_IS_READ_WRITE);
414✔
968

969
        r = path_is_read_only_fs(c->parameter);
414✔
970

971
        return r <= 0 && r != -ENOENT;
414✔
972
}
973

974
static int condition_test_cpufeature(Condition *c, char **env) {
3✔
975
        assert(c);
3✔
976
        assert(c->parameter);
3✔
977
        assert(c->type == CONDITION_CPU_FEATURE);
3✔
978

979
        return has_cpu_with_flag(ascii_strlower(c->parameter));
3✔
980
}
981

982
static int condition_test_path_is_encrypted(Condition *c, char **env) {
1✔
983
        int r;
1✔
984

985
        assert(c);
1✔
986
        assert(c->parameter);
1✔
987
        assert(c->type == CONDITION_PATH_IS_ENCRYPTED);
1✔
988

989
        r = path_is_encrypted(c->parameter);
1✔
990
        if (r < 0 && r != -ENOENT)
1✔
991
                log_debug_errno(r, "Failed to determine if '%s' is encrypted: %m", c->parameter);
×
992

993
        return r > 0;
1✔
994
}
995

996
static int condition_test_directory_not_empty(Condition *c, char **env) {
883✔
997
        int r;
883✔
998

999
        assert(c);
883✔
1000
        assert(c->parameter);
883✔
1001
        assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
883✔
1002

1003
        r = dir_is_empty(c->parameter, /* ignore_hidden_or_backup= */ true);
883✔
1004
        return r <= 0 && !IN_SET(r, -ENOENT, -ENOTDIR);
883✔
1005
}
1006

1007
static int condition_test_file_not_empty(Condition *c, char **env) {
69✔
1008
        struct stat st;
69✔
1009

1010
        assert(c);
69✔
1011
        assert(c->parameter);
69✔
1012
        assert(c->type == CONDITION_FILE_NOT_EMPTY);
69✔
1013

1014
        return (stat(c->parameter, &st) >= 0 &&
69✔
1015
                S_ISREG(st.st_mode) &&
69✔
1016
                st.st_size > 0);
51✔
1017
}
1018

1019
static int condition_test_file_is_executable(Condition *c, char **env) {
2✔
1020
        struct stat st;
2✔
1021

1022
        assert(c);
2✔
1023
        assert(c->parameter);
2✔
1024
        assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
2✔
1025

1026
        return (stat(c->parameter, &st) >= 0 &&
2✔
1027
                S_ISREG(st.st_mode) &&
2✔
1028
                (st.st_mode & 0111));
2✔
1029
}
1030

1031
static int condition_test_psi(Condition *c, char **env) {
26✔
1032
        _cleanup_free_ char *first = NULL, *second = NULL, *third = NULL, *fourth = NULL, *pressure_path = NULL;
26✔
1033
        const char *p, *value, *pressure_type;
26✔
1034
        loadavg_t *current, limit;
26✔
1035
        ResourcePressure pressure;
26✔
1036
        PressureType preferred_pressure_type = PRESSURE_TYPE_FULL;
26✔
1037
        int r;
26✔
1038

1039
        assert(c);
26✔
1040
        assert(c->parameter);
26✔
1041
        assert(IN_SET(c->type, CONDITION_MEMORY_PRESSURE, CONDITION_CPU_PRESSURE, CONDITION_IO_PRESSURE));
26✔
1042

1043
        if (!is_pressure_supported()) {
26✔
1044
                log_debug("Pressure Stall Information (PSI) is not supported, skipping.");
×
1045
                return 1;
×
1046
        }
1047

1048
        pressure_type = c->type == CONDITION_MEMORY_PRESSURE ? "memory" :
29✔
1049
                        c->type == CONDITION_CPU_PRESSURE ? "cpu" :
18✔
1050
                        "io";
1051

1052
        p = c->parameter;
26✔
1053
        r = extract_many_words(&p, ":", 0, &first, &second);
26✔
1054
        if (r <= 0)
26✔
1055
                return log_debug_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s: %m", c->parameter);
1✔
1056
        /* If only one parameter is passed, then we look at the global system pressure rather than a specific cgroup. */
1057
        if (r == 1) {
25✔
1058
                /* cpu.pressure 'full' is reported but undefined at system level */
1059
                if (c->type == CONDITION_CPU_PRESSURE)
19✔
1060
                        preferred_pressure_type = PRESSURE_TYPE_SOME;
13✔
1061

1062
                pressure_path = path_join("/proc/pressure", pressure_type);
19✔
1063
                if (!pressure_path)
19✔
1064
                        return log_oom_debug();
×
1065

1066
                value = first;
19✔
1067
        } else {
1068
                const char *controller = strjoina(pressure_type, ".pressure");
30✔
1069
                _cleanup_free_ char *slice_path = NULL, *root_scope = NULL;
6✔
1070
                CGroupMask mask, required_mask;
6✔
1071
                char *slice, *e;
6✔
1072

1073
                required_mask = c->type == CONDITION_MEMORY_PRESSURE ? CGROUP_MASK_MEMORY :
6✔
1074
                                c->type == CONDITION_CPU_PRESSURE ? CGROUP_MASK_CPU :
1075
                                CGROUP_MASK_IO;
1076

1077
                slice = strstrip(first);
6✔
1078
                if (!slice)
6✔
1079
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
×
1080

1081
                r = cg_mask_supported(&mask);
6✔
1082
                if (r < 0)
6✔
1083
                        return log_debug_errno(r, "Failed to get supported cgroup controllers: %m");
×
1084

1085
                if (!FLAGS_SET(mask, required_mask)) {
6✔
1086
                        log_debug("Cgroup %s controller not available, skipping PSI condition check.", pressure_type);
×
1087
                        return 1;
×
1088
                }
1089

1090
                r = cg_slice_to_path(slice, &slice_path);
6✔
1091
                if (r < 0)
6✔
1092
                        return log_debug_errno(r, "Cannot determine slice \"%s\" cgroup path: %m", slice);
1✔
1093

1094
                /* We might be running under the user manager, so get the root path and prefix it accordingly. */
1095
                r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &root_scope);
5✔
1096
                if (r < 0)
5✔
1097
                        return log_debug_errno(r, "Failed to get root cgroup path: %m");
×
1098

1099
                /* Drop init.scope, we want the parent. We could get an empty or / path, but that's fine,
1100
                 * just skip it in that case. */
1101
                e = endswith(root_scope, "/" SPECIAL_INIT_SCOPE);
5✔
1102
                if (e)
5✔
1103
                        *e = 0;
×
1104
                if (!empty_or_root(root_scope)) {
5✔
1105
                        _cleanup_free_ char *slice_joined = NULL;
×
1106

1107
                        slice_joined = path_join(root_scope, slice_path);
5✔
1108
                        if (!slice_joined)
5✔
1109
                                return log_oom_debug();
×
1110

1111
                        free_and_replace(slice_path, slice_joined);
5✔
1112
                }
1113

1114
                r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, slice_path, controller, &pressure_path);
5✔
1115
                if (r < 0)
5✔
1116
                        return log_debug_errno(r, "Error getting cgroup pressure path from %s: %m", slice_path);
×
1117

1118
                value = second;
5✔
1119
        }
1120

1121
        /* If a value including a specific timespan (in the intervals allowed by the kernel),
1122
         * parse it, otherwise we assume just a plain percentage that will be checked if it is
1123
         * smaller or equal to the current pressure average over 5 minutes. */
1124
        r = extract_many_words(&value, "/", 0, &third, &fourth);
24✔
1125
        if (r <= 0)
24✔
1126
                return log_debug_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s: %m", c->parameter);
×
1127
        if (r == 1)
24✔
1128
                current = &pressure.avg300;
1129
        else {
1130
                const char *timespan;
9✔
1131

1132
                timespan = skip_leading_chars(fourth, NULL);
9✔
1133
                if (!timespan)
9✔
1134
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
×
1135

1136
                if (startswith(timespan, "10sec"))
9✔
1137
                        current = &pressure.avg10;
1138
                else if (startswith(timespan, "1min"))
7✔
1139
                        current = &pressure.avg60;
1140
                else if (startswith(timespan, "5min"))
4✔
1141
                        current = &pressure.avg300;
1142
                else
1143
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
3✔
1144
        }
1145

1146
        value = strstrip(third);
21✔
1147
        if (!value)
21✔
1148
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse condition parameter %s.", c->parameter);
×
1149

1150
        r = parse_permyriad(value);
21✔
1151
        if (r < 0)
21✔
1152
                return log_debug_errno(r, "Failed to parse permyriad: %s", c->parameter);
6✔
1153

1154
        r = store_loadavg_fixed_point(r / 100LU, r % 100LU, &limit);
15✔
1155
        if (r < 0)
15✔
1156
                return log_debug_errno(r, "Failed to parse loadavg: %s", c->parameter);
×
1157

1158
        r = read_resource_pressure(pressure_path, preferred_pressure_type, &pressure);
15✔
1159
        /* cpu.pressure 'full' was recently added at cgroup level, fall back to 'some' */
1160
        if (r == -ENODATA && preferred_pressure_type == PRESSURE_TYPE_FULL)
15✔
1161
                r = read_resource_pressure(pressure_path, PRESSURE_TYPE_SOME, &pressure);
×
1162
        if (r == -ENOENT) {
15✔
1163
                /* We already checked that /proc/pressure exists, so this means we were given a cgroup
1164
                 * that doesn't exist or doesn't exist any longer. */
1165
                log_debug("\"%s\" not found, skipping PSI check.", pressure_path);
1✔
1166
                return 1;
1✔
1167
        }
1168
        if (r < 0)
14✔
1169
                return log_debug_errno(r, "Error parsing pressure from %s: %m", pressure_path);
×
1170

1171
        return *current <= limit;
14✔
1172
}
1173

1174
static int condition_test_kernel_module_loaded(Condition *c, char **env) {
240✔
1175
        int r;
240✔
1176

1177
        assert(c);
240✔
1178
        assert(c->parameter);
240✔
1179
        assert(c->type == CONDITION_KERNEL_MODULE_LOADED);
240✔
1180

1181
        /* Checks whether a specific kernel module is fully loaded (i.e. with the full initialization routine
1182
         * complete). */
1183

1184
        _cleanup_free_ char *normalized = strreplace(c->parameter, "-", "_");
480✔
1185
        if (!normalized)
240✔
1186
                return log_oom_debug();
×
1187

1188
        if (!filename_is_valid(normalized)) {
240✔
1189
                log_debug("Kernel module name '%s' is not valid, hence reporting it to not be loaded.", normalized);
2✔
1190
                return false;
2✔
1191
        }
1192

1193
        _cleanup_free_ char *p = path_join("/sys/module/", normalized);
476✔
1194
        if (!p)
238✔
1195
                return log_oom_debug();
×
1196

1197
        _cleanup_close_ int dir_fd = open(p, O_PATH|O_DIRECTORY|O_CLOEXEC);
476✔
1198
        if (dir_fd < 0) {
238✔
1199
                if (errno == ENOENT) {
158✔
1200
                        log_debug_errno(errno, "'%s/' does not exist, kernel module '%s' not loaded.", p, normalized);
158✔
1201
                        return false;
158✔
1202
                }
1203

1204
                return log_debug_errno(errno, "Failed to open directory '%s/': %m", p);
×
1205
        }
1206

1207
        _cleanup_free_ char *initstate = NULL;
80✔
1208
        r = read_virtual_file_at(dir_fd, "initstate", SIZE_MAX, &initstate, NULL);
80✔
1209
        if (r == -ENOENT) {
80✔
1210
                log_debug_errno(r, "'%s/' exists but '%s/initstate' does not, kernel module '%s' is built-in, hence loaded.", p, p, normalized);
54✔
1211
                return true;
54✔
1212
        }
1213
        if (r < 0)
26✔
1214
                return log_debug_errno(r, "Failed to open '%s/initstate': %m", p);
×
1215

1216
        delete_trailing_chars(initstate, WHITESPACE);
26✔
1217

1218
        if (!streq(initstate, "live")) {
26✔
1219
                log_debug("Kernel module '%s' is reported as '%s', hence not loaded.", normalized, initstate);
×
1220
                return false;
×
1221
        }
1222

1223
        log_debug("Kernel module '%s' detected as loaded.", normalized);
26✔
1224
        return true;
1225
}
1226

1227
int condition_test(Condition *c, char **env) {
5,959✔
1228

1229
        static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c, char **env) = {
5,959✔
1230
                [CONDITION_PATH_EXISTS]              = condition_test_path_exists,
1231
                [CONDITION_PATH_EXISTS_GLOB]         = condition_test_path_exists_glob,
1232
                [CONDITION_PATH_IS_DIRECTORY]        = condition_test_path_is_directory,
1233
                [CONDITION_PATH_IS_SYMBOLIC_LINK]    = condition_test_path_is_symbolic_link,
1234
                [CONDITION_PATH_IS_MOUNT_POINT]      = condition_test_path_is_mount_point,
1235
                [CONDITION_PATH_IS_READ_WRITE]       = condition_test_path_is_read_write,
1236
                [CONDITION_PATH_IS_ENCRYPTED]        = condition_test_path_is_encrypted,
1237
                [CONDITION_DIRECTORY_NOT_EMPTY]      = condition_test_directory_not_empty,
1238
                [CONDITION_FILE_NOT_EMPTY]           = condition_test_file_not_empty,
1239
                [CONDITION_FILE_IS_EXECUTABLE]       = condition_test_file_is_executable,
1240
                [CONDITION_KERNEL_COMMAND_LINE]      = condition_test_kernel_command_line,
1241
                [CONDITION_VERSION]                  = condition_test_version,
1242
                [CONDITION_CREDENTIAL]               = condition_test_credential,
1243
                [CONDITION_VIRTUALIZATION]           = condition_test_virtualization,
1244
                [CONDITION_SECURITY]                 = condition_test_security,
1245
                [CONDITION_CAPABILITY]               = condition_test_capability,
1246
                [CONDITION_HOST]                     = condition_test_host,
1247
                [CONDITION_AC_POWER]                 = condition_test_ac_power,
1248
                [CONDITION_ARCHITECTURE]             = condition_test_architecture,
1249
                [CONDITION_FIRMWARE]                 = condition_test_firmware,
1250
                [CONDITION_NEEDS_UPDATE]             = condition_test_needs_update,
1251
                [CONDITION_FIRST_BOOT]               = condition_test_first_boot,
1252
                [CONDITION_USER]                     = condition_test_user,
1253
                [CONDITION_GROUP]                    = condition_test_group,
1254
                [CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller,
1255
                [CONDITION_CPUS]                     = condition_test_cpus,
1256
                [CONDITION_MEMORY]                   = condition_test_memory,
1257
                [CONDITION_ENVIRONMENT]              = condition_test_environment,
1258
                [CONDITION_CPU_FEATURE]              = condition_test_cpufeature,
1259
                [CONDITION_OS_RELEASE]               = condition_test_osrelease,
1260
                [CONDITION_MEMORY_PRESSURE]          = condition_test_psi,
1261
                [CONDITION_CPU_PRESSURE]             = condition_test_psi,
1262
                [CONDITION_IO_PRESSURE]              = condition_test_psi,
1263
                [CONDITION_KERNEL_MODULE_LOADED]     = condition_test_kernel_module_loaded,
1264
        };
1265

1266
        int r, b;
5,959✔
1267

1268
        assert(c);
5,959✔
1269
        assert(c->type >= 0);
5,959✔
1270
        assert(c->type < _CONDITION_TYPE_MAX);
5,959✔
1271

1272
        r = condition_tests[c->type](c, env);
5,959✔
1273
        if (r < 0) {
5,959✔
1274
                c->result = CONDITION_ERROR;
28✔
1275
                return r;
28✔
1276
        }
1277

1278
        b = (r > 0) == !c->negate;
5,931✔
1279
        c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
5,931✔
1280
        return b;
5,931✔
1281
}
1282

1283
bool condition_test_list(
27,161✔
1284
                Condition *first,
1285
                char **env,
1286
                condition_to_string_t to_string,
1287
                condition_test_logger_t logger,
1288
                void *userdata) {
1289

1290
        int triggered = -1;
27,161✔
1291

1292
        /* If the condition list is empty, then it is true */
1293
        if (!first)
27,161✔
1294
                return true;
1295

1296
        /* Otherwise, if all of the non-trigger conditions apply and
1297
         * if any of the trigger conditions apply (unless there are
1298
         * none) we return true */
1299
        LIST_FOREACH(conditions, c, first) {
7,092✔
1300
                int r;
5,673✔
1301

1302
                r = condition_test(c, env);
5,673✔
1303

1304
                if (logger) {
5,673✔
1305
                        if (r < 0)
4,489✔
1306
                                logger(userdata, LOG_WARNING, r, PROJECT_FILE, __LINE__, __func__,
×
1307
                                       "Couldn't determine result for %s=%s%s%s, assuming failed: %m",
1308
                                       to_string(c->type),
×
1309
                                       c->trigger ? "|" : "",
×
1310
                                       c->negate ? "!" : "",
×
1311
                                       c->parameter);
1312
                        else
1313
                                logger(userdata, LOG_DEBUG, 0, PROJECT_FILE, __LINE__, __func__,
4,489✔
1314
                                       "%s=%s%s%s %s.",
1315
                                       to_string(c->type),
4,489✔
1316
                                       c->trigger ? "|" : "",
4,489✔
1317
                                       c->negate ? "!" : "",
4,489✔
1318
                                       c->parameter,
1319
                                       condition_result_to_string(c->result));
4,489✔
1320
                }
1321

1322
                if (!c->trigger && r <= 0)
5,673✔
1323
                        return false;
1324

1325
                if (c->trigger && triggered <= 0)
3,420✔
1326
                        triggered = r > 0;
1,250✔
1327
        }
1328

1329
        return triggered != 0;
1,419✔
1330
}
1331

1332
void condition_dump(Condition *c, FILE *f, const char *prefix, condition_to_string_t to_string) {
×
1333
        assert(c);
×
1334
        assert(f);
×
1335
        assert(to_string);
×
1336

1337
        prefix = strempty(prefix);
×
1338

1339
        fprintf(f,
×
1340
                "%s\t%s: %s%s%s %s\n",
1341
                prefix,
1342
                to_string(c->type),
×
1343
                c->trigger ? "|" : "",
×
1344
                c->negate ? "!" : "",
×
1345
                c->parameter,
1346
                condition_result_to_string(c->result));
×
1347
}
×
1348

1349
void condition_dump_list(Condition *first, FILE *f, const char *prefix, condition_to_string_t to_string) {
736✔
1350
        LIST_FOREACH(conditions, c, first)
736✔
1351
                condition_dump(c, f, prefix, to_string);
×
1352
}
736✔
1353

1354
static const char* const _condition_type_table[_CONDITION_TYPE_MAX] = {
1355
        [CONDITION_ARCHITECTURE]             = "ConditionArchitecture",
1356
        [CONDITION_FIRMWARE]                 = "ConditionFirmware",
1357
        [CONDITION_VIRTUALIZATION]           = "ConditionVirtualization",
1358
        [CONDITION_HOST]                     = "ConditionHost",
1359
        [CONDITION_KERNEL_COMMAND_LINE]      = "ConditionKernelCommandLine",
1360
        [CONDITION_VERSION]                  = "ConditionVersion",
1361
        [CONDITION_CREDENTIAL]               = "ConditionCredential",
1362
        [CONDITION_SECURITY]                 = "ConditionSecurity",
1363
        [CONDITION_CAPABILITY]               = "ConditionCapability",
1364
        [CONDITION_AC_POWER]                 = "ConditionACPower",
1365
        [CONDITION_NEEDS_UPDATE]             = "ConditionNeedsUpdate",
1366
        [CONDITION_FIRST_BOOT]               = "ConditionFirstBoot",
1367
        [CONDITION_PATH_EXISTS]              = "ConditionPathExists",
1368
        [CONDITION_PATH_EXISTS_GLOB]         = "ConditionPathExistsGlob",
1369
        [CONDITION_PATH_IS_DIRECTORY]        = "ConditionPathIsDirectory",
1370
        [CONDITION_PATH_IS_SYMBOLIC_LINK]    = "ConditionPathIsSymbolicLink",
1371
        [CONDITION_PATH_IS_MOUNT_POINT]      = "ConditionPathIsMountPoint",
1372
        [CONDITION_PATH_IS_READ_WRITE]       = "ConditionPathIsReadWrite",
1373
        [CONDITION_PATH_IS_ENCRYPTED]        = "ConditionPathIsEncrypted",
1374
        [CONDITION_DIRECTORY_NOT_EMPTY]      = "ConditionDirectoryNotEmpty",
1375
        [CONDITION_FILE_NOT_EMPTY]           = "ConditionFileNotEmpty",
1376
        [CONDITION_FILE_IS_EXECUTABLE]       = "ConditionFileIsExecutable",
1377
        [CONDITION_USER]                     = "ConditionUser",
1378
        [CONDITION_GROUP]                    = "ConditionGroup",
1379
        [CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController",
1380
        [CONDITION_CPUS]                     = "ConditionCPUs",
1381
        [CONDITION_MEMORY]                   = "ConditionMemory",
1382
        [CONDITION_ENVIRONMENT]              = "ConditionEnvironment",
1383
        [CONDITION_CPU_FEATURE]              = "ConditionCPUFeature",
1384
        [CONDITION_OS_RELEASE]               = "ConditionOSRelease",
1385
        [CONDITION_MEMORY_PRESSURE]          = "ConditionMemoryPressure",
1386
        [CONDITION_CPU_PRESSURE]             = "ConditionCPUPressure",
1387
        [CONDITION_IO_PRESSURE]              = "ConditionIOPressure",
1388
        [CONDITION_KERNEL_MODULE_LOADED]     = "ConditionKernelModuleLoaded",
1389
};
1390

1391
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(_condition_type, ConditionType);
6,892✔
1392

1393
const char* condition_type_to_string(ConditionType t) {
5,568✔
1394
        return _condition_type_to_string(t);
5,568✔
1395
}
1396

1397
ConditionType condition_type_from_string(const char *s) {
137✔
1398
        /* for backward compatibility */
1399
        if (streq_ptr(s, "ConditionKernelVersion"))
137✔
1400
                return CONDITION_VERSION;
1401

1402
        return _condition_type_from_string(s);
134✔
1403
}
1404

1405
void condition_types_list(void) {
35✔
1406
        DUMP_STRING_TABLE(_condition_type, ConditionType, _CONDITION_TYPE_MAX);
1,225✔
1407
}
35✔
1408

1409
static const char* const _assert_type_table[_CONDITION_TYPE_MAX] = {
1410
        [CONDITION_ARCHITECTURE]             = "AssertArchitecture",
1411
        [CONDITION_FIRMWARE]                 = "AssertFirmware",
1412
        [CONDITION_VIRTUALIZATION]           = "AssertVirtualization",
1413
        [CONDITION_HOST]                     = "AssertHost",
1414
        [CONDITION_KERNEL_COMMAND_LINE]      = "AssertKernelCommandLine",
1415
        [CONDITION_VERSION]                  = "AssertVersion",
1416
        [CONDITION_CREDENTIAL]               = "AssertCredential",
1417
        [CONDITION_SECURITY]                 = "AssertSecurity",
1418
        [CONDITION_CAPABILITY]               = "AssertCapability",
1419
        [CONDITION_AC_POWER]                 = "AssertACPower",
1420
        [CONDITION_NEEDS_UPDATE]             = "AssertNeedsUpdate",
1421
        [CONDITION_FIRST_BOOT]               = "AssertFirstBoot",
1422
        [CONDITION_PATH_EXISTS]              = "AssertPathExists",
1423
        [CONDITION_PATH_EXISTS_GLOB]         = "AssertPathExistsGlob",
1424
        [CONDITION_PATH_IS_DIRECTORY]        = "AssertPathIsDirectory",
1425
        [CONDITION_PATH_IS_SYMBOLIC_LINK]    = "AssertPathIsSymbolicLink",
1426
        [CONDITION_PATH_IS_MOUNT_POINT]      = "AssertPathIsMountPoint",
1427
        [CONDITION_PATH_IS_READ_WRITE]       = "AssertPathIsReadWrite",
1428
        [CONDITION_PATH_IS_ENCRYPTED]        = "AssertPathIsEncrypted",
1429
        [CONDITION_DIRECTORY_NOT_EMPTY]      = "AssertDirectoryNotEmpty",
1430
        [CONDITION_FILE_NOT_EMPTY]           = "AssertFileNotEmpty",
1431
        [CONDITION_FILE_IS_EXECUTABLE]       = "AssertFileIsExecutable",
1432
        [CONDITION_USER]                     = "AssertUser",
1433
        [CONDITION_GROUP]                    = "AssertGroup",
1434
        [CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController",
1435
        [CONDITION_CPUS]                     = "AssertCPUs",
1436
        [CONDITION_MEMORY]                   = "AssertMemory",
1437
        [CONDITION_ENVIRONMENT]              = "AssertEnvironment",
1438
        [CONDITION_CPU_FEATURE]              = "AssertCPUFeature",
1439
        [CONDITION_OS_RELEASE]               = "AssertOSRelease",
1440
        [CONDITION_MEMORY_PRESSURE]          = "AssertMemoryPressure",
1441
        [CONDITION_CPU_PRESSURE]             = "AssertCPUPressure",
1442
        [CONDITION_IO_PRESSURE]              = "AssertIOPressure",
1443
        [CONDITION_KERNEL_MODULE_LOADED]     = "AssertKernelModuleLoaded",
1444
};
1445

1446
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(_assert_type, ConditionType);
1,470✔
1447

1448
const char* assert_type_to_string(ConditionType t) {
185✔
1449
        return _assert_type_to_string(t);
185✔
1450
}
1451

1452
ConditionType assert_type_from_string(const char *s) {
96✔
1453
        /* for backward compatibility */
1454
        if (streq_ptr(s, "AssertKernelVersion"))
96✔
1455
                return CONDITION_VERSION;
1456

1457
        return _assert_type_from_string(s);
95✔
1458
}
1459

1460
void assert_types_list(void) {
35✔
1461
        DUMP_STRING_TABLE(_assert_type, ConditionType, _CONDITION_TYPE_MAX);
1,225✔
1462
}
35✔
1463

1464
static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
1465
        [CONDITION_UNTESTED]  = "untested",
1466
        [CONDITION_SUCCEEDED] = "succeeded",
1467
        [CONDITION_FAILED]    = "failed",
1468
        [CONDITION_ERROR]     = "error",
1469
};
1470

1471
DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);
4,543✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc