Merge pull request #19166 from bluca/coredump_compress_on_the_fly

coredump: compress on the fly
This commit is contained in:
Lennart Poettering 2021-06-08 18:24:34 +02:00 committed by GitHub
commit 66973219c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 305 additions and 93 deletions

View file

@ -99,7 +99,7 @@
<term><varname>ExternalSizeMax=</varname></term>
<term><varname>JournalSizeMax=</varname></term>
<listitem><para>The maximum (uncompressed) size in bytes of a
<listitem><para>The maximum (compressed or uncompressed) size in bytes of a
core to be saved. Unit suffixes are allowed just as in
<option>ProcessSizeMax=</option></para></listitem>.
</varlistentry>

View file

@ -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 {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@ -4063,6 +4067,11 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<varname>MountImages</varname>
<varname>ExtensionImages</varname>
see systemd.exec(5) for their meaning.</para>
<para><varname>MemoryAvailable</varname> indicates how much unused memory is available to the unit before
the <literal>MemoryMax</literal> or <literal>MemoryHigh</literal> (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.</para>
</refsect2>
</refsect1>
@ -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 {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@ -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 {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@ -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 {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@ -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 {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@ -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 {
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>

View file

@ -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;

View file

@ -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);

View file

@ -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),

View file

@ -3,6 +3,7 @@
#include <errno.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/statvfs.h>
#include <sys/xattr.h>
#include <unistd.h>
@ -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;

View file

@ -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);

View file

@ -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 ""

View file

@ -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);

View file

@ -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))

View file

@ -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,