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