chiark / gitweb /
Merge nss-myhostname
[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 static bool test_ac_power(const char *parameter) {
225         int r;
226
227         r = parse_boolean(parameter);
228         if (r < 0)
229                 return true;
230
231         return (on_ac_power() != 0) == !!r;
232 }
233
234 bool condition_test(Condition *c) {
235         assert(c);
236
237         switch(c->type) {
238
239         case CONDITION_PATH_EXISTS:
240                 return (access(c->parameter, F_OK) >= 0) == !c->negate;
241
242         case CONDITION_PATH_EXISTS_GLOB:
243                 return (glob_exists(c->parameter) > 0) == !c->negate;
244
245         case CONDITION_PATH_IS_DIRECTORY: {
246                 struct stat st;
247
248                 if (stat(c->parameter, &st) < 0)
249                         return c->negate;
250                 return S_ISDIR(st.st_mode) == !c->negate;
251         }
252
253         case CONDITION_PATH_IS_SYMBOLIC_LINK: {
254                 struct stat st;
255
256                 if (lstat(c->parameter, &st) < 0)
257                         return c->negate;
258                 return S_ISLNK(st.st_mode) == !c->negate;
259         }
260
261         case CONDITION_PATH_IS_MOUNT_POINT:
262                 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
263
264         case CONDITION_PATH_IS_READ_WRITE:
265                 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
266
267         case CONDITION_DIRECTORY_NOT_EMPTY: {
268                 int k;
269
270                 k = dir_is_empty(c->parameter);
271                 return !(k == -ENOENT || k > 0) == !c->negate;
272         }
273
274         case CONDITION_FILE_NOT_EMPTY: {
275                 struct stat st;
276
277                 if (stat(c->parameter, &st) < 0)
278                         return c->negate;
279
280                 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
281         }
282
283         case CONDITION_FILE_IS_EXECUTABLE: {
284                 struct stat st;
285
286                 if (stat(c->parameter, &st) < 0)
287                         return c->negate;
288
289                 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
290         }
291
292         case CONDITION_KERNEL_COMMAND_LINE:
293                 return test_kernel_command_line(c->parameter) == !c->negate;
294
295         case CONDITION_VIRTUALIZATION:
296                 return test_virtualization(c->parameter) == !c->negate;
297
298         case CONDITION_SECURITY:
299                 return test_security(c->parameter) == !c->negate;
300
301         case CONDITION_CAPABILITY:
302                 return test_capability(c->parameter) == !c->negate;
303
304         case CONDITION_HOST:
305                 return test_host(c->parameter) == !c->negate;
306
307         case CONDITION_AC_POWER:
308                 return test_ac_power(c->parameter) == !c->negate;
309
310         case CONDITION_NULL:
311                 return !c->negate;
312
313         default:
314                 assert_not_reached("Invalid condition type.");
315         }
316 }
317
318 bool condition_test_list(Condition *first) {
319         Condition *c;
320         int triggered = -1;
321
322         /* If the condition list is empty, then it is true */
323         if (!first)
324                 return true;
325
326         /* Otherwise, if all of the non-trigger conditions apply and
327          * if any of the trigger conditions apply (unless there are
328          * none) we return true */
329         LIST_FOREACH(conditions, c, first) {
330                 bool b;
331
332                 b = condition_test(c);
333
334                 if (!c->trigger && !b)
335                         return false;
336
337                 if (c->trigger && triggered <= 0)
338                         triggered = b;
339         }
340
341         return triggered != 0;
342 }
343
344 void condition_dump(Condition *c, FILE *f, const char *prefix) {
345         assert(c);
346         assert(f);
347
348         if (!prefix)
349                 prefix = "";
350
351         fprintf(f,
352                 "%s\t%s: %s%s%s\n",
353                 prefix,
354                 condition_type_to_string(c->type),
355                 c->trigger ? "|" : "",
356                 c->negate ? "!" : "",
357                 c->parameter);
358 }
359
360 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
361         Condition *c;
362
363         LIST_FOREACH(conditions, c, first)
364                 condition_dump(c, f, prefix);
365 }
366
367 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
368         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
369         [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
370         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
371         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
372         [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
373         [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
374         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
375         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
376         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
377         [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
378         [CONDITION_SECURITY] = "ConditionSecurity",
379         [CONDITION_HOST] = "ConditionHost",
380         [CONDITION_AC_POWER] = "ConditionACPower",
381         [CONDITION_NULL] = "ConditionNull"
382 };
383
384 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);