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