]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/coredumpctl.c
coredumpctl: optimize journal entry parsing a bit by enumerating only once
[thirdparty/systemd.git] / src / journal / coredumpctl.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 Zbigniew Jędrzejewski-Szmek
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 <stdio.h>
23 #include <string.h>
24 #include <getopt.h>
25
26 #include <systemd/sd-journal.h>
27
28 #include "build.h"
29 #include "set.h"
30 #include "util.h"
31 #include "log.h"
32 #include "path-util.h"
33 #include "pager.h"
34
35 static enum {
36 ACTION_NONE,
37 ACTION_LIST,
38 ACTION_DUMP,
39 } arg_action = ACTION_LIST;
40
41 static Set *matches = NULL;
42 static FILE* output = NULL;
43
44 static int arg_no_pager = false;
45
46 static Set *new_matches(void) {
47 Set *set;
48 char *tmp;
49 int r;
50
51 set = set_new(trivial_hash_func, trivial_compare_func);
52 if (!set) {
53 log_oom();
54 return NULL;
55 }
56
57 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
58 if (!tmp) {
59 log_oom();
60 set_clear_free(set);
61 return NULL;
62 }
63
64 r = set_put(set, tmp);
65 if (r < 0) {
66 log_error("failed to add to set: %s", strerror(-r));
67 free(tmp);
68 set_clear_free(set);
69 return NULL;
70 }
71
72 return set;
73 }
74
75 static int help(void) {
76 printf("%s [OPTIONS...] [MATCHES...]\n\n"
77 "List or retrieve coredumps from the journal.\n\n"
78 "Flags:\n"
79 " -o --output=FILE Write output to FILE\n"
80 " --no-pager Do not pipe output into a pager\n"
81
82 "Commands:\n"
83 " -h --help Show this help\n"
84 " --version Print version string\n"
85 " list List available coredumps\n"
86 " dump PID Print coredump to stdout\n"
87 " dump PATH Print coredump to stdout\n"
88 , program_invocation_short_name);
89
90 return 0;
91 }
92
93 static int add_match(Set *set, const char *match) {
94 int r = -ENOMEM;
95 unsigned pid;
96 const char* prefix;
97 char *pattern = NULL;
98 char _cleanup_free_ *p = NULL;
99
100 if (strchr(match, '='))
101 prefix = "";
102 else if (strchr(match, '/')) {
103 p = path_make_absolute_cwd(match);
104 if (!p)
105 goto fail;
106
107 match = p;
108 prefix = "COREDUMP_EXE=";
109 }
110 else if (safe_atou(match, &pid) == 0)
111 prefix = "COREDUMP_PID=";
112 else
113 prefix = "COREDUMP_COMM=";
114
115 pattern = strjoin(prefix, match, NULL);
116 if (!pattern)
117 goto fail;
118
119 r = set_put(set, pattern);
120 if (r < 0) {
121 log_error("failed to add pattern '%s': %s",
122 pattern, strerror(-r));
123 goto fail;
124 }
125 log_debug("Added pattern: %s", pattern);
126
127 return 0;
128 fail:
129 free(pattern);
130 log_error("failed to add match: %s", strerror(-r));
131 return r;
132 }
133
134 static int parse_argv(int argc, char *argv[]) {
135 enum {
136 ARG_VERSION = 0x100,
137 ARG_NO_PAGER,
138 };
139
140 int r, c;
141
142 static const struct option options[] = {
143 { "help", no_argument, NULL, 'h' },
144 { "version" , no_argument, NULL, ARG_VERSION },
145 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
146 { "output", required_argument, NULL, 'o' },
147 };
148
149 assert(argc >= 0);
150 assert(argv);
151
152 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
153 switch(c) {
154 case 'h':
155 help();
156 arg_action = ACTION_NONE;
157 return 0;
158
159 case ARG_VERSION:
160 puts(PACKAGE_STRING);
161 puts(DISTRIBUTION);
162 puts(SYSTEMD_FEATURES);
163 arg_action = ACTION_NONE;
164 return 0;
165
166 case ARG_NO_PAGER:
167 arg_no_pager = true;
168 break;
169
170 case 'o':
171 if (output) {
172 log_error("cannot set output more than once");
173 return -EINVAL;
174 }
175
176 output = fopen(optarg, "we");
177 if (!output) {
178 log_error("writing to '%s': %m", optarg);
179 return -errno;
180 }
181
182 break;
183 default:
184 log_error("Unknown option code %c", c);
185 return -EINVAL;
186 }
187
188 if (optind < argc) {
189 const char *cmd = argv[optind++];
190 if(streq(cmd, "list"))
191 arg_action = ACTION_LIST;
192 else if (streq(cmd, "dump"))
193 arg_action = ACTION_DUMP;
194 else {
195 log_error("Unknown action '%s'", cmd);
196 return -EINVAL;
197 }
198 }
199
200 while (optind < argc) {
201 r = add_match(matches, argv[optind]);
202 if (r != 0)
203 return r;
204 optind++;
205 }
206
207 return 0;
208 }
209
210 static int retrieve(const void *data,
211 size_t len,
212 const char *name,
213 const char **var) {
214
215 size_t field;
216
217 field = strlen(name) + 1; /* name + "=" */
218
219 if (len < field)
220 return 0;
221
222 if (memcmp(data, name, field - 1) != 0)
223 return 0;
224
225 if (((const char*) data)[field - 1] != '=')
226 return 0;
227
228 *var = strndup((const char*)data + field, len - field);
229 if (!var)
230 return log_oom();
231
232 return 0;
233 }
234
235 static void print_entry(FILE* file, sd_journal *j, int had_header) {
236 const char _cleanup_free_
237 *pid = NULL, *uid = NULL, *gid = NULL,
238 *sgnl = NULL, *exe = NULL;
239 const void *d;
240 size_t l;
241
242 SD_JOURNAL_FOREACH_DATA(j, d, l) {
243 retrieve(d, l, "COREDUMP_PID", &pid);
244 retrieve(d, l, "COREDUMP_PID", &pid);
245 retrieve(d, l, "COREDUMP_UID", &uid);
246 retrieve(d, l, "COREDUMP_GID", &gid);
247 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
248 retrieve(d, l, "COREDUMP_EXE", &exe);
249 if (!exe)
250 retrieve(d, l, "COREDUMP_COMM", &exe);
251 if (!exe)
252 retrieve(d, l, "COREDUMP_CMDLINE", &exe);
253 }
254
255 if (!pid && !uid && !gid && !sgnl && !exe) {
256 log_warning("empty coredump log entry");
257 return;
258 }
259
260 if (!had_header)
261 fprintf(file, "%*s %*s %*s %*s %s\n",
262 6, "PID",
263 5, "UID",
264 5, "GID",
265 3, "sig",
266 "exe");
267
268 fprintf(file, "%*s %*s %*s %*s %s\n",
269 6, pid,
270 5, uid,
271 5, gid,
272 3, sgnl,
273 exe);
274 }
275
276 static int dump_list(sd_journal *j) {
277 int found = 0;
278
279 assert(j);
280
281 SD_JOURNAL_FOREACH(j)
282 print_entry(stdout, j, found++);
283
284 if (!found) {
285 log_error("no coredumps found");
286 return -ESRCH;
287 }
288
289 return 0;
290 }
291
292 static int dump_core(sd_journal* j) {
293 const char *data;
294 size_t len, ret;
295 int r;
296
297 assert(j);
298
299 r = sd_journal_seek_tail(j);
300 if (r == 0)
301 r = sd_journal_previous(j);
302 if (r < 0) {
303 log_error("Failed to search journal: %s", strerror(-r));
304 return r;
305 }
306
307 if (r == 0) {
308 log_error("No match found");
309 return -ESRCH;
310 }
311
312 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
313 if (r != 0) {
314 log_error("retrieve COREDUMP field: %s", strerror(-r));
315 return r;
316 }
317
318 print_entry(output ? stdout : stderr, j, false);
319
320 if (on_tty() && !output) {
321 log_error("Refusing to dump core to tty");
322 return -ENOTTY;
323 }
324
325 assert(len >= 9);
326
327 ret = fwrite(data+9, len-9, 1, output ? output : stdout);
328 if (ret != 1) {
329 log_error("dumping coredump: %m (%zu)", ret);
330 return -errno;
331 }
332
333 r = sd_journal_previous(j);
334 if (r >= 0)
335 log_warning("More than one entry matches, ignoring rest.\n");
336
337 return 0;
338 }
339
340 int main(int argc, char *argv[]) {
341 sd_journal *j = NULL;
342 const char* match;
343 Iterator it;
344 int r = 0;
345
346 log_parse_environment();
347 log_open();
348
349 matches = new_matches();
350 if (!matches)
351 goto end;
352
353 if (parse_argv(argc, argv))
354 goto end;
355
356 if (arg_action == ACTION_NONE)
357 goto end;
358
359 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
360 if (r < 0) {
361 log_error("Failed to open journal: %s", strerror(-r));
362 goto end;
363 }
364
365 SET_FOREACH(match, matches, it) {
366 r = sd_journal_add_match(j, match, strlen(match));
367 if (r != 0) {
368 log_error("Failed to add match '%s': %s",
369 match, strerror(-r));
370 goto end;
371 }
372 }
373
374 switch(arg_action) {
375 case ACTION_LIST:
376 if (!arg_no_pager)
377 pager_open();
378
379 r = dump_list(j);
380 break;
381 case ACTION_DUMP:
382 r = dump_core(j);
383 break;
384 case ACTION_NONE:
385 assert_not_reached("Shouldn't be here");
386 }
387
388 end:
389 if (j)
390 sd_journal_close(j);
391
392 set_free_free(matches);
393
394 pager_close();
395
396 if (output)
397 fclose(output);
398
399 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
400 }