]>
Commit | Line | Data |
---|---|---|
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 | 42 | Condition* 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 | ||
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 | ||
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 | 128 | static 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 |
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 | ||
07e833bc MS |
172 | static 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 |
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 | ||
7670e5a2 TJ |
213 | fclose(f); |
214 | ||
62590f23 LP |
215 | return !!(capabilities & (1ULL << value)); |
216 | } | |
217 | ||
c0d6e764 LP |
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 | ||
240dbaa4 LP |
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 | ||
52990c2e | 253 | static bool condition_test(Condition *c) { |
52661efd LP |
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 | 337 | bool 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); | |
52990c2e | 361 | c->state = b ? 1 : -1; |
267632f0 LP |
362 | |
363 | if (!c->trigger && !b) | |
364 | return false; | |
365 | ||
366 | if (c->trigger && triggered <= 0) | |
367 | triggered = b; | |
368 | } | |
52661efd | 369 | |
267632f0 | 370 | return triggered != 0; |
52661efd LP |
371 | } |
372 | ||
373 | void condition_dump(Condition *c, FILE *f, const char *prefix) { | |
374 | assert(c); | |
375 | assert(f); | |
376 | ||
377 | if (!prefix) | |
378 | prefix = ""; | |
379 | ||
380 | fprintf(f, | |
52990c2e | 381 | "%s\t%s: %s%s%s %s\n", |
52661efd LP |
382 | prefix, |
383 | condition_type_to_string(c->type), | |
267632f0 | 384 | c->trigger ? "|" : "", |
52661efd | 385 | c->negate ? "!" : "", |
52990c2e ZJS |
386 | c->parameter, |
387 | c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested"); | |
52661efd LP |
388 | } |
389 | ||
390 | void condition_dump_list(Condition *first, FILE *f, const char *prefix) { | |
391 | Condition *c; | |
392 | ||
393 | LIST_FOREACH(conditions, c, first) | |
394 | condition_dump(c, f, prefix); | |
395 | } | |
396 | ||
397 | static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { | |
d257ddef | 398 | [CONDITION_PATH_EXISTS] = "ConditionPathExists", |
8092a428 | 399 | [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob", |
8fb81fa7 | 400 | [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory", |
0d60602c | 401 | [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink", |
ab7f148f | 402 | [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint", |
d0516109 | 403 | [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite", |
8fb81fa7 | 404 | [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", |
742a862b | 405 | [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty", |
a8b409db | 406 | [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable", |
8fb81fa7 MS |
407 | [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", |
408 | [CONDITION_VIRTUALIZATION] = "ConditionVirtualization", | |
07e833bc | 409 | [CONDITION_SECURITY] = "ConditionSecurity", |
a8b409db | 410 | [CONDITION_CAPABILITY] = "ConditionCapability", |
c0d6e764 | 411 | [CONDITION_HOST] = "ConditionHost", |
240dbaa4 | 412 | [CONDITION_AC_POWER] = "ConditionACPower", |
d257ddef | 413 | [CONDITION_NULL] = "ConditionNull" |
52661efd LP |
414 | }; |
415 | ||
416 | DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType); |