]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #13216 from poettering/busctl-format-table
authorLennart Poettering <lennart@poettering.net>
Mon, 29 Jul 2019 18:44:54 +0000 (20:44 +0200)
committerGitHub <noreply@github.com>
Mon, 29 Jul 2019 18:44:54 +0000 (20:44 +0200)
port "busctl list" to format-table.h

37 files changed:
NEWS
TODO
man/crypttab.xml
man/systemd-analyze.xml
man/systemd.service.xml
src/analyze/analyze.c
src/basic/time-util.c
src/basic/time-util.h
src/core/dbus-service.c
src/core/execute.c
src/core/load-fragment.c
src/core/main.c
src/core/unit.c
src/cryptsetup/cryptsetup-generator.c
src/cryptsetup/cryptsetup.c
src/login/logind-session-dbus.c
src/network/networkctl.c
src/shared/bitmap.c
src/shared/bitmap.h
src/shared/bus-unit-util.c
src/shared/bus-util.c
src/shared/bus-util.h
src/shared/exit-status.c
src/shared/exit-status.h
src/shared/format-table.c
src/shared/format-table.h
src/systemctl/systemctl.c
src/test/meson.build
src/test/test-exit-status.c [new file with mode: 0644]
src/tty-ask-password-agent/tty-ask-password-agent.c
src/udev/udev-rules.c
test/test-network/systemd-networkd-tests.py
units/systemd-tmpfiles-clean.service.in
units/systemd-tmpfiles-setup-dev.service.in
units/systemd-tmpfiles-setup.service.in
units/user/systemd-tmpfiles-clean.service.in
units/user/systemd-tmpfiles-setup.service.in

diff --git a/NEWS b/NEWS
index 51d4713f2acf7a34e274aecfc26743861a66305b..b26c749a5f8f48a76e42c4da1f47f06fb12e8ff5 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -105,6 +105,11 @@ CHANGES WITH 243 in spe:
           long number (with the length varying by architecture), so they can be
           unambiguously distinguished.
 
+        * SuccessExitStatus=, RestartPreventExitStatus=, and
+          RestartForceExitStatus= now accept exit code names (e.g. "DATAERR" is
+          equivalent to "65"). systemd-analyze learnt a new 'exit-codes' verb
+          to display those exit code name mappings.
+
         * /usr/sbin/halt.local is no longer supported. Implementation in
           distributions was inconsistent and it seems this functionality was
           very rarely used.
diff --git a/TODO b/TODO
index 159d540cea739676bcae95d471403b5615ac0f47..b10eafe66340df80d7beb03bff3262199b4574a8 100644 (file)
--- a/TODO
+++ b/TODO
@@ -83,8 +83,6 @@ Features:
 * make rfkill uaccess controllable by default, i.e. steal rule from
   gnome-bluetooth and friends
 
-* warn if udev rules files are marked executable (docker?)
-
 * tweak journald context caching. In addition to caching per-process attributes
   keyed by PID, cache per-cgroup attributes (i.e. the various xattrs we read)
   keyed by cgroup path, and guarded by ctime changes. This should provide us
@@ -116,9 +114,6 @@ Features:
 
 * when no locale is configured, default to UEFI's PlatformLang variable
 
-* When logind.conf contains HandleLidSwitch=suspend-then-hibernate and we can't
-  hibernate because the swap partition isn't large enough, still suspend
-
 * bootctl,sd-boot: actually honour the "architecture" key
 
 * add a new syscall group "@esoteric" for more esoteric stuff such as bpf() and
@@ -154,7 +149,8 @@ Features:
   non-zero "tries done" count
 
 * maybe set a special xattr on cgroups that have delegate=yes set, to make it
-  easy to mark cut points
+  easy to mark cut points, then use this information in "systemd-cgls" to show
+  them (e.g. color delegated subtrees in a different color)
 
 * introduce an option (or replacement) for "systemctl show" that outputs all
   properties as JSON, similar to busctl's new JSON output. In contrast to that
@@ -235,9 +231,6 @@ Features:
 
 * add --vacuum-xyz options to coredumpctl, matching those journalctl already has.
 
-* SuccessExitStatus= and friends should probably also accept symbolic exit
-  codes names, i.e. error codes from the list maintained in exit-codes.[ch]
-
 * introduce Ephemeral= unit file switch, that creates an ephemeral copy of all
   files and directories that are left writable for a unit, and which are
   removed after the unit goes down again. A bit like --ephemeral for
index 5eb1c12232c784a554362e3908a27f3bed7fdbe4..76eef06bed8d15990e7f3c69391b9763ff501488 100644 (file)
         sequential order.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>keyfile-timeout=</option></term>
+
+        <listitem><para> Specifies the timeout for the device on
+        which the key file resides and falls back to a password if
+        it could not be mounted. See
+        <citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        for key files on external devices.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>luks</option></term>
 
       <programlisting>luks       UUID=2505567a-9e27-4efe-a4d5-15ad146c258b
 swap       /dev/sda7       /dev/urandom       swap
 truecrypt  /dev/sda2       /etc/container_password  tcrypt
-hidden     /mnt/tc_hidden  /dev/null    tcrypt-hidden,tcrypt-keyfile=/etc/keyfile</programlisting>
+hidden     /mnt/tc_hidden  /dev/null    tcrypt-hidden,tcrypt-keyfile=/etc/keyfile
+external   /dev/sda3       keyfile:LABEL=keydev keyfile-timeout=10s</programlisting>
     </example>
   </refsect1>
 
index 7112362ac5b1701dd2e719f859c8ceeb6d137346..8e9f24caacd57725283ad0d0ee21d72083ea3572 100644 (file)
       <arg choice="opt" rep="repeat">OPTIONS</arg>
       <arg choice="plain">unit-paths</arg>
     </cmdsynopsis>
+    <cmdsynopsis>
+      <command>systemd-analyze</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="plain">exit-codes</arg>
+      <arg choice="opt" rep="repeat"><replaceable>CODE</replaceable></arg>
+    </cmdsynopsis>
     <cmdsynopsis>
       <command>systemd-analyze</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
@@ -365,6 +371,30 @@ $ eog targets.svg</programlisting>
       to retrieve the actual list that the manager uses, with any empty directories omitted.</para>
     </refsect2>
 
+    <refsect2>
+      <title><command>systemd-analyze exit-codes <optional><replaceable>CODE</replaceable>...</optional></command></title>
+
+      <para>This command prints a list of exit codes along with their "class", i.e. the source of the
+      definition (one of <literal>glibc</literal>, <literal>systemd</literal>, <literal>LSB</literal>, or
+      <literal>BSD</literal>), see the Process Exit Codes section in
+      <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+      If no additional arguments are specified, all known codes are are shown. Otherwise, only the
+      definitions for the specified codes are shown.</para>
+
+      <example>
+        <title><command>Show some example exit code names</command></title>
+
+        <programlisting>$ systemd-analyze exit-codes 0 1 {63..65}
+NAME    CODE CLASS
+SUCCESS 0    glibc
+FAILURE 1    glibc
+-       63   -
+USAGE   64   BSD
+DATAERR 65   BSD
+</programlisting>
+      </example>
+    </refsect2>
+
     <refsect2>
       <title><command>systemd-analyze condition <replaceable>CONDITION</replaceable>...</command></title>
 
index 90c1257f37eadb68a380408195f0ac6376727c10..40ac052ba5c9b246c42660bbd82d277dd5f79f7e 100644 (file)
 
       <varlistentry>
         <term><varname>SuccessExitStatus=</varname></term>
-        <listitem><para>Takes a list of exit status definitions that,
-        when returned by the main service process, will be considered
-        successful termination, in addition to the normal successful
-        exit code 0 and the signals <constant>SIGHUP</constant>,
-        <constant>SIGINT</constant>, <constant>SIGTERM</constant>, and
-        <constant>SIGPIPE</constant>. Exit status definitions can
-        either be numeric exit codes or termination signal names,
-        separated by spaces. For example:
-
-        <programlisting>SuccessExitStatus=1 2 8 SIGKILL</programlisting>
-
-        ensures that exit codes 1, 2, 8 and
-        the termination signal <constant>SIGKILL</constant> are
-        considered clean service terminations.
-        </para>
+        <listitem><para>Takes a list of exit status definitions that, when returned by the main service
+        process, will be considered successful termination, in addition to the normal successful exit code 0
+        and the signals <constant>SIGHUP</constant>, <constant>SIGINT</constant>,
+        <constant>SIGTERM</constant>, and <constant>SIGPIPE</constant>. Exit status definitions can be
+        numeric exit codes, termination code names, or termination signal names, separated by spaces. See the
+        Process Exit Codes section in
+        <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        a list of termination codes names (for this setting only the part without the
+        <literal>EXIT_</literal> or <literal>EX_</literal> prefix should be used). See
+        <citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        a list of signal names.</para>
 
         <para>This option may appear more than once, in which case the
         list of successful exit statuses is merged. If the empty
         string is assigned to this option, the list is reset, all
         prior assignments of this option will have no
-        effect.</para></listitem>
+        effect.</para>
+
+        <example>
+          <title>A service with with the the <varname>SuccessExitStatus=</varname> setting</title>
+
+          <programlisting>SuccessExitStatus=TEMPFAIL 250 SIGUSR1</programlisting>
+
+          <para>Exit codes 75 (<constant>TEMPFAIL</constant>), 250, and the termination signal
+          <constant>SIGKILL</constant> are considered clean service terminations.</para>
+        </example>
+
+        <para>Note: <command>systemd-analyze exit-codes</command> may be used to list exit
+        codes and translate between numerical code values and names.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 384924ed348b194cfca17fdefcdacd17b962ecf0..45e41fedee2b9dc33ee06e90a7f8ee1ea286c863 100644 (file)
@@ -24,6 +24,7 @@
 #include "conf-files.h"
 #include "copy.h"
 #include "def.h"
+#include "exit-status.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-table.h"
@@ -1637,6 +1638,48 @@ static void dump_syscall_filter(const SyscallFilterSet *set) {
                 printf("    %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal());
 }
 
+static int dump_exit_codes(int argc, char *argv[], void *userdata) {
+        _cleanup_(table_unrefp) Table *table = NULL;
+        int r;
+
+        table = table_new("name", "code", "class");
+        if (!table)
+                return log_oom();
+
+        if (strv_isempty(strv_skip(argv, 1)))
+                for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++) {
+                        if (!exit_status_mappings[i].name)
+                                continue;
+
+                        r = table_add_many(table,
+                                           TABLE_STRING, exit_status_mappings[i].name,
+                                           TABLE_UINT, i,
+                                           TABLE_STRING, exit_status_class(i));
+                        if (r < 0)
+                                return r;
+                }
+        else
+                for (int i = 1; i < argc; i++) {
+                        int code;
+
+                        code = exit_status_from_string(argv[i]);
+                        if (code < 0)
+                                return log_error_errno(r, "Invalid exit code \"%s\": %m", argv[i]);
+
+                        assert(code >= 0 && (size_t) code < ELEMENTSOF(exit_status_mappings));
+                        r = table_add_many(table,
+                                           TABLE_STRING, exit_status_mappings[code].name ?: "-",
+                                           TABLE_UINT, code,
+                                           TABLE_STRING, exit_status_class(code) ?: "-");
+                        if (r < 0)
+                                return r;
+                }
+
+        (void) pager_open(arg_pager_flags);
+
+        return table_print(table, NULL);
+}
+
 static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
         bool first = true;
 
@@ -2170,6 +2213,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  dump                     Output state serialization of service manager\n"
                "  cat-config               Show configuration file and drop-ins\n"
                "  unit-paths               List load directories for units\n"
+               "  exit-codes               List exit code definitions\n"
                "  syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
                "  condition CONDITION...   Evaluate conditions and asserts\n"
                "  verify FILE...           Check unit files for correctness\n"
@@ -2374,6 +2418,7 @@ static int run(int argc, char *argv[]) {
                 { "dump",              VERB_ANY, 1,        0,            dump                   },
                 { "cat-config",        2,        VERB_ANY, 0,            cat_config             },
                 { "unit-paths",        1,        1,        0,            dump_unit_paths        },
+                { "exit-codes",        VERB_ANY, VERB_ANY, 0,            dump_exit_codes        },
                 { "syscall-filter",    VERB_ANY, VERB_ANY, 0,            dump_syscall_filters   },
                 { "condition",         2,        VERB_ANY, 0,            do_condition           },
                 { "verify",            2,        VERB_ANY, 0,            do_verify              },
index 434159f41ced6df0bebd7ec60f708b300aba4cc8..e13361463beda91136967480042f0012415c07b9 100644 (file)
@@ -1414,8 +1414,8 @@ struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
         return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
 }
 
-unsigned long usec_to_jiffies(usec_t u) {
-        static thread_local unsigned long hz = 0;
+static uint32_t sysconf_clock_ticks_cached(void) {
+        static thread_local uint32_t hz = 0;
         long r;
 
         if (hz == 0) {
@@ -1425,7 +1425,17 @@ unsigned long usec_to_jiffies(usec_t u) {
                 hz = r;
         }
 
-        return DIV_ROUND_UP(u , USEC_PER_SEC / hz);
+        return hz;
+}
+
+uint32_t usec_to_jiffies(usec_t u) {
+        uint32_t hz = sysconf_clock_ticks_cached();
+        return DIV_ROUND_UP(u, USEC_PER_SEC / hz);
+}
+
+usec_t jiffies_to_usec(uint32_t j) {
+        uint32_t hz = sysconf_clock_ticks_cached();
+        return DIV_ROUND_UP(j * USEC_PER_SEC, hz);
 }
 
 usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
index e3a529d9709f6f549520929385d69eb4fd881fd3..4c371257e332c7056407afe2cfff01c516e04102 100644 (file)
@@ -136,7 +136,8 @@ int get_timezone(char **timezone);
 time_t mktime_or_timegm(struct tm *tm, bool utc);
 struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
 
-unsigned long usec_to_jiffies(usec_t usec);
+uint32_t usec_to_jiffies(usec_t usec);
+usec_t jiffies_to_usec(uint32_t jiffies);
 
 bool in_utc_timezone(void);
 
index 07f02deed6e6d2bdc08345df0f26352d13317a06..fbda8d8a4c6d0a149acfeb1e601fc0c26083a7a1 100644 (file)
@@ -39,9 +39,9 @@ static int property_get_exit_status_set(
                 void *userdata,
                 sd_bus_error *error) {
 
-        ExitStatusSet *status_set = userdata;
+        const ExitStatusSet *status_set = userdata;
+        unsigned n;
         Iterator i;
-        void *id;
         int r;
 
         assert(bus);
@@ -56,13 +56,10 @@ static int property_get_exit_status_set(
         if (r < 0)
                 return r;
 
-        SET_FOREACH(id, status_set->status, i) {
-                int32_t val = PTR_TO_INT(id);
+        BITMAP_FOREACH(n, &status_set->status, i) {
+                assert(n < 256);
 
-                if (val < 0 || val > 255)
-                        continue;
-
-                r = sd_bus_message_append_basic(reply, 'i', &val);
+                r = sd_bus_message_append_basic(reply, 'i', &n);
                 if (r < 0)
                         return r;
         }
@@ -75,15 +72,14 @@ static int property_get_exit_status_set(
         if (r < 0)
                 return r;
 
-        SET_FOREACH(id, status_set->signal, i) {
-                int32_t val = PTR_TO_INT(id);
+        BITMAP_FOREACH(n, &status_set->signal, i) {
                 const char *str;
 
-                str = signal_to_string((int) val);
+                str = signal_to_string(n);
                 if (!str)
                         continue;
 
-                r = sd_bus_message_append_basic(reply, 'i', &val);
+                r = sd_bus_message_append_basic(reply, 'i', &n);
                 if (r < 0)
                         return r;
         }
@@ -163,18 +159,18 @@ static int bus_set_transient_exit_status(
                 sd_bus_error *error) {
 
         const int32_t *status, *signal;
-        size_t sz_status, sz_signal, i;
+        size_t n_status, n_signal, i;
         int r;
 
         r = sd_bus_message_enter_container(message, 'r', "aiai");
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_read_array(message, 'i', (const void **) &status, &sz_status);
+        r = sd_bus_message_read_array(message, 'i', (const void **) &status, &n_status);
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_read_array(message, 'i', (const void **) &signal, &sz_signal);
+        r = sd_bus_message_read_array(message, 'i', (const void **) &signal, &n_signal);
         if (r < 0)
                 return r;
 
@@ -182,25 +178,21 @@ static int bus_set_transient_exit_status(
         if (r < 0)
                 return r;
 
-        sz_status /= sizeof(int32_t);
-        sz_signal /= sizeof(int32_t);
+        n_status /= sizeof(int32_t);
+        n_signal /= sizeof(int32_t);
 
-        if (sz_status == 0 && sz_signal == 0 && !UNIT_WRITE_FLAGS_NOOP(flags)) {
+        if (n_status == 0 && n_signal == 0 && !UNIT_WRITE_FLAGS_NOOP(flags)) {
                 exit_status_set_free(status_set);
                 unit_write_settingf(u, flags, name, "%s=", name);
                 return 1;
         }
 
-        for (i = 0; i < sz_status; i++) {
+        for (i = 0; i < n_status; i++) {
                 if (status[i] < 0 || status[i] > 255)
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid status code in %s: %"PRIi32, name, status[i]);
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                        r = set_ensure_allocated(&status_set->status, NULL);
-                        if (r < 0)
-                                return r;
-
-                        r = set_put(status_set->status, INT_TO_PTR((int) status[i]));
+                        r = bitmap_set(&status_set->status, status[i]);
                         if (r < 0)
                                 return r;
 
@@ -208,7 +200,7 @@ static int bus_set_transient_exit_status(
                 }
         }
 
-        for (i = 0; i < sz_signal; i++) {
+        for (i = 0; i < n_signal; i++) {
                 const char *str;
 
                 str = signal_to_string((int) signal[i]);
@@ -216,11 +208,7 @@ static int bus_set_transient_exit_status(
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal in %s: %"PRIi32, name, signal[i]);
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
-                        r = set_ensure_allocated(&status_set->signal, NULL);
-                        if (r < 0)
-                                return r;
-
-                        r = set_put(status_set->signal, INT_TO_PTR((int) signal[i]));
+                        r = bitmap_set(&status_set->signal, signal[i]);
                         if (r < 0)
                                 return r;
 
index 911c3690428ae73fbf43bccba8069a51b5ece48e..5b55557f4e8cb51afe6b6f800dbba7b3a6e28c2b 100644 (file)
@@ -3878,15 +3878,19 @@ int exec_spawn(Unit *unit,
                                unit->manager->user_lookup_fds[1],
                                &exit_status);
 
-                if (r < 0)
+                if (r < 0) {
+                        const char *status =
+                                exit_status_to_string(exit_status,
+                                                      EXIT_STATUS_GLIBC | EXIT_STATUS_SYSTEMD);
+
                         log_struct_errno(LOG_ERR, r,
                                          "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
                                          LOG_UNIT_ID(unit),
                                          LOG_UNIT_INVOCATION_ID(unit),
                                          LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
-                                                          exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
-                                                          command->path),
+                                                          status, command->path),
                                          "EXECUTABLE=%s", command->path);
+                }
 
                 _exit(exit_status);
         }
index 3288b0b83885c2fb07631705e0d126e615c32cc6..8664500e1d317c10d93a0f0f6463cd50be9e0999 100644 (file)
@@ -3936,37 +3936,33 @@ int config_parse_set_status(
 
         FOREACH_WORD(word, l, rvalue, state) {
                 _cleanup_free_ char *temp;
-                int val;
-                Set **set;
+                Bitmap *bitmap;
 
                 temp = strndup(word, l);
                 if (!temp)
                         return log_oom();
 
-                r = safe_atoi(temp, &val);
-                if (r < 0) {
-                        val = signal_from_string(temp);
+                /* We need to call exit_status_from_string() first, because we want
+                 * to parse numbers as exit statuses, not signals. */
 
-                        if (val <= 0) {
-                                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
-                                continue;
-                        }
-                        set = &status_set->signal;
+                r = exit_status_from_string(temp);
+                if (r >= 0) {
+                        assert(r >= 0 && r < 256);
+                        bitmap = &status_set->status;
                 } else {
-                        if (val < 0 || val > 255) {
-                                log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
+                        r = signal_from_string(temp);
+
+                        if (r <= 0) {
+                                log_syntax(unit, LOG_ERR, filename, line, 0,
+                                           "Failed to parse value, ignoring: %s", word);
                                 continue;
                         }
-                        set = &status_set->status;
+                        bitmap = &status_set->signal;
                 }
 
-                r = set_ensure_allocated(set, NULL);
+                r = bitmap_set(bitmap, r);
                 if (r < 0)
-                        return log_oom();
-
-                r = set_put(*set, INT_TO_PTR(val));
-                if (r < 0)
-                        return log_oom();
+                        return log_error_errno(r, "Failed to set signal or status %s: %m", word);
         }
         if (!isempty(state))
                 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
index 0674e00ab01f849af4130155e26c33ed5351d830..0698f893fd9d92202ee8da85bf6a7868b329931e 100644 (file)
@@ -221,16 +221,19 @@ _noreturn_ static void crash(int sig) {
                         r = wait_for_terminate(pid, &status);
                         if (r < 0)
                                 log_emergency_errno(r, "Caught <%s>, waitpid() failed: %m", signal_to_string(sig));
-                        else if (status.si_code != CLD_DUMPED)
+                        else if (status.si_code != CLD_DUMPED) {
+                                const char *s = status.si_code == CLD_EXITED
+                                        ? exit_status_to_string(status.si_status, EXIT_STATUS_GLIBC)
+                                        : signal_to_string(status.si_status);
+
                                 log_emergency("Caught <%s>, core dump failed (child "PID_FMT", code=%s, status=%i/%s).",
                                               signal_to_string(sig),
-                                              pid, sigchld_code_to_string(status.si_code),
-                                              status.si_status,
-                                              strna(status.si_code == CLD_EXITED
-                                                    ? exit_status_to_string(status.si_status, EXIT_STATUS_MINIMAL)
-                                                    : signal_to_string(status.si_status)));
-                        else
-                                log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid);
+                                              pid,
+                                              sigchld_code_to_string(status.si_code),
+                                              status.si_status, strna(s));
+                        } else
+                                log_emergency("Caught <%s>, dumped core as pid "PID_FMT".",
+                                              signal_to_string(sig), pid);
                 }
         }
 
index fa89bd4a4d4bbdc475831158d58abbcfc648fa17..d783e5c86784eb3e1c5c212cb61412a0b5a8cbc8 100644 (file)
 #include "user-util.h"
 #include "virt.h"
 
+/* Thresholds for logging at INFO level about resource consumption */
+#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC)
+#define MENTIONWORTHY_IO_BYTES (1024 * 1024ULL)
+#define MENTIONWORTHY_IP_BYTES (0ULL)
+
+/* Thresholds for logging at INFO level about resource consumption */
+#define NOTICEWORTHY_CPU_NSEC (10*60 * NSEC_PER_SEC) /* 10 minutes */
+#define NOTICEWORTHY_IO_BYTES (10 * 1024 * 1024ULL)  /* 10 MB */
+#define NOTICEWORTHY_IP_BYTES (128 * 1024 * 1024ULL) /* 128 MB */
+
 const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = &service_vtable,
         [UNIT_SOCKET] = &socket_vtable,
@@ -2138,10 +2148,19 @@ void unit_trigger_notify(Unit *u) {
                         UNIT_VTABLE(other)->trigger_notify(other, u);
 }
 
+static int raise_level(int log_level, bool condition_info, bool condition_notice) {
+        if (condition_notice && log_level > LOG_NOTICE)
+                return LOG_NOTICE;
+        if (condition_info && log_level > LOG_INFO)
+                return LOG_INFO;
+        return log_level;
+}
+
 static int unit_log_resources(Unit *u) {
         struct iovec iovec[1 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4];
         bool any_traffic = false, have_ip_accounting = false, any_io = false, have_io_accounting = false;
         _cleanup_free_ char *igress = NULL, *egress = NULL, *rr = NULL, *wr = NULL;
+        int log_level = LOG_DEBUG; /* May be raised if resources consumed over a treshold */
         size_t n_message_parts = 0, n_iovec = 0;
         char* message_parts[1 + 2 + 2 + 1], *t;
         nsec_t nsec = NSEC_INFINITY;
@@ -2187,6 +2206,10 @@ static int unit_log_resources(Unit *u) {
                 }
 
                 message_parts[n_message_parts++] = t;
+
+                log_level = raise_level(log_level,
+                                        nsec > NOTICEWORTHY_CPU_NSEC,
+                                        nsec > MENTIONWORTHY_CPU_NSEC);
         }
 
         for (CGroupIOAccountingMetric k = 0; k < _CGROUP_IO_ACCOUNTING_METRIC_MAX; k++) {
@@ -2227,6 +2250,11 @@ static int unit_log_resources(Unit *u) {
                                 goto finish;
                         }
                 }
+
+                if (IN_SET(k, CGROUP_IO_READ_BYTES, CGROUP_IO_WRITE_BYTES))
+                        log_level = raise_level(log_level,
+                                                value > MENTIONWORTHY_IO_BYTES,
+                                                value > NOTICEWORTHY_IO_BYTES);
         }
 
         if (have_io_accounting) {
@@ -2287,6 +2315,11 @@ static int unit_log_resources(Unit *u) {
                                 goto finish;
                         }
                 }
+
+                if (IN_SET(m, CGROUP_IP_INGRESS_BYTES, CGROUP_IP_EGRESS_BYTES))
+                        log_level = raise_level(log_level,
+                                                value > MENTIONWORTHY_IP_BYTES,
+                                                value > NOTICEWORTHY_IP_BYTES);
         }
 
         if (have_ip_accounting) {
@@ -2343,7 +2376,7 @@ static int unit_log_resources(Unit *u) {
         t = strjoina(u->manager->invocation_log_field, u->invocation_id_string);
         iovec[n_iovec + 3] = IOVEC_MAKE_STRING(t);
 
-        log_struct_iovec(LOG_INFO, iovec, n_iovec + 4);
+        log_struct_iovec(log_level, iovec, n_iovec + 4);
         r = 0;
 
 finish:
@@ -5764,9 +5797,11 @@ int unit_test_trigger_loaded(Unit *u) {
 
         trigger = UNIT_TRIGGER(u);
         if (!trigger)
-                return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), "Refusing to start, unit to trigger not loaded.");
+                return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT),
+                                            "Refusing to start, no unit to trigger.");
         if (trigger->load_state != UNIT_LOADED)
-                return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), "Refusing to start, unit %s to trigger not loaded.", u->id);
+                return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT),
+                                            "Refusing to start, unit %s to trigger not loaded.", trigger->id);
 
         return 0;
 }
index 2197160c0fac65fa574ca832d2bc787fb21bc526..c51bb9ae189c362341b59bf7a2ec6998e29a69bb 100644 (file)
@@ -46,10 +46,39 @@ STATIC_DESTRUCTOR_REGISTER(arg_disks, hashmap_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_default_options, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_default_keyfile, freep);
 
-static int generate_keydev_mount(const char *name, const char *keydev, char **unit, char **mount) {
-        _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL;
+static int split_keyspec(const char *keyspec, char **keyfile, char **keydev) {
+        _cleanup_free_ char *kfile = NULL, *kdev = NULL;
+        char *c;
+
+        assert(keyspec);
+        assert(keyfile);
+        assert(keydev);
+
+        c = strrchr(keyspec, ':');
+        if (c) {
+                kfile = strndup(keyspec, c-keyspec);
+                kdev = strdup(c + 1);
+                if (!*kfile || !*kdev)
+                        return log_oom();
+        } else {
+                /* No keydev specified */
+                kfile = strdup(keyspec);
+                kdev = NULL;
+                if (!*kfile)
+                        return log_oom();
+        }
+
+        *keyfile = TAKE_PTR(kfile);
+        *keydev = TAKE_PTR(kdev);
+
+        return 0;
+}
+
+static int generate_keydev_mount(const char *name, const char *keydev, const char *keydev_timeout, bool canfail, char **unit, char **mount) {
+        _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
+        usec_t timeout_us;
 
         assert(name);
         assert(keydev);
@@ -94,7 +123,25 @@ static int generate_keydev_mount(const char *name, const char *keydev, char **un
                 "[Mount]\n"
                 "What=%s\n"
                 "Where=%s\n"
-                "Options=ro\n", what, where);
+                "Options=ro%s\n", what, where, canfail ? ",nofail" : "");
+
+        if (keydev_timeout) {
+                r = parse_sec_fix_0(keydev_timeout, &timeout_us);
+                if (r >= 0) {
+                        r = unit_name_from_path(what, ".device", &device_unit);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to generate unit name: %m");
+
+                        r = write_drop_in_format(arg_dest, device_unit, 90, "device-timeout",
+                                "# Automatically generated by systemd-cryptsetup-generator \n\n"
+                                "[Unit]\nJobRunningTimeoutSec=%s", keydev_timeout);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to write device drop-in: %m");
+
+                } else
+                        log_warning_errno(r, "Failed to parse %s, ignoring: %m", keydev_timeout);
+
+        }
 
         r = fflush_and_check(f);
         if (r < 0)
@@ -150,16 +197,17 @@ static int print_dependencies(FILE *f, const char* device_path) {
 static int create_disk(
                 const char *name,
                 const char *device,
-                const char *keydev,
                 const char *password,
+                const char *keydev,
                 const char *options) {
 
         _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
-                *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *keydev_mount = NULL, *header_path = NULL;
+                *keydev_mount = NULL, *keyfile_timeout_value = NULL, *password_escaped = NULL,
+                *filtered = NULL, *u_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *header_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         const char *dmname;
         bool noauto, nofail, tmp, swap, netdev;
-        int r, detached_header;
+        int r, detached_header, keyfile_can_timeout;
 
         assert(name);
         assert(device);
@@ -170,6 +218,10 @@ static int create_disk(
         swap = fstab_test_option(options, "swap\0");
         netdev = fstab_test_option(options, "_netdev\0");
 
+        keyfile_can_timeout = fstab_filter_options(options, "keyfile-timeout\0", NULL, &keyfile_timeout_value, NULL);
+        if (keyfile_can_timeout < 0)
+                return log_error_errno(keyfile_can_timeout, "Failed to parse keyfile-timeout= option value: %m");
+
         detached_header = fstab_filter_options(options, "header\0", NULL, &header_path, NULL);
         if (detached_header < 0)
                 return log_error_errno(detached_header, "Failed to parse header= option value: %m");
@@ -203,12 +255,6 @@ static int create_disk(
         if (r < 0)
                 return log_error_errno(r, "Failed to generate unit name: %m");
 
-        if (password) {
-                password_escaped = specifier_escape(password);
-                if (!password_escaped)
-                        return log_oom();
-        }
-
         if (keydev && !password)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Key device is specified, but path to the password file is missing.");
@@ -228,10 +274,16 @@ static int create_disk(
                 "After=%s\n",
                 netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
 
+        if (password) {
+                password_escaped = specifier_escape(password);
+                if (!password_escaped)
+                        return log_oom();
+        }
+
         if (keydev) {
                 _cleanup_free_ char *unit = NULL, *p = NULL;
 
-                r = generate_keydev_mount(name, keydev, &unit, &keydev_mount);
+                r = generate_keydev_mount(name, keydev, keyfile_timeout_value, keyfile_can_timeout > 0, &unit, &keydev_mount);
                 if (r < 0)
                         return log_error_errno(r, "Failed to generate keydev mount unit: %m");
 
@@ -240,6 +292,12 @@ static int create_disk(
                         return log_oom();
 
                 free_and_replace(password_escaped, p);
+
+                fprintf(f, "After=%s\n", unit);
+                if (keyfile_can_timeout > 0)
+                        fprintf(f, "Wants=%s\n", unit);
+                else
+                        fprintf(f, "Requires=%s\n", unit);
         }
 
         if (!nofail)
@@ -247,7 +305,7 @@ static int create_disk(
                         "Before=%s\n",
                         netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
 
-        if (password) {
+        if (password && !keydev) {
                 r = print_dependencies(f, password);
                 if (r < 0)
                         return r;
@@ -314,7 +372,7 @@ static int create_disk(
 
         if (keydev)
                 fprintf(f,
-                        "ExecStartPost=" UMOUNT_PATH " %s\n\n",
+                        "ExecStartPost=-" UMOUNT_PATH " %s\n\n",
                         keydev_mount);
 
         r = fflush_and_check(f);
@@ -433,7 +491,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
         } else if (streq(key, "luks.key")) {
                 size_t n;
                 _cleanup_free_ char *keyfile = NULL, *keydev = NULL;
-                char *c;
                 const char *keyspec;
 
                 if (proc_cmdline_value_missing(key, value))
@@ -460,23 +517,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                         return log_oom();
 
                 keyspec = value + n + 1;
-                c = strrchr(keyspec, ':');
-                if (c) {
-                         *c = '\0';
-                        keyfile = strdup(keyspec);
-                        keydev = strdup(c + 1);
-
-                        if (!keyfile || !keydev)
-                                return log_oom();
-                } else {
-                        /* No keydev specified */
-                        keyfile = strdup(keyspec);
-                        if (!keyfile)
-                                return log_oom();
-                }
+                r = split_keyspec(keyspec, &keyfile, &keydev);
+                if (r < 0)
+                        return r;
 
                 free_and_replace(d->keyfile, keyfile);
                 free_and_replace(d->keydev, keydev);
+
         } else if (streq(key, "luks.name")) {
 
                 if (proc_cmdline_value_missing(key, value))
@@ -520,7 +567,7 @@ static int add_crypttab_devices(void) {
         }
 
         for (;;) {
-                _cleanup_free_ char *line = NULL, *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL;
+                _cleanup_free_ char *line = NULL, *name = NULL, *device = NULL, *keydev = NULL, *keyfile = NULL, *keyspec = NULL, *options = NULL;
                 crypto_device *d = NULL;
                 char *l, *uuid;
                 int k;
@@ -537,7 +584,7 @@ static int add_crypttab_devices(void) {
                 if (IN_SET(l[0], 0, '#'))
                         continue;
 
-                k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options);
+                k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyspec, &options);
                 if (k < 2 || k > 4) {
                         log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line);
                         continue;
@@ -556,7 +603,11 @@ static int add_crypttab_devices(void) {
                         continue;
                 }
 
-                r = create_disk(name, device, NULL, keyfile, (d && d->options) ? d->options : options);
+                r = split_keyspec(keyspec, &keyfile, &keydev);
+                if (r < 0)
+                        return r;
+
+                r = create_disk(name, device, keyfile, keydev, (d && d->options) ? d->options : options);
                 if (r < 0)
                         return r;
 
@@ -596,7 +647,7 @@ static int add_proc_cmdline_devices(void) {
                 else
                         options = "timeout=0";
 
-                r = create_disk(d->name, device, d->keydev, d->keyfile ?: arg_default_keyfile, options);
+                r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, d->keydev, options);
                 if (r < 0)
                         return r;
         }
index 02f131086bd08d9898ca3c280a74eeefcf65b869..78732a0a577f994b2e63ab4cde7954f88c4ca193 100644 (file)
@@ -81,7 +81,10 @@ static int parse_one_option(const char *option) {
         assert(option);
 
         /* Handled outside of this tool */
-        if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail", "_netdev"))
+        if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail", "_netdev", "keyfile-timeout"))
+                return 0;
+
+        if (startswith(option, "keyfile-timeout="))
                 return 0;
 
         if ((val = startswith(option, "cipher="))) {
index fc194a3e72c20e416efbc48472e963a19024b0e1..a37bbf56b75ad2b5d18ff0f229938eb1f45364f2 100644 (file)
@@ -559,7 +559,7 @@ const sd_bus_vtable session_vtable[] = {
         SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
+        SD_BUS_PROPERTY("State", "s", property_get_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
index 5f74f7b5cc8d7661f8221429ed1228d5f1f7808e..03b916d4129a540f09164beb8dc29bb4b3cb7528 100644 (file)
@@ -47,6 +47,9 @@
 #include "terminal-util.h"
 #include "verbs.h"
 
+/* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */
+#define NETDEV_KIND_MAX 64
+
 static PagerFlags arg_pager_flags = 0;
 static bool arg_legend = true;
 static bool arg_all = false;
@@ -104,8 +107,23 @@ static void setup_state_to_color(const char *state, const char **on, const char
                 *on = *off = "";
 }
 
+typedef struct VxLanInfo {
+        uint32_t vni;
+        uint32_t link;
+
+        int local_family;
+        int group_family;
+
+        union in_addr_union local;
+        union in_addr_union group;
+
+        uint16_t dest_port;
+
+} VxLanInfo;
+
 typedef struct LinkInfo {
         char name[IFNAMSIZ+1];
+        char netdev_kind[NETDEV_KIND_MAX];
         int ifindex;
         unsigned short iftype;
         struct ether_addr mac_address;
@@ -123,6 +141,18 @@ typedef struct LinkInfo {
         uint64_t tx_bitrate;
         uint64_t rx_bitrate;
 
+        /* bridge info */
+        uint32_t forward_delay;
+        uint32_t hello_time;
+        uint32_t max_age;
+        uint32_t ageing_time;
+        uint32_t stp_state;
+        uint16_t priority;
+        uint8_t mcast_igmp_version;
+
+        /* vxlan info */
+        VxLanInfo vxlan_info;
+
         /* ethtool info */
         int autonegotiation;
         size_t speed;
@@ -142,10 +172,71 @@ static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
         return CMP(a->ifindex, b->ifindex);
 }
 
+static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
+        const char *received_kind;
+        int r;
+
+        assert(m);
+        assert(info);
+
+        r = sd_netlink_message_enter_container(m, IFLA_LINKINFO);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_read_string(m, IFLA_INFO_KIND, &received_kind);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_enter_container(m, IFLA_INFO_DATA);
+        if (r < 0)
+                return r;
+
+        if (streq(received_kind, "bridge")) {
+                (void) sd_netlink_message_read_u32(m, IFLA_BR_FORWARD_DELAY, &info->forward_delay);
+                (void) sd_netlink_message_read_u32(m, IFLA_BR_HELLO_TIME, &info->hello_time);
+                (void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age);
+                (void) sd_netlink_message_read_u32(m, IFLA_BR_AGEING_TIME, &info->ageing_time);
+                (void) sd_netlink_message_read_u32(m, IFLA_BR_STP_STATE, &info->stp_state);
+                (void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority);
+                (void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version);
+
+        } else if (streq(received_kind, "vxlan")) {
+                (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni);
+
+                r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_GROUP, &info->vxlan_info.group.in);
+                if (r >= 0)
+                        info->vxlan_info.group_family = AF_INET;
+                else {
+                        r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_GROUP6, &info->vxlan_info.group.in6);
+                        if (r >= 0)
+                                info->vxlan_info.group_family = AF_INET6;
+                }
+
+                r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_LOCAL, &info->vxlan_info.local.in);
+                if (r >= 0)
+                        info->vxlan_info.local_family = AF_INET;
+                else {
+                        r = sd_netlink_message_read_in6_addr(m, IFLA_VXLAN_LOCAL6, &info->vxlan_info.local.in6);
+                        if (r >= 0)
+                                info->vxlan_info.local_family = AF_INET6;
+                }
+
+                (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_LINK, &info->vxlan_info.link);
+                (void) sd_netlink_message_read_u16(m, IFLA_VXLAN_PORT, &info->vxlan_info.dest_port);
+        }
+
+        strncpy(info->netdev_kind, received_kind, IFNAMSIZ);
+
+        (void) sd_netlink_message_exit_container(m);
+        (void) sd_netlink_message_exit_container(m);
+
+        return 0;
+}
+
 static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) {
         const char *name;
-        uint16_t type;
         int ifindex, r;
+        uint16_t type;
 
         assert(m);
         assert(info);
@@ -202,6 +293,9 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) {
         else if (sd_netlink_message_read(m, IFLA_STATS, sizeof info->stats, &info->stats) >= 0)
                 info->has_stats = true;
 
+        /* fill kind info */
+        (void) decode_netdev(m, info);
+
         return 1;
 }
 
@@ -360,27 +454,16 @@ static int list_links(int argc, char *argv[], void *userdata) {
 
                 t = link_get_type_string(links[i].iftype, d);
 
-                r = table_add_cell(table, NULL, TABLE_INT, &links[i].ifindex);
-                if (r < 0)
-                        return r;
-
                 r = table_add_many(table,
+                                   TABLE_INT, links[i].ifindex,
                                    TABLE_STRING, links[i].name,
-                                   TABLE_STRING, strna(t));
-                if (r < 0)
-                        return r;
-
-                r = table_add_cell(table, &cell, TABLE_STRING, strna(operational_state));
-                if (r < 0)
-                        return r;
-
-                (void) table_set_color(table, cell, on_color_operational);
-
-                r = table_add_cell(table, &cell, TABLE_STRING, strna(setup_state));
+                                   TABLE_STRING, strna(t),
+                                   TABLE_STRING, strna(operational_state),
+                                   TABLE_SET_COLOR, on_color_operational,
+                                   TABLE_STRING, strna(setup_state),
+                                   TABLE_SET_COLOR, on_color_setup);
                 if (r < 0)
                         return r;
-
-                (void) table_set_color(table, cell, on_color_setup);
         }
 
         r = table_print(table, NULL);
@@ -551,11 +634,9 @@ static int dump_gateways(
         for (i = 0; i < n; i++) {
                 _cleanup_free_ char *gateway = NULL, *description = NULL, *with_description = NULL;
 
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-
-                r = table_add_cell(table, NULL, TABLE_STRING, i == 0 ? "Gateway:" : "");
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, i == 0 ? "Gateway:" : "");
                 if (r < 0)
                         return r;
 
@@ -609,11 +690,9 @@ static int dump_addresses(
         for (i = 0; i < n; i++) {
                 _cleanup_free_ char *pretty = NULL;
 
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-
-                r = table_add_cell(table, NULL, TABLE_STRING, i == 0 ? "Address:" : "");
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, i == 0 ? "Address:" : "");
                 if (r < 0)
                         return r;
 
@@ -707,7 +786,7 @@ static int dump_address_labels(sd_netlink *rtnl) {
                 if (r < 0)
                         return r;
 
-                r = table_add_cell_stringf(table, &cell, "%s/%u", pretty, prefixlen);
+                r = table_add_cell_stringf(table, NULL, "%s/%u", pretty, prefixlen);
                 if (r < 0)
                         return r;
         }
@@ -793,7 +872,6 @@ static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
         for (;;) {
                 const char *system_name = NULL, *port_id = NULL, *port_description = NULL;
                 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
-                _cleanup_free_ char *str = NULL;
 
                 r = next_lldp_neighbor(f, &n);
                 if (r < 0)
@@ -801,11 +879,9 @@ static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
                 if (r == 0)
                         break;
 
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-
-                r = table_add_cell(table, NULL, TABLE_STRING, c == 0 ? prefix : "");
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, c == 0 ? prefix : "");
                 if (r < 0)
                         return r;
 
@@ -813,14 +889,12 @@ static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
                 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
                 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
 
-                if (asprintf(&str, "%s on port %s%s%s%s",
-                             strna(system_name), strna(port_id),
-                             isempty(port_description) ? "" : " (",
-                             port_description,
-                             isempty(port_description) ? "" : ")") < 0)
-                        return -ENOMEM;
-
-                r = table_add_cell(table, NULL, TABLE_STRING, str);
+                r = table_add_cell_stringf(table, NULL,
+                                           "%s on port %s%s%s%s",
+                                           strna(system_name), strna(port_id),
+                                           isempty(port_description) ? "" : " (",
+                                           port_description,
+                                           isempty(port_description) ? "" : ")");
                 if (r < 0)
                         return r;
 
@@ -840,15 +914,10 @@ static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes
                 return 0;
 
         for (c = 0; ifindexes[c] > 0; c++) {
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-
-                r = table_add_cell(table, NULL, TABLE_STRING, c == 0 ? prefix : "");
-                if (r < 0)
-                        return r;
-
-                r = table_add_cell(table, NULL, TABLE_IFINDEX, ifindexes + c);
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, c == 0 ? prefix : "",
+                                   TABLE_IFINDEX, ifindexes[c]);
                 if (r < 0)
                         return r;
         }
@@ -864,15 +933,10 @@ static int dump_list(Table *table, const char *prefix, char **l) {
                 return 0;
 
         STRV_FOREACH(i, l) {
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-
-                r = table_add_cell(table, NULL, TABLE_STRING, i == l ? prefix : "");
-                if (r < 0)
-                        return r;
-
-                r = table_add_cell(table, NULL, TABLE_STRING, *i);
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, i == l ? prefix : "",
+                                   TABLE_STRING, *i);
                 if (r < 0)
                         return r;
         }
@@ -881,13 +945,13 @@ static int dump_list(Table *table, const char *prefix, char **l) {
 }
 
 #define DUMP_STATS_ONE(name, val_name)                                  \
-        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);             \
+        r = table_add_many(table,                                       \
+                           TABLE_EMPTY,                                 \
+                           TABLE_STRING, name ":");                     \
         if (r < 0)                                                      \
                 return r;                                               \
-        r = table_add_cell(table, NULL, TABLE_STRING, name ":");   \
-        if (r < 0)                                                      \
-                return r;                                               \
-        r = table_add_cell(table, NULL, info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
+        r = table_add_cell(table, NULL,                                 \
+                           info->has_stats64 ? TABLE_UINT64 : TABLE_UINT32, \
                            info->has_stats64 ? (void*) &info->stats64.val_name : (void*) &info->stats.val_name); \
         if (r < 0)                                                      \
                 return r;
@@ -984,53 +1048,30 @@ static int link_status_one(
 
         table_set_header(table, false);
 
-        r = table_add_cell(table, &cell, TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE));
+        r = table_add_many(table,
+                           TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
+                           TABLE_SET_COLOR, on_color_operational);
         if (r < 0)
                 return r;
-        (void) table_set_color(table, cell, on_color_operational);
         r = table_add_cell_stringf(table, &cell, "%i: %s", info->ifindex, info->name);
         if (r < 0)
                 return r;
         (void) table_set_align_percent(table, cell, 0);
-        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-        if (r < 0)
-                return r;
-
-        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-        if (r < 0)
-                return r;
-        r = table_add_cell(table, &cell, TABLE_STRING, "Link File:");
-        if (r < 0)
-                return r;
-        (void) table_set_align_percent(table, cell, 100);
-        r = table_add_cell(table, NULL, TABLE_STRING, strna(link));
-        if (r < 0)
-                return r;
-
-        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-        if (r < 0)
-                return r;
-        r = table_add_cell(table, NULL, TABLE_STRING, "Network File:");
-        if (r < 0)
-                return r;
-        r = table_add_cell(table, NULL, TABLE_STRING, strna(network));
-        if (r < 0)
-                return r;
-
-        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-        if (r < 0)
-                return r;
-        r = table_add_cell(table, NULL, TABLE_STRING, "Type:");
-        if (r < 0)
-                return r;
-        r = table_add_cell(table, NULL, TABLE_STRING, strna(t));
-        if (r < 0)
-                return r;
 
-        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-        if (r < 0)
-                return r;
-        r = table_add_cell(table, NULL, TABLE_STRING, "State:");
+        r = table_add_many(table,
+                           TABLE_EMPTY,
+                           TABLE_EMPTY,
+                           TABLE_STRING, "Link File:",
+                           TABLE_SET_ALIGN_PERCENT, 100,
+                           TABLE_STRING, strna(link),
+                           TABLE_EMPTY,
+                           TABLE_STRING, "Network File:",
+                           TABLE_STRING, strna(network),
+                           TABLE_EMPTY,
+                           TABLE_STRING, "Type:",
+                           TABLE_STRING, strna(t),
+                           TABLE_EMPTY,
+                           TABLE_STRING, "State:");
         if (r < 0)
                 return r;
         r = table_add_cell_stringf(table, NULL, "%s%s%s (%s%s%s)",
@@ -1040,46 +1081,34 @@ static int link_status_one(
                 return r;
 
         if (path) {
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, "Path:");
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, path);
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Path:",
+                                   TABLE_STRING, path);
                 if (r < 0)
                         return r;
         }
         if (driver) {
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, "Driver:");
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, driver);
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Driver:",
+                                   TABLE_STRING, driver);
                 if (r < 0)
                         return r;
         }
         if (vendor) {
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, "Vendor:");
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, vendor);
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Vendor:",
+                                   TABLE_STRING, vendor);
                 if (r < 0)
                         return r;
         }
         if (model) {
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, "Model:");
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, model);
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Model:",
+                                   TABLE_STRING, model);
                 if (r < 0)
                         return r;
         }
@@ -1090,10 +1119,9 @@ static int link_status_one(
 
                 (void) ieee_oui(hwdb, &info->mac_address, &description);
 
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, "HW Address:");
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "HW Address:");
                 if (r < 0)
                         return r;
                 r = table_add_cell_stringf(table, NULL, "%s%s%s%s",
@@ -1111,10 +1139,9 @@ static int link_status_one(
                 xsprintf(min_str, "%" PRIu32, info->min_mtu);
                 xsprintf(max_str, "%" PRIu32, info->max_mtu);
 
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, "MTU:");
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "MTU:");
                 if (r < 0)
                         return r;
                 r = table_add_cell_stringf(table, NULL, "%" PRIu32 "%s%s%s%s%s%s%s",
@@ -1130,16 +1157,89 @@ static int link_status_one(
                         return r;
         }
 
+        if (streq_ptr(info->netdev_kind, "bridge")) {
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Forward Delay:",
+                                   TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->forward_delay),
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Hello Time:",
+                                   TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->hello_time),
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Max Age:",
+                                   TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->max_age),
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Ageing Time:",
+                                   TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->ageing_time),
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Priority:",
+                                   TABLE_UINT16, info->priority,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "STP:",
+                                   TABLE_BOOLEAN, info->stp_state > 0,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Multicast IGMP Version:",
+                                   TABLE_UINT8, info->mcast_igmp_version);
+                if (r < 0)
+                        return r;
+
+        } else if (streq_ptr(info->netdev_kind, "vxlan")) {
+                if (info->vxlan_info.vni > 0) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "VNI:",
+                                           TABLE_UINT32, info->vxlan_info.vni);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (IN_SET(info->vxlan_info.group_family, AF_INET, AF_INET6)) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Group:",
+                                           info->vxlan_info.group_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
+                                           &info->vxlan_info.group);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (IN_SET(info->vxlan_info.local_family, AF_INET, AF_INET6)) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Local:",
+                                           info->vxlan_info.local_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
+                                           &info->vxlan_info.local);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (info->vxlan_info.dest_port > 0) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Destination Port:",
+                                           TABLE_UINT16, be16toh(info->vxlan_info.dest_port));
+                        if (r < 0)
+                                return r;
+                }
+
+                if (info->vxlan_info.link > 0) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Underlying Device:",
+                                           TABLE_IFINDEX, info->vxlan_info.link);
+                        if (r < 0)
+                                 return r;
+                }
+        }
+
         if (info->has_bitrates) {
                 char tx[FORMAT_BYTES_MAX], rx[FORMAT_BYTES_MAX];
 
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, "Bit Rate (Tx/Rx):");
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Bit Rate (Tx/Rx):");
                 if (r < 0)
                         return r;
-
                 r = table_add_cell_stringf(table, NULL, "%sbps/%sbps",
                                            format_bytes_full(tx, sizeof tx, info->tx_bitrate, 0),
                                            format_bytes_full(rx, sizeof rx, info->rx_bitrate, 0));
@@ -1148,10 +1248,9 @@ static int link_status_one(
         }
 
         if (info->has_tx_queues || info->has_rx_queues) {
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, "Queue Length (Tx/Rx):");
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Queue Length (Tx/Rx):");
                 if (r < 0)
                         return r;
                 r = table_add_cell_stringf(table, NULL, "%" PRIu32 "/%" PRIu32, info->tx_queues, info->rx_queues);
@@ -1164,49 +1263,37 @@ static int link_status_one(
                 const char *port = port_to_string(info->port);
 
                 if (IN_SET(info->autonegotiation, AUTONEG_DISABLE, AUTONEG_ENABLE)) {
-                        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                        if (r < 0)
-                                return r;
-                        r = table_add_cell(table, NULL, TABLE_STRING, "Auto negotiation:");
-                        if (r < 0)
-                                return r;
-                        r = table_add_cell(table, NULL, TABLE_BOOLEAN, &info->autonegotiation);
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Auto negotiation:",
+                                           TABLE_BOOLEAN, info->autonegotiation == AUTONEG_ENABLE);
                         if (r < 0)
                                 return r;
                 }
 
                 if (info->speed > 0) {
-                        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                        if (r < 0)
-                                return r;
-                        r = table_add_cell(table, NULL, TABLE_STRING, "Speed:");
-                        if (r < 0)
-                                return r;
-                        r = table_add_cell(table, NULL, TABLE_BPS, &info->speed);
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Speed:",
+                                           TABLE_BPS, info->speed);
                         if (r < 0)
                                 return r;
                 }
 
                 if (duplex) {
-                        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                        if (r < 0)
-                                return r;
-                        r = table_add_cell(table, NULL, TABLE_STRING, "Duplex:");
-                        if (r < 0)
-                                return r;
-                        r = table_add_cell(table, NULL, TABLE_STRING, duplex);
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Duplex:",
+                                           TABLE_STRING, duplex);
                         if (r < 0)
                                 return r;
                 }
 
                 if (port) {
-                        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                        if (r < 0)
-                                return r;
-                        r = table_add_cell(table, NULL, TABLE_STRING, "Port:");
-                        if (r < 0)
-                                return r;
-                        r = table_add_cell(table, NULL, TABLE_STRING, port);
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Port:",
+                                           TABLE_STRING, port);
                         if (r < 0)
                                 return r;
                 }
@@ -1239,13 +1326,10 @@ static int link_status_one(
 
         (void) sd_network_link_get_timezone(info->ifindex, &tz);
         if (tz) {
-                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, "Time Zone:");
-                if (r < 0)
-                        return r;
-                r = table_add_cell(table, NULL, TABLE_STRING, tz);
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Time Zone:",
+                                   TABLE_STRING, tz);
                 if (r < 0)
                         return r;
         }
@@ -1287,19 +1371,12 @@ static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
 
         table_set_header(table, false);
 
-        r = table_add_cell(table, &cell, TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE));
-        if (r < 0)
-                return r;
-        (void) table_set_color(table, cell, on_color_operational);
-
-        r = table_add_cell(table, NULL, TABLE_STRING, "State:");
-        if (r < 0)
-                return r;
-
-        r = table_add_cell(table, &cell, TABLE_STRING, strna(operational_state));
-        if (r < 0)
-                return r;
-        (void) table_set_color(table, cell, on_color_operational);
+        r = table_add_many(table,
+                           TABLE_STRING, special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE),
+                           TABLE_SET_COLOR, on_color_operational,
+                           TABLE_STRING, "State:",
+                           TABLE_STRING, strna(operational_state),
+                           TABLE_SET_COLOR, on_color_operational);
 
         r = dump_addresses(rtnl, table, 0);
         if (r < 0)
index a956a42cab0a5c3dafff4e011fa32420cc1db768..2eba72dd59fc4b1602cada712ae189f7bc530855 100644 (file)
 #include "macro.h"
 #include "memory-util.h"
 
-struct Bitmap {
-        uint64_t *bitmaps;
-        size_t n_bitmaps;
-        size_t bitmaps_allocated;
-};
-
 /* Bitmaps are only meant to store relatively small numbers
  * (corresponding to, say, an enum), so it is ok to limit
  * the max entry. 64k should be plenty. */
@@ -117,7 +111,7 @@ void bitmap_unset(Bitmap *b, unsigned n) {
         b->bitmaps[offset] &= ~bitmask;
 }
 
-bool bitmap_isset(Bitmap *b, unsigned n) {
+bool bitmap_isset(const Bitmap *b, unsigned n) {
         uint64_t bitmask;
         unsigned offset;
 
@@ -134,7 +128,7 @@ bool bitmap_isset(Bitmap *b, unsigned n) {
         return !!(b->bitmaps[offset] & bitmask);
 }
 
-bool bitmap_isclear(Bitmap *b) {
+bool bitmap_isclear(const Bitmap *b) {
         unsigned i;
 
         if (!b)
@@ -148,7 +142,6 @@ bool bitmap_isclear(Bitmap *b) {
 }
 
 void bitmap_clear(Bitmap *b) {
-
         if (!b)
                 return;
 
@@ -157,7 +150,7 @@ void bitmap_clear(Bitmap *b) {
         b->bitmaps_allocated = 0;
 }
 
-bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) {
+bool bitmap_iterate(const Bitmap *b, Iterator *i, unsigned *n) {
         uint64_t bitmask;
         unsigned offset, rem;
 
@@ -192,9 +185,9 @@ bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) {
         return false;
 }
 
-bool bitmap_equal(Bitmap *a, Bitmap *b) {
+bool bitmap_equal(const Bitmap *a, const Bitmap *b) {
         size_t common_n_bitmaps;
-        Bitmap *c;
+        const Bitmap *c;
         unsigned i;
 
         if (a == b)
index 843d27d24d2c8b3160c37613dc06093177167655..f65a050584a91940ebf95ed9e42266ce4a4472f4 100644 (file)
@@ -6,7 +6,11 @@
 #include "hashmap.h"
 #include "macro.h"
 
-typedef struct Bitmap Bitmap;
+typedef struct Bitmap {
+        uint64_t *bitmaps;
+        size_t n_bitmaps;
+        size_t bitmaps_allocated;
+} Bitmap;
 
 Bitmap *bitmap_new(void);
 Bitmap *bitmap_copy(Bitmap *b);
@@ -15,13 +19,13 @@ void bitmap_free(Bitmap *b);
 
 int bitmap_set(Bitmap *b, unsigned n);
 void bitmap_unset(Bitmap *b, unsigned n);
-bool bitmap_isset(Bitmap *b, unsigned n);
-bool bitmap_isclear(Bitmap *b);
+bool bitmap_isset(const Bitmap *b, unsigned n);
+bool bitmap_isclear(const Bitmap *b);
 void bitmap_clear(Bitmap *b);
 
-bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n);
+bool bitmap_iterate(const Bitmap *b, Iterator *i, unsigned *n);
 
-bool bitmap_equal(Bitmap *a, Bitmap *b);
+bool bitmap_equal(const Bitmap *a, const Bitmap *b);
 
 #define BITMAP_FOREACH(n, b, i) \
         for ((i).idx = 0; bitmap_iterate((b), &(i), (unsigned*)&(n)); )
index 322204dd2214dd5f59caaa2abbae6b53e195d47c..e53b9d5ea29c36f630e89496395c4327969856cd 100644 (file)
@@ -10,6 +10,7 @@
 #include "cpu-set-util.h"
 #include "escape.h"
 #include "exec-util.h"
+#include "exit-status.h"
 #include "hexdecoct.h"
 #include "hostname-util.h"
 #include "in-addr-util.h"
@@ -1439,12 +1440,11 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
 
         if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
                 _cleanup_free_ int *status = NULL, *signal = NULL;
-                size_t sz_status = 0, sz_signal = 0;
+                size_t n_status = 0, n_signal = 0;
                 const char *p;
 
                 for (p = eq;;) {
                         _cleanup_free_ char *word = NULL;
-                        int val;
 
                         r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
                         if (r == 0)
@@ -1454,24 +1454,30 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
                         if (r < 0)
                                 return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
 
-                        r = safe_atoi(word, &val);
-                        if (r < 0) {
-                                val = signal_from_string(word);
-                                if (val < 0)
-                                        return log_error_errno(r, "Invalid status or signal %s in %s: %m", word, field);
+                        /* We need to call exit_status_from_string() first, because we want
+                         * to parse numbers as exit statuses, not signals. */
 
-                                signal = reallocarray(signal, sz_signal + 1, sizeof(int));
-                                if (!signal)
-                                        return log_oom();
+                        r = exit_status_from_string(word);
+                        if (r >= 0) {
+                                assert(r >= 0 && r < 256);
 
-                                signal[sz_signal++] = val;
-                        } else {
-                                status = reallocarray(status, sz_status + 1, sizeof(int));
+                                status = reallocarray(status, n_status + 1, sizeof(int));
                                 if (!status)
                                         return log_oom();
 
-                                status[sz_status++] = val;
-                        }
+                                status[n_status++] = r;
+
+                        } else if ((r = signal_from_string(word)) >= 0) {
+                                signal = reallocarray(signal, n_signal + 1, sizeof(int));
+                                if (!signal)
+                                        return log_oom();
+
+                                signal[n_signal++] = r;
+
+                        } else
+                                /* original r from exit_status_to_string() */
+                                return log_error_errno(r, "Invalid status or signal %s in %s: %m",
+                                                       word, field);
                 }
 
                 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
@@ -1490,11 +1496,11 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
                 if (r < 0)
                         return bus_log_create_error(r);
 
-                r = sd_bus_message_append_array(m, 'i', status, sz_status);
+                r = sd_bus_message_append_array(m, 'i', status, n_status * sizeof(int));
                 if (r < 0)
                         return bus_log_create_error(r);
 
-                r = sd_bus_message_append_array(m, 'i', signal, sz_signal);
+                r = sd_bus_message_append_array(m, 'i', signal, n_signal * sizeof(int));
                 if (r < 0)
                         return bus_log_create_error(r);
 
index 8e301250bcbb58b3152b09bbc1369c4b40f118af..6af115e7aad8d463430676a2880b332a6dbe973d 100644 (file)
@@ -1480,14 +1480,6 @@ int bus_property_get_ulong(
 }
 #endif
 
-int bus_log_parse_error(int r) {
-        return log_error_errno(r, "Failed to parse bus message: %m");
-}
-
-int bus_log_create_error(int r) {
-        return log_error_errno(r, "Failed to create bus message: %m");
-}
-
 /**
  * bus_path_encode_unique() - encode unique object path
  * @b: bus connection or NULL
index 3216b0c37a1bf0da613923b33ae8973caba3ca1f..1e2f04cc5dffcfc21fdb33bbb9eb8e56d828bc7e 100644 (file)
@@ -114,8 +114,11 @@ assert_cc(sizeof(pid_t) == sizeof(uint32_t));
 assert_cc(sizeof(mode_t) == sizeof(uint32_t));
 #define bus_property_get_mode ((sd_bus_property_get_t) NULL)
 
-int bus_log_parse_error(int r);
-int bus_log_create_error(int r);
+#define bus_log_parse_error(r) \
+        log_error_errno(r, "Failed to parse bus message: %m")
+
+#define bus_log_create_error(r) \
+        log_error_errno(r, "Failed to create bus message: %m")
 
 #define BUS_DEFINE_PROPERTY_GET_GLOBAL(function, bus_type, val)         \
         int function(sd_bus *bus,                                       \
index 58ebc3ca4d6558cf1d28ba544f9e9bcf81c43da0..80ac4868cbfd35e485dffe045320966dba5b073e 100644 (file)
@@ -6,10 +6,11 @@
 
 #include "exit-status.h"
 #include "macro.h"
+#include "parse-util.h"
 #include "set.h"
+#include "string-util.h"
 
-const char* exit_status_to_string(int status, ExitStatusLevel level) {
-
+const ExitStatusMapping exit_status_mappings[256] = {
         /* Exit status ranges:
          *
          *   0…1   â”‚ ISO C, EXIT_SUCCESS + EXIT_FAILURE
@@ -25,235 +26,127 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) {
          *         â”‚ signal or such, and we follow that logic here.)
          */
 
-        switch (status) {  /* We always cover the ISO C ones */
-
-        case EXIT_SUCCESS:
-                return "SUCCESS";
-
-        case EXIT_FAILURE:
-                return "FAILURE";
-        }
-
-        if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB, EXIT_STATUS_FULL)) {
-                switch (status) { /* Optionally we cover our own ones */
-
-                case EXIT_CHDIR:
-                        return "CHDIR";
-
-                case EXIT_NICE:
-                        return "NICE";
-
-                case EXIT_FDS:
-                        return "FDS";
-
-                case EXIT_EXEC:
-                        return "EXEC";
-
-                case EXIT_MEMORY:
-                        return "MEMORY";
-
-                case EXIT_LIMITS:
-                        return "LIMITS";
-
-                case EXIT_OOM_ADJUST:
-                        return "OOM_ADJUST";
-
-                case EXIT_SIGNAL_MASK:
-                        return "SIGNAL_MASK";
-
-                case EXIT_STDIN:
-                        return "STDIN";
-
-                case EXIT_STDOUT:
-                        return "STDOUT";
-
-                case EXIT_CHROOT:
-                        return "CHROOT";
-
-                case EXIT_IOPRIO:
-                        return "IOPRIO";
-
-                case EXIT_TIMERSLACK:
-                        return "TIMERSLACK";
-
-                case EXIT_SECUREBITS:
-                        return "SECUREBITS";
-
-                case EXIT_SETSCHEDULER:
-                        return "SETSCHEDULER";
-
-                case EXIT_CPUAFFINITY:
-                        return "CPUAFFINITY";
-
-                case EXIT_GROUP:
-                        return "GROUP";
-
-                case EXIT_USER:
-                        return "USER";
-
-                case EXIT_CAPABILITIES:
-                        return "CAPABILITIES";
-
-                case EXIT_CGROUP:
-                        return "CGROUP";
-
-                case EXIT_SETSID:
-                        return "SETSID";
-
-                case EXIT_CONFIRM:
-                        return "CONFIRM";
-
-                case EXIT_STDERR:
-                        return "STDERR";
-
-                case EXIT_PAM:
-                        return "PAM";
-
-                case EXIT_NETWORK:
-                        return "NETWORK";
-
-                case EXIT_NAMESPACE:
-                        return "NAMESPACE";
-
-                case EXIT_NO_NEW_PRIVILEGES:
-                        return "NO_NEW_PRIVILEGES";
-
-                case EXIT_SECCOMP:
-                        return "SECCOMP";
-
-                case EXIT_SELINUX_CONTEXT:
-                        return "SELINUX_CONTEXT";
-
-                case EXIT_PERSONALITY:
-                        return "PERSONALITY";
-
-                case EXIT_APPARMOR_PROFILE:
-                        return "APPARMOR";
-
-                case EXIT_ADDRESS_FAMILIES:
-                        return "ADDRESS_FAMILIES";
-
-                case EXIT_RUNTIME_DIRECTORY:
-                        return "RUNTIME_DIRECTORY";
-
-                case EXIT_CHOWN:
-                        return "CHOWN";
-
-                case EXIT_SMACK_PROCESS_LABEL:
-                        return "SMACK_PROCESS_LABEL";
-
-                case EXIT_KEYRING:
-                        return "KEYRING";
-
-                case EXIT_STATE_DIRECTORY:
-                        return "STATE_DIRECTORY";
-
-                case EXIT_CACHE_DIRECTORY:
-                        return "CACHE_DIRECTORY";
-
-                case EXIT_LOGS_DIRECTORY:
-                        return "LOGS_DIRECTORY";
-
-                case EXIT_CONFIGURATION_DIRECTORY:
-                        return "CONFIGURATION_DIRECTORY";
-
-                case EXIT_NUMA_POLICY:
-                        return "NUMA_POLICY";
-
-                case EXIT_EXCEPTION:
-                        return "EXCEPTION";
-                }
-        }
-
-        if (IN_SET(level, EXIT_STATUS_LSB, EXIT_STATUS_FULL)) {
-                switch (status) { /* Optionally we support LSB ones */
-
-                case EXIT_INVALIDARGUMENT:
-                        return "INVALIDARGUMENT";
-
-                case EXIT_NOTIMPLEMENTED:
-                        return "NOTIMPLEMENTED";
-
-                case EXIT_NOPERMISSION:
-                        return "NOPERMISSION";
-
-                case EXIT_NOTINSTALLED:
-                        return "NOTINSTALLED";
-
-                case EXIT_NOTCONFIGURED:
-                        return "NOTCONFIGURED";
+        [EXIT_SUCCESS] =                 { "SUCCESS",                 EXIT_STATUS_GLIBC },
+        [EXIT_FAILURE] =                 { "FAILURE",                 EXIT_STATUS_GLIBC },
+
+        [EXIT_CHDIR] =                   { "CHDIR",                   EXIT_STATUS_SYSTEMD },
+        [EXIT_NICE] =                    { "NICE",                    EXIT_STATUS_SYSTEMD },
+        [EXIT_FDS] =                     { "FDS",                     EXIT_STATUS_SYSTEMD },
+        [EXIT_EXEC] =                    { "EXEC",                    EXIT_STATUS_SYSTEMD },
+        [EXIT_MEMORY] =                  { "MEMORY",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_LIMITS] =                  { "LIMITS",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_OOM_ADJUST] =              { "OOM_ADJUST",              EXIT_STATUS_SYSTEMD },
+        [EXIT_SIGNAL_MASK] =             { "SIGNAL_MASK",             EXIT_STATUS_SYSTEMD },
+        [EXIT_STDIN] =                   { "STDIN",                   EXIT_STATUS_SYSTEMD },
+        [EXIT_STDOUT] =                  { "STDOUT",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_CHROOT] =                  { "CHROOT",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_IOPRIO] =                  { "IOPRIO",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_TIMERSLACK] =              { "TIMERSLACK",              EXIT_STATUS_SYSTEMD },
+        [EXIT_SECUREBITS] =              { "SECUREBITS",              EXIT_STATUS_SYSTEMD },
+        [EXIT_SETSCHEDULER] =            { "SETSCHEDULER",            EXIT_STATUS_SYSTEMD },
+        [EXIT_CPUAFFINITY] =             { "CPUAFFINITY",             EXIT_STATUS_SYSTEMD },
+        [EXIT_GROUP] =                   { "GROUP",                   EXIT_STATUS_SYSTEMD },
+        [EXIT_USER] =                    { "USER",                    EXIT_STATUS_SYSTEMD },
+        [EXIT_CAPABILITIES] =            { "CAPABILITIES",            EXIT_STATUS_SYSTEMD },
+        [EXIT_CGROUP] =                  { "CGROUP",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_SETSID] =                  { "SETSID",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_CONFIRM] =                 { "CONFIRM",                 EXIT_STATUS_SYSTEMD },
+        [EXIT_STDERR] =                  { "STDERR",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_PAM] =                     { "PAM",                     EXIT_STATUS_SYSTEMD },
+        [EXIT_NETWORK] =                 { "NETWORK",                 EXIT_STATUS_SYSTEMD },
+        [EXIT_NAMESPACE] =               { "NAMESPACE",               EXIT_STATUS_SYSTEMD },
+        [EXIT_NO_NEW_PRIVILEGES] =       { "NO_NEW_PRIVILEGES",       EXIT_STATUS_SYSTEMD },
+        [EXIT_SECCOMP] =                 { "SECCOMP",                 EXIT_STATUS_SYSTEMD },
+        [EXIT_SELINUX_CONTEXT] =         { "SELINUX_CONTEXT",         EXIT_STATUS_SYSTEMD },
+        [EXIT_PERSONALITY] =             { "PERSONALITY",             EXIT_STATUS_SYSTEMD },
+        [EXIT_APPARMOR_PROFILE] =        { "APPARMOR",                EXIT_STATUS_SYSTEMD },
+        [EXIT_ADDRESS_FAMILIES] =        { "ADDRESS_FAMILIES",        EXIT_STATUS_SYSTEMD },
+        [EXIT_RUNTIME_DIRECTORY] =       { "RUNTIME_DIRECTORY",       EXIT_STATUS_SYSTEMD },
+        [EXIT_CHOWN] =                   { "CHOWN",                   EXIT_STATUS_SYSTEMD },
+        [EXIT_SMACK_PROCESS_LABEL] =     { "SMACK_PROCESS_LABEL",     EXIT_STATUS_SYSTEMD },
+        [EXIT_KEYRING] =                 { "KEYRING",                 EXIT_STATUS_SYSTEMD },
+        [EXIT_STATE_DIRECTORY] =         { "STATE_DIRECTORY",         EXIT_STATUS_SYSTEMD },
+        [EXIT_CACHE_DIRECTORY] =         { "CACHE_DIRECTORY",         EXIT_STATUS_SYSTEMD },
+        [EXIT_LOGS_DIRECTORY] =          { "LOGS_DIRECTORY",          EXIT_STATUS_SYSTEMD },
+        [EXIT_CONFIGURATION_DIRECTORY] = { "CONFIGURATION_DIRECTORY", EXIT_STATUS_SYSTEMD },
+        [EXIT_NUMA_POLICY] =             { "NUMA_POLICY",             EXIT_STATUS_SYSTEMD },
+        [EXIT_EXCEPTION] =               { "EXCEPTION",               EXIT_STATUS_SYSTEMD },
+
+        [EXIT_INVALIDARGUMENT] =         { "INVALIDARGUMENT",         EXIT_STATUS_LSB },
+        [EXIT_NOTIMPLEMENTED] =          { "NOTIMPLEMENTED",          EXIT_STATUS_LSB },
+        [EXIT_NOPERMISSION] =            { "NOPERMISSION",            EXIT_STATUS_LSB },
+        [EXIT_NOTINSTALLED] =            { "NOTINSTALLED",            EXIT_STATUS_LSB },
+        [EXIT_NOTCONFIGURED] =           { "NOTCONFIGURED",           EXIT_STATUS_LSB },
+        [EXIT_NOTRUNNING] =              { "NOTRUNNING",              EXIT_STATUS_LSB },
+
+        [EX_USAGE] =                     { "USAGE",                   EXIT_STATUS_BSD },
+        [EX_DATAERR] =                   { "DATAERR",                 EXIT_STATUS_BSD },
+        [EX_NOINPUT] =                   { "NOINPUT",                 EXIT_STATUS_BSD },
+        [EX_NOUSER] =                    { "NOUSER",                  EXIT_STATUS_BSD },
+        [EX_NOHOST] =                    { "NOHOST",                  EXIT_STATUS_BSD },
+        [EX_UNAVAILABLE] =               { "UNAVAILABLE",             EXIT_STATUS_BSD },
+        [EX_SOFTWARE] =                  { "SOFTWARE",                EXIT_STATUS_BSD },
+        [EX_OSERR] =                     { "OSERR",                   EXIT_STATUS_BSD },
+        [EX_OSFILE] =                    { "OSFILE",                  EXIT_STATUS_BSD },
+        [EX_CANTCREAT] =                 { "CANTCREAT",               EXIT_STATUS_BSD },
+        [EX_IOERR] =                     { "IOERR",                   EXIT_STATUS_BSD },
+        [EX_TEMPFAIL] =                  { "TEMPFAIL",                EXIT_STATUS_BSD },
+        [EX_PROTOCOL] =                  { "PROTOCOL",                EXIT_STATUS_BSD },
+        [EX_NOPERM] =                    { "NOPERM",                  EXIT_STATUS_BSD },
+        [EX_CONFIG] =                    { "CONFIG",                  EXIT_STATUS_BSD },
+};
+
+const char* exit_status_to_string(int code, ExitStatusClass class) {
+        if (code < 0 || (size_t) code >= ELEMENTSOF(exit_status_mappings))
+                return NULL;
+        return FLAGS_SET(exit_status_mappings[code].class, class) ? exit_status_mappings[code].name : NULL;
+}
 
-                case EXIT_NOTRUNNING:
-                        return "NOTRUNNING";
-                }
+const char* exit_status_class(int code) {
+        if (code < 0 || (size_t) code >= ELEMENTSOF(exit_status_mappings))
+                return NULL;
+
+        switch (exit_status_mappings[code].class) {
+        case EXIT_STATUS_GLIBC:
+                return "glibc";
+        case EXIT_STATUS_SYSTEMD:
+                return "systemd";
+        case EXIT_STATUS_LSB:
+                return "LSB";
+        case EXIT_STATUS_BSD:
+                return "BSD";
+        default: return NULL;
         }
+}
 
-        if (level == EXIT_STATUS_FULL) {
-                switch (status) { /* Optionally, we support BSD exit statusses */
-
-                case EX_USAGE:
-                        return "USAGE";
-
-                case EX_DATAERR:
-                        return "DATAERR";
-
-                case EX_NOINPUT:
-                        return "NOINPUT";
-
-                case EX_NOUSER:
-                        return "NOUSER";
-
-                case EX_NOHOST:
-                        return "NOHOST";
-
-                case EX_UNAVAILABLE:
-                        return "UNAVAILABLE";
-
-                case EX_SOFTWARE:
-                        return "SOFTWARE";
-
-                case EX_OSERR:
-                        return "OSERR";
-
-                case EX_OSFILE:
-                        return "OSFILE";
-
-                case EX_CANTCREAT:
-                        return "CANTCREAT";
-
-                case EX_IOERR:
-                        return "IOERR";
-
-                case EX_TEMPFAIL:
-                        return "TEMPFAIL";
+int exit_status_from_string(const char *s) {
+        uint8_t val;
+        int r;
 
-                case EX_PROTOCOL:
-                        return "PROTOCOL";
+        for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++)
+                if (streq_ptr(s, exit_status_mappings[i].name))
+                        return i;
 
-                case EX_NOPERM:
-                        return "NOPERM";
+        r = safe_atou8(s, &val);
+        if (r < 0)
+                return r;
 
-                case EX_CONFIG:
-                        return "CONFIG";
-                }
-        }
-
-        return NULL;
+        return val;
 }
 
-bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status) {
-
+bool is_clean_exit(int code, int status, ExitClean clean, const ExitStatusSet *success_status) {
         if (code == CLD_EXITED)
                 return status == 0 ||
                        (success_status &&
-                        set_contains(success_status->status, INT_TO_PTR(status)));
+                        bitmap_isset(&success_status->status, status));
 
-        /* If a daemon does not implement handlers for some of the signals that's not considered an unclean shutdown */
+        /* If a daemon does not implement handlers for some of the signals, we do not consider this an
+           unclean shutdown */
         if (code == CLD_KILLED)
                 return
                         (clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) ||
                         (success_status &&
-                         set_contains(success_status->signal, INT_TO_PTR(status)));
+                         bitmap_isset(&success_status->signal, status));
 
         return false;
 }
@@ -261,26 +154,22 @@ bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success
 void exit_status_set_free(ExitStatusSet *x) {
         assert(x);
 
-        x->status = set_free(x->status);
-        x->signal = set_free(x->signal);
+        bitmap_clear(&x->status);
+        bitmap_clear(&x->signal);
 }
 
-bool exit_status_set_is_empty(ExitStatusSet *x) {
+bool exit_status_set_is_empty(const ExitStatusSet *x) {
         if (!x)
                 return true;
 
-        return set_isempty(x->status) && set_isempty(x->signal);
+        return bitmap_isclear(&x->status) && bitmap_isclear(&x->signal);
 }
 
-bool exit_status_set_test(ExitStatusSet *x, int code, int status) {
-
-        if (exit_status_set_is_empty(x))
-                return false;
-
-        if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status)))
+bool exit_status_set_test(const ExitStatusSet *x, int code, int status) {
+        if (code == CLD_EXITED && bitmap_isset(&x->status, status))
                 return true;
 
-        if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && set_contains(x->signal, INT_TO_PTR(status)))
+        if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && bitmap_isset(&x->signal, status))
                 return true;
 
         return false;
index 5637e6aa04ddd9df731caf843baebc5b29667539..d6da8c19b964fbf33b588cef3902e2eea2d2c2e2 100644 (file)
@@ -3,12 +3,12 @@
 
 #include <stdbool.h>
 
+#include "bitmap.h"
 #include "hashmap.h"
 #include "macro.h"
-#include "set.h"
 
-/* This defines pretty names for the LSB 'start' verb exit codes. Note that they shouldn't be confused with the LSB
- * 'status' verb exit codes which are defined very differently. For details see:
+/* This defines pretty names for the LSB 'start' verb exit codes. Note that they shouldn't be confused with
+ * the LSB 'status' verb exit codes which are defined very differently. For details see:
  *
  * https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
  */
@@ -25,8 +25,8 @@ enum {
 
         /* BSD's sysexits.h defines a couple EX_xyz exit codes in the range 64 â€¦ 78 */
 
-        /* The LSB suggests that error codes >= 200 are "reserved". We use them here under the assumption that they
-         * hence are unused by init scripts. */
+        /* The LSB suggests that error codes >= 200 are "reserved". We use them here under the assumption
+         * that they hence are unused by init scripts. */
         EXIT_CHDIR = 200,
         EXIT_NICE,
         EXIT_FDS,
@@ -74,27 +74,37 @@ enum {
         EXIT_EXCEPTION = 255,  /* Whenever we want to propagate an abnormal/signal exit, in line with bash */
 };
 
-typedef enum ExitStatusLevel {
-        EXIT_STATUS_MINIMAL,   /* only cover libc EXIT_STATUS/EXIT_FAILURE */
-        EXIT_STATUS_SYSTEMD,   /* cover libc and systemd's own exit codes */
-        EXIT_STATUS_LSB,       /* cover libc, systemd's own and LSB exit codes */
-        EXIT_STATUS_FULL,      /* cover libc, systemd's own, LSB and BSD (EX_xyz) exit codes */
-} ExitStatusLevel;
+typedef enum ExitStatusClass {
+        EXIT_STATUS_GLIBC   = 1 << 0,  /* libc EXIT_STATUS/EXIT_FAILURE */
+        EXIT_STATUS_SYSTEMD = 1 << 1,  /* systemd's own exit codes */
+        EXIT_STATUS_LSB     = 1 << 2,  /* LSB exit codes */
+        EXIT_STATUS_BSD     = 1 << 3,  /* BSD (EX_xyz) exit codes */
+        EXIT_STATUS_FULL    = EXIT_STATUS_GLIBC | EXIT_STATUS_SYSTEMD | EXIT_STATUS_LSB | EXIT_STATUS_BSD,
+} ExitStatusClass;
 
 typedef struct ExitStatusSet {
-        Set *status;
-        Set *signal;
+        Bitmap status;
+        Bitmap signal;
 } ExitStatusSet;
 
-const char* exit_status_to_string(int status, ExitStatusLevel level) _const_;
+const char* exit_status_to_string(int code, ExitStatusClass class) _const_;
+const char* exit_status_class(int code) _const_;
+int exit_status_from_string(const char *s) _pure_;
+
+typedef struct ExitStatusMapping {
+        const char *name;
+        ExitStatusClass class;
+} ExitStatusMapping;
+
+extern const ExitStatusMapping exit_status_mappings[256];
 
 typedef enum ExitClean {
         EXIT_CLEAN_DAEMON,
         EXIT_CLEAN_COMMAND,
 } ExitClean;
 
-bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status);
+bool is_clean_exit(int code, int status, ExitClean clean, const ExitStatusSet *success_status);
 
 void exit_status_set_free(ExitStatusSet *x);
-bool exit_status_set_is_empty(ExitStatusSet *x);
-bool exit_status_set_test(ExitStatusSet *x, int code, int status);
+bool exit_status_set_is_empty(const ExitStatusSet *x);
+bool exit_status_set_test(const ExitStatusSet *x, int code, int status);
index 54e8f68ab0b79e0521f11811811257c37014d931..e41cbb1720ba6ba976ac0de4123c2a6d61604c15 100644 (file)
@@ -9,6 +9,7 @@
 #include "format-table.h"
 #include "format-util.h"
 #include "gunicode.h"
+#include "in-addr-util.h"
 #include "memory-util.h"
 #include "pager.h"
 #include "parse-util.h"
@@ -77,13 +78,18 @@ typedef struct TableData {
                 uint64_t size;
                 char string[0];
                 int int_val;
+                int8_t int8;
+                int16_t int16;
                 int32_t int32;
                 int64_t int64;
                 unsigned uint_val;
+                uint8_t uint8;
+                uint16_t uint16;
                 uint32_t uint32;
                 uint64_t uint64;
                 int percent;        /* we use 'int' as datatype for percent values in order to match the result of parse_percent() */
                 int ifindex;
+                union in_addr_union address;
                 /* â€¦ add more here as we start supporting more cell data types â€¦ */
         };
 } TableData;
@@ -249,12 +255,26 @@ static size_t table_data_size(TableDataType type, const void *data) {
         case TABLE_UINT32:
                 return sizeof(uint32_t);
 
+        case TABLE_INT16:
+        case TABLE_UINT16:
+                return sizeof(uint16_t);
+
+        case TABLE_INT8:
+        case TABLE_UINT8:
+                return sizeof(uint8_t);
+
         case TABLE_INT:
         case TABLE_UINT:
         case TABLE_PERCENT:
         case TABLE_IFINDEX:
                 return sizeof(int);
 
+        case TABLE_IN_ADDR:
+                return sizeof(struct in_addr);
+
+        case TABLE_IN6_ADDR:
+                return sizeof(struct in6_addr);
+
         default:
                 assert_not_reached("Uh? Unexpected cell type");
         }
@@ -725,14 +745,19 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                         uint64_t size;
                         usec_t usec;
                         int int_val;
+                        int8_t int8;
+                        int16_t int16;
                         int32_t int32;
                         int64_t int64;
                         unsigned uint_val;
+                        uint8_t uint8;
+                        uint16_t uint16;
                         uint32_t uint32;
                         uint64_t uint64;
                         int percent;
                         int ifindex;
                         bool b;
+                        union in_addr_union address;
                 } buffer;
 
                 switch (type) {
@@ -770,6 +795,24 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                         data = &buffer.int_val;
                         break;
 
+                case TABLE_INT8: {
+                        int x = va_arg(ap, int);
+                        assert(x >= INT8_MIN && x <= INT8_MAX);
+
+                        buffer.int8 = x;
+                        data = &buffer.int8;
+                        break;
+                }
+
+                case TABLE_INT16: {
+                        int x = va_arg(ap, int);
+                        assert(x >= INT16_MIN && x <= INT16_MAX);
+
+                        buffer.int16 = x;
+                        data = &buffer.int16;
+                        break;
+                }
+
                 case TABLE_INT32:
                         buffer.int32 = va_arg(ap, int32_t);
                         data = &buffer.int32;
@@ -785,6 +828,24 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                         data = &buffer.uint_val;
                         break;
 
+                case TABLE_UINT8: {
+                        unsigned x = va_arg(ap, unsigned);
+                        assert(x <= UINT8_MAX);
+
+                        buffer.uint8 = x;
+                        data = &buffer.uint8;
+                        break;
+                }
+
+                case TABLE_UINT16: {
+                        unsigned x = va_arg(ap, unsigned);
+                        assert(x <= UINT16_MAX);
+
+                        buffer.uint16 = x;
+                        data = &buffer.uint16;
+                        break;
+                }
+
                 case TABLE_UINT32:
                         buffer.uint32 = va_arg(ap, uint32_t);
                         data = &buffer.uint32;
@@ -805,6 +866,16 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                         data = &buffer.ifindex;
                         break;
 
+                case TABLE_IN_ADDR:
+                        buffer.address = *va_arg(ap, union in_addr_union *);
+                        data = &buffer.address.in;
+                        break;
+
+                case TABLE_IN6_ADDR:
+                        buffer.address = *va_arg(ap, union in_addr_union *);
+                        data = &buffer.address.in6;
+                        break;
+
                 case TABLE_SET_MINIMUM_WIDTH: {
                         size_t w = va_arg(ap, size_t);
 
@@ -989,6 +1060,12 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
                 case TABLE_INT:
                         return CMP(a->int_val, b->int_val);
 
+                case TABLE_INT8:
+                        return CMP(a->int8, b->int8);
+
+                case TABLE_INT16:
+                        return CMP(a->int16, b->int16);
+
                 case TABLE_INT32:
                         return CMP(a->int32, b->int32);
 
@@ -998,6 +1075,12 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
                 case TABLE_UINT:
                         return CMP(a->uint_val, b->uint_val);
 
+                case TABLE_UINT8:
+                        return CMP(a->uint8, b->uint8);
+
+                case TABLE_UINT16:
+                        return CMP(a->uint16, b->uint16);
+
                 case TABLE_UINT32:
                         return CMP(a->uint32, b->uint32);
 
@@ -1010,6 +1093,12 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
                 case TABLE_IFINDEX:
                         return CMP(a->ifindex, b->ifindex);
 
+                case TABLE_IN_ADDR:
+                        return CMP(a->address.in.s_addr, b->address.in.s_addr);
+
+                case TABLE_IN6_ADDR:
+                        return memcmp(&a->address.in6, &b->address.in6, FAMILY_ADDRESS_SIZE(AF_INET6));
+
                 default:
                         ;
                 }
@@ -1163,6 +1252,30 @@ static const char *table_data_format(Table *t, TableData *d) {
                 break;
         }
 
+        case TABLE_INT8: {
+                _cleanup_free_ char *p;
+
+                p = new(char, DECIMAL_STR_WIDTH(d->int8) + 1);
+                if (!p)
+                        return NULL;
+
+                sprintf(p, "%" PRIi8, d->int8);
+                d->formatted = TAKE_PTR(p);
+                break;
+        }
+
+        case TABLE_INT16: {
+                _cleanup_free_ char *p;
+
+                p = new(char, DECIMAL_STR_WIDTH(d->int16) + 1);
+                if (!p)
+                        return NULL;
+
+                sprintf(p, "%" PRIi16, d->int16);
+                d->formatted = TAKE_PTR(p);
+                break;
+        }
+
         case TABLE_INT32: {
                 _cleanup_free_ char *p;
 
@@ -1199,6 +1312,30 @@ static const char *table_data_format(Table *t, TableData *d) {
                 break;
         }
 
+        case TABLE_UINT8: {
+                _cleanup_free_ char *p;
+
+                p = new(char, DECIMAL_STR_WIDTH(d->uint8) + 1);
+                if (!p)
+                        return NULL;
+
+                sprintf(p, "%" PRIu8, d->uint8);
+                d->formatted = TAKE_PTR(p);
+                break;
+        }
+
+        case TABLE_UINT16: {
+                _cleanup_free_ char *p;
+
+                p = new(char, DECIMAL_STR_WIDTH(d->uint16) + 1);
+                if (!p)
+                        return NULL;
+
+                sprintf(p, "%" PRIu16, d->uint16);
+                d->formatted = TAKE_PTR(p);
+                break;
+        }
+
         case TABLE_UINT32: {
                 _cleanup_free_ char *p;
 
@@ -1236,7 +1373,7 @@ static const char *table_data_format(Table *t, TableData *d) {
         }
 
         case TABLE_IFINDEX: {
-                _cleanup_free_ char *p;
+                _cleanup_free_ char *p = NULL;
                 char name[IF_NAMESIZE + 1];
 
                 if (format_ifname(d->ifindex, name)) {
@@ -1252,6 +1389,18 @@ static const char *table_data_format(Table *t, TableData *d) {
                 break;
         }
 
+        case TABLE_IN_ADDR:
+        case TABLE_IN6_ADDR: {
+                _cleanup_free_ char *p = NULL;
+
+                if (in_addr_to_string(d->type == TABLE_IN_ADDR ? AF_INET : AF_INET6,
+                                      &d->address, &p) < 0)
+                        return NULL;
+
+                d->formatted = TAKE_PTR(p);
+                break;
+        }
+
         default:
                 assert_not_reached("Unexpected type?");
         }
@@ -1775,6 +1924,12 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
         case TABLE_INT:
                 return json_variant_new_integer(ret, d->int_val);
 
+        case TABLE_INT8:
+                return json_variant_new_integer(ret, d->int8);
+
+        case TABLE_INT16:
+                return json_variant_new_integer(ret, d->int16);
+
         case TABLE_INT32:
                 return json_variant_new_integer(ret, d->int32);
 
@@ -1784,6 +1939,12 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
         case TABLE_UINT:
                 return json_variant_new_unsigned(ret, d->uint_val);
 
+        case TABLE_UINT8:
+                return json_variant_new_unsigned(ret, d->uint8);
+
+        case TABLE_UINT16:
+                return json_variant_new_unsigned(ret, d->uint16);
+
         case TABLE_UINT32:
                 return json_variant_new_unsigned(ret, d->uint32);
 
@@ -1796,6 +1957,12 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
         case TABLE_IFINDEX:
                 return json_variant_new_integer(ret, d->ifindex);
 
+        case TABLE_IN_ADDR:
+                return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET));
+
+        case TABLE_IN6_ADDR:
+                return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET6));
+
         default:
                 return -EINVAL;
         }
index e18ca307d177fd415459891a90d778fb58ba32b2..aacf978978847180e003065e2f11175f84e97528 100644 (file)
@@ -20,13 +20,19 @@ typedef enum TableDataType {
         TABLE_SIZE,
         TABLE_BPS,
         TABLE_INT,
+        TABLE_INT8,
+        TABLE_INT16,
         TABLE_INT32,
         TABLE_INT64,
         TABLE_UINT,
+        TABLE_UINT8,
+        TABLE_UINT16,
         TABLE_UINT32,
         TABLE_UINT64,
         TABLE_PERCENT,
         TABLE_IFINDEX,
+        TABLE_IN_ADDR,  /* Takes a union in_addr_union (or a struct in_addr) */
+        TABLE_IN6_ADDR, /* Takes a union in_addr_union (or a struct in6_addr) */
         _TABLE_DATA_TYPE_MAX,
 
         /* The following are not really data types, but commands for table_add_cell_many() to make changes to
index 95a85245946a88f0f5f65ba4bd529a2863e89b1a..880a04411c11e98a84ad0d0e15d95f19086fb392 100644 (file)
@@ -4380,7 +4380,7 @@ static void print_status_info(
 
                         printf("status=%i", p->status);
 
-                        c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD);
+                        c = exit_status_to_string(p->status, EXIT_STATUS_GLIBC | EXIT_STATUS_SYSTEMD);
                         if (c)
                                 printf("/%s", c);
 
@@ -4421,7 +4421,8 @@ static void print_status_info(
 
                                         printf("status=%i", i->exit_status);
 
-                                        c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD);
+                                        c = exit_status_to_string(i->exit_status,
+                                                                  EXIT_STATUS_GLIBC | EXIT_STATUS_SYSTEMD);
                                         if (c)
                                                 printf("/%s", c);
 
@@ -4910,17 +4911,17 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
 
                 } else if (endswith(name, "ExitStatus") && streq(contents, "aiai")) {
                         const int32_t *status, *signal;
-                        size_t sz_status, sz_signal, i;
+                        size_t n_status, n_signal, i;
 
                         r = sd_bus_message_enter_container(m, 'r', "aiai");
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
-                        r = sd_bus_message_read_array(m, 'i', (const void **) &status, &sz_status);
+                        r = sd_bus_message_read_array(m, 'i', (const void **) &status, &n_status);
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
-                        r = sd_bus_message_read_array(m, 'i', (const void **) &signal, &sz_signal);
+                        r = sd_bus_message_read_array(m, 'i', (const void **) &signal, &n_signal);
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
@@ -4928,10 +4929,10 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
-                        sz_status /= sizeof(int32_t);
-                        sz_signal /= sizeof(int32_t);
+                        n_status /= sizeof(int32_t);
+                        n_signal /= sizeof(int32_t);
 
-                        if (all || sz_status > 0 || sz_signal > 0) {
+                        if (all || n_status > 0 || n_signal > 0) {
                                 bool first = true;
 
                                 if (!value) {
@@ -4939,10 +4940,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
                                         fputc('=', stdout);
                                 }
 
-                                for (i = 0; i < sz_status; i++) {
-                                        if (status[i] < 0 || status[i] > 255)
-                                                continue;
-
+                                for (i = 0; i < n_status; i++) {
                                         if (first)
                                                 first = false;
                                         else
@@ -4951,19 +4949,20 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
                                         printf("%"PRIi32, status[i]);
                                 }
 
-                                for (i = 0; i < sz_signal; i++) {
+                                for (i = 0; i < n_signal; i++) {
                                         const char *str;
 
                                         str = signal_to_string((int) signal[i]);
-                                        if (!str)
-                                                continue;
 
                                         if (first)
                                                 first = false;
                                         else
                                                 fputc(' ', stdout);
 
-                                        fputs(str, stdout);
+                                        if (str)
+                                                fputs(str, stdout);
+                                        else
+                                                printf("%"PRIi32, status[i]);
                                 }
 
                                 fputc('\n', stdout);
index 0595cfe37a6dc271af20e479b9379edbef86cbad..5625e682cf1cbc2f24c355bab54075867b961e97 100644 (file)
@@ -305,6 +305,10 @@ tests += [
          [],
          []],
 
+        [['src/test/test-exit-status.c'],
+         [],
+         []],
+
         [['src/test/test-specifier.c'],
          [],
          []],
diff --git a/src/test/test-exit-status.c b/src/test/test-exit-status.c
new file mode 100644 (file)
index 0000000..3bcebd0
--- /dev/null
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "exit-status.h"
+#include "tests.h"
+
+static void test_exit_status_to_string(void) {
+        log_info("/* %s */", __func__);
+
+        for (int i = -1; i <= 256; i++) {
+                const char *s, *class;
+
+                s = exit_status_to_string(i, EXIT_STATUS_FULL);
+                class = exit_status_class(i);
+                log_info("%d: %s%s%s%s",
+                         i, s ?: "-",
+                         class ? " (" : "", class ?: "", class ? ")" : "");
+
+                if (s)
+                        assert_se(exit_status_from_string(s) == i);
+        }
+}
+
+static void test_exit_status_from_string(void) {
+        log_info("/* %s */", __func__);
+
+        assert_se(exit_status_from_string("11") == 11);
+        assert_se(exit_status_from_string("-1") == -ERANGE);
+        assert_se(exit_status_from_string("256") == -ERANGE);
+        assert_se(exit_status_from_string("foo") == -EINVAL);
+        assert_se(exit_status_from_string("SUCCESS") == 0);
+        assert_se(exit_status_from_string("FAILURE") == 1);
+}
+
+int main(int argc, char *argv[]) {
+        test_setup_logging(LOG_DEBUG);
+
+        test_exit_status_to_string();
+        test_exit_status_from_string();
+
+        return 0;
+}
index e17140ea0ce79926ba07be3742565285f71385bd..5f5245e48a3e53bba2de0982ac1c943fe252d558 100644 (file)
@@ -39,6 +39,7 @@
 #include "plymouth-util.h"
 #include "pretty-print.h"
 #include "process-util.h"
+#include "set.h"
 #include "signal-util.h"
 #include "socket-util.h"
 #include "string-util.h"
index 64b0b9b102061e2f8467b9b33e1e9422995e662b..3473a7eb7e5bc1ca2f7e2cde267dcf5d2079d927 100644 (file)
@@ -1186,6 +1186,8 @@ static int parse_file(UdevRules *rules, const char *filename) {
                 return -errno;
         }
 
+        (void) fd_warn_permissions(filename, fileno(f));
+
         if (null_or_empty_fd(fileno(f))) {
                 log_debug("Skipping empty file: %s", filename);
                 return 0;
index 49a04f313a827c5e33657639e5bb34e108a53e2c..76313cf026583c233b4acf194bc71765a0bf5771 100755 (executable)
@@ -705,6 +705,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         self.assertEqual(1,         int(read_link_attr('bridge99', 'bridge', 'stp_state')))
         self.assertEqual(3,         int(read_link_attr('bridge99', 'bridge', 'multicast_igmp_version')))
 
+        output = check_output(*networkctl_cmd, 'status', 'bridge99')
+        print(output)
+        self.assertRegex(output, 'Priority: 9')
+        self.assertRegex(output, 'STP: yes')
+        self.assertRegex(output, 'Multicast IGMP Version: 3')
+
     def test_bond(self):
         copy_unit_to_networkd_unit_path('25-bond.netdev', '25-bond-balanced-tlb.netdev')
         start_networkd()
@@ -1266,6 +1272,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         self.assertRegex(output, '00:11:22:33:44:66 dst 10.0.0.6 self permanent')
         self.assertRegex(output, '00:11:22:33:44:77 dst 10.0.0.7 self permanent')
 
+        output = check_output(*networkctl_cmd, 'status', 'vxlan99')
+        print(output)
+        self.assertRegex(output, 'VNI: 999')
+        self.assertRegex(output, 'Destination Port: 5555')
+        self.assertRegex(output, 'Underlying Device: test1')
+
     def test_macsec(self):
         copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
                                         'macsec.network', '12-dummy.netdev')
index 9e2f04bfefdbe358369dc66e34a582f742bd932e..5d70aafb29f7cf411906b7c0925788dae00e7386 100644 (file)
@@ -18,5 +18,5 @@ Before=shutdown.target
 [Service]
 Type=oneshot
 ExecStart=@rootbindir@/systemd-tmpfiles --clean
-SuccessExitStatus=65
+SuccessExitStatus=DATAERR
 IOSchedulingClass=idle
index 50df15c291c15400c105cd6546086b408224cb35..ed52db4953a6c7a5a4f309520a387414d779a60d 100644 (file)
@@ -19,4 +19,4 @@ Before=sysinit.target local-fs-pre.target systemd-udevd.service shutdown.target
 Type=oneshot
 RemainAfterExit=yes
 ExecStart=@rootbindir@/systemd-tmpfiles --prefix=/dev --create --boot
-SuccessExitStatus=65 73
+SuccessExitStatus=DATAERR CANTCREAT
index b02bbcd61b7004a481b4b56f4aea278bb8cb8e3c..32a475d71531a2121ebecf89388014733228c1e1 100644 (file)
@@ -20,4 +20,4 @@ RefuseManualStop=yes
 Type=oneshot
 RemainAfterExit=yes
 ExecStart=@rootbindir@/systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
-SuccessExitStatus=65 73
+SuccessExitStatus=DATAERR CANTCREAT
index 9cd19720d3e1d481beed7847f0e0171b3477c628..306b064e895330e443b43af8e14407eb5cc1d691 100644 (file)
@@ -17,5 +17,5 @@ Before=basic.target shutdown.target
 [Service]
 Type=oneshot
 ExecStart=@rootbindir@/systemd-tmpfiles --user --clean
-SuccessExitStatus=65
+SuccessExitStatus=DATAERR
 IOSchedulingClass=idle
index 6467dab8969ab1593a9f8390456ab2403b4a9327..a852ef5748038647bccd69874590414bb532377f 100644 (file)
@@ -19,7 +19,7 @@ RefuseManualStop=yes
 Type=oneshot
 RemainAfterExit=yes
 ExecStart=@rootbindir@/systemd-tmpfiles --user --create --remove --boot
-SuccessExitStatus=65
+SuccessExitStatus=DATAERR
 
 [Install]
 WantedBy=basic.target