summaryrefslogtreecommitdiffstats
path: root/src/cli/CLI.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli/CLI.cpp')
-rw-r--r--src/cli/CLI.cpp436
1 files changed, 436 insertions, 0 deletions
diff --git a/src/cli/CLI.cpp b/src/cli/CLI.cpp
new file mode 100644
index 00000000..276703dc
--- /dev/null
+++ b/src/cli/CLI.cpp
@@ -0,0 +1,436 @@
+/*
+ Copyright (C) 2009, 2010 Red Hat, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <getopt.h>
+#include "abrt_exception.h"
+#include "abrtlib.h"
+#include "abrt_dbus.h"
+#include "dbus_common.h"
+#include "report.h"
+#include "dbus.h"
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(S) gettext(S)
+#else
+# define _(S) (S)
+#endif
+
+/** Creates a localized string from crash time. */
+static char *localize_crash_time(const char *timestr)
+{
+ long time = xatou(timestr);
+ char timeloc[256];
+ int success = strftime(timeloc, 128, "%c", localtime(&time));
+ if (!success)
+ error_msg_and_die("Error while converting time to string");
+ return xasprintf("%s", timeloc);
+}
+
+/** Prints basic information about a crash to stdout. */
+static void print_crash(const map_crash_data_t &crash)
+{
+ /* Create a localized string from crash time. */
+ const char *timestr = get_crash_data_item_content(crash, FILENAME_TIME).c_str();
+ const char *timeloc = localize_crash_time(timestr);
+
+ printf(_("\tUID : %s\n"
+ "\tUUID : %s\n"
+ "\tPackage : %s\n"
+ "\tExecutable : %s\n"
+ "\tCrash Time : %s\n"
+ "\tCrash Count: %s\n"),
+ get_crash_data_item_content(crash, CD_UID).c_str(),
+ get_crash_data_item_content(crash, CD_UUID).c_str(),
+ get_crash_data_item_content(crash, FILENAME_PACKAGE).c_str(),
+ get_crash_data_item_content(crash, FILENAME_EXECUTABLE).c_str(),
+ timeloc,
+ get_crash_data_item_content(crash, CD_COUNT).c_str());
+
+ free((void *)timeloc);
+
+ /* Print the hostname if it's available. */
+ const char *hostname = get_crash_data_item_content_or_NULL(crash, FILENAME_HOSTNAME);
+ if (hostname)
+ printf(_("\tHostname : %s\n"), hostname);
+}
+
+/**
+ * Prints a list containing "crashes" to stdout.
+ * @param include_reported
+ * Do not skip entries marked as already reported.
+ */
+static void print_crash_list(const vector_map_crash_data_t& crash_list, bool include_reported)
+{
+ for (unsigned i = 0; i < crash_list.size(); ++i)
+ {
+ const map_crash_data_t& crash = crash_list[i];
+ if (get_crash_data_item_content(crash, CD_REPORTED) == "1" && !include_reported)
+ continue;
+
+ printf("%u.\n", i);
+ print_crash(crash);
+ }
+}
+
+/**
+ * Prints full information about a crash
+ */
+static void print_crash_info(const map_crash_data_t& crash, bool show_backtrace)
+{
+ const char *timestr = get_crash_data_item_content(crash, FILENAME_TIME).c_str();
+ const char *timeloc = localize_crash_time(timestr);
+
+ printf(_("Crash ID: %s:%s\n"
+ "Last crash: %s\n"
+ "Analyzer: %s\n"
+ "Component: %s\n"
+ "Package: %s\n"
+ "Command: %s\n"
+ "Executable: %s\n"
+ "System: %s, kernel %s\n"
+ "Rating: %s\n"
+ "Coredump file: %s\n"
+ "Reason: %s\n"),
+ get_crash_data_item_content(crash, CD_UID).c_str(),
+ get_crash_data_item_content(crash, CD_UUID).c_str(),
+ timeloc,
+ get_crash_data_item_content(crash, FILENAME_ANALYZER).c_str(),
+ get_crash_data_item_content(crash, FILENAME_COMPONENT).c_str(),
+ get_crash_data_item_content(crash, FILENAME_PACKAGE).c_str(),
+ get_crash_data_item_content(crash, FILENAME_CMDLINE).c_str(),
+ get_crash_data_item_content(crash, FILENAME_EXECUTABLE).c_str(),
+ get_crash_data_item_content(crash, FILENAME_RELEASE).c_str(),
+ get_crash_data_item_content(crash, FILENAME_KERNEL).c_str(),
+ get_crash_data_item_content(crash, FILENAME_RATING).c_str(),
+ get_crash_data_item_content(crash, FILENAME_COREDUMP).c_str(),
+ get_crash_data_item_content(crash, FILENAME_REASON).c_str());
+
+ free((void *)timeloc);
+
+ /* print only if available */
+ const char *crash_function = get_crash_data_item_content_or_NULL(crash, FILENAME_CRASH_FUNCTION);
+ if (crash_function)
+ printf(_("Crash function: %s\n"), crash_function);
+
+ const char *hostname = get_crash_data_item_content_or_NULL(crash, FILENAME_HOSTNAME);
+ if (hostname)
+ printf(_("Hostname: %s\n"), hostname);
+
+ const char *reproduce = get_crash_data_item_content_or_NULL(crash, FILENAME_REPRODUCE);
+ if (reproduce)
+ printf(_("\nHow to reproduce:\n%s\n"), reproduce);
+
+ const char *comment = get_crash_data_item_content_or_NULL(crash, FILENAME_COMMENT);
+ if (comment)
+ printf(_("\nComment:\n%s\n"), comment);
+
+ if (show_backtrace)
+ {
+ const char *backtrace = get_crash_data_item_content_or_NULL(crash, FILENAME_BACKTRACE);
+ if (backtrace)
+ printf(_("\nBacktrace:\n%s\n"), backtrace);
+ }
+}
+
+/**
+ * Converts crash reference from user's input to unique crash identification
+ * in form UID:UUID.
+ * The returned string must be released by caller.
+ */
+static char *guess_crash_id(const char *str)
+{
+ vector_map_crash_data_t ci = call_GetCrashInfos();
+ unsigned num_crashinfos = ci.size();
+ if (str[0] == '@') /* "--report @N" syntax */
+ {
+ unsigned position = xatoi_u(str + 1);
+ if (position >= num_crashinfos)
+ error_msg_and_die("There are only %u crash infos", num_crashinfos);
+ map_crash_data_t& info = ci[position];
+ return xasprintf("%s:%s",
+ get_crash_data_item_content(info, CD_UID).c_str(),
+ get_crash_data_item_content(info, CD_UUID).c_str()
+ );
+ }
+
+ unsigned len = strlen(str);
+ unsigned ii;
+ char *result = NULL;
+ for (ii = 0; ii < num_crashinfos; ii++)
+ {
+ map_crash_data_t& info = ci[ii];
+ const char *this_uuid = get_crash_data_item_content(info, CD_UUID).c_str();
+ if (strncmp(str, this_uuid, len) == 0)
+ {
+ if (result)
+ error_msg_and_die("Crash prefix '%s' is not unique", str);
+ result = xasprintf("%s:%s",
+ get_crash_data_item_content(info, CD_UID).c_str(),
+ this_uuid
+ );
+ }
+ }
+ if (!result)
+ error_msg_and_die("Crash '%s' not found", str);
+ return result;
+}
+
+/* Program options */
+enum
+{
+ OPT_GET_LIST,
+ OPT_REPORT,
+ OPT_DELETE,
+ OPT_INFO
+};
+
+/**
+ * Long options.
+ * Do not use the has_arg field. Arguments are handled after parsing all options.
+ * The reason is that we want to use all the following combinations:
+ * --report ID
+ * --report ID --always
+ * --report --always ID
+ */
+static const struct option longopts[] =
+{
+ /* name, has_arg, flag, val */
+ { "help" , no_argument, NULL, '?' },
+ { "version" , no_argument, NULL, 'V' },
+ { "list" , no_argument, NULL, 'l' },
+ { "full" , no_argument, NULL, 'f' },
+ { "always" , no_argument, NULL, 'y' },
+ { "report" , no_argument, NULL, 'r' },
+ { "delete" , no_argument, NULL, 'd' },
+ { "info" , no_argument, NULL, 'i' },
+ { "backtrace", no_argument, NULL, 'b' },
+ { 0, 0, 0, 0 } /* prevents crashes for unknown options*/
+};
+
+/* Gets the program name from the first command line argument. */
+static const char *progname(const char *argv0)
+{
+ const char* name = strrchr(argv0, '/');
+ if (name)
+ return ++name;
+ return argv0;
+}
+
+/**
+ * Prints abrt-cli version and some help text.
+ * Then exits the program with return value 1.
+ */
+static void usage(char *argv0)
+{
+ const char *name = progname(argv0);
+ printf("%s "VERSION"\n\n", name);
+
+ /* Message has embedded tabs. */
+ printf(_("Usage: %s [OPTION]\n\n"
+ "Startup:\n"
+ " -V, --version display the version of %s and exit\n"
+ " -?, --help print this help\n\n"
+ "Actions:\n"
+ " -l, --list print a list of all crashes which are not yet reported\n"
+ " -f, --full print a list of all crashes, including the already reported ones\n"
+ " -r, --report CRASH_ID create and send a report\n"
+ " -y, --always create and send a report without asking\n"
+ " -d, --delete CRASH_ID remove a crash\n"
+ " -i, --info CRASH_ID print detailed information about a crash\n"
+ " -b, --backtrace print detailed information about a crash including backtrace\n"
+ "CRASH_ID can be:\n"
+ " UID:UUID pair,\n"
+ " unique UUID prefix - the crash with matching UUID will be acted upon\n"
+ " @N - N'th crash (as displayed by --list --full) will be acted upon\n"
+ ),
+ name, name);
+
+ exit(1);
+}
+
+int main(int argc, char** argv)
+{
+ const char* crash_id = NULL;
+ int op = -1;
+ bool full = false;
+ bool always = false;
+ bool backtrace = false;
+
+ setlocale(LC_ALL, "");
+#if ENABLE_NLS
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+ while (1)
+ {
+ int option_index;
+ /* Do not use colons, arguments are handled after parsing all options. */
+ int c = getopt_long_only(argc, argv, "?Vrdlfyib",
+ longopts, &option_index);
+
+#define SET_OP(newop) \
+ if (op != -1 && op != newop) \
+ { \
+ error_msg(_("You must specify exactly one operation")); \
+ return 1; \
+ } \
+ op = newop;
+
+ switch (c)
+ {
+ case 'r': SET_OP(OPT_REPORT); break;
+ case 'd': SET_OP(OPT_DELETE); break;
+ case 'l': SET_OP(OPT_GET_LIST); break;
+ case 'i': SET_OP(OPT_INFO); break;
+ case 'f': full = true; break;
+ case 'y': always = true; break;
+ case 'b': backtrace = true; break;
+ case -1: /* end of options */ break;
+ default: /* some error */
+ case '?':
+ usage(argv[0]); /* exits app */
+ case 'V':
+ printf("%s "VERSION"\n", progname(argv[0]));
+ return 0;
+ }
+#undef SET_OP
+ if (c == -1)
+ break;
+ }
+
+ /* Handle option arguments. */
+ int arg_count = argc - optind;
+ switch (arg_count)
+ {
+ case 0:
+ if (op == OPT_REPORT || op == OPT_DELETE || op == OPT_INFO)
+ usage(argv[0]);
+ break;
+ case 1:
+ if (op != OPT_REPORT && op != OPT_DELETE && op != OPT_INFO)
+ usage(argv[0]);
+ crash_id = argv[optind];
+ break;
+ default:
+ usage(argv[0]);
+ }
+
+ /* Check if we have an operation.
+ * Limit --full and --always to certain operations.
+ */
+ if ((full && op != OPT_GET_LIST) ||
+ (always && op != OPT_REPORT) ||
+ (backtrace && op != OPT_INFO) ||
+ op == -1)
+ {
+ usage(argv[0]);
+ return 1;
+ }
+
+ DBusError err;
+ dbus_error_init(&err);
+ s_dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+ handle_dbus_err(s_dbus_conn == NULL, &err);
+
+ /* Do the selected operation. */
+ int exitcode = 0;
+ switch (op)
+ {
+ case OPT_GET_LIST:
+ {
+ vector_map_crash_data_t ci = call_GetCrashInfos();
+ print_crash_list(ci, full);
+ break;
+ }
+ case OPT_REPORT:
+ {
+ int flags = CLI_REPORT_SILENT_IF_NOT_FOUND;
+ if (always)
+ flags |= CLI_REPORT_BATCH;
+ exitcode = report(crash_id, flags);
+ if (exitcode == -1) /* no such crash_id */
+ {
+ crash_id = guess_crash_id(crash_id);
+ exitcode = report(crash_id, always ? CLI_REPORT_BATCH : 0);
+ if (exitcode == -1)
+ {
+ error_msg("Crash '%s' not found", crash_id);
+ free((void *)crash_id);
+ xfunc_die();
+ }
+
+ free((void *)crash_id);
+ }
+ break;
+ }
+ case OPT_DELETE:
+ {
+ exitcode = call_DeleteDebugDump(crash_id);
+ if (exitcode == ENOENT)
+ {
+ crash_id = guess_crash_id(crash_id);
+ exitcode = call_DeleteDebugDump(crash_id);
+ if (exitcode == ENOENT)
+ {
+ error_msg("Crash '%s' not found", crash_id);
+ free((void *)crash_id);
+ xfunc_die();
+ }
+
+ free((void *)crash_id);
+ }
+ if (exitcode != 0)
+ error_msg_and_die("Can't delete debug dump '%s'", crash_id);
+ break;
+ }
+ case OPT_INFO:
+ {
+ int old_logmode = logmode;
+ logmode = 0;
+
+ map_crash_data_t crashData = call_CreateReport(crash_id);
+ if (crashData.empty()) /* no such crash_id */
+ {
+ crash_id = guess_crash_id(crash_id);
+ crashData = call_CreateReport(crash_id);
+ if (crashData.empty())
+ {
+ error_msg("Crash '%s' not found", crash_id);
+ free((void *)crash_id);
+ xfunc_die();
+ }
+
+ free((void *)crash_id);
+ }
+
+ logmode = old_logmode;
+
+ print_crash_info(crashData, backtrace);
+
+ break;
+ }
+ }
+
+ return exitcode;
+}