Blame src/applet/applet.c

Packit 8ea169
/*
Packit 8ea169
    Copyright (C) 2009  Jiri Moskovcak (jmoskovc@redhat.com)
Packit 8ea169
    Copyright (C) 2009  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
    You should have received a copy of the GNU General Public License along
Packit 8ea169
    with this program; if not, write to the Free Software Foundation, Inc.,
Packit 8ea169
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 8ea169
*/
Packit 8ea169
#if HAVE_LOCALE_H
Packit 8ea169
# include <locale.h>
Packit 8ea169
#endif
Packit 8ea169
Packit 8ea169
#ifdef HAVE_CONFIG_H
Packit 8ea169
#include <config.h>
Packit 8ea169
#endif
Packit 8ea169
Packit 8ea169
#include <gio/gdesktopappinfo.h>
Packit 8ea169
#define GDK_DISABLE_DEPRECATION_WARNINGS
Packit 8ea169
/* https://bugzilla.gnome.org/show_bug.cgi?id=734826 */
Packit 8ea169
#include <gtk/gtk.h>
Packit 8ea169
Packit 8ea169
#ifdef HAVE_POLKIT
Packit 8ea169
#include <polkit/polkit.h>
Packit 8ea169
#endif
Packit 8ea169
Packit 8ea169
#include <libnotify/notify.h>
Packit 8ea169
#include <glib.h>
Packit 8ea169
Packit 8ea169
#include <libreport/internal_abrt_dbus.h>
Packit 8ea169
#include <libreport/event_config.h>
Packit 8ea169
#include <libreport/internal_libreport_gtk.h>
Packit 8ea169
#include <libreport/problem_utils.h>
Packit 8ea169
#include "libabrt.h"
Packit 8ea169
#include "problem_api.h"
Packit 8ea169
Packit 8ea169
#define APP_NAME "abrt-applet"
Packit 8ea169
#define GS_SCHEMA_ID_PRIVACY "org.gnome.desktop.privacy"
Packit 8ea169
#define GS_PRIVACY_OPT_AUTO_REPORTING "report-technical-problems"
Packit 8ea169
Packit 8ea169
/* libnotify action keys */
Packit 8ea169
#define A_REPORT_REPORT "REPORT"
Packit 8ea169
#define A_RESTART_APPLICATION "RESTART"
Packit 8ea169
Packit 8ea169
#define GUI_EXECUTABLE "gnome-abrt"
Packit 8ea169
Packit 8ea169
#define NOTIFICATION_ICON_NAME "face-sad-symbolic"
Packit 8ea169
Packit 8ea169
static GNetworkMonitor *netmon;
Packit 8ea169
static GList *g_deferred_crash_queue;
Packit 8ea169
static guint g_deferred_timeout;
Packit 8ea169
static bool g_gnome_abrt_available;
Packit 8ea169
static bool g_user_is_admin;
Packit 8ea169
Packit 8ea169
static bool is_autoreporting_enabled(void)
Packit 8ea169
{
Packit 8ea169
    GSettings *settings;
Packit 8ea169
    gboolean ret;
Packit 8ea169
Packit 8ea169
    settings = g_settings_new (GS_SCHEMA_ID_PRIVACY);
Packit 8ea169
    ret = g_settings_get_boolean (settings, GS_PRIVACY_OPT_AUTO_REPORTING);
Packit 8ea169
    g_object_unref (settings);
Packit 8ea169
    return ret;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void migrate_auto_reporting_to_gsettings(void)
Packit 8ea169
{
Packit 8ea169
#define OPT_NAME "AutoreportingEnabled"
Packit 8ea169
    map_string_t *settings = new_map_string();
Packit 8ea169
    if (!load_app_conf_file(APP_NAME, settings))
Packit 8ea169
        goto finito;
Packit 8ea169
Packit 8ea169
    /* Silently ignore not configured options */
Packit 8ea169
    int sv_logmode = logmode;
Packit 8ea169
    /* but only if we run in silent mode (no -v on command line) */
Packit 8ea169
    logmode = g_verbose == 0 ? 0 : sv_logmode;
Packit 8ea169
Packit 8ea169
    int auto_reporting = 0;
Packit 8ea169
    int configured = try_get_map_string_item_as_bool(settings, OPT_NAME, &auto_reporting);
Packit 8ea169
Packit 8ea169
    logmode = sv_logmode;
Packit 8ea169
Packit 8ea169
    if (!configured)
Packit 8ea169
        goto finito;
Packit 8ea169
Packit 8ea169
    /* Enable the GS option if AutoreportingEnabled is true because the user
Packit 8ea169
     * turned the Autoreporting in abrt-applet in a before GS.
Packit 8ea169
     *
Packit 8ea169
     * Do not disable the GS option if AutoreportingEvent is false because the
Packit 8ea169
     * GS option is false by default, thus disabling would revert the user's
Packit 8ea169
     * decision to  automatically report technical problems.
Packit 8ea169
     */
Packit 8ea169
    if (auto_reporting)
Packit 8ea169
    {
Packit 8ea169
        GSettings *settings = g_settings_new(GS_SCHEMA_ID_PRIVACY);
Packit 8ea169
        g_settings_set_boolean(settings, GS_PRIVACY_OPT_AUTO_REPORTING, TRUE);
Packit 8ea169
        g_object_unref(settings);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    remove_map_string_item(settings, OPT_NAME);
Packit 8ea169
    save_app_conf_file(APP_NAME, settings);
Packit 8ea169
Packit 8ea169
    log_warning("Successfully migrated "APP_NAME":"OPT_NAME" to "GS_SCHEMA_ID_PRIVACY":"GS_PRIVACY_OPT_AUTO_REPORTING);
Packit 8ea169
Packit 8ea169
#undef OPT_NAME
Packit 8ea169
finito:
Packit 8ea169
    free_map_string(settings);
Packit 8ea169
    return;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static const char *get_autoreport_event_name(void)
Packit 8ea169
{
Packit 8ea169
    load_user_settings(APP_NAME);
Packit 8ea169
    const char *configured = get_user_setting("AutoreportingEvent");
Packit 8ea169
    return configured ? configured : g_settings_autoreporting_event;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static bool is_networking_enabled(void)
Packit 8ea169
{
Packit 8ea169
    if (!g_network_monitor_get_network_available(netmon))
Packit 8ea169
        return FALSE;
Packit 8ea169
    return g_network_monitor_get_connectivity(netmon) == G_NETWORK_CONNECTIVITY_FULL;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void show_problem_list_notification(GList *problems);
Packit 8ea169
static void problem_info_unref(gpointer data);
Packit 8ea169
Packit 8ea169
static gboolean process_deferred_queue_timeout_fn(void)
Packit 8ea169
{
Packit 8ea169
    g_deferred_timeout = 0;
Packit 8ea169
Packit 8ea169
    GList *tmp = g_deferred_crash_queue;
Packit 8ea169
    g_deferred_crash_queue = NULL;
Packit 8ea169
Packit 8ea169
    /* this function calls push_to_deferred_queue() which appends data to
Packit 8ea169
     * g_deferred_crash_queue but the function also modifies the argument
Packit 8ea169
     * so we must reset g_deferred_crash_queue before the call */
Packit 8ea169
    show_problem_list_notification(tmp);
Packit 8ea169
Packit 8ea169
    /* Remove this timeout fn from the main loop*/
Packit 8ea169
    return G_SOURCE_REMOVE;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void connectivity_changed_cb(GObject    *gobject,
Packit 8ea169
                                    GParamSpec *pspec,
Packit 8ea169
                                    gpointer    user_data)
Packit 8ea169
{
Packit 8ea169
    if (g_network_monitor_get_network_available(netmon) &&
Packit 8ea169
        g_network_monitor_get_connectivity(netmon) == G_NETWORK_CONNECTIVITY_FULL)
Packit 8ea169
    {
Packit 8ea169
        if (g_deferred_timeout)
Packit 8ea169
            g_source_remove(g_deferred_timeout);
Packit 8ea169
Packit 8ea169
        g_deferred_timeout = g_idle_add ((GSourceFunc)process_deferred_queue_timeout_fn, NULL);
Packit 8ea169
    }
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
typedef struct problem_info {
Packit 8ea169
    problem_data_t *problem_data;
Packit 8ea169
    int refcount;
Packit 8ea169
    bool foreign;
Packit 8ea169
    guint count;
Packit 8ea169
    bool is_packaged;
Packit 8ea169
    char **envp;
Packit 8ea169
    pid_t pid;
Packit 8ea169
    bool known;
Packit 8ea169
    bool reported;
Packit 8ea169
    bool was_announced;
Packit 8ea169
    bool is_writable;
Packit 8ea169
    int time;
Packit 8ea169
} problem_info_t;
Packit 8ea169
Packit 8ea169
static void push_to_deferred_queue(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    g_deferred_crash_queue = g_list_append(g_deferred_crash_queue, pi);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static const char *problem_info_get_dir(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    return problem_data_get_content_or_NULL(pi->problem_data, CD_DUMPDIR);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static const char *problem_info_get_command_line(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    return problem_data_get_content_or_NULL(pi->problem_data, FILENAME_CMDLINE);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static int problem_info_get_time(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    if (pi->time == -1)
Packit 8ea169
    {
Packit 8ea169
        const char *time_str = problem_data_get_content_or_NULL(pi->problem_data, FILENAME_TIME);
Packit 8ea169
Packit 8ea169
        if (time_str == NULL)
Packit 8ea169
            error_msg_and_die("BUG: Problem info has data without the element time");
Packit 8ea169
Packit 8ea169
        pi->time = atoi(time_str);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return pi->time;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static const char **problem_info_get_env(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    if (pi->envp == NULL)
Packit 8ea169
    {
Packit 8ea169
        const char *env_str = problem_data_get_content_or_NULL(pi->problem_data, FILENAME_ENVIRON);
Packit 8ea169
        pi->envp = (env_str != NULL) ? g_strsplit (env_str, "\n", -1) : NULL;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return (const char **)pi->envp;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static int problem_info_get_pid(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    if (pi->pid == -1)
Packit 8ea169
    {
Packit 8ea169
        const char *pid_str = problem_data_get_content_or_NULL(pi->problem_data, FILENAME_PID);
Packit 8ea169
        pi->pid = (pid_str != NULL) ? atoi (pid_str) : -1;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return pi->pid;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static int problem_info_get_count(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    if (pi->count == -1)
Packit 8ea169
    {
Packit 8ea169
        const char *count_str = problem_data_get_content_or_NULL(pi->problem_data, FILENAME_COUNT);
Packit 8ea169
        pi->count = count_str ? atoi(count_str) : 1;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return pi->count;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static bool problem_info_is_reported(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    return problem_data_get_content_or_NULL(pi->problem_data, FILENAME_REPORTED_TO) != NULL;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void problem_info_set_dir(problem_info_t *pi, const char *dir)
Packit 8ea169
{
Packit 8ea169
    problem_data_add_text_noteditable(pi->problem_data, CD_DUMPDIR, dir);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static bool problem_info_ensure_writable(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    if (pi->is_writable)
Packit 8ea169
        return true;
Packit 8ea169
Packit 8ea169
    /* chown the directory in any case, because kernel oopses are not foreign */
Packit 8ea169
    /* but their dump directories are not writable without chowning them or */
Packit 8ea169
    /* stealing them. The stealing is deprecated as it breaks the local */
Packit 8ea169
    /* duplicate search and root cannot see them */
Packit 8ea169
    const int res = chown_dir_over_dbus(problem_info_get_dir(pi));
Packit 8ea169
    if (pi->foreign && res != 0)
Packit 8ea169
    {
Packit 8ea169
        error_msg(_("Can't take ownership of '%s'"), problem_info_get_dir(pi));
Packit 8ea169
        return false;
Packit 8ea169
    }
Packit 8ea169
    pi->foreign = false;
Packit 8ea169
Packit 8ea169
    struct dump_dir *dd = open_directory_for_writing(problem_info_get_dir(pi), /* don't ask */ NULL);
Packit 8ea169
    if (!dd)
Packit 8ea169
    {
Packit 8ea169
        error_msg(_("Can't open directory for writing '%s'"), problem_info_get_dir(pi));
Packit 8ea169
        return false;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    problem_info_set_dir(pi, dd->dd_dirname);
Packit 8ea169
    pi->is_writable = true;
Packit 8ea169
    dd_close(dd);
Packit 8ea169
    return true;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static problem_info_t *problem_info_new(const char *dir)
Packit 8ea169
{
Packit 8ea169
    problem_info_t *pi = g_new0(problem_info_t, 1);
Packit 8ea169
    pi->refcount = 1;
Packit 8ea169
    pi->time = -1;
Packit 8ea169
    pi->pid = -1;
Packit 8ea169
    pi->count = -1;
Packit 8ea169
    pi->problem_data = problem_data_new();
Packit 8ea169
    problem_info_set_dir(pi, dir);
Packit 8ea169
    return pi;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void problem_info_unref(gpointer data)
Packit 8ea169
{
Packit 8ea169
    problem_info_t *pi;
Packit 8ea169
Packit 8ea169
    if (data == NULL)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    pi = data;
Packit 8ea169
    pi->refcount--;
Packit 8ea169
    if (pi->refcount > 0)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    problem_data_free(pi->problem_data);
Packit 8ea169
    g_free(pi);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static problem_info_t* problem_info_ref(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    g_return_val_if_fail (pi != NULL, NULL);
Packit 8ea169
Packit 8ea169
    pi->refcount++;
Packit 8ea169
    return pi;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void run_event_async(problem_info_t *pi, const char *event_name);
Packit 8ea169
Packit 8ea169
struct event_processing_state
Packit 8ea169
{
Packit 8ea169
    pid_t child_pid;
Packit 8ea169
    int   child_stdout_fd;
Packit 8ea169
    struct strbuf *cmd_output;
Packit 8ea169
Packit 8ea169
    problem_info_t *pi;
Packit 8ea169
    int flags;
Packit 8ea169
};
Packit 8ea169
Packit 8ea169
static struct event_processing_state *new_event_processing_state(void)
Packit 8ea169
{
Packit 8ea169
    struct event_processing_state *p = g_new0(struct event_processing_state, 1);
Packit 8ea169
    p->child_pid = -1;
Packit 8ea169
    p->child_stdout_fd = -1;
Packit 8ea169
    p->cmd_output = strbuf_new();
Packit 8ea169
    return p;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void free_event_processing_state(struct event_processing_state *p)
Packit 8ea169
{
Packit 8ea169
    if (!p)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    strbuf_free(p->cmd_output);
Packit 8ea169
    g_free(p);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/* Compares the problem directories to list saved in
Packit 8ea169
 * $XDG_CACHE_HOME/abrt/applet_dirlist and updates the applet_dirlist
Packit 8ea169
 * with updated list.
Packit 8ea169
 *
Packit 8ea169
 * @param new_dirs The list where new directories are stored if caller
Packit 8ea169
 * wishes it. Can be NULL.
Packit 8ea169
 */
Packit 8ea169
static void new_dir_exists(GList **new_dirs)
Packit 8ea169
{
Packit 8ea169
    GList *dirlist = get_problems_over_dbus(/*don't authorize*/false);
Packit 8ea169
    if (dirlist == ERR_PTR)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    const char *cachedir = g_get_user_cache_dir();
Packit 8ea169
    char *dirlist_name = concat_path_file(cachedir, "abrt");
Packit 8ea169
    g_mkdir_with_parents(dirlist_name, 0777);
Packit 8ea169
    free(dirlist_name);
Packit 8ea169
    dirlist_name = concat_path_file(cachedir, "abrt/applet_dirlist");
Packit 8ea169
    FILE *fp = fopen(dirlist_name, "r+");
Packit 8ea169
    if (!fp)
Packit 8ea169
        fp = fopen(dirlist_name, "w+");
Packit 8ea169
    free(dirlist_name);
Packit 8ea169
    if (fp)
Packit 8ea169
    {
Packit 8ea169
        GList *old_dirlist = NULL;
Packit 8ea169
        char *line;
Packit 8ea169
        while ((line = xmalloc_fgetline(fp)) != NULL)
Packit 8ea169
            old_dirlist = g_list_prepend(old_dirlist, line);
Packit 8ea169
Packit 8ea169
        old_dirlist = g_list_reverse(old_dirlist);
Packit 8ea169
        /* We will sort and compare current dir list with last known one.
Packit 8ea169
         * Possible combinations:
Packit 8ea169
         * DIR1 DIR1 - Both lists have the same element, advance both ptrs.
Packit 8ea169
         * DIR2      - Current dir list has new element. IOW: new dir exists!
Packit 8ea169
         *             Advance only current dirlist ptr.
Packit 8ea169
         *      DIR3 - Only old list has element. Advance only old ptr.
Packit 8ea169
         * DIR4 ==== - Old list ended, current one didn't. New dir exists!
Packit 8ea169
         * ====
Packit 8ea169
         */
Packit 8ea169
        GList *l1 = dirlist = g_list_sort(dirlist, (GCompareFunc)strcmp);
Packit 8ea169
        GList *l2 = old_dirlist = g_list_sort(old_dirlist, (GCompareFunc)strcmp);
Packit 8ea169
        int different = 0;
Packit 8ea169
        while (l1 && l2)
Packit 8ea169
        {
Packit 8ea169
            int diff = strcmp(l1->data, l2->data);
Packit 8ea169
            different |= diff;
Packit 8ea169
            if (diff < 0)
Packit 8ea169
            {
Packit 8ea169
                if (new_dirs)
Packit 8ea169
                {
Packit 8ea169
                    *new_dirs = g_list_prepend(*new_dirs, xstrdup(l1->data));
Packit 8ea169
                    log_notice("New dir detected: %s", (char *)l1->data);
Packit 8ea169
                }
Packit 8ea169
                l1 = g_list_next(l1);
Packit 8ea169
                continue;
Packit 8ea169
            }
Packit 8ea169
            l2 = g_list_next(l2);
Packit 8ea169
            if (diff == 0)
Packit 8ea169
                l1 = g_list_next(l1);
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        different |= (l1 != NULL);
Packit 8ea169
        if (different && new_dirs)
Packit 8ea169
        {
Packit 8ea169
            while (l1)
Packit 8ea169
            {
Packit 8ea169
                *new_dirs = g_list_prepend(*new_dirs, xstrdup(l1->data));
Packit 8ea169
                log_notice("New dir detected: %s", (char *)l1->data);
Packit 8ea169
                l1 = g_list_next(l1);
Packit 8ea169
            }
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        if (different || l2)
Packit 8ea169
        {
Packit 8ea169
            rewind(fp);
Packit 8ea169
            if (ftruncate(fileno(fp), 0)) /* shut up gcc */;
Packit 8ea169
            l1 = dirlist;
Packit 8ea169
            while (l1)
Packit 8ea169
            {
Packit 8ea169
                fprintf(fp, "%s\n", (char*) l1->data);
Packit 8ea169
                l1 = g_list_next(l1);
Packit 8ea169
            }
Packit 8ea169
        }
Packit 8ea169
        fclose(fp);
Packit 8ea169
        list_free_with_free(old_dirlist);
Packit 8ea169
    }
Packit 8ea169
    list_free_with_free(dirlist);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static bool is_gnome_abrt_available(void)
Packit 8ea169
{
Packit 8ea169
    GAppInfo *app;
Packit 8ea169
    GError *error = NULL;
Packit 8ea169
    bool ret = TRUE;
Packit 8ea169
Packit 8ea169
    app = g_app_info_create_from_commandline (GUI_EXECUTABLE, GUI_EXECUTABLE,
Packit 8ea169
                                              G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION,
Packit 8ea169
                                              &error);
Packit 8ea169
    if (!app)
Packit 8ea169
    {
Packit 8ea169
        log_debug("Cannot find " GUI_EXECUTABLE ": %s", error->message);
Packit 8ea169
        g_error_free(error);
Packit 8ea169
        ret = FALSE;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    g_clear_object(&app);
Packit 8ea169
Packit 8ea169
    return ret;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static bool is_user_admin(void)
Packit 8ea169
{
Packit 8ea169
#ifdef HAVE_POLKIT
Packit 8ea169
    GError *error = NULL;
Packit 8ea169
    bool ret = false;
Packit 8ea169
    GPermission *perm = polkit_permission_new_sync ("org.freedesktop.problems.getall",
Packit 8ea169
                                                    NULL, NULL, &error);
Packit 8ea169
    if (!perm)
Packit 8ea169
        perror_msg_and_die("Can't get Polkit configuration: %s", error->message);
Packit 8ea169
Packit 8ea169
    ret = g_permission_get_allowed (perm);
Packit 8ea169
Packit 8ea169
    g_object_unref (perm);
Packit 8ea169
Packit 8ea169
    return ret;
Packit 8ea169
#else
Packit 8ea169
    return true;
Packit 8ea169
#endif
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static gboolean
Packit 8ea169
is_app_running (GAppInfo *app)
Packit 8ea169
{
Packit 8ea169
    /* FIXME ask gnome-shell about that */
Packit 8ea169
    return FALSE;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void fork_exec_gui(const char *problem_id)
Packit 8ea169
{
Packit 8ea169
    GAppInfo *app;
Packit 8ea169
    GError *error = NULL;
Packit 8ea169
    char *cmd;
Packit 8ea169
Packit 8ea169
    cmd = g_strdup_printf (GUI_EXECUTABLE " -p %s", problem_id);
Packit 8ea169
    app = g_app_info_create_from_commandline (cmd, GUI_EXECUTABLE,
Packit 8ea169
                                              G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION,
Packit 8ea169
                                              &error);
Packit 8ea169
    g_free(cmd);
Packit 8ea169
Packit 8ea169
    if (!app)
Packit 8ea169
        error_msg_and_die("Cannot find " GUI_EXECUTABLE);
Packit 8ea169
Packit 8ea169
    if (!g_app_info_launch(G_APP_INFO(app), NULL, NULL, &error))
Packit 8ea169
        perror_msg_and_die("Could not launch " GUI_EXECUTABLE ": %s", error->message);
Packit 8ea169
Packit 8ea169
    /* Scan dirs and save new $XDG_CACHE_HOME/abrt/applet_dirlist.
Packit 8ea169
     * (Otherwise, after a crash, next time applet is started,
Packit 8ea169
     * it will show alert icon even if we did click on it
Packit 8ea169
     * "in previous life"). We ignore function return value.
Packit 8ea169
     */
Packit 8ea169
    new_dir_exists(/* new dirs list */ NULL);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static pid_t spawn_event_handler_child(const char *dump_dir_name, const char *event_name, int *fdp)
Packit 8ea169
{
Packit 8ea169
    char *args[7];
Packit 8ea169
    args[0] = (char *) LIBEXEC_DIR"/abrt-handle-event";
Packit 8ea169
    args[1] = (char *) "-i"; /* Interactive? - Sure, applet is like a user */
Packit 8ea169
    args[2] = (char *) "-e";
Packit 8ea169
    args[3] = (char *) event_name;
Packit 8ea169
    args[4] = (char *) "--";
Packit 8ea169
    args[5] = (char *) dump_dir_name;
Packit 8ea169
    args[6] = NULL;
Packit 8ea169
Packit 8ea169
    int pipeout[2];
Packit 8ea169
    int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_QUIET | EXECFLG_ERR2OUT;
Packit 8ea169
    VERB1 flags &= ~EXECFLG_QUIET;
Packit 8ea169
Packit 8ea169
    char *env_vec[2];
Packit 8ea169
Packit 8ea169
    /* WTF? We use 'abrt-handle-event -i' but here we export REPORT_CLIENT_NONINTERACTIVE */
Packit 8ea169
    /* - Exactly, REPORT_CLIENT_NONINTERACTIVE causes that abrt-handle-event in */
Packit 8ea169
    /* interactive mode replies with empty responses to all event's questions. */
Packit 8ea169
    env_vec[0] = g_strdup("REPORT_CLIENT_NONINTERACTIVE=1");
Packit 8ea169
    env_vec[1] = NULL;
Packit 8ea169
Packit 8ea169
    pid_t child = fork_execv_on_steroids(flags, args, fdp ? pipeout : NULL,
Packit 8ea169
            env_vec, /*dir:*/ NULL, /*uid(unused):*/ 0);
Packit 8ea169
Packit 8ea169
    if (fdp)
Packit 8ea169
        *fdp = pipeout[0];
Packit 8ea169
Packit 8ea169
    g_free(env_vec[0]);
Packit 8ea169
Packit 8ea169
    return child;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
//this action should open gnome-abrt
Packit 8ea169
static void action_report(NotifyNotification *notification, gchar *action, gpointer user_data)
Packit 8ea169
{
Packit 8ea169
    log_debug("Reporting a problem!");
Packit 8ea169
    /* must be closed before ask_yes_no dialog run */
Packit 8ea169
    GError *err = NULL;
Packit 8ea169
    notify_notification_close(notification, &err;;
Packit 8ea169
    if (err != NULL)
Packit 8ea169
    {
Packit 8ea169
        error_msg(_("Can't close notification: %s"), err->message);
Packit 8ea169
        g_error_free(err);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    problem_info_t *pi = (problem_info_t *)user_data;
Packit 8ea169
    if (problem_info_get_dir(pi))
Packit 8ea169
        fork_exec_gui(problem_info_get_dir(pi));
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void action_restart(NotifyNotification *notification, gchar *action, gpointer user_data)
Packit 8ea169
{
Packit 8ea169
    GAppInfo *app;
Packit 8ea169
    log_debug("Restarting an application!");
Packit 8ea169
    /* must be closed before ask_yes_no dialog run */
Packit 8ea169
    GError *err = NULL;
Packit 8ea169
    notify_notification_close(notification, &err;;
Packit 8ea169
    if (err != NULL)
Packit 8ea169
    {
Packit 8ea169
        error_msg(_("Can't close notification: %s"), err->message);
Packit 8ea169
        g_error_free(err);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    problem_info_t *pi = (problem_info_t *)user_data;
Packit 8ea169
    app = problem_create_app_from_cmdline (problem_info_get_command_line(pi));
Packit 8ea169
    g_assert (app);
Packit 8ea169
Packit 8ea169
    if (!g_app_info_launch(G_APP_INFO(app), NULL, NULL, &err))
Packit 8ea169
    {
Packit 8ea169
        perror_msg("Could not launch '%s': %s",
Packit 8ea169
                   g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (app)),
Packit 8ea169
                   err->message);
Packit 8ea169
    }
Packit 8ea169
    g_object_unref (app);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void on_notify_close(NotifyNotification *notification, gpointer user_data)
Packit 8ea169
{
Packit 8ea169
    log_debug("Notify closed!");
Packit 8ea169
    g_object_unref(notification);
Packit 8ea169
Packit 8ea169
    /* Scan dirs and save new $XDG_CACHE_HOME/abrt/applet_dirlist.
Packit 8ea169
     * (Otherwise, after a crash, next time applet is started,
Packit 8ea169
     * it will show alert icon even if we did click on it
Packit 8ea169
     * "in previous life"). We ignore finction return value.
Packit 8ea169
     */
Packit 8ea169
    new_dir_exists(/* new dirs list */ NULL);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static NotifyNotification *new_warn_notification(const char *body)
Packit 8ea169
{
Packit 8ea169
    NotifyNotification *notification;
Packit 8ea169
Packit 8ea169
    notification = notify_notification_new(_("Oops!"), body, NOTIFICATION_ICON_NAME);
Packit 8ea169
Packit 8ea169
    g_signal_connect(notification, "closed", G_CALLBACK(on_notify_close), NULL);
Packit 8ea169
Packit 8ea169
    notify_notification_set_urgency(notification, NOTIFY_URGENCY_NORMAL);
Packit 8ea169
    notify_notification_set_timeout(notification, NOTIFY_EXPIRES_DEFAULT);
Packit 8ea169
    notify_notification_set_hint(notification, "desktop-entry", g_variant_new_string(APP_NAME));
Packit 8ea169
Packit 8ea169
    return notification;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void
Packit 8ea169
add_default_action (NotifyNotification *notification,
Packit 8ea169
                    problem_info_t     *pi)
Packit 8ea169
{
Packit 8ea169
    if (!g_gnome_abrt_available)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    /* Using the same action as for report */
Packit 8ea169
    notify_notification_add_action(notification, "default", _("Report"),
Packit 8ea169
            NOTIFY_ACTION_CALLBACK(action_report),
Packit 8ea169
            problem_info_ref (pi), problem_info_unref);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void
Packit 8ea169
add_send_a_report_button (NotifyNotification *notification,
Packit 8ea169
                          problem_info_t     *pi)
Packit 8ea169
{
Packit 8ea169
    if (!g_gnome_abrt_available)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    notify_notification_add_action(notification, A_REPORT_REPORT, _("Report"),
Packit 8ea169
            NOTIFY_ACTION_CALLBACK(action_report),
Packit 8ea169
            problem_info_ref (pi), problem_info_unref);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void
Packit 8ea169
add_restart_app_button (NotifyNotification *notification,
Packit 8ea169
                        problem_info_t     *pi)
Packit 8ea169
{
Packit 8ea169
    notify_notification_add_action(notification, A_RESTART_APPLICATION, _("Restart"),
Packit 8ea169
            NOTIFY_ACTION_CALLBACK(action_restart),
Packit 8ea169
            problem_info_ref (pi), problem_info_unref);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/*
Packit 8ea169
 * Destroys the problems argument
Packit 8ea169
 */
Packit 8ea169
static void notify_problem_list(GList *problems)
Packit 8ea169
{
Packit 8ea169
    if (problems == NULL)
Packit 8ea169
    {
Packit 8ea169
        log_debug("Not showing any notification bubble because the list of problems is empty.");
Packit 8ea169
        return;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    /* For the whole system, we'll need to know:
Packit 8ea169
     * - Whether automatic reporting is enabled or not
Packit 8ea169
     * - Whether the network is available
Packit 8ea169
     */
Packit 8ea169
    gboolean auto_reporting = is_autoreporting_enabled();
Packit 8ea169
    gboolean network_available = is_networking_enabled();
Packit 8ea169
Packit 8ea169
    for (GList *iter = problems; iter; iter = g_list_next(iter))
Packit 8ea169
    {
Packit 8ea169
        char *notify_body = NULL;
Packit 8ea169
        GAppInfo *app;
Packit 8ea169
Packit 8ea169
        problem_info_t *pi = iter->data;
Packit 8ea169
Packit 8ea169
        if (pi->was_announced)
Packit 8ea169
        {
Packit 8ea169
            problem_info_unref (pi);
Packit 8ea169
            continue;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        app = problem_create_app_from_env (problem_info_get_env(pi), problem_info_get_pid(pi));
Packit 8ea169
Packit 8ea169
        if (!app)
Packit 8ea169
        {
Packit 8ea169
            const char *const cmd_line = problem_info_get_command_line(pi);
Packit 8ea169
            if (cmd_line != NULL)
Packit 8ea169
                app = problem_create_app_from_cmdline(cmd_line);
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        /* For each problem we'll need to know:
Packit 8ea169
         * - Whether or not the crash happened in an “app”
Packit 8ea169
         * - Whether the app is packaged (in Fedora) or not
Packit 8ea169
         * - Whether the app is back up and running
Packit 8ea169
         * - Whether the user is the one for which the app crashed
Packit 8ea169
         * - Whether the problem has already been reported on this machine
Packit 8ea169
         */
Packit 8ea169
        gboolean is_app = (app != NULL);
Packit 8ea169
        gboolean is_packaged = pi->is_packaged;
Packit 8ea169
        gboolean is_running_again = is_app_running(app);
Packit 8ea169
        gboolean is_current_user = !pi->foreign;
Packit 8ea169
        gboolean already_reported = problem_info_get_count(pi) > 1;
Packit 8ea169
Packit 8ea169
        gboolean report_button = FALSE;
Packit 8ea169
        gboolean restart_button = FALSE;
Packit 8ea169
Packit 8ea169
        if (is_app)
Packit 8ea169
        {
Packit 8ea169
            if (auto_reporting)
Packit 8ea169
            {
Packit 8ea169
                if (is_packaged)
Packit 8ea169
                {
Packit 8ea169
                    if (network_available)
Packit 8ea169
                    {
Packit 8ea169
                        notify_body = g_strdup_printf (_("We're sorry, it looks like %s crashed. The problem has been automatically reported."),
Packit 8ea169
                                                       g_app_info_get_display_name (app));
Packit 8ea169
                    }
Packit 8ea169
                    else
Packit 8ea169
                    {
Packit 8ea169
                        notify_body = g_strdup_printf (_("We’re sorry, it looks like %s crashed. The problem will be reported when the internet is available."),
Packit 8ea169
                                                       g_app_info_get_display_name (app));
Packit 8ea169
                    }
Packit 8ea169
                }
Packit 8ea169
                else if (!already_reported)
Packit 8ea169
                {
Packit 8ea169
                    notify_body = g_strdup_printf (_("We're sorry, it looks like %s crashed. Please contact the developer if you want to report the issue."),
Packit 8ea169
                                                   g_app_info_get_display_name (app));
Packit 8ea169
                }
Packit 8ea169
            }
Packit 8ea169
            else
Packit 8ea169
            {
Packit 8ea169
                if (is_packaged)
Packit 8ea169
                {
Packit 8ea169
                    notify_body = g_strdup_printf (_("We're sorry, it looks like %s crashed. If you'd like to help resolve the issue, please send a report."),
Packit 8ea169
                                                   g_app_info_get_display_name (app));
Packit 8ea169
                    report_button = TRUE;
Packit 8ea169
                }
Packit 8ea169
                else if (!already_reported)
Packit 8ea169
                {
Packit 8ea169
                    notify_body = g_strdup_printf (_("We're sorry, it looks like %s crashed. Please contact the developer if you want to report the issue."),
Packit 8ea169
                                                   g_app_info_get_display_name (app));
Packit 8ea169
                }
Packit 8ea169
            }
Packit 8ea169
            if (is_current_user && !is_running_again)
Packit 8ea169
                restart_button = TRUE;
Packit 8ea169
        } else {
Packit 8ea169
            if (!already_reported)
Packit 8ea169
            {
Packit 8ea169
                if (auto_reporting && is_packaged)
Packit 8ea169
                {
Packit 8ea169
                    if (network_available)
Packit 8ea169
                    {
Packit 8ea169
                        notify_body = g_strdup (_("We're sorry, it looks like a problem occurred in a component. The problem has been automatically reported."));
Packit 8ea169
                    }
Packit 8ea169
                    else
Packit 8ea169
                    {
Packit 8ea169
                        notify_body = g_strdup (_("We're sorry, it looks like a problem occurred in a component. The problem will be reported when the internet is available."));
Packit 8ea169
                    }
Packit 8ea169
                }
Packit 8ea169
                else if (!auto_reporting && is_packaged)
Packit 8ea169
                {
Packit 8ea169
                    notify_body = g_strdup (_("We're sorry, it looks like a problem occurred. If you'd like to help resolve the issue, please send a report."));
Packit 8ea169
                    report_button = TRUE;
Packit 8ea169
                }
Packit 8ea169
                else
Packit 8ea169
                {
Packit 8ea169
                    char *binary = problem_get_argv0 (problem_info_get_command_line(pi));
Packit 8ea169
                    notify_body = g_strdup_printf (_("We're sorry, it looks like %s crashed. Please contact the developer if you want to report the issue."),
Packit 8ea169
                                                   binary);
Packit 8ea169
                    g_free (binary);
Packit 8ea169
                }
Packit 8ea169
            }
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        if (!notify_body)
Packit 8ea169
        {
Packit 8ea169
#define BOOL_AS_STR(x)  x ? "true" : "false"
Packit 8ea169
            log_debug ("Not showing a notification, as we have no message to show:");
Packit 8ea169
            log_debug ("auto reporting:    %s", BOOL_AS_STR(auto_reporting));
Packit 8ea169
            log_debug ("network available: %s", BOOL_AS_STR(network_available));
Packit 8ea169
            log_debug ("is app:            %s", BOOL_AS_STR(is_app));
Packit 8ea169
            log_debug ("is packaged:       %s", BOOL_AS_STR(is_packaged));
Packit 8ea169
            log_debug ("is running again:  %s", BOOL_AS_STR(is_running_again));
Packit 8ea169
            log_debug ("is current user:   %s", BOOL_AS_STR(is_current_user));
Packit 8ea169
            log_debug ("already reported:  %s", BOOL_AS_STR(already_reported));
Packit 8ea169
Packit 8ea169
            g_clear_object (&app);
Packit 8ea169
            problem_info_unref (pi);
Packit 8ea169
            continue;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        NotifyNotification *notification = new_warn_notification(notify_body);
Packit 8ea169
        g_free(notify_body);
Packit 8ea169
Packit 8ea169
        pi->was_announced = true;
Packit 8ea169
Packit 8ea169
        if (report_button)
Packit 8ea169
            add_send_a_report_button (notification, pi);
Packit 8ea169
        if (restart_button)
Packit 8ea169
            add_restart_app_button (notification, pi);
Packit 8ea169
Packit 8ea169
        add_default_action (notification, pi);
Packit 8ea169
Packit 8ea169
        GError *err = NULL;
Packit 8ea169
        log_debug("Showing a notification");
Packit 8ea169
        notify_notification_show(notification, &err;;
Packit 8ea169
        if (err != NULL)
Packit 8ea169
        {
Packit 8ea169
            error_msg(_("Can't show notification: %s"), err->message);
Packit 8ea169
            g_error_free(err);
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        problem_info_unref (pi);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    g_list_free(problems);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void notify_problem(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    GList *problems = g_list_append(NULL, pi);
Packit 8ea169
    notify_problem_list(problems);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/* Event-processing child output handler */
Packit 8ea169
static gboolean handle_event_output_cb(GIOChannel *gio, GIOCondition condition, gpointer ptr)
Packit 8ea169
{
Packit 8ea169
    struct event_processing_state *state = ptr;
Packit 8ea169
    problem_info_t *pi = state->pi;
Packit 8ea169
Packit 8ea169
    /* Read streamed data and split lines */
Packit 8ea169
    for (;;)
Packit 8ea169
    {
Packit 8ea169
        char buf[250]; /* usually we get one line, no need to have big buf */
Packit 8ea169
        errno = 0;
Packit 8ea169
        gsize r = 0;
Packit 8ea169
        GError *error = NULL;
Packit 8ea169
        GIOStatus stat = g_io_channel_read_chars(gio, buf, sizeof(buf) - 1, &r, &error);
Packit 8ea169
        if (stat == G_IO_STATUS_ERROR)
Packit 8ea169
        {   /* TODO: Terminate child's process? */
Packit 8ea169
            error_msg(_("Can't read from gio channel: '%s'"), error ? error->message : "");
Packit 8ea169
            g_error_free(error);
Packit 8ea169
            break;
Packit 8ea169
        }
Packit 8ea169
        if (stat == G_IO_STATUS_AGAIN)
Packit 8ea169
        {   /* We got all buffered data, but fd is still open. Done for now */
Packit 8ea169
            return TRUE; /* "glib, please don't remove this event (yet)" */
Packit 8ea169
        }
Packit 8ea169
        if (stat == G_IO_STATUS_EOF)
Packit 8ea169
            break;
Packit 8ea169
Packit 8ea169
        buf[r] = '\0';
Packit 8ea169
Packit 8ea169
        /* split lines in the current buffer */
Packit 8ea169
        char *raw = buf;
Packit 8ea169
        char *newline;
Packit 8ea169
        while ((newline = strchr(raw, '\n')) != NULL)
Packit 8ea169
        {
Packit 8ea169
            *newline = '\0';
Packit 8ea169
            strbuf_append_str(state->cmd_output, raw);
Packit 8ea169
            char *msg = state->cmd_output->buf;
Packit 8ea169
Packit 8ea169
            log_debug("%s", msg);
Packit 8ea169
Packit 8ea169
            strbuf_clear(state->cmd_output);
Packit 8ea169
            /* jump to next line */
Packit 8ea169
            raw = newline + 1;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        /* beginning of next line. the line continues by next read */
Packit 8ea169
        strbuf_append_str(state->cmd_output, raw);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    /* EOF/error */
Packit 8ea169
Packit 8ea169
    /* Wait for child to actually exit, collect status */
Packit 8ea169
    int status = 1;
Packit 8ea169
    if (safe_waitpid(state->child_pid, &status, 0) <= 0)
Packit 8ea169
        perror_msg("waitpid(%d)", (int)state->child_pid);
Packit 8ea169
Packit 8ea169
    if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_STOP_EVENT_RUN)
Packit 8ea169
    {
Packit 8ea169
        pi->known = 1;
Packit 8ea169
        status = 0;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (status == 0)
Packit 8ea169
    {
Packit 8ea169
        pi->reported = 1;
Packit 8ea169
Packit 8ea169
        log_debug("fast report finished successfully");
Packit 8ea169
        notify_problem(pi);
Packit 8ea169
    }
Packit 8ea169
    else
Packit 8ea169
    {
Packit 8ea169
        log_debug("fast report failed, deferring");
Packit 8ea169
        push_to_deferred_queue(pi);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    free_event_processing_state(state);
Packit 8ea169
Packit 8ea169
    /* We stop using this channel */
Packit 8ea169
    g_io_channel_unref(gio);
Packit 8ea169
Packit 8ea169
    return FALSE;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static GIOChannel *my_io_channel_unix_new(int fd)
Packit 8ea169
{
Packit 8ea169
    GIOChannel *ch = g_io_channel_unix_new(fd);
Packit 8ea169
Packit 8ea169
    /* Need to set the encoding otherwise we get:
Packit 8ea169
     * "Invalid byte sequence in conversion input".
Packit 8ea169
     * According to manual "NULL" is safe for binary data.
Packit 8ea169
     */
Packit 8ea169
    GError *error = NULL;
Packit 8ea169
    g_io_channel_set_encoding(ch, NULL, &error);
Packit 8ea169
    if (error)
Packit 8ea169
        perror_msg_and_die(_("Can't set encoding on gio channel: %s"), error->message);
Packit 8ea169
Packit 8ea169
    g_io_channel_set_flags(ch, G_IO_FLAG_NONBLOCK, &error);
Packit 8ea169
    if (error)
Packit 8ea169
        perror_msg_and_die(_("Can't turn on nonblocking mode for gio channel: %s"), error->message);
Packit 8ea169
Packit 8ea169
    g_io_channel_set_close_on_unref(ch, TRUE);
Packit 8ea169
Packit 8ea169
    return ch;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void export_event_configuration(const char *event_name)
Packit 8ea169
{
Packit 8ea169
    static bool exported = false;
Packit 8ea169
    if (exported)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    exported = true;
Packit 8ea169
Packit 8ea169
    event_config_t *event_config = get_event_config(event_name);
Packit 8ea169
Packit 8ea169
    /* load event config data only for the event */
Packit 8ea169
    if (event_config != NULL)
Packit 8ea169
        load_single_event_config_data_from_user_storage(event_config);
Packit 8ea169
Packit 8ea169
    GList *ex_env = export_event_config(event_name);
Packit 8ea169
    g_list_free(ex_env);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void run_event_async(problem_info_t *pi, const char *event_name)
Packit 8ea169
{
Packit 8ea169
    if (!problem_info_ensure_writable(pi))
Packit 8ea169
    {
Packit 8ea169
        problem_info_unref(pi);
Packit 8ea169
        return;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    export_event_configuration(event_name);
Packit 8ea169
Packit 8ea169
    struct event_processing_state *state = new_event_processing_state();
Packit 8ea169
    state->pi = pi;
Packit 8ea169
Packit 8ea169
    state->child_pid = spawn_event_handler_child(problem_info_get_dir(state->pi), event_name, &state->child_stdout_fd);
Packit 8ea169
Packit 8ea169
    GIOChannel *channel_event_output = my_io_channel_unix_new(state->child_stdout_fd);
Packit 8ea169
    g_io_add_watch(channel_event_output, G_IO_IN | G_IO_PRI | G_IO_HUP,
Packit 8ea169
                   handle_event_output_cb, state);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
/*
Packit 8ea169
 * Destroys the problems argument
Packit 8ea169
 */
Packit 8ea169
static void show_problem_list_notification(GList *problems)
Packit 8ea169
{
Packit 8ea169
    if (is_autoreporting_enabled())
Packit 8ea169
    {
Packit 8ea169
        /* Automatically report only own problems */
Packit 8ea169
        /* and skip foreign problems */
Packit 8ea169
        for (GList *iter = problems; iter;)
Packit 8ea169
        {
Packit 8ea169
            problem_info_t *pi = (problem_info_t *)iter->data;
Packit 8ea169
            GList *next = g_list_next(iter);
Packit 8ea169
Packit 8ea169
            if (!pi->foreign || g_user_is_admin)
Packit 8ea169
            {
Packit 8ea169
                if (is_networking_enabled ())
Packit 8ea169
                {
Packit 8ea169
                    run_event_async(pi, get_autoreport_event_name());
Packit 8ea169
                    problems = g_list_delete_link(problems, iter);
Packit 8ea169
                }
Packit 8ea169
                else
Packit 8ea169
                {
Packit 8ea169
                    /* Don't remove from the list, we'll tell the user
Packit 8ea169
                     * we'll report later, if it's not a dupe */
Packit 8ea169
                    push_to_deferred_queue(pi);
Packit 8ea169
                }
Packit 8ea169
            }
Packit 8ea169
Packit 8ea169
            iter = next;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    /* report the rest:
Packit 8ea169
     *  - only foreign if autoreporting is enabled
Packit 8ea169
     *  - the whole list otherwise
Packit 8ea169
     */
Packit 8ea169
    if (problems)
Packit 8ea169
        notify_problem_list(problems);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void show_problem_notification(problem_info_t *pi)
Packit 8ea169
{
Packit 8ea169
    GList *problems = g_list_append(NULL, pi);
Packit 8ea169
    show_problem_list_notification(problems);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void Crash(GVariant *parameters)
Packit 8ea169
{
Packit 8ea169
    const char *package_name, *dir, *uid_str, *uuid, *duphash;
Packit 8ea169
Packit 8ea169
    log_debug("Crash recorded");
Packit 8ea169
Packit 8ea169
    g_variant_get (parameters, "(&s&s&s&s&s)",
Packit 8ea169
                   &package_name,
Packit 8ea169
                   &dir,
Packit 8ea169
                   &uid_str,
Packit 8ea169
                   &uuid,
Packit 8ea169
                   &duphash);
Packit 8ea169
Packit 8ea169
    bool foreign_problem = false;
Packit 8ea169
    if (uid_str[0] != '\0')
Packit 8ea169
    {
Packit 8ea169
        char *end;
Packit 8ea169
        errno = 0;
Packit 8ea169
        unsigned long uid_num = strtoul(uid_str, &end, 10);
Packit 8ea169
        if (errno || *end != '\0' || uid_num != getuid())
Packit 8ea169
        {
Packit 8ea169
            foreign_problem = true;
Packit 8ea169
            log_notice("foreign problem %i", foreign_problem);
Packit 8ea169
        }
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    /* Non-admins shouldn't see other people's crashes */
Packit 8ea169
    if (foreign_problem && !g_user_is_admin)
Packit 8ea169
        return;
Packit 8ea169
Packit 8ea169
    static const char *elements[] = {
Packit 8ea169
        FILENAME_CMDLINE,
Packit 8ea169
        FILENAME_COUNT,
Packit 8ea169
        FILENAME_UUID,
Packit 8ea169
        FILENAME_DUPHASH,
Packit 8ea169
        FILENAME_COMPONENT,
Packit 8ea169
        FILENAME_ENVIRON,
Packit 8ea169
        FILENAME_PID,
Packit 8ea169
        NULL,
Packit 8ea169
    };
Packit 8ea169
Packit 8ea169
    problem_info_t *pi = problem_info_new(dir);
Packit 8ea169
    fill_problem_data_over_dbus(dir, elements, pi->problem_data);
Packit 8ea169
Packit 8ea169
    pi->foreign = foreign_problem;
Packit 8ea169
    pi->is_packaged = (package_name != NULL);
Packit 8ea169
Packit 8ea169
    /*
Packit 8ea169
     * Can't append dir to the seen list because of directory stealing
Packit 8ea169
     *
Packit 8ea169
     * append_dirlist(dir);
Packit 8ea169
     *
Packit 8ea169
     */
Packit 8ea169
    show_problem_notification(pi);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void handle_message(GDBusConnection *connection,
Packit 8ea169
                           const gchar     *sender_name,
Packit 8ea169
                           const gchar     *object_path,
Packit 8ea169
                           const gchar     *interface_name,
Packit 8ea169
                           const gchar     *signal_name,
Packit 8ea169
                           GVariant        *parameters,
Packit 8ea169
                           gpointer         user_data)
Packit 8ea169
{
Packit 8ea169
    g_debug ("Received signal: sender_name: %s, object_path: %s, "
Packit 8ea169
             "interface_name: %s, signal_name: %s",
Packit 8ea169
             sender_name, object_path, interface_name, signal_name);
Packit 8ea169
Packit 8ea169
    Crash(parameters);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void
Packit 8ea169
name_acquired_handler (GDBusConnection *connection,
Packit 8ea169
                       const gchar *name,
Packit 8ea169
                       gpointer user_data)
Packit 8ea169
{
Packit 8ea169
    static const char *elements[] = {
Packit 8ea169
        FILENAME_CMDLINE,
Packit 8ea169
        FILENAME_COUNT,
Packit 8ea169
        FILENAME_UUID,
Packit 8ea169
        FILENAME_DUPHASH,
Packit 8ea169
        FILENAME_COMPONENT,
Packit 8ea169
        FILENAME_UID,
Packit 8ea169
        FILENAME_TIME,
Packit 8ea169
        FILENAME_REPORTED_TO,
Packit 8ea169
        FILENAME_NOT_REPORTABLE,
Packit 8ea169
        NULL
Packit 8ea169
    };
Packit 8ea169
Packit 8ea169
    /* If some new dirs appeared since our last run, let user know it */
Packit 8ea169
    GList *new_dirs = NULL;
Packit 8ea169
    GList *notify_list = NULL;
Packit 8ea169
    new_dir_exists(&new_dirs);
Packit 8ea169
Packit 8ea169
#define time_before_ndays(n) (time(NULL) - (n)*24*60*60)
Packit 8ea169
Packit 8ea169
    /* Age limit = now - 3 days */
Packit 8ea169
    const unsigned long min_born_time = (unsigned long)(time_before_ndays(3));
Packit 8ea169
Packit 8ea169
    for ( ; new_dirs != NULL; new_dirs = g_list_next(new_dirs))
Packit 8ea169
    {
Packit 8ea169
        const char *problem_id = (const char *)new_dirs->data;
Packit 8ea169
        problem_info_t *pi = problem_info_new(problem_id);
Packit 8ea169
Packit 8ea169
        if (fill_problem_data_over_dbus(problem_id, elements, pi->problem_data) != 0)
Packit 8ea169
        {
Packit 8ea169
            log_notice("'%s' is not a dump dir - ignoring\n", problem_id);
Packit 8ea169
            problem_info_unref(pi);
Packit 8ea169
            continue;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        /* TODO: add a filter for only complete problems to GetProblems D-Bus method */
Packit 8ea169
        if (!dbus_problem_is_complete(problem_id))
Packit 8ea169
        {
Packit 8ea169
            log_notice("Ignoring incomplete problem '%s'", problem_id);
Packit 8ea169
            problem_info_unref(pi);
Packit 8ea169
            continue;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        /* TODO: add a filter for max-old reported problems to GetProblems D-Bus method */
Packit 8ea169
        if (problem_info_get_time(pi) < min_born_time)
Packit 8ea169
        {
Packit 8ea169
            log_notice("Ignoring outdated problem '%s'", problem_id);
Packit 8ea169
            problem_info_unref(pi);
Packit 8ea169
            continue;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        /* TODO: add a filter for not-yet reported problems to GetProblems D-Bus method */
Packit 8ea169
        if (problem_info_is_reported(pi))
Packit 8ea169
        {
Packit 8ea169
            log_notice("Ignoring already reported problem '%s'", problem_id);
Packit 8ea169
            problem_info_unref(pi);
Packit 8ea169
            continue;
Packit 8ea169
        }
Packit 8ea169
Packit 8ea169
        /* Can't be foreig because new_dir_exists() returns only own problems */
Packit 8ea169
        pi->foreign = false;
Packit 8ea169
        notify_list = g_list_prepend(notify_list, pi);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    if (notify_list)
Packit 8ea169
        show_problem_list_notification(notify_list);
Packit 8ea169
Packit 8ea169
    list_free_with_free(new_dirs);
Packit 8ea169
Packit 8ea169
    /*
Packit 8ea169
     * We want to update "seen directories" list on SIGTERM.
Packit 8ea169
     * Updating it after each notification doesn't account for stealing directories:
Packit 8ea169
     * if directory is stolen after seen list is updated,
Packit 8ea169
     * on next startup applet will notify user about stolen directory. WRONG.
Packit 8ea169
     *
Packit 8ea169
     * SIGTERM handler simply stops GTK main loop and the applet saves user
Packit 8ea169
     * settings, releases notify resources, releases dbus resources and updates
Packit 8ea169
     * the seen list.
Packit 8ea169
     */
Packit 8ea169
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static void
Packit 8ea169
name_lost_handler (GDBusConnection *connection,
Packit 8ea169
                   const gchar *name,
Packit 8ea169
                   gpointer user_data)
Packit 8ea169
{
Packit 8ea169
  if (connection == NULL)
Packit 8ea169
    error_msg_and_die("Problem connecting to dbus");
Packit 8ea169
Packit 8ea169
  gtk_main_quit ();
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
    notify_init("Problem detected");
Packit 8ea169
Packit 8ea169
    /* Monitor NetworkManager state */
Packit 8ea169
    netmon = g_network_monitor_get_default ();
Packit 8ea169
    g_signal_connect (G_OBJECT (netmon), "notify::connectivity",
Packit 8ea169
                      G_CALLBACK (connectivity_changed_cb), NULL);
Packit 8ea169
    g_signal_connect (G_OBJECT (netmon), "notify::network-available",
Packit 8ea169
                      G_CALLBACK (connectivity_changed_cb), NULL);
Packit 8ea169
Packit 8ea169
    g_set_prgname("abrt");
Packit 8ea169
    gtk_init(&argc, &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
        "& [-v] [DIR]...\n"
Packit 8ea169
        "\n"
Packit 8ea169
        "Applet which notifies user when new problems are detected by ABRT\n"
Packit 8ea169
    );
Packit 8ea169
    enum {
Packit 8ea169
        OPT_v = 1 << 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_END()
Packit 8ea169
    };
Packit 8ea169
    /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
Packit 8ea169
Packit 8ea169
    migrate_to_xdg_dirs();
Packit 8ea169
    migrate_auto_reporting_to_gsettings();
Packit 8ea169
Packit 8ea169
    export_abrt_envvars(0);
Packit 8ea169
    msg_prefix = g_progname;
Packit 8ea169
Packit 8ea169
    load_abrt_conf();
Packit 8ea169
    load_event_config_data();
Packit 8ea169
    load_user_settings(APP_NAME);
Packit 8ea169
Packit 8ea169
    /* Initialize our (dbus_abrt) machinery by filtering
Packit 8ea169
     * for signals:
Packit 8ea169
     *     signal sender=:1.73 -> path=/org/freedesktop/problems; interface=org.freedesktop.problems; member=Crash
Packit 8ea169
     *       string "coreutils-7.2-3.fc11"
Packit 8ea169
     *       string "0"
Packit 8ea169
     */
Packit 8ea169
    GError *error = NULL;
Packit 8ea169
    GDBusConnection *system_conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
Packit 8ea169
                                                   NULL, &error);
Packit 8ea169
    if (system_conn == NULL)
Packit 8ea169
        perror_msg_and_die("Can't connect to system dbus: %s", error->message);
Packit 8ea169
    guint filter_id = g_dbus_connection_signal_subscribe(system_conn,
Packit 8ea169
                                                         NULL,
Packit 8ea169
                                                         ABRT_DBUS_NAME,
Packit 8ea169
                                                         "Crash",
Packit 8ea169
                                                         ABRT_DBUS_OBJECT,
Packit 8ea169
                                                         NULL,
Packit 8ea169
                                                         G_DBUS_SIGNAL_FLAGS_NONE,
Packit 8ea169
                                                         handle_message,
Packit 8ea169
                                                         NULL, NULL);
Packit 8ea169
Packit 8ea169
    guint name_own_id = g_bus_own_name (G_BUS_TYPE_SESSION,
Packit 8ea169
                                        ABRT_DBUS_NAME".applet",
Packit 8ea169
                                        G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_REPLACE,
Packit 8ea169
                                        NULL,
Packit 8ea169
                                        name_acquired_handler,
Packit 8ea169
                                        name_lost_handler,
Packit 8ea169
                                        NULL, NULL);
Packit 8ea169
Packit 8ea169
    g_user_is_admin = is_user_admin();
Packit 8ea169
Packit 8ea169
    g_gnome_abrt_available = is_gnome_abrt_available();
Packit 8ea169
Packit 8ea169
    /* Enter main loop
Packit 8ea169
     */
Packit 8ea169
    gtk_main();
Packit 8ea169
Packit 8ea169
    g_bus_unown_name (name_own_id);
Packit 8ea169
Packit 8ea169
    g_dbus_connection_signal_unsubscribe(system_conn, filter_id);
Packit 8ea169
Packit 8ea169
    /* new_dir_exists() is called for each notification and if user clicks on
Packit 8ea169
     * the abrt icon. Those calls cover 99.97% of detected crashes
Packit 8ea169
     *
Packit 8ea169
     * The rest of detected crashes:
Packit 8ea169
     *
Packit 8ea169
     * 0.01%
Packit 8ea169
     * applet doesn't append a repeated crash to the seen list if the crash was
Packit 8ea169
     * the last caught crash before exit (notification is not shown in case of
Packit 8ea169
     * repeated crash)
Packit 8ea169
     *
Packit 8ea169
     * 0.01%
Packit 8ea169
     * applet doesn't append a stolen directory to the seen list if
Packit 8ea169
     * notification was closed before the notified directory had been stolen
Packit 8ea169
     *
Packit 8ea169
     * 0.1%
Packit 8ea169
     * crashes of abrt-applet
Packit 8ea169
     */
Packit 8ea169
    new_dir_exists(/* new dirs list */ NULL);
Packit 8ea169
Packit 8ea169
    if (notify_is_initted())
Packit 8ea169
        notify_uninit();
Packit 8ea169
Packit 8ea169
    /* It does not make much sense to save settings at exit and after
Packit 8ea169
     * introduction of system-config-abrt it is wrong to do that. abrt-applet
Packit 8ea169
     * is long-running application and user can modify the configuration files
Packit 8ea169
     * while abrt-applet run. Thus, saving configuration at desktop session
Packit 8ea169
     * exit would make someone's life really hard.
Packit 8ea169
     *
Packit 8ea169
     * abrt-applet saves configuration immediately after user input.
Packit 8ea169
     *
Packit 8ea169
     * save_user_settings();
Packit 8ea169
     */
Packit 8ea169
Packit 8ea169
    return 0;
Packit 8ea169
}