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