]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/condition-util.c
condition: record test state internally and beef it up to be a full enum
[thirdparty/systemd.git] / src / shared / condition-util.c
CommitLineData
b77c08e0
TG
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 <sys/statvfs.h>
27#include <fnmatch.h>
28
d1bddcec 29#include "sd-id128.h"
b77c08e0
TG
30#include "util.h"
31#include "condition-util.h"
32#include "virt.h"
33#include "path-util.h"
34#include "fileio.h"
35#include "unit.h"
099524d7 36#include "architecture.h"
d1bddcec
LP
37#include "virt.h"
38#include "smack-util.h"
39#include "apparmor-util.h"
40#include "ima-util.h"
41#include "selinux-util.h"
42#include "audit.h"
b77c08e0
TG
43
44Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
45 Condition *c;
d1bddcec 46 int r;
b77c08e0 47
b80ba1da 48 assert(type >= 0);
b77c08e0 49 assert(type < _CONDITION_TYPE_MAX);
b80ba1da 50 assert(!parameter == (type == CONDITION_NULL));
b77c08e0
TG
51
52 c = new0(Condition, 1);
53 if (!c)
54 return NULL;
55
56 c->type = type;
57 c->trigger = trigger;
58 c->negate = negate;
59
d1bddcec
LP
60 r = free_and_strdup(&c->parameter, parameter);
61 if (r < 0) {
62 free(c);
63 return NULL;
b77c08e0
TG
64 }
65
66 return c;
67}
68
69void condition_free(Condition *c) {
70 assert(c);
71
72 free(c->parameter);
73 free(c);
74}
75
76void condition_free_list(Condition *first) {
77 Condition *c, *n;
78
79 LIST_FOREACH_SAFE(conditions, c, n, first)
80 condition_free(c);
81}
82
a4705396 83static int condition_test_kernel_command_line(Condition *c) {
07318c29
LP
84 _cleanup_free_ char *line = NULL;
85 const char *p;
b77c08e0
TG
86 bool equal;
87 int r;
b77c08e0
TG
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)
592fd144
LP
95 return r;
96 if (r == 0)
a4705396 97 return false;
b77c08e0
TG
98
99 equal = !!strchr(c->parameter, '=');
07318c29
LP
100 p = line;
101
102 for (;;) {
103 _cleanup_free_ char *word = NULL;
104 bool found;
105
106 r = unquote_first_word(&p, &word);
592fd144
LP
107 if (r < 0)
108 return r;
109 if (r == 0)
a4705396 110 break;
07318c29
LP
111
112 if (equal)
113 found = streq(word, c->parameter);
114 else {
115 const char *f;
116
117 f = startswith(word, c->parameter);
118 found = f && (*f == '=' || *f == 0);
b77c08e0
TG
119 }
120
07318c29 121 if (found)
a4705396 122 return true;
b77c08e0 123 }
b77c08e0 124
a4705396 125 return false;
b77c08e0
TG
126}
127
a4705396 128static int condition_test_virtualization(Condition *c) {
248fab74 129 int b, v;
b77c08e0
TG
130 const char *id;
131
132 assert(c);
133 assert(c->parameter);
134 assert(c->type == CONDITION_VIRTUALIZATION);
135
136 v = detect_virtualization(&id);
592fd144
LP
137 if (v < 0)
138 return v;
b77c08e0
TG
139
140 /* First, compare with yes/no */
141 b = parse_boolean(c->parameter);
142
143 if (v > 0 && b > 0)
a4705396 144 return true;
b77c08e0
TG
145
146 if (v == 0 && b == 0)
a4705396 147 return true;
b77c08e0
TG
148
149 /* Then, compare categorization */
150 if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
a4705396 151 return true;
b77c08e0
TG
152
153 if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
a4705396 154 return true;
b77c08e0
TG
155
156 /* Finally compare id */
a4705396 157 return v > 0 && streq(c->parameter, id);
b77c08e0
TG
158}
159
a4705396 160static int condition_test_architecture(Condition *c) {
592fd144 161 int a, b;
099524d7
LP
162
163 assert(c);
164 assert(c->parameter);
165 assert(c->type == CONDITION_ARCHITECTURE);
166
167 a = uname_architecture();
168 if (a < 0)
592fd144 169 return a;
099524d7
LP
170
171 if (streq(c->parameter, "native"))
172 b = native_architecture();
173 else
174 b = architecture_from_string(c->parameter);
099524d7 175 if (b < 0)
592fd144 176 return b;
099524d7 177
a4705396 178 return a == b;
099524d7
LP
179}
180
a4705396 181static int condition_test_host(Condition *c) {
dc92e62c 182 _cleanup_free_ char *h = NULL;
b77c08e0 183 sd_id128_t x, y;
b77c08e0 184 int r;
b77c08e0
TG
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)
592fd144 194 return r;
b77c08e0 195
a4705396 196 return sd_id128_equal(x, y);
b77c08e0
TG
197 }
198
199 h = gethostname_malloc();
200 if (!h)
592fd144 201 return -ENOMEM;
b77c08e0 202
a4705396 203 return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
b77c08e0
TG
204}
205
a4705396 206static int condition_test_ac_power(Condition *c) {
b77c08e0
TG
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)
592fd144 215 return r;
b77c08e0 216
a4705396 217 return (on_ac_power() != 0) == !!r;
b77c08e0
TG
218}
219
d1bddcec
LP
220static 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"))
a4705396 226 return mac_selinux_use();
d1bddcec 227 if (streq(c->parameter, "smack"))
a4705396 228 return mac_smack_use();
d1bddcec 229 if (streq(c->parameter, "apparmor"))
a4705396 230 return mac_apparmor_use();
d1bddcec 231 if (streq(c->parameter, "audit"))
a4705396 232 return use_audit();
d1bddcec 233 if (streq(c->parameter, "ima"))
a4705396 234 return use_ima();
d1bddcec 235
a4705396 236 return false;
d1bddcec
LP
237}
238
239static int condition_test_capability(Condition *c) {
240 _cleanup_fclose_ FILE *f = NULL;
241 cap_value_t 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
251 if (cap_from_name(c->parameter, &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
a4705396 270 return !!(capabilities & (1ULL << value));
d1bddcec
LP
271}
272
273static 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)
a4705396 283 return false;
d1bddcec
LP
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))
a4705396 290 return true;
d1bddcec
LP
291
292 p = strappenda(c->parameter, "/.updated");
293 if (lstat(p, &other) < 0)
a4705396 294 return true;
d1bddcec
LP
295
296 if (lstat("/usr/", &usr) < 0)
a4705396 297 return true;
d1bddcec 298
a4705396
LP
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);
d1bddcec
LP
301}
302
303static 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
a4705396 314 return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
d1bddcec
LP
315}
316
317static int condition_test_path_exists(Condition *c) {
318 assert(c);
319 assert(c->parameter);
320 assert(c->type == CONDITION_PATH_EXISTS);
321
a4705396 322 return access(c->parameter, F_OK) >= 0;
d1bddcec
LP
323}
324
325static int condition_test_path_exists_glob(Condition *c) {
326 assert(c);
327 assert(c->parameter);
328 assert(c->type == CONDITION_PATH_EXISTS_GLOB);
329
a4705396 330 return glob_exists(c->parameter) > 0;
d1bddcec
LP
331}
332
333static int condition_test_path_is_directory(Condition *c) {
334 assert(c);
335 assert(c->parameter);
336 assert(c->type == CONDITION_PATH_IS_DIRECTORY);
337
a4705396 338 return is_dir(c->parameter, true) > 0;
d1bddcec
LP
339}
340
341static 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
a4705396 346 return is_symlink(c->parameter) > 0;
d1bddcec
LP
347}
348
349static 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
a4705396 354 return path_is_mount_point(c->parameter, true) > 0;
d1bddcec
LP
355}
356
357static 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
a4705396 362 return path_is_read_only_fs(c->parameter) <= 0;
d1bddcec
LP
363}
364
365static 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);
a4705396 373 return r <= 0 && r != -ENOENT;
d1bddcec
LP
374}
375
376static 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) &&
a4705396 385 st.st_size > 0);
d1bddcec
LP
386}
387
388static 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) &&
a4705396 397 (st.st_mode & 0111));
d1bddcec
LP
398}
399
400static int condition_test_null(Condition *c) {
401 assert(c);
d1bddcec
LP
402 assert(c->type == CONDITION_NULL);
403
404 /* Note that during parsing we already evaluate the string and
405 * store it in c->negate */
a4705396 406 return true;
d1bddcec
LP
407}
408
409int 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 };
cc50ef13
LP
432
433 int r, b;
d1bddcec
LP
434
435 assert(c);
436 assert(c->type >= 0);
437 assert(c->type < _CONDITION_TYPE_MAX);
438
a4705396 439 r = condition_tests[c->type](c);
cc50ef13
LP
440 if (r < 0) {
441 c->result = CONDITION_ERROR;
a4705396 442 return r;
cc50ef13 443 }
a4705396 444
cc50ef13
LP
445 b = (r > 0) == !c->negate;
446 c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
447 return b;
d1bddcec
LP
448}
449
b77c08e0
TG
450void condition_dump(Condition *c, FILE *f, const char *prefix) {
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 condition_type_to_string(c->type),
461 c->trigger ? "|" : "",
462 c->negate ? "!" : "",
463 c->parameter,
cc50ef13 464 condition_result_to_string(c->result));
b77c08e0
TG
465}
466
467void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
468 Condition *c;
469
470 LIST_FOREACH(conditions, c, first)
471 condition_dump(c, f, prefix);
472}
473
474static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
475 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
476 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
477 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
478 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
479 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
480 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
481 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
482 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
483 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
484 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
485 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
486 [CONDITION_SECURITY] = "ConditionSecurity",
487 [CONDITION_CAPABILITY] = "ConditionCapability",
488 [CONDITION_HOST] = "ConditionHost",
489 [CONDITION_AC_POWER] = "ConditionACPower",
099524d7 490 [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
a55654d5 491 [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
e2680723 492 [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
b77c08e0
TG
493 [CONDITION_NULL] = "ConditionNull"
494};
495
496DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
cc50ef13
LP
497
498static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
499 [CONDITION_UNTESTED] = "untested",
500 [CONDITION_SUCCEEDED] = "succeeded",
501 [CONDITION_FAILED] = "failed",
502 [CONDITION_ERROR] = "error",
503};
504
505DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);