]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/coredump.c
coredump: rework compose_open_fds()
[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 28
4d229b31
UTL
29#ifdef HAVE_ELFUTILS
30# include <dwarf.h>
31# include <elfutils/libdwfl.h>
32#endif
33
73f860db
ZJS
34#include "systemd/sd-journal.h"
35#include "systemd/sd-login.h"
f5e04665
LP
36
37#include "log.h"
38#include "util.h"
3f132692 39#include "fileio.h"
1eef15b1 40#include "strv.h"
b7f7c685 41#include "macro.h"
49e942b2 42#include "mkdir.h"
803a3464 43#include "special.h"
ba1261bc 44#include "cgroup-util.h"
d18d46ec 45#include "journald-native.h"
34c10968
LP
46#include "conf-parser.h"
47#include "copy.h"
8d4e028f 48#include "stacktrace.h"
6388c315 49#include "path-util.h"
cfd652ed 50#include "compress.h"
0dc5d23c 51#include "coredump-vacuum.h"
34c10968
LP
52
53#ifdef HAVE_ACL
34727273
ZJS
54# include <sys/acl.h>
55# include "acl-util.h"
56#endif
57
34c10968
LP
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))
f5e04665 68
c4aa09b0 69/* Make sure to not make this larger than the maximum journal entry
874bc134
ZJS
70 * size. See DATA_SIZE_MAX in journald-native.c. */
71assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
f5e04665
LP
72
73enum {
1eef15b1
ZJS
74 INFO_PID,
75 INFO_UID,
76 INFO_GID,
77 INFO_SIGNAL,
78 INFO_TIMESTAMP,
79 INFO_COMM,
80 INFO_EXE,
81 _INFO_LEN
f5e04665
LP
82};
83
34c10968
LP
84typedef 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
34c10968
LP
93static 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
100DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
8c9571d0 101static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
34727273
ZJS
102
103static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
8c9571d0 104static bool arg_compress = true;
34727273
ZJS
105static off_t arg_process_size_max = PROCESS_SIZE_MAX;
106static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
107static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
0dc5d23c
LP
108static off_t arg_keep_free = (off_t) -1;
109static off_t arg_max_use = (off_t) -1;
34c10968
LP
110
111static int parse_config(void) {
34c10968 112 static const ConfigTableItem items[] = {
8c9571d0
LP
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 },
34c10968
LP
120 {}
121 };
122
36f822c4
ZJS
123 return config_parse(NULL, "/etc/systemd/coredump.conf", NULL,
124 "Coredump\0",
125 config_item_table_lookup, items,
126 false, false, true, NULL);
34c10968
LP
127}
128
129static 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
b59233e6
LP
136 assert(fd >= 0);
137
34c10968
LP
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);
4a62c710
MS
145 if (!acl)
146 return log_error_errno(errno, "Failed to get ACL: %m");
34c10968
LP
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) {
56f64d95 151 log_error_errno(errno, "Failed to patch ACL: %m");
34c10968
LP
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) {
56f64d95 158 log_warning_errno(errno, "Failed to patch ACL: %m");
34c10968
LP
159 return -errno;
160 }
161
4a62c710
MS
162 if (acl_set_fd(fd, acl) < 0)
163 return log_error_errno(errno, "Failed to apply ACL: %m");
34c10968
LP
164#endif
165
166 return 0;
167}
168
1eef15b1 169static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
0cd77f97 170
1eef15b1
ZJS
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",
0cd77f97
LP
179 };
180
34c10968 181 int r = 0;
0cd77f97 182 unsigned i;
34c10968 183
b59233e6
LP
184 assert(fd >= 0);
185
1eef15b1 186 /* Attach some metadata to coredumps via extended
34c10968
LP
187 * attributes. Just because we can. */
188
1eef15b1
ZJS
189 for (i = 0; i < _INFO_LEN; i++) {
190 int k;
191
192 if (isempty(info[i]) || !xattrs[i])
0cd77f97 193 continue;
34c10968 194
1eef15b1
ZJS
195 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
196 if (k < 0 && r == 0)
34c10968 197 r = -errno;
0cd77f97 198 }
34c10968
LP
199
200 return r;
201}
202
b0b21dce 203#define filename_escape(s) xescape((s), "./ ")
34c10968 204
b59233e6
LP
205static 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);
cfd652ed
ZJS
216
217 /* Ignore errors on these */
218 fchmod(fd, 0640);
219 fix_acl(fd, uid);
220 fix_xattr(fd, info);
221
4a62c710
MS
222 if (fsync(fd) < 0)
223 return log_error_errno(errno, "Failed to sync coredump %s: %m", filename);
cfd652ed 224
4a62c710
MS
225 if (rename(filename, target) < 0)
226 return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target);
cfd652ed
ZJS
227
228 return 0;
229}
230
231static int maybe_remove_external_coredump(const char *filename, off_t size) {
232
b59233e6 233 /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */
cfd652ed
ZJS
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
4a62c710
MS
242 if (unlink(filename) < 0 && errno != ENOENT)
243 return log_error_errno(errno, "Failed to unlink %s: %m", filename);
cfd652ed
ZJS
244
245 return 1;
246}
247
b59233e6
LP
248static int make_filename(const char *info[_INFO_LEN], char **ret) {
249 _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL;
34c10968 250 sd_id128_t boot;
34c10968
LP
251 int r;
252
1eef15b1 253 assert(info);
34c10968 254
1eef15b1 255 c = filename_escape(info[INFO_COMM]);
34c10968 256 if (!c)
b59233e6 257 return -ENOMEM;
34c10968 258
0dc5d23c
LP
259 u = filename_escape(info[INFO_UID]);
260 if (!u)
b59233e6 261 return -ENOMEM;
34c10968
LP
262
263 r = sd_id128_get_boot(&boot);
b59233e6 264 if (r < 0)
34c10968 265 return r;
34c10968 266
b59233e6
LP
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,
0dc5d23c 276 "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
34c10968 277 c,
0dc5d23c 278 u,
34c10968
LP
279 SD_ID128_FORMAT_VAL(boot),
280 p,
b59233e6
LP
281 t) < 0)
282 return -ENOMEM;
283
284 return 0;
285}
286
287static 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);
23bbb0de
MS
305 if (r < 0)
306 return log_error_errno(r, "Failed to determine coredump file name: %m");
34c10968
LP
307
308 tmp = tempfn_random(fn);
309 if (!tmp)
310 return log_oom();
803a3464 311
d2e54fae 312 mkdir_p_label("/var/lib/systemd/coredump", 0755);
803a3464 313
34c10968 314 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
4a62c710
MS
315 if (fd < 0)
316 return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp);
803a3464 317
93240d3a 318 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
84ee0960 319 if (r == -EFBIG) {
0dc5d23c 320 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]);
93240d3a
LP
321 goto fail;
322 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
0dc5d23c 323 log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]);
93240d3a
LP
324 goto fail;
325 } else if (r < 0) {
da927ba9 326 log_error_errno(r, "Failed to dump coredump to file: %m");
34c10968
LP
327 goto fail;
328 }
803a3464 329
34c10968 330 if (fstat(fd, &st) < 0) {
56f64d95 331 log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp);
34c10968
LP
332 goto fail;
333 }
334
7849c2ac 335 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
56f64d95 336 log_error_errno(errno, "Failed to seek on %s: %m", tmp);
b59233e6 337 goto fail;
7849c2ac
TA
338 }
339
d89c8fdf 340#if defined(HAVE_XZ) || defined(HAVE_LZ4)
cfd652ed 341 /* If we will remove the coredump anyway, do not compress. */
34727273 342 if (maybe_remove_external_coredump(NULL, st.st_size) == 0
8c9571d0 343 && arg_compress) {
cfd652ed 344
b59233e6
LP
345 _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
346 _cleanup_close_ int fd_compressed = -1;
cfd652ed 347
d89c8fdf 348 fn_compressed = strappend(fn, COMPRESSED_EXT);
b59233e6 349 if (!fn_compressed) {
d89c8fdf 350 log_oom();
cfd652ed
ZJS
351 goto uncompressed;
352 }
353
b59233e6
LP
354 tmp_compressed = tempfn_random(fn_compressed);
355 if (!tmp_compressed) {
d89c8fdf 356 log_oom();
b59233e6 357 goto uncompressed;
cfd652ed
ZJS
358 }
359
b59233e6
LP
360 fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
361 if (fd_compressed < 0) {
56f64d95 362 log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed);
b59233e6 363 goto uncompressed;
cfd652ed
ZJS
364 }
365
d89c8fdf 366 r = compress_stream(fd, fd_compressed, -1);
b59233e6 367 if (r < 0) {
da927ba9 368 log_error_errno(r, "Failed to compress %s: %m", tmp_compressed);
b59233e6
LP
369 goto fail_compressed;
370 }
371
372 r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, info, uid);
cfd652ed 373 if (r < 0)
b59233e6
LP
374 goto fail_compressed;
375
376 /* OK, this worked, we can get rid of the uncompressed version now */
377 unlink_noerrno(tmp);
cfd652ed 378
b59233e6
LP
379 *ret_filename = fn_compressed; /* compressed */
380 *ret_fd = fd; /* uncompressed */
381 *ret_size = st.st_size; /* uncompressed */
cfd652ed 382
b59233e6 383 fn_compressed = NULL;
cfd652ed
ZJS
384 fd = -1;
385
386 return 0;
387
b59233e6
LP
388 fail_compressed:
389 unlink_noerrno(tmp_compressed);
34c10968 390 }
cfd652ed
ZJS
391
392uncompressed:
3b1a55e1 393#endif
cfd652ed
ZJS
394 r = fix_permissions(fd, tmp, fn, info, uid);
395 if (r < 0)
396 goto fail;
34c10968
LP
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
407fail:
408 unlink_noerrno(tmp);
409 return r;
410}
411
412static 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
8d4e028f 416 assert(fd >= 0);
34c10968
LP
417 assert(ret);
418 assert(ret_size);
419
4a62c710
MS
420 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
421 return log_warning_errno(errno, "Failed to seek: %m");
803a3464 422
34c10968
LP
423 field = malloc(9 + size);
424 if (!field) {
cfd652ed 425 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
34c10968
LP
426 return -ENOMEM;
427 }
428
429 memcpy(field, "COREDUMP=", 9);
430
431 n = read(fd, field + 9, size);
23bbb0de
MS
432 if (n < 0)
433 return log_error_errno((int) n, "Failed to read core data: %m");
34c10968
LP
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}
803a3464 446
3f132692
JF
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 */
461static int compose_open_fds(pid_t pid, char **open_fds) {
4d84bc2f
LP
462 _cleanup_closedir_ DIR *proc_fd_dir = NULL;
463 _cleanup_close_ int proc_fdinfo_fd = -1;
464 _cleanup_free_ char *buffer = NULL;
3f132692 465 _cleanup_fclose_ FILE *stream = NULL;
59059b4a 466 const char *fddelim = "", *path;
3f132692 467 struct dirent *dent = NULL;
4d84bc2f 468 size_t size = 0;
3f132692
JF
469 int r = 0;
470
471 assert(pid >= 0);
472 assert(open_fds != NULL);
473
59059b4a 474 path = procfs_file_alloca(pid, "fd");
3f132692 475 proc_fd_dir = opendir(path);
59059b4a
ZJS
476 if (!proc_fd_dir)
477 return -errno;
3f132692 478
4d84bc2f 479 proc_fdinfo_fd = openat(dirfd(proc_fd_dir), "../fdinfo", O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC|O_PATH);
59059b4a
ZJS
480 if (proc_fdinfo_fd < 0)
481 return -errno;
3f132692 482
4d84bc2f 483 stream = open_memstream(&buffer, &size);
3f132692
JF
484 if (!stream)
485 return -ENOMEM;
486
4d84bc2f 487 FOREACH_DIRENT(dent, proc_fd_dir, return -errno) {
3f132692 488 _cleanup_fclose_ FILE *fdinfo = NULL;
4d84bc2f 489 _cleanup_free_ char *fdname = NULL;
59059b4a 490 char line[LINE_MAX];
4d84bc2f 491 int fd;
3f132692 492
59059b4a 493 r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname);
3f132692
JF
494 if (r < 0)
495 return r;
496
497 fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname);
498 fddelim = "\n";
499
500 /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */
59059b4a
ZJS
501 fd = openat(proc_fdinfo_fd, dent->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY);
502 if (fd < 0)
3f132692
JF
503 continue;
504
59059b4a
ZJS
505 fdinfo = fdopen(fd, "re");
506 if (fdinfo == NULL) {
507 close(fd);
3f132692 508 continue;
59059b4a 509 }
3f132692 510
4d84bc2f
LP
511 FOREACH_LINE(line, fdinfo, break) {
512 fputs(line, stream);
513 if (!endswith(line, "\n"))
514 fputc('\n', stream);
515 }
3f132692
JF
516 }
517
4d84bc2f
LP
518 errno = 0;
519 fclose(stream);
520 stream = NULL;
521
522 if (errno != 0)
523 return -errno;
524
525 *open_fds = buffer;
526 buffer = NULL;
527
3f132692
JF
528 return 0;
529}
530
f5e04665 531int main(int argc, char* argv[]) {
34c10968
LP
532
533 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
534 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
535 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
3f132692
JF
536 *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL, *core_open_fds = NULL,
537 *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL,
538 *core_cwd = NULL, *core_root = NULL, *core_environ = NULL,
9fe13294 539 *exe = NULL, *comm = NULL, *filename = NULL;
1eef15b1 540 const char *info[_INFO_LEN];
34c10968
LP
541
542 _cleanup_close_ int coredump_fd = -1;
543
3f132692 544 struct iovec iovec[26];
34c10968 545 off_t coredump_size;
f5e04665 546 int r, j = 0;
a035f819 547 uid_t uid, owner_uid;
fee80f69 548 gid_t gid;
a035f819 549 pid_t pid;
34c10968 550 char *t;
3f132692 551 const char *p;
f5e04665 552
34c10968 553 /* Make sure we never enter a loop */
803a3464 554 prctl(PR_SET_DUMPABLE, 0);
f5e04665 555
34c10968
LP
556 /* First, log to a safe place, since we don't know what
557 * crashed and it might be journald which we'd rather not log
558 * to then. */
559 log_set_target(LOG_TARGET_KMSG);
34c10968 560 log_open();
803a3464 561
1eef15b1
ZJS
562 if (argc < INFO_COMM + 1) {
563 log_error("Not enough arguments passed from kernel (%d, expected %d).",
564 argc - 1, INFO_COMM + 1 - 1);
f5e04665
LP
565 r = -EINVAL;
566 goto finish;
567 }
568
34c10968
LP
569 /* Ignore all parse errors */
570 parse_config();
8c9571d0
LP
571
572 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
573 log_debug("Selected compression %s.", yes_no(arg_compress));
34c10968 574
1eef15b1 575 r = parse_uid(argv[INFO_UID + 1], &uid);
f5e04665 576 if (r < 0) {
34c10968
LP
577 log_error("Failed to parse UID.");
578 goto finish;
579 }
803a3464 580
1eef15b1 581 r = parse_pid(argv[INFO_PID + 1], &pid);
34c10968 582 if (r < 0) {
f5e04665 583 log_error("Failed to parse PID.");
f5e04665
LP
584 goto finish;
585 }
586
1eef15b1 587 r = parse_gid(argv[INFO_GID + 1], &gid);
34c10968
LP
588 if (r < 0) {
589 log_error("Failed to parse GID.");
590 goto finish;
591 }
592
1eef15b1 593 if (get_process_comm(pid, &comm) < 0) {
3f85ef0f 594 log_warning("Failed to get COMM, falling back to the command line.");
1eef15b1
ZJS
595 comm = strv_join(argv + INFO_COMM + 1, " ");
596 }
597
598 if (get_process_exe(pid, &exe) < 0)
599 log_warning("Failed to get EXE.");
600
601 info[INFO_PID] = argv[INFO_PID + 1];
602 info[INFO_UID] = argv[INFO_UID + 1];
603 info[INFO_GID] = argv[INFO_GID + 1];
604 info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
605 info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
606 info[INFO_COMM] = comm;
607 info[INFO_EXE] = exe;
608
ba1261bc 609 if (cg_pid_get_unit(pid, &t) >= 0) {
803a3464
LP
610
611 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
803a3464 612
34c10968
LP
613 /* If we are journald, we cut things short,
614 * don't write to the journal, but still
615 * create a coredump. */
616
617 if (arg_storage != COREDUMP_STORAGE_NONE)
618 arg_storage = COREDUMP_STORAGE_EXTERNAL;
619
9fe13294 620 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
34c10968
LP
621 if (r < 0)
622 goto finish;
623
9fe13294 624 r = maybe_remove_external_coredump(filename, coredump_size);
34c10968
LP
625 if (r < 0)
626 goto finish;
627
9fe13294 628 log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
803a3464
LP
629 goto finish;
630 }
631
632 core_unit = strappend("COREDUMP_UNIT=", t);
b7f7c685 633 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
f9045468 634 core_unit = strappend("COREDUMP_USER_UNIT=", t);
803a3464 635
f9045468
MT
636 if (core_unit)
637 IOVEC_SET_STRING(iovec[j++], core_unit);
638
34c10968
LP
639 /* OK, now we know it's not the journal, hence we can make use
640 * of it now. */
803a3464
LP
641 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
642 log_open();
643
1eef15b1 644 core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
f5e04665
LP
645 if (core_pid)
646 IOVEC_SET_STRING(iovec[j++], core_pid);
647
1eef15b1 648 core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
f5e04665
LP
649 if (core_uid)
650 IOVEC_SET_STRING(iovec[j++], core_uid);
651
1eef15b1 652 core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
f5e04665
LP
653 if (core_gid)
654 IOVEC_SET_STRING(iovec[j++], core_gid);
655
1eef15b1 656 core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
f5e04665
LP
657 if (core_signal)
658 IOVEC_SET_STRING(iovec[j++], core_signal);
659
f5e04665
LP
660 if (sd_pid_get_session(pid, &t) >= 0) {
661 core_session = strappend("COREDUMP_SESSION=", t);
662 free(t);
663
664 if (core_session)
665 IOVEC_SET_STRING(iovec[j++], core_session);
666 }
667
a035f819 668 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
7de80bfe
KZ
669 r = asprintf(&core_owner_uid,
670 "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
671 if (r > 0)
a035f819
LP
672 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
673 }
674
675 if (sd_pid_get_slice(pid, &t) >= 0) {
676 core_slice = strappend("COREDUMP_SLICE=", t);
677 free(t);
678
679 if (core_slice)
680 IOVEC_SET_STRING(iovec[j++], core_slice);
681 }
682
1eef15b1
ZJS
683 if (comm) {
684 core_comm = strappend("COREDUMP_COMM=", comm);
685 if (core_comm)
686 IOVEC_SET_STRING(iovec[j++], core_comm);
687 }
688
689 if (exe) {
8d4e028f 690 core_exe = strappend("COREDUMP_EXE=", exe);
f5e04665
LP
691 if (core_exe)
692 IOVEC_SET_STRING(iovec[j++], core_exe);
693 }
694
9bdbc2e2 695 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
f5e04665
LP
696 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
697 free(t);
698
699 if (core_cmdline)
700 IOVEC_SET_STRING(iovec[j++], core_cmdline);
701 }
702
a035f819
LP
703 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
704 core_cgroup = strappend("COREDUMP_CGROUP=", t);
705 free(t);
706
707 if (core_cgroup)
708 IOVEC_SET_STRING(iovec[j++], core_cgroup);
709 }
710
3f132692
JF
711 if (compose_open_fds(pid, &t) >= 0) {
712 core_open_fds = strappend("COREDUMP_OPEN_FDS=", t);
713 free(t);
714
715 if (core_open_fds)
716 IOVEC_SET_STRING(iovec[j++], core_open_fds);
717 }
718
719 p = procfs_file_alloca(pid, "status");
720 if (read_full_file(p, &t, NULL) >= 0) {
721 core_proc_status = strappend("COREDUMP_PROC_STATUS=", t);
722 free(t);
723
724 if (core_proc_status)
725 IOVEC_SET_STRING(iovec[j++], core_proc_status);
726 }
727
728 p = procfs_file_alloca(pid, "maps");
729 if (read_full_file(p, &t, NULL) >= 0) {
730 core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t);
731 free(t);
732
733 if (core_proc_maps)
734 IOVEC_SET_STRING(iovec[j++], core_proc_maps);
735 }
736
737 p = procfs_file_alloca(pid, "limits");
738 if (read_full_file(p, &t, NULL) >= 0) {
739 core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t);
740 free(t);
741
742 if (core_proc_limits)
743 IOVEC_SET_STRING(iovec[j++], core_proc_limits);
744 }
745
746 p = procfs_file_alloca(pid, "cgroup");
747 if (read_full_file(p, &t, NULL) >=0) {
748 core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t);
749 free(t);
750
751 if (core_proc_cgroup)
752 IOVEC_SET_STRING(iovec[j++], core_proc_cgroup);
753 }
754
755 if (get_process_cwd(pid, &t) >= 0) {
756 core_cwd = strappend("COREDUMP_CWD=", t);
757 free(t);
758
759 if (core_cwd)
760 IOVEC_SET_STRING(iovec[j++], core_cwd);
761 }
762
763 if (get_process_root(pid, &t) >= 0) {
764 core_root = strappend("COREDUMP_ROOT=", t);
765 free(t);
766
767 if (core_root)
768 IOVEC_SET_STRING(iovec[j++], core_root);
769 }
770
771 if (get_process_environ(pid, &t) >= 0) {
772 core_environ = strappend("COREDUMP_ENVIRON=", t);
773 free(t);
774
775 if (core_environ)
776 IOVEC_SET_STRING(iovec[j++], core_environ);
777 }
778
1eef15b1 779 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
f5e04665
LP
780 if (core_timestamp)
781 IOVEC_SET_STRING(iovec[j++], core_timestamp);
782
783 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
784 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
785
0dc5d23c
LP
786 /* Vacuum before we write anything again */
787 coredump_vacuum(-1, arg_keep_free, arg_max_use);
788
34c10968 789 /* Always stream the coredump to disk, if that's possible */
9fe13294 790 r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
34c10968 791 if (r < 0)
2424a475
ZJS
792 /* skip whole core dumping part */
793 goto log;
34c10968
LP
794
795 /* If we don't want to keep the coredump on disk, remove it
8d4e028f
LP
796 * now, as later on we will lack the privileges for
797 * it. However, we keep the fd to it, so that we can still
798 * process it and log it. */
9fe13294 799 r = maybe_remove_external_coredump(filename, coredump_size);
34c10968
LP
800 if (r < 0)
801 goto finish;
9fe13294
ZJS
802 if (r == 0) {
803 const char *coredump_filename;
804
805 coredump_filename = strappenda("COREDUMP_FILENAME=", filename);
806 IOVEC_SET_STRING(iovec[j++], coredump_filename);
807 }
34c10968 808
0dc5d23c
LP
809 /* Vacuum again, but exclude the coredump we just created */
810 coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);
811
fee80f69
LP
812 /* Now, let's drop privileges to become the user who owns the
813 * segfaulted process and allocate the coredump memory under
b26c631a
KP
814 * the user's uid. This also ensures that the credentials
815 * journald will see are the ones of the coredumping user,
816 * thus making sure the user gets access to the core dump. */
fee80f69
LP
817 if (setresgid(gid, gid, gid) < 0 ||
818 setresuid(uid, uid, uid) < 0) {
56f64d95 819 log_error_errno(errno, "Failed to drop privileges: %m");
fee80f69
LP
820 r = -errno;
821 goto finish;
822 }
823
8d4e028f
LP
824#ifdef HAVE_ELFUTILS
825 /* Try to get a strack trace if we can */
826 if (coredump_size <= arg_process_size_max) {
827 _cleanup_free_ char *stacktrace = NULL;
828
829 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
830 if (r >= 0)
1eef15b1 831 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
4d229b31
UTL
832 else if (r == -EINVAL)
833 log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
8d4e028f 834 else
da927ba9 835 log_warning_errno(r, "Failed to generate stack trace: %m");
8d4e028f
LP
836 }
837
838 if (!core_message)
839#endif
2424a475 840log:
1eef15b1 841 core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
8d4e028f
LP
842 if (core_message)
843 IOVEC_SET_STRING(iovec[j++], core_message);
844
34c10968
LP
845 /* Optionally store the entire coredump in the journal */
846 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
847 coredump_size <= (off_t) arg_journal_size_max) {
848 size_t sz;
fee80f69 849
34c10968 850 /* Store the coredump itself in the journal */
fee80f69 851
34c10968
LP
852 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
853 if (r >= 0) {
854 iovec[j].iov_base = coredump_data;
855 iovec[j].iov_len = sz;
856 j++;
ca0ceb6f 857 }
fee80f69
LP
858 }
859
f5e04665
LP
860 r = sd_journal_sendv(iovec, j);
861 if (r < 0)
da927ba9 862 log_error_errno(r, "Failed to log coredump: %m");
f5e04665
LP
863
864finish:
f5e04665
LP
865 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
866}