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 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_security(const char *parameter) {
162 if (streq(parameter, "selinux"))
163 return use_selinux();
164 if (streq(parameter, "apparmor"))
165 return use_apparmor();
166 if (streq(parameter, "ima"))
168 if (streq(parameter, "smack"))
173 static bool test_capability(const char *parameter) {
177 unsigned long long capabilities = (unsigned long long) -1;
179 /* If it's an invalid capability, we don't have it */
181 if (cap_from_name(parameter, &value) < 0)
184 /* If it's a valid capability we default to assume
187 f = fopen("/proc/self/status", "re");
191 while (fgets(line, sizeof(line), f)) {
194 if (startswith(line, "CapBnd:")) {
195 (void) sscanf(line+7, "%llx", &capabilities);
202 return !!(capabilities & (1ULL << value));
205 static bool test_host(const char *parameter) {
211 if (sd_id128_from_string(parameter, &x) >= 0) {
213 r = sd_id128_get_machine(&y);
217 return sd_id128_equal(x, y);
220 h = gethostname_malloc();
224 b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
230 static bool test_ac_power(const char *parameter) {
233 r = parse_boolean(parameter);
237 return (on_ac_power() != 0) == !!r;
240 static bool condition_test(Condition *c) {
245 case CONDITION_PATH_EXISTS:
246 return (access(c->parameter, F_OK) >= 0) == !c->negate;
248 case CONDITION_PATH_EXISTS_GLOB:
249 return (glob_exists(c->parameter) > 0) == !c->negate;
251 case CONDITION_PATH_IS_DIRECTORY: {
254 if (stat(c->parameter, &st) < 0)
256 return S_ISDIR(st.st_mode) == !c->negate;
259 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
262 if (lstat(c->parameter, &st) < 0)
264 return S_ISLNK(st.st_mode) == !c->negate;
267 case CONDITION_PATH_IS_MOUNT_POINT:
268 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
270 case CONDITION_PATH_IS_READ_WRITE:
271 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
273 case CONDITION_DIRECTORY_NOT_EMPTY: {
276 k = dir_is_empty(c->parameter);
277 return !(k == -ENOENT || k > 0) == !c->negate;
280 case CONDITION_FILE_NOT_EMPTY: {
283 if (stat(c->parameter, &st) < 0)
286 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
289 case CONDITION_FILE_IS_EXECUTABLE: {
292 if (stat(c->parameter, &st) < 0)
295 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
298 case CONDITION_KERNEL_COMMAND_LINE:
299 return test_kernel_command_line(c->parameter) == !c->negate;
301 case CONDITION_VIRTUALIZATION:
302 return test_virtualization(c->parameter) == !c->negate;
304 case CONDITION_SECURITY:
305 return test_security(c->parameter) == !c->negate;
307 case CONDITION_CAPABILITY:
308 return test_capability(c->parameter) == !c->negate;
311 return test_host(c->parameter) == !c->negate;
313 case CONDITION_AC_POWER:
314 return test_ac_power(c->parameter) == !c->negate;
320 assert_not_reached("Invalid condition type.");
324 bool condition_test_list(const char *unit, Condition *first) {
328 /* If the condition list is empty, then it is true */
332 /* Otherwise, if all of the non-trigger conditions apply and
333 * if any of the trigger conditions apply (unless there are
334 * none) we return true */
335 LIST_FOREACH(conditions, c, first) {
338 b = condition_test(c);
341 "%s=%s%s%s %s for %s.",
342 condition_type_to_string(c->type),
343 c->trigger ? "|" : "",
344 c->negate ? "!" : "",
346 b ? "succeeded" : "failed",
348 c->state = b ? 1 : -1;
350 if (!c->trigger && !b)
353 if (c->trigger && triggered <= 0)
357 return triggered != 0;
360 void condition_dump(Condition *c, FILE *f, const char *prefix) {
368 "%s\t%s: %s%s%s %s\n",
370 condition_type_to_string(c->type),
371 c->trigger ? "|" : "",
372 c->negate ? "!" : "",
374 c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
377 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
380 LIST_FOREACH(conditions, c, first)
381 condition_dump(c, f, prefix);
384 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
385 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
386 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
387 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
388 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
389 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
390 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
391 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
392 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
393 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
394 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
395 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
396 [CONDITION_SECURITY] = "ConditionSecurity",
397 [CONDITION_CAPABILITY] = "ConditionCapability",
398 [CONDITION_HOST] = "ConditionHost",
399 [CONDITION_AC_POWER] = "ConditionACPower",
400 [CONDITION_NULL] = "ConditionNull"
403 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);