summaryrefslogtreecommitdiffstats
path: root/src/analyze
diff options
context:
space:
mode:
Diffstat (limited to 'src/analyze')
-rw-r--r--src/analyze/analyze-architectures.c88
-rw-r--r--src/analyze/analyze-architectures.h4
-rw-r--r--src/analyze/analyze-capability.c6
-rw-r--r--src/analyze/analyze-cat-config.c5
-rw-r--r--src/analyze/analyze-critical-chain.c13
-rw-r--r--src/analyze/analyze-dot.c31
-rw-r--r--src/analyze/analyze-exit-status.c6
-rw-r--r--src/analyze/analyze-fdstore.c4
-rw-r--r--src/analyze/analyze-image-policy.c2
-rw-r--r--src/analyze/analyze-pcrs.c2
-rw-r--r--src/analyze/analyze-plot.c20
-rw-r--r--src/analyze/analyze-security.c115
-rw-r--r--src/analyze/analyze-srk.c2
-rw-r--r--src/analyze/analyze-time-data.c56
-rw-r--r--src/analyze/analyze-time-data.h3
-rw-r--r--src/analyze/analyze-unit-files.c2
-rw-r--r--src/analyze/analyze-unit-paths.c2
-rw-r--r--src/analyze/analyze-verify-util.c18
-rw-r--r--src/analyze/analyze.c26
-rw-r--r--src/analyze/meson.build1
20 files changed, 280 insertions, 126 deletions
diff --git a/src/analyze/analyze-architectures.c b/src/analyze/analyze-architectures.c
new file mode 100644
index 0000000..2d155d5
--- /dev/null
+++ b/src/analyze/analyze-architectures.c
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "analyze.h"
+#include "analyze-architectures.h"
+#include "format-table.h"
+
+static int add_arch(Table *t, Architecture a) {
+ const char *c, *color;
+ int r;
+
+ assert(t);
+
+ if (a == native_architecture()) {
+ c = "native";
+ color = ANSI_HIGHLIGHT_GREEN;
+ } else if (a == uname_architecture()) {
+ c = "uname";
+ color = ANSI_HIGHLIGHT;
+#ifdef ARCHITECTURE_SECONDARY
+ } else if (a == ARCHITECTURE_SECONDARY) {
+ c = "secondary";
+ color = NULL;
+#endif
+ } else {
+ c = "foreign";
+ color = ANSI_GREY;
+ }
+
+ r = table_add_many(t,
+ TABLE_INT, (int) a,
+ TABLE_STRING, architecture_to_string(a),
+ TABLE_STRING, c,
+ TABLE_SET_COLOR, color);
+ if (r < 0)
+ return table_log_add_error(r);
+
+ return 0;
+}
+
+int verb_architectures(int argc, char *argv[], void *userdata) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ int r;
+
+ table = table_new("id", "name", "support");
+ if (!table)
+ return log_oom();
+
+ (void) table_hide_column_from_display(table, (size_t) 0);
+
+ if (strv_isempty(strv_skip(argv, 1)))
+ for (Architecture a = 0; a < _ARCHITECTURE_MAX; a++) {
+ r = add_arch(table, a);
+ if (r < 0)
+ return r;
+ }
+ else {
+ STRV_FOREACH(as, strv_skip(argv, 1)) {
+ Architecture a;
+
+ if (streq(*as, "native"))
+ a = native_architecture();
+ else if (streq(*as, "uname"))
+ a = uname_architecture();
+ else if (streq(*as, "secondary")) {
+#ifdef ARCHITECTURE_SECONDARY
+ a = ARCHITECTURE_SECONDARY;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No secondary architecture.");
+#endif
+ } else
+ a = architecture_from_string(*as);
+ if (a < 0)
+ return log_error_errno(a, "Architecture \"%s\" not known.", *as);
+
+ r = add_arch(table, a);
+ if (r < 0)
+ return r;
+ }
+
+ (void) table_set_sort(table, (size_t) 0);
+ }
+
+ r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
+ if (r < 0)
+ return log_error_errno(r, "Failed to output table: %m");
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/analyze/analyze-architectures.h b/src/analyze/analyze-architectures.h
new file mode 100644
index 0000000..06b9473
--- /dev/null
+++ b/src/analyze/analyze-architectures.h
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_architectures(int argc, char *argv[], void *userdata);
diff --git a/src/analyze/analyze-capability.c b/src/analyze/analyze-capability.c
index 8072175..7cdc0e3 100644
--- a/src/analyze/analyze-capability.c
+++ b/src/analyze/analyze-capability.c
@@ -46,11 +46,9 @@ int verb_capabilities(int argc, char *argv[], void *userdata) {
(void) table_set_sort(table, (size_t) 1);
}
- pager_open(arg_pager_flags);
-
- r = table_print(table, NULL);
+ r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to output table: %m");
return EXIT_SUCCESS;
}
diff --git a/src/analyze/analyze-cat-config.c b/src/analyze/analyze-cat-config.c
index 66bbbc1..b480d4a 100644
--- a/src/analyze/analyze-cat-config.c
+++ b/src/analyze/analyze-cat-config.c
@@ -4,7 +4,6 @@
#include "analyze-cat-config.h"
#include "conf-files.h"
#include "constants.h"
-#include "nulstr-util.h"
#include "path-util.h"
#include "pretty-print.h"
#include "strv.h"
@@ -23,7 +22,7 @@ int verb_cat_config(int argc, char *argv[], void *userdata) {
print_separator();
if (path_is_absolute(*arg)) {
- NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
+ FOREACH_STRING(dir, CONF_PATHS("")) {
t = path_startswith(*arg, dir);
if (t)
break;
@@ -35,7 +34,7 @@ int verb_cat_config(int argc, char *argv[], void *userdata) {
} else
t = *arg;
- r = conf_files_cat(arg_root, t, arg_cat_flags);
+ r = conf_files_cat(arg_root, t, arg_cat_flags | CAT_FORMAT_HAS_SECTIONS);
if (r < 0)
return r;
}
diff --git a/src/analyze/analyze-critical-chain.c b/src/analyze/analyze-critical-chain.c
index 4a7f452..7d78de3 100644
--- a/src/analyze/analyze-critical-chain.c
+++ b/src/analyze/analyze-critical-chain.c
@@ -23,23 +23,20 @@ static int list_dependencies_print(
UnitTimes *times,
BootTimes *boot) {
- for (unsigned i = level; i != 0; i--)
+ for (unsigned i = level; i > 0; i--)
printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH));
- if (times) {
+ if (times && times->activating >= boot->userspace_time) {
if (timestamp_is_set(times->time))
- printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
+ printf("%s%s @%s +%s%s\n", ansi_highlight_red(), name,
FORMAT_TIMESPAN(times->activating - boot->userspace_time, USEC_PER_MSEC),
FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal());
- else if (times->activated > boot->userspace_time)
- printf("%s @%s", name, FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC));
else
- printf("%s", name);
+ printf("%s @%s\n", name, FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC));
} else
- printf("%s", name);
- printf("\n");
+ printf("%s\n", name);
return 0;
}
diff --git a/src/analyze/analyze-dot.c b/src/analyze/analyze-dot.c
index bf8aa81..9e92d59 100644
--- a/src/analyze/analyze-dot.c
+++ b/src/analyze/analyze-dot.c
@@ -13,14 +13,15 @@ static int graph_one_property(
const UnitInfo *u,
const char *prop,
const char *color,
- char *patterns[],
- char *from_patterns[],
- char *to_patterns[]) {
+ char **patterns,
+ char **from_patterns,
+ char **to_patterns) {
_cleanup_strv_free_ char **units = NULL;
bool match_patterns;
int r;
+ assert(bus);
assert(u);
assert(prop);
assert(color);
@@ -51,7 +52,13 @@ static int graph_one_property(
return 0;
}
-static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
+static int graph_one(
+ sd_bus *bus,
+ const UnitInfo *u,
+ char **patterns,
+ char **from_patterns,
+ char **to_patterns) {
+
int r;
assert(bus);
@@ -67,12 +74,19 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *fro
r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
if (r < 0)
return r;
+
r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
if (r < 0)
return r;
+
+ r = graph_one_property(bus, u, "BindsTo", "gold", patterns, from_patterns, to_patterns);
+ if (r < 0)
+ return r;
+
r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
if (r < 0)
return r;
+
r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
if (r < 0)
return r;
@@ -85,6 +99,9 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
_cleanup_strv_free_ char **expanded_patterns = NULL;
int r;
+ assert(bus);
+ assert(ret);
+
STRV_FOREACH(pattern, patterns) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *unit = NULL, *unit_id = NULL;
@@ -110,10 +127,9 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
if (r < 0)
return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
- if (!streq(*pattern, unit_id)) {
+ if (!streq(*pattern, unit_id))
if (strv_extend(&expanded_patterns, unit_id) < 0)
return log_oom();
- }
}
*ret = TAKE_PTR(expanded_patterns); /* do not free */
@@ -128,8 +144,8 @@ int verb_dot(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **expanded_patterns = NULL;
_cleanup_strv_free_ char **expanded_from_patterns = NULL;
_cleanup_strv_free_ char **expanded_to_patterns = NULL;
- int r;
UnitInfo u;
+ int r;
r = acquire_bus(&bus, NULL);
if (r < 0)
@@ -170,6 +186,7 @@ int verb_dot(int argc, char *argv[], void *userdata) {
log_info(" Color legend: black = Requires\n"
" dark blue = Requisite\n"
+ " gold = BindsTo\n"
" dark grey = Wants\n"
" red = Conflicts\n"
" green = After\n");
diff --git a/src/analyze/analyze-exit-status.c b/src/analyze/analyze-exit-status.c
index 3a8d3f4..1032f1a 100644
--- a/src/analyze/analyze-exit-status.c
+++ b/src/analyze/analyze-exit-status.c
@@ -46,11 +46,9 @@ int verb_exit_status(int argc, char *argv[], void *userdata) {
return table_log_add_error(r);
}
- pager_open(arg_pager_flags);
-
- r = table_print(table, NULL);
+ r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to output table: %m");
return EXIT_SUCCESS;
}
diff --git a/src/analyze/analyze-fdstore.c b/src/analyze/analyze-fdstore.c
index 13db7f5..8ada6d4 100644
--- a/src/analyze/analyze-fdstore.c
+++ b/src/analyze/analyze-fdstore.c
@@ -81,12 +81,12 @@ static int dump_fdstore(sd_bus *bus, const char *arg) {
if (r < 0)
return r;
- if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && table_get_rows(table) <= 0)
+ if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) && table_isempty(table))
log_info("No file descriptors in fdstore of '%s'.", unit);
else {
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */true);
if (r < 0)
- return log_error_errno(r, "Failed to output table: %m");
+ return r;
}
return EXIT_SUCCESS;
diff --git a/src/analyze/analyze-image-policy.c b/src/analyze/analyze-image-policy.c
index 0146b50..7d4f549 100644
--- a/src/analyze/analyze-image-policy.c
+++ b/src/analyze/analyze-image-policy.c
@@ -94,6 +94,8 @@ int verb_image_policy(int argc, char *argv[], void *userdata) {
p = &image_policy_sysext_strict;
else if (streq(argv[i], "@confext"))
p = &image_policy_confext;
+ else if (streq(argv[i], "@confext-strict"))
+ p = &image_policy_confext_strict;
else if (streq(argv[i], "@container"))
p = &image_policy_container;
else if (streq(argv[i], "@service"))
diff --git a/src/analyze/analyze-pcrs.c b/src/analyze/analyze-pcrs.c
index ed907f7..43e415f 100644
--- a/src/analyze/analyze-pcrs.c
+++ b/src/analyze/analyze-pcrs.c
@@ -48,7 +48,7 @@ static int get_current_pcr(const char *alg, uint32_t pcr, void **ret, size_t *re
if (r < 0)
return log_error_errno(r, "Failed to read '%s': %m", p);
- r = unhexmem(s, ss, &buf, &bufsize);
+ r = unhexmem_full(s, ss, /* secure = */ false, &buf, &bufsize);
if (r < 0)
return log_error_errno(r, "Failed to decode hex PCR data '%s': %m", s);
diff --git a/src/analyze/analyze-plot.c b/src/analyze/analyze-plot.c
index 81fc25b..e271296 100644
--- a/src/analyze/analyze-plot.c
+++ b/src/analyze/analyze-plot.c
@@ -168,7 +168,7 @@ static void plot_tooltip(const UnitTimes *ut) {
svg("%s:\n", ut->name);
UnitDependency i;
- VA_ARGS_FOREACH(i, UNIT_AFTER, UNIT_BEFORE, UNIT_REQUIRES, UNIT_REQUISITE, UNIT_WANTS, UNIT_CONFLICTS, UNIT_UPHOLDS)
+ FOREACH_ARGUMENT(i, UNIT_AFTER, UNIT_BEFORE, UNIT_REQUIRES, UNIT_REQUISITE, UNIT_WANTS, UNIT_CONFLICTS, UNIT_UPHOLDS)
if (!strv_isempty(ut->deps[i])) {
svg("\n%s:\n", unit_dependency_to_string(i));
STRV_FOREACH(s, ut->deps[i])
@@ -316,7 +316,10 @@ static int produce_plot_as_svg(
strempty(host->virtualization));
svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
- svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
+ if (boot->soft_reboots_count > 0)
+ svg_graph_box(m, 0, boot->finish_time);
+ else
+ svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
if (timestamp_is_set(boot->firmware_time)) {
svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
@@ -344,6 +347,11 @@ static int produce_plot_as_svg(
svg_text(true, boot->initrd_time, y, "initrd");
y++;
}
+ if (boot->soft_reboots_count > 0) {
+ svg_bar("soft-reboot", 0, boot->userspace_time, y);
+ svg_text(true, 0, y, "soft-reboot");
+ y++;
+ }
for (u = times; u->has_data; u++) {
if (u->activating >= boot->userspace_time)
@@ -402,7 +410,7 @@ static int show_table(Table *table, const char *word) {
assert(table);
assert(word);
- if (table_get_rows(table) > 1) {
+ if (!table_isempty(table)) {
table_set_header(table, arg_legend);
if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF))
@@ -414,10 +422,10 @@ static int show_table(Table *table, const char *word) {
}
if (arg_legend) {
- if (table_get_rows(table) > 1)
- printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
- else
+ if (table_isempty(table))
printf("No %s.\n", word);
+ else
+ printf("\n%zu %s listed.\n", table_get_rows(table) - 1, word);
}
return 0;
diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c
index 5f1b5e6..75508f4 100644
--- a/src/analyze/analyze-security.c
+++ b/src/analyze/analyze-security.c
@@ -215,20 +215,21 @@ static int assess_user(
uint64_t *ret_badness,
char **ret_description) {
- _cleanup_free_ char *d = NULL;
+ const char *d;
uint64_t b;
+ int r;
assert(ret_badness);
assert(ret_description);
if (streq_ptr(info->user, NOBODY_USER_NAME)) {
- d = strdup("Service runs under as '" NOBODY_USER_NAME "' user, which should not be used for services");
+ d = "Service runs under as '" NOBODY_USER_NAME "' user, which should not be used for services";
b = 9;
} else if (info->dynamic_user && !STR_IN_SET(info->user, "0", "root")) {
- d = strdup("Service runs under a transient non-root user identity");
+ d = "Service runs under a transient non-root user identity";
b = 0;
} else if (info->user && !STR_IN_SET(info->user, "0", "root", "")) {
- d = strdup("Service runs under a static non-root user identity");
+ d = "Service runs under a static non-root user identity";
b = 0;
} else {
*ret_badness = 10;
@@ -236,12 +237,11 @@ static int assess_user(
return 0;
}
- if (!d)
- return log_oom();
+ r = strdup_to(ret_description, d);
+ if (r < 0)
+ return r;
*ret_badness = b;
- *ret_description = TAKE_PTR(d);
-
return 0;
}
@@ -254,7 +254,6 @@ static int assess_protect_home(
const char *description;
uint64_t badness;
- char *copy;
int r;
assert(ret_badness);
@@ -277,13 +276,11 @@ static int assess_protect_home(
description = "Service has no access to home directories";
}
- copy = strdup(description);
- if (!copy)
- return log_oom();
+ r = strdup_to(ret_description, description);
+ if (r < 0)
+ return r;
*ret_badness = badness;
- *ret_description = copy;
-
return 0;
}
@@ -296,7 +293,6 @@ static int assess_protect_system(
const char *description;
uint64_t badness;
- char *copy;
int r;
assert(ret_badness);
@@ -319,13 +315,11 @@ static int assess_protect_system(
description = "Service has limited write access to the OS file hierarchy";
}
- copy = strdup(description);
- if (!copy)
- return log_oom();
+ r = strdup_to(ret_description, description);
+ if (r < 0)
+ return r;
*ret_badness = badness;
- *ret_description = copy;
-
return 0;
}
@@ -370,9 +364,9 @@ static int assess_umask(
uint64_t *ret_badness,
char **ret_description) {
- char *copy = NULL;
const char *d;
uint64_t b;
+ int r;
assert(ret_badness);
assert(ret_description);
@@ -394,13 +388,11 @@ static int assess_umask(
b = 0;
}
- copy = strdup(d);
- if (!copy)
- return log_oom();
+ r = strdup_to(ret_description, d);
+ if (r < 0)
+ return r;
*ret_badness = b;
- *ret_description = copy;
-
return 0;
}
@@ -537,30 +529,30 @@ static int assess_system_call_architectures(
uint64_t *ret_badness,
char **ret_description) {
- char *d;
+ const char *d;
uint64_t b;
+ int r;
assert(ret_badness);
assert(ret_description);
if (set_isempty(info->system_call_architectures)) {
b = 10;
- d = strdup("Service may execute system calls with all ABIs");
+ d = "Service may execute system calls with all ABIs";
} else if (set_contains(info->system_call_architectures, "native") &&
set_size(info->system_call_architectures) == 1) {
b = 0;
- d = strdup("Service may execute system calls only with native ABI");
+ d = "Service may execute system calls only with native ABI";
} else {
b = 8;
- d = strdup("Service may execute system calls with multiple ABIs");
+ d = "Service may execute system calls with multiple ABIs";
}
- if (!d)
- return log_oom();
+ r = strdup_to(ret_description, d);
+ if (r < 0)
+ return r;
*ret_badness = b;
- *ret_description = d;
-
return 0;
}
@@ -607,12 +599,12 @@ static int assess_system_call_filter(
assert(a->parameter < _SYSCALL_FILTER_SET_MAX);
const SyscallFilterSet *f = syscall_filter_sets + a->parameter;
- _cleanup_free_ char *d = NULL;
+ char *d;
uint64_t b;
int r;
if (!info->system_call_filter_allow_list && set_isempty(info->system_call_filter)) {
- r = free_and_strdup(&d, "Service does not filter system calls");
+ r = strdup_to(&d, "Service does not filter system calls");
b = 10;
} else {
bool bad;
@@ -649,8 +641,8 @@ static int assess_system_call_filter(
if (r < 0)
return log_oom();
+ *ret_description = d;
*ret_badness = b;
- *ret_description = TAKE_PTR(d);
return 0;
}
@@ -664,36 +656,36 @@ static int assess_ip_address_allow(
uint64_t *ret_badness,
char **ret_description) {
- char *d = NULL;
+ const char *d;
uint64_t b;
+ int r;
assert(info);
assert(ret_badness);
assert(ret_description);
if (info->ip_filters_custom_ingress || info->ip_filters_custom_egress) {
- d = strdup("Service defines custom ingress/egress IP filters with BPF programs");
+ d = "Service defines custom ingress/egress IP filters with BPF programs";
b = 0;
} else if (!info->ip_address_deny_all) {
- d = strdup("Service does not define an IP address allow list");
+ d = "Service does not define an IP address allow list";
b = 10;
} else if (info->ip_address_allow_other) {
- d = strdup("Service defines IP address allow list with non-localhost entries");
+ d = "Service defines IP address allow list with non-localhost entries";
b = 5;
} else if (info->ip_address_allow_localhost) {
- d = strdup("Service defines IP address allow list with only localhost entries");
+ d = "Service defines IP address allow list with only localhost entries";
b = 2;
} else {
- d = strdup("Service blocks all IP address ranges");
+ d = "Service blocks all IP address ranges";
b = 0;
}
- if (!d)
- return log_oom();
+ r = strdup_to(ret_description, d);
+ if (r < 0)
+ return r;
*ret_badness = b;
- *ret_description = d;
-
return 0;
}
@@ -704,7 +696,7 @@ static int assess_device_allow(
uint64_t *ret_badness,
char **ret_description) {
- char *d = NULL;
+ char *d;
uint64_t b;
assert(info);
@@ -1651,7 +1643,7 @@ static uint64_t access_weight(const struct security_assessor *a, JsonVariant *po
assert(a);
val = security_assessor_find_in_policy(a, policy, "weight");
- if (val) {
+ if (val) {
if (json_variant_is_unsigned(val))
return json_variant_unsigned(val);
log_debug("JSON field 'weight' of policy for %s is not an unsigned integer, ignoring.", a->id);
@@ -1666,7 +1658,7 @@ static uint64_t access_range(const struct security_assessor *a, JsonVariant *pol
assert(a);
val = security_assessor_find_in_policy(a, policy, "range");
- if (val) {
+ if (val) {
if (json_variant_is_unsigned(val))
return json_variant_unsigned(val);
log_debug("JSON field 'range' of policy for %s is not an unsigned integer, ignoring.", a->id);
@@ -1681,7 +1673,7 @@ static const char *access_description_na(const struct security_assessor *a, Json
assert(a);
val = security_assessor_find_in_policy(a, policy, "description_na");
- if (val) {
+ if (val) {
if (json_variant_is_string(val))
return json_variant_string(val);
log_debug("JSON field 'description_na' of policy for %s is not a string, ignoring.", a->id);
@@ -1696,7 +1688,7 @@ static const char *access_description_good(const struct security_assessor *a, Js
assert(a);
val = security_assessor_find_in_policy(a, policy, "description_good");
- if (val) {
+ if (val) {
if (json_variant_is_string(val))
return json_variant_string(val);
log_debug("JSON field 'description_good' of policy for %s is not a string, ignoring.", a->id);
@@ -1711,7 +1703,7 @@ static const char *access_description_bad(const struct security_assessor *a, Jso
assert(a);
val = security_assessor_find_in_policy(a, policy, "description_bad");
- if (val) {
+ if (val) {
if (json_variant_is_string(val))
return json_variant_string(val);
log_debug("JSON field 'description_bad' of policy for %s is not a string, ignoring.", a->id);
@@ -1764,15 +1756,14 @@ static int assess(const SecurityInfo *info,
(void) table_set_display(details_table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 7);
}
- for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) {
- const struct security_assessor *a = security_assessor_table + i;
+ FOREACH_ELEMENT(a, security_assessor_table) {
_cleanup_free_ char *d = NULL;
uint64_t badness;
void *data;
uint64_t weight = access_weight(a, policy);
uint64_t range = access_range(a, policy);
- data = (uint8_t *) info + a->offset;
+ data = (uint8_t*) info + a->offset;
if (a->default_dependencies_only && !info->default_dependencies) {
badness = UINT64_MAX;
@@ -2738,7 +2729,7 @@ static int offline_security_checks(
/* When a portable image is analyzed, the profile is what provides a good chunk of
* the security-related settings, but they are obviously not shipped with the image.
- * This allows to take them in consideration. */
+ * This allows them to be taken into consideration. */
if (profile) {
_cleanup_free_ char *unit_name = NULL, *dropin = NULL, *profile_path = NULL;
@@ -2828,7 +2819,6 @@ static int analyze_security(sd_bus *bus,
for (;;) {
UnitInfo info;
- char *copy = NULL;
r = bus_parse_unit_info(reply, &info);
if (r < 0)
@@ -2842,12 +2832,11 @@ static int analyze_security(sd_bus *bus,
if (!GREEDY_REALLOC(list, n + 2))
return log_oom();
- copy = strdup(info.id);
- if (!copy)
- return log_oom();
+ r = strdup_to(&list[n], info.id);
+ if (r < 0)
+ return r;
- list[n++] = copy;
- list[n] = NULL;
+ list[++n] = NULL;
}
strv_sort(list);
diff --git a/src/analyze/analyze-srk.c b/src/analyze/analyze-srk.c
index 6faf2c2..acfd8b0 100644
--- a/src/analyze/analyze-srk.c
+++ b/src/analyze/analyze-srk.c
@@ -38,7 +38,7 @@ int verb_srk(int argc, char *argv[], void *userdata) {
"Refusing to write binary data to TTY, please redirect output to file.");
if (fwrite(marshalled, 1, marshalled_size, stdout) != marshalled_size)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write SRK to stdout: %m");
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write SRK to stdout.");
r = fflush_and_check(stdout);
if (r < 0)
diff --git a/src/analyze/analyze-time-data.c b/src/analyze/analyze-time-data.c
index 741cab3..1a26991 100644
--- a/src/analyze/analyze-time-data.c
+++ b/src/analyze/analyze-time-data.c
@@ -38,6 +38,7 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) {
{ "FinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, finish_time) },
{ "SecurityStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_start_time) },
{ "SecurityFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_finish_time) },
+ { "ShutdownStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, shutdown_start_time) },
{ "GeneratorsStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_start_time) },
{ "GeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_finish_time) },
{ "UnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, unitsload_start_time) },
@@ -48,6 +49,7 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) {
{ "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_generators_finish_time) },
{ "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_start_time) },
{ "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_finish_time) },
+ { "SoftRebootsCount", "t", NULL, offsetof(BootTimes, soft_reboots_count) },
{},
};
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -81,7 +83,25 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) {
if (require_finished && times.finish_time <= 0)
return log_not_finished(times.finish_time);
- if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && times.security_start_time > 0) {
+ if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && times.soft_reboots_count > 0) {
+ /* On soft-reboot ignore kernel/firmware/initrd times as they are from the previous boot */
+ times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time =
+ times.initrd_security_start_time = times.initrd_security_finish_time =
+ times.initrd_generators_start_time = times.initrd_generators_finish_time =
+ times.initrd_unitsload_start_time = times.initrd_unitsload_finish_time = 0;
+ times.reverse_offset = times.shutdown_start_time;
+
+ /* Clamp all timestamps to avoid showing huge graphs */
+ if (timestamp_is_set(times.finish_time))
+ subtract_timestamp(&times.finish_time, times.reverse_offset);
+ subtract_timestamp(&times.userspace_time, times.reverse_offset);
+
+ subtract_timestamp(&times.generators_start_time, times.reverse_offset);
+ subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
+
+ subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
+ subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
+ } else if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && timestamp_is_set(times.security_start_time)) {
/* security_start_time is set when systemd is not running under container environment. */
if (times.initrd_time > 0)
times.kernel_done_time = times.initrd_time;
@@ -183,6 +203,8 @@ int pretty_boot_time(sd_bus *bus, char **ret) {
return log_oom();
if (timestamp_is_set(t->initrd_time) && !strextend(&text, FORMAT_TIMESPAN(t->userspace_time - t->initrd_time, USEC_PER_MSEC), " (initrd) + "))
return log_oom();
+ if (t->soft_reboots_count > 0 && strextendf(&text, "%s (soft reboot #%" PRIu64 ") + ", FORMAT_TIMESPAN(t->userspace_time, USEC_PER_MSEC), t->soft_reboots_count) < 0)
+ return log_oom();
if (!strextend(&text, FORMAT_TIMESPAN(t->finish_time - t->userspace_time, USEC_PER_MSEC), " (userspace) "))
return log_oom();
@@ -192,7 +214,13 @@ int pretty_boot_time(sd_bus *bus, char **ret) {
return log_oom();
if (unit_id && timestamp_is_set(activated_time)) {
- usec_t base = timestamp_is_set(t->userspace_time) ? t->userspace_time : t->reverse_offset;
+ usec_t base;
+
+ /* On soft-reboot times are clamped to avoid showing huge graphs */
+ if (t->soft_reboots_count > 0 && timestamp_is_set(t->userspace_time))
+ base = t->userspace_time + t->reverse_offset;
+ else
+ base = timestamp_is_set(t->userspace_time) ? t->userspace_time : t->reverse_offset;
if (!strextend(&text, "\n", unit_id, " reached after ", FORMAT_TIMESPAN(activated_time - base, USEC_PER_MSEC), " in userspace."))
return log_oom();
@@ -221,7 +249,7 @@ void unit_times_clear(UnitTimes *t) {
if (!t)
return;
- FOREACH_ARRAY(d, t->deps, ELEMENTSOF(t->deps))
+ FOREACH_ELEMENT(d, t->deps)
*d = strv_free(*d);
t->name = mfree(t->name);
@@ -299,10 +327,28 @@ int acquire_time_data(sd_bus *bus, bool require_finished, UnitTimes **out) {
return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s",
u.id, bus_error_message(&error, r));
+ /* Activated in the previous soft-reboot iteration? Ignore it, we want new activations */
+ if ((t->activated > 0 && t->activated < boot_times->shutdown_start_time) ||
+ (t->activating > 0 && t->activating < boot_times->shutdown_start_time))
+ continue;
+
subtract_timestamp(&t->activating, boot_times->reverse_offset);
subtract_timestamp(&t->activated, boot_times->reverse_offset);
- subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
- subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+
+ /* If the last deactivation was in the previous soft-reboot, ignore it */
+ if (boot_times->soft_reboots_count > 0) {
+ if (t->deactivating < boot_times->reverse_offset)
+ t->deactivating = 0;
+ else
+ subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
+ if (t->deactivated < boot_times->reverse_offset)
+ t->deactivated = 0;
+ else
+ subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+ } else {
+ subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
+ subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
+ }
if (t->activated >= t->activating)
t->time = t->activated - t->activating;
diff --git a/src/analyze/analyze-time-data.h b/src/analyze/analyze-time-data.h
index 9049d87..e7ffd85 100644
--- a/src/analyze/analyze-time-data.h
+++ b/src/analyze/analyze-time-data.h
@@ -14,6 +14,7 @@ typedef struct BootTimes {
usec_t initrd_time;
usec_t userspace_time;
usec_t finish_time;
+ usec_t shutdown_start_time;
usec_t security_start_time;
usec_t security_finish_time;
usec_t generators_start_time;
@@ -26,6 +27,8 @@ typedef struct BootTimes {
usec_t initrd_generators_finish_time;
usec_t initrd_unitsload_start_time;
usec_t initrd_unitsload_finish_time;
+ /* Not strictly a timestamp, but we are going to show it next to the other timestamps */
+ uint64_t soft_reboots_count;
/*
* If we're analyzing the user instance, all timestamps will be offset by its own start-up timestamp,
diff --git a/src/analyze/analyze-unit-files.c b/src/analyze/analyze-unit-files.c
index d9b3313..e0c4867 100644
--- a/src/analyze/analyze-unit-files.c
+++ b/src/analyze/analyze-unit-files.c
@@ -15,7 +15,7 @@ static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int f
int verb_unit_files(int argc, char *argv[], void *userdata) {
_cleanup_hashmap_free_ Hashmap *unit_ids = NULL, *unit_names = NULL;
- _cleanup_(lookup_paths_free) LookupPaths lp = {};
+ _cleanup_(lookup_paths_done) LookupPaths lp = {};
char **patterns = strv_skip(argv, 1);
const char *k, *dst;
char **v;
diff --git a/src/analyze/analyze-unit-paths.c b/src/analyze/analyze-unit-paths.c
index bb00a4f..17f18e0 100644
--- a/src/analyze/analyze-unit-paths.c
+++ b/src/analyze/analyze-unit-paths.c
@@ -6,7 +6,7 @@
#include "strv.h"
int verb_unit_paths(int argc, char *argv[], void *userdata) {
- _cleanup_(lookup_paths_free) LookupPaths paths = {};
+ _cleanup_(lookup_paths_done) LookupPaths paths = {};
int r;
r = lookup_paths_init_or_warn(&paths, arg_runtime_scope, 0, NULL);
diff --git a/src/analyze/analyze-verify-util.c b/src/analyze/analyze-verify-util.c
index 6fbd6fa..8e83c9a 100644
--- a/src/analyze/analyze-verify-util.c
+++ b/src/analyze/analyze-verify-util.c
@@ -152,7 +152,7 @@ int verify_set_unit_path(char **filenames) {
* Treat explicit empty path to mean that nothing should be appended. */
old = getenv("SYSTEMD_UNIT_PATH");
if (!streq_ptr(old, "") &&
- !strextend_with_separator(&joined, ":", old ?: ""))
+ !strextend_with_separator(&joined, ":", strempty(old)))
return -ENOMEM;
assert_se(set_unit_path(joined) >= 0);
@@ -201,19 +201,15 @@ static int verify_executables(Unit *u, const char *root) {
assert(u);
- ExecCommand *exec =
- u->type == UNIT_SOCKET ? SOCKET(u)->control_command :
- u->type == UNIT_MOUNT ? MOUNT(u)->control_command :
- u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL;
- RET_GATHER(r, verify_executable(u, exec, root));
-
if (u->type == UNIT_SERVICE)
- FOREACH_ARRAY(i, SERVICE(u)->exec_command, ELEMENTSOF(SERVICE(u)->exec_command))
- RET_GATHER(r, verify_executable(u, *i, root));
+ FOREACH_ELEMENT(i, SERVICE(u)->exec_command)
+ LIST_FOREACH(command, j, *i)
+ RET_GATHER(r, verify_executable(u, j, root));
if (u->type == UNIT_SOCKET)
- FOREACH_ARRAY(i, SOCKET(u)->exec_command, ELEMENTSOF(SOCKET(u)->exec_command))
- RET_GATHER(r, verify_executable(u, *i, root));
+ FOREACH_ELEMENT(i, SOCKET(u)->exec_command)
+ LIST_FOREACH(command, j, *i)
+ RET_GATHER(r, verify_executable(u, j, root));
return r;
}
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 021de65..cf4894a 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -13,6 +13,7 @@
#include "alloc-util.h"
#include "analyze.h"
+#include "analyze-architectures.h"
#include "analyze-blame.h"
#include "analyze-calendar.h"
#include "analyze-capability.h"
@@ -25,6 +26,7 @@
#include "analyze-exit-status.h"
#include "analyze-fdstore.h"
#include "analyze-filesystems.h"
+#include "analyze-image-policy.h"
#include "analyze-inspect-elf.h"
#include "analyze-log-control.h"
#include "analyze-malloc.h"
@@ -41,7 +43,6 @@
#include "analyze-unit-files.h"
#include "analyze-unit-paths.h"
#include "analyze-verify.h"
-#include "analyze-image-policy.h"
#include "build.h"
#include "bus-error.h"
#include "bus-locator.h"
@@ -224,6 +225,7 @@ static int help(int argc, char *argv[], void *userdata) {
" capability [CAP...] List capability definitions\n"
" syscall-filter [NAME...] List syscalls in seccomp filters\n"
" filesystems [NAME...] List known filesystems\n"
+ " architectures [NAME...] List known architectures\n"
" condition CONDITION... Evaluate conditions and asserts\n"
" compare-versions VERSION1 [OP] VERSION2\n"
" Compare two version strings\n"
@@ -238,7 +240,7 @@ static int help(int argc, char *argv[], void *userdata) {
" fdstore SERVICE... Show file descriptor store contents of service\n"
" image-policy POLICY... Analyze image policy string\n"
" pcrs [PCR...] Show TPM2 PCRs and their names\n"
- " srk > FILE Write TPM2 SRK to stdout\n"
+ " srk [>FILE] Write TPM2 SRK (to FILE)\n"
"\nOptions:\n"
" --recursive-errors=MODE Control which units are verified\n"
" --offline=BOOL Perform a security review on unit file(s)\n"
@@ -270,6 +272,7 @@ static int help(int argc, char *argv[], void *userdata) {
" specified time\n"
" --profile=name|PATH Include the specified profile in the\n"
" security review of the unit(s)\n"
+ " --unit=UNIT Evaluate conditions and asserts of unit\n"
" --table Output plot's raw time data as a table\n"
" -h --help Show this help\n"
" --version Show package version\n"
@@ -557,18 +560,21 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --offline= is only supported for security right now.");
- if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore", "pcrs"))
+ if (arg_offline && optind >= argc - 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Option --offline= requires one or more units to perform a security review.");
+
+ if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore", "pcrs", "architectures", "capability", "exit-status"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Option --json= is only supported for security, inspect-elf, plot, fdstore, pcrs right now.");
+ "Option --json= is only supported for security, inspect-elf, plot, fdstore, pcrs, architectures, capability, exit-status right now.");
if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --threshold= is only supported for security right now.");
- if (arg_runtime_scope == RUNTIME_SCOPE_GLOBAL &&
- !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
+ if (arg_runtime_scope == RUNTIME_SCOPE_GLOBAL && !streq_ptr(argv[optind], "unit-paths"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Option --global only makes sense with verbs dot, unit-paths, verify.");
+ "Option --global only makes sense with verb unit-paths.");
if (streq_ptr(argv[optind], "cat-config") && arg_runtime_scope == RUNTIME_SCOPE_USER)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -596,7 +602,7 @@ static int parse_argv(int argc, char *argv[]) {
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.");
- if ((!arg_legend && !streq_ptr(argv[optind], "plot")) ||
+ if ((!arg_legend && !STRPTR_IN_SET(argv[optind], "plot", "architectures")) ||
(streq_ptr(argv[optind], "plot") && !arg_legend && !arg_table && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --no-legend is only supported for plot with either --table or --json=.");
@@ -650,6 +656,7 @@ static int run(int argc, char *argv[]) {
{ "image-policy", 2, 2, 0, verb_image_policy },
{ "pcrs", VERB_ANY, VERB_ANY, 0, verb_pcrs },
{ "srk", VERB_ANY, 1, 0, verb_srk },
+ { "architectures", VERB_ANY, VERB_ANY, 0, verb_architectures },
{}
};
@@ -673,7 +680,8 @@ static int run(int argc, char *argv[]) {
arg_image_policy,
DISSECT_IMAGE_GENERIC_ROOT |
DISSECT_IMAGE_RELAX_VAR_CHECK |
- DISSECT_IMAGE_READ_ONLY,
+ DISSECT_IMAGE_READ_ONLY |
+ DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
&mounted_dir,
/* ret_dir_fd= */ NULL,
&loop_device);
diff --git a/src/analyze/meson.build b/src/analyze/meson.build
index a505447..f150ed7 100644
--- a/src/analyze/meson.build
+++ b/src/analyze/meson.build
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
systemd_analyze_sources = files(
+ 'analyze-architectures.c',
'analyze-blame.c',
'analyze-calendar.c',
'analyze-capability.c',