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_security(const char *parameter) {
162 if (streq(parameter, "selinux"))
163 return is_selinux_enabled() > 0;
165 if (streq(parameter, "apparmor"))
166 return access("/sys/kernel/security/apparmor/", F_OK) == 0;
167 if (streq(parameter, "ima"))
168 return access("/sys/kernel/security/ima/", F_OK) == 0;
169 if (streq(parameter, "smack"))
170 return access("/sys/fs/smackfs", F_OK) == 0;
174 static bool test_capability(const char *parameter) {
178 unsigned long long capabilities = (unsigned long long) -1;
180 /* If it's an invalid capability, we don't have it */
182 if (cap_from_name(parameter, &value) < 0)
185 /* If it's a valid capability we default to assume
188 f = fopen("/proc/self/status", "re");
192 while (fgets(line, sizeof(line), f)) {
195 if (startswith(line, "CapBnd:")) {
196 (void) sscanf(line+7, "%llx", &capabilities);
203 return !!(capabilities & (1ULL << value));
206 static bool test_host(const char *parameter) {
212 if (sd_id128_from_string(parameter, &x) >= 0) {
214 r = sd_id128_get_machine(&y);
218 return sd_id128_equal(x, y);
221 h = gethostname_malloc();
225 b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
231 static bool test_ac_power(const char *parameter) {
234 r = parse_boolean(parameter);
238 return (on_ac_power() != 0) == !!r;
241 bool condition_test(Condition *c) {
246 case CONDITION_PATH_EXISTS:
247 return (access(c->parameter, F_OK) >= 0) == !c->negate;
249 case CONDITION_PATH_EXISTS_GLOB:
250 return (glob_exists(c->parameter) > 0) == !c->negate;
252 case CONDITION_PATH_IS_DIRECTORY: {
255 if (stat(c->parameter, &st) < 0)
257 return S_ISDIR(st.st_mode) == !c->negate;
260 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
263 if (lstat(c->parameter, &st) < 0)
265 return S_ISLNK(st.st_mode) == !c->negate;
268 case CONDITION_PATH_IS_MOUNT_POINT:
269 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
271 case CONDITION_PATH_IS_READ_WRITE:
272 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
274 case CONDITION_DIRECTORY_NOT_EMPTY: {
277 k = dir_is_empty(c->parameter);
278 return !(k == -ENOENT || k > 0) == !c->negate;
281 case CONDITION_FILE_NOT_EMPTY: {
284 if (stat(c->parameter, &st) < 0)
287 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
290 case CONDITION_FILE_IS_EXECUTABLE: {
293 if (stat(c->parameter, &st) < 0)
296 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
299 case CONDITION_KERNEL_COMMAND_LINE:
300 return test_kernel_command_line(c->parameter) == !c->negate;
302 case CONDITION_VIRTUALIZATION:
303 return test_virtualization(c->parameter) == !c->negate;
305 case CONDITION_SECURITY:
306 return test_security(c->parameter) == !c->negate;
308 case CONDITION_CAPABILITY:
309 return test_capability(c->parameter) == !c->negate;
312 return test_host(c->parameter) == !c->negate;
314 case CONDITION_AC_POWER:
315 return test_ac_power(c->parameter) == !c->negate;
321 assert_not_reached("Invalid condition type.");
325 bool condition_test_list(Condition *first) {
329 /* If the condition list is empty, then it is true */
333 /* Otherwise, if all of the non-trigger conditions apply and
334 * if any of the trigger conditions apply (unless there are
335 * none) we return true */
336 LIST_FOREACH(conditions, c, first) {
339 b = condition_test(c);
341 if (!c->trigger && !b)
344 if (c->trigger && triggered <= 0)
348 return triggered != 0;
351 void condition_dump(Condition *c, FILE *f, const char *prefix) {
361 condition_type_to_string(c->type),
362 c->trigger ? "|" : "",
363 c->negate ? "!" : "",
367 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
370 LIST_FOREACH(conditions, c, first)
371 condition_dump(c, f, prefix);
374 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
375 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
376 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
377 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
378 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
379 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
380 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
381 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
382 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
383 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
384 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
385 [CONDITION_SECURITY] = "ConditionSecurity",
386 [CONDITION_HOST] = "ConditionHost",
387 [CONDITION_AC_POWER] = "ConditionACPower",
388 [CONDITION_NULL] = "ConditionNull"
391 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);