]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/coredump.c
Fix write-only use of a few variables
[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 "macro.h"
36 #include "mkdir.h"
37 #include "special.h"
38 #include "cgroup-util.h"
39
40 /* Few programs have less than 3MiB resident */
41 #define COREDUMP_MIN_START (3*1024*1024)
42 /* Make sure to not make this larger than the maximum journal entry
43 * size. See ENTRY_SIZE_MAX in journald-native.c. */
44 #define COREDUMP_MAX (767*1024*1024)
45
46 enum {
47 ARG_PID = 1,
48 ARG_UID,
49 ARG_GID,
50 ARG_SIGNAL,
51 ARG_TIMESTAMP,
52 ARG_COMM,
53 _ARG_MAX
54 };
55
56 static int divert_coredump(void) {
57 _cleanup_fclose_ FILE *f = NULL;
58
59 log_info("Detected coredump of the journal daemon itself, diverting coredump to /var/lib/systemd/coredump/.");
60
61 mkdir_p_label("/var/lib/systemd/coredump", 0755);
62
63 f = fopen("/var/lib/systemd/coredump/core.systemd-journald", "we");
64 if (!f) {
65 log_error("Failed to create coredump file: %m");
66 return -errno;
67 }
68
69 for (;;) {
70 uint8_t buffer[4096];
71 size_t l, q;
72
73 l = fread(buffer, 1, sizeof(buffer), stdin);
74 if (l <= 0) {
75 if (ferror(f)) {
76 log_error("Failed to read coredump: %m");
77 return -errno;
78 }
79
80 break;
81 }
82
83 q = fwrite(buffer, 1, l, f);
84 if (q != l) {
85 log_error("Failed to write coredump: %m");
86 return -errno;
87 }
88 }
89
90 fflush(f);
91
92 if (ferror(f)) {
93 log_error("Failed to write coredump: %m");
94 return -errno;
95 }
96
97 return 0;
98 }
99
100 int main(int argc, char* argv[]) {
101 int r, j = 0;
102 char *t;
103 ssize_t n;
104 pid_t pid;
105 uid_t uid;
106 gid_t gid;
107 struct iovec iovec[14];
108 size_t coredump_bufsize, coredump_size;
109 _cleanup_free_ 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, *coredump_data = NULL;
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 } else if (cg_pid_get_user_unit(pid, &t) >= 0)
147 core_unit = strappend("COREDUMP_USER_UNIT=", t);
148
149 if (core_unit)
150 IOVEC_SET_STRING(iovec[j++], core_unit);
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, 0, 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 coredump_bufsize = COREDUMP_MIN_START;
241 coredump_data = malloc(coredump_bufsize);
242 if (!coredump_data) {
243 log_warning("Failed to allocate memory for core, core will not be stored.");
244 goto finalize;
245 }
246
247 memcpy(coredump_data, "COREDUMP=", 9);
248 coredump_size = 9;
249
250 for (;;) {
251 n = loop_read(STDIN_FILENO, coredump_data + coredump_size,
252 coredump_bufsize - coredump_size, false);
253 if (n < 0) {
254 log_error("Failed to read core data: %s", strerror(-n));
255 r = (int) n;
256 goto finish;
257 } else if (n == 0)
258 break;
259
260 coredump_size += n;
261
262 if (coredump_size > COREDUMP_MAX) {
263 log_error("Core too large, core will not be stored.");
264 goto finalize;
265 }
266
267 if (!GREEDY_REALLOC(coredump_data, coredump_bufsize, coredump_size + 1)) {
268 log_warning("Failed to allocate memory for core, core will not be stored.");
269 goto finalize;
270 }
271 }
272
273 iovec[j].iov_base = coredump_data;
274 iovec[j].iov_len = coredump_size;
275 j++;
276
277 finalize:
278 r = sd_journal_sendv(iovec, j);
279 if (r < 0)
280 log_error("Failed to log coredump: %s", strerror(-r));
281
282 finish:
283 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
284 }