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 " list List available coredumps\n"
91 " dump PID Print coredump to stdout\n"
92 " dump PATH Print coredump to stdout\n"
93 , program_invocation_short_name);
98 static int add_match(Set *set, const char *match) {
102 char *pattern = NULL;
103 char _cleanup_free_ *p = NULL;
105 if (strchr(match, '='))
107 else if (strchr(match, '/')) {
108 p = path_make_absolute_cwd(match);
113 prefix = "COREDUMP_EXE=";
115 else if (safe_atou(match, &pid) == 0)
116 prefix = "COREDUMP_PID=";
118 prefix = "COREDUMP_COMM=";
120 pattern = strjoin(prefix, match, NULL);
124 r = set_put(set, pattern);
126 log_error("failed to add pattern '%s': %s",
127 pattern, strerror(-r));
130 log_debug("Added pattern: %s", pattern);
135 log_error("failed to add match: %s", strerror(-r));
139 static int parse_argv(int argc, char *argv[]) {
148 static const struct option options[] = {
149 { "help", no_argument, NULL, 'h' },
150 { "version" , no_argument, NULL, ARG_VERSION },
151 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
152 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
153 { "output", required_argument, NULL, 'o' },
154 { "field", required_argument, NULL, 'F' },
161 while ((c = getopt_long(argc, argv, "ho:F:", options, NULL)) >= 0)
165 arg_action = ACTION_NONE;
169 puts(PACKAGE_STRING);
171 puts(SYSTEMD_FEATURES);
172 arg_action = ACTION_NONE;
180 arg_no_legend = true;
185 log_error("cannot set output more than once");
189 output = fopen(optarg, "we");
191 log_error("writing to '%s': %m", optarg);
199 log_error("cannot use --field/-F more than once");
210 log_error("Unknown option code %c", c);
215 const char *cmd = argv[optind++];
216 if(streq(cmd, "list"))
217 arg_action = ACTION_LIST;
218 else if (streq(cmd, "dump"))
219 arg_action = ACTION_DUMP;
220 else if (streq(cmd, "gdb"))
221 arg_action = ACTION_GDB;
223 log_error("Unknown action '%s'", cmd);
228 if (field && arg_action != ACTION_LIST) {
229 log_error("Option --field/-F only makes sense with list");
233 while (optind < argc) {
234 r = add_match(matches, argv[optind]);
243 static int retrieve(const void *data,
250 ident = strlen(name) + 1; /* name + "=" */
255 if (memcmp(data, name, ident - 1) != 0)
258 if (((const char*) data)[ident - 1] != '=')
261 *var = strndup((const char*)data + ident, len - ident);
268 static void print_field(FILE* file, sd_journal *j) {
269 const char _cleanup_free_ *value = NULL;
275 SD_JOURNAL_FOREACH_DATA(j, d, l)
276 retrieve(d, l, field, &value);
278 fprintf(file, "%s\n", value);
281 static int print_entry(FILE* file, sd_journal *j, int had_legend) {
282 const char _cleanup_free_
283 *pid = NULL, *uid = NULL, *gid = NULL,
284 *sgnl = NULL, *exe = NULL;
288 char buf[FORMAT_TIMESTAMP_MAX];
291 SD_JOURNAL_FOREACH_DATA(j, d, l) {
292 retrieve(d, l, "COREDUMP_PID", &pid);
293 retrieve(d, l, "COREDUMP_PID", &pid);
294 retrieve(d, l, "COREDUMP_UID", &uid);
295 retrieve(d, l, "COREDUMP_GID", &gid);
296 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
297 retrieve(d, l, "COREDUMP_EXE", &exe);
299 retrieve(d, l, "COREDUMP_COMM", &exe);
301 retrieve(d, l, "COREDUMP_CMDLINE", &exe);
304 if (!pid && !uid && !gid && !sgnl && !exe) {
305 log_warning("Empty coredump log entry");
309 r = sd_journal_get_realtime_usec(j, &t);
311 log_error("Failed to get realtime timestamp: %s", strerror(-r));
315 format_timestamp(buf, sizeof(buf), t);
317 if (!had_legend && !arg_no_legend)
318 fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
319 FORMAT_TIMESTAMP_MAX-1, "TIME",
326 fprintf(file, "%*s %*s %*s %*s %*s %s\n",
327 FORMAT_TIMESTAMP_MAX-1, buf,
337 static int dump_list(sd_journal *j) {
342 SD_JOURNAL_FOREACH(j) {
344 print_field(stdout, j);
346 print_entry(stdout, j, found++);
349 if (!field && !found) {
350 log_notice("No coredumps found");
357 static int focus(sd_journal *j) {
360 r = sd_journal_seek_tail(j);
362 r = sd_journal_previous(j);
364 log_error("Failed to search journal: %s", strerror(-r));
368 log_error("No match found");
374 static int dump_core(sd_journal* j) {
385 print_entry(output ? stdout : stderr, j, false);
387 if (on_tty() && !output) {
388 log_error("Refusing to dump core to tty");
392 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
394 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
399 data = (const uint8_t*) data + 9;
402 ret = fwrite(data, len, 1, output ? output : stdout);
404 log_error("dumping coredump: %m (%zu)", ret);
408 r = sd_journal_previous(j);
410 log_warning("More than one entry matches, ignoring rest.\n");
415 static int run_gdb(sd_journal *j) {
416 char path[] = "/var/tmp/coredump-XXXXXX";
421 _cleanup_free_ char *exe = NULL;
423 _cleanup_close_ int fd = -1;
432 print_entry(stdout, j, false);
434 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
436 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
441 data = (const uint8_t*) data + 13;
444 exe = strndup(data, len);
448 if (endswith(exe, " (deleted)")) {
449 log_error("Binary already deleted.");
453 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
455 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
460 data = (const uint8_t*) data + 9;
463 fd = mkostemp(path, O_WRONLY);
465 log_error("Failed to create temporary file: %m");
469 sz = write(fd, data, len);
471 log_error("Failed to write temporary file: %s", strerror(errno));
475 if (sz != (ssize_t) len) {
476 log_error("Short write to temporary file.");
481 close_nointr_nofail(fd);
486 log_error("Failed to fork(): %m");
491 execlp("gdb", "gdb", exe, path, NULL);
492 log_error("Failed to invoke gdb: %m");
496 r = wait_for_terminate(pid, &st);
498 log_error("Failed to wait for gdb: %m");
502 r = st.si_code == CLD_EXITED ? st.si_status : 255;
509 int main(int argc, char *argv[]) {
510 sd_journal *j = NULL;
515 log_parse_environment();
518 matches = new_matches();
524 r = parse_argv(argc, argv);
528 if (arg_action == ACTION_NONE)
531 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
533 log_error("Failed to open journal: %s", strerror(-r));
537 SET_FOREACH(match, matches, it) {
538 r = sd_journal_add_match(j, match, strlen(match));
540 log_error("Failed to add match '%s': %s",
541 match, strerror(-r));
564 assert_not_reached("Shouldn't be here");
571 set_free_free(matches);
578 return r >= 0 ? r : EXIT_FAILURE;