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"
43 } arg_action = ACTION_LIST;
45 static Set *matches = NULL;
46 static FILE* output = NULL;
47 static char* field = NULL;
49 static int arg_no_pager = false;
50 static int arg_no_legend = false;
52 static Set *new_matches(void) {
57 set = set_new(trivial_hash_func, trivial_compare_func);
63 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
70 r = set_put(set, tmp);
72 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 char _cleanup_free_ *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 r = set_put(set, pattern);
129 log_error("failed to add pattern '%s': %s",
130 pattern, strerror(-r));
133 log_debug("Added pattern: %s", pattern);
138 log_error("failed to add match: %s", strerror(-r));
142 static int parse_argv(int argc, char *argv[]) {
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 puts(PACKAGE_STRING);
174 puts(SYSTEMD_FEATURES);
175 arg_action = ACTION_NONE;
183 arg_no_legend = true;
188 log_error("cannot set output more than once");
192 output = fopen(optarg, "we");
194 log_error("writing to '%s': %m", optarg);
202 log_error("cannot use --field/-F more than once");
213 log_error("Unknown option code %c", c);
218 const char *cmd = argv[optind++];
219 if(streq(cmd, "list"))
220 arg_action = ACTION_LIST;
221 else if (streq(cmd, "dump"))
222 arg_action = ACTION_DUMP;
223 else if (streq(cmd, "gdb"))
224 arg_action = ACTION_GDB;
226 log_error("Unknown action '%s'", cmd);
231 if (field && arg_action != ACTION_LIST) {
232 log_error("Option --field/-F only makes sense with list");
236 while (optind < argc) {
237 r = add_match(matches, argv[optind]);
246 static int retrieve(const void *data,
253 ident = strlen(name) + 1; /* name + "=" */
258 if (memcmp(data, name, ident - 1) != 0)
261 if (((const char*) data)[ident - 1] != '=')
264 *var = strndup((const char*)data + ident, len - ident);
271 static void print_field(FILE* file, sd_journal *j) {
272 const char _cleanup_free_ *value = NULL;
278 SD_JOURNAL_FOREACH_DATA(j, d, l)
279 retrieve(d, l, field, &value);
281 fprintf(file, "%s\n", value);
284 static int print_entry(FILE* file, sd_journal *j, int had_legend) {
285 const char _cleanup_free_
286 *pid = NULL, *uid = NULL, *gid = NULL,
287 *sgnl = NULL, *exe = NULL;
291 char buf[FORMAT_TIMESTAMP_MAX];
294 SD_JOURNAL_FOREACH_DATA(j, d, l) {
295 retrieve(d, l, "COREDUMP_PID", &pid);
296 retrieve(d, l, "COREDUMP_PID", &pid);
297 retrieve(d, l, "COREDUMP_UID", &uid);
298 retrieve(d, l, "COREDUMP_GID", &gid);
299 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
300 retrieve(d, l, "COREDUMP_EXE", &exe);
302 retrieve(d, l, "COREDUMP_COMM", &exe);
304 retrieve(d, l, "COREDUMP_CMDLINE", &exe);
307 if (!pid && !uid && !gid && !sgnl && !exe) {
308 log_warning("Empty coredump log entry");
312 r = sd_journal_get_realtime_usec(j, &t);
314 log_error("Failed to get realtime timestamp: %s", strerror(-r));
318 format_timestamp(buf, sizeof(buf), t);
320 if (!had_legend && !arg_no_legend)
321 fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
322 FORMAT_TIMESTAMP_MAX-1, "TIME",
329 fprintf(file, "%*s %*s %*s %*s %*s %s\n",
330 FORMAT_TIMESTAMP_MAX-1, buf,
340 static int dump_list(sd_journal *j) {
345 SD_JOURNAL_FOREACH(j) {
347 print_field(stdout, j);
349 print_entry(stdout, j, found++);
352 if (!field && !found) {
353 log_notice("No coredumps found");
360 static int focus(sd_journal *j) {
363 r = sd_journal_seek_tail(j);
365 r = sd_journal_previous(j);
367 log_error("Failed to search journal: %s", strerror(-r));
371 log_error("No match found");
377 static int dump_core(sd_journal* j) {
388 print_entry(output ? stdout : stderr, j, false);
390 if (on_tty() && !output) {
391 log_error("Refusing to dump core to tty");
395 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
397 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
402 data = (const uint8_t*) data + 9;
405 ret = fwrite(data, len, 1, output ? output : stdout);
407 log_error("dumping coredump: %m (%zu)", ret);
411 r = sd_journal_previous(j);
413 log_warning("More than one entry matches, ignoring rest.\n");
418 static int run_gdb(sd_journal *j) {
419 char path[] = "/var/tmp/coredump-XXXXXX";
424 _cleanup_free_ char *exe = NULL;
426 _cleanup_close_ int fd = -1;
435 print_entry(stdout, j, false);
437 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
439 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
444 data = (const uint8_t*) data + 13;
447 exe = strndup(data, len);
451 if (endswith(exe, " (deleted)")) {
452 log_error("Binary already deleted.");
456 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
458 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
463 data = (const uint8_t*) data + 9;
466 fd = mkostemp(path, O_WRONLY);
468 log_error("Failed to create temporary file: %m");
472 sz = write(fd, data, len);
474 log_error("Failed to write temporary file: %s", strerror(errno));
478 if (sz != (ssize_t) len) {
479 log_error("Short write to temporary file.");
484 close_nointr_nofail(fd);
489 log_error("Failed to fork(): %m");
494 execlp("gdb", "gdb", exe, path, NULL);
495 log_error("Failed to invoke gdb: %m");
499 r = wait_for_terminate(pid, &st);
501 log_error("Failed to wait for gdb: %m");
505 r = st.si_code == CLD_EXITED ? st.si_status : 255;
512 int main(int argc, char *argv[]) {
513 sd_journal *j = NULL;
518 setlocale(LC_ALL, "");
519 log_parse_environment();
522 matches = new_matches();
528 r = parse_argv(argc, argv);
532 if (arg_action == ACTION_NONE)
535 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
537 log_error("Failed to open journal: %s", strerror(-r));
541 SET_FOREACH(match, matches, it) {
542 r = sd_journal_add_match(j, match, strlen(match));
544 log_error("Failed to add match '%s': %s",
545 match, strerror(-r));
568 assert_not_reached("Shouldn't be here");
575 set_free_free(matches);
582 return r >= 0 ? r : EXIT_FAILURE;