mirror of
https://github.com/systemd/systemd.git
synced 2025-06-02 17:07:47 +03:00
Merge pull request #21454 from bluca/inspect_elf
analyze: add inspect-elf verb to parse package metadata
This commit is contained in:
commit
ec1574cd8e
@ -681,6 +681,39 @@ $ systemd-analyze verify /tmp/source:alias.service
|
||||
</programlisting>
|
||||
</example>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title><command>systemd-analyze inspect-elf <replaceable>FILE</replaceable>...</command></title>
|
||||
|
||||
<para>This command will load the specified file(s), and if they are ELF objects (executables,
|
||||
libraries, core files, etc.) it will parse the embedded packaging metadata, if any, and print
|
||||
it in a table or json format. See the <ulink url="https://systemd.io/COREDUMP_PACKAGE_METADATA/">
|
||||
Packaging Metadata</ulink> documentation for more information.</para>
|
||||
|
||||
<example>
|
||||
<title>Table output</title>
|
||||
|
||||
<programlisting>$ systemd-analyze inspect-elf --json=pretty /tmp/core.fsverity.1000.f77dac5dc161402aa44e15b7dd9dcf97.58561.1637106137000000
|
||||
{
|
||||
"elfType" : "coredump",
|
||||
"elfArchitecture" : "AMD x86-64",
|
||||
"/home/bluca/git/fsverity-utils/fsverity" : {
|
||||
"type" : "deb",
|
||||
"name" : "fsverity-utils",
|
||||
"version" : "1.3-1",
|
||||
"buildId" : "7c895ecd2a271f93e96268f479fdc3c64a2ec4ee"
|
||||
},
|
||||
"/home/bluca/git/fsverity-utils/libfsverity.so.0" : {
|
||||
"type" : "deb",
|
||||
"name" : "fsverity-utils",
|
||||
"version" : "1.3-1",
|
||||
"buildId" : "b5e428254abf14237b0ae70ed85fffbb98a78f88"
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -1338,6 +1338,10 @@ if want_elfutils != 'false' and not skip_deps
|
||||
libdw = dependency('libdw',
|
||||
required : want_elfutils == 'true')
|
||||
have = libdw.found()
|
||||
|
||||
# New in elfutils 0.177
|
||||
conf.set10('HAVE_DWELF_ELF_E_MACHINE_STRING',
|
||||
have and cc.has_function('dwelf_elf_e_machine_string', dependencies : libdw))
|
||||
else
|
||||
have = false
|
||||
libdw = []
|
||||
|
@ -63,6 +63,7 @@ _systemd_analyze() {
|
||||
[CAT_CONFIG]='cat-config'
|
||||
[SECURITY]='security'
|
||||
[CONDITION]='condition'
|
||||
[INSPECT_ELF]='inspect-elf'
|
||||
)
|
||||
|
||||
local CONFIGS='systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
|
||||
@ -169,6 +170,14 @@ _systemd_analyze() {
|
||||
fi
|
||||
comps=$( __get_services $mode )
|
||||
fi
|
||||
|
||||
elif __contains_word "$verb" ${VERBS[INSPECT_ELF]}; then
|
||||
if [[ $cur = -* ]]; then
|
||||
comps='--help --version --json=off --json=pretty --json=short'
|
||||
else
|
||||
comps=$( compgen -A file -- "$cur" )
|
||||
compopt -o filenames
|
||||
fi
|
||||
fi
|
||||
|
||||
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
|
||||
|
@ -54,6 +54,7 @@
|
||||
'timestamp:Parse a systemd syntax timestamp'
|
||||
'timespan:Parse a systemd syntax timespan'
|
||||
'security:Analyze security settings of a service'
|
||||
'inspect-elf:Parse and print ELF package metadata'
|
||||
# log-level, log-target, service-watchdogs have been deprecated
|
||||
)
|
||||
|
||||
|
128
src/analyze/analyze-elf.c
Normal file
128
src/analyze/analyze-elf.c
Normal file
@ -0,0 +1,128 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "analyze-elf.h"
|
||||
#include "elf-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-table.h"
|
||||
#include "format-util.h"
|
||||
#include "json.h"
|
||||
#include "path-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
int analyze_elf(char **filenames, JsonFormatFlags json_flags) {
|
||||
char **filename;
|
||||
int r;
|
||||
|
||||
STRV_FOREACH(filename, filenames) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
|
||||
_cleanup_(table_unrefp) Table *t = NULL;
|
||||
_cleanup_free_ char *abspath = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
r = path_make_absolute_cwd(*filename, &abspath);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not make an absolute path out of \"%s\": %m", *filename);
|
||||
|
||||
path_simplify(abspath);
|
||||
|
||||
fd = RET_NERRNO(open(abspath, O_RDONLY|O_CLOEXEC));
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Could not open \"%s\": %m", abspath);
|
||||
|
||||
r = parse_elf_object(fd, abspath, /* fork_disable_dump= */false, NULL, &package_metadata);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath);
|
||||
|
||||
t = table_new("", "");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
r = table_set_align_percent(t, TABLE_HEADER_CELL(0), 100);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
r = table_add_many(
|
||||
t,
|
||||
TABLE_STRING, "path:",
|
||||
TABLE_STRING, abspath);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
if (package_metadata) {
|
||||
JsonVariant *module_json;
|
||||
const char *module_name;
|
||||
|
||||
JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, package_metadata) {
|
||||
const char *field_name;
|
||||
JsonVariant *field;
|
||||
|
||||
/* The ELF type and architecture are added as top-level objects,
|
||||
* since they are only parsed for the file itself, but the packaging
|
||||
* metadata is parsed recursively in core files, so there might be
|
||||
* multiple modules. */
|
||||
if (STR_IN_SET(module_name, "elfType", "elfArchitecture")) {
|
||||
_cleanup_free_ char *suffixed = NULL;
|
||||
|
||||
suffixed = strjoin(module_name, ":");
|
||||
if (!suffixed)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(
|
||||
t,
|
||||
TABLE_STRING, suffixed,
|
||||
TABLE_STRING, json_variant_string(module_json));
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* path/elfType/elfArchitecture come first just once per file,
|
||||
* then we might have multiple modules, so add a separator between
|
||||
* them to make the output more readable. */
|
||||
r = table_add_many(t, TABLE_EMPTY, TABLE_EMPTY);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
/* In case of core files the module name will be the executable,
|
||||
* but for binaries/libraries it's just the path, so don't print it
|
||||
* twice. */
|
||||
if (!streq(abspath, module_name)) {
|
||||
r = table_add_many(
|
||||
t,
|
||||
TABLE_STRING, "module name:",
|
||||
TABLE_STRING, module_name);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
|
||||
JSON_VARIANT_OBJECT_FOREACH(field_name, field, module_json)
|
||||
if (json_variant_is_string(field)) {
|
||||
_cleanup_free_ char *suffixed = NULL;
|
||||
|
||||
suffixed = strjoin(field_name, ":");
|
||||
if (!suffixed)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(
|
||||
t,
|
||||
TABLE_STRING, suffixed,
|
||||
TABLE_STRING, json_variant_string(field));
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (json_flags & JSON_FORMAT_OFF) {
|
||||
(void) table_set_header(t, true);
|
||||
|
||||
r = table_print(t, NULL);
|
||||
if (r < 0)
|
||||
return table_log_print_error(r);
|
||||
} else
|
||||
json_variant_dump(package_metadata, json_flags, stdout, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
6
src/analyze/analyze-elf.h
Normal file
6
src/analyze/analyze-elf.h
Normal file
@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "json.h"
|
||||
|
||||
int analyze_elf(char **filenames, JsonFormatFlags json_flags);
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "analyze-condition.h"
|
||||
#include "analyze-elf.h"
|
||||
#include "analyze-security.h"
|
||||
#include "analyze-verify.h"
|
||||
#include "bus-error.h"
|
||||
@ -2431,6 +2432,12 @@ static int do_security(int argc, char *argv[], void *userdata) {
|
||||
/*flags=*/ 0);
|
||||
}
|
||||
|
||||
static int do_elf_inspection(int argc, char *argv[], void *userdata) {
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
return analyze_elf(strv_skip(argv, 1), arg_json_format_flags);
|
||||
}
|
||||
|
||||
static int help(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ char *link = NULL, *dot_link = NULL;
|
||||
int r;
|
||||
@ -2473,6 +2480,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" timestamp TIMESTAMP... Validate a timestamp\n"
|
||||
" timespan SPAN... Validate a time span\n"
|
||||
" security [UNIT...] Analyze security of unit\n"
|
||||
" inspect-elf FILE... Parse and print ELF package metadata\n"
|
||||
"\nOptions:\n"
|
||||
" --recursive-errors=MODE Control which units are verified\n"
|
||||
" --offline=BOOL Perform a security review on unit file(s)\n"
|
||||
@ -2759,7 +2767,7 @@ 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 && !streq_ptr(argv[optind], "security"))
|
||||
if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Option --json= is only supported for security right now.");
|
||||
|
||||
@ -2835,6 +2843,7 @@ static int run(int argc, char *argv[]) {
|
||||
{ "timestamp", 2, VERB_ANY, 0, test_timestamp },
|
||||
{ "timespan", 2, VERB_ANY, 0, dump_timespan },
|
||||
{ "security", VERB_ANY, VERB_ANY, 0, do_security },
|
||||
{ "inspect-elf", 2, VERB_ANY, 0, do_elf_inspection },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,8 @@ systemd_analyze_sources = files('''
|
||||
analyze.c
|
||||
analyze-condition.c
|
||||
analyze-condition.h
|
||||
analyze-elf.c
|
||||
analyze-elf.h
|
||||
analyze-verify.c
|
||||
analyze-verify.h
|
||||
analyze-security.c
|
||||
|
@ -40,6 +40,9 @@ const char *(*sym_dwarf_formstring)(Dwarf_Attribute *);
|
||||
int (*sym_dwarf_getscopes)(Dwarf_Die *, Dwarf_Addr, Dwarf_Die **);
|
||||
int (*sym_dwarf_getscopes_die)(Dwarf_Die *, Dwarf_Die **);
|
||||
Elf *(*sym_dwelf_elf_begin)(int);
|
||||
#if HAVE_DWELF_ELF_E_MACHINE_STRING
|
||||
const char *(*sym_dwelf_elf_e_machine_string)(int);
|
||||
#endif
|
||||
ssize_t (*sym_dwelf_elf_gnu_build_id)(Elf *, const void **);
|
||||
int (*sym_dwarf_tag)(Dwarf_Die *);
|
||||
Dwfl_Module *(*sym_dwfl_addrmodule)(Dwfl *, Dwarf_Addr);
|
||||
@ -90,6 +93,9 @@ static int dlopen_dw(void) {
|
||||
DLSYM_ARG(dwarf_diename),
|
||||
DLSYM_ARG(dwelf_elf_gnu_build_id),
|
||||
DLSYM_ARG(dwelf_elf_begin),
|
||||
#if HAVE_DWELF_ELF_E_MACHINE_STRING
|
||||
DLSYM_ARG(dwelf_elf_e_machine_string),
|
||||
#endif
|
||||
DLSYM_ARG(dwfl_addrmodule),
|
||||
DLSYM_ARG(dwfl_frame_pc),
|
||||
DLSYM_ARG(dwfl_module_addrdie),
|
||||
@ -260,7 +266,8 @@ static int thread_callback(Dwfl_Thread *thread, void *userdata) {
|
||||
return DWARF_CB_OK;
|
||||
}
|
||||
|
||||
static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *elf, StackContext *c) {
|
||||
static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *elf, bool *ret_interpreter_found, StackContext *c) {
|
||||
bool interpreter_found = false;
|
||||
size_t n_program_headers;
|
||||
int r;
|
||||
|
||||
@ -286,9 +293,14 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e
|
||||
|
||||
/* Package metadata is in PT_NOTE headers. */
|
||||
program_header = sym_gelf_getphdr(elf, i, &mem);
|
||||
if (!program_header || program_header->p_type != PT_NOTE)
|
||||
if (!program_header || (program_header->p_type != PT_NOTE && program_header->p_type != PT_INTERP))
|
||||
continue;
|
||||
|
||||
if (program_header->p_type == PT_INTERP) {
|
||||
interpreter_found = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fortunately there is an iterator we can use to walk over the
|
||||
* elements of a PT_NOTE program header. We are interested in the
|
||||
* note with type. */
|
||||
@ -348,11 +360,17 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "set_put_strdup failed: %m");
|
||||
|
||||
if (ret_interpreter_found)
|
||||
*ret_interpreter_found = interpreter_found;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret_interpreter_found)
|
||||
*ret_interpreter_found = interpreter_found;
|
||||
|
||||
/* Didn't find package metadata for this module - that's ok, just go to the next. */
|
||||
return 0;
|
||||
}
|
||||
@ -426,7 +444,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. */
|
||||
elf = sym_dwfl_module_getelf(mod, &bias);
|
||||
if (elf) {
|
||||
r = parse_package_metadata(name, id_json, elf, c);
|
||||
r = parse_package_metadata(name, id_json, elf, NULL, c);
|
||||
if (r < 0)
|
||||
return DWARF_CB_ABORT;
|
||||
if (r > 0)
|
||||
@ -468,7 +486,7 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name,
|
||||
_cleanup_(sym_elf_endp) Elf *memelf = sym_elf_memory(data->d_buf, data->d_size);
|
||||
if (!memelf)
|
||||
continue;
|
||||
r = parse_package_metadata(name, id_json, memelf, c);
|
||||
r = parse_package_metadata(name, id_json, memelf, NULL, c);
|
||||
if (r < 0)
|
||||
return DWARF_CB_ABORT;
|
||||
if (r > 0)
|
||||
@ -546,6 +564,119 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant **
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_elf(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL, *elf_metadata = NULL;
|
||||
_cleanup_(set_freep) Set *modules = NULL;
|
||||
_cleanup_free_ char *buf = NULL; /* buf should be freed last, c.f closed first (via stack_context_destroy) */
|
||||
_cleanup_(stack_context_destroy) StackContext c = {
|
||||
.package_metadata = &package_metadata,
|
||||
.modules = &modules,
|
||||
};
|
||||
const char *elf_architecture = NULL, *elf_type;
|
||||
GElf_Ehdr elf_header;
|
||||
size_t sz = 0;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
|
||||
return log_warning_errno(errno, "Failed to seek to beginning of the ELF file: %m");
|
||||
|
||||
if (ret) {
|
||||
c.f = open_memstream_unlocked(&buf, &sz);
|
||||
if (!c.f)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
sym_elf_version(EV_CURRENT);
|
||||
|
||||
c.elf = sym_elf_begin(fd, ELF_C_READ_MMAP, NULL);
|
||||
if (!c.elf)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse ELF file, elf_begin() failed: %s", sym_elf_errmsg(sym_elf_errno()));
|
||||
|
||||
if (!sym_gelf_getehdr(c.elf, &elf_header))
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse ELF file, gelf_getehdr() failed: %s", sym_elf_errmsg(sym_elf_errno()));
|
||||
|
||||
if (elf_header.e_type == ET_CORE) {
|
||||
_cleanup_free_ char *out = NULL;
|
||||
|
||||
r = parse_core(fd, executable, ret ? &out : NULL, &package_metadata);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to inspect core file: %m");
|
||||
|
||||
if (out)
|
||||
fprintf(c.f, "%s", out);
|
||||
|
||||
elf_type = "coredump";
|
||||
} else {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *id_json = NULL;
|
||||
bool interpreter_found = false;
|
||||
|
||||
r = parse_buildid(NULL, c.elf, executable, &c, &id_json);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to parse build-id of ELF file: %m");
|
||||
|
||||
r = parse_package_metadata(executable, id_json, c.elf, &interpreter_found, &c);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to parse package metadata of ELF file: %m");
|
||||
|
||||
/* If we found a build-id and nothing else, return at least that. */
|
||||
if (!package_metadata && id_json) {
|
||||
r = json_build(&package_metadata, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(executable, JSON_BUILD_VARIANT(id_json))));
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to build JSON object: %m");
|
||||
}
|
||||
|
||||
if (interpreter_found)
|
||||
elf_type = "executable";
|
||||
else
|
||||
elf_type = "library";
|
||||
}
|
||||
|
||||
/* Note that e_type is always DYN for both executables and libraries, so we can't tell them apart from the header,
|
||||
* but we will search for the PT_INTERP section when parsing the metadata. */
|
||||
r = json_build(&elf_metadata, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("elfType", JSON_BUILD_STRING(elf_type))));
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to build JSON object: %m");
|
||||
|
||||
#if HAVE_DWELF_ELF_E_MACHINE_STRING
|
||||
elf_architecture = sym_dwelf_elf_e_machine_string(elf_header.e_machine);
|
||||
#endif
|
||||
if (elf_architecture) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *json_architecture = NULL;
|
||||
|
||||
r = json_build(&json_architecture,
|
||||
JSON_BUILD_OBJECT(JSON_BUILD_PAIR("elfArchitecture", JSON_BUILD_STRING(elf_architecture))));
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to build JSON object: %m");
|
||||
|
||||
r = json_variant_merge(&elf_metadata, json_architecture);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to merge JSON objects: %m");
|
||||
|
||||
if (ret)
|
||||
fprintf(c.f, "ELF object binary architecture: %s\n", elf_architecture);
|
||||
}
|
||||
|
||||
/* We always at least have the ELF type, so merge that (and possibly the arch). */
|
||||
r = json_variant_merge(&elf_metadata, package_metadata);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to merge JSON objects: %m");
|
||||
|
||||
if (ret) {
|
||||
r = fflush_and_check(c.f);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Could not parse ELF file, flushing file buffer failed: %m");
|
||||
|
||||
c.f = safe_fclose(c.f);
|
||||
*ret = TAKE_PTR(buf);
|
||||
}
|
||||
if (ret_package_metadata)
|
||||
*ret_package_metadata = TAKE_PTR(elf_metadata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, char **ret, JsonVariant **ret_package_metadata) {
|
||||
_cleanup_close_pair_ int error_pipe[2] = { -1, -1 }, return_pipe[2] = { -1, -1 }, json_pipe[2] = { -1, -1 };
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
|
||||
@ -610,7 +741,7 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha
|
||||
goto child_fail;
|
||||
}
|
||||
|
||||
r = parse_core(fd, executable, ret ? &buf : NULL, ret_package_metadata ? &package_metadata : NULL);
|
||||
r = parse_elf(fd, executable, ret ? &buf : NULL, ret_package_metadata ? &package_metadata : NULL);
|
||||
if (r < 0)
|
||||
goto child_fail;
|
||||
|
||||
|
@ -596,6 +596,10 @@ set -e
|
||||
|
||||
rm /tmp/img/usr/lib/systemd/system/testfile.service
|
||||
|
||||
if systemd-analyze --version | grep -q -F "+ELFUTILS"; then
|
||||
systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"'
|
||||
fi
|
||||
|
||||
systemd-analyze log-level info
|
||||
|
||||
echo OK >/testok
|
||||
|
Loading…
x
Reference in New Issue
Block a user