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/>.
28 #include <systemd/sd-journal.h>
34 #include "path-util.h"
42 } arg_action = ACTION_LIST;
44 static Set *matches = NULL;
45 static FILE* output = NULL;
46 static char* field = NULL;
48 static int arg_no_pager = false;
49 static int arg_no_legend = false;
51 static Set *new_matches(void) {
56 set = set_new(trivial_hash_func, trivial_compare_func);
62 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
69 r = set_put(set, tmp);
71 log_error("failed to add to set: %s", strerror(-r));
80 static int help(void) {
81 printf("%s [OPTIONS...] [MATCHES...]\n\n"
82 "List or retrieve coredumps from the journal.\n\n"
84 " -o --output=FILE Write output to FILE\n"
85 " --no-pager Do not pipe output into a pager\n"
88 " -h --help Show this help\n"
89 " --version Print version string\n"
90 " gdb Start gdb for the first matching coredump\n"
91 " list List available coredumps\n"
92 " dump PID Print coredump to stdout\n"
93 " dump PATH Print coredump to stdout\n"
94 , program_invocation_short_name);
99 static int add_match(Set *set, const char *match) {
103 char *pattern = NULL;
104 char _cleanup_free_ *p = NULL;
106 if (strchr(match, '='))
108 else if (strchr(match, '/')) {
109 p = path_make_absolute_cwd(match);
114 prefix = "COREDUMP_EXE=";
116 else if (safe_atou(match, &pid) == 0)
117 prefix = "COREDUMP_PID=";
119 prefix = "COREDUMP_COMM=";
121 pattern = strjoin(prefix, match, NULL);
125 r = set_put(set, pattern);
127 log_error("failed to add pattern '%s': %s",
128 pattern, strerror(-r));
131 log_debug("Added pattern: %s", pattern);
136 log_error("failed to add match: %s", strerror(-r));
140 static int parse_argv(int argc, char *argv[]) {
149 static const struct option options[] = {
150 { "help", no_argument, NULL, 'h' },
151 { "version" , no_argument, NULL, ARG_VERSION },
152 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
153 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
154 { "output", required_argument, NULL, 'o' },
155 { "field", required_argument, NULL, 'F' },
162 while ((c = getopt_long(argc, argv, "ho:F:", options, NULL)) >= 0)
166 arg_action = ACTION_NONE;
170 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 const char _cleanup_free_ *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 const char _cleanup_free_
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 SD_JOURNAL_FOREACH(j) {
345 print_field(stdout, j);
347 print_entry(stdout, j, found++);
350 if (!field && !found) {
351 log_notice("No coredumps found");
358 static int focus(sd_journal *j) {
361 r = sd_journal_seek_tail(j);
363 r = sd_journal_previous(j);
365 log_error("Failed to search journal: %s", strerror(-r));
369 log_error("No match found");
375 static int dump_core(sd_journal* j) {
386 print_entry(output ? stdout : stderr, j, false);
388 if (on_tty() && !output) {
389 log_error("Refusing to dump core to tty");
393 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
395 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
400 data = (const uint8_t*) data + 9;
403 ret = fwrite(data, len, 1, output ? output : stdout);
405 log_error("dumping coredump: %m (%zu)", ret);
409 r = sd_journal_previous(j);
411 log_warning("More than one entry matches, ignoring rest.\n");
416 static int run_gdb(sd_journal *j) {
417 char path[] = "/var/tmp/coredump-XXXXXX";
422 _cleanup_free_ char *exe = NULL;
424 _cleanup_close_ int fd = -1;
433 print_entry(stdout, j, false);
435 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
437 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
442 data = (const uint8_t*) data + 13;
445 exe = strndup(data, len);
449 if (endswith(exe, " (deleted)")) {
450 log_error("Binary already deleted.");
454 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
456 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
461 data = (const uint8_t*) data + 9;
464 fd = mkostemp(path, O_WRONLY);
466 log_error("Failed to create temporary file: %m");
470 sz = write(fd, data, len);
472 log_error("Failed to write temporary file: %s", strerror(errno));
476 if (sz != (ssize_t) len) {
477 log_error("Short write to temporary file.");
482 close_nointr_nofail(fd);
487 log_error("Failed to fork(): %m");
492 execlp("gdb", "gdb", exe, path, NULL);
493 log_error("Failed to invoke gdb: %m");
497 r = wait_for_terminate(pid, &st);
499 log_error("Failed to wait for gdb: %m");
503 r = st.si_code == CLD_EXITED ? st.si_status : 255;
510 int main(int argc, char *argv[]) {
511 sd_journal *j = NULL;
516 log_parse_environment();
519 matches = new_matches();
525 r = parse_argv(argc, argv);
529 if (arg_action == ACTION_NONE)
532 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
534 log_error("Failed to open journal: %s", strerror(-r));
538 SET_FOREACH(match, matches, it) {
539 r = sd_journal_add_match(j, match, strlen(match));
541 log_error("Failed to add match '%s': %s",
542 match, strerror(-r));
565 assert_not_reached("Shouldn't be here");
572 set_free_free(matches);
579 return r >= 0 ? r : EXIT_FAILURE;