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