]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/journal/coredump.c
coredump: optionally store coredumps on disk, not in the journal
[thirdparty/systemd.git] / src / journal / coredump.c
... / ...
CommitLineData
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <unistd.h>
24#include <stdio.h>
25#include <sys/prctl.h>
26#include <sys/types.h>
27#include <attr/xattr.h>
28
29#include <systemd/sd-journal.h>
30#include <systemd/sd-login.h>
31
32#include "log.h"
33#include "util.h"
34#include "macro.h"
35#include "mkdir.h"
36#include "special.h"
37#include "cgroup-util.h"
38#include "journald-native.h"
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))
57
58/* Make sure to not make this larger than the maximum journal entry
59 * size. See ENTRY_SIZE_MAX in journald-native.c. */
60assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
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
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;
185
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();
237
238 mkdir_p_label("/var/lib/systemd/coredump", 0755);
239
240 fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
241 if (fd < 0) {
242 log_error("Failed to create coredump file: %m");
243 return -errno;
244 }
245
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 }
251
252 /* Ignore errors on these */
253 fchmod(fd, 0640);
254 fix_acl(fd, uid);
255 fix_xattr(fd, argv);
256
257 if (fsync(fd) < 0) {
258 log_error("Failed to sync coredump: %m");
259 r = -errno;
260 goto fail;
261 }
262
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;
299 }
300
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}
326
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);
338 return -errno;
339 }
340
341 return 0;
342}
343
344int main(int argc, char* argv[]) {
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;
355 int r, j = 0;
356 pid_t pid;
357 uid_t uid;
358 gid_t gid;
359 char *t;
360
361 /* Make sure we never enter a loop */
362 prctl(PR_SET_DUMPABLE, 0);
363
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();
370
371 if (argc != _ARG_MAX) {
372 log_error("Invalid number of arguments passed from kernel.");
373 r = -EINVAL;
374 goto finish;
375 }
376
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);
382 if (r < 0) {
383 log_error("Failed to parse UID.");
384 goto finish;
385 }
386
387 r = parse_pid(argv[ARG_PID], &pid);
388 if (r < 0) {
389 log_error("Failed to parse PID.");
390 goto finish;
391 }
392
393 r = parse_gid(argv[ARG_GID], &gid);
394 if (r < 0) {
395 log_error("Failed to parse GID.");
396 goto finish;
397 }
398
399 if (cg_pid_get_unit(pid, &t) >= 0) {
400
401 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
402
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);
419 goto finish;
420 }
421
422 core_unit = strappend("COREDUMP_UNIT=", t);
423 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
424 core_unit = strappend("COREDUMP_USER_UNIT=", t);
425
426 if (core_unit)
427 IOVEC_SET_STRING(iovec[j++], core_unit);
428
429 /* OK, now we know it's not the journal, hence we can make use
430 * of it now. */
431 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
432 log_open();
433
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
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
470 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
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
478 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
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
485 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL);
486 if (core_message)
487 IOVEC_SET_STRING(iovec[j++], core_message);
488
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
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. */
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
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;
516
517 /* Store the coredump itself in the journal */
518
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++;
524 }
525 }
526
527 r = sd_journal_sendv(iovec, j);
528 if (r < 0)
529 log_error("Failed to log coredump: %s", strerror(-r));
530
531finish:
532 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
533}