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 "mount-util.h"
40 #include "parse-util.h"
41 #include "path-util.h"
42 #include "selinux-util.h"
43 #include "smack-util.h"
44 #include "stat-util.h"
45 #include "string-table.h"
46 #include "string-util.h"
50 Condition
* condition_new(ConditionType type
, const char *parameter
, bool trigger
, bool negate
) {
55 assert(type
< _CONDITION_TYPE_MAX
);
56 assert((!parameter
) == (type
== CONDITION_NULL
));
58 c
= new0(Condition
, 1);
66 r
= free_and_strdup(&c
->parameter
, parameter
);
75 void condition_free(Condition
*c
) {
82 Condition
* condition_free_list(Condition
*first
) {
85 LIST_FOREACH_SAFE(conditions
, c
, n
, first
)
91 static int condition_test_kernel_command_line(Condition
*c
) {
92 _cleanup_free_
char *line
= NULL
;
99 assert(c
->type
== CONDITION_KERNEL_COMMAND_LINE
);
101 r
= proc_cmdline(&line
);
105 equal
= !!strchr(c
->parameter
, '=');
109 _cleanup_free_
char *word
= NULL
;
112 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_RELAX
);
119 found
= streq(word
, c
->parameter
);
123 f
= startswith(word
, c
->parameter
);
124 found
= f
&& (*f
== '=' || *f
== 0);
134 static int condition_test_virtualization(Condition
*c
) {
138 assert(c
->parameter
);
139 assert(c
->type
== CONDITION_VIRTUALIZATION
);
141 v
= detect_virtualization();
145 /* First, compare with yes/no */
146 b
= parse_boolean(c
->parameter
);
151 if (v
== 0 && b
== 0)
154 /* Then, compare categorization */
155 if (VIRTUALIZATION_IS_VM(v
) && streq(c
->parameter
, "vm"))
158 if (VIRTUALIZATION_IS_CONTAINER(v
) && streq(c
->parameter
, "container"))
161 /* Finally compare id */
162 return v
!= VIRTUALIZATION_NONE
&& streq(c
->parameter
, virtualization_to_string(v
));
165 static int condition_test_architecture(Condition
*c
) {
169 assert(c
->parameter
);
170 assert(c
->type
== CONDITION_ARCHITECTURE
);
172 a
= uname_architecture();
176 if (streq(c
->parameter
, "native"))
177 b
= native_architecture();
179 b
= architecture_from_string(c
->parameter
);
186 static int condition_test_host(Condition
*c
) {
187 _cleanup_free_
char *h
= NULL
;
192 assert(c
->parameter
);
193 assert(c
->type
== CONDITION_HOST
);
195 if (sd_id128_from_string(c
->parameter
, &x
) >= 0) {
197 r
= sd_id128_get_machine(&y
);
201 return sd_id128_equal(x
, y
);
204 h
= gethostname_malloc();
208 return fnmatch(c
->parameter
, h
, FNM_CASEFOLD
) == 0;
211 static int condition_test_ac_power(Condition
*c
) {
215 assert(c
->parameter
);
216 assert(c
->type
== CONDITION_AC_POWER
);
218 r
= parse_boolean(c
->parameter
);
222 return (on_ac_power() != 0) == !!r
;
225 static int condition_test_security(Condition
*c
) {
227 assert(c
->parameter
);
228 assert(c
->type
== CONDITION_SECURITY
);
230 if (streq(c
->parameter
, "selinux"))
231 return mac_selinux_use();
232 if (streq(c
->parameter
, "smack"))
233 return mac_smack_use();
234 if (streq(c
->parameter
, "apparmor"))
235 return mac_apparmor_use();
236 if (streq(c
->parameter
, "audit"))
238 if (streq(c
->parameter
, "ima"))
244 static int condition_test_capability(Condition
*c
) {
245 _cleanup_fclose_
FILE *f
= NULL
;
248 unsigned long long capabilities
= -1;
251 assert(c
->parameter
);
252 assert(c
->type
== CONDITION_CAPABILITY
);
254 /* If it's an invalid capability, we don't have it */
255 value
= capability_from_name(c
->parameter
);
259 /* If it's a valid capability we default to assume
262 f
= fopen("/proc/self/status", "re");
266 while (fgets(line
, sizeof(line
), f
)) {
269 if (startswith(line
, "CapBnd:")) {
270 (void) sscanf(line
+7, "%llx", &capabilities
);
275 return !!(capabilities
& (1ULL << value
));
278 static int condition_test_needs_update(Condition
*c
) {
280 struct stat usr
, other
;
283 assert(c
->parameter
);
284 assert(c
->type
== CONDITION_NEEDS_UPDATE
);
286 /* If the file system is read-only we shouldn't suggest an update */
287 if (path_is_read_only_fs(c
->parameter
) > 0)
290 /* Any other failure means we should allow the condition to be true,
291 * so that we rather invoke too many update tools then too
294 if (!path_is_absolute(c
->parameter
))
297 p
= strjoina(c
->parameter
, "/.updated");
298 if (lstat(p
, &other
) < 0)
301 if (lstat("/usr/", &usr
) < 0)
304 return usr
.st_mtim
.tv_sec
> other
.st_mtim
.tv_sec
||
305 (usr
.st_mtim
.tv_sec
== other
.st_mtim
.tv_sec
&& usr
.st_mtim
.tv_nsec
> other
.st_mtim
.tv_nsec
);
308 static int condition_test_first_boot(Condition
*c
) {
312 assert(c
->parameter
);
313 assert(c
->type
== CONDITION_FIRST_BOOT
);
315 r
= parse_boolean(c
->parameter
);
319 return (access("/run/systemd/first-boot", F_OK
) >= 0) == !!r
;
322 static int condition_test_path_exists(Condition
*c
) {
324 assert(c
->parameter
);
325 assert(c
->type
== CONDITION_PATH_EXISTS
);
327 return access(c
->parameter
, F_OK
) >= 0;
330 static int condition_test_path_exists_glob(Condition
*c
) {
332 assert(c
->parameter
);
333 assert(c
->type
== CONDITION_PATH_EXISTS_GLOB
);
335 return glob_exists(c
->parameter
) > 0;
338 static int condition_test_path_is_directory(Condition
*c
) {
340 assert(c
->parameter
);
341 assert(c
->type
== CONDITION_PATH_IS_DIRECTORY
);
343 return is_dir(c
->parameter
, true) > 0;
346 static int condition_test_path_is_symbolic_link(Condition
*c
) {
348 assert(c
->parameter
);
349 assert(c
->type
== CONDITION_PATH_IS_SYMBOLIC_LINK
);
351 return is_symlink(c
->parameter
) > 0;
354 static int condition_test_path_is_mount_point(Condition
*c
) {
356 assert(c
->parameter
);
357 assert(c
->type
== CONDITION_PATH_IS_MOUNT_POINT
);
359 return path_is_mount_point(c
->parameter
, AT_SYMLINK_FOLLOW
) > 0;
362 static int condition_test_path_is_read_write(Condition
*c
) {
364 assert(c
->parameter
);
365 assert(c
->type
== CONDITION_PATH_IS_READ_WRITE
);
367 return path_is_read_only_fs(c
->parameter
) <= 0;
370 static int condition_test_directory_not_empty(Condition
*c
) {
374 assert(c
->parameter
);
375 assert(c
->type
== CONDITION_DIRECTORY_NOT_EMPTY
);
377 r
= dir_is_empty(c
->parameter
);
378 return r
<= 0 && r
!= -ENOENT
;
381 static int condition_test_file_not_empty(Condition
*c
) {
385 assert(c
->parameter
);
386 assert(c
->type
== CONDITION_FILE_NOT_EMPTY
);
388 return (stat(c
->parameter
, &st
) >= 0 &&
389 S_ISREG(st
.st_mode
) &&
393 static int condition_test_file_is_executable(Condition
*c
) {
397 assert(c
->parameter
);
398 assert(c
->type
== CONDITION_FILE_IS_EXECUTABLE
);
400 return (stat(c
->parameter
, &st
) >= 0 &&
401 S_ISREG(st
.st_mode
) &&
402 (st
.st_mode
& 0111));
405 static int condition_test_null(Condition
*c
) {
407 assert(c
->type
== CONDITION_NULL
);
409 /* Note that during parsing we already evaluate the string and
410 * store it in c->negate */
414 int condition_test(Condition
*c
) {
416 static int (*const condition_tests
[_CONDITION_TYPE_MAX
])(Condition
*c
) = {
417 [CONDITION_PATH_EXISTS
] = condition_test_path_exists
,
418 [CONDITION_PATH_EXISTS_GLOB
] = condition_test_path_exists_glob
,
419 [CONDITION_PATH_IS_DIRECTORY
] = condition_test_path_is_directory
,
420 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = condition_test_path_is_symbolic_link
,
421 [CONDITION_PATH_IS_MOUNT_POINT
] = condition_test_path_is_mount_point
,
422 [CONDITION_PATH_IS_READ_WRITE
] = condition_test_path_is_read_write
,
423 [CONDITION_DIRECTORY_NOT_EMPTY
] = condition_test_directory_not_empty
,
424 [CONDITION_FILE_NOT_EMPTY
] = condition_test_file_not_empty
,
425 [CONDITION_FILE_IS_EXECUTABLE
] = condition_test_file_is_executable
,
426 [CONDITION_KERNEL_COMMAND_LINE
] = condition_test_kernel_command_line
,
427 [CONDITION_VIRTUALIZATION
] = condition_test_virtualization
,
428 [CONDITION_SECURITY
] = condition_test_security
,
429 [CONDITION_CAPABILITY
] = condition_test_capability
,
430 [CONDITION_HOST
] = condition_test_host
,
431 [CONDITION_AC_POWER
] = condition_test_ac_power
,
432 [CONDITION_ARCHITECTURE
] = condition_test_architecture
,
433 [CONDITION_NEEDS_UPDATE
] = condition_test_needs_update
,
434 [CONDITION_FIRST_BOOT
] = condition_test_first_boot
,
435 [CONDITION_NULL
] = condition_test_null
,
441 assert(c
->type
>= 0);
442 assert(c
->type
< _CONDITION_TYPE_MAX
);
444 r
= condition_tests
[c
->type
](c
);
446 c
->result
= CONDITION_ERROR
;
450 b
= (r
> 0) == !c
->negate
;
451 c
->result
= b
? CONDITION_SUCCEEDED
: CONDITION_FAILED
;
455 void condition_dump(Condition
*c
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
463 "%s\t%s: %s%s%s %s\n",
466 c
->trigger
? "|" : "",
467 c
->negate
? "!" : "",
469 condition_result_to_string(c
->result
));
472 void condition_dump_list(Condition
*first
, FILE *f
, const char *prefix
, const char *(*to_string
)(ConditionType t
)) {
475 LIST_FOREACH(conditions
, c
, first
)
476 condition_dump(c
, f
, prefix
, to_string
);
479 static const char* const condition_type_table
[_CONDITION_TYPE_MAX
] = {
480 [CONDITION_ARCHITECTURE
] = "ConditionArchitecture",
481 [CONDITION_VIRTUALIZATION
] = "ConditionVirtualization",
482 [CONDITION_HOST
] = "ConditionHost",
483 [CONDITION_KERNEL_COMMAND_LINE
] = "ConditionKernelCommandLine",
484 [CONDITION_SECURITY
] = "ConditionSecurity",
485 [CONDITION_CAPABILITY
] = "ConditionCapability",
486 [CONDITION_AC_POWER
] = "ConditionACPower",
487 [CONDITION_NEEDS_UPDATE
] = "ConditionNeedsUpdate",
488 [CONDITION_FIRST_BOOT
] = "ConditionFirstBoot",
489 [CONDITION_PATH_EXISTS
] = "ConditionPathExists",
490 [CONDITION_PATH_EXISTS_GLOB
] = "ConditionPathExistsGlob",
491 [CONDITION_PATH_IS_DIRECTORY
] = "ConditionPathIsDirectory",
492 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "ConditionPathIsSymbolicLink",
493 [CONDITION_PATH_IS_MOUNT_POINT
] = "ConditionPathIsMountPoint",
494 [CONDITION_PATH_IS_READ_WRITE
] = "ConditionPathIsReadWrite",
495 [CONDITION_DIRECTORY_NOT_EMPTY
] = "ConditionDirectoryNotEmpty",
496 [CONDITION_FILE_NOT_EMPTY
] = "ConditionFileNotEmpty",
497 [CONDITION_FILE_IS_EXECUTABLE
] = "ConditionFileIsExecutable",
498 [CONDITION_NULL
] = "ConditionNull"
501 DEFINE_STRING_TABLE_LOOKUP(condition_type
, ConditionType
);
503 static const char* const assert_type_table
[_CONDITION_TYPE_MAX
] = {
504 [CONDITION_ARCHITECTURE
] = "AssertArchitecture",
505 [CONDITION_VIRTUALIZATION
] = "AssertVirtualization",
506 [CONDITION_HOST
] = "AssertHost",
507 [CONDITION_KERNEL_COMMAND_LINE
] = "AssertKernelCommandLine",
508 [CONDITION_SECURITY
] = "AssertSecurity",
509 [CONDITION_CAPABILITY
] = "AssertCapability",
510 [CONDITION_AC_POWER
] = "AssertACPower",
511 [CONDITION_NEEDS_UPDATE
] = "AssertNeedsUpdate",
512 [CONDITION_FIRST_BOOT
] = "AssertFirstBoot",
513 [CONDITION_PATH_EXISTS
] = "AssertPathExists",
514 [CONDITION_PATH_EXISTS_GLOB
] = "AssertPathExistsGlob",
515 [CONDITION_PATH_IS_DIRECTORY
] = "AssertPathIsDirectory",
516 [CONDITION_PATH_IS_SYMBOLIC_LINK
] = "AssertPathIsSymbolicLink",
517 [CONDITION_PATH_IS_MOUNT_POINT
] = "AssertPathIsMountPoint",
518 [CONDITION_PATH_IS_READ_WRITE
] = "AssertPathIsReadWrite",
519 [CONDITION_DIRECTORY_NOT_EMPTY
] = "AssertDirectoryNotEmpty",
520 [CONDITION_FILE_NOT_EMPTY
] = "AssertFileNotEmpty",
521 [CONDITION_FILE_IS_EXECUTABLE
] = "AssertFileIsExecutable",
522 [CONDITION_NULL
] = "AssertNull"
525 DEFINE_STRING_TABLE_LOOKUP(assert_type
, ConditionType
);
527 static const char* const condition_result_table
[_CONDITION_RESULT_MAX
] = {
528 [CONDITION_UNTESTED
] = "untested",
529 [CONDITION_SUCCEEDED
] = "succeeded",
530 [CONDITION_FAILED
] = "failed",
531 [CONDITION_ERROR
] = "error",
534 DEFINE_STRING_TABLE_LOOKUP(condition_result
, ConditionResult
);