diff --git a/man/coredump.conf.xml b/man/coredump.conf.xml
index d0f46cfe053..28e6017c7db 100644
--- a/man/coredump.conf.xml
+++ b/man/coredump.conf.xml
@@ -99,7 +99,7 @@
ExternalSizeMax=
JournalSizeMax=
- The maximum (uncompressed) size in bytes of a
+ The maximum (compressed or uncompressed) size in bytes of a
core to be saved. Unit suffixes are allowed just as in
.
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index d55f3c8f9bd..1edaf157b9e 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -2401,6 +2401,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t MemoryAvailable = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@@ -3504,6 +3506,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
@@ -4063,6 +4067,11 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
MountImages
ExtensionImages
see systemd.exec(5) for their meaning.
+
+ MemoryAvailable indicates how much unused memory is available to the unit before
+ the MemoryMax or MemoryHigh (whichever is lower) limit set by the cgroup
+ memory controller is reached. It will take into consideration limits on all parent slices, other than the
+ limits set on the unit itself.
@@ -4196,6 +4205,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t MemoryAvailable = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@@ -5321,6 +5332,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
@@ -5915,6 +5928,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t MemoryAvailable = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@@ -6886,6 +6901,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
+
+
@@ -7601,6 +7618,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t MemoryAvailable = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@@ -8544,6 +8563,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+
+
@@ -9112,6 +9133,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t MemoryAvailable = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@@ -9403,6 +9426,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
+
+
@@ -9571,6 +9596,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t MemoryAvailable = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@@ -9904,6 +9931,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
+
+
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 6a46e14556d..7fde1efce42 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -3402,6 +3402,77 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
return 1;
}
+int unit_get_memory_available(Unit *u, uint64_t *ret) {
+ uint64_t unit_current, available = UINT64_MAX;
+ CGroupContext *unit_context;
+ const char *memory_file;
+ int r;
+
+ assert(u);
+ assert(ret);
+
+ /* If data from cgroups can be accessed, try to find out how much more memory a unit can
+ * claim before hitting the configured cgroup limits (if any). Consider both MemoryHigh
+ * and MemoryMax, and also any slice the unit might be nested below. */
+
+ if (!UNIT_CGROUP_BOOL(u, memory_accounting))
+ return -ENODATA;
+
+ if (!u->cgroup_path)
+ return -ENODATA;
+
+ /* The root cgroup doesn't expose this information */
+ if (unit_has_host_root_cgroup(u))
+ return -ENODATA;
+
+ if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
+ return -ENODATA;
+
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ memory_file = r > 0 ? "memory.current" : "memory.usage_in_bytes";
+
+ r = cg_get_attribute_as_uint64("memory", u->cgroup_path, memory_file, &unit_current);
+ if (r < 0)
+ return r;
+
+ assert_se(unit_context = unit_get_cgroup_context(u));
+
+ if (unit_context->memory_max != UINT64_MAX || unit_context->memory_high != UINT64_MAX)
+ available = LESS_BY(MIN(unit_context->memory_max, unit_context->memory_high), unit_current);
+
+ for (Unit *slice = UNIT_GET_SLICE(u); slice; slice = UNIT_GET_SLICE(slice)) {
+ uint64_t slice_current, slice_available = UINT64_MAX;
+ CGroupContext *slice_context;
+
+ /* No point in continuing if we can't go any lower */
+ if (available == 0)
+ break;
+
+ if (!slice->cgroup_path)
+ continue;
+
+ slice_context = unit_get_cgroup_context(slice);
+ if (!slice_context)
+ continue;
+
+ if (slice_context->memory_max == UINT64_MAX && slice_context->memory_high == UINT64_MAX)
+ continue;
+
+ r = cg_get_attribute_as_uint64("memory", slice->cgroup_path, memory_file, &slice_current);
+ if (r < 0)
+ continue;
+
+ slice_available = LESS_BY(MIN(slice_context->memory_max, slice_context->memory_high), slice_current);
+ available = MIN(slice_available, available);
+ }
+
+ *ret = available;
+
+ return 0;
+}
+
int unit_get_memory_current(Unit *u, uint64_t *ret) {
int r;
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 1ad5dd38389..e6790eb0e83 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -282,6 +282,7 @@ int unit_watch_all_pids(Unit *u);
int unit_synthesize_cgroup_empty_event(Unit *u);
int unit_get_memory_current(Unit *u, uint64_t *ret);
+int unit_get_memory_available(Unit *u, uint64_t *ret);
int unit_get_tasks_current(Unit *u, uint64_t *ret);
int unit_get_cpu_usage(Unit *u, nsec_t *ret);
int unit_get_io_accounting(Unit *u, CGroupIOAccountingMetric metric, bool allow_cache, uint64_t *ret);
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 6c04c6e5db8..aa10939a041 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -1097,6 +1097,30 @@ static int property_get_current_memory(
return sd_bus_message_append(reply, "t", sz);
}
+static int property_get_available_memory(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ uint64_t sz = UINT64_MAX;
+ Unit *u = userdata;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(u);
+
+ r = unit_get_memory_available(u, &sz);
+ if (r < 0 && r != -ENODATA)
+ log_unit_warning_errno(u, r, "Failed to get total available memory from cgroup: %m");
+
+ return sd_bus_message_append(reply, "t", sz);
+}
+
static int property_get_current_tasks(
sd_bus *bus,
const char *path,
@@ -1541,6 +1565,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
+ SD_BUS_PROPERTY("MemoryAvailable", "t", property_get_available_memory, 0, 0),
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
SD_BUS_PROPERTY("EffectiveCPUs", "ay", property_get_cpuset_cpus, 0, 0),
SD_BUS_PROPERTY("EffectiveMemoryNodes", "ay", property_get_cpuset_mems, 0, 0),
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index b75a7c39ce6..c6c232c7e73 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include
#include
@@ -18,6 +19,7 @@
#include "acl-util.h"
#include "alloc-util.h"
+#include "bus-error.h"
#include "capability-util.h"
#include "cgroup-util.h"
#include "compress.h"
@@ -42,6 +44,7 @@
#include "socket-util.h"
#include "special.h"
#include "stacktrace.h"
+#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
@@ -63,6 +66,10 @@
#define JOURNAL_SIZE_MAX ((size_t) (10LU*1024LU*1024LU))
#endif
+/* When checking for available memory and setting lower limits, don't
+ * go below 4MB for writing core files to storage. */
+#define PROCESS_SIZE_MIN (4U*1024U*1024U)
+
/* Make sure to not make this larger than the maximum journal entry
* size. See DATA_SIZE_MAX in journal-importer.h. */
assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
@@ -329,11 +336,14 @@ static int save_external_coredump(
int *ret_node_fd,
int *ret_data_fd,
uint64_t *ret_size,
+ uint64_t *ret_compressed_size,
bool *ret_truncated) {
- _cleanup_free_ char *fn = NULL, *tmp = NULL;
+ _cleanup_(unlink_and_freep) char *tmp = NULL;
+ _cleanup_free_ char *fn = NULL;
_cleanup_close_ int fd = -1;
uint64_t rlimit, process_limit, max_size;
+ bool truncated, storage_on_tmpfs;
struct stat st;
uid_t uid;
int r;
@@ -343,6 +353,8 @@ static int save_external_coredump(
assert(ret_node_fd);
assert(ret_data_fd);
assert(ret_size);
+ assert(ret_compressed_size);
+ assert(ret_truncated);
r = parse_uid(context->meta[META_ARGV_UID], &uid);
if (r < 0)
@@ -379,92 +391,145 @@ static int save_external_coredump(
if (fd < 0)
return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
- r = copy_bytes(input_fd, fd, max_size, 0);
- if (r < 0) {
- log_error_errno(r, "Cannot store coredump of %s (%s): %m",
- context->meta[META_ARGV_PID], context->meta[META_COMM]);
- goto fail;
+ /* If storage is on tmpfs, the kernel oomd might kill us if there's MemoryMax set on
+ * the service or the slice it belongs to. This is common on low-resources systems,
+ * to avoid crashing processes to take away too many system resources.
+ * Check the cgroup settings, and set max_size to a bit less than half of the
+ * available memory left to the process.
+ * Then, attempt to write the core file uncompressed first - if the write gets
+ * interrupted, we know we won't be able to write it all, so instead compress what
+ * was written so far, delete the uncompressed truncated core, and then continue
+ * compressing from STDIN. Given the compressed core cannot be larger than the
+ * uncompressed one, and 1KB for metadata is accounted for in the calculation, we
+ * should be able to at least store the full compressed core file. */
+
+ storage_on_tmpfs = fd_is_temporary_fs(fd) > 0;
+ if (storage_on_tmpfs && arg_compress) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ uint64_t cgroup_limit = UINT64_MAX;
+ struct statvfs sv;
+
+ /* If we can't get the cgroup limit, just ignore it, but don't fail,
+ * try anyway with the config settings. */
+ r = sd_bus_default_system(&bus);
+ if (r < 0)
+ log_info_errno(r, "Failed to connect to system bus, skipping MemoryAvailable check: %m");
+ else {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ r = sd_bus_get_property_trivial(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1/unit/self",
+ "org.freedesktop.systemd1.Service",
+ "MemoryAvailable",
+ &error,
+ 't', &cgroup_limit);
+ if (r < 0)
+ log_warning_errno(r,
+ "Failed to query MemoryAvailable for current unit, "
+ "falling back to static config settings: %s",
+ bus_error_message(&error, r));
+ }
+
+ max_size = MIN(cgroup_limit, max_size);
+ max_size = LESS_BY(max_size, 1024U) / 2; /* Account for 1KB metadata overhead for compressing */
+ max_size = MAX(PROCESS_SIZE_MIN, max_size); /* Impose a lower minimum */
+
+ /* tmpfs might get full quickly, so check the available space too.
+ * But don't worry about errors here, failing to access the storage
+ * location will be better logged when writing to it. */
+ if (statvfs("/var/lib/systemd/coredump/", &sv) >= 0)
+ max_size = MIN((uint64_t)sv.f_frsize * (uint64_t)sv.f_bfree, max_size);
+
+ log_debug("Limiting core file size to %" PRIu64 " bytes due to cgroup memory limits.", max_size);
}
- *ret_truncated = r == 1;
- if (*ret_truncated)
+
+ r = copy_bytes(input_fd, fd, max_size, 0);
+ if (r < 0)
+ return log_error_errno(r, "Cannot store coredump of %s (%s): %m",
+ context->meta[META_ARGV_PID], context->meta[META_COMM]);
+ truncated = r == 1;
+
+#if HAVE_COMPRESSION
+ if (arg_compress) {
+ _cleanup_(unlink_and_freep) char *tmp_compressed = NULL;
+ _cleanup_free_ char *fn_compressed = NULL;
+ _cleanup_close_ int fd_compressed = -1;
+ uint64_t uncompressed_size = 0;
+
+ if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+ return log_error_errno(errno, "Failed to seek on coredump %s: %m", fn);
+
+ fn_compressed = strjoin(fn, COMPRESSED_EXT);
+ if (!fn_compressed)
+ return log_oom();
+
+ fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
+ if (fd_compressed < 0)
+ return log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
+
+ r = compress_stream(fd, fd_compressed, max_size, &uncompressed_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
+
+ if (truncated && storage_on_tmpfs) {
+ uint64_t partial_uncompressed_size = 0;
+
+ /* Uncompressed write was truncated and we are writing to tmpfs: delete
+ * the uncompressed core, and compress the remaining part from STDIN. */
+
+ tmp = unlink_and_free(tmp);
+ fd = safe_close(fd);
+
+ r = compress_stream(input_fd, fd_compressed, max_size, &partial_uncompressed_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
+ uncompressed_size += partial_uncompressed_size;
+ }
+
+ r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
+ if (r < 0)
+ return r;
+
+ if (fstat(fd_compressed, &st) < 0)
+ return log_error_errno(errno,
+ "Failed to fstat core file %s: %m",
+ coredump_tmpfile_name(tmp_compressed));
+
+ *ret_filename = TAKE_PTR(fn_compressed); /* compressed */
+ *ret_node_fd = TAKE_FD(fd_compressed); /* compressed */
+ *ret_compressed_size = (uint64_t) st.st_size; /* compressed */
+ *ret_data_fd = TAKE_FD(fd);
+ *ret_size = uncompressed_size;
+ *ret_truncated = truncated;
+ tmp_compressed = mfree(tmp_compressed);
+
+ return 0;
+ }
+#endif
+
+ if (truncated)
log_struct(LOG_INFO,
LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size),
"SIZE_LIMIT=%zu", max_size,
"MESSAGE_ID=" SD_MESSAGE_TRUNCATED_CORE_STR);
- if (fstat(fd, &st) < 0) {
- log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp));
- goto fail;
- }
-
- if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
- log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp));
- goto fail;
- }
-
-#if HAVE_COMPRESSION
- /* If we will remove the coredump anyway, do not compress. */
- if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) {
-
- _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
- _cleanup_close_ int fd_compressed = -1;
-
- fn_compressed = strjoin(fn, COMPRESSED_EXT);
- if (!fn_compressed) {
- log_oom();
- goto uncompressed;
- }
-
- fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
- if (fd_compressed < 0) {
- log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
- goto uncompressed;
- }
-
- r = compress_stream(fd, fd_compressed, -1);
- if (r < 0) {
- log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
- goto fail_compressed;
- }
-
- r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
- if (r < 0)
- goto fail_compressed;
-
- /* OK, this worked, we can get rid of the uncompressed version now */
- if (tmp)
- unlink_noerrno(tmp);
-
- *ret_filename = TAKE_PTR(fn_compressed); /* compressed */
- *ret_node_fd = TAKE_FD(fd_compressed); /* compressed */
- *ret_data_fd = TAKE_FD(fd); /* uncompressed */
- *ret_size = (uint64_t) st.st_size; /* uncompressed */
-
- return 0;
-
- fail_compressed:
- if (tmp_compressed)
- (void) unlink(tmp_compressed);
- }
-
-uncompressed:
-#endif
-
r = fix_permissions(fd, tmp, fn, context, uid);
if (r < 0)
- goto fail;
+ return log_error_errno(r, "Failed to fix permissions and finalize coredump %s into %s: %m", coredump_tmpfile_name(tmp), fn);
+
+ if (fstat(fd, &st) < 0)
+ return log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp));
+
+ if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+ return log_error_errno(errno, "Failed to seek on coredump %s: %m", fn);
- *ret_filename = TAKE_PTR(fn);
*ret_data_fd = TAKE_FD(fd);
- *ret_node_fd = -1;
*ret_size = (uint64_t) st.st_size;
+ *ret_truncated = truncated;
return 0;
-
-fail:
- if (tmp)
- (void) unlink(tmp);
- return r;
}
static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
@@ -709,7 +774,7 @@ static int submit_coredump(
_cleanup_free_ char *stacktrace = NULL;
char *core_message;
const char *module_name;
- uint64_t coredump_size = UINT64_MAX;
+ uint64_t coredump_size = UINT64_MAX, coredump_compressed_size = UINT64_MAX;
bool truncated = false;
JsonVariant *module_json;
int r;
@@ -722,7 +787,8 @@ static int submit_coredump(
/* Always stream the coredump to disk, if that's possible */
r = save_external_coredump(context, input_fd,
- &filename, &coredump_node_fd, &coredump_fd, &coredump_size, &truncated);
+ &filename, &coredump_node_fd, &coredump_fd,
+ &coredump_size, &coredump_compressed_size, &truncated);
if (r < 0)
/* Skip whole core dumping part */
goto log;
@@ -730,7 +796,7 @@ static int submit_coredump(
/* If we don't want to keep the coredump on disk, remove it now, as later on we
* will lack the privileges for it. However, we keep the fd to it, so that we can
* still process it and log it. */
- r = maybe_remove_external_coredump(filename, coredump_size);
+ r = maybe_remove_external_coredump(filename, coredump_node_fd >= 0 ? coredump_compressed_size : coredump_size);
if (r < 0)
return r;
if (r == 0) {
@@ -738,7 +804,7 @@ static int submit_coredump(
} else if (arg_storage == COREDUMP_STORAGE_EXTERNAL)
log_info("The core will not be stored: size %"PRIu64" is greater than %"PRIu64" (the configured maximum)",
- coredump_size, arg_external_size_max);
+ coredump_node_fd >= 0 ? coredump_compressed_size : coredump_size, arg_external_size_max);
/* Vacuum again, but exclude the coredump we just created */
(void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
@@ -758,7 +824,7 @@ static int submit_coredump(
log_debug("Not generating stack trace: core size %"PRIu64" is greater "
"than %"PRIu64" (the configured maximum)",
coredump_size, arg_process_size_max);
- } else
+ } else if (coredump_fd >= 0)
coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata);
#endif
@@ -812,7 +878,7 @@ log:
}
/* Optionally store the entire coredump in the journal */
- if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
+ if (arg_storage == COREDUMP_STORAGE_JOURNAL && coredump_fd >= 0) {
if (coredump_size <= arg_journal_size_max) {
size_t sz = 0;
diff --git a/src/libsystemd/sd-journal/compress.c b/src/libsystemd/sd-journal/compress.c
index c788dd8caf9..837abab76c8 100644
--- a/src/libsystemd/sd-journal/compress.c
+++ b/src/libsystemd/sd-journal/compress.c
@@ -550,7 +550,7 @@ int decompress_startswith(
return -EBADMSG;
}
-int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
+int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
#if HAVE_XZ
_cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
lzma_ret ret;
@@ -611,6 +611,9 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
return k;
if (ret == LZMA_STREAM_END) {
+ if (ret_uncompressed_size)
+ *ret_uncompressed_size = s.total_in;
+
log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
s.total_in, s.total_out,
(double) s.total_out / s.total_in * 100);
@@ -626,14 +629,15 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
#define LZ4_BUFSIZE (512*1024u)
-int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
+int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
#if HAVE_LZ4
LZ4F_errorCode_t c;
_cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
_cleanup_free_ void *in_buff = NULL;
_cleanup_free_ char *out_buff = NULL;
- size_t out_allocsize, n, total_in = 0, total_out, offset = 0, frame_size;
+ size_t out_allocsize, n, offset = 0, frame_size;
+ uint64_t total_in = 0, total_out;
int r;
static const LZ4F_preferences_t preferences = {
.frameInfo.blockSizeID = 5,
@@ -698,7 +702,10 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
if (r < 0)
return r;
- log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
+ if (ret_uncompressed_size)
+ *ret_uncompressed_size = total_in;
+
+ log_debug("LZ4 compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
total_in, total_out,
(double) total_out / total_in * 100);
@@ -844,7 +851,7 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
#endif
}
-int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
+int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
#if HAVE_ZSTD
_cleanup_(ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL;
_cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
@@ -933,6 +940,9 @@ int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
break;
}
+ if (ret_uncompressed_size)
+ *ret_uncompressed_size = in_bytes;
+
if (in_bytes > 0)
log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
in_bytes, max_bytes - left, (double) (max_bytes - left) / in_bytes * 100);
diff --git a/src/libsystemd/sd-journal/compress.h b/src/libsystemd/sd-journal/compress.h
index a82049edd8a..005e60e2e3e 100644
--- a/src/libsystemd/sd-journal/compress.h
+++ b/src/libsystemd/sd-journal/compress.h
@@ -64,9 +64,9 @@ int decompress_startswith(int compression,
const void *prefix, size_t prefix_len,
uint8_t extra);
-int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes);
-int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes);
-int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes);
+int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size);
+int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size);
+int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size);
int decompress_stream_xz(int fdf, int fdt, uint64_t max_size);
int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size);
@@ -82,7 +82,7 @@ int decompress_stream_zstd(int fdf, int fdt, uint64_t max_size);
# define compress_stream compress_stream_xz
# define COMPRESSED_EXT ".xz"
#else
-static inline int compress_stream(int fdf, int fdt, uint64_t max_size) {
+static inline int compress_stream(int fdf, int fdt, uint64_t max_size, uint64_t *ret_uncompressed_size) {
return -EOPNOTSUPP;
}
# define COMPRESSED_EXT ""
diff --git a/src/libsystemd/sd-journal/test-compress.c b/src/libsystemd/sd-journal/test-compress.c
index 0d5069d4772..54e0e738b3b 100644
--- a/src/libsystemd/sd-journal/test-compress.c
+++ b/src/libsystemd/sd-journal/test-compress.c
@@ -41,7 +41,7 @@ typedef int (decompress_sw_t)(const void *src, uint64_t src_size,
const void *prefix, size_t prefix_len,
uint8_t extra);
-typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes);
+typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes, uint64_t *uncompressed_size);
typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size);
#if HAVE_COMPRESSION
@@ -176,6 +176,7 @@ _unused_ static void test_compress_stream(const char *compression,
int r;
_cleanup_free_ char *cmd = NULL, *cmd2 = NULL;
struct stat st = {};
+ uint64_t uncompressed_size;
r = find_executable(cat, NULL);
if (r < 0) {
@@ -193,7 +194,7 @@ _unused_ static void test_compress_stream(const char *compression,
assert_se((dst = mkostemp_safe(pattern)) >= 0);
- assert_se(compress(src, dst, -1) == 0);
+ assert_se(compress(src, dst, -1, &uncompressed_size) == 0);
if (cat) {
assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0);
@@ -205,6 +206,7 @@ _unused_ static void test_compress_stream(const char *compression,
assert_se((dst2 = mkostemp_safe(pattern2)) >= 0);
assert_se(stat(srcfile, &st) == 0);
+ assert_se((uint64_t)st.st_size == uncompressed_size);
assert_se(lseek(dst, 0, SEEK_SET) == 0);
r = decompress(dst, dst2, st.st_size);
diff --git a/src/shared/bus-print-properties.c b/src/shared/bus-print-properties.c
index cbd5fb087e0..b45921943a8 100644
--- a/src/shared/bus-print-properties.c
+++ b/src/shared/bus-print-properties.c
@@ -165,7 +165,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
bus_print_property_value(name, expected_value, flags, "[not set]");
- else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
+ else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "MemoryAvailable") && u == CGROUP_LIMIT_MAX) ||
(STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) ||
(startswith(name, "Limit") && u == UINT64_MAX) ||
(startswith(name, "DefaultLimit") && u == UINT64_MAX))
diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c
index 3686ac3c768..d4d5a2b427f 100644
--- a/src/systemctl/systemctl-show.c
+++ b/src/systemctl/systemctl-show.c
@@ -247,6 +247,7 @@ typedef struct UnitStatusInfo {
uint64_t memory_max;
uint64_t memory_swap_max;
uint64_t memory_limit;
+ uint64_t memory_available;
uint64_t cpu_usage_nsec;
uint64_t tasks_current;
uint64_t tasks_max;
@@ -682,6 +683,7 @@ static void print_status_info(
if (i->memory_min > 0 || i->memory_low > 0 ||
i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
i->memory_swap_max != CGROUP_LIMIT_MAX ||
+ i->memory_available != CGROUP_LIMIT_MAX ||
i->memory_limit != CGROUP_LIMIT_MAX) {
const char *prefix = "";
@@ -710,6 +712,10 @@ static void print_status_info(
printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit));
prefix = " ";
}
+ if (i->memory_available != CGROUP_LIMIT_MAX) {
+ printf("%savailable: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_available));
+ prefix = " ";
+ }
printf(")");
}
printf("\n");
@@ -1827,6 +1833,7 @@ static int show_one(
{ "Where", "s", NULL, offsetof(UnitStatusInfo, where) },
{ "What", "s", NULL, offsetof(UnitStatusInfo, what) },
{ "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) },
+ { "MemoryAvailable", "t", NULL, offsetof(UnitStatusInfo, memory_available) },
{ "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) },
{ "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) },
{ "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) },
@@ -1869,6 +1876,7 @@ static int show_one(
.memory_max = CGROUP_LIMIT_MAX,
.memory_swap_max = CGROUP_LIMIT_MAX,
.memory_limit = UINT64_MAX,
+ .memory_available = CGROUP_LIMIT_MAX,
.cpu_usage_nsec = UINT64_MAX,
.tasks_current = UINT64_MAX,
.tasks_max = UINT64_MAX,