diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/cli/Makefile.am | 50 | ||||
-rw-r--r-- | src/cli/abrt-cli.bash | 50 | ||||
-rw-r--r-- | src/cli/abrt-cli.txt | 75 | ||||
-rw-r--r-- | src/cli/cli.c | 368 | ||||
-rw-r--r-- | src/cli/report.c | 834 | ||||
-rw-r--r-- | src/cli/report.h | 39 | ||||
-rw-r--r-- | src/cli/run-command.c | 73 | ||||
-rw-r--r-- | src/cli/run-command.h | 31 |
9 files changed, 1 insertions, 1521 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 2e368af8..b41b939b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1 +1 @@ -SUBDIRS = include lib hooks daemon applet gui-gtk cli plugins +SUBDIRS = include lib hooks daemon applet gui-gtk plugins diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am deleted file mode 100644 index 330061de..00000000 --- a/src/cli/Makefile.am +++ /dev/null @@ -1,50 +0,0 @@ --include ../../config.mak - -bin_PROGRAMS = \ - abrt-cli - -abrt_cli_SOURCES = \ - cli.c \ - run-command.h run-command.c \ - report.h report.c -abrt_cli_CPPFLAGS = \ - -I$(srcdir)/../include \ - -I$(srcdir)/../lib \ - -DVAR_RUN=\"$(VAR_RUN)\" \ - -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ - -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ - $(GLIB_CFLAGS) \ - -D_GNU_SOURCE \ - $(LIBREPORT_CFLAGS) \ - -Wall -Wwrite-strings -Werror -# $(GTK_CFLAGS) -abrt_cli_LDADD = \ - $(LIBREPORT_LIBS) \ - $(GLIB_LIBS) - -MAN_TXT = \ - abrt-cli.txt - -# Manual pages are generated from .txt via Docbook -man1_MANS = ${MAN_TXT:%.txt=%.1} - -%.1 %.5 %.7: %.xml - $(XMLTO_SILENT) xmlto man $< 2>&1 | sed '/Note/d' - -%.xml: %.txt ../../asciidoc.conf - $(ASCIIDOC_SILENT) asciidoc --backend=docbook --doctype=manpage --conf-file ../../asciidoc.conf -aabrt_version=$(PACKAGE_VERSION) -o $@ $< - -CLEANFILES = $(man1_MANS) - -PYTHON_FILES = \ - abrt-action-install-debuginfo \ - abrt-action-list-dsos.py \ - abrt-action-analyze-core.py - -completiondir = $(sysconfdir)/bash_completion.d -dist_completion_DATA = abrt-cli.bash - -DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ - -EXTRA_DIST = $(MAN_TXT) - diff --git a/src/cli/abrt-cli.bash b/src/cli/abrt-cli.bash deleted file mode 100644 index fd0a85f3..00000000 --- a/src/cli/abrt-cli.bash +++ /dev/null @@ -1,50 +0,0 @@ -# bash-completion add-on for abrt-cli(1) -# http://bash-completion.alioth.debian.org/ - -# $1 = additional options for abrt-cli -_abrt_list() -{ - echo $(abrt-cli --list $1 | grep UUID | awk '{print $3}') - return 0 -} - -_abrt_cli() -{ - local cur prev opts - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - opts="--help --version --list --report --delete" - - # - # Complete the arguments to some of the basic commands. - # - case "${prev}" in - --list) - opts="--full" - ;; - --report) - # Include only not-yet-reported crashes. - opts="--always $(_abrt_list)" - ;; - --always) # This is for --report --always - # Include only not-yet-reported crashes. - opts=$(_abrt_list) - ;; - --delete) - opts=$(_abrt_list "--full") - ;; - esac - - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 -} -complete -F _abrt_cli abrt-cli - -# Local variables: -# mode: shell-script -# sh-basic-offset: 4 -# sh-indent-comment: t -# indent-tabs-mode: nil -# End: -# ex: ts=4 sw=4 et filetype=sh
\ No newline at end of file diff --git a/src/cli/abrt-cli.txt b/src/cli/abrt-cli.txt deleted file mode 100644 index ea3111c8..00000000 --- a/src/cli/abrt-cli.txt +++ /dev/null @@ -1,75 +0,0 @@ -abrt-cli(1) -=========== - -NAME ----- -abrt-cli - Work with ABRT dump directories from command line. - -SYNOPSIS --------- -'abrt-cli' [-vsp] -l[f] [-D BASE_DIR]... - -'abrt-cli' [-vsp] -i[f] DUMP_DIR - -'abrt-cli' [-vsp] -L[PREFIX] [DUMP_DIR] - -'abrt-cli' [-vsp] -e EVENT DUMP_DIR - -'abrt-cli' [-vsp] -a[y] DUMP_DIR - -'abrt-cli' [-vsp] -r[y] DUMP_DIR - -'abrt-cli' [-vsp] -d DUMP_DIR - -DESCRIPTION ------------ -'abrt-cli' is a command line tool that manages application crashes and other problems -catched by abrtd daemon. It enables access to, manipulation of problem data, and reporting. - -OPTIONS -------- --l:: - List not yet reported problems, or all problems with -f - --D BASE_DIR:: - Directory to list problems from (default: -D $HOME/.abrt/spool -D /var/spool/abrt) - --i, --info:: - Print information about DUMP_DIR (detailed with -f) - --L[PREFIX]:: - List possible events [which start with PREFIX] - --e EVENT:: - Run EVENT on DUMP_DIR - --a, --analyze:: - Run analyze event(s) on DUMP_DIR - --r, --report:: - Send a report about DUMP_DIR - --d, --delete:: - Remove DUMP_DIR - --f, --full:: - Full listing - --y, --always:: - Noninteractive: don't ask questions, assume positive answer to all of them - --v, --verbose:: - Be verbose - --s:: - Log to syslog - --p:: - Add program names to log - --V, --version:: - Display version and exit - -AUTHORS -------- -* ABRT team diff --git a/src/cli/cli.c b/src/cli/cli.c deleted file mode 100644 index db971b83..00000000 --- a/src/cli/cli.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - 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. -*/ -#if HAVE_LOCALE_H -# include <locale.h> -#endif -#include <getopt.h> -#include "abrtlib.h" -#include "report.h" - - -/* Vector of problems: */ -/* problem_data_vector[i] = { "name" = { "content", CD_FLAG_foo_bits } } */ - -typedef GPtrArray vector_of_problem_data_t; - -static inline problem_data_t *get_problem_data(vector_of_problem_data_t *vector, unsigned i) -{ - return (problem_data_t *)g_ptr_array_index(vector, i); -} - -static void free_vector_of_problem_data(vector_of_problem_data_t *vector) -{ - if (vector) - g_ptr_array_free(vector, TRUE); -} - -static vector_of_problem_data_t *new_vector_of_problem_data(void) -{ - return g_ptr_array_new_with_free_func((void (*)(void*)) &free_problem_data); -} - - -static problem_data_t *FillCrashInfo(const char *dump_dir_name) -{ - int sv_logmode = logmode; - logmode = 0; /* suppress EPERM/EACCES errors in opendir */ - struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ DD_OPEN_READONLY); - logmode = sv_logmode; - - if (!dd) - return NULL; - - problem_data_t *problem_data = create_problem_data_from_dump_dir(dd); - dd_close(dd); - add_to_problem_data_ext(problem_data, CD_DUMPDIR, dump_dir_name, CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE + CD_FLAG_LIST); - - return problem_data; -} - -static void GetCrashInfos(vector_of_problem_data_t *retval, const char *dir_name) -{ - VERB1 log("Loading dumps from '%s'", dir_name); - - DIR *dir = opendir(dir_name); - if (dir != NULL) - { - struct dirent *dent; - while ((dent = readdir(dir)) != NULL) - { - if (dot_or_dotdot(dent->d_name)) - continue; /* skip "." and ".." */ - - char *dump_dir_name = concat_path_file(dir_name, dent->d_name); - - struct stat statbuf; - if (stat(dump_dir_name, &statbuf) == 0 - && S_ISDIR(statbuf.st_mode) - ) { - problem_data_t *problem_data = FillCrashInfo(dump_dir_name); - if (problem_data) - g_ptr_array_add(retval, problem_data); - } - free(dump_dir_name); - } - closedir(dir); - } -} - -/** Prints basic information about a crash to stdout. */ -static void print_crash(problem_data_t *problem_data) -{ - char* desc = make_description( - problem_data, - /*names_to_skip:*/ NULL, - /*max_text_size:*/ CD_TEXT_ATT_SIZE, - MAKEDESC_SHOW_ONLY_LIST - ); - fputs(desc, stdout); - free(desc); -} - -/** - * Prints a list containing "crashes" to stdout. - * @param include_reported - * Do not skip entries marked as already reported. - */ -static void print_crash_list(vector_of_problem_data_t *crash_list, bool include_reported) -{ - unsigned i; - for (i = 0; i < crash_list->len; ++i) - { - problem_data_t *crash = get_problem_data(crash_list, i); - if (!include_reported) - { - const char *msg = get_problem_item_content_or_NULL(crash, FILENAME_REPORTED_TO); - if (msg) - continue; - } - - printf("%u.\n", i); - print_crash(crash); - } -} - -/** - * Prints full information about a crash - */ -static void print_crash_info(problem_data_t *problem_data, bool show_multiline) -{ - char* desc = make_description( - problem_data, - /*names_to_skip:*/ NULL, - /*max_text_size:*/ CD_TEXT_ATT_SIZE, - MAKEDESC_SHOW_FILES | (show_multiline ? MAKEDESC_SHOW_MULTILINE : 0) - ); - fputs(desc, stdout); - free(desc); -} - -static char *do_log(char *log_line, void *param) -{ - log("%s", log_line); - return log_line; -} - -int main(int argc, char** argv) -{ - abrt_init(argv); - - setlocale(LC_ALL, ""); -#if ENABLE_NLS - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); -#endif - - GList *D_list = NULL; - const char *event_name = NULL; - const char *pfx = ""; - - /* Can't keep these strings/structs static: _() doesn't support that */ - const char *program_usage_string = _( - "\b [-vsp] -l[f] [-D BASE_DIR]...\n" - "or: \b [-vsp] -i[f] DUMP_DIR\n" - "or: \b [-vsp] -L[PREFIX] [DUMP_DIR]\n" - "or: \b [-vsp] -e EVENT DUMP_DIR\n" - "or: \b [-vsp] -a[y] DUMP_DIR\n" - "or: \b [-vsp] -r[y] DUMP_DIR\n" - "or: \b [-vsp] -d DUMP_DIR" - ); - enum { - OPT_list = 1 << 0, - OPT_D = 1 << 1, - OPT_info = 1 << 2, - OPT_list_events = 1 << 3, - OPT_run_event = 1 << 4, - OPT_analyze = 1 << 5, - OPT_report = 1 << 6, - OPT_delete = 1 << 7, - OPT_version = 1 << 8, - OPTMASK_op = OPT_list|OPT_info|OPT_list_events|OPT_run_event|OPT_analyze|OPT_report|OPT_delete|OPT_version, - OPTMASK_need_arg = OPT_info|OPT_run_event|OPT_analyze|OPT_report|OPT_delete, - OPT_f = 1 << 9, - OPT_y = 1 << 10, - OPT_v = 1 << 11, - OPT_s = 1 << 12, - OPT_p = 1 << 13, - }; - /* Keep enum above and order of options below in sync! */ - struct options program_options[] = { - /* short_name long_name value parameter_name help */ - OPT_BOOL( 'l', "list" , NULL, _("List not yet reported problems, or all with -f")), - OPT_LIST( 'D', NULL , &D_list, "BASE_DIR", _("Directory to list problems from (default: -D $HOME/.abrt/spool -D "DEBUG_DUMPS_DIR")")), - OPT_BOOL( 'i', "info" , NULL, _("Print information about DUMP_DIR (detailed with -f)")), - OPT_OPTSTRING('L', NULL , &pfx, "PREFIX", _("List possible events [which start with PREFIX]")), - OPT_STRING( 'e', NULL , &event_name, "EVENT", _("Run EVENT on DUMP_DIR")), - OPT_BOOL( 'a', "analyze", NULL, _("Run analyze event(s) on DUMP_DIR")), - OPT_BOOL( 'r', "report" , NULL, _("Send a report about DUMP_DIR")), - OPT_BOOL( 'd', "delete" , NULL, _("Remove DUMP_DIR")), - OPT_BOOL( 'V', "version", NULL, _("Display version and exit")), - OPT_BOOL( 'f', "full" , NULL, _("Full listing")), - OPT_BOOL( 'y', "always" , NULL, _("Noninteractive: don't ask questions, assume 'yes'")), - OPT__VERBOSE(&g_verbose), - OPT_BOOL( 's', NULL , NULL, _("Log to syslog")), - OPT_BOOL( 'p', NULL , NULL, _("Add program names to log")), - OPT_END() - }; - unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); - unsigned op = (opts & OPTMASK_op); - if (!op || ((op-1) & op)) - /* "You must specify exactly one operation" */ - show_usage_and_die(program_usage_string, program_options); - argv += optind; - argc -= optind; - if (argc > 1 - /* dont_need_arg == have_arg? bad in both cases: - * TRUE == TRUE (dont need arg but have) or - * FALSE == FALSE (need arg but havent). - * OPT_list_events is an exception, it can be used in both cases. - */ - || ((op != OPT_list_events) && (!(opts & OPTMASK_need_arg) == argc)) - ) { - show_usage_and_die(program_usage_string, program_options); - } - - if (op == OPT_version) - { - printf("%s "VERSION"\n", g_progname); - return 0; - } - - export_abrt_envvars(opts & OPT_p); - if (opts & OPT_s) - { - openlog(msg_prefix, 0, LOG_DAEMON); - logmode = LOGMODE_SYSLOG; - } - - char *dump_dir_name = argv[0]; - bool full = (opts & OPT_f); - bool always = (opts & OPT_y); - - if (!D_list) - { - char *home = getenv("HOME"); - if (home) - D_list = g_list_append(D_list, concat_path_file(home, ".abrt/spool")); - D_list = g_list_append(D_list, (void*)DEBUG_DUMPS_DIR); - } - - /* Get settings */ - load_event_config_data(); - - /* Do the selected operation. */ - int exitcode = 0; - switch (op) - { - case OPT_list: - { - vector_of_problem_data_t *ci = new_vector_of_problem_data(); - while (D_list) - { - char *dir = (char *)D_list->data; - GetCrashInfos(ci, dir); - D_list = g_list_remove(D_list, dir); - } - print_crash_list(ci, full); - free_vector_of_problem_data(ci); - break; - } - case OPT_list_events: /* -L[PREFIX] */ - { - /* Note that dump_dir_name may be NULL here, it means "show all - * possible events regardless of dir" - */ - char *events = list_possible_events(NULL, dump_dir_name, pfx); - if (!events) - return 1; /* error msg is already logged */ - fputs(events, stdout); - free(events); - break; - } - case OPT_run_event: /* -e EVENT: run event */ - { - struct run_event_state *run_state = new_run_event_state(); - run_state->logging_callback = do_log; - int r = run_event_on_dir_name(run_state, dump_dir_name, event_name); - if (r == 0 && run_state->children_count == 0) - error_msg_and_die("No actions are found for event '%s'", event_name); - free_run_event_state(run_state); - break; - } - case OPT_analyze: - { - /* Load problem_data from dump dir */ - struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY); - if (!dd) - return 1; - char *analyze_events_as_lines = list_possible_events(dd, NULL, "analyze"); - dd_close(dd); - - if (analyze_events_as_lines && *analyze_events_as_lines) - { - GList *list_analyze_events = str_to_glist(analyze_events_as_lines, '\n'); - char *event = select_event_option(list_analyze_events); - list_free_with_free(list_analyze_events); - exitcode = run_analyze_event(dump_dir_name, event); - free(event); - } - free(analyze_events_as_lines); - break; - } - case OPT_report: - { - struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY); - if (!dd) - break; - int readonly = !dd->locked; - dd_close(dd); - if (readonly) - { - log("'%s' is not writable", dump_dir_name); - /* D_list can't be NULL here */ - struct dump_dir *dd_copy = steal_directory((char *)D_list->data, dump_dir_name); - if (dd_copy) - { - delete_dump_dir_possibly_using_abrtd(dump_dir_name); - dump_dir_name = xstrdup(dd_copy->dd_dirname); - dd_close(dd_copy); - } - } - - exitcode = report(dump_dir_name, (always ? CLI_REPORT_BATCH : 0)); - if (exitcode == -1) - error_msg_and_die("Crash '%s' not found", dump_dir_name); - break; - } - case OPT_delete: - { - exitcode = delete_dump_dir_possibly_using_abrtd(dump_dir_name); - break; - } - case OPT_info: - { - /* Load problem_data from dump dir */ - struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY); - if (!dd) - return -1; - - problem_data_t *problem_data = create_problem_data_from_dump_dir(dd); - dd_close(dd); - - add_to_problem_data_ext(problem_data, CD_DUMPDIR, dump_dir_name, - CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE); - - print_crash_info(problem_data, full); - free_problem_data(problem_data); - - break; - } - } - - return exitcode; -} diff --git a/src/cli/report.c b/src/cli/report.c deleted file mode 100644 index 78b7273e..00000000 --- a/src/cli/report.c +++ /dev/null @@ -1,834 +0,0 @@ -/* - 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 "abrtlib.h" -#include "run-command.h" -#include "report.h" - -/* Field separator for the crash report file that is edited by user. */ -#define FIELD_SEP "%----" - -/* - * Escapes the field content string to avoid confusion with file comments. - * Returned field must be free()d by caller. - */ -static char *escape(const char *str) -{ - // Determine the size of resultant string. - // Count the required number of escape characters. - // 1. NEWLINE followed by # - // 2. NEWLINE followed by \# (escaped version) - const char *ptr = str; - bool newline = true; - int count = 0; - while (*ptr) - { - if (newline) - { - if (*ptr == '#') - ++count; - if (*ptr == '\\' && ptr[1] == '#') - ++count; - } - - newline = (*ptr == '\n'); - ++ptr; - } - - // Copy the input string to the resultant string, and escape all - // occurences of \# and #. - char *result = (char*)xmalloc(strlen(str) + 1 + count); - - const char *src = str; - char *dest = result; - newline = true; - while (*src) - { - if (newline) - { - if (*src == '#') - *dest++ = '\\'; - else if (*src == '\\' && *(src + 1) == '#') - *dest++ = '\\'; - } - - newline = (*src == '\n'); - *dest++ = *src++; - } - *dest = '\0'; - return result; -} - -/* - * Removes all comment lines, and unescapes the string previously escaped - * by escape(). Works in-place. - */ -static void remove_comments_and_unescape(char *str) -{ - char *src = str, *dest = str; - bool newline = true; - while (*src) - { - if (newline) - { - if (*src == '#') - { // Skip the comment line! - while (*src && *src != '\n') - ++src; - - if (*src == '\0') - break; - - ++src; - continue; - } - if (*src == '\\' - && (src[1] == '#' || (src[1] == '\\' && src[2] == '#')) - ) { - ++src; // Unescape escaped char. - } - } - - newline = (*src == '\n'); - *dest++ = *src++; - } - *dest = '\0'; -} - -/* - * Writes a field of crash report to a file. - * Field must be writable. - */ -static void write_crash_report_field(FILE *fp, problem_data_t *problem_data, - const char *field, const char *description) -{ - const struct problem_item *value = get_problem_data_item_or_NULL(problem_data, field); - if (!value) - { - // exit silently, all fields are optional for now - //error_msg("Field %s not found", field); - return; - } - - fprintf(fp, "%s%s\n", FIELD_SEP, field); - - fprintf(fp, "%s\n", description); - if (!(value->flags & CD_FLAG_ISEDITABLE)) - fprintf(fp, _("# This field is read only\n")); - - char *escaped_content = escape(value->content); - fprintf(fp, "%s\n", escaped_content); - free(escaped_content); -} - -/* - * Saves the crash report to a file. - * Parameter 'fp' must be opened before write_crash_report is called. - * Returned value: - * If the report is successfully stored to the file, a zero value is returned. - * On failure, nonzero value is returned. - */ -static void write_crash_report(problem_data_t *report, FILE *fp) -{ - fprintf(fp, "# Please check this report. Lines starting with '#' will be ignored.\n" - "# Lines starting with '%%----' separate fields, please do not delete them.\n\n"); - - write_crash_report_field(fp, report, FILENAME_COMMENT, - _("# Describe the circumstances of this crash below")); - write_crash_report_field(fp, report, FILENAME_BACKTRACE, - _("# Backtrace\n# Check that it does not contain any sensitive data (passwords, etc.)")); - write_crash_report_field(fp, report, FILENAME_DUPHASH, "# DUPHASH"); - write_crash_report_field(fp, report, FILENAME_ARCHITECTURE, _("# Architecture")); - write_crash_report_field(fp, report, FILENAME_CMDLINE, _("# Command line")); - write_crash_report_field(fp, report, FILENAME_COMPONENT, _("# Component")); - write_crash_report_field(fp, report, FILENAME_COREDUMP, _("# Core dump")); - write_crash_report_field(fp, report, FILENAME_EXECUTABLE, _("# Executable")); - write_crash_report_field(fp, report, FILENAME_KERNEL, _("# Kernel version")); - write_crash_report_field(fp, report, FILENAME_PACKAGE, _("# Package")); - write_crash_report_field(fp, report, FILENAME_REASON, _("# Reason of crash")); - write_crash_report_field(fp, report, FILENAME_OS_RELEASE, _("# Release string of the operating system")); -} - -/* - * Updates appropriate field in the report from the text. The text can - * contain multiple fields. - * Returns: - * 0 if no change to the field was detected. - * 1 if the field was changed. - * Changes to read-only fields are ignored. - */ -static int read_crash_report_field(const char *text, problem_data_t *report, - const char *field) -{ - char separator[sizeof("\n" FIELD_SEP)-1 + strlen(field) + 2]; // 2 = '\n\0' - sprintf(separator, "\n%s%s\n", FIELD_SEP, field); - const char *textfield = strstr(text, separator); - if (!textfield) - return 0; // exit silently because all fields are optional - - textfield += strlen(separator); - int length = 0; - const char *end = strstr(textfield, "\n" FIELD_SEP); - if (!end) - length = strlen(textfield); - else - length = end - textfield; - - struct problem_item *value = get_problem_data_item_or_NULL(report, field); - if (!value) - { - error_msg("Field %s not found", field); - return 0; - } - - // Do not change noneditable fields. - if (!(value->flags & CD_FLAG_ISEDITABLE)) - return 0; - - // Compare the old field contents with the new field contents. - char newvalue[length + 1]; - strncpy(newvalue, textfield, length); - newvalue[length] = '\0'; - strtrim(newvalue); - - char oldvalue[strlen(value->content) + 1]; - strcpy(oldvalue, value->content); - strtrim(oldvalue); - - // Return if no change in the contents detected. - if (strcmp(newvalue, oldvalue) == 0) - return 0; - - free(value->content); - value->content = xstrdup(newvalue); - return 1; -} - -/* - * Updates the crash report 'report' from the text. The text must not contain - * any comments. - * Returns: - * 0 if no field was changed. - * 1 if any field was changed. - * Changes to read-only fields are ignored. - */ -static int read_crash_report(problem_data_t *report, const char *text) -{ - int result = 0; - result |= read_crash_report_field(text, report, FILENAME_COMMENT); - result |= read_crash_report_field(text, report, FILENAME_BACKTRACE); - result |= read_crash_report_field(text, report, FILENAME_DUPHASH); - result |= read_crash_report_field(text, report, FILENAME_ARCHITECTURE); - result |= read_crash_report_field(text, report, FILENAME_CMDLINE); - result |= read_crash_report_field(text, report, FILENAME_COMPONENT); - result |= read_crash_report_field(text, report, FILENAME_COREDUMP); - result |= read_crash_report_field(text, report, FILENAME_EXECUTABLE); - result |= read_crash_report_field(text, report, FILENAME_KERNEL); - result |= read_crash_report_field(text, report, FILENAME_PACKAGE); - result |= read_crash_report_field(text, report, FILENAME_REASON); - result |= read_crash_report_field(text, report, FILENAME_OS_RELEASE); - return result; -} - -/** - * Ensures that the fields needed for editor are present in the problem data. - * Fields: comments. - */ -static void create_fields_for_editor(problem_data_t *problem_data) -{ - if (!get_problem_data_item_or_NULL(problem_data, FILENAME_COMMENT)) - add_to_problem_data_ext(problem_data, FILENAME_COMMENT, "", CD_FLAG_TXT + CD_FLAG_ISEDITABLE); -} - -/** - * Runs external editor. - * Returns: - * 0 if the launch was successful - * 1 if it failed. The error reason is logged using error_msg() - */ -static int launch_editor(const char *path) -{ - const char *editor, *terminal; - - editor = getenv("ABRT_EDITOR"); - if (!editor) - editor = getenv("VISUAL"); - if (!editor) - editor = getenv("EDITOR"); - - terminal = getenv("TERM"); - if (!editor && (!terminal || strcmp(terminal, "dumb") == 0)) - { - error_msg(_("Cannot run vi: $TERM, $VISUAL and $EDITOR are not set")); - return 1; - } - - if (!editor) - editor = "vi"; - - char *args[3]; - args[0] = (char*)editor; - args[1] = (char*)path; - args[2] = NULL; - run_command(args); - - return 0; -} - -/** - * Returns: - * 0 on success, problem data has been updated - * 2 on failure, unable to create, open, or close temporary file - * 3 on failure, cannot launch text editor - */ -static int run_report_editor(problem_data_t *problem_data) -{ - /* Open a temporary file and write the crash report to it. */ - char filename[] = "/tmp/abrt-report.XXXXXX"; - int fd = mkstemp(filename); - if (fd == -1) /* errno is set */ - { - perror_msg("can't generate temporary file name"); - return 2; - } - - FILE *fp = fdopen(fd, "w"); - if (!fp) /* errno is set */ - { - die_out_of_memory(); - } - - write_crash_report(problem_data, fp); - - if (fclose(fp)) /* errno is set */ - { - perror_msg("can't write '%s'", filename); - return 2; - } - - // Start a text editor on the temporary file. - if (launch_editor(filename) != 0) - return 3; /* exit with error */ - - // Read the file back and update the report from the file. - fp = fopen(filename, "r"); - if (!fp) /* errno is set */ - { - perror_msg("can't open '%s' to read the crash report", filename); - return 2; - } - - fseek(fp, 0, SEEK_END); - unsigned long size = ftell(fp); - fseek(fp, 0, SEEK_SET); - - char *text = (char*)xmalloc(size + 1); - if (fread(text, 1, size, fp) != size) - { - error_msg("can't read '%s'", filename); - fclose(fp); - return 2; - } - text[size] = '\0'; - fclose(fp); - - // Delete the tempfile. - if (unlink(filename) == -1) /* errno is set */ - { - perror_msg("can't unlink %s", filename); - } - - remove_comments_and_unescape(text); - // Updates the crash report from the file text. - int report_changed = read_crash_report(problem_data, text); - free(text); - if (report_changed) - puts(_("\nThe report has been updated")); - else - puts(_("\nNo changes were detected in the report")); - - return 0; -} - -/** - * Asks user for a text response. - * @param question - * Question displayed to user. - * @param result - * Output array. - * @param result_size - * Maximum byte count to be written. - */ -static void read_from_stdin(const char *question, char *result, int result_size) -{ - assert(result_size > 1); - printf("%s", question); - fflush(NULL); - if (NULL == fgets(result, result_size, stdin)) - result[0] = '\0'; - // Remove the newline from the login. - strchrnul(result, '\n')[0] = '\0'; -} - -/** - * Asks a [y/n] question on stdin/stdout. - * Returns true if the answer is yes, false otherwise. - */ -static bool ask_yesno(const char *question) -{ - /* The response might take more than 1 char in non-latin scripts. */ - const char *yes = _("y"); - const char *no = _("N"); - printf("%s [%s/%s]: ", question, yes, no); - fflush(NULL); - - char answer[16]; - if (!fgets(answer, sizeof(answer), stdin)) - return false; - /* Use strncmp here because the answer might contain a newline as - the last char. */ - return 0 == strncmp(answer, yes, strlen(yes)); -} - -/* Returns true if echo has been changed from another state. */ -static bool set_echo(bool enable) -{ - struct termios t; - if (tcgetattr(STDIN_FILENO, &t) < 0) - return false; - - /* No change needed? */ - if ((bool)(t.c_lflag & ECHO) == enable) - return false; - - t.c_lflag ^= ECHO; - if (tcsetattr(STDIN_FILENO, TCSANOW, &t) < 0) - perror_msg_and_die("tcsetattr"); - - return true; -} - -/* Returns true if the string contains the specified number. */ -static bool is_number_in_string(unsigned number, const char *str) -{ - const char *c; - char numstr[sizeof(int) * 3 + 2]; - int len; - - len = snprintf(numstr, sizeof(numstr), "%u", number); - for (c = str; *c; c++) - { - c = strstr(c, numstr); - if (!c) - /* no such number exists in the string */ - return false; - if ((c == str || !isalnum(c[-1])) && !isalnum(c[len])) - /* found */ - return true; - - /* found, but it's part of another number. Continue - * from the next position. */ - } - - return false; -} - -/** - * Asks user for missing information - */ -static void ask_for_missing_settings(const char *event_name) -{ - for (int i = 0; i < 3; ++i) - { - GHashTable *error_table = validate_event(event_name); - if (!error_table) - return; - - event_config_t *event_config = get_event_config(event_name); - - GHashTableIter iter; - char *opt_name, *err_msg; - g_hash_table_iter_init(&iter, error_table); - while (g_hash_table_iter_next(&iter, (void**)&opt_name, (void**)&err_msg)) - { - event_option_t *opt = get_event_option_from_list(opt_name, - event_config->options); - - free(opt->eo_value); - opt->eo_value = NULL; - - char result[512]; - - char *question = xasprintf("%s: ", (opt->eo_label) ? opt->eo_label : opt->eo_name); - switch (opt->eo_type) { - case OPTION_TYPE_TEXT: - case OPTION_TYPE_NUMBER: - read_from_stdin(question, result, 512); - opt->eo_value = xstrdup(result); - break; - case OPTION_TYPE_PASSWORD: - { - bool changed = set_echo(false); - read_from_stdin(question, result, 512); - if (changed) - set_echo(true); - - opt->eo_value = xstrdup(result); - /* Newline was not added by pressing Enter because ECHO was - disabled, so add it now. */ - puts(""); - break; - } - case OPTION_TYPE_BOOL: - if (ask_yesno(question)) - opt->eo_value = xstrdup("yes"); - else - opt->eo_value = xstrdup("no"); - - break; - case OPTION_TYPE_HINT_HTML: /* TODO? */ - case OPTION_TYPE_INVALID: - break; - }; - - free(question); - } - - g_hash_table_destroy(error_table); - - error_table = validate_event(event_name); - if (!error_table) - return; - - log(_("Your input is not valid, because of:")); - g_hash_table_iter_init(&iter, error_table); - while (g_hash_table_iter_next(&iter, (void**)&opt_name, (void**)&err_msg)) - log(_("Bad value for '%s': %s"), opt_name, err_msg); - - g_hash_table_destroy(error_table); - } - - /* we ask for 3 times and still don't have valid infromation */ - error_msg_and_die("Invalid input, program exiting..."); -} - -struct logging_state { - char *last_line; -}; -static char *do_log_and_save_line(char *log_line, void *param) -{ - struct logging_state *l_state = (struct logging_state *)param; - log("%s", log_line); - free(l_state->last_line); - l_state->last_line = log_line; - return NULL; -} -static int run_events(const char *dump_dir_name, GList *events) -{ - int error_cnt = 0; - GList *env_list = NULL; - - // Run events - struct logging_state l_state; - l_state.last_line = NULL; - struct run_event_state *run_state = new_run_event_state(); - run_state->logging_callback = do_log_and_save_line; - run_state->logging_param = &l_state; - for (GList *li = events; li; li = li->next) - { - char *event = (char *) li->data; - - // Export overridden settings as environment variables - env_list = export_event_config(event); - - int r = run_event_on_dir_name(run_state, dump_dir_name, event); - if (r == 0 && run_state->children_count == 0) - { - l_state.last_line = xasprintf("Error: no processing is specified for event '%s'", - event); - r = -1; - } - if (r == 0) - { - printf("%s: %s\n", event, (l_state.last_line ? : "Reporting succeeded")); - } - else - { - error_msg("Reporting via '%s' was not successful%s%s", - event, - l_state.last_line ? ": " : "", - l_state.last_line ? l_state.last_line : "" - ); - error_cnt++; - } - free(l_state.last_line); - l_state.last_line = NULL; - - // Unexport overridden settings - unexport_event_config(env_list); - } - free_run_event_state(run_state); - - return error_cnt; -} - -static char *do_log(char *log_line, void *param) -{ - log("%s", log_line); - return log_line; -} - -int run_analyze_event(const char *dump_dir_name, const char *analyzer) -{ - VERB2 log("run_analyze_event('%s')", dump_dir_name); - - struct run_event_state *run_state = new_run_event_state(); - run_state->logging_callback = do_log; - int res = run_event_on_dir_name(run_state, dump_dir_name, analyzer); - free_run_event_state(run_state); - return res; -} - -/* show even description? */ -char *select_event_option(GList *list_options) -{ - if (!list_options) - return NULL; - - unsigned count = g_list_length(list_options); - if (count == 1) - return xstrdup((char*)list_options->data); - - int pos = 0; - fprintf(stdout, _("How you would like to analyze the problem?\n")); - for (GList *li = list_options; li; li = li->next) - { - char *opt = (char*)li->data; - event_config_t *config = get_event_config(opt); - if (config) - { - ++pos; - printf(" %i) %s\n", pos, config->screen_name); - } - } - - unsigned picked; - unsigned ii; - for (ii = 0; ii < 3; ++ii) - { - char answer[16]; - - read_from_stdin(_("Select analyzer: "), answer, sizeof(answer)); - if (!*answer) - continue; - - picked = xatou(answer); - if (picked > count) - { - fprintf(stdout, _("You have chosen number out of range")); - fprintf(stdout, "\n"); - continue; - } - - break; - } - - if (ii == 3) - error_msg_and_die(_("Invalid input, program exiting...")); - - GList *choosen = g_list_nth(list_options, picked - 1); - return xstrdup((char*)choosen->data); -} - -GList *str_to_glist(char *str, int delim) -{ - GList *list = NULL; - while (*str) - { - char *end = strchrnul(str, delim); - char *tmp = xstrndup(str, end - str); - if (*tmp) - list = g_list_append(list, tmp); - - str = end; - if (!*str) - break; - str++; - } - - if (!list && !g_list_length(list)) - return NULL; - - return list; -} - -/* Report the crash */ -int report(const char *dump_dir_name, int flags) -{ - /* Load problem_data from (possibly updated by analyze) dump dir */ - struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); - if (!dd) - return -1; - - char *analyze_events_as_lines = list_possible_events(dd, NULL, "analyze"); - dd_close(dd); - - if (analyze_events_as_lines && *analyze_events_as_lines) - { - GList *list_analyze_events = str_to_glist(analyze_events_as_lines, '\n'); - free(analyze_events_as_lines); - - char *event = select_event_option(list_analyze_events); - list_free_with_free(list_analyze_events); - - int analyzer_result = run_analyze_event(dump_dir_name, event); - free(event); - - if (analyzer_result != 0) - return 1; - } - - /* Load problem_data from (possibly updated by analyze) dump dir */ - dd = dd_opendir(dump_dir_name, /*flags:*/ 0); - if (!dd) - return -1; - - char *report_events_as_lines = list_possible_events(dd, NULL, "report"); - problem_data_t *problem_data = create_problem_data_from_dump_dir(dd); - dd_close(dd); - - if (!(flags & CLI_REPORT_BATCH)) - { - /* Open text editor and give a chance to review the backtrace etc */ - create_fields_for_editor(problem_data); - int result = run_report_editor(problem_data); - if (result != 0) - { - free_problem_data(problem_data); - free(report_events_as_lines); - return 1; - } - /* Save comment, backtrace */ - dd = dd_opendir(dump_dir_name, /*flags:*/ 0); - if (dd) - { -//TODO: we should iterate through problem_data and modify all modifiable fields - const char *comment = get_problem_item_content_or_NULL(problem_data, FILENAME_COMMENT); - const char *backtrace = get_problem_item_content_or_NULL(problem_data, FILENAME_BACKTRACE); - if (comment) - dd_save_text(dd, FILENAME_COMMENT, comment); - if (backtrace) - dd_save_text(dd, FILENAME_BACKTRACE, backtrace); - dd_close(dd); - } - } - - /* Get possible reporters associated with this particular crash */ - GList *report_events = NULL; - if (report_events_as_lines && *report_events_as_lines) - report_events = str_to_glist(report_events_as_lines, '\n'); - - free(report_events_as_lines); - - if (!report_events) - { - free_problem_data(problem_data); - error_msg_and_die("The dump directory '%s' has no defined reporters", - dump_dir_name); - } - - /* Get settings */ - load_event_config_data(); - - int errors = 0; - int plugins = 0; - if (flags & CLI_REPORT_BATCH) - { - puts(_("Reporting...")); - errors += run_events(dump_dir_name, report_events); - plugins += g_list_length(report_events); - } - else - { - const char *rating_str = get_problem_item_content_or_NULL(problem_data, FILENAME_RATING); -//COMPAT, remove after 2.1 release - if (!rating_str) rating_str = get_problem_item_content_or_NULL(problem_data, "rating"); - unsigned i, rating = rating_str ? xatou(rating_str) : 4; - GList *li; - char wanted_reporters[255]; - - puts(_("How would you like to report the problem?")); - /* Print list of reporters and ask the user which should be used. */ - for (li = report_events, i = 1; li; li = li->next, i++) - { - char *reporter_name = (char *) li->data; - event_config_t *config = get_event_config(reporter_name); - - printf(" %d) %s\n", i, (config && config->screen_name) ? config->screen_name : reporter_name); - } - - read_from_stdin(_("Select reporter(s): "), wanted_reporters, sizeof(wanted_reporters)); - - for (li = report_events, i = 1; li; li = li->next, i++) - { - char *reporter_name = (char *) li->data; - event_config_t *config = get_event_config(reporter_name); - - if (!config) - VERB1 log("No configuration file found for '%s' reporter", reporter_name); - - /* Was this reporter requested? */ - if (!is_number_in_string(i, wanted_reporters)) - continue; - - /* TODO: npajkovs; not implemented yet */ - //const char *rating_required = get_map_string_item_or_NULL(single_plugin_settings, "RatingRequired"); - //if (rating_required - // && string_to_bool(rating_required) == true - if (rating < 3) - { - puts(_("Reporting disabled because the backtrace is unusable")); - - const char *package = get_problem_item_content_or_NULL(problem_data, FILENAME_PACKAGE); - if (package && package[0]) - printf(_("Please try to install debuginfo manually using the command: \"debuginfo-install %s\" and try again\n"), package); - - plugins++; - errors++; - continue; - } - ask_for_missing_settings(reporter_name); - - /* - * to avoid creating list with one item, we probably should - * provide something like - * run_event(char*, char*) - */ - GList *cur_event = NULL; - cur_event = g_list_append(cur_event, reporter_name); - errors += run_events(dump_dir_name, cur_event); - g_list_free(cur_event); - - plugins++; - } - } - - printf(_("Problem reported via %d report events (%d errors)\n"), plugins, errors); - free_problem_data(problem_data); - list_free_with_free(report_events); - return errors; -} diff --git a/src/cli/report.h b/src/cli/report.h deleted file mode 100644 index a3937840..00000000 --- a/src/cli/report.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright (C) 2009 RedHat 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. -*/ -#ifndef ABRT_CLI_REPORT_H -#define ABRT_CLI_REPORT_H - -#ifdef __cplusplus -extern "C" { -#endif - -int run_analyze_event(const char *dump_dir_name, const char *analyzer); -char *select_event_option(GList *list_options); -GList *str_to_glist(char *str, int delim); - -/* Report the crash */ -enum { - CLI_REPORT_BATCH = 1 << 0, -}; -int report(const char *dump_dir_name, int flags); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/cli/run-command.c b/src/cli/run-command.c deleted file mode 100644 index 1f6836d6..00000000 --- a/src/cli/run-command.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - Copyright (C) 2009 RedHat 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 "run-command.h" -#include "abrtlib.h" - -/* - Inspired by git code. - http://git.kernel.org/?p=git/git.git;a=blob;f=run-command.c;hb=HEAD -*/ - -static pid_t start_command(char **argv) -{ - pid_t pid = vfork(); - if (pid < 0) - { - perror_msg_and_die("vfork"); - } - if (pid == 0) - { // new process - execvp(argv[0], argv); - exit(127); - } - return pid; -} - -static int finish_command(pid_t pid, char **argv) -{ - pid_t waiting; - int status; - while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR) - continue; - if (waiting < 0) - perror_msg_and_die("waitpid"); - - int code = -1; - if (WIFSIGNALED(status)) - { - code = WTERMSIG(status); - error_msg("'%s' killed by signal %d", argv[0], code); - code += 128; /* shells use this convention for deaths by signal */ - } - else /* if (WIFEXITED(status)) */ - { - code = WEXITSTATUS(status); - if (code == 127) - { - error_msg_and_die("Can't run '%s'", argv[0]); - } - } - - return code; -} - -int run_command(char **argv) -{ - pid_t pid = start_command(argv); - return finish_command(pid, argv); -} diff --git a/src/cli/run-command.h b/src/cli/run-command.h deleted file mode 100644 index 1ebb0a94..00000000 --- a/src/cli/run-command.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (C) 2009 RedHat 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. -*/ -#ifndef ABRT_CLI_RUN_COMMAND_H -#define ABRT_CLI_RUN_COMMAND_H - -#ifdef __cplusplus -extern "C" { -#endif - -int run_command(char **argv); - -#ifdef __cplusplus -} -#endif - -#endif |