]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/coredump.c
fstab-generator: simplify add_root_mount
[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
93240d3a
LP
243 r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
244 if (r == -E2BIG) {
245 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", argv[ARG_PID], argv[ARG_COMM]);
246 goto fail;
247 } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
248 log_error("Not enough disk space for coredump of %s (%s), refusing.", argv[ARG_PID], argv[ARG_COMM]);
249 goto fail;
250 } else if (r < 0) {
34c10968
LP
251 log_error("Failed to dump coredump to file: %s", strerror(-r));
252 goto fail;
253 }
803a3464 254
34c10968
LP
255 /* Ignore errors on these */
256 fchmod(fd, 0640);
257 fix_acl(fd, uid);
258 fix_xattr(fd, argv);
803a3464 259
34c10968
LP
260 if (fsync(fd) < 0) {
261 log_error("Failed to sync coredump: %m");
262 r = -errno;
263 goto fail;
264 }
803a3464 265
34c10968
LP
266 if (fstat(fd, &st) < 0) {
267 log_error("Failed to fstat coredump: %m");
268 r = -errno;
269 goto fail;
270 }
271
272 if (rename(tmp, fn) < 0) {
273 log_error("Failed to rename coredump: %m");
274 r = -errno;
275 goto fail;
276 }
277
278 *ret_filename = fn;
279 *ret_fd = fd;
280 *ret_size = st.st_size;
281
282 fn = NULL;
283 fd = -1;
284
285 return 0;
286
287fail:
288 unlink_noerrno(tmp);
289 return r;
290}
291
292static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
293 _cleanup_free_ char *field = NULL;
294 ssize_t n;
295
8d4e028f 296 assert(fd >= 0);
34c10968
LP
297 assert(ret);
298 assert(ret_size);
299
300 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
301 log_warning("Failed to seek: %m");
302 return -errno;
803a3464
LP
303 }
304
34c10968
LP
305 field = malloc(9 + size);
306 if (!field) {
307 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
308 return -ENOMEM;
309 }
310
311 memcpy(field, "COREDUMP=", 9);
312
313 n = read(fd, field + 9, size);
314 if (n < 0) {
315 log_error("Failed to read core data: %s", strerror(-n));
316 return (int) n;
317 }
318 if ((size_t) n < size) {
319 log_error("Core data too short.");
320 return -EIO;
321 }
322
323 *ret = field;
324 *ret_size = size + 9;
325
326 field = NULL;
327
328 return 0;
329}
803a3464 330
34c10968
LP
331static int maybe_remove_external_coredump(const char *filename, off_t size) {
332
333 if (!filename)
334 return 0;
335
336 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
337 size <= arg_external_size_max)
338 return 0;
339
340 if (unlink(filename) < 0) {
341 log_error("Failed to unlink %s: %m", filename);
b7f7c685 342 return -errno;
803a3464
LP
343 }
344
b7f7c685 345 return 0;
803a3464
LP
346}
347
f5e04665 348int main(int argc, char* argv[]) {
34c10968
LP
349
350 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
351 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
352 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
8d4e028f
LP
353 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
354 *exe = NULL;
34c10968
LP
355
356 _cleanup_close_ int coredump_fd = -1;
357
a035f819 358 struct iovec iovec[17];
34c10968 359 off_t coredump_size;
f5e04665 360 int r, j = 0;
a035f819 361 uid_t uid, owner_uid;
fee80f69 362 gid_t gid;
a035f819 363 pid_t pid;
34c10968 364 char *t;
f5e04665 365
34c10968 366 /* Make sure we never enter a loop */
803a3464 367 prctl(PR_SET_DUMPABLE, 0);
f5e04665 368
34c10968
LP
369 /* First, log to a safe place, since we don't know what
370 * crashed and it might be journald which we'd rather not log
371 * to then. */
372 log_set_target(LOG_TARGET_KMSG);
34c10968 373 log_open();
803a3464 374
34c10968 375 if (argc != _ARG_MAX) {
f5e04665
LP
376 log_error("Invalid number of arguments passed from kernel.");
377 r = -EINVAL;
378 goto finish;
379 }
380
34c10968
LP
381 /* Ignore all parse errors */
382 parse_config();
383 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
384
6388c315
LP
385 /* Exit early if we cannot write the coredump to disk anyway */
386 if (path_is_read_only_fs("/var/lib") != 0) {
387 log_error("Coredump directory not mounted or not writable, skipping coredump.");
388 r = -EROFS;
389 goto finish;
390 }
391
34c10968 392 r = parse_uid(argv[ARG_UID], &uid);
f5e04665 393 if (r < 0) {
34c10968
LP
394 log_error("Failed to parse UID.");
395 goto finish;
396 }
803a3464 397
34c10968
LP
398 r = parse_pid(argv[ARG_PID], &pid);
399 if (r < 0) {
f5e04665 400 log_error("Failed to parse PID.");
f5e04665
LP
401 goto finish;
402 }
403
34c10968
LP
404 r = parse_gid(argv[ARG_GID], &gid);
405 if (r < 0) {
406 log_error("Failed to parse GID.");
407 goto finish;
408 }
409
ba1261bc 410 if (cg_pid_get_unit(pid, &t) >= 0) {
803a3464
LP
411
412 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
803a3464 413
34c10968
LP
414 /* If we are journald, we cut things short,
415 * don't write to the journal, but still
416 * create a coredump. */
417
418 if (arg_storage != COREDUMP_STORAGE_NONE)
419 arg_storage = COREDUMP_STORAGE_EXTERNAL;
420
421 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
422 if (r < 0)
423 goto finish;
424
425 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
426 if (r < 0)
427 goto finish;
428
429 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
803a3464
LP
430 goto finish;
431 }
432
433 core_unit = strappend("COREDUMP_UNIT=", t);
b7f7c685 434 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
f9045468 435 core_unit = strappend("COREDUMP_USER_UNIT=", t);
803a3464 436
f9045468
MT
437 if (core_unit)
438 IOVEC_SET_STRING(iovec[j++], core_unit);
439
34c10968
LP
440 /* OK, now we know it's not the journal, hence we can make use
441 * of it now. */
803a3464
LP
442 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
443 log_open();
444
f5e04665
LP
445 core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
446 if (core_pid)
447 IOVEC_SET_STRING(iovec[j++], core_pid);
448
449 core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
450 if (core_uid)
451 IOVEC_SET_STRING(iovec[j++], core_uid);
452
453 core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
454 if (core_gid)
455 IOVEC_SET_STRING(iovec[j++], core_gid);
456
457 core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
458 if (core_signal)
459 IOVEC_SET_STRING(iovec[j++], core_signal);
460
461 core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
462 if (core_comm)
463 IOVEC_SET_STRING(iovec[j++], core_comm);
464
465 if (sd_pid_get_session(pid, &t) >= 0) {
466 core_session = strappend("COREDUMP_SESSION=", t);
467 free(t);
468
469 if (core_session)
470 IOVEC_SET_STRING(iovec[j++], core_session);
471 }
472
a035f819
LP
473 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
474 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
475
476 if (core_owner_uid)
477 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
478 }
479
480 if (sd_pid_get_slice(pid, &t) >= 0) {
481 core_slice = strappend("COREDUMP_SLICE=", t);
482 free(t);
483
484 if (core_slice)
485 IOVEC_SET_STRING(iovec[j++], core_slice);
486 }
487
8d4e028f
LP
488 if (get_process_exe(pid, &exe) >= 0) {
489 core_exe = strappend("COREDUMP_EXE=", exe);
f5e04665
LP
490 if (core_exe)
491 IOVEC_SET_STRING(iovec[j++], core_exe);
492 }
493
9bdbc2e2 494 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
f5e04665
LP
495 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
496 free(t);
497
498 if (core_cmdline)
499 IOVEC_SET_STRING(iovec[j++], core_cmdline);
500 }
501
a035f819
LP
502 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
503 core_cgroup = strappend("COREDUMP_CGROUP=", t);
504 free(t);
505
506 if (core_cgroup)
507 IOVEC_SET_STRING(iovec[j++], core_cgroup);
508 }
509
b7def684 510 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
f5e04665
LP
511 if (core_timestamp)
512 IOVEC_SET_STRING(iovec[j++], core_timestamp);
513
514 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
515 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
516
34c10968
LP
517 /* Always stream the coredump to disk, if that's possible */
518 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
519 if (r < 0)
520 goto finish;
521
522 /* If we don't want to keep the coredump on disk, remove it
8d4e028f
LP
523 * now, as later on we will lack the privileges for
524 * it. However, we keep the fd to it, so that we can still
525 * process it and log it. */
34c10968
LP
526 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
527 if (r < 0)
528 goto finish;
529
fee80f69
LP
530 /* Now, let's drop privileges to become the user who owns the
531 * segfaulted process and allocate the coredump memory under
532 * his uid. This also ensures that the credentials journald
533 * will see are the ones of the coredumping user, thus making
534 * sure the user himself gets access to the core dump. */
fee80f69
LP
535 if (setresgid(gid, gid, gid) < 0 ||
536 setresuid(uid, uid, uid) < 0) {
537 log_error("Failed to drop privileges: %m");
538 r = -errno;
539 goto finish;
540 }
541
8d4e028f
LP
542#ifdef HAVE_ELFUTILS
543 /* Try to get a strack trace if we can */
544 if (coredump_size <= arg_process_size_max) {
545 _cleanup_free_ char *stacktrace = NULL;
546
547 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
548 if (r >= 0)
549 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.\n\n", stacktrace, NULL);
550 else
551 log_warning("Failed to generate stack trace: %s", strerror(-r));
552 }
553
554 if (!core_message)
555#endif
556 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.", NULL);
557 if (core_message)
558 IOVEC_SET_STRING(iovec[j++], core_message);
559
34c10968
LP
560 /* Optionally store the entire coredump in the journal */
561 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
562 coredump_size <= (off_t) arg_journal_size_max) {
563 size_t sz;
fee80f69 564
34c10968 565 /* Store the coredump itself in the journal */
fee80f69 566
34c10968
LP
567 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
568 if (r >= 0) {
569 iovec[j].iov_base = coredump_data;
570 iovec[j].iov_len = sz;
571 j++;
ca0ceb6f 572 }
fee80f69
LP
573 }
574
f5e04665
LP
575 r = sd_journal_sendv(iovec, j);
576 if (r < 0)
872c8faa 577 log_error("Failed to log coredump: %s", strerror(-r));
f5e04665
LP
578
579finish:
f5e04665
LP
580 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
581}