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