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