]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/condition-util.c
condition: internalize condition test functions
[thirdparty/systemd.git] / src / shared / condition-util.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/statvfs.h>
27 #include <fnmatch.h>
28
29 #include "sd-id128.h"
30 #include "util.h"
31 #include "condition-util.h"
32 #include "virt.h"
33 #include "path-util.h"
34 #include "fileio.h"
35 #include "unit.h"
36 #include "architecture.h"
37 #include "virt.h"
38 #include "smack-util.h"
39 #include "apparmor-util.h"
40 #include "ima-util.h"
41 #include "selinux-util.h"
42 #include "audit.h"
43
44 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
45 Condition *c;
46 int r;
47
48 assert(type < _CONDITION_TYPE_MAX);
49
50 c = new0(Condition, 1);
51 if (!c)
52 return NULL;
53
54 c->type = type;
55 c->trigger = trigger;
56 c->negate = negate;
57
58 r = free_and_strdup(&c->parameter, parameter);
59 if (r < 0) {
60 free(c);
61 return NULL;
62 }
63
64 return c;
65 }
66
67 void condition_free(Condition *c) {
68 assert(c);
69
70 free(c->parameter);
71 free(c);
72 }
73
74 void condition_free_list(Condition *first) {
75 Condition *c, *n;
76
77 LIST_FOREACH_SAFE(conditions, c, n, first)
78 condition_free(c);
79 }
80
81 static int condition_test_kernel_command_line(Condition *c) {
82 _cleanup_free_ char *line = NULL;
83 const char *p;
84 bool equal;
85 int r;
86
87 assert(c);
88 assert(c->parameter);
89 assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
90
91 r = proc_cmdline(&line);
92 if (r < 0)
93 return r;
94 if (r == 0)
95 return false;
96
97 equal = !!strchr(c->parameter, '=');
98 p = line;
99
100 for (;;) {
101 _cleanup_free_ char *word = NULL;
102 bool found;
103
104 r = unquote_first_word(&p, &word);
105 if (r < 0)
106 return r;
107 if (r == 0)
108 break;
109
110 if (equal)
111 found = streq(word, c->parameter);
112 else {
113 const char *f;
114
115 f = startswith(word, c->parameter);
116 found = f && (*f == '=' || *f == 0);
117 }
118
119 if (found)
120 return true;
121 }
122
123 return false;
124 }
125
126 static int condition_test_virtualization(Condition *c) {
127 int b, v;
128 const char *id;
129
130 assert(c);
131 assert(c->parameter);
132 assert(c->type == CONDITION_VIRTUALIZATION);
133
134 v = detect_virtualization(&id);
135 if (v < 0)
136 return v;
137
138 /* First, compare with yes/no */
139 b = parse_boolean(c->parameter);
140
141 if (v > 0 && b > 0)
142 return true;
143
144 if (v == 0 && b == 0)
145 return true;
146
147 /* Then, compare categorization */
148 if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
149 return true;
150
151 if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
152 return true;
153
154 /* Finally compare id */
155 return v > 0 && streq(c->parameter, id);
156 }
157
158 static int condition_test_architecture(Condition *c) {
159 int a, b;
160
161 assert(c);
162 assert(c->parameter);
163 assert(c->type == CONDITION_ARCHITECTURE);
164
165 a = uname_architecture();
166 if (a < 0)
167 return a;
168
169 if (streq(c->parameter, "native"))
170 b = native_architecture();
171 else
172 b = architecture_from_string(c->parameter);
173 if (b < 0)
174 return b;
175
176 return a == b;
177 }
178
179 static int condition_test_host(Condition *c) {
180 _cleanup_free_ char *h = NULL;
181 sd_id128_t x, y;
182 int r;
183
184 assert(c);
185 assert(c->parameter);
186 assert(c->type == CONDITION_HOST);
187
188 if (sd_id128_from_string(c->parameter, &x) >= 0) {
189
190 r = sd_id128_get_machine(&y);
191 if (r < 0)
192 return r;
193
194 return sd_id128_equal(x, y);
195 }
196
197 h = gethostname_malloc();
198 if (!h)
199 return -ENOMEM;
200
201 return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
202 }
203
204 static int condition_test_ac_power(Condition *c) {
205 int r;
206
207 assert(c);
208 assert(c->parameter);
209 assert(c->type == CONDITION_AC_POWER);
210
211 r = parse_boolean(c->parameter);
212 if (r < 0)
213 return r;
214
215 return (on_ac_power() != 0) == !!r;
216 }
217
218 static int condition_test_security(Condition *c) {
219 assert(c);
220 assert(c->parameter);
221 assert(c->type == CONDITION_SECURITY);
222
223 if (streq(c->parameter, "selinux"))
224 return mac_selinux_use();
225 if (streq(c->parameter, "smack"))
226 return mac_smack_use();
227 if (streq(c->parameter, "apparmor"))
228 return mac_apparmor_use();
229 if (streq(c->parameter, "audit"))
230 return use_audit();
231 if (streq(c->parameter, "ima"))
232 return use_ima();
233
234 return false;
235 }
236
237 static int condition_test_capability(Condition *c) {
238 _cleanup_fclose_ FILE *f = NULL;
239 cap_value_t value;
240 char line[LINE_MAX];
241 unsigned long long capabilities = -1;
242
243 assert(c);
244 assert(c->parameter);
245 assert(c->type == CONDITION_CAPABILITY);
246
247 /* If it's an invalid capability, we don't have it */
248
249 if (cap_from_name(c->parameter, &value) < 0)
250 return -EINVAL;
251
252 /* If it's a valid capability we default to assume
253 * that we have it */
254
255 f = fopen("/proc/self/status", "re");
256 if (!f)
257 return -errno;
258
259 while (fgets(line, sizeof(line), f)) {
260 truncate_nl(line);
261
262 if (startswith(line, "CapBnd:")) {
263 (void) sscanf(line+7, "%llx", &capabilities);
264 break;
265 }
266 }
267
268 return !!(capabilities & (1ULL << value));
269 }
270
271 static int condition_test_needs_update(Condition *c) {
272 const char *p;
273 struct stat usr, other;
274
275 assert(c);
276 assert(c->parameter);
277 assert(c->type == CONDITION_NEEDS_UPDATE);
278
279 /* If the file system is read-only we shouldn't suggest an update */
280 if (path_is_read_only_fs(c->parameter) > 0)
281 return false;
282
283 /* Any other failure means we should allow the condition to be true,
284 * so that we rather invoke too many update tools then too
285 * few. */
286
287 if (!path_is_absolute(c->parameter))
288 return true;
289
290 p = strappenda(c->parameter, "/.updated");
291 if (lstat(p, &other) < 0)
292 return true;
293
294 if (lstat("/usr/", &usr) < 0)
295 return true;
296
297 return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
298 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
299 }
300
301 static int condition_test_first_boot(Condition *c) {
302 int r;
303
304 assert(c);
305 assert(c->parameter);
306 assert(c->type == CONDITION_FIRST_BOOT);
307
308 r = parse_boolean(c->parameter);
309 if (r < 0)
310 return r;
311
312 return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
313 }
314
315 static int condition_test_path_exists(Condition *c) {
316 assert(c);
317 assert(c->parameter);
318 assert(c->type == CONDITION_PATH_EXISTS);
319
320 return access(c->parameter, F_OK) >= 0;
321 }
322
323 static int condition_test_path_exists_glob(Condition *c) {
324 assert(c);
325 assert(c->parameter);
326 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
327
328 return glob_exists(c->parameter) > 0;
329 }
330
331 static int condition_test_path_is_directory(Condition *c) {
332 assert(c);
333 assert(c->parameter);
334 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
335
336 return is_dir(c->parameter, true) > 0;
337 }
338
339 static int condition_test_path_is_symbolic_link(Condition *c) {
340 assert(c);
341 assert(c->parameter);
342 assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
343
344 return is_symlink(c->parameter) > 0;
345 }
346
347 static int condition_test_path_is_mount_point(Condition *c) {
348 assert(c);
349 assert(c->parameter);
350 assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
351
352 return path_is_mount_point(c->parameter, true) > 0;
353 }
354
355 static int condition_test_path_is_read_write(Condition *c) {
356 assert(c);
357 assert(c->parameter);
358 assert(c->type == CONDITION_PATH_IS_READ_WRITE);
359
360 return path_is_read_only_fs(c->parameter) <= 0;
361 }
362
363 static int condition_test_directory_not_empty(Condition *c) {
364 int r;
365
366 assert(c);
367 assert(c->parameter);
368 assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
369
370 r = dir_is_empty(c->parameter);
371 return r <= 0 && r != -ENOENT;
372 }
373
374 static int condition_test_file_not_empty(Condition *c) {
375 struct stat st;
376
377 assert(c);
378 assert(c->parameter);
379 assert(c->type == CONDITION_FILE_NOT_EMPTY);
380
381 return (stat(c->parameter, &st) >= 0 &&
382 S_ISREG(st.st_mode) &&
383 st.st_size > 0);
384 }
385
386 static int condition_test_file_is_executable(Condition *c) {
387 struct stat st;
388
389 assert(c);
390 assert(c->parameter);
391 assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
392
393 return (stat(c->parameter, &st) >= 0 &&
394 S_ISREG(st.st_mode) &&
395 (st.st_mode & 0111));
396 }
397
398 static int condition_test_null(Condition *c) {
399 assert(c);
400 assert(c->parameter);
401 assert(c->type == CONDITION_NULL);
402
403 /* Note that during parsing we already evaluate the string and
404 * store it in c->negate */
405 return true;
406 }
407
408 int condition_test(Condition *c) {
409
410 static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
411 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
412 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
413 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
414 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
415 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
416 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
417 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
418 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
419 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
420 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
421 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
422 [CONDITION_SECURITY] = condition_test_security,
423 [CONDITION_CAPABILITY] = condition_test_capability,
424 [CONDITION_HOST] = condition_test_host,
425 [CONDITION_AC_POWER] = condition_test_ac_power,
426 [CONDITION_ARCHITECTURE] = condition_test_architecture,
427 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
428 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
429 [CONDITION_NULL] = condition_test_null,
430 };
431 int r;
432
433 assert(c);
434 assert(c->type >= 0);
435 assert(c->type < _CONDITION_TYPE_MAX);
436
437 r = condition_tests[c->type](c);
438 if (r < 0)
439 return r;
440
441 return (r > 0) == !c->negate;
442 }
443
444 void condition_dump(Condition *c, FILE *f, const char *prefix) {
445 assert(c);
446 assert(f);
447
448 if (!prefix)
449 prefix = "";
450
451 fprintf(f,
452 "%s\t%s: %s%s%s %s\n",
453 prefix,
454 condition_type_to_string(c->type),
455 c->trigger ? "|" : "",
456 c->negate ? "!" : "",
457 c->parameter,
458 CONDITION_STATE_IS_FAILED(c->state) ? "failed" : CONDITION_STATE_IS_SUCCEEDED(c->state) ? "succeeded" : "untested");
459 }
460
461 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
462 Condition *c;
463
464 LIST_FOREACH(conditions, c, first)
465 condition_dump(c, f, prefix);
466 }
467
468 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
469 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
470 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
471 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
472 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
473 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
474 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
475 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
476 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
477 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
478 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
479 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
480 [CONDITION_SECURITY] = "ConditionSecurity",
481 [CONDITION_CAPABILITY] = "ConditionCapability",
482 [CONDITION_HOST] = "ConditionHost",
483 [CONDITION_AC_POWER] = "ConditionACPower",
484 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
485 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
486 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
487 [CONDITION_NULL] = "ConditionNull"
488 };
489
490 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);