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>
30 #include <systemd/sd-id128.h>
32 #include "condition.h"
34 #include "path-util.h"
37 #include "smack-util.h"
38 #include "apparmor-util.h"
40 #include "selinux-util.h"
42 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
45 assert(type < _CONDITION_TYPE_MAX);
47 c = new0(Condition, 1);
56 c->parameter = strdup(parameter);
66 void condition_free(Condition *c) {
73 void condition_free_list(Condition *first) {
76 LIST_FOREACH_SAFE(conditions, c, n, first)
80 static bool test_kernel_command_line(const char *parameter) {
81 char *line, *w, *state, *word = NULL;
89 r = proc_cmdline(&line);
91 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
95 equal = !!strchr(parameter, '=');
96 pl = strlen(parameter);
98 FOREACH_WORD_QUOTED(w, l, line, state) {
101 word = strndup(w, l);
106 if (streq(word, parameter)) {
111 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
125 static bool test_virtualization(const char *parameter) {
132 v = detect_virtualization(&id);
134 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
138 /* First, compare with yes/no */
139 b = parse_boolean(parameter);
144 if (v == 0 && b == 0)
147 /* Then, compare categorization */
148 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
151 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
154 /* Finally compare id */
155 return v > 0 && streq(parameter, id);
158 static bool test_security(const char *parameter) {
159 if (streq(parameter, "selinux"))
160 return use_selinux();
161 if (streq(parameter, "apparmor"))
162 return use_apparmor();
163 if (streq(parameter, "ima"))
165 if (streq(parameter, "smack"))
170 static bool test_capability(const char *parameter) {
174 unsigned long long capabilities = (unsigned long long) -1;
176 /* If it's an invalid capability, we don't have it */
178 if (cap_from_name(parameter, &value) < 0)
181 /* If it's a valid capability we default to assume
184 f = fopen("/proc/self/status", "re");
188 while (fgets(line, sizeof(line), f)) {
191 if (startswith(line, "CapBnd:")) {
192 (void) sscanf(line+7, "%llx", &capabilities);
199 return !!(capabilities & (1ULL << value));
202 static bool test_host(const char *parameter) {
208 if (sd_id128_from_string(parameter, &x) >= 0) {
210 r = sd_id128_get_machine(&y);
214 return sd_id128_equal(x, y);
217 h = gethostname_malloc();
221 b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
227 static bool test_ac_power(const char *parameter) {
230 r = parse_boolean(parameter);
234 return (on_ac_power() != 0) == !!r;
237 static bool condition_test(Condition *c) {
242 case CONDITION_PATH_EXISTS:
243 return (access(c->parameter, F_OK) >= 0) == !c->negate;
245 case CONDITION_PATH_EXISTS_GLOB:
246 return (glob_exists(c->parameter) > 0) == !c->negate;
248 case CONDITION_PATH_IS_DIRECTORY: {
251 if (stat(c->parameter, &st) < 0)
253 return S_ISDIR(st.st_mode) == !c->negate;
256 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
259 if (lstat(c->parameter, &st) < 0)
261 return S_ISLNK(st.st_mode) == !c->negate;
264 case CONDITION_PATH_IS_MOUNT_POINT:
265 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
267 case CONDITION_PATH_IS_READ_WRITE:
268 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
270 case CONDITION_DIRECTORY_NOT_EMPTY: {
273 k = dir_is_empty(c->parameter);
274 return !(k == -ENOENT || k > 0) == !c->negate;
277 case CONDITION_FILE_NOT_EMPTY: {
280 if (stat(c->parameter, &st) < 0)
283 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
286 case CONDITION_FILE_IS_EXECUTABLE: {
289 if (stat(c->parameter, &st) < 0)
292 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
295 case CONDITION_KERNEL_COMMAND_LINE:
296 return test_kernel_command_line(c->parameter) == !c->negate;
298 case CONDITION_VIRTUALIZATION:
299 return test_virtualization(c->parameter) == !c->negate;
301 case CONDITION_SECURITY:
302 return test_security(c->parameter) == !c->negate;
304 case CONDITION_CAPABILITY:
305 return test_capability(c->parameter) == !c->negate;
308 return test_host(c->parameter) == !c->negate;
310 case CONDITION_AC_POWER:
311 return test_ac_power(c->parameter) == !c->negate;
317 assert_not_reached("Invalid condition type.");
321 bool condition_test_list(const char *unit, Condition *first) {
325 /* If the condition list is empty, then it is true */
329 /* Otherwise, if all of the non-trigger conditions apply and
330 * if any of the trigger conditions apply (unless there are
331 * none) we return true */
332 LIST_FOREACH(conditions, c, first) {
335 b = condition_test(c);
338 "%s=%s%s%s %s for %s.",
339 condition_type_to_string(c->type),
340 c->trigger ? "|" : "",
341 c->negate ? "!" : "",
343 b ? "succeeded" : "failed",
345 c->state = b ? 1 : -1;
347 if (!c->trigger && !b)
350 if (c->trigger && triggered <= 0)
354 return triggered != 0;
357 void condition_dump(Condition *c, FILE *f, const char *prefix) {
365 "%s\t%s: %s%s%s %s\n",
367 condition_type_to_string(c->type),
368 c->trigger ? "|" : "",
369 c->negate ? "!" : "",
371 c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
374 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
377 LIST_FOREACH(conditions, c, first)
378 condition_dump(c, f, prefix);
381 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
382 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
383 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
384 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
385 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
386 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
387 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
388 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
389 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
390 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
391 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
392 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
393 [CONDITION_SECURITY] = "ConditionSecurity",
394 [CONDITION_CAPABILITY] = "ConditionCapability",
395 [CONDITION_HOST] = "ConditionHost",
396 [CONDITION_AC_POWER] = "ConditionACPower",
397 [CONDITION_NULL] = "ConditionNull"
400 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);