1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
30 #include "apparmor-util.h"
31 #include "architecture.h"
34 #include "condition.h"
35 #include "extract-word.h"
37 #include "hostname-util.h"
39 #include "parse-util.h"
40 #include "path-util.h"
41 #include "selinux-util.h"
42 #include "smack-util.h"
43 #include "string-util.h"
47 Condition
* condition_new(ConditionType type
, const char *parameter
, bool trigger
, bool negate
) {
52 assert(type
< _CONDITION_TYPE_MAX
);
53 assert((!parameter
) == (type
== CONDITION_NULL
));
55 c
= new0(Condition
, 1);
63 r
= free_and_strdup(&c
->parameter
, parameter
);
72 void condition_free(Condition
*c
) {
79 Condition
* condition_free_list(Condition
*first
) {
82 LIST_FOREACH_SAFE(conditions
, c
, n
, first
)
88 static int condition_test_kernel_command_line(Condition
*c
) {
89 _cleanup_free_
char *line
= NULL
;
96 assert(c
->type
== CONDITION_KERNEL_COMMAND_LINE
);
98 r
= proc_cmdline(&line
);
102 equal
= !!strchr(c
->parameter
, '=');
106 _cleanup_free_
char *word
= NULL
;
109 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_RELAX
);
116 found
= streq(word
, c
->parameter
);
120 f
= startswith(word
, c
->parameter
);
121 found
= f
&& (*f
== '=' || *f
== 0);
131 static int condition_test_virtualization(Condition
*c
) {
135 assert(c
->parameter
);
136 assert(c
->type
== CONDITION_VIRTUALIZATION
);
138 v
= detect_virtualization();
142 /* First, compare with yes/no */
143 b
= parse_boolean(c
->parameter
);
148 if (v
== 0 && b
== 0)
151 /* Then, compare categorization */
152 if (VIRTUALIZATION_IS_VM(v
) && streq(c
->parameter
, "vm"))
155 if (VIRTUALIZATION_IS_CONTAINER(v
) && streq(c
->parameter
, "container"))
158 /* Finally compare id */
159 return v
!= VIRTUALIZATION_NONE
&& streq(c
->parameter
, virtualization_to_string(v
));
162 static int condition_test_architecture(Condition
*c
) {
166 assert(c
->parameter
);
167 assert(c
->type
== CONDITION_ARCHITECTURE
);
169 a
= uname_architecture();
173 if (streq(c
->parameter
, "native"))
174 b
= native_architecture();
176 b
= architecture_from_string(c
->parameter
);
183 static int condition_test_host(Condition
*c
) {
184 _cleanup_free_
char *h
= NULL
;
189 assert(c
->parameter
);
190 assert(c
->type
== CONDITION_HOST
);
192 if (sd_id128_from_string(c
->parameter
, &x
) >= 0) {
194 r
= sd_id128_get_machine(&y
);
198 return sd_id128_equal(x
, y
);
201 h
= gethostname_malloc();
205 return fnmatch(c
->parameter
, h
, FNM_CASEFOLD
) == 0;
208 static int condition_test_ac_power(Condition
*c
) {
212 assert(c
->parameter
);
213 assert(c
->type
== CONDITION_AC_POWER
);
215 r
= parse_boolean(c
->parameter
);
219 return (on_ac_power() != 0) == !!r
;
222 static int condition_test_security(Condition
*c
) {
224 assert(c
->parameter
);
225 assert(c
->type
== CONDITION_SECURITY
);
227 if (streq(c
->parameter
, "selinux"))
228 return mac_selinux_use();
229 if (streq(c
->parameter
, "smack"))
230 return mac_smack_use();
231 if (streq(c
->parameter
, "apparmor"))
232 return mac_apparmor_use();
233 if (streq(c
->parameter
, "audit"))
235 if (streq(c
->parameter
, "ima"))
241 static int condition_test_capability(Condition
*c
) {
242 _cleanup_fclose_
FILE *f
= NULL
;
245 unsigned long long capabilities
= -1;
248 assert(c
->parameter
);
249 assert(c
->type
== CONDITION_CAPABILITY
);
251 /* If it's an invalid capability, we don't have it */
252 value
= capability_from_name(c
->parameter
);
256 /* If it's a valid capability we default to assume
259 f
= fopen("/proc/self/status", "re");
263 while (fgets(line
, sizeof(line
), f
)) {
266 if (startswith(line
, "CapBnd:")) {
267 (void) sscanf(line
+7, "%llx", &capabilities
);
272 return !!(capabilities
& (1ULL << value
));
275 static int condition_test_needs_update(Condition
*c
) {
277 struct stat usr
, other
;
280 assert(c
->parameter
);
281 assert(c
->type
== CONDITION_NEEDS_UPDATE
);
283 /* If the file system is read-only we shouldn't suggest an update */
284 if (path_is_read_only_fs(c
->parameter
) > 0)
287 /* Any other failure means we should allow the condition to be true,
288 * so that we rather invoke too many update tools then too
291 if (!path_is_absolute(c
->parameter
))
294 p
= strjoina(c
->parameter
, "/.updated");
295 if (lstat(p
, &other
) < 0)
298 if (lstat("/usr/", &usr
) < 0)
301 return usr
.st_mtim
.tv_sec
> other
.st_mtim
.tv_sec
||
302 (usr
.st_mtim
.tv_sec
== other
.st_mtim
.tv_sec
&& usr
.st_mtim
.tv_nsec
> other
.st_mtim
.tv_nsec
);
305 static int condition_test_first_boot(Condition
*c
) {
309 assert(c
->parameter
);
310 assert(c
->type
== CONDITION_FIRST_BOOT
);
312 r
= parse_boolean(c
->parameter
);
316 return (access("/run/systemd/first-boot", F_OK
) >= 0) == !!r
;
319 static int condition_test_path_exists(Condition
*c
) {
321 assert(c
->parameter
);
322 assert(c
->type
== CONDITION_PATH_EXISTS
);
324 return access(c
->parameter
, F_OK
) >= 0;
327 static int condition_test_path_exists_glob(Condition
*c
) {
329 assert(c
->parameter
);
330 assert(c
->type
== CONDITION_PATH_EXISTS_GLOB
);
332 return glob_exists(c
->parameter
) > 0;
335 static int condition_test_path_is_directory(Condition
*c
) {
337 assert(c
->parameter
);
338 assert(c
->type
== CONDITION_PATH_IS_DIRECTORY
);
340 return is_dir(c
->parameter
, true) > 0;
343 static int condition_test_path_is_symbolic_link(Condition
*c
) {
345 assert(c
->parameter
);
346 assert(c
->type
== CONDITION_PATH_IS_SYMBOLIC_LINK
);
348 return is_symlink(c
->parameter
) > 0;
351 static int condition_test_path_is_mount_point(Condition
*c
) {
353 assert(c
->parameter
);
354 assert(c
->type
== CONDITION_PATH_IS_MOUNT_POINT
);
356 return path_is_mount_point(c
->parameter
, AT_SYMLINK_FOLLOW
) > 0;
359 static int condition_test_path_is_read_write(Condition
*c
) {
361 assert(c
->parameter
);
362 assert(c
->type
== CONDITION_PATH_IS_READ_WRITE
);
364 return path_is_read_only_fs(c
->parameter
) <= 0;
367 static int condition_test_directory_not_empty(Condition
*c
) {
371 assert(c
->parameter
);
372 assert(c
->type
== CONDITION_DIRECTORY_NOT_EMPTY
);
374 r
= dir_is_empty(c
->parameter
);
375 return r
<= 0 && r
!= -ENOENT
;
378 static int condition_test_file_not_empty(Condition
*c
) {
382 assert(c
->parameter
);
383 assert(c
->type
== CONDITION_FILE_NOT_EMPTY
);
385 return (stat(c
->parameter
, &st
) >= 0 &&
386 S_ISREG(st
.st_mode
) &&
390 static int condition_test_file_is_executable(Condition
*c
) {
394 assert(c
->parameter
);
395 assert(c
->type
== CONDITION_FILE_IS_EXECUTABLE
);
397 return (stat(c
->parameter
, &st
) >= 0 &&
398 S_ISREG(st
.st_mode
) &&
399 (st
.st_mode
& 0111));
402 static int condition_test_null(Condition
*c
) {
404 assert(c
->type
== CONDITION_NULL
);
406 /* Note that during parsing we already evaluate the string and
407 * store it in c->negate */
411 int condition_test(Condition
*c
) {
413 static int (*const condition_tests
[_CONDITION_TYPE_MAX
])(Condition
*c
) = {
414 [CONDITION_PATH_EXISTS
] = condition_test_path_exists
,
415 [CONDITION_PATH_EXISTS_GLOB
] = condition_test_path_exists_glob
,
416 [CONDITION_PATH_IS_DIRECTORY
] = condition_test_path_is_directory
,
417 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = condition_test_path_is_symbolic_link
,
418 [CONDITION_PATH_IS_MOUNT_POINT
] = condition_test_path_is_mount_point
,
419 [CONDITION_PATH_IS_READ_WRITE
] = condition_test_path_is_read_write
,
420 [CONDITION_DIRECTORY_NOT_EMPTY
] = condition_test_directory_not_empty
,
421 [CONDITION_FILE_NOT_EMPTY
] = condition_test_file_not_empty
,
422 [CONDITION_FILE_IS_EXECUTABLE
] = condition_test_file_is_executable
,
423 [CONDITION_KERNEL_COMMAND_LINE
] = condition_test_kernel_command_line
,
424 [CONDITION_VIRTUALIZATION
] = condition_test_virtualization
,
425 [CONDITION_SECURITY
] = condition_test_security
,
426 [CONDITION_CAPABILITY
] = condition_test_capability
,
427 [CONDITION_HOST
] = condition_test_host
,
428 [CONDITION_AC_POWER
] = condition_test_ac_power
,
429 [CONDITION_ARCHITECTURE
] = condition_test_architecture
,
430 [CONDITION_NEEDS_UPDATE
] = condition_test_needs_update
,
431 [CONDITION_FIRST_BOOT
] = condition_test_first_boot
,
432 [CONDITION_NULL
] = condition_test_null
,
438 assert(c
->type
>= 0);
439 assert(c
->type
< _CONDITION_TYPE_MAX
);
441 r
= condition_tests
[c
->type
](c
);
443 c
->result
= CONDITION_ERROR
;
447 b
= (r
> 0) == !c
->negate
;
448 c
->result
= b
? CONDITION_SUCCEEDED
: CONDITION_FAILED
;
452 void condition_dump(Condition
*c
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
460 "%s\t%s: %s%s%s %s\n",
463 c
->trigger
? "|" : "",
464 c
->negate
? "!" : "",
466 condition_result_to_string(c
->result
));
469 void condition_dump_list(Condition
*first
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
472 LIST_FOREACH(conditions
, c
, first
)
473 condition_dump(c
, f
, prefix
, to_string
);
476 static const char* const condition_type_table
[_CONDITION_TYPE_MAX
] = {
477 [CONDITION_ARCHITECTURE
] = "ConditionArchitecture",
478 [CONDITION_VIRTUALIZATION
] = "ConditionVirtualization",
479 [CONDITION_HOST
] = "ConditionHost",
480 [CONDITION_KERNEL_COMMAND_LINE
] = "ConditionKernelCommandLine",
481 [CONDITION_SECURITY
] = "ConditionSecurity",
482 [CONDITION_CAPABILITY
] = "ConditionCapability",
483 [CONDITION_AC_POWER
] = "ConditionACPower",
484 [CONDITION_NEEDS_UPDATE
] = "ConditionNeedsUpdate",
485 [CONDITION_FIRST_BOOT
] = "ConditionFirstBoot",
486 [CONDITION_PATH_EXISTS
] = "ConditionPathExists",
487 [CONDITION_PATH_EXISTS_GLOB
] = "ConditionPathExistsGlob",
488 [CONDITION_PATH_IS_DIRECTORY
] = "ConditionPathIsDirectory",
489 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "ConditionPathIsSymbolicLink",
490 [CONDITION_PATH_IS_MOUNT_POINT
] = "ConditionPathIsMountPoint",
491 [CONDITION_PATH_IS_READ_WRITE
] = "ConditionPathIsReadWrite",
492 [CONDITION_DIRECTORY_NOT_EMPTY
] = "ConditionDirectoryNotEmpty",
493 [CONDITION_FILE_NOT_EMPTY
] = "ConditionFileNotEmpty",
494 [CONDITION_FILE_IS_EXECUTABLE
] = "ConditionFileIsExecutable",
495 [CONDITION_NULL
] = "ConditionNull"
498 DEFINE_STRING_TABLE_LOOKUP(condition_type
, ConditionType
);
500 static const char* const assert_type_table
[_CONDITION_TYPE_MAX
] = {
501 [CONDITION_ARCHITECTURE
] = "AssertArchitecture",
502 [CONDITION_VIRTUALIZATION
] = "AssertVirtualization",
503 [CONDITION_HOST
] = "AssertHost",
504 [CONDITION_KERNEL_COMMAND_LINE
] = "AssertKernelCommandLine",
505 [CONDITION_SECURITY
] = "AssertSecurity",
506 [CONDITION_CAPABILITY
] = "AssertCapability",
507 [CONDITION_AC_POWER
] = "AssertACPower",
508 [CONDITION_NEEDS_UPDATE
] = "AssertNeedsUpdate",
509 [CONDITION_FIRST_BOOT
] = "AssertFirstBoot",
510 [CONDITION_PATH_EXISTS
] = "AssertPathExists",
511 [CONDITION_PATH_EXISTS_GLOB
] = "AssertPathExistsGlob",
512 [CONDITION_PATH_IS_DIRECTORY
] = "AssertPathIsDirectory",
513 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "AssertPathIsSymbolicLink",
514 [CONDITION_PATH_IS_MOUNT_POINT
] = "AssertPathIsMountPoint",
515 [CONDITION_PATH_IS_READ_WRITE
] = "AssertPathIsReadWrite",
516 [CONDITION_DIRECTORY_NOT_EMPTY
] = "AssertDirectoryNotEmpty",
517 [CONDITION_FILE_NOT_EMPTY
] = "AssertFileNotEmpty",
518 [CONDITION_FILE_IS_EXECUTABLE
] = "AssertFileIsExecutable",
519 [CONDITION_NULL
] = "AssertNull"
522 DEFINE_STRING_TABLE_LOOKUP(assert_type
, ConditionType
);
524 static const char* const condition_result_table
[_CONDITION_RESULT_MAX
] = {
525 [CONDITION_UNTESTED
] = "untested",
526 [CONDITION_SUCCEEDED
] = "succeeded",
527 [CONDITION_FAILED
] = "failed",
528 [CONDITION_ERROR
] = "error",
531 DEFINE_STRING_TABLE_LOOKUP(condition_result
, ConditionResult
);