Exemplo n.º 1
0
int main(int argc, char **argv)
{
    abrt_init(argv);

    /* I18n */
    setlocale(LC_ALL, "");
#if ENABLE_NLS
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif

    /* Can't keep these strings/structs static: _() doesn't support that */
    const char *program_usage_string = _(
        "& [-v] [-d DIR] [-m MESSAGEID] [-F FMTFILE] [-p NONE|ESSENTIAL|FULL] [-s SYSLOGID]\n"
        "\n"
        "Reports problem information into systemd journal.\n"
        "\n"
        "The tool reads problem directory DIR and sends its details\n"
        "into systemd journal as a message. If MESSAGEID is defined, the tool\n"
        "creates a catalog message as well.\n"
    );
    enum {
        OPT_v = 1 << 0,
        OPT_d = 1 << 1,
        OPT_m = 1 << 2,
        OPT_F = 1 << 3,
        OPT_p = 1 << 4,
        OPT_s = 1 << 5,
        OPT_D = 1 << 6,
    };
    const char *dump_dir_name = ".";
    const char *message_id = NULL;
    const char *fmt_file = NULL;
    const char *dump = NULL;
    const char *syslog_id = NULL;
    /* Keep enum above and order of options below in sync! */
    struct options program_options[] = {
        OPT__VERBOSE(&g_verbose),
        OPT_STRING('d', NULL          , &dump_dir_name, "DIR"   , _("Problem directory")),
        OPT_STRING('m', "message-id"  , &message_id,    "STR"   , _("Catalog message id")),
        OPT_STRING('F', NULL          , &fmt_file     , "FILE"  , _("Formatting file for catalog message")),
        OPT_STRING('p', "dump"        , &dump         , "STR"   , _("Dump problem dir into systemd journal fields")),
        OPT_STRING('s', "syslog-id"   , &syslog_id    , "STR"   , _("Define SYSLOG_IDENTIFIER systemd journal field")),
        OPT_BOOL(  'D', NULL          , NULL                    , _("Debug")),
        OPT_END()
    };
    unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);

    unsigned dump_opt = DUMP_NONE;
    if (opts & OPT_p)
    {
        if (dump && strcmp(dump, "NONE") == 0)
            /* PASS */;
        else if (dump && strcmp(dump, "ESSENTIAL") == 0)
            dump_opt = DUMP_ESSENTIAL;
        else if (dump && strcmp(dump, "FULL") == 0)
            dump_opt = DUMP_FULL;
        else
        {
            error_msg("Parameter --dump takes NONE|ESSENTIAL|FULL values");
            show_usage_and_die(program_usage_string, program_options);
        }
    }

    export_abrt_envvars(0);

    problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
    if (!problem_data)
        xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */

    problem_formatter_t *pf = problem_formatter_new();

    if (fmt_file)
    {
        if (problem_formatter_load_file(pf, fmt_file))
            error_msg_and_die("Invalid format file: %s", fmt_file);
    }
    else
    {
        if (problem_formatter_load_string(pf, PROBLEM_REPORT_DEFAULT_TEMPLATE))
            error_msg_and_die("BUG: Invalid default problem report format string");
    }

    problem_report_settings_t report_settings = problem_formatter_get_settings(pf);
    report_settings.prs_shortbt_max_frames = 5;
    report_settings.prs_shortbt_max_text_size = 0; /* always short bt */
    problem_formatter_set_settings(pf, report_settings);

    /* Modify problem_data to meet reporter's needs */
    /* We want to have only binary name in problem report assigned to executable element */
    const char *exe = problem_data_get_content_or_NULL(problem_data, FILENAME_EXECUTABLE);
    char *binary_name = NULL;
    if (exe)
        binary_name = strrchr(exe, '/') + 1;

    if (binary_name)
        problem_data_add_text_noteditable(problem_data, BINARY_NAME, binary_name);

    /* crash_function element is neeeded by systemd journal messages, save ??, if it doesn't exist */
    const char *crash_function = problem_data_get_content_or_NULL(problem_data, FILENAME_CRASH_FUNCTION);
    if (!crash_function)
        problem_data_add_text_noteditable(problem_data, "crash_function", "??");

    /* Add SYSLOG_IDENTIFIER into problem data */
    if (syslog_id || (syslog_id = getenv("REPORTER_JOURNAL_SYSLOG_ID")))
        problem_data_add_text_noteditable(problem_data, SYSLOG_ID, syslog_id);

    /* Add MESSAGE_ID into problem data */
    if (message_id)
        problem_data_add_text_noteditable(problem_data, MESSAGE_ID, message_id);

    /* Generating of problem report */
    problem_report_t *pr = NULL;
    if (problem_formatter_generate_report(pf, problem_data, &pr))
        error_msg_and_die("Failed to format bug report from problem data");

    /* Debug */
    if (opts & OPT_D)
    {
        log("Message: %s\n"
                "\n"
                "%s"
                "\n"
                , problem_report_get_summary(pr)
                , problem_report_get_description(pr)
        );

        problem_data_free(problem_data);
        problem_report_free(pr);
        problem_formatter_free(pf);
        return 0;
    }

    msg_content_t *msg_c = create_journal_message(problem_data, pr, dump_opt);

    /* post journal message */
    sd_journal_sendv(msg_content_get_data(msg_c), msg_content_get_size(msg_c));

    msg_content_free(msg_c);

    problem_data_free(problem_data);
    problem_formatter_free(pf);
    problem_report_free(pr);

    return 0;
}
Exemplo n.º 2
0
int main(int argc, char **argv)
{
    abrt_init(argv);

    /* I18n */
    setlocale(LC_ALL, "");
#if ENABLE_NLS
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif

    const char *program_usage_string = _(
        "\n& [-vf] [-c CONFFILE]... [-F FMTFILE] [-A FMTFILE2] -d DIR"
        "\nor:"
        "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] FILE..."
        "\nor:"
        "\n& [-v] [-c CONFFILE]... [-d DIR] -t[ID] -w"
        "\nor:"
        "\n& [-v] [-c CONFFILE]... -h DUPHASH"
        "\n"
        "\nReports problem to MantisBT."
        "\n"
        "\nThe tool reads DIR. Then it tries to find an issue"
        "\nwith the same abrt_hash in custom field 'abrt_hash'."
        "\n"
        "\nIf such issue is not found, then a new issue is created. Elements of DIR"
        "\nare stored in the issue as part of issue description or as attachments,"
        "\ndepending on their type and size."
        "\n"
        "\nOtherwise, if such issue is found and it is marked as CLOSED DUPLICATE,"
        "\nthe tool follows the chain of duplicates until it finds a non-DUPLICATE issue."
        "\nThe tool adds a new comment to found issue."
        "\n"
        "\nThe URL to new or modified issue is printed to stdout and recorded in"
        "\n'reported_to' element."
        "\n"
        "\nOption -t uploads FILEs to the already created issue on MantisBT site."
        "\nThe issue ID is retrieved from directory specified by -d DIR."
        "\nIf problem data in DIR was never reported to MantisBT, upload will fail."
        "\n"
        "\nOption -tID uploads FILEs to the issue with specified ID on MantisBT site."
        "\n-d DIR is ignored."
        "\n"
        "\nOption -r sets the last url from reporter_to element which is prefixed with"
        "\nTRACKER_NAME to URL field. This option is applied only when a new issue is to be"
        "\nfiled. The default value is 'ABRT Server'"
        "\n"
        "\nIf not specified, CONFFILE defaults to "CONF_DIR"/plugins/mantisbt.conf"
        "\nand user's local ~"USER_HOME_CONFIG_PATH"/mantisbt.conf."
        "\nIts lines should have 'PARAM = VALUE' format."
        "\nRecognized string parameters: MantisbtURL, Login, Password, Project, ProjectVersion."
        "\nRecognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify, CreatePrivate."
        "\nUser's local configuration overrides the system wide configuration."
        "\nParameters can be overridden via $Mantisbt_PARAM environment variables."
        "\n"
        "\nFMTFILE default to "CONF_DIR"/plugins/mantisbt_format.conf."
        "\nFMTFILE2 default to "CONF_DIR"/plugins/mantisbt_formatdup.conf."
    );

    enum {
        OPT_v = 1 << 0,
        OPT_d = 1 << 1,
        OPT_c = 1 << 2,
        OPT_F = 1 << 3,
        OPT_A = 1 << 4,
        OPT_t = 1 << 5,
        OPT_f = 1 << 6,
        OPT_h = 1 << 7,
        OPT_r = 1 << 8,
        OPT_D = 1 << 9,
    };

    const char *dump_dir_name = ".";
    GList *conf_file = NULL;
    const char *fmt_file = CONF_DIR"/plugins/mantisbt_format.conf";
    const char *fmt_file2 = CONF_DIR"/plugins/mantisbt_formatdup.conf";
    char *abrt_hash = NULL;
    char *ticket_no = NULL;
    const char *tracker_str = "ABRT Server";
    char *debug_str = NULL;
    mantisbt_settings_t mbt_settings = { 0 };
    /* Keep enum above and order of options below in sync! */
    struct options program_options[] = {
        OPT__VERBOSE(&g_verbose),
        OPT_STRING(   'd', NULL, &dump_dir_name , "DIR"    , _("Problem directory")),
        OPT_LIST(     'c', NULL, &conf_file     , "FILE"   , _("Configuration file (may be given many times)")),
        OPT_STRING(   'F', NULL, &fmt_file      , "FILE"   , _("Formatting file for initial comment")),
        OPT_STRING(   'A', NULL, &fmt_file2     , "FILE"   , _("Formatting file for duplicates")),
        OPT_OPTSTRING('t', "ticket", &ticket_no , "ID"     , _("Attach FILEs [to issue with this ID]")),
        OPT_BOOL(     'f', NULL, NULL,                       _("Force reporting even if this problem is already reported")),
        OPT_STRING(   'h', "duphash", &abrt_hash, "DUPHASH", _("Print BUG_ID which has given DUPHASH")),
        OPT_STRING(   'r', "tracker", &tracker_str, "TRACKER_NAME", _("A name of bug tracker for an additional URL from 'reported_to'")),

        OPT_OPTSTRING('D', "debug", &debug_str  , "STR"    , _("Debug")),
        OPT_END()
    };

    unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
    argv += optind;

    export_abrt_envvars(0);

    map_string_t *settings = new_map_string();

    {
        char *local_conf = NULL;
        if (!conf_file)
        {
            conf_file = g_list_append(conf_file, (char*) CONF_DIR"/plugins/mantisbt.conf");
            local_conf = xasprintf("%s"USER_HOME_CONFIG_PATH"/mantisbt.conf", getenv("HOME"));
            conf_file = g_list_append(conf_file, local_conf);
        }
        while (conf_file)
        {
            char *fn = (char *)conf_file->data;
            log_notice("Loading settings from '%s'", fn);
            load_conf_file(fn, settings, /*skip key w/o values:*/ false);
            log_debug("Loaded '%s'", fn);
            conf_file = g_list_delete_link(conf_file, conf_file);
        }
        free(local_conf);

        struct dump_dir *dd = NULL;
        if (abrt_hash == NULL)
        {
            dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
            if (!dd)
                error_msg_and_die(_("Can't open problem dir '%s'."), dump_dir_name);
        }

        set_settings(&mbt_settings, settings, dd);
        dd_close(dd);
        /* WRONG! set_settings() does not copy the strings, it merely sets up pointers
         * to settings[] dictionary:
         */
        /*free_map_string(settings);*/
    }

    /* No connection is opened between client and server. Users authentication
     * is performed on every SOAP method call. In the first step we verify the
     * credentials by calling 'mc_login' method.  In the case the credentials are
     * correctly applies the reporter uses them in the next requests. It is not
     * necessary to call 'mc_login' method because the method provides only
     * verification of credentials.
     */
    verify_credentials(&mbt_settings);

    if (abrt_hash)
    {
        log_warning(_("Looking for similar problems in MantisBT"));
        GList *ids = mantisbt_search_by_abrt_hash(&mbt_settings, abrt_hash);
        mantisbt_settings_free(&mbt_settings);

        if (ids == NULL)
            return EXIT_FAILURE;

        puts(ids->data);
        response_values_free(ids);
        return EXIT_SUCCESS;
    }

    mantisbt_get_project_id_from_name(&mbt_settings);

    if (opts & OPT_t)
    {
        if (!argv[0])
            show_usage_and_die(program_usage_string, program_options);

        if (!ticket_no)
        {
            struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
            if (!dd)
                xfunc_die();
            report_result_t *reported_to = find_in_reported_to(dd, "MantisBT");
            dd_close(dd);

            if (!reported_to || !reported_to->url)
                error_msg_and_die(_("Can't get MantisBT ID because this problem has not yet been reported to MantisBT."));

            char *url = reported_to->url;
            reported_to->url = NULL;
            free_report_result(reported_to);

            if (prefixcmp(url, mbt_settings.m_mantisbt_url) != 0)
                error_msg_and_die(_("This problem has been reported to MantisBT '%s' which differs from the configured MantisBT '%s'."), url, mbt_settings.m_mantisbt_url);

            ticket_no = strrchr(url, '=');
            if (!ticket_no)
                error_msg_and_die(_("Malformed url to MantisBT '%s'."), url);

            /* won't ever call free on it - it simplifies the code a lot */
            ticket_no = xstrdup(ticket_no + 1);
            log_warning(_("Using MantisBT ID '%s'"), ticket_no);
        }

        /* Attach files to existing MantisBT issues */
        while (*argv)
        {
            const char *path = *argv++;
            char *filename = basename(path);
            log_warning(_("Attaching file '%s' to issue %s"), filename, ticket_no);
            mantisbt_attach_file(&mbt_settings, ticket_no, filename, path);
        }

        return 0;
    }

    /* Create new issue in MantisBT */

    if (!(opts & OPT_f))
    {
        struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
        if (!dd)
            xfunc_die();
        report_result_t *reported_to = find_in_reported_to(dd, "MantisBT");
        dd_close(dd);

        if (reported_to && reported_to->url)
        {
            char *msg = xasprintf(_("This problem was already reported to MantisBT (see '%s')."
                            " Do you still want to create a new issue?"),
                            reported_to->url);
            int yes = ask_yes_no(msg);
            free(msg);
            if (!yes)
                return 0;
        }
        free_report_result(reported_to);
    }

    problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
    if (!problem_data)
        xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */

    const char *category = problem_data_get_content_or_die(problem_data, FILENAME_COMPONENT);
    const char *duphash   = problem_data_get_content_or_die(problem_data, FILENAME_DUPHASH);

    if (opts & OPT_D)
    {
        problem_formatter_t *pf = problem_formatter_new();
        problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, /* optional section */ 0);

        if (problem_formatter_load_file(pf, fmt_file))
            error_msg_and_die("Invalid format file: %s", fmt_file);

        problem_report_t *pr = NULL;
        if (problem_formatter_generate_report(pf, problem_data, &pr))
            error_msg_and_die("Failed to format issue report from problem data");

        printf("summary: %s\n"
                "\n"
                "Description:\n%s\n"
                "Additional info:\n%s\n"
                , problem_report_get_summary(pr)
                , problem_report_get_description(pr)
                , problem_report_get_section(pr, PR_SEC_ADDITIONAL_INFO)
        );

        puts("attachments:");
        for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
            printf(" %s\n", (const char *)a->data);

        problem_report_free(pr);
        problem_formatter_free(pf);
        exit(0);
    }

     unsigned long bug_id = 0;

    /* If REMOTE_RESULT contains "DUPLICATE 12345", we consider it a dup of 12345
     * and won't search on MantisBT server.
     */
    char *remote_result;
    remote_result = problem_data_get_content_or_NULL(problem_data, FILENAME_REMOTE_RESULT);
    if (remote_result)
    {
        char *cmd = strtok(remote_result, " \n");
        char *id = strtok(NULL, " \n");

        if (!prefixcmp(cmd, "DUPLICATE"))
        {
            errno = 0;
            char *e;
            bug_id = strtoul(id, &e, 10);
            if (errno || id == e || *e != '\0' || bug_id > INT_MAX)
            {
                /* error / no digits / illegal trailing chars / too big a number */
                bug_id = 0;
            }
        }
    }

    mantisbt_issue_info_t *ii;
    if (!bug_id)
    {
        log_warning(_("Checking for duplicates"));

        int existing_id = -1;
        int crossver_id = -1;
        {
            /* Figure out whether we want to match category
             * when doing dup search.
             */
            const char *category_substitute = is_in_comma_separated_list(category, mbt_settings.m_DontMatchComponents) ? NULL : category;

            /* We don't do dup detection across versions (see below why),
             * but we do add a note if cross-version potential dup exists.
             * For that, we search for cross version dups first:
             */
            // SOAP API searching method is not in the final version, it's possible the project will be string
            GList *crossver_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, /*version*/ NULL, duphash);

            unsigned crossver_bugs_count = g_list_length(crossver_bugs_ids);
            log_debug("MantisBT has %i reports with duphash '%s' including cross-version ones",
                    crossver_bugs_count, duphash);
            if (crossver_bugs_count > 0)
                crossver_id = atoi(g_list_first(crossver_bugs_ids)->data);

            if (crossver_bugs_count > 0)
            {
                // SOAP API searching method is not in the final version, it's possible the project will be string
                GList *dup_bugs_ids = mantisbt_search_duplicate_issues(&mbt_settings, category_substitute, mbt_settings.m_project_version, duphash);

                unsigned dup_bugs_count =  g_list_length(dup_bugs_ids);
                log_debug("MantisBT has %i reports with duphash '%s'",
                        dup_bugs_count, duphash);
                if (dup_bugs_count > 0)
                    existing_id = atoi(g_list_first(dup_bugs_ids)->data);
            }
        }

        if (existing_id < 0)
        {
            /* Create new issue */
            log_warning(_("Creating a new issue"));
            problem_formatter_t *pf = problem_formatter_new();
            problem_formatter_add_section(pf, PR_SEC_ADDITIONAL_INFO, 0);

            if (problem_formatter_load_file(pf, fmt_file))
                error_msg_and_die(_("Invalid format file: %s"), fmt_file);

            problem_report_t *pr = NULL;
            if (problem_formatter_generate_report(pf, problem_data, &pr))
                error_msg_and_die(_("Failed to format problem data"));

            if (crossver_id >= 0)
                problem_report_buffer_printf(
                        problem_report_get_buffer(pr, PR_SEC_DESCRIPTION),
                        "\nPotential duplicate: issue %u\n", crossver_id);

            problem_formatter_free(pf);

            /* get tracker URL if exists */
            struct dump_dir *dd = dd_opendir(dump_dir_name, 0);
            char *tracker_url = NULL;
            if (dd)
            {
                report_result_t *reported_to = find_in_reported_to(dd, tracker_str);
                dd_close(dd);

                if (reported_to && reported_to->url)
                {
                    log_warning(_("Adding External URL to issue"));
                    tracker_url = xstrdup(reported_to->url);
                    free_report_result(reported_to);
                }
            }

            int new_id = mantisbt_create_new_issue(&mbt_settings, problem_data, pr, tracker_url);

            free(tracker_url);

            if (new_id == -1)
                return EXIT_FAILURE;

            log_warning(_("Adding attachments to issue %i"), new_id);
            char *new_id_str = xasprintf("%u", new_id);

            for (GList *a = problem_report_get_attachments(pr); a != NULL; a = g_list_next(a))
            {
                const char *item_name = (const char *)a->data;
                struct problem_item *item = problem_data_get_item_or_NULL(problem_data, item_name);
                if (!item)
                    continue;
                else if (item->flags & CD_FLAG_TXT)
                    mantisbt_attach_data(&mbt_settings, new_id_str, item_name, item->content, strlen(item->content));
                else if (item->flags & CD_FLAG_BIN)
                    mantisbt_attach_file(&mbt_settings, new_id_str, item_name, item->content);
            }

            free(new_id_str);
            problem_report_free(pr);
            ii = mantisbt_issue_info_new();
            ii->mii_id = new_id;
            ii->mii_status = xstrdup("new");

            goto finish;
        }

        bug_id = existing_id;
    }

    ii = mantisbt_get_issue_info(&mbt_settings, bug_id);

    log_warning(_("Bug is already reported: %i"), ii->mii_id);

    /* Follow duplicates */
    if ((strcmp(ii->mii_status, "closed") == 0)
     && (strcmp(ii->mii_resolution, "duplicate") == 0)
    ) {
        mantisbt_issue_info_t *origin = mantisbt_find_origin_bug_closed_duplicate(&mbt_settings, ii);
        if (origin)
        {
            mantisbt_issue_info_free(ii);
            ii = origin;
        }
    }

    /* TODO CC list
     * Is no MantisBT SOAP API method which allows adding users to CC list
     * without updating issue.
     */

    /* Add comment and bt */
    const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT);
    if (comment && comment[0])
    {
        problem_formatter_t *pf = problem_formatter_new();

        if (problem_formatter_load_file(pf, fmt_file2))
            error_msg_and_die(_("Invalid duplicate format file: '%s"), fmt_file2);

        problem_report_t *pr;
        if (problem_formatter_generate_report(pf, problem_data, &pr))
            error_msg_and_die(_("Failed to format duplicate comment from problem data"));

        const char *mbtcomment = problem_report_get_description(pr);

        int dup_comment = is_comment_dup(ii->mii_notes, mbtcomment);
        if (!dup_comment)
        {
            log_warning(_("Adding new comment to issue %d"), ii->mii_id);
            mantisbt_add_issue_note(&mbt_settings, ii->mii_id, mbtcomment);

            const char *bt = problem_data_get_content_or_NULL(problem_data, FILENAME_BACKTRACE);
            unsigned rating = 0;
            const char *rating_str = problem_data_get_content_or_NULL(problem_data, FILENAME_RATING);
            /* python doesn't have rating file */
            if (rating_str)
                rating = xatou(rating_str);
            if (bt && rating > ii->mii_best_bt_rating)
            {
                char *bug_id_str = xasprintf("%i", ii->mii_id);

                log_warning(_("Attaching better backtrace"));

                // find unique filename of attachment
                char *name = NULL;
                for (int i = 0;; ++i)
                {
                    if (i == 0)
                        name = xasprintf("%s", FILENAME_BACKTRACE);
                    else
                        name = xasprintf("%s%d", FILENAME_BACKTRACE, i);

                    if (g_list_find_custom(ii->mii_attachments, name, (GCompareFunc) strcmp) == NULL)
                        break;

                    free(name);
                }
                mantisbt_attach_data(&mbt_settings, bug_id_str, name, bt, strlen(bt));

                free(name);
                free(bug_id_str);
            }
        }
        else
            log_warning(_("Found the same comment in the issue history, not adding a new one"));

        problem_report_free(pr);
        problem_formatter_free(pf);
    }

finish:
    log_warning(_("Status: %s%s%s %s/view.php?id=%u"),
                ii->mii_status,
                ii->mii_resolution ? " " : "",
                ii->mii_resolution ? ii->mii_resolution : "",
                mbt_settings.m_mantisbt_url,
                ii->mii_id);

    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (dd)
    {
        report_result_t rr = { .label = (char *)"MantisBT" };
        rr.url = xasprintf("%s/view.php?id=%u", mbt_settings.m_mantisbt_url, ii->mii_id);
        add_reported_to_entry(dd, &rr);
        free(rr.url);
        dd_close(dd);
    }

    mantisbt_settings_free(&mbt_settings);
    return 0;
}