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"
37 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
40 assert(type < _CONDITION_TYPE_MAX);
42 c = new0(Condition, 1);
51 c->parameter = strdup(parameter);
61 void condition_free(Condition *c) {
68 void condition_free_list(Condition *first) {
71 LIST_FOREACH_SAFE(conditions, c, n, first)
75 static bool test_kernel_command_line(const char *parameter) {
76 char *line, *w, *state, *word = NULL;
84 if (detect_container(NULL) > 0)
87 r = read_one_line_file("/proc/cmdline", &line);
89 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
93 equal = !!strchr(parameter, '=');
94 pl = strlen(parameter);
96 FOREACH_WORD_QUOTED(w, l, line, state) {
104 if (streq(word, parameter)) {
109 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
123 static bool test_virtualization(const char *parameter) {
130 v = detect_virtualization(&id);
132 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
136 /* First, compare with yes/no */
137 b = parse_boolean(parameter);
142 if (v == 0 && b == 0)
145 /* Then, compare categorization */
146 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
149 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
152 /* Finally compare id */
153 return v > 0 && streq(parameter, id);
156 static bool test_security(const char *parameter) {
158 if (streq(parameter, "selinux"))
159 return is_selinux_enabled() > 0;
164 static bool test_capability(const char *parameter) {
168 unsigned long long capabilities = (unsigned long long) -1;
170 /* If it's an invalid capability, we don't have it */
172 if (cap_from_name(parameter, &value) < 0)
175 /* If it's a valid capability we default to assume
178 f = fopen("/proc/self/status", "re");
182 while (fgets(line, sizeof(line), f)) {
185 if (startswith(line, "CapBnd:")) {
186 (void) sscanf(line+7, "%llx", &capabilities);
193 return !!(capabilities & (1ULL << value));
196 bool condition_test(Condition *c) {
201 case CONDITION_PATH_EXISTS:
202 return (access(c->parameter, F_OK) >= 0) == !c->negate;
204 case CONDITION_PATH_EXISTS_GLOB:
205 return (glob_exists(c->parameter) > 0) == !c->negate;
207 case CONDITION_PATH_IS_DIRECTORY: {
210 if (stat(c->parameter, &st) < 0)
212 return S_ISDIR(st.st_mode) == !c->negate;
215 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
218 if (lstat(c->parameter, &st) < 0)
220 return S_ISLNK(st.st_mode) == !c->negate;
223 case CONDITION_PATH_IS_MOUNT_POINT:
224 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
226 case CONDITION_PATH_IS_READ_WRITE: {
229 if (statvfs(c->parameter, &st) < 0)
232 return !(st.f_flag & ST_RDONLY) == !c->negate;
235 case CONDITION_DIRECTORY_NOT_EMPTY: {
238 k = dir_is_empty(c->parameter);
239 return !(k == -ENOENT || k > 0) == !c->negate;
242 case CONDITION_FILE_IS_EXECUTABLE: {
245 if (stat(c->parameter, &st) < 0)
248 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
251 case CONDITION_KERNEL_COMMAND_LINE:
252 return test_kernel_command_line(c->parameter) == !c->negate;
254 case CONDITION_VIRTUALIZATION:
255 return test_virtualization(c->parameter) == !c->negate;
257 case CONDITION_SECURITY:
258 return test_security(c->parameter) == !c->negate;
260 case CONDITION_CAPABILITY:
261 return test_capability(c->parameter) == !c->negate;
267 assert_not_reached("Invalid condition type.");
271 bool condition_test_list(Condition *first) {
275 /* If the condition list is empty, then it is true */
279 /* Otherwise, if all of the non-trigger conditions apply and
280 * if any of the trigger conditions apply (unless there are
281 * none) we return true */
282 LIST_FOREACH(conditions, c, first) {
285 b = condition_test(c);
287 if (!c->trigger && !b)
290 if (c->trigger && triggered <= 0)
294 return triggered != 0;
297 void condition_dump(Condition *c, FILE *f, const char *prefix) {
307 condition_type_to_string(c->type),
308 c->trigger ? "|" : "",
309 c->negate ? "!" : "",
313 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
316 LIST_FOREACH(conditions, c, first)
317 condition_dump(c, f, prefix);
320 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
321 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
322 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
323 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
324 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
325 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
326 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
327 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
328 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
329 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
330 [CONDITION_SECURITY] = "ConditionSecurity",
331 [CONDITION_NULL] = "ConditionNull"
334 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);