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/capability.h>
27 #include <sys/statvfs.h>
31 #include <selinux/selinux.h>
34 #include <systemd/sd-id128.h>
36 #include "condition.h"
38 #include "path-util.h"
41 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
44 assert(type < _CONDITION_TYPE_MAX);
46 c = new0(Condition, 1);
55 c->parameter = strdup(parameter);
65 void condition_free(Condition *c) {
72 void condition_free_list(Condition *first) {
75 LIST_FOREACH_SAFE(conditions, c, n, first)
79 static bool test_kernel_command_line(const char *parameter) {
80 char *line, *w, *state, *word = NULL;
88 if (detect_container(NULL) > 0)
91 r = read_one_line_file("/proc/cmdline", &line);
93 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
97 equal = !!strchr(parameter, '=');
98 pl = strlen(parameter);
100 FOREACH_WORD_QUOTED(w, l, line, state) {
103 word = strndup(w, l);
108 if (streq(word, parameter)) {
113 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
127 static bool test_virtualization(const char *parameter) {
134 v = detect_virtualization(&id);
136 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
140 /* First, compare with yes/no */
141 b = parse_boolean(parameter);
146 if (v == 0 && b == 0)
149 /* Then, compare categorization */
150 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
153 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
156 /* Finally compare id */
157 return v > 0 && streq(parameter, id);
160 static bool test_apparmor_enabled(void) {
162 _cleanup_free_ char *p = NULL;
164 r = read_one_line_file("/sys/module/apparmor/parameters/enabled", &p);
168 return parse_boolean(p) > 0;
171 static bool test_security(const char *parameter) {
173 if (streq(parameter, "selinux"))
174 return is_selinux_enabled() > 0;
176 if (streq(parameter, "apparmor"))
177 return test_apparmor_enabled();
178 if (streq(parameter, "ima"))
179 return access("/sys/kernel/security/ima/", F_OK) == 0;
180 if (streq(parameter, "smack"))
181 return access("/sys/fs/smackfs", F_OK) == 0;
185 static bool test_capability(const char *parameter) {
189 unsigned long long capabilities = (unsigned long long) -1;
191 /* If it's an invalid capability, we don't have it */
193 if (cap_from_name(parameter, &value) < 0)
196 /* If it's a valid capability we default to assume
199 f = fopen("/proc/self/status", "re");
203 while (fgets(line, sizeof(line), f)) {
206 if (startswith(line, "CapBnd:")) {
207 (void) sscanf(line+7, "%llx", &capabilities);
214 return !!(capabilities & (1ULL << value));
217 static bool test_host(const char *parameter) {
223 if (sd_id128_from_string(parameter, &x) >= 0) {
225 r = sd_id128_get_machine(&y);
229 return sd_id128_equal(x, y);
232 h = gethostname_malloc();
236 b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
242 static bool test_ac_power(const char *parameter) {
245 r = parse_boolean(parameter);
249 return (on_ac_power() != 0) == !!r;
252 bool condition_test(Condition *c) {
257 case CONDITION_PATH_EXISTS:
258 return (access(c->parameter, F_OK) >= 0) == !c->negate;
260 case CONDITION_PATH_EXISTS_GLOB:
261 return (glob_exists(c->parameter) > 0) == !c->negate;
263 case CONDITION_PATH_IS_DIRECTORY: {
266 if (stat(c->parameter, &st) < 0)
268 return S_ISDIR(st.st_mode) == !c->negate;
271 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
274 if (lstat(c->parameter, &st) < 0)
276 return S_ISLNK(st.st_mode) == !c->negate;
279 case CONDITION_PATH_IS_MOUNT_POINT:
280 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
282 case CONDITION_PATH_IS_READ_WRITE:
283 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
285 case CONDITION_DIRECTORY_NOT_EMPTY: {
288 k = dir_is_empty(c->parameter);
289 return !(k == -ENOENT || k > 0) == !c->negate;
292 case CONDITION_FILE_NOT_EMPTY: {
295 if (stat(c->parameter, &st) < 0)
298 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
301 case CONDITION_FILE_IS_EXECUTABLE: {
304 if (stat(c->parameter, &st) < 0)
307 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
310 case CONDITION_KERNEL_COMMAND_LINE:
311 return test_kernel_command_line(c->parameter) == !c->negate;
313 case CONDITION_VIRTUALIZATION:
314 return test_virtualization(c->parameter) == !c->negate;
316 case CONDITION_SECURITY:
317 return test_security(c->parameter) == !c->negate;
319 case CONDITION_CAPABILITY:
320 return test_capability(c->parameter) == !c->negate;
323 return test_host(c->parameter) == !c->negate;
325 case CONDITION_AC_POWER:
326 return test_ac_power(c->parameter) == !c->negate;
332 assert_not_reached("Invalid condition type.");
336 bool condition_test_list(Condition *first) {
340 /* If the condition list is empty, then it is true */
344 /* Otherwise, if all of the non-trigger conditions apply and
345 * if any of the trigger conditions apply (unless there are
346 * none) we return true */
347 LIST_FOREACH(conditions, c, first) {
350 b = condition_test(c);
352 if (!c->trigger && !b)
355 if (c->trigger && triggered <= 0)
359 return triggered != 0;
362 void condition_dump(Condition *c, FILE *f, const char *prefix) {
372 condition_type_to_string(c->type),
373 c->trigger ? "|" : "",
374 c->negate ? "!" : "",
378 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
381 LIST_FOREACH(conditions, c, first)
382 condition_dump(c, f, prefix);
385 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
386 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
387 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
388 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
389 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
390 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
391 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
392 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
393 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
394 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
395 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
396 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
397 [CONDITION_SECURITY] = "ConditionSecurity",
398 [CONDITION_CAPABILITY] = "ConditionCapability",
399 [CONDITION_HOST] = "ConditionHost",
400 [CONDITION_AC_POWER] = "ConditionACPower",
401 [CONDITION_NULL] = "ConditionNull"
404 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);