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>
31 #include "condition-util.h"
33 #include "path-util.h"
36 #include "architecture.h"
38 #include "smack-util.h"
39 #include "apparmor-util.h"
41 #include "selinux-util.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);
99 equal = !!strchr(c->parameter, '=');
103 _cleanup_free_ char *word = NULL;
106 r = unquote_first_word(&p, &word);
113 found = streq(word, c->parameter);
117 f = startswith(word, c->parameter);
118 found = f && (*f == '=' || *f == 0);
128 static int condition_test_virtualization(Condition *c) {
133 assert(c->parameter);
134 assert(c->type == CONDITION_VIRTUALIZATION);
136 v = detect_virtualization(&id);
140 /* First, compare with yes/no */
141 b = parse_boolean(c->parameter);
146 if (v == 0 && b == 0)
149 /* Then, compare categorization */
150 if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
153 if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
156 /* Finally compare id */
157 return v > 0 && streq(c->parameter, id);
160 static int condition_test_architecture(Condition *c) {
164 assert(c->parameter);
165 assert(c->type == CONDITION_ARCHITECTURE);
167 a = uname_architecture();
171 if (streq(c->parameter, "native"))
172 b = native_architecture();
174 b = architecture_from_string(c->parameter);
181 static int condition_test_host(Condition *c) {
182 _cleanup_free_ char *h = NULL;
187 assert(c->parameter);
188 assert(c->type == CONDITION_HOST);
190 if (sd_id128_from_string(c->parameter, &x) >= 0) {
192 r = sd_id128_get_machine(&y);
196 return sd_id128_equal(x, y);
199 h = gethostname_malloc();
203 return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
206 static int condition_test_ac_power(Condition *c) {
210 assert(c->parameter);
211 assert(c->type == CONDITION_AC_POWER);
213 r = parse_boolean(c->parameter);
217 return (on_ac_power() != 0) == !!r;
220 static int condition_test_security(Condition *c) {
222 assert(c->parameter);
223 assert(c->type == CONDITION_SECURITY);
225 if (streq(c->parameter, "selinux"))
226 return mac_selinux_use();
227 if (streq(c->parameter, "smack"))
228 return mac_smack_use();
229 if (streq(c->parameter, "apparmor"))
230 return mac_apparmor_use();
231 if (streq(c->parameter, "audit"))
233 if (streq(c->parameter, "ima"))
239 static int condition_test_capability(Condition *c) {
240 _cleanup_fclose_ FILE *f = NULL;
243 unsigned long long capabilities = -1;
246 assert(c->parameter);
247 assert(c->type == CONDITION_CAPABILITY);
249 /* If it's an invalid capability, we don't have it */
251 if (cap_from_name(c->parameter, &value) < 0)
254 /* If it's a valid capability we default to assume
257 f = fopen("/proc/self/status", "re");
261 while (fgets(line, sizeof(line), f)) {
264 if (startswith(line, "CapBnd:")) {
265 (void) sscanf(line+7, "%llx", &capabilities);
270 return !!(capabilities & (1ULL << value));
273 static int condition_test_needs_update(Condition *c) {
275 struct stat usr, other;
278 assert(c->parameter);
279 assert(c->type == CONDITION_NEEDS_UPDATE);
281 /* If the file system is read-only we shouldn't suggest an update */
282 if (path_is_read_only_fs(c->parameter) > 0)
285 /* Any other failure means we should allow the condition to be true,
286 * so that we rather invoke too many update tools then too
289 if (!path_is_absolute(c->parameter))
292 p = strappenda(c->parameter, "/.updated");
293 if (lstat(p, &other) < 0)
296 if (lstat("/usr/", &usr) < 0)
299 return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
300 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
303 static int condition_test_first_boot(Condition *c) {
307 assert(c->parameter);
308 assert(c->type == CONDITION_FIRST_BOOT);
310 r = parse_boolean(c->parameter);
314 return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
317 static int condition_test_path_exists(Condition *c) {
319 assert(c->parameter);
320 assert(c->type == CONDITION_PATH_EXISTS);
322 return access(c->parameter, F_OK) >= 0;
325 static int condition_test_path_exists_glob(Condition *c) {
327 assert(c->parameter);
328 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
330 return glob_exists(c->parameter) > 0;
333 static int condition_test_path_is_directory(Condition *c) {
335 assert(c->parameter);
336 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
338 return is_dir(c->parameter, true) > 0;
341 static int condition_test_path_is_symbolic_link(Condition *c) {
343 assert(c->parameter);
344 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
346 return is_symlink(c->parameter) > 0;
349 static int condition_test_path_is_mount_point(Condition *c) {
351 assert(c->parameter);
352 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
354 return path_is_mount_point(c->parameter, true) > 0;
357 static int condition_test_path_is_read_write(Condition *c) {
359 assert(c->parameter);
360 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
362 return path_is_read_only_fs(c->parameter) <= 0;
365 static int condition_test_directory_not_empty(Condition *c) {
369 assert(c->parameter);
370 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
372 r = dir_is_empty(c->parameter);
373 return r <= 0 && r != -ENOENT;
376 static int condition_test_file_not_empty(Condition *c) {
380 assert(c->parameter);
381 assert(c->type == CONDITION_FILE_NOT_EMPTY);
383 return (stat(c->parameter, &st) >= 0 &&
384 S_ISREG(st.st_mode) &&
388 static int condition_test_file_is_executable(Condition *c) {
392 assert(c->parameter);
393 assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
395 return (stat(c->parameter, &st) >= 0 &&
396 S_ISREG(st.st_mode) &&
397 (st.st_mode & 0111));
400 static int condition_test_null(Condition *c) {
402 assert(c->type == CONDITION_NULL);
404 /* Note that during parsing we already evaluate the string and
405 * store it in c->negate */
409 int condition_test(Condition *c) {
411 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
412 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
413 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
414 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
415 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
416 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
417 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
418 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
419 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
420 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
421 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
422 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
423 [CONDITION_SECURITY] = condition_test_security,
424 [CONDITION_CAPABILITY] = condition_test_capability,
425 [CONDITION_HOST] = condition_test_host,
426 [CONDITION_AC_POWER] = condition_test_ac_power,
427 [CONDITION_ARCHITECTURE] = condition_test_architecture,
428 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
429 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
430 [CONDITION_NULL] = condition_test_null,
436 assert(c->type >= 0);
437 assert(c->type < _CONDITION_TYPE_MAX);
439 r = condition_tests[c->type](c);
441 c->result = CONDITION_ERROR;
445 b = (r > 0) == !c->negate;
446 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
450 void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
458 "%s\t%s: %s%s%s %s\n",
461 c->trigger ? "|" : "",
462 c->negate ? "!" : "",
464 condition_result_to_string(c->result));
467 void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
470 LIST_FOREACH(conditions, c, first)
471 condition_dump(c, f, prefix, to_string);
474 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
475 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
476 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
477 [CONDITION_HOST] = "ConditionHost",
478 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
479 [CONDITION_SECURITY] = "ConditionSecurity",
480 [CONDITION_CAPABILITY] = "ConditionCapability",
481 [CONDITION_AC_POWER] = "ConditionACPower",
482 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
483 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
484 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
485 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
486 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
487 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
488 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
489 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
490 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
491 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
492 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
493 [CONDITION_NULL] = "ConditionNull"
496 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
498 static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
499 [CONDITION_ARCHITECTURE] = "AssertArchitecture",
500 [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
501 [CONDITION_HOST] = "AssertHost",
502 [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
503 [CONDITION_SECURITY] = "AssertSecurity",
504 [CONDITION_CAPABILITY] = "AssertCapability",
505 [CONDITION_AC_POWER] = "AssertACPower",
506 [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
507 [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
508 [CONDITION_PATH_EXISTS] = "AssertPathExists",
509 [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
510 [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
511 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
512 [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
513 [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
514 [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
515 [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
516 [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
517 [CONDITION_NULL] = "AssertNull"
520 DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
522 static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
523 [CONDITION_UNTESTED] = "untested",
524 [CONDITION_SUCCEEDED] = "succeeded",
525 [CONDITION_FAILED] = "failed",
526 [CONDITION_ERROR] = "error",
529 DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);