]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/condition.c
systemd: log failed conditions
[thirdparty/systemd.git] / src / core / condition.c
CommitLineData
52661efd
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
f23c09b0 6 Copyright 2010 Lennart Poettering
52661efd
LP
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
52661efd
LP
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
5430f7f2 16 Lesser General Public License for more details.
52661efd 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
52661efd
LP
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>
62590f23 26#include <sys/capability.h>
d0516109 27#include <sys/statvfs.h>
c0d6e764 28#include <fnmatch.h>
52661efd 29
07e833bc
MS
30#ifdef HAVE_SELINUX
31#include <selinux/selinux.h>
32#endif
33
c0d6e764 34#include <systemd/sd-id128.h>
52661efd
LP
35#include "util.h"
36#include "condition.h"
8095200d 37#include "virt.h"
9eb977db 38#include "path-util.h"
a5c32cff 39#include "fileio.h"
4b744dfa 40#include "unit.h"
52661efd 41
267632f0 42Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
52661efd
LP
43 Condition *c;
44
8092a428
LP
45 assert(type < _CONDITION_TYPE_MAX);
46
ab7f148f
LP
47 c = new0(Condition, 1);
48 if (!c)
da19d5c1
LP
49 return NULL;
50
52661efd 51 c->type = type;
267632f0 52 c->trigger = trigger;
52661efd
LP
53 c->negate = negate;
54
ab7f148f
LP
55 if (parameter) {
56 c->parameter = strdup(parameter);
57 if (!c->parameter) {
d257ddef
LP
58 free(c);
59 return NULL;
60 }
ab7f148f 61 }
52661efd
LP
62
63 return c;
64}
65
66void condition_free(Condition *c) {
67 assert(c);
68
69 free(c->parameter);
70 free(c);
71}
72
73void condition_free_list(Condition *first) {
74 Condition *c, *n;
75
76 LIST_FOREACH_SAFE(conditions, c, n, first)
77 condition_free(c);
78}
79
80static 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
039655a4
LP
87 assert(parameter);
88
a373b0e7 89 if (detect_container(NULL) > 0)
2fc97846
LP
90 return false;
91
ab7f148f
LP
92 r = read_one_line_file("/proc/cmdline", &line);
93 if (r < 0) {
52661efd
LP
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);
ab7f148f
LP
104 word = strndup(w, l);
105 if (!word)
52661efd
LP
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
039655a4 128static bool test_virtualization(const char *parameter) {
8095200d
LP
129 int b;
130 Virtualization v;
039655a4
LP
131 const char *id;
132
133 assert(parameter);
134
8095200d
LP
135 v = detect_virtualization(&id);
136 if (v < 0) {
137 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
039655a4
LP
138 return false;
139 }
140
8095200d 141 /* First, compare with yes/no */
039655a4
LP
142 b = parse_boolean(parameter);
143
8095200d
LP
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"))
039655a4
LP
152 return true;
153
8095200d 154 if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
039655a4
LP
155 return true;
156
8095200d 157 /* Finally compare id */
e5396fed 158 return v > 0 && streq(parameter, id);
039655a4
LP
159}
160
cb0edd73
NC
161static 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
07e833bc
MS
172static bool test_security(const char *parameter) {
173#ifdef HAVE_SELINUX
d24e1b48 174 if (streq(parameter, "selinux"))
07e833bc
MS
175 return is_selinux_enabled() > 0;
176#endif
a41f47ab 177 if (streq(parameter, "apparmor"))
cb0edd73 178 return test_apparmor_enabled();
9d995d54
AK
179 if (streq(parameter, "ima"))
180 return access("/sys/kernel/security/ima/", F_OK) == 0;
a41f47ab
AK
181 if (streq(parameter, "smack"))
182 return access("/sys/fs/smackfs", F_OK) == 0;
07e833bc
MS
183 return false;
184}
185
62590f23
LP
186static 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
7670e5a2
TJ
213 fclose(f);
214
62590f23
LP
215 return !!(capabilities & (1ULL << value));
216}
217
c0d6e764
LP
218static 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
240dbaa4
LP
243static 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
52661efd
LP
253bool 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
8092a428
LP
261 case CONDITION_PATH_EXISTS_GLOB:
262 return (glob_exists(c->parameter) > 0) == !c->negate;
263
2b583ce6
KS
264 case CONDITION_PATH_IS_DIRECTORY: {
265 struct stat st;
266
8571962c 267 if (stat(c->parameter, &st) < 0)
1f8fef5a 268 return c->negate;
2b583ce6
KS
269 return S_ISDIR(st.st_mode) == !c->negate;
270 }
271
0d60602c
MS
272 case CONDITION_PATH_IS_SYMBOLIC_LINK: {
273 struct stat st;
274
275 if (lstat(c->parameter, &st) < 0)
1f8fef5a 276 return c->negate;
0d60602c
MS
277 return S_ISLNK(st.st_mode) == !c->negate;
278 }
279
ab7f148f
LP
280 case CONDITION_PATH_IS_MOUNT_POINT:
281 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
282
3d9a4122
LP
283 case CONDITION_PATH_IS_READ_WRITE:
284 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
d0516109 285
36af55d9
LP
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
742a862b
LP
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
82e487c5
LP
302 case CONDITION_FILE_IS_EXECUTABLE: {
303 struct stat st;
304
34a2dc4b 305 if (stat(c->parameter, &st) < 0)
1f8fef5a 306 return c->negate;
82e487c5
LP
307
308 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
309 }
310
52661efd 311 case CONDITION_KERNEL_COMMAND_LINE:
cb40c15e 312 return test_kernel_command_line(c->parameter) == !c->negate;
52661efd 313
039655a4 314 case CONDITION_VIRTUALIZATION:
cb40c15e 315 return test_virtualization(c->parameter) == !c->negate;
039655a4 316
07e833bc
MS
317 case CONDITION_SECURITY:
318 return test_security(c->parameter) == !c->negate;
319
62590f23
LP
320 case CONDITION_CAPABILITY:
321 return test_capability(c->parameter) == !c->negate;
322
c0d6e764
LP
323 case CONDITION_HOST:
324 return test_host(c->parameter) == !c->negate;
325
240dbaa4
LP
326 case CONDITION_AC_POWER:
327 return test_ac_power(c->parameter) == !c->negate;
328
d257ddef
LP
329 case CONDITION_NULL:
330 return !c->negate;
331
52661efd
LP
332 default:
333 assert_not_reached("Invalid condition type.");
334 }
335}
336
4b744dfa 337bool condition_test_list(const char *unit, Condition *first) {
52661efd 338 Condition *c;
267632f0 339 int triggered = -1;
52661efd
LP
340
341 /* If the condition list is empty, then it is true */
342 if (!first)
343 return true;
344
267632f0
LP
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);
4b744dfa
ZJS
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);
267632f0
LP
361
362 if (!c->trigger && !b)
363 return false;
364
365 if (c->trigger && triggered <= 0)
366 triggered = b;
367 }
52661efd 368
267632f0 369 return triggered != 0;
52661efd
LP
370}
371
372void 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,
8fb81fa7 380 "%s\t%s: %s%s%s\n",
52661efd
LP
381 prefix,
382 condition_type_to_string(c->type),
267632f0 383 c->trigger ? "|" : "",
52661efd
LP
384 c->negate ? "!" : "",
385 c->parameter);
386}
387
388void 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
395static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
d257ddef 396 [CONDITION_PATH_EXISTS] = "ConditionPathExists",
8092a428 397 [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
8fb81fa7 398 [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
0d60602c 399 [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
ab7f148f 400 [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
d0516109 401 [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
8fb81fa7 402 [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
742a862b 403 [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
a8b409db 404 [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
8fb81fa7
MS
405 [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
406 [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
07e833bc 407 [CONDITION_SECURITY] = "ConditionSecurity",
a8b409db 408 [CONDITION_CAPABILITY] = "ConditionCapability",
c0d6e764 409 [CONDITION_HOST] = "ConditionHost",
240dbaa4 410 [CONDITION_AC_POWER] = "ConditionACPower",
d257ddef 411 [CONDITION_NULL] = "ConditionNull"
52661efd
LP
412};
413
414DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);