summaryrefslogtreecommitdiff
path: root/src/analyze
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-06-26 14:58:45 +0200
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-06-27 10:54:37 +0200
commitedfea9fe0db025d8b90f07d969b48a1017399265 (patch)
tree4cf0ac0cc9085fbe600cf0a15f733f809b8fdb4f /src/analyze
parentb1d5246d2953415cb6ef7c229cf93e3151740725 (diff)
downloadsystemd-edfea9fe0db025d8b90f07d969b48a1017399265.tar.gz
systemd-edfea9fe0db025d8b90f07d969b48a1017399265.tar.bz2
systemd-edfea9fe0db025d8b90f07d969b48a1017399265.zip
analyze: add 'condition' verb
We didn't have a straightforward way to parse and evaluate those strings. Prompted by #12881.
Diffstat (limited to 'src/analyze')
-rw-r--r--src/analyze/analyze-condition.c155
-rw-r--r--src/analyze/analyze-condition.h6
-rw-r--r--src/analyze/analyze.c7
-rw-r--r--src/analyze/meson.build2
4 files changed, 170 insertions, 0 deletions
diff --git a/src/analyze/analyze-condition.c b/src/analyze/analyze-condition.c
new file mode 100644
index 0000000000..d0cefa0992
--- /dev/null
+++ b/src/analyze/analyze-condition.c
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <stdlib.h>
+
+#include "analyze-condition.h"
+#include "condition.h"
+#include "conf-parser.h"
+#include "load-fragment.h"
+#include "service.h"
+
+typedef struct condition_definition {
+ const char *name;
+ ConfigParserCallback parser;
+ ConditionType type;
+} condition_definition;
+
+static const condition_definition condition_definitions[] = {
+ { "ConditionPathExists", config_parse_unit_condition_path, CONDITION_PATH_EXISTS },
+ { "ConditionPathExistsGlob", config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB },
+ { "ConditionPathIsDirectory", config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY },
+ { "ConditionPathIsSymbolicLink", config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK },
+ { "ConditionPathIsMountPoint", config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT },
+ { "ConditionPathIsReadWrite", config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE },
+ { "ConditionDirectoryNotEmpty", config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY },
+ { "ConditionFileNotEmpty", config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY },
+ { "ConditionFileIsExecutable", config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE },
+ { "ConditionNeedsUpdate", config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE },
+ { "ConditionFirstBoot", config_parse_unit_condition_string, CONDITION_FIRST_BOOT },
+ { "ConditionKernelCommandLine", config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE },
+ { "ConditionKernelVersion", config_parse_unit_condition_string, CONDITION_KERNEL_VERSION },
+ { "ConditionArchitecture", config_parse_unit_condition_string, CONDITION_ARCHITECTURE },
+ { "ConditionVirtualization", config_parse_unit_condition_string, CONDITION_VIRTUALIZATION },
+ { "ConditionSecurity", config_parse_unit_condition_string, CONDITION_SECURITY },
+ { "ConditionCapability", config_parse_unit_condition_string, CONDITION_CAPABILITY },
+ { "ConditionHost", config_parse_unit_condition_string, CONDITION_HOST },
+ { "ConditionACPower", config_parse_unit_condition_string, CONDITION_AC_POWER },
+ { "ConditionUser", config_parse_unit_condition_string, CONDITION_USER },
+ { "ConditionGroup", config_parse_unit_condition_string, CONDITION_GROUP },
+ { "ConditionControlGroupController", config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER },
+
+ { "AssertPathExists", config_parse_unit_condition_path, CONDITION_PATH_EXISTS },
+ { "AssertPathExistsGlob", config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB },
+ { "AssertPathIsDirectory", config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY },
+ { "AssertPathIsSymbolicLink", config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK },
+ { "AssertPathIsMountPoint", config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT },
+ { "AssertPathIsReadWrite", config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE },
+ { "AssertDirectoryNotEmpty", config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY },
+ { "AssertFileNotEmpty", config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY },
+ { "AssertFileIsExecutable", config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE },
+ { "AssertNeedsUpdate", config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE },
+ { "AssertFirstBoot", config_parse_unit_condition_string, CONDITION_FIRST_BOOT },
+ { "AssertKernelCommandLine", config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE },
+ { "AssertKernelVersion", config_parse_unit_condition_string, CONDITION_KERNEL_VERSION },
+ { "AssertArchitecture", config_parse_unit_condition_string, CONDITION_ARCHITECTURE },
+ { "AssertVirtualization", config_parse_unit_condition_string, CONDITION_VIRTUALIZATION },
+ { "AssertSecurity", config_parse_unit_condition_string, CONDITION_SECURITY },
+ { "AssertCapability", config_parse_unit_condition_string, CONDITION_CAPABILITY },
+ { "AssertHost", config_parse_unit_condition_string, CONDITION_HOST },
+ { "AssertACPower", config_parse_unit_condition_string, CONDITION_AC_POWER },
+ { "AssertUser", config_parse_unit_condition_string, CONDITION_USER },
+ { "AssertGroup", config_parse_unit_condition_string, CONDITION_GROUP },
+ { "AssertControlGroupController", config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER },
+
+ /* deprecated, but we should still parse them */
+ { "ConditionNull", config_parse_unit_condition_null, 0 },
+ { "AssertNull", config_parse_unit_condition_null, 0 },
+};
+
+static int parse_condition(Unit *u, const char *line) {
+ const char *p;
+ Condition **target;
+
+ if ((p = startswith(line, "Condition")))
+ target = &u->conditions;
+ else if ((p = startswith(line, "Assert")))
+ target = &u->asserts;
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot parse \"%s\".", line);
+
+ for (size_t i = 0; i < ELEMENTSOF(condition_definitions); i++) {
+ const condition_definition *c = &condition_definitions[i];
+
+ p = startswith(line, c->name);
+ if (!p)
+ continue;
+ p += strspn(p, WHITESPACE);
+ if (*p != '=')
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected \"=\" in \"%s\".", line);
+
+ p += 1 + strspn(p + 1, WHITESPACE);
+
+ return c->parser(NULL, "(stdin)", 0, NULL, 0, c->name, c->type, p, target, u);
+ }
+
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot parse \"%s\".", line);
+}
+
+_printf_(7, 8)
+static int log_helper(void *userdata, int level, int error, const char *file, int line, const char *func, const char *format, ...) {
+ Unit *u = userdata;
+ va_list ap;
+ int r;
+
+ assert(u);
+
+ /* "upgrade" debug messages */
+ level = MIN(LOG_INFO, level);
+
+ va_start(ap, format);
+ r = log_object_internalv(level, error, file, line, func,
+ NULL,
+ u->id,
+ NULL,
+ NULL,
+ format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int verify_conditions(char **lines, UnitFileScope scope) {
+ _cleanup_(manager_freep) Manager *m = NULL;
+ Unit *u;
+ char **line;
+ int r, q = 1;
+
+ r = manager_new(scope, MANAGER_TEST_RUN_MINIMAL, &m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize manager: %m");
+
+ log_debug("Starting manager...");
+ r = manager_startup(m, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ r = unit_new_for_name(m, sizeof(Service), "test.service", &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create test.service: %m");
+
+ STRV_FOREACH(line, lines) {
+ r = parse_condition(u, *line);
+ if (r < 0)
+ return r;
+ }
+
+ r = condition_test_list(u->asserts, assert_type_to_string, log_helper, u);
+ if (u->asserts)
+ log_notice("Asserts %s.", r > 0 ? "succeeded" : "failed");
+
+ q = condition_test_list(u->conditions, condition_type_to_string, log_helper, u);
+ if (u->conditions)
+ log_notice("Conditions %s.", q > 0 ? "succeeded" : "failed");
+
+ return r > 0 && q > 0 ? 0 : -EIO;
+}
diff --git a/src/analyze/analyze-condition.h b/src/analyze/analyze-condition.h
new file mode 100644
index 0000000000..2ef278eb5c
--- /dev/null
+++ b/src/analyze/analyze-condition.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "install.h"
+
+int verify_conditions(char **lines, UnitFileScope scope);
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 5217a92b43..40f54f9d46 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -13,6 +13,7 @@
#include "sd-bus.h"
#include "alloc-util.h"
+#include "analyze-condition.h"
#include "analyze-security.h"
#include "analyze-verify.h"
#include "build.h"
@@ -1897,6 +1898,10 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) {
return 0;
}
+static int do_condition(int argc, char *argv[], void *userdata) {
+ return verify_conditions(strv_skip(argv, 1), arg_scope);
+}
+
static int do_verify(int argc, char *argv[], void *userdata) {
return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
}
@@ -1955,6 +1960,7 @@ static int help(int argc, char *argv[], void *userdata) {
" cat-config Show configuration file and drop-ins\n"
" unit-paths List load directories for units\n"
" syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
+ " condition CONDITION... Evaluate conditions and asserts\n"
" verify FILE... Check unit files for correctness\n"
" service-watchdogs [BOOL] Get/set service watchdog state\n"
" calendar SPEC... Validate repetitive calendar time events\n"
@@ -2157,6 +2163,7 @@ static int run(int argc, char *argv[]) {
{ "cat-config", 2, VERB_ANY, 0, cat_config },
{ "unit-paths", 1, 1, 0, dump_unit_paths },
{ "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
+ { "condition", 2, VERB_ANY, 0, do_condition },
{ "verify", 2, VERB_ANY, 0, do_verify },
{ "calendar", 2, VERB_ANY, 0, test_calendar },
{ "timestamp", 2, VERB_ANY, 0, test_timestamp },
diff --git a/src/analyze/meson.build b/src/analyze/meson.build
index 4db4dfa552..58760d609b 100644
--- a/src/analyze/meson.build
+++ b/src/analyze/meson.build
@@ -2,6 +2,8 @@
systemd_analyze_sources = files('''
analyze.c
+ analyze-condition.c
+ analyze-condition.h
analyze-verify.c
analyze-verify.h
analyze-security.c