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>
29 #include <selinux/selinux.h>
33 #include "condition.h"
36 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
39 assert(type < _CONDITION_TYPE_MAX);
41 c = new0(Condition, 1);
50 c->parameter = strdup(parameter);
60 void condition_free(Condition *c) {
67 void condition_free_list(Condition *first) {
70 LIST_FOREACH_SAFE(conditions, c, n, first)
74 static bool test_kernel_command_line(const char *parameter) {
75 char *line, *w, *state, *word = NULL;
83 if (detect_container(NULL) > 0)
86 r = read_one_line_file("/proc/cmdline", &line);
88 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
92 equal = !!strchr(parameter, '=');
93 pl = strlen(parameter);
95 FOREACH_WORD_QUOTED(w, l, line, state) {
103 if (streq(word, parameter)) {
108 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
122 static bool test_virtualization(const char *parameter) {
129 v = detect_virtualization(&id);
131 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
135 /* First, compare with yes/no */
136 b = parse_boolean(parameter);
141 if (v == 0 && b == 0)
144 /* Then, compare categorization */
145 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
148 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
151 /* Finally compare id */
152 return v > 0 && streq(parameter, id);
155 static bool test_security(const char *parameter) {
157 if (streq(parameter, "selinux"))
158 return is_selinux_enabled() > 0;
163 static bool test_capability(const char *parameter) {
167 unsigned long long capabilities = (unsigned long long) -1;
169 /* If it's an invalid capability, we don't have it */
171 if (cap_from_name(parameter, &value) < 0)
174 /* If it's a valid capability we default to assume
177 f = fopen("/proc/self/status", "re");
181 while (fgets(line, sizeof(line), f)) {
184 if (startswith(line, "CapBnd:")) {
185 (void) sscanf(line+7, "%llx", &capabilities);
192 return !!(capabilities & (1ULL << value));
195 bool condition_test(Condition *c) {
200 case CONDITION_PATH_EXISTS:
201 return (access(c->parameter, F_OK) >= 0) == !c->negate;
203 case CONDITION_PATH_EXISTS_GLOB:
204 return (glob_exists(c->parameter) > 0) == !c->negate;
206 case CONDITION_PATH_IS_DIRECTORY: {
209 if (stat(c->parameter, &st) < 0)
211 return S_ISDIR(st.st_mode) == !c->negate;
214 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
217 if (lstat(c->parameter, &st) < 0)
219 return S_ISLNK(st.st_mode) == !c->negate;
222 case CONDITION_PATH_IS_MOUNT_POINT:
223 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
225 case CONDITION_DIRECTORY_NOT_EMPTY: {
228 k = dir_is_empty(c->parameter);
229 return !(k == -ENOENT || k > 0) == !c->negate;
232 case CONDITION_FILE_IS_EXECUTABLE: {
235 if (stat(c->parameter, &st) < 0)
238 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
241 case CONDITION_KERNEL_COMMAND_LINE:
242 return test_kernel_command_line(c->parameter) == !c->negate;
244 case CONDITION_VIRTUALIZATION:
245 return test_virtualization(c->parameter) == !c->negate;
247 case CONDITION_SECURITY:
248 return test_security(c->parameter) == !c->negate;
250 case CONDITION_CAPABILITY:
251 return test_capability(c->parameter) == !c->negate;
257 assert_not_reached("Invalid condition type.");
261 bool condition_test_list(Condition *first) {
265 /* If the condition list is empty, then it is true */
269 /* Otherwise, if all of the non-trigger conditions apply and
270 * if any of the trigger conditions apply (unless there are
271 * none) we return true */
272 LIST_FOREACH(conditions, c, first) {
275 b = condition_test(c);
277 if (!c->trigger && !b)
280 if (c->trigger && triggered <= 0)
284 return triggered != 0;
287 void condition_dump(Condition *c, FILE *f, const char *prefix) {
297 condition_type_to_string(c->type),
298 c->trigger ? "|" : "",
299 c->negate ? "!" : "",
303 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
306 LIST_FOREACH(conditions, c, first)
307 condition_dump(c, f, prefix);
310 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
311 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
312 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
313 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
314 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
315 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
316 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
317 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
318 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
319 [CONDITION_SECURITY] = "ConditionSecurity",
320 [CONDITION_NULL] = "ConditionNull"
323 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);