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"
40 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
43 assert(type < _CONDITION_TYPE_MAX);
45 c = new0(Condition, 1);
54 c->parameter = strdup(parameter);
64 void condition_free(Condition *c) {
71 void condition_free_list(Condition *first) {
74 LIST_FOREACH_SAFE(conditions, c, n, first)
78 static bool test_kernel_command_line(const char *parameter) {
79 char *line, *w, *state, *word = NULL;
87 if (detect_container(NULL) > 0)
90 r = read_one_line_file("/proc/cmdline", &line);
92 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
96 equal = !!strchr(parameter, '=');
97 pl = strlen(parameter);
99 FOREACH_WORD_QUOTED(w, l, line, state) {
102 word = strndup(w, l);
107 if (streq(word, parameter)) {
112 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
126 static bool test_virtualization(const char *parameter) {
133 v = detect_virtualization(&id);
135 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
139 /* First, compare with yes/no */
140 b = parse_boolean(parameter);
145 if (v == 0 && b == 0)
148 /* Then, compare categorization */
149 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
152 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
155 /* Finally compare id */
156 return v > 0 && streq(parameter, id);
159 static bool test_security(const char *parameter) {
161 if (streq(parameter, "selinux"))
162 return is_selinux_enabled() > 0;
167 static bool test_capability(const char *parameter) {
171 unsigned long long capabilities = (unsigned long long) -1;
173 /* If it's an invalid capability, we don't have it */
175 if (cap_from_name(parameter, &value) < 0)
178 /* If it's a valid capability we default to assume
181 f = fopen("/proc/self/status", "re");
185 while (fgets(line, sizeof(line), f)) {
188 if (startswith(line, "CapBnd:")) {
189 (void) sscanf(line+7, "%llx", &capabilities);
196 return !!(capabilities & (1ULL << value));
199 static bool test_host(const char *parameter) {
205 if (sd_id128_from_string(parameter, &x) >= 0) {
207 r = sd_id128_get_machine(&y);
211 return sd_id128_equal(x, y);
214 h = gethostname_malloc();
218 b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
224 static bool test_ac_power(const char *parameter) {
227 r = parse_boolean(parameter);
231 return (on_ac_power() != 0) == !!r;
234 bool condition_test(Condition *c) {
239 case CONDITION_PATH_EXISTS:
240 return (access(c->parameter, F_OK) >= 0) == !c->negate;
242 case CONDITION_PATH_EXISTS_GLOB:
243 return (glob_exists(c->parameter) > 0) == !c->negate;
245 case CONDITION_PATH_IS_DIRECTORY: {
248 if (stat(c->parameter, &st) < 0)
250 return S_ISDIR(st.st_mode) == !c->negate;
253 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
256 if (lstat(c->parameter, &st) < 0)
258 return S_ISLNK(st.st_mode) == !c->negate;
261 case CONDITION_PATH_IS_MOUNT_POINT:
262 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
264 case CONDITION_PATH_IS_READ_WRITE:
265 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
267 case CONDITION_DIRECTORY_NOT_EMPTY: {
270 k = dir_is_empty(c->parameter);
271 return !(k == -ENOENT || k > 0) == !c->negate;
274 case CONDITION_FILE_NOT_EMPTY: {
277 if (stat(c->parameter, &st) < 0)
280 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
283 case CONDITION_FILE_IS_EXECUTABLE: {
286 if (stat(c->parameter, &st) < 0)
289 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
292 case CONDITION_KERNEL_COMMAND_LINE:
293 return test_kernel_command_line(c->parameter) == !c->negate;
295 case CONDITION_VIRTUALIZATION:
296 return test_virtualization(c->parameter) == !c->negate;
298 case CONDITION_SECURITY:
299 return test_security(c->parameter) == !c->negate;
301 case CONDITION_CAPABILITY:
302 return test_capability(c->parameter) == !c->negate;
305 return test_host(c->parameter) == !c->negate;
307 case CONDITION_AC_POWER:
308 return test_ac_power(c->parameter) == !c->negate;
314 assert_not_reached("Invalid condition type.");
318 bool condition_test_list(Condition *first) {
322 /* If the condition list is empty, then it is true */
326 /* Otherwise, if all of the non-trigger conditions apply and
327 * if any of the trigger conditions apply (unless there are
328 * none) we return true */
329 LIST_FOREACH(conditions, c, first) {
332 b = condition_test(c);
334 if (!c->trigger && !b)
337 if (c->trigger && triggered <= 0)
341 return triggered != 0;
344 void condition_dump(Condition *c, FILE *f, const char *prefix) {
354 condition_type_to_string(c->type),
355 c->trigger ? "|" : "",
356 c->negate ? "!" : "",
360 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
363 LIST_FOREACH(conditions, c, first)
364 condition_dump(c, f, prefix);
367 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
368 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
369 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
370 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
371 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
372 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
373 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
374 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
375 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
376 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
377 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
378 [CONDITION_SECURITY] = "ConditionSecurity",
379 [CONDITION_HOST] = "ConditionHost",
380 [CONDITION_AC_POWER] = "ConditionACPower",
381 [CONDITION_NULL] = "ConditionNull"
384 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);