#include "macro.h"
#include "main-func.h"
#include "memory-util.h"
+#include "memstream-util.h"
#include "mkdir-label.h"
+#include "namespace-util.h"
#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "uid-alloc-range.h"
#include "user-util.h"
-/* The maximum size up to which we process coredumps. We use 1G on 32bit systems, and 32G on 64bit systems */
+/* The maximum size up to which we process coredumps. We use 1G on 32-bit systems, and 32G on 64-bit systems */
#if __SIZEOF_POINTER__ == 4
#define PROCESS_SIZE_MAX ((uint64_t) (1LLU*1024LLU*1024LLU*1024LLU))
#elif __SIZEOF_POINTER__ == 8
META_ARGV_UID, /* %u: as seen in the initial user namespace */
META_ARGV_GID, /* %g: as seen in the initial user namespace */
META_ARGV_SIGNAL, /* %s: number of signal causing dump */
- META_ARGV_TIMESTAMP, /* %t: time of dump, expressed as seconds since the Epoch (we expand this to µs granularity) */
+ META_ARGV_TIMESTAMP, /* %t: time of dump, expressed as seconds since the Epoch (we expand this to μs granularity) */
META_ARGV_RLIMIT, /* %c: core file size soft resource limit */
META_ARGV_HOSTNAME, /* %h: hostname */
_META_ARGV_MAX,
const char *meta[_META_MAX];
size_t meta_size[_META_MAX];
pid_t pid;
+ uid_t uid;
+ gid_t gid;
bool is_pid1;
bool is_journald;
} Context;
{}
};
- return config_parse_many_nulstr(
- PKGSYSCONFDIR "/coredump.conf",
- CONF_PATHS_NULSTR("systemd/coredump.conf.d"),
+ int r;
+
+ r = config_parse_config_file(
+ "coredump.conf",
"Coredump\0",
- config_item_table_lookup, items,
+ config_item_table_lookup,
+ items,
CONFIG_PARSE_WARN,
- NULL,
- NULL);
+ /* userdata= */ NULL);
+ if (r < 0)
+ return r;
+
+ /* Let's make sure we fix up the maximum size we send to the journal here on the client side, for
+ * efficiency reasons. journald wouldn't accept anything larger anyway. */
+ if (arg_journal_size_max > JOURNAL_SIZE_MAX) {
+ log_warning("JournalSizeMax= set to larger value (%s) than journald would accept (%s), lowering automatically.",
+ FORMAT_BYTES(arg_journal_size_max), FORMAT_BYTES(JOURNAL_SIZE_MAX));
+ arg_journal_size_max = JOURNAL_SIZE_MAX;
+ }
+
+ return 0;
}
static uint64_t storage_size_max(void) {
assert(fd >= 0);
- /* Attach some metadata to coredumps via extended
- * attributes. Just because we can. */
+ /* Attach some metadata to coredumps via extended attributes. Just because we can. */
for (unsigned i = 0; i < _META_MAX; i++) {
int k;
if (isempty(context->meta[i]) || !xattrs[i])
continue;
- k = fsetxattr(fd, xattrs[i], context->meta[i], strlen(context->meta[i]), XATTR_CREATE);
- if (k < 0 && r == 0)
- r = -errno;
+ k = RET_NERRNO(fsetxattr(fd, xattrs[i], context->meta[i], strlen(context->meta[i]), XATTR_CREATE));
+ RET_GATHER(r, k);
}
return r;
#define filename_escape(s) xescape((s), "./ ")
static const char *coredump_tmpfile_name(const char *s) {
- return s ? s : "(unnamed temporary file)";
+ return s ?: "(unnamed temporary file)";
}
static int fix_permissions(
const char *filename,
const char *target,
const Context *context,
- uid_t uid,
bool allow_user) {
int r;
/* Ignore errors on these */
(void) fchmod(fd, 0640);
- (void) fix_acl(fd, uid, allow_user);
+ (void) fix_acl(fd, context->uid, allow_user);
(void) fix_xattr(fd, context);
- r = fsync_full(fd);
- if (r < 0)
- return log_error_errno(r, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename));
-
- r = link_tmpfile(fd, filename, target, /* replace= */ false);
+ r = link_tmpfile(fd, filename, target, LINK_TMPFILE_SYNC);
if (r < 0)
return log_error_errno(r, "Failed to move coredump %s into place: %m", target);
return 0;
}
-static int parse_auxv64(
- const uint64_t *auxv,
- size_t size_bytes,
- int *at_secure,
- uid_t *uid,
- uid_t *euid,
- gid_t *gid,
- gid_t *egid) {
-
- assert(auxv || size_bytes == 0);
-
- if (size_bytes % (2 * sizeof(uint64_t)) != 0)
- return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Incomplete auxv structure (%zu bytes).", size_bytes);
-
- size_t words = size_bytes / sizeof(uint64_t);
-
- /* Note that we set output variables even on error. */
-
- for (size_t i = 0; i + 1 < words; i += 2)
- switch (auxv[i]) {
- case AT_SECURE:
- *at_secure = auxv[i + 1] != 0;
- break;
- case AT_UID:
- *uid = auxv[i + 1];
- break;
- case AT_EUID:
- *euid = auxv[i + 1];
- break;
- case AT_GID:
- *gid = auxv[i + 1];
- break;
- case AT_EGID:
- *egid = auxv[i + 1];
- break;
- case AT_NULL:
- if (auxv[i + 1] != 0)
- goto error;
- return 0;
- }
- error:
- return log_warning_errno(SYNTHETIC_ERRNO(ENODATA),
- "AT_NULL terminator not found, cannot parse auxv structure.");
-}
-
-static int parse_auxv32(
- const uint32_t *auxv,
- size_t size_bytes,
- int *at_secure,
- uid_t *uid,
- uid_t *euid,
- gid_t *gid,
- gid_t *egid) {
-
- assert(auxv || size_bytes == 0);
-
- size_t words = size_bytes / sizeof(uint32_t);
-
- if (size_bytes % (2 * sizeof(uint32_t)) != 0)
- return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Incomplete auxv structure (%zu bytes).", size_bytes);
-
- /* Note that we set output variables even on error. */
-
- for (size_t i = 0; i + 1 < words; i += 2)
- switch (auxv[i]) {
- case AT_SECURE:
- *at_secure = auxv[i + 1] != 0;
- break;
- case AT_UID:
- *uid = auxv[i + 1];
- break;
- case AT_EUID:
- *euid = auxv[i + 1];
- break;
- case AT_GID:
- *gid = auxv[i + 1];
- break;
- case AT_EGID:
- *egid = auxv[i + 1];
- break;
- case AT_NULL:
- if (auxv[i + 1] != 0)
- goto error;
- return 0;
- }
- error:
- return log_warning_errno(SYNTHETIC_ERRNO(ENODATA),
- "AT_NULL terminator not found, cannot parse auxv structure.");
-}
-
static int grant_user_access(int core_fd, const Context *context) {
int at_secure = -1;
uid_t uid = UID_INVALID, euid = UID_INVALID;
return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN),
"Core file has non-native endianness, not adjusting permissions.");
- if (elf[EI_CLASS] == ELFCLASS64)
- r = parse_auxv64((const uint64_t*) context->meta[META_PROC_AUXV],
- context->meta_size[META_PROC_AUXV],
- &at_secure, &uid, &euid, &gid, &egid);
- else
- r = parse_auxv32((const uint32_t*) context->meta[META_PROC_AUXV],
- context->meta_size[META_PROC_AUXV],
- &at_secure, &uid, &euid, &gid, &egid);
+ r = parse_auxv(LOG_WARNING,
+ /* elf_class= */ elf[EI_CLASS],
+ context->meta[META_PROC_AUXV],
+ context->meta_size[META_PROC_AUXV],
+ &at_secure, &uid, &euid, &gid, &egid);
if (r < 0)
return r;
uint64_t rlimit, process_limit, max_size;
bool truncated, storage_on_tmpfs;
struct stat st;
- uid_t uid;
int r;
assert(context);
assert(ret_compressed_size);
assert(ret_truncated);
- r = parse_uid(context->meta[META_ARGV_UID], &uid);
- if (r < 0)
- return log_error_errno(r, "Failed to parse UID: %m");
-
r = safe_atou64(context->meta[META_ARGV_RLIMIT], &rlimit);
if (r < 0)
return log_error_errno(r, "Failed to parse resource limit '%s': %m",
_cleanup_close_ int fd_compressed = -EBADF;
uint64_t uncompressed_size = 0;
- if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+ if (lseek(fd, 0, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek on coredump %s: %m", fn);
fn_compressed = strjoin(fn, default_compression_extension());
uncompressed_size += partial_uncompressed_size;
}
- r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid, allow_user);
+ r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, allow_user);
if (r < 0)
return r;
"SIZE_LIMIT=%"PRIu64, max_size,
"MESSAGE_ID=" SD_MESSAGE_TRUNCATED_CORE_STR);
- r = fix_permissions(fd, tmp, fn, context, uid, allow_user);
+ r = fix_permissions(fd, tmp, fn, context, allow_user);
if (r < 0)
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)
+ if (lseek(fd, 0, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek on coredump %s: %m", fn);
*ret_filename = TAKE_PTR(fn);
assert(ret);
assert(ret_size);
- if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+ if (lseek(fd, 0, SEEK_SET) < 0)
return log_warning_errno(errno, "Failed to seek: %m");
field = malloc(9 + size);
- if (!field) {
- log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
- return -ENOMEM;
- }
+ if (!field)
+ return log_warning_errno(SYNTHETIC_ERRNO(ENOMEM),
+ "Failed to allocate memory for coredump, coredump will not be stored.");
memcpy(field, "COREDUMP=", 9);
- n = read(fd, field + 9, size);
+ /* NB: simple read() would fail for overly large coredumps, since read() on Linux can only deal with
+ * 0x7ffff000 bytes max. Hence call things in a loop. */
+ n = loop_read(fd, field + 9, size, /* do_poll= */ false);
if (n < 0)
return log_error_errno((int) n, "Failed to read core data: %m");
if ((size_t) n < size)
* flags: 0100002
* EOF
*/
-static int compose_open_fds(pid_t pid, char **open_fds) {
+static int compose_open_fds(pid_t pid, char **ret) {
+ _cleanup_(memstream_done) MemStream m = {};
_cleanup_closedir_ DIR *proc_fd_dir = NULL;
_cleanup_close_ int proc_fdinfo_fd = -EBADF;
- _cleanup_free_ char *buffer = NULL;
- _cleanup_fclose_ FILE *stream = NULL;
const char *fddelim = "", *path;
- size_t size = 0;
+ FILE *stream;
int r;
assert(pid >= 0);
- assert(open_fds != NULL);
+ assert(ret);
path = procfs_file_alloca(pid, "fd");
proc_fd_dir = opendir(path);
if (proc_fdinfo_fd < 0)
return -errno;
- stream = open_memstream_unlocked(&buffer, &size);
+ stream = memstream_init(&m);
if (!stream)
return -ENOMEM;
}
}
- errno = 0;
- stream = safe_fclose(stream);
-
- if (errno > 0)
- return -errno;
-
- *open_fds = TAKE_PTR(buffer);
-
- 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 = -EBADF;
-
- 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 *ret) {
- ino_t proc_mntns;
- int r;
-
- r = get_process_ns(pid, "mnt", &proc_mntns);
- if (r < 0)
- return r;
-
- for (;;) {
- ino_t parent_mntns;
- pid_t ppid;
-
- r = get_process_ppid(pid, &ppid);
- if (r == -EADDRNOTAVAIL) /* Reached the top (i.e. typically PID 1, but could also be a process
- * whose parent is not in our pidns) */
- return -ENOENT;
- if (r < 0)
- return r;
-
- r = get_process_ns(ppid, "mnt", &parent_mntns);
- if (r < 0)
- return r;
-
- if (proc_mntns != parent_mntns) {
- *ret = ppid;
- return 0;
- }
-
- pid = ppid;
- }
+ return memstream_finalize(&m, ret, NULL);
}
/* Returns 1 if the parent was found.
return 0;
}
- r = get_mount_namespace_leader(pid, &container_pid);
+ r = namespace_get_leader(pid, NAMESPACE_MOUNT, &container_pid);
if (r < 0)
return r;
- r = get_process_cmdline(container_pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, cmdline);
+ r = pid_get_cmdline(container_pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, cmdline);
if (r < 0)
return r;
}
static int change_uid_gid(const Context *context) {
- uid_t uid;
- gid_t gid;
+ uid_t uid = context->uid;
+ gid_t gid = context->gid;
int r;
- r = parse_uid(context->meta[META_ARGV_UID], &uid);
- if (r < 0)
- return r;
-
if (uid_is_system(uid)) {
const char *user = "systemd-coredump";
log_warning_errno(r, "Cannot resolve %s user. Proceeding to dump core as root: %m", user);
uid = gid = 0;
}
- } else {
- r = parse_gid(context->meta[META_ARGV_GID], &gid);
- if (r < 0)
- return r;
}
return drop_privileges(uid, gid, 0);
if (r < 0)
return log_error_errno(r, "Failed to parse PID \"%s\": %m", context->meta[META_ARGV_PID]);
+ r = parse_uid(context->meta[META_ARGV_UID], &context->uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID \"%s\": %m", context->meta[META_ARGV_UID]);
+
+ r = parse_gid(context->meta[META_ARGV_GID], &context->gid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse GID \"%s\": %m", context->meta[META_ARGV_GID]);
+
unit = context->meta[META_UNIT];
context->is_pid1 = streq(context->meta[META_ARGV_PID], "1") || streq_ptr(unit, SPECIAL_INIT_SCOPE);
context->is_journald = streq_ptr(unit, SPECIAL_JOURNALD_SERVICE);
}
assert(input_fd < 0);
- input_fd = *(int*) CMSG_DATA(found);
+ input_fd = *CMSG_TYPED_DATA(found, int);
break;
} else
cmsg_close_all(&mh);
int r, signo;
char *t;
+ assert(iovw);
+ assert(context);
+
/* We gather all metadata that were passed via argv[] into an array of iovecs that
* we'll forward to the socket unit */
case META_ARGV_TIMESTAMP:
/* The journal fields contain the timestamp padded with six
* zeroes, so that the kernel-supplied 1s granularity timestamps
- * becomes 1µs granularity, i.e. the granularity systemd usually
+ * becomes 1μs granularity, i.e. the granularity systemd usually
* operates in. */
t = free_timestamp = strjoin(argv[i], "000000");
if (!t)
return save_context(context, iovw);
}
-static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) {
+static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context *context) {
uid_t owner_uid;
pid_t pid;
char *t;
const char *p;
int r;
+ assert(iovw);
+ assert(context);
+
/* Note that if we fail on oom later on, we do not roll-back changes to the iovec
* structure. (It remains valid, with the first iovec fields initialized.) */
pid = context->pid;
/* The following is mandatory */
- r = get_process_comm(pid, &t);
+ r = pid_get_comm(pid, &t);
if (r < 0)
return log_error_errno(r, "Failed to get COMM: %m");
if (sd_pid_get_slice(pid, &t) >= 0)
(void) iovw_put_string_field_free(iovw, "COREDUMP_SLICE=", t);
- if (get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &t) >= 0)
+ if (pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &t) >= 0)
(void) iovw_put_string_field_free(iovw, "COREDUMP_CMDLINE=", t);
if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0)
return save_context(context, iovw);
}
+static int send_ucred(int transport_fd, struct ucred *ucred) {
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+
+ assert(transport_fd >= 0);
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ *cmsg = (struct cmsghdr) {
+ .cmsg_level = SOL_SOCKET,
+ .cmsg_type = SCM_CREDENTIALS,
+ .cmsg_len = CMSG_LEN(sizeof(struct ucred)),
+ };
+ memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
+
+ return RET_NERRNO(sendmsg(transport_fd, &mh, MSG_NOSIGNAL));
+}
+
+static int receive_ucred(int transport_fd, struct ucred *ret_ucred) {
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg = NULL;
+ struct ucred *ucred = NULL;
+ ssize_t n;
+
+ assert(ret_ucred);
+
+ n = recvmsg_safe(transport_fd, &mh, 0);
+ if (n < 0)
+ return n;
+
+ CMSG_FOREACH(cmsg, &mh)
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+
+ assert(!ucred);
+ ucred = CMSG_TYPED_DATA(cmsg, struct ucred);
+ }
+
+ if (!ucred)
+ return -EIO;
+
+ *ret_ucred = *ucred;
+
+ return 0;
+}
+
+static int can_forward_coredump(pid_t pid) {
+ _cleanup_free_ char *cgroup = NULL, *path = NULL, *unit = NULL;
+ int r;
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
+ if (r < 0)
+ return r;
+
+ r = path_extract_directory(cgroup, &path);
+ if (r < 0)
+ return r;
+
+ r = cg_path_get_unit_path(path, &unit);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r == -ENXIO)
+ /* No valid units in this path. */
+ return false;
+ if (r < 0)
+ return r;
+
+ /* We require that this process belongs to a delegated cgroup
+ * (i.e. Delegate=yes), with CoredumpReceive=yes also. */
+ r = cg_is_delegated(unit);
+ if (r <= 0)
+ return r;
+
+ return cg_has_coredump_receive(unit);
+}
+
+static int forward_coredump_to_container(Context *context) {
+ _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, netnsfd = -EBADF, usernsfd = -EBADF, rootfd = -EBADF;
+ _cleanup_close_pair_ int pair[2] = PIPE_EBADF;
+ pid_t pid, child;
+ struct ucred ucred = {
+ .pid = context->pid,
+ .uid = context->uid,
+ .gid = context->gid,
+ };
+ int r;
+
+ r = namespace_get_leader(context->pid, NAMESPACE_PID, &pid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get namespace leader: %m");
+
+ r = can_forward_coredump(pid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to check if coredump can be forwarded: %m");
+ if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Coredump will not be forwarded because no target cgroup was found.");
+
+ r = RET_NERRNO(socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pair));
+ if (r < 0)
+ return log_debug_errno(r, "Failed to create socket pair: %m");
+
+ r = setsockopt_int(pair[1], SOL_SOCKET, SO_PASSCRED, true);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to set SO_PASSCRED: %m");
+
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, &netnsfd, &usernsfd, &rootfd);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to join namespaces of PID " PID_FMT ": %m", pid);
+
+ r = namespace_fork("(sd-coredumpns)", "(sd-coredump)", NULL, 0,
+ FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ pidnsfd, mntnsfd, netnsfd, usernsfd, rootfd, &child);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to fork into namespaces of PID " PID_FMT ": %m", pid);
+ if (r == 0) {
+ _cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = NULL;
+ Context child_context = {};
+
+ pair[0] = safe_close(pair[0]);
+
+ if (laccess("/run/systemd/coredump", W_OK) < 0) {
+ log_debug_errno(errno, "Cannot find coredump socket, exiting: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = receive_ucred(pair[1], &ucred);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to receive ucred and fd: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ iovw = iovw_new();
+ if (!iovw) {
+ log_oom();
+ _exit(EXIT_FAILURE);
+ }
+
+ (void) iovw_put_string_field(iovw, "MESSAGE_ID=", SD_MESSAGE_COREDUMP_STR);
+ (void) iovw_put_string_field(iovw, "PRIORITY=", STRINGIFY(LOG_CRIT));
+ (void) iovw_put_string_field(iovw, "COREDUMP_FORWARDED=", "1");
+
+ for (int i = 0; i < _META_ARGV_MAX; i++) {
+ int signo;
+ char buf[DECIMAL_STR_MAX(pid_t)];
+ const char *t = context->meta[i];
+
+ switch(i) {
+
+ case META_ARGV_PID:
+ xsprintf(buf, PID_FMT, ucred.pid);
+ t = buf;
+
+ break;
+
+ case META_ARGV_UID:
+ xsprintf(buf, UID_FMT, ucred.uid);
+ t = buf;
+ break;
+
+ case META_ARGV_GID:
+ xsprintf(buf, GID_FMT, ucred.gid);
+ t = buf;
+ break;
+
+ case META_ARGV_SIGNAL:
+ if (safe_atoi(t, &signo) >= 0 && SIGNAL_VALID(signo))
+ (void) iovw_put_string_field(iovw,
+ "COREDUMP_SIGNAL_NAME=SIG",
+ signal_to_string(signo));
+ break;
+
+ default:
+ break;
+ }
+
+ r = iovw_put_string_field(iovw, meta_field_names[i], t);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to construct iovec: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ r = save_context(&child_context, iovw);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to save context: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = gather_pid_metadata_from_procfs(iovw, &child_context);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to gather metadata from procfs: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = send_iovec(iovw, STDIN_FILENO);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to send iovec to coredump socket: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+
+ /* We need to translate the PID, UID, and GID of the crashing process
+ * to the container's namespaces. Do this by sending an SCM_CREDENTIALS
+ * message on a socket pair, and read the result when we join the
+ * container. The kernel will perform the translation for us. */
+ r = send_ucred(pair[0], &ucred);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to send metadata to container: %m");
+
+ r = wait_for_terminate_and_check("(sd-coredumpns)", child, 0);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to wait for child to terminate: %m");
+ if (r != EXIT_SUCCESS)
+ return log_debug_errno(SYNTHETIC_ERRNO(EPROTO), "Failed to process coredump in container: %m");
+
+ return 0;
+}
+
static int process_kernel(int argc, char* argv[]) {
+ _cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = NULL;
Context context = {};
- struct iovec_wrapper *iovw;
int r;
/* When we're invoked by the kernel, stdout/stderr are closed which is dangerous because the fds
if (!iovw)
return log_oom();
- (void) iovw_put_string_field(iovw, "MESSAGE_ID=", SD_MESSAGE_COREDUMP_STR);
- (void) iovw_put_string_field(iovw, "PRIORITY=", STRINGIFY(LOG_CRIT));
-
/* Collect all process metadata passed by the kernel through argv[] */
r = gather_pid_metadata_from_argv(iovw, &context, argc - 1, argv + 1);
if (r < 0)
- goto finish;
+ return r;
/* Collect the rest of the process metadata retrieved from the runtime */
- r = gather_pid_metadata(iovw, &context);
+ r = gather_pid_metadata_from_procfs(iovw, &context);
if (r < 0)
- goto finish;
+ return r;
if (!context.is_journald)
/* OK, now we know it's not the journal, hence we can make use of it now. */
log_set_target_and_open(LOG_TARGET_JOURNAL_OR_KMSG);
+ r = in_same_namespace(getpid_cached(), context.pid, NAMESPACE_PID);
+ if (r < 0)
+ log_debug_errno(r, "Failed to check pidns of crashing process, ignoring: %m");
+ if (r == 0) {
+ /* If this fails, fallback to the old behavior so that
+ * there is still some record of the crash. */
+ r = forward_coredump_to_container(&context);
+ if (r >= 0)
+ return 0;
+ }
+
/* If this is PID 1 disable coredump collection, we'll unlikely be able to process
* it later on.
*
disable_coredumps();
}
+ (void) iovw_put_string_field(iovw, "MESSAGE_ID=", SD_MESSAGE_COREDUMP_STR);
+ (void) iovw_put_string_field(iovw, "PRIORITY=", STRINGIFY(LOG_CRIT));
+
if (context.is_journald || context.is_pid1)
- r = submit_coredump(&context, iovw, STDIN_FILENO);
- else
- r = send_iovec(iovw, STDIN_FILENO);
+ return submit_coredump(&context, iovw, STDIN_FILENO);
- finish:
- iovw = iovw_free_free(iovw);
- return r;
+ return send_iovec(iovw, STDIN_FILENO);
}
static int process_backtrace(int argc, char *argv[]) {
+ _cleanup_(journal_importer_cleanup) JournalImporter importer = JOURNAL_IMPORTER_INIT(STDIN_FILENO);
+ _cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = NULL;
Context context = {};
- struct iovec_wrapper *iovw;
char *message;
int r;
- _cleanup_(journal_importer_cleanup) JournalImporter importer = JOURNAL_IMPORTER_INIT(STDIN_FILENO);
log_debug("Processing backtrace on stdin...");
* '--backtrace' option */
r = gather_pid_metadata_from_argv(iovw, &context, argc - 2, argv + 2);
if (r < 0)
- goto finish;
+ return r;
/* Collect the rest of the process metadata retrieved from the runtime */
- r = gather_pid_metadata(iovw, &context);
+ r = gather_pid_metadata_from_procfs(iovw, &context);
if (r < 0)
- goto finish;
+ return r;
for (;;) {
r = journal_importer_process_data(&importer);
- if (r < 0) {
- log_error_errno(r, "Failed to parse journal entry on stdin: %m");
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse journal entry on stdin: %m");
if (r == 1 || /* complete entry */
journal_importer_eof(&importer)) /* end of data */
break;
if (r < 0)
return r;
} else {
- /* The imported iovecs are not supposed to be freed by us so let's store
- * them at the end of the array so we can skip them while freeing the
- * rest. */
- for (size_t i = 0; i < importer.iovw.count; i++) {
- struct iovec *iovec = importer.iovw.iovec + i;
-
- iovw_put(iovw, iovec->iov_base, iovec->iov_len);
- }
+ /* The imported iovecs are not supposed to be freed by us so let's copy and merge them at the
+ * end of the array. */
+ r = iovw_append(iovw, &importer.iovw);
+ if (r < 0)
+ return r;
}
r = sd_journal_sendv(iovw->iovec, iovw->count);
if (r < 0)
- log_error_errno(r, "Failed to log backtrace: %m");
+ return log_error_errno(r, "Failed to log backtrace: %m");
- finish:
- iovw->count -= importer.iovw.count;
- iovw = iovw_free_free(iovw);
- return r;
+ return 0;
}
static int run(int argc, char *argv[]) {