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.
<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>,
<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>
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>
/* 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);
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);
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);
+}
}
+#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);
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);
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;
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,
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,
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");
return buf;
}
-char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
+char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
static const struct {
const char *suffix;
usec_t usec;
#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);
_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);
s++; \
} \
\
- return NULL; \
+ return c ? NULL : (type *) s; \
}
DEFINE_STRCHR(char, strchr8);
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);
#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;
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);
* 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
#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);
#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 " ####";
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,
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) {
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) {
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)); \
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);
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;
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;
if (size < sizeof(SmbiosHeader))
return NULL;
- SmbiosHeader *header = (SmbiosHeader *) p;
+ const SmbiosHeader *header = (const SmbiosHeader *) p;
/* End of table. */
if (header->type == 127)
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;
/* 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;
}
}
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;
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;
+}
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);
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);
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;
* 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 */
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
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;
(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");
* 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");
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");
#include <unistd.h>
#include "alloc-util.h"
+#include "cgroup-setup.h"
#include "dbus-scope.h"
#include "dbus-unit.h"
#include "exit-status.h"
_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;
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),
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;
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);
(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);
/* 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);
/* 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)
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)
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
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) {
}
_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);
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;
}
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);
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:
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:
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);
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;
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 "-";
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);
TABLE_TIMESTAMP,
TABLE_TIMESTAMP_UTC,
TABLE_TIMESTAMP_RELATIVE,
+ TABLE_TIMESTAMP_LEFT,
TABLE_TIMESTAMP_DATE,
TABLE_TIMESPAN,
TABLE_TIMESPAN_MSEC,
* the underlying block device. */
r = blockdev_get_sector_size(fd, §or_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));
#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;
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);
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;
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;
}
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,
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);
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)
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);
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);
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)
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(
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);
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)
(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);
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);
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);
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)
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);
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;
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",
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),
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;
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);
}
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)
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;
/* 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);
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,
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);
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)
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);
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)
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;
}
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);
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);
_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;
}
}
+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;