1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
26 #include <sys/statvfs.h>
32 #include "path-util.h"
34 #include "architecture.h"
35 #include "smack-util.h"
36 #include "apparmor-util.h"
38 #include "selinux-util.h"
40 #include "condition.h"
43 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
48 assert(type < _CONDITION_TYPE_MAX);
49 assert(!parameter == (type == CONDITION_NULL));
51 c = new0(Condition, 1);
59 r = free_and_strdup(&c->parameter, parameter);
68 void condition_free(Condition *c) {
75 Condition* condition_free_list(Condition *first) {
78 LIST_FOREACH_SAFE(conditions, c, n, first)
84 static int condition_test_kernel_command_line(Condition *c) {
85 _cleanup_free_ char *line = NULL;
92 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
94 r = proc_cmdline(&line);
98 equal = !!strchr(c->parameter, '=');
102 _cleanup_free_ char *word = NULL;
105 r = unquote_first_word(&p, &word, true);
112 found = streq(word, c->parameter);
116 f = startswith(word, c->parameter);
117 found = f && (*f == '=' || *f == 0);
127 static int condition_test_virtualization(Condition *c) {
132 assert(c->parameter);
133 assert(c->type == CONDITION_VIRTUALIZATION);
135 v = detect_virtualization(&id);
139 /* First, compare with yes/no */
140 b = parse_boolean(c->parameter);
145 if (v == 0 && b == 0)
148 /* Then, compare categorization */
149 if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
152 if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
155 /* Finally compare id */
156 return v > 0 && streq(c->parameter, id);
159 static int condition_test_architecture(Condition *c) {
163 assert(c->parameter);
164 assert(c->type == CONDITION_ARCHITECTURE);
166 a = uname_architecture();
170 if (streq(c->parameter, "native"))
171 b = native_architecture();
173 b = architecture_from_string(c->parameter);
180 static int condition_test_host(Condition *c) {
181 _cleanup_free_ char *h = NULL;
186 assert(c->parameter);
187 assert(c->type == CONDITION_HOST);
189 if (sd_id128_from_string(c->parameter, &x) >= 0) {
191 r = sd_id128_get_machine(&y);
195 return sd_id128_equal(x, y);
198 h = gethostname_malloc();
202 return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
205 static int condition_test_ac_power(Condition *c) {
209 assert(c->parameter);
210 assert(c->type == CONDITION_AC_POWER);
212 r = parse_boolean(c->parameter);
216 return (on_ac_power() != 0) == !!r;
219 static int condition_test_security(Condition *c) {
221 assert(c->parameter);
222 assert(c->type == CONDITION_SECURITY);
224 if (streq(c->parameter, "selinux"))
225 return mac_selinux_use();
226 if (streq(c->parameter, "smack"))
227 return mac_smack_use();
228 if (streq(c->parameter, "apparmor"))
229 return mac_apparmor_use();
230 if (streq(c->parameter, "audit"))
232 if (streq(c->parameter, "ima"))
238 static int condition_test_capability(Condition *c) {
239 _cleanup_fclose_ FILE *f = NULL;
242 unsigned long long capabilities = -1;
245 assert(c->parameter);
246 assert(c->type == CONDITION_CAPABILITY);
248 /* If it's an invalid capability, we don't have it */
249 value = capability_from_name(c->parameter);
253 /* If it's a valid capability we default to assume
256 f = fopen("/proc/self/status", "re");
260 while (fgets(line, sizeof(line), f)) {
263 if (startswith(line, "CapBnd:")) {
264 (void) sscanf(line+7, "%llx", &capabilities);
269 return !!(capabilities & (1ULL << value));
272 static int condition_test_needs_update(Condition *c) {
274 struct stat usr, other;
277 assert(c->parameter);
278 assert(c->type == CONDITION_NEEDS_UPDATE);
280 /* If the file system is read-only we shouldn't suggest an update */
281 if (path_is_read_only_fs(c->parameter) > 0)
284 /* Any other failure means we should allow the condition to be true,
285 * so that we rather invoke too many update tools then too
288 if (!path_is_absolute(c->parameter))
291 p = strjoina(c->parameter, "/.updated");
292 if (lstat(p, &other) < 0)
295 if (lstat("/usr/", &usr) < 0)
298 return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
299 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
302 static int condition_test_first_boot(Condition *c) {
306 assert(c->parameter);
307 assert(c->type == CONDITION_FIRST_BOOT);
309 r = parse_boolean(c->parameter);
313 return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
316 static int condition_test_path_exists(Condition *c) {
318 assert(c->parameter);
319 assert(c->type == CONDITION_PATH_EXISTS);
321 return access(c->parameter, F_OK) >= 0;
324 static int condition_test_path_exists_glob(Condition *c) {
326 assert(c->parameter);
327 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
329 return glob_exists(c->parameter) > 0;
332 static int condition_test_path_is_directory(Condition *c) {
334 assert(c->parameter);
335 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
337 return is_dir(c->parameter, true) > 0;
340 static int condition_test_path_is_symbolic_link(Condition *c) {
342 assert(c->parameter);
343 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
345 return is_symlink(c->parameter) > 0;
348 static int condition_test_path_is_mount_point(Condition *c) {
350 assert(c->parameter);
351 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
353 return path_is_mount_point(c->parameter, true) > 0;
356 static int condition_test_path_is_read_write(Condition *c) {
358 assert(c->parameter);
359 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
361 return path_is_read_only_fs(c->parameter) <= 0;
364 static int condition_test_directory_not_empty(Condition *c) {
368 assert(c->parameter);
369 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
371 r = dir_is_empty(c->parameter);
372 return r <= 0 && r != -ENOENT;
375 static int condition_test_file_not_empty(Condition *c) {
379 assert(c->parameter);
380 assert(c->type == CONDITION_FILE_NOT_EMPTY);
382 return (stat(c->parameter, &st) >= 0 &&
383 S_ISREG(st.st_mode) &&
387 static int condition_test_file_is_executable(Condition *c) {
391 assert(c->parameter);
392 assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
394 return (stat(c->parameter, &st) >= 0 &&
395 S_ISREG(st.st_mode) &&
396 (st.st_mode & 0111));
399 static int condition_test_null(Condition *c) {
401 assert(c->type == CONDITION_NULL);
403 /* Note that during parsing we already evaluate the string and
404 * store it in c->negate */
408 int condition_test(Condition *c) {
410 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
411 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
412 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
413 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
414 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
415 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
416 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
417 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
418 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
419 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
420 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
421 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
422 [CONDITION_SECURITY] = condition_test_security,
423 [CONDITION_CAPABILITY] = condition_test_capability,
424 [CONDITION_HOST] = condition_test_host,
425 [CONDITION_AC_POWER] = condition_test_ac_power,
426 [CONDITION_ARCHITECTURE] = condition_test_architecture,
427 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
428 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
429 [CONDITION_NULL] = condition_test_null,
435 assert(c->type >= 0);
436 assert(c->type < _CONDITION_TYPE_MAX);
438 r = condition_tests[c->type](c);
440 c->result = CONDITION_ERROR;
444 b = (r > 0) == !c->negate;
445 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
449 void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
457 "%s\t%s: %s%s%s %s\n",
460 c->trigger ? "|" : "",
461 c->negate ? "!" : "",
463 condition_result_to_string(c->result));
466 void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
469 LIST_FOREACH(conditions, c, first)
470 condition_dump(c, f, prefix, to_string);
473 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
474 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
475 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
476 [CONDITION_HOST] = "ConditionHost",
477 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
478 [CONDITION_SECURITY] = "ConditionSecurity",
479 [CONDITION_CAPABILITY] = "ConditionCapability",
480 [CONDITION_AC_POWER] = "ConditionACPower",
481 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
482 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
483 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
484 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
485 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
486 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
487 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
488 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
489 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
490 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
491 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
492 [CONDITION_NULL] = "ConditionNull"
495 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
497 static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
498 [CONDITION_ARCHITECTURE] = "AssertArchitecture",
499 [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
500 [CONDITION_HOST] = "AssertHost",
501 [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
502 [CONDITION_SECURITY] = "AssertSecurity",
503 [CONDITION_CAPABILITY] = "AssertCapability",
504 [CONDITION_AC_POWER] = "AssertACPower",
505 [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
506 [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
507 [CONDITION_PATH_EXISTS] = "AssertPathExists",
508 [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
509 [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
510 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
511 [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
512 [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
513 [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
514 [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
515 [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
516 [CONDITION_NULL] = "AssertNull"
519 DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
521 static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
522 [CONDITION_UNTESTED] = "untested",
523 [CONDITION_SUCCEEDED] = "succeeded",
524 [CONDITION_FAILED] = "failed",
525 [CONDITION_ERROR] = "error",
528 DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);