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