mirror of
https://github.com/systemd/systemd.git
synced 2025-03-31 14:50:15 +03:00
Merge pull request #19166 from bluca/coredump_compress_on_the_fly
coredump: compress on the fly
This commit is contained in:
commit
66973219c0
@ -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>
|
||||
|
@ -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"/>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 ""
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user