#include "bus-error.h"
#include "bus-util.h"
#include "compress.h"
+#include "def.h"
#include "fd-util.h"
-#include "fileio.h"
#include "fs-util.h"
#include "journal-internal.h"
#include "journal-util.h"
#include "log.h"
#include "macro.h"
+#include "main-func.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "sigbus.h"
#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
+#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
#include "verbs.h"
static const char* arg_field = NULL;
static const char *arg_debugger = NULL;
static const char *arg_directory = NULL;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static int arg_no_legend = false;
static int arg_one = false;
-static FILE* arg_output = NULL;
+static const char* arg_output = NULL;
static bool arg_reverse = false;
static bool arg_quiet = false;
}
static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("coredumpctl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...]\n\n"
"List or retrieve coredumps from the journal.\n\n"
"Flags:\n"
" info [MATCHES...] Show detailed information about one or more coredumps\n"
" dump [MATCHES...] Print first matching coredump to stdout\n"
" debug [MATCHES...] Start a debugger for the first matching coredump\n"
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
return 0;
}
return version();
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
break;
case 'o':
- if (arg_output) {
- log_error("Cannot set output more than once.");
- return -EINVAL;
- }
-
- arg_output = fopen(optarg, "we");
- if (!arg_output)
- return log_error_errno(errno, "writing to '%s': %m", optarg);
+ if (arg_output)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot set output more than once.");
+ arg_output = optarg;
break;
case 'S':
r = parse_timestamp(optarg, &arg_since);
if (r < 0)
- return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
+ return log_error_errno(r, "Failed to parse timestamp '%s': %m", optarg);
break;
case 'U':
r = parse_timestamp(optarg, &arg_until);
if (r < 0)
- return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
+ return log_error_errno(r, "Failed to parse timestamp '%s': %m", optarg);
break;
case 'F':
- if (arg_field) {
- log_error("Cannot use --field/-F more than once.");
- return -EINVAL;
- }
+ if (arg_field)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Cannot use --field/-F more than once.");
arg_field = optarg;
break;
}
if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
- arg_since > arg_until) {
- log_error("--since= must be before --until=.");
- return -EINVAL;
- }
+ arg_since > arg_until)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "--since= must be before --until=.");
return 1;
}
assert(arg_field);
/* A (user-specified) field may appear more than once for a given entry.
- * We will print all of the occurences.
+ * We will print all of the occurrences.
* This is different below for fields that systemd-coredump uses,
* because they cannot meaningfully appear more than once.
*/
r = sd_journal_previous(j);
if (r < 0)
return log_error_errno(r, "Failed to search journal: %m");
- if (r == 0) {
- log_error("No match found.");
- return -ESRCH;
- }
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+ "No match found.");
return r;
}
if (r < 0)
return r;
- (void) pager_open(arg_no_pager, false);
+ (void) pager_open(arg_pager_flags);
/* The coredumps are likely to compressed, and for just
* listing them we don't need to decompress them, so let's
/* If neither path or file are specified, we will write to stdout. Let's now check
* if stdout is connected to a tty. We checked that the file exists, or that the
* core might be stored in the journal. In this second case, if we found the entry,
- * in all likelyhood we will be able to access the COREDUMP= field. In either case,
+ * in all likelihood we will be able to access the COREDUMP= field. In either case,
* we stop before doing any "real" work, i.e. before starting decompression or
* reading from the file or creating temporary files.
*/
if (!file) {
if (on_tty())
- return log_error_errno(ENOTTY, "Refusing to dump core to tty"
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
+ "Refusing to dump core to tty"
" (use shell redirection or specify --output).");
file = stdout;
}
error:
if (temp) {
- unlink(temp);
+ (void) unlink(temp);
log_debug("Removed temporary file %s", temp);
}
return r;
static int dump_core(int argc, char **argv, void *userdata) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
int r;
if (arg_field) {
if (r < 0)
return r;
- print_info(arg_output ? stdout : stderr, j, false);
+ if (arg_output) {
+ f = fopen(arg_output, "we");
+ if (!f)
+ return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", arg_output);
+ }
+
+ print_info(f ? stdout : stderr, j, false);
- r = save_core(j, arg_output, NULL, NULL);
+ r = save_core(j, f, NULL, NULL);
if (r < 0)
return r;
static int run_debug(int argc, char **argv, void *userdata) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
- _cleanup_free_ char *exe = NULL, *path = NULL;
+ _cleanup_free_ char *exe = NULL, *path = NULL, *debugger = NULL;
bool unlink_path = false;
const char *data, *fork_name;
size_t len;
arg_debugger = "gdb";
}
+ debugger = strdup(arg_debugger);
+ if (!debugger)
+ return -ENOMEM;
+
if (arg_field) {
log_error("Option --field/-F only makes sense with list");
return -EINVAL;
/* Don't interfere with gdb and its handling of SIGINT. */
(void) ignore_signals(SIGINT, -1);
- fork_name = strjoina("(", arg_debugger, ")");
+ fork_name = strjoina("(", debugger, ")");
- r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+ r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
goto finish;
if (r == 0) {
- execlp(arg_debugger, arg_debugger, exe, "-c", path, NULL);
+ execlp(debugger, debugger, exe, "-c", path, NULL);
log_open();
- log_error_errno(errno, "Failed to invoke %s: %m", arg_debugger);
+ log_error_errno(errno, "Failed to invoke %s: %m", debugger);
_exit(EXIT_FAILURE);
}
- r = wait_for_terminate_and_check(arg_debugger, pid, WAIT_LOG_ABNORMAL);
+ r = wait_for_terminate_and_check(debugger, pid, WAIT_LOG_ABNORMAL);
finish:
(void) default_signals(SIGINT, -1);
if (unlink_path) {
log_debug("Removed temporary file %s", path);
- unlink(path);
+ (void) unlink(path);
}
return r;
}
static int check_units_active(void) {
- _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
return dispatch_verb(argc, argv, verbs, NULL);
}
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
int r, units_active;
setlocale(LC_ALL, "");
+ log_show_color(true);
log_parse_environment();
log_open();
+ /* The journal merging logic potentially needs a lot of fds. */
+ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
r = parse_argv(argc, argv);
if (r <= 0)
- goto end;
+ return r;
sigbus_install();
ansi_highlight_red(),
units_active, units_active == 1 ? "unit is running" : "units are running",
ansi_normal());
-end:
- pager_close();
-
- safe_fclose(arg_output);
-
- return r >= 0 ? r : EXIT_FAILURE;
+ return r;
}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);