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