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