chiark / gitweb /
util: split-out hwclock.[ch]
[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                 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
228
229         case CONDITION_DIRECTORY_NOT_EMPTY: {
230                 int k;
231
232                 k = dir_is_empty(c->parameter);
233                 return !(k == -ENOENT || k > 0) == !c->negate;
234         }
235
236         case CONDITION_FILE_IS_EXECUTABLE: {
237                 struct stat st;
238
239                 if (stat(c->parameter, &st) < 0)
240                         return c->negate;
241
242                 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
243         }
244
245         case CONDITION_KERNEL_COMMAND_LINE:
246                 return test_kernel_command_line(c->parameter) == !c->negate;
247
248         case CONDITION_VIRTUALIZATION:
249                 return test_virtualization(c->parameter) == !c->negate;
250
251         case CONDITION_SECURITY:
252                 return test_security(c->parameter) == !c->negate;
253
254         case CONDITION_CAPABILITY:
255                 return test_capability(c->parameter) == !c->negate;
256
257         case CONDITION_NULL:
258                 return !c->negate;
259
260         default:
261                 assert_not_reached("Invalid condition type.");
262         }
263 }
264
265 bool condition_test_list(Condition *first) {
266         Condition *c;
267         int triggered = -1;
268
269         /* If the condition list is empty, then it is true */
270         if (!first)
271                 return true;
272
273         /* Otherwise, if all of the non-trigger conditions apply and
274          * if any of the trigger conditions apply (unless there are
275          * none) we return true */
276         LIST_FOREACH(conditions, c, first) {
277                 bool b;
278
279                 b = condition_test(c);
280
281                 if (!c->trigger && !b)
282                         return false;
283
284                 if (c->trigger && triggered <= 0)
285                         triggered = b;
286         }
287
288         return triggered != 0;
289 }
290
291 void condition_dump(Condition *c, FILE *f, const char *prefix) {
292         assert(c);
293         assert(f);
294
295         if (!prefix)
296                 prefix = "";
297
298         fprintf(f,
299                 "%s\t%s: %s%s%s\n",
300                 prefix,
301                 condition_type_to_string(c->type),
302                 c->trigger ? "|" : "",
303                 c->negate ? "!" : "",
304                 c->parameter);
305 }
306
307 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
308         Condition *c;
309
310         LIST_FOREACH(conditions, c, first)
311                 condition_dump(c, f, prefix);
312 }
313
314 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
315         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
316         [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
317         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
318         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
319         [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
320         [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
321         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
322         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
323         [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
324         [CONDITION_SECURITY] = "ConditionSecurity",
325         [CONDITION_NULL] = "ConditionNull"
326 };
327
328 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);