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 condition_test_kernel_command_line(Condition *c) {
81 char *line, *w, *state, *word = NULL;
89 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
91 r = proc_cmdline(&line);
93 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
97 equal = !!strchr(c->parameter, '=');
98 pl = strlen(c->parameter);
100 FOREACH_WORD_QUOTED(w, l, line, state) {
103 word = strndup(w, l);
108 if (streq(word, c->parameter)) {
113 if (startswith(word, c->parameter) && (word[pl] == '=' || word[pl] == 0)) {
124 return found == !c->negate;
127 static bool condition_test_virtualization(Condition *c) {
133 assert(c->parameter);
134 assert(c->type == CONDITION_VIRTUALIZATION);
136 v = detect_virtualization(&id);
138 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
142 /* First, compare with yes/no */
143 b = parse_boolean(c->parameter);
148 if (v == 0 && b == 0)
151 /* Then, compare categorization */
152 if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
155 if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
158 /* Finally compare id */
159 return (v > 0 && streq(c->parameter, id)) == !c->negate;
162 static bool condition_test_security(Condition *c) {
164 assert(c->parameter);
165 assert(c->type == CONDITION_SECURITY);
167 if (streq(c->parameter, "selinux"))
168 return use_selinux() == !c->negate;
169 if (streq(c->parameter, "apparmor"))
170 return use_apparmor() == !c->negate;
171 if (streq(c->parameter, "ima"))
172 return use_ima() == !c->negate;
173 if (streq(c->parameter, "smack"))
174 return use_smack() == !c->negate;
178 static bool condition_test_capability(Condition *c) {
182 unsigned long long capabilities = (unsigned long long) -1;
185 assert(c->parameter);
186 assert(c->type == CONDITION_CAPABILITY);
188 /* If it's an invalid capability, we don't have it */
190 if (cap_from_name(c->parameter, &value) < 0)
193 /* If it's a valid capability we default to assume
196 f = fopen("/proc/self/status", "re");
200 while (fgets(line, sizeof(line), f)) {
203 if (startswith(line, "CapBnd:")) {
204 (void) sscanf(line+7, "%llx", &capabilities);
211 return !!(capabilities & (1ULL << value)) == !c->negate;
214 static bool condition_test_host(Condition *c) {
221 assert(c->parameter);
222 assert(c->type == CONDITION_HOST);
224 if (sd_id128_from_string(c->parameter, &x) >= 0) {
226 r = sd_id128_get_machine(&y);
230 return sd_id128_equal(x, y);
233 h = gethostname_malloc();
237 b = fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
240 return b == !c->negate;
243 static bool condition_test_ac_power(Condition *c) {
247 assert(c->parameter);
248 assert(c->type == CONDITION_AC_POWER);
250 r = parse_boolean(c->parameter);
254 return ((on_ac_power() != 0) == !!r) == !c->negate;
257 static bool condition_test(Condition *c) {
262 case CONDITION_PATH_EXISTS:
263 return (access(c->parameter, F_OK) >= 0) == !c->negate;
265 case CONDITION_PATH_EXISTS_GLOB:
266 return (glob_exists(c->parameter) > 0) == !c->negate;
268 case CONDITION_PATH_IS_DIRECTORY: {
271 if (stat(c->parameter, &st) < 0)
273 return S_ISDIR(st.st_mode) == !c->negate;
276 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
279 if (lstat(c->parameter, &st) < 0)
281 return S_ISLNK(st.st_mode) == !c->negate;
284 case CONDITION_PATH_IS_MOUNT_POINT:
285 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
287 case CONDITION_PATH_IS_READ_WRITE:
288 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
290 case CONDITION_DIRECTORY_NOT_EMPTY: {
293 k = dir_is_empty(c->parameter);
294 return !(k == -ENOENT || k > 0) == !c->negate;
297 case CONDITION_FILE_NOT_EMPTY: {
300 if (stat(c->parameter, &st) < 0)
303 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
306 case CONDITION_FILE_IS_EXECUTABLE: {
309 if (stat(c->parameter, &st) < 0)
312 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
315 case CONDITION_KERNEL_COMMAND_LINE:
316 return condition_test_kernel_command_line(c);
318 case CONDITION_VIRTUALIZATION:
319 return condition_test_virtualization(c);
321 case CONDITION_SECURITY:
322 return condition_test_security(c);
324 case CONDITION_CAPABILITY:
325 return condition_test_capability(c);
328 return condition_test_host(c);
330 case CONDITION_AC_POWER:
331 return condition_test_ac_power(c);
337 assert_not_reached("Invalid condition type.");
341 bool condition_test_list(const char *unit, Condition *first) {
345 /* If the condition list is empty, then it is true */
349 /* Otherwise, if all of the non-trigger conditions apply and
350 * if any of the trigger conditions apply (unless there are
351 * none) we return true */
352 LIST_FOREACH(conditions, c, first) {
355 b = condition_test(c);
358 "%s=%s%s%s %s for %s.",
359 condition_type_to_string(c->type),
360 c->trigger ? "|" : "",
361 c->negate ? "!" : "",
363 b ? "succeeded" : "failed",
365 c->state = b ? 1 : -1;
367 if (!c->trigger && !b)
370 if (c->trigger && triggered <= 0)
374 return triggered != 0;
377 void condition_dump(Condition *c, FILE *f, const char *prefix) {
385 "%s\t%s: %s%s%s %s\n",
387 condition_type_to_string(c->type),
388 c->trigger ? "|" : "",
389 c->negate ? "!" : "",
391 c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
394 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
397 LIST_FOREACH(conditions, c, first)
398 condition_dump(c, f, prefix);
401 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
402 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
403 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
404 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
405 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
406 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
407 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
408 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
409 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
410 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
411 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
412 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
413 [CONDITION_SECURITY] = "ConditionSecurity",
414 [CONDITION_CAPABILITY] = "ConditionCapability",
415 [CONDITION_HOST] = "ConditionHost",
416 [CONDITION_AC_POWER] = "ConditionACPower",
417 [CONDITION_NULL] = "ConditionNull"
420 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);