]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/coredump.c
coredump: add 3 more metadata fields to coredump entries
[thirdparty/systemd.git] / src / journal / coredump.c
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. */
60 assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
61
62 enum {
63 ARG_PID = 1,
64 ARG_UID,
65 ARG_GID,
66 ARG_SIGNAL,
67 ARG_TIMESTAMP,
68 ARG_COMM,
69 _ARG_MAX
70 };
71
72 typedef 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
81 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
82 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
83 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
84 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
85
86 static 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
93 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
94 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
95
96 static 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
118 static 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
160 static 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
195 static 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
284 fail:
285 unlink_noerrno(tmp);
286 return r;
287 }
288
289 static 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
327 static 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
344 int 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, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL;
350
351 _cleanup_close_ int coredump_fd = -1;
352
353 struct iovec iovec[17];
354 off_t coredump_size;
355 int r, j = 0;
356 uid_t uid, owner_uid;
357 gid_t gid;
358 pid_t pid;
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 (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
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
485 if (get_process_cmdline(pid, 0, false, &t) >= 0) {
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
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
501 core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
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
508 core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL);
509 if (core_message)
510 IOVEC_SET_STRING(iovec[j++], core_message);
511
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
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. */
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
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;
539
540 /* Store the coredump itself in the journal */
541
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++;
547 }
548 }
549
550 r = sd_journal_sendv(iovec, j);
551 if (r < 0)
552 log_error("Failed to log coredump: %s", strerror(-r));
553
554 finish:
555 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
556 }