chiark / gitweb /
coredump: add 3 more metadata fields to coredump entries
[elogind.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 }