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 <selinux/selinux.h>
34 #include "condition.h"
36 #include "path-util.h"
38 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
41 assert(type < _CONDITION_TYPE_MAX);
43 c = new0(Condition, 1);
52 c->parameter = strdup(parameter);
62 void condition_free(Condition *c) {
69 void condition_free_list(Condition *first) {
72 LIST_FOREACH_SAFE(conditions, c, n, first)
76 static bool test_kernel_command_line(const char *parameter) {
77 char *line, *w, *state, *word = NULL;
85 if (detect_container(NULL) > 0)
88 r = read_one_line_file("/proc/cmdline", &line);
90 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
94 equal = !!strchr(parameter, '=');
95 pl = strlen(parameter);
97 FOREACH_WORD_QUOTED(w, l, line, state) {
100 word = strndup(w, l);
105 if (streq(word, parameter)) {
110 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
124 static bool test_virtualization(const char *parameter) {
131 v = detect_virtualization(&id);
133 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
137 /* First, compare with yes/no */
138 b = parse_boolean(parameter);
143 if (v == 0 && b == 0)
146 /* Then, compare categorization */
147 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
150 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
153 /* Finally compare id */
154 return v > 0 && streq(parameter, id);
157 static bool test_security(const char *parameter) {
159 if (streq(parameter, "selinux"))
160 return is_selinux_enabled() > 0;
165 static bool test_capability(const char *parameter) {
169 unsigned long long capabilities = (unsigned long long) -1;
171 /* If it's an invalid capability, we don't have it */
173 if (cap_from_name(parameter, &value) < 0)
176 /* If it's a valid capability we default to assume
179 f = fopen("/proc/self/status", "re");
183 while (fgets(line, sizeof(line), f)) {
186 if (startswith(line, "CapBnd:")) {
187 (void) sscanf(line+7, "%llx", &capabilities);
194 return !!(capabilities & (1ULL << value));
197 bool condition_test(Condition *c) {
202 case CONDITION_PATH_EXISTS:
203 return (access(c->parameter, F_OK) >= 0) == !c->negate;
205 case CONDITION_PATH_EXISTS_GLOB:
206 return (glob_exists(c->parameter) > 0) == !c->negate;
208 case CONDITION_PATH_IS_DIRECTORY: {
211 if (stat(c->parameter, &st) < 0)
213 return S_ISDIR(st.st_mode) == !c->negate;
216 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
219 if (lstat(c->parameter, &st) < 0)
221 return S_ISLNK(st.st_mode) == !c->negate;
224 case CONDITION_PATH_IS_MOUNT_POINT:
225 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
227 case CONDITION_PATH_IS_READ_WRITE:
228 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
230 case CONDITION_DIRECTORY_NOT_EMPTY: {
233 k = dir_is_empty(c->parameter);
234 return !(k == -ENOENT || k > 0) == !c->negate;
237 case CONDITION_FILE_IS_EXECUTABLE: {
240 if (stat(c->parameter, &st) < 0)
243 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
246 case CONDITION_KERNEL_COMMAND_LINE:
247 return test_kernel_command_line(c->parameter) == !c->negate;
249 case CONDITION_VIRTUALIZATION:
250 return test_virtualization(c->parameter) == !c->negate;
252 case CONDITION_SECURITY:
253 return test_security(c->parameter) == !c->negate;
255 case CONDITION_CAPABILITY:
256 return test_capability(c->parameter) == !c->negate;
262 assert_not_reached("Invalid condition type.");
266 bool condition_test_list(Condition *first) {
270 /* If the condition list is empty, then it is true */
274 /* Otherwise, if all of the non-trigger conditions apply and
275 * if any of the trigger conditions apply (unless there are
276 * none) we return true */
277 LIST_FOREACH(conditions, c, first) {
280 b = condition_test(c);
282 if (!c->trigger && !b)
285 if (c->trigger && triggered <= 0)
289 return triggered != 0;
292 void condition_dump(Condition *c, FILE *f, const char *prefix) {
302 condition_type_to_string(c->type),
303 c->trigger ? "|" : "",
304 c->negate ? "!" : "",
308 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
311 LIST_FOREACH(conditions, c, first)
312 condition_dump(c, f, prefix);
315 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
316 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
317 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
318 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
319 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
320 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
321 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
322 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
323 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
324 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
325 [CONDITION_SECURITY] = "ConditionSecurity",
326 [CONDITION_NULL] = "ConditionNull"
329 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);