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/>.
31 #include "path-util.h"
32 #include "architecture.h"
33 #include "smack-util.h"
34 #include "apparmor-util.h"
36 #include "selinux-util.h"
38 #include "condition.h"
41 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
46 assert(type < _CONDITION_TYPE_MAX);
47 assert((!parameter) == (type == CONDITION_NULL));
49 c = new0(Condition, 1);
57 r = free_and_strdup(&c->parameter, parameter);
66 void condition_free(Condition *c) {
73 Condition* condition_free_list(Condition *first) {
76 LIST_FOREACH_SAFE(conditions, c, n, first)
82 static int condition_test_kernel_command_line(Condition *c) {
83 _cleanup_free_ char *line = NULL;
90 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
92 r = proc_cmdline(&line);
96 equal = !!strchr(c->parameter, '=');
100 _cleanup_free_ char *word = NULL;
103 r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
110 found = streq(word, c->parameter);
114 f = startswith(word, c->parameter);
115 found = f && (*f == '=' || *f == 0);
125 static int condition_test_virtualization(Condition *c) {
130 assert(c->parameter);
131 assert(c->type == CONDITION_VIRTUALIZATION);
133 v = detect_virtualization(&id);
137 /* First, compare with yes/no */
138 b = parse_boolean(c->parameter);
143 if (v == 0 && b == 0)
146 /* Then, compare categorization */
147 if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
150 if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
153 /* Finally compare id */
154 return v > 0 && streq(c->parameter, id);
157 static int condition_test_architecture(Condition *c) {
161 assert(c->parameter);
162 assert(c->type == CONDITION_ARCHITECTURE);
164 a = uname_architecture();
168 if (streq(c->parameter, "native"))
169 b = native_architecture();
171 b = architecture_from_string(c->parameter);
178 static int condition_test_host(Condition *c) {
179 _cleanup_free_ char *h = NULL;
184 assert(c->parameter);
185 assert(c->type == CONDITION_HOST);
187 if (sd_id128_from_string(c->parameter, &x) >= 0) {
189 r = sd_id128_get_machine(&y);
193 return sd_id128_equal(x, y);
196 h = gethostname_malloc();
200 return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
203 static int condition_test_ac_power(Condition *c) {
207 assert(c->parameter);
208 assert(c->type == CONDITION_AC_POWER);
210 r = parse_boolean(c->parameter);
214 return (on_ac_power() != 0) == !!r;
217 static int condition_test_security(Condition *c) {
219 assert(c->parameter);
220 assert(c->type == CONDITION_SECURITY);
222 if (streq(c->parameter, "selinux"))
223 return mac_selinux_use();
224 if (streq(c->parameter, "smack"))
225 return mac_smack_use();
226 if (streq(c->parameter, "apparmor"))
227 return mac_apparmor_use();
228 if (streq(c->parameter, "audit"))
230 if (streq(c->parameter, "ima"))
236 static int condition_test_capability(Condition *c) {
237 _cleanup_fclose_ FILE *f = NULL;
240 unsigned long long capabilities = -1;
243 assert(c->parameter);
244 assert(c->type == CONDITION_CAPABILITY);
246 /* If it's an invalid capability, we don't have it */
247 value = capability_from_name(c->parameter);
251 /* If it's a valid capability we default to assume
254 f = fopen("/proc/self/status", "re");
258 while (fgets(line, sizeof(line), f)) {
261 if (startswith(line, "CapBnd:")) {
262 (void) sscanf(line+7, "%llx", &capabilities);
267 return !!(capabilities & (1ULL << value));
270 static int condition_test_needs_update(Condition *c) {
272 struct stat usr, other;
275 assert(c->parameter);
276 assert(c->type == CONDITION_NEEDS_UPDATE);
278 /* If the file system is read-only we shouldn't suggest an update */
279 if (path_is_read_only_fs(c->parameter) > 0)
282 /* Any other failure means we should allow the condition to be true,
283 * so that we rather invoke too many update tools then too
286 if (!path_is_absolute(c->parameter))
289 p = strjoina(c->parameter, "/.updated");
290 if (lstat(p, &other) < 0)
293 if (lstat("/usr/", &usr) < 0)
296 return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
297 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
300 static int condition_test_first_boot(Condition *c) {
304 assert(c->parameter);
305 assert(c->type == CONDITION_FIRST_BOOT);
307 r = parse_boolean(c->parameter);
311 return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
314 static int condition_test_path_exists(Condition *c) {
316 assert(c->parameter);
317 assert(c->type == CONDITION_PATH_EXISTS);
319 return access(c->parameter, F_OK) >= 0;
322 static int condition_test_path_exists_glob(Condition *c) {
324 assert(c->parameter);
325 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
327 return glob_exists(c->parameter) > 0;
330 static int condition_test_path_is_directory(Condition *c) {
332 assert(c->parameter);
333 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
335 return is_dir(c->parameter, true) > 0;
338 static int condition_test_path_is_symbolic_link(Condition *c) {
340 assert(c->parameter);
341 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
343 return is_symlink(c->parameter) > 0;
346 static int condition_test_path_is_mount_point(Condition *c) {
348 assert(c->parameter);
349 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
351 return path_is_mount_point(c->parameter, true) > 0;
354 static int condition_test_path_is_read_write(Condition *c) {
356 assert(c->parameter);
357 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
359 return path_is_read_only_fs(c->parameter) <= 0;
362 static int condition_test_directory_not_empty(Condition *c) {
366 assert(c->parameter);
367 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
369 r = dir_is_empty(c->parameter);
370 return r <= 0 && r != -ENOENT;
373 static int condition_test_file_not_empty(Condition *c) {
377 assert(c->parameter);
378 assert(c->type == CONDITION_FILE_NOT_EMPTY);
380 return (stat(c->parameter, &st) >= 0 &&
381 S_ISREG(st.st_mode) &&
385 static int condition_test_file_is_executable(Condition *c) {
389 assert(c->parameter);
390 assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
392 return (stat(c->parameter, &st) >= 0 &&
393 S_ISREG(st.st_mode) &&
394 (st.st_mode & 0111));
397 static int condition_test_null(Condition *c) {
399 assert(c->type == CONDITION_NULL);
401 /* Note that during parsing we already evaluate the string and
402 * store it in c->negate */
406 int condition_test(Condition *c) {
408 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
409 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
410 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
411 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
412 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
413 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
414 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
415 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
416 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
417 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
418 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
419 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
420 [CONDITION_SECURITY] = condition_test_security,
421 [CONDITION_CAPABILITY] = condition_test_capability,
422 [CONDITION_HOST] = condition_test_host,
423 [CONDITION_AC_POWER] = condition_test_ac_power,
424 [CONDITION_ARCHITECTURE] = condition_test_architecture,
425 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
426 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
427 [CONDITION_NULL] = condition_test_null,
433 assert(c->type >= 0);
434 assert(c->type < _CONDITION_TYPE_MAX);
436 r = condition_tests[c->type](c);
438 c->result = CONDITION_ERROR;
442 b = (r > 0) == !c->negate;
443 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
447 void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
455 "%s\t%s: %s%s%s %s\n",
458 c->trigger ? "|" : "",
459 c->negate ? "!" : "",
461 condition_result_to_string(c->result));
464 void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
467 LIST_FOREACH(conditions, c, first)
468 condition_dump(c, f, prefix, to_string);
471 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
472 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
473 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
474 [CONDITION_HOST] = "ConditionHost",
475 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
476 [CONDITION_SECURITY] = "ConditionSecurity",
477 [CONDITION_CAPABILITY] = "ConditionCapability",
478 [CONDITION_AC_POWER] = "ConditionACPower",
479 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
480 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
481 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
482 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
483 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
484 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
485 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
486 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
487 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
488 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
489 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
490 [CONDITION_NULL] = "ConditionNull"
493 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
495 static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
496 [CONDITION_ARCHITECTURE] = "AssertArchitecture",
497 [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
498 [CONDITION_HOST] = "AssertHost",
499 [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
500 [CONDITION_SECURITY] = "AssertSecurity",
501 [CONDITION_CAPABILITY] = "AssertCapability",
502 [CONDITION_AC_POWER] = "AssertACPower",
503 [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
504 [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
505 [CONDITION_PATH_EXISTS] = "AssertPathExists",
506 [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
507 [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
508 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
509 [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
510 [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
511 [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
512 [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
513 [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
514 [CONDITION_NULL] = "AssertNull"
517 DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
519 static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
520 [CONDITION_UNTESTED] = "untested",
521 [CONDITION_SUCCEEDED] = "succeeded",
522 [CONDITION_FAILED] = "failed",
523 [CONDITION_ERROR] = "error",
526 DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);