Example #1
0
static
xmlrpc_value *rhbz_search_duphash(struct abrt_xmlrpc *ax,
                        const char *product,
                        const char *version,
                        const char *component,
                        const char *duphash)
{
    struct strbuf *query = strbuf_new();

    strbuf_append_strf(query, "ALL whiteboard:\"%s\"", duphash);

    if (product)
        strbuf_append_strf(query, " product:\"%s\"", product);

    if (version)
        strbuf_append_strf(query, " version:\"%s\"", version);

    if (component)
        strbuf_append_strf(query, " component:\"%s\"", component);

    char *s = strbuf_free_nobuf(query);
    VERB3 log("search for '%s'", s);
    xmlrpc_value *search = abrt_xmlrpc_call(ax, "Bug.search", "({s:s})",
                                         "quicksearch", s);
    free(s);
    xmlrpc_value *bugs = rhbz_get_member("bugs", search);
    xmlrpc_DECREF(search);

    if (!bugs)
        error_msg_and_die(_("Bug.search(quicksearch) return value did not contain member 'bugs'"));

    return bugs;
}
Example #2
0
static
void generate_bz_comment(struct strbuf *result, problem_data_t *pd, GList *comment_fmt_spec)
{
    bool last_line_is_empty = true;
    GList *l = comment_fmt_spec;
    while (l)
    {
        section_t *sec = l->data;
        l = l->next;

        /* Skip special sections such as "%attach" */
        if (sec->name[0] == '%')
            continue;

        if (sec->items)
        {
            /* "Text: item[,item]..." */
            struct strbuf *output = strbuf_new();
            GList *item = sec->items;
            while (item)
            {
                const char *str = item->data;
                item = item->next;
                if (str[0] == '-') /* "-name", ignore it */
                    continue;
                append_item(output, str, pd, comment_fmt_spec);
            }

            if (output->len != 0)
            {
                strbuf_append_strf(result,
                            sec->name[0] ? "%s:\n%s" : "%s%s",
                            sec->name,
                            output->buf
                );
                last_line_is_empty = false;
            }
            strbuf_free(output);
        }
        else
        {
            /* Just "Text" (can be "") */

            /* Filter out consecutive empty lines */
            if (sec->name[0] != '\0' || !last_line_is_empty)
                strbuf_append_strf(result, "%s\n", sec->name);
            last_line_is_empty = (sec->name[0] == '\0');
        }
    }

    /* Nuke any trailing empty lines */
    while (result->len >= 1
     && result->buf[result->len-1] == '\n'
     && (result->len == 1 || result->buf[result->len-2] == '\n')
    ) {
        result->buf[--result->len] = '\0';
    }
}
Example #3
0
static char *build_ids_from_core_backtrace(const char *dump_dir_name)
{
    char *error = NULL;
    char *core_backtrace_path = xasprintf("%s/"FILENAME_CORE_BACKTRACE, dump_dir_name);
    char *json = xmalloc_open_read_close(core_backtrace_path, /*maxsize:*/ NULL);
    free(core_backtrace_path);

    if (!json)
        return NULL;

    struct sr_core_stacktrace *stacktrace = sr_core_stacktrace_from_json_text(json, &error);
    free(json);
    if (!stacktrace)
    {
        if (error)
        {
            log_info("Failed to parse core backtrace: %s", error);
            free(error);
        }
        return NULL;
    }

    struct sr_core_thread *thread = sr_core_stacktrace_find_crash_thread(stacktrace);
    if (!thread)
    {
        log_info("Failed to find crash thread");
        sr_core_stacktrace_free(stacktrace);
        return NULL;
    }

    void *build_id_list = NULL;

    struct strbuf *strbuf = strbuf_new();
    for (struct sr_core_frame *frame = thread->frames;
         frame;
         frame = frame->next)
    {
        if (frame->build_id)
            build_id_list = g_list_prepend(build_id_list, frame->build_id);
    }

    build_id_list = g_list_sort(build_id_list, (GCompareFunc)strcmp);
    for (GList *iter = build_id_list; iter; iter = g_list_next(iter))
    {
        GList *next = g_list_next(iter);
        if (next == NULL || 0 != strcmp(iter->data, next->data))
        {
            strbuf = strbuf_append_strf(strbuf, "%s\n", (char *)iter->data);
        }
    }
    g_list_free(build_id_list);
    sr_core_stacktrace_free(stacktrace);

    return strbuf_free_nobuf(strbuf);
}
Example #4
0
static
int append_text(struct strbuf *result, const char *item_name, const char *content, bool print_item_name)
{
    char *eol = strchrnul(content, '\n');
    if (eol[0] == '\0' || eol[1] == '\0')
    {
        /* one-liner */
        int pad = 16 - (strlen(item_name) + 2);
        if (pad < 0)
            pad = 0;
        if (print_item_name)
            strbuf_append_strf(result,
                    eol[0] == '\0' ? "%s: %*s%s\n" : "%s: %*s%s",
                    item_name, pad, "", content
            );
        else
            strbuf_append_strf(result,
                    eol[0] == '\0' ? "%s\n" : "%s",
                    content
            );
    }
    else
    {
        /* multi-line item */
        if (print_item_name)
            strbuf_append_strf(result, "%s:\n", item_name);
        for (;;)
        {
            eol = strchrnul(content, '\n');
            strbuf_append_strf(result,
                    /* For %bare_multiline_item, we don't want to print colons */
                    (print_item_name ? ":%.*s\n" : "%.*s\n"),
                    (int)(eol - content), content
            );
            if (eol[0] == '\0' || eol[1] == '\0')
                break;
            content = eol + 1;
        }
    }
    return 1;
}
Example #5
0
char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx)
{
    struct strbuf *result = strbuf_new();

    GList *rule_list = load_rule_list(NULL, CONF_DIR"/report_event.conf", /*recursion_depth:*/ 0);

    unsigned pfx_len = strlen(pfx);
    for (;;)
    {
        /* Retrieve each cmd, and fetch its EVENT=foo value */
        char *event_name = NULL;
        char *cmd = pop_next_command(&rule_list,
                &event_name,       /* return event_name */
                (dd ? &dd : NULL), /* match this dd... */
                dump_dir_name,     /* ...or if NULL, this dirname */
                pfx, pfx_len       /* for events with this prefix */
        );
        if (!cmd)
        {
            free_rule_list(rule_list);
            free(event_name);
            break;
        }
        free(cmd);

        if (event_name)
        {
            /* Append "EVENT\n" - only if it is not there yet */
            unsigned e_len = strlen(event_name);
            char *p = result->buf;
            while (p && *p)
            {
                if (strncmp(p, event_name, e_len) == 0 && p[e_len] == '\n')
                    goto skip; /* This event is already in the result */
                p = strchr(p, '\n');
                if (p)
                    p++;
            }
            strbuf_append_strf(result, "%s\n", event_name);
 skip:
            free(event_name);
        }
    }

    return strbuf_free_nobuf(result);
}
Example #6
0
char *kernel_tainted_long(const char *tainted_short)
{
    struct strbuf *tnt_long = strbuf_new();
    while (tainted_short[0] != '\0')
    {
        const int tnt_index = tainted_short[0] - 'A';
        if (tnt_index >= 0 && tnt_index <= 'Z' - 'A')
        {
            const char *const txt = tnts_long[tnt_index];
            if (txt)
                strbuf_append_strf(tnt_long, "%s\n", txt);
        }

        ++tainted_short;
    }

    return strbuf_free_nobuf(tnt_long);
}
Example #7
0
File: thread.c Project: rplnt/abrt
void
btp_thread_append_to_str(struct btp_thread *thread,
                         struct strbuf *str,
                         bool verbose)
{
    int framecount = btp_thread_get_frame_count(thread);
    if (verbose)
    {
        strbuf_append_strf(str, "Thread no. %d (%d frames)\n",
                               thread->number, framecount);
    }
    else
        strbuf_append_str(str, "Thread\n");

    struct btp_frame *frame = thread->frames;
    while (frame)
    {
        btp_frame_append_to_str(frame, str, verbose);
        frame = frame->next;
    }
}
Example #8
0
static
char *make_description_item_multiline(const char *name, const char *content)
{
    char *eol = strchr(content, '\n');
    if (!eol)
        return NULL;

    struct strbuf *buf = strbuf_new();
    strbuf_append_str(buf, name);
    strbuf_append_str(buf, ":\n");
    for (;;)
    {
        eol = strchrnul(content, '\n');
        strbuf_append_strf(buf, ":%.*s\n", (int)(eol - content), content);
        if (*eol == '\0' || eol[1] == '\0')
            break;
        content = eol + 1;
    }

    return strbuf_free_nobuf(buf);
}
Example #9
0
static char *abrt_oops_list_of_tainted_modules(const char *proc_modules)
{
    struct strbuf *result = strbuf_new();

    const char *p = proc_modules;
    for (;;)
    {
        const char *end = strchrnul(p, '\n');
        const char *paren = strchrnul(p, '(');
        /* We look for a line with this format:
         * "kvm_intel 126289 0 - Live 0xf829e000 (taint_flags)"
         * where taint_flags have letters
         * (flags '+' and '-' indicate (un)loading, we must ignore them).
         */
        while (++paren < end)
        {
            if ((unsigned)(toupper(*paren) - 'A') <= 'Z'-'A')
            {
                strbuf_append_strf(result, result->len == 0 ? "%.*s" : ",%.*s",
                        (int)(strchrnul(p,' ') - p), p
                );
                break;
            }
            if (*paren == ')')
                break;
        }

        if (*end == '\0')
            break;
        p = end + 1;
    }

    if (result->len == 0)
    {
        strbuf_free(result);
        return NULL;
    }
    return strbuf_free_nobuf(result);
}
Example #10
0
char* make_description_mailx(problem_data_t *problem_data)
{
    struct strbuf *buf_dsc = strbuf_new();
    struct strbuf *buf_additional_files = strbuf_new();
    struct strbuf *buf_duphash_file = strbuf_new();
    struct strbuf *buf_common_files = strbuf_new();

    GHashTableIter iter;
    char *name;
    struct problem_item *value;
    g_hash_table_iter_init(&iter, problem_data);
    while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
    {
        if (value->flags & CD_FLAG_TXT)
        {
            if ((strcmp(name, FILENAME_DUPHASH) != 0)
             && (strcmp(name, FILENAME_ARCHITECTURE) != 0)
             && (strcmp(name, FILENAME_KERNEL) != 0)
             && (strcmp(name, FILENAME_PACKAGE) != 0)
            ) {
                strbuf_append_strf(buf_additional_files, "%s\n-----\n%s\n\n", name, value->content);
            }
            else if (strcmp(name, FILENAME_DUPHASH) == 0)
                strbuf_append_strf(buf_duphash_file, "%s\n-----\n%s\n\n", name, value->content);
            else
                strbuf_append_strf(buf_common_files, "%s\n-----\n%s\n\n", name, value->content);
        }
    }

    char *common_files = strbuf_free_nobuf(buf_common_files);
    char *duphash_file = strbuf_free_nobuf(buf_duphash_file);
    char *additional_files = strbuf_free_nobuf(buf_additional_files);

    strbuf_append_strf(buf_dsc, "Duplicate check\n=====\n%s\n\n", duphash_file);
    strbuf_append_strf(buf_dsc, "Common information\n=====\n%s\n\n", common_files);
    strbuf_append_strf(buf_dsc, "Additional information\n=====\n%s\n", additional_files);

    free(common_files);
    free(duphash_file);
    free(additional_files);

    return strbuf_free_nobuf(buf_dsc);
}
Example #11
0
char *make_description(problem_data_t *problem_data, char **names_to_skip,
                       unsigned max_text_size, unsigned desc_flags)
{
    struct strbuf *buf_dsc = strbuf_new();

    const char *analyzer = problem_data_get_content_or_NULL(problem_data,
                                                            FILENAME_ANALYZER);

    GList *list = g_hash_table_get_keys(problem_data);
    list = g_list_sort(list, (GCompareFunc)strcmp);
    GList *l;

    /* Print one-liners. Format:
     * NAME1: <maybe more spaces>VALUE1
     * NAME2: <maybe more spaces>VALUE2
     */
    bool empty = true;
    l = list;
    while (l)
    {
        const char *key = l->data;
        l = l->next;

        /* Skip items we are not interested in */
//TODO: optimize by doing this once, not 3 times:
        if (names_to_skip
            && rejected_name(key, names_to_skip, desc_flags))
            continue;

        struct problem_item *item = g_hash_table_lookup(problem_data, key);
        if (!item)
            continue;

        if ((desc_flags & MAKEDESC_SHOW_ONLY_LIST) && !(item->flags & CD_FLAG_LIST))
            continue;

        if ((item->flags & CD_FLAG_TXT)
         && strlen(item->content) <= max_text_size
        ) {
            char *formatted = problem_item_format(item);
            char *output = formatted ? formatted : item->content;
            char *eol = strchr(output, '\n');
            if (!eol)
            {
                int pad = 16 - (strlen(key) + 2);
                if (pad < 0) pad = 0;
                strbuf_append_strf(buf_dsc, "%s: %*s%s\n", key, pad, "", output);
                empty = false;
            }
            free(formatted);
        }
    }

    bool append_empty_line = !empty;
    if (desc_flags & MAKEDESC_SHOW_FILES)
    {
        /* Print file info. Format:
         * <empty line if needed>
         * NAME1: <maybe more spaces>Binary file, NNN bytes
         * NAME2: <maybe more spaces>Text file, NNN bytes
         *
         * In many cases, it is useful to know how big binary files are
         * (for example, helps with diagnosing bug upload problems)
         */
        l = list;
        while (l)
        {
            const char *key = l->data;
            l = l->next;

            /* Skip items we are not interested in */
            if (names_to_skip
                && rejected_name(key, names_to_skip, desc_flags))
                continue;

            struct problem_item *item = g_hash_table_lookup(problem_data, key);
            if (!item)
                continue;

            if ((desc_flags & MAKEDESC_SHOW_ONLY_LIST) && !(item->flags & CD_FLAG_LIST))
                continue;

            if ((item->flags & CD_FLAG_BIN)
             || ((item->flags & CD_FLAG_TXT) && strlen(item->content) > max_text_size)
            ) {
                if (append_empty_line)
                    strbuf_append_char(buf_dsc, '\n');
                append_empty_line = false;

                struct stat statbuf;
                int stat_err = 0;
                if (item->flags & CD_FLAG_BIN)
                    stat_err = stat(item->content, &statbuf);
                else
                    statbuf.st_size = strlen(item->content);

                /* We don't print item->content for CD_FLAG_BIN, as it is
                 * always "/path/to/dump/dir/KEY" - not informative.
                 */
                int pad = 16 - (strlen(key) + 2);
                if (pad < 0) pad = 0;
                strbuf_append_strf(buf_dsc,
                        (!stat_err ? "%s: %*s%s file, %llu bytes\n" : "%s: %*s%s file\n"),
                        key,
                        pad, "",
                        ((item->flags & CD_FLAG_BIN) ? "Binary" : "Text"),
                        (long long)statbuf.st_size
                );
                empty = false;
            }
        }
    }

    if (desc_flags & MAKEDESC_SHOW_MULTILINE)
    {
        /* Print multi-liners. Format:
         * <empty line if needed>
         * NAME:
         * :LINE1
         * :LINE2
         * :LINE3
         */
        l = list;
        while (l)
        {
            const char *key = l->data;
            l = l->next;

            /* Skip items we are not interested in */
            if (names_to_skip
                && rejected_name(key, names_to_skip, desc_flags))
                continue;

            struct problem_item *item = g_hash_table_lookup(problem_data, key);
            if (!item)
                continue;

            if ((desc_flags & MAKEDESC_SHOW_ONLY_LIST) && !(item->flags & CD_FLAG_LIST))
                continue;

            if ((item->flags & CD_FLAG_TXT)
                && (strlen(item->content) <= max_text_size
                    || (!strcmp(analyzer, "Kerneloops") && !strcmp(key, FILENAME_BACKTRACE))))
            {
                char *formatted = problem_item_format(item);
                char *output = make_description_item_multiline(key, formatted ? formatted : item->content);

                if (output)
                {
                    if (!empty)
                        strbuf_append_str(buf_dsc, "\n");

                    strbuf_append_str(buf_dsc, output);
                    empty = false;
                    free(output);
                }

                free(formatted);
            }
        }
    }

    g_list_free(list);

    return strbuf_free_nobuf(buf_dsc);
}
Example #12
0
static char *run_unstrip_n(const char *dump_dir_name, unsigned timeout_sec)
{
    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        return NULL;

    char *uid_str = dd_load_text_ext(dd, FILENAME_UID, DD_FAIL_QUIETLY_ENOENT | DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
    dd_close(dd);
    uid_t uid = -1L;
    if (uid_str)
    {
        uid = xatoi_positive(uid_str);
        free(uid_str);
        if (uid == geteuid())
        {
            uid = -1L; /* no need to setuid/gid if we are already under right uid */
        }
    }

    int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETSID | EXECFLG_QUIET;
    if (uid != (uid_t)-1L)
        flags |= EXECFLG_SETGUID;
    VERB1 flags &= ~EXECFLG_QUIET;
    int pipeout[2];
    char* args[4];
    args[0] = (char*)"eu-unstrip";
    args[1] = xasprintf("--core=%s/"FILENAME_COREDUMP, dump_dir_name);
    args[2] = (char*)"-n";
    args[3] = NULL;
    pid_t child = fork_execv_on_steroids(flags, args, pipeout, /*env_vec:*/ NULL, /*dir:*/ NULL, uid);
    free(args[1]);

    /* Bugs in unstrip or corrupted coredumps can cause it to enter infinite loop.
     * Therefore we have a (largish) timeout, after which we kill the child.
     */
    int t = time(NULL); /* int is enough, no need to use time_t */
    int endtime = t + timeout_sec;
    struct strbuf *buf_out = strbuf_new();
    while (1)
    {
        int timeout = endtime - t;
        if (timeout < 0)
        {
            kill(child, SIGKILL);
            strbuf_append_strf(buf_out, "\nTimeout exceeded: %u seconds, killing %s\n", timeout_sec, args[0]);
            break;
        }

        /* We don't check poll result - checking read result is enough */
        struct pollfd pfd;
        pfd.fd = pipeout[0];
        pfd.events = POLLIN;
        poll(&pfd, 1, timeout * 1000);

        char buff[1024];
        int r = read(pipeout[0], buff, sizeof(buff) - 1);
        if (r <= 0)
            break;
        buff[r] = '\0';
        strbuf_append_str(buf_out, buff);
        t = time(NULL);
    }
    close(pipeout[0]);

    /* Prevent having zombie child process */
    int status;
    waitpid(child, &status, 0);

    if (status != 0)
    {
        /* unstrip didnt exit with exit code 0 */
        strbuf_free(buf_out);
        return NULL;
    }

    return strbuf_free_nobuf(buf_out);
}
Example #13
0
/**
 *
 * @param[out] status See `man 2 wait` for status information.
 * @return Malloc'ed string
 */
static char* exec_vp(char **args, int redirect_stderr, int exec_timeout_sec, int *status)
{
    /* Nuke everything which may make setlocale() switch to non-POSIX locale:
     * we need to avoid having gdb output in some obscure language.
     */
    static const char *const env_vec[] = {
        "LANG",
        "LC_ALL",
        "LC_COLLATE",
        "LC_CTYPE",
        "LC_MESSAGES",
        "LC_MONETARY",
        "LC_NUMERIC",
        "LC_TIME",
        /* Workaround for
         * http://sourceware.org/bugzilla/show_bug.cgi?id=9622
         * (gdb emitting ESC sequences even with -batch)
         */
        "TERM",
        NULL
    };

    int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETSID | EXECFLG_QUIET;
    if (redirect_stderr)
        flags |= EXECFLG_ERR2OUT;
    VERB1 flags &= ~EXECFLG_QUIET;

    int pipeout[2];
    pid_t child = fork_execv_on_steroids(flags, args, pipeout, (char**)env_vec, /*dir:*/ NULL, /*uid(unused):*/ 0);

    /* We use this function to run gdb and unstrip. Bugs in gdb or corrupted
     * coredumps were observed to cause gdb to enter infinite loop.
     * Therefore we have a (largish) timeout, after which we kill the child.
     */
    ndelay_on(pipeout[0]);
    int t = time(NULL); /* int is enough, no need to use time_t */
    int endtime = t + exec_timeout_sec;
    struct strbuf *buf_out = strbuf_new();
    while (1)
    {
        int timeout = endtime - t;
        if (timeout < 0)
        {
            kill(child, SIGKILL);
            strbuf_append_strf(buf_out, "\n"
                        "Timeout exceeded: %u seconds, killing %s.\n"
                        "Looks like gdb hung while generating backtrace.\n"
                        "This may be a bug in gdb. Consider submitting a bug report to gdb developers.\n"
                        "Please attach coredump from this crash to the bug report if you do.\n",
                        exec_timeout_sec, args[0]
            );
            break;
        }

        /* We don't check poll result - checking read result is enough */
        struct pollfd pfd;
        pfd.fd = pipeout[0];
        pfd.events = POLLIN;
        poll(&pfd, 1, timeout * 1000);

        char buff[1024];
        int r = read(pipeout[0], buff, sizeof(buff) - 1);
        if (r <= 0)
        {
            /* I did see EAGAIN happening here */
            if (r < 0 && errno == EAGAIN)
                goto next;
            break;
        }
        buff[r] = '\0';
        strbuf_append_str(buf_out, buff);
 next:
        t = time(NULL);
    }
    close(pipeout[0]);

    /* Prevent having zombie child process, and maybe collect status
     * (note that status == NULL is ok too) */
    safe_waitpid(child, status, 0);

    return strbuf_free_nobuf(buf_out);
}
Example #14
0
char *get_backtrace(const char *dump_dir_name, unsigned timeout_sec, const char *debuginfo_dirs)
{
    INITIALIZE_LIBABRT();

    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        return NULL;
    char *executable = dd_load_text(dd, FILENAME_EXECUTABLE);
    dd_close(dd);

    /* Let user know what's going on */
    log(_("Generating backtrace"));

    unsigned i = 0;
    char *args[25];
    args[i++] = (char*)"gdb";
    args[i++] = (char*)"-batch";
    struct strbuf *set_debug_file_directory = strbuf_new();
    unsigned auto_load_base_index = 0;
    if(debuginfo_dirs == NULL)
    {
        // set non-existent debug file directory to prevent resolving
        // function names - we need offsets for core backtrace.
        strbuf_append_str(set_debug_file_directory, "set debug-file-directory /");
    }
    else
    {
        strbuf_append_str(set_debug_file_directory, "set debug-file-directory /usr/lib/debug");

        struct strbuf *debug_directories = strbuf_new();
        const char *p = debuginfo_dirs;
        while (1)
        {
            while (*p == ':')
                p++;
            if (*p == '\0')
                break;
            const char *colon_or_nul = strchrnul(p, ':');
            strbuf_append_strf(debug_directories, "%s%.*s/usr/lib/debug", (debug_directories->len == 0 ? "" : ":"),
                                                                          (int)(colon_or_nul - p), p);
            p = colon_or_nul;
        }

        strbuf_append_strf(set_debug_file_directory, ":%s", debug_directories->buf);

        args[i++] = (char*)"-iex";
        auto_load_base_index = i;
        args[i++] = xasprintf("add-auto-load-safe-path %s", debug_directories->buf);
        args[i++] = (char*)"-iex";
        args[i++] = xasprintf("add-auto-load-scripts-directory %s", debug_directories->buf);

        strbuf_free(debug_directories);
    }

    args[i++] = (char*)"-ex";
    const unsigned debug_dir_cmd_index = i++;
    args[debug_dir_cmd_index] = strbuf_free_nobuf(set_debug_file_directory);

    /* "file BINARY_FILE" is needed, without it gdb cannot properly
     * unwind the stack. Currently the unwind information is located
     * in .eh_frame which is stored only in binary, not in coredump
     * or debuginfo.
     *
     * Fedora GDB does not strictly need it, it will find the binary
     * by its build-id.  But for binaries either without build-id
     * (= built on non-Fedora GCC) or which do not have
     * their debuginfo rpm installed gdb would not find BINARY_FILE
     * so it is still makes sense to supply "file BINARY_FILE".
     *
     * Unfortunately, "file BINARY_FILE" doesn't work well if BINARY_FILE
     * was deleted (as often happens during system updates):
     * gdb uses specified BINARY_FILE
     * even if it is completely unrelated to the coredump.
     * See https://bugzilla.redhat.com/show_bug.cgi?id=525721
     *
     * TODO: check mtimes on COREFILE and BINARY_FILE and not supply
     * BINARY_FILE if it is newer (to at least avoid gdb complaining).
     */
    args[i++] = (char*)"-ex";
    const unsigned file_cmd_index = i++;
    args[file_cmd_index] = xasprintf("file %s", executable);
    free(executable);

    args[i++] = (char*)"-ex";
    const unsigned core_cmd_index = i++;
    args[core_cmd_index] = xasprintf("core-file %s/"FILENAME_COREDUMP, dump_dir_name);

    args[i++] = (char*)"-ex";
    const unsigned bt_cmd_index = i++;
    /*args[9] = ... see below */
    args[i++] = (char*)"-ex";
    args[i++] = (char*)"info sharedlib";
    /* glibc's abort() stores its message in __abort_msg variable */
    args[i++] = (char*)"-ex";
    args[i++] = (char*)"print (char*)__abort_msg";
    args[i++] = (char*)"-ex";
    args[i++] = (char*)"print (char*)__glib_assert_msg";
    args[i++] = (char*)"-ex";
    args[i++] = (char*)"info all-registers";
    args[i++] = (char*)"-ex";
    const unsigned dis_cmd_index = i++;
    args[dis_cmd_index] = (char*)"disassemble";
    args[i++] = NULL;

    /* Get the backtrace, but try to cap its size */
    /* Limit bt depth. With no limit, gdb sometimes OOMs the machine */
    unsigned bt_depth = 1024;
    const char *thread_apply_all = "thread apply all -ascending";
    const char *full = " full";
    char *bt = NULL;
    while (1)
    {
        args[bt_cmd_index] = xasprintf("%s backtrace %u%s", thread_apply_all, bt_depth, full);
        bt = exec_vp(args, /*redirect_stderr:*/ 1, timeout_sec, NULL);
        free(args[bt_cmd_index]);
        if ((bt && strnlen(bt, 256*1024) < 256*1024) || bt_depth <= 32)
        {
            break;
        }

        bt_depth /= 2;
        if (bt)
            log("Backtrace is too big (%u bytes), reducing depth to %u",
                        (unsigned)strlen(bt), bt_depth);
        else
            /* (NB: in fact, current impl. of exec_vp() never returns NULL) */
            log("Failed to generate backtrace, reducing depth to %u",
                        bt_depth);
        free(bt);

        /* Replace -ex disassemble (which disasms entire function $pc points to)
         * to a version which analyzes limited, small patch of code around $pc.
         * (Users reported a case where bare "disassemble" attempted to process
         * entire .bss).
         * TODO: what if "$pc-N" underflows? in my test, this happens:
         * Dump of assembler code from 0xfffffffffffffff0 to 0x30:
         * End of assembler dump.
         * (IOW: "empty" dump)
         */
        args[dis_cmd_index] = (char*)"disassemble $pc-20, $pc+64";

        if (bt_depth <= 64 && thread_apply_all[0] != '\0')
        {
            /* This program likely has gazillion threads, dont try to bt them all */
            bt_depth = 128;
            thread_apply_all = "";
        }
        if (bt_depth <= 64 && full[0] != '\0')
        {
            /* Looks like there are gigantic local structures or arrays, disable "full" bt */
            bt_depth = 128;
            full = "";
        }
    }

    if (auto_load_base_index > 0)
    {
        free(args[auto_load_base_index]);
        free(args[auto_load_base_index + 2]);
    }

    free(args[debug_dir_cmd_index]);
    free(args[file_cmd_index]);
    free(args[core_cmd_index]);
    return bt;
}
Example #15
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 = _(
        "\n& [-vbf] [-g GROUP-NAME]... [-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 Bugzilla."
        "\n"
        "\nThe tool reads DIR. Then it logs in to Bugzilla and tries to find a bug"
        "\nwith the same abrt_hash:HEXSTRING in 'Whiteboard'."
        "\n"
        "\nIf such bug is not found, then a new bug is created. Elements of DIR"
        "\nare stored in the bug as part of bug description or as attachments,"
        "\ndepending on their type and size."
        "\n"
        "\nOtherwise, if such bug is found and it is marked as CLOSED DUPLICATE,"
        "\nthe tool follows the chain of duplicates until it finds a non-DUPLICATE bug."
        "\nThe tool adds a new comment to found bug."
        "\n"
        "\nThe URL to new or modified bug is printed to stdout and recorded in"
        "\n'reported_to' element."
        "\n"
        "\nOption -t uploads FILEs to the already created bug on Bugzilla site."
        "\nThe bug ID is retrieved from directory specified by -d DIR."
        "\nIf problem data in DIR was never reported to Bugzilla, upload will fail."
        "\n"
        "\nOption -tID uploads FILEs to the bug with specified ID on Bugzilla site."
        "\n-d DIR is ignored."
        "\n"
        "\nOption -w adds bugzilla user to bug's CC list."
        "\n"
        "\nIf not specified, CONFFILE defaults to "CONF_DIR"/plugins/bugzilla.conf"
        "\nIts lines should have 'PARAM = VALUE' format."
        "\nRecognized string parameters: BugzillaURL, Login, Password, OSRelease."
        "\nRecognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify."
        "\nParameters can be overridden via $Bugzilla_PARAM environment variables."
        "\n"
        "\nFMTFILE and FMTFILE2 default to "CONF_DIR"/plugins/bugzilla_format.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_b = 1 << 6,
        OPT_f = 1 << 7,
        OPT_w = 1 << 8,
        OPT_h = 1 << 9,
        OPT_g = 1 << 10,
        OPT_D = 1 << 11,
    };
    const char *dump_dir_name = ".";
    GList *conf_file = NULL;
    const char *fmt_file = CONF_DIR"/plugins/bugzilla_format.conf";
    const char *fmt_file2 = fmt_file;
    char *abrt_hash = NULL;
    char *ticket_no = NULL;
    char *debug_str = NULL;
    GList *group = 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_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 bug with this ID]")),
        OPT_BOOL(     'b', NULL, NULL,                       _("When creating bug, attach binary files too")),
        OPT_BOOL(     'f', NULL, NULL,                       _("Force reporting even if this problem is already reported")),
        OPT_BOOL(     'w', NULL, NULL,                       _("Add bugzilla user to CC list [of bug with this ID]")),
        OPT_STRING(   'h', "duphash", &abrt_hash, "DUPHASH", _("Print BUG_ID which has given DUPHASH")),
        OPT_LIST(     'g', "group", &group      , "GROUP"  , _("Restrict access to this group only")),
        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();
    struct bugzilla_struct rhbz = { 0 };
    {
        if (!conf_file)
            conf_file = g_list_append(conf_file, (char*) CONF_DIR"/plugins/bugzilla.conf");
        while (conf_file)
        {
            char *fn = (char *)conf_file->data;
            VERB1 log("Loading settings from '%s'", fn);
            load_conf_file(fn, settings, /*skip key w/o values:*/ false);
            VERB3 log("Loaded '%s'", fn);
            conf_file = g_list_delete_link(conf_file, conf_file);
        }
        set_settings(&rhbz, settings);
        /* WRONG! set_settings() does not copy the strings, it merely sets up pointers
         * to settings[] dictionary:
         */
        /*free_map_string(settings);*/
    }

    VERB1 log("Initializing XML-RPC library");
    xmlrpc_env env;
    xmlrpc_env_init(&env);
    xmlrpc_client_setup_global_const(&env);
    if (env.fault_occurred)
        abrt_xmlrpc_die(&env);
    xmlrpc_env_clean(&env);

    struct abrt_xmlrpc *client;
    client = abrt_xmlrpc_new_client(rhbz.b_bugzilla_xmlrpc, rhbz.b_ssl_verify);
    unsigned rhbz_ver = rhbz_version(client);

    if (abrt_hash)
    {
        log(_("Looking for similar problems in bugzilla"));
        char *hash;
        if (prefixcmp(abrt_hash, "abrt_hash:"))
            hash = xasprintf("abrt_hash:%s", abrt_hash);
        else
            hash = xstrdup(abrt_hash);

        /* it's Fedora specific */
        xmlrpc_value *all_bugs = rhbz_search_duphash(client,
                                /*product:*/ "Fedora",
                                /*version:*/ NULL,
                                /*component:*/ NULL,
                                hash);
        free(hash);
        unsigned all_bugs_size = rhbz_array_size(all_bugs);
        if (all_bugs_size > 0)
        {
            int bug_id = rhbz_get_bug_id_from_array0(all_bugs, rhbz_ver);
            printf("%i\n", bug_id);
        }

        return EXIT_SUCCESS;
    }

    if (rhbz.b_login[0] == '\0')
    {
        free(rhbz.b_login);
        rhbz.b_login = ask_bz_login(_("Login is not provided by configuration. Please enter your BZ login:"******"Password is not provided by configuration. Please enter the password for '%s':"), rhbz.b_login);
        rhbz.b_password = ask_bz_password(question);
        free(question);
    }

    if (opts & OPT_t)
    {
        if ((!argv[0] && !(opts & OPT_w)) || (argv[0] && (opts & OPT_w)))
            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, "Bugzilla:");
            dd_close(dd);

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

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

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

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

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

        login(client, &rhbz);

        if (opts & OPT_w)
            rhbz_mail_to_cc(client, xatoi_positive(ticket_no), rhbz.b_login, /* require mail notify */ 0);
        else
        {   /* Attach files to existing BZ */
            while (*argv)
            {
                const char *filename = *argv++;
                VERB1 log("Attaching file '%s' to bug %s", filename, ticket_no);

                int fd = open(filename, O_RDONLY);
                if (fd < 0)
                {
                    perror_msg("Can't open '%s'", filename);
                    continue;
                }

                struct stat st;
                if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode))
                {
                    error_msg("'%s': not a regular file", filename);
                    close(fd);
                    continue;
                }

                rhbz_attach_fd(client, ticket_no, filename, fd, /*flags*/ 0);
                close(fd);
            }
        }

        log(_("Logging out"));
        rhbz_logout(client);

#if 0  /* enable if you search for leaks (valgrind etc) */
        abrt_xmlrpc_free_client(client);
#endif
        return 0;
    }

    /* Create new bug in Bugzilla */

    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, "Bugzilla:");
        dd_close(dd);

        if (reported_to && reported_to->url)
        {
            char *msg = xasprintf("This problem was already reported to Bugzilla (see '%s')."
                            " Do you still want to create a new bug?",
                            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 *component = problem_data_get_content_or_die(problem_data, FILENAME_COMPONENT);
    const char *duphash   = problem_data_get_content_or_NULL(problem_data, FILENAME_DUPHASH);
//COMPAT, remove after 2.1 release
    if (!duphash) duphash = problem_data_get_content_or_die(problem_data, "global_uuid");

    if (!rhbz.b_product || !*rhbz.b_product) /* if not overridden or empty... */
    {
        free(rhbz.b_product);
        free(rhbz.b_product_version);
        map_string_t *osinfo = new_map_string();
        problem_data_get_osinfo(problem_data, osinfo);
        parse_osinfo_for_bz(osinfo, &rhbz.b_product, &rhbz.b_product_version);
        free_map_string(osinfo);

        if (!rhbz.b_product)
            error_msg_and_die(_("Can't determine Bugzilla Product from problem data."));
    }

    if (opts & OPT_D)
    {
        GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file);
        struct strbuf *bzcomment_buf = strbuf_new();
        generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec);
        char *bzcomment = strbuf_free_nobuf(bzcomment_buf);
        char *summary = create_summary_string(problem_data, comment_fmt_spec);
        printf("summary: %s\n"
                "\n"
                "%s"
                , summary, bzcomment
        );
        free(bzcomment);
        free(summary);
        exit(0);
    }

    login(client, &rhbz);


    int bug_id = 0;

    /* If REMOTE_RESULT contains "DUPLICATE 12345", we consider it a dup of 12345
     * and won't search on bz 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;
            }
        }
    }

    struct bug_info *bz = NULL;
    if (!bug_id)
    {
        log(_("Checking for duplicates"));

        int existing_id = -1;
        int crossver_id = -1;
        {
            /* Figure out whether we want to match component
             * when doing dup search.
             */
            const char *component_substitute = is_in_comma_separated_list(component, rhbz.b_DontMatchComponents) ? NULL : component;

            /* 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:
             */
            xmlrpc_value *crossver_bugs = rhbz_search_duphash(client, rhbz.b_product, /*version:*/ NULL,
                            component_substitute, duphash);
            unsigned crossver_bugs_count = rhbz_array_size(crossver_bugs);
            VERB3 log("Bugzilla has %i reports with duphash '%s' including cross-version ones",
                    crossver_bugs_count, duphash);
            if (crossver_bugs_count > 0)
                crossver_id = rhbz_get_bug_id_from_array0(crossver_bugs, rhbz_ver);
            xmlrpc_DECREF(crossver_bugs);

            if (crossver_bugs_count > 0)
            {
                /* In dup detection we require match in product *and version*.
                 * Otherwise we sometimes have bugs in e.g. Fedora 17
                 * considered to be dups of Fedora 16 bugs.
                 * Imagine that F16 is "end-of-lifed" - allowing cross-version
                 * match will make all newly detected crashes DUPed
                 * to a bug in a dead release.
                 */
                xmlrpc_value *dup_bugs = rhbz_search_duphash(client, rhbz.b_product,
                                rhbz.b_product_version, component_substitute, duphash);
                unsigned dup_bugs_count = rhbz_array_size(dup_bugs);
                VERB3 log("Bugzilla has %i reports with duphash '%s'",
                        dup_bugs_count, duphash);
                if (dup_bugs_count > 0)
                    existing_id = rhbz_get_bug_id_from_array0(dup_bugs, rhbz_ver);
                xmlrpc_DECREF(dup_bugs);
            }
        }

        if (existing_id < 0)
        {
            /* Create new bug */
            log(_("Creating a new bug"));

            GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file);

            struct strbuf *bzcomment_buf = strbuf_new();
            generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec);
            if (crossver_id >= 0)
                strbuf_append_strf(bzcomment_buf, "\nPotential duplicate: bug %u\n", crossver_id);
            char *bzcomment = strbuf_free_nobuf(bzcomment_buf);
            char *summary = create_summary_string(problem_data, comment_fmt_spec);
            int new_id = rhbz_new_bug(client, problem_data, rhbz.b_product, rhbz.b_product_version,
                    summary, bzcomment,
                    group
            );
            free(bzcomment);
            free(summary);

            if (new_id == -1)
            {
                error_msg_and_die(_("Failed to create a new bug."));
            }

            log(_("Adding attachments to bug %i"), new_id);
            char new_id_str[sizeof(int)*3 + 2];
            sprintf(new_id_str, "%i", new_id);

            attach_files(client, new_id_str, problem_data, comment_fmt_spec);

//TODO: free_comment_fmt_spec(comment_fmt_spec);

            bz = new_bug_info();
            bz->bi_status = xstrdup("NEW");
            bz->bi_id = new_id;
            goto log_out;
        }

        bug_id = existing_id;
    }

    bz = rhbz_bug_info(client, bug_id);

    log(_("Bug is already reported: %i"), bz->bi_id);

    /* Follow duplicates */
    if ((strcmp(bz->bi_status, "CLOSED") == 0)
     && (strcmp(bz->bi_resolution, "DUPLICATE") == 0)
    ) {
        struct bug_info *origin;
        origin = rhbz_find_origin_bug_closed_duplicate(client, bz);
        if (origin)
        {
            free_bug_info(bz);
            bz = origin;
        }
    }

    if (strcmp(bz->bi_status, "CLOSED") != 0)
    {
        /* Add user's login to CC if not there already */
        if (strcmp(bz->bi_reporter, rhbz.b_login) != 0
         && !g_list_find_custom(bz->bi_cc_list, rhbz.b_login, (GCompareFunc)g_strcmp0)
        ) {
            log(_("Adding %s to CC list"), rhbz.b_login);
            rhbz_mail_to_cc(client, bz->bi_id, rhbz.b_login, RHBZ_NOMAIL_NOTIFY);
        }

        /* Add comment and bt */
        const char *comment = problem_data_get_content_or_NULL(problem_data, FILENAME_COMMENT);
        if (comment && comment[0])
        {
            GList *comment_fmt_spec = load_bzrep_conf_file(fmt_file2);
            struct strbuf *bzcomment_buf = strbuf_new();
            generate_bz_comment(bzcomment_buf, problem_data, comment_fmt_spec);
            char *bzcomment = strbuf_free_nobuf(bzcomment_buf);
//TODO: free_comment_fmt_spec(comment_fmt_spec);

            int dup_comment = is_comment_dup(bz->bi_comments, bzcomment);
            if (!dup_comment)
            {
                log(_("Adding new comment to bug %d"), bz->bi_id);
                rhbz_add_comment(client, bz->bi_id, bzcomment, 0);
                free(bzcomment);

                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 > bz->bi_best_bt_rating)
                {
                    char bug_id_str[sizeof(int)*3 + 2];
                    sprintf(bug_id_str, "%i", bz->bi_id);
                    log(_("Attaching better backtrace"));
                    rhbz_attach_blob(client, bug_id_str, FILENAME_BACKTRACE, bt, strlen(bt),
                                     RHBZ_NOMAIL_NOTIFY);
                }
            }
            else
            {
                free(bzcomment);
                log(_("Found the same comment in the bug history, not adding a new one"));
            }
        }
    }

 log_out:
    log(_("Logging out"));
    rhbz_logout(client);

    log(_("Status: %s%s%s %s/show_bug.cgi?id=%u"),
                bz->bi_status,
                bz->bi_resolution ? " " : "",
                bz->bi_resolution ? bz->bi_resolution : "",
                rhbz.b_bugzilla_url,
                bz->bi_id);

    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (dd)
    {
        char *msg = xasprintf("Bugzilla: URL=%s/show_bug.cgi?id=%u", rhbz.b_bugzilla_url, bz->bi_id);
        add_reported_to(dd, msg);
        free(msg);
        dd_close(dd);
    }

#if 0  /* enable if you search for leaks (valgrind etc) */
    free(rhbz.b_product);
    free(rhbz.b_product_version);
    problem_data_free(problem_data);
    free_bug_info(bz);
    abrt_xmlrpc_free_client(client);
#endif
    return 0;
}
Example #16
0
static char *parse_solution_from_json_list(struct json_object *list, GList **reported_to)
{
    json_object *list_elem, *struct_elem;
    const char *cause, *note, *url;
    struct strbuf *solution_buf = strbuf_new();

    const unsigned length = json_object_array_length(list);

    const char *one_format = _("Your problem seems to be caused by %s\n\n%s\n");
    if (length > 1)
    {
        strbuf_append_str(solution_buf, _("Your problem seems to be caused by one of the following:\n"));
        one_format = "\n* %s\n\n%s\n";
    }

    bool empty = true;
    for (unsigned i = 0; i < length; ++i)
    {
        list_elem = json_object_array_get_idx(list, i);
        if (!list_elem)
            continue;

        struct_elem = json_object_object_get(list_elem, "cause");
        if (!struct_elem)
            continue;

        cause = json_object_get_string(struct_elem);
        if (!cause)
            continue;

        struct_elem = json_object_object_get(list_elem, "note");
        if (!struct_elem)
            continue;

        note = json_object_get_string(struct_elem);
        if (!note)
            continue;

        empty = false;
        strbuf_append_strf(solution_buf, one_format, cause, note);

        struct_elem = json_object_object_get(list_elem, "url");
        if (!struct_elem)
            continue;

        url = json_object_get_string(struct_elem);
        if (url)
        {
            char *reported_to_line = xasprintf("%s: URL=%s", cause, url);
            *reported_to = g_list_append(*reported_to, reported_to_line);
        }
    }

    if (empty)
    {
        strbuf_free(solution_buf);
        return NULL;
    }

    return strbuf_free_nobuf(solution_buf);
}
Example #17
0
void abrt_oops_save_data_in_dump_dir(struct dump_dir *dd, char *oops, const char *proc_modules)
{
    char *first_line = oops;
    char *second_line = (char*)strchr(first_line, '\n'); /* never NULL */
    *second_line++ = '\0';

    if (first_line[0])
        dd_save_text(dd, FILENAME_KERNEL, first_line);
    dd_save_text(dd, FILENAME_BACKTRACE, second_line);

    /* check if trace doesn't have line: 'Your BIOS is broken' */
    if (strstr(second_line, "Your BIOS is broken"))
        dd_save_text(dd, FILENAME_NOT_REPORTABLE,
                _("A kernel problem occurred because of broken BIOS. "
                  "Unfortunately, such problems are not fixable by kernel maintainers."));
    /* check if trace doesn't have line: 'Your hardware is unsupported' */
    else if (strstr(second_line, "Your hardware is unsupported"))
        dd_save_text(dd, FILENAME_NOT_REPORTABLE,
                _("A kernel problem occurred, but your hardware is unsupported, "
                  "therefore kernel maintainers are unable to fix this problem."));
    else
    {
        char *tainted_short = kernel_tainted_short(second_line);
        if (tainted_short)
        {
            log_notice("Kernel is tainted '%s'", tainted_short);
            dd_save_text(dd, FILENAME_TAINTED_SHORT, tainted_short);

            char *tnt_long = kernel_tainted_long(tainted_short);
            dd_save_text(dd, FILENAME_TAINTED_LONG, tnt_long);
            free(tnt_long);

            struct strbuf *reason = strbuf_new();
            const char *fmt = _("A kernel problem occurred, but your kernel has been "
                    "tainted (flags:%s). Kernel maintainers are unable to "
                    "diagnose tainted reports.");
            strbuf_append_strf(reason, fmt, tainted_short);

            char *modlist = !proc_modules ? NULL : abrt_oops_list_of_tainted_modules(proc_modules);
            if (modlist)
            {
                strbuf_append_strf(reason, _(" Tainted modules: %s."), modlist);
                free(modlist);
            }

            dd_save_text(dd, FILENAME_NOT_REPORTABLE, reason->buf);
            strbuf_free(reason);
            free(tainted_short);
        }
    }

    // TODO: add "Kernel oops: " prefix, so that all oopses have recognizable FILENAME_REASON?
    // kernel oops 1st line may look quite puzzling otherwise...
    char *reason_pretty = NULL;
    char *error = NULL;
    struct sr_stacktrace *trace = sr_stacktrace_parse(SR_REPORT_KERNELOOPS, second_line, &error);
    if (trace)
    {
        reason_pretty = sr_stacktrace_get_reason(trace);
        sr_stacktrace_free(trace);
    }
    else
        free(error);

    if (reason_pretty)
    {
        dd_save_text(dd, FILENAME_REASON, reason_pretty);
        free(reason_pretty);
    }
    else
        dd_save_text(dd, FILENAME_REASON, second_line);
}
Example #18
0
int main(int argc, char **argv)
{
    abrt_init(argv);
    enum {
        OPT_v = 1 << 0,
        OPT_d = 1 << 1,
        OPT_g = 1 << 2,
        OPT_b = 1 << 3,
        OPT_u = 1 << 4,
        OPT_r = 1 << 5,
    };

    const char *bugs = NULL, *release = NULL, *dump_dir_path = ".";
    /* Keep enum above and order of options below in sync! */
    struct options program_options[] = {
        OPT__VERBOSE(&g_verbose),
        OPT__DUMP_DIR(&dump_dir_path),
        OPT_GROUP(""),
        OPT_STRING('b', "bugs", &bugs, "ID1[,ID2,...]" , _("List of bug ids")),
        OPT_STRING('u', "url", &bodhi_url, "URL", _("Specify a bodhi server url")),
        OPT_OPTSTRING('r', "release", &release, "RELEASE", _("Specify a release")),
        OPT_END()
    };

    const char *program_usage_string = _(
        "& [-v] [-r[RELEASE]] (-b ID1[,ID2,...] | PKG-NAME) [PKG-NAME]... \n"
        "\n"
        "Search for updates on bodhi server"
    );

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

    if (!bugs && !argv[optind])
        show_usage_and_die(program_usage_string, program_options);

    struct strbuf *query = strbuf_new();
    if (bugs)
        query = strbuf_append_strf(query, "bugs=%s&", bugs);

    if (opts & OPT_r)
    {
        if (release)
        {
            query = strbuf_append_strf(query, "release=%s&", release);
        }
        else
        {
            struct dump_dir *dd = dd_opendir(dump_dir_path, DD_OPEN_READONLY);
            if (!dd)
                xfunc_die();

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

            char *product, *version;
            map_string_t *osinfo = new_map_string();
            problem_data_get_osinfo(problem_data, osinfo);
            parse_osinfo_for_rhts(osinfo, &product, &version);
            query = strbuf_append_strf(query, "release=f%s&", version);
            free(product);
            free(version);
            free_map_string(osinfo);
        }
    }

    if (argv[optind])
    {
        char *escaped = g_uri_escape_string(argv[optind], NULL, 0);
        query = strbuf_append_strf(query, "package=%s&", escaped);
        free(escaped);
    }

    if (query->buf[query->len - 1] == '&')
        query->buf[query->len - 1] = '\0';

    log(_("Searching for updates"));
    GHashTable *update_hash_tbl = bodhi_query_list(query->buf, release);
    strbuf_free(query);

    if (!update_hash_tbl || !g_hash_table_size(update_hash_tbl))
    {
        log(_("No updates for this package found"));
        /*if (update_hash_tbl) g_hash_table_unref(update_hash_tbl);*/
        return 0;
    }

    GHashTableIter iter;
    char *name;
    struct bodhi *b;
    struct strbuf *q = strbuf_new();
    g_hash_table_iter_init(&iter, update_hash_tbl);
    while (g_hash_table_iter_next(&iter, (void **) &name, (void **) &b))
    {
        char *installed_pkg_nvr = rpm_get_nvr_by_pkg_name(name);
        if (installed_pkg_nvr && rpmvercmp(installed_pkg_nvr, b->nvr) >= 0)
        {
            log_info("Update %s is older or same as local version %s, skipping", b->nvr, installed_pkg_nvr);
            free(installed_pkg_nvr);
            continue;
        }
        free(installed_pkg_nvr);

        strbuf_append_strf(q, " %s", b->nvr);
    }

    /*g_hash_table_unref(update_hash_tbl);*/

    if (!q->len)
    {
        /*strbuf_free(q);*/
        log(_("Local version of the package is newer than available updates"));
        return 0;
    }

    /* Message is split into text and command in order to make
     * translator's job easier
     */

    /* We suggest the command which is most likely to exist on user's system,
     * and which is familiar to the largest population of users.
     * There are other tools (pkcon et al) which might be somewhat more
     * convenient (for example, they might be usable from non-root), but they
     * might be not present on the system, may evolve or be superseded,
     * while yum is unlikely to do so.
     */
    strbuf_prepend_str(q, "yum update --enablerepo=fedora --enablerepo=updates-testing");

    char *msg = xasprintf(_("An update exists which might fix your problem. "
                "You can install it by running: %s. "
                "Do you want to continue with reporting the bug?"),
                q->buf
    );
    /*strbuf_free(q);*/

    return !ask_yes_no(msg);
}
Example #19
0
// caller is reposible for freeing *product* and *version*
static void parse_release(const char *release, char** product, char** version, int flags)
{
    /* Fedora has a single non-numeric release - Rawhide */
    if (strstr(release, "Rawhide"))
    {
        *product = xstrdup("Fedora");
        *version = xstrdup("rawhide");
        log_debug("%s: version:'%s' product:'%s'", __func__, *version, *product);
        return;
    }

    /* openSUSE has two non-numeric releases - Factory and Tumbleweed
       None of them is unfortunately identified in any of /etc/SuSE-brand,
       /etc/SuSE-release or /etc/os-release. Keep this piece of code commented
       just not to forget about that. */

    /*
    if (strstr(release, "Factory"))
    {
        *product = xstrdup("openSUSE");
        *version = xstrdup("Factory");
        log_debug("%s: version:'%s' product:'%s'", __func__, *version, *product);
        return;
    }

    if (strstr(release, "Tumbleweed"))
    {
        *product = xstrdup("openSUSE");
        *version = xstrdup("Tumbleweed");
        log_debug("%s: version:'%s' product:'%s'", __func__, *version, *product);
        return;
    }
    */

    bool it_is_rhel = false;

    struct strbuf *buf_product = strbuf_new();
    if (strstr(release, "Fedora"))
    {
        strbuf_append_str(buf_product, "Fedora");
    }
    else if (strstr(release, "Red Hat Enterprise Linux"))
    {
        strbuf_append_str(buf_product, "Red Hat Enterprise Linux");
        it_is_rhel = true;
    }
    else if (strstr(release, "openSUSE"))
    {
        strbuf_append_str(buf_product, "openSUSE");
    }
    else
    {
        /* TODO: add logic for parsing other distros' names here */
        strbuf_append_str(buf_product, release);
    }

    /* Examples of release strings:
     * installed system: "Red Hat Enterprise Linux Server release 6.2 Beta (Santiago)"
     * anaconda: "Red Hat Enterprise Linux 6.2"
     */
    struct strbuf *buf_version = strbuf_new();
    const char *r = strstr(release, "release");
    const char *space = r ? strchr(r, ' ') : NULL;
    if (!space)
    {
        /* Try to find "<space><digit>" sequence */
        space = release;
        while ((space = strchr(space, ' ')) != NULL)
        {
            if (space[1] >= '0' && space[1] <= '9')
                break;
            space++;
        }
    }
    if (space)
    {
        space++;
        /* Observed also: "Fedora 16-Alpha" rhbz#730887 */
        while ((*space >= '0' && *space <= '9') || *space == '.')
        {
            /* Eat string like "5.2" */
            strbuf_append_char(buf_version, *space);
            space++;
        }

        if (flags & RETAIN_ALPHA_BETA_TAIL_IN_VER)
        {
            /* Example: "... 6.2 [Beta ](Santiago)".
             * 'space' variable points to non-digit char after "2".
             * We assume that non-parenthesized text is "Alpha"/"Beta"/etc.
             * If this text is only whitespace, we won't append it.
             */
            const char *to_append = space;
            while (*space && *space != '(') /* go to '(' */
                space++;
            while (space > to_append && space[-1] == ' ') /* back to 1st non-space */
                space--;
            strbuf_append_strf(buf_version, "%.*s", (int)(space - to_append), to_append);
        }
    }

    if ((flags & APPEND_MAJOR_VER_TO_RHEL_PRODUCT) && it_is_rhel)
    {
        char *v = buf_version->buf;
        /* Append "integer part" of version to product:
         * "10.2<anything>" -> append " 10"
         * "10 <anything>"  -> append " 10"
         * "10"             -> append " 10"
         * "10abcde"        -> append ?????
         */
        unsigned idx_dot = strchrnul(v, '.') - v;
        unsigned idx_space = strchrnul(v, ' ') - v;
        strbuf_append_strf(buf_product, " %.*s",
                        (idx_dot < idx_space ? idx_dot : idx_space), v
        );
    }

    *version = strbuf_free_nobuf(buf_version);
    *product = strbuf_free_nobuf(buf_product);

    log_debug("%s: version:'%s' product:'%s'", __func__, *version, *product);
}
Example #20
0
unsigned parse_opts(int argc, char **argv, const struct options *opt,
                const char *usage)
{
    int help = 0;
    int size = parse_opt_size(opt);
    const int LONGOPT_OFFSET = 256;

    struct strbuf *shortopts = strbuf_new();

    struct option *longopts = xzalloc(sizeof(longopts[0]) * (size+2));
    struct option *curopt = longopts;
    int ii;
    for (ii = 0; ii < size; ++ii)
    {
        curopt->name = opt[ii].long_name;
        /*curopt->flag = 0; - xzalloc did it */
        if (opt[ii].short_name)
            curopt->val = opt[ii].short_name;
        else
            curopt->val = LONGOPT_OFFSET + ii;

        switch (opt[ii].type)
        {
            case OPTION_BOOL:
                curopt->has_arg = no_argument;
                if (opt[ii].short_name)
                    strbuf_append_char(shortopts, opt[ii].short_name);
                break;
            case OPTION_INTEGER:
            case OPTION_STRING:
            case OPTION_LIST:
                curopt->has_arg = required_argument;
                if (opt[ii].short_name)
                    strbuf_append_strf(shortopts, "%c:", opt[ii].short_name);
                break;
            case OPTION_OPTSTRING:
                curopt->has_arg = optional_argument;
                if (opt[ii].short_name)
                    strbuf_append_strf(shortopts, "%c::", opt[ii].short_name);
                break;
            case OPTION_GROUP:
            case OPTION_END:
                break;
        }
        //log("curopt[%d].name:'%s' .has_arg:%d .flag:%p .val:%d", (int)(curopt-longopts),
        //      curopt->name, curopt->has_arg, curopt->flag, curopt->val);
        /*
         * getopt_long() thinks that NULL name marks the end of longopts.
         * Example:
         * [0] name:'verbose' val:'v'
         * [1] name:NULL      val:'c'
         * [2] name:'force'   val:'f'
         * ... ... ...
         * In this case, --force won't be accepted!
         * Therefore we can only advance if name is not NULL.
         */
        if (curopt->name)
            curopt++;
    }
    curopt->name = "help";
    curopt->has_arg = no_argument;
    curopt->flag = &help;
    curopt->val = 1;
    /* xzalloc did it already:
    curopt++;
    curopt->name = NULL;
    curopt->has_arg = 0;
    curopt->flag = NULL;
    curopt->val = 0;
    */

    unsigned retval = 0;
    while (1)
    {
        int c = getopt_long(argc, argv, shortopts->buf, longopts, NULL);

        if (c == -1)
            break;

        if (c == '?' || help)
        {
            free(longopts);
            strbuf_free(shortopts);
            xfunc_error_retval = 0; /* this isn't error, exit code = 0 */
            show_usage_and_die(usage, opt);
        }

        for (ii = 0; ii < size; ++ii)
        {
            if (opt[ii].short_name == c || LONGOPT_OFFSET + ii == c)
            {
                if (ii < sizeof(retval)*8)
                    retval |= (1 << ii);

                if (opt[ii].value != NULL) switch (opt[ii].type)
                {
                    case OPTION_BOOL:
                        *(int*)(opt[ii].value) += 1;
                        break;
                    case OPTION_INTEGER:
                        *(int*)(opt[ii].value) = xatoi(optarg);
                        break;
                    case OPTION_STRING:
                    case OPTION_OPTSTRING:
                        if (optarg)
                            *(char**)(opt[ii].value) = (char*)optarg;
                        break;
                    case OPTION_LIST:
                        *(GList**)(opt[ii].value) = g_list_append(*(GList**)(opt[ii].value), optarg);
                        break;
                    case OPTION_GROUP:
                    case OPTION_END:
                        break;
                }
            }
        }
    }

    free(longopts);
    strbuf_free(shortopts);

    return retval;
}