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