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