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