]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/condition.c
2fbc5ad0e6914534ca2173c3fd06b7f1ab751266
[thirdparty/systemd.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 #include "fileio.h"
40 #include "unit.h"
41
42 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
43 Condition *c;
44
45 assert(type < _CONDITION_TYPE_MAX);
46
47 c = new0(Condition, 1);
48 if (!c)
49 return NULL;
50
51 c->type = type;
52 c->trigger = trigger;
53 c->negate = negate;
54
55 if (parameter) {
56 c->parameter = strdup(parameter);
57 if (!c->parameter) {
58 free(c);
59 return NULL;
60 }
61 }
62
63 return c;
64 }
65
66 void condition_free(Condition *c) {
67 assert(c);
68
69 free(c->parameter);
70 free(c);
71 }
72
73 void condition_free_list(Condition *first) {
74 Condition *c, *n;
75
76 LIST_FOREACH_SAFE(conditions, c, n, first)
77 condition_free(c);
78 }
79
80 static bool test_kernel_command_line(const char *parameter) {
81 char *line, *w, *state, *word = NULL;
82 bool equal;
83 int r;
84 size_t l, pl;
85 bool found = false;
86
87 assert(parameter);
88
89 if (detect_container(NULL) > 0)
90 return false;
91
92 r = read_one_line_file("/proc/cmdline", &line);
93 if (r < 0) {
94 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
95 return false;
96 }
97
98 equal = !!strchr(parameter, '=');
99 pl = strlen(parameter);
100
101 FOREACH_WORD_QUOTED(w, l, line, state) {
102
103 free(word);
104 word = strndup(w, l);
105 if (!word)
106 break;
107
108 if (equal) {
109 if (streq(word, parameter)) {
110 found = true;
111 break;
112 }
113 } else {
114 if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
115 found = true;
116 break;
117 }
118 }
119
120 }
121
122 free(word);
123 free(line);
124
125 return found;
126 }
127
128 static bool test_virtualization(const char *parameter) {
129 int b;
130 Virtualization v;
131 const char *id;
132
133 assert(parameter);
134
135 v = detect_virtualization(&id);
136 if (v < 0) {
137 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
138 return false;
139 }
140
141 /* First, compare with yes/no */
142 b = parse_boolean(parameter);
143
144 if (v > 0 && b > 0)
145 return true;
146
147 if (v == 0 && b == 0)
148 return true;
149
150 /* Then, compare categorization */
151 if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
152 return true;
153
154 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
155 return true;
156
157 /* Finally compare id */
158 return v > 0 && streq(parameter, id);
159 }
160
161 static bool test_apparmor_enabled(void) {
162 int r;
163 _cleanup_free_ char *p = NULL;
164
165 r = read_one_line_file("/sys/module/apparmor/parameters/enabled", &p);
166 if (r < 0)
167 return false;
168
169 return parse_boolean(p) > 0;
170 }
171
172 static bool test_security(const char *parameter) {
173 #ifdef HAVE_SELINUX
174 if (streq(parameter, "selinux"))
175 return is_selinux_enabled() > 0;
176 #endif
177 if (streq(parameter, "apparmor"))
178 return test_apparmor_enabled();
179 if (streq(parameter, "ima"))
180 return access("/sys/kernel/security/ima/", F_OK) == 0;
181 if (streq(parameter, "smack"))
182 return access("/sys/fs/smackfs", F_OK) == 0;
183 return false;
184 }
185
186 static bool test_capability(const char *parameter) {
187 cap_value_t value;
188 FILE *f;
189 char line[LINE_MAX];
190 unsigned long long capabilities = (unsigned long long) -1;
191
192 /* If it's an invalid capability, we don't have it */
193
194 if (cap_from_name(parameter, &value) < 0)
195 return false;
196
197 /* If it's a valid capability we default to assume
198 * that we have it */
199
200 f = fopen("/proc/self/status", "re");
201 if (!f)
202 return true;
203
204 while (fgets(line, sizeof(line), f)) {
205 truncate_nl(line);
206
207 if (startswith(line, "CapBnd:")) {
208 (void) sscanf(line+7, "%llx", &capabilities);
209 break;
210 }
211 }
212
213 fclose(f);
214
215 return !!(capabilities & (1ULL << value));
216 }
217
218 static bool test_host(const char *parameter) {
219 sd_id128_t x, y;
220 char *h;
221 int r;
222 bool b;
223
224 if (sd_id128_from_string(parameter, &x) >= 0) {
225
226 r = sd_id128_get_machine(&y);
227 if (r < 0)
228 return false;
229
230 return sd_id128_equal(x, y);
231 }
232
233 h = gethostname_malloc();
234 if (!h)
235 return false;
236
237 b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
238 free(h);
239
240 return b;
241 }
242
243 static bool test_ac_power(const char *parameter) {
244 int r;
245
246 r = parse_boolean(parameter);
247 if (r < 0)
248 return true;
249
250 return (on_ac_power() != 0) == !!r;
251 }
252
253 bool condition_test(Condition *c) {
254 assert(c);
255
256 switch(c->type) {
257
258 case CONDITION_PATH_EXISTS:
259 return (access(c->parameter, F_OK) >= 0) == !c->negate;
260
261 case CONDITION_PATH_EXISTS_GLOB:
262 return (glob_exists(c->parameter) > 0) == !c->negate;
263
264 case CONDITION_PATH_IS_DIRECTORY: {
265 struct stat st;
266
267 if (stat(c->parameter, &st) < 0)
268 return c->negate;
269 return S_ISDIR(st.st_mode) == !c->negate;
270 }
271
272 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
273 struct stat st;
274
275 if (lstat(c->parameter, &st) < 0)
276 return c->negate;
277 return S_ISLNK(st.st_mode) == !c->negate;
278 }
279
280 case CONDITION_PATH_IS_MOUNT_POINT:
281 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
282
283 case CONDITION_PATH_IS_READ_WRITE:
284 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
285
286 case CONDITION_DIRECTORY_NOT_EMPTY: {
287 int k;
288
289 k = dir_is_empty(c->parameter);
290 return !(k == -ENOENT || k > 0) == !c->negate;
291 }
292
293 case CONDITION_FILE_NOT_EMPTY: {
294 struct stat st;
295
296 if (stat(c->parameter, &st) < 0)
297 return c->negate;
298
299 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
300 }
301
302 case CONDITION_FILE_IS_EXECUTABLE: {
303 struct stat st;
304
305 if (stat(c->parameter, &st) < 0)
306 return c->negate;
307
308 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
309 }
310
311 case CONDITION_KERNEL_COMMAND_LINE:
312 return test_kernel_command_line(c->parameter) == !c->negate;
313
314 case CONDITION_VIRTUALIZATION:
315 return test_virtualization(c->parameter) == !c->negate;
316
317 case CONDITION_SECURITY:
318 return test_security(c->parameter) == !c->negate;
319
320 case CONDITION_CAPABILITY:
321 return test_capability(c->parameter) == !c->negate;
322
323 case CONDITION_HOST:
324 return test_host(c->parameter) == !c->negate;
325
326 case CONDITION_AC_POWER:
327 return test_ac_power(c->parameter) == !c->negate;
328
329 case CONDITION_NULL:
330 return !c->negate;
331
332 default:
333 assert_not_reached("Invalid condition type.");
334 }
335 }
336
337 bool condition_test_list(const char *unit, Condition *first) {
338 Condition *c;
339 int triggered = -1;
340
341 /* If the condition list is empty, then it is true */
342 if (!first)
343 return true;
344
345 /* Otherwise, if all of the non-trigger conditions apply and
346 * if any of the trigger conditions apply (unless there are
347 * none) we return true */
348 LIST_FOREACH(conditions, c, first) {
349 bool b;
350
351 b = condition_test(c);
352 if (unit)
353 log_debug_unit(unit,
354 "%s=%s%s%s %s for %s.",
355 condition_type_to_string(c->type),
356 c->trigger ? "|" : "",
357 c->negate ? "!" : "",
358 c->parameter,
359 b ? "succeeded" : "failed",
360 unit);
361
362 if (!c->trigger && !b)
363 return false;
364
365 if (c->trigger && triggered <= 0)
366 triggered = b;
367 }
368
369 return triggered != 0;
370 }
371
372 void condition_dump(Condition *c, FILE *f, const char *prefix) {
373 assert(c);
374 assert(f);
375
376 if (!prefix)
377 prefix = "";
378
379 fprintf(f,
380 "%s\t%s: %s%s%s\n",
381 prefix,
382 condition_type_to_string(c->type),
383 c->trigger ? "|" : "",
384 c->negate ? "!" : "",
385 c->parameter);
386 }
387
388 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
389 Condition *c;
390
391 LIST_FOREACH(conditions, c, first)
392 condition_dump(c, f, prefix);
393 }
394
395 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
396 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
397 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
398 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
399 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
400 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
401 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
402 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
403 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
404 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
405 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
406 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
407 [CONDITION_SECURITY] = "ConditionSecurity",
408 [CONDITION_CAPABILITY] = "ConditionCapability",
409 [CONDITION_HOST] = "ConditionHost",
410 [CONDITION_AC_POWER] = "ConditionACPower",
411 [CONDITION_NULL] = "ConditionNull"
412 };
413
414 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);