1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Zbigniew Jędrzejewski-Szmek
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.
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.
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/>.
29 #include <systemd/sd-journal.h>
35 #include "path-util.h"
38 #include "journal-internal.h"
45 } arg_action = ACTION_LIST;
47 static FILE* output = NULL;
48 static char* field = NULL;
50 static int arg_no_pager = false;
51 static int arg_no_legend = false;
53 static Set *new_matches(void) {
58 set = set_new(trivial_hash_func, trivial_compare_func);
64 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
71 r = set_consume(set, tmp);
73 log_error("failed to add to set: %s", strerror(-r));
81 static int help(void) {
82 printf("%s [OPTIONS...] [MATCHES...]\n\n"
83 "List or retrieve coredumps from the journal.\n\n"
85 " -o --output=FILE Write output to FILE\n"
86 " --no-pager Do not pipe output into a pager\n"
89 " -h --help Show this help\n"
90 " --version Print version string\n"
91 " -F --field=FIELD List all values a certain field takes\n"
92 " gdb Start gdb for the first matching coredump\n"
93 " list List available coredumps\n"
94 " dump PID Print coredump to stdout\n"
95 " dump PATH Print coredump to stdout\n"
96 , program_invocation_short_name);
101 static int add_match(Set *set, const char *match) {
105 char *pattern = NULL;
106 _cleanup_free_ char *p = NULL;
108 if (strchr(match, '='))
110 else if (strchr(match, '/')) {
111 p = path_make_absolute_cwd(match);
116 prefix = "COREDUMP_EXE=";
118 else if (safe_atou(match, &pid) == 0)
119 prefix = "COREDUMP_PID=";
121 prefix = "COREDUMP_COMM=";
123 pattern = strjoin(prefix, match, NULL);
127 log_debug("Adding pattern: %s", pattern);
128 r = set_consume(set, pattern);
130 log_error("Failed to add pattern '%s': %s",
131 pattern, strerror(-r));
137 log_error("Failed to add match: %s", strerror(-r));
141 static int parse_argv(int argc, char *argv[], Set *matches) {
150 static const struct option options[] = {
151 { "help", no_argument, NULL, 'h' },
152 { "version" , no_argument, NULL, ARG_VERSION },
153 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
154 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
155 { "output", required_argument, NULL, 'o' },
156 { "field", required_argument, NULL, 'F' },
163 while ((c = getopt_long(argc, argv, "ho:F:", options, NULL)) >= 0)
167 arg_action = ACTION_NONE;
171 puts(PACKAGE_STRING);
172 puts(SYSTEMD_FEATURES);
173 arg_action = ACTION_NONE;
181 arg_no_legend = true;
186 log_error("cannot set output more than once");
190 output = fopen(optarg, "we");
192 log_error("writing to '%s': %m", optarg);
200 log_error("cannot use --field/-F more than once");
211 log_error("Unknown option code %c", c);
216 const char *cmd = argv[optind++];
217 if(streq(cmd, "list"))
218 arg_action = ACTION_LIST;
219 else if (streq(cmd, "dump"))
220 arg_action = ACTION_DUMP;
221 else if (streq(cmd, "gdb"))
222 arg_action = ACTION_GDB;
224 log_error("Unknown action '%s'", cmd);
229 if (field && arg_action != ACTION_LIST) {
230 log_error("Option --field/-F only makes sense with list");
234 while (optind < argc) {
235 r = add_match(matches, argv[optind]);
244 static int retrieve(const void *data,
251 ident = strlen(name) + 1; /* name + "=" */
256 if (memcmp(data, name, ident - 1) != 0)
259 if (((const char*) data)[ident - 1] != '=')
262 *var = strndup((const char*)data + ident, len - ident);
269 static void print_field(FILE* file, sd_journal *j) {
270 _cleanup_free_ const char *value = NULL;
276 SD_JOURNAL_FOREACH_DATA(j, d, l)
277 retrieve(d, l, field, &value);
279 fprintf(file, "%s\n", value);
282 static int print_entry(FILE* file, sd_journal *j, int had_legend) {
283 _cleanup_free_ const char
284 *pid = NULL, *uid = NULL, *gid = NULL,
285 *sgnl = NULL, *exe = NULL;
289 char buf[FORMAT_TIMESTAMP_MAX];
292 SD_JOURNAL_FOREACH_DATA(j, d, l) {
293 retrieve(d, l, "COREDUMP_PID", &pid);
294 retrieve(d, l, "COREDUMP_PID", &pid);
295 retrieve(d, l, "COREDUMP_UID", &uid);
296 retrieve(d, l, "COREDUMP_GID", &gid);
297 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
298 retrieve(d, l, "COREDUMP_EXE", &exe);
300 retrieve(d, l, "COREDUMP_COMM", &exe);
302 retrieve(d, l, "COREDUMP_CMDLINE", &exe);
305 if (!pid && !uid && !gid && !sgnl && !exe) {
306 log_warning("Empty coredump log entry");
310 r = sd_journal_get_realtime_usec(j, &t);
312 log_error("Failed to get realtime timestamp: %s", strerror(-r));
316 format_timestamp(buf, sizeof(buf), t);
318 if (!had_legend && !arg_no_legend)
319 fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
320 FORMAT_TIMESTAMP_MAX-1, "TIME",
327 fprintf(file, "%*s %*s %*s %*s %*s %s\n",
328 FORMAT_TIMESTAMP_MAX-1, buf,
338 static int dump_list(sd_journal *j) {
343 /* The coredumps are likely to compressed, and for just
344 * listing them we don#t need to decompress them, so let's
345 * pick a fairly low data threshold here */
346 sd_journal_set_data_threshold(j, 4096);
348 SD_JOURNAL_FOREACH(j) {
350 print_field(stdout, j);
352 print_entry(stdout, j, found++);
355 if (!field && !found) {
356 log_notice("No coredumps found");
363 static int focus(sd_journal *j) {
366 r = sd_journal_seek_tail(j);
368 r = sd_journal_previous(j);
370 log_error("Failed to search journal: %s", strerror(-r));
374 log_error("No match found");
380 static int dump_core(sd_journal* j) {
387 /* We want full data, nothing truncated. */
388 sd_journal_set_data_threshold(j, 0);
394 print_entry(output ? stdout : stderr, j, false);
396 if (on_tty() && !output) {
397 log_error("Refusing to dump core to tty");
401 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
403 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
408 data = (const uint8_t*) data + 9;
411 ret = fwrite(data, len, 1, output ? output : stdout);
413 log_error("dumping coredump: %m (%zu)", ret);
417 r = sd_journal_previous(j);
419 log_warning("More than one entry matches, ignoring rest.\n");
424 static int run_gdb(sd_journal *j) {
425 char path[] = "/var/tmp/coredump-XXXXXX";
430 _cleanup_free_ char *exe = NULL;
432 _cleanup_close_ int fd = -1;
437 sd_journal_set_data_threshold(j, 0);
443 print_entry(stdout, j, false);
445 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
447 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
452 data = (const uint8_t*) data + 13;
455 exe = strndup(data, len);
459 if (endswith(exe, " (deleted)")) {
460 log_error("Binary already deleted.");
464 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
466 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
471 data = (const uint8_t*) data + 9;
474 fd = mkostemp(path, O_WRONLY);
476 log_error("Failed to create temporary file: %m");
480 sz = write(fd, data, len);
482 log_error("Failed to write temporary file: %s", strerror(errno));
486 if (sz != (ssize_t) len) {
487 log_error("Short write to temporary file.");
492 close_nointr_nofail(fd);
497 log_error("Failed to fork(): %m");
502 execlp("gdb", "gdb", exe, path, NULL);
503 log_error("Failed to invoke gdb: %m");
507 r = wait_for_terminate(pid, &st);
509 log_error("Failed to wait for gdb: %m");
513 r = st.si_code == CLD_EXITED ? st.si_status : 255;
520 int main(int argc, char *argv[]) {
521 _cleanup_journal_close_ sd_journal*j = NULL;
525 _cleanup_set_free_free_ Set *matches = NULL;
527 setlocale(LC_ALL, "");
528 log_parse_environment();
531 matches = new_matches();
537 r = parse_argv(argc, argv, matches);
541 if (arg_action == ACTION_NONE)
544 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
546 log_error("Failed to open journal: %s", strerror(-r));
550 SET_FOREACH(match, matches, it) {
551 r = sd_journal_add_match(j, match, strlen(match));
553 log_error("Failed to add match '%s': %s",
554 match, strerror(-r));
577 assert_not_reached("Shouldn't be here");
586 return r >= 0 ? r : EXIT_FAILURE;