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