chiark / gitweb /
Report about syntax errors with metadata
[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
27 #include <systemd/sd-journal.h>
28
29 #ifdef HAVE_LOGIND
30 #include <systemd/sd-login.h>
31 #endif
32
33 #include "log.h"
34 #include "util.h"
35 #include "macro.h"
36 #include "mkdir.h"
37 #include "special.h"
38 #include "cgroup-util.h"
39
40 /* Make sure to not make this larger than the maximum journal entry
41  * size. See ENTRY_SIZE_MAX in journald-native.c. */
42 #define COREDUMP_MAX (768*1024*1024)
43
44 enum {
45         ARG_PID = 1,
46         ARG_UID,
47         ARG_GID,
48         ARG_SIGNAL,
49         ARG_TIMESTAMP,
50         ARG_COMM,
51         _ARG_MAX
52 };
53
54 static int divert_coredump(void) {
55         _cleanup_fclose_ FILE *f = NULL;
56
57         log_info("Detected coredump of the journal daemon itself, diverting coredump to /var/lib/systemd/coredump/.");
58
59         mkdir_p_label("/var/lib/systemd/coredump", 0755);
60
61         f = fopen("/var/lib/systemd/coredump/core.systemd-journald", "we");
62         if (!f) {
63                 log_error("Failed to create coredump file: %m");
64                 return -errno;
65         }
66
67         for (;;) {
68                 uint8_t buffer[4096];
69                 size_t l, q;
70
71                 l = fread(buffer, 1, sizeof(buffer), stdin);
72                 if (l <= 0) {
73                         if (ferror(f)) {
74                                 log_error("Failed to read coredump: %m");
75                                 return -errno;
76                         }
77
78                         break;
79                 }
80
81                 q = fwrite(buffer, 1, l, f);
82                 if (q != l) {
83                         log_error("Failed to write coredump: %m");
84                         return -errno;
85                 }
86         }
87
88         fflush(f);
89
90         if (ferror(f)) {
91                 log_error("Failed to write coredump: %m");
92                 return -errno;
93         }
94
95         return 0;
96 }
97
98 int main(int argc, char* argv[]) {
99         int r, j = 0;
100         char *t;
101         ssize_t n;
102         pid_t pid;
103         uid_t uid;
104         gid_t gid;
105         struct iovec iovec[14];
106         char _cleanup_free_ *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
107                 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
108                 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *p = NULL;
109
110         prctl(PR_SET_DUMPABLE, 0);
111
112         if (argc != _ARG_MAX) {
113                 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
114                 log_open();
115
116                 log_error("Invalid number of arguments passed from kernel.");
117                 r = -EINVAL;
118                 goto finish;
119         }
120
121         r = parse_pid(argv[ARG_PID], &pid);
122         if (r < 0) {
123                 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
124                 log_open();
125
126                 log_error("Failed to parse PID.");
127                 goto finish;
128         }
129
130         if (cg_pid_get_unit(pid, &t) >= 0) {
131
132                 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
133                         /* Make sure we don't make use of the journal,
134                          * if it's the journal which is crashing */
135                         log_set_target(LOG_TARGET_KMSG);
136                         log_open();
137
138                         r = divert_coredump();
139                         goto finish;
140                 }
141
142                 core_unit = strappend("COREDUMP_UNIT=", t);
143         } else if (cg_pid_get_user_unit(pid, &t) >= 0)
144                 core_unit = strappend("COREDUMP_USER_UNIT=", t);
145
146         if (core_unit)
147                 IOVEC_SET_STRING(iovec[j++], core_unit);
148
149         /* OK, now we know it's not the journal, hence make use of
150          * it */
151         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
152         log_open();
153
154         r = parse_uid(argv[ARG_UID], &uid);
155         if (r < 0) {
156                 log_error("Failed to parse UID.");
157                 goto finish;
158         }
159
160         r = parse_gid(argv[ARG_GID], &gid);
161         if (r < 0) {
162                 log_error("Failed to parse GID.");
163                 goto finish;
164         }
165
166         core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
167         if (core_pid)
168                 IOVEC_SET_STRING(iovec[j++], core_pid);
169
170         core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
171         if (core_uid)
172                 IOVEC_SET_STRING(iovec[j++], core_uid);
173
174         core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
175         if (core_gid)
176                 IOVEC_SET_STRING(iovec[j++], core_gid);
177
178         core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
179         if (core_signal)
180                 IOVEC_SET_STRING(iovec[j++], core_signal);
181
182         core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
183         if (core_comm)
184                 IOVEC_SET_STRING(iovec[j++], core_comm);
185
186 #ifdef HAVE_LOGIND
187         if (sd_pid_get_session(pid, &t) >= 0) {
188                 core_session = strappend("COREDUMP_SESSION=", t);
189                 free(t);
190
191                 if (core_session)
192                         IOVEC_SET_STRING(iovec[j++], core_session);
193         }
194
195 #endif
196
197         if (get_process_exe(pid, &t) >= 0) {
198                 core_exe = strappend("COREDUMP_EXE=", t);
199                 free(t);
200
201                 if (core_exe)
202                         IOVEC_SET_STRING(iovec[j++], core_exe);
203         }
204
205         if (get_process_cmdline(pid, 0, false, &t) >= 0) {
206                 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
207                 free(t);
208
209                 if (core_cmdline)
210                         IOVEC_SET_STRING(iovec[j++], core_cmdline);
211         }
212
213         core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
214         if (core_timestamp)
215                 IOVEC_SET_STRING(iovec[j++], core_timestamp);
216
217         IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
218         IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
219
220         core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL);
221         if (core_message)
222                 IOVEC_SET_STRING(iovec[j++], core_message);
223
224         /* Now, let's drop privileges to become the user who owns the
225          * segfaulted process and allocate the coredump memory under
226          * his uid. This also ensures that the credentials journald
227          * will see are the ones of the coredumping user, thus making
228          * sure the user himself gets access to the core dump. */
229
230         if (setresgid(gid, gid, gid) < 0 ||
231             setresuid(uid, uid, uid) < 0) {
232                 log_error("Failed to drop privileges: %m");
233                 r = -errno;
234                 goto finish;
235         }
236
237         p = malloc(9 + COREDUMP_MAX);
238         if (!p) {
239                 r = log_oom();
240                 goto finish;
241         }
242
243         memcpy(p, "COREDUMP=", 9);
244
245         n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false);
246         if (n < 0) {
247                 log_error("Failed to read core dump data: %s", strerror(-n));
248                 r = (int) n;
249                 goto finish;
250         }
251
252         iovec[j].iov_base = p;
253         iovec[j].iov_len = 9 + n;
254         j++;
255
256         r = sd_journal_sendv(iovec, j);
257         if (r < 0)
258                 log_error("Failed to send coredump: %s", strerror(-r));
259
260 finish:
261         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
262 }