chiark / gitweb /
event: implement quit handlers
[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 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_security(const char *parameter) {
162         if (streq(parameter, "selinux"))
163                 return use_selinux();
164         if (streq(parameter, "apparmor"))
165                 return use_apparmor();
166         if (streq(parameter, "ima"))
167                 return use_ima();
168         if (streq(parameter, "smack"))
169                 return use_smack();
170         return false;
171 }
172
173 static bool test_capability(const char *parameter) {
174         cap_value_t value;
175         FILE *f;
176         char line[LINE_MAX];
177         unsigned long long capabilities = (unsigned long long) -1;
178
179         /* If it's an invalid capability, we don't have it */
180
181         if (cap_from_name(parameter, &value) < 0)
182                 return false;
183
184         /* If it's a valid capability we default to assume
185          * that we have it */
186
187         f = fopen("/proc/self/status", "re");
188         if (!f)
189                 return true;
190
191         while (fgets(line, sizeof(line), f)) {
192                 truncate_nl(line);
193
194                 if (startswith(line, "CapBnd:")) {
195                         (void) sscanf(line+7, "%llx", &capabilities);
196                         break;
197                 }
198         }
199
200         fclose(f);
201
202         return !!(capabilities & (1ULL << value));
203 }
204
205 static bool test_host(const char *parameter) {
206         sd_id128_t x, y;
207         char *h;
208         int r;
209         bool b;
210
211         if (sd_id128_from_string(parameter, &x) >= 0) {
212
213                 r = sd_id128_get_machine(&y);
214                 if (r < 0)
215                         return false;
216
217                 return sd_id128_equal(x, y);
218         }
219
220         h = gethostname_malloc();
221         if (!h)
222                 return false;
223
224         b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
225         free(h);
226
227         return b;
228 }
229
230 static bool test_ac_power(const char *parameter) {
231         int r;
232
233         r = parse_boolean(parameter);
234         if (r < 0)
235                 return true;
236
237         return (on_ac_power() != 0) == !!r;
238 }
239
240 static bool condition_test(Condition *c) {
241         assert(c);
242
243         switch(c->type) {
244
245         case CONDITION_PATH_EXISTS:
246                 return (access(c->parameter, F_OK) >= 0) == !c->negate;
247
248         case CONDITION_PATH_EXISTS_GLOB:
249                 return (glob_exists(c->parameter) > 0) == !c->negate;
250
251         case CONDITION_PATH_IS_DIRECTORY: {
252                 struct stat st;
253
254                 if (stat(c->parameter, &st) < 0)
255                         return c->negate;
256                 return S_ISDIR(st.st_mode) == !c->negate;
257         }
258
259         case CONDITION_PATH_IS_SYMBOLIC_LINK: {
260                 struct stat st;
261
262                 if (lstat(c->parameter, &st) < 0)
263                         return c->negate;
264                 return S_ISLNK(st.st_mode) == !c->negate;
265         }
266
267         case CONDITION_PATH_IS_MOUNT_POINT:
268                 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
269
270         case CONDITION_PATH_IS_READ_WRITE:
271                 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
272
273         case CONDITION_DIRECTORY_NOT_EMPTY: {
274                 int k;
275
276                 k = dir_is_empty(c->parameter);
277                 return !(k == -ENOENT || k > 0) == !c->negate;
278         }
279
280         case CONDITION_FILE_NOT_EMPTY: {
281                 struct stat st;
282
283                 if (stat(c->parameter, &st) < 0)
284                         return c->negate;
285
286                 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
287         }
288
289         case CONDITION_FILE_IS_EXECUTABLE: {
290                 struct stat st;
291
292                 if (stat(c->parameter, &st) < 0)
293                         return c->negate;
294
295                 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
296         }
297
298         case CONDITION_KERNEL_COMMAND_LINE:
299                 return test_kernel_command_line(c->parameter) == !c->negate;
300
301         case CONDITION_VIRTUALIZATION:
302                 return test_virtualization(c->parameter) == !c->negate;
303
304         case CONDITION_SECURITY:
305                 return test_security(c->parameter) == !c->negate;
306
307         case CONDITION_CAPABILITY:
308                 return test_capability(c->parameter) == !c->negate;
309
310         case CONDITION_HOST:
311                 return test_host(c->parameter) == !c->negate;
312
313         case CONDITION_AC_POWER:
314                 return test_ac_power(c->parameter) == !c->negate;
315
316         case CONDITION_NULL:
317                 return !c->negate;
318
319         default:
320                 assert_not_reached("Invalid condition type.");
321         }
322 }
323
324 bool condition_test_list(const char *unit, Condition *first) {
325         Condition *c;
326         int triggered = -1;
327
328         /* If the condition list is empty, then it is true */
329         if (!first)
330                 return true;
331
332         /* Otherwise, if all of the non-trigger conditions apply and
333          * if any of the trigger conditions apply (unless there are
334          * none) we return true */
335         LIST_FOREACH(conditions, c, first) {
336                 bool b;
337
338                 b = condition_test(c);
339                 if (unit)
340                         log_debug_unit(unit,
341                                        "%s=%s%s%s %s for %s.",
342                                        condition_type_to_string(c->type),
343                                        c->trigger ? "|" : "",
344                                        c->negate ? "!" : "",
345                                        c->parameter,
346                                        b ? "succeeded" : "failed",
347                                        unit);
348                 c->state = b ? 1 : -1;
349
350                 if (!c->trigger && !b)
351                         return false;
352
353                 if (c->trigger && triggered <= 0)
354                         triggered = b;
355         }
356
357         return triggered != 0;
358 }
359
360 void condition_dump(Condition *c, FILE *f, const char *prefix) {
361         assert(c);
362         assert(f);
363
364         if (!prefix)
365                 prefix = "";
366
367         fprintf(f,
368                 "%s\t%s: %s%s%s %s\n",
369                 prefix,
370                 condition_type_to_string(c->type),
371                 c->trigger ? "|" : "",
372                 c->negate ? "!" : "",
373                 c->parameter,
374                 c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
375 }
376
377 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
378         Condition *c;
379
380         LIST_FOREACH(conditions, c, first)
381                 condition_dump(c, f, prefix);
382 }
383
384 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
385         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
386         [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
387         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
388         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
389         [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
390         [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
391         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
392         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
393         [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
394         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
395         [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
396         [CONDITION_SECURITY] = "ConditionSecurity",
397         [CONDITION_CAPABILITY] = "ConditionCapability",
398         [CONDITION_HOST] = "ConditionHost",
399         [CONDITION_AC_POWER] = "ConditionACPower",
400         [CONDITION_NULL] = "ConditionNull"
401 };
402
403 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);