]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/coredump.c
cfd3a910d99247785fbeb634bd9de85472694067
[thirdparty/systemd.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 }