summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mkosi/mkosi.arch3
-rw-r--r--man/systemd-analyze.xml14
-rw-r--r--shell-completion/bash/systemd-analyze15
-rw-r--r--src/analyze/analyze-condition.c45
-rw-r--r--src/analyze/analyze-condition.h2
-rw-r--r--src/analyze/analyze.c32
6 files changed, 96 insertions, 15 deletions
diff --git a/.mkosi/mkosi.arch b/.mkosi/mkosi.arch
index cb8fb01ef2..f584d2d126 100644
--- a/.mkosi/mkosi.arch
+++ b/.mkosi/mkosi.arch
@@ -56,3 +56,6 @@ Packages=
man-db
# For testing systemd's bash completion scripts.
bash-completion
+ # For testing systemd's zsh completion scripts
+ # Run `autoload -Uz compinit; compinit` from a zsh shell in the booted image to enable completions.
+ zsh
diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml
index 932218d80e..fb2a1caa9e 100644
--- a/man/systemd-analyze.xml
+++ b/man/systemd-analyze.xml
@@ -1123,6 +1123,20 @@ Service b@0.service not loaded, b.socket cannot be started.
to the specified point in time. If not specified defaults to the current time.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--unit=<replaceable>UNIT</replaceable></option></term>
+
+ <listitem><para>When used with the <command>condition</command> command, evaluate all the
+ <varname index="false">Condition*=...</varname> and <varname index="false">Assert*=...</varname>
+ assignments in the specified unit file. The full unit search path is formed by combining the
+ directories for the specified unit with the usual unit load paths. The variable
+ <varname>$SYSTEMD_UNIT_PATH</varname> is supported, and may be used to replace or augment the
+ compiled in set of unit load paths; see
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. All
+ units files present in the directory containing the specified unit will be used in preference to the
+ other paths.</para></listitem>
+ </varlistentry>
+
<xi:include href="user-system-options.xml" xpointer="host" />
<xi:include href="user-system-options.xml" xpointer="machine" />
diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze
index 07d069a6d7..6bed5e73e8 100644
--- a/shell-completion/bash/systemd-analyze
+++ b/shell-completion/bash/systemd-analyze
@@ -55,13 +55,14 @@ _systemd_analyze() {
)
local -A VERBS=(
- [STANDALONE]='time blame plot dump unit-paths exit-status condition calendar timestamp timespan'
+ [STANDALONE]='time blame plot dump unit-paths exit-status calendar timestamp timespan'
[CRITICAL_CHAIN]='critical-chain'
[DOT]='dot'
[VERIFY]='verify'
[SECCOMP_FILTER]='syscall-filter'
[CAT_CONFIG]='cat-config'
[SECURITY]='security'
+ [CONDITION]='condition'
)
local CONFIGS='systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
@@ -153,6 +154,18 @@ _systemd_analyze() {
fi
comps=$( __get_services $mode )
fi
+
+ elif __contains_word "$verb" ${VERBS[CONDITION]}; then
+ if [[ $cur = -* ]]; then
+ comps='--help --version --system --user --global --no-pager --root --image'
+ elif [[ $prev = "-u" ]] || [[ $prev = "--unit" ]]; then
+ if __contains_word "--user" ${COMP_WORDS[*]}; then
+ mode=--user
+ else
+ mode=--system
+ fi
+ comps=$( __get_services $mode )
+ fi
fi
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
diff --git a/src/analyze/analyze-condition.c b/src/analyze/analyze-condition.c
index 09870b95ec..f57bb27b9a 100644
--- a/src/analyze/analyze-condition.c
+++ b/src/analyze/analyze-condition.c
@@ -3,6 +3,7 @@
#include <stdlib.h>
#include "analyze-condition.h"
+#include "analyze-verify.h"
#include "condition.h"
#include "conf-parser.h"
#include "load-fragment.h"
@@ -72,29 +73,57 @@ static int log_helper(void *userdata, int level, int error, const char *file, in
return r;
}
-int verify_conditions(char **lines, UnitFileScope scope) {
+int verify_conditions(char **lines, UnitFileScope scope, const char *unit, const char *root) {
_cleanup_(manager_freep) Manager *m = NULL;
Unit *u;
- char **line;
int r, q = 1;
+ if (unit) {
+ _cleanup_strv_free_ char **filenames = NULL;
+ _cleanup_free_ char *var = NULL;
+
+ filenames = strv_new(unit);
+ if (!filenames)
+ return log_oom();
+
+ r = verify_generate_path(&var, filenames);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit load path: %m");
+
+ assert_se(set_unit_path(var) >= 0);
+ }
+
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, /* serialization= */ NULL, /* fds= */ NULL, /* root= */ NULL);
+ r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root);
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");
+ if (unit) {
+ _cleanup_free_ char *prepared = NULL;
- STRV_FOREACH(line, lines) {
- r = parse_condition(u, *line);
+ r = verify_prepare_filename(unit, &prepared);
+ if (r < 0)
+ return log_error_errno(r, "Failed to prepare filename %s: %m", unit);
+
+ r = manager_load_startable_unit_or_warn(m, NULL, prepared, &u);
if (r < 0)
return r;
+ } else {
+ char **line;
+
+ 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, environ, assert_type_to_string, log_helper, u);
diff --git a/src/analyze/analyze-condition.h b/src/analyze/analyze-condition.h
index 7b52669d05..9ebd205b6d 100644
--- a/src/analyze/analyze-condition.h
+++ b/src/analyze/analyze-condition.h
@@ -3,4 +3,4 @@
#include "install.h"
-int verify_conditions(char **lines, UnitFileScope scope);
+int verify_conditions(char **lines, UnitFileScope scope, const char *unit, const char *root);
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 816532f69e..68b9941afe 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -96,12 +96,14 @@ static bool arg_offline = false;
static unsigned arg_threshold = 100;
static unsigned arg_iterations = 1;
static usec_t arg_base_time = USEC_INFINITY;
+static char *arg_unit = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_unit, freep);
typedef struct BootTimes {
usec_t firmware_time;
@@ -2147,7 +2149,7 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) {
}
static int do_condition(int argc, char *argv[], void *userdata) {
- return verify_conditions(strv_skip(argv, 1), arg_scope);
+ return verify_conditions(strv_skip(argv, 1), arg_scope, arg_unit, arg_root);
}
static int do_verify(int argc, char *argv[], void *userdata) {
@@ -2327,6 +2329,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "machine", required_argument, NULL, 'M' },
{ "iterations", required_argument, NULL, ARG_ITERATIONS },
{ "base-time", required_argument, NULL, ARG_BASE_TIME },
+ { "unit", required_argument, NULL, 'U' },
{}
};
@@ -2335,7 +2338,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hH:M:U:", options, NULL)) >= 0)
switch (c) {
case 'h':
@@ -2465,6 +2468,16 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case 'U': {
+ _cleanup_free_ char *mangled = NULL;
+
+ r = unit_name_mangle(optarg, UNIT_NAME_MANGLE_WARN, &mangled);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle unit name %s: %m", optarg);
+
+ free_and_replace(arg_unit, mangled);
+ break;
+ }
case '?':
return -EINVAL;
@@ -2493,15 +2506,24 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --security-policy= is only supported for security.");
- if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify")) &&
+ if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify", "condition")) &&
(!(streq_ptr(argv[optind], "security") && arg_offline)))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Options --root= and --image= are only supported for cat-config, verify and security when used with --offline= right now.");
+ "Options --root= and --image= are only supported for cat-config, verify, condition and security when used with --offline= right now.");
/* Having both an image and a root is not supported by the code */
if (arg_root && arg_image)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
+ if (arg_unit && !streq_ptr(argv[optind], "condition"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --unit= is only supported for condition");
+
+ if (streq_ptr(argv[optind], "condition") && !arg_unit && optind >= argc - 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too few arguments for condition");
+
+ if (streq_ptr(argv[optind], "condition") && arg_unit && optind < argc - 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No conditions can be passed if --unit= is used.");
+
return 1; /* work to do */
}
@@ -2532,7 +2554,7 @@ static int run(int argc, char *argv[]) {
{ "exit-status", VERB_ANY, VERB_ANY, 0, dump_exit_status },
{ "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
{ "capability", VERB_ANY, VERB_ANY, 0, dump_capabilities },
- { "condition", 2, VERB_ANY, 0, do_condition },
+ { "condition", VERB_ANY, 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 },