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