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