Blame src/plugins/abrt-dump-journal-core.c

Packit 8ea169
/*
Packit 8ea169
 * Copyright (C) 2014  ABRT team
Packit 8ea169
 * Copyright (C) 2014  RedHat Inc
Packit 8ea169
 *
Packit 8ea169
 * This program is free software; you can redistribute it and/or modify
Packit 8ea169
 * it under the terms of the GNU General Public License as published by
Packit 8ea169
 * the Free Software Foundation; either version 2 of the License, or
Packit 8ea169
 * (at your option) any later version.
Packit 8ea169
 *
Packit 8ea169
 * This program is distributed in the hope that it will be useful,
Packit 8ea169
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8ea169
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8ea169
 * GNU General Public License for more details.
Packit 8ea169
 */
Packit 8ea169
#include "libabrt.h"
Packit 8ea169
#include "abrt-journal.h"
Packit 8ea169
Packit 8ea169
#define ABRT_JOURNAL_WATCH_STATE_FILE VAR_STATE"/abrt-dump-journal-core.state"
Packit 8ea169
Packit 8ea169
/*
Packit 8ea169
 * A journal message is a set of key value pairs in the following format:
Packit 8ea169
 *   FIELD_NAME=${binary data}
Packit 8ea169
 *
Packit 8ea169
 * A journal message contains many fields useful in syslog but ABRT doesn't
Packit 8ea169
 * need all of them. So the following list defines mapping between journal
Packit 8ea169
 * fields and ABRT problem items.
Packit 8ea169
 *
Packit 8ea169
 * ABRT goes through the list and for each item reads journal field called
Packit 8ea169
 * 'item.name' and saves its contents in $DUMP_DIRECTORY/'item.file'.
Packit 8ea169
 */
Packit 8ea169
struct field_mapping {
Packit 8ea169
    const char *name;
Packit 8ea169
    const char *file;
Packit 8ea169
} fields [] = {
Packit 8ea169
    { .name = "COREDUMP_EXE",               .file = FILENAME_EXECUTABLE, },
Packit 8ea169
    { .name = "COREDUMP_CMDLINE",           .file = FILENAME_CMDLINE, },
Packit 8ea169
    { .name = "COREDUMP_PROC_STATUS",       .file = FILENAME_PROC_PID_STATUS, },
Packit 8ea169
    { .name = "COREDUMP_PROC_MAPS",         .file = FILENAME_MAPS, },
Packit 8ea169
    { .name = "COREDUMP_PROC_LIMITS",       .file = FILENAME_LIMITS, },
Packit 8ea169
    { .name = "COREDUMP_PROC_CGROUP",       .file = FILENAME_CGROUP, },
Packit 8ea169
    { .name = "COREDUMP_ENVIRON",           .file = FILENAME_ENVIRON, },
Packit 8ea169
    { .name = "COREDUMP_CWD",               .file = FILENAME_PWD, },
Packit 8ea169
    { .name = "COREDUMP_ROOT",              .file = FILENAME_ROOTDIR, },
Packit 8ea169
    { .name = "COREDUMP_OPEN_FDS",          .file = FILENAME_OPEN_FDS, },
Packit 8ea169
    { .name = "COREDUMP_UID",               .file = FILENAME_UID, },
Packit 8ea169
    //{ .name = "COREDUMP_GID",               .file = FILENAME_GID, },
Packit 8ea169
    { .name = "COREDUMP_PID",               .file = FILENAME_PID, },
Packit 8ea169
    { .name = "COREDUMP_PROC_MOUNTINFO",    .file = FILENAME_MOUNTINFO, },
Packit 8ea169
};
Packit 8ea169
Packit 8ea169
/*
Packit 8ea169
 * Something like 'struct problem_data' but optimized for copying data from
Packit 8ea169
 * journald to ABRT.
Packit 8ea169
 *
Packit 8ea169
 * 'struct problem_data' allocates a new memory for every single item and I
Packit 8ea169
 * found that very inefficient in this case.
Packit 8ea169
 *
Packit 8ea169
 * The following structure holds data that we already retreived from journald
Packit 8ea169
 * so we won't need to retrieve the data again.
Packit 8ea169
 *
Packit 8ea169
 * Why we retrieve data before we store them? Because we do some checking
Packit 8ea169
 * before we start saving data in ABRT. We check whether the signal is one of
Packit 8ea169
 * those we are interested in or whether the executable crashes too often to
Packit 8ea169
 * ignore the current crash ...
Packit 8ea169
 */
Packit 8ea169
struct crash_info
Packit 8ea169
{
Packit 8ea169
    abrt_journal_t *ci_journal;
Packit 8ea169
Packit 8ea169
    int ci_signal_no;
Packit 8ea169
    const char *ci_signal_name;
Packit 8ea169
    char *ci_executable_path;          ///< /full/path/to/executable
Packit 8ea169
    const char *ci_executable_name;    ///< executable
Packit 8ea169
    uid_t ci_uid;
Packit 8ea169
    pid_t ci_pid;
Packit 8ea169
Packit 8ea169
    struct field_mapping *ci_mapping;
Packit 8ea169
    size_t ci_mapping_items;
Packit 8ea169
};
Packit 8ea169
Packit 8ea169
/*
Packit 8ea169
 * ABRT watch core configuration
Packit 8ea169
 */
Packit 8ea169
typedef struct
Packit 8ea169
{
Packit 8ea169
    const char *awc_dump_location;
Packit 8ea169
    int awc_throttle;
Packit 8ea169
}
Packit 8ea169
abrt_watch_core_conf_t;
Packit 8ea169
Packit 8ea169
Packit 8ea169
/*
Packit 8ea169
 * A helper structured holding executable name and its last occurrence time.
Packit 8ea169
 *
Packit 8ea169
 * It is rather an array than a queue. Ii uses statically allocated array and
Packit 8ea169
 * the head member points the next position for creating a new entry (the next
Packit 8ea169
 * position may be already occupied and in such case the data shall be released).
Packit 8ea169
 */
Packit 8ea169
struct occurrence_queue
Packit 8ea169
{
Packit 8ea169
    int oq_head;       ///< the first empty index
Packit 8ea169
    unsigned oq_size;  ///< size of the queue
Packit 8ea169
Packit 8ea169
    struct last_occurrence
Packit 8ea169
    {
Packit 8ea169
        unsigned oqlc_stamp;
Packit 8ea169
        char *oqlc_executable;
Packit 8ea169
    } oq_occurrences[8];
Packit 8ea169
Packit 8ea169
} s_queue = {
Packit 8ea169
    .oq_head = -1,
Packit 8ea169
    .oq_size = 8,
Packit 8ea169
};
Packit 8ea169
Packit 8ea169
static unsigned
Packit 8ea169
abrt_journal_get_last_occurrence(const char *executable)
Packit 8ea169
{
Packit 8ea169
    if (s_queue.oq_head < 0)
Packit 8ea169
        return 0;
Packit 8ea169
Packit 8ea169
    unsigned index = s_queue.oq_head == 0 ? s_queue.oq_size - 1 : s_queue.oq_head - 1;
Packit 8ea169
    for (unsigned i = 0; i < s_queue.oq_size; ++i)
Packit 8ea169
    {
Packit 8ea169
        if (s_queue.oq_occurrences[index].oqlc_executable == NULL)
Packit 8ea169
            break;
Packit 8ea169
Packit 8ea169
        if (strcmp(executable, s_queue.oq_occurrences[index].oqlc_executable) == 0)
Packit 8ea169
            return s_queue.oq_occurrences[index].oqlc_stamp;
Packit 8ea169
Packit 8ea169
        if (index-- == 0)
Packit 8ea169
            index = s_queue.oq_size - 1;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return 0;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void
Packit 8ea169
abrt_journal_update_occurrence(const char *executable, unsigned ts)
Packit 8ea169
{
Packit 8ea169
    if (s_queue.oq_head < 0)
Packit 8ea169
        s_queue.oq_head = 0;
Packit 8ea169
    else
Packit 8ea169
    {
Packit 8ea169
        unsigned index = s_queue.oq_head == 0 ? s_queue.oq_size - 1 : s_queue.oq_head - 1;
Packit 8ea169
        for (unsigned i = 0; i < s_queue.oq_size; ++i)
Packit 8ea169
        {
Packit 8ea169
            if (s_queue.oq_occurrences[index].oqlc_executable == NULL)
Packit 8ea169
                break;
Packit 8ea169
Packit 8ea169
            if (strcmp(executable, s_queue.oq_occurrences[index].oqlc_executable) == 0)
Packit 8ea169
            {
Packit 8ea169
                /* Enhancement: move this entry right behind head */
Packit 8ea169
                s_queue.oq_occurrences[index].oqlc_stamp = ts;
Packit 8ea169
                return;
Packit 8ea169
            }
Packit 8ea169
Packit 8ea169
            if (index-- == 0)
Packit 8ea169
                index = s_queue.oq_size - 1;
Packit 8ea169
        }
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    s_queue.oq_occurrences[s_queue.oq_head].oqlc_stamp = ts;
Packit 8ea169
    free(s_queue.oq_occurrences[s_queue.oq_head].oqlc_executable);
Packit 8ea169
    s_queue.oq_occurrences[s_queue.oq_head].oqlc_executable = xstrdup(executable);
Packit 8ea169
Packit 8ea169
    if (++s_queue.oq_head >= s_queue.oq_size)
Packit 8ea169
        s_queue.oq_head = 0;
Packit 8ea169
Packit 8ea169
    return;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/*
Packit 8ea169
 * Converts a journal message into an intermediate ABRT problem (struct crash_info).
Packit 8ea169
 *
Packit 8ea169
 * Refuses to create the problem in the following cases:
Packit 8ea169
 * - the crashed executable has 'abrt' prefix
Packit 8ea169
 * - the signals is not fatal (see signal_is_fatal())
Packit 8ea169
 * - the journal message misses one of the following fields
Packit 8ea169
 *   - COREDUMP_SIGNAL
Packit 8ea169
 *   - COREDUMP_EXE
Packit 8ea169
 *   - COREDUMP_UID
Packit 8ea169
 *   - COREDUMP_PROC_STATUS
Packit 8ea169
 * - if any data does not have an expected format
Packit 8ea169
 */
Packit 8ea169
static int
Packit 8ea169
abrt_journal_core_retrieve_information(abrt_journal_t *journal, struct crash_info *info)
Packit 8ea169
{
Packit 8ea169
    if (abrt_journal_get_int_field(journal, "COREDUMP_SIGNAL", &(info->ci_signal_no)) != 0)
Packit 8ea169
    {
Packit 8ea169
        log_info("Failed to get signal number from journal message");
Packit 8ea169
        return -EINVAL;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (!signal_is_fatal(info->ci_signal_no, &(info->ci_signal_name)))
Packit 8ea169
    {
Packit 8ea169
        log_info("Signal '%d' is not fatal: ignoring crash", info->ci_signal_no);
Packit 8ea169
        return 1;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    info->ci_executable_path = abrt_journal_get_string_field(journal, "COREDUMP_EXE", NULL);
Packit 8ea169
    if (info->ci_executable_path == NULL)
Packit 8ea169
    {
Packit 8ea169
        log_notice("Could not get crashed 'executable'.");
Packit 8ea169
        return -ENOENT;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    info->ci_executable_name = strrchr(info->ci_executable_path, '/');
Packit 8ea169
    if (info->ci_executable_name == NULL)
Packit 8ea169
    {
Packit 8ea169
        info->ci_executable_name = info->ci_executable_path;
Packit 8ea169
    }
Packit 8ea169
    else if(strncmp(++(info->ci_executable_name), "abrt", 4) == 0)
Packit 8ea169
    {
Packit 8ea169
        error_msg("Ignoring crash of ABRT executable '%s'", info->ci_executable_path);
Packit 8ea169
        return 1;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (abrt_journal_get_unsigned_field(journal, "COREDUMP_UID", &(info->ci_uid)))
Packit 8ea169
    {
Packit 8ea169
        log_info("Failed to get UID from journal message");
Packit 8ea169
        return -EINVAL;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    /* This is not fatal, the pid is used only in dumpdir name */
Packit 8ea169
    if (abrt_journal_get_int_field(journal, "COREDUMP_PID", &(info->ci_pid)))
Packit 8ea169
    {
Packit 8ea169
        log_notice("Failed to get PID from journal message.");
Packit 8ea169
        info->ci_pid = getpid();
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    char *proc_status = abrt_journal_get_string_field(journal, "COREDUMP_PROC_STATUS", NULL);
Packit 8ea169
    if (proc_status == NULL)
Packit 8ea169
    {
Packit 8ea169
        log_info("Failed to get /proc/[pid]/status from journal message");
Packit 8ea169
        return -ENOENT;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    uid_t tmp_fsuid = get_fsuid(proc_status);
Packit 8ea169
    if (tmp_fsuid < 0)
Packit 8ea169
        return -EINVAL;
Packit 8ea169
Packit 8ea169
    if (tmp_fsuid != info->ci_uid)
Packit 8ea169
    {
Packit 8ea169
        /* use root for suided apps unless it's explicitly set to UNSAFE */
Packit 8ea169
        info->ci_uid = (dump_suid_policy() != DUMP_SUID_UNSAFE) ? 0 : tmp_fsuid;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return 0;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/*
Packit 8ea169
 * Initializes ABRT problem directory and save the relevant journal message
Packit 8ea169
 * fileds in that directory.
Packit 8ea169
 */
Packit 8ea169
static int
Packit 8ea169
save_systemd_coredump_in_dump_directory(struct dump_dir *dd, struct crash_info *info)
Packit 8ea169
{
Packit 8ea169
    char coredump_path[PATH_MAX + 1] = { '\0' };
Packit 8ea169
    if (coredump_path != abrt_journal_get_string_field(info->ci_journal, "COREDUMP_FILENAME", coredump_path))
Packit 8ea169
        log_debug("Processing coredumpctl entry without a real file");
Packit 8ea169
Packit 8ea169
    const size_t len = strlen(coredump_path);
Packit 8ea169
    if (   (len >= 3
Packit 8ea169
            && coredump_path[len - 3] == '.'
Packit 8ea169
            && coredump_path[len - 2] == 'x'
Packit 8ea169
            && coredump_path[len - 1] == 'z')
Packit 8ea169
        || (len >= 4
Packit 8ea169
            && coredump_path[len - 4] == '.'
Packit 8ea169
            && coredump_path[len - 3] == 'l'
Packit 8ea169
            && coredump_path[len - 2] == 'z'
Packit 8ea169
            && coredump_path[len - 1] == '4'))
Packit 8ea169
    {
Packit 8ea169
        if (dd_copy_file_unpack(dd, FILENAME_COREDUMP, coredump_path))
Packit 8ea169
            return -1;
Packit 8ea169
    }
Packit 8ea169
    else if (len > 0)
Packit 8ea169
    {
Packit 8ea169
        if (dd_copy_file(dd, FILENAME_COREDUMP, coredump_path))
Packit 8ea169
            return -1;
Packit 8ea169
    }
Packit 8ea169
    else
Packit 8ea169
    {
Packit 8ea169
        const char *data = NULL;
Packit 8ea169
        size_t data_len = 0;
Packit 8ea169
        int r = abrt_journal_get_field(info->ci_journal, "COREDUMP", (const void **)&data, &data_len);
Packit 8ea169
        if (r < 0)
Packit 8ea169
        {
Packit 8ea169
            log_info("Ignoring coredumpctl entry without core dump file.");
Packit 8ea169
            return -1;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        dd_save_binary(dd, FILENAME_COREDUMP, data, data_len);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    dd_save_text(dd, FILENAME_ABRT_VERSION, VERSION);
Packit 8ea169
    dd_save_text(dd, FILENAME_TYPE, "CCpp");
Packit 8ea169
    dd_save_text(dd, FILENAME_ANALYZER, "abrt-journal-core");
Packit 8ea169
Packit 8ea169
    char *reason;
Packit 8ea169
    if (info->ci_signal_name == NULL)
Packit 8ea169
        reason = xasprintf("%s killed by signal %d", info->ci_executable_name, info->ci_signal_no);
Packit 8ea169
    else
Packit 8ea169
        reason = xasprintf("%s killed by SIG%s", info->ci_executable_name, info->ci_signal_name);
Packit 8ea169
Packit 8ea169
    dd_save_text(dd, FILENAME_REASON, reason);
Packit 8ea169
    free(reason);
Packit 8ea169
Packit 8ea169
    char *cursor = NULL;
Packit 8ea169
    if (abrt_journal_get_cursor(info->ci_journal, &cursor) == 0)
Packit 8ea169
        dd_save_text(dd, "journald_cursor", cursor);
Packit 8ea169
    free(cursor);
Packit 8ea169
Packit 8ea169
    const char *data = NULL;
Packit 8ea169
    size_t data_len = 0;
Packit 8ea169
Packit 8ea169
    if (!abrt_journal_get_field(info->ci_journal, "COREDUMP_CONTAINER_CMDLINE", (const void **)&data, &data_len))
Packit 8ea169
    {
Packit 8ea169
        dd_save_binary(dd, FILENAME_CONTAINER_CMDLINE, data, data_len);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    for (size_t i = 0; i < info->ci_mapping_items; ++i)
Packit 8ea169
    {
Packit 8ea169
        const char *data;
Packit 8ea169
        size_t data_len;
Packit 8ea169
        struct field_mapping *f = info->ci_mapping + i;
Packit 8ea169
Packit 8ea169
        if (abrt_journal_get_field(info->ci_journal, f->name, (const void **)&data, &data_len))
Packit 8ea169
        {
Packit 8ea169
            log_info("systemd-coredump journald message misses field: '%s'", f->name);
Packit 8ea169
            continue;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        dd_save_binary(dd, f->file, data, data_len);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return 0;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static int
Packit 8ea169
abrt_journal_core_to_abrt_problem(struct crash_info *info, const char *dump_location)
Packit 8ea169
{
Packit 8ea169
    struct dump_dir *dd = create_dump_dir_ext(dump_location, "ccpp", info->ci_pid, /*fs owner*/0,
Packit 8ea169
            (save_data_call_back)save_systemd_coredump_in_dump_directory, info);
Packit 8ea169
Packit 8ea169
    if (dd != NULL)
Packit 8ea169
    {
Packit 8ea169
        char *path = xstrdup(dd->dd_dirname);
Packit 8ea169
        dd_close(dd);
Packit 8ea169
        notify_new_path(path);
Packit 8ea169
        log_debug("ABRT daemon has been notified about directory: '%s'", path);
Packit 8ea169
        free(path);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return dd == NULL;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/*
Packit 8ea169
 * Creates an abrt problem from a journal message
Packit 8ea169
 */
Packit 8ea169
static int
Packit 8ea169
abrt_journal_dump_core(abrt_journal_t *journal, const char *dump_location)
Packit 8ea169
{
Packit 8ea169
    struct crash_info info = { 0 };
Packit 8ea169
    info.ci_journal = journal;
Packit 8ea169
    info.ci_mapping = fields;
Packit 8ea169
    info.ci_mapping_items = sizeof(fields)/sizeof(*fields);
Packit 8ea169
Packit 8ea169
    /* Compatibility hack, a watch's callback gets the journal already moved
Packit 8ea169
     * to a next message. */
Packit 8ea169
    abrt_journal_next(journal);
Packit 8ea169
Packit 8ea169
    /* This the watch call back mentioned in the comment above. We use the
Packit 8ea169
     * following function also in abrt_journal_watch_cores(). */
Packit 8ea169
    int r = abrt_journal_core_retrieve_information(journal, &info;;
Packit 8ea169
    if (r != 0)
Packit 8ea169
    {
Packit 8ea169
        if (r < 0)
Packit 8ea169
            error_msg(_("Failed to obtain all required information from journald"));
Packit 8ea169
Packit 8ea169
        goto dump_cleanup;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    r = abrt_journal_core_to_abrt_problem(&info, dump_location);
Packit 8ea169
Packit 8ea169
dump_cleanup:
Packit 8ea169
    if (info.ci_executable_path != NULL)
Packit 8ea169
        free(info.ci_executable_path);
Packit 8ea169
Packit 8ea169
    return r;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/*
Packit 8ea169
 * A function called when a new journal core is detected.
Packit 8ea169
 *
Packit 8ea169
 * The function retrieves information from journal, checks the last occurrence
Packit 8ea169
 * time of the crashed executable and if there was no recent occurrence creates
Packit 8ea169
 * an ABRT problem from the journal message. Finally updates the last occurrence
Packit 8ea169
 * time.
Packit 8ea169
 */
Packit 8ea169
static void
Packit 8ea169
abrt_journal_watch_cores(abrt_journal_watch_t *watch, void *user_data)
Packit 8ea169
{
Packit 8ea169
    const abrt_watch_core_conf_t *conf = (const abrt_watch_core_conf_t *)user_data;
Packit 8ea169
Packit 8ea169
    struct crash_info info = { 0 };
Packit 8ea169
    info.ci_journal = abrt_journal_watch_get_journal(watch);
Packit 8ea169
    info.ci_mapping = fields;
Packit 8ea169
    info.ci_mapping_items = sizeof(fields)/sizeof(*fields);
Packit 8ea169
Packit 8ea169
    int r = abrt_journal_core_retrieve_information(abrt_journal_watch_get_journal(watch), &info;;
Packit 8ea169
    if (r)
Packit 8ea169
    {
Packit 8ea169
        if (r < 0)
Packit 8ea169
            error_msg(_("Failed to obtain all required information from journald"));
Packit 8ea169
Packit 8ea169
        goto watch_cleanup;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    // do not dump too often
Packit 8ea169
    //   ignore crashes of a single executable appearing in THROTTLE s (keep last 10 executable)
Packit 8ea169
    const unsigned current = time(NULL);
Packit 8ea169
    const unsigned last = abrt_journal_get_last_occurrence(info.ci_executable_path);
Packit 8ea169
Packit 8ea169
    if (current < last)
Packit 8ea169
    {
Packit 8ea169
        error_msg("BUG: current time stamp lower than an old one");
Packit 8ea169
Packit 8ea169
        if (g_verbose > 2)
Packit 8ea169
            abort();
Packit 8ea169
Packit 8ea169
        goto watch_cleanup;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    const unsigned sub = current - last;
Packit 8ea169
    if (sub < conf->awc_throttle)
Packit 8ea169
    {
Packit 8ea169
        /* We don't want to update the counter here. */
Packit 8ea169
        error_msg(_("Not saving repeating crash after %ds (limit is %ds)"), sub, conf->awc_throttle);
Packit 8ea169
        goto watch_cleanup;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (abrt_journal_core_to_abrt_problem(&info, conf->awc_dump_location))
Packit 8ea169
    {
Packit 8ea169
        error_msg(_("Failed to save detect problem data in abrt database"));
Packit 8ea169
        goto watch_cleanup;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    abrt_journal_update_occurrence(info.ci_executable_path, current);
Packit 8ea169
Packit 8ea169
watch_cleanup:
Packit 8ea169
    abrt_journal_save_current_position(info.ci_journal, ABRT_JOURNAL_WATCH_STATE_FILE);
Packit 8ea169
Packit 8ea169
    if (info.ci_executable_path != NULL)
Packit 8ea169
        free(info.ci_executable_path);
Packit 8ea169
Packit 8ea169
    return;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void
Packit 8ea169
watch_journald(abrt_journal_t *journal, abrt_watch_core_conf_t *conf)
Packit 8ea169
{
Packit 8ea169
    abrt_journal_watch_t *watch = NULL;
Packit 8ea169
    if (abrt_journal_watch_new(&watch, journal, abrt_journal_watch_cores, (void *)conf) < 0)
Packit 8ea169
        error_msg_and_die(_("Failed to initialize systemd-journal watch"));
Packit 8ea169
Packit 8ea169
    abrt_journal_watch_run_sync(watch);
Packit 8ea169
    abrt_journal_watch_free(watch);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
int
Packit 8ea169
main(int argc, char *argv[])
Packit 8ea169
{
Packit 8ea169
    /* I18n */
Packit 8ea169
    setlocale(LC_ALL, "");
Packit 8ea169
#if ENABLE_NLS
Packit 8ea169
    bindtextdomain(PACKAGE, LOCALEDIR);
Packit 8ea169
    textdomain(PACKAGE);
Packit 8ea169
#endif
Packit 8ea169
Packit 8ea169
    abrt_init(argv);
Packit 8ea169
Packit 8ea169
    /* Can't keep these strings/structs static: _() doesn't support that */
Packit 8ea169
    const char *program_usage_string = _(
Packit 8ea169
        "& [-vsf] [-e]/[-c CURSOR] [-t INT]/[-T] [-d DIR]/[-D]\n"
Packit 8ea169
        "\n"
Packit 8ea169
        "Extract coredumps from systemd-journal\n"
Packit 8ea169
        "\n"
Packit 8ea169
        "-c and -e options conflicts because both specifies the first read message.\n"
Packit 8ea169
        "\n"
Packit 8ea169
        "-e is useful only for -f because the following of journal starts by reading \n"
Packit 8ea169
        "the entire journal if the last seen possition is not available.\n"
Packit 8ea169
        "\n"
Packit 8ea169
        "The last seen position is saved in "ABRT_JOURNAL_WATCH_STATE_FILE"\n"
Packit 8ea169
    );
Packit 8ea169
    enum {
Packit 8ea169
        OPT_v = 1 << 0,
Packit 8ea169
        OPT_s = 1 << 1,
Packit 8ea169
        OPT_d = 1 << 2,
Packit 8ea169
        OPT_D = 1 << 3,
Packit 8ea169
        OPT_c = 1 << 4,
Packit 8ea169
        OPT_e = 1 << 5,
Packit 8ea169
        OPT_t = 1 << 6,
Packit 8ea169
        OPT_T = 1 << 7,
Packit 8ea169
        OPT_f = 1 << 8,
Packit 8ea169
    };
Packit 8ea169
Packit 8ea169
    char *cursor = NULL;
Packit 8ea169
    char *dump_location = NULL;
Packit 8ea169
    int throttle = 0;
Packit 8ea169
Packit 8ea169
    /* Keep enum above and order of options below in sync! */
Packit 8ea169
    struct options program_options[] = {
Packit 8ea169
        OPT__VERBOSE(&g_verbose),
Packit 8ea169
        OPT_BOOL(  's', NULL, NULL, _("Log to syslog")),
Packit 8ea169
        OPT_STRING('d', NULL, &dump_location, "DIR", _("Create new problem directory in DIR for every coredump")),
Packit 8ea169
        OPT_BOOL(  'D', NULL, NULL, _("Same as -d DumpLocation, DumpLocation is specified in abrt.conf")),
Packit 8ea169
        OPT_STRING('c', NULL, &cursor, "CURSOR", _("Start reading systemd-journal from the CURSOR position")),
Packit 8ea169
        OPT_BOOL(  'e', NULL, NULL, _("Start reading systemd-journal from the end")),
Packit 8ea169
        OPT_INTEGER('t', NULL, &throttle, _("Throttle problem directory creation to 1 per INT second")),
Packit 8ea169
        OPT_BOOL(  'T', NULL, NULL, _("Same as -t INT, INT is specified in plugins/CCpp.conf")),
Packit 8ea169
        OPT_BOOL(  'f', NULL, NULL, _("Follow systemd-journal from the last seen position (if available)")),
Packit 8ea169
        OPT_END()
Packit 8ea169
    };
Packit 8ea169
    unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
Packit 8ea169
Packit 8ea169
    export_abrt_envvars(0);
Packit 8ea169
Packit 8ea169
    if ((opts & OPT_s) || getenv("ABRT_SYSLOG"))
Packit 8ea169
        logmode = LOGMODE_JOURNAL;
Packit 8ea169
Packit 8ea169
    if ((opts & OPT_c) && (opts & OPT_e))
Packit 8ea169
        error_msg_and_die(_("You need to specify either -c CURSOR or -e"));
Packit 8ea169
Packit 8ea169
    /* Initialize ABRT configuration */
Packit 8ea169
    load_abrt_conf();
Packit 8ea169
Packit 8ea169
    if (opts & OPT_D)
Packit 8ea169
    {
Packit 8ea169
        if (opts & OPT_d)
Packit 8ea169
            show_usage_and_die(program_usage_string, program_options);
Packit 8ea169
        dump_location = g_settings_dump_location;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    {   /* Load CCpp.conf */
Packit 8ea169
        map_string_t *settings = new_map_string();
Packit 8ea169
        load_abrt_plugin_conf_file("CCpp.conf", settings);
Packit 8ea169
        const char *value;
Packit 8ea169
Packit 8ea169
        /* The following commented out lines are not supported by
Packit 8ea169
         * abrt-dump-journal-core but are supported by abrt-hook-ccpp */
Packit 8ea169
        //value = get_map_string_item_or_NULL(settings, "MakeCompatCore");
Packit 8ea169
        //setting_MakeCompatCore = value && string_to_bool(value);
Packit 8ea169
        //value = get_map_string_item_or_NULL(settings, "SaveBinaryImage");
Packit 8ea169
        //setting_SaveBinaryImage = value && string_to_bool(value);
Packit 8ea169
        //value = get_map_string_item_or_NULL(settings, "SaveFullCore");
Packit 8ea169
        //setting_SaveFullCore = value ? string_to_bool(value) : true;
Packit 8ea169
Packit 8ea169
        value = get_map_string_item_or_NULL(settings, "VerboseLog");
Packit 8ea169
        if (value)
Packit 8ea169
            g_verbose = xatoi_positive(value);
Packit 8ea169
Packit 8ea169
        free_map_string(settings);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    /* systemd-coredump creates journal messages with SYSLOG_IDENTIFIER equals
Packit 8ea169
     * 'systemd-coredump' and we are interested only in the systemd-coredump
Packit 8ea169
     * messages.
Packit 8ea169
     *
Packit 8ea169
     * Of cores, it is possible to override this when need while debugging.
Packit 8ea169
     */
Packit 8ea169
    const char *const env_journal_filter = getenv("ABRT_DUMP_JOURNAL_CORE_DEBUG_FILTER");
Packit 8ea169
    GList *coredump_journal_filter = NULL;
Packit 8ea169
    coredump_journal_filter = g_list_append(coredump_journal_filter,
Packit 8ea169
           (env_journal_filter ? (gpointer)env_journal_filter : (gpointer)"SYSLOG_IDENTIFIER=systemd-coredump"));
Packit 8ea169
Packit 8ea169
    abrt_journal_t *journal = NULL;
Packit 8ea169
    if (abrt_journal_new(&journal))
Packit 8ea169
        error_msg_and_die(_("Cannot open systemd-journal"));
Packit 8ea169
Packit 8ea169
    if (abrt_journal_set_journal_filter(journal, coredump_journal_filter) < 0)
Packit 8ea169
        error_msg_and_die(_("Cannot filter systemd-journal to systemd-coredump data only"));
Packit 8ea169
Packit 8ea169
    g_list_free(coredump_journal_filter);
Packit 8ea169
Packit 8ea169
    if ((opts & OPT_e) && abrt_journal_seek_tail(journal) < 0)
Packit 8ea169
        error_msg_and_die(_("Cannot seek to the end of journal"));
Packit 8ea169
Packit 8ea169
    if (cursor && abrt_journal_set_cursor(journal, cursor))
Packit 8ea169
        error_msg_and_die(_("Failed to set systemd-journal cursor '%s'"), cursor);
Packit 8ea169
Packit 8ea169
    if ((opts & OPT_f))
Packit 8ea169
    {
Packit 8ea169
        if (!cursor && !(opts & OPT_e))
Packit 8ea169
        {
Packit 8ea169
            abrt_journal_restore_position(journal, ABRT_JOURNAL_WATCH_STATE_FILE);
Packit 8ea169
Packit 8ea169
            /* The stored position has already been seen, so move to the next one. */
Packit 8ea169
            abrt_journal_next(journal);
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        abrt_watch_core_conf_t conf = {
Packit 8ea169
            .awc_dump_location = dump_location,
Packit 8ea169
            .awc_throttle = throttle,
Packit 8ea169
        };
Packit 8ea169
Packit 8ea169
        watch_journald(journal, &conf;;
Packit 8ea169
Packit 8ea169
        abrt_journal_save_current_position(journal, ABRT_JOURNAL_WATCH_STATE_FILE);
Packit 8ea169
    }
Packit 8ea169
    else
Packit 8ea169
        abrt_journal_dump_core(journal, dump_location);
Packit 8ea169
Packit 8ea169
    abrt_journal_free(journal);
Packit 8ea169
    free_abrt_conf_data();
Packit 8ea169
Packit 8ea169
    return EXIT_SUCCESS;
Packit 8ea169
}