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