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