]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/coredump.c
README: add gobject-introspection
[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"
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[]) {
0cd77f97
LP
162
163 static const char * const xattrs[_ARG_MAX] = {
164 [ARG_PID] = "user.coredump.pid",
165 [ARG_UID] = "user.coredump.uid",
166 [ARG_GID] = "user.coredump.gid",
167 [ARG_SIGNAL] = "user.coredump.signal",
168 [ARG_TIMESTAMP] = "user.coredump.timestamp",
169 [ARG_COMM] = "user.coredump.comm",
170 };
171
34c10968 172 int r = 0;
0cd77f97 173 unsigned i;
34c10968
LP
174
175 /* Attach some metadate to coredumps via extended
176 * attributes. Just because we can. */
177
0cd77f97
LP
178 for (i = 0; i < _ARG_MAX; i++) {
179 if (isempty(argv[i]))
180 continue;
34c10968 181
0cd77f97 182 if (fsetxattr(fd, xattrs[i], argv[i], strlen(argv[i]), XATTR_CREATE) < 0)
34c10968 183 r = -errno;
0cd77f97 184 }
34c10968
LP
185
186 return r;
187}
188
189#define filename_escape(s) xescape((s), "./")
190
191static int save_external_coredump(char **argv, uid_t uid, char **ret_filename, int *ret_fd, off_t *ret_size) {
192 _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
193 _cleanup_close_ int fd = -1;
194 sd_id128_t boot;
195 struct stat st;
196 int r;
197
198 assert(argv);
199 assert(ret_filename);
200 assert(ret_fd);
201 assert(ret_size);
202
203 c = filename_escape(argv[ARG_COMM]);
204 if (!c)
205 return log_oom();
206
207 p = filename_escape(argv[ARG_PID]);
208 if (!p)
209 return log_oom();
210
211 t = filename_escape(argv[ARG_TIMESTAMP]);
212 if (!t)
213 return log_oom();
214
215 r = sd_id128_get_boot(&boot);
216 if (r < 0) {
217 log_error("Failed to determine boot ID: %s", strerror(-r));
218 return r;
219 }
220
221 r = asprintf(&fn,
222 "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
223 c,
224 SD_ID128_FORMAT_VAL(boot),
225 p,
226 t);
227 if (r < 0)
228 return log_oom();
229
230 tmp = tempfn_random(fn);
231 if (!tmp)
232 return log_oom();
803a3464 233
d2e54fae 234 mkdir_p_label("/var/lib/systemd/coredump", 0755);
803a3464 235
34c10968
LP
236 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
237 if (fd < 0) {
803a3464
LP
238 log_error("Failed to create coredump file: %m");
239 return -errno;
240 }
241
34c10968
LP
242 r = copy_bytes(STDIN_FILENO, fd);
243 if (r < 0) {
244 log_error("Failed to dump coredump to file: %s", strerror(-r));
245 goto fail;
246 }
803a3464 247
34c10968
LP
248 /* Ignore errors on these */
249 fchmod(fd, 0640);
250 fix_acl(fd, uid);
251 fix_xattr(fd, argv);
803a3464 252
34c10968
LP
253 if (fsync(fd) < 0) {
254 log_error("Failed to sync coredump: %m");
255 r = -errno;
256 goto fail;
257 }
803a3464 258
34c10968
LP
259 if (fstat(fd, &st) < 0) {
260 log_error("Failed to fstat coredump: %m");
261 r = -errno;
262 goto fail;
263 }
264
265 if (rename(tmp, fn) < 0) {
266 log_error("Failed to rename coredump: %m");
267 r = -errno;
268 goto fail;
269 }
270
271 *ret_filename = fn;
272 *ret_fd = fd;
273 *ret_size = st.st_size;
274
275 fn = NULL;
276 fd = -1;
277
278 return 0;
279
280fail:
281 unlink_noerrno(tmp);
282 return r;
283}
284
285static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
286 _cleanup_free_ char *field = NULL;
287 ssize_t n;
288
8d4e028f 289 assert(fd >= 0);
34c10968
LP
290 assert(ret);
291 assert(ret_size);
292
293 if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
294 log_warning("Failed to seek: %m");
295 return -errno;
803a3464
LP
296 }
297
34c10968
LP
298 field = malloc(9 + size);
299 if (!field) {
300 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
301 return -ENOMEM;
302 }
303
304 memcpy(field, "COREDUMP=", 9);
305
306 n = read(fd, field + 9, size);
307 if (n < 0) {
308 log_error("Failed to read core data: %s", strerror(-n));
309 return (int) n;
310 }
311 if ((size_t) n < size) {
312 log_error("Core data too short.");
313 return -EIO;
314 }
315
316 *ret = field;
317 *ret_size = size + 9;
318
319 field = NULL;
320
321 return 0;
322}
803a3464 323
34c10968
LP
324static int maybe_remove_external_coredump(const char *filename, off_t size) {
325
326 if (!filename)
327 return 0;
328
329 if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
330 size <= arg_external_size_max)
331 return 0;
332
333 if (unlink(filename) < 0) {
334 log_error("Failed to unlink %s: %m", filename);
b7f7c685 335 return -errno;
803a3464
LP
336 }
337
b7f7c685 338 return 0;
803a3464
LP
339}
340
f5e04665 341int main(int argc, char* argv[]) {
34c10968
LP
342
343 _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
344 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
345 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
8d4e028f
LP
346 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
347 *exe = NULL;
34c10968
LP
348
349 _cleanup_close_ int coredump_fd = -1;
350
a035f819 351 struct iovec iovec[17];
34c10968 352 off_t coredump_size;
f5e04665 353 int r, j = 0;
a035f819 354 uid_t uid, owner_uid;
fee80f69 355 gid_t gid;
a035f819 356 pid_t pid;
34c10968 357 char *t;
f5e04665 358
34c10968 359 /* Make sure we never enter a loop */
803a3464 360 prctl(PR_SET_DUMPABLE, 0);
f5e04665 361
34c10968
LP
362 /* First, log to a safe place, since we don't know what
363 * crashed and it might be journald which we'd rather not log
364 * to then. */
365 log_set_target(LOG_TARGET_KMSG);
34c10968 366 log_open();
803a3464 367
34c10968 368 if (argc != _ARG_MAX) {
f5e04665
LP
369 log_error("Invalid number of arguments passed from kernel.");
370 r = -EINVAL;
371 goto finish;
372 }
373
34c10968
LP
374 /* Ignore all parse errors */
375 parse_config();
376 log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
377
378 r = parse_uid(argv[ARG_UID], &uid);
f5e04665 379 if (r < 0) {
34c10968
LP
380 log_error("Failed to parse UID.");
381 goto finish;
382 }
803a3464 383
34c10968
LP
384 r = parse_pid(argv[ARG_PID], &pid);
385 if (r < 0) {
f5e04665 386 log_error("Failed to parse PID.");
f5e04665
LP
387 goto finish;
388 }
389
34c10968
LP
390 r = parse_gid(argv[ARG_GID], &gid);
391 if (r < 0) {
392 log_error("Failed to parse GID.");
393 goto finish;
394 }
395
ba1261bc 396 if (cg_pid_get_unit(pid, &t) >= 0) {
803a3464
LP
397
398 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
803a3464 399
34c10968
LP
400 /* If we are journald, we cut things short,
401 * don't write to the journal, but still
402 * create a coredump. */
403
404 if (arg_storage != COREDUMP_STORAGE_NONE)
405 arg_storage = COREDUMP_STORAGE_EXTERNAL;
406
407 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
408 if (r < 0)
409 goto finish;
410
411 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
412 if (r < 0)
413 goto finish;
414
415 log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
803a3464
LP
416 goto finish;
417 }
418
419 core_unit = strappend("COREDUMP_UNIT=", t);
b7f7c685 420 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
f9045468 421 core_unit = strappend("COREDUMP_USER_UNIT=", t);
803a3464 422
f9045468
MT
423 if (core_unit)
424 IOVEC_SET_STRING(iovec[j++], core_unit);
425
34c10968
LP
426 /* OK, now we know it's not the journal, hence we can make use
427 * of it now. */
803a3464
LP
428 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
429 log_open();
430
f5e04665
LP
431 core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
432 if (core_pid)
433 IOVEC_SET_STRING(iovec[j++], core_pid);
434
435 core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
436 if (core_uid)
437 IOVEC_SET_STRING(iovec[j++], core_uid);
438
439 core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
440 if (core_gid)
441 IOVEC_SET_STRING(iovec[j++], core_gid);
442
443 core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
444 if (core_signal)
445 IOVEC_SET_STRING(iovec[j++], core_signal);
446
447 core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
448 if (core_comm)
449 IOVEC_SET_STRING(iovec[j++], core_comm);
450
451 if (sd_pid_get_session(pid, &t) >= 0) {
452 core_session = strappend("COREDUMP_SESSION=", t);
453 free(t);
454
455 if (core_session)
456 IOVEC_SET_STRING(iovec[j++], core_session);
457 }
458
a035f819
LP
459 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
460 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
461
462 if (core_owner_uid)
463 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
464 }
465
466 if (sd_pid_get_slice(pid, &t) >= 0) {
467 core_slice = strappend("COREDUMP_SLICE=", t);
468 free(t);
469
470 if (core_slice)
471 IOVEC_SET_STRING(iovec[j++], core_slice);
472 }
473
8d4e028f
LP
474 if (get_process_exe(pid, &exe) >= 0) {
475 core_exe = strappend("COREDUMP_EXE=", exe);
f5e04665
LP
476 if (core_exe)
477 IOVEC_SET_STRING(iovec[j++], core_exe);
478 }
479
9bdbc2e2 480 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
f5e04665
LP
481 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
482 free(t);
483
484 if (core_cmdline)
485 IOVEC_SET_STRING(iovec[j++], core_cmdline);
486 }
487
a035f819
LP
488 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
489 core_cgroup = strappend("COREDUMP_CGROUP=", t);
490 free(t);
491
492 if (core_cgroup)
493 IOVEC_SET_STRING(iovec[j++], core_cgroup);
494 }
495
b7def684 496 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
f5e04665
LP
497 if (core_timestamp)
498 IOVEC_SET_STRING(iovec[j++], core_timestamp);
499
500 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
501 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
502
34c10968
LP
503 /* Always stream the coredump to disk, if that's possible */
504 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
505 if (r < 0)
506 goto finish;
507
508 /* If we don't want to keep the coredump on disk, remove it
8d4e028f
LP
509 * now, as later on we will lack the privileges for
510 * it. However, we keep the fd to it, so that we can still
511 * process it and log it. */
34c10968
LP
512 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
513 if (r < 0)
514 goto finish;
515
fee80f69
LP
516 /* Now, let's drop privileges to become the user who owns the
517 * segfaulted process and allocate the coredump memory under
518 * his uid. This also ensures that the credentials journald
519 * will see are the ones of the coredumping user, thus making
520 * sure the user himself gets access to the core dump. */
fee80f69
LP
521 if (setresgid(gid, gid, gid) < 0 ||
522 setresuid(uid, uid, uid) < 0) {
523 log_error("Failed to drop privileges: %m");
524 r = -errno;
525 goto finish;
526 }
527
8d4e028f
LP
528#ifdef HAVE_ELFUTILS
529 /* Try to get a strack trace if we can */
530 if (coredump_size <= arg_process_size_max) {
531 _cleanup_free_ char *stacktrace = NULL;
532
533 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
534 if (r >= 0)
535 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.\n\n", stacktrace, NULL);
536 else
537 log_warning("Failed to generate stack trace: %s", strerror(-r));
538 }
539
540 if (!core_message)
541#endif
542 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.", NULL);
543 if (core_message)
544 IOVEC_SET_STRING(iovec[j++], core_message);
545
34c10968
LP
546 /* Optionally store the entire coredump in the journal */
547 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
548 coredump_size <= (off_t) arg_journal_size_max) {
549 size_t sz;
fee80f69 550
34c10968 551 /* Store the coredump itself in the journal */
fee80f69 552
34c10968
LP
553 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
554 if (r >= 0) {
555 iovec[j].iov_base = coredump_data;
556 iovec[j].iov_len = sz;
557 j++;
ca0ceb6f 558 }
fee80f69
LP
559 }
560
f5e04665
LP
561 r = sd_journal_sendv(iovec, j);
562 if (r < 0)
872c8faa 563 log_error("Failed to log coredump: %s", strerror(-r));
f5e04665
LP
564
565finish:
f5e04665
LP
566 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
567}