]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/condition.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / core / condition.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/capability.h>
27
28 #ifdef HAVE_SELINUX
29 #include <selinux/selinux.h>
30 #endif
31
32 #include "util.h"
33 #include "condition.h"
34 #include "virt.h"
35
36 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
37 Condition *c;
38
39 assert(type < _CONDITION_TYPE_MAX);
40
41 c = new0(Condition, 1);
42 if (!c)
43 return NULL;
44
45 c->type = type;
46 c->trigger = trigger;
47 c->negate = negate;
48
49 if (parameter) {
50 c->parameter = strdup(parameter);
51 if (!c->parameter) {
52 free(c);
53 return NULL;
54 }
55 }
56
57 return c;
58 }
59
60 void condition_free(Condition *c) {
61 assert(c);
62
63 free(c->parameter);
64 free(c);
65 }
66
67 void condition_free_list(Condition *first) {
68 Condition *c, *n;
69
70 LIST_FOREACH_SAFE(conditions, c, n, first)
71 condition_free(c);
72 }
73
74 static bool test_kernel_command_line(const char *parameter) {
75 char *line, *w, *state, *word = NULL;
76 bool equal;
77 int r;
78 size_t l, pl;
79 bool found = false;
80
81 assert(parameter);
82
83 if (detect_container(NULL) > 0)
84 return false;
85
86 r = read_one_line_file("/proc/cmdline", &line);
87 if (r < 0) {
88 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
89 return false;
90 }
91
92 equal = !!strchr(parameter, '=');
93 pl = strlen(parameter);
94
95 FOREACH_WORD_QUOTED(w, l, line, state) {
96
97 free(word);
98 word = strndup(w, l);
99 if (!word)
100 break;
101
102 if (equal) {
103 if (streq(word, parameter)) {
104 found = true;
105 break;
106 }
107 } else {
108 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
109 found = true;
110 break;
111 }
112 }
113
114 }
115
116 free(word);
117 free(line);
118
119 return found;
120 }
121
122 static bool test_virtualization(const char *parameter) {
123 int b;
124 Virtualization v;
125 const char *id;
126
127 assert(parameter);
128
129 v = detect_virtualization(&id);
130 if (v < 0) {
131 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
132 return false;
133 }
134
135 /* First, compare with yes/no */
136 b = parse_boolean(parameter);
137
138 if (v > 0 && b > 0)
139 return true;
140
141 if (v == 0 && b == 0)
142 return true;
143
144 /* Then, compare categorization */
145 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
146 return true;
147
148 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
149 return true;
150
151 /* Finally compare id */
152 return v > 0 && streq(parameter, id);
153 }
154
155 static bool test_security(const char *parameter) {
156 #ifdef HAVE_SELINUX
157 if (streq(parameter, "selinux"))
158 return is_selinux_enabled() > 0;
159 #endif
160 return false;
161 }
162
163 static bool test_capability(const char *parameter) {
164 cap_value_t value;
165 FILE *f;
166 char line[LINE_MAX];
167 unsigned long long capabilities = (unsigned long long) -1;
168
169 /* If it's an invalid capability, we don't have it */
170
171 if (cap_from_name(parameter, &value) < 0)
172 return false;
173
174 /* If it's a valid capability we default to assume
175 * that we have it */
176
177 f = fopen("/proc/self/status", "re");
178 if (!f)
179 return true;
180
181 while (fgets(line, sizeof(line), f)) {
182 truncate_nl(line);
183
184 if (startswith(line, "CapBnd:")) {
185 (void) sscanf(line+7, "%llx", &capabilities);
186 break;
187 }
188 }
189
190 fclose(f);
191
192 return !!(capabilities & (1ULL << value));
193 }
194
195 bool condition_test(Condition *c) {
196 assert(c);
197
198 switch(c->type) {
199
200 case CONDITION_PATH_EXISTS:
201 return (access(c->parameter, F_OK) >= 0) == !c->negate;
202
203 case CONDITION_PATH_EXISTS_GLOB:
204 return (glob_exists(c->parameter) > 0) == !c->negate;
205
206 case CONDITION_PATH_IS_DIRECTORY: {
207 struct stat st;
208
209 if (stat(c->parameter, &st) < 0)
210 return c->negate;
211 return S_ISDIR(st.st_mode) == !c->negate;
212 }
213
214 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
215 struct stat st;
216
217 if (lstat(c->parameter, &st) < 0)
218 return c->negate;
219 return S_ISLNK(st.st_mode) == !c->negate;
220 }
221
222 case CONDITION_PATH_IS_MOUNT_POINT:
223 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
224
225 case CONDITION_DIRECTORY_NOT_EMPTY: {
226 int k;
227
228 k = dir_is_empty(c->parameter);
229 return !(k == -ENOENT || k > 0) == !c->negate;
230 }
231
232 case CONDITION_FILE_IS_EXECUTABLE: {
233 struct stat st;
234
235 if (stat(c->parameter, &st) < 0)
236 return c->negate;
237
238 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
239 }
240
241 case CONDITION_KERNEL_COMMAND_LINE:
242 return test_kernel_command_line(c->parameter) == !c->negate;
243
244 case CONDITION_VIRTUALIZATION:
245 return test_virtualization(c->parameter) == !c->negate;
246
247 case CONDITION_SECURITY:
248 return test_security(c->parameter) == !c->negate;
249
250 case CONDITION_CAPABILITY:
251 return test_capability(c->parameter) == !c->negate;
252
253 case CONDITION_NULL:
254 return !c->negate;
255
256 default:
257 assert_not_reached("Invalid condition type.");
258 }
259 }
260
261 bool condition_test_list(Condition *first) {
262 Condition *c;
263 int triggered = -1;
264
265 /* If the condition list is empty, then it is true */
266 if (!first)
267 return true;
268
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) {
273 bool b;
274
275 b = condition_test(c);
276
277 if (!c->trigger && !b)
278 return false;
279
280 if (c->trigger && triggered <= 0)
281 triggered = b;
282 }
283
284 return triggered != 0;
285 }
286
287 void condition_dump(Condition *c, FILE *f, const char *prefix) {
288 assert(c);
289 assert(f);
290
291 if (!prefix)
292 prefix = "";
293
294 fprintf(f,
295 "%s\t%s: %s%s%s\n",
296 prefix,
297 condition_type_to_string(c->type),
298 c->trigger ? "|" : "",
299 c->negate ? "!" : "",
300 c->parameter);
301 }
302
303 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
304 Condition *c;
305
306 LIST_FOREACH(conditions, c, first)
307 condition_dump(c, f, prefix);
308 }
309
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"
321 };
322
323 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);