chiark / gitweb /
treewide: another round of simplifications
[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 #ifdef HAVE_ELFUTILS
30 #  include <dwarf.h>
31 #  include <elfutils/libdwfl.h>
32 #endif
33
34 #include "systemd/sd-journal.h"
35 #include "systemd/sd-login.h"
36
37 #include "log.h"
38 #include "util.h"
39 #include "fileio.h"
40 #include "strv.h"
41 #include "macro.h"
42 #include "mkdir.h"
43 #include "special.h"
44 #include "cgroup-util.h"
45 #include "journald-native.h"
46 #include "conf-parser.h"
47 #include "copy.h"
48 #include "stacktrace.h"
49 #include "path-util.h"
50 #include "compress.h"
51 #include "coredump-vacuum.h"
52
53 #ifdef HAVE_ACL
54 #  include <sys/acl.h>
55 #  include "acl-util.h"
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 DATA_SIZE_MAX in journald-native.c. */
71 assert_cc(JOURNAL_SIZE_MAX <= DATA_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(NULL, "/etc/systemd/coredump.conf", NULL,
124                             "Coredump\0",
125                             config_item_table_lookup, items,
126                             false, false, true, NULL);
127 }
128
129 static int fix_acl(int fd, uid_t uid) {
130
131 #ifdef HAVE_ACL
132         _cleanup_(acl_freep) acl_t acl = NULL;
133         acl_entry_t entry;
134         acl_permset_t permset;
135
136         assert(fd >= 0);
137
138         if (uid <= SYSTEM_UID_MAX)
139                 return 0;
140
141         /* Make sure normal users can read (but not write or delete)
142          * their own coredumps */
143
144         acl = acl_get_fd(fd);
145         if (!acl)
146                 return log_error_errno(errno, "Failed to get ACL: %m");
147
148         if (acl_create_entry(&acl, &entry) < 0 ||
149             acl_set_tag_type(entry, ACL_USER) < 0 ||
150             acl_set_qualifier(entry, &uid) < 0) {
151                 log_error_errno(errno, "Failed to patch ACL: %m");
152                 return -errno;
153         }
154
155         if (acl_get_permset(entry, &permset) < 0 ||
156             acl_add_perm(permset, ACL_READ) < 0 ||
157             calc_acl_mask_if_needed(&acl) < 0) {
158                 log_warning_errno(errno, "Failed to patch ACL: %m");
159                 return -errno;
160         }
161
162         if (acl_set_fd(fd, acl) < 0)
163                 return log_error_errno(errno, "Failed to apply ACL: %m");
164 #endif
165
166         return 0;
167 }
168
169 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
170
171         static const char * const xattrs[_INFO_LEN] = {
172                 [INFO_PID] = "user.coredump.pid",
173                 [INFO_UID] = "user.coredump.uid",
174                 [INFO_GID] = "user.coredump.gid",
175                 [INFO_SIGNAL] = "user.coredump.signal",
176                 [INFO_TIMESTAMP] = "user.coredump.timestamp",
177                 [INFO_COMM] = "user.coredump.comm",
178                 [INFO_EXE] = "user.coredump.exe",
179         };
180
181         int r = 0;
182         unsigned i;
183
184         assert(fd >= 0);
185
186         /* Attach some metadata to coredumps via extended
187          * attributes. Just because we can. */
188
189         for (i = 0; i < _INFO_LEN; i++) {
190                 int k;
191
192                 if (isempty(info[i]) || !xattrs[i])
193                         continue;
194
195                 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
196                 if (k < 0 && r == 0)
197                         r = -errno;
198         }
199
200         return r;
201 }
202
203 #define filename_escape(s) xescape((s), "./ ")
204
205 static int fix_permissions(
206                 int fd,
207                 const char *filename,
208                 const char *target,
209                 const char *info[_INFO_LEN],
210                 uid_t uid) {
211
212         assert(fd >= 0);
213         assert(filename);
214         assert(target);
215         assert(info);
216
217         /* Ignore errors on these */
218         fchmod(fd, 0640);
219         fix_acl(fd, uid);
220         fix_xattr(fd, info);
221
222         if (fsync(fd) < 0)
223                 return log_error_errno(errno, "Failed to sync coredump %s: %m", filename);
224
225         if (rename(filename, target) < 0)
226                 return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target);
227
228         return 0;
229 }
230
231 static int maybe_remove_external_coredump(const char *filename, off_t size) {
232
233         /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
234
235         if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
236             size <= arg_external_size_max)
237                 return 0;
238
239         if (!filename)
240                 return 1;
241
242         if (unlink(filename) < 0 && errno != ENOENT)
243                 return log_error_errno(errno, "Failed to unlink %s: %m", filename);
244
245         return 1;
246 }
247
248 static int make_filename(const char *info[_INFO_LEN], char **ret) {
249         _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
250         sd_id128_t boot;
251         int r;
252
253         assert(info);
254
255         c = filename_escape(info[INFO_COMM]);
256         if (!c)
257                 return -ENOMEM;
258
259         u = filename_escape(info[INFO_UID]);
260         if (!u)
261                 return -ENOMEM;
262
263         r = sd_id128_get_boot(&boot);
264         if (r < 0)
265                 return r;
266
267         p = filename_escape(info[INFO_PID]);
268         if (!p)
269                 return -ENOMEM;
270
271         t = filename_escape(info[INFO_TIMESTAMP]);
272         if (!t)
273                 return -ENOMEM;
274
275         if (asprintf(ret,
276                      "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
277                      c,
278                      u,
279                      SD_ID128_FORMAT_VAL(boot),
280                      p,
281                      t) < 0)
282                 return -ENOMEM;
283
284         return 0;
285 }
286
287 static int save_external_coredump(
288                 const char *info[_INFO_LEN],
289                 uid_t uid,
290                 char **ret_filename,
291                 int *ret_fd,
292                 off_t *ret_size) {
293
294         _cleanup_free_ char *fn = NULL, *tmp = NULL;
295         _cleanup_close_ int fd = -1;
296         struct stat st;
297         int r;
298
299         assert(info);
300         assert(ret_filename);
301         assert(ret_fd);
302         assert(ret_size);
303
304         r = make_filename(info, &fn);
305         if (r < 0)
306                 return log_error_errno(r, "Failed to determine coredump file name: %m");
307
308         tmp = tempfn_random(fn);
309         if (!tmp)
310                 return log_oom();
311
312         mkdir_p_label("/var/lib/systemd/coredump", 0755);
313
314         fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
315         if (fd < 0)
316                 return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp);
317
318         r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
319         if (r == -EFBIG) {
320                 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
321                 goto fail;
322         } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
323                 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
324                 goto fail;
325         } else if (r < 0) {
326                 log_error_errno(r, "Failed to dump coredump to file: %m");
327                 goto fail;
328         }
329
330         if (fstat(fd, &st) < 0) {
331                 log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp);
332                 goto fail;
333         }
334
335         if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
336                 log_error_errno(errno, "Failed to seek on %s: %m", tmp);
337                 goto fail;
338         }
339
340 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
341         /* If we will remove the coredump anyway, do not compress. */
342         if (maybe_remove_external_coredump(NULL, st.st_size) == 0
343             && arg_compress) {
344
345                 _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
346                 _cleanup_close_ int fd_compressed = -1;
347
348                 fn_compressed = strappend(fn, COMPRESSED_EXT);
349                 if (!fn_compressed) {
350                         log_oom();
351                         goto uncompressed;
352                 }
353
354                 tmp_compressed = tempfn_random(fn_compressed);
355                 if (!tmp_compressed) {
356                         log_oom();
357                         goto uncompressed;
358                 }
359
360                 fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
361                 if (fd_compressed < 0) {
362                         log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed);
363                         goto uncompressed;
364                 }
365
366                 r = compress_stream(fd, fd_compressed, -1);
367                 if (r < 0) {
368                         log_error_errno(r, "Failed to compress %s: %m", tmp_compressed);
369                         goto fail_compressed;
370                 }
371
372                 r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
373                 if (r < 0)
374                         goto fail_compressed;
375
376                 /* OK, this worked, we can get rid of the uncompressed version now */
377                 unlink_noerrno(tmp);
378
379                 *ret_filename = fn_compressed;    /* compressed */
380                 *ret_fd = fd;                     /* uncompressed */
381                 *ret_size = st.st_size;           /* uncompressed */
382
383                 fn_compressed = NULL;
384                 fd = -1;
385
386                 return 0;
387
388         fail_compressed:
389                 unlink_noerrno(tmp_compressed);
390         }
391
392 uncompressed:
393 #endif
394         r = fix_permissions(fd, tmp, fn, info, uid);
395         if (r < 0)
396                 goto fail;
397
398         *ret_filename = fn;
399         *ret_fd = fd;
400         *ret_size = st.st_size;
401
402         fn = NULL;
403         fd = -1;
404
405         return 0;
406
407 fail:
408         unlink_noerrno(tmp);
409         return r;
410 }
411
412 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
413         _cleanup_free_ char *field = NULL;
414         ssize_t n;
415
416         assert(fd >= 0);
417         assert(ret);
418         assert(ret_size);
419
420         if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
421                 return log_warning_errno(errno, "Failed to seek: %m");
422
423         field = malloc(9 + size);
424         if (!field) {
425                 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
426                 return -ENOMEM;
427         }
428
429         memcpy(field, "COREDUMP=", 9);
430
431         n = read(fd, field + 9, size);
432         if (n < 0)
433                 return log_error_errno((int) n, "Failed to read core data: %m");
434         if ((size_t) n < size) {
435                 log_error("Core data too short.");
436                 return -EIO;
437         }
438
439         *ret = field;
440         *ret_size = size + 9;
441
442         field = NULL;
443
444         return 0;
445 }
446
447 /* Joins /proc/[pid]/fd/ and /proc/[pid]/fdinfo/ into the following lines:
448  * 0:/dev/pts/23
449  * pos:    0
450  * flags:  0100002
451  *
452  * 1:/dev/pts/23
453  * pos:    0
454  * flags:  0100002
455  *
456  * 2:/dev/pts/23
457  * pos:    0
458  * flags:  0100002
459  * EOF
460  */
461 static int compose_open_fds(pid_t pid, char **open_fds) {
462         _cleanup_fclose_ FILE *stream = NULL;
463         size_t ignored_size;
464         const char *fddelim = "", *path;
465         struct dirent *dent = NULL;
466         _cleanup_closedir_ DIR *proc_fd_dir = NULL;
467         _cleanup_close_ int proc_fdinfo_fd = -1;
468         int r = 0;
469
470         assert(pid >= 0);
471         assert(open_fds != NULL);
472
473         path = procfs_file_alloca(pid, "fd");
474         proc_fd_dir = opendir(path);
475         if (!proc_fd_dir)
476                 return -errno;
477
478         proc_fdinfo_fd = openat(dirfd(proc_fd_dir), "../fdinfo",
479                                 O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC|O_PATH);
480         if (proc_fdinfo_fd < 0)
481                 return -errno;
482
483         stream = open_memstream(open_fds, &ignored_size);
484         if (!stream)
485                 return -ENOMEM;
486
487         for (dent = readdir(proc_fd_dir); dent != NULL; dent = readdir(proc_fd_dir)) {
488                 _cleanup_free_ char *fdname = NULL;
489                 int fd;
490                 _cleanup_fclose_ FILE *fdinfo = NULL;
491                 char line[LINE_MAX];
492
493                 if (dent->d_name[0] == '.' || strcmp(dent->d_name, "..") == 0)
494                         continue;
495
496                 r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname);
497                 if (r < 0)
498                         return r;
499
500                 fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname);
501                 fddelim = "\n";
502
503                 /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */
504                 fd = openat(proc_fdinfo_fd, dent->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY);
505                 if (fd < 0)
506                         continue;
507
508                 fdinfo = fdopen(fd, "re");
509                 if (fdinfo == NULL) {
510                         close(fd);
511                         continue;
512                 }
513
514                 while(fgets(line, sizeof(line), fdinfo) != NULL)
515                         fprintf(stream, "%s%s",
516                                 line, strchr(line, '\n') == NULL ? "\n" : "");
517         }
518
519         return 0;
520 }
521
522 int main(int argc, char* argv[]) {
523
524         _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
525                 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
526                 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
527                 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL, *core_open_fds = NULL,
528                 *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL,
529                 *core_cwd = NULL, *core_root = NULL, *core_environ = NULL,
530                 *exe = NULL, *comm = NULL, *filename = NULL;
531         const char *info[_INFO_LEN];
532
533         _cleanup_close_ int coredump_fd = -1;
534
535         struct iovec iovec[26];
536         off_t coredump_size;
537         int r, j = 0;
538         uid_t uid, owner_uid;
539         gid_t gid;
540         pid_t pid;
541         char *t;
542         const char *p;
543
544         /* Make sure we never enter a loop */
545         prctl(PR_SET_DUMPABLE, 0);
546
547         /* First, log to a safe place, since we don't know what
548          * crashed and it might be journald which we'd rather not log
549          * to then. */
550         log_set_target(LOG_TARGET_KMSG);
551         log_open();
552
553         if (argc < INFO_COMM + 1) {
554                 log_error("Not enough arguments passed from kernel (%d, expected %d).",
555                           argc - 1, INFO_COMM + 1 - 1);
556                 r = -EINVAL;
557                 goto finish;
558         }
559
560         /* Ignore all parse errors */
561         parse_config();
562
563         log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
564         log_debug("Selected compression %s.", yes_no(arg_compress));
565
566         r = parse_uid(argv[INFO_UID + 1], &uid);
567         if (r < 0) {
568                 log_error("Failed to parse UID.");
569                 goto finish;
570         }
571
572         r = parse_pid(argv[INFO_PID + 1], &pid);
573         if (r < 0) {
574                 log_error("Failed to parse PID.");
575                 goto finish;
576         }
577
578         r = parse_gid(argv[INFO_GID + 1], &gid);
579         if (r < 0) {
580                 log_error("Failed to parse GID.");
581                 goto finish;
582         }
583
584         if (get_process_comm(pid, &comm) < 0) {
585                 log_warning("Failed to get COMM, falling back to the command line.");
586                 comm = strv_join(argv + INFO_COMM + 1, " ");
587         }
588
589         if (get_process_exe(pid, &exe) < 0)
590                 log_warning("Failed to get EXE.");
591
592         info[INFO_PID] = argv[INFO_PID + 1];
593         info[INFO_UID] = argv[INFO_UID + 1];
594         info[INFO_GID] = argv[INFO_GID + 1];
595         info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
596         info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
597         info[INFO_COMM] = comm;
598         info[INFO_EXE] = exe;
599
600         if (cg_pid_get_unit(pid, &t) >= 0) {
601
602                 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
603
604                         /* If we are journald, we cut things short,
605                          * don't write to the journal, but still
606                          * create a coredump. */
607
608                         if (arg_storage != COREDUMP_STORAGE_NONE)
609                                 arg_storage = COREDUMP_STORAGE_EXTERNAL;
610
611                         r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
612                         if (r < 0)
613                                 goto finish;
614
615                         r = maybe_remove_external_coredump(filename, coredump_size);
616                         if (r < 0)
617                                 goto finish;
618
619                         log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
620                         goto finish;
621                 }
622
623                 core_unit = strappend("COREDUMP_UNIT=", t);
624         } else if (cg_pid_get_user_unit(pid, &t) >= 0)
625                 core_unit = strappend("COREDUMP_USER_UNIT=", t);
626
627         if (core_unit)
628                 IOVEC_SET_STRING(iovec[j++], core_unit);
629
630         /* OK, now we know it's not the journal, hence we can make use
631          * of it now. */
632         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
633         log_open();
634
635         core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
636         if (core_pid)
637                 IOVEC_SET_STRING(iovec[j++], core_pid);
638
639         core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
640         if (core_uid)
641                 IOVEC_SET_STRING(iovec[j++], core_uid);
642
643         core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
644         if (core_gid)
645                 IOVEC_SET_STRING(iovec[j++], core_gid);
646
647         core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
648         if (core_signal)
649                 IOVEC_SET_STRING(iovec[j++], core_signal);
650
651         if (sd_pid_get_session(pid, &t) >= 0) {
652                 core_session = strappend("COREDUMP_SESSION=", t);
653                 free(t);
654
655                 if (core_session)
656                         IOVEC_SET_STRING(iovec[j++], core_session);
657         }
658
659         if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
660                 r = asprintf(&core_owner_uid,
661                              "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
662                 if (r > 0)
663                         IOVEC_SET_STRING(iovec[j++], core_owner_uid);
664         }
665
666         if (sd_pid_get_slice(pid, &t) >= 0) {
667                 core_slice = strappend("COREDUMP_SLICE=", t);
668                 free(t);
669
670                 if (core_slice)
671                         IOVEC_SET_STRING(iovec[j++], core_slice);
672         }
673
674         if (comm) {
675                 core_comm = strappend("COREDUMP_COMM=", comm);
676                 if (core_comm)
677                         IOVEC_SET_STRING(iovec[j++], core_comm);
678         }
679
680         if (exe) {
681                 core_exe = strappend("COREDUMP_EXE=", exe);
682                 if (core_exe)
683                         IOVEC_SET_STRING(iovec[j++], core_exe);
684         }
685
686         if (get_process_cmdline(pid, 0, false, &t) >= 0) {
687                 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
688                 free(t);
689
690                 if (core_cmdline)
691                         IOVEC_SET_STRING(iovec[j++], core_cmdline);
692         }
693
694         if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
695                 core_cgroup = strappend("COREDUMP_CGROUP=", t);
696                 free(t);
697
698                 if (core_cgroup)
699                         IOVEC_SET_STRING(iovec[j++], core_cgroup);
700         }
701
702         if (compose_open_fds(pid, &t) >= 0) {
703                 core_open_fds = strappend("COREDUMP_OPEN_FDS=", t);
704                 free(t);
705
706                 if (core_open_fds)
707                         IOVEC_SET_STRING(iovec[j++], core_open_fds);
708         }
709
710         p = procfs_file_alloca(pid, "status");
711         if (read_full_file(p, &t, NULL) >= 0) {
712                 core_proc_status = strappend("COREDUMP_PROC_STATUS=", t);
713                 free(t);
714
715                 if (core_proc_status)
716                         IOVEC_SET_STRING(iovec[j++], core_proc_status);
717         }
718
719         p = procfs_file_alloca(pid, "maps");
720         if (read_full_file(p, &t, NULL) >= 0) {
721                 core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t);
722                 free(t);
723
724                 if (core_proc_maps)
725                         IOVEC_SET_STRING(iovec[j++], core_proc_maps);
726         }
727
728         p = procfs_file_alloca(pid, "limits");
729         if (read_full_file(p, &t, NULL) >= 0) {
730                 core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t);
731                 free(t);
732
733                 if (core_proc_limits)
734                         IOVEC_SET_STRING(iovec[j++], core_proc_limits);
735         }
736
737         p = procfs_file_alloca(pid, "cgroup");
738         if (read_full_file(p, &t, NULL) >=0) {
739                 core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t);
740                 free(t);
741
742                 if (core_proc_cgroup)
743                         IOVEC_SET_STRING(iovec[j++], core_proc_cgroup);
744         }
745
746         if (get_process_cwd(pid, &t) >= 0) {
747                 core_cwd = strappend("COREDUMP_CWD=", t);
748                 free(t);
749
750                 if (core_cwd)
751                         IOVEC_SET_STRING(iovec[j++], core_cwd);
752         }
753
754         if (get_process_root(pid, &t) >= 0) {
755                 core_root = strappend("COREDUMP_ROOT=", t);
756                 free(t);
757
758                 if (core_root)
759                         IOVEC_SET_STRING(iovec[j++], core_root);
760         }
761
762         if (get_process_environ(pid, &t) >= 0) {
763                 core_environ = strappend("COREDUMP_ENVIRON=", t);
764                 free(t);
765
766                 if (core_environ)
767                         IOVEC_SET_STRING(iovec[j++], core_environ);
768         }
769
770         core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
771         if (core_timestamp)
772                 IOVEC_SET_STRING(iovec[j++], core_timestamp);
773
774         IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
775         IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
776
777         /* Vacuum before we write anything again */
778         coredump_vacuum(-1, arg_keep_free, arg_max_use);
779
780         /* Always stream the coredump to disk, if that's possible */
781         r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
782         if (r < 0)
783                 /* skip whole core dumping part */
784                 goto log;
785
786         /* If we don't want to keep the coredump on disk, remove it
787          * now, as later on we will lack the privileges for
788          * it. However, we keep the fd to it, so that we can still
789          * process it and log it. */
790         r = maybe_remove_external_coredump(filename, coredump_size);
791         if (r < 0)
792                 goto finish;
793         if (r == 0) {
794                 const char *coredump_filename;
795
796                 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
797                 IOVEC_SET_STRING(iovec[j++], coredump_filename);
798         }
799
800         /* Vacuum again, but exclude the coredump we just created */
801         coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
802
803         /* Now, let's drop privileges to become the user who owns the
804          * segfaulted process and allocate the coredump memory under
805          * the user's uid. This also ensures that the credentials
806          * journald will see are the ones of the coredumping user,
807          * thus making sure the user gets access to the core dump. */
808         if (setresgid(gid, gid, gid) < 0 ||
809             setresuid(uid, uid, uid) < 0) {
810                 log_error_errno(errno, "Failed to drop privileges: %m");
811                 r = -errno;
812                 goto finish;
813         }
814
815 #ifdef HAVE_ELFUTILS
816         /* Try to get a strack trace if we can */
817         if (coredump_size <= arg_process_size_max) {
818                 _cleanup_free_ char *stacktrace = NULL;
819
820                 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
821                 if (r >= 0)
822                         core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
823                 else if (r == -EINVAL)
824                         log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
825                 else
826                         log_warning_errno(r, "Failed to generate stack trace: %m");
827         }
828
829         if (!core_message)
830 #endif
831 log:
832         core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
833         if (core_message)
834                 IOVEC_SET_STRING(iovec[j++], core_message);
835
836         /* Optionally store the entire coredump in the journal */
837         if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
838             coredump_size <= (off_t) arg_journal_size_max) {
839                 size_t sz;
840
841                 /* Store the coredump itself in the journal */
842
843                 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
844                 if (r >= 0) {
845                         iovec[j].iov_base = coredump_data;
846                         iovec[j].iov_len = sz;
847                         j++;
848                 }
849         }
850
851         r = sd_journal_sendv(iovec, j);
852         if (r < 0)
853                 log_error_errno(r, "Failed to log coredump: %m");
854
855 finish:
856         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
857 }