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

Packit 8ea169
/*
Packit 8ea169
 * Copyright (C) 2015  ABRT team
Packit 8ea169
 * Copyright (C) 2015  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
#include "xorg-utils.h"
Packit 8ea169
#define ABRT_JOURNAL_XORG_WATCH_STATE_FILE VAR_STATE"/abrt-dump-journal-xorg.state"
Packit 8ea169
#define XORG_CONF "xorg.conf"
Packit 8ea169
#define XORG_CONF_PATH "/etc/abrt/plugins/"XORG_CONF
Packit 8ea169
Packit 8ea169
static void
Packit 8ea169
abrt_xorg_process_list_of_crashes(GList *crashes, const char *dump_location, int flags)
Packit 8ea169
{
Packit 8ea169
    if (crashes == NULL)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    GList *list;
Packit 8ea169
    for (list = crashes; list != NULL; list = list->next)
Packit 8ea169
    {
Packit 8ea169
        xorg_crash_info_create_dump_dir(list->data, dump_location, (flags & ABRT_XORG_WORLD_READABLE));
Packit 8ea169
Packit 8ea169
        if (flags & ABRT_XORG_PRINT_STDOUT)
Packit 8ea169
            xorg_crash_info_print_crash(list->data);
Packit 8ea169
Packit 8ea169
        if (flags & ABRT_XORG_THROTTLE_CREATION)
Packit 8ea169
            if (abrt_xorg_signaled_sleep(1) > 0)
Packit 8ea169
                break;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static GList *abrt_journal_extract_xorg_crashes(abrt_journal_t *journal)
Packit 8ea169
{
Packit 8ea169
    GList *crash_info_list = NULL;
Packit 8ea169
Packit 8ea169
    do
Packit 8ea169
    {
Packit 8ea169
        char *line = abrt_journal_get_log_line(journal);
Packit 8ea169
        if (line == NULL)
Packit 8ea169
            error_msg_and_die(_("Cannot read journal data."));
Packit 8ea169
Packit 8ea169
        char *p = skip_pfx(line);
Packit 8ea169
        if (strcmp(p, XORG_SEARCH_STRING) == 0)
Packit 8ea169
        {
Packit 8ea169
            struct xorg_crash_info *crash_info = process_xorg_bt(&abrt_journal_get_next_log_line, journal);
Packit 8ea169
            if (crash_info)
Packit 8ea169
                crash_info_list = g_list_append(crash_info_list, crash_info);
Packit 8ea169
            else
Packit 8ea169
                log_warning(_("Failed to parse Backtrace from journal"));
Packit 8ea169
        }
Packit 8ea169
        free(line);
Packit 8ea169
    }
Packit 8ea169
    while (abrt_journal_next(journal) > 0);
Packit 8ea169
Packit 8ea169
    log_warning("Found crashes: %d", g_list_length(crash_info_list));
Packit 8ea169
Packit 8ea169
    return crash_info_list;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
struct watch_journald_xorg_settings
Packit 8ea169
{
Packit 8ea169
    const char *dump_location;
Packit 8ea169
    int xorg_utils_flags;
Packit 8ea169
};
Packit 8ea169
Packit 8ea169
static void abrt_journal_watch_extract_xorg_crashes(abrt_journal_watch_t *watch, void *data)
Packit 8ea169
{
Packit 8ea169
    const struct watch_journald_xorg_settings *conf = (const struct watch_journald_xorg_settings *)data;
Packit 8ea169
Packit 8ea169
    abrt_journal_t *journal = abrt_journal_watch_get_journal(watch);
Packit 8ea169
Packit 8ea169
    /* Give systemd-journal one second to suck in all crash strings */
Packit 8ea169
    if (abrt_xorg_signaled_sleep(1) > 0)
Packit 8ea169
    {
Packit 8ea169
        abrt_journal_watch_stop(watch);
Packit 8ea169
        return;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    GList *crashes = abrt_journal_extract_xorg_crashes(journal);
Packit 8ea169
    abrt_xorg_process_list_of_crashes(crashes, conf->dump_location, conf->xorg_utils_flags);
Packit 8ea169
    g_list_free_full(crashes, (GDestroyNotify)xorg_crash_info_free);
Packit 8ea169
Packit 8ea169
    /* In case of disaster, lets make sure we won't read the journal messages */
Packit 8ea169
    /* again. */
Packit 8ea169
    abrt_journal_save_current_position(journal, ABRT_JOURNAL_XORG_WATCH_STATE_FILE);
Packit 8ea169
Packit 8ea169
    if (g_abrt_xorg_sleep_woke_up_on_signal > 0)
Packit 8ea169
        abrt_journal_watch_stop(watch);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void watch_journald(abrt_journal_t *journal, const char *dump_location, int flags)
Packit 8ea169
{
Packit 8ea169
    GList *xorg_strings = NULL;
Packit 8ea169
    xorg_strings = g_list_prepend(xorg_strings, (gpointer)XORG_SEARCH_STRING);
Packit 8ea169
Packit 8ea169
    struct watch_journald_xorg_settings watch_conf = {
Packit 8ea169
        .dump_location = dump_location,
Packit 8ea169
        .xorg_utils_flags = flags,
Packit 8ea169
    };
Packit 8ea169
Packit 8ea169
    struct abrt_journal_watch_notify_strings notify_strings_conf = {
Packit 8ea169
        .decorated_cb = abrt_journal_watch_extract_xorg_crashes,
Packit 8ea169
        .decorated_cb_data = &watch_conf,
Packit 8ea169
        .strings = xorg_strings,
Packit 8ea169
        .blacklisted_strings = NULL,
Packit 8ea169
    };
Packit 8ea169
Packit 8ea169
    abrt_journal_watch_t *watch = NULL;
Packit 8ea169
    if (abrt_journal_watch_new(&watch, journal, abrt_journal_watch_notify_strings, &notify_strings_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
    g_list_free(xorg_strings);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
int 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
Packit 8ea169
    const char *program_usage_string_template = _(
Packit 8ea169
        "& [-vsoxtf] [-e]/[-c CURSOR] [-d DIR]/[-D]\n"
Packit 8ea169
        "\n"
Packit 8ea169
        "Extract Xorg crash 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 position is not available.\n"
Packit 8ea169
        "\n"
Packit 8ea169
        "The last seen position is saved in %s\n"
Packit 8ea169
        "\n"
Packit 8ea169
        "Journal filter is required parameter and must be specified either by parameter\n"
Packit 8ea169
        "-j or in %s conf file.\n"
Packit 8ea169
    );
Packit 8ea169
Packit 8ea169
    char program_usage_string[strlen(program_usage_string_template)
Packit 8ea169
                            + strlen(ABRT_JOURNAL_XORG_WATCH_STATE_FILE)
Packit 8ea169
                            + strlen(XORG_CONF_PATH)];
Packit 8ea169
    sprintf(program_usage_string, program_usage_string_template,
Packit 8ea169
            ABRT_JOURNAL_XORG_WATCH_STATE_FILE, XORG_CONF_PATH);
Packit 8ea169
Packit 8ea169
    enum {
Packit 8ea169
        OPT_v = 1 << 0,
Packit 8ea169
        OPT_s = 1 << 1,
Packit 8ea169
        OPT_o = 1 << 2,
Packit 8ea169
        OPT_d = 1 << 3,
Packit 8ea169
        OPT_D = 1 << 4,
Packit 8ea169
        OPT_x = 1 << 5,
Packit 8ea169
        OPT_t = 1 << 6,
Packit 8ea169
        OPT_c = 1 << 7,
Packit 8ea169
        OPT_e = 1 << 8,
Packit 8ea169
        OPT_f = 1 << 9,
Packit 8ea169
        OPT_a = 1 << 10,
Packit 8ea169
        OPT_J = 1 << 11,
Packit 8ea169
        OPT_j = 1 << 12,
Packit 8ea169
    };
Packit 8ea169
Packit 8ea169
    char *cursor = NULL;
Packit 8ea169
    char *dump_location = NULL;
Packit 8ea169
    char *journal_dir = NULL;
Packit 8ea169
    GList *journal_filters = NULL;
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_BOOL(  'o', NULL, NULL, _("Print found crashes on standard output")),
Packit 8ea169
        OPT_STRING('d', NULL, &dump_location, "DIR", _("Create new problem directory in DIR for every crash found")),
Packit 8ea169
        OPT_BOOL(  'D', NULL, NULL, _("Same as -d DumpLocation, DumpLocation is specified in abrt.conf")),
Packit 8ea169
        OPT_BOOL(  'x', NULL, NULL, _("Make the problem directory world readable")),
Packit 8ea169
        OPT_BOOL(  't', NULL, NULL, _("Throttle problem directory creation to 1 per second")),
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_BOOL(  'f', NULL, NULL, _("Follow systemd-journal from the last seen position (if available)")),
Packit 8ea169
        OPT_BOOL(  'a', NULL, NULL, _("Read journal files from all machines")),
Packit 8ea169
        OPT_STRING('J', NULL, &journal_dir,  "PATH", _("Read all journal files from directory at PATH")),
Packit 8ea169
        OPT_LIST(  'j', NULL, &journal_filters,  "FILTER", _("Journal filter e.g. '_COMM=gdm-x-session' (may be given many times)")),
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
    msg_prefix = g_progname;
Packit 8ea169
    if ((opts & OPT_s) || getenv("ABRT_SYSLOG"))
Packit 8ea169
    {
Packit 8ea169
        logmode = LOGMODE_JOURNAL;
Packit 8ea169
    }
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
    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
        load_abrt_conf();
Packit 8ea169
        dump_location = g_settings_dump_location;
Packit 8ea169
        g_settings_dump_location = NULL;
Packit 8ea169
        free_abrt_conf_data();
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    int xorg_utils_flags = 0;
Packit 8ea169
    if ((opts & OPT_x))
Packit 8ea169
        xorg_utils_flags |= ABRT_XORG_WORLD_READABLE;
Packit 8ea169
Packit 8ea169
    if ((opts & OPT_t))
Packit 8ea169
        xorg_utils_flags |= ABRT_XORG_THROTTLE_CREATION;
Packit 8ea169
Packit 8ea169
    if ((opts & OPT_o))
Packit 8ea169
        xorg_utils_flags |= ABRT_XORG_PRINT_STDOUT;
Packit 8ea169
Packit 8ea169
    /* get journal filters */
Packit 8ea169
    const char *const env_journal_filter = getenv("ABRT_DUMP_JOURNAL_XORG_DEBUG_FILTER");
Packit 8ea169
    bool free_filter_list_data = false;
Packit 8ea169
    GList *xorg_journal_filter = NULL;
Packit 8ea169
    if (env_journal_filter != NULL)
Packit 8ea169
    {
Packit 8ea169
        xorg_journal_filter = g_list_append(xorg_journal_filter, (gpointer)env_journal_filter);
Packit 8ea169
        log_debug("Using journal filter from environment variable");
Packit 8ea169
    }
Packit 8ea169
    else if (journal_filters != NULL)
Packit 8ea169
    {
Packit 8ea169
        xorg_journal_filter = journal_filters;
Packit 8ea169
        log_debug("Using journal filter passed by parameter -j");
Packit 8ea169
    }
Packit 8ea169
    else
Packit 8ea169
    {
Packit 8ea169
        map_string_t *settings = new_map_string();
Packit 8ea169
        log_notice("Loading settings from '%s'", XORG_CONF);
Packit 8ea169
        load_abrt_plugin_conf_file(XORG_CONF, settings);
Packit 8ea169
        log_debug("Loaded '%s'", XORG_CONF);
Packit 8ea169
        const char *conf_journal_filters = get_map_string_item_or_NULL(settings, "JournalFilters");
Packit 8ea169
        xorg_journal_filter = parse_list(conf_journal_filters);
Packit 8ea169
        /* list data will be free by g_list_free_full */
Packit 8ea169
        free_filter_list_data = true;
Packit 8ea169
        free_map_string(settings);
Packit 8ea169
        if (xorg_journal_filter)
Packit 8ea169
            log_debug("Using journal filter from conf file %s", XORG_CONF);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (xorg_journal_filter == NULL)
Packit 8ea169
        error_msg_and_die(_("Journal filter must be specified either by parameter -j or stored in /etc/abrt/plugins/xorg.conf file"));
Packit 8ea169
Packit 8ea169
    abrt_journal_t *journal = NULL;
Packit 8ea169
    if ((opts & OPT_J))
Packit 8ea169
    {
Packit 8ea169
        log_debug("Using journal files from directory '%s'", journal_dir);
Packit 8ea169
Packit 8ea169
        if (abrt_journal_open_directory(&journal, journal_dir))
Packit 8ea169
            error_msg_and_die(_("Cannot initialize systemd-journal in directory '%s'"), journal_dir);
Packit 8ea169
    }
Packit 8ea169
    else
Packit 8ea169
    {
Packit 8ea169
        if (((opts & OPT_a) ? abrt_journal_new_merged : abrt_journal_new)(&journal))
Packit 8ea169
            error_msg_and_die(_("Cannot open systemd-journal"));
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (abrt_journal_set_journal_filter(journal, xorg_journal_filter) < 0)
Packit 8ea169
        error_msg_and_die(_("Cannot filter systemd-journal to Xorg data only"));
Packit 8ea169
Packit 8ea169
    /* free filter list */
Packit 8ea169
    if (free_filter_list_data)
Packit 8ea169
        g_list_free_full(xorg_journal_filter, free);
Packit 8ea169
    else
Packit 8ea169
        g_list_free(xorg_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 ((opts & OPT_f))
Packit 8ea169
    {
Packit 8ea169
        if (!cursor)
Packit 8ea169
            abrt_journal_restore_position(journal, ABRT_JOURNAL_XORG_WATCH_STATE_FILE);
Packit 8ea169
        else if(abrt_journal_set_cursor(journal, cursor))
Packit 8ea169
            error_msg_and_die(_("Failed to start watch from cursor '%s'"), cursor);
Packit 8ea169
Packit 8ea169
        watch_journald(journal, dump_location, xorg_utils_flags);
Packit 8ea169
Packit 8ea169
        abrt_journal_save_current_position(journal, ABRT_JOURNAL_XORG_WATCH_STATE_FILE);
Packit 8ea169
    }
Packit 8ea169
    else
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
        /* 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
        GList *crashes = abrt_journal_extract_xorg_crashes(journal);
Packit 8ea169
        abrt_xorg_process_list_of_crashes(crashes, dump_location, xorg_utils_flags);
Packit 8ea169
        g_list_free_full(crashes, (GDestroyNotify)xorg_crash_info_free);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    abrt_journal_free(journal);
Packit 8ea169
Packit 8ea169
    return EXIT_SUCCESS;
Packit 8ea169
}