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