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