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