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