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