]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/coredump.c
coredump: include stacktrace of coredumps in the log message
[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
LP
26#include <sys/types.h>
27#include <attr/xattr.h>
f5e04665
LP
28
29#include <systemd/sd-journal.h>
30#include <systemd/sd-login.h>
31
32#include "log.h"
33#include "util.h"
b7f7c685 34#include "macro.h"
49e942b2 35#include "mkdir.h"
803a3464 36#include "special.h"
ba1261bc 37#include "cgroup-util.h"
d18d46ec 38#include "journald-native.h"
34c10968
LP
39#include "conf-parser.h"
40#include "copy.h"
8d4e028f 41#include "stacktrace.h"
34c10968
LP
42
43#ifdef HAVE_ACL
44#include <sys/acl.h>
45#include "acl-util.h"
46#endif
47
48/* The maximum size up to which we process coredumps */
49#define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
50
51/* The maximum size up to which we leave the coredump around on
52 * disk */
53#define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
54
55/* The maximum size up to which we store the coredump in the
56 * journal */
57#define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
f5e04665 58
c4aa09b0
LP
59/* Make sure to not make this larger than the maximum journal entry
60 * size. See ENTRY_SIZE_MAX in journald-native.c. */
34c10968 61assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
f5e04665
LP
62
63enum {
64 ARG_PID = 1,
65 ARG_UID,
66 ARG_GID,
67 ARG_SIGNAL,
68 ARG_TIMESTAMP,
69 ARG_COMM,
70 _ARG_MAX
71};
72
34c10968
LP
73typedef enum CoredumpStorage {
74 COREDUMP_STORAGE_NONE,
75 COREDUMP_STORAGE_EXTERNAL,
76 COREDUMP_STORAGE_JOURNAL,
77 COREDUMP_STORAGE_BOTH,
78 _COREDUMP_STORAGE_MAX,
79 _COREDUMP_STORAGE_INVALID = -1
80} CoredumpStorage;
81
82static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
83static off_t arg_process_size_max = PROCESS_SIZE_MAX;
84static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
85static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
86
87static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
88 [COREDUMP_STORAGE_NONE] = "none",
89 [COREDUMP_STORAGE_EXTERNAL] = "external",
90 [COREDUMP_STORAGE_JOURNAL] = "journal",
91 [COREDUMP_STORAGE_BOTH] = "both",
92};
93
94DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
95static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
96
97static int parse_config(void) {
98
99 static const ConfigTableItem items[] = {
100 { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max },
101 { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max },
102 { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
103 { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
104 {}
105 };
106
107 return config_parse(
108 NULL,
109 "/etc/systemd/coredump.conf",
110 NULL,
111 "Coredump\0",
112 config_item_table_lookup,
113 (void*) items,
114 false,
115 false,
116 NULL);
117}
118
119static int fix_acl(int fd, uid_t uid) {
120
121#ifdef HAVE_ACL
122 _cleanup_(acl_freep) acl_t acl = NULL;
123 acl_entry_t entry;
124 acl_permset_t permset;
125
126 if (uid <= SYSTEM_UID_MAX)
127 return 0;
128
129 /* Make sure normal users can read (but not write or delete)
130 * their own coredumps */
131
132 acl = acl_get_fd(fd);
133 if (!acl) {
134 log_error("Failed to get ACL: %m");
135 return -errno;
136 }
137
138 if (acl_create_entry(&acl, &entry) < 0 ||
139 acl_set_tag_type(entry, ACL_USER) < 0 ||
140 acl_set_qualifier(entry, &uid) < 0) {
141 log_error("Failed to patch ACL: %m");
142 return -errno;
143 }
144
145 if (acl_get_permset(entry, &permset) < 0 ||
146 acl_add_perm(permset, ACL_READ) < 0 ||
147 calc_acl_mask_if_needed(&acl) < 0) {
148 log_warning("Failed to patch ACL: %m");
149 return -errno;
150 }
151
152 if (acl_set_fd(fd, acl) < 0) {
153 log_error("Failed to apply ACL: %m");
154 return -errno;
155 }
156#endif
157
158 return 0;
159}
160
161static int fix_xattr(int fd, char *argv[]) {
162 int r = 0;
163
164 /* Attach some metadate to coredumps via extended
165 * attributes. Just because we can. */
166
167 if (!isempty(argv[ARG_PID]))
168 if (fsetxattr(fd, "user.coredump.pid", argv[ARG_PID], strlen(argv[ARG_PID]), XATTR_CREATE) < 0)
169 r = -errno;
170
171 if (!isempty(argv[ARG_UID]))
172 if (fsetxattr(fd, "user.coredump.uid", argv[ARG_UID], strlen(argv[ARG_UID]), XATTR_CREATE) < 0)
173 r = -errno;
174
175 if (!isempty(argv[ARG_GID]))
176 if (fsetxattr(fd, "user.coredump.gid", argv[ARG_GID], strlen(argv[ARG_GID]), XATTR_CREATE) < 0)
177 r = -errno;
178
179 if (!isempty(argv[ARG_SIGNAL]))
180 if (fsetxattr(fd, "user.coredump.signal", argv[ARG_SIGNAL], strlen(argv[ARG_SIGNAL]), XATTR_CREATE) < 0)
181 r = -errno;
182
183 if (!isempty(argv[ARG_TIMESTAMP]))
184 if (fsetxattr(fd, "user.coredump.timestamp", argv[ARG_TIMESTAMP], strlen(argv[ARG_TIMESTAMP]), XATTR_CREATE) < 0)
185 r = -errno;
803a3464 186
34c10968
LP
187 if (!isempty(argv[ARG_COMM]))
188 if (fsetxattr(fd, "user.coredump.comm", argv[ARG_COMM], strlen(argv[ARG_COMM]), XATTR_CREATE) < 0)
189 r = -errno;
190
191 return r;
192}
193
194#define filename_escape(s) xescape((s), "./")
195
196static int save_external_coredump(char **argv, uid_t uid, char **ret_filename, int *ret_fd, off_t *ret_size) {
197 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
198 _cleanup_close_ int fd = -1;
199 sd_id128_t boot;
200 struct stat st;
201 int r;
202
203 assert(argv);
204 assert(ret_filename);
205 assert(ret_fd);
206 assert(ret_size);
207
208 c = filename_escape(argv[ARG_COMM]);
209 if (!c)
210 return log_oom();
211
212 p = filename_escape(argv[ARG_PID]);
213 if (!p)
214 return log_oom();
215
216 t = filename_escape(argv[ARG_TIMESTAMP]);
217 if (!t)
218 return log_oom();
219
220 r = sd_id128_get_boot(&boot);
221 if (r < 0) {
222 log_error("Failed to determine boot ID: %s", strerror(-r));
223 return r;
224 }
225
226 r = asprintf(&fn,
227 "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
228 c,
229 SD_ID128_FORMAT_VAL(boot),
230 p,
231 t);
232 if (r < 0)
233 return log_oom();
234
235 tmp = tempfn_random(fn);
236 if (!tmp)
237 return log_oom();
803a3464 238
d2e54fae 239 mkdir_p_label("/var/lib/systemd/coredump", 0755);
803a3464 240
34c10968
LP
241 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
242 if (fd < 0) {
803a3464
LP
243 log_error("Failed to create coredump file: %m");
244 return -errno;
245 }
246
34c10968
LP
247 r = copy_bytes(STDIN_FILENO, fd);
248 if (r < 0) {
249 log_error("Failed to dump coredump to file: %s", strerror(-r));
250 goto fail;
251 }
803a3464 252
34c10968
LP
253 /* Ignore errors on these */
254 fchmod(fd, 0640);
255 fix_acl(fd, uid);
256 fix_xattr(fd, argv);
803a3464 257
34c10968
LP
258 if (fsync(fd) < 0) {
259 log_error("Failed to sync coredump: %m");
260 r = -errno;
261 goto fail;
262 }
803a3464 263
34c10968
LP
264 if (fstat(fd, &st) < 0) {
265 log_error("Failed to fstat coredump: %m");
266 r = -errno;
267 goto fail;
268 }
269
270 if (rename(tmp, fn) < 0) {
271 log_error("Failed to rename coredump: %m");
272 r = -errno;
273 goto fail;
274 }
275
276 *ret_filename = fn;
277 *ret_fd = fd;
278 *ret_size = st.st_size;
279
280 fn = NULL;
281 fd = -1;
282
283 return 0;
284
285fail:
286 unlink_noerrno(tmp);
287 return r;
288}
289
290static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
291 _cleanup_free_ char *field = NULL;
292 ssize_t n;
293
8d4e028f 294 assert(fd >= 0);
34c10968
LP
295 assert(ret);
296 assert(ret_size);
297
298 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
299 log_warning("Failed to seek: %m");
300 return -errno;
803a3464
LP
301 }
302
34c10968
LP
303 field = malloc(9 + size);
304 if (!field) {
305 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
306 return -ENOMEM;
307 }
308
309 memcpy(field, "COREDUMP=", 9);
310
311 n = read(fd, field + 9, size);
312 if (n < 0) {
313 log_error("Failed to read core data: %s", strerror(-n));
314 return (int) n;
315 }
316 if ((size_t) n < size) {
317 log_error("Core data too short.");
318 return -EIO;
319 }
320
321 *ret = field;
322 *ret_size = size + 9;
323
324 field = NULL;
325
326 return 0;
327}
803a3464 328
34c10968
LP
329static int maybe_remove_external_coredump(const char *filename, off_t size) {
330
331 if (!filename)
332 return 0;
333
334 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
335 size <= arg_external_size_max)
336 return 0;
337
338 if (unlink(filename) < 0) {
339 log_error("Failed to unlink %s: %m", filename);
b7f7c685 340 return -errno;
803a3464
LP
341 }
342
b7f7c685 343 return 0;
803a3464
LP
344}
345
f5e04665 346int main(int argc, char* argv[]) {
34c10968
LP
347
348 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
349 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
350 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
8d4e028f
LP
351 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
352 *exe = NULL;
34c10968
LP
353
354 _cleanup_close_ int coredump_fd = -1;
355
a035f819 356 struct iovec iovec[17];
34c10968 357 off_t coredump_size;
f5e04665 358 int r, j = 0;
a035f819 359 uid_t uid, owner_uid;
fee80f69 360 gid_t gid;
a035f819 361 pid_t pid;
34c10968 362 char *t;
f5e04665 363
34c10968 364 /* Make sure we never enter a loop */
803a3464 365 prctl(PR_SET_DUMPABLE, 0);
f5e04665 366
34c10968
LP
367 /* First, log to a safe place, since we don't know what
368 * crashed and it might be journald which we'd rather not log
369 * to then. */
370 log_set_target(LOG_TARGET_KMSG);
34c10968 371 log_open();
803a3464 372
34c10968 373 if (argc != _ARG_MAX) {
f5e04665
LP
374 log_error("Invalid number of arguments passed from kernel.");
375 r = -EINVAL;
376 goto finish;
377 }
378
34c10968
LP
379 /* Ignore all parse errors */
380 parse_config();
381 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
382
383 r = parse_uid(argv[ARG_UID], &uid);
f5e04665 384 if (r < 0) {
34c10968
LP
385 log_error("Failed to parse UID.");
386 goto finish;
387 }
803a3464 388
34c10968
LP
389 r = parse_pid(argv[ARG_PID], &pid);
390 if (r < 0) {
f5e04665 391 log_error("Failed to parse PID.");
f5e04665
LP
392 goto finish;
393 }
394
34c10968
LP
395 r = parse_gid(argv[ARG_GID], &gid);
396 if (r < 0) {
397 log_error("Failed to parse GID.");
398 goto finish;
399 }
400
ba1261bc 401 if (cg_pid_get_unit(pid, &t) >= 0) {
803a3464
LP
402
403 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
803a3464 404
34c10968
LP
405 /* If we are journald, we cut things short,
406 * don't write to the journal, but still
407 * create a coredump. */
408
409 if (arg_storage != COREDUMP_STORAGE_NONE)
410 arg_storage = COREDUMP_STORAGE_EXTERNAL;
411
412 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
413 if (r < 0)
414 goto finish;
415
416 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
417 if (r < 0)
418 goto finish;
419
420 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
803a3464
LP
421 goto finish;
422 }
423
424 core_unit = strappend("COREDUMP_UNIT=", t);
b7f7c685 425 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
f9045468 426 core_unit = strappend("COREDUMP_USER_UNIT=", t);
803a3464 427
f9045468
MT
428 if (core_unit)
429 IOVEC_SET_STRING(iovec[j++], core_unit);
430
34c10968
LP
431 /* OK, now we know it's not the journal, hence we can make use
432 * of it now. */
803a3464
LP
433 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
434 log_open();
435
f5e04665
LP
436 core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
437 if (core_pid)
438 IOVEC_SET_STRING(iovec[j++], core_pid);
439
440 core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
441 if (core_uid)
442 IOVEC_SET_STRING(iovec[j++], core_uid);
443
444 core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
445 if (core_gid)
446 IOVEC_SET_STRING(iovec[j++], core_gid);
447
448 core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
449 if (core_signal)
450 IOVEC_SET_STRING(iovec[j++], core_signal);
451
452 core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
453 if (core_comm)
454 IOVEC_SET_STRING(iovec[j++], core_comm);
455
456 if (sd_pid_get_session(pid, &t) >= 0) {
457 core_session = strappend("COREDUMP_SESSION=", t);
458 free(t);
459
460 if (core_session)
461 IOVEC_SET_STRING(iovec[j++], core_session);
462 }
463
a035f819
LP
464 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
465 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
466
467 if (core_owner_uid)
468 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
469 }
470
471 if (sd_pid_get_slice(pid, &t) >= 0) {
472 core_slice = strappend("COREDUMP_SLICE=", t);
473 free(t);
474
475 if (core_slice)
476 IOVEC_SET_STRING(iovec[j++], core_slice);
477 }
478
8d4e028f
LP
479 if (get_process_exe(pid, &exe) >= 0) {
480 core_exe = strappend("COREDUMP_EXE=", exe);
f5e04665
LP
481 if (core_exe)
482 IOVEC_SET_STRING(iovec[j++], core_exe);
483 }
484
9bdbc2e2 485 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
f5e04665
LP
486 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
487 free(t);
488
489 if (core_cmdline)
490 IOVEC_SET_STRING(iovec[j++], core_cmdline);
491 }
492
a035f819
LP
493 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
494 core_cgroup = strappend("COREDUMP_CGROUP=", t);
495 free(t);
496
497 if (core_cgroup)
498 IOVEC_SET_STRING(iovec[j++], core_cgroup);
499 }
500
b7def684 501 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
f5e04665
LP
502 if (core_timestamp)
503 IOVEC_SET_STRING(iovec[j++], core_timestamp);
504
505 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
506 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
507
34c10968
LP
508 /* Always stream the coredump to disk, if that's possible */
509 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
510 if (r < 0)
511 goto finish;
512
513 /* If we don't want to keep the coredump on disk, remove it
8d4e028f
LP
514 * now, as later on we will lack the privileges for
515 * it. However, we keep the fd to it, so that we can still
516 * process it and log it. */
34c10968
LP
517 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
518 if (r < 0)
519 goto finish;
520
fee80f69
LP
521 /* Now, let's drop privileges to become the user who owns the
522 * segfaulted process and allocate the coredump memory under
523 * his uid. This also ensures that the credentials journald
524 * will see are the ones of the coredumping user, thus making
525 * sure the user himself gets access to the core dump. */
fee80f69
LP
526 if (setresgid(gid, gid, gid) < 0 ||
527 setresuid(uid, uid, uid) < 0) {
528 log_error("Failed to drop privileges: %m");
529 r = -errno;
530 goto finish;
531 }
532
8d4e028f
LP
533#ifdef HAVE_ELFUTILS
534 /* Try to get a strack trace if we can */
535 if (coredump_size <= arg_process_size_max) {
536 _cleanup_free_ char *stacktrace = NULL;
537
538 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
539 if (r >= 0)
540 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.\n\n", stacktrace, NULL);
541 else
542 log_warning("Failed to generate stack trace: %s", strerror(-r));
543 }
544
545 if (!core_message)
546#endif
547 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.", NULL);
548 if (core_message)
549 IOVEC_SET_STRING(iovec[j++], core_message);
550
34c10968
LP
551 /* Optionally store the entire coredump in the journal */
552 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
553 coredump_size <= (off_t) arg_journal_size_max) {
554 size_t sz;
fee80f69 555
34c10968 556 /* Store the coredump itself in the journal */
fee80f69 557
34c10968
LP
558 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
559 if (r >= 0) {
560 iovec[j].iov_base = coredump_data;
561 iovec[j].iov_len = sz;
562 j++;
ca0ceb6f 563 }
fee80f69
LP
564 }
565
f5e04665
LP
566 r = sd_journal_sendv(iovec, j);
567 if (r < 0)
872c8faa 568 log_error("Failed to log coredump: %s", strerror(-r));
f5e04665
LP
569
570finish:
f5e04665
LP
571 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
572}