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"
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 if (detect_container(NULL) > 0)
92 r = read_one_line_file("/proc/cmdline", &line);
94 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
98 equal = !!strchr(parameter, '=');
99 pl = strlen(parameter);
101 FOREACH_WORD_QUOTED(w, l, line, state) {
104 word = strndup(w, l);
109 if (streq(word, parameter)) {
114 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
128 static bool test_virtualization(const char *parameter) {
135 v = detect_virtualization(&id);
137 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
141 /* First, compare with yes/no */
142 b = parse_boolean(parameter);
147 if (v == 0 && b == 0)
150 /* Then, compare categorization */
151 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
154 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
157 /* Finally compare id */
158 return v > 0 && streq(parameter, id);
161 static bool test_apparmor_enabled(void) {
163 _cleanup_free_ char *p = NULL;
165 r = read_one_line_file("/sys/module/apparmor/parameters/enabled", &p);
169 return parse_boolean(p) > 0;
172 static bool test_security(const char *parameter) {
174 if (streq(parameter, "selinux"))
175 return is_selinux_enabled() > 0;
177 if (streq(parameter, "apparmor"))
178 return test_apparmor_enabled();
179 if (streq(parameter, "ima"))
180 return access("/sys/kernel/security/ima/", F_OK) == 0;
181 if (streq(parameter, "smack"))
182 return access("/sys/fs/smackfs", F_OK) == 0;
186 static bool test_capability(const char *parameter) {
190 unsigned long long capabilities = (unsigned long long) -1;
192 /* If it's an invalid capability, we don't have it */
194 if (cap_from_name(parameter, &value) < 0)
197 /* If it's a valid capability we default to assume
200 f = fopen("/proc/self/status", "re");
204 while (fgets(line, sizeof(line), f)) {
207 if (startswith(line, "CapBnd:")) {
208 (void) sscanf(line+7, "%llx", &capabilities);
215 return !!(capabilities & (1ULL << value));
218 static bool test_host(const char *parameter) {
224 if (sd_id128_from_string(parameter, &x) >= 0) {
226 r = sd_id128_get_machine(&y);
230 return sd_id128_equal(x, y);
233 h = gethostname_malloc();
237 b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
243 static bool test_ac_power(const char *parameter) {
246 r = parse_boolean(parameter);
250 return (on_ac_power() != 0) == !!r;
253 static bool condition_test(Condition *c) {
258 case CONDITION_PATH_EXISTS:
259 return (access(c->parameter, F_OK) >= 0) == !c->negate;
261 case CONDITION_PATH_EXISTS_GLOB:
262 return (glob_exists(c->parameter) > 0) == !c->negate;
264 case CONDITION_PATH_IS_DIRECTORY: {
267 if (stat(c->parameter, &st) < 0)
269 return S_ISDIR(st.st_mode) == !c->negate;
272 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
275 if (lstat(c->parameter, &st) < 0)
277 return S_ISLNK(st.st_mode) == !c->negate;
280 case CONDITION_PATH_IS_MOUNT_POINT:
281 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
283 case CONDITION_PATH_IS_READ_WRITE:
284 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
286 case CONDITION_DIRECTORY_NOT_EMPTY: {
289 k = dir_is_empty(c->parameter);
290 return !(k == -ENOENT || k > 0) == !c->negate;
293 case CONDITION_FILE_NOT_EMPTY: {
296 if (stat(c->parameter, &st) < 0)
299 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
302 case CONDITION_FILE_IS_EXECUTABLE: {
305 if (stat(c->parameter, &st) < 0)
308 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
311 case CONDITION_KERNEL_COMMAND_LINE:
312 return test_kernel_command_line(c->parameter) == !c->negate;
314 case CONDITION_VIRTUALIZATION:
315 return test_virtualization(c->parameter) == !c->negate;
317 case CONDITION_SECURITY:
318 return test_security(c->parameter) == !c->negate;
320 case CONDITION_CAPABILITY:
321 return test_capability(c->parameter) == !c->negate;
324 return test_host(c->parameter) == !c->negate;
326 case CONDITION_AC_POWER:
327 return test_ac_power(c->parameter) == !c->negate;
333 assert_not_reached("Invalid condition type.");
337 bool condition_test_list(const char *unit, Condition *first) {
341 /* If the condition list is empty, then it is true */
345 /* Otherwise, if all of the non-trigger conditions apply and
346 * if any of the trigger conditions apply (unless there are
347 * none) we return true */
348 LIST_FOREACH(conditions, c, first) {
351 b = condition_test(c);
354 "%s=%s%s%s %s for %s.",
355 condition_type_to_string(c->type),
356 c->trigger ? "|" : "",
357 c->negate ? "!" : "",
359 b ? "succeeded" : "failed",
361 c->state = b ? 1 : -1;
363 if (!c->trigger && !b)
366 if (c->trigger && triggered <= 0)
370 return triggered != 0;
373 void condition_dump(Condition *c, FILE *f, const char *prefix) {
381 "%s\t%s: %s%s%s %s\n",
383 condition_type_to_string(c->type),
384 c->trigger ? "|" : "",
385 c->negate ? "!" : "",
387 c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
390 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
393 LIST_FOREACH(conditions, c, first)
394 condition_dump(c, f, prefix);
397 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
398 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
399 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
400 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
401 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
402 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
403 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
404 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
405 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
406 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
407 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
408 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
409 [CONDITION_SECURITY] = "ConditionSecurity",
410 [CONDITION_CAPABILITY] = "ConditionCapability",
411 [CONDITION_HOST] = "ConditionHost",
412 [CONDITION_AC_POWER] = "ConditionACPower",
413 [CONDITION_NULL] = "ConditionNull"
416 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);