]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/condition.c
tests: add tests for string lookup tables
[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 #include <sys/statvfs.h>
28 #include <fnmatch.h>
29
30 #ifdef HAVE_SELINUX
31 #include <selinux/selinux.h>
32 #endif
33
34 #include <systemd/sd-id128.h>
35 #include "util.h"
36 #include "condition.h"
37 #include "virt.h"
38 #include "path-util.h"
39 #include "fileio.h"
40
41 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
42 Condition *c;
43
44 assert(type < _CONDITION_TYPE_MAX);
45
46 c = new0(Condition, 1);
47 if (!c)
48 return NULL;
49
50 c->type = type;
51 c->trigger = trigger;
52 c->negate = negate;
53
54 if (parameter) {
55 c->parameter = strdup(parameter);
56 if (!c->parameter) {
57 free(c);
58 return NULL;
59 }
60 }
61
62 return c;
63 }
64
65 void condition_free(Condition *c) {
66 assert(c);
67
68 free(c->parameter);
69 free(c);
70 }
71
72 void condition_free_list(Condition *first) {
73 Condition *c, *n;
74
75 LIST_FOREACH_SAFE(conditions, c, n, first)
76 condition_free(c);
77 }
78
79 static bool test_kernel_command_line(const char *parameter) {
80 char *line, *w, *state, *word = NULL;
81 bool equal;
82 int r;
83 size_t l, pl;
84 bool found = false;
85
86 assert(parameter);
87
88 if (detect_container(NULL) > 0)
89 return false;
90
91 r = read_one_line_file("/proc/cmdline", &line);
92 if (r < 0) {
93 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
94 return false;
95 }
96
97 equal = !!strchr(parameter, '=');
98 pl = strlen(parameter);
99
100 FOREACH_WORD_QUOTED(w, l, line, state) {
101
102 free(word);
103 word = strndup(w, l);
104 if (!word)
105 break;
106
107 if (equal) {
108 if (streq(word, parameter)) {
109 found = true;
110 break;
111 }
112 } else {
113 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
114 found = true;
115 break;
116 }
117 }
118
119 }
120
121 free(word);
122 free(line);
123
124 return found;
125 }
126
127 static bool test_virtualization(const char *parameter) {
128 int b;
129 Virtualization v;
130 const char *id;
131
132 assert(parameter);
133
134 v = detect_virtualization(&id);
135 if (v < 0) {
136 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
137 return false;
138 }
139
140 /* First, compare with yes/no */
141 b = parse_boolean(parameter);
142
143 if (v > 0 && b > 0)
144 return true;
145
146 if (v == 0 && b == 0)
147 return true;
148
149 /* Then, compare categorization */
150 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
151 return true;
152
153 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
154 return true;
155
156 /* Finally compare id */
157 return v > 0 && streq(parameter, id);
158 }
159
160 static bool test_apparmor_enabled(void) {
161 int r;
162 _cleanup_free_ char *p = NULL;
163
164 r = read_one_line_file("/sys/module/apparmor/parameters/enabled", &p);
165 if (r < 0)
166 return false;
167
168 return parse_boolean(p) > 0;
169 }
170
171 static bool test_security(const char *parameter) {
172 #ifdef HAVE_SELINUX
173 if (streq(parameter, "selinux"))
174 return is_selinux_enabled() > 0;
175 #endif
176 if (streq(parameter, "apparmor"))
177 return test_apparmor_enabled();
178 if (streq(parameter, "ima"))
179 return access("/sys/kernel/security/ima/", F_OK) == 0;
180 if (streq(parameter, "smack"))
181 return access("/sys/fs/smackfs", F_OK) == 0;
182 return false;
183 }
184
185 static bool test_capability(const char *parameter) {
186 cap_value_t value;
187 FILE *f;
188 char line[LINE_MAX];
189 unsigned long long capabilities = (unsigned long long) -1;
190
191 /* If it's an invalid capability, we don't have it */
192
193 if (cap_from_name(parameter, &value) < 0)
194 return false;
195
196 /* If it's a valid capability we default to assume
197 * that we have it */
198
199 f = fopen("/proc/self/status", "re");
200 if (!f)
201 return true;
202
203 while (fgets(line, sizeof(line), f)) {
204 truncate_nl(line);
205
206 if (startswith(line, "CapBnd:")) {
207 (void) sscanf(line+7, "%llx", &capabilities);
208 break;
209 }
210 }
211
212 fclose(f);
213
214 return !!(capabilities & (1ULL << value));
215 }
216
217 static bool test_host(const char *parameter) {
218 sd_id128_t x, y;
219 char *h;
220 int r;
221 bool b;
222
223 if (sd_id128_from_string(parameter, &x) >= 0) {
224
225 r = sd_id128_get_machine(&y);
226 if (r < 0)
227 return false;
228
229 return sd_id128_equal(x, y);
230 }
231
232 h = gethostname_malloc();
233 if (!h)
234 return false;
235
236 b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
237 free(h);
238
239 return b;
240 }
241
242 static bool test_ac_power(const char *parameter) {
243 int r;
244
245 r = parse_boolean(parameter);
246 if (r < 0)
247 return true;
248
249 return (on_ac_power() != 0) == !!r;
250 }
251
252 bool condition_test(Condition *c) {
253 assert(c);
254
255 switch(c->type) {
256
257 case CONDITION_PATH_EXISTS:
258 return (access(c->parameter, F_OK) >= 0) == !c->negate;
259
260 case CONDITION_PATH_EXISTS_GLOB:
261 return (glob_exists(c->parameter) > 0) == !c->negate;
262
263 case CONDITION_PATH_IS_DIRECTORY: {
264 struct stat st;
265
266 if (stat(c->parameter, &st) < 0)
267 return c->negate;
268 return S_ISDIR(st.st_mode) == !c->negate;
269 }
270
271 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
272 struct stat st;
273
274 if (lstat(c->parameter, &st) < 0)
275 return c->negate;
276 return S_ISLNK(st.st_mode) == !c->negate;
277 }
278
279 case CONDITION_PATH_IS_MOUNT_POINT:
280 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
281
282 case CONDITION_PATH_IS_READ_WRITE:
283 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
284
285 case CONDITION_DIRECTORY_NOT_EMPTY: {
286 int k;
287
288 k = dir_is_empty(c->parameter);
289 return !(k == -ENOENT || k > 0) == !c->negate;
290 }
291
292 case CONDITION_FILE_NOT_EMPTY: {
293 struct stat st;
294
295 if (stat(c->parameter, &st) < 0)
296 return c->negate;
297
298 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
299 }
300
301 case CONDITION_FILE_IS_EXECUTABLE: {
302 struct stat st;
303
304 if (stat(c->parameter, &st) < 0)
305 return c->negate;
306
307 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
308 }
309
310 case CONDITION_KERNEL_COMMAND_LINE:
311 return test_kernel_command_line(c->parameter) == !c->negate;
312
313 case CONDITION_VIRTUALIZATION:
314 return test_virtualization(c->parameter) == !c->negate;
315
316 case CONDITION_SECURITY:
317 return test_security(c->parameter) == !c->negate;
318
319 case CONDITION_CAPABILITY:
320 return test_capability(c->parameter) == !c->negate;
321
322 case CONDITION_HOST:
323 return test_host(c->parameter) == !c->negate;
324
325 case CONDITION_AC_POWER:
326 return test_ac_power(c->parameter) == !c->negate;
327
328 case CONDITION_NULL:
329 return !c->negate;
330
331 default:
332 assert_not_reached("Invalid condition type.");
333 }
334 }
335
336 bool condition_test_list(Condition *first) {
337 Condition *c;
338 int triggered = -1;
339
340 /* If the condition list is empty, then it is true */
341 if (!first)
342 return true;
343
344 /* Otherwise, if all of the non-trigger conditions apply and
345 * if any of the trigger conditions apply (unless there are
346 * none) we return true */
347 LIST_FOREACH(conditions, c, first) {
348 bool b;
349
350 b = condition_test(c);
351
352 if (!c->trigger && !b)
353 return false;
354
355 if (c->trigger && triggered <= 0)
356 triggered = b;
357 }
358
359 return triggered != 0;
360 }
361
362 void condition_dump(Condition *c, FILE *f, const char *prefix) {
363 assert(c);
364 assert(f);
365
366 if (!prefix)
367 prefix = "";
368
369 fprintf(f,
370 "%s\t%s: %s%s%s\n",
371 prefix,
372 condition_type_to_string(c->type),
373 c->trigger ? "|" : "",
374 c->negate ? "!" : "",
375 c->parameter);
376 }
377
378 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
379 Condition *c;
380
381 LIST_FOREACH(conditions, c, first)
382 condition_dump(c, f, prefix);
383 }
384
385 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
386 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
387 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
388 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
389 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
390 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
391 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
392 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
393 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
394 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
395 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
396 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
397 [CONDITION_SECURITY] = "ConditionSecurity",
398 [CONDITION_CAPABILITY] = "ConditionCapability",
399 [CONDITION_HOST] = "ConditionHost",
400 [CONDITION_AC_POWER] = "ConditionACPower",
401 [CONDITION_NULL] = "ConditionNull"
402 };
403
404 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);