summaryrefslogtreecommitdiff
path: root/src/coredump
diff options
context:
space:
mode:
Diffstat (limited to 'src/coredump')
-rw-r--r--src/coredump/coredump.c195
-rw-r--r--src/coredump/coredumpctl.c349
2 files changed, 361 insertions, 183 deletions
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index dcc09fcc6d..a982c204be 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -28,9 +28,10 @@
#include <elfutils/libdwfl.h>
#endif
+#include "sd-daemon.h"
#include "sd-journal.h"
#include "sd-login.h"
-#include "sd-daemon.h"
+#include "sd-messages.h"
#include "acl-util.h"
#include "alloc-util.h"
@@ -93,7 +94,6 @@ typedef enum CoredumpStorage {
COREDUMP_STORAGE_NONE,
COREDUMP_STORAGE_EXTERNAL,
COREDUMP_STORAGE_JOURNAL,
- COREDUMP_STORAGE_BOTH,
_COREDUMP_STORAGE_MAX,
_COREDUMP_STORAGE_INVALID = -1
} CoredumpStorage;
@@ -102,7 +102,6 @@ static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
[COREDUMP_STORAGE_NONE] = "none",
[COREDUMP_STORAGE_EXTERNAL] = "external",
[COREDUMP_STORAGE_JOURNAL] = "journal",
- [COREDUMP_STORAGE_BOTH] = "both",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
@@ -128,13 +127,17 @@ static int parse_config(void) {
{}
};
- return config_parse_many(PKGSYSCONFDIR "/coredump.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/coredump.conf",
CONF_PATHS_NULSTR("systemd/coredump.conf.d"),
"Coredump\0",
config_item_table_lookup, items,
false, NULL);
}
+static inline uint64_t storage_size_max(void) {
+ return arg_storage == COREDUMP_STORAGE_EXTERNAL ? arg_external_size_max : arg_journal_size_max;
+}
+
static int fix_acl(int fd, uid_t uid) {
#ifdef HAVE_ACL
@@ -247,7 +250,7 @@ static int maybe_remove_external_coredump(const char *filename, uint64_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) &&
+ if (arg_storage == COREDUMP_STORAGE_EXTERNAL &&
size <= arg_external_size_max)
return 0;
@@ -327,14 +330,17 @@ static int save_external_coredump(
r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit);
if (r < 0)
return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]);
- if (rlimit <= 0) {
- /* Is coredumping disabled? Then don't bother saving/processing the coredump */
- log_info("Core Dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]);
+ if (rlimit < page_size()) {
+ /* Is coredumping disabled? Then don't bother saving/processing the coredump.
+ * Anything below PAGE_SIZE cannot give a readable coredump (the kernel uses
+ * ELF_EXEC_PAGESIZE which is not easily accessible, but is usually the same as PAGE_SIZE. */
+ log_info("Resource limits disable core dumping for process %s (%s).",
+ context[CONTEXT_PID], context[CONTEXT_COMM]);
return -EBADSLT;
}
/* Never store more than the process configured, or than we actually shall keep or process */
- max_size = MIN(rlimit, MAX(arg_process_size_max, arg_external_size_max));
+ max_size = MIN(rlimit, MAX(arg_process_size_max, storage_size_max()));
r = make_filename(context, &fn);
if (r < 0)
@@ -347,19 +353,18 @@ static int save_external_coredump(
return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
r = copy_bytes(input_fd, fd, max_size, false);
- if (r == -EFBIG) {
- log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]);
- goto fail;
- } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
- log_error("Not enough disk space for coredump of %s (%s), refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]);
- goto fail;
- } else if (r < 0) {
- log_error_errno(r, "Failed to dump coredump to file: %m");
+ if (r < 0) {
+ log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]);
goto fail;
- }
+ } else if (r == 1)
+ log_struct(LOG_INFO,
+ LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size),
+ "SIZE_LIMIT=%zu", max_size,
+ LOG_MESSAGE_ID(SD_MESSAGE_TRUNCATED_CORE),
+ NULL);
if (fstat(fd, &st) < 0) {
- log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp));
+ log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp));
goto fail;
}
@@ -370,8 +375,7 @@ static int save_external_coredump(
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
/* If we will remove the coredump anyway, do not compress. */
- if (maybe_remove_external_coredump(NULL, st.st_size) == 0
- && arg_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;
@@ -558,6 +562,89 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
return 0;
}
+static int get_process_ns(pid_t pid, const char *namespace, ino_t *ns) {
+ const char *p;
+ struct stat stbuf;
+ _cleanup_close_ int proc_ns_dir_fd;
+
+ p = procfs_file_alloca(pid, "ns");
+
+ proc_ns_dir_fd = open(p, O_DIRECTORY | O_CLOEXEC | O_RDONLY);
+ if (proc_ns_dir_fd < 0)
+ return -errno;
+
+ if (fstatat(proc_ns_dir_fd, namespace, &stbuf, /* flags */0) < 0)
+ return -errno;
+
+ *ns = stbuf.st_ino;
+ return 0;
+}
+
+static int get_mount_namespace_leader(pid_t pid, pid_t *container_pid) {
+ pid_t cpid = pid, ppid = 0;
+ ino_t proc_mntns;
+ int r = 0;
+
+ r = get_process_ns(pid, "mnt", &proc_mntns);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ ino_t parent_mntns;
+
+ r = get_process_ppid(cpid, &ppid);
+ if (r < 0)
+ return r;
+
+ r = get_process_ns(ppid, "mnt", &parent_mntns);
+ if (r < 0)
+ return r;
+
+ if (proc_mntns != parent_mntns)
+ break;
+
+ if (ppid == 1)
+ return -ENOENT;
+
+ cpid = ppid;
+ }
+
+ *container_pid = ppid;
+ return 0;
+}
+
+/* Returns 1 if the parent was found.
+ * Returns 0 if there is not a process we can call the pid's
+ * container parent (the pid's process isn't 'containerized').
+ * Returns a negative number on errors.
+ */
+static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) {
+ int r = 0;
+ pid_t container_pid;
+ const char *proc_root_path;
+ struct stat root_stat, proc_root_stat;
+
+ /* To compare inodes of / and /proc/[pid]/root */
+ if (stat("/", &root_stat) < 0)
+ return -errno;
+
+ proc_root_path = procfs_file_alloca(pid, "root");
+ if (stat(proc_root_path, &proc_root_stat) < 0)
+ return -errno;
+
+ /* The process uses system root. */
+ if (proc_root_stat.st_ino == root_stat.st_ino) {
+ *cmdline = NULL;
+ return 0;
+ }
+
+ r = get_mount_namespace_leader(pid, &container_pid);
+ if (r < 0)
+ return r;
+
+ return get_process_cmdline(container_pid, 0, false, cmdline);
+}
+
static int change_uid_gid(const char *context[]) {
uid_t uid;
gid_t gid;
@@ -593,7 +680,7 @@ static int submit_coredump(
_cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
_cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL;
- uint64_t coredump_size;
+ uint64_t coredump_size = UINT64_MAX;
int r;
assert(context);
@@ -620,7 +707,9 @@ static int submit_coredump(
coredump_filename = strjoina("COREDUMP_FILENAME=", filename);
IOVEC_SET_STRING(iovec[n_iovec++], coredump_filename);
- }
+ } else if (arg_storage == COREDUMP_STORAGE_EXTERNAL)
+ log_info("The core will not be stored: size %zu is greater than %zu (the configured maximum)",
+ 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);
@@ -645,7 +734,9 @@ static int submit_coredump(
log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
else
log_warning_errno(r, "Failed to generate stack trace: %m");
- }
+ } else
+ log_debug("Not generating stack trace: core size %zu is greater than %zu (the configured maximum)",
+ coredump_size, arg_process_size_max);
if (!core_message)
#endif
@@ -655,18 +746,22 @@ log:
IOVEC_SET_STRING(iovec[n_iovec++], core_message);
/* Optionally store the entire coredump in the journal */
- if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
- coredump_size <= arg_journal_size_max) {
- size_t sz = 0;
-
- /* Store the coredump itself in the journal */
-
- r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
- if (r >= 0) {
- iovec[n_iovec].iov_base = coredump_data;
- iovec[n_iovec].iov_len = sz;
- n_iovec++;
- }
+ if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
+ if (coredump_size <= arg_journal_size_max) {
+ size_t sz = 0;
+
+ /* Store the coredump itself in the journal */
+
+ r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
+ if (r >= 0) {
+ iovec[n_iovec].iov_base = coredump_data;
+ iovec[n_iovec].iov_len = sz;
+ n_iovec++;
+ } else
+ log_warning_errno(r, "Failed to attach the core to the journal entry: %m");
+ } else
+ log_info("The core will not be stored: size %zu is greater than %zu (the configured maximum)",
+ coredump_size, arg_journal_size_max);
}
assert(n_iovec <= n_iovec_allocated);
@@ -933,11 +1028,13 @@ static int process_kernel(int argc, char* argv[]) {
/* The larger ones we allocate on the heap */
_cleanup_free_ char
*core_owner_uid = NULL, *core_open_fds = NULL, *core_proc_status = NULL,
- *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL;
+ *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL,
+ *core_proc_mountinfo = NULL, *core_container_cmdline = NULL;
_cleanup_free_ char *exe = NULL, *comm = NULL;
const char *context[_CONTEXT_MAX];
- struct iovec iovec[25];
+ bool proc_self_root_is_slash;
+ struct iovec iovec[27];
size_t n_iovec = 0;
uid_t owner_uid;
const char *p;
@@ -1110,6 +1207,15 @@ static int process_kernel(int argc, char* argv[]) {
IOVEC_SET_STRING(iovec[n_iovec++], core_proc_cgroup);
}
+ p = procfs_file_alloca(pid, "mountinfo");
+ if (read_full_file(p, &t, NULL) >=0) {
+ core_proc_mountinfo = strappend("COREDUMP_PROC_MOUNTINFO=", t);
+ free(t);
+
+ if (core_proc_mountinfo)
+ IOVEC_SET_STRING(iovec[n_iovec++], core_proc_mountinfo);
+ }
+
if (get_process_cwd(pid, &t) >= 0) {
core_cwd = strjoina("COREDUMP_CWD=", t);
free(t);
@@ -1119,9 +1225,20 @@ static int process_kernel(int argc, char* argv[]) {
if (get_process_root(pid, &t) >= 0) {
core_root = strjoina("COREDUMP_ROOT=", t);
- free(t);
IOVEC_SET_STRING(iovec[n_iovec++], core_root);
+
+ /* If the process' root is "/", then there is a chance it has
+ * mounted own root and hence being containerized. */
+ proc_self_root_is_slash = strcmp(t, "/") == 0;
+ free(t);
+ if (proc_self_root_is_slash && get_process_container_parent_cmdline(pid, &t) > 0) {
+ core_container_cmdline = strappend("COREDUMP_CONTAINER_CMDLINE=", t);
+ free(t);
+
+ if (core_container_cmdline)
+ IOVEC_SET_STRING(iovec[n_iovec++], core_container_cmdline);
+ }
}
if (get_process_environ(pid, &t) >= 0) {
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index 27b1e0fb3f..0e5351e621 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -30,6 +30,7 @@
#include "compress.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "journal-internal.h"
#include "log.h"
#include "macro.h"
@@ -279,11 +280,10 @@ static int retrieve(const void *data,
free(*var);
*var = v;
- return 0;
+ return 1;
}
-static void print_field(FILE* file, sd_journal *j) {
- _cleanup_free_ char *value = NULL;
+static int print_field(FILE* file, sd_journal *j) {
const void *d;
size_t l;
@@ -292,37 +292,59 @@ static void print_field(FILE* file, sd_journal *j) {
assert(arg_field);
- SD_JOURNAL_FOREACH_DATA(j, d, l)
- retrieve(d, l, arg_field, &value);
+ /* A (user-specified) field may appear more than once for a given entry.
+ * We will print all of the occurences.
+ * This is different below for fields that systemd-coredump uses,
+ * because they cannot meaningfully appear more than once.
+ */
+ SD_JOURNAL_FOREACH_DATA(j, d, l) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ r = retrieve(d, l, arg_field, &value);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ fprintf(file, "%s\n", value);
+ }
- if (value)
- fprintf(file, "%s\n", value);
+ return 0;
}
+#define RETRIEVE(d, l, name, arg) \
+ { \
+ int _r = retrieve(d, l, name, &arg); \
+ if (_r < 0) \
+ return _r; \
+ if (_r > 0) \
+ continue; \
+ }
+
static int print_list(FILE* file, sd_journal *j, int had_legend) {
_cleanup_free_ char
*pid = NULL, *uid = NULL, *gid = NULL,
*sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
- *filename = NULL;
+ *filename = NULL, *coredump = NULL;
const void *d;
size_t l;
usec_t t;
char buf[FORMAT_TIMESTAMP_MAX];
int r;
- bool present;
+ const char *present;
assert(file);
assert(j);
SD_JOURNAL_FOREACH_DATA(j, d, l) {
- retrieve(d, l, "COREDUMP_PID", &pid);
- retrieve(d, l, "COREDUMP_UID", &uid);
- retrieve(d, l, "COREDUMP_GID", &gid);
- retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
- retrieve(d, l, "COREDUMP_EXE", &exe);
- retrieve(d, l, "COREDUMP_COMM", &comm);
- retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
- retrieve(d, l, "COREDUMP_FILENAME", &filename);
+ RETRIEVE(d, l, "COREDUMP_PID", pid);
+ RETRIEVE(d, l, "COREDUMP_UID", uid);
+ RETRIEVE(d, l, "COREDUMP_GID", gid);
+ RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl);
+ RETRIEVE(d, l, "COREDUMP_EXE", exe);
+ RETRIEVE(d, l, "COREDUMP_COMM", comm);
+ RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
+ RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
+ RETRIEVE(d, l, "COREDUMP", coredump);
}
if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
@@ -335,7 +357,6 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
return log_error_errno(r, "Failed to get realtime timestamp: %m");
format_timestamp(buf, sizeof(buf), t);
- present = filename && access(filename, F_OK) == 0;
if (!had_legend && !arg_no_legend)
fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
@@ -344,16 +365,28 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
5, "UID",
5, "GID",
3, "SIG",
- 1, "PRESENT",
+ 8, "COREFILE",
"EXE");
- fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
+ if (filename)
+ if (access(filename, R_OK) == 0)
+ present = "present";
+ else if (errno == ENOENT)
+ present = "missing";
+ else
+ present = "error";
+ else if (coredump)
+ present = "journal";
+ else
+ present = "none";
+
+ fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
FORMAT_TIMESTAMP_WIDTH, buf,
6, strna(pid),
5, strna(uid),
5, strna(gid),
3, strna(sgnl),
- 1, present ? "*" : "",
+ 8, present,
strna(exe ?: (comm ?: cmdline)));
return 0;
@@ -366,7 +399,8 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
*unit = NULL, *user_unit = NULL, *session = NULL,
*boot_id = NULL, *machine_id = NULL, *hostname = NULL,
*slice = NULL, *cgroup = NULL, *owner_uid = NULL,
- *message = NULL, *timestamp = NULL, *filename = NULL;
+ *message = NULL, *timestamp = NULL, *filename = NULL,
+ *coredump = NULL;
const void *d;
size_t l;
int r;
@@ -375,25 +409,26 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
assert(j);
SD_JOURNAL_FOREACH_DATA(j, d, l) {
- retrieve(d, l, "COREDUMP_PID", &pid);
- retrieve(d, l, "COREDUMP_UID", &uid);
- retrieve(d, l, "COREDUMP_GID", &gid);
- retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
- retrieve(d, l, "COREDUMP_EXE", &exe);
- retrieve(d, l, "COREDUMP_COMM", &comm);
- retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
- retrieve(d, l, "COREDUMP_UNIT", &unit);
- retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
- retrieve(d, l, "COREDUMP_SESSION", &session);
- retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
- retrieve(d, l, "COREDUMP_SLICE", &slice);
- retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
- retrieve(d, l, "COREDUMP_TIMESTAMP", &timestamp);
- retrieve(d, l, "COREDUMP_FILENAME", &filename);
- retrieve(d, l, "_BOOT_ID", &boot_id);
- retrieve(d, l, "_MACHINE_ID", &machine_id);
- retrieve(d, l, "_HOSTNAME", &hostname);
- retrieve(d, l, "MESSAGE", &message);
+ RETRIEVE(d, l, "COREDUMP_PID", pid);
+ RETRIEVE(d, l, "COREDUMP_UID", uid);
+ RETRIEVE(d, l, "COREDUMP_GID", gid);
+ RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl);
+ RETRIEVE(d, l, "COREDUMP_EXE", exe);
+ RETRIEVE(d, l, "COREDUMP_COMM", comm);
+ RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
+ RETRIEVE(d, l, "COREDUMP_UNIT", unit);
+ RETRIEVE(d, l, "COREDUMP_USER_UNIT", user_unit);
+ RETRIEVE(d, l, "COREDUMP_SESSION", session);
+ RETRIEVE(d, l, "COREDUMP_OWNER_UID", owner_uid);
+ RETRIEVE(d, l, "COREDUMP_SLICE", slice);
+ RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup);
+ RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp);
+ RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
+ RETRIEVE(d, l, "COREDUMP", coredump);
+ RETRIEVE(d, l, "_BOOT_ID", boot_id);
+ RETRIEVE(d, l, "_MACHINE_ID", machine_id);
+ RETRIEVE(d, l, "_HOSTNAME", hostname);
+ RETRIEVE(d, l, "MESSAGE", message);
}
if (need_space)
@@ -476,7 +511,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
if (unit)
fprintf(file, " Unit: %s\n", unit);
if (user_unit)
- fprintf(file, " User Unit: %s\n", unit);
+ fprintf(file, " User Unit: %s\n", user_unit);
if (slice)
fprintf(file, " Slice: %s\n", slice);
if (session)
@@ -504,8 +539,13 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
if (hostname)
fprintf(file, " Hostname: %s\n", hostname);
- if (filename && access(filename, F_OK) == 0)
- fprintf(file, " Coredump: %s\n", filename);
+ if (filename)
+ fprintf(file, " Storage: %s%s\n", filename,
+ access(filename, R_OK) < 0 ? " (inaccessible)" : "");
+ else if (coredump)
+ fprintf(file, " Storage: journal\n");
+ else
+ fprintf(file, " Storage: none\n");
if (message) {
_cleanup_free_ char *m = NULL;
@@ -533,15 +573,15 @@ static int focus(sd_journal *j) {
return r;
}
-static void print_entry(sd_journal *j, unsigned n_found) {
+static int print_entry(sd_journal *j, unsigned n_found) {
assert(j);
if (arg_action == ACTION_INFO)
- print_info(stdout, j, n_found);
+ return print_info(stdout, j, n_found);
else if (arg_field)
- print_field(stdout, j);
+ return print_field(stdout, j);
else
- print_list(stdout, j, n_found);
+ return print_list(stdout, j, n_found);
}
static int dump_list(sd_journal *j) {
@@ -560,10 +600,13 @@ static int dump_list(sd_journal *j) {
if (r < 0)
return r;
- print_entry(j, 0);
+ return print_entry(j, 0);
} else {
- SD_JOURNAL_FOREACH(j)
- print_entry(j, n_found++);
+ SD_JOURNAL_FOREACH(j) {
+ r = print_entry(j, n_found++);
+ if (r < 0)
+ return r;
+ }
if (!arg_field && n_found <= 0) {
log_notice("No coredumps found.");
@@ -574,116 +617,142 @@ static int dump_list(sd_journal *j) {
return 0;
}
-static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
+static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) {
const char *data;
_cleanup_free_ char *filename = NULL;
size_t len;
- int r;
+ int r, fd;
+ _cleanup_close_ int fdt = -1;
+ char *temp = NULL;
- assert((fd >= 0) != !!path);
- assert(!!path == !!unlink_temp);
+ assert(!(file && path)); /* At most one can be specified */
+ assert(!!path == !!unlink_temp); /* Those must be specified together */
- /* Prefer uncompressed file to journal (probably cached) to
- * compressed file (probably uncached). */
+ /* Look for a coredump on disk first. */
r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
- if (r < 0 && r != -ENOENT)
- log_warning_errno(r, "Failed to retrieve COREDUMP_FILENAME: %m");
- else if (r == 0)
+ if (r == 0)
retrieve(data, len, "COREDUMP_FILENAME", &filename);
+ else {
+ if (r != -ENOENT)
+ return log_error_errno(r, "Failed to retrieve COREDUMP_FILENAME field: %m");
+ /* Check that we can have a COREDUMP field. We still haven't set a high
+ * data threshold, so we'll get a few kilobytes at most.
+ */
- if (filename && access(filename, R_OK) < 0) {
- log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
- "File %s is not readable: %m", filename);
- filename = mfree(filename);
+ r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
+ if (r == -ENOENT)
+ return log_error_errno(r, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
+ if (r < 0)
+ return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
}
- if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
- if (path) {
+ if (filename) {
+ if (access(filename, R_OK) < 0)
+ return log_error_errno(errno, "File \"%s\" is not readable: %m", filename);
+
+ if (path && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
*path = filename;
filename = NULL;
+
+ return 0;
}
+ }
- return 0;
- } else {
- _cleanup_close_ int fdt = -1;
- char *temp = NULL;
+ if (path) {
+ const char *vt;
- if (fd < 0) {
- temp = strdup("/var/tmp/coredump-XXXXXX");
- if (!temp)
- return log_oom();
+ /* Create a temporary file to write the uncompressed core to. */
- fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
- if (fdt < 0)
- return log_error_errno(fdt, "Failed to create temporary file: %m");
- log_debug("Created temporary file %s", temp);
+ r = var_tmp_dir(&vt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire temporary directory path: %m");
+
+ temp = strjoin(vt, "/coredump-XXXXXX", NULL);
+ if (!temp)
+ return log_oom();
- fd = fdt;
+ fdt = mkostemp_safe(temp);
+ if (fdt < 0)
+ return log_error_errno(fdt, "Failed to create temporary file: %m");
+ log_debug("Created temporary file %s", temp);
+
+ fd = fdt;
+ } else {
+ /* If neither path or file are specified, we will write to stdout. Let's now check
+ * if stdout is connected to a tty. We checked that the file exists, or that the
+ * core might be stored in the journal. In this second case, if we found the entry,
+ * in all likelyhood we will be able to access the COREDUMP= field. In either case,
+ * we stop before doing any "real" work, i.e. before starting decompression or
+ * reading from the file or creating temporary files.
+ */
+ if (!file) {
+ if (on_tty())
+ return log_error_errno(ENOTTY, "Refusing to dump core to tty"
+ " (use shell redirection or specify --output).");
+ file = stdout;
}
- r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
- if (r == 0) {
- ssize_t sz;
-
- assert(len >= 9);
- data += 9;
- len -= 9;
-
- sz = write(fdt, data, len);
- if (sz < 0) {
- r = log_error_errno(errno,
- "Failed to write temporary file: %m");
- goto error;
- }
- if (sz != (ssize_t) len) {
- log_error("Short write to temporary file.");
- r = -EIO;
- goto error;
- }
- } else if (filename) {
+ fd = fileno(file);
+ }
+
+ if (filename) {
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
- _cleanup_close_ int fdf;
-
- fdf = open(filename, O_RDONLY | O_CLOEXEC);
- if (fdf < 0) {
- r = log_error_errno(errno,
- "Failed to open %s: %m",
- filename);
- goto error;
- }
+ _cleanup_close_ int fdf;
- r = decompress_stream(filename, fdf, fd, -1);
- if (r < 0) {
- log_error_errno(r, "Failed to decompress %s: %m", filename);
- goto error;
- }
-#else
- log_error("Cannot decompress file. Compiled without compression support.");
- r = -EOPNOTSUPP;
+ fdf = open(filename, O_RDONLY | O_CLOEXEC);
+ if (fdf < 0) {
+ r = log_error_errno(errno, "Failed to open %s: %m", filename);
goto error;
-#endif
- } else {
- if (r == -ENOENT)
- log_error("Cannot retrieve coredump from journal or disk.");
- else
- log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
+ }
+
+ r = decompress_stream(filename, fdf, fd, -1);
+ if (r < 0) {
+ log_error_errno(r, "Failed to decompress %s: %m", filename);
goto error;
}
+#else
+ log_error("Cannot decompress file. Compiled without compression support.");
+ r = -EOPNOTSUPP;
+ goto error;
+#endif
+ } else {
+ ssize_t sz;
+
+ /* We want full data, nothing truncated. */
+ sd_journal_set_data_threshold(j, 0);
+
+ r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
+ if (r < 0)
+ return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
- if (temp) {
- *path = temp;
- *unlink_temp = true;
+ assert(len >= 9);
+ data += 9;
+ len -= 9;
+
+ sz = write(fd, data, len);
+ if (sz < 0) {
+ r = log_error_errno(errno, "Failed to write output: %m");
+ goto error;
+ }
+ if (sz != (ssize_t) len) {
+ log_error("Short write to output.");
+ r = -EIO;
+ goto error;
}
+ }
- return 0;
+ if (temp) {
+ *path = temp;
+ *unlink_temp = true;
+ }
+ return 0;
error:
- if (temp) {
- unlink(temp);
- log_debug("Removed temporary file %s", temp);
- }
- return r;
+ if (temp) {
+ unlink(temp);
+ log_debug("Removed temporary file %s", temp);
}
+ return r;
}
static int dump_core(sd_journal* j) {
@@ -697,17 +766,12 @@ static int dump_core(sd_journal* j) {
print_info(arg_output ? stdout : stderr, j, false);
- if (on_tty() && !arg_output) {
- log_error("Refusing to dump core to tty.");
- return -ENOTTY;
- }
-
- r = save_core(j, arg_output ? fileno(arg_output) : STDOUT_FILENO, NULL, NULL);
+ r = save_core(j, arg_output, NULL, NULL);
if (r < 0)
- return log_error_errno(r, "Coredump retrieval failed: %m");
+ return r;
r = sd_journal_previous(j);
- if (r >= 0)
+ if (r > 0)
log_warning("More than one entry matches, ignoring rest.");
return 0;
@@ -753,9 +817,9 @@ static int run_gdb(sd_journal *j) {
return -ENOENT;
}
- r = save_core(j, -1, &path, &unlink_path);
+ r = save_core(j, NULL, &path, &unlink_path);
if (r < 0)
- return log_error_errno(r, "Failed to retrieve core: %m");
+ return r;
pid = fork();
if (pid < 0) {
@@ -829,9 +893,6 @@ int main(int argc, char *argv[]) {
}
}
- /* We want full data, nothing truncated. */
- sd_journal_set_data_threshold(j, 0);
-
SET_FOREACH(match, matches, it) {
r = sd_journal_add_match(j, match, strlen(match));
if (r != 0) {