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' },
155 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
159 arg_action = ACTION_NONE;
163 puts(PACKAGE_STRING);
165 puts(SYSTEMD_FEATURES);
166 arg_action = ACTION_NONE;
175 log_error("cannot set output more than once");
179 output = fopen(optarg, "we");
181 log_error("writing to '%s': %m", optarg);
187 log_error("Unknown option code %c", c);
192 const char *cmd = argv[optind++];
193 if(streq(cmd, "list"))
194 arg_action = ACTION_LIST;
195 else if (streq(cmd, "dump"))
196 arg_action = ACTION_DUMP;
197 else if (streq(cmd, "gdb"))
198 arg_action = ACTION_GDB;
200 log_error("Unknown action '%s'", cmd);
205 while (optind < argc) {
206 r = add_match(matches, argv[optind]);
215 static int retrieve(const void *data,
222 field = strlen(name) + 1; /* name + "=" */
227 if (memcmp(data, name, field - 1) != 0)
230 if (((const char*) data)[field - 1] != '=')
233 *var = strndup((const char*)data + field, len - field);
240 static int print_entry(FILE* file, sd_journal *j, int had_header) {
241 const char _cleanup_free_
242 *pid = NULL, *uid = NULL, *gid = NULL,
243 *sgnl = NULL, *exe = NULL;
247 char buf[FORMAT_TIMESTAMP_MAX];
250 SD_JOURNAL_FOREACH_DATA(j, d, l) {
251 retrieve(d, l, "COREDUMP_PID", &pid);
252 retrieve(d, l, "COREDUMP_PID", &pid);
253 retrieve(d, l, "COREDUMP_UID", &uid);
254 retrieve(d, l, "COREDUMP_GID", &gid);
255 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
256 retrieve(d, l, "COREDUMP_EXE", &exe);
258 retrieve(d, l, "COREDUMP_COMM", &exe);
260 retrieve(d, l, "COREDUMP_CMDLINE", &exe);
263 if (!pid && !uid && !gid && !sgnl && !exe) {
264 log_warning("Empty coredump log entry");
268 r = sd_journal_get_realtime_usec(j, &t);
270 log_error("Failed to get realtime timestamp: %s", strerror(-r));
274 format_timestamp(buf, sizeof(buf), t);
277 fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
278 FORMAT_TIMESTAMP_MAX-1, "TIME",
285 fprintf(file, "%*s %*s %*s %*s %*s %s\n",
286 FORMAT_TIMESTAMP_MAX-1, buf,
296 static int dump_list(sd_journal *j) {
301 SD_JOURNAL_FOREACH(j)
302 print_entry(stdout, j, found++);
305 log_notice("No coredumps found");
312 static int focus(sd_journal *j) {
315 r = sd_journal_seek_tail(j);
317 r = sd_journal_previous(j);
319 log_error("Failed to search journal: %s", strerror(-r));
323 log_error("No match found");
329 static int dump_core(sd_journal* j) {
340 print_entry(output ? stdout : stderr, j, false);
342 if (on_tty() && !output) {
343 log_error("Refusing to dump core to tty");
347 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
349 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
354 data = (const uint8_t*) data + 9;
357 ret = fwrite(data, len, 1, output ? output : stdout);
359 log_error("dumping coredump: %m (%zu)", ret);
363 r = sd_journal_previous(j);
365 log_warning("More than one entry matches, ignoring rest.\n");
370 static int run_gdb(sd_journal *j) {
371 char path[] = "/var/tmp/coredump-XXXXXX";
376 _cleanup_free_ char *exe = NULL;
378 _cleanup_close_ int fd = -1;
387 print_entry(stdout, j, false);
389 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
391 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
396 data = (const uint8_t*) data + 13;
399 exe = strndup(data, len);
403 if (endswith(exe, " (deleted)")) {
404 log_error("Binary already deleted.");
408 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
410 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
415 data = (const uint8_t*) data + 9;
418 fd = mkostemp(path, O_WRONLY);
420 log_error("Failed to create temporary file: %m");
424 sz = write(fd, data, len);
426 log_error("Failed to write temporary file: %s", strerror(errno));
430 if (sz != (ssize_t) len) {
431 log_error("Short write to temporary file.");
436 close_nointr_nofail(fd);
441 log_error("Failed to fork(): %m");
446 execlp("gdb", "gdb", exe, path, NULL);
447 log_error("Failed to invoke gdb: %m");
451 r = wait_for_terminate(pid, &st);
453 log_error("Failed to wait for gdb: %m");
457 r = st.si_code == CLD_EXITED ? st.si_status : 255;
464 int main(int argc, char *argv[]) {
465 sd_journal *j = NULL;
470 log_parse_environment();
473 matches = new_matches();
477 if (parse_argv(argc, argv))
480 if (arg_action == ACTION_NONE)
483 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
485 log_error("Failed to open journal: %s", strerror(-r));
489 SET_FOREACH(match, matches, it) {
490 r = sd_journal_add_match(j, match, strlen(match));
492 log_error("Failed to add match '%s': %s",
493 match, strerror(-r));
516 assert_not_reached("Shouldn't be here");
523 set_free_free(matches);
530 return r >= 0 ? r : EXIT_FAILURE;