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;
47 static int arg_no_pager = false;
49 static Set *new_matches(void) {
54 set = set_new(trivial_hash_func, trivial_compare_func);
60 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
67 r = set_put(set, tmp);
69 log_error("failed to add to set: %s", strerror(-r));
78 static int help(void) {
79 printf("%s [OPTIONS...] [MATCHES...]\n\n"
80 "List or retrieve coredumps from the journal.\n\n"
82 " -o --output=FILE Write output to FILE\n"
83 " --no-pager Do not pipe output into a pager\n"
86 " -h --help Show this help\n"
87 " --version Print version string\n"
88 " list List available coredumps\n"
89 " dump PID Print coredump to stdout\n"
90 " dump PATH Print coredump to stdout\n"
91 , program_invocation_short_name);
96 static int add_match(Set *set, const char *match) {
100 char *pattern = NULL;
101 char _cleanup_free_ *p = NULL;
103 if (strchr(match, '='))
105 else if (strchr(match, '/')) {
106 p = path_make_absolute_cwd(match);
111 prefix = "COREDUMP_EXE=";
113 else if (safe_atou(match, &pid) == 0)
114 prefix = "COREDUMP_PID=";
116 prefix = "COREDUMP_COMM=";
118 pattern = strjoin(prefix, match, NULL);
122 r = set_put(set, pattern);
124 log_error("failed to add pattern '%s': %s",
125 pattern, strerror(-r));
128 log_debug("Added pattern: %s", pattern);
133 log_error("failed to add match: %s", strerror(-r));
137 static int parse_argv(int argc, char *argv[]) {
145 static const struct option options[] = {
146 { "help", no_argument, NULL, 'h' },
147 { "version" , no_argument, NULL, ARG_VERSION },
148 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
149 { "output", required_argument, NULL, 'o' },
156 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
160 arg_action = ACTION_NONE;
164 puts(PACKAGE_STRING);
166 puts(SYSTEMD_FEATURES);
167 arg_action = ACTION_NONE;
176 log_error("cannot set output more than once");
180 output = fopen(optarg, "we");
182 log_error("writing to '%s': %m", optarg);
192 log_error("Unknown option code %c", c);
197 const char *cmd = argv[optind++];
198 if(streq(cmd, "list"))
199 arg_action = ACTION_LIST;
200 else if (streq(cmd, "dump"))
201 arg_action = ACTION_DUMP;
202 else if (streq(cmd, "gdb"))
203 arg_action = ACTION_GDB;
205 log_error("Unknown action '%s'", cmd);
210 while (optind < argc) {
211 r = add_match(matches, argv[optind]);
220 static int retrieve(const void *data,
227 field = strlen(name) + 1; /* name + "=" */
232 if (memcmp(data, name, field - 1) != 0)
235 if (((const char*) data)[field - 1] != '=')
238 *var = strndup((const char*)data + field, len - field);
245 static int print_entry(FILE* file, sd_journal *j, int had_header) {
246 const char _cleanup_free_
247 *pid = NULL, *uid = NULL, *gid = NULL,
248 *sgnl = NULL, *exe = NULL;
252 char buf[FORMAT_TIMESTAMP_MAX];
255 SD_JOURNAL_FOREACH_DATA(j, d, l) {
256 retrieve(d, l, "COREDUMP_PID", &pid);
257 retrieve(d, l, "COREDUMP_PID", &pid);
258 retrieve(d, l, "COREDUMP_UID", &uid);
259 retrieve(d, l, "COREDUMP_GID", &gid);
260 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
261 retrieve(d, l, "COREDUMP_EXE", &exe);
263 retrieve(d, l, "COREDUMP_COMM", &exe);
265 retrieve(d, l, "COREDUMP_CMDLINE", &exe);
268 if (!pid && !uid && !gid && !sgnl && !exe) {
269 log_warning("Empty coredump log entry");
273 r = sd_journal_get_realtime_usec(j, &t);
275 log_error("Failed to get realtime timestamp: %s", strerror(-r));
279 format_timestamp(buf, sizeof(buf), t);
282 fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
283 FORMAT_TIMESTAMP_MAX-1, "TIME",
290 fprintf(file, "%*s %*s %*s %*s %*s %s\n",
291 FORMAT_TIMESTAMP_MAX-1, buf,
301 static int dump_list(sd_journal *j) {
306 SD_JOURNAL_FOREACH(j)
307 print_entry(stdout, j, found++);
310 log_notice("No coredumps found");
317 static int focus(sd_journal *j) {
320 r = sd_journal_seek_tail(j);
322 r = sd_journal_previous(j);
324 log_error("Failed to search journal: %s", strerror(-r));
328 log_error("No match found");
334 static int dump_core(sd_journal* j) {
345 print_entry(output ? stdout : stderr, j, false);
347 if (on_tty() && !output) {
348 log_error("Refusing to dump core to tty");
352 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
354 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
359 data = (const uint8_t*) data + 9;
362 ret = fwrite(data, len, 1, output ? output : stdout);
364 log_error("dumping coredump: %m (%zu)", ret);
368 r = sd_journal_previous(j);
370 log_warning("More than one entry matches, ignoring rest.\n");
375 static int run_gdb(sd_journal *j) {
376 char path[] = "/var/tmp/coredump-XXXXXX";
381 _cleanup_free_ char *exe = NULL;
383 _cleanup_close_ int fd = -1;
392 print_entry(stdout, j, false);
394 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
396 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
401 data = (const uint8_t*) data + 13;
404 exe = strndup(data, len);
408 if (endswith(exe, " (deleted)")) {
409 log_error("Binary already deleted.");
413 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
415 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
420 data = (const uint8_t*) data + 9;
423 fd = mkostemp(path, O_WRONLY);
425 log_error("Failed to create temporary file: %m");
429 sz = write(fd, data, len);
431 log_error("Failed to write temporary file: %s", strerror(errno));
435 if (sz != (ssize_t) len) {
436 log_error("Short write to temporary file.");
441 close_nointr_nofail(fd);
446 log_error("Failed to fork(): %m");
451 execlp("gdb", "gdb", exe, path, NULL);
452 log_error("Failed to invoke gdb: %m");
456 r = wait_for_terminate(pid, &st);
458 log_error("Failed to wait for gdb: %m");
462 r = st.si_code == CLD_EXITED ? st.si_status : 255;
469 int main(int argc, char *argv[]) {
470 sd_journal *j = NULL;
475 log_parse_environment();
478 matches = new_matches();
482 if (parse_argv(argc, argv))
485 if (arg_action == ACTION_NONE)
488 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
490 log_error("Failed to open journal: %s", strerror(-r));
494 SET_FOREACH(match, matches, it) {
495 r = sd_journal_add_match(j, match, strlen(match));
497 log_error("Failed to add match '%s': %s",
498 match, strerror(-r));
521 assert_not_reached("Shouldn't be here");
528 set_free_free(matches);
535 return r >= 0 ? r : EXIT_FAILURE;