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