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