chiark / gitweb /
condition: refactor a bit
[elogind.git] / src / core / 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/capability.h>
27 #include <sys/statvfs.h>
28 #include <fnmatch.h>
29
30 #include <systemd/sd-id128.h>
31 #include "util.h"
32 #include "condition.h"
33 #include "virt.h"
34 #include "path-util.h"
35 #include "fileio.h"
36 #include "unit.h"
37 #include "smack-util.h"
38 #include "apparmor-util.h"
39 #include "ima-util.h"
40 #include "selinux-util.h"
41
42 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
43         Condition *c;
44
45         assert(type < _CONDITION_TYPE_MAX);
46
47         c = new0(Condition, 1);
48         if (!c)
49                 return NULL;
50
51         c->type = type;
52         c->trigger = trigger;
53         c->negate = negate;
54
55         if (parameter) {
56                 c->parameter = strdup(parameter);
57                 if (!c->parameter) {
58                         free(c);
59                         return NULL;
60                 }
61         }
62
63         return c;
64 }
65
66 void condition_free(Condition *c) {
67         assert(c);
68
69         free(c->parameter);
70         free(c);
71 }
72
73 void condition_free_list(Condition *first) {
74         Condition *c, *n;
75
76         LIST_FOREACH_SAFE(conditions, c, n, first)
77                 condition_free(c);
78 }
79
80 static bool condition_test_kernel_command_line(Condition *c) {
81         char *line, *w, *state, *word = NULL;
82         bool equal;
83         int r;
84         size_t l, pl;
85         bool found = false;
86
87         assert(c);
88         assert(c->parameter);
89         assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
90
91         r = proc_cmdline(&line);
92         if (r < 0)
93                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
94         if (r <= 0)
95                 return c->negate;
96
97         equal = !!strchr(c->parameter, '=');
98         pl = strlen(c->parameter);
99
100         FOREACH_WORD_QUOTED(w, l, line, state) {
101
102                 free(word);
103                 word = strndup(w, l);
104                 if (!word)
105                         break;
106
107                 if (equal) {
108                         if (streq(word, c->parameter)) {
109                                 found = true;
110                                 break;
111                         }
112                 } else {
113                         if (startswith(word, c->parameter) && (word[pl] == '=' || word[pl] == 0)) {
114                                 found = true;
115                                 break;
116                         }
117                 }
118
119         }
120
121         free(word);
122         free(line);
123
124         return found == !c->negate;
125 }
126
127 static bool condition_test_virtualization(Condition *c) {
128         int b;
129         Virtualization v;
130         const char *id;
131
132         assert(c);
133         assert(c->parameter);
134         assert(c->type == CONDITION_VIRTUALIZATION);
135
136         v = detect_virtualization(&id);
137         if (v < 0) {
138                 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
139                 return c->negate;
140         }
141
142         /* First, compare with yes/no */
143         b = parse_boolean(c->parameter);
144
145         if (v > 0 && b > 0)
146                 return !c->negate;
147
148         if (v == 0 && b == 0)
149                 return !c->negate;
150
151         /* Then, compare categorization */
152         if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
153                 return !c->negate;
154
155         if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
156                 return !c->negate;
157
158         /* Finally compare id */
159         return (v > 0 && streq(c->parameter, id)) == !c->negate;
160 }
161
162 static bool condition_test_security(Condition *c) {
163         assert(c);
164         assert(c->parameter);
165         assert(c->type == CONDITION_SECURITY);
166
167         if (streq(c->parameter, "selinux"))
168                 return use_selinux() == !c->negate;
169         if (streq(c->parameter, "apparmor"))
170                 return use_apparmor() == !c->negate;
171         if (streq(c->parameter, "ima"))
172                 return use_ima() == !c->negate;
173         if (streq(c->parameter, "smack"))
174                 return use_smack() == !c->negate;
175         return c->negate;
176 }
177
178 static bool condition_test_capability(Condition *c) {
179         cap_value_t value;
180         FILE *f;
181         char line[LINE_MAX];
182         unsigned long long capabilities = (unsigned long long) -1;
183
184         assert(c);
185         assert(c->parameter);
186         assert(c->type == CONDITION_CAPABILITY);
187
188         /* If it's an invalid capability, we don't have it */
189
190         if (cap_from_name(c->parameter, &value) < 0)
191                 return c->negate;
192
193         /* If it's a valid capability we default to assume
194          * that we have it */
195
196         f = fopen("/proc/self/status", "re");
197         if (!f)
198                 return !c->negate;
199
200         while (fgets(line, sizeof(line), f)) {
201                 truncate_nl(line);
202
203                 if (startswith(line, "CapBnd:")) {
204                         (void) sscanf(line+7, "%llx", &capabilities);
205                         break;
206                 }
207         }
208
209         fclose(f);
210
211         return !!(capabilities & (1ULL << value)) == !c->negate;
212 }
213
214 static bool condition_test_host(Condition *c) {
215         sd_id128_t x, y;
216         char *h;
217         int r;
218         bool b;
219
220         assert(c);
221         assert(c->parameter);
222         assert(c->type == CONDITION_HOST);
223
224         if (sd_id128_from_string(c->parameter, &x) >= 0) {
225
226                 r = sd_id128_get_machine(&y);
227                 if (r < 0)
228                         return c->negate;
229
230                 return sd_id128_equal(x, y);
231         }
232
233         h = gethostname_malloc();
234         if (!h)
235                 return c->negate;
236
237         b = fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
238         free(h);
239
240         return b == !c->negate;
241 }
242
243 static bool condition_test_ac_power(Condition *c) {
244         int r;
245
246         assert(c);
247         assert(c->parameter);
248         assert(c->type == CONDITION_AC_POWER);
249
250         r = parse_boolean(c->parameter);
251         if (r < 0)
252                 return !c->negate;
253
254         return ((on_ac_power() != 0) == !!r) == !c->negate;
255 }
256
257 static bool condition_test(Condition *c) {
258         assert(c);
259
260         switch(c->type) {
261
262         case CONDITION_PATH_EXISTS:
263                 return (access(c->parameter, F_OK) >= 0) == !c->negate;
264
265         case CONDITION_PATH_EXISTS_GLOB:
266                 return (glob_exists(c->parameter) > 0) == !c->negate;
267
268         case CONDITION_PATH_IS_DIRECTORY: {
269                 struct stat st;
270
271                 if (stat(c->parameter, &st) < 0)
272                         return c->negate;
273                 return S_ISDIR(st.st_mode) == !c->negate;
274         }
275
276         case CONDITION_PATH_IS_SYMBOLIC_LINK: {
277                 struct stat st;
278
279                 if (lstat(c->parameter, &st) < 0)
280                         return c->negate;
281                 return S_ISLNK(st.st_mode) == !c->negate;
282         }
283
284         case CONDITION_PATH_IS_MOUNT_POINT:
285                 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
286
287         case CONDITION_PATH_IS_READ_WRITE:
288                 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
289
290         case CONDITION_DIRECTORY_NOT_EMPTY: {
291                 int k;
292
293                 k = dir_is_empty(c->parameter);
294                 return !(k == -ENOENT || k > 0) == !c->negate;
295         }
296
297         case CONDITION_FILE_NOT_EMPTY: {
298                 struct stat st;
299
300                 if (stat(c->parameter, &st) < 0)
301                         return c->negate;
302
303                 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
304         }
305
306         case CONDITION_FILE_IS_EXECUTABLE: {
307                 struct stat st;
308
309                 if (stat(c->parameter, &st) < 0)
310                         return c->negate;
311
312                 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
313         }
314
315         case CONDITION_KERNEL_COMMAND_LINE:
316                 return condition_test_kernel_command_line(c);
317
318         case CONDITION_VIRTUALIZATION:
319                 return condition_test_virtualization(c);
320
321         case CONDITION_SECURITY:
322                 return condition_test_security(c);
323
324         case CONDITION_CAPABILITY:
325                 return condition_test_capability(c);
326
327         case CONDITION_HOST:
328                 return condition_test_host(c);
329
330         case CONDITION_AC_POWER:
331                 return condition_test_ac_power(c);
332
333         case CONDITION_NULL:
334                 return !c->negate;
335
336         default:
337                 assert_not_reached("Invalid condition type.");
338         }
339 }
340
341 bool condition_test_list(const char *unit, Condition *first) {
342         Condition *c;
343         int triggered = -1;
344
345         /* If the condition list is empty, then it is true */
346         if (!first)
347                 return true;
348
349         /* Otherwise, if all of the non-trigger conditions apply and
350          * if any of the trigger conditions apply (unless there are
351          * none) we return true */
352         LIST_FOREACH(conditions, c, first) {
353                 bool b;
354
355                 b = condition_test(c);
356                 if (unit)
357                         log_debug_unit(unit,
358                                        "%s=%s%s%s %s for %s.",
359                                        condition_type_to_string(c->type),
360                                        c->trigger ? "|" : "",
361                                        c->negate ? "!" : "",
362                                        c->parameter,
363                                        b ? "succeeded" : "failed",
364                                        unit);
365                 c->state = b ? 1 : -1;
366
367                 if (!c->trigger && !b)
368                         return false;
369
370                 if (c->trigger && triggered <= 0)
371                         triggered = b;
372         }
373
374         return triggered != 0;
375 }
376
377 void condition_dump(Condition *c, FILE *f, const char *prefix) {
378         assert(c);
379         assert(f);
380
381         if (!prefix)
382                 prefix = "";
383
384         fprintf(f,
385                 "%s\t%s: %s%s%s %s\n",
386                 prefix,
387                 condition_type_to_string(c->type),
388                 c->trigger ? "|" : "",
389                 c->negate ? "!" : "",
390                 c->parameter,
391                 c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
392 }
393
394 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
395         Condition *c;
396
397         LIST_FOREACH(conditions, c, first)
398                 condition_dump(c, f, prefix);
399 }
400
401 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
402         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
403         [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
404         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
405         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
406         [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
407         [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
408         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
409         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
410         [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
411         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
412         [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
413         [CONDITION_SECURITY] = "ConditionSecurity",
414         [CONDITION_CAPABILITY] = "ConditionCapability",
415         [CONDITION_HOST] = "ConditionHost",
416         [CONDITION_AC_POWER] = "ConditionACPower",
417         [CONDITION_NULL] = "ConditionNull"
418 };
419
420 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);