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