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"
35 #include "architecture.h"
36 #include "smack-util.h"
37 #include "apparmor-util.h"
39 #include "selinux-util.h"
41 #include "condition.h"
44 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
49 assert(type < _CONDITION_TYPE_MAX);
50 assert(!parameter == (type == CONDITION_NULL));
52 c = new0(Condition, 1);
60 r = free_and_strdup(&c->parameter, parameter);
69 void condition_free(Condition *c) {
76 void condition_free_list(Condition *first) {
79 LIST_FOREACH_SAFE(conditions, c, n, first)
83 static int condition_test_kernel_command_line(Condition *c) {
84 _cleanup_free_ char *line = NULL;
91 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
93 r = proc_cmdline(&line);
97 equal = !!strchr(c->parameter, '=');
101 _cleanup_free_ char *word = NULL;
104 r = unquote_first_word(&p, &word, true);
111 found = streq(word, c->parameter);
115 f = startswith(word, c->parameter);
116 found = f && (*f == '=' || *f == 0);
126 static int condition_test_virtualization(Condition *c) {
131 assert(c->parameter);
132 assert(c->type == CONDITION_VIRTUALIZATION);
134 v = detect_virtualization(&id);
138 /* First, compare with yes/no */
139 b = parse_boolean(c->parameter);
144 if (v == 0 && b == 0)
147 /* Then, compare categorization */
148 if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
151 if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
154 /* Finally compare id */
155 return v > 0 && streq(c->parameter, id);
158 static int condition_test_architecture(Condition *c) {
162 assert(c->parameter);
163 assert(c->type == CONDITION_ARCHITECTURE);
165 a = uname_architecture();
169 if (streq(c->parameter, "native"))
170 b = native_architecture();
172 b = architecture_from_string(c->parameter);
179 static int condition_test_host(Condition *c) {
180 _cleanup_free_ char *h = NULL;
185 assert(c->parameter);
186 assert(c->type == CONDITION_HOST);
188 if (sd_id128_from_string(c->parameter, &x) >= 0) {
190 r = sd_id128_get_machine(&y);
194 return sd_id128_equal(x, y);
197 h = gethostname_malloc();
201 return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
204 static int condition_test_ac_power(Condition *c) {
208 assert(c->parameter);
209 assert(c->type == CONDITION_AC_POWER);
211 r = parse_boolean(c->parameter);
215 return (on_ac_power() != 0) == !!r;
218 static int condition_test_security(Condition *c) {
220 assert(c->parameter);
221 assert(c->type == CONDITION_SECURITY);
223 if (streq(c->parameter, "selinux"))
224 return mac_selinux_use();
225 if (streq(c->parameter, "smack"))
226 return mac_smack_use();
227 if (streq(c->parameter, "apparmor"))
228 return mac_apparmor_use();
229 if (streq(c->parameter, "audit"))
231 if (streq(c->parameter, "ima"))
237 static int condition_test_capability(Condition *c) {
238 _cleanup_fclose_ FILE *f = NULL;
241 unsigned long long capabilities = -1;
244 assert(c->parameter);
245 assert(c->type == CONDITION_CAPABILITY);
247 /* If it's an invalid capability, we don't have it */
248 value = capability_from_name(c->parameter);
252 /* If it's a valid capability we default to assume
255 f = fopen("/proc/self/status", "re");
259 while (fgets(line, sizeof(line), f)) {
262 if (startswith(line, "CapBnd:")) {
263 (void) sscanf(line+7, "%llx", &capabilities);
268 return !!(capabilities & (1ULL << value));
271 static int condition_test_needs_update(Condition *c) {
273 struct stat usr, other;
276 assert(c->parameter);
277 assert(c->type == CONDITION_NEEDS_UPDATE);
279 /* If the file system is read-only we shouldn't suggest an update */
280 if (path_is_read_only_fs(c->parameter) > 0)
283 /* Any other failure means we should allow the condition to be true,
284 * so that we rather invoke too many update tools then too
287 if (!path_is_absolute(c->parameter))
290 p = strappenda(c->parameter, "/.updated");
291 if (lstat(p, &other) < 0)
294 if (lstat("/usr/", &usr) < 0)
297 return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
298 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
301 static int condition_test_first_boot(Condition *c) {
305 assert(c->parameter);
306 assert(c->type == CONDITION_FIRST_BOOT);
308 r = parse_boolean(c->parameter);
312 return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
315 static int condition_test_path_exists(Condition *c) {
317 assert(c->parameter);
318 assert(c->type == CONDITION_PATH_EXISTS);
320 return access(c->parameter, F_OK) >= 0;
323 static int condition_test_path_exists_glob(Condition *c) {
325 assert(c->parameter);
326 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
328 return glob_exists(c->parameter) > 0;
331 static int condition_test_path_is_directory(Condition *c) {
333 assert(c->parameter);
334 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
336 return is_dir(c->parameter, true) > 0;
339 static int condition_test_path_is_symbolic_link(Condition *c) {
341 assert(c->parameter);
342 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
344 return is_symlink(c->parameter) > 0;
347 static int condition_test_path_is_mount_point(Condition *c) {
349 assert(c->parameter);
350 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
352 return path_is_mount_point(c->parameter, true) > 0;
355 static int condition_test_path_is_read_write(Condition *c) {
357 assert(c->parameter);
358 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
360 return path_is_read_only_fs(c->parameter) <= 0;
363 static int condition_test_directory_not_empty(Condition *c) {
367 assert(c->parameter);
368 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
370 r = dir_is_empty(c->parameter);
371 return r <= 0 && r != -ENOENT;
374 static int condition_test_file_not_empty(Condition *c) {
378 assert(c->parameter);
379 assert(c->type == CONDITION_FILE_NOT_EMPTY);
381 return (stat(c->parameter, &st) >= 0 &&
382 S_ISREG(st.st_mode) &&
386 static int condition_test_file_is_executable(Condition *c) {
390 assert(c->parameter);
391 assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
393 return (stat(c->parameter, &st) >= 0 &&
394 S_ISREG(st.st_mode) &&
395 (st.st_mode & 0111));
398 static int condition_test_null(Condition *c) {
400 assert(c->type == CONDITION_NULL);
402 /* Note that during parsing we already evaluate the string and
403 * store it in c->negate */
407 int condition_test(Condition *c) {
409 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
410 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
411 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
412 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
413 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
414 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
415 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
416 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
417 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
418 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
419 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
420 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
421 [CONDITION_SECURITY] = condition_test_security,
422 [CONDITION_CAPABILITY] = condition_test_capability,
423 [CONDITION_HOST] = condition_test_host,
424 [CONDITION_AC_POWER] = condition_test_ac_power,
425 [CONDITION_ARCHITECTURE] = condition_test_architecture,
426 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
427 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
428 [CONDITION_NULL] = condition_test_null,
434 assert(c->type >= 0);
435 assert(c->type < _CONDITION_TYPE_MAX);
437 r = condition_tests[c->type](c);
439 c->result = CONDITION_ERROR;
443 b = (r > 0) == !c->negate;
444 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
448 void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
456 "%s\t%s: %s%s%s %s\n",
459 c->trigger ? "|" : "",
460 c->negate ? "!" : "",
462 condition_result_to_string(c->result));
465 void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
468 LIST_FOREACH(conditions, c, first)
469 condition_dump(c, f, prefix, to_string);
472 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
473 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
474 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
475 [CONDITION_HOST] = "ConditionHost",
476 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
477 [CONDITION_SECURITY] = "ConditionSecurity",
478 [CONDITION_CAPABILITY] = "ConditionCapability",
479 [CONDITION_AC_POWER] = "ConditionACPower",
480 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
481 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
482 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
483 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
484 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
485 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
486 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
487 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
488 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
489 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
490 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
491 [CONDITION_NULL] = "ConditionNull"
494 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
496 static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
497 [CONDITION_ARCHITECTURE] = "AssertArchitecture",
498 [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
499 [CONDITION_HOST] = "AssertHost",
500 [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
501 [CONDITION_SECURITY] = "AssertSecurity",
502 [CONDITION_CAPABILITY] = "AssertCapability",
503 [CONDITION_AC_POWER] = "AssertACPower",
504 [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
505 [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
506 [CONDITION_PATH_EXISTS] = "AssertPathExists",
507 [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
508 [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
509 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
510 [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
511 [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
512 [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
513 [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
514 [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
515 [CONDITION_NULL] = "AssertNull"
518 DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
520 static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
521 [CONDITION_UNTESTED] = "untested",
522 [CONDITION_SUCCEEDED] = "succeeded",
523 [CONDITION_FAILED] = "failed",
524 [CONDITION_ERROR] = "error",
527 DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);