diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index e991fe4af00..6308cda6101 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -700,25 +700,21 @@ static bool is_pid1_crash(const char *context[_CONTEXT_MAX]) { 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); @@ -740,10 +736,8 @@ static int submit_coredump( 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); @@ -771,27 +765,25 @@ static int submit_coredump( #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) { @@ -801,18 +793,17 @@ log: /* 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"); @@ -857,9 +848,10 @@ static void map_context_fields(const struct iovec *iovec, const char* context[]) 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); @@ -881,38 +873,34 @@ static int process_socket(int fd) { 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 && @@ -935,17 +923,15 @@ static int process_socket(int fd) { } /* 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; + + r = iovw_put(&iovw, iovec.iov_base, iovec.iov_len); + if (r < 0) + goto finish; cmsg_close_all(&mh); - map_context_fields(iovec + n_iovec, context); - n_iovec++; - } - - if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) { - r = log_oom(); - goto finish; + map_context_fields(&iovec, context); } /* Make sure we got all data we really need */ @@ -959,24 +945,22 @@ static int process_socket(int fd) { 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, @@ -986,7 +970,7 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) 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); @@ -996,9 +980,9 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) 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]; @@ -1017,7 +1001,7 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) * 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; @@ -1039,11 +1023,9 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) 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; @@ -1077,104 +1059,113 @@ static int gather_pid_metadata(char *context[_CONTEXT_MAX], struct iovec *iovec, 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 */ } @@ -1182,8 +1173,7 @@ static int gather_pid_metadata(char *context[_CONTEXT_MAX], struct iovec *iovec, 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..."); @@ -1201,27 +1191,24 @@ static int process_kernel(int argc, char* argv[]) { 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]); @@ -1233,9 +1220,9 @@ static int process_kernel(int argc, char* argv[]) { 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); @@ -1254,17 +1241,16 @@ static int process_backtrace(int argc, char *argv[]) { 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); @@ -1277,38 +1263,36 @@ static int process_backtrace(int argc, char *argv[]) { 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. */ + + for (i = 0; i < importer.iovw.count; i++) { + struct iovec *iovec = importer.iovw.iovec + i; + + iovw_put(iovw, iovec->iov_base, iovec->iov_len); + } } - iovec[n_iovec++] = IOVEC_MAKE_STRING("MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR); - iovec[n_iovec++] = IOVEC_MAKE_STRING("PRIORITY=" STRINGIFY(LOG_CRIT)); - - assert(n_iovec <= n_allocated); - - 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]);