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