chiark / gitweb /
journal: when watching directories actually watch the directories asked for
[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
148                 if (core_unit)
149                         IOVEC_SET_STRING(iovec[j++], core_unit);
150         }
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, LINE_MAX, 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         p = malloc(9 + COREDUMP_MAX);
241         if (!p) {
242                 log_error("Out of memory");
243                 r = -ENOMEM;
244                 goto finish;
245         }
246
247         memcpy(p, "COREDUMP=", 9);
248
249         n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false);
250         if (n < 0) {
251                 log_error("Failed to read core dump data: %s", strerror(-n));
252                 r = (int) n;
253                 goto finish;
254         }
255
256         iovec[j].iov_base = p;
257         iovec[j].iov_len = 9 + n;
258         j++;
259
260         r = sd_journal_sendv(iovec, j);
261         if (r < 0)
262                 log_error("Failed to send coredump: %s", strerror(-r));
263
264 finish:
265         free(p);
266         free(core_pid);
267         free(core_uid);
268         free(core_gid);
269         free(core_signal);
270         free(core_timestamp);
271         free(core_comm);
272         free(core_exe);
273         free(core_cmdline);
274         free(core_unit);
275         free(core_session);
276         free(core_message);
277
278         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
279 }