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