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"
87 " --no-legend Do not print the column headers.\n\n"
90 " -h --help Show this help\n"
91 " --version Print version string\n"
92 " -F --field=FIELD List all values a certain field takes\n"
93 " gdb Start gdb for the first matching coredump\n"
94 " list List available coredumps\n"
95 " dump PID Print coredump to stdout\n"
96 " dump PATH Print coredump to stdout\n"
97 , program_invocation_short_name);
102 static int add_match(Set *set, const char *match) {
106 char *pattern = NULL;
107 _cleanup_free_ char *p = NULL;
109 if (strchr(match, '='))
111 else if (strchr(match, '/')) {
112 p = path_make_absolute_cwd(match);
117 prefix = "COREDUMP_EXE=";
119 else if (safe_atou(match, &pid) == 0)
120 prefix = "COREDUMP_PID=";
122 prefix = "COREDUMP_COMM=";
124 pattern = strjoin(prefix, match, NULL);
128 log_debug("Adding pattern: %s", pattern);
129 r = set_consume(set, pattern);
131 log_error("Failed to add pattern '%s': %s",
132 pattern, strerror(-r));
138 log_error("Failed to add match: %s", strerror(-r));
142 static int parse_argv(int argc, char *argv[], Set *matches) {
151 static const struct option options[] = {
152 { "help", no_argument, NULL, 'h' },
153 { "version" , no_argument, NULL, ARG_VERSION },
154 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
155 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
156 { "output", required_argument, NULL, 'o' },
157 { "field", required_argument, NULL, 'F' },
164 while ((c = getopt_long(argc, argv, "ho:F:", options, NULL)) >= 0)
168 arg_action = ACTION_NONE;
172 arg_action = ACTION_NONE;
173 puts(PACKAGE_STRING);
174 puts(SYSTEMD_FEATURES);
182 arg_no_legend = true;
187 log_error("cannot set output more than once");
191 output = fopen(optarg, "we");
193 log_error("writing to '%s': %m", optarg);
201 log_error("cannot use --field/-F more than once");
212 assert_not_reached("Unhandled option");
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: %m");
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));
559 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
560 _cleanup_free_ char *filter;
562 filter = journal_make_match_string(j);
563 log_debug("Journal filter: %s", filter);
584 assert_not_reached("Shouldn't be here");
593 return r >= 0 ? r : EXIT_FAILURE;