import cpp
import semmle.code.cpp.controlflow.StackVariableReachability
-/**
- * Auxiliary predicate: Types that don't require initialization
- * before they are used, since they're stack-allocated.
- */
-predicate allocatedType(Type t) {
- /* Arrays: "int foo[1]; foo[0] = 42;" is ok. */
- t instanceof ArrayType
- or
- /* Structs: "struct foo bar; bar.baz = 42" is ok. */
- t instanceof Class
- or
- /* Typedefs to other allocated types are fine. */
- allocatedType(t.(TypedefType).getUnderlyingType())
- or
- /* Type specifiers don't affect whether or not a type is allocated. */
- allocatedType(t.getUnspecifiedType())
+/** Auxiliary predicate: List cleanup functions we want to explicitly ignore
+ * since they don't do anything illegal even when the variable is uninitialized
+ */
+predicate cleanupFunctionDenyList(string fun) {
+ fun = "erase_char"
}
/**
*/
DeclStmt declWithNoInit(LocalVariable v) {
result.getADeclaration() = v and
- not exists(v.getInitializer()) and
+ not v.hasInitializer() and
/* The variable has __attribute__((__cleanup__(...))) set */
v.getAnAttribute().hasName("cleanup") and
- /* The type of the variable is not stack-allocated. */
- exists(Type t | t = v.getType() | not allocatedType(t))
+ /* Check if the cleanup function is not on a deny list */
+ not cleanupFunctionDenyList(v.getAnAttribute().getAnArgument().getValueText())
}
class UninitialisedLocalReachability extends StackVariableReachability {
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
// only report the _first_ possibly uninitialized use
useOfVar(v, node) or
- definitionBarrier(v, node)
+ (
+ /* If there's an return statement somewhere between the variable declaration
+ * and a possible definition, don't accept is as a valid initialization.
+ *
+ * E.g.:
+ * _cleanup_free_ char *x;
+ * ...
+ * if (...)
+ * return;
+ * ...
+ * x = malloc(...);
+ *
+ * is not a valid initialization, since we might return from the function
+ * _before_ the actual iniitialization (emphasis on _might_, since we
+ * don't know if the return statement might ever evaluate to true).
+ */
+ definitionBarrier(v, node) and
+ not exists(ReturnStmt rs |
+ /* The attribute check is "just" a complexity optimization */
+ v.getFunction() = rs.getEnclosingFunction() and v.getAnAttribute().hasName("cleanup") |
+ rs.getLocation().isBefore(node.getLocation())
+ )
+ )
}
}
</varlistentry>
<varlistentry>
<term>
+ <filename>/usr/lib/kernel/cmdline</filename>
<filename>/etc/kernel/cmdline</filename>
<filename>/proc/cmdline</filename>
</term>
<listitem>
<para>Read by <filename>90-loaderentry.install</filename>. The content of the file
<filename>/etc/kernel/cmdline</filename> specifies the kernel command line to use. If that file does not
- exist, <filename>/proc/cmdline</filename> is used.</para>
+ exist, <filename>/usr/lib/kernel/cmdline</filename> is used. If that also does not exist,
+ <filename>/proc/cmdline</filename> is used.</para>
</listitem>
</varlistentry>
<varlistentry>
</term>
<listitem>
<para>The content of this file specifies the machine identification
- <replaceable>MACHINE-ID</replaceable>. If it cannot read <filename>/etc/machine-id</filename>,
- kernel-install will use "Linux" as the machine ID instead.</para>
+ <replaceable>MACHINE-ID</replaceable>. If <filename>$BOOT/Default</filename> exists,
+ or <filename>/etc/machine-id</filename> doesn't, <command>kernel-install</command>
+ will use the literal <literal>Default</literal> as the machine ID instead.</para>
</listitem>
</varlistentry>
<varlistentry>
<filename>/usr/lib/os-release</filename>
</term>
<listitem>
- <para>The content of the file specifies the operating system title <replaceable>PRETTY_NAME</replaceable>.</para>
+ <para>Read by <filename>90-loaderentry.install</filename>.
+ If available, <varname>PRETTY_NAME</varname> is read from these files and used as the title of the boot menu entry.
+ Otherwise, <literal>Linux <replaceable>KERNEL-VERSION</replaceable></literal> will be used.</para>
</listitem>
</varlistentry>
</variablelist>
without any applied timeout. Note that the returned timeout should be considered only a
maximum sleeping time. It is permissible (and even expected) that shorter timeouts are used by
the calling program, in case other event sources are polled in the same event loop. Note that
- the returned time-value is relative and specified in microseconds. When converting this value in
- order to pass it as third argument to <function>poll()</function> (which expects milliseconds),
- care should be taken to use a division that rounds up to ensure the I/O polling operation
+ the returned time-value is absolute, based of <constant>CLOCK_MONOTONIC</constant> and specified
+ in microseconds. When converting this value in order to pass it as third argument to
+ <function>poll()</function> (which expects relative milliseconds), care should be taken to convert
+ to a relative time and use a division that rounds up to ensure the I/O polling operation
doesn't sleep for shorter than necessary, which might result in unintended busy looping
(alternatively, use
<citerefentry project='man-pages'><refentrytitle>ppoll</refentrytitle><manvolnum>2</manvolnum></citerefentry>
<parameter>require_active</parameter> parameter controls whether
the returned list shall consist of only those sessions where the
user is currently active (> 0), where the user is currently
- online but possibly inactive (= 0), or logged in at all but
+ online but possibly inactive (= 0), or logged in but
possibly closing the session (< 0). The call returns a
<constant>NULL</constant> terminated string array of session
identifiers in <parameter>sessions</parameter> which needs to be
</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>
<term><option>eui64</option></term>
<listitem>
<para>
- The EUI-64 algorithm will be used to generate an address for that prefix.
+ The EUI-64 algorithm will be used to generate an address for that prefix. Only
+ supported by Ethernet or InfiniBand interfaces.
</para>
</listitem>
</varlistentry>
<para>If no address generation mode is specified (which is the default), or a received
prefix does not match any of the addresses provided in <literal>prefixstable</literal>
- mode, then the EUI-64 algorithm will be used to form an interface identifier for that
- prefix.</para>
+ mode, then the EUI-64 algorithm will be used for Ethernet or InfiniBand interfaces,
+ otherwise <literal>prefixstable</literal> will be used to form an interface identifier for
+ that prefix.</para>
<para>This setting can be specified multiple times. If an empty string is assigned, then
the all previous assignments are cleared.</para>
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 = []
[CAT_CONFIG]='cat-config'
[SECURITY]='security'
[CONDITION]='condition'
+ [INSPECT_ELF]='inspect-elf'
)
local CONFIGS='systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
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") )
'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
)
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "json.h"
+
+int analyze_elf(char **filenames, JsonFormatFlags json_flags);
#include "alloc-util.h"
#include "analyze-condition.h"
+#include "analyze-elf.h"
#include "analyze-security.h"
#include "analyze-verify.h"
#include "bus-error.h"
/*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;
" 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"
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.");
{ "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 },
{}
};
analyze.c
analyze-condition.c
analyze-condition.h
+ analyze-elf.c
+ analyze-elf.h
analyze-verify.c
analyze-verify.h
analyze-security.c
# Protocol analyzers
ENV{ID_SIGNAL_ANALYZER}=="?*", ENV{DEVTYPE}=="usb_device", TAG+="uaccess"
+# rfkill / radio killswitches
+KERNEL=="rfkill", SUBSYSTEM=="misc", TAG+="uaccess"
+
LABEL="uaccess_end"
#include "sd-id128.h"
+#include "arphrd-util.h"
#include "id128-util.h"
#include "memory-util.h"
#include "networkd-address-generation.h"
sd_id128_t secret_key;
} IPv6Token;
-static void generate_eui64_address(const Link *link, const struct in6_addr *prefix, struct in6_addr *ret) {
+static int generate_eui64_address(const Link *link, const struct in6_addr *prefix, struct in6_addr *ret) {
assert(link);
assert(prefix);
assert(ret);
memcpy(ret->s6_addr, prefix, 8);
- if (link->iftype == ARPHRD_INFINIBAND)
+ switch (link->iftype) {
+ case ARPHRD_INFINIBAND:
/* Use last 8 byte. See RFC4391 section 8 */
memcpy(&ret->s6_addr[8], &link->hw_addr.infiniband[INFINIBAND_ALEN - 8], 8);
- else {
+ break;
+ case ARPHRD_ETHER:
/* see RFC4291 section 2.5.1 */
ret->s6_addr[8] = link->hw_addr.ether.ether_addr_octet[0];
ret->s6_addr[9] = link->hw_addr.ether.ether_addr_octet[1];
ret->s6_addr[13] = link->hw_addr.ether.ether_addr_octet[3];
ret->s6_addr[14] = link->hw_addr.ether.ether_addr_octet[4];
ret->s6_addr[15] = link->hw_addr.ether.ether_addr_octet[5];
+ break;
+ default:
+ return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
+ "Token=eui64 is not supported for interface type %s, ignoring.",
+ strna(arphrd_to_name(link->iftype)));
}
ret->s6_addr[8] ^= 1 << 1;
+ return 0;
}
static bool stable_private_address_is_valid(const struct in6_addr *addr) {
switch (j->type) {
case ADDRESS_GENERATION_EUI64:
- generate_eui64_address(link, &masked, &addr);
+ if (generate_eui64_address(link, &masked, &addr) < 0)
+ continue;
break;
case ADDRESS_GENERATION_STATIC:
if (!addr)
return -ENOMEM;
- generate_eui64_address(link, &masked, addr);
+ if (IN_SET(link->iftype, ARPHRD_ETHER, ARPHRD_INFINIBAND))
+ r = generate_eui64_address(link, &masked, addr);
+ else
+ r = generate_stable_private_address(link, app_id, &SD_ID128_NULL, &masked, addr);
+ if (r < 0)
+ return r;
r = set_ensure_consume(&addresses, &in6_addr_hash_ops_free, addr);
if (r < 0)
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);
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),
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;
/* 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. */
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;
}
* 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)
_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)
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;
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;
#include "systemctl-util.h"
#include "systemctl.h"
+static int json_transform_message(sd_bus_message *m, JsonVariant **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ char *text;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &text)) > 0) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+
+ char *sep = strchr(text, '=');
+ if (!sep)
+ return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN),
+ "Invalid environment block");
+
+ *sep++ = '\0';
+
+ r = json_build(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(text, JSON_BUILD_STRING(sep))));
+ if (r < 0)
+ return r;
+
+ r = json_variant_merge(&v, w);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ *ret = TAKE_PTR(v);
+
+ return r;
+}
+
static int print_variable(const char *s) {
const char *sep;
_cleanup_free_ char *esc = NULL;
if (r < 0)
return bus_log_parse_error(r);
- while ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &text)) > 0) {
- r = print_variable(text);
+ if (OUTPUT_MODE_IS_JSON(arg_output)) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ JsonFormatFlags flags = output_mode_to_json_format_flags(arg_output);
+
+ r = json_transform_message(reply, &v);
if (r < 0)
return r;
+
+ json_variant_dump(v, flags, stdout, NULL);
+ } else {
+ while ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &text)) > 0) {
+ r = print_variable(text);
+ if (r < 0)
+ return r;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
}
- if (r < 0)
- return bus_log_parse_error(r);
r = sd_bus_message_exit_container(reply);
if (r < 0)
if os.path.exists(dnsmasq_log_file):
os.remove(dnsmasq_log_file)
-def start_isc_dhcpd(interface, conf_file):
+def start_isc_dhcpd(interface, conf_file, ip):
conf_file_path = os.path.join(networkd_ci_path, conf_file)
- isc_dhcpd_command = f'dhcpd -6 -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
+ isc_dhcpd_command = f'dhcpd {ip} -cf {conf_file_path} -lf {isc_dhcpd_lease_file} -pf {isc_dhcpd_pid_file} {interface}'
Path(isc_dhcpd_lease_file).touch()
check_output(isc_dhcpd_command)
'13-dummy.netdev', 'dhcp6pd-downstream-dummy99.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
- start_isc_dhcpd('veth-peer', 'isc-dhcpd-dhcp6pd.conf')
- self.wait_online(['veth-peer:routable', 'veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
+ self.wait_online(['veth-peer:routable'])
+ start_isc_dhcpd('veth-peer', 'isc-dhcpd-dhcp6pd.conf', ip='-6')
+ self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
print('### ip -6 address show dev veth-peer scope global')
systemctl show-environment | grep -q '^PATH=.*testaddition$'
systemctl show-environment | grep -q '^FOO=BAR$'
+# Check that JSON output is supported
+systemctl show-environment --output=json | grep -q '^{.*"FOO":"BAR".*}$'
+
# Drop both
systemctl unset-environment FOO PATH
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
Description=Flush Journal to Persistent Storage
Documentation=man:systemd-journald.service(8) man:journald.conf(5)
DefaultDependencies=no
-Requires=systemd-journald.service
After=systemd-journald.service systemd-remount-fs.service
Before=systemd-tmpfiles-setup.service
RequiresMountsFor=/var/log/journal