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) {
48 assert(type < _CONDITION_TYPE_MAX);
50 c = new0(Condition, 1);
58 r = free_and_strdup(&c->parameter, parameter);
67 void condition_free(Condition *c) {
74 void condition_free_list(Condition *first) {
77 LIST_FOREACH_SAFE(conditions, c, n, first)
81 int condition_test_kernel_command_line(Condition *c) {
82 _cleanup_free_ char *line = NULL;
89 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
91 r = proc_cmdline(&line);
97 equal = !!strchr(c->parameter, '=');
101 _cleanup_free_ char *word = NULL;
104 r = unquote_first_word(&p, &word);
111 found = streq(word, c->parameter);
115 f = startswith(word, c->parameter);
116 found = f && (*f == '=' || *f == 0);
126 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)) == !c->negate;
158 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);
176 return (a == b) == !c->negate;
179 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) == !c->negate;
197 h = gethostname_malloc();
201 return (fnmatch(c->parameter, h, FNM_CASEFOLD) == 0) == !c->negate;
204 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) == !c->negate;
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() == !c->negate;
225 if (streq(c->parameter, "smack"))
226 return mac_smack_use() == !c->negate;
227 if (streq(c->parameter, "apparmor"))
228 return mac_apparmor_use() == !c->negate;
229 if (streq(c->parameter, "audit"))
230 return use_audit() == !c->negate;
231 if (streq(c->parameter, "ima"))
232 return use_ima() == !c->negate;
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 */
249 if (cap_from_name(c->parameter, &value) < 0)
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)) == !c->negate;
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)) == !c->negate;
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) == !c->negate;
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) == !c->negate;
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) == !c->negate;
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) == !c->negate;
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) == !c->negate;
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) == !c->negate;
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) == c->negate;
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 == -ENOENT || r > 0) == !c->negate;
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) &&
383 st.st_size > 0) == !c->negate;
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)) == !c->negate;
398 static int condition_test_null(Condition *c) {
400 assert(c->parameter);
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,
433 assert(c->type >= 0);
434 assert(c->type < _CONDITION_TYPE_MAX);
436 return condition_tests[c->type](c);
439 void condition_dump(Condition *c, FILE *f, const char *prefix) {
447 "%s\t%s: %s%s%s %s\n",
449 condition_type_to_string(c->type),
450 c->trigger ? "|" : "",
451 c->negate ? "!" : "",
453 CONDITION_STATE_IS_FAILED(c->state) ? "failed" : CONDITION_STATE_IS_SUCCEEDED(c->state) ? "succeeded" : "untested");
456 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
459 LIST_FOREACH(conditions, c, first)
460 condition_dump(c, f, prefix);
463 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
464 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
465 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
466 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
467 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
468 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
469 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
470 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
471 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
472 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
473 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
474 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
475 [CONDITION_SECURITY] = "ConditionSecurity",
476 [CONDITION_CAPABILITY] = "ConditionCapability",
477 [CONDITION_HOST] = "ConditionHost",
478 [CONDITION_AC_POWER] = "ConditionACPower",
479 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
480 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
481 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
482 [CONDITION_NULL] = "ConditionNull"
485 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);