|
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 |
}
|