mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-22 17:57:49 +03:00
analyze: add 'condition' verb
We didn't have a straightforward way to parse and evaluate those strings. Prompted by #12881.
This commit is contained in:
parent
b1d5246d29
commit
edfea9fe0d
@ -83,6 +83,12 @@
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">unit-paths</arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-analyze</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">condition</arg>
|
||||
<arg choice="plain"><replaceable>CONDITION</replaceable>…</arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-analyze</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
@ -348,6 +354,33 @@ $ eog targets.svg</programlisting>
|
||||
to retrieve the actual list that the manager uses, with any empty directories omitted.</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title><command>systemd-analyze condition <replaceable>CONDITION</replaceable>...</command></title>
|
||||
|
||||
<para>This command will evaluate <varname noindex='true'>Condition*=...</varname> and
|
||||
<varname noindex='true'>Assert*=...</varname> assignments, and print their values, and
|
||||
the resulting value of the combined condition set. See
|
||||
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for a list of available conditions and asserts.</para>
|
||||
|
||||
<example>
|
||||
<title>Evaluate conditions that check kernel versions</title>
|
||||
|
||||
<programlisting>$ systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \
|
||||
'ConditionKernelVersion = >=5.1' \
|
||||
'ConditionACPower=|false' \
|
||||
'ConditionArchitecture=|!arm' \
|
||||
'AssertPathExists=/etc/os-release'
|
||||
test.service: AssertPathExists=/etc/os-release succeeded.
|
||||
Asserts succeeded.
|
||||
test.service: ConditionArchitecture=|!arm succeeded.
|
||||
test.service: ConditionACPower=|false failed.
|
||||
test.service: ConditionKernelVersion=>=5.1 succeeded.
|
||||
test.service: ConditionKernelVersion=!<4.0 succeeded.
|
||||
Conditions succeeded.</programlisting>
|
||||
</example>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title><command>systemd-analyze syscall-filter <optional><replaceable>SET</replaceable>...</optional></command></title>
|
||||
|
||||
|
@ -1030,7 +1030,9 @@
|
||||
exclamation mark, the pipe symbol must be passed first, the exclamation second. Except for
|
||||
<varname>ConditionPathIsSymbolicLink=</varname>, all path checks follow symlinks. If any of these
|
||||
options is assigned the empty string, the list of conditions is reset completely, all previous
|
||||
condition settings (of any kind) will have no effect.</para>
|
||||
condition settings (of any kind) will have no effect. The <command>condition</command> verb of
|
||||
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
can be used to test condition and assert expressions.</para>
|
||||
|
||||
<para><varname>ConditionArchitecture=</varname> may be used to
|
||||
check whether the system is running on a specific
|
||||
@ -1326,7 +1328,11 @@
|
||||
<para>Note that neither assertion nor condition expressions result in unit state changes. Also note that both
|
||||
are checked at the time the job is to be executed, i.e. long after depending jobs and it itself were
|
||||
queued. Thus, neither condition nor assertion expressions are suitable for conditionalizing unit
|
||||
dependencies.</para></listitem>
|
||||
dependencies.</para>
|
||||
|
||||
<para>The <command>condition</command> verb of
|
||||
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
can be used to test condition and assert expressions.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
155
src/analyze/analyze-condition.c
Normal file
155
src/analyze/analyze-condition.c
Normal file
@ -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;
|
||||
}
|
6
src/analyze/analyze-condition.h
Normal file
6
src/analyze/analyze-condition.h
Normal file
@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include "install.h"
|
||||
|
||||
int verify_conditions(char **lines, UnitFileScope scope);
|
@ -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 },
|
||||
|
@ -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
|
||||
|
@ -254,6 +254,7 @@ Unit.SuccessAction, config_parse_emergency_action, 0,
|
||||
Unit.FailureActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, failure_action_exit_status)
|
||||
Unit.SuccessActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, success_action_exit_status)
|
||||
Unit.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
|
||||
m4_dnl Also add any conditions to condition_definitions[] in src/analyze/analyze-condition.c.
|
||||
Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions)
|
||||
Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions)
|
||||
Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions)
|
||||
|
Loading…
x
Reference in New Issue
Block a user