]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/coredump.c
journal/compress: add stream compression/decompression functions
[thirdparty/systemd.git] / src / journal / coredump.c
CommitLineData
f5e04665
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
f5e04665
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
f5e04665 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
f5e04665
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <unistd.h>
803a3464
LP
24#include <stdio.h>
25#include <sys/prctl.h>
34c10968 26#include <sys/types.h>
cacd6403 27#include <sys/xattr.h>
f5e04665
LP
28
29#include <systemd/sd-journal.h>
30#include <systemd/sd-login.h>
31
32#include "log.h"
33#include "util.h"
1eef15b1 34#include "strv.h"
b7f7c685 35#include "macro.h"
49e942b2 36#include "mkdir.h"
803a3464 37#include "special.h"
ba1261bc 38#include "cgroup-util.h"
d18d46ec 39#include "journald-native.h"
34c10968
LP
40#include "conf-parser.h"
41#include "copy.h"
8d4e028f 42#include "stacktrace.h"
6388c315 43#include "path-util.h"
34c10968
LP
44
45#ifdef HAVE_ACL
46#include <sys/acl.h>
47#include "acl-util.h"
48#endif
49
50/* The maximum size up to which we process coredumps */
51#define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
52
53/* The maximum size up to which we leave the coredump around on
54 * disk */
55#define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
56
57/* The maximum size up to which we store the coredump in the
58 * journal */
59#define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
f5e04665 60
c4aa09b0
LP
61/* Make sure to not make this larger than the maximum journal entry
62 * size. See ENTRY_SIZE_MAX in journald-native.c. */
34c10968 63assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
f5e04665
LP
64
65enum {
1eef15b1
ZJS
66 INFO_PID,
67 INFO_UID,
68 INFO_GID,
69 INFO_SIGNAL,
70 INFO_TIMESTAMP,
71 INFO_COMM,
72 INFO_EXE,
73 _INFO_LEN
f5e04665
LP
74};
75
34c10968
LP
76typedef enum CoredumpStorage {
77 COREDUMP_STORAGE_NONE,
78 COREDUMP_STORAGE_EXTERNAL,
79 COREDUMP_STORAGE_JOURNAL,
80 COREDUMP_STORAGE_BOTH,
81 _COREDUMP_STORAGE_MAX,
82 _COREDUMP_STORAGE_INVALID = -1
83} CoredumpStorage;
84
85static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
86static off_t arg_process_size_max = PROCESS_SIZE_MAX;
87static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
88static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
89
90static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
91 [COREDUMP_STORAGE_NONE] = "none",
92 [COREDUMP_STORAGE_EXTERNAL] = "external",
93 [COREDUMP_STORAGE_JOURNAL] = "journal",
94 [COREDUMP_STORAGE_BOTH] = "both",
95};
96
97DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
98static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
99
100static int parse_config(void) {
101
102 static const ConfigTableItem items[] = {
103 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
104 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
105 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
106 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
107 {}
108 };
109
110 return config_parse(
111 NULL,
112 "/etc/systemd/coredump.conf",
113 NULL,
114 "Coredump\0",
115 config_item_table_lookup,
116 (void*) items,
117 false,
118 false,
119 NULL);
120}
121
122static int fix_acl(int fd, uid_t uid) {
123
124#ifdef HAVE_ACL
125 _cleanup_(acl_freep) acl_t acl = NULL;
126 acl_entry_t entry;
127 acl_permset_t permset;
128
129 if (uid <= SYSTEM_UID_MAX)
130 return 0;
131
132 /* Make sure normal users can read (but not write or delete)
133 * their own coredumps */
134
135 acl = acl_get_fd(fd);
136 if (!acl) {
137 log_error("Failed to get ACL: %m");
138 return -errno;
139 }
140
141 if (acl_create_entry(&acl, &entry) < 0 ||
142 acl_set_tag_type(entry, ACL_USER) < 0 ||
143 acl_set_qualifier(entry, &uid) < 0) {
144 log_error("Failed to patch ACL: %m");
145 return -errno;
146 }
147
148 if (acl_get_permset(entry, &permset) < 0 ||
149 acl_add_perm(permset, ACL_READ) < 0 ||
150 calc_acl_mask_if_needed(&acl) < 0) {
151 log_warning("Failed to patch ACL: %m");
152 return -errno;
153 }
154
155 if (acl_set_fd(fd, acl) < 0) {
156 log_error("Failed to apply ACL: %m");
157 return -errno;
158 }
159#endif
160
161 return 0;
162}
163
1eef15b1 164static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
0cd77f97 165
1eef15b1
ZJS
166 static const char * const xattrs[_INFO_LEN] = {
167 [INFO_PID] = "user.coredump.pid",
168 [INFO_UID] = "user.coredump.uid",
169 [INFO_GID] = "user.coredump.gid",
170 [INFO_SIGNAL] = "user.coredump.signal",
171 [INFO_TIMESTAMP] = "user.coredump.timestamp",
172 [INFO_COMM] = "user.coredump.comm",
173 [INFO_EXE] = "user.coredump.exe",
0cd77f97
LP
174 };
175
34c10968 176 int r = 0;
0cd77f97 177 unsigned i;
34c10968 178
1eef15b1 179 /* Attach some metadata to coredumps via extended
34c10968
LP
180 * attributes. Just because we can. */
181
1eef15b1
ZJS
182 for (i = 0; i < _INFO_LEN; i++) {
183 int k;
184
185 if (isempty(info[i]) || !xattrs[i])
0cd77f97 186 continue;
34c10968 187
1eef15b1
ZJS
188 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
189 if (k < 0 && r == 0)
34c10968 190 r = -errno;
0cd77f97 191 }
34c10968
LP
192
193 return r;
194}
195
b0b21dce 196#define filename_escape(s) xescape((s), "./ ")
34c10968 197
1eef15b1
ZJS
198static int save_external_coredump(const char *info[_INFO_LEN],
199 uid_t uid,
200 char **ret_filename,
201 int *ret_fd,
202 off_t *ret_size) {
203
34c10968
LP
204 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
205 _cleanup_close_ int fd = -1;
206 sd_id128_t boot;
207 struct stat st;
208 int r;
209
1eef15b1 210 assert(info);
34c10968
LP
211 assert(ret_filename);
212 assert(ret_fd);
213 assert(ret_size);
214
1eef15b1 215 c = filename_escape(info[INFO_COMM]);
34c10968
LP
216 if (!c)
217 return log_oom();
218
1eef15b1 219 p = filename_escape(info[INFO_PID]);
34c10968
LP
220 if (!p)
221 return log_oom();
222
1eef15b1 223 t = filename_escape(info[INFO_TIMESTAMP]);
34c10968
LP
224 if (!t)
225 return log_oom();
226
227 r = sd_id128_get_boot(&boot);
228 if (r < 0) {
229 log_error("Failed to determine boot ID: %s", strerror(-r));
230 return r;
231 }
232
233 r = asprintf(&fn,
234 "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
235 c,
236 SD_ID128_FORMAT_VAL(boot),
237 p,
238 t);
239 if (r < 0)
240 return log_oom();
241
242 tmp = tempfn_random(fn);
243 if (!tmp)
244 return log_oom();
803a3464 245
d2e54fae 246 mkdir_p_label("/var/lib/systemd/coredump", 0755);
803a3464 247
34c10968
LP
248 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
249 if (fd < 0) {
803a3464
LP
250 log_error("Failed to create coredump file: %m");
251 return -errno;
252 }
253
93240d3a
LP
254 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
255 if (r == -E2BIG) {
1eef15b1
ZJS
256 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.",
257 info[INFO_PID], info[INFO_COMM]);
93240d3a
LP
258 goto fail;
259 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
1eef15b1
ZJS
260 log_error("Not enough disk space for coredump of %s (%s), refusing.",
261 info[INFO_PID], info[INFO_COMM]);
93240d3a
LP
262 goto fail;
263 } else if (r < 0) {
34c10968
LP
264 log_error("Failed to dump coredump to file: %s", strerror(-r));
265 goto fail;
266 }
803a3464 267
34c10968
LP
268 /* Ignore errors on these */
269 fchmod(fd, 0640);
270 fix_acl(fd, uid);
1eef15b1 271 fix_xattr(fd, info);
803a3464 272
34c10968 273 if (fsync(fd) < 0) {
1eef15b1 274 log_error("Failed to sync coredump %s: %m", tmp);
34c10968
LP
275 r = -errno;
276 goto fail;
277 }
803a3464 278
34c10968 279 if (fstat(fd, &st) < 0) {
1eef15b1 280 log_error("Failed to fstat coredump %s: %m", tmp);
34c10968
LP
281 r = -errno;
282 goto fail;
283 }
284
285 if (rename(tmp, fn) < 0) {
1eef15b1 286 log_error("Failed to rename coredump %s -> %s: %m", tmp, fn);
34c10968
LP
287 r = -errno;
288 goto fail;
289 }
290
291 *ret_filename = fn;
292 *ret_fd = fd;
293 *ret_size = st.st_size;
294
295 fn = NULL;
296 fd = -1;
297
298 return 0;
299
300fail:
301 unlink_noerrno(tmp);
302 return r;
303}
304
305static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
306 _cleanup_free_ char *field = NULL;
307 ssize_t n;
308
8d4e028f 309 assert(fd >= 0);
34c10968
LP
310 assert(ret);
311 assert(ret_size);
312
313 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
314 log_warning("Failed to seek: %m");
315 return -errno;
803a3464
LP
316 }
317
34c10968
LP
318 field = malloc(9 + size);
319 if (!field) {
320 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
321 return -ENOMEM;
322 }
323
324 memcpy(field, "COREDUMP=", 9);
325
326 n = read(fd, field + 9, size);
327 if (n < 0) {
328 log_error("Failed to read core data: %s", strerror(-n));
329 return (int) n;
330 }
331 if ((size_t) n < size) {
332 log_error("Core data too short.");
333 return -EIO;
334 }
335
336 *ret = field;
337 *ret_size = size + 9;
338
339 field = NULL;
340
341 return 0;
342}
803a3464 343
34c10968
LP
344static int maybe_remove_external_coredump(const char *filename, off_t size) {
345
346 if (!filename)
347 return 0;
348
349 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
350 size <= arg_external_size_max)
351 return 0;
352
353 if (unlink(filename) < 0) {
354 log_error("Failed to unlink %s: %m", filename);
b7f7c685 355 return -errno;
803a3464
LP
356 }
357
b7f7c685 358 return 0;
803a3464
LP
359}
360
f5e04665 361int main(int argc, char* argv[]) {
34c10968
LP
362
363 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
364 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
365 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
8d4e028f 366 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
1eef15b1
ZJS
367 *exe = NULL, *comm = NULL;
368 const char *info[_INFO_LEN];
34c10968
LP
369
370 _cleanup_close_ int coredump_fd = -1;
371
a035f819 372 struct iovec iovec[17];
34c10968 373 off_t coredump_size;
f5e04665 374 int r, j = 0;
a035f819 375 uid_t uid, owner_uid;
fee80f69 376 gid_t gid;
a035f819 377 pid_t pid;
34c10968 378 char *t;
f5e04665 379
34c10968 380 /* Make sure we never enter a loop */
803a3464 381 prctl(PR_SET_DUMPABLE, 0);
f5e04665 382
34c10968
LP
383 /* First, log to a safe place, since we don't know what
384 * crashed and it might be journald which we'd rather not log
385 * to then. */
386 log_set_target(LOG_TARGET_KMSG);
34c10968 387 log_open();
803a3464 388
1eef15b1
ZJS
389 if (argc < INFO_COMM + 1) {
390 log_error("Not enough arguments passed from kernel (%d, expected %d).",
391 argc - 1, INFO_COMM + 1 - 1);
f5e04665
LP
392 r = -EINVAL;
393 goto finish;
394 }
395
34c10968
LP
396 /* Ignore all parse errors */
397 parse_config();
398 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
399
1eef15b1 400 r = parse_uid(argv[INFO_UID + 1], &uid);
f5e04665 401 if (r < 0) {
34c10968
LP
402 log_error("Failed to parse UID.");
403 goto finish;
404 }
803a3464 405
1eef15b1 406 r = parse_pid(argv[INFO_PID + 1], &pid);
34c10968 407 if (r < 0) {
f5e04665 408 log_error("Failed to parse PID.");
f5e04665
LP
409 goto finish;
410 }
411
1eef15b1 412 r = parse_gid(argv[INFO_GID + 1], &gid);
34c10968
LP
413 if (r < 0) {
414 log_error("Failed to parse GID.");
415 goto finish;
416 }
417
1eef15b1
ZJS
418 if (get_process_comm(pid, &comm) < 0) {
419 log_warning("Failed to get COMM, falling back to the commandline.");
420 comm = strv_join(argv + INFO_COMM + 1, " ");
421 }
422
423 if (get_process_exe(pid, &exe) < 0)
424 log_warning("Failed to get EXE.");
425
426 info[INFO_PID] = argv[INFO_PID + 1];
427 info[INFO_UID] = argv[INFO_UID + 1];
428 info[INFO_GID] = argv[INFO_GID + 1];
429 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
430 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
431 info[INFO_COMM] = comm;
432 info[INFO_EXE] = exe;
433
ba1261bc 434 if (cg_pid_get_unit(pid, &t) >= 0) {
803a3464
LP
435
436 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
803a3464 437
34c10968
LP
438 /* If we are journald, we cut things short,
439 * don't write to the journal, but still
440 * create a coredump. */
441
442 if (arg_storage != COREDUMP_STORAGE_NONE)
443 arg_storage = COREDUMP_STORAGE_EXTERNAL;
444
1eef15b1 445 r = save_external_coredump(info, uid, &coredump_filename, &coredump_fd, &coredump_size);
34c10968
LP
446 if (r < 0)
447 goto finish;
448
449 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
450 if (r < 0)
451 goto finish;
452
453 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
803a3464
LP
454 goto finish;
455 }
456
457 core_unit = strappend("COREDUMP_UNIT=", t);
b7f7c685 458 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
f9045468 459 core_unit = strappend("COREDUMP_USER_UNIT=", t);
803a3464 460
f9045468
MT
461 if (core_unit)
462 IOVEC_SET_STRING(iovec[j++], core_unit);
463
34c10968
LP
464 /* OK, now we know it's not the journal, hence we can make use
465 * of it now. */
803a3464
LP
466 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
467 log_open();
468
1eef15b1 469 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
f5e04665
LP
470 if (core_pid)
471 IOVEC_SET_STRING(iovec[j++], core_pid);
472
1eef15b1 473 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
f5e04665
LP
474 if (core_uid)
475 IOVEC_SET_STRING(iovec[j++], core_uid);
476
1eef15b1 477 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
f5e04665
LP
478 if (core_gid)
479 IOVEC_SET_STRING(iovec[j++], core_gid);
480
1eef15b1 481 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
f5e04665
LP
482 if (core_signal)
483 IOVEC_SET_STRING(iovec[j++], core_signal);
484
f5e04665
LP
485 if (sd_pid_get_session(pid, &t) >= 0) {
486 core_session = strappend("COREDUMP_SESSION=", t);
487 free(t);
488
489 if (core_session)
490 IOVEC_SET_STRING(iovec[j++], core_session);
491 }
492
a035f819
LP
493 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
494 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
495
496 if (core_owner_uid)
497 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
498 }
499
500 if (sd_pid_get_slice(pid, &t) >= 0) {
501 core_slice = strappend("COREDUMP_SLICE=", t);
502 free(t);
503
504 if (core_slice)
505 IOVEC_SET_STRING(iovec[j++], core_slice);
506 }
507
1eef15b1
ZJS
508 if (comm) {
509 core_comm = strappend("COREDUMP_COMM=", comm);
510 if (core_comm)
511 IOVEC_SET_STRING(iovec[j++], core_comm);
512 }
513
514 if (exe) {
8d4e028f 515 core_exe = strappend("COREDUMP_EXE=", exe);
f5e04665
LP
516 if (core_exe)
517 IOVEC_SET_STRING(iovec[j++], core_exe);
518 }
519
9bdbc2e2 520 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
f5e04665
LP
521 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
522 free(t);
523
524 if (core_cmdline)
525 IOVEC_SET_STRING(iovec[j++], core_cmdline);
526 }
527
a035f819
LP
528 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
529 core_cgroup = strappend("COREDUMP_CGROUP=", t);
530 free(t);
531
532 if (core_cgroup)
533 IOVEC_SET_STRING(iovec[j++], core_cgroup);
534 }
535
1eef15b1 536 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
f5e04665
LP
537 if (core_timestamp)
538 IOVEC_SET_STRING(iovec[j++], core_timestamp);
539
540 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
541 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
542
34c10968 543 /* Always stream the coredump to disk, if that's possible */
1eef15b1 544 r = save_external_coredump(info, uid, &coredump_filename, &coredump_fd, &coredump_size);
34c10968 545 if (r < 0)
2424a475
ZJS
546 /* skip whole core dumping part */
547 goto log;
34c10968
LP
548
549 /* If we don't want to keep the coredump on disk, remove it
8d4e028f
LP
550 * now, as later on we will lack the privileges for
551 * it. However, we keep the fd to it, so that we can still
552 * process it and log it. */
34c10968
LP
553 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
554 if (r < 0)
555 goto finish;
556
fee80f69
LP
557 /* Now, let's drop privileges to become the user who owns the
558 * segfaulted process and allocate the coredump memory under
559 * his uid. This also ensures that the credentials journald
560 * will see are the ones of the coredumping user, thus making
561 * sure the user himself gets access to the core dump. */
fee80f69
LP
562 if (setresgid(gid, gid, gid) < 0 ||
563 setresuid(uid, uid, uid) < 0) {
564 log_error("Failed to drop privileges: %m");
565 r = -errno;
566 goto finish;
567 }
568
8d4e028f
LP
569#ifdef HAVE_ELFUTILS
570 /* Try to get a strack trace if we can */
571 if (coredump_size <= arg_process_size_max) {
572 _cleanup_free_ char *stacktrace = NULL;
573
574 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
575 if (r >= 0)
1eef15b1 576 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
8d4e028f
LP
577 else
578 log_warning("Failed to generate stack trace: %s", strerror(-r));
579 }
580
581 if (!core_message)
582#endif
2424a475 583log:
1eef15b1 584 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
8d4e028f
LP
585 if (core_message)
586 IOVEC_SET_STRING(iovec[j++], core_message);
587
34c10968
LP
588 /* Optionally store the entire coredump in the journal */
589 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
590 coredump_size <= (off_t) arg_journal_size_max) {
591 size_t sz;
fee80f69 592
34c10968 593 /* Store the coredump itself in the journal */
fee80f69 594
34c10968
LP
595 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
596 if (r >= 0) {
597 iovec[j].iov_base = coredump_data;
598 iovec[j].iov_len = sz;
599 j++;
ca0ceb6f 600 }
fee80f69
LP
601 }
602
f5e04665
LP
603 r = sd_journal_sendv(iovec, j);
604 if (r < 0)
872c8faa 605 log_error("Failed to log coredump: %s", strerror(-r));
f5e04665
LP
606
607finish:
f5e04665
LP
608 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
609}