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