streq_ptr(context[CONTEXT_PID], "1");
}
-#define SUBMIT_COREDUMP_FIELDS 4
-
static int submit_coredump(
const char *context[_CONTEXT_MAX],
- struct iovec *iovec,
- size_t n_iovec_allocated,
- size_t n_iovec,
+ struct iovec_wrapper *iovw,
int input_fd) {
_cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
- _cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL;
+ _cleanup_free_ char *filename = NULL, *coredump_data = NULL;
_cleanup_free_ char *stacktrace = NULL;
+ char *core_message;
uint64_t coredump_size = UINT64_MAX;
bool truncated = false, journald_crash;
int r;
assert(context);
- assert(iovec);
- assert(n_iovec_allocated >= n_iovec + SUBMIT_COREDUMP_FIELDS);
+ assert(iovw);
assert(input_fd >= 0);
journald_crash = is_journald_crash(context);
if (r < 0)
return r;
if (r == 0) {
- const char *coredump_filename;
+ iovw_put_string_field(iovw, "COREDUMP_FILENAME=", filename);
- coredump_filename = strjoina("COREDUMP_FILENAME=", filename);
- iovec[n_iovec++] = IOVEC_MAKE_STRING(coredump_filename);
} 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);
#endif
log:
- core_message = strjoina("MESSAGE=Process ", context[CONTEXT_PID],
+ core_message = strjoina("Process ", context[CONTEXT_PID],
" (", context[CONTEXT_COMM], ") of user ",
context[CONTEXT_UID], " dumped core.",
journald_crash && filename ? "\nCoredump diverted to " : NULL,
journald_crash && filename ? filename : NULL);
- core_message = strjoin(core_message, stacktrace ? "\n\n" : NULL, stacktrace);
- if (!core_message)
- return log_oom();
+ core_message = strjoina(core_message, stacktrace ? "\n\n" : NULL, stacktrace);
if (journald_crash) {
/* We cannot log to the journal, so just print the message.
* The target was set previously to something safe. */
- log_dispatch(LOG_ERR, 0, core_message + strlen("MESSAGE="));
+ log_dispatch(LOG_ERR, 0, core_message);
return 0;
}
- iovec[n_iovec++] = IOVEC_MAKE_STRING(core_message);
+ iovw_put_string_field(iovw, "MESSAGE=", core_message);
if (truncated)
- iovec[n_iovec++] = IOVEC_MAKE_STRING("COREDUMP_TRUNCATED=1");
+ iovw_put_string_field(iovw, "COREDUMP_TRUNCATED=", "1");
/* Optionally store the entire coredump in the journal */
if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
/* 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++] = IOVEC_MAKE(coredump_data, sz);
- else
+ if (r >= 0) {
+ if (iovw_put(iovw, coredump_data, sz) >= 0)
+ TAKE_PTR(coredump_data);
+ } 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 %"PRIu64" is greater than %"PRIu64" (the configured maximum)",
coredump_size, arg_journal_size_max);
}
- assert(n_iovec <= n_iovec_allocated);
-
- r = sd_journal_sendv(iovec, n_iovec);
+ r = sd_journal_sendv(iovw->iovec, iovw->count);
if (r < 0)
return log_error_errno(r, "Failed to log coredump: %m");
static int process_socket(int fd) {
_cleanup_close_ int coredump_fd = -1;
- struct iovec *iovec = NULL;
- size_t n_iovec = 0, n_allocated = 0, i, k;
const char *context[_CONTEXT_MAX] = {};
+ struct iovec_wrapper iovw = {};
+ struct iovec iovec;
+ size_t k;
int r;
assert(fd >= 0);
ssize_t n;
ssize_t l;
- if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) {
- r = log_oom();
- goto finish;
- }
-
l = next_datagram_size_fd(fd);
if (l < 0) {
r = log_error_errno(l, "Failed to determine datagram size to read: %m");
goto finish;
}
- iovec[n_iovec].iov_len = l;
- iovec[n_iovec].iov_base = malloc(l + 1);
- if (!iovec[n_iovec].iov_base) {
+ iovec.iov_len = l;
+ iovec.iov_base = malloc(l + 1);
+ if (!iovec.iov_base) {
r = log_oom();
goto finish;
}
- mh.msg_iov = iovec + n_iovec;
+ mh.msg_iov = &iovec;
n = recvmsg(fd, &mh, MSG_CMSG_CLOEXEC);
if (n < 0) {
- free(iovec[n_iovec].iov_base);
+ free(iovec.iov_base);
r = log_error_errno(errno, "Failed to receive datagram: %m");
goto finish;
}
+ /* The final zero-length datagram carries the file descriptor and tells us
+ * that we're done. */
if (n == 0) {
struct cmsghdr *cmsg, *found = NULL;
- /* The final zero-length datagram carries the file descriptor and tells us that we're done. */
- free(iovec[n_iovec].iov_base);
+ free(iovec.iov_base);
CMSG_FOREACH(cmsg, &mh) {
if (cmsg->cmsg_level == SOL_SOCKET &&
}
/* Add trailing NUL byte, in case these are strings */
- ((char*) iovec[n_iovec].iov_base)[n] = 0;
- iovec[n_iovec].iov_len = (size_t) n;
+ ((char*) iovec.iov_base)[n] = 0;
+ iovec.iov_len = (size_t) n;
- cmsg_close_all(&mh);
- map_context_fields(iovec + n_iovec, context);
- n_iovec++;
- }
+ r = iovw_put(&iovw, iovec.iov_base, iovec.iov_len);
+ if (r < 0)
+ goto finish;
- if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) {
- r = log_oom();
- goto finish;
+ cmsg_close_all(&mh);
+ map_context_fields(&iovec, context);
}
/* Make sure we got all data we really need */
assert(context[CONTEXT_COMM]);
assert(coredump_fd >= 0);
- /* Small quirk: 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 operates in. Since we
- * are reconstructing the original kernel context, we chop this off again, here. */
+ /* Small quirk: 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 operates in. Since we are reconstructing
+ * the original kernel context, we chop this off again, here. */
k = strlen(context[CONTEXT_TIMESTAMP]);
if (k > 6)
context[CONTEXT_TIMESTAMP] = strndupa(context[CONTEXT_TIMESTAMP], k - 6);
- r = submit_coredump(context, iovec, n_allocated, n_iovec, coredump_fd);
+ r = submit_coredump(context, &iovw, coredump_fd);
finish:
- for (i = 0; i < n_iovec; i++)
- free(iovec[i].iov_base);
- free(iovec);
-
+ iovw_free_contents(&iovw, true);
return r;
}
-static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) {
+static int send_iovec(const struct iovec_wrapper *iovw, int input_fd) {
static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
size_t i;
int r;
- assert(iovec || n_iovec <= 0);
+ assert(iovw);
assert(input_fd >= 0);
fd = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
return log_error_errno(errno, "Failed to connect to coredump service: %m");
- for (i = 0; i < n_iovec; i++) {
+ for (i = 0; i < iovw->count; i++) {
struct msghdr mh = {
- .msg_iov = (struct iovec*) iovec + i,
+ .msg_iov = iovw->iovec + i,
.msg_iovlen = 1,
};
struct iovec copy[2];
* own array, consisting of two new iovecs, where the first is a
* (truncated) copy of what we want to send, and the second one
* contains the trailing dots. */
- copy[0] = iovec[i];
+ copy[0] = iovw->iovec[i];
copy[1] = IOVEC_MAKE(((char[]){'.', '.', '.'}), 3);
mh.msg_iov = copy;
return 0;
}
-static int gather_pid_metadata(char *context[_CONTEXT_MAX], struct iovec *iovec, size_t *n_iovec) {
+static int gather_pid_metadata(char *context[_CONTEXT_MAX], struct iovec_wrapper *iovw) {
- /* We need 27 empty slots in iovec!
- *
- * Note that if we fail on oom later on, we do not roll-back changes to the iovec
+ /* 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 n_iovec fields initialized.) */
uid_t owner_uid;
disable_coredumps();
}
- set_iovec_string_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]);
+ iovw_put_string_field(iovw, "COREDUMP_UNIT=", context[CONTEXT_UNIT]);
}
if (cg_pid_get_user_unit(pid, &t) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_USER_UNIT=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_USER_UNIT=", t);
/* The next few are mandatory */
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_PID=", context[CONTEXT_PID]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_PID=", context[CONTEXT_PID]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_UID=", context[CONTEXT_UID]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_UID=", context[CONTEXT_UID]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_GID=", context[CONTEXT_GID]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_GID=", context[CONTEXT_GID]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_HOSTNAME=", context[CONTEXT_HOSTNAME]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_HOSTNAME=", context[CONTEXT_HOSTNAME]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_COMM=", context[CONTEXT_COMM]);
+ if (r < 0)
+ return r;
- if (context[CONTEXT_EXE] &&
- !set_iovec_string_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE]))
- return log_oom();
+ if (context[CONTEXT_EXE]) {
+ r = iovw_put_string_field(iovw, "COREDUMP_EXE=", context[CONTEXT_EXE]);
+ if (r < 0)
+ return r;
+ }
+ /* The next are optional */
if (sd_pid_get_session(pid, &t) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_SESSION=", t);
+ (void) iovw_put_string_field_free(iovw, "COREDUMP_SESSION=", t);
if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
- r = asprintf(&t, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
+ r = asprintf(&t, UID_FMT, owner_uid);
if (r > 0)
- iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(t);
+ (void) iovw_put_string_field_free(iovw, "COREDUMP_OWNER_UID=", t);
}
if (sd_pid_get_slice(pid, &t) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_SLICE=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_SLICE=", t);
if (get_process_cmdline(pid, SIZE_MAX, 0, &t) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_CMDLINE=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_CMDLINE=", t);
if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_CGROUP=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_CGROUP=", t);
if (compose_open_fds(pid, &t) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_OPEN_FDS=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_OPEN_FDS=", t);
p = procfs_file_alloca(pid, "status");
if (read_full_file(p, &t, NULL) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_PROC_STATUS=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_PROC_STATUS=", t);
p = procfs_file_alloca(pid, "maps");
if (read_full_file(p, &t, NULL) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_PROC_MAPS=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_PROC_MAPS=", t);
p = procfs_file_alloca(pid, "limits");
if (read_full_file(p, &t, NULL) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_PROC_LIMITS=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_PROC_LIMITS=", t);
p = procfs_file_alloca(pid, "cgroup");
if (read_full_file(p, &t, NULL) >=0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_PROC_CGROUP=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_PROC_CGROUP=", t);
p = procfs_file_alloca(pid, "mountinfo");
if (read_full_file(p, &t, NULL) >=0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_PROC_MOUNTINFO=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_PROC_MOUNTINFO=", t);
if (get_process_cwd(pid, &t) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_CWD=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_CWD=", t);
if (get_process_root(pid, &t) >= 0) {
bool proc_self_root_is_slash;
proc_self_root_is_slash = strcmp(t, "/") == 0;
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_ROOT=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_ROOT=", t);
/* If the process' root is "/", then there is a chance it has
* mounted own root and hence being containerized. */
if (proc_self_root_is_slash && get_process_container_parent_cmdline(pid, &t) > 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_CONTAINER_CMDLINE=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_CONTAINER_CMDLINE=", t);
}
if (get_process_environ(pid, &t) >= 0)
- set_iovec_string_field_free(iovec, n_iovec, "COREDUMP_ENVIRON=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_ENVIRON=", t);
- t = strjoin("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000");
- if (t)
- iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(t);
+ t = strjoina(context[CONTEXT_TIMESTAMP], "000000");
+ (void) iovw_put_string_field(iovw, "COREDUMP_TIMESTAMP=", t);
if (safe_atoi(context[CONTEXT_SIGNAL], &signo) >= 0 && SIGNAL_VALID(signo))
- set_iovec_string_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo));
+ iovw_put_string_field(iovw, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo));
return 0; /* we successfully acquired all metadata */
}
static int process_kernel(int argc, char* argv[]) {
char* context[_CONTEXT_MAX] = {};
- struct iovec iovec[29 + SUBMIT_COREDUMP_FIELDS];
- size_t i, n_iovec, n_to_free = 0;
+ struct iovec_wrapper *iovw;
int r;
log_debug("Processing coredump received from the kernel...");
context[CONTEXT_RLIMIT] = argv[1 + CONTEXT_RLIMIT];
context[CONTEXT_HOSTNAME] = argv[1 + CONTEXT_HOSTNAME];
- r = gather_pid_metadata(context, iovec, &n_to_free);
+ iovw = iovw_new();
+ if (!iovw)
+ return log_oom();
+
+ r = gather_pid_metadata(context, iovw);
if (r < 0)
goto finish;
- n_iovec = n_to_free;
-
- iovec[n_iovec++] = IOVEC_MAKE_STRING("MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
- iovec[n_iovec++] = IOVEC_MAKE_STRING("PRIORITY=" STRINGIFY(LOG_CRIT));
-
- assert(n_iovec <= ELEMENTSOF(iovec));
+ iovw_put_string_field(iovw, "MESSAGE_ID=", SD_MESSAGE_COREDUMP_STR);
+ iovw_put_string_field(iovw, "PRIORITY=", STRINGIFY(LOG_CRIT));
if (is_journald_crash((const char**) context) || is_pid1_crash((const char**) context))
- r = submit_coredump((const char**) context,
- iovec, ELEMENTSOF(iovec), n_iovec,
- STDIN_FILENO);
+ r = submit_coredump((const char**) context, iovw, STDIN_FILENO);
else
- r = send_iovec(iovec, n_iovec, STDIN_FILENO);
+ r = send_iovec(iovw, STDIN_FILENO);
finish:
- for (i = 0; i < n_to_free; i++)
- free(iovec[i].iov_base);
+ iovw = iovw_free_free(iovw);
/* Those fields are allocated by gather_pid_metadata */
free(context[CONTEXT_COMM]);
static int process_backtrace(int argc, char *argv[]) {
char *context[_CONTEXT_MAX] = {};
- _cleanup_free_ char *message = NULL;
- _cleanup_free_ struct iovec *iovec = NULL;
- size_t n_iovec, n_allocated, n_to_free = 0, i;
+ struct iovec_wrapper *iovw;
+ char *message;
+ size_t i;
int r;
_cleanup_(journal_importer_cleanup) JournalImporter importer = JOURNAL_IMPORTER_INIT(STDIN_FILENO);
context[CONTEXT_RLIMIT] = argv[2 + CONTEXT_RLIMIT];
context[CONTEXT_HOSTNAME] = argv[2 + CONTEXT_HOSTNAME];
- n_allocated = 34 + COREDUMP_STORAGE_EXTERNAL;
- /* 26 metadata, 2 static, +unknown input, 4 storage, rounded up */
- iovec = new(struct iovec, n_allocated);
- if (!iovec)
+ iovw = iovw_new();
+ if (!iovw)
return log_oom();
- r = gather_pid_metadata(context, iovec, &n_to_free);
+ r = gather_pid_metadata(context, iovw);
if (r < 0)
goto finish;
- n_iovec = n_to_free;
+ iovw_put_string_field(iovw, "MESSAGE_ID=", SD_MESSAGE_BACKTRACE_STR);
+ iovw_put_string_field(iovw, "PRIORITY=", STRINGIFY(LOG_CRIT));
for (;;) {
r = journal_importer_process_data(&importer);
break;
}
- if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + importer.iovw.count + 2))
- return log_oom();
-
if (journal_importer_eof(&importer)) {
log_warning("Did not receive a full journal entry on stdin, ignoring message sent by reporter");
- message = strjoin("MESSAGE=Process ", context[CONTEXT_PID],
+ message = strjoina("Process ", context[CONTEXT_PID],
" (", context[CONTEXT_COMM], ")"
" of user ", context[CONTEXT_UID],
" failed with ", context[CONTEXT_SIGNAL]);
- if (!message) {
- r = log_oom();
- goto finish;
- }
- iovec[n_iovec++] = IOVEC_MAKE_STRING(message);
+
+ r = iovw_put_string_field(iovw, "MESSAGE=", message);
+ if (r < 0)
+ return r;
} else {
- for (i = 0; i < importer.iovw.count; i++)
- iovec[n_iovec++] = importer.iovw.iovec[i];
- }
+ /* 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. */
- iovec[n_iovec++] = IOVEC_MAKE_STRING("MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR);
- iovec[n_iovec++] = IOVEC_MAKE_STRING("PRIORITY=" STRINGIFY(LOG_CRIT));
+ for (i = 0; i < importer.iovw.count; i++) {
+ struct iovec *iovec = importer.iovw.iovec + i;
- assert(n_iovec <= n_allocated);
+ iovw_put(iovw, iovec->iov_base, iovec->iov_len);
+ }
+ }
- r = sd_journal_sendv(iovec, n_iovec);
+ r = sd_journal_sendv(iovw->iovec, iovw->count);
if (r < 0)
log_error_errno(r, "Failed to log backtrace: %m");
finish:
- for (i = 0; i < n_to_free; i++)
- free(iovec[i].iov_base);
+ iovw->count -= importer.iovw.count;
+ iovw = iovw_free_free(iovw);
/* Those fields are allocated by gather_pid_metadata */
free(context[CONTEXT_COMM]);