]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/condition.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[thirdparty/systemd.git] / src / shared / condition.c
CommitLineData
b77c08e0
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
b77c08e0 22#include <errno.h>
84ac7bea
LP
23#include <fnmatch.h>
24#include <stdlib.h>
b77c08e0
TG
25#include <string.h>
26#include <unistd.h>
b77c08e0 27
d1bddcec 28#include "sd-id128.h"
84ac7bea 29
d1bddcec 30#include "apparmor-util.h"
84ac7bea 31#include "architecture.h"
d1bddcec 32#include "audit.h"
2822da4f 33#include "cap-list.h"
3ffd4af2 34#include "condition.h"
84ac7bea 35#include "extract-word.h"
3ffd4af2 36#include "fd-util.h"
958b66ea 37#include "hostname-util.h"
84ac7bea 38#include "ima-util.h"
6bedfcbb 39#include "parse-util.h"
84ac7bea
LP
40#include "path-util.h"
41#include "selinux-util.h"
42#include "smack-util.h"
07630cea 43#include "string-util.h"
84ac7bea
LP
44#include "util.h"
45#include "virt.h"
b77c08e0
TG
46
47Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
48 Condition *c;
d1bddcec 49 int r;
b77c08e0 50
b80ba1da 51 assert(type >= 0);
b77c08e0 52 assert(type < _CONDITION_TYPE_MAX);
8a9c6071 53 assert((!parameter) == (type == CONDITION_NULL));
b77c08e0
TG
54
55 c = new0(Condition, 1);
56 if (!c)
57 return NULL;
58
59 c->type = type;
60 c->trigger = trigger;
61 c->negate = negate;
62
d1bddcec
LP
63 r = free_and_strdup(&c->parameter, parameter);
64 if (r < 0) {
65 free(c);
66 return NULL;
b77c08e0
TG
67 }
68
69 return c;
70}
71
72void condition_free(Condition *c) {
73 assert(c);
74
75 free(c->parameter);
76 free(c);
77}
78
447021aa 79Condition* condition_free_list(Condition *first) {
b77c08e0
TG
80 Condition *c, *n;
81
82 LIST_FOREACH_SAFE(conditions, c, n, first)
83 condition_free(c);
447021aa
ZJS
84
85 return NULL;
b77c08e0
TG
86}
87
a4705396 88static int condition_test_kernel_command_line(Condition *c) {
07318c29
LP
89 _cleanup_free_ char *line = NULL;
90 const char *p;
b77c08e0
TG
91 bool equal;
92 int r;
b77c08e0
TG
93
94 assert(c);
95 assert(c->parameter);
96 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
97
98 r = proc_cmdline(&line);
99 if (r < 0)
592fd144 100 return r;
b77c08e0
TG
101
102 equal = !!strchr(c->parameter, '=');
07318c29
LP
103 p = line;
104
105 for (;;) {
106 _cleanup_free_ char *word = NULL;
107 bool found;
108
12ba2c44 109 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
592fd144
LP
110 if (r < 0)
111 return r;
112 if (r == 0)
a4705396 113 break;
07318c29
LP
114
115 if (equal)
116 found = streq(word, c->parameter);
117 else {
118 const char *f;
119
120 f = startswith(word, c->parameter);
121 found = f && (*f == '=' || *f == 0);
b77c08e0
TG
122 }
123
07318c29 124 if (found)
a4705396 125 return true;
b77c08e0 126 }
b77c08e0 127
a4705396 128 return false;
b77c08e0
TG
129}
130
a4705396 131static int condition_test_virtualization(Condition *c) {
248fab74 132 int b, v;
b77c08e0
TG
133
134 assert(c);
135 assert(c->parameter);
136 assert(c->type == CONDITION_VIRTUALIZATION);
137
75f86906 138 v = detect_virtualization();
592fd144
LP
139 if (v < 0)
140 return v;
b77c08e0
TG
141
142 /* First, compare with yes/no */
143 b = parse_boolean(c->parameter);
144
145 if (v > 0 && b > 0)
a4705396 146 return true;
b77c08e0
TG
147
148 if (v == 0 && b == 0)
a4705396 149 return true;
b77c08e0
TG
150
151 /* Then, compare categorization */
75f86906 152 if (VIRTUALIZATION_IS_VM(v) && streq(c->parameter, "vm"))
a4705396 153 return true;
b77c08e0 154
75f86906 155 if (VIRTUALIZATION_IS_CONTAINER(v) && streq(c->parameter, "container"))
a4705396 156 return true;
b77c08e0
TG
157
158 /* Finally compare id */
75f86906 159 return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
b77c08e0
TG
160}
161
a4705396 162static int condition_test_architecture(Condition *c) {
592fd144 163 int a, b;
099524d7
LP
164
165 assert(c);
166 assert(c->parameter);
167 assert(c->type == CONDITION_ARCHITECTURE);
168
169 a = uname_architecture();
170 if (a < 0)
592fd144 171 return a;
099524d7
LP
172
173 if (streq(c->parameter, "native"))
174 b = native_architecture();
175 else
176 b = architecture_from_string(c->parameter);
099524d7 177 if (b < 0)
592fd144 178 return b;
099524d7 179
a4705396 180 return a == b;
099524d7
LP
181}
182
a4705396 183static int condition_test_host(Condition *c) {
dc92e62c 184 _cleanup_free_ char *h = NULL;
b77c08e0 185 sd_id128_t x, y;
b77c08e0 186 int r;
b77c08e0
TG
187
188 assert(c);
189 assert(c->parameter);
190 assert(c->type == CONDITION_HOST);
191
192 if (sd_id128_from_string(c->parameter, &x) >= 0) {
193
194 r = sd_id128_get_machine(&y);
195 if (r < 0)
592fd144 196 return r;
b77c08e0 197
a4705396 198 return sd_id128_equal(x, y);
b77c08e0
TG
199 }
200
201 h = gethostname_malloc();
202 if (!h)
592fd144 203 return -ENOMEM;
b77c08e0 204
a4705396 205 return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
b77c08e0
TG
206}
207
a4705396 208static int condition_test_ac_power(Condition *c) {
b77c08e0
TG
209 int r;
210
211 assert(c);
212 assert(c->parameter);
213 assert(c->type == CONDITION_AC_POWER);
214
215 r = parse_boolean(c->parameter);
216 if (r < 0)
592fd144 217 return r;
b77c08e0 218
a4705396 219 return (on_ac_power() != 0) == !!r;
b77c08e0
TG
220}
221
d1bddcec
LP
222static int condition_test_security(Condition *c) {
223 assert(c);
224 assert(c->parameter);
225 assert(c->type == CONDITION_SECURITY);
226
227 if (streq(c->parameter, "selinux"))
a4705396 228 return mac_selinux_use();
d1bddcec 229 if (streq(c->parameter, "smack"))
a4705396 230 return mac_smack_use();
d1bddcec 231 if (streq(c->parameter, "apparmor"))
a4705396 232 return mac_apparmor_use();
d1bddcec 233 if (streq(c->parameter, "audit"))
a4705396 234 return use_audit();
d1bddcec 235 if (streq(c->parameter, "ima"))
a4705396 236 return use_ima();
d1bddcec 237
a4705396 238 return false;
d1bddcec
LP
239}
240
241static int condition_test_capability(Condition *c) {
242 _cleanup_fclose_ FILE *f = NULL;
2822da4f 243 int value;
d1bddcec
LP
244 char line[LINE_MAX];
245 unsigned long long capabilities = -1;
246
247 assert(c);
248 assert(c->parameter);
249 assert(c->type == CONDITION_CAPABILITY);
250
251 /* If it's an invalid capability, we don't have it */
2822da4f
LP
252 value = capability_from_name(c->parameter);
253 if (value < 0)
d1bddcec
LP
254 return -EINVAL;
255
256 /* If it's a valid capability we default to assume
257 * that we have it */
258
259 f = fopen("/proc/self/status", "re");
260 if (!f)
261 return -errno;
262
263 while (fgets(line, sizeof(line), f)) {
264 truncate_nl(line);
265
266 if (startswith(line, "CapBnd:")) {
267 (void) sscanf(line+7, "%llx", &capabilities);
268 break;
269 }
270 }
271
a4705396 272 return !!(capabilities & (1ULL << value));
d1bddcec
LP
273}
274
275static int condition_test_needs_update(Condition *c) {
276 const char *p;
277 struct stat usr, other;
278
279 assert(c);
280 assert(c->parameter);
281 assert(c->type == CONDITION_NEEDS_UPDATE);
282
283 /* If the file system is read-only we shouldn't suggest an update */
284 if (path_is_read_only_fs(c->parameter) > 0)
a4705396 285 return false;
d1bddcec
LP
286
287 /* Any other failure means we should allow the condition to be true,
288 * so that we rather invoke too many update tools then too
289 * few. */
290
291 if (!path_is_absolute(c->parameter))
a4705396 292 return true;
d1bddcec 293
63c372cb 294 p = strjoina(c->parameter, "/.updated");
d1bddcec 295 if (lstat(p, &other) < 0)
a4705396 296 return true;
d1bddcec
LP
297
298 if (lstat("/usr/", &usr) < 0)
a4705396 299 return true;
d1bddcec 300
a4705396
LP
301 return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
302 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
d1bddcec
LP
303}
304
305static int condition_test_first_boot(Condition *c) {
306 int r;
307
308 assert(c);
309 assert(c->parameter);
310 assert(c->type == CONDITION_FIRST_BOOT);
311
312 r = parse_boolean(c->parameter);
313 if (r < 0)
314 return r;
315
a4705396 316 return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
d1bddcec
LP
317}
318
319static int condition_test_path_exists(Condition *c) {
320 assert(c);
321 assert(c->parameter);
322 assert(c->type == CONDITION_PATH_EXISTS);
323
a4705396 324 return access(c->parameter, F_OK) >= 0;
d1bddcec
LP
325}
326
327static int condition_test_path_exists_glob(Condition *c) {
328 assert(c);
329 assert(c->parameter);
330 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
331
a4705396 332 return glob_exists(c->parameter) > 0;
d1bddcec
LP
333}
334
335static int condition_test_path_is_directory(Condition *c) {
336 assert(c);
337 assert(c->parameter);
338 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
339
a4705396 340 return is_dir(c->parameter, true) > 0;
d1bddcec
LP
341}
342
343static int condition_test_path_is_symbolic_link(Condition *c) {
344 assert(c);
345 assert(c->parameter);
346 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
347
a4705396 348 return is_symlink(c->parameter) > 0;
d1bddcec
LP
349}
350
351static int condition_test_path_is_mount_point(Condition *c) {
352 assert(c);
353 assert(c->parameter);
354 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
355
e26d6ce5 356 return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0;
d1bddcec
LP
357}
358
359static int condition_test_path_is_read_write(Condition *c) {
360 assert(c);
361 assert(c->parameter);
362 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
363
a4705396 364 return path_is_read_only_fs(c->parameter) <= 0;
d1bddcec
LP
365}
366
367static int condition_test_directory_not_empty(Condition *c) {
368 int r;
369
370 assert(c);
371 assert(c->parameter);
372 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
373
374 r = dir_is_empty(c->parameter);
a4705396 375 return r <= 0 && r != -ENOENT;
d1bddcec
LP
376}
377
378static int condition_test_file_not_empty(Condition *c) {
379 struct stat st;
380
381 assert(c);
382 assert(c->parameter);
383 assert(c->type == CONDITION_FILE_NOT_EMPTY);
384
385 return (stat(c->parameter, &st) >= 0 &&
386 S_ISREG(st.st_mode) &&
a4705396 387 st.st_size > 0);
d1bddcec
LP
388}
389
390static int condition_test_file_is_executable(Condition *c) {
391 struct stat st;
392
393 assert(c);
394 assert(c->parameter);
395 assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
396
397 return (stat(c->parameter, &st) >= 0 &&
398 S_ISREG(st.st_mode) &&
a4705396 399 (st.st_mode & 0111));
d1bddcec
LP
400}
401
402static int condition_test_null(Condition *c) {
403 assert(c);
d1bddcec
LP
404 assert(c->type == CONDITION_NULL);
405
406 /* Note that during parsing we already evaluate the string and
407 * store it in c->negate */
a4705396 408 return true;
d1bddcec
LP
409}
410
411int condition_test(Condition *c) {
412
413 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
414 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
415 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
416 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
417 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
418 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
419 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
420 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
421 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
422 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
423 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
424 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
425 [CONDITION_SECURITY] = condition_test_security,
426 [CONDITION_CAPABILITY] = condition_test_capability,
427 [CONDITION_HOST] = condition_test_host,
428 [CONDITION_AC_POWER] = condition_test_ac_power,
429 [CONDITION_ARCHITECTURE] = condition_test_architecture,
430 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
431 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
432 [CONDITION_NULL] = condition_test_null,
433 };
cc50ef13
LP
434
435 int r, b;
d1bddcec
LP
436
437 assert(c);
438 assert(c->type >= 0);
439 assert(c->type < _CONDITION_TYPE_MAX);
440
a4705396 441 r = condition_tests[c->type](c);
cc50ef13
LP
442 if (r < 0) {
443 c->result = CONDITION_ERROR;
a4705396 444 return r;
cc50ef13 445 }
a4705396 446
cc50ef13
LP
447 b = (r > 0) == !c->negate;
448 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
449 return b;
d1bddcec
LP
450}
451
59fccdc5 452void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
b77c08e0
TG
453 assert(c);
454 assert(f);
455
456 if (!prefix)
457 prefix = "";
458
459 fprintf(f,
460 "%s\t%s: %s%s%s %s\n",
461 prefix,
59fccdc5 462 to_string(c->type),
b77c08e0
TG
463 c->trigger ? "|" : "",
464 c->negate ? "!" : "",
465 c->parameter,
cc50ef13 466 condition_result_to_string(c->result));
b77c08e0
TG
467}
468
59fccdc5 469void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
b77c08e0
TG
470 Condition *c;
471
472 LIST_FOREACH(conditions, c, first)
59fccdc5 473 condition_dump(c, f, prefix, to_string);
b77c08e0
TG
474}
475
476static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
651c3318
LP
477 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
478 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
479 [CONDITION_HOST] = "ConditionHost",
480 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
481 [CONDITION_SECURITY] = "ConditionSecurity",
482 [CONDITION_CAPABILITY] = "ConditionCapability",
483 [CONDITION_AC_POWER] = "ConditionACPower",
484 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
485 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
b77c08e0
TG
486 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
487 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
488 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
489 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
490 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
491 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
492 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
493 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
494 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
b77c08e0
TG
495 [CONDITION_NULL] = "ConditionNull"
496};
497
498DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
cc50ef13 499
59fccdc5 500static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
651c3318
LP
501 [CONDITION_ARCHITECTURE] = "AssertArchitecture",
502 [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
503 [CONDITION_HOST] = "AssertHost",
504 [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
505 [CONDITION_SECURITY] = "AssertSecurity",
506 [CONDITION_CAPABILITY] = "AssertCapability",
507 [CONDITION_AC_POWER] = "AssertACPower",
508 [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
509 [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
59fccdc5
LP
510 [CONDITION_PATH_EXISTS] = "AssertPathExists",
511 [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
512 [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
513 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
514 [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
515 [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
516 [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
517 [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
518 [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
59fccdc5
LP
519 [CONDITION_NULL] = "AssertNull"
520};
521
522DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
523
cc50ef13
LP
524static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
525 [CONDITION_UNTESTED] = "untested",
526 [CONDITION_SUCCEEDED] = "succeeded",
527 [CONDITION_FAILED] = "failed",
528 [CONDITION_ERROR] = "error",
529};
530
531DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);