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;
48 static int arg_no_legend = false;
50 static Set *new_matches(void) {
55 set = set_new(trivial_hash_func, trivial_compare_func);
61 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
68 r = set_put(set, tmp);
70 log_error("failed to add to set: %s", strerror(-r));
79 static int help(void) {
80 printf("%s [OPTIONS...] [MATCHES...]\n\n"
81 "List or retrieve coredumps from the journal.\n\n"
83 " -o --output=FILE Write output to FILE\n"
84 " --no-pager Do not pipe output into a pager\n"
87 " -h --help Show this help\n"
88 " --version Print version string\n"
89 " list List available coredumps\n"
90 " dump PID Print coredump to stdout\n"
91 " dump PATH Print coredump to stdout\n"
92 , program_invocation_short_name);
97 static int add_match(Set *set, const char *match) {
101 char *pattern = NULL;
102 char _cleanup_free_ *p = NULL;
104 if (strchr(match, '='))
106 else if (strchr(match, '/')) {
107 p = path_make_absolute_cwd(match);
112 prefix = "COREDUMP_EXE=";
114 else if (safe_atou(match, &pid) == 0)
115 prefix = "COREDUMP_PID=";
117 prefix = "COREDUMP_COMM=";
119 pattern = strjoin(prefix, match, NULL);
123 r = set_put(set, pattern);
125 log_error("failed to add pattern '%s': %s",
126 pattern, strerror(-r));
129 log_debug("Added pattern: %s", pattern);
134 log_error("failed to add match: %s", strerror(-r));
138 static int parse_argv(int argc, char *argv[]) {
147 static const struct option options[] = {
148 { "help", no_argument, NULL, 'h' },
149 { "version" , no_argument, NULL, ARG_VERSION },
150 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
151 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
152 { "output", required_argument, NULL, 'o' },
159 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
163 arg_action = ACTION_NONE;
167 puts(PACKAGE_STRING);
169 puts(SYSTEMD_FEATURES);
170 arg_action = ACTION_NONE;
178 arg_no_legend = true;
183 log_error("cannot set output more than once");
187 output = fopen(optarg, "we");
189 log_error("writing to '%s': %m", optarg);
199 log_error("Unknown option code %c", c);
204 const char *cmd = argv[optind++];
205 if(streq(cmd, "list"))
206 arg_action = ACTION_LIST;
207 else if (streq(cmd, "dump"))
208 arg_action = ACTION_DUMP;
209 else if (streq(cmd, "gdb"))
210 arg_action = ACTION_GDB;
212 log_error("Unknown action '%s'", cmd);
217 while (optind < argc) {
218 r = add_match(matches, argv[optind]);
227 static int retrieve(const void *data,
234 field = strlen(name) + 1; /* name + "=" */
239 if (memcmp(data, name, field - 1) != 0)
242 if (((const char*) data)[field - 1] != '=')
245 *var = strndup((const char*)data + field, len - field);
252 static int print_entry(FILE* file, sd_journal *j, int had_legend) {
253 const char _cleanup_free_
254 *pid = NULL, *uid = NULL, *gid = NULL,
255 *sgnl = NULL, *exe = NULL;
259 char buf[FORMAT_TIMESTAMP_MAX];
262 SD_JOURNAL_FOREACH_DATA(j, d, l) {
263 retrieve(d, l, "COREDUMP_PID", &pid);
264 retrieve(d, l, "COREDUMP_PID", &pid);
265 retrieve(d, l, "COREDUMP_UID", &uid);
266 retrieve(d, l, "COREDUMP_GID", &gid);
267 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
268 retrieve(d, l, "COREDUMP_EXE", &exe);
270 retrieve(d, l, "COREDUMP_COMM", &exe);
272 retrieve(d, l, "COREDUMP_CMDLINE", &exe);
275 if (!pid && !uid && !gid && !sgnl && !exe) {
276 log_warning("Empty coredump log entry");
280 r = sd_journal_get_realtime_usec(j, &t);
282 log_error("Failed to get realtime timestamp: %s", strerror(-r));
286 format_timestamp(buf, sizeof(buf), t);
288 if (!had_legend && !arg_no_legend)
289 fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
290 FORMAT_TIMESTAMP_MAX-1, "TIME",
297 fprintf(file, "%*s %*s %*s %*s %*s %s\n",
298 FORMAT_TIMESTAMP_MAX-1, buf,
308 static int dump_list(sd_journal *j) {
313 SD_JOURNAL_FOREACH(j)
314 print_entry(stdout, j, found++);
317 log_notice("No coredumps found");
324 static int focus(sd_journal *j) {
327 r = sd_journal_seek_tail(j);
329 r = sd_journal_previous(j);
331 log_error("Failed to search journal: %s", strerror(-r));
335 log_error("No match found");
341 static int dump_core(sd_journal* j) {
352 print_entry(output ? stdout : stderr, j, false);
354 if (on_tty() && !output) {
355 log_error("Refusing to dump core to tty");
359 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
361 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
366 data = (const uint8_t*) data + 9;
369 ret = fwrite(data, len, 1, output ? output : stdout);
371 log_error("dumping coredump: %m (%zu)", ret);
375 r = sd_journal_previous(j);
377 log_warning("More than one entry matches, ignoring rest.\n");
382 static int run_gdb(sd_journal *j) {
383 char path[] = "/var/tmp/coredump-XXXXXX";
388 _cleanup_free_ char *exe = NULL;
390 _cleanup_close_ int fd = -1;
399 print_entry(stdout, j, false);
401 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
403 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
408 data = (const uint8_t*) data + 13;
411 exe = strndup(data, len);
415 if (endswith(exe, " (deleted)")) {
416 log_error("Binary already deleted.");
420 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
422 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
427 data = (const uint8_t*) data + 9;
430 fd = mkostemp(path, O_WRONLY);
432 log_error("Failed to create temporary file: %m");
436 sz = write(fd, data, len);
438 log_error("Failed to write temporary file: %s", strerror(errno));
442 if (sz != (ssize_t) len) {
443 log_error("Short write to temporary file.");
448 close_nointr_nofail(fd);
453 log_error("Failed to fork(): %m");
458 execlp("gdb", "gdb", exe, path, NULL);
459 log_error("Failed to invoke gdb: %m");
463 r = wait_for_terminate(pid, &st);
465 log_error("Failed to wait for gdb: %m");
469 r = st.si_code == CLD_EXITED ? st.si_status : 255;
476 int main(int argc, char *argv[]) {
477 sd_journal *j = NULL;
482 log_parse_environment();
485 matches = new_matches();
491 r = parse_argv(argc, argv);
495 if (arg_action == ACTION_NONE)
498 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
500 log_error("Failed to open journal: %s", strerror(-r));
504 SET_FOREACH(match, matches, it) {
505 r = sd_journal_add_match(j, match, strlen(match));
507 log_error("Failed to add match '%s': %s",
508 match, strerror(-r));
531 assert_not_reached("Shouldn't be here");
538 set_free_free(matches);
545 return r >= 0 ? r : EXIT_FAILURE;