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