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 "alloc-util.h"
31 #include "apparmor-util.h"
32 #include "architecture.h"
33 #include "audit-util.h"
35 #include "condition.h"
36 #include "extract-word.h"
38 #include "glob-util.h"
39 #include "hostname-util.h"
41 #include "mount-util.h"
42 #include "parse-util.h"
43 #include "path-util.h"
44 #include "proc-cmdline.h"
45 #include "selinux-util.h"
46 #include "smack-util.h"
47 #include "stat-util.h"
48 #include "string-table.h"
49 #include "string-util.h"
53 Condition
* condition_new(ConditionType type
, const char *parameter
, bool trigger
, bool negate
) {
58 assert(type
< _CONDITION_TYPE_MAX
);
59 assert((!parameter
) == (type
== CONDITION_NULL
));
61 c
= new0(Condition
, 1);
69 r
= free_and_strdup(&c
->parameter
, parameter
);
78 void condition_free(Condition
*c
) {
85 Condition
* condition_free_list(Condition
*first
) {
88 LIST_FOREACH_SAFE(conditions
, c
, n
, first
)
94 static int condition_test_kernel_command_line(Condition
*c
) {
95 _cleanup_free_
char *line
= NULL
;
101 assert(c
->parameter
);
102 assert(c
->type
== CONDITION_KERNEL_COMMAND_LINE
);
104 r
= proc_cmdline(&line
);
108 equal
= !!strchr(c
->parameter
, '=');
112 _cleanup_free_
char *word
= NULL
;
115 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_RELAX
);
122 found
= streq(word
, c
->parameter
);
126 f
= startswith(word
, c
->parameter
);
127 found
= f
&& (*f
== '=' || *f
== 0);
137 static int condition_test_virtualization(Condition
*c
) {
141 assert(c
->parameter
);
142 assert(c
->type
== CONDITION_VIRTUALIZATION
);
144 v
= detect_virtualization();
148 /* First, compare with yes/no */
149 b
= parse_boolean(c
->parameter
);
154 if (v
== 0 && b
== 0)
157 /* Then, compare categorization */
158 if (VIRTUALIZATION_IS_VM(v
) && streq(c
->parameter
, "vm"))
161 if (VIRTUALIZATION_IS_CONTAINER(v
) && streq(c
->parameter
, "container"))
164 /* Finally compare id */
165 return v
!= VIRTUALIZATION_NONE
&& streq(c
->parameter
, virtualization_to_string(v
));
168 static int condition_test_architecture(Condition
*c
) {
172 assert(c
->parameter
);
173 assert(c
->type
== CONDITION_ARCHITECTURE
);
175 a
= uname_architecture();
179 if (streq(c
->parameter
, "native"))
180 b
= native_architecture();
182 b
= architecture_from_string(c
->parameter
);
189 static int condition_test_host(Condition
*c
) {
190 _cleanup_free_
char *h
= NULL
;
195 assert(c
->parameter
);
196 assert(c
->type
== CONDITION_HOST
);
198 if (sd_id128_from_string(c
->parameter
, &x
) >= 0) {
200 r
= sd_id128_get_machine(&y
);
204 return sd_id128_equal(x
, y
);
207 h
= gethostname_malloc();
211 return fnmatch(c
->parameter
, h
, FNM_CASEFOLD
) == 0;
214 static int condition_test_ac_power(Condition
*c
) {
218 assert(c
->parameter
);
219 assert(c
->type
== CONDITION_AC_POWER
);
221 r
= parse_boolean(c
->parameter
);
225 return (on_ac_power() != 0) == !!r
;
228 static int condition_test_security(Condition
*c
) {
230 assert(c
->parameter
);
231 assert(c
->type
== CONDITION_SECURITY
);
233 if (streq(c
->parameter
, "selinux"))
234 return mac_selinux_use();
235 if (streq(c
->parameter
, "smack"))
236 return mac_smack_use();
237 if (streq(c
->parameter
, "apparmor"))
238 return mac_apparmor_use();
239 if (streq(c
->parameter
, "audit"))
241 if (streq(c
->parameter
, "ima"))
247 static int condition_test_capability(Condition
*c
) {
248 _cleanup_fclose_
FILE *f
= NULL
;
251 unsigned long long capabilities
= -1;
254 assert(c
->parameter
);
255 assert(c
->type
== CONDITION_CAPABILITY
);
257 /* If it's an invalid capability, we don't have it */
258 value
= capability_from_name(c
->parameter
);
262 /* If it's a valid capability we default to assume
265 f
= fopen("/proc/self/status", "re");
269 while (fgets(line
, sizeof(line
), f
)) {
272 if (startswith(line
, "CapBnd:")) {
273 (void) sscanf(line
+7, "%llx", &capabilities
);
278 return !!(capabilities
& (1ULL << value
));
281 static int condition_test_needs_update(Condition
*c
) {
283 struct stat usr
, other
;
286 assert(c
->parameter
);
287 assert(c
->type
== CONDITION_NEEDS_UPDATE
);
289 /* If the file system is read-only we shouldn't suggest an update */
290 if (path_is_read_only_fs(c
->parameter
) > 0)
293 /* Any other failure means we should allow the condition to be true,
294 * so that we rather invoke too many update tools then too
297 if (!path_is_absolute(c
->parameter
))
300 p
= strjoina(c
->parameter
, "/.updated");
301 if (lstat(p
, &other
) < 0)
304 if (lstat("/usr/", &usr
) < 0)
307 return usr
.st_mtim
.tv_sec
> other
.st_mtim
.tv_sec
||
308 (usr
.st_mtim
.tv_sec
== other
.st_mtim
.tv_sec
&& usr
.st_mtim
.tv_nsec
> other
.st_mtim
.tv_nsec
);
311 static int condition_test_first_boot(Condition
*c
) {
315 assert(c
->parameter
);
316 assert(c
->type
== CONDITION_FIRST_BOOT
);
318 r
= parse_boolean(c
->parameter
);
322 return (access("/run/systemd/first-boot", F_OK
) >= 0) == !!r
;
325 static int condition_test_path_exists(Condition
*c
) {
327 assert(c
->parameter
);
328 assert(c
->type
== CONDITION_PATH_EXISTS
);
330 return access(c
->parameter
, F_OK
) >= 0;
333 static int condition_test_path_exists_glob(Condition
*c
) {
335 assert(c
->parameter
);
336 assert(c
->type
== CONDITION_PATH_EXISTS_GLOB
);
338 return glob_exists(c
->parameter
) > 0;
341 static int condition_test_path_is_directory(Condition
*c
) {
343 assert(c
->parameter
);
344 assert(c
->type
== CONDITION_PATH_IS_DIRECTORY
);
346 return is_dir(c
->parameter
, true) > 0;
349 static int condition_test_path_is_symbolic_link(Condition
*c
) {
351 assert(c
->parameter
);
352 assert(c
->type
== CONDITION_PATH_IS_SYMBOLIC_LINK
);
354 return is_symlink(c
->parameter
) > 0;
357 static int condition_test_path_is_mount_point(Condition
*c
) {
359 assert(c
->parameter
);
360 assert(c
->type
== CONDITION_PATH_IS_MOUNT_POINT
);
362 return path_is_mount_point(c
->parameter
, AT_SYMLINK_FOLLOW
) > 0;
365 static int condition_test_path_is_read_write(Condition
*c
) {
367 assert(c
->parameter
);
368 assert(c
->type
== CONDITION_PATH_IS_READ_WRITE
);
370 return path_is_read_only_fs(c
->parameter
) <= 0;
373 static int condition_test_directory_not_empty(Condition
*c
) {
377 assert(c
->parameter
);
378 assert(c
->type
== CONDITION_DIRECTORY_NOT_EMPTY
);
380 r
= dir_is_empty(c
->parameter
);
381 return r
<= 0 && r
!= -ENOENT
;
384 static int condition_test_file_not_empty(Condition
*c
) {
388 assert(c
->parameter
);
389 assert(c
->type
== CONDITION_FILE_NOT_EMPTY
);
391 return (stat(c
->parameter
, &st
) >= 0 &&
392 S_ISREG(st
.st_mode
) &&
396 static int condition_test_file_is_executable(Condition
*c
) {
400 assert(c
->parameter
);
401 assert(c
->type
== CONDITION_FILE_IS_EXECUTABLE
);
403 return (stat(c
->parameter
, &st
) >= 0 &&
404 S_ISREG(st
.st_mode
) &&
405 (st
.st_mode
& 0111));
408 static int condition_test_null(Condition
*c
) {
410 assert(c
->type
== CONDITION_NULL
);
412 /* Note that during parsing we already evaluate the string and
413 * store it in c->negate */
417 int condition_test(Condition
*c
) {
419 static int (*const condition_tests
[_CONDITION_TYPE_MAX
])(Condition
*c
) = {
420 [CONDITION_PATH_EXISTS
] = condition_test_path_exists
,
421 [CONDITION_PATH_EXISTS_GLOB
] = condition_test_path_exists_glob
,
422 [CONDITION_PATH_IS_DIRECTORY
] = condition_test_path_is_directory
,
423 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = condition_test_path_is_symbolic_link
,
424 [CONDITION_PATH_IS_MOUNT_POINT
] = condition_test_path_is_mount_point
,
425 [CONDITION_PATH_IS_READ_WRITE
] = condition_test_path_is_read_write
,
426 [CONDITION_DIRECTORY_NOT_EMPTY
] = condition_test_directory_not_empty
,
427 [CONDITION_FILE_NOT_EMPTY
] = condition_test_file_not_empty
,
428 [CONDITION_FILE_IS_EXECUTABLE
] = condition_test_file_is_executable
,
429 [CONDITION_KERNEL_COMMAND_LINE
] = condition_test_kernel_command_line
,
430 [CONDITION_VIRTUALIZATION
] = condition_test_virtualization
,
431 [CONDITION_SECURITY
] = condition_test_security
,
432 [CONDITION_CAPABILITY
] = condition_test_capability
,
433 [CONDITION_HOST
] = condition_test_host
,
434 [CONDITION_AC_POWER
] = condition_test_ac_power
,
435 [CONDITION_ARCHITECTURE
] = condition_test_architecture
,
436 [CONDITION_NEEDS_UPDATE
] = condition_test_needs_update
,
437 [CONDITION_FIRST_BOOT
] = condition_test_first_boot
,
438 [CONDITION_NULL
] = condition_test_null
,
444 assert(c
->type
>= 0);
445 assert(c
->type
< _CONDITION_TYPE_MAX
);
447 r
= condition_tests
[c
->type
](c
);
449 c
->result
= CONDITION_ERROR
;
453 b
= (r
> 0) == !c
->negate
;
454 c
->result
= b
? CONDITION_SUCCEEDED
: CONDITION_FAILED
;
458 void condition_dump(Condition
*c
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
466 "%s\t%s: %s%s%s %s\n",
469 c
->trigger
? "|" : "",
470 c
->negate
? "!" : "",
472 condition_result_to_string(c
->result
));
475 void condition_dump_list(Condition
*first
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
478 LIST_FOREACH(conditions
, c
, first
)
479 condition_dump(c
, f
, prefix
, to_string
);
482 static const char* const condition_type_table
[_CONDITION_TYPE_MAX
] = {
483 [CONDITION_ARCHITECTURE
] = "ConditionArchitecture",
484 [CONDITION_VIRTUALIZATION
] = "ConditionVirtualization",
485 [CONDITION_HOST
] = "ConditionHost",
486 [CONDITION_KERNEL_COMMAND_LINE
] = "ConditionKernelCommandLine",
487 [CONDITION_SECURITY
] = "ConditionSecurity",
488 [CONDITION_CAPABILITY
] = "ConditionCapability",
489 [CONDITION_AC_POWER
] = "ConditionACPower",
490 [CONDITION_NEEDS_UPDATE
] = "ConditionNeedsUpdate",
491 [CONDITION_FIRST_BOOT
] = "ConditionFirstBoot",
492 [CONDITION_PATH_EXISTS
] = "ConditionPathExists",
493 [CONDITION_PATH_EXISTS_GLOB
] = "ConditionPathExistsGlob",
494 [CONDITION_PATH_IS_DIRECTORY
] = "ConditionPathIsDirectory",
495 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "ConditionPathIsSymbolicLink",
496 [CONDITION_PATH_IS_MOUNT_POINT
] = "ConditionPathIsMountPoint",
497 [CONDITION_PATH_IS_READ_WRITE
] = "ConditionPathIsReadWrite",
498 [CONDITION_DIRECTORY_NOT_EMPTY
] = "ConditionDirectoryNotEmpty",
499 [CONDITION_FILE_NOT_EMPTY
] = "ConditionFileNotEmpty",
500 [CONDITION_FILE_IS_EXECUTABLE
] = "ConditionFileIsExecutable",
501 [CONDITION_NULL
] = "ConditionNull"
504 DEFINE_STRING_TABLE_LOOKUP(condition_type
, ConditionType
);
506 static const char* const assert_type_table
[_CONDITION_TYPE_MAX
] = {
507 [CONDITION_ARCHITECTURE
] = "AssertArchitecture",
508 [CONDITION_VIRTUALIZATION
] = "AssertVirtualization",
509 [CONDITION_HOST
] = "AssertHost",
510 [CONDITION_KERNEL_COMMAND_LINE
] = "AssertKernelCommandLine",
511 [CONDITION_SECURITY
] = "AssertSecurity",
512 [CONDITION_CAPABILITY
] = "AssertCapability",
513 [CONDITION_AC_POWER
] = "AssertACPower",
514 [CONDITION_NEEDS_UPDATE
] = "AssertNeedsUpdate",
515 [CONDITION_FIRST_BOOT
] = "AssertFirstBoot",
516 [CONDITION_PATH_EXISTS
] = "AssertPathExists",
517 [CONDITION_PATH_EXISTS_GLOB
] = "AssertPathExistsGlob",
518 [CONDITION_PATH_IS_DIRECTORY
] = "AssertPathIsDirectory",
519 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "AssertPathIsSymbolicLink",
520 [CONDITION_PATH_IS_MOUNT_POINT
] = "AssertPathIsMountPoint",
521 [CONDITION_PATH_IS_READ_WRITE
] = "AssertPathIsReadWrite",
522 [CONDITION_DIRECTORY_NOT_EMPTY
] = "AssertDirectoryNotEmpty",
523 [CONDITION_FILE_NOT_EMPTY
] = "AssertFileNotEmpty",
524 [CONDITION_FILE_IS_EXECUTABLE
] = "AssertFileIsExecutable",
525 [CONDITION_NULL
] = "AssertNull"
528 DEFINE_STRING_TABLE_LOOKUP(assert_type
, ConditionType
);
530 static const char* const condition_result_table
[_CONDITION_RESULT_MAX
] = {
531 [CONDITION_UNTESTED
] = "untested",
532 [CONDITION_SUCCEEDED
] = "succeeded",
533 [CONDITION_FAILED
] = "failed",
534 [CONDITION_ERROR
] = "error",
537 DEFINE_STRING_TABLE_LOOKUP(condition_result
, ConditionResult
);