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