1
0
mirror of https://github.com/systemd/systemd synced 2025-11-17 15:54:45 +01:00

Compare commits

..

8 Commits

Author SHA1 Message Date
Yu Watanabe
b5d63191ca network/sysctl: logs when per-link IPMasquerade= setting changes the global IPv6Forwarding= setting
All other cases, settings on different interfaces are completely
independent. But IPMasquerade=yes on an interface enables the global
IPv6Forwarding= setting, and hence affects other interfaces.
Let's log about that.

Prompted by https://github.com/systemd/systemd/issues/39304#issuecomment-3430382233.
2025-10-30 14:58:16 +01:00
Daan De Meyer
097536a49c
analyze: Add dlopen-metadata verb (#39457)
systemd-analyze dlopen-metadata will show dlopen metadata
in the ELF binary.
2025-10-30 13:56:01 +01:00
Daan De Meyer
92ea9584c6 test-namespace: Migrate to new assertion macros 2025-10-30 13:36:05 +01:00
Lennart Poettering
9851382c12 homed: always report that registered users are members of their own groups
As per the userdb spec we should report in GetMemberships() that users
are in their own groups. Hence follow the spec.

Fixes: #26061
2025-10-30 12:12:00 +00:00
Marcos Alano
44ca5b8002
hwdb: add support for the Logitech MX Master 4 (#39490) 2025-10-30 20:16:26 +09:00
Daan De Meyer
0fe29d0672 analyze: Add dlopen-metadata verb
systemd-analyze dlopen-metadata will show dlopen metadata
in the ELF binary.
2025-10-30 11:58:23 +01:00
Daan De Meyer
da30f59f60 TEST-65-ANALYZE: Add missing --no-pager 2025-10-30 08:41:44 +01:00
Daan De Meyer
004b7f1f7e elf-util: Add support for parsing dlopen metadata
Then we can add support for showing dlopen metadata to systemd-analyze.
2025-10-30 08:41:43 +01:00
14 changed files with 348 additions and 154 deletions

View File

@ -611,6 +611,10 @@ mouse:usb:v046dpc548:name:Logitech USB Receiver Mouse:*
mouse:bluetooth:v046dpb035:name:MX Master 3S B Mouse:* mouse:bluetooth:v046dpb035:name:MX Master 3S B Mouse:*
MOUSE_DPI=1000@142 MOUSE_DPI=1000@142
# Logitech MX Master 4 (via Bluetooth)
mouse:bluetooth:v046dpb042:name:MX Master 4 Mouse:*
MOUSE_DPI=1000@142
# Logitech MX Ergo # Logitech MX Ergo
mouse:usb:v046dp406f:name:Logitech MX Ergo:* mouse:usb:v046dp406f:name:Logitech MX Ergo:*
mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:406f:* mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:406f:*

View File

@ -915,6 +915,16 @@ alias.service:7: Unknown key name 'MysteryKey' in section 'Service', ignoring.
</example> </example>
</refsect2> </refsect2>
<refsect2>
<title><command>systemd-analyze dlopen-metadata <replaceable>FILE</replaceable></command></title>
<para>This command will load the specified file, and if it is an ELF object (executables,
libraries, core files, etc.) it will parse the embedded dlopen metadata, if any, and print
it in a table or json format. See the
<ulink url="https://systemd.io/ELF_DLOPEN_METADATA/">
dlopen() Metadata for ELF Files</ulink> document for more information.</para>
</refsect2>
<refsect2> <refsect2>
<title><command>systemd-analyze fdstore <replaceable>UNIT</replaceable>...</command></title> <title><command>systemd-analyze fdstore <replaceable>UNIT</replaceable>...</command></title>

View File

@ -0,0 +1,72 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-json.h"
#include "alloc-util.h"
#include "analyze.h"
#include "analyze-dlopen-metadata.h"
#include "chase.h"
#include "elf-util.h"
#include "fd-util.h"
#include "format-table.h"
#include "json-util.h"
#include "strv.h"
int verb_dlopen_metadata(int argc, char *argv[], void *userdata) {
int r;
_cleanup_free_ char *abspath = NULL;
_cleanup_close_ int fd = -EBADF;
fd = chase_and_open(argv[1], arg_root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, &abspath);
if (fd < 0)
return log_error_errno(fd, "Could not open \"%s\": %m", argv[1]);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *dlopen_metadata = NULL;
r = parse_elf_object(
fd,
abspath,
arg_root,
/* fork_disable_dump= */ false,
/* ret= */ NULL,
NULL,
&dlopen_metadata);
if (r < 0)
return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath);
if (!dlopen_metadata)
return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "%s does not contain any .note.dlopen sections", argv[1]);
if (sd_json_format_enabled(arg_json_format_flags))
return sd_json_variant_dump(dlopen_metadata, arg_json_format_flags, stdout, NULL);
_cleanup_(table_unrefp) Table *t = NULL;
t = table_new("feature", "description", "soname", "priority");
if (!t)
return log_oom();
table_set_ersatz_string(t, TABLE_ERSATZ_NA);
sd_json_variant *z;
JSON_VARIANT_ARRAY_FOREACH(z, dlopen_metadata) {
_cleanup_strv_free_ char **sonames = NULL;
r = sd_json_variant_strv(sd_json_variant_by_key(z, "soname"), &sonames);
if (r < 0)
return log_error_errno(r, "Failed to extract sonames from dlopen metadata: %m");
r = table_add_many(
t,
TABLE_STRING, sd_json_variant_string(sd_json_variant_by_key(z, "feature")),
TABLE_STRING, sd_json_variant_string(sd_json_variant_by_key(z, "description")),
TABLE_STRV_WRAPPED, sonames,
TABLE_STRING, sd_json_variant_string(sd_json_variant_by_key(z, "priority")));
if (r < 0)
return table_log_add_error(r);
}
r = table_print(t, NULL);
if (r < 0)
return table_log_print_error(r);
return 0;
}

View File

@ -0,0 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
int verb_dlopen_metadata(int argc, char *argv[], void *userdata);

View File

@ -27,7 +27,14 @@ static int analyze_elf(char **filenames, sd_json_format_flags_t json_flags) {
if (fd < 0) if (fd < 0)
return log_error_errno(fd, "Could not open \"%s\": %m", *filename); return log_error_errno(fd, "Could not open \"%s\": %m", *filename);
r = parse_elf_object(fd, abspath, arg_root, /* fork_disable_dump= */false, &stacktrace, &package_metadata); r = parse_elf_object(
fd,
abspath,
arg_root,
/* fork_disable_dump= */ false,
&stacktrace,
&package_metadata,
/* ret_dlopen_metadata= */ NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath); return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath);

View File

@ -22,6 +22,7 @@
#include "analyze-compare-versions.h" #include "analyze-compare-versions.h"
#include "analyze-condition.h" #include "analyze-condition.h"
#include "analyze-critical-chain.h" #include "analyze-critical-chain.h"
#include "analyze-dlopen-metadata.h"
#include "analyze-dot.h" #include "analyze-dot.h"
#include "analyze-dump.h" #include "analyze-dump.h"
#include "analyze-exit-status.h" #include "analyze-exit-status.h"
@ -798,6 +799,7 @@ static int run(int argc, char *argv[]) {
{ "timespan", 2, VERB_ANY, 0, verb_timespan }, { "timespan", 2, VERB_ANY, 0, verb_timespan },
{ "security", VERB_ANY, VERB_ANY, 0, verb_security }, { "security", VERB_ANY, VERB_ANY, 0, verb_security },
{ "inspect-elf", 2, VERB_ANY, 0, verb_elf_inspection }, { "inspect-elf", 2, VERB_ANY, 0, verb_elf_inspection },
{ "dlopen-metadata", 2, 2, 0, verb_dlopen_metadata },
{ "malloc", VERB_ANY, VERB_ANY, 0, verb_malloc }, { "malloc", VERB_ANY, VERB_ANY, 0, verb_malloc },
{ "fdstore", 2, VERB_ANY, 0, verb_fdstore }, { "fdstore", 2, VERB_ANY, 0, verb_fdstore },
{ "image-policy", 2, 2, 0, verb_image_policy }, { "image-policy", 2, 2, 0, verb_image_policy },

View File

@ -10,6 +10,7 @@ systemd_analyze_sources = files(
'analyze-compare-versions.c', 'analyze-compare-versions.c',
'analyze-condition.c', 'analyze-condition.c',
'analyze-critical-chain.c', 'analyze-critical-chain.c',
'analyze-dlopen-metadata.c',
'analyze-dot.c', 'analyze-dot.c',
'analyze-dump.c', 'analyze-dump.c',
'analyze-exit-status.c', 'analyze-exit-status.c',

View File

@ -689,7 +689,8 @@ int coredump_submit(
root, root,
/* fork_disable_dump= */ skip, /* avoid loops */ /* fork_disable_dump= */ skip, /* avoid loops */
&stacktrace, &stacktrace,
&json_metadata); &json_metadata,
/* ret_dlopen_metadata= */ NULL);
} }
} }

View File

@ -287,8 +287,6 @@ int vl_method_get_memberships(sd_varlink *link, sd_json_variant *parameters, sd_
return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL); return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
if (p.user_name) { if (p.user_name) {
const char *last = NULL;
r = manager_get_home_by_name(m, p.user_name, &h); r = manager_get_home_by_name(m, p.user_name, &h);
if (r < 0) if (r < 0)
return r; return r;
@ -296,40 +294,37 @@ int vl_method_get_memberships(sd_varlink *link, sd_json_variant *parameters, sd_
return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
if (p.group_name) { if (p.group_name) {
if (!strv_contains(h->record->member_of, p.group_name)) if (!strv_contains(h->record->member_of, p.group_name) &&
!user_record_matches_user_name(h->record, p.group_name))
return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
return sd_varlink_replybo( return sd_varlink_replybo(
link, link,
SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(h->user_name)), SD_JSON_BUILD_PAIR_STRING("userName", h->user_name),
SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(p.group_name))); SD_JSON_BUILD_PAIR_STRING("groupName", p.group_name));
} }
STRV_FOREACH(i, h->record->member_of) { STRV_FOREACH(i, h->record->member_of) {
if (last) { r = sd_varlink_notifybo(
r = sd_varlink_notifybo( link,
link, SD_JSON_BUILD_PAIR_STRING("userName", h->user_name),
SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(h->user_name)), SD_JSON_BUILD_PAIR_STRING("groupName", *i));
SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(last))); if (r < 0)
if (r < 0) return r;
return r;
}
last = *i;
} }
if (last) return sd_varlink_replybo(
return sd_varlink_replybo( link,
link, SD_JSON_BUILD_PAIR_STRING("userName", h->user_name),
SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(h->user_name)), SD_JSON_BUILD_PAIR_STRING("groupName", h->user_name));
SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(last)));
} else if (p.group_name) { } else if (p.group_name) {
const char *last = NULL; const char *last = NULL;
HASHMAP_FOREACH(h, m->homes_by_uid) { HASHMAP_FOREACH(h, m->homes_by_uid) {
if (!strv_contains(h->record->member_of, p.group_name)) if (!strv_contains(h->record->member_of, p.group_name) &&
!user_record_matches_user_name(h->record, p.group_name))
continue; continue;
if (last) { if (last) {
@ -350,34 +345,37 @@ int vl_method_get_memberships(sd_varlink *link, sd_json_variant *parameters, sd_
SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(last)), SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(last)),
SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(p.group_name))); SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(p.group_name)));
} else { } else {
const char *last_user_name = NULL, *last_group_name = NULL; const char *last = NULL;
HASHMAP_FOREACH(h, m->homes_by_uid) HASHMAP_FOREACH(h, m->homes_by_uid) {
STRV_FOREACH(j, h->record->member_of) { STRV_FOREACH(j, h->record->member_of) {
if (last) {
if (last_user_name) {
assert(last_group_name);
r = sd_varlink_notifybo( r = sd_varlink_notifybo(
link, link,
SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(last_user_name)), SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(last)),
SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(last_group_name))); SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(last)));
if (r < 0) if (r < 0)
return r; return r;
last = NULL;
} }
last_user_name = h->user_name; r = sd_varlink_notifybo(
last_group_name = *j; link,
SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(h->user_name)),
SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(*j)));
if (r < 0)
return r;
} }
if (last_user_name) { last = h->user_name;
assert(last_group_name); }
if (last)
return sd_varlink_replybo( return sd_varlink_replybo(
link, link,
SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(last_user_name)), SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(last)),
SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(last_group_name))); SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(last)));
}
} }
return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL); return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);

View File

@ -377,6 +377,8 @@ static int link_set_ip_forwarding(Link *link, int family) {
if (FLAGS_SET(link->network->ip_masquerade, AF_TO_ADDRESS_FAMILY(family)) && if (FLAGS_SET(link->network->ip_masquerade, AF_TO_ADDRESS_FAMILY(family)) &&
link->manager->ip_forwarding[family == AF_INET6] < 0) { link->manager->ip_forwarding[family == AF_INET6] < 0) {
log_link_notice(link, "IPMasquerade= is enabled on the interface, enabling the global IPv6Forwarding= setting, which may affect NDisc and DHCPv6 client on other interfaces.");
link->manager->ip_forwarding[family == AF_INET6] = true; link->manager->ip_forwarding[family == AF_INET6] = true;
manager_set_ip_forwarding(link->manager, family); manager_set_ip_forwarding(link->manager, family);

View File

@ -20,6 +20,7 @@
#include "fileio.h" #include "fileio.h"
#include "format-util.h" #include "format-util.h"
#include "io-util.h" #include "io-util.h"
#include "json-util.h"
#include "log.h" #include "log.h"
#include "memstream-util.h" #include "memstream-util.h"
#include "path-util.h" #include "path-util.h"
@ -186,6 +187,7 @@ typedef struct StackContext {
unsigned n_thread; unsigned n_thread;
unsigned n_frame; unsigned n_frame;
sd_json_variant **package_metadata; sd_json_variant **package_metadata;
sd_json_variant **dlopen_metadata;
Set **modules; Set **modules;
} StackContext; } StackContext;
@ -352,8 +354,8 @@ static void report_module_metadata(StackContext *c, const char *name, sd_json_va
fputs("\n", c->m.f); fputs("\n", c->m.f);
} }
static int parse_package_metadata(const char *name, sd_json_variant *id_json, Elf *elf, bool *ret_interpreter_found, StackContext *c) { static int parse_metadata(const char *name, sd_json_variant *id_json, Elf *elf, bool *ret_interpreter_found, StackContext *c) {
bool interpreter_found = false; bool package_metadata_found = false, interpreter_found = false;
size_t n_program_headers; size_t n_program_headers;
int r; int r;
@ -408,7 +410,7 @@ static int parse_package_metadata(const char *name, sd_json_variant *id_json, El
/* Package metadata might have different owners, but the /* Package metadata might have different owners, but the
* magic ID is always the same. */ * magic ID is always the same. */
if (note_header.n_type != ELF_PACKAGE_METADATA_ID) if (!IN_SET(note_header.n_type, ELF_PACKAGE_METADATA_ID, ELF_NOTE_DLOPEN_TYPE))
continue; continue;
_cleanup_free_ char *payload_0suffixed = NULL; _cleanup_free_ char *payload_0suffixed = NULL;
@ -430,46 +432,59 @@ static int parse_package_metadata(const char *name, sd_json_variant *id_json, El
return log_error_errno(r, "json_parse on \"%s\" failed: %m", strnull(esc)); return log_error_errno(r, "json_parse on \"%s\" failed: %m", strnull(esc));
} }
/* If we have a build-id, merge it in the same JSON object so that it appears all if (note_header.n_type == ELF_PACKAGE_METADATA_ID) {
* nicely together in the logs/metadata. */ /* If we have a build-id, merge it in the same JSON object so that it appears all
if (id_json) { * nicely together in the logs/metadata. */
r = sd_json_variant_merge_object(&v, id_json); if (id_json) {
r = sd_json_variant_merge_object(&v, id_json);
if (r < 0)
return log_error_errno(r, "sd_json_variant_merge of package meta with buildId failed: %m");
}
/* Pretty-print to the buffer, so that the metadata goes as plaintext in the
* journal. */
report_module_metadata(c, name, v);
/* Then we build a new object using the module name as the key, and merge it
* with the previous parses, so that in the end it all fits together in a single
* JSON blob. */
r = sd_json_buildo(&w, SD_JSON_BUILD_PAIR(name, SD_JSON_BUILD_VARIANT(v)));
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");
r = sd_json_variant_merge_object(c->package_metadata, w);
if (r < 0) if (r < 0)
return log_error_errno(r, "sd_json_variant_merge of package meta with buildId failed: %m"); return log_error_errno(r, "sd_json_variant_merge of package meta with buildId failed: %m");
package_metadata_found = true;
} else if (c->dlopen_metadata) {
sd_json_variant *z;
JSON_VARIANT_ARRAY_FOREACH(z, v) {
r = sd_json_variant_append_array(c->dlopen_metadata, z);
if (r < 0)
return log_error_errno(r, "Failed to append entry to dlopen metadata: %m");
}
} }
/* Pretty-print to the buffer, so that the metadata goes as plaintext in the
* journal. */
report_module_metadata(c, name, v);
/* Then we build a new object using the module name as the key, and merge it
* with the previous parses, so that in the end it all fits together in a single
* JSON blob. */
r = sd_json_buildo(&w, SD_JSON_BUILD_PAIR(name, SD_JSON_BUILD_VARIANT(v)));
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");
r = sd_json_variant_merge_object(c->package_metadata, w);
if (r < 0)
return log_error_errno(r, "sd_json_variant_merge of package meta with buildId failed: %m");
/* Finally stash the name, so we avoid double visits. */ /* Finally stash the name, so we avoid double visits. */
r = set_put_strdup(c->modules, name); r = set_put_strdup(c->modules, name);
if (r < 0) if (r < 0)
return log_error_errno(r, "set_put_strdup failed: %m"); return log_error_errno(r, "set_put_strdup failed: %m");
if (ret_interpreter_found) if (!c->dlopen_metadata) {
*ret_interpreter_found = interpreter_found; if (ret_interpreter_found)
*ret_interpreter_found = interpreter_found;
return 1; return 1;
}
} }
} }
if (ret_interpreter_found) if (ret_interpreter_found)
*ret_interpreter_found = interpreter_found; *ret_interpreter_found = interpreter_found;
/* Didn't find package metadata for this module - that's ok, just go to the next. */ return c->dlopen_metadata ? 0 : package_metadata_found;
return 0;
} }
/* Get the build-id out of an ELF object or a dwarf core module. */ /* Get the build-id out of an ELF object or a dwarf core module. */
@ -535,7 +550,7 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name,
* to the ELF object first. We might be lucky and just get it from elfutils. */ * to the ELF object first. We might be lucky and just get it from elfutils. */
elf = sym_dwfl_module_getelf(mod, &bias); elf = sym_dwfl_module_getelf(mod, &bias);
if (elf) { if (elf) {
r = parse_package_metadata(name, id_json, elf, NULL, c); r = parse_metadata(name, id_json, elf, NULL, c);
if (r < 0) if (r < 0)
return DWARF_CB_ABORT; return DWARF_CB_ABORT;
if (r > 0) if (r > 0)
@ -590,7 +605,8 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name,
_cleanup_(elf_endp) Elf *memelf = sym_elf_memory(data->d_buf, data->d_size); _cleanup_(elf_endp) Elf *memelf = sym_elf_memory(data->d_buf, data->d_size);
if (!memelf) if (!memelf)
continue; continue;
r = parse_package_metadata(name, id_json, memelf, NULL, c);
r = parse_metadata(name, id_json, memelf, NULL, c);
if (r < 0) if (r < 0)
return DWARF_CB_ABORT; return DWARF_CB_ABORT;
if (r > 0) if (r > 0)
@ -600,7 +616,12 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name,
return DWARF_CB_OK; return DWARF_CB_OK;
} }
static int parse_core(int fd, const char *root, char **ret, sd_json_variant **ret_package_metadata) { static int parse_core(
int fd,
const char *root,
char **ret,
sd_json_variant **ret_package_metadata,
sd_json_variant **ret_dlopen_metadata) {
const Dwfl_Callbacks callbacks = { const Dwfl_Callbacks callbacks = {
.find_elf = sym_dwfl_build_id_find_elf, .find_elf = sym_dwfl_build_id_find_elf,
@ -608,10 +629,11 @@ static int parse_core(int fd, const char *root, char **ret, sd_json_variant **re
.find_debuginfo = sym_dwfl_standard_find_debuginfo, .find_debuginfo = sym_dwfl_standard_find_debuginfo,
}; };
_cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL, *dlopen_metadata = NULL;
_cleanup_set_free_ Set *modules = NULL; _cleanup_set_free_ Set *modules = NULL;
_cleanup_(stack_context_done) StackContext c = { _cleanup_(stack_context_done) StackContext c = {
.package_metadata = &package_metadata, .package_metadata = &package_metadata,
.dlopen_metadata = ret_dlopen_metadata ? &dlopen_metadata : NULL,
.modules = &modules, .modules = &modules,
}; };
int r; int r;
@ -667,15 +689,24 @@ static int parse_core(int fd, const char *root, char **ret, sd_json_variant **re
if (ret_package_metadata) if (ret_package_metadata)
*ret_package_metadata = TAKE_PTR(package_metadata); *ret_package_metadata = TAKE_PTR(package_metadata);
if (ret_dlopen_metadata)
*ret_dlopen_metadata = TAKE_PTR(dlopen_metadata);
return 0; return 0;
} }
static int parse_elf(int fd, const char *executable, const char *root, char **ret, sd_json_variant **ret_package_metadata) { static int parse_elf(
_cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL, *elf_metadata = NULL; int fd,
const char *executable,
const char *root,
char **ret,
sd_json_variant **ret_package_metadata,
sd_json_variant **ret_dlopen_metadata) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL, *dlopen_metadata = NULL, *elf_metadata = NULL;
_cleanup_set_free_ Set *modules = NULL; _cleanup_set_free_ Set *modules = NULL;
_cleanup_(stack_context_done) StackContext c = { _cleanup_(stack_context_done) StackContext c = {
.package_metadata = &package_metadata, .package_metadata = &package_metadata,
.dlopen_metadata = ret_dlopen_metadata ? &dlopen_metadata : NULL,
.modules = &modules, .modules = &modules,
}; };
const char *elf_type; const char *elf_type;
@ -702,7 +733,7 @@ static int parse_elf(int fd, const char *executable, const char *root, char **re
if (elf_header.e_type == ET_CORE) { if (elf_header.e_type == ET_CORE) {
_cleanup_free_ char *out = NULL; _cleanup_free_ char *out = NULL;
r = parse_core(fd, root, ret ? &out : NULL, &package_metadata); r = parse_core(fd, root, ret ? &out : NULL, &package_metadata, &dlopen_metadata);
if (r < 0) if (r < 0)
return log_warning_errno(r, "Failed to inspect core file: %m"); return log_warning_errno(r, "Failed to inspect core file: %m");
@ -719,7 +750,7 @@ static int parse_elf(int fd, const char *executable, const char *root, char **re
if (r < 0) if (r < 0)
return log_warning_errno(r, "Failed to parse build-id of ELF file: %m"); return log_warning_errno(r, "Failed to parse build-id of ELF file: %m");
r = parse_package_metadata(e, id_json, c.elf, &interpreter_found, &c); r = parse_metadata(e, id_json, c.elf, &interpreter_found, &c);
if (r < 0) if (r < 0)
return log_warning_errno(r, "Failed to parse package metadata of ELF file: %m"); return log_warning_errno(r, "Failed to parse package metadata of ELF file: %m");
@ -769,18 +800,28 @@ static int parse_elf(int fd, const char *executable, const char *root, char **re
if (ret_package_metadata) if (ret_package_metadata)
*ret_package_metadata = TAKE_PTR(elf_metadata); *ret_package_metadata = TAKE_PTR(elf_metadata);
if (ret_dlopen_metadata)
*ret_dlopen_metadata = TAKE_PTR(dlopen_metadata);
return 0; return 0;
} }
#endif #endif
int parse_elf_object(int fd, const char *executable, const char *root, bool fork_disable_dump, char **ret, sd_json_variant **ret_package_metadata) { int parse_elf_object(
int fd,
const char *executable,
const char *root,
bool fork_disable_dump,
char **ret,
sd_json_variant **ret_package_metadata,
sd_json_variant **ret_dlopen_metadata) {
#if HAVE_ELFUTILS #if HAVE_ELFUTILS
_cleanup_close_pair_ int error_pipe[2] = EBADF_PAIR, _cleanup_close_pair_ int error_pipe[2] = EBADF_PAIR,
return_pipe[2] = EBADF_PAIR, return_pipe[2] = EBADF_PAIR,
json_pipe[2] = EBADF_PAIR; package_metadata_pipe[2] = EBADF_PAIR,
_cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL; dlopen_metadata_pipe[2] = EBADF_PAIR;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL, *dlopen_metadata = NULL;
_cleanup_free_ char *buf = NULL; _cleanup_free_ char *buf = NULL;
int r; int r;
@ -805,7 +846,13 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork
} }
if (ret_package_metadata) { if (ret_package_metadata) {
r = RET_NERRNO(pipe2(json_pipe, O_CLOEXEC|O_NONBLOCK)); r = RET_NERRNO(pipe2(package_metadata_pipe, O_CLOEXEC|O_NONBLOCK));
if (r < 0)
return r;
}
if (ret_dlopen_metadata) {
r = RET_NERRNO(pipe2(dlopen_metadata_pipe, O_CLOEXEC|O_NONBLOCK));
if (r < 0) if (r < 0)
return r; return r;
} }
@ -818,8 +865,8 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork
* the file descriptor and writing into these four pipes. */ * the file descriptor and writing into these four pipes. */
r = safe_fork_full("(sd-parse-elf)", r = safe_fork_full("(sd-parse-elf)",
NULL, NULL,
(int[]){ fd, error_pipe[1], return_pipe[1], json_pipe[1] }, (int[]){ fd, error_pipe[1], return_pipe[1], package_metadata_pipe[1], dlopen_metadata_pipe[1] },
4, 5,
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_NEW_USERNS|FORK_WAIT|FORK_REOPEN_LOG, FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_NEW_USERNS|FORK_WAIT|FORK_REOPEN_LOG,
NULL); NULL);
if (r < 0) { if (r < 0) {
@ -846,7 +893,13 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork
report_errno_and_exit(error_pipe[1], r); report_errno_and_exit(error_pipe[1], r);
} }
r = parse_elf(fd, executable, root, ret ? &buf : NULL, ret_package_metadata ? &package_metadata : NULL); r = parse_elf(
fd,
executable,
root,
ret ? &buf : NULL,
ret_package_metadata ? &package_metadata : NULL,
ret_dlopen_metadata ? &dlopen_metadata : NULL);
if (r < 0) if (r < 0)
report_errno_and_exit(error_pipe[1], r); report_errno_and_exit(error_pipe[1], r);
@ -879,9 +932,9 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork
/* Bump the space for the returned string. We don't know how much space we'll need in /* Bump the space for the returned string. We don't know how much space we'll need in
* advance, so we'll just try to write as much as possible and maybe fail later. */ * advance, so we'll just try to write as much as possible and maybe fail later. */
(void) fcntl(json_pipe[1], F_SETPIPE_SZ, COREDUMP_PIPE_MAX); (void) fcntl(package_metadata_pipe[1], F_SETPIPE_SZ, COREDUMP_PIPE_MAX);
json_out = take_fdopen(&json_pipe[1], "w"); json_out = take_fdopen(&package_metadata_pipe[1], "w");
if (!json_out) if (!json_out)
report_errno_and_exit(error_pipe[1], -errno); report_errno_and_exit(error_pipe[1], -errno);
@ -890,12 +943,29 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork
log_warning_errno(r, "Failed to write JSON package metadata, ignoring: %m"); log_warning_errno(r, "Failed to write JSON package metadata, ignoring: %m");
} }
if (dlopen_metadata) {
_cleanup_fclose_ FILE *json_out = NULL;
/* Bump the space for the returned string. We don't know how much space we'll need in
* advance, so we'll just try to write as much as possible and maybe fail later. */
(void) fcntl(dlopen_metadata_pipe[1], F_SETPIPE_SZ, COREDUMP_PIPE_MAX);
json_out = take_fdopen(&dlopen_metadata_pipe[1], "w");
if (!json_out)
report_errno_and_exit(error_pipe[1], -errno);
r = sd_json_variant_dump(dlopen_metadata, SD_JSON_FORMAT_FLUSH, json_out, NULL);
if (r < 0)
log_warning_errno(r, "Failed to write JSON package metadata, ignoring: %m");
}
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
} }
error_pipe[1] = safe_close(error_pipe[1]); error_pipe[1] = safe_close(error_pipe[1]);
return_pipe[1] = safe_close(return_pipe[1]); return_pipe[1] = safe_close(return_pipe[1]);
json_pipe[1] = safe_close(json_pipe[1]); package_metadata_pipe[1] = safe_close(package_metadata_pipe[1]);
dlopen_metadata_pipe[1] = safe_close(dlopen_metadata_pipe[1]);
if (ret) { if (ret) {
_cleanup_fclose_ FILE *in = NULL; _cleanup_fclose_ FILE *in = NULL;
@ -912,19 +982,33 @@ int parse_elf_object(int fd, const char *executable, const char *root, bool fork
if (ret_package_metadata) { if (ret_package_metadata) {
_cleanup_fclose_ FILE *json_in = NULL; _cleanup_fclose_ FILE *json_in = NULL;
json_in = take_fdopen(&json_pipe[0], "r"); json_in = take_fdopen(&package_metadata_pipe[0], "r");
if (!json_in) if (!json_in)
return -errno; return -errno;
r = sd_json_parse_file(json_in, NULL, 0, &package_metadata, NULL, NULL); r = sd_json_parse_file(json_in, NULL, 0, &package_metadata, NULL, NULL);
if (r < 0 && r != -ENODATA) /* ENODATA: json was empty, so we got nothing, but that's ok */ if (r < 0 && r != -ENODATA) /* ENODATA: json was empty, so we got nothing, but that's ok */
log_warning_errno(r, "Failed to read or parse json metadata, ignoring: %m"); log_warning_errno(r, "Failed to read or parse package metadata, ignoring: %m");
}
if (ret_dlopen_metadata) {
_cleanup_fclose_ FILE *json_in = NULL;
json_in = take_fdopen(&dlopen_metadata_pipe[0], "r");
if (!json_in)
return -errno;
r = sd_json_parse_file(json_in, NULL, 0, &dlopen_metadata, NULL, NULL);
if (r < 0 && r != -ENODATA) /* ENODATA: json was empty, so we got nothing, but that's ok */
log_warning_errno(r, "Failed to read or parse dlopen metadata, ignoring: %m");
} }
if (ret) if (ret)
*ret = TAKE_PTR(buf); *ret = TAKE_PTR(buf);
if (ret_package_metadata) if (ret_package_metadata)
*ret_package_metadata = TAKE_PTR(package_metadata); *ret_package_metadata = TAKE_PTR(package_metadata);
if (ret_dlopen_metadata)
*ret_dlopen_metadata = TAKE_PTR(dlopen_metadata);
return 0; return 0;
#else #else

View File

@ -9,4 +9,11 @@ int dlopen_elf(void);
/* Parse an ELF object in a forked process, so that errors while iterating over /* Parse an ELF object in a forked process, so that errors while iterating over
* untrusted and potentially malicious data do not propagate to the main caller's process. * untrusted and potentially malicious data do not propagate to the main caller's process.
* If fork_disable_dump, the child process will not dump core if it crashes. */ * If fork_disable_dump, the child process will not dump core if it crashes. */
int parse_elf_object(int fd, const char *executable, const char *root, bool fork_disable_dump, char **ret, sd_json_variant **ret_package_metadata); int parse_elf_object(
int fd,
const char *executable,
const char *root,
bool fork_disable_dump,
char **ret,
sd_json_variant **ret_package_metadata,
sd_json_variant **ret_dlopen_metadata);

View File

@ -21,20 +21,20 @@
#include "process-util.h" #include "process-util.h"
#include "string-util.h" #include "string-util.h"
#include "tests.h" #include "tests.h"
#include "tmpfile-util.h"
#include "uid-range.h" #include "uid-range.h"
#include "user-util.h" #include "user-util.h"
#include "virt.h" #include "virt.h"
TEST(namespace_cleanup_tmpdir) { TEST(namespace_cleanup_tmpdir) {
{ {
_cleanup_(namespace_cleanup_tmpdirp) char *dir; _cleanup_(namespace_cleanup_tmpdirp) char *dir = NULL;
assert_se(dir = strdup(RUN_SYSTEMD_EMPTY)); ASSERT_NOT_NULL(dir = strdup(RUN_SYSTEMD_EMPTY));
} }
{ {
_cleanup_(namespace_cleanup_tmpdirp) char *dir; _cleanup_(namespace_cleanup_tmpdirp) char *dir = NULL;
assert_se(dir = strdup("/tmp/systemd-test-namespace.XXXXXX")); ASSERT_OK(mkdtemp_malloc("/tmp/systemd-test-namespace.XXXXXX", &dir));
assert_se(mkdtemp(dir));
} }
} }
@ -43,34 +43,34 @@ static void test_tmpdir_one(const char *id, const char *A, const char *B) {
struct stat x, y; struct stat x, y;
char *c, *d; char *c, *d;
assert_se(setup_tmp_dirs(id, &a, &b) == 0); ASSERT_OK_ZERO(setup_tmp_dirs(id, &a, &b));
assert_se(stat(a, &x) >= 0); ASSERT_OK_ERRNO(stat(a, &x));
assert_se(stat(b, &y) >= 0); ASSERT_OK_ERRNO(stat(b, &y));
assert_se(S_ISDIR(x.st_mode)); ASSERT_TRUE(S_ISDIR(x.st_mode));
assert_se(S_ISDIR(y.st_mode)); ASSERT_TRUE(S_ISDIR(y.st_mode));
if (!streq(a, RUN_SYSTEMD_EMPTY)) { if (!streq(a, RUN_SYSTEMD_EMPTY)) {
assert_se(startswith(a, A)); ASSERT_TRUE(startswith(a, A));
assert_se((x.st_mode & 01777) == 0700); ASSERT_EQ((x.st_mode & 01777), 0700U);
c = strjoina(a, "/tmp"); ASSERT_NOT_NULL(c = strjoina(a, "/tmp"));
assert_se(stat(c, &x) >= 0); ASSERT_OK_ERRNO(stat(c, &x));
assert_se(S_ISDIR(x.st_mode)); ASSERT_TRUE(S_ISDIR(x.st_mode));
assert_se(FLAGS_SET(x.st_mode, 01777)); ASSERT_TRUE(FLAGS_SET(x.st_mode, 01777));
assert_se(rmdir(c) >= 0); ASSERT_OK_ERRNO(rmdir(c));
assert_se(rmdir(a) >= 0); ASSERT_OK_ERRNO(rmdir(a));
} }
if (!streq(b, RUN_SYSTEMD_EMPTY)) { if (!streq(b, RUN_SYSTEMD_EMPTY)) {
assert_se(startswith(b, B)); ASSERT_TRUE(startswith(b, B));
assert_se((y.st_mode & 01777) == 0700); ASSERT_EQ((y.st_mode & 01777), 0700U);
d = strjoina(b, "/tmp"); ASSERT_NOT_NULL(d = strjoina(b, "/tmp"));
assert_se(stat(d, &y) >= 0); ASSERT_OK_ERRNO(stat(d, &y));
assert_se(S_ISDIR(y.st_mode)); ASSERT_TRUE(S_ISDIR(y.st_mode));
assert_se(FLAGS_SET(y.st_mode, 01777)); ASSERT_TRUE(FLAGS_SET(y.st_mode, 01777));
assert_se(rmdir(d) >= 0); ASSERT_OK_ERRNO(rmdir(d));
assert_se(rmdir(b) >= 0); ASSERT_OK_ERRNO(rmdir(b));
} }
} }
@ -78,18 +78,15 @@ TEST(tmpdir) {
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *zz = NULL; _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *zz = NULL;
sd_id128_t bid; sd_id128_t bid;
assert_se(sd_id128_get_boot(&bid) >= 0); ASSERT_OK(sd_id128_get_boot(&bid));
x = strjoin("/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-abcd.service-"); ASSERT_NOT_NULL(x = strjoin("/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-abcd.service-"));
y = strjoin("/var/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-abcd.service-"); ASSERT_NOT_NULL(y = strjoin("/var/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-abcd.service-"));
assert_se(x && y);
test_tmpdir_one("abcd.service", x, y); test_tmpdir_one("abcd.service", x, y);
z = strjoin("/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-"); ASSERT_NOT_NULL(z = strjoin("/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-"));
zz = strjoin("/var/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-"); ASSERT_NOT_NULL(zz = strjoin("/var/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-"));
assert_se(z && zz);
test_tmpdir_one("sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device", z, zz); test_tmpdir_one("sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device", z, zz);
} }
@ -106,54 +103,54 @@ static void test_shareable_ns(unsigned long nsflag) {
return; return;
} }
assert_se(socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, s) >= 0); ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, s));
pid1 = fork(); pid1 = fork();
assert_se(pid1 >= 0); ASSERT_OK_ERRNO(pid1);
if (pid1 == 0) { if (pid1 == 0) {
r = setup_shareable_ns(s, nsflag); r = setup_shareable_ns(s, nsflag);
assert_se(r >= 0 || ERRNO_IS_NEG_PRIVILEGE(r)); if (!ERRNO_IS_PRIVILEGE(r))
ASSERT_OK(r);
_exit(r >= 0 ? r : EX_NOPERM); _exit(r >= 0 ? r : EX_NOPERM);
} }
pid2 = fork(); pid2 = fork();
assert_se(pid2 >= 0); ASSERT_OK_ERRNO(pid2);
if (pid2 == 0) { if (pid2 == 0) {
r = setup_shareable_ns(s, nsflag); r = setup_shareable_ns(s, nsflag);
assert_se(r >= 0 || ERRNO_IS_NEG_PRIVILEGE(r)); if (!ERRNO_IS_PRIVILEGE(r))
ASSERT_OK(r);
_exit(r >= 0 ? r : EX_NOPERM); _exit(r >= 0 ? r : EX_NOPERM);
} }
pid3 = fork(); pid3 = fork();
assert_se(pid3 >= 0); ASSERT_OK_ERRNO(pid3);
if (pid3 == 0) { if (pid3 == 0) {
r = setup_shareable_ns(s, nsflag); r = setup_shareable_ns(s, nsflag);
assert_se(r >= 0 || ERRNO_IS_NEG_PRIVILEGE(r)); if (!ERRNO_IS_PRIVILEGE(r))
ASSERT_OK(r);
_exit(r >= 0 ? r : EX_NOPERM); _exit(r >= 0 ? r : EX_NOPERM);
} }
r = wait_for_terminate(pid1, &si); ASSERT_OK(wait_for_terminate(pid1, &si));
assert_se(r >= 0); ASSERT_EQ(si.si_code, CLD_EXITED);
assert_se(si.si_code == CLD_EXITED);
if (si.si_status == EX_NOPERM) if (si.si_status == EX_NOPERM)
permission_denied = true; permission_denied = true;
else else
n += si.si_status; n += si.si_status;
r = wait_for_terminate(pid2, &si); ASSERT_OK(wait_for_terminate(pid2, &si));
assert_se(r >= 0); ASSERT_EQ(si.si_code, CLD_EXITED);
assert_se(si.si_code == CLD_EXITED);
if (si.si_status == EX_NOPERM) if (si.si_status == EX_NOPERM)
permission_denied = true; permission_denied = true;
else else
n += si.si_status; n += si.si_status;
r = wait_for_terminate(pid3, &si); ASSERT_OK(wait_for_terminate(pid3, &si));
assert_se(r >= 0); ASSERT_EQ(si.si_code, CLD_EXITED);
assert_se(si.si_code == CLD_EXITED);
if (si.si_status == EX_NOPERM) if (si.si_status == EX_NOPERM)
permission_denied = true; permission_denied = true;
else else
@ -164,7 +161,7 @@ static void test_shareable_ns(unsigned long nsflag) {
if (permission_denied) if (permission_denied)
return (void) log_tests_skipped("insufficient privileges"); return (void) log_tests_skipped("insufficient privileges");
assert_se(n == 1); ASSERT_EQ(n, 1);
} }
TEST(netns) { TEST(netns) {
@ -206,7 +203,6 @@ TEST(protect_kernel_logs) {
.protect_kernel_logs = true, .protect_kernel_logs = true,
}; };
pid_t pid; pid_t pid;
int r;
if (geteuid() > 0) { if (geteuid() > 0) {
(void) log_tests_skipped("not root"); (void) log_tests_skipped("not root");
@ -220,33 +216,30 @@ TEST(protect_kernel_logs) {
} }
pid = fork(); pid = fork();
assert_se(pid >= 0); ASSERT_OK_ERRNO(pid);
if (pid == 0) { if (pid == 0) {
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
fd = open("/dev/kmsg", O_RDONLY | O_CLOEXEC); ASSERT_OK_ERRNO(fd = open("/dev/kmsg", O_RDONLY | O_CLOEXEC));
assert_se(fd > 0);
r = setup_namespace(&p, NULL); ASSERT_OK_ZERO(setup_namespace(&p, NULL));
assert_se(r == 0);
assert_se(setresuid(UID_NOBODY, UID_NOBODY, UID_NOBODY) >= 0); ASSERT_OK_ERRNO(setresuid(UID_NOBODY, UID_NOBODY, UID_NOBODY));
assert_se(open("/dev/kmsg", O_RDONLY | O_CLOEXEC) < 0); ASSERT_ERROR_ERRNO(open("/dev/kmsg", O_RDONLY | O_CLOEXEC), EACCES);
assert_se(errno == EACCES);
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
} }
assert_se(wait_for_terminate_and_check("ns-kernellogs", pid, WAIT_LOG) == EXIT_SUCCESS); ASSERT_OK_EQ(wait_for_terminate_and_check("ns-kernellogs", pid, WAIT_LOG), EXIT_SUCCESS);
} }
TEST(idmapping_supported) { TEST(idmapping_supported) {
assert_se(is_idmapping_supported("/run") >= 0); ASSERT_OK(is_idmapping_supported("/run"));
assert_se(is_idmapping_supported("/var/lib") >= 0); ASSERT_OK(is_idmapping_supported("/var/lib"));
assert_se(is_idmapping_supported("/var/cache") >= 0); ASSERT_OK(is_idmapping_supported("/var/cache"));
assert_se(is_idmapping_supported("/var/log") >= 0); ASSERT_OK(is_idmapping_supported("/var/log"));
assert_se(is_idmapping_supported("/etc") >= 0); ASSERT_OK(is_idmapping_supported("/etc"));
} }
TEST(namespace_is_init) { TEST(namespace_is_init) {

View File

@ -18,7 +18,7 @@ systemd-analyze time || :
systemd-analyze critical-chain || : systemd-analyze critical-chain || :
# blame # blame
systemd-analyze blame systemd-analyze blame
systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame --no-pager
(! systemd-analyze blame --global) (! systemd-analyze blame --global)
# plot # plot
systemd-analyze plot >/dev/null || : systemd-analyze plot >/dev/null || :
@ -1006,7 +1006,16 @@ systemd-analyze security --threshold=25 --offline=true \
rm /tmp/img/usr/lib/systemd/system/testfile.service rm /tmp/img/usr/lib/systemd/system/testfile.service
if systemd-analyze --version | grep -q -F "+ELFUTILS"; then if systemd-analyze --version | grep -q -F "+ELFUTILS"; then
systemd-analyze inspect-elf /lib/systemd/systemd
systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"' systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"'
# For some unknown reason the .note.dlopen sections are removed when building with sanitizers, so only
# run this test if we're not running under sanitizers.
if [[ ! -v ASAN_OPTIONS ]]; then
shared="$(ldd /lib/systemd/systemd | grep shared | cut -d' ' -f3)"
systemd-analyze dlopen-metadata "$shared"
systemd-analyze dlopen-metadata --json=short "$shared"
fi
fi fi
systemd-analyze --threshold=90 security systemd-journald.service systemd-analyze --threshold=90 security systemd-journald.service