chiark / gitweb /
util: simplify proc_cmdline() to reuse get_process_cmdline()
[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 "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);