]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #26574 from YHNdnzj/sd-login-new-interface
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 24 Feb 2023 00:20:52 +0000 (09:20 +0900)
committerGitHub <noreply@github.com>
Fri, 24 Feb 2023 00:20:52 +0000 (09:20 +0900)
sd-login: add two interfaces for retriving session info

31 files changed:
catalog/systemd.catalog.in
man/sd-event.xml
man/sd_event_add_memory_pressure.xml
man/systemd-stub.xml
src/basic/cgroup-util.h
src/basic/chase-symlinks.c
src/basic/io-util.c
src/basic/io-util.h
src/basic/time-util.c
src/basic/time-util.h
src/boot/bootctl-status.c
src/boot/efi/efi-string.c
src/boot/efi/efi-string.h
src/boot/efi/stub.c
src/boot/efi/test-efi-string.c
src/boot/efi/vmm.c
src/boot/efi/vmm.h
src/core/dbus-socket.c
src/core/execute.c
src/core/scope.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/journal-send.c
src/shared/cgroup-setup.h
src/shared/format-table.c
src/shared/format-table.h
src/shared/loop-util.c
src/systemctl/systemctl-list-units.c
src/systemctl/systemctl-list-units.h
src/test/test-mempress.c
src/test/test-time-util.c

index 82d4820b80602e63f5602df0e8c1150a657af5db..d3d03dafe7162767e5f3efed81cdb7f41d5efd35 100644 (file)
@@ -558,7 +558,7 @@ Support: %SUPPORT_URL%
 Memory of process @_PID@ (@_COMM@) has been trimmed.
 
 Either on user request or as result of a memory pressure event, memory of the
-process has been trimmed, returning unneded allocation caches and other
+process has been trimmed, returning unneeded allocation caches and other
 resources back to the OS kernel, making them available for other components of
 the OS.
 
index 1bcf4e32a5b2d653280f0b3180619cfa96fd531e..cb3108aee6a7d53926790275f55828db3e4e7e82 100644 (file)
@@ -48,6 +48,7 @@
     <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_event_add_memory_pressure</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_add_memory_pressure</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_source_set_priority</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
index 46cae0d16db8ee93c5925bdfa3addd427198b9e2..5baa411dfa79e5084c2e2ee769efe434763fd90d 100644 (file)
     <literal>full</literal> as second parameter, the latter takes threshold and period times in microseconds
     as parameters. For details about these three parameters see the PSI documentation. Note that these two
     calls must be invoked immediately after allocating the event source, as they must be configured before
-    polling begins. Also note that these calls will fail if memory pressure paramterization has been passed
+    polling begins. Also note that these calls will fail if memory pressure parameterization has been passed
     in via the <varname>$MEMORY_PRESSURE_WATCH</varname>/<varname>$MEMORY_PRESSURE_WRITE</varname>
     environment variables (or in other words: configuration supplied by a service manager wins over internal
     settings).</para>
 
           <listitem><para>This is returned by <function>sd_event_source_set_memory_pressure_type()</function>
           and <function>sd_event_source_set_memory_pressure_period()</function> if invoked on event sources
-          at a time later than immediately after allocting them.</para></listitem>
+          at a time later than immediately after allocating them.</para></listitem>
         </varlistentry>
 
         <varlistentry>
index c2301687806ef4d2c190234fd30f095619dc1348..7934f344f86446770f21e3552ca4557a6976d7e2 100644 (file)
     default, this is done for the TPM2 PCR signature and public key files.</para>
   </refsect1>
 
+  <refsect1>
+    <title>SMBIOS Type 11 Strings</title>
+
+    <para><command>systemd-stub</command> can be configured using SMBIOS Type 11 strings. Applicable strings
+    consist of a name, followed by <literal>=</literal>, followed by the value.
+    <command>systemd-stub</command> will search the table for a string with a specific name, and if found,
+    use its value. The following strings are read:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>io.systemd.stub.kernel-cmdline-extra</varname></term>
+        <listitem><para>If set, the value of this string is added to the list of kernel command line
+        arguments that are passed to the kernel.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>Assembling Kernel Images</title>
 
index b69f1683dbe781e6b3ab04b1eb07a530097ca841..5de000c4ceb51a1794d4ebe21977ca84ce4a6ec3 100644 (file)
@@ -239,7 +239,6 @@ int cg_get_attribute_as_uint64(const char *controller, const char *path, const c
 /* Does a parse_boolean() on the attribute contents and sets ret accordingly */
 int cg_get_attribute_as_bool(const char *controller, const char *path, const char *attribute, bool *ret);
 
-int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid);
 int cg_get_owner(const char *controller, const char *path, uid_t *ret_uid);
 
 int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags);
index 3a4fdcd1e0247b4e53b359b2b9a7a2cab2ebacf6..7cbe0b45ae02e3bff0f41fba631297e8f463c7a9 100644 (file)
@@ -705,7 +705,7 @@ int chase_symlinks_and_unlink(
                 char **ret_path) {
 
         _cleanup_free_ char *p = NULL, *rp = NULL, *dir = NULL, *fname = NULL;
-        _cleanup_close_ int fd = -1;
+        _cleanup_close_ int fd = -EBADF;
         int r;
 
         assert(path);
index f642beca3a94ff7303e405dee2f3f9f2a34c6bef..6f6fb8068c441087ea3ffe207e7d863b7800ce61 100644 (file)
@@ -362,3 +362,13 @@ size_t iovw_size(struct iovec_wrapper *iovw) {
 
         return n;
 }
+
+void iovec_array_free(struct iovec *iov, size_t n) {
+        if (!iov)
+                return;
+
+        for (size_t i = 0; i < n; i++)
+                free(iov[i].iov_base);
+
+        free(iov);
+}
index 3afb134266a070274a116f4c5ce425504df310ec..a5c1f89a8dd4f39d985e63d039e29d2d4e18fec4 100644 (file)
@@ -74,10 +74,15 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
 
 }
 
+#define IOVEC_NULL (struct iovec) {}
 #define IOVEC_INIT(base, len) { .iov_base = (base), .iov_len = (len) }
 #define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len)
-#define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string))
-#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string)
+#define IOVEC_INIT_STRING(string)               \
+        ({                                      \
+                char *_s = (char*) (string);    \
+                IOVEC_MAKE(_s, strlen(_s));     \
+        })
+#define IOVEC_MAKE_STRING(string) IOVEC_INIT_STRING(string)
 
 char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
 char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
@@ -105,3 +110,5 @@ int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const c
 int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
 void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
 size_t iovw_size(struct iovec_wrapper *iovw);
+
+void iovec_array_free(struct iovec *iov, size_t n);
index b700f364eff9be9aac033008209d88bb18b7e399..bae62ce411f9b06ef95a6b4551fb9fc109f2faf0 100644 (file)
@@ -418,7 +418,7 @@ char *format_timestamp_style(
         return buf;
 }
 
-char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
+char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, bool implicit_left) {
         const char *s;
         usec_t n, d;
 
@@ -428,17 +428,17 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
         n = now(CLOCK_REALTIME);
         if (n > t) {
                 d = n - t;
-                s = "ago";
+                s = " ago";
         } else {
                 d = t - n;
-                s = "left";
+                s = implicit_left ? "" : " left";
         }
 
         if (d >= USEC_PER_YEAR) {
                 usec_t years = d / USEC_PER_YEAR;
                 usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
 
-                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
                                 years,
                                 years == 1 ? "year" : "years",
                                 months,
@@ -448,7 +448,7 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
                 usec_t months = d / USEC_PER_MONTH;
                 usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
 
-                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
                                 months,
                                 months == 1 ? "month" : "months",
                                 days,
@@ -458,39 +458,39 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
                 usec_t weeks = d / USEC_PER_WEEK;
                 usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
 
-                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
                                 weeks,
                                 weeks == 1 ? "week" : "weeks",
                                 days,
                                 days == 1 ? "day" : "days",
                                 s);
         } else if (d >= 2*USEC_PER_DAY)
-                (void) snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
+                (void) snprintf(buf, l, USEC_FMT " days%s", d / USEC_PER_DAY,s);
         else if (d >= 25*USEC_PER_HOUR)
-                (void) snprintf(buf, l, "1 day " USEC_FMT "h %s",
+                (void) snprintf(buf, l, "1 day " USEC_FMT "h%s",
                                 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
         else if (d >= 6*USEC_PER_HOUR)
-                (void) snprintf(buf, l, USEC_FMT "h %s",
+                (void) snprintf(buf, l, USEC_FMT "h%s",
                                 d / USEC_PER_HOUR, s);
         else if (d >= USEC_PER_HOUR)
-                (void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
+                (void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min%s",
                                 d / USEC_PER_HOUR,
                                 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
         else if (d >= 5*USEC_PER_MINUTE)
-                (void) snprintf(buf, l, USEC_FMT "min %s",
+                (void) snprintf(buf, l, USEC_FMT "min%s",
                                 d / USEC_PER_MINUTE, s);
         else if (d >= USEC_PER_MINUTE)
-                (void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
+                (void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s%s",
                                 d / USEC_PER_MINUTE,
                                 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
         else if (d >= USEC_PER_SEC)
-                (void) snprintf(buf, l, USEC_FMT "s %s",
+                (void) snprintf(buf, l, USEC_FMT "s%s",
                                 d / USEC_PER_SEC, s);
         else if (d >= USEC_PER_MSEC)
-                (void) snprintf(buf, l, USEC_FMT "ms %s",
+                (void) snprintf(buf, l, USEC_FMT "ms%s",
                                 d / USEC_PER_MSEC, s);
         else if (d > 0)
-                (void) snprintf(buf, l, USEC_FMT"us %s",
+                (void) snprintf(buf, l, USEC_FMT"us%s",
                                 d, s);
         else
                 (void) snprintf(buf, l, "now");
@@ -499,7 +499,7 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
         return buf;
 }
 
-char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
+charformat_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
         static const struct {
                 const char *suffix;
                 usec_t usec;
index c5ae0c98d493ece73adc5689765c418b7a23e184..0ed19d04adb8bbe58f15361ba19b5bee3a4e4b25 100644 (file)
@@ -124,9 +124,14 @@ struct timeval* timeval_store(struct timeval *tv, usec_t u);
 #define TIMEVAL_STORE(u) timeval_store(&(struct timeval) {}, (u))
 
 char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_;
-char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_;
+char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, bool implicit_left) _warn_unused_result_;
 char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_;
 
+_warn_unused_result_
+static inline char* format_timestamp_relative(char *buf, size_t l, usec_t t)  {
+        return format_timestamp_relative_full(buf, l, t, false);
+}
+
 _warn_unused_result_
 static inline char* format_timestamp(char *buf, size_t l, usec_t t) {
         return format_timestamp_style(buf, l, t, TIMESTAMP_PRETTY);
index 8586e64480bf10f6881681c0534ec28cf0b543dd..e25cd963ae42fcf1a17a9bc5d7d55001bf833851 100644 (file)
@@ -718,8 +718,8 @@ static int cleanup_orphaned_files(
 
         _cleanup_(hashmap_free_free_keyp) Hashmap *known_files = NULL;
         _cleanup_free_ char *full = NULL, *p = NULL;
-        _cleanup_close_ int dir_fd = -1;
-        int r = -1;
+        _cleanup_close_ int dir_fd = -EBADF;
+        int r;
 
         assert(config);
         assert(root);
index ee3dc1c4a9540e73286e0f9fd801bad63b77fd5e..a94e2e4c17bb0238b8f744be238f33c42bdd815c 100644 (file)
@@ -112,7 +112,7 @@ DEFINE_STRCPY(char16_t, strcpy16);
                         s++;                       \
                 }                                  \
                                                    \
-                return NULL;                       \
+                return c ? NULL : (type *) s;      \
         }
 
 DEFINE_STRCHR(char, strchr8);
@@ -212,6 +212,21 @@ char16_t *xstrn8_to_16(const char *str8, size_t n) {
         return str16;
 }
 
+char *startswith8(const char *s, const char *prefix) {
+        size_t l;
+
+        assert(prefix);
+
+        if (!s)
+                return NULL;
+
+        l = strlen8(prefix);
+        if (!strneq8(s, prefix, l))
+                return NULL;
+
+        return (char*) s + l;
+}
+
 static bool efi_fnmatch_prefix(const char16_t *p, const char16_t *h, const char16_t **ret_p, const char16_t **ret_h) {
         assert(p);
         assert(h);
@@ -863,16 +878,30 @@ char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap) {
 
 #if SD_BOOT
 /* To provide the actual implementation for these we need to remove the redirection to the builtins. */
+#  undef memchr
 #  undef memcmp
 #  undef memcpy
 #  undef memset
 #else
 /* And for userspace unit testing we need to give them an efi_ prefix. */
+#  define memchr efi_memchr
 #  define memcmp efi_memcmp
 #  define memcpy efi_memcpy
 #  define memset efi_memset
 #endif
 
+_used_ void *memchr(const void *p, int c, size_t n) {
+        if (!p || n == 0)
+                return NULL;
+
+        const uint8_t *q = p;
+        for (size_t i = 0; i < n; i++)
+                if (q[i] == (unsigned char) c)
+                        return (void *) (q + i);
+
+        return NULL;
+}
+
 _used_ int memcmp(const void *p1, const void *p2, size_t n) {
         const uint8_t *up1 = p1, *up2 = p2;
         int r;
index 6b6b0d57b4601b5fde6851aae04ff2e49b84c5be..410bfd8ef5e5dff11379ee62e72534d2b9f0bf2b 100644 (file)
@@ -101,6 +101,8 @@ static inline char16_t *xstr8_to_16(const char *str8) {
         return xstrn8_to_16(str8, strlen8(str8));
 }
 
+char *startswith8(const char *s, const char *prefix);
+
 bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack);
 
 bool parse_number8(const char *s, uint64_t *ret_u, const char **ret_tail);
@@ -151,6 +153,7 @@ _gnu_printf_(2, 0) _warn_unused_result_ char16_t *xvasprintf_status(EFI_STATUS s
  * compiling with -ffreestanding. By referring to builtins, the compiler can check arguments and do
  * optimizations again. Note that we still need to provide implementations as the compiler is free to not
  * inline its own implementation and instead issue a library call. */
+#  define memchr __builtin_memchr
 #  define memcmp __builtin_memcmp
 #  define memcpy __builtin_memcpy
 #  define memset __builtin_memset
@@ -164,6 +167,7 @@ static inline void *mempcpy(void * restrict dest, const void * restrict src, siz
 
 #else
 /* For unit testing. */
+void *efi_memchr(const void *p, int c, size_t n);
 int efi_memcmp(const void *p1, const void *p2, size_t n);
 void *efi_memcpy(void * restrict dest, const void * restrict src, size_t n);
 void *efi_memset(void *p, int c, size_t n);
index 2635445b04030ade51ef425a0d15c7b71f44de86..25c81ca164780e7bb299f93ea8815e2ababef711 100644 (file)
@@ -14,6 +14,7 @@
 #include "splash.h"
 #include "tpm-pcr.h"
 #include "util.h"
+#include "vmm.h"
 
 /* magic string to find in the binary image */
 _used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
@@ -275,6 +276,12 @@ static EFI_STATUS run(EFI_HANDLE image) {
                 mangle_stub_cmdline(cmdline);
         }
 
+        const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra");
+        if (extra) {
+                _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra);
+                cmdline = xasprintf("%ls %ls", tmp, extra16);
+        }
+
         export_variables(loaded_image);
 
         if (pack_cpio(loaded_image,
index c26973d8bd16e1a996e5f139534a1469b87c591c..d214b1536e3160863b4cd7a2bd6f2f8b22c3aefe 100644 (file)
@@ -229,6 +229,8 @@ TEST(strchr8) {
         assert_se(strchr8(str, 'a') == &str[0]);
         assert_se(strchr8(str, 'c') == &str[2]);
         assert_se(strchr8(str, 'B') == &str[4]);
+
+        assert_se(strchr8(str, 0) == str + strlen8(str));
 }
 
 TEST(strchr16) {
@@ -240,6 +242,8 @@ TEST(strchr16) {
         assert_se(strchr16(str, 'a') == &str[0]);
         assert_se(strchr16(str, 'c') == &str[2]);
         assert_se(strchr16(str, 'B') == &str[4]);
+
+        assert_se(strchr16(str, 0) == str + strlen16(str));
 }
 
 TEST(xstrndup8) {
@@ -351,6 +355,18 @@ TEST(xstrn8_to_16) {
         free(s);
 }
 
+TEST(startswith8) {
+        assert_se(streq8(startswith8("", ""), ""));
+        assert_se(streq8(startswith8("x", ""), "x"));
+        assert_se(!startswith8("", "x"));
+        assert_se(!startswith8("", "xxxxxxxx"));
+        assert_se(streq8(startswith8("xxx", "x"), "xx"));
+        assert_se(streq8(startswith8("xxx", "xx"), "x"));
+        assert_se(streq8(startswith8("xxx", "xxx"), ""));
+        assert_se(!startswith8("xxx", "xxxx"));
+        assert_se(!startswith8(NULL, ""));
+}
+
 #define TEST_FNMATCH_ONE(pattern, haystack, expect)                                     \
         ({                                                                              \
                 assert_se(fnmatch(pattern, haystack, 0) == (expect ? 0 : FNM_NOMATCH)); \
@@ -610,6 +626,19 @@ TEST(xvasprintf_status) {
         s = mfree(s);
 }
 
+TEST(efi_memchr) {
+        assert_se(streq8(efi_memchr("abcde", 'c', 5), "cde"));
+        assert_se(streq8(efi_memchr("abcde", 'c', 3), "cde"));
+        assert_se(streq8(efi_memchr("abcde", 'c', 2), NULL));
+        assert_se(streq8(efi_memchr("abcde", 'c', 7), "cde"));
+        assert_se(streq8(efi_memchr("abcde", 'q', 5), NULL));
+        assert_se(streq8(efi_memchr("abcde", 'q', 0), NULL));
+        /* Test that the character is interpreted as unsigned char. */
+        assert_se(streq8(efi_memchr("abcde", 'a', 6), efi_memchr("abcde", 'a' + 0x100, 6)));
+        assert_se(streq8(efi_memchr("abcde", 0, 6), ""));
+        assert_se(efi_memchr(NULL, 0, 0) == NULL);
+}
+
 TEST(efi_memcmp) {
         assert_se(efi_memcmp(NULL, NULL, 0) == 0);
         assert_se(efi_memcmp(NULL, NULL, 1) == 0);
index f5c17a9eca0e70144a6b02afe05be2aeaab1c2ad..c0e8823fe6d5add6d887b1f6c85a365fcae9acd4 100644 (file)
@@ -184,17 +184,23 @@ typedef struct {
         uint8_t bios_characteristics_ext[2];
 } _packed_ SmbiosTableType0;
 
-static void *find_smbios_configuration_table(uint64_t *ret_size) {
+typedef struct {
+        SmbiosHeader header;
+        uint8_t count;
+        char contents[];
+} _packed_ SmbiosTableType11;
+
+static const void *find_smbios_configuration_table(uint64_t *ret_size) {
         assert(ret_size);
 
-        Smbios3EntryPoint *entry3 = find_configuration_table(MAKE_GUID_PTR(SMBIOS3_TABLE));
+        const Smbios3EntryPoint *entry3 = find_configuration_table(MAKE_GUID_PTR(SMBIOS3_TABLE));
         if (entry3 && memcmp(entry3->anchor_string, "_SM3_", 5) == 0 &&
             entry3->entry_point_length <= sizeof(*entry3)) {
                 *ret_size = entry3->table_maximum_size;
                 return PHYSICAL_ADDRESS_TO_POINTER(entry3->table_address);
         }
 
-        SmbiosEntryPoint *entry = find_configuration_table(MAKE_GUID_PTR(SMBIOS_TABLE));
+        const SmbiosEntryPoint *entry = find_configuration_table(MAKE_GUID_PTR(SMBIOS_TABLE));
         if (entry && memcmp(entry->anchor_string, "_SM_", 4) == 0 &&
             entry->entry_point_length <= sizeof(*entry)) {
                 *ret_size = entry->table_length;
@@ -204,9 +210,9 @@ static void *find_smbios_configuration_table(uint64_t *ret_size) {
         return NULL;
 }
 
-static SmbiosHeader *get_smbios_table(uint8_t type) {
+static const SmbiosHeader *get_smbios_table(uint8_t type, uint64_t *ret_size_left) {
         uint64_t size = 0;
-        uint8_t *p = find_smbios_configuration_table(&size);
+        const uint8_t *p = find_smbios_configuration_table(&size);
         if (!p)
                 return false;
 
@@ -214,7 +220,7 @@ static SmbiosHeader *get_smbios_table(uint8_t type) {
                 if (size < sizeof(SmbiosHeader))
                         return NULL;
 
-                SmbiosHeader *header = (SmbiosHeader *) p;
+                const SmbiosHeader *header = (const SmbiosHeader *) p;
 
                 /* End of table. */
                 if (header->type == 127)
@@ -223,8 +229,11 @@ static SmbiosHeader *get_smbios_table(uint8_t type) {
                 if (size < header->length)
                         return NULL;
 
-                if (header->type == type)
+                if (header->type == type) {
+                        if (ret_size_left)
+                                *ret_size_left = size;
                         return header; /* Yay! */
+                }
 
                 /* Skip over formatted area. */
                 size -= header->length;
@@ -232,22 +241,18 @@ static SmbiosHeader *get_smbios_table(uint8_t type) {
 
                 /* Skip over string table. */
                 for (;;) {
-                        while (size > 0 && *p != '\0') {
-                                p++;
-                                size--;
-                        }
-                        if (size == 0)
+                        const uint8_t *e = memchr(p, 0, size);
+                        if (!e)
                                 return NULL;
-                        p++;
-                        size--;
 
-                        /* Double NUL terminates string table. */
-                        if (*p == '\0') {
-                                if (size == 0)
-                                        return NULL;
+                        if (e == p) {/* Double NUL byte means we've reached the end of the string table. */
                                 p++;
+                                size--;
                                 break;
                         }
+
+                        size -= e + 1 - p;
+                        p = e + 1;
                 }
         }
 
@@ -256,7 +261,7 @@ static SmbiosHeader *get_smbios_table(uint8_t type) {
 
 static bool smbios_in_hypervisor(void) {
         /* Look up BIOS Information (Type 0). */
-        SmbiosTableType0 *type0 = (SmbiosTableType0 *) get_smbios_table(0);
+        const SmbiosTableType0 *type0 = (const SmbiosTableType0 *) get_smbios_table(0, NULL);
         if (!type0 || type0->header.length < sizeof(SmbiosTableType0))
                 return false;
 
@@ -272,3 +277,32 @@ bool in_hypervisor(void) {
         cache = cpuid_in_hypervisor() || smbios_in_hypervisor();
         return cache;
 }
+
+const char* smbios_find_oem_string(const char *name) {
+        uint64_t left;
+
+        assert(name);
+
+        const SmbiosTableType11 *type11 = (const SmbiosTableType11 *) get_smbios_table(11, &left);
+        if (!type11 || type11->header.length < sizeof(SmbiosTableType11))
+                return NULL;
+
+        assert(left >= type11->header.length);
+
+        const char *s = type11->contents;
+        left -= type11->header.length;
+
+        for (const char *p = s; p < s + left; ) {
+                const char *e = memchr(p, 0, s + left - p);
+                if (!e || e == p) /* Double NUL byte means we've reached the end of the OEM strings. */
+                        break;
+
+                const char *eq = startswith8(p, name);
+                if (eq && *eq == '=')
+                        return eq + 1;
+
+                p = e + 1;
+        }
+
+        return NULL;
+}
index 32f4fa3345e3ca0e9d3b44b597639436a553abdd..061ad692ec51479de00c48379b14e3dff6bf6782 100644 (file)
@@ -7,3 +7,5 @@ bool is_direct_boot(EFI_HANDLE device);
 EFI_STATUS vmm_open(EFI_HANDLE *ret_qemu_dev, EFI_FILE **ret_qemu_dir);
 
 bool in_hypervisor(void);
+
+const char* smbios_find_oem_string(const char *name);
index d0d92507d6ab47baee6c5395e7b652f3e4c61783..48e6419130f836a0fdb87e95c40e9c5d5e573b91 100644 (file)
@@ -380,7 +380,7 @@ static int bus_socket_set_transient_property(
                                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown Socket type: %s", t);
 
                         if (p->type != SOCKET_SOCKET) {
-                                if (!path_is_valid(a))
+                                if (!path_is_absolute(a) || !path_is_valid(a))
                                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid socket path: %s", a);
 
                                 p->path = strdup(a);
index 3971695fb668dd2da883402e8e06dfcdc8c170fb..bba2753bee3263361d7b4b69f492366328e55362 100644 (file)
@@ -1114,7 +1114,10 @@ static int set_securebits(unsigned bits, unsigned mask) {
         return 1;
 }
 
-static int enforce_user(const ExecContext *context, uid_t uid) {
+static int enforce_user(
+                const ExecContext *context,
+                uid_t uid,
+                uint64_t capability_ambient_set) {
         assert(context);
         int r;
 
@@ -1125,7 +1128,7 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
          * setting secure bits the capability CAP_SETPCAP is required, so we also need keep-caps in this
          * case. */
 
-        if ((context->capability_ambient_set != 0 || context->secure_bits != 0) && uid != 0) {
+        if ((capability_ambient_set != 0 || context->secure_bits != 0) && uid != 0) {
 
                 /* First step: If we need to keep capabilities but drop privileges we need to make sure we
                  * keep our caps, while we drop privileges. Add KEEP_CAPS to the securebits */
@@ -4775,6 +4778,8 @@ static int exec_child(
         else
                 needs_setuid = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID));
 
+        uint64_t capability_ambient_set = context->capability_ambient_set;
+
         if (needs_sandboxing) {
                 /* MAC enablement checks need to be done before a new mount ns is created, as they rely on
                  * /sys being present. The actual MAC context application will happen later, as late as
@@ -4815,6 +4820,20 @@ static int exec_child(
                         return log_unit_error_errno(unit, r, "Failed to set up PAM session: %m");
                 }
 
+                if (ambient_capabilities_supported()) {
+                        uint64_t ambient_after_pam;
+
+                        /* PAM modules might have set some ambient caps. Query them here and merge them into
+                         * the caps we want to set in the end, so that we don't end up unsetting them. */
+                        r = capability_get_ambient(&ambient_after_pam);
+                        if (r < 0) {
+                                *exit_status = EXIT_CAPABILITIES;
+                                return log_unit_error_errno(unit, r, "Failed to query ambient caps: %m");
+                        }
+
+                        capability_ambient_set |= ambient_after_pam;
+                }
+
                 ngids_after_pam = getgroups_alloc(&gids_after_pam);
                 if (ngids_after_pam < 0) {
                         *exit_status = EXIT_MEMORY;
@@ -5043,7 +5062,7 @@ static int exec_child(
                                 (UINT64_C(1) << CAP_SETGID);
 
                 if (!cap_test_all(bset)) {
-                        r = capability_bounding_set_drop(bset, false);
+                        r = capability_bounding_set_drop(bset, /* right_now= */ false);
                         if (r < 0) {
                                 *exit_status = EXIT_CAPABILITIES;
                                 return log_unit_error_errno(unit, r, "Failed to drop capabilities: %m");
@@ -5062,7 +5081,7 @@ static int exec_child(
                  * The requested ambient capabilities are raised in the inheritable set if the second
                  * argument is true. */
                 if (!needs_ambient_hack) {
-                        r = capability_ambient_set_apply(context->capability_ambient_set, true);
+                        r = capability_ambient_set_apply(capability_ambient_set, /* also_inherit= */ true);
                         if (r < 0) {
                                 *exit_status = EXIT_CAPABILITIES;
                                 return log_unit_error_errno(unit, r, "Failed to apply ambient capabilities (before UID change): %m");
@@ -5077,17 +5096,16 @@ static int exec_child(
 
         if (needs_setuid) {
                 if (uid_is_valid(uid)) {
-                        r = enforce_user(context, uid);
+                        r = enforce_user(context, uid, capability_ambient_set);
                         if (r < 0) {
                                 *exit_status = EXIT_USER;
                                 return log_unit_error_errno(unit, r, "Failed to change UID to " UID_FMT ": %m", uid);
                         }
 
-                        if (!needs_ambient_hack &&
-                            context->capability_ambient_set != 0) {
+                        if (!needs_ambient_hack && capability_ambient_set != 0) {
 
                                 /* Raise the ambient capabilities after user change. */
-                                r = capability_ambient_set_apply(context->capability_ambient_set, false);
+                                r = capability_ambient_set_apply(capability_ambient_set, /* also_inherit= */ false);
                                 if (r < 0) {
                                         *exit_status = EXIT_CAPABILITIES;
                                         return log_unit_error_errno(unit, r, "Failed to apply ambient capabilities (after UID change): %m");
index 62f23a9e1ecf2d29f2b185bbc6f75009b427eca2..510bb28c9375f875bbd3456bd8a07caede6ca73a 100644 (file)
@@ -4,6 +4,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "cgroup-setup.h"
 #include "dbus-scope.h"
 #include "dbus-unit.h"
 #include "exit-status.h"
index 46292da2a1f3e5c7ecd7b83fcb0a015494eb33d0..73b5267ae8179f0594302bc352c99ae73ad02f36 100644 (file)
@@ -1915,7 +1915,7 @@ _public_ int sd_event_add_memory_pressure(
 
         _cleanup_free_ char *w = NULL;
         _cleanup_(source_freep) sd_event_source *s = NULL;
-        _cleanup_close_ int path_fd = -1, fd = -1;
+        _cleanup_close_ int path_fd = -EBADF, fd = -EBADF;
         _cleanup_free_ void *write_buffer = NULL;
         const char *watch, *watch_fallback = NULL, *env;
         size_t write_buffer_size = 0;
index c4971a807c79db4d664b42b092db340ff1cdd886..e6b31d4e7c022197b1d929231276bfd1325fb7b2 100644 (file)
@@ -727,8 +727,8 @@ static int check_object_header(JournalFile *f, Object *o, ObjectType type, uint6
 
         if (s < sizeof(ObjectHeader))
                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                       "Attempt to move to overly short object: %" PRIu64,
-                                       offset);
+                                       "Attempt to move to overly short object with size %"PRIu64": %" PRIu64,
+                                       s, offset);
 
         if (o->object.type <= OBJECT_UNUSED)
                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
@@ -737,12 +737,17 @@ static int check_object_header(JournalFile *f, Object *o, ObjectType type, uint6
 
         if (type > OBJECT_UNUSED && o->object.type != type)
                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                       "Attempt to move to object of unexpected type: %" PRIu64,
+                                       "Found %s object while expecting %s object: %" PRIu64,
+                                       journal_object_type_to_string(o->object.type),
+                                       journal_object_type_to_string(type),
                                        offset);
 
         if (s < minimum_header_size(f, o))
                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                       "Attempt to move to truncated object: %" PRIu64,
+                                       "Size of %s object (%"PRIu64") is smaller than the minimum object size (%"PRIu64"): %" PRIu64,
+                                       journal_object_type_to_string(o->object.type),
+                                       s,
+                                       minimum_header_size(f, o),
                                        offset);
 
         return 0;
@@ -759,13 +764,13 @@ static int check_object(JournalFile *f, Object *o, uint64_t offset) {
         case OBJECT_DATA:
                 if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0))
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                               "Bad n_entries: %" PRIu64 ": %" PRIu64,
+                                               "Bad data n_entries: %" PRIu64 ": %" PRIu64,
                                                le64toh(o->data.n_entries),
                                                offset);
 
                 if (le64toh(o->object.size) <= journal_file_data_payload_offset(f))
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                               "Bad object size (<= %zu): %" PRIu64 ": %" PRIu64,
+                                               "Bad data size (<= %zu): %" PRIu64 ": %" PRIu64,
                                                journal_file_data_payload_offset(f),
                                                le64toh(o->object.size),
                                                offset);
@@ -850,7 +855,7 @@ static int check_object(JournalFile *f, Object *o, uint64_t offset) {
                     (sz - offsetof(Object, hash_table.items)) / sizeof(HashItem) <= 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Invalid %s hash table size: %" PRIu64 ": %" PRIu64,
-                                               o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
+                                               journal_object_type_to_string(o->object.type),
                                                sz,
                                                offset);
 
@@ -909,13 +914,15 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
         /* Objects may only be located at multiple of 64 bit */
         if (!VALID64(offset))
                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                       "Attempt to move to object at non-64bit boundary: %" PRIu64,
+                                       "Attempt to move to %s object at non-64bit boundary: %" PRIu64,
+                                       journal_object_type_to_string(type),
                                        offset);
 
         /* Object may not be located in the file header */
         if (offset < le64toh(f->header->header_size))
                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                       "Attempt to move to object located in file header: %" PRIu64,
+                                       "Attempt to move to %s object located in file header: %" PRIu64,
+                                       journal_object_type_to_string(type),
                                        offset);
 
         r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), (void**) &o);
@@ -954,25 +961,25 @@ int journal_file_read_object_header(JournalFile *f, ObjectType type, uint64_t of
         /* Objects may only be located at multiple of 64 bit */
         if (!VALID64(offset))
                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                       "Attempt to read object at non-64bit boundary: %" PRIu64,
-                                       offset);
+                                       "Attempt to read %s object at non-64bit boundary: %" PRIu64,
+                                       journal_object_type_to_string(type), offset);
 
         /* Object may not be located in the file header */
         if (offset < le64toh(f->header->header_size))
                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                       "Attempt to read object located in file header: %" PRIu64,
-                                       offset);
+                                       "Attempt to read %s object located in file header: %" PRIu64,
+                                       journal_object_type_to_string(type), offset);
 
         /* This will likely read too much data but it avoids having to call pread() twice. */
         n = pread(f->fd, &o, sizeof(o), offset);
         if (n < 0)
-                return log_debug_errno(errno, "Failed to read journal file at offset: %" PRIu64,
-                                       offset);
+                return log_debug_errno(errno, "Failed to read journal %s object at offset: %" PRIu64,
+                                       journal_object_type_to_string(type), offset);
 
         if ((size_t) n < sizeof(o.object))
                 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Failed to read short object at offset: %" PRIu64,
-                                       offset);
+                                       "Failed to read short %s object at offset: %" PRIu64,
+                                       journal_object_type_to_string(type), offset);
 
         r = check_object_header(f, &o, type, offset);
         if (r < 0)
@@ -980,8 +987,8 @@ int journal_file_read_object_header(JournalFile *f, ObjectType type, uint64_t of
 
         if ((size_t) n < minimum_header_size(f, &o))
                 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
-                                       "Short read while reading object: %" PRIu64,
-                                       offset);
+                                       "Short read while reading %s object: %" PRIu64,
+                                       journal_object_type_to_string(type), offset);
 
         r = check_object(f, &o, offset);
         if (r < 0)
@@ -2516,7 +2523,8 @@ static int bump_entry_array(
 
         if (direction == DIRECTION_DOWN) {
                 assert(o);
-                return le64toh(o->entry_array.next_entry_array_offset);
+                *ret = le64toh(o->entry_array.next_entry_array_offset);
+                return 0;
         }
 
         /* Entry array chains are a singly linked list, so to find the previous array in the chain, we have
index 00c53f6d5b8cad3bbfea379e6a20af09dfd5e602..c360d0e1826a979f5c37f36717263b5d5bb1ce55 100644 (file)
@@ -148,93 +148,64 @@ _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
         return sd_journal_sendv(iov, 2);
 }
 
-_printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) {
+_printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, size_t extra, struct iovec **ret_iov, size_t *ret_n_iov) {
         PROTECT_ERRNO;
-        int r, n = 0, i = 0, j;
         struct iovec *iov = NULL;
+        size_t n = 0;
 
-        assert(_iov);
+        assert(ret_iov);
+        assert(ret_n_iov);
 
         if (extra > 0) {
-                n = MAX(extra * 2, extra + 4);
-                iov = malloc0(n * sizeof(struct iovec));
-                if (!iov) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
+                if (!GREEDY_REALLOC0(iov, extra))
+                        return -ENOMEM;
 
-                i = extra;
+                n = extra;
         }
 
+        CLEANUP_ARRAY(iov, n, iovec_array_free);
+
         while (format) {
-                struct iovec *c;
-                char *buffer;
+                _cleanup_free_ char *buffer = NULL;
                 va_list aq;
 
-                if (i >= n) {
-                        n = MAX(i*2, 4);
-                        c = reallocarray(iov, n, sizeof(struct iovec));
-                        if (!c) {
-                                r = -ENOMEM;
-                                goto fail;
-                        }
-
-                        iov = c;
-                }
-
                 va_copy(aq, ap);
                 if (vasprintf(&buffer, format, aq) < 0) {
                         va_end(aq);
-                        r = -ENOMEM;
-                        goto fail;
+                        return -ENOMEM;
                 }
                 va_end(aq);
 
                 VA_FORMAT_ADVANCE(format, ap);
-
-                (void) strstrip(buffer); /* strip trailing whitespace, keep prefixing whitespace */
-
-                iov[i++] = IOVEC_MAKE_STRING(buffer);
-
                 format = va_arg(ap, char *);
-        }
-
-        *_iov = iov;
 
-        return i;
+                if (!GREEDY_REALLOC(iov, n + 1))
+                        return -ENOMEM;
 
-fail:
-        for (j = 0; j < i; j++)
-                free(iov[j].iov_base);
-
-        free(iov);
+                /* strip trailing whitespace, keep prefixing whitespace */
+                iov[n++] = IOVEC_MAKE_STRING(delete_trailing_chars(TAKE_PTR(buffer), NULL));
+        }
 
-        return r;
+        *ret_iov = TAKE_PTR(iov);
+        *ret_n_iov = n;
+        return 0;
 }
 
 _public_ int sd_journal_send(const char *format, ...) {
-        int r, i, j;
-        va_list ap;
         struct iovec *iov = NULL;
+        size_t n_iov = 0;
+        va_list ap;
+        int r;
+
+        CLEANUP_ARRAY(iov, n_iov, iovec_array_free);
 
         va_start(ap, format);
-        i = fill_iovec_sprintf(format, ap, 0, &iov);
+        r = fill_iovec_sprintf(format, ap, 0, &iov, &n_iov);
         va_end(ap);
+        if (r < 0)
+                return r;
 
-        if (_unlikely_(i < 0)) {
-                r = i;
-                goto finish;
-        }
-
-        r = sd_journal_sendv(iov, i);
-
-finish:
-        for (j = 0; j < i; j++)
-                free(iov[j].iov_base);
-
-        free(iov);
-
-        return r;
+        return sd_journal_sendv(iov, n_iov);
 }
 
 _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
@@ -529,19 +500,19 @@ _public_ int sd_journal_printv_with_location(int priority, const char *file, con
 }
 
 _public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) {
-        _cleanup_free_ struct iovec *iov = NULL;
-        int r, i, j;
+        struct iovec *iov = NULL;
+        size_t n_iov = 0;
         va_list ap;
         char *f;
+        int r;
+
+        CLEANUP_ARRAY(iov, n_iov, iovec_array_free);
 
         va_start(ap, format);
-        i = fill_iovec_sprintf(format, ap, 3, &iov);
+        r = fill_iovec_sprintf(format, ap, 3, &iov, &n_iov);
         va_end(ap);
-
-        if (_unlikely_(i < 0)) {
-                r = i;
-                goto finish;
-        }
+        if (r < 0)
+                return r;
 
         ALLOCA_CODE_FUNC(f, func);
 
@@ -549,11 +520,9 @@ _public_ int sd_journal_send_with_location(const char *file, const char *line, c
         iov[1] = IOVEC_MAKE_STRING(line);
         iov[2] = IOVEC_MAKE_STRING(f);
 
-        r = sd_journal_sendv(iov, i);
+        r = sd_journal_sendv(iov, n_iov);
 
-finish:
-        for (j = 3; j < i; j++)
-                free(iov[j].iov_base);
+        iov[0] = iov[1] = iov[2] = IOVEC_NULL;
 
         return r;
 }
index 95a515339d66d339711cd9528e4c5f7c23225b17..13f836bd90a218102b46378643bbb704648100af 100644 (file)
@@ -23,6 +23,8 @@ int cg_attach(const char *controller, const char *path, pid_t pid);
 int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
 int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
 
+int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid);
+
 int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
 int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
 int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
index ee45d4fd9e237596e8e53c2bca9beafa8ce25313..351a5ede118445f9cd1eb40726f73b08f954afad 100644 (file)
@@ -297,6 +297,7 @@ static size_t table_data_size(TableDataType type, const void *data) {
         case TABLE_TIMESTAMP:
         case TABLE_TIMESTAMP_UTC:
         case TABLE_TIMESTAMP_RELATIVE:
+        case TABLE_TIMESTAMP_LEFT:
         case TABLE_TIMESTAMP_DATE:
         case TABLE_TIMESPAN:
         case TABLE_TIMESPAN_MSEC:
@@ -896,6 +897,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                 case TABLE_TIMESTAMP:
                 case TABLE_TIMESTAMP_UTC:
                 case TABLE_TIMESTAMP_RELATIVE:
+                case TABLE_TIMESTAMP_LEFT:
                 case TABLE_TIMESTAMP_DATE:
                 case TABLE_TIMESPAN:
                 case TABLE_TIMESPAN_MSEC:
@@ -1304,6 +1306,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
                 case TABLE_TIMESTAMP:
                 case TABLE_TIMESTAMP_UTC:
                 case TABLE_TIMESTAMP_RELATIVE:
+                case TABLE_TIMESTAMP_LEFT:
                 case TABLE_TIMESTAMP_DATE:
                         return CMP(a->timestamp, b->timestamp);
 
@@ -1537,11 +1540,14 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
         case TABLE_TIMESTAMP:
         case TABLE_TIMESTAMP_UTC:
         case TABLE_TIMESTAMP_RELATIVE:
+        case TABLE_TIMESTAMP_LEFT:
         case TABLE_TIMESTAMP_DATE: {
                 _cleanup_free_ char *p = NULL;
                 char *ret;
 
-                p = new(char, d->type == TABLE_TIMESTAMP_RELATIVE ? FORMAT_TIMESTAMP_RELATIVE_MAX : FORMAT_TIMESTAMP_MAX);
+                p = new(char,
+                        IN_SET(d->type, TABLE_TIMESTAMP_RELATIVE, TABLE_TIMESTAMP_LEFT) ?
+                                FORMAT_TIMESTAMP_RELATIVE_MAX : FORMAT_TIMESTAMP_MAX);
                 if (!p)
                         return NULL;
 
@@ -1552,7 +1558,9 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
                 else if (d->type == TABLE_TIMESTAMP_DATE)
                         ret = format_timestamp_style(p, FORMAT_TIMESTAMP_MAX, d->timestamp, TIMESTAMP_DATE);
                 else
-                        ret = format_timestamp_relative(p, FORMAT_TIMESTAMP_RELATIVE_MAX, d->timestamp);
+                        ret = format_timestamp_relative_full(
+                                        p, FORMAT_TIMESTAMP_RELATIVE_MAX, d->timestamp,
+                                        /* implicit_left= */  d->type == TABLE_TIMESTAMP_LEFT);
                 if (!ret)
                         return "-";
 
@@ -2589,6 +2597,7 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
         case TABLE_TIMESTAMP:
         case TABLE_TIMESTAMP_UTC:
         case TABLE_TIMESTAMP_RELATIVE:
+        case TABLE_TIMESTAMP_LEFT:
         case TABLE_TIMESTAMP_DATE:
                 if (d->timestamp == USEC_INFINITY)
                         return json_variant_new_null(ret);
index 97255f5aef24eb42b54d6af20d8cad091e2d57af..5a2b366b59c9b9e9f8c3ae59e1df660c3bb80680 100644 (file)
@@ -23,6 +23,7 @@ typedef enum TableDataType {
         TABLE_TIMESTAMP,
         TABLE_TIMESTAMP_UTC,
         TABLE_TIMESTAMP_RELATIVE,
+        TABLE_TIMESTAMP_LEFT,
         TABLE_TIMESTAMP_DATE,
         TABLE_TIMESPAN,
         TABLE_TIMESPAN_MSEC,
index 049ec21ab9465d2955e8fefee275e4dcbcb4d09d..6e187efe94b65f5e89d75ce7b71d85586a1e0db2 100644 (file)
@@ -504,7 +504,7 @@ static int loop_device_make_internal(
                          * the underlying block device. */
                         r = blockdev_get_sector_size(fd, &sector_size);
                 else {
-                        _cleanup_close_ int non_direct_io_fd = -1;
+                        _cleanup_close_ int non_direct_io_fd = -EBADF;
                         int probe_fd;
 
                         assert(S_ISREG(st.st_mode));
index 8e537300782be546eaf687bb56ca46cadde0ff7c..f9f53bd5254fb074c91516959dbc7f1a36491822 100644 (file)
 #include "systemctl.h"
 #include "terminal-util.h"
 
-static void message_set_freep(Set **set) {
-        set_free_with_destructor(*set, sd_bus_message_unref);
-}
-
 static int get_unit_list_recursive(
                 sd_bus *bus,
                 char **patterns,
                 UnitInfo **ret_unit_infos,
-                Set **ret_replies,
-                char ***ret_machines) {
+                Set **ret_replies) {
 
         _cleanup_free_ UnitInfo *unit_infos = NULL;
-        _cleanup_(message_set_freep) Set *replies = NULL;
-        sd_bus_message *reply;
+        _cleanup_set_free_ Set *replies = NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         int c, r;
 
         assert(bus);
         assert(ret_replies);
         assert(ret_unit_infos);
-        assert(ret_machines);
-
-        replies = set_new(NULL);
-        if (!replies)
-                return log_oom();
 
         c = get_unit_list(bus, NULL, patterns, &unit_infos, 0, &reply);
         if (c < 0)
                 return c;
 
-        r = set_put(replies, reply);
-        if (r < 0) {
-                sd_bus_message_unref(reply);
+        r = set_ensure_consume(&replies, &bus_message_hash_ops, TAKE_PTR(reply));
+        if (r < 0)
                 return log_oom();
-        }
 
         if (arg_recursive) {
                 _cleanup_strv_free_ char **machines = NULL;
@@ -72,16 +60,11 @@ static int get_unit_list_recursive(
 
                         c = k;
 
-                        r = set_put(replies, reply);
-                        if (r < 0) {
-                                sd_bus_message_unref(reply);
+                        r = set_consume(replies, TAKE_PTR(reply));
+                        if (r < 0)
                                 return log_oom();
-                        }
                 }
-
-                *ret_machines = TAKE_PTR(machines);
-        } else
-                *ret_machines = NULL;
+        }
 
         *ret_unit_infos = TAKE_PTR(unit_infos);
         *ret_replies = TAKE_PTR(replies);
@@ -238,8 +221,7 @@ static int output_units_list(const UnitInfo *unit_infos, size_t c) {
 
 int verb_list_units(int argc, char *argv[], void *userdata) {
         _cleanup_free_ UnitInfo *unit_infos = NULL;
-        _cleanup_(message_set_freep) Set *replies = NULL;
-        _cleanup_strv_free_ char **machines = NULL;
+        _cleanup_set_free_ Set *replies = NULL;
         sd_bus *bus;
         int r;
 
@@ -256,11 +238,11 @@ int verb_list_units(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return r;
 
-                r = get_unit_list_recursive(bus, names, &unit_infos, &replies, &machines);
+                r = get_unit_list_recursive(bus, names, &unit_infos, &replies);
                 if (r < 0)
                         return r;
         } else {
-                r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
+                r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies);
                 if (r < 0)
                         return r;
         }
@@ -295,20 +277,79 @@ static int get_triggered_units(
         return 0;
 }
 
-static int get_listening(
+typedef struct SocketInfo {
+        const char *machine;
+        const char* id;
+
+        char* type;
+        char* path; /* absolute path or socket address */
+
+        /* Note: triggered is a list here, although it almost certainly will always be one
+         * unit. Nevertheless, dbus API allows for multiple values, so let's follow that. */
+        char** triggered;
+} SocketInfo;
+
+static void socket_info_array_free(SocketInfo *sockets, size_t n_sockets) {
+        assert(sockets || n_sockets == 0);
+
+        for (SocketInfo *s = sockets; s < sockets + n_sockets; s++) {
+                free(s->type);
+                free(s->path);
+                strv_free(s->triggered);
+        }
+
+        free(sockets);
+}
+
+static int socket_info_compare(const SocketInfo *a, const SocketInfo *b) {
+        int r;
+
+        assert(a);
+        assert(b);
+
+        r = strcasecmp_ptr(a->machine, b->machine);
+        if (r != 0)
+                return r;
+
+        r = CMP(path_is_absolute(a->path), path_is_absolute(b->path));
+        if (r != 0)
+                return r;
+
+        r = path_is_absolute(a->path) ? path_compare(a->path, b->path) : strcmp(a->path, b->path);
+        if (r != 0)
+                return r;
+
+        return strcmp(a->type, b->type);
+}
+
+static int socket_info_add(
                 sd_bus *bus,
-                const char* unit_path,
-                char*** listening) {
+                const UnitInfo *u,
+                SocketInfo **sockets,
+                size_t *n_sockets) {
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_strv_free_ char **triggered = NULL;
         const char *type, *path;
-        int r, n = 0;
+        int r;
+
+        assert(bus);
+        assert(u);
+        assert(sockets);
+        assert(n_sockets);
+
+        if (!endswith(u->id, ".socket"))
+                return 0;
+
+        r = get_triggered_units(bus, u->unit_path, &triggered);
+        if (r < 0)
+                return r;
 
         r = sd_bus_get_property(
                         bus,
                         "org.freedesktop.systemd1",
-                        unit_path,
+                        u->unit_path,
                         "org.freedesktop.systemd1.Socket",
                         "Listen",
                         &error,
@@ -322,16 +363,31 @@ static int get_listening(
                 return bus_log_parse_error(r);
 
         while ((r = sd_bus_message_read(reply, "(ss)", &type, &path)) > 0) {
+                _cleanup_free_ char *type_dup = NULL, *path_dup = NULL;
+                _cleanup_strv_free_ char **triggered_dup = NULL;
 
-                r = strv_extend(listening, type);
-                if (r < 0)
+                type_dup = strdup(type);
+                if (!type_dup)
                         return log_oom();
 
-                r = strv_extend(listening, path);
-                if (r < 0)
+                path_dup = strdup(path);
+                if (!path_dup)
+                        return log_oom();
+
+                triggered_dup = strv_copy(triggered);
+                if (!triggered_dup)
                         return log_oom();
 
-                n++;
+                if (!GREEDY_REALLOC(*sockets, *n_sockets + 1))
+                        return log_oom();
+
+                (*sockets)[(*n_sockets)++] = (SocketInfo) {
+                        .machine = u->machine,
+                        .id = u->id,
+                        .type = TAKE_PTR(type_dup),
+                        .path = TAKE_PTR(path_dup),
+                        .triggered = TAKE_PTR(triggered_dup),
+                };
         }
         if (r < 0)
                 return bus_log_parse_error(r);
@@ -340,46 +396,14 @@ static int get_listening(
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        return n;
-}
-
-struct socket_info {
-        const char *machine;
-        const char* id;
-
-        char* type;
-        char* path;
-
-        /* Note: triggered is a list here, although it almost certainly will always be one
-         * unit. Nevertheless, dbus API allows for multiple values, so let's follow that. */
-        char** triggered;
-
-        /* The strv above is shared. free is set only in the first one. */
-        bool own_triggered;
-};
-
-static int socket_info_compare(const struct socket_info *a, const struct socket_info *b) {
-        int r;
-
-        assert(a);
-        assert(b);
-
-        r = strcasecmp_ptr(a->machine, b->machine);
-        if (r != 0)
-                return r;
-
-        r = strcmp(a->path, b->path);
-        if (r != 0)
-                return r;
-
-        return strcmp(a->type, b->type);
+        return 0;
 }
 
-static int output_sockets_list(struct socket_info *socket_infos, size_t cs) {
+static int output_sockets_list(const SocketInfo *sockets, size_t n_sockets) {
         _cleanup_(table_unrefp) Table *table = NULL;
         int r;
 
-        assert(socket_infos || cs == 0);
+        assert(sockets || n_sockets == 0);
 
         table = table_new("listen", "type", "unit", "activates");
         if (!table)
@@ -398,7 +422,7 @@ static int output_sockets_list(struct socket_info *socket_infos, size_t cs) {
 
         table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
 
-        for (struct socket_info *s = socket_infos; s < socket_infos + cs; s++) {
+        for (const SocketInfo *s = sockets; s < sockets + n_sockets; s++) {
                 _cleanup_free_ char *unit = NULL;
 
                 unit = format_unit_id(s->id, s->machine);
@@ -406,9 +430,9 @@ static int output_sockets_list(struct socket_info *socket_infos, size_t cs) {
                         return log_oom();
 
                 r = table_add_many(table,
-                                        TABLE_STRING, s->path,
-                                        TABLE_STRING, s->type,
-                                        TABLE_STRING, unit);
+                                   TABLE_STRING, s->path,
+                                   TABLE_STRING, s->type,
+                                   TABLE_STRING, unit);
                 if (r < 0)
                         return table_log_add_error(r);
 
@@ -422,20 +446,21 @@ static int output_sockets_list(struct socket_info *socket_infos, size_t cs) {
                 return r;
 
         if (arg_legend != 0)
-                output_legend("socket", cs);
+                output_legend("socket", n_sockets);
 
         return 0;
 }
 
 int verb_list_sockets(int argc, char *argv[], void *userdata) {
-        _cleanup_(message_set_freep) Set *replies = NULL;
-        _cleanup_strv_free_ char **machines = NULL;
+        _cleanup_set_free_ Set *replies = NULL;
         _cleanup_strv_free_ char **sockets_with_suffix = NULL;
         _cleanup_free_ UnitInfo *unit_infos = NULL;
-        _cleanup_free_ struct socket_info *socket_infos = NULL;
-        size_t cs = 0;
-        int r, n;
+        SocketInfo *sockets = NULL;
+        size_t n_sockets = 0;
         sd_bus *bus;
+        int r;
+
+        CLEANUP_ARRAY(sockets, n_sockets, socket_info_array_free);
 
         r = acquire_bus(BUS_MANAGER, &bus);
         if (r < 0)
@@ -448,63 +473,23 @@ int verb_list_sockets(int argc, char *argv[], void *userdata) {
                 return r;
 
         if (argc == 1 || sockets_with_suffix) {
-                n = get_unit_list_recursive(bus, sockets_with_suffix, &unit_infos, &replies, &machines);
+                int n;
+
+                n = get_unit_list_recursive(bus, sockets_with_suffix, &unit_infos, &replies);
                 if (n < 0)
                         return n;
 
                 for (const UnitInfo *u = unit_infos; u < unit_infos + n; u++) {
-                        _cleanup_strv_free_ char **listening = NULL, **triggered = NULL;
-                        int c;
-
-                        if (!endswith(u->id, ".socket"))
-                                continue;
-
-                        r = get_triggered_units(bus, u->unit_path, &triggered);
+                        r = socket_info_add(bus, u, &sockets, &n_sockets);
                         if (r < 0)
-                                goto cleanup;
-
-                        c = get_listening(bus, u->unit_path, &listening);
-                        if (c < 0) {
-                                r = c;
-                                goto cleanup;
-                        }
-
-                        if (!GREEDY_REALLOC(socket_infos, cs + c)) {
-                                r = log_oom();
-                                goto cleanup;
-                        }
-
-                        for (int i = 0; i < c; i++)
-                                socket_infos[cs + i] = (struct socket_info) {
-                                        .machine = u->machine,
-                                        .id = u->id,
-                                        .type = listening[i*2],
-                                        .path = listening[i*2 + 1],
-                                        .triggered = triggered,
-                                        .own_triggered = i==0,
-                                };
-
-                        /* from this point on we will cleanup those socket_infos */
-                        cs += c;
-                        free(listening);
-                        listening = triggered = NULL; /* avoid cleanup */
+                                return r;
                 }
-
-                typesafe_qsort(socket_infos, cs, socket_info_compare);
         }
 
-        output_sockets_list(socket_infos, cs);
+        typesafe_qsort(sockets, n_sockets, socket_info_compare);
+        output_sockets_list(sockets, n_sockets);
 
- cleanup:
-        assert(cs == 0 || socket_infos);
-        for (struct socket_info *s = socket_infos; s < socket_infos + cs; s++) {
-                free(s->type);
-                free(s->path);
-                if (s->own_triggered)
-                        strv_free(s->triggered);
-        }
-
-        return r;
+        return 0;
 }
 
 static int get_next_elapse(
@@ -575,15 +560,24 @@ static int get_last_trigger(
         return 0;
 }
 
-struct timer_info {
+typedef struct TimerInfo {
         const char* machine;
         const char* id;
         usec_t next_elapse;
         usec_t last_trigger;
-        char** triggered;
-};
+        char **triggered;
+} TimerInfo;
+
+static void timer_info_array_free(TimerInfo *timers, size_t n_timers) {
+        assert(timers || n_timers == 0);
+
+        for (TimerInfo *t = timers; t < timers + n_timers; t++)
+                strv_free(t->triggered);
 
-static int timer_info_compare(const struct timer_info *a, const struct timer_info *b) {
+        free(timers);
+}
+
+static int timer_info_compare(const TimerInfo *a, const TimerInfo *b) {
         int r;
 
         assert(a);
@@ -600,11 +594,11 @@ static int timer_info_compare(const struct timer_info *a, const struct timer_inf
         return strcmp(a->id, b->id);
 }
 
-static int output_timers_list(struct timer_info *timer_infos, size_t n) {
+static int output_timers_list(const TimerInfo *timers, size_t n_timers) {
         _cleanup_(table_unrefp) Table *table = NULL;
         int r;
 
-        assert(timer_infos || n == 0);
+        assert(timers || n_timers == 0);
 
         table = table_new("next", "left", "last", "passed", "unit", "activates");
         if (!table)
@@ -619,7 +613,7 @@ static int output_timers_list(struct timer_info *timer_infos, size_t n) {
         (void) table_set_align_percent(table, table_get_cell(table, 0, 1), 100);
         (void) table_set_align_percent(table, table_get_cell(table, 0, 3), 100);
 
-        for (struct timer_info *t = timer_infos; t < timer_infos + n; t++) {
+        for (const TimerInfo *t = timers; t < timers + n_timers; t++) {
                 _cleanup_free_ char *unit = NULL;
 
                 unit = format_unit_id(t->id, t->machine);
@@ -628,7 +622,7 @@ static int output_timers_list(struct timer_info *timer_infos, size_t n) {
 
                 r = table_add_many(table,
                                    TABLE_TIMESTAMP, t->next_elapse,
-                                   TABLE_TIMESTAMP_RELATIVE, t->next_elapse,
+                                   TABLE_TIMESTAMP_LEFT, t->next_elapse,
                                    TABLE_TIMESTAMP, t->last_trigger,
                                    TABLE_TIMESTAMP_RELATIVE, t->last_trigger,
                                    TABLE_STRING, unit);
@@ -645,12 +639,12 @@ static int output_timers_list(struct timer_info *timer_infos, size_t n) {
                 return r;
 
         if (arg_legend != 0)
-                output_legend("timer", n);
+                output_legend("timer", n_timers);
 
         return 0;
 }
 
-usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) {
+usec_t calc_next_elapse(const dual_timestamp *nw, const dual_timestamp *next) {
         usec_t next_elapse;
 
         assert(nw);
@@ -675,16 +669,65 @@ usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) {
         return next_elapse;
 }
 
+static int add_timer_info(
+                sd_bus *bus,
+                const UnitInfo *u,
+                const dual_timestamp *nw,
+                TimerInfo **timers,
+                size_t *n_timers) {
+
+        _cleanup_strv_free_ char **triggered = NULL;
+        dual_timestamp next = DUAL_TIMESTAMP_NULL;
+        usec_t m, last = 0;
+        int r;
+
+        assert(bus);
+        assert(u);
+        assert(nw);
+        assert(timers);
+        assert(n_timers);
+
+        if (!endswith(u->id, ".timer"))
+                return 0;
+
+        r = get_triggered_units(bus, u->unit_path, &triggered);
+        if (r < 0)
+                return r;
+
+        r = get_next_elapse(bus, u->unit_path, &next);
+        if (r < 0)
+                return r;
+
+        r = get_last_trigger(bus, u->unit_path, &last);
+        if (r < 0)
+                return r;
+
+        m = calc_next_elapse(nw, &next);
+
+        if (!GREEDY_REALLOC(*timers, *n_timers + 1))
+                return log_oom();
+
+        (*timers)[(*n_timers)++] = (TimerInfo) {
+                .machine = u->machine,
+                .id = u->id,
+                .next_elapse = m,
+                .last_trigger = last,
+                .triggered = TAKE_PTR(triggered),
+        };
+
+        return 0;
+}
+
 int verb_list_timers(int argc, char *argv[], void *userdata) {
-        _cleanup_(message_set_freep) Set *replies = NULL;
-        _cleanup_strv_free_ char **machines = NULL;
+        _cleanup_set_free_ Set *replies = NULL;
         _cleanup_strv_free_ char **timers_with_suffix = NULL;
-        _cleanup_free_ struct timer_info *timer_infos = NULL;
         _cleanup_free_ UnitInfo *unit_infos = NULL;
-        dual_timestamp nw;
-        size_t c = 0;
+        TimerInfo *timers = NULL;
+        size_t n_timers = 0;
         sd_bus *bus;
-        int n, r;
+        int r;
+
+        CLEANUP_ARRAY(timers, n_timers, timer_info_array_free);
 
         r = acquire_bus(BUS_MANAGER, &bus);
         if (r < 0)
@@ -697,68 +740,49 @@ int verb_list_timers(int argc, char *argv[], void *userdata) {
                 return r;
 
         if (argc == 1 || timers_with_suffix) {
-                n = get_unit_list_recursive(bus, timers_with_suffix, &unit_infos, &replies, &machines);
+                dual_timestamp nw;
+                int n;
+
+                n = get_unit_list_recursive(bus, timers_with_suffix, &unit_infos, &replies);
                 if (n < 0)
                         return n;
 
                 dual_timestamp_get(&nw);
 
                 for (const UnitInfo *u = unit_infos; u < unit_infos + n; u++) {
-                        _cleanup_strv_free_ char **triggered = NULL;
-                        dual_timestamp next = DUAL_TIMESTAMP_NULL;
-                        usec_t m, last = 0;
-
-                        if (!endswith(u->id, ".timer"))
-                                continue;
-
-                        r = get_triggered_units(bus, u->unit_path, &triggered);
+                        r = add_timer_info(bus, u, &nw, &timers, &n_timers);
                         if (r < 0)
-                                goto cleanup;
-
-                        r = get_next_elapse(bus, u->unit_path, &next);
-                        if (r < 0)
-                                goto cleanup;
-
-                        get_last_trigger(bus, u->unit_path, &last);
-
-                        if (!GREEDY_REALLOC(timer_infos, c+1)) {
-                                r = log_oom();
-                                goto cleanup;
-                        }
-
-                        m = calc_next_elapse(&nw, &next);
-
-                        timer_infos[c++] = (struct timer_info) {
-                                .machine = u->machine,
-                                .id = u->id,
-                                .next_elapse = m,
-                                .last_trigger = last,
-                                .triggered = TAKE_PTR(triggered),
-                        };
+                                return r;
                 }
-
-                typesafe_qsort(timer_infos, c, timer_info_compare);
         }
 
-        output_timers_list(timer_infos, c);
+        typesafe_qsort(timers, n_timers, timer_info_compare);
+        output_timers_list(timers, n_timers);
 
- cleanup:
-        for (struct timer_info *t = timer_infos; t < timer_infos + c; t++)
-                strv_free(t->triggered);
-
-        return r;
+        return 0;
 }
 
-struct automount_info {
+typedef struct AutomountInfo {
         const char *machine;
         const char *id;
         char *what;
         char *where;
         usec_t timeout_idle_usec;
         bool mounted;
-};
+} AutomountInfo;
 
-static int automount_info_compare(const struct automount_info *a, const struct automount_info *b) {
+static void automount_info_array_free(AutomountInfo *automounts, size_t n_automounts) {
+        assert(automounts || n_automounts == 0);
+
+        for (AutomountInfo *i = automounts; i < automounts + n_automounts; i++) {
+                free(i->what);
+                free(i->where);
+        }
+
+        free(automounts);
+}
+
+static int automount_info_compare(const AutomountInfo *a, const AutomountInfo *b) {
         int r;
 
         assert(a);
@@ -768,10 +792,15 @@ static int automount_info_compare(const struct automount_info *a, const struct a
         if (r != 0)
                 return r;
 
-        return strcmp(a->where, b->where);
+        return path_compare(a->where, b->where);
 }
 
-static int collect_automount_info(sd_bus* bus, const UnitInfo* info, struct automount_info *ret_info) {
+static int automount_info_add(
+                sd_bus* bus,
+                const UnitInfo *info,
+                AutomountInfo **automounts,
+                size_t *n_automounts) {
+
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_free_ char *mount = NULL, *mount_path = NULL, *where = NULL, *what = NULL, *state = NULL;
         uint64_t timeout_idle_usec;
@@ -780,7 +809,11 @@ static int collect_automount_info(sd_bus* bus, const UnitInfo* info, struct auto
 
         assert(bus);
         assert(info);
-        assert(ret_info);
+        assert(automounts);
+        assert(n_automounts);
+
+        if (!endswith(info->id, ".automount"))
+                return 0;
 
         locator = (BusLocator) {
                 .destination = "org.freedesktop.systemd1",
@@ -817,7 +850,10 @@ static int collect_automount_info(sd_bus* bus, const UnitInfo* info, struct auto
         if (r < 0)
                 return log_error_errno(r, "Failed to get mount state: %s", bus_error_message(&error, r));
 
-        *ret_info = (struct automount_info) {
+        if (!GREEDY_REALLOC(*automounts, *n_automounts + 1))
+                return log_oom();
+
+        (*automounts)[(*n_automounts)++] = (AutomountInfo) {
                 .machine = info->machine,
                 .id = info->id,
                 .what = TAKE_PTR(what),
@@ -829,7 +865,7 @@ static int collect_automount_info(sd_bus* bus, const UnitInfo* info, struct auto
         return 0;
 }
 
-static int output_automounts_list(struct automount_info *infos, size_t n_infos) {
+static int output_automounts_list(const AutomountInfo *infos, size_t n_infos) {
         _cleanup_(table_unrefp) Table *table = NULL;
         int r;
 
@@ -845,7 +881,7 @@ static int output_automounts_list(struct automount_info *infos, size_t n_infos)
 
         table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
 
-        for (struct automount_info *info = infos; info < infos + n_infos; info++) {
+        for (const AutomountInfo *info = infos; info < infos + n_infos; info++) {
                 _cleanup_free_ char *unit = NULL;
 
                 unit = format_unit_id(info->id, info->machine);
@@ -882,13 +918,15 @@ static int output_automounts_list(struct automount_info *infos, size_t n_infos)
 }
 
 int verb_list_automounts(int argc, char *argv[], void *userdata) {
-        _cleanup_(message_set_freep) Set *replies = NULL;
-        _cleanup_strv_free_ char **machines = NULL, **automounts = NULL;
+        _cleanup_set_free_ Set *replies = NULL;
+        _cleanup_strv_free_ char **names = NULL;
         _cleanup_free_ UnitInfo *unit_infos = NULL;
-        _cleanup_free_ struct automount_info *automount_infos = NULL;
-        size_t c = 0;
-        int r, n;
+        AutomountInfo *automounts = NULL;
+        size_t n_automounts = 0;
         sd_bus *bus;
+        int r;
+
+        CLEANUP_ARRAY(automounts, n_automounts, automount_info_array_free);
 
         r = acquire_bus(BUS_MANAGER, &bus);
         if (r < 0)
@@ -896,47 +934,32 @@ int verb_list_automounts(int argc, char *argv[], void *userdata) {
 
         pager_open(arg_pager_flags);
 
-        r = expand_unit_names(bus, strv_skip(argv, 1), ".automount", &automounts, NULL);
+        r = expand_unit_names(bus, strv_skip(argv, 1), ".automount", &names, NULL);
         if (r < 0)
                 return r;
 
         if (argc == 1 || automounts) {
-                n = get_unit_list_recursive(bus, automounts, &unit_infos, &replies, &machines);
+                int n;
+
+                n = get_unit_list_recursive(bus, names, &unit_infos, &replies);
                 if (n < 0)
                         return n;
 
                 for (const UnitInfo *u = unit_infos; u < unit_infos + n; u++) {
-                        if (!endswith(u->id, ".automount"))
-                                continue;
-
-                        if (!GREEDY_REALLOC(automount_infos, c + 1)) {
-                                r = log_oom();
-                                goto cleanup;
-                        }
-
-                        r = collect_automount_info(bus, u, &automount_infos[c]);
+                        r = automount_info_add(bus, u, &automounts, &n_automounts);
                         if (r < 0)
-                                goto cleanup;
-
-                        c++;
+                                return r;
                 }
 
-                typesafe_qsort(automount_infos, c, automount_info_compare);
         }
 
-        output_automounts_list(automount_infos, c);
-
- cleanup:
-        assert(c == 0 || automount_infos);
-        for (struct automount_info *info = automount_infos; info < automount_infos + c; info++) {
-                free(info->what);
-                free(info->where);
-        }
+        typesafe_qsort(automounts, n_automounts, automount_info_compare);
+        output_automounts_list(automounts, n_automounts);
 
-        return r;
+        return 0;
 }
 
-struct path_info {
+typedef struct PathInfo {
         const char *machine;
         const char *id;
 
@@ -946,14 +969,9 @@ struct path_info {
         /* Note: triggered is a list here, although it almost certainly will always be one
          * unit. Nevertheless, dbus API allows for multiple values, so let's follow that. */
         char** triggered;
-};
-
-struct path_infos {
-        size_t count;
-        struct path_info *items;
-};
+} PathInfo;
 
-static int path_info_compare(const struct path_info *a, const struct path_info *b) {
+static int path_info_compare(const PathInfo *a, const PathInfo *b) {
         int r;
 
         assert(a);
@@ -974,34 +992,45 @@ static int path_info_compare(const struct path_info *a, const struct path_info *
         return strcasecmp_ptr(a->id, b->id);
 }
 
-static void path_infos_done(struct path_infos *ps) {
-        assert(ps);
-        assert(ps->items || ps->count == 0);
+static void path_info_array_free(PathInfo *paths, size_t n_paths) {
+        assert(paths || n_paths == 0);
 
-        for (struct path_info *p = ps->items; p < ps->items + ps->count; p++) {
+        for (PathInfo *p = paths; p < paths + n_paths; p++) {
                 free(p->condition);
                 free(p->path);
                 strv_free(p->triggered);
         }
 
-        free(ps->items);
+        free(paths);
 }
 
-static int get_paths(sd_bus *bus, const char *unit_path, char ***ret_conditions, char ***ret_paths) {
+static int path_info_add(
+                sd_bus *bus,
+                const struct UnitInfo *u,
+                PathInfo **paths,
+                size_t *n_paths) {
+
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_strv_free_ char **conditions = NULL, **paths = NULL;
+        _cleanup_strv_free_ char **triggered = NULL;
         const char *condition, *path;
-        int r, n = 0;
+        int r;
 
         assert(bus);
-        assert(unit_path);
-        assert(ret_conditions);
-        assert(ret_paths);
+        assert(u);
+        assert(paths);
+        assert(n_paths);
+
+        if (!endswith(u->id, ".path"))
+                return 0;
+
+        r = get_triggered_units(bus, u->unit_path, &triggered);
+        if (r < 0)
+                return r;
 
         r = sd_bus_get_property(bus,
                                 "org.freedesktop.systemd1",
-                                unit_path,
+                                u->unit_path,
                                 "org.freedesktop.systemd1.Path",
                                 "Paths",
                                 &error,
@@ -1015,13 +1044,31 @@ static int get_paths(sd_bus *bus, const char *unit_path, char ***ret_conditions,
                 return bus_log_parse_error(r);
 
         while ((r = sd_bus_message_read(reply, "(ss)", &condition, &path)) > 0) {
-                if (strv_extend(&conditions, condition) < 0)
+                _cleanup_free_ char *condition_dup = NULL, *path_dup = NULL;
+                _cleanup_strv_free_ char **triggered_dup = NULL;
+
+                condition_dup = strdup(condition);
+                if (!condition_dup)
+                        return log_oom();
+
+                path_dup = strdup(path);
+                if (!path_dup)
                         return log_oom();
 
-                if (strv_extend(&paths, path) < 0)
+                triggered_dup = strv_copy(triggered);
+                if (!triggered_dup)
                         return log_oom();
 
-                n++;
+                if (!GREEDY_REALLOC(*paths, *n_paths + 1))
+                        return log_oom();
+
+                (*paths)[(*n_paths)++] = (PathInfo) {
+                        .machine = u->machine,
+                        .id = u->id,
+                        .condition = TAKE_PTR(condition_dup),
+                        .path = TAKE_PTR(path_dup),
+                        .triggered = TAKE_PTR(triggered_dup),
+                };
         }
         if (r < 0)
                 return bus_log_parse_error(r);
@@ -1030,18 +1077,14 @@ static int get_paths(sd_bus *bus, const char *unit_path, char ***ret_conditions,
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        *ret_conditions = TAKE_PTR(conditions);
-        *ret_paths = TAKE_PTR(paths);
-
-        return n;
+        return 0;
 }
 
-static int output_paths_list(struct path_infos *ps) {
+static int output_paths_list(const PathInfo *paths, size_t n_paths) {
         _cleanup_(table_unrefp) Table *table = NULL;
         int r;
 
-        assert(ps);
-        assert(ps->items || ps->count == 0);
+        assert(paths || n_paths == 0);
 
         table = table_new("path", "condition", "unit", "activates");
         if (!table)
@@ -1053,7 +1096,7 @@ static int output_paths_list(struct path_infos *ps) {
 
         table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
 
-        for (struct path_info *p = ps->items; p < ps->items + ps->count; p++) {
+        for (const PathInfo *p = paths; p < paths + n_paths; p++) {
                 _cleanup_free_ char *unit = NULL;
 
                 unit = format_unit_id(p->id, p->machine);
@@ -1077,18 +1120,21 @@ static int output_paths_list(struct path_infos *ps) {
                 return r;
 
         if (arg_legend != 0)
-                output_legend("path", ps->count);
+                output_legend("path", n_paths);
 
         return 0;
 }
 
 int verb_list_paths(int argc, char *argv[], void *userdata) {
-        _cleanup_(message_set_freep) Set *replies = NULL;
-        _cleanup_strv_free_ char **machines = NULL, **units = NULL;
+        _cleanup_set_free_ Set *replies = NULL;
+        _cleanup_strv_free_ char **units = NULL;
         _cleanup_free_ UnitInfo *unit_infos = NULL;
-        _cleanup_(path_infos_done) struct path_infos path_infos = {};
-        int r, n;
+        PathInfo *paths = NULL;
+        size_t n_paths = 0;
         sd_bus *bus;
+        int r;
+
+        CLEANUP_ARRAY(paths, n_paths, path_info_array_free);
 
         r = acquire_bus(BUS_MANAGER, &bus);
         if (r < 0)
@@ -1101,49 +1147,21 @@ int verb_list_paths(int argc, char *argv[], void *userdata) {
                 return r;
 
         if (argc == 1 || units) {
-                n = get_unit_list_recursive(bus, units, &unit_infos, &replies, &machines);
+                int n;
+
+                n = get_unit_list_recursive(bus, units, &unit_infos, &replies);
                 if (n < 0)
                         return n;
 
                 for (const UnitInfo *u = unit_infos; u < unit_infos + n; u++) {
-                        _cleanup_strv_free_ char **conditions = NULL, **paths = NULL, **triggered = NULL;
-                        int c;
-
-                        if (!endswith(u->id, ".path"))
-                                continue;
-
-                        r = get_triggered_units(bus, u->unit_path, &triggered);
+                        r = path_info_add(bus, u, &paths, &n_paths);
                         if (r < 0)
                                 return r;
-
-                        c = get_paths(bus, u->unit_path, &conditions, &paths);
-                        if (c < 0)
-                                return c;
-
-                        if (!GREEDY_REALLOC(path_infos.items, path_infos.count + c))
-                                return log_oom();
-
-                        for (int i = c - 1; i >= 0; i--) {
-                                char **t;
-
-                                t = strv_copy(triggered);
-                                if (!t)
-                                        return log_oom();
-
-                                path_infos.items[path_infos.count++] = (struct path_info) {
-                                        .machine = u->machine,
-                                        .id = u->id,
-                                        .condition = TAKE_PTR(conditions[i]),
-                                        .path = TAKE_PTR(paths[i]),
-                                        .triggered = t,
-                                };
-                        }
                 }
-
-                typesafe_qsort(path_infos.items, path_infos.count, path_info_compare);
         }
 
-        output_paths_list(&path_infos);
+        typesafe_qsort(paths, n_paths, path_info_compare);
+        output_paths_list(paths, n_paths);
 
         return 0;
 }
index f7353d037e8b5ac499ab635eef8bcd2738080559..cb190545e3cd037775279bbf749aad4d057dd976 100644 (file)
@@ -7,4 +7,4 @@ int verb_list_timers(int argc, char *argv[], void *userdata);
 int verb_list_automounts(int argc, char *argv[], void *userdata);
 int verb_list_paths(int argc, char *argv[], void *userdata);
 
-usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next);
+usec_t calc_next_elapse(const dual_timestamp *nw, const dual_timestamp *next);
index 3371965ac3358981af1057bdc0f821ae1f52dc3b..510c7327156428b7024673e0ef3a0ca28cabe7cc 100644 (file)
@@ -28,7 +28,7 @@ struct fake_pressure_context {
 
 static void *fake_pressure_thread(void *p) {
         _cleanup_free_ struct fake_pressure_context *c = ASSERT_PTR(p);
-        _cleanup_close_ int cfd = -1;
+        _cleanup_close_ int cfd = -EBADF;
 
         usleep(150);
 
@@ -68,7 +68,7 @@ TEST(fake_pressure) {
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
         _cleanup_free_ char *j = NULL, *k = NULL;
         _cleanup_(rm_rf_physical_and_freep) char *tmp = NULL;
-        _cleanup_close_ int fifo_fd = -1, socket_fd = -1;
+        _cleanup_close_ int fifo_fd = -EBADF, socket_fd = -EBADF;
         union sockaddr_union sa;
         pthread_t th;
         int value = 7;
index 0fb76391fd1f4dab870a0db2ca4b102d1489384c..dee012fa2effb80608357ad4a824a15ef3515d1c 100644 (file)
@@ -401,6 +401,74 @@ TEST(FORMAT_TIMESTAMP) {
         }
 }
 
+TEST(format_timestamp_relative_full) {
+        char buf[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESPAN_MAX)];
+        usec_t x;
+
+        /* Years and months */
+        x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 1*USEC_PER_MONTH);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "1 year 1 month ago"));
+
+        x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 2*USEC_PER_MONTH);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "1 year 2 months ago"));
+
+        x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 1*USEC_PER_MONTH);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "2 years 1 month ago"));
+
+        x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 2*USEC_PER_MONTH);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "2 years 2 months ago"));
+
+        /* Months and days */
+        x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 1*USEC_PER_DAY);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "1 month 1 day ago"));
+
+        x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 2*USEC_PER_DAY);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "1 month 2 days ago"));
+
+        x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 1*USEC_PER_DAY);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "2 months 1 day ago"));
+
+        x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 2*USEC_PER_DAY);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "2 months 2 days ago"));
+
+        /* Weeks and days */
+        x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 1*USEC_PER_DAY);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "1 week 1 day ago"));
+
+        x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 2*USEC_PER_DAY);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "1 week 2 days ago"));
+
+        x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 1*USEC_PER_DAY);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "2 weeks 1 day ago"));
+
+        x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 2*USEC_PER_DAY);
+        assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true));
+        log_debug("%s", buf);
+        assert_se(streq(buf, "2 weeks 2 days ago"));
+}
+
 TEST(format_timestamp_relative) {
         char buf[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESPAN_MAX)];
         usec_t x;