summaryrefslogtreecommitdiff
path: root/src/journal/coredump.c
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2014-06-25 01:03:03 -0400
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2014-06-26 01:41:04 -0400
commitcfd652ed617282b06ccbdedb7adc6963b901dde7 (patch)
tree48a3f85ce61a74ecb1d40c6d5ad9a581e592ca1c /src/journal/coredump.c
parent355b59e252c9910e44a1ad95c045ba8db58a4f6a (diff)
coredump: compress core files
Unfortunately the core is first written uncompressed, then compressed by reading from disk and writing to the output file. This is ugly and slow, but I don't see a way around, if we want to get the backtrace without keeping everything in memory.
Diffstat (limited to 'src/journal/coredump.c')
-rw-r--r--src/journal/coredump.c129
1 files changed, 96 insertions, 33 deletions
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index 390f9598eb..1c5c99c39d 100644
--- a/src/journal/coredump.c
+++ b/src/journal/coredump.c
@@ -41,6 +41,7 @@
#include "copy.h"
#include "stacktrace.h"
#include "path-util.h"
+#include "compress.h"
#ifdef HAVE_ACL
#include <sys/acl.h>
@@ -195,6 +196,47 @@ static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
#define filename_escape(s) xescape((s), "./ ")
+static int fix_permissions(int fd, const char *filename, const char *target,
+ const char *info[_INFO_LEN], uid_t uid) {
+
+ /* Ignore errors on these */
+ fchmod(fd, 0640);
+ fix_acl(fd, uid);
+ fix_xattr(fd, info);
+
+ if (fsync(fd) < 0) {
+ log_error("Failed to sync coredump %s: %m", filename);
+ return -errno;
+ }
+
+ if (rename(filename, target) < 0) {
+ log_error("Failed to rename coredump %s -> %s: %m", filename, target);
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int maybe_remove_external_coredump(const char *filename, off_t size) {
+
+ /* Returns 1 if might remove, 0 if will not remove, <0 on error. */
+
+ if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
+ size <= arg_external_size_max)
+ return 0;
+
+ if (!filename)
+ return 1;
+
+ if (unlink(filename) < 0) {
+ log_error("Failed to unlink %s: %m", filename);
+ return -errno;
+ }
+
+ return 1;
+}
+
+
static int save_external_coredump(const char *info[_INFO_LEN],
uid_t uid,
char **ret_filename,
@@ -247,7 +289,7 @@ static int save_external_coredump(const char *info[_INFO_LEN],
fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
if (fd < 0) {
- log_error("Failed to create coredump file: %m");
+ log_error("Failed to create coredump file %s: %m", tmp);
return -errno;
}
@@ -265,28 +307,66 @@ static int save_external_coredump(const char *info[_INFO_LEN],
goto fail;
}
- /* Ignore errors on these */
- fchmod(fd, 0640);
- fix_acl(fd, uid);
- fix_xattr(fd, info);
-
- if (fsync(fd) < 0) {
- log_error("Failed to sync coredump %s: %m", tmp);
- r = -errno;
- goto fail;
+ if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
+ log_error("Failed to seek on %s: %m", tmp);
+ goto uncompressed;
}
if (fstat(fd, &st) < 0) {
log_error("Failed to fstat coredump %s: %m", tmp);
- r = -errno;
goto fail;
}
- if (rename(tmp, fn) < 0) {
- log_error("Failed to rename coredump %s -> %s: %m", tmp, fn);
- r = -errno;
- goto fail;
+#ifdef HAVE_XZ
+ /* If we will remove the coredump anyway, do not compress. */
+ if (maybe_remove_external_coredump(NULL, st.st_size) == 0) {
+
+ _cleanup_free_ char *fn2 = NULL;
+ char *tmp2;
+ _cleanup_close_ int fd2 = -1;
+
+ tmp2 = strappenda(tmp, ".xz");
+ fd2 = open(tmp2, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
+ if (fd2 < 0) {
+ log_error("Failed to create file %s: %m", tmp2);
+ goto uncompressed;
+ }
+
+ r = compress_stream(fd, fd2, -1);
+ if (r < 0) {
+ log_error("Failed to compress %s: %s", tmp2, strerror(-r));
+ unlink_noerrno(tmp2);
+ goto fail2;
+ }
+
+ fn2 = strappend(fn, ".xz");
+ if (!fn2) {
+ log_oom();
+ goto fail2;
+ }
+
+ r = fix_permissions(fd2, tmp2, fn2, info, uid);
+ if (r < 0)
+ goto fail2;
+
+ *ret_filename = fn2; /* compressed */
+ *ret_fd = fd; /* uncompressed */
+ *ret_size = st.st_size; /* uncompressed */
+
+ fn2 = NULL;
+ fd = -1;
+
+ return 0;
+
+ fail2:
+ unlink_noerrno(tmp2);
}
+#endif
+
+uncompressed:
+ r = fix_permissions(fd, tmp, fn, info, uid);
+ if (r < 0)
+ goto fail;
*ret_filename = fn;
*ret_fd = fd;
@@ -317,7 +397,7 @@ static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_s
field = malloc(9 + size);
if (!field) {
- log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
+ log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
return -ENOMEM;
}
@@ -341,23 +421,6 @@ static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_s
return 0;
}
-static int maybe_remove_external_coredump(const char *filename, off_t size) {
-
- if (!filename)
- return 0;
-
- if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
- size <= arg_external_size_max)
- return 0;
-
- if (unlink(filename) < 0) {
- log_error("Failed to unlink %s: %m", filename);
- return -errno;
- }
-
- return 0;
-}
-
int main(int argc, char* argv[]) {
_cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,