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