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 " -F --field=FIELD List all values a certain field takes\n"
91 " gdb Start gdb for the first matching coredump\n"
92 " list List available coredumps\n"
93 " dump PID Print coredump to stdout\n"
94 " dump PATH Print coredump to stdout\n"
95 , program_invocation_short_name);
100 static int add_match(Set *set, const char *match) {
104 char *pattern = NULL;
105 char _cleanup_free_ *p = NULL;
107 if (strchr(match, '='))
109 else if (strchr(match, '/')) {
110 p = path_make_absolute_cwd(match);
115 prefix = "COREDUMP_EXE=";
117 else if (safe_atou(match, &pid) == 0)
118 prefix = "COREDUMP_PID=";
120 prefix = "COREDUMP_COMM=";
122 pattern = strjoin(prefix, match, NULL);
126 r = set_put(set, pattern);
128 log_error("failed to add pattern '%s': %s",
129 pattern, strerror(-r));
132 log_debug("Added pattern: %s", pattern);
137 log_error("failed to add match: %s", strerror(-r));
141 static int parse_argv(int argc, char *argv[]) {
150 static const struct option options[] = {
151 { "help", no_argument, NULL, 'h' },
152 { "version" , no_argument, NULL, ARG_VERSION },
153 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
154 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
155 { "output", required_argument, NULL, 'o' },
156 { "field", required_argument, NULL, 'F' },
163 while ((c = getopt_long(argc, argv, "ho:F:", options, NULL)) >= 0)
167 arg_action = ACTION_NONE;
171 puts(PACKAGE_STRING);
173 puts(SYSTEMD_FEATURES);
174 arg_action = ACTION_NONE;
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 log_error("Unknown option code %c", c);
217 const char *cmd = argv[optind++];
218 if(streq(cmd, "list"))
219 arg_action = ACTION_LIST;
220 else if (streq(cmd, "dump"))
221 arg_action = ACTION_DUMP;
222 else if (streq(cmd, "gdb"))
223 arg_action = ACTION_GDB;
225 log_error("Unknown action '%s'", cmd);
230 if (field && arg_action != ACTION_LIST) {
231 log_error("Option --field/-F only makes sense with list");
235 while (optind < argc) {
236 r = add_match(matches, argv[optind]);
245 static int retrieve(const void *data,
252 ident = strlen(name) + 1; /* name + "=" */
257 if (memcmp(data, name, ident - 1) != 0)
260 if (((const char*) data)[ident - 1] != '=')
263 *var = strndup((const char*)data + ident, len - ident);
270 static void print_field(FILE* file, sd_journal *j) {
271 const char _cleanup_free_ *value = NULL;
277 SD_JOURNAL_FOREACH_DATA(j, d, l)
278 retrieve(d, l, field, &value);
280 fprintf(file, "%s\n", value);
283 static int print_entry(FILE* file, sd_journal *j, int had_legend) {
284 const char _cleanup_free_
285 *pid = NULL, *uid = NULL, *gid = NULL,
286 *sgnl = NULL, *exe = NULL;
290 char buf[FORMAT_TIMESTAMP_MAX];
293 SD_JOURNAL_FOREACH_DATA(j, d, l) {
294 retrieve(d, l, "COREDUMP_PID", &pid);
295 retrieve(d, l, "COREDUMP_PID", &pid);
296 retrieve(d, l, "COREDUMP_UID", &uid);
297 retrieve(d, l, "COREDUMP_GID", &gid);
298 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
299 retrieve(d, l, "COREDUMP_EXE", &exe);
301 retrieve(d, l, "COREDUMP_COMM", &exe);
303 retrieve(d, l, "COREDUMP_CMDLINE", &exe);
306 if (!pid && !uid && !gid && !sgnl && !exe) {
307 log_warning("Empty coredump log entry");
311 r = sd_journal_get_realtime_usec(j, &t);
313 log_error("Failed to get realtime timestamp: %s", strerror(-r));
317 format_timestamp(buf, sizeof(buf), t);
319 if (!had_legend && !arg_no_legend)
320 fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
321 FORMAT_TIMESTAMP_MAX-1, "TIME",
328 fprintf(file, "%*s %*s %*s %*s %*s %s\n",
329 FORMAT_TIMESTAMP_MAX-1, buf,
339 static int dump_list(sd_journal *j) {
344 SD_JOURNAL_FOREACH(j) {
346 print_field(stdout, j);
348 print_entry(stdout, j, found++);
351 if (!field && !found) {
352 log_notice("No coredumps found");
359 static int focus(sd_journal *j) {
362 r = sd_journal_seek_tail(j);
364 r = sd_journal_previous(j);
366 log_error("Failed to search journal: %s", strerror(-r));
370 log_error("No match found");
376 static int dump_core(sd_journal* j) {
387 print_entry(output ? stdout : stderr, j, false);
389 if (on_tty() && !output) {
390 log_error("Refusing to dump core to tty");
394 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
396 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
401 data = (const uint8_t*) data + 9;
404 ret = fwrite(data, len, 1, output ? output : stdout);
406 log_error("dumping coredump: %m (%zu)", ret);
410 r = sd_journal_previous(j);
412 log_warning("More than one entry matches, ignoring rest.\n");
417 static int run_gdb(sd_journal *j) {
418 char path[] = "/var/tmp/coredump-XXXXXX";
423 _cleanup_free_ char *exe = NULL;
425 _cleanup_close_ int fd = -1;
434 print_entry(stdout, j, false);
436 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
438 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
443 data = (const uint8_t*) data + 13;
446 exe = strndup(data, len);
450 if (endswith(exe, " (deleted)")) {
451 log_error("Binary already deleted.");
455 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
457 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
462 data = (const uint8_t*) data + 9;
465 fd = mkostemp(path, O_WRONLY);
467 log_error("Failed to create temporary file: %m");
471 sz = write(fd, data, len);
473 log_error("Failed to write temporary file: %s", strerror(errno));
477 if (sz != (ssize_t) len) {
478 log_error("Short write to temporary file.");
483 close_nointr_nofail(fd);
488 log_error("Failed to fork(): %m");
493 execlp("gdb", "gdb", exe, path, NULL);
494 log_error("Failed to invoke gdb: %m");
498 r = wait_for_terminate(pid, &st);
500 log_error("Failed to wait for gdb: %m");
504 r = st.si_code == CLD_EXITED ? st.si_status : 255;
511 int main(int argc, char *argv[]) {
512 sd_journal *j = NULL;
517 log_parse_environment();
520 matches = new_matches();
526 r = parse_argv(argc, argv);
530 if (arg_action == ACTION_NONE)
533 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
535 log_error("Failed to open journal: %s", strerror(-r));
539 SET_FOREACH(match, matches, it) {
540 r = sd_journal_add_match(j, match, strlen(match));
542 log_error("Failed to add match '%s': %s",
543 match, strerror(-r));
566 assert_not_reached("Shouldn't be here");
573 set_free_free(matches);
580 return r >= 0 ? r : EXIT_FAILURE;