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