]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #21632 from yuwata/network-dhcp6pd-fix-typo-and-split
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 6 Dec 2021 12:24:47 +0000 (21:24 +0900)
committerGitHub <noreply@github.com>
Mon, 6 Dec 2021 12:24:47 +0000 (21:24 +0900)
network: dhcp6pd: fix typo and split file

21 files changed:
.lgtm/cpp-queries/UninitializedVariableWithCleanup.ql
man/kernel-install.xml
man/sd_bus_get_fd.xml
man/sd_uid_get_state.xml
man/systemd-analyze.xml
man/systemd.network.xml
meson.build
shell-completion/bash/systemd-analyze
shell-completion/zsh/_systemd-analyze
src/analyze/analyze-elf.c [new file with mode: 0644]
src/analyze/analyze-elf.h [new file with mode: 0644]
src/analyze/analyze.c
src/analyze/meson.build
src/login/70-uaccess.rules.in
src/network/networkd-address-generation.c
src/shared/elf-util.c
src/systemctl/systemctl-set-environment.c
test/test-network/systemd-networkd-tests.py
test/units/testsuite-26.sh
test/units/testsuite-65.sh
units/systemd-journal-flush.service

index 6bf0ae01ebb0b807b6508dafe4d80934d3079112..6b3b62f8bc9dc598e2c3d50f9067cef660409a05 100644 (file)
 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"
 }
 
 /**
@@ -40,11 +29,11 @@ predicate allocatedType(Type t) {
  */
 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 {
@@ -69,7 +58,29 @@ 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())
+      )
+    )
   }
 }
 
index 37eefe256e90b309256d19f4f0a71e11dfbfde2a..88208250e8824834dea16307898e9126d6ff502b 100644 (file)
       </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>
index 2c0ec8fc055eb588ee782049059cf2684a2c03d5..a8a16159902a786997646d40757953259a679e4d 100644 (file)
     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>
index 2d6fb0c8a347eedadd6cc60fceecfe59cd816570..087a2d539d0ada267b2625b3b2a22424e8b1014a 100644 (file)
     <parameter>require_active</parameter> parameter controls whether
     the returned list shall consist of only those sessions where the
     user is currently active (&gt; 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 (&lt; 0). The call returns a
     <constant>NULL</constant> terminated string array of session
     identifiers in <parameter>sessions</parameter> which needs to be
index 6482fcfe485dc651a06323e571d231f015ef789d..8bc67a1ea89b4ce77ef50edfa3ca5bd8cee88e9d 100644 (file)
@@ -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>
index 82d50c9569ebe2d8b8ef65761ac88020b8a82c34..0714ebcd772dfe50d4ab7a185642cc9d767de030 100644 (file)
@@ -2208,7 +2208,8 @@ Table=1234</programlisting></para>
               <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>
@@ -2267,8 +2268,9 @@ Table=1234</programlisting></para>
 
           <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>
index 60ed4d454e0060c7cb5a741ff4c12b54ea219a5e..43f5bf2c5ab080ddfaa9c5421ea5b59c59ca4e0b 100644 (file)
@@ -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 = []
index ddee57b0e71db3bb4d8440674e45d77363d43889..f6dc972f031f234a6b452ed89bf72fc3724346bd 100644 (file)
@@ -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") )
index 639964f064583c9ba896cb113cfd24798180199c..9c33d73f981e36f0b9b3a56bbe9936290bae60a1 100644 (file)
@@ -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
         )
 
diff --git a/src/analyze/analyze-elf.c b/src/analyze/analyze-elf.c
new file mode 100644 (file)
index 0000000..741cd20
--- /dev/null
@@ -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;
+}
diff --git a/src/analyze/analyze-elf.h b/src/analyze/analyze-elf.h
new file mode 100644 (file)
index 0000000..e0d4712
--- /dev/null
@@ -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);
index d5057bbe85baecb027dc9a634f64a5c4c0086b63..2293fcea6a4b0a3b10a66d94e81da0694ee65280 100644 (file)
@@ -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      },
                 {}
         };
 
index f796629cc2802204d7ffb686e60dba5ccee6a611..492b79069fbaec6e426b3c9b5da429c423e4f1ab 100644 (file)
@@ -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
index 2e29846e113518a10cafe04d5c6e7480d89799c0..052194b6c935bbba23f152b74f374628e5f32001 100644 (file)
@@ -84,4 +84,7 @@ ENV{ID_MAKER_TOOL}=="?*", TAG+="uaccess"
 # Protocol analyzers
 ENV{ID_SIGNAL_ANALYZER}=="?*", ENV{DEVTYPE}=="usb_device", TAG+="uaccess"
 
+# rfkill / radio killswitches
+KERNEL=="rfkill", SUBSYSTEM=="misc", TAG+="uaccess"
+
 LABEL="uaccess_end"
index 0d7d3ee6534a349e533bc39e0e2ed48238f98cc0..1e119732c50e58d45e73b9930cbbe41cc37d93d6 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "sd-id128.h"
 
+#include "arphrd-util.h"
 #include "id128-util.h"
 #include "memory-util.h"
 #include "networkd-address-generation.h"
@@ -39,17 +40,19 @@ typedef struct IPv6Token {
         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];
@@ -59,9 +62,15 @@ static void generate_eui64_address(const Link *link, const struct in6_addr *pref
                 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) {
@@ -188,7 +197,8 @@ static int generate_addresses(
 
                 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:
@@ -226,7 +236,12 @@ static int generate_addresses(
                 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)
index 084920a501556d6c6fec5c61d7717a1004175f00..dafd219bb2553ca61e7676003af901391982a989 100644 (file)
@@ -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;
 
index 4c8d1acba13ddb75d1568fca54759270dfec2976..aab0fe5fd027e98bf684e33a93c6f050726acf28 100644 (file)
@@ -8,6 +8,40 @@
 #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;
@@ -46,13 +80,24 @@ int show_environment(int argc, char *argv[], void *userdata) {
         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)
index b8655db370bf786153d4442328aa472a56f07311..0e0e6987d74838edadb83f1cb1e78619c6b839b6 100755 (executable)
@@ -547,9 +547,9 @@ def remove_dnsmasq_log_file():
     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)
 
@@ -5047,9 +5047,9 @@ class NetworkdDHCP6PDTests(unittest.TestCase, Utilities):
                                         '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')
index 7df0d1dc8f3bfab4ff4c37c888f0c9f8a44733e7..ad084153174ee470815a340e2de05000c672f632 100755 (executable)
@@ -19,6 +19,9 @@ systemctl daemon-reload
 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
 
index 867a77a1b7c569db58f0cc38a48e9551553f9087..dcd11161f491c11cb95480d2cec52b5a3a5a2a3a 100755 (executable)
@@ -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
index 6423f6ea80c848e2de745261130cf43374484cd0..6efb8734a77cf9bdb435638a1da1c2925c005931 100644 (file)
@@ -11,7 +11,6 @@
 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