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/>.
34 #include "alloc-util.h"
35 #include "apparmor-util.h"
36 #include "architecture.h"
37 #include "audit-util.h"
39 #include "condition.h"
40 #include "extract-word.h"
42 #include "glob-util.h"
43 #include "hostname-util.h"
47 #include "mount-util.h"
48 #include "parse-util.h"
49 #include "path-util.h"
50 #include "proc-cmdline.h"
51 #include "selinux-util.h"
52 #include "smack-util.h"
53 #include "stat-util.h"
54 #include "string-table.h"
55 #include "string-util.h"
59 Condition
* condition_new(ConditionType type
, const char *parameter
, bool trigger
, bool negate
) {
64 assert(type
< _CONDITION_TYPE_MAX
);
65 assert((!parameter
) == (type
== CONDITION_NULL
));
67 c
= new0(Condition
, 1);
75 r
= free_and_strdup(&c
->parameter
, parameter
);
84 void condition_free(Condition
*c
) {
91 Condition
* condition_free_list(Condition
*first
) {
94 LIST_FOREACH_SAFE(conditions
, c
, n
, first
)
100 static int condition_test_kernel_command_line(Condition
*c
) {
101 _cleanup_free_
char *line
= NULL
;
107 assert(c
->parameter
);
108 assert(c
->type
== CONDITION_KERNEL_COMMAND_LINE
);
110 r
= proc_cmdline(&line
);
114 equal
= !!strchr(c
->parameter
, '=');
118 _cleanup_free_
char *word
= NULL
;
121 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_RELAX
);
128 found
= streq(word
, c
->parameter
);
132 f
= startswith(word
, c
->parameter
);
133 found
= f
&& (*f
== '=' || *f
== 0);
143 static int condition_test_virtualization(Condition
*c
) {
147 assert(c
->parameter
);
148 assert(c
->type
== CONDITION_VIRTUALIZATION
);
150 v
= detect_virtualization();
154 /* First, compare with yes/no */
155 b
= parse_boolean(c
->parameter
);
160 if (v
== 0 && b
== 0)
163 /* Then, compare categorization */
164 if (VIRTUALIZATION_IS_VM(v
) && streq(c
->parameter
, "vm"))
167 if (VIRTUALIZATION_IS_CONTAINER(v
) && streq(c
->parameter
, "container"))
170 /* Finally compare id */
171 return v
!= VIRTUALIZATION_NONE
&& streq(c
->parameter
, virtualization_to_string(v
));
174 static int condition_test_architecture(Condition
*c
) {
178 assert(c
->parameter
);
179 assert(c
->type
== CONDITION_ARCHITECTURE
);
181 a
= uname_architecture();
185 if (streq(c
->parameter
, "native"))
186 b
= native_architecture();
188 b
= architecture_from_string(c
->parameter
);
195 static int condition_test_host(Condition
*c
) {
196 _cleanup_free_
char *h
= NULL
;
201 assert(c
->parameter
);
202 assert(c
->type
== CONDITION_HOST
);
204 if (sd_id128_from_string(c
->parameter
, &x
) >= 0) {
206 r
= sd_id128_get_machine(&y
);
210 return sd_id128_equal(x
, y
);
213 h
= gethostname_malloc();
217 return fnmatch(c
->parameter
, h
, FNM_CASEFOLD
) == 0;
220 static int condition_test_ac_power(Condition
*c
) {
224 assert(c
->parameter
);
225 assert(c
->type
== CONDITION_AC_POWER
);
227 r
= parse_boolean(c
->parameter
);
231 return (on_ac_power() != 0) == !!r
;
234 static int condition_test_security(Condition
*c
) {
236 assert(c
->parameter
);
237 assert(c
->type
== CONDITION_SECURITY
);
239 if (streq(c
->parameter
, "selinux"))
240 return mac_selinux_have();
241 if (streq(c
->parameter
, "smack"))
242 return mac_smack_use();
243 if (streq(c
->parameter
, "apparmor"))
244 return mac_apparmor_use();
245 if (streq(c
->parameter
, "audit"))
247 if (streq(c
->parameter
, "ima"))
253 static int condition_test_capability(Condition
*c
) {
254 _cleanup_fclose_
FILE *f
= NULL
;
257 unsigned long long capabilities
= -1;
260 assert(c
->parameter
);
261 assert(c
->type
== CONDITION_CAPABILITY
);
263 /* If it's an invalid capability, we don't have it */
264 value
= capability_from_name(c
->parameter
);
268 /* If it's a valid capability we default to assume
271 f
= fopen("/proc/self/status", "re");
275 while (fgets(line
, sizeof(line
), f
)) {
278 if (startswith(line
, "CapBnd:")) {
279 (void) sscanf(line
+7, "%llx", &capabilities
);
284 return !!(capabilities
& (1ULL << value
));
287 static int condition_test_needs_update(Condition
*c
) {
289 struct stat usr
, other
;
292 assert(c
->parameter
);
293 assert(c
->type
== CONDITION_NEEDS_UPDATE
);
295 /* If the file system is read-only we shouldn't suggest an update */
296 if (path_is_read_only_fs(c
->parameter
) > 0)
299 /* Any other failure means we should allow the condition to be true,
300 * so that we rather invoke too many update tools then too
303 if (!path_is_absolute(c
->parameter
))
306 p
= strjoina(c
->parameter
, "/.updated");
307 if (lstat(p
, &other
) < 0)
310 if (lstat("/usr/", &usr
) < 0)
313 return usr
.st_mtim
.tv_sec
> other
.st_mtim
.tv_sec
||
314 (usr
.st_mtim
.tv_sec
== other
.st_mtim
.tv_sec
&& usr
.st_mtim
.tv_nsec
> other
.st_mtim
.tv_nsec
);
317 static int condition_test_first_boot(Condition
*c
) {
321 assert(c
->parameter
);
322 assert(c
->type
== CONDITION_FIRST_BOOT
);
324 r
= parse_boolean(c
->parameter
);
328 return (access("/run/systemd/first-boot", F_OK
) >= 0) == !!r
;
331 static int condition_test_path_exists(Condition
*c
) {
333 assert(c
->parameter
);
334 assert(c
->type
== CONDITION_PATH_EXISTS
);
336 return access(c
->parameter
, F_OK
) >= 0;
339 static int condition_test_path_exists_glob(Condition
*c
) {
341 assert(c
->parameter
);
342 assert(c
->type
== CONDITION_PATH_EXISTS_GLOB
);
344 return glob_exists(c
->parameter
) > 0;
347 static int condition_test_path_is_directory(Condition
*c
) {
349 assert(c
->parameter
);
350 assert(c
->type
== CONDITION_PATH_IS_DIRECTORY
);
352 return is_dir(c
->parameter
, true) > 0;
355 static int condition_test_path_is_symbolic_link(Condition
*c
) {
357 assert(c
->parameter
);
358 assert(c
->type
== CONDITION_PATH_IS_SYMBOLIC_LINK
);
360 return is_symlink(c
->parameter
) > 0;
363 static int condition_test_path_is_mount_point(Condition
*c
) {
365 assert(c
->parameter
);
366 assert(c
->type
== CONDITION_PATH_IS_MOUNT_POINT
);
368 return path_is_mount_point(c
->parameter
, AT_SYMLINK_FOLLOW
) > 0;
371 static int condition_test_path_is_read_write(Condition
*c
) {
373 assert(c
->parameter
);
374 assert(c
->type
== CONDITION_PATH_IS_READ_WRITE
);
376 return path_is_read_only_fs(c
->parameter
) <= 0;
379 static int condition_test_directory_not_empty(Condition
*c
) {
383 assert(c
->parameter
);
384 assert(c
->type
== CONDITION_DIRECTORY_NOT_EMPTY
);
386 r
= dir_is_empty(c
->parameter
);
387 return r
<= 0 && r
!= -ENOENT
;
390 static int condition_test_file_not_empty(Condition
*c
) {
394 assert(c
->parameter
);
395 assert(c
->type
== CONDITION_FILE_NOT_EMPTY
);
397 return (stat(c
->parameter
, &st
) >= 0 &&
398 S_ISREG(st
.st_mode
) &&
402 static int condition_test_file_is_executable(Condition
*c
) {
406 assert(c
->parameter
);
407 assert(c
->type
== CONDITION_FILE_IS_EXECUTABLE
);
409 return (stat(c
->parameter
, &st
) >= 0 &&
410 S_ISREG(st
.st_mode
) &&
411 (st
.st_mode
& 0111));
414 static int condition_test_null(Condition
*c
) {
416 assert(c
->type
== CONDITION_NULL
);
418 /* Note that during parsing we already evaluate the string and
419 * store it in c->negate */
423 int condition_test(Condition
*c
) {
425 static int (*const condition_tests
[_CONDITION_TYPE_MAX
])(Condition
*c
) = {
426 [CONDITION_PATH_EXISTS
] = condition_test_path_exists
,
427 [CONDITION_PATH_EXISTS_GLOB
] = condition_test_path_exists_glob
,
428 [CONDITION_PATH_IS_DIRECTORY
] = condition_test_path_is_directory
,
429 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = condition_test_path_is_symbolic_link
,
430 [CONDITION_PATH_IS_MOUNT_POINT
] = condition_test_path_is_mount_point
,
431 [CONDITION_PATH_IS_READ_WRITE
] = condition_test_path_is_read_write
,
432 [CONDITION_DIRECTORY_NOT_EMPTY
] = condition_test_directory_not_empty
,
433 [CONDITION_FILE_NOT_EMPTY
] = condition_test_file_not_empty
,
434 [CONDITION_FILE_IS_EXECUTABLE
] = condition_test_file_is_executable
,
435 [CONDITION_KERNEL_COMMAND_LINE
] = condition_test_kernel_command_line
,
436 [CONDITION_VIRTUALIZATION
] = condition_test_virtualization
,
437 [CONDITION_SECURITY
] = condition_test_security
,
438 [CONDITION_CAPABILITY
] = condition_test_capability
,
439 [CONDITION_HOST
] = condition_test_host
,
440 [CONDITION_AC_POWER
] = condition_test_ac_power
,
441 [CONDITION_ARCHITECTURE
] = condition_test_architecture
,
442 [CONDITION_NEEDS_UPDATE
] = condition_test_needs_update
,
443 [CONDITION_FIRST_BOOT
] = condition_test_first_boot
,
444 [CONDITION_NULL
] = condition_test_null
,
450 assert(c
->type
>= 0);
451 assert(c
->type
< _CONDITION_TYPE_MAX
);
453 r
= condition_tests
[c
->type
](c
);
455 c
->result
= CONDITION_ERROR
;
459 b
= (r
> 0) == !c
->negate
;
460 c
->result
= b
? CONDITION_SUCCEEDED
: CONDITION_FAILED
;
464 void condition_dump(Condition
*c
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
472 "%s\t%s: %s%s%s %s\n",
475 c
->trigger
? "|" : "",
476 c
->negate
? "!" : "",
478 condition_result_to_string(c
->result
));
481 void condition_dump_list(Condition
*first
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
484 LIST_FOREACH(conditions
, c
, first
)
485 condition_dump(c
, f
, prefix
, to_string
);
488 static const char* const condition_type_table
[_CONDITION_TYPE_MAX
] = {
489 [CONDITION_ARCHITECTURE
] = "ConditionArchitecture",
490 [CONDITION_VIRTUALIZATION
] = "ConditionVirtualization",
491 [CONDITION_HOST
] = "ConditionHost",
492 [CONDITION_KERNEL_COMMAND_LINE
] = "ConditionKernelCommandLine",
493 [CONDITION_SECURITY
] = "ConditionSecurity",
494 [CONDITION_CAPABILITY
] = "ConditionCapability",
495 [CONDITION_AC_POWER
] = "ConditionACPower",
496 [CONDITION_NEEDS_UPDATE
] = "ConditionNeedsUpdate",
497 [CONDITION_FIRST_BOOT
] = "ConditionFirstBoot",
498 [CONDITION_PATH_EXISTS
] = "ConditionPathExists",
499 [CONDITION_PATH_EXISTS_GLOB
] = "ConditionPathExistsGlob",
500 [CONDITION_PATH_IS_DIRECTORY
] = "ConditionPathIsDirectory",
501 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "ConditionPathIsSymbolicLink",
502 [CONDITION_PATH_IS_MOUNT_POINT
] = "ConditionPathIsMountPoint",
503 [CONDITION_PATH_IS_READ_WRITE
] = "ConditionPathIsReadWrite",
504 [CONDITION_DIRECTORY_NOT_EMPTY
] = "ConditionDirectoryNotEmpty",
505 [CONDITION_FILE_NOT_EMPTY
] = "ConditionFileNotEmpty",
506 [CONDITION_FILE_IS_EXECUTABLE
] = "ConditionFileIsExecutable",
507 [CONDITION_NULL
] = "ConditionNull"
510 DEFINE_STRING_TABLE_LOOKUP(condition_type
, ConditionType
);
512 static const char* const assert_type_table
[_CONDITION_TYPE_MAX
] = {
513 [CONDITION_ARCHITECTURE
] = "AssertArchitecture",
514 [CONDITION_VIRTUALIZATION
] = "AssertVirtualization",
515 [CONDITION_HOST
] = "AssertHost",
516 [CONDITION_KERNEL_COMMAND_LINE
] = "AssertKernelCommandLine",
517 [CONDITION_SECURITY
] = "AssertSecurity",
518 [CONDITION_CAPABILITY
] = "AssertCapability",
519 [CONDITION_AC_POWER
] = "AssertACPower",
520 [CONDITION_NEEDS_UPDATE
] = "AssertNeedsUpdate",
521 [CONDITION_FIRST_BOOT
] = "AssertFirstBoot",
522 [CONDITION_PATH_EXISTS
] = "AssertPathExists",
523 [CONDITION_PATH_EXISTS_GLOB
] = "AssertPathExistsGlob",
524 [CONDITION_PATH_IS_DIRECTORY
] = "AssertPathIsDirectory",
525 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "AssertPathIsSymbolicLink",
526 [CONDITION_PATH_IS_MOUNT_POINT
] = "AssertPathIsMountPoint",
527 [CONDITION_PATH_IS_READ_WRITE
] = "AssertPathIsReadWrite",
528 [CONDITION_DIRECTORY_NOT_EMPTY
] = "AssertDirectoryNotEmpty",
529 [CONDITION_FILE_NOT_EMPTY
] = "AssertFileNotEmpty",
530 [CONDITION_FILE_IS_EXECUTABLE
] = "AssertFileIsExecutable",
531 [CONDITION_NULL
] = "AssertNull"
534 DEFINE_STRING_TABLE_LOOKUP(assert_type
, ConditionType
);
536 static const char* const condition_result_table
[_CONDITION_RESULT_MAX
] = {
537 [CONDITION_UNTESTED
] = "untested",
538 [CONDITION_SUCCEEDED
] = "succeeded",
539 [CONDITION_FAILED
] = "failed",
540 [CONDITION_ERROR
] = "error",
543 DEFINE_STRING_TABLE_LOOKUP(condition_result
, ConditionResult
);