]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/coredump.c
bootchart: add assert
[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,
a035f819 349 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL;
34c10968
LP
350
351 _cleanup_close_ int coredump_fd = -1;
352
a035f819 353 struct iovec iovec[17];
34c10968 354 off_t coredump_size;
f5e04665 355 int r, j = 0;
a035f819 356 uid_t uid, owner_uid;
fee80f69 357 gid_t gid;
a035f819 358 pid_t pid;
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
a035f819
LP
462 if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
463 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
464
465 if (core_owner_uid)
466 IOVEC_SET_STRING(iovec[j++], core_owner_uid);
467 }
468
469 if (sd_pid_get_slice(pid, &t) >= 0) {
470 core_slice = strappend("COREDUMP_SLICE=", t);
471 free(t);
472
473 if (core_slice)
474 IOVEC_SET_STRING(iovec[j++], core_slice);
475 }
476
f5e04665
LP
477 if (get_process_exe(pid, &t) >= 0) {
478 core_exe = strappend("COREDUMP_EXE=", t);
479 free(t);
480
481 if (core_exe)
482 IOVEC_SET_STRING(iovec[j++], core_exe);
483 }
484
9bdbc2e2 485 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
f5e04665
LP
486 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
487 free(t);
488
489 if (core_cmdline)
490 IOVEC_SET_STRING(iovec[j++], core_cmdline);
491 }
492
a035f819
LP
493 if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
494 core_cgroup = strappend("COREDUMP_CGROUP=", t);
495 free(t);
496
497 if (core_cgroup)
498 IOVEC_SET_STRING(iovec[j++], core_cgroup);
499 }
500
b7def684 501 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
f5e04665
LP
502 if (core_timestamp)
503 IOVEC_SET_STRING(iovec[j++], core_timestamp);
504
505 IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
506 IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
507
b7def684 508 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL);
f5e04665
LP
509 if (core_message)
510 IOVEC_SET_STRING(iovec[j++], core_message);
511
34c10968
LP
512 /* Always stream the coredump to disk, if that's possible */
513 r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
514 if (r < 0)
515 goto finish;
516
517 /* If we don't want to keep the coredump on disk, remove it
518 * now, as later on we will lack the privileges for it. */
519 r = maybe_remove_external_coredump(coredump_filename, coredump_size);
520 if (r < 0)
521 goto finish;
522
fee80f69
LP
523 /* Now, let's drop privileges to become the user who owns the
524 * segfaulted process and allocate the coredump memory under
525 * his uid. This also ensures that the credentials journald
526 * will see are the ones of the coredumping user, thus making
527 * sure the user himself gets access to the core dump. */
fee80f69
LP
528 if (setresgid(gid, gid, gid) < 0 ||
529 setresuid(uid, uid, uid) < 0) {
530 log_error("Failed to drop privileges: %m");
531 r = -errno;
532 goto finish;
533 }
534
34c10968
LP
535 /* Optionally store the entire coredump in the journal */
536 if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
537 coredump_size <= (off_t) arg_journal_size_max) {
538 size_t sz;
fee80f69 539
34c10968 540 /* Store the coredump itself in the journal */
fee80f69 541
34c10968
LP
542 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
543 if (r >= 0) {
544 iovec[j].iov_base = coredump_data;
545 iovec[j].iov_len = sz;
546 j++;
ca0ceb6f 547 }
fee80f69
LP
548 }
549
f5e04665
LP
550 r = sd_journal_sendv(iovec, j);
551 if (r < 0)
872c8faa 552 log_error("Failed to log coredump: %s", strerror(-r));
f5e04665
LP
553
554finish:
f5e04665
LP
555 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
556}