summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/applet/Makefile.am25
-rw-r--r--src/applet/applet.c (renamed from src/applet/Applet.cpp)35
-rw-r--r--src/applet/test_report.c62
-rw-r--r--src/btparser/Makefile.am2
-rw-r--r--src/btparser/normalize_glibc.c19
-rw-r--r--src/btparser/thread.c71
-rw-r--r--src/btparser/thread.h11
-rw-r--r--src/cli/CLI.cpp358
-rw-r--r--src/cli/Makefile.am14
-rw-r--r--src/cli/dbus.cpp282
-rw-r--r--src/cli/dbus.h68
-rw-r--r--src/cli/report.cpp531
-rw-r--r--src/cli/report.h7
-rw-r--r--src/daemon/CommLayerServer.h45
-rw-r--r--src/daemon/CommLayerServerDBus.cpp153
-rw-r--r--src/daemon/CommLayerServerDBus.h30
-rw-r--r--src/daemon/CrashWatcher.cpp44
-rw-r--r--src/daemon/CrashWatcher.h38
-rw-r--r--src/daemon/Daemon.cpp484
-rw-r--r--src/daemon/Makefile.am35
-rw-r--r--src/daemon/MiddleWare.cpp379
-rw-r--r--src/daemon/MiddleWare.h43
-rw-r--r--src/daemon/PluginManager.cpp259
-rw-r--r--src/daemon/PluginManager.h99
-rw-r--r--src/daemon/Settings.cpp201
-rw-r--r--src/daemon/Settings.h32
-rw-r--r--src/daemon/abrt-action-save-package-data.c (renamed from src/daemon/abrt-action-save-package-data.cpp)81
-rw-r--r--src/daemon/abrt-handle-crashdump.c24
-rw-r--r--src/daemon/abrt-server.c43
-rw-r--r--src/daemon/abrt.conf15
-rw-r--r--src/daemon/abrt.conf.512
-rw-r--r--src/daemon/abrt_event.conf60
-rw-r--r--src/daemon/comm_layer_inner.cpp (renamed from src/lib/CommLayerInner.cpp)20
-rw-r--r--src/daemon/comm_layer_inner.h (renamed from src/include/comm_layer_inner.h)11
-rw-r--r--src/daemon/rpm.c27
-rw-r--r--src/daemon/rpm.h6
-rw-r--r--src/gui-gtk/Makefile.am45
-rw-r--r--src/gui-gtk/abrt-gtk.c307
-rw-r--r--src/gui-gtk/abrt-gtk.h5
-rw-r--r--src/gui-gtk/main.c101
-rw-r--r--src/gui-wizard-gtk/Makefile.am60
-rw-r--r--src/gui-wizard-gtk/main.c80
-rw-r--r--src/gui-wizard-gtk/wizard.c968
-rw-r--r--src/gui-wizard-gtk/wizard.glade746
-rw-r--r--src/gui-wizard-gtk/wizard.h38
-rw-r--r--src/gui/CCDBusBackend.py15
-rw-r--r--src/gui/CCDump.py11
-rw-r--r--src/gui/CCMainWindow.py16
-rw-r--r--src/gui/CCReporterDialog.py578
-rw-r--r--src/gui/CReporterAssistant.py68
-rw-r--r--src/gui/CellRenderers.py66
-rw-r--r--src/gui/Makefile.am4
-rw-r--r--src/gui/SettingsDialog.py53
-rw-r--r--src/gui/ccgui.glade42
-rw-r--r--src/gui/progress_window.glade19
-rw-r--r--src/gui/settings.glade295
-rw-r--r--src/hooks/Makefile.am27
-rw-r--r--src/hooks/abrt-hook-ccpp.c165
-rw-r--r--src/hooks/abrt_exception_handler.py.in7
-rw-r--r--src/hooks/dumpoops.cpp131
-rw-r--r--src/include/Makefile.am22
-rw-r--r--src/include/abrt_crash_data.h (renamed from src/include/abrt_crash_dump.h)65
-rw-r--r--src/include/abrt_exception.h57
-rw-r--r--src/include/abrt_types.h28
-rw-r--r--src/include/abrtlib.h221
-rw-r--r--src/include/action.h44
-rw-r--r--src/include/analyzer.h34
-rw-r--r--src/include/dbus_common.h28
-rw-r--r--src/include/observer.h32
-rw-r--r--src/include/plugin.h113
-rw-r--r--src/include/report/crash_data.h101
-rw-r--r--src/include/report/dump_dir.h (renamed from src/include/dump_dir.h)18
-rw-r--r--src/include/report/run_event.h82
-rw-r--r--src/include/xfuncs.h38
-rw-r--r--src/lib/Makefile.am66
-rw-r--r--src/lib/Plugin.cpp41
-rw-r--r--src/lib/abrt_curl.c15
-rw-r--r--src/lib/abrt_dbus.c438
-rw-r--r--src/lib/abrt_dbus.h59
-rw-r--r--src/lib/abrt_types.c (renamed from src/lib/ABRTException.cpp)30
-rw-r--r--src/lib/abrt_xmlrpc.cpp18
-rw-r--r--src/lib/abrt_xmlrpc.h12
-rw-r--r--src/lib/binhex.c74
-rw-r--r--src/lib/copy_file_recursive.c139
-rw-r--r--src/lib/crash_data.c (renamed from src/lib/crash_dump.cpp)187
-rw-r--r--src/lib/create_dump_dir.c85
-rw-r--r--src/lib/dump_dir.c468
-rw-r--r--src/lib/glib_support.c (renamed from src/daemon/CommLayerServer.cpp)17
-rw-r--r--src/lib/hash_md5.h3
-rw-r--r--src/lib/hash_sha1.h3
-rw-r--r--src/lib/hooklib.c2
-rw-r--r--src/lib/hooklib.h8
-rw-r--r--src/lib/iso_date_string.c (renamed from src/lib/numtoa.cpp)25
-rw-r--r--src/lib/load_plugin_settings.c (renamed from src/lib/load_plugin_settings.cpp)38
-rw-r--r--src/lib/logging.h29
-rw-r--r--src/lib/make_descr.c (renamed from src/lib/make_descr.cpp)136
-rw-r--r--src/lib/parse_options.c45
-rw-r--r--src/lib/parse_options.h32
-rw-r--r--src/lib/parse_release.c (renamed from src/lib/parse_release.cpp)32
-rw-r--r--src/lib/read_write.h6
-rw-r--r--src/lib/run_event.c414
-rw-r--r--src/lib/spawn.c2
-rw-r--r--src/lib/steal_directory.c40
-rw-r--r--src/lib/strbuf.c2
-rw-r--r--src/lib/strbuf.h11
-rw-r--r--src/lib/stringops.cpp57
-rw-r--r--src/lib/xatonum.c4
-rw-r--r--src/lib/xfuncs.c22
-rw-r--r--src/plugins/CCpp.cpp280
-rw-r--r--src/plugins/CCpp.h49
-rw-r--r--src/plugins/Kerneloops.conf6
-rw-r--r--src/plugins/KerneloopsScanner.cpp230
-rw-r--r--src/plugins/KerneloopsScanner.h42
-rw-r--r--src/plugins/KerneloopsSysLog.cpp383
-rw-r--r--src/plugins/KerneloopsSysLog.h35
-rw-r--r--src/plugins/Makefile.am142
-rw-r--r--src/plugins/abrt-KerneloopsScanner.746
-rw-r--r--src/plugins/abrt-action-analyze-c.c16
-rw-r--r--src/plugins/abrt-action-analyze-oops.c8
-rw-r--r--src/plugins/abrt-action-analyze-python.c12
-rw-r--r--src/plugins/abrt-action-bugzilla.cpp221
-rw-r--r--src/plugins/abrt-action-generate-backtrace.c122
-rw-r--r--src/plugins/abrt-action-install-debuginfo.c43
-rwxr-xr-xsrc/plugins/abrt-action-install-debuginfo.py160
-rw-r--r--src/plugins/abrt-action-kerneloops.c (renamed from src/plugins/abrt-action-kerneloops.cpp)97
-rw-r--r--src/plugins/abrt-action-mailx.c (renamed from src/plugins/abrt-action-mailx.cpp)80
-rw-r--r--src/plugins/abrt-action-print.c (renamed from src/plugins/abrt-action-print.cpp)37
-rw-r--r--src/plugins/abrt-action-rhtsupport.c (renamed from src/plugins/abrt-action-rhtsupport.cpp)153
-rw-r--r--src/plugins/abrt-action-upload.c (renamed from src/plugins/abrt-action-upload.cpp)38
-rw-r--r--src/plugins/abrt-dump-oops.c710
-rw-r--r--src/plugins/abrt-plugins.71
-rw-r--r--src/plugins/abrt_rh_support.c95
-rw-r--r--src/plugins/ccpp_events.conf17
-rw-r--r--src/report-python/Makefile.am42
-rw-r--r--src/report-python/__init__.py130
-rw-r--r--src/report-python/accountmanager.py6
-rw-r--r--src/report-python/common.h (renamed from src/daemon/Daemon.h)49
-rw-r--r--src/report-python/crash_data.c137
-rw-r--r--src/report-python/dump_dir.c264
-rw-r--r--src/report-python/io/GTKIO.py11
-rw-r--r--src/report-python/io/NewtIO.py7
-rw-r--r--src/report-python/io/TextIO.py6
-rw-r--r--src/report-python/io/__init__.py1
-rw-r--r--src/report-python/reportmodule.c83
-rw-r--r--src/report-python/run_event.c220
-rwxr-xr-xsrc/report-python/test_crash_data21
-rwxr-xr-xsrc/report-python/test_crash_data210
-rwxr-xr-xsrc/report-python/test_dd_create25
-rwxr-xr-xsrc/report-python/test_full27
-rwxr-xr-xsrc/report-python/test_full217
-rwxr-xr-xsrc/report-python/test_run_event_state13
-rwxr-xr-xsrc/report-python/test_run_event_state127
-rwxr-xr-xsrc/report-python/test_setroubleshoot_example18
-rwxr-xr-xsrc/report-python/test_setroubleshoot_example227
155 files changed, 8692 insertions, 6390 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index e42f1aec..c6d3abd9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1 +1 @@
-SUBDIRS = include lib hooks btparser daemon applet gui cli plugins retrace
+SUBDIRS = include lib report-python hooks btparser daemon applet gui gui-gtk cli plugins gui-wizard-gtk retrace
diff --git a/src/applet/Makefile.am b/src/applet/Makefile.am
index 4207e818..cd4a643f 100644
--- a/src/applet/Makefile.am
+++ b/src/applet/Makefile.am
@@ -1,14 +1,12 @@
bin_PROGRAMS = abrt-applet
+#test-report
abrt_applet_SOURCES = \
- Applet.cpp \
+ applet.c \
applet_gtk.h applet_gtk.c
abrt_applet_CPPFLAGS = \
- -Wall -Werror \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
- -I/usr/include/glib-2.0 \
- -I/usr/lib/glib-2.0/include \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
-DCONF_DIR=\"$(CONF_DIR)\" \
@@ -18,11 +16,14 @@ abrt_applet_CPPFLAGS = \
-DICON_DIR=\"${datadir}/abrt/icons/hicolor/48x48/status\" \
$(GTK_CFLAGS) \
$(DBUS_CFLAGS) \
- -D_GNU_SOURCE
+ -D_GNU_SOURCE \
+ -Wall -Werror
+# -I/usr/include/glib-2.0
+# -I/usr/lib/glib-2.0/include
# $(LIBNOTIFY_CFLAGS)
# $(DBUS_GLIB_CFLAGS)
abrt_applet_LDADD = \
- ../lib/libabrt.la \
+ ../lib/libreport.la \
../lib/libabrt_dbus.la \
-lglib-2.0 \
-lgthread-2.0 \
@@ -30,6 +31,16 @@ abrt_applet_LDADD = \
$(LIBNOTIFY_LIBS) \
$(GTK_LIBS)
+#test_report_SOURCES = \
+# test_report.c
+#test_report_CPPFLAGS = \
+# -I$(srcdir)/../include/report -I$(srcdir)/../include \
+# $(GLIB_CFLAGS) \
+# -D_GNU_SOURCE \
+# -Wall -Werror
+#test_report_LDADD = \
+# ../lib/libreport.la
+
DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
@INTLTOOL_DESKTOP_RULE@
diff --git a/src/applet/Applet.cpp b/src/applet/applet.c
index 4ce497cf..1a2bf5c0 100644
--- a/src/applet/Applet.cpp
+++ b/src/applet/applet.c
@@ -24,7 +24,6 @@
#include <dbus/dbus-glib-lowlevel.h>
#include "abrtlib.h"
#include "abrt_dbus.h"
-#include "dbus_common.h"
#include "applet_gtk.h"
@@ -38,8 +37,8 @@ static void Crash(DBusMessage* signal)
dbus_message_iter_init(signal, &in_iter);
/* 1st param: package */
- const char* package_name;
- r = load_val(&in_iter, package_name);
+ const char* package_name = NULL;
+ r = load_charp(&in_iter, &package_name);
/* 2nd param: crash_id */
if (r != ABRT_DBUS_MORE_FIELDS)
@@ -48,7 +47,7 @@ static void Crash(DBusMessage* signal)
return;
}
const char* crash_id = NULL;
- r = load_val(&in_iter, crash_id);
+ r = load_charp(&in_iter, &crash_id);
/* 3rd param: dir */
//dir parameter is not used for now, use is planned in the future
@@ -58,13 +57,13 @@ static void Crash(DBusMessage* signal)
return;
}
const char* dir = NULL;
- r = load_val(&in_iter, dir);
+ r = load_charp(&in_iter, &dir);
/* Optional 4th param: uid */
const char* uid_str = NULL;
if (r == ABRT_DBUS_MORE_FIELDS)
{
- r = load_val(&in_iter, uid_str);
+ r = load_charp(&in_iter, &uid_str);
}
if (r != ABRT_DBUS_LAST_FIELD)
{
@@ -113,13 +112,13 @@ static void Crash(DBusMessage* signal)
show_crash_notification(applet, crash_id, message, package_name);
}
-static void QuotaExceed(DBusMessage* signal)
+static void QuotaExceeded(DBusMessage* signal)
{
int r;
DBusMessageIter in_iter;
dbus_message_iter_init(signal, &in_iter);
- const char* str;
- r = load_val(&in_iter, str);
+ const char* str = NULL;
+ r = load_charp(&in_iter, &str);
if (r != ABRT_DBUS_LAST_FIELD)
{
error_msg("dbus signal %s: parameter type mismatch", __func__);
@@ -137,8 +136,8 @@ static void NameOwnerChanged(DBusMessage* signal)
int r;
DBusMessageIter in_iter;
dbus_message_iter_init(signal, &in_iter);
- const char* name;
- r = load_val(&in_iter, name);
+ const char* name = NULL;
+ r = load_charp(&in_iter, &name);
if (r != ABRT_DBUS_MORE_FIELDS)
{
error_msg("dbus signal %s: parameter type mismatch", __func__);
@@ -149,15 +148,15 @@ static void NameOwnerChanged(DBusMessage* signal)
if (strcmp(name, "com.redhat.abrt") != 0)
return;
- const char* old_owner;
- r = load_val(&in_iter, old_owner);
+ const char* old_owner = NULL;
+ r = load_charp(&in_iter, &old_owner);
if (r != ABRT_DBUS_MORE_FIELDS)
{
error_msg("dbus signal %s: parameter type mismatch", __func__);
return;
}
- const char* new_owner;
- r = load_val(&in_iter, new_owner);
+ const char* new_owner = NULL;
+ r = load_charp(&in_iter, &new_owner);
if (r != ABRT_DBUS_LAST_FIELD)
{
error_msg("dbus signal %s: parameter type mismatch", __func__);
@@ -186,8 +185,8 @@ static DBusHandlerResult handle_message(DBusConnection* conn, DBusMessage* msg,
NameOwnerChanged(msg);
else if (strcmp(member, "Crash") == 0)
Crash(msg);
- else if (strcmp(member, "QuotaExceed") == 0)
- QuotaExceed(msg);
+ else if (strcmp(member, "QuotaExceeded") == 0)
+ QuotaExceeded(msg);
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -234,7 +233,7 @@ int main(int argc, char** argv)
error_msg_and_die(
"Usage: abrt-applet [-v]\n"
"\nOptions:"
- "\n\t-v\tVerbose"
+ "\n\t-v\tBe verbose"
);
}
}
diff --git a/src/applet/test_report.c b/src/applet/test_report.c
new file mode 100644
index 00000000..f5e79d92
--- /dev/null
+++ b/src/applet/test_report.c
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2009 Jiri Moskovcak (jmoskovc@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "crash_data.h"
+#include "dump_dir.h"
+#include "run_event.h"
+
+static char *do_log(char *log_line, void *param)
+{
+ printf("%s\n", log_line);
+ return log_line;
+}
+
+int main(int argc, char** argv)
+{
+ crash_data_t *crash_data = new_crash_data();
+
+ add_to_crash_data(crash_data, "analyzer", "wow");
+ const char *event = "report";
+
+ struct dump_dir *dd = create_dump_dir_from_crash_data(crash_data, "/tmp");
+ free_crash_data(crash_data);
+ if (!dd)
+ return 1;
+ char *dir_name = strdup(dd->dd_dirname);
+ dd_close(dd);
+
+ printf("Temp dump dir: '%s'\n", dir_name);
+
+ struct run_event_state *run_state = new_run_event_state();
+ run_state->logging_callback = do_log;
+ int r = run_event_on_dir_name(run_state, dir_name, event);
+ if (r == 0 && run_state->children_count == 0)
+ printf("No actions are found for event '%s'\n", event);
+ free_run_event_state(run_state);
+
+// delete_dump_dir(dir_name);
+ free(dir_name);
+
+ return 0;
+}
diff --git a/src/btparser/Makefile.am b/src/btparser/Makefile.am
index 349483a8..9f64f826 100644
--- a/src/btparser/Makefile.am
+++ b/src/btparser/Makefile.am
@@ -15,7 +15,7 @@ libbtparser_la_SOURCES = \
utils.h utils.c
libbtparser_la_CFLAGS = -Wall -Werror -D_GNU_SOURCE -I../lib
libbtparser_la_LDFLAGS = -version-info 1:1:0
-libbtparser_la_LIBADD = ../lib/libabrt.la
+libbtparser_la_LIBADD = ../lib/libreport.la
# From http://www.seul.org/docs/autotut/
# Version consists 3 numbers: CURRENT, REVISION, AGE.
diff --git a/src/btparser/normalize_glibc.c b/src/btparser/normalize_glibc.c
index ea40ba9d..3d0bca2b 100644
--- a/src/btparser/normalize_glibc.c
+++ b/src/btparser/normalize_glibc.c
@@ -71,9 +71,12 @@ btp_normalize_glibc_thread(struct btp_thread *thread)
/* Normalize frame names. */
#define NORMALIZE_ARCH_SPECIFIC(func) \
- if (btp_frame_calls_func_in_file2(frame, "__" func "_sse2", func ".S", "libc.so") || \
- btp_frame_calls_func_in_file2(frame, "__" func "_ssse3", func ".S", "libc.so") || \
- btp_frame_calls_func_in_file2(frame, "__" func "_ia32", func ".S", "libc.so")) \
+ if (btp_frame_calls_func_in_file3(frame, "__" func "_sse2", func, "/sysdeps/", "libc.so") || \
+ btp_frame_calls_func_in_file3(frame, "__" func "_sse2_bsf", func, "/sysdeps/", "libc.so") || \
+ btp_frame_calls_func_in_file3(frame, "__" func "_ssse3", func, "/sysdeps/", "libc.so") /* ssse3, not sse3! */ || \
+ btp_frame_calls_func_in_file3(frame, "__" func "_ssse3_rep", func, "/sysdeps/", "libc.so") || \
+ btp_frame_calls_func_in_file3(frame, "__" func "_sse42", func, "/sysdeps/", "libc.so") || \
+ btp_frame_calls_func_in_file3(frame, "__" func "_ia32", func, "/sysdeps", "libc.so")) \
{ \
strcpy(frame->function_name, func); \
}
@@ -81,8 +84,11 @@ btp_normalize_glibc_thread(struct btp_thread *thread)
NORMALIZE_ARCH_SPECIFIC("memchr");
NORMALIZE_ARCH_SPECIFIC("memcmp");
NORMALIZE_ARCH_SPECIFIC("memcpy");
+ NORMALIZE_ARCH_SPECIFIC("memmove");
NORMALIZE_ARCH_SPECIFIC("memset");
NORMALIZE_ARCH_SPECIFIC("rawmemchr");
+ NORMALIZE_ARCH_SPECIFIC("strcasecmp");
+ NORMALIZE_ARCH_SPECIFIC("strcasecmp_l");
NORMALIZE_ARCH_SPECIFIC("strcat");
NORMALIZE_ARCH_SPECIFIC("strchr");
NORMALIZE_ARCH_SPECIFIC("strchrnul");
@@ -91,9 +97,11 @@ btp_normalize_glibc_thread(struct btp_thread *thread)
NORMALIZE_ARCH_SPECIFIC("strcspn");
NORMALIZE_ARCH_SPECIFIC("strlen");
NORMALIZE_ARCH_SPECIFIC("strncmp");
+ NORMALIZE_ARCH_SPECIFIC("strncpy");
NORMALIZE_ARCH_SPECIFIC("strpbrk");
NORMALIZE_ARCH_SPECIFIC("strrchr");
NORMALIZE_ARCH_SPECIFIC("strspn");
+ NORMALIZE_ARCH_SPECIFIC("strstr");
NORMALIZE_ARCH_SPECIFIC("strtok");
/* Remove frames which are not a cause of the crash. */
@@ -106,7 +114,10 @@ btp_normalize_glibc_thread(struct btp_thread *thread)
btp_frame_calls_func(frame, "___vsnprintf_chk") ||
btp_frame_calls_func(frame, "__snprintf_chk") ||
btp_frame_calls_func(frame, "___snprintf_chk") ||
- btp_frame_calls_func(frame, "__vasprintf_chk");
+ btp_frame_calls_func(frame, "__vasprintf_chk") ||
+ btp_frame_calls_func_in_file(frame, "malloc_consolidate", "malloc.c") ||
+ btp_frame_calls_func_in_file(frame, "_int_malloc", "malloc.c") ||
+ btp_frame_calls_func_in_file(frame, "__libc_calloc", "malloc.c");
if (removable)
{
bool success = btp_thread_remove_frames_above(thread, frame);
diff --git a/src/btparser/thread.c b/src/btparser/thread.c
index af480eb3..05f154d6 100644
--- a/src/btparser/thread.c
+++ b/src/btparser/thread.c
@@ -293,7 +293,7 @@ btp_thread_parse(char **input,
return NULL;
}
- /* Skip spaces after the thread number and before the parenthesis. */
+ /* Skip spaces after the thread number and before parentheses. */
spaces = btp_skip_char_sequence(&local_input, ' ');
location->column += spaces;
if (0 == spaces)
@@ -303,31 +303,49 @@ btp_thread_parse(char **input,
return NULL;
}
- /* Read the Thread keyword in parenthesis, which is mandatory. */
+ /* Read the LWP section in parentheses, optional. */
+ location->column += btp_thread_skip_lwp(&local_input);
+
+ /* Read the Thread keyword in parentheses, optional. */
chars = btp_skip_string(&local_input, "(Thread ");
location->column += chars;
- if (0 == chars)
+ if (0 != chars)
{
- location->message = "Thread keyword in the parenthesis expected in the form '(Thread '.";
- btp_thread_free(imthread);
- return NULL;
- }
+ /* Read the thread identification number. It can be either in
+ * decimal or hexadecimal form.
+ * Examples:
+ * "Thread 10 (Thread 2476):"
+ * "Thread 8 (Thread 0xb07fdb70 (LWP 6357)):"
+ */
+ digits = btp_skip_hexadecimal_number(&local_input);
+ if (0 == digits)
+ digits = btp_skip_unsigned_integer(&local_input);
+ location->column += digits;
+ if (0 == digits)
+ {
+ location->message = "The thread identification number expected.";
+ btp_thread_free(imthread);
+ return NULL;
+ }
- /* Read the thread identification number. */
- digits = btp_skip_unsigned_integer(&local_input);
- location->column += digits;
- if (0 == digits)
- {
- location->message = "The thread identification number expected.";
- btp_thread_free(imthread);
- return NULL;
+ /* Handle the optional " (LWP [0-9]+)" section. */
+ location->column += btp_skip_char_sequence(&local_input, ' ');
+ location->column += btp_thread_skip_lwp(&local_input);
+
+ /* Read the end of the parenthesis. */
+ if (!btp_skip_char(&local_input, ')'))
+ {
+ location->message = "Closing parenthesis for Thread expected.";
+ btp_thread_free(imthread);
+ return NULL;
+ }
}
- /* Read the end of the parenthesis. */
- chars = btp_skip_string(&local_input, "):\n");
+ /* Read the end of the header line. */
+ chars = btp_skip_string(&local_input, ":\n");
if (0 == chars)
{
- location->message = "The end of the parenthesis expected in the form of '):\\n'.";
+ location->message = "Expected a colon followed by a newline ':\\n'.";
btp_thread_free(imthread);
return NULL;
}
@@ -362,3 +380,20 @@ btp_thread_parse(char **input,
*input = local_input;
return imthread;
}
+
+int
+btp_thread_skip_lwp(char **input)
+{
+ char *local_input = *input;
+ int count = btp_skip_string(&local_input, "(LWP ");
+ if (0 == count)
+ return 0;
+ int digits = btp_skip_unsigned_integer(&local_input);
+ if (0 == digits)
+ return 0;
+ count += digits;
+ if (!btp_skip_char(&local_input, ')'))
+ return 0;
+ *input = local_input;
+ return count + 1;
+}
diff --git a/src/btparser/thread.h b/src/btparser/thread.h
index f7287385..47a0211b 100644
--- a/src/btparser/thread.h
+++ b/src/btparser/thread.h
@@ -197,6 +197,17 @@ struct btp_thread *
btp_thread_parse(char **input,
struct btp_location *location);
+/**
+ * If the input contains a LWP section in form of "(LWP [0-9]+), move
+ * the input pointer after this section. Otherwise do not modify
+ * input.
+ * @returns
+ * The number of characters parsed from input. 0 if the input does not
+ * contain a LWP section.
+ */
+int
+btp_thread_skip_lwp(char **input);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/cli/CLI.cpp b/src/cli/CLI.cpp
index 44271329..5ece3171 100644
--- a/src/cli/CLI.cpp
+++ b/src/cli/CLI.cpp
@@ -19,30 +19,73 @@
# include <locale.h>
#endif
#include <getopt.h>
-#include "abrt_exception.h"
#include "abrtlib.h"
#include "abrt_dbus.h"
-#include "dbus_common.h"
#include "report.h"
-#include "dbus.h"
/** Creates a localized string from crash time. */
static char *localize_crash_time(const char *timestr)
{
long time = xatou(timestr);
char timeloc[256];
- int success = strftime(timeloc, 128, "%c", localtime(&time));
+ int success = strftime(timeloc, sizeof(timeloc), "%c", localtime(&time));
if (!success)
- error_msg_and_die("Error while converting time to string");
- return xasprintf("%s", timeloc);
+ error_msg_and_die("Error while converting time '%s' to string", timestr);
+ return xstrdup(timeloc);
+}
+
+static crash_data_t *FillCrashInfo(const char *dump_dir_name)
+{
+ int sv_logmode = logmode;
+ logmode = 0; /* suppress EPERM/EACCES errors in opendir */
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ DD_OPEN_READONLY);
+ logmode = sv_logmode;
+
+ if (!dd)
+ return NULL;
+
+ crash_data_t *crash_data = create_crash_data_from_dump_dir(dd);
+ dd_close(dd);
+ add_to_crash_data_ext(crash_data, CD_DUMPDIR, dump_dir_name, CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);
+
+ return crash_data;
+}
+
+static void GetCrashInfos(vector_of_crash_data_t *retval, const char *dir_name)
+{
+ VERB1 log("Loading dumps from '%s'", dir_name);
+
+ DIR *dir = opendir(dir_name);
+ if (dir != NULL)
+ {
+ struct dirent *dent;
+ while ((dent = readdir(dir)) != NULL)
+ {
+ if (dot_or_dotdot(dent->d_name))
+ continue; /* skip "." and ".." */
+
+ char *dump_dir_name = concat_path_file(dir_name, dent->d_name);
+
+ struct stat statbuf;
+ if (stat(dump_dir_name, &statbuf) == 0
+ && S_ISDIR(statbuf.st_mode)
+ ) {
+ crash_data_t *crash_data = FillCrashInfo(dump_dir_name);
+ if (crash_data)
+ g_ptr_array_add(retval, crash_data);
+ }
+ free(dump_dir_name);
+ }
+ closedir(dir);
+ }
}
/** Prints basic information about a crash to stdout. */
-static void print_crash(const map_crash_data_t &crash)
+static void print_crash(crash_data_t *crash_data)
{
/* Create a localized string from crash time. */
- const char *timestr = get_crash_data_item_content(crash, FILENAME_TIME).c_str();
- const char *timeloc = localize_crash_time(timestr);
+ const char *timestr = get_crash_item_content_or_die(crash_data, FILENAME_TIME);
+ char *timeloc = localize_crash_time(timestr);
printf(_("\tCrash dump : %s\n"
"\tUID : %s\n"
@@ -50,17 +93,18 @@ static void print_crash(const map_crash_data_t &crash)
"\tExecutable : %s\n"
"\tCrash Time : %s\n"
"\tCrash Count: %s\n"),
- get_crash_data_item_content(crash, CD_DUMPDIR).c_str(),
- get_crash_data_item_content(crash, FILENAME_UID).c_str(),
- get_crash_data_item_content(crash, FILENAME_PACKAGE).c_str(),
- get_crash_data_item_content(crash, FILENAME_EXECUTABLE).c_str(),
+ get_crash_item_content_or_NULL(crash_data, CD_DUMPDIR),
+ get_crash_item_content_or_NULL(crash_data, FILENAME_UID),
+ get_crash_item_content_or_NULL(crash_data, FILENAME_PACKAGE),
+ get_crash_item_content_or_NULL(crash_data, FILENAME_EXECUTABLE),
timeloc,
- get_crash_data_item_content(crash, FILENAME_COUNT).c_str());
+ get_crash_item_content_or_NULL(crash_data, FILENAME_COUNT)
+ );
- free((void *)timeloc);
+ free(timeloc);
/* Print the hostname if it's available. */
- const char *hostname = get_crash_data_item_content_or_NULL(crash, FILENAME_HOSTNAME);
+ const char *hostname = get_crash_item_content_or_NULL(crash_data, FILENAME_HOSTNAME);
if (hostname)
printf(_("\tHostname : %s\n"), hostname);
}
@@ -70,14 +114,14 @@ static void print_crash(const map_crash_data_t &crash)
* @param include_reported
* Do not skip entries marked as already reported.
*/
-static void print_crash_list(const vector_map_crash_data_t& crash_list, bool include_reported)
+static void print_crash_list(vector_of_crash_data_t *crash_list, bool include_reported)
{
- for (unsigned i = 0; i < crash_list.size(); ++i)
+ for (unsigned i = 0; i < crash_list->len; ++i)
{
- const map_crash_data_t& crash = crash_list[i];
+ crash_data_t *crash = get_crash_data(crash_list, i);
if (!include_reported)
{
- const char *msg = get_crash_data_item_content_or_NULL(crash, FILENAME_MESSAGE);
+ const char *msg = get_crash_item_content_or_NULL(crash, FILENAME_MESSAGE);
if (!msg || !msg[0])
continue;
}
@@ -90,10 +134,10 @@ static void print_crash_list(const vector_map_crash_data_t& crash_list, bool inc
/**
* Prints full information about a crash
*/
-static void print_crash_info(const map_crash_data_t& crash, bool show_backtrace)
+static void print_crash_info(crash_data_t *crash_data, bool show_backtrace)
{
- const char *timestr = get_crash_data_item_content(crash, FILENAME_TIME).c_str();
- const char *timeloc = localize_crash_time(timestr);
+ const char *timestr = get_crash_item_content_or_die(crash_data, FILENAME_TIME);
+ char *timeloc = localize_crash_time(timestr);
printf(_("Dump directory: %s\n"
"Last crash: %s\n"
@@ -104,91 +148,56 @@ static void print_crash_info(const map_crash_data_t& crash, bool show_backtrace)
"Executable: %s\n"
"System: %s, kernel %s\n"
"Reason: %s\n"),
- get_crash_data_item_content(crash, CD_DUMPDIR).c_str(),
+ get_crash_item_content_or_die(crash_data, CD_DUMPDIR),
timeloc,
- get_crash_data_item_content(crash, FILENAME_ANALYZER).c_str(),
- get_crash_data_item_content(crash, FILENAME_COMPONENT).c_str(),
- get_crash_data_item_content(crash, FILENAME_PACKAGE).c_str(),
- get_crash_data_item_content(crash, FILENAME_CMDLINE).c_str(),
- get_crash_data_item_content(crash, FILENAME_EXECUTABLE).c_str(),
- get_crash_data_item_content(crash, FILENAME_RELEASE).c_str(),
- get_crash_data_item_content(crash, FILENAME_KERNEL).c_str(),
- get_crash_data_item_content(crash, FILENAME_REASON).c_str());
-
- free((void *)timeloc);
+ get_crash_item_content_or_die(crash_data, FILENAME_ANALYZER),
+ get_crash_item_content_or_die(crash_data, FILENAME_COMPONENT),
+ get_crash_item_content_or_die(crash_data, FILENAME_PACKAGE),
+ get_crash_item_content_or_die(crash_data, FILENAME_CMDLINE),
+ get_crash_item_content_or_die(crash_data, FILENAME_EXECUTABLE),
+ get_crash_item_content_or_die(crash_data, FILENAME_OS_RELEASE),
+ get_crash_item_content_or_die(crash_data, FILENAME_KERNEL),
+ get_crash_item_content_or_die(crash_data, FILENAME_REASON)
+ );
+
+ free(timeloc);
/* Print optional fields only if they are available */
/* Coredump is not present in kerneloopses and Python exceptions. */
- const char *coredump = get_crash_data_item_content_or_NULL(crash, FILENAME_COREDUMP);
+ const char *coredump = get_crash_item_content_or_NULL(crash_data, FILENAME_COREDUMP);
if (coredump)
printf(_("Coredump file: %s\n"), coredump);
- const char *rating = get_crash_data_item_content_or_NULL(crash, FILENAME_RATING);
+ const char *rating = get_crash_item_content_or_NULL(crash_data, FILENAME_RATING);
if (rating)
printf(_("Rating: %s\n"), rating);
/* Crash function is not present in kerneloopses, and before the full report is created.*/
- const char *crash_function = get_crash_data_item_content_or_NULL(crash, FILENAME_CRASH_FUNCTION);
+ const char *crash_function = get_crash_item_content_or_NULL(crash_data, FILENAME_CRASH_FUNCTION);
if (crash_function)
printf(_("Crash function: %s\n"), crash_function);
- const char *hostname = get_crash_data_item_content_or_NULL(crash, FILENAME_HOSTNAME);
+ const char *hostname = get_crash_item_content_or_NULL(crash_data, FILENAME_HOSTNAME);
if (hostname)
printf(_("Hostname: %s\n"), hostname);
- const char *reproduce = get_crash_data_item_content_or_NULL(crash, FILENAME_REPRODUCE);
+ const char *reproduce = get_crash_item_content_or_NULL(crash_data, FILENAME_REPRODUCE);
if (reproduce)
printf(_("\nHow to reproduce:\n%s\n"), reproduce);
- const char *comment = get_crash_data_item_content_or_NULL(crash, FILENAME_COMMENT);
+ const char *comment = get_crash_item_content_or_NULL(crash_data, FILENAME_COMMENT);
if (comment)
printf(_("\nComment:\n%s\n"), comment);
if (show_backtrace)
{
- const char *backtrace = get_crash_data_item_content_or_NULL(crash, FILENAME_BACKTRACE);
+ const char *backtrace = get_crash_item_content_or_NULL(crash_data, FILENAME_BACKTRACE);
if (backtrace)
printf(_("\nBacktrace:\n%s\n"), backtrace);
}
}
-/**
- * Converts crash reference from user's input to crash dump dir name.
- * The returned string must be released by caller.
- */
-static char *guess_crash_id(const char *str)
-{
- vector_map_crash_data_t ci = call_GetCrashInfos();
- unsigned num_crashinfos = ci.size();
- if (str[0] == '@') /* "--report @N" syntax */
- {
- unsigned position = xatoi_u(str + 1);
- if (position >= num_crashinfos)
- error_msg_and_die("There are only %u crash infos", num_crashinfos);
- map_crash_data_t& info = ci[position];
- return xstrdup(get_crash_data_item_content(info, CD_DUMPDIR).c_str());
- }
-
- unsigned len = strlen(str);
- unsigned ii;
- char *result = NULL;
- for (ii = 0; ii < num_crashinfos; ii++)
- {
- map_crash_data_t& info = ci[ii];
- const char *this_dir = get_crash_data_item_content(info, CD_DUMPDIR).c_str();
- if (strncmp(str, this_dir, len) == 0)
- {
- if (result)
- error_msg_and_die("Crash prefix '%s' is not unique", str);
- result = xstrdup(this_dir);
- }
- }
- if (!result)
- error_msg_and_die("Crash dump directory '%s' not found", str);
- return result;
-}
-
/* Program options */
enum
{
@@ -210,6 +219,7 @@ static const struct option longopts[] =
{
/* name, has_arg, flag, val */
{ "help" , no_argument, NULL, '?' },
+ { "verbose" , no_argument, NULL, 'v' },
{ "version" , no_argument, NULL, 'V' },
{ "list" , no_argument, NULL, 'l' },
{ "full" , no_argument, NULL, 'f' },
@@ -234,36 +244,42 @@ static const char *progname(const char *argv0)
* Prints abrt-cli version and some help text.
* Then exits the program with return value 1.
*/
-static void usage(char *argv0)
+static void print_usage_and_die(char *argv0)
{
const char *name = progname(argv0);
printf("%s "VERSION"\n\n", name);
/* Message has embedded tabs. */
- printf(_("Usage: %s [OPTION]\n\n"
- "Startup:\n"
- " -V, --version display the version of %s and exit\n"
- " -?, --help print this help\n\n"
- "Actions:\n"
- " -l, --list print a list of all crashes which are not yet reported\n"
- " -f, --full print a list of all crashes, including the already reported ones\n"
- " -r, --report CRASH_ID create and send a report\n"
- " -y, --always create and send a report without asking\n"
- " -d, --delete CRASH_ID remove a crash\n"
- " -i, --info CRASH_ID print detailed information about a crash\n"
- " -b, --backtrace print detailed information about a crash including backtrace\n"
- "CRASH_ID can be:\n"
- " a name of dump directory, or\n"
- " @N - N'th crash (as displayed by --list --full) will be acted upon\n"
+ printf(_(
+ "Usage: %s -l[f] [-D BASE_DIR]...]\n"
+ " or: %s -r[y] CRASH_DIR\n"
+ " or: %s -i[b] CRASH_DIR\n"
+ " or: %s -d CRASH_DIR\n"
+ "\n"
+ " -l, --list List not yet reported crashes\n"
+ " -f, --full List all crashes\n"
+ " -D BASE_DIR Directory to list crashes from\n"
+ " (default: -D $HOME/.abrt/spool -D %s)\n"
+ "\n"
+ " -r, --report Send a report about CRASH_DIR\n"
+ " -y, --always ...without editing and asking\n"
+ " -i, --info Print detailed information about CRASH_DIR\n"
+ " -b, --backtrace ...including backtrace\n"
+ " -d, --delete Remove CRASH_DIR\n"
+ "\n"
+ " -V, --version Display version and exit\n"
+ " -v, --verbose Be verbose\n"
),
- name, name);
-
+ name, name, name, name,
+ DEBUG_DUMPS_DIR
+ );
exit(1);
}
int main(int argc, char** argv)
{
- const char* crash_id = NULL;
+ GList *D_list = NULL;
+ char *dump_dir_name = NULL;
int op = -1;
bool full = false;
bool always = false;
@@ -278,19 +294,18 @@ int main(int argc, char** argv)
while (1)
{
/* Do not use colons, arguments are handled after parsing all options. */
- int c = getopt_long_only(argc, argv, "?Vrdlfyib",
- longopts, NULL);
+ int c = getopt_long(argc, argv, "?Vvrdlfyib", longopts, NULL);
-#define SET_OP(newop) \
- if (op != -1 && op != newop) \
- { \
- error_msg(_("You must specify exactly one operation")); \
- return 1; \
- } \
- op = newop;
+#define SET_OP(newop) \
+ do { \
+ if (op != -1 && op != newop) \
+ error_msg_and_die(_("You must specify exactly one operation")); \
+ op = newop; \
+ } while (0)
switch (c)
{
+ case -1: goto end_of_arg_parsing;
case 'r': SET_OP(OPT_REPORT); break;
case 'd': SET_OP(OPT_DELETE); break;
case 'l': SET_OP(OPT_GET_LIST); break;
@@ -298,34 +313,44 @@ int main(int argc, char** argv)
case 'f': full = true; break;
case 'y': always = true; break;
case 'b': backtrace = true; break;
- case -1: /* end of options */ break;
- default: /* some error */
- case '?':
- usage(argv[0]); /* exits app */
+ case 'v': g_verbose++; break;
+ case 'D':
+ D_list = g_list_append(D_list, optarg);
+ break;
case 'V':
printf("%s "VERSION"\n", progname(argv[0]));
return 0;
+ case '?':
+ default: /* some error */
+ print_usage_and_die(argv[0]); /* exits app */
}
#undef SET_OP
- if (c == -1)
- break;
+ }
+ end_of_arg_parsing: ;
+
+ if (!D_list)
+ {
+ char *home = getenv("HOME");
+ if (home)
+ D_list = g_list_append(D_list, concat_path_file(home, ".abrt/spool"));
+ D_list = g_list_append(D_list, (void*)DEBUG_DUMPS_DIR);
}
/* Handle option arguments. */
- int arg_count = argc - optind;
- switch (arg_count)
+ argc -= optind;
+ switch (argc)
{
case 0:
if (op == OPT_REPORT || op == OPT_DELETE || op == OPT_INFO)
- usage(argv[0]);
+ print_usage_and_die(argv[0]);
break;
case 1:
if (op != OPT_REPORT && op != OPT_DELETE && op != OPT_INFO)
- usage(argv[0]);
- crash_id = argv[optind];
+ print_usage_and_die(argv[0]);
+ dump_dir_name = argv[optind];
break;
default:
- usage(argv[0]);
+ print_usage_and_die(argv[0]);
}
/* Check if we have an operation.
@@ -336,89 +361,72 @@ int main(int argc, char** argv)
(backtrace && op != OPT_INFO) ||
op == -1)
{
- usage(argv[0]);
- return 1;
+ print_usage_and_die(argv[0]);
}
- DBusError err;
- dbus_error_init(&err);
- s_dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
- handle_dbus_err(s_dbus_conn == NULL, &err);
-
/* Do the selected operation. */
int exitcode = 0;
switch (op)
{
case OPT_GET_LIST:
{
- vector_map_crash_data_t ci = call_GetCrashInfos();
+ vector_of_crash_data_t *ci = new_vector_of_crash_data();
+ while (D_list)
+ {
+ char *dir = (char *)D_list->data;
+ GetCrashInfos(ci, dir);
+ D_list = g_list_remove(D_list, dir);
+ }
print_crash_list(ci, full);
+ free_vector_of_crash_data(ci);
break;
}
case OPT_REPORT:
{
- int flags = CLI_REPORT_SILENT_IF_NOT_FOUND;
- if (always)
- flags |= CLI_REPORT_BATCH;
- exitcode = report(crash_id, flags);
- if (exitcode == -1) /* no such crash_id */
+ struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY);
+ if (!dd)
+ break;
+ int readonly = !dd->locked;
+ dd_close(dd);
+ if (readonly)
{
- crash_id = guess_crash_id(crash_id);
- exitcode = report(crash_id, always ? CLI_REPORT_BATCH : 0);
- if (exitcode == -1)
+ log("'%s' is not writable", dump_dir_name);
+ /* D_list can't be NULL here */
+ struct dump_dir *dd_copy = steal_directory((char *)D_list->data, dump_dir_name);
+ if (dd_copy)
{
- error_msg("Crash '%s' not found", crash_id);
- free((void *)crash_id);
- xfunc_die();
+ delete_dump_dir_possibly_using_abrtd(dump_dir_name);
+ dump_dir_name = xstrdup(dd_copy->dd_dirname);
+ dd_close(dd_copy);
}
-
- free((void *)crash_id);
}
+
+ exitcode = report(dump_dir_name, (always ? CLI_REPORT_BATCH : 0));
+ if (exitcode == -1)
+ error_msg_and_die("Crash '%s' not found", dump_dir_name);
break;
}
case OPT_DELETE:
{
- exitcode = call_DeleteDebugDump(crash_id);
- if (exitcode == ENOENT)
- {
- crash_id = guess_crash_id(crash_id);
- exitcode = call_DeleteDebugDump(crash_id);
- if (exitcode == ENOENT)
- {
- error_msg("Crash '%s' not found", crash_id);
- free((void *)crash_id);
- xfunc_die();
- }
-
- free((void *)crash_id);
- }
- if (exitcode != 0)
- error_msg_and_die("Can't delete debug dump '%s'", crash_id);
+ exitcode = delete_dump_dir_possibly_using_abrtd(dump_dir_name);
break;
}
case OPT_INFO:
{
- int old_logmode = logmode;
- logmode = 0;
-
- map_crash_data_t crashData = call_CreateReport(crash_id);
- if (crashData.empty()) /* no such crash_id */
- {
- crash_id = guess_crash_id(crash_id);
- crashData = call_CreateReport(crash_id);
- if (crashData.empty())
- {
- error_msg("Crash '%s' not found", crash_id);
- free((void *)crash_id);
- xfunc_die();
- }
-
- free((void *)crash_id);
- }
-
- logmode = old_logmode;
-
- print_crash_info(crashData, backtrace);
+ if (run_analyze_event(dump_dir_name) != 0)
+ return 1;
+
+ /* Load crash_data from (possibly updated by analyze) dump dir */
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ return -1;
+ crash_data_t *crash_data = create_crash_data_from_dump_dir(dd);
+ dd_close(dd);
+ add_to_crash_data_ext(crash_data, CD_DUMPDIR, dump_dir_name,
+ CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);
+
+ print_crash_info(crash_data, backtrace);
+ free_crash_data(crash_data);
break;
}
diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am
index 3584fd6c..0a5c6e1a 100644
--- a/src/cli/Makefile.am
+++ b/src/cli/Makefile.am
@@ -3,20 +3,20 @@ bin_PROGRAMS = abrt-cli
abrt_cli_SOURCES = \
CLI.cpp \
run-command.h run-command.c \
- report.h report.cpp \
- dbus.h dbus.cpp
-
+ report.h report.cpp
abrt_cli_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DVAR_RUN=\"$(VAR_RUN)\" \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
$(ENABLE_SOCKET_OR_DBUS) \
$(DBUS_CFLAGS) $(GLIB_CFLAGS) \
- -D_GNU_SOURCE
+ -D_GNU_SOURCE \
+ -Wall -Werror
# $(GTK_CFLAGS)
-
abrt_cli_LDADD = \
- ../lib/libabrt.la \
+ ../lib/libreport.la \
../lib/libabrt_dbus.la \
$(GLIB_LIBS)
diff --git a/src/cli/dbus.cpp b/src/cli/dbus.cpp
deleted file mode 100644
index 7565d5bc..00000000
--- a/src/cli/dbus.cpp
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include "dbus.h"
-#include "dbus_common.h"
-
-DBusConnection* s_dbus_conn;
-
-/*
- * DBus member calls
- */
-
-/* helpers */
-static DBusMessage* new_call_msg(const char* method)
-{
- DBusMessage* msg = dbus_message_new_method_call(ABRTD_DBUS_NAME, ABRTD_DBUS_PATH, ABRTD_DBUS_IFACE, method);
- if (!msg)
- die_out_of_memory();
- return msg;
-}
-
-static DBusMessage* send_get_reply_and_unref(DBusMessage* msg)
-{
- dbus_uint32_t serial;
- if (TRUE != dbus_connection_send(s_dbus_conn, msg, &serial))
- error_msg_and_die("Error sending DBus message");
- dbus_message_unref(msg);
-
- while (true)
- {
- DBusMessage *received = dbus_connection_pop_message(s_dbus_conn);
- if (!received)
- {
- if (FALSE == dbus_connection_read_write(s_dbus_conn, -1))
- error_msg_and_die("dbus connection closed");
- continue;
- }
-
- int tp = dbus_message_get_type(received);
- const char *error_str = dbus_message_get_error_name(received);
-#if 0
- /* Debugging */
- printf("type:%u (CALL:%u, RETURN:%u, ERROR:%u, SIGNAL:%u)\n", tp,
- DBUS_MESSAGE_TYPE_METHOD_CALL,
- DBUS_MESSAGE_TYPE_METHOD_RETURN,
- DBUS_MESSAGE_TYPE_ERROR,
- DBUS_MESSAGE_TYPE_SIGNAL
- );
- const char *sender = dbus_message_get_sender(received);
- if (sender)
- printf("sender: %s\n", sender);
- const char *path = dbus_message_get_path(received);
- if (path)
- printf("path: %s\n", path);
- const char *member = dbus_message_get_member(received);
- if (member)
- printf("member: %s\n", member);
- const char *interface = dbus_message_get_interface(received);
- if (interface)
- printf("interface: %s\n", interface);
- const char *destination = dbus_message_get_destination(received);
- if (destination)
- printf("destination: %s\n", destination);
- if (error_str)
- printf("error: '%s'\n", error_str);
-#endif
-
- DBusError err;
- dbus_error_init(&err);
-
- if (dbus_message_is_signal(received, ABRTD_DBUS_IFACE, "Update"))
- {
- const char *update_msg;
- if (!dbus_message_get_args(received, &err,
- DBUS_TYPE_STRING, &update_msg,
- DBUS_TYPE_INVALID))
- {
- error_msg_and_die("dbus Update message: arguments mismatch");
- }
- printf(">> %s\n", update_msg);
- }
- else if (dbus_message_is_signal(received, ABRTD_DBUS_IFACE, "Warning"))
- {
- const char *warning_msg;
- if (!dbus_message_get_args(received, &err,
- DBUS_TYPE_STRING, &warning_msg,
- DBUS_TYPE_INVALID))
- {
- error_msg_and_die("dbus Warning message: arguments mismatch");
- }
- log(">! %s\n", warning_msg);
- }
- else
- if (tp == DBUS_MESSAGE_TYPE_METHOD_RETURN
- && dbus_message_get_reply_serial(received) == serial
- ) {
- return received;
- }
- else
- if (tp == DBUS_MESSAGE_TYPE_ERROR
- && dbus_message_get_reply_serial(received) == serial
- ) {
- error_msg_and_die("dbus call returned error: '%s'", error_str);
- }
-
- dbus_message_unref(received);
- }
-}
-
-vector_map_crash_data_t call_GetCrashInfos()
-{
- DBusMessage* msg = new_call_msg(__func__ + 5);
- DBusMessage *reply = send_get_reply_and_unref(msg);
-
- DBusMessageIter in_iter;
- dbus_message_iter_init(reply, &in_iter);
-
- vector_map_crash_data_t argout;
- int r = load_val(&in_iter, argout);
- if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */
- error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5);
-
- dbus_message_unref(reply);
- return argout;
-}
-
-map_crash_data_t call_CreateReport(const char* crash_id)
-{
- DBusMessage* msg = new_call_msg(__func__ + 5);
- dbus_message_append_args(msg,
- DBUS_TYPE_STRING, &crash_id,
- DBUS_TYPE_INVALID);
-
- DBusMessage *reply = send_get_reply_and_unref(msg);
-
- DBusMessageIter in_iter;
- dbus_message_iter_init(reply, &in_iter);
-
- map_crash_data_t argout;
- int r = load_val(&in_iter, argout);
- if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */
- error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5);
-
- dbus_message_unref(reply);
- return argout;
-}
-
-report_status_t call_Report(const map_crash_data_t& report,
- const vector_string_t& reporters,
- const map_map_string_t &plugins)
-{
- DBusMessage* msg = new_call_msg(__func__ + 5);
- DBusMessageIter out_iter;
- dbus_message_iter_init_append(msg, &out_iter);
-
- /* parameter #1: report data */
- store_val(&out_iter, report);
- /* parameter #2: reporters to use */
- store_val(&out_iter, reporters);
- /* parameter #3 (opt): plugin config */
- if (!plugins.empty())
- store_val(&out_iter, plugins);
-
- DBusMessage *reply = send_get_reply_and_unref(msg);
-
- DBusMessageIter in_iter;
- dbus_message_iter_init(reply, &in_iter);
-
- report_status_t result;
- int r = load_val(&in_iter, result);
- if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */
- error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5);
-
- dbus_message_unref(reply);
- return result;
-}
-
-int32_t call_DeleteDebugDump(const char* crash_id)
-{
- DBusMessage* msg = new_call_msg(__func__ + 5);
- dbus_message_append_args(msg,
- DBUS_TYPE_STRING, &crash_id,
- DBUS_TYPE_INVALID);
-
- DBusMessage *reply = send_get_reply_and_unref(msg);
-
- DBusMessageIter in_iter;
- dbus_message_iter_init(reply, &in_iter);
-
- int32_t result;
- int r = load_val(&in_iter, result);
- if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */
- error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5);
-
- dbus_message_unref(reply);
- return result;
-}
-
-map_map_string_t call_GetPluginsInfo()
-{
- DBusMessage *msg = new_call_msg(__func__ + 5);
- DBusMessage *reply = send_get_reply_and_unref(msg);
-
- DBusMessageIter in_iter;
- dbus_message_iter_init(reply, &in_iter);
-
- map_map_string_t argout;
- int r = load_val(&in_iter, argout);
- if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */
- error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5);
-
- dbus_message_unref(reply);
- return argout;
-}
-
-map_plugin_settings_t call_GetPluginSettings(const char *name)
-{
- DBusMessage *msg = new_call_msg(__func__ + 5);
- dbus_message_append_args(msg,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID);
-
- DBusMessage *reply = send_get_reply_and_unref(msg);
-
- DBusMessageIter in_iter;
- dbus_message_iter_init(reply, &in_iter);
-
- map_string_t argout;
- int r = load_val(&in_iter, argout);
- if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */
- error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5);
-
- dbus_message_unref(reply);
- return argout;
-}
-
-map_map_string_t call_GetSettings()
-{
- DBusMessage *msg = new_call_msg(__func__ + 5);
- DBusMessage *reply = send_get_reply_and_unref(msg);
-
- DBusMessageIter in_iter;
- dbus_message_iter_init(reply, &in_iter);
- map_map_string_t argout;
- int r = load_val(&in_iter, argout);
- if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */
- error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5);
-
- dbus_message_unref(reply);
- return argout;
-}
-
-void handle_dbus_err(bool error_flag, DBusError *err)
-{
- if (dbus_error_is_set(err))
- {
- error_msg("dbus error: %s", err->message);
- /* dbus_error_free(&err); */
- error_flag = true;
- }
- if (!error_flag)
- return;
- error_msg_and_die(
- "error requesting DBus name %s, possible reasons: "
- "abrt run by non-root; dbus config is incorrect; "
- "or dbus daemon needs to be restarted to reload dbus config",
- ABRTD_DBUS_NAME);
-}
diff --git a/src/cli/dbus.h b/src/cli/dbus.h
deleted file mode 100644
index 9c99c662..00000000
--- a/src/cli/dbus.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef ABRT_CLI_DBUS_H
-#define ABRT_CLI_DBUS_H
-
-#include "abrt_dbus.h"
-#include "abrt_crash_dump.h"
-
-extern DBusConnection* s_dbus_conn;
-
-vector_map_crash_data_t call_GetCrashInfos();
-
-map_crash_data_t call_CreateReport(const char *crash_id);
-
-/** Sends report using enabled Reporter plugins.
- * @param report
- * The report sent to Reporter plugins.
- * @param reporters
- * List of names of Reporters which should be called.
- * @param plugins
- * Optional settings for Reporter plugins, can be empty.
- * Format: plugins["PluginName"]["SettingsKey"] = "SettingsValue"
- * If it contains settings for some plugin, it must contain _all fields_
- * obtained by call_GetPluginSettings, otherwise the plugin might ignore
- * the settings.
- */
-report_status_t call_Report(const map_crash_data_t& report,
- const vector_string_t& reporters,
- const map_map_string_t &plugins);
-
-int32_t call_DeleteDebugDump(const char* crash_id);
-
-/* Gets basic data about all installed plugins.
- * @todo
- * Return more semantically structured output - maybe a struct instead of a map.
- */
-map_map_string_t call_GetPluginsInfo();
-
-/** Gets default plugin settings.
- * @param name
- * Corresponds to name obtained from call_GetPluginsInfo.
- */
-map_plugin_settings_t call_GetPluginSettings(const char *name);
-
-/** Gets global daemon settings.
- * @todo
- * Return more semantically structured output - maybe a struct instead of a map.
- */
-map_map_string_t call_GetSettings();
-
-void handle_dbus_err(bool error_flag, DBusError *err);
-
-#endif
diff --git a/src/cli/report.cpp b/src/cli/report.cpp
index 78a38916..6b2bf2e2 100644
--- a/src/cli/report.cpp
+++ b/src/cli/report.cpp
@@ -15,11 +15,8 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include <termios.h>
-#include <glib.h>
#include "report.h"
#include "run-command.h"
-#include "dbus.h"
#include "abrtlib.h"
/* Field separator for the crash report file that is edited by user. */
@@ -35,13 +32,10 @@ static char *trim(char *str)
return NULL;
// Remove leading spaces.
- char *ibuf;
- ibuf = skip_whitespace(str);
- int i = strlen(ibuf);
- if (str != ibuf)
- memmove(str, ibuf, i + 1);
+ overlapping_strcpy(str, skip_whitespace(str));
// Remove trailing spaces.
+ int i = strlen(str);
while (--i >= 0)
{
if (!isspace(str[i]))
@@ -142,30 +136,24 @@ static void remove_comments_and_unescape(char *str)
* Writes a field of crash report to a file.
* Field must be writable.
*/
-static void write_crash_report_field(FILE *fp, const map_crash_data_t &report,
+static void write_crash_report_field(FILE *fp, crash_data_t *crash_data,
const char *field, const char *description)
{
- const map_crash_data_t::const_iterator it = report.find(field);
- if (it == report.end())
+ const struct crash_item *value = get_crash_data_item_or_NULL(crash_data, field);
+ if (!value)
{
// exit silently, all fields are optional for now
//error_msg("Field %s not found", field);
return;
}
- if (it->second[CD_TYPE] == CD_SYS)
- {
- error_msg("Cannot update field %s because it is a system value", field);
- return;
- }
-
- fprintf(fp, "%s%s\n", FIELD_SEP, it->first.c_str());
+ fprintf(fp, "%s%s\n", FIELD_SEP, field);
fprintf(fp, "%s\n", description);
- if (it->second[CD_EDITABLE] != CD_ISEDITABLE)
+ if (!(value->flags & CD_FLAG_ISEDITABLE))
fprintf(fp, _("# This field is read only\n"));
- char *escaped_content = escape(it->second[CD_CONTENT].c_str());
+ char *escaped_content = escape(value->content);
fprintf(fp, "%s\n", escaped_content);
free(escaped_content);
}
@@ -177,7 +165,7 @@ static void write_crash_report_field(FILE *fp, const map_crash_data_t &report,
* If the report is successfully stored to the file, a zero value is returned.
* On failure, nonzero value is returned.
*/
-static void write_crash_report(const map_crash_data_t &report, FILE *fp)
+static void write_crash_report(crash_data_t *report, FILE *fp)
{
fprintf(fp, "# Please check this report. Lines starting with '#' will be ignored.\n"
"# Lines starting with '%%----' separate fields, please do not delete them.\n\n");
@@ -197,7 +185,7 @@ static void write_crash_report(const map_crash_data_t &report, FILE *fp)
write_crash_report_field(fp, report, FILENAME_KERNEL, _("# Kernel version"));
write_crash_report_field(fp, report, FILENAME_PACKAGE, _("# Package"));
write_crash_report_field(fp, report, FILENAME_REASON, _("# Reason of crash"));
- write_crash_report_field(fp, report, FILENAME_RELEASE, _("# Release string of the operating system"));
+ write_crash_report_field(fp, report, FILENAME_OS_RELEASE, _("# Release string of the operating system"));
}
/*
@@ -208,7 +196,7 @@ static void write_crash_report(const map_crash_data_t &report, FILE *fp)
* 1 if the field was changed.
* Changes to read-only fields are ignored.
*/
-static int read_crash_report_field(const char *text, map_crash_data_t &report,
+static int read_crash_report_field(const char *text, crash_data_t *report,
const char *field)
{
char separator[sizeof("\n" FIELD_SEP)-1 + strlen(field) + 2]; // 2 = '\n\0'
@@ -225,21 +213,15 @@ static int read_crash_report_field(const char *text, map_crash_data_t &report,
else
length = end - textfield;
- const map_crash_data_t::iterator it = report.find(field);
- if (it == report.end())
+ struct crash_item *value = get_crash_data_item_or_NULL(report, field);
+ if (!value)
{
error_msg("Field %s not found", field);
return 0;
}
- if (it->second[CD_TYPE] == CD_SYS)
- {
- error_msg("Cannot update field %s because it is a system value", field);
- return 0;
- }
-
// Do not change noneditable fields.
- if (it->second[CD_EDITABLE] != CD_ISEDITABLE)
+ if (!(value->flags & CD_FLAG_ISEDITABLE))
return 0;
// Compare the old field contents with the new field contents.
@@ -248,16 +230,16 @@ static int read_crash_report_field(const char *text, map_crash_data_t &report,
newvalue[length] = '\0';
trim(newvalue);
- char oldvalue[it->second[CD_CONTENT].length() + 1];
- strcpy(oldvalue, it->second[CD_CONTENT].c_str());
+ char oldvalue[strlen(value->content) + 1];
+ strcpy(oldvalue, value->content);
trim(oldvalue);
// Return if no change in the contents detected.
- int cmp = strcmp(newvalue, oldvalue);
- if (!cmp)
+ if (strcmp(newvalue, oldvalue) == 0)
return 0;
- it->second[CD_CONTENT].assign(newvalue);
+ free(value->content);
+ value->content = xstrdup(newvalue);
return 1;
}
@@ -269,7 +251,7 @@ static int read_crash_report_field(const char *text, map_crash_data_t &report,
* 1 if any field was changed.
* Changes to read-only fields are ignored.
*/
-static int read_crash_report(map_crash_data_t &report, const char *text)
+static int read_crash_report(crash_data_t *report, const char *text)
{
int result = 0;
result |= read_crash_report_field(text, report, FILENAME_COMMENT);
@@ -284,7 +266,7 @@ static int read_crash_report(map_crash_data_t &report, const char *text)
result |= read_crash_report_field(text, report, FILENAME_KERNEL);
result |= read_crash_report_field(text, report, FILENAME_PACKAGE);
result |= read_crash_report_field(text, report, FILENAME_REASON);
- result |= read_crash_report_field(text, report, FILENAME_RELEASE);
+ result |= read_crash_report_field(text, report, FILENAME_OS_RELEASE);
return result;
}
@@ -292,13 +274,13 @@ static int read_crash_report(map_crash_data_t &report, const char *text)
* Ensures that the fields needed for editor are present in the crash data.
* Fields: comments, how to reproduce.
*/
-static void create_fields_for_editor(map_crash_data_t &crash_data)
+static void create_fields_for_editor(crash_data_t *crash_data)
{
- if (crash_data.find(FILENAME_COMMENT) == crash_data.end())
- add_to_crash_data_ext(crash_data, FILENAME_COMMENT, CD_TXT, CD_ISEDITABLE, "");
+ if (!get_crash_data_item_or_NULL(crash_data, FILENAME_COMMENT))
+ add_to_crash_data_ext(crash_data, FILENAME_COMMENT, "", CD_FLAG_TXT + CD_FLAG_ISEDITABLE);
- if (crash_data.find(FILENAME_REPRODUCE) == crash_data.end())
- add_to_crash_data_ext(crash_data, FILENAME_REPRODUCE, CD_TXT, CD_ISEDITABLE, "1. \n2. \n3. \n");
+ if (!get_crash_data_item_or_NULL(crash_data, FILENAME_REPRODUCE))
+ add_to_crash_data_ext(crash_data, FILENAME_REPRODUCE, "1. \n2. \n3. \n", CD_FLAG_TXT + CD_FLAG_ISEDITABLE);
}
/**
@@ -342,7 +324,7 @@ static int launch_editor(const char *path)
* 2 on failure, unable to create, open, or close temporary file
* 3 on failure, cannot launch text editor
*/
-static int run_report_editor(map_crash_data_t &cr)
+static int run_report_editor(crash_data_t *crash_data)
{
/* Open a temporary file and write the crash report to it. */
char filename[] = "/tmp/abrt-report.XXXXXX";
@@ -356,15 +338,14 @@ static int run_report_editor(map_crash_data_t &cr)
FILE *fp = fdopen(fd, "w");
if (!fp) /* errno is set */
{
- perror_msg("can't open '%s' to save the crash report", filename);
- return 2;
+ die_out_of_memory();
}
- write_crash_report(cr, fp);
+ write_crash_report(crash_data, fp);
if (fclose(fp)) /* errno is set */
{
- perror_msg("can't close '%s'", filename);
+ perror_msg("can't write '%s'", filename);
return 2;
}
@@ -381,21 +362,18 @@ static int run_report_editor(map_crash_data_t &cr)
}
fseek(fp, 0, SEEK_END);
- long size = ftell(fp);
+ unsigned long size = ftell(fp);
fseek(fp, 0, SEEK_SET);
char *text = (char*)xmalloc(size + 1);
if (fread(text, 1, size, fp) != size)
{
error_msg("can't read '%s'", filename);
+ fclose(fp);
return 2;
}
text[size] = '\0';
- if (fclose(fp) != 0) /* errno is set */
- {
- perror_msg("can't close '%s'", filename);
- return 2;
- }
+ fclose(fp);
// Delete the tempfile.
if (unlink(filename) == -1) /* errno is set */
@@ -405,7 +383,7 @@ static int run_report_editor(map_crash_data_t &cr)
remove_comments_and_unescape(text);
// Updates the crash report from the file text.
- int report_changed = read_crash_report(cr, text);
+ int report_changed = read_crash_report(crash_data, text);
free(text);
if (report_changed)
puts(_("\nThe report has been updated"));
@@ -435,25 +413,6 @@ static void read_from_stdin(const char *question, char *result, int result_size)
strchrnul(result, '\n')[0] = '\0';
}
-/** Splits a string into substrings using chosen delimiters.
- * @param delim
- * Specifies a set of characters that delimit the
- * tokens in the parsed string
- */
-static GList *split(const char *s, const char delim)
-{
- GList *elems = NULL;
- while (1)
- {
- const char *end = strchrnul(s, delim);
- elems = g_list_append(elems, xstrndup(s, end - s));
- if (*end == '\0')
- break;
- s = end + 1;
- }
- return elems;
-}
-
/**
* Asks a [y/n] question on stdin/stdout.
* Returns true if the answer is yes, false otherwise.
@@ -467,97 +426,128 @@ static bool ask_yesno(const char *question)
fflush(NULL);
char answer[16];
- fgets(answer, sizeof(answer), stdin);
+ if (!fgets(answer, sizeof(answer), stdin))
+ return false;
/* Use strncmp here because the answer might contain a newline as
the last char. */
return 0 == strncmp(answer, yes, strlen(yes));
}
/* Returns true if echo has been changed from another state. */
-static bool set_echo(bool enabled)
+static bool set_echo(bool enable)
{
- if (isatty(STDIN_FILENO) == 0)
- {
- /* Clean errno, which is set by isatty. */
- errno = 0;
- return false;
- }
-
struct termios t;
if (tcgetattr(STDIN_FILENO, &t) < 0)
return false;
- /* No change needed. */
- if ((bool)(t.c_lflag & ECHO) == enabled)
+ /* No change needed? */
+ if ((bool)(t.c_lflag & ECHO) == enable)
return false;
- if (enabled)
- t.c_lflag |= ECHO;
- else
- t.c_lflag &= ~ECHO;
-
+ t.c_lflag ^= ECHO;
if (tcsetattr(STDIN_FILENO, TCSANOW, &t) < 0)
perror_msg_and_die("tcsetattr");
return true;
}
+
/**
* Gets reporter plugin settings.
- * @param reporters
- * List of reporter names. Settings of these reporters are handled.
- * @param settings
+ * @return settings
* A structure filled with reporter plugin settings.
+ * It's GHashTable<char *, map_plugin_t *> and must be passed to
+ * g_hash_table_destroy();
*/
-static void get_reporter_plugin_settings(const vector_string_t& reporters,
- map_map_string_t &settings)
+static void get_plugin_system_settings(GHashTable *settings)
{
- /* First of all, load system-wide report plugin settings. */
- for (vector_string_t::const_iterator it = reporters.begin(); it != reporters.end(); ++it)
+ DIR *dir = opendir(PLUGINS_CONF_DIR);
+ if (!dir)
+ return;
+
+ struct dirent *dent;
+ while ((dent = readdir(dir)) != NULL)
{
- map_string_t single_plugin_settings = call_GetPluginSettings(it->c_str());
- // Copy the received settings as defaults.
- // Plugins won't work without it, if some value is missing
- // they use their default values for all fields.
- settings[it->c_str()] = single_plugin_settings;
+ char *ext = strrchr(dent->d_name, '.');
+ if (!ext || strcmp(ext + 1, "conf") != 0)
+ continue;
+ if (!is_regular_file(dent, PLUGINS_CONF_DIR))
+ continue;
+ VERB3 log("Found %s", dent->d_name);
+
+ char *conf_file = concat_path_file(PLUGINS_CONF_DIR, dent->d_name);
+ map_string_h *single_plugin_settings = new_map_string();
+ if (load_conf_file(conf_file, single_plugin_settings, /*skip w/o value:*/ false))
+ VERB3 log("Loaded %s", dent->d_name);
+ free(conf_file);
+
+ *ext = '\0';
+ g_hash_table_replace(settings, xstrdup(dent->d_name), single_plugin_settings);
}
+ closedir(dir);
+}
+
+static GHashTable *get_plugin_settings(void)
+{
+ /* First of all, load system-wide plugin settings. */
+ GHashTable *settings = g_hash_table_new_full(
+ g_str_hash, g_str_equal,
+ free, (void (*)(void*))free_map_string
+ );
+
+ get_plugin_system_settings(settings);
/* Second, load user-specific settings, which override
- the system-wide settings. */
+ * the system-wide settings. */
struct passwd* pw = getpwuid(geteuid());
const char* homedir = pw ? pw->pw_dir : NULL;
if (homedir)
{
- map_map_string_t::const_iterator itend = settings.end();
- for (map_map_string_t::iterator it = settings.begin(); it != itend; ++it)
+ GHashTableIter iter;
+ char *plugin_name;
+ map_string_h *plugin_settings;
+ g_hash_table_iter_init(&iter, settings);
+ while (g_hash_table_iter_next(&iter, (void**)&plugin_name, (void**)&plugin_settings))
{
- map_string_t single_plugin_settings;
- std::string path = std::string(homedir) + "/.abrt/"
- + it->first + ".conf";
- /* Load plugin config in the home dir. Do not skip lines with empty value (but containing a "key="),
- because user may want to override password from /etc/abrt/plugins/*.conf, but he prefers to
- enter it every time he reports. */
- bool success = LoadPluginSettings(path.c_str(), single_plugin_settings, false);
+ /* Load plugin config in the home dir. Do not skip lines
+ * with empty value (but containing a "key="),
+ * because user may want to override password
+ * from /etc/abrt/plugins/foo.conf, but he prefers to
+ * enter it every time he reports. */
+ map_string_h *single_plugin_settings = new_map_string();
+ char *path = xasprintf("%s/.abrt/%s.conf", homedir, plugin_name);
+ bool success = load_conf_file(path, single_plugin_settings, /*skip key w/o values:*/ false);
+ free(path);
if (!success)
+ {
+ free_map_string(single_plugin_settings);
continue;
- // Merge user's plugin settings into already loaded settings.
- map_string_t::const_iterator valit, valitend = single_plugin_settings.end();
- for (valit = single_plugin_settings.begin(); valit != valitend; ++valit)
- it->second[valit->first] = valit->second;
+ }
+
+ /* Merge user's plugin settings into already loaded settings */
+ GHashTableIter iter2;
+ char *key;
+ char *value;
+ g_hash_table_iter_init(&iter2, single_plugin_settings);
+ while (g_hash_table_iter_next(&iter2, (void**)&key, (void**)&value))
+ g_hash_table_replace(plugin_settings, xstrdup(key), xstrdup(value));
+
+ free_map_string(single_plugin_settings);
}
}
+ return settings;
}
/**
* Asks user for missing login information
*/
-static void ask_for_missing_settings(const char *plugin_name, map_string_t &single_plugin_settings)
+static void ask_for_missing_settings(const char *plugin_name, map_string_h *single_plugin_settings)
{
// Login information is missing.
- bool loginMissing = single_plugin_settings.find("Login") != single_plugin_settings.end()
- && 0 == strcmp(single_plugin_settings["Login"].c_str(), "");
- bool passwordMissing = single_plugin_settings.find("Password") != single_plugin_settings.end()
- && 0 == strcmp(single_plugin_settings["Password"].c_str(), "");
+ const char *login = get_map_string_item_or_NULL(single_plugin_settings, "Login");
+ const char *password = get_map_string_item_or_NULL(single_plugin_settings, "Password");
+ bool loginMissing = (login && login[0] == '\0');
+ bool passwordMissing = (password && password[0] == '\0');
if (!loginMissing && !passwordMissing)
return;
@@ -567,7 +557,7 @@ static void ask_for_missing_settings(const char *plugin_name, map_string_t &sing
if (loginMissing)
{
read_from_stdin(_("Enter your login: "), result, 64);
- single_plugin_settings["Login"] = std::string(result);
+ g_hash_table_replace(single_plugin_settings, xstrdup("Login"), xstrdup(result));
}
if (passwordMissing)
{
@@ -578,129 +568,266 @@ static void ask_for_missing_settings(const char *plugin_name, map_string_t &sing
// Newline was not added by pressing Enter because ECHO was disabled, so add it now.
puts("");
- single_plugin_settings["Password"] = std::string(result);
+ g_hash_table_replace(single_plugin_settings, xstrdup("Password"), xstrdup(result));
}
}
-/* Reports the crash with corresponding crash_id over DBus. */
-int report(const char *crash_id, int flags)
+
+struct logging_state {
+ char *last_line;
+};
+static char *do_log_and_save_line(char *log_line, void *param)
{
- int old_logmode = logmode;
- if (flags & CLI_REPORT_SILENT_IF_NOT_FOUND)
- logmode = 0;
- // Ask for an initial report.
- map_crash_data_t cr = call_CreateReport(crash_id);
- logmode = old_logmode;
- if (cr.size() == 0)
+ struct logging_state *l_state = (struct logging_state *)param;
+ log("%s", log_line);
+ free(l_state->last_line);
+ l_state->last_line = log_line;
+ return NULL;
+}
+static int run_events(const char *dump_dir_name,
+ const vector_string_t& events,
+ GHashTable *map_map_settings
+) {
+ int error_cnt = 0;
+ GList *env_list = NULL;
+
+ // Export overridden settings as environment variables
+ GHashTableIter iter;
+ char *plugin_name;
+ map_string_h *single_plugin_settings;
+ g_hash_table_iter_init(&iter, map_map_settings);
+ while (g_hash_table_iter_next(&iter, (void**)&plugin_name, (void**)&single_plugin_settings))
+ {
+ GHashTableIter iter2;
+ char *key;
+ char *value;
+ g_hash_table_iter_init(&iter2, single_plugin_settings);
+ while (g_hash_table_iter_next(&iter2, (void**)&key, (void**)&value))
+ {
+ char *s = xasprintf("%s_%s=%s", plugin_name, key, value);
+ VERB3 log("Exporting '%s'", s);
+ putenv(s);
+ env_list = g_list_append(env_list, s);
+ }
+ }
+
+ // Run events
+ bool at_least_one_reporter_succeeded = false;
+ std::string message;
+ struct logging_state l_state;
+ l_state.last_line = NULL;
+ struct run_event_state *run_state = new_run_event_state();
+ run_state->logging_callback = do_log_and_save_line;
+ run_state->logging_param = &l_state;
+ for (unsigned i = 0; i < events.size(); i++)
+ {
+ std::string event = events[i];
+
+ int r = run_event_on_dir_name(run_state, dump_dir_name, event.c_str());
+ if (r == 0 && run_state->children_count == 0)
+ {
+ l_state.last_line = xasprintf("Error: no processing is specified for event '%s'", event.c_str());
+ r = -1;
+ }
+ if (r == 0)
+ {
+ at_least_one_reporter_succeeded = true;
+ printf("%s: %s\n", event.c_str(), (l_state.last_line ? : "Reporting succeeded"));
+ if (message != "")
+ message += ";";
+ message += (l_state.last_line ? : "Reporting succeeded");
+ }
+ else
+ {
+ error_msg("Reporting via '%s' was not successful%s%s",
+ event.c_str(),
+ l_state.last_line ? ": " : "",
+ l_state.last_line ? l_state.last_line : ""
+ );
+ error_cnt++;
+ }
+ free(l_state.last_line);
+ l_state.last_line = NULL;
+ }
+ free_run_event_state(run_state);
+
+ // Unexport overridden settings
+ for (GList *li = env_list; li; li = g_list_next(li))
{
- return -1;
+ char *s = (char*)li->data;
+ /* Need to make a copy: just cutting s at '=' and unsetenv'ing
+ * the result would be a bug! s _itself_ is in environment now,
+ * we must not modify it there!
+ */
+ char *name = xstrndup(s, strchrnul(s, '=') - s);
+ VERB3 log("Unexporting '%s'", name);
+ unsetenv(name);
+ free(name);
+ free(s);
}
+ g_list_free(env_list);
+
+ // Save reporting results
+ if (at_least_one_reporter_succeeded)
+ {
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (dd)
+ {
+ dd_save_text(dd, FILENAME_MESSAGE, message.c_str());
+ dd_close(dd);
+ }
+ }
+
+ return error_cnt;
+}
+
+
+static char *do_log(char *log_line, void *param)
+{
+ log("%s", log_line);
+ return log_line;
+}
+int run_analyze_event(const char *dump_dir_name)
+{
+ VERB2 log("run_analyze_event('%s')", dump_dir_name);
+
+ struct run_event_state *run_state = new_run_event_state();
+ run_state->logging_callback = do_log;
+ int res = run_event_on_dir_name(run_state, dump_dir_name, "analyze");
+ free_run_event_state(run_state);
+ return res;
+}
- const char *rating_str = get_crash_data_item_content_or_NULL(cr, FILENAME_RATING);
- unsigned rating = rating_str ? xatou(rating_str) : 4;
- /* Open text editor and give a chance to review the backtrace etc. */
+/* Report the crash */
+int report(const char *dump_dir_name, int flags)
+{
+ if (run_analyze_event(dump_dir_name) != 0)
+ return 1;
+
+ /* Load crash_data from (possibly updated by analyze) dump dir */
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ return -1;
+
+ crash_data_t *crash_data = create_crash_data_from_dump_dir(dd);
+ char *events_as_lines = list_possible_events(dd, NULL, "");
+ dd_close(dd);
+
if (!(flags & CLI_REPORT_BATCH))
{
- create_fields_for_editor(cr);
- int result = run_report_editor(cr);
+ /* Open text editor and give a chance to review the backtrace etc */
+ create_fields_for_editor(crash_data);
+ int result = run_report_editor(crash_data);
if (result != 0)
- return result;
+ {
+ free_crash_data(crash_data);
+ free(events_as_lines);
+ return 1;
+ }
+ /* Save comment, "how to reproduce", backtrace */
+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (dd)
+ {
+//TODO: we should iterate through crash_data and modify all modifiable fields
+ const char *comment = get_crash_item_content_or_NULL(crash_data, FILENAME_COMMENT);
+ const char *reproduce = get_crash_item_content_or_NULL(crash_data, FILENAME_REPRODUCE);
+ const char *backtrace = get_crash_item_content_or_NULL(crash_data, FILENAME_BACKTRACE);
+ if (comment)
+ dd_save_text(dd, FILENAME_COMMENT, comment);
+ if (reproduce)
+ dd_save_text(dd, FILENAME_REPRODUCE, reproduce);
+ if (backtrace)
+ dd_save_text(dd, FILENAME_BACKTRACE, backtrace);
+ dd_close(dd);
+ }
}
- /* Get possible reporters associated with this particular crash. */
- const char *events = get_crash_data_item_content_or_NULL(cr, CD_EVENTS);
- vector_string_t reporters;
- if (events) while (*events)
+ /* Get possible reporters associated with this particular crash */
+ vector_string_t report_events;
+ if (events_as_lines)
{
- const char *end = strchrnul(events, '\n');
- if (strncmp(events, "report", 6) == 0
- && (events[6] == '\0' || events[6] == '_')
- ) {
- char *tmp = xstrndup(events, end - events);
- reporters.push_back(tmp);
- free(tmp);
+ char *events = events_as_lines;
+ while (*events)
+ {
+ char *end = strchrnul(events, '\n');
+ if (strncmp(events, "report", 6) == 0
+ && (events[6] == '\0' || events[6] == '_')
+ ) {
+ char *tmp = xstrndup(events, end - events);
+ report_events.push_back(tmp);
+ free(tmp);
+ }
+ events = end;
+ if (!*events)
+ break;
+ events++;
}
- events = end;
- if (!*events)
- break;
- events++;
}
/* Get settings */
- map_map_string_t reporters_settings;
- get_reporter_plugin_settings(reporters, reporters_settings);
+ GHashTable *map_map_settings = get_plugin_settings();
int errors = 0;
int plugins = 0;
if (flags & CLI_REPORT_BATCH)
{
puts(_("Reporting..."));
- report_status_t r = call_Report(cr, reporters, reporters_settings);
- report_status_t::iterator it = r.begin();
- while (it != r.end())
- {
- vector_string_t &v = it->second;
- printf("%s: %s\n", it->first.c_str(), v[REPORT_STATUS_IDX_MSG].c_str());
- plugins++;
- if (v[REPORT_STATUS_IDX_FLAG] == "0")
- errors++;
- it++;
- }
+ errors += run_events(dump_dir_name, report_events, map_map_settings);
+ plugins += report_events.size();
}
else
{
+ const char *rating_str = get_crash_item_content_or_NULL(crash_data, FILENAME_RATING);
+ unsigned rating = rating_str ? xatou(rating_str) : 4;
+
/* For every reporter, ask if user really wants to report using it. */
- for (vector_string_t::const_iterator it = reporters.begin(); it != reporters.end(); ++it)
+ for (vector_string_t::const_iterator it = report_events.begin(); it != report_events.end(); ++it)
{
char question[255];
- snprintf(question, 255, _("Report using %s?"), it->c_str());
+ snprintf(question, sizeof(question), _("Report using %s?"), it->c_str());
if (!ask_yesno(question))
{
puts(_("Skipping..."));
continue;
}
- map_map_string_t::iterator settings = reporters_settings.find(it->c_str());
- if (settings != reporters_settings.end())
+//TODO: rethink how we associate report events with configs
+ if (strncmp(it->c_str(), "report_", strlen("report_")) == 0)
{
- map_string_t::iterator rating_setting = settings->second.find("RatingRequired");
- if (rating_setting != settings->second.end()
- && string_to_bool(rating_setting->second.c_str())
- && rating < 3)
+ const char *config_name = it->c_str() + strlen("report_");
+ map_string_h *single_plugin_settings = (map_string_h *)g_hash_table_lookup(map_map_settings, config_name);
+ if (single_plugin_settings)
{
- puts(_("Reporting disabled because the backtrace is unusable"));
-
- const char *package = get_crash_data_item_content_or_NULL(cr, FILENAME_PACKAGE);
- if (package[0])
- printf(_("Please try to install debuginfo manually using the command: \"debuginfo-install %s\" and try again\n"), package);
-
- plugins++;
- errors++;
- continue;
+ const char *rating_required = get_map_string_item_or_NULL(single_plugin_settings, "RatingRequired");
+ if (rating_required
+ && string_to_bool(rating_required) == true
+ && rating < 3
+ ) {
+ puts(_("Reporting disabled because the backtrace is unusable"));
+
+ const char *package = get_crash_item_content_or_NULL(crash_data, FILENAME_PACKAGE);
+ if (package && package[0])
+ printf(_("Please try to install debuginfo manually using the command: \"debuginfo-install %s\" and try again\n"), package);
+
+ plugins++;
+ errors++;
+ continue;
+ }
+ ask_for_missing_settings(it->c_str(), single_plugin_settings);
}
}
- else
- {
- puts(_("Error loading reporter settings"));
- plugins++;
- errors++;
- continue;
- }
- ask_for_missing_settings(it->c_str(), settings->second);
-
- vector_string_t cur_reporter(1, *it);
- report_status_t r = call_Report(cr, cur_reporter, reporters_settings);
- assert(r.size() == 1); /* one reporter --> one report status */
- vector_string_t &v = r.begin()->second;
- printf("%s: %s\n", r.begin()->first.c_str(), v[REPORT_STATUS_IDX_MSG].c_str());
+ vector_string_t cur_event(1, *it);
+ errors += run_events(dump_dir_name, cur_event, map_map_settings);
plugins++;
- if (v[REPORT_STATUS_IDX_FLAG] == "0")
- errors++;
}
}
+ g_hash_table_destroy(map_map_settings);
+
printf(_("Crash reported via %d report events (%d errors)\n"), plugins, errors);
- return errors != 0;
+ free_crash_data(crash_data);
+ free(events_as_lines);
+ return errors;
}
diff --git a/src/cli/report.h b/src/cli/report.h
index 8a851b8e..80c0b9f2 100644
--- a/src/cli/report.h
+++ b/src/cli/report.h
@@ -18,11 +18,12 @@
#ifndef ABRT_CLI_REPORT_H
#define ABRT_CLI_REPORT_H
-/* Reports the crash with corresponding uuid over DBus. */
+int run_analyze_event(const char *dump_dir_name);
+
+/* Report the crash */
enum {
CLI_REPORT_BATCH = 1 << 0,
- CLI_REPORT_SILENT_IF_NOT_FOUND = 1 << 1,
};
-int report(const char *uuid, int flags);
+int report(const char *dump_dir_name, int flags);
#endif
diff --git a/src/daemon/CommLayerServer.h b/src/daemon/CommLayerServer.h
deleted file mode 100644
index 5b7ecf57..00000000
--- a/src/daemon/CommLayerServer.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- Copyright (C) 2010 ABRT team
- Copyright (C) 2010 RedHat Inc
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef COMMLAYERSERVER_H_
-#define COMMLAYERSERVER_H_
-
-#include "abrtlib.h"
-#include "abrt_crash_dump.h"
-
-class CCommLayerServer {
- public:
- int m_init_error;
-
- CCommLayerServer();
- virtual ~CCommLayerServer();
-
- /* just stubs to be called when not implemented in specific comm layer */
- virtual void Crash(const char *package_name,
- const char *crash_id,
- const char *dir,
- const char *uid_str
- ) {}
- virtual void JobDone(const char* peer) = 0;
- virtual void QuotaExceed(const char* str) {}
-
- virtual void Update(const char* pMessage, const char* peer) {};
- virtual void Warning(const char* pMessage, const char* peer) {};
-};
-
-#endif
diff --git a/src/daemon/CommLayerServerDBus.cpp b/src/daemon/CommLayerServerDBus.cpp
index 28d6ee05..133feb7b 100644
--- a/src/daemon/CommLayerServerDBus.cpp
+++ b/src/daemon/CommLayerServerDBus.cpp
@@ -19,12 +19,9 @@
#include <dbus/dbus.h>
#include "abrtlib.h"
#include "abrt_dbus.h"
-#include "abrt_exception.h"
#include "comm_layer_inner.h"
-#include "dbus_common.h"
#include "MiddleWare.h"
#include "Settings.h"
-#include "Daemon.h"
#include "CommLayerServerDBus.h"
// 16kB message limit
@@ -48,6 +45,11 @@ static DBusMessage* new_signal_msg(const char* member, const char* peer = NULL)
}
static void send_flush_and_unref(DBusMessage* msg)
{
+ if (!g_dbus_conn)
+ {
+ /* Not logging this, it may recurse */
+ return;
+ }
if (!dbus_connection_send(g_dbus_conn, msg, NULL /* &serial */))
error_msg_and_die("Error sending DBus message");
dbus_connection_flush(g_dbus_conn);
@@ -56,7 +58,7 @@ static void send_flush_and_unref(DBusMessage* msg)
}
/* Notify the clients (UI) about a new crash */
-void CCommLayerServerDBus::Crash(const char *package_name,
+void send_dbus_sig_Crash(const char *package_name,
const char *crash_id,
const char *dir,
const char *uid_str
@@ -84,24 +86,24 @@ void CCommLayerServerDBus::Crash(const char *package_name,
send_flush_and_unref(msg);
}
-void CCommLayerServerDBus::QuotaExceed(const char* str)
+void send_dbus_sig_QuotaExceeded(const char* str)
{
- DBusMessage* msg = new_signal_msg("QuotaExceed");
+ DBusMessage* msg = new_signal_msg("QuotaExceeded");
dbus_message_append_args(msg,
DBUS_TYPE_STRING, &str,
DBUS_TYPE_INVALID);
- VERB2 log("Sending signal QuotaExceed('%s')", str);
+ VERB2 log("Sending signal QuotaExceeded('%s')", str);
send_flush_and_unref(msg);
}
-void CCommLayerServerDBus::JobDone(const char* peer)
+void send_dbus_sig_JobDone(const char* peer)
{
DBusMessage* msg = new_signal_msg("JobDone", peer);
VERB2 log("Sending signal JobDone() to peer %s", peer);
send_flush_and_unref(msg);
}
-void CCommLayerServerDBus::Update(const char* pMessage, const char* peer)
+void send_dbus_sig_Update(const char* pMessage, const char* peer)
{
DBusMessage* msg = new_signal_msg("Update", peer);
dbus_message_append_args(msg,
@@ -110,7 +112,7 @@ void CCommLayerServerDBus::Update(const char* pMessage, const char* peer)
send_flush_and_unref(msg);
}
-void CCommLayerServerDBus::Warning(const char* pMessage, const char* peer)
+void send_dbus_sig_Warning(const char* pMessage, const char* peer)
{
DBusMessage* msg = new_signal_msg("Warning", peer);
dbus_message_append_args(msg,
@@ -144,11 +146,12 @@ static long get_remote_uid(DBusMessage* call, const char** ppSender = NULL)
static int handle_GetCrashInfos(DBusMessage* call, DBusMessage* reply)
{
long unix_uid = get_remote_uid(call);
- vector_map_crash_data_t argout1 = GetCrashInfos(unix_uid);
+ vector_of_crash_data_t *argout1 = GetCrashInfos(unix_uid);
DBusMessageIter out_iter;
dbus_message_iter_init_append(reply, &out_iter);
- store_val(&out_iter, argout1);
+ store_vector_of_crash_data(&out_iter, argout1);
+ free_vector_of_crash_data(argout1);
send_flush_and_unref(reply);
return 0;
@@ -197,12 +200,12 @@ static int handle_CreateReport(DBusMessage* call, DBusMessage* reply)
}
long unix_uid = get_remote_uid(call);
- map_crash_data_t report;
- CreateReport(crash_id, unix_uid, /*force:*/ 0, report);
+ crash_data_t *report = NULL;
+ CreateReport(crash_id, unix_uid, /*force:*/ 0, &report);
DBusMessageIter out_iter;
dbus_message_iter_init_append(reply, &out_iter);
- store_val(&out_iter, report);
+ store_crash_data(&out_iter, report);
send_flush_and_unref(reply);
return 0;
@@ -211,19 +214,28 @@ static int handle_CreateReport(DBusMessage* call, DBusMessage* reply)
static int handle_Report(DBusMessage* call, DBusMessage* reply)
{
int r;
+ long unix_uid;
+ report_status_t argout1;
+ map_map_string_t user_conf_data;
+ vector_string_t events;
+ const char* comment = NULL;
+ const char* reproduce = NULL;
+ const char* errmsg = NULL;
DBusMessageIter in_iter;
+
dbus_message_iter_init(call, &in_iter);
- map_crash_data_t argin1;
- r = load_val(&in_iter, argin1);
+ crash_data_t *crash_data = NULL;
+ r = load_crash_data(&in_iter, &crash_data);
if (r != ABRT_DBUS_MORE_FIELDS)
{
error_msg("dbus call %s: parameter type mismatch", __func__ + 7);
- return -1;
+ r = -1;
+ goto ret;
}
- const char* comment = get_crash_data_item_content_or_NULL(argin1, FILENAME_COMMENT) ? : "";
- const char* reproduce = get_crash_data_item_content_or_NULL(argin1, FILENAME_REPRODUCE) ? : "";
- const char* errmsg = NULL;
+//TODO? get_crash_item_content_or_die_or_empty?
+ comment = get_crash_item_content_or_NULL(crash_data, FILENAME_COMMENT) ? : "";
+ reproduce = get_crash_item_content_or_NULL(crash_data, FILENAME_REPRODUCE) ? : "";
if (strlen(comment) > LIMIT_MESSAGE)
{
errmsg = _("Comment is too long");
@@ -239,52 +251,43 @@ static int handle_Report(DBusMessage* call, DBusMessage* reply)
if (!reply)
die_out_of_memory();
send_flush_and_unref(reply);
- return 0;
+ r = 0;
+ goto ret;
}
/* Second parameter: list of events to run */
- vector_string_t events;
r = load_val(&in_iter, events);
if (r == ABRT_DBUS_ERROR)
{
error_msg("dbus call %s: parameter type mismatch", __func__ + 7);
- return -1;
+ r = -1;
+ goto ret;
}
/* Third parameter (optional): configuration data for plugins */
- map_map_string_t user_conf_data;
if (r == ABRT_DBUS_MORE_FIELDS)
{
r = load_val(&in_iter, user_conf_data);
if (r != ABRT_DBUS_LAST_FIELD)
{
error_msg("dbus call %s: parameter type mismatch", __func__ + 7);
- return -1;
+ r = -1;
+ goto ret;
}
}
- long unix_uid = get_remote_uid(call);
- report_status_t argout1;
- try
- {
- argout1 = Report(argin1, events, user_conf_data, unix_uid);
- }
- catch (CABRTException &e)
- {
- dbus_message_unref(reply);
- reply = dbus_message_new_error(call, DBUS_ERROR_FAILED, e.what());
- if (!reply)
- die_out_of_memory();
- send_flush_and_unref(reply);
- return 0;
- }
+ unix_uid = get_remote_uid(call);
+ argout1 = Report(crash_data, events, user_conf_data, unix_uid);
DBusMessageIter out_iter;
dbus_message_iter_init_append(reply, &out_iter);
store_val(&out_iter, argout1);
send_flush_and_unref(reply);
- return 0;
+ r = 0;
+ ret:
+ free_crash_data(crash_data);
+ return r;
}
static int handle_DeleteDebugDump(DBusMessage* call, DBusMessage* reply)
@@ -339,12 +342,13 @@ static int handle_GetPluginSettings(DBusMessage* call, DBusMessage* reply)
//long unix_uid = get_remote_uid(call);
//VERB1 log("got %s('%s') call from uid %ld", "GetPluginSettings", PluginName, unix_uid);
- map_plugin_settings_t plugin_settings;
- GetPluginSettings(PluginName, plugin_settings);
+ map_string_h *plugin_settings = GetPluginSettings(PluginName);
DBusMessageIter out_iter;
dbus_message_iter_init_append(reply, &out_iter);
- store_val(&out_iter, plugin_settings);
+ store_map_string(&out_iter, plugin_settings);
+
+ free_map_string(plugin_settings);
send_flush_and_unref(reply);
return 0;
@@ -362,25 +366,25 @@ static int handle_GetSettings(DBusMessage* call, DBusMessage* reply)
return 0;
}
-static int handle_SetSettings(DBusMessage* call, DBusMessage* reply)
-{
- int r;
- DBusMessageIter in_iter;
- dbus_message_iter_init(call, &in_iter);
- map_abrt_settings_t param1;
- r = load_val(&in_iter, param1);
- if (r != ABRT_DBUS_LAST_FIELD)
- {
- error_msg("dbus call %s: parameter type mismatch", __func__ + 7);
- return -1;
- }
-
- const char * sender = dbus_message_get_sender(call);
- SetSettings(param1, sender);
-
- send_flush_and_unref(reply);
- return 0;
-}
+//static int handle_SetSettings(DBusMessage* call, DBusMessage* reply)
+//{
+// int r;
+// DBusMessageIter in_iter;
+// dbus_message_iter_init(call, &in_iter);
+// map_abrt_settings_t param1;
+// r = load_val(&in_iter, param1);
+// if (r != ABRT_DBUS_LAST_FIELD)
+// {
+// error_msg("dbus call %s: parameter type mismatch", __func__ + 7);
+// return -1;
+// }
+//
+// const char * sender = dbus_message_get_sender(call);
+// SetSettings(param1, sender);
+//
+// send_flush_and_unref(reply);
+// return 0;
+//}
/*
@@ -413,8 +417,11 @@ static DBusHandlerResult message_received(DBusConnection* conn, DBusMessage* msg
r = handle_GetPluginSettings(msg, reply);
else if (strcmp(member, "GetSettings") == 0)
r = handle_GetSettings(msg, reply);
- else if (strcmp(member, "SetSettings") == 0)
- r = handle_SetSettings(msg, reply);
+// looks unused to me.
+// Ok to grep for SetSettings and delete after 2011-04-01.
+// else if (strcmp(member, "SetSettings") == 0)
+// r = handle_SetSettings(msg, reply);
+
// NB: C++ binding also handles "Introspect" method, which returns a string.
// It was sending "dummy" introspection answer whick looks like this:
// "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
@@ -460,7 +467,7 @@ static void handle_dbus_err(bool error_flag, DBusError *err)
ABRTD_DBUS_NAME);
}
-CCommLayerServerDBus::CCommLayerServerDBus()
+int init_dbus()
{
DBusConnection* conn;
DBusError err;
@@ -481,7 +488,7 @@ CCommLayerServerDBus::CCommLayerServerDBus()
//
// dbus-daemon drops connections if it recvs a malformed message
// (we actually observed this when we sent bad UTF-8 string).
- // Currently, in this case abrtd just exits with exitcode 1.
+ // Currently, in this case abrtd just exits with exit code 1.
// (symptom: last two log messages are "abrtd: remove_watch()")
// If we want to have better logging or other nontrivial handling,
// here we need to do:
@@ -510,9 +517,15 @@ CCommLayerServerDBus::CCommLayerServerDBus()
int cnt = 10;
while (dbus_connection_dispatch(conn) != DBUS_DISPATCH_COMPLETE && --cnt)
VERB3 log("processed initial buffered dbus message");
+
+ return 0;
}
-CCommLayerServerDBus::~CCommLayerServerDBus()
+void deinit_dbus()
{
- dbus_connection_unref(g_dbus_conn);
+ if (g_dbus_conn != NULL)
+ {
+ dbus_connection_unref(g_dbus_conn);
+ g_dbus_conn = NULL;
+ }
}
diff --git a/src/daemon/CommLayerServerDBus.h b/src/daemon/CommLayerServerDBus.h
index df767436..979fef69 100644
--- a/src/daemon/CommLayerServerDBus.h
+++ b/src/daemon/CommLayerServerDBus.h
@@ -19,26 +19,26 @@
#ifndef COMMLAYERSERVERDBUS_H_
#define COMMLAYERSERVERDBUS_H_
-#include "CommLayerServer.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
-class CCommLayerServerDBus
-: public CCommLayerServer
-{
- public:
- CCommLayerServerDBus();
- virtual ~CCommLayerServerDBus();
+int init_dbus(void);
+void deinit_dbus(void);
- /* DBus signal senders */
- virtual void Crash(const char *package_name,
+void send_dbus_sig_Crash(const char *package_name,
const char *crash_id,
const char *dir,
const char *uid_str
- );
- virtual void JobDone(const char* peer);
- virtual void QuotaExceed(const char* str);
+);
+void send_dbus_sig_JobDone(const char* peer);
+void send_dbus_sig_QuotaExceeded(const char* str);
+
+void send_dbus_sig_Update(const char* pMessage, const char* peer);
+void send_dbus_sig_Warning(const char* pMessage, const char* peer);
- virtual void Update(const char* pMessage, const char* peer);
- virtual void Warning(const char* pMessage, const char* peer);
-};
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/src/daemon/CrashWatcher.cpp b/src/daemon/CrashWatcher.cpp
deleted file mode 100644
index a74fa3aa..00000000
--- a/src/daemon/CrashWatcher.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- Copyright (C) 2009 Jiri Moskovcak (jmoskovc@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include "abrtlib.h"
-#include "Daemon.h"
-#include "CommLayerServer.h"
-#include "CrashWatcher.h"
-
-void CCrashWatcher::Status(const char *pMessage, const char* peer)
-{
- VERB1 log("Update('%s'): %s", peer, pMessage);
- if (g_pCommLayer != NULL)
- g_pCommLayer->Update(pMessage, peer);
-}
-
-void CCrashWatcher::Warning(const char *pMessage, const char* peer)
-{
- VERB1 log("Warning('%s'): %s", peer, pMessage);
- if (g_pCommLayer != NULL)
- g_pCommLayer->Warning(pMessage, peer);
-}
-
-CCrashWatcher::CCrashWatcher()
-{
-}
-
-CCrashWatcher::~CCrashWatcher()
-{
-}
diff --git a/src/daemon/CrashWatcher.h b/src/daemon/CrashWatcher.h
deleted file mode 100644
index 4f755a36..00000000
--- a/src/daemon/CrashWatcher.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- Copyright (C) 2009 Jiri Moskovcak (jmoskovc@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef CRASHWATCHER_H_
-#define CRASHWATCHER_H_
-
-#include "observer.h"
-
-
-class CCrashWatcher
-: public CObserver
-{
- public:
- CCrashWatcher();
- virtual ~CCrashWatcher();
-
- public:
- /* Observer methods */
- virtual void Status(const char *pMessage, const char* peer);
- virtual void Warning(const char *pMessage, const char* peer);
-};
-
-#endif
diff --git a/src/daemon/Daemon.cpp b/src/daemon/Daemon.cpp
index 609b0b26..f791fb38 100644
--- a/src/daemon/Daemon.cpp
+++ b/src/daemon/Daemon.cpp
@@ -25,21 +25,18 @@
#include <string>
#include <sys/inotify.h>
#include <sys/ioctl.h> /* ioctl(FIONREAD) */
-#include <glib.h>
#include "abrtlib.h"
-#include "abrt_exception.h"
#include "comm_layer_inner.h"
#include "Settings.h"
#include "CommLayerServerDBus.h"
-#include "CrashWatcher.h"
#include "MiddleWare.h"
-#include "Daemon.h"
#include "parse_options.h"
+#define PROGNAME "abrtd"
+
using namespace std;
-#define VAR_RUN_LOCK_FILE VAR_RUN"/abrt/abrtd.lock"
#define VAR_RUN_PIDFILE VAR_RUN"/abrtd.pid"
#define SOCKET_FILE VAR_RUN"/abrt/abrt.socket"
@@ -55,15 +52,15 @@ using namespace std;
* - signal: we got SIGTERM or SIGINT
*
* DBus methods we have:
- * - GetCrashInfos(): returns a vector_map_crash_data_t (vector_map_vector_string_t)
+ * - GetCrashInfos(): returns a vector_of_crash_data
* of crashes for given uid
* v[N]["executable"/"uid"/"kernel"/"backtrace"][N] = "contents"
* - StartJob(crash_id,force): starts creating a report for /var/spool/abrt/DIR with this UID:UUID.
* Returns job id (uint64).
* After thread returns, when report creation thread has finished,
* JobDone() dbus signal is emitted.
- * - CreateReport(crash_id): returns map_crash_data_t (map_vector_string_t)
- * - Report(map_crash_data_t (map_vector_string_t[, map_map_string_t])):
+ * - CreateReport(crash_id): returns crash data (hash table of struct crash_item)
+ * - Report(crash_data[, map_map_string_t]):
* "Please report this crash": calls Report() of all registered reporter plugins.
* Returns report_status_t (map_vector_string_t) - the status of each call.
* 2nd parameter is the contents of user's abrt.conf.
@@ -85,12 +82,11 @@ using namespace std;
* Both are sent as unicast to last client set by set_client_name(name).
* If set_client_name(NULL) was done, they are not sent.
*/
-CCommLayerServer* g_pCommLayer;
-
static volatile sig_atomic_t s_sig_caught;
static int s_signal_pipe[2];
static int s_signal_pipe_write = -1;
static int s_upload_watch = -1;
+static pid_t log_scanner_pid = -1;
static unsigned s_timeout;
static bool s_exiting;
@@ -203,205 +199,36 @@ static void dumpsocket_shutdown()
}
}
-
-/* Cron handling */
-
-typedef struct cron_callback_data_t
-{
- std::string m_sPluginName;
- std::string m_sPluginArgs;
- unsigned int m_nTimeout;
-
- cron_callback_data_t(
- const std::string& pPluginName,
- const std::string& pPluginArgs,
- const unsigned int& pTimeout) :
- m_sPluginName(pPluginName),
- m_sPluginArgs(pPluginArgs),
- m_nTimeout(pTimeout)
- {}
-} cron_callback_data_t;
-
-static void cron_delete_callback_data_cb(gpointer data)
-{
- cron_callback_data_t* cronDeleteCallbackData = static_cast<cron_callback_data_t*>(data);
- delete cronDeleteCallbackData;
-}
-
-static gboolean cron_activation_periodic_cb(gpointer data)
+static int create_pidfile()
{
- cron_callback_data_t* cronPeriodicCallbackData = static_cast<cron_callback_data_t*>(data);
- VERB1 log("Activating plugin: %s", cronPeriodicCallbackData->m_sPluginName.c_str());
- RunAction(DEBUG_DUMPS_DIR,
- cronPeriodicCallbackData->m_sPluginName.c_str(),
- cronPeriodicCallbackData->m_sPluginArgs.c_str()
- );
- return TRUE;
-}
-static gboolean cron_activation_one_cb(gpointer data)
-{
- cron_callback_data_t* cronOneCallbackData = static_cast<cron_callback_data_t*>(data);
- VERB1 log("Activating plugin: %s", cronOneCallbackData->m_sPluginName.c_str());
- RunAction(DEBUG_DUMPS_DIR,
- cronOneCallbackData->m_sPluginName.c_str(),
- cronOneCallbackData->m_sPluginArgs.c_str()
- );
- return FALSE;
-}
-static gboolean cron_activation_reshedule_cb(gpointer data)
-{
- cron_callback_data_t* cronResheduleCallbackData = static_cast<cron_callback_data_t*>(data);
- VERB1 log("Rescheduling plugin: %s", cronResheduleCallbackData->m_sPluginName.c_str());
- cron_callback_data_t* cronPeriodicCallbackData = new cron_callback_data_t(cronResheduleCallbackData->m_sPluginName,
- cronResheduleCallbackData->m_sPluginArgs,
- cronResheduleCallbackData->m_nTimeout);
- g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
- cronPeriodicCallbackData->m_nTimeout,
- cron_activation_periodic_cb,
- static_cast<gpointer>(cronPeriodicCallbackData),
- cron_delete_callback_data_cb
- );
- return FALSE;
-}
-
-static int SetUpCron()
-{
- map_cron_t::iterator it_c = g_settings_mapCron.begin();
- for (; it_c != g_settings_mapCron.end(); it_c++)
+ /* Note:
+ * No O_EXCL: we would happily overwrite stale pidfile from previous boot.
+ * No O_TRUNC: we must first try to lock the file, and if lock fails,
+ * there is another live abrtd. O_TRUNCing the file in this case
+ * would be wrong - it'll erase the pid to empty string!
+ */
+ int fd = open(VAR_RUN_PIDFILE, O_WRONLY|O_CREAT, 0644);
+ if (fd >= 0)
{
- std::string::size_type pos = it_c->first.find(":");
- int timeout = 0;
- int nH = -1;
- int nM = -1;
- int nS = -1;
-
-//TODO: rewrite using good old sscanf?
-
- if (pos != std::string::npos)
- {
- std::string sH;
- std::string sM;
-
- sH = it_c->first.substr(0, pos);
- nH = xatou(sH.c_str());
- nH = nH > 23 ? 23 : nH;
- nH = nH < 0 ? 0 : nH;
- timeout += nH * 60 * 60;
- sM = it_c->first.substr(pos + 1);
- nM = xatou(sM.c_str());
- nM = nM > 59 ? 59 : nM;
- nM = nM < 0 ? 0 : nM;
- timeout += nM * 60;
- }
- else
- {
- std::string sS;
-
- sS = it_c->first;
- nS = xatou(sS.c_str());
- nS = nS <= 0 ? 1 : nS;
- timeout = nS;
- }
-
- if (nS != -1)
+ if (lockf(fd, F_TLOCK, 0) < 0)
{
- vector_pair_string_string_t::iterator it_ar = it_c->second.begin();
- for (; it_ar != it_c->second.end(); it_ar++)
- {
- cron_callback_data_t* cronPeriodicCallbackData = new cron_callback_data_t(it_ar->first, it_ar->second, timeout);
- g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
- timeout,
- cron_activation_periodic_cb,
- static_cast<gpointer>(cronPeriodicCallbackData),
- cron_delete_callback_data_cb);
- }
+ perror_msg("Can't lock file '%s'", VAR_RUN_PIDFILE);
+ return -1;
}
- else
- {
- time_t actTime = time(NULL);
- struct tm locTime;
- localtime_r(&actTime, &locTime);
- locTime.tm_hour = nH;
- locTime.tm_min = nM;
- locTime.tm_sec = 0;
- time_t nextTime = mktime(&locTime);
- if (nextTime == ((time_t)-1))
- {
- /* paranoia */
- perror_msg("Can't set up cron time");
- return -1;
- }
- if (actTime > nextTime)
- {
- timeout = 24*60*60 + (nextTime - actTime);
- }
- else
- {
- timeout = nextTime - actTime;
- }
- vector_pair_string_string_t::iterator it_ar = it_c->second.begin();
- for (; it_ar != it_c->second.end(); it_ar++)
- {
- cron_callback_data_t* cronOneCallbackData = new cron_callback_data_t(it_ar->first, it_ar->second, timeout);
- g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
- timeout,
- cron_activation_one_cb,
- static_cast<gpointer>(cronOneCallbackData),
- cron_delete_callback_data_cb);
- cron_callback_data_t* cronResheduleCallbackData = new cron_callback_data_t(it_ar->first, it_ar->second, 24 * 60 * 60);
- g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
- timeout,
- cron_activation_reshedule_cb,
- static_cast<gpointer>(cronResheduleCallbackData),
- cron_delete_callback_data_cb);
- }
- }
- }
- return 0;
-}
-
-static int CreatePidFile()
-{
- int fd;
-
- /* JIC */
- unlink(VAR_RUN_PIDFILE);
-
- /* open the pidfile */
- fd = open(VAR_RUN_PIDFILE, O_WRONLY|O_CREAT|O_EXCL, 0644);
- if (fd >= 0)
- {
+ close_on_exec_on(fd);
/* write our pid to it */
char buf[sizeof(long)*3 + 2];
int len = sprintf(buf, "%lu\n", (long)getpid());
write(fd, buf, len);
- close(fd);
+ ftruncate(fd, len);
+ /* we leak opened+locked fd intentionally */
return 0;
}
- /* something went wrong */
perror_msg("Can't open '%s'", VAR_RUN_PIDFILE);
return -1;
}
-static int Lock()
-{
- int lfd = open(VAR_RUN_LOCK_FILE, O_RDWR|O_CREAT, 0640);
- if (lfd < 0)
- {
- perror_msg("Can't open '%s'", VAR_RUN_LOCK_FILE);
- return -1;
- }
- if (lockf(lfd, F_TLOCK, 0) < 0)
- {
- perror_msg("Can't lock file '%s'", VAR_RUN_LOCK_FILE);
- return -1;
- }
- close_on_exec_on(lfd);
- return 0;
- /* we leak opened lfd intentionally */
-}
-
static void handle_signal(int signo)
{
int save_errno = errno;
@@ -433,12 +260,22 @@ static gboolean handle_signal_cb(GIOChannel *gio, GIOCondition condition, gpoint
s_exiting = 1;
else
{
- if (socket_client_count)
- socket_client_count--;
- if (!socket_channel_cb_id)
+ pid_t pid;
+ while ((pid = waitpid(-1, NULL, WNOHANG)) > 0)
{
- log("Accepting connections on '%s'", SOCKET_FILE);
- socket_channel_cb_id = add_watch_or_die(socket_channel, G_IO_IN | G_IO_PRI, server_socket_cb);
+ if (pid == log_scanner_pid)
+ {
+ log("log scanner exited");
+ log_scanner_pid = -1;
+ continue;
+ }
+ if (socket_client_count)
+ socket_client_count--;
+ if (!socket_channel_cb_id)
+ {
+ log("Accepting connections on '%s'", SOCKET_FILE);
+ socket_channel_cb_id = add_watch_or_die(socket_channel, G_IO_IN | G_IO_PRI, server_socket_cb);
+ }
}
}
return TRUE;
@@ -531,76 +368,63 @@ static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpoin
&& worst_dir
) {
log("Size of '%s' >= %u MB, deleting '%s'", DEBUG_DUMPS_DIR, g_settings_nMaxCrashReportsSize, worst_dir);
- g_pCommLayer->QuotaExceed(_("The size of the report exceeded the quota. Please check system's MaxCrashReportsSize value in abrt.conf."));
+ send_dbus_sig_QuotaExceeded(_("The size of the report exceeded the quota. Please check system's MaxCrashReportsSize value in abrt.conf."));
/* deletes both directory and DB record */
char *d = concat_path_file(DEBUG_DUMPS_DIR, worst_dir);
free(worst_dir);
worst_dir = NULL;
- delete_crash_dump_dir(d);
+ delete_dump_dir(d);
free(d);
}
}
char *fullname = NULL;
- try
+ crash_data_t *crash_data = NULL;
+ fullname = concat_path_file(DEBUG_DUMPS_DIR, name);
+ mw_result_t res = LoadDebugDump(fullname, &crash_data);
+ switch (res)
{
- fullname = concat_path_file(DEBUG_DUMPS_DIR, name);
- map_crash_data_t crashinfo;
- mw_result_t res = LoadDebugDump(fullname, crashinfo);
- switch (res)
- {
- case MW_OK:
- log("New crash %s, processing", fullname);
- /* Fall through */
+ case MW_OK:
+ log("New crash %s, processing", fullname);
+ /* Fall through */
- case MW_OCCURRED: /* dup */
+ case MW_OCCURRED: /* dup */
+ {
+ if (res != MW_OK)
{
- if (res != MW_OK)
- {
- const char *first = get_crash_data_item_content_or_NULL(crashinfo, CD_DUMPDIR);
- log("Deleting crash %s (dup of %s), sending dbus signal",
- strrchr(fullname, '/') + 1,
- strrchr(first, '/') + 1);
- delete_crash_dump_dir(fullname);
- }
-
- const char *uid_str = get_crash_data_item_content_or_NULL(crashinfo, FILENAME_UID);
- const char *inform_all = get_crash_data_item_content_or_NULL(crashinfo, FILENAME_INFORMALL);
-
- if (inform_all && string_to_bool(inform_all))
- uid_str = NULL;
- char *crash_id = xasprintf("%s:%s",
- get_crash_data_item_content_or_NULL(crashinfo, FILENAME_UID),
- get_crash_data_item_content_or_NULL(crashinfo, FILENAME_UUID)
- );
- /* Send dbus signal */
- g_pCommLayer->Crash(get_crash_data_item_content_or_NULL(crashinfo, FILENAME_PACKAGE),
- crash_id, //TODO: stop passing this param, it is unused
- fullname,
- uid_str
- );
- free(crash_id);
- break;
+ const char *first = get_crash_item_content_or_NULL(crash_data, CD_DUMPDIR);
+ log("Deleting crash %s (dup of %s), sending dbus signal",
+ strrchr(fullname, '/') + 1,
+ strrchr(first, '/') + 1);
+ delete_dump_dir(fullname);
}
- case MW_CORRUPTED:
- case MW_GPG_ERROR:
- default:
- log("Corrupted or bad crash %s (res:%d), deleting", fullname, (int)res);
- delete_crash_dump_dir(fullname);
- break;
+
+ const char *uid_str = get_crash_item_content_or_NULL(crash_data, FILENAME_UID);
+ const char *inform_all = get_crash_item_content_or_NULL(crash_data, FILENAME_INFORMALL);
+
+ if (inform_all && string_to_bool(inform_all))
+ uid_str = NULL;
+ char *crash_id = xasprintf("%s:%s",
+ get_crash_item_content_or_NULL(crash_data, FILENAME_UID),
+ get_crash_item_content_or_NULL(crash_data, FILENAME_UUID)
+ );
+ send_dbus_sig_Crash(get_crash_item_content_or_NULL(crash_data, FILENAME_PACKAGE),
+ crash_id, //TODO: stop passing this param, it is unused
+ fullname,
+ uid_str
+ );
+ free(crash_id);
+ break;
}
- }
- catch (CABRTException& e)
- {
- error_msg("%s", e.what());
- }
- catch (...)
- {
- free(fullname);
- free(buf);
- throw;
+ case MW_CORRUPTED:
+ case MW_GPG_ERROR:
+ default:
+ log("Corrupted or bad crash %s (res:%d), deleting", fullname, (int)res);
+ delete_dump_dir(fullname);
+ break;
}
free(fullname);
+ free_crash_data(crash_data);
} /* while */
free(buf);
@@ -636,10 +460,10 @@ static void run_main_loop(GMainLoop* loop)
fds = (GPollFD *)xrealloc(fds, fds_size * sizeof(fds[0]));
}
- if (s_timeout)
+ if (s_timeout != 0)
alarm(s_timeout);
g_poll(fds, nfds, timeout);
- if (s_timeout)
+ if (s_timeout != 0)
alarm(0);
some_ready = g_main_context_check(context, max_priority, fds, nfds);
@@ -659,8 +483,9 @@ static void start_syslog_logging()
* Otherwise fprintf(stderr) dumps messages into random fds, etc. */
xdup2(STDIN_FILENO, STDOUT_FILENO);
xdup2(STDIN_FILENO, STDERR_FILENO);
- openlog("abrtd", 0, LOG_DAEMON);
+ openlog(PROGNAME, 0, LOG_DAEMON);
logmode = LOGMODE_SYSLOG;
+ putenv((char*)"ABRT_SYSLOG=1");
}
static void ensure_writable_dir(const char *dir, mode_t mode, const char *user)
@@ -690,28 +515,11 @@ static void sanitize_dump_dir_rights()
/* 00777 bits are usual "rwxrwxrwx" access rights */
ensure_writable_dir(DEBUG_DUMPS_DIR, 0755, "abrt");
/* debuginfo cache */
- ensure_writable_dir(DEBUG_INFO_DIR, 0755, "root");
+ ensure_writable_dir(DEBUG_INFO_DIR, 0775, "abrt");
/* temp dir */
ensure_writable_dir(VAR_RUN"/abrt", 0755, "root");
}
-static char *timeout_opt;
-static const char* abrtd_usage = _("abrtd [options]");
-enum {
- OPT_v = 1 << 0,
- OPT_d = 1 << 1,
- OPT_s = 1 << 2,
- OPT_t = 1 << 3,
-};
-/* Keep enum above and order of options below in sync! */
-static struct options abrtd_options[] = {
- OPT__VERBOSE(&g_verbose),
- OPT_BOOL( 'd' , 0, NULL, _("Do not daemonize")),
- OPT_BOOL( 's' , 0, NULL, _("Log to syslog even with -d")),
- OPT_INTEGER( 't' , 0, &timeout_opt, _("Exit after SEC seconds of inactivity")),
- OPT_END()
-};
-
int main(int argc, char** argv)
{
int parent_pid = getpid();
@@ -730,26 +538,37 @@ int main(int argc, char** argv)
if (env_verbose)
g_verbose = atoi(env_verbose);
- unsigned opts = parse_opts(argc, argv, abrtd_options, abrtd_usage);
-
- if (opts & OPT_s)
- start_syslog_logging();
-
+ const char *program_usage_string = _(
+ PROGNAME" [options]"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_s = 1 << 2,
+ OPT_t = 1 << 3,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_BOOL( 'd', NULL, NULL , _("Do not daemonize")),
+ OPT_BOOL( 's', NULL, NULL , _("Log to syslog even with -d")),
+ OPT_INTEGER('t', NULL, &s_timeout, _("Exit after SEC seconds of inactivity")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
+
+ unsetenv("ABRT_SYSLOG");
+ putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
/* When dbus daemon starts us, it doesn't set PATH
* (I saw it set only DBUS_STARTER_ADDRESS and DBUS_STARTER_BUS_TYPE).
* In this case, set something sane:
*/
- /* Need to add LIBEXEC_DIR to PATH, because otherwise abrt-action-*
- * are not found by exec()
- */
const char *env_path = getenv("PATH");
if (!env_path || !env_path[0])
- env_path = "/usr/sbin:/usr/bin:/sbin:/bin";
- putenv(xasprintf("PATH=%s:%s", LIBEXEC_DIR, env_path));
-
- putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
-
- msg_prefix = "abrtd"; /* for log(), error_msg() and such */
+ putenv((char*)"PATH=/usr/sbin:/usr/bin:/sbin:/bin");
+ msg_prefix = PROGNAME; /* for log(), error_msg() and such */
+ if (opts & OPT_s)
+ start_syslog_logging();
xpipe(s_signal_pipe);
close_on_exec_on(s_signal_pipe[0]);
@@ -757,7 +576,7 @@ int main(int argc, char** argv)
signal(SIGTERM, handle_signal);
signal(SIGINT, handle_signal);
signal(SIGCHLD, handle_signal);
- if (s_timeout)
+ if (s_timeout != 0)
signal(SIGALRM, handle_signal);
/* Daemonize unless -d */
@@ -799,37 +618,43 @@ int main(int argc, char** argv)
guint channel_inotify_event_id = 0;
GIOChannel* channel_signal = NULL;
guint channel_signal_event_id = 0;
- bool lockfile_created = false;
bool pidfile_created = false;
- CCrashWatcher watcher;
/* Initialization */
try
{
- init_daemon_logging(&watcher);
+ init_daemon_logging();
VERB1 log("Loading settings");
- if (LoadSettings() != 0)
+ if (load_settings() != 0)
throw 1;
+ sanitize_dump_dir_rights();
+
VERB1 log("Creating glib main loop");
pMainloop = g_main_loop_new(NULL, FALSE);
VERB1 log("Initializing inotify");
- sanitize_dump_dir_rights();
errno = 0;
int inotify_fd = inotify_init();
if (inotify_fd == -1)
perror_msg_and_die("inotify_init failed");
close_on_exec_on(inotify_fd);
+
/* Watching DEBUG_DUMPS_DIR for new files... */
if (inotify_add_watch(inotify_fd, DEBUG_DUMPS_DIR, IN_CREATE | IN_MOVED_TO) < 0)
- perror_msg_and_die("inotify_add_watch failed on '%s'", DEBUG_DUMPS_DIR);
+ {
+ perror_msg("inotify_add_watch failed on '%s'", DEBUG_DUMPS_DIR);
+ throw 1;
+ }
if (g_settings_sWatchCrashdumpArchiveDir)
{
s_upload_watch = inotify_add_watch(inotify_fd, g_settings_sWatchCrashdumpArchiveDir, IN_CLOSE_WRITE|IN_MOVED_TO);
if (s_upload_watch < 0)
- perror_msg_and_die("inotify_add_watch failed on '%s'", g_settings_sWatchCrashdumpArchiveDir);
+ {
+ perror_msg("inotify_add_watch failed on '%s'", g_settings_sWatchCrashdumpArchiveDir);
+ throw 1;
+ }
}
VERB1 log("Adding inotify watch to glib main loop");
channel_inotify = g_io_channel_unix_new(inotify_fd);
@@ -838,13 +663,6 @@ int main(int argc, char** argv)
handle_inotify_cb,
NULL);
- VERB1 log("Loading plugins from "PLUGINS_LIB_DIR);
- g_pPluginManager = new CPluginManager();
- g_pPluginManager->LoadPlugins();
-
- if (SetUpCron() != 0)
- throw 1;
-
/* Add an event source which waits for INT/TERM signal */
VERB1 log("Adding signal pipe watch to glib main loop");
channel_signal = g_io_channel_unix_new(s_signal_pipe[0]);
@@ -854,12 +672,8 @@ int main(int argc, char** argv)
NULL);
/* Mark the territory */
- VERB1 log("Creating lock file");
- if (Lock() != 0)
- throw 1;
- lockfile_created = true;
VERB1 log("Creating pid file");
- if (CreatePidFile() != 0)
+ if (create_pidfile() != 0)
throw 1;
pidfile_created = true;
@@ -870,8 +684,7 @@ int main(int argc, char** argv)
* therefore it should be the last thing to initialize.
*/
VERB1 log("Initializing dbus");
- g_pCommLayer = new CCommLayerServerDBus();
- if (g_pCommLayer->m_init_error)
+ if (init_dbus() != 0)
throw 1;
}
catch (...)
@@ -896,21 +709,26 @@ int main(int argc, char** argv)
/* Only now we want signal pipe to work */
s_signal_pipe_write = s_signal_pipe[1];
- /* Enter the event loop */
- try
- {
- log("Init complete, entering main loop");
- run_main_loop(pMainloop);
- }
- catch (CABRTException& e)
+ if (g_settings_sLogScanners)
{
- error_msg("Error: %s", e.what());
- }
- catch (std::exception& e)
- {
- error_msg("Error: %s", e.what());
+ const char *scanner_argv[] = {
+ "/bin/sh", "-c",
+ g_settings_sLogScanners,
+ NULL
+ };
+ log_scanner_pid = fork_execv_on_steroids(EXECFLG_INPUT_NUL,
+ (char**)scanner_argv,
+ /*pipefds:*/ NULL,
+ /*unsetenv_vec:*/ NULL,
+ /*dir:*/ NULL,
+ /*uid:*/ 0);
+ VERB1 log("Started log scanner, pid:%d", (int)log_scanner_pid);
}
+ /* Enter the event loop */
+ log("Init complete, entering main loop");
+ run_main_loop(pMainloop);
+
cleanup:
/* Error or INT/TERM. Clean up, in reverse order.
* Take care to not undo things we did not do.
@@ -918,8 +736,6 @@ int main(int argc, char** argv)
dumpsocket_shutdown();
if (pidfile_created)
unlink(VAR_RUN_PIDFILE);
- if (lockfile_created)
- unlink(VAR_RUN_LOCK_FILE);
if (channel_signal_event_id > 0)
g_source_remove(channel_signal_event_id);
@@ -930,21 +746,23 @@ int main(int argc, char** argv)
if (channel_inotify)
g_io_channel_unref(channel_inotify);
- delete g_pCommLayer;
- if (g_pPluginManager)
- {
- /* This restores /proc/sys/kernel/core_pattern, among other things: */
- g_pPluginManager->UnLoadPlugins();
- delete g_pPluginManager;
- }
+ deinit_dbus();
+
if (pMainloop)
g_main_loop_unref(pMainloop);
- settings_free();
+ free_settings();
+
+ if (log_scanner_pid > 0)
+ {
+ VERB2 log("Sending SIGTERM to %d", log_scanner_pid);
+ kill(log_scanner_pid, SIGTERM);
+ }
+
/* Exiting */
if (s_sig_caught && s_sig_caught != SIGALRM && s_sig_caught != SIGCHLD)
{
- error_msg_and_die("Got signal %d, exiting", s_sig_caught);
+ error_msg("Got signal %d, exiting", s_sig_caught);
signal(s_sig_caught, SIG_DFL);
raise(s_sig_caught);
}
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index 34c7aa7b..6fe73065 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -1,24 +1,22 @@
bin_SCRIPTS = \
abrt-handle-upload
-libexec_PROGRAMS = \
- abrt-action-save-package-data
bin_PROGRAMS = \
- abrt-handle-crashdump
+ abrt-handle-crashdump \
+ abrt-action-save-package-data
-sbin_PROGRAMS = abrtd \
+sbin_PROGRAMS = \
+ abrtd \
abrt-server
abrtd_SOURCES = \
- PluginManager.h PluginManager.cpp \
MiddleWare.h MiddleWare.cpp \
- CrashWatcher.h CrashWatcher.cpp \
- CommLayerServer.h CommLayerServer.cpp \
CommLayerServerDBus.h CommLayerServerDBus.cpp \
Settings.h Settings.cpp \
- Daemon.h Daemon.cpp
+ comm_layer_inner.h comm_layer_inner.cpp \
+ Daemon.cpp
abrtd_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -36,14 +34,13 @@ abrtd_CPPFLAGS = \
abrtd_LDADD = \
$(DL_LIBS) \
$(DBUS_LIBS) \
- ../lib/libabrt.la \
- ../lib/libabrt_daemon.la \
+ ../lib/libreport.la \
../lib/libabrt_dbus.la
abrt_server_SOURCES = \
abrt-server.c
abrt_server_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -52,15 +49,16 @@ abrt_server_CPPFLAGS = \
-DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
-D_GNU_SOURCE \
-Wall -Werror
abrt_server_LDADD = \
- ../lib/libabrt.la
+ ../lib/libreport.la
abrt_handle_crashdump_SOURCES = \
abrt-handle-crashdump.c
abrt_handle_crashdump_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -71,17 +69,18 @@ abrt_handle_crashdump_CPPFLAGS = \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
-DLIBEXEC_DIR=\"$(LIBEXEC_DIR)\" \
+ $(GLIB_CFLAGS) \
-D_GNU_SOURCE \
-Wall -Werror
abrt_handle_crashdump_LDADD = \
- ../lib/libabrt.la
+ ../lib/libreport.la
abrt_action_save_package_data_SOURCES = \
rpm.h rpm.c \
Settings.h Settings.cpp \
- abrt-action-save-package-data.cpp
+ abrt-action-save-package-data.c
abrt_action_save_package_data_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -96,7 +95,7 @@ abrt_action_save_package_data_CPPFLAGS = \
-Wall -Werror
abrt_action_save_package_data_LDADD = \
$(RPM_LIBS) \
- ../lib/libabrt.la
+ ../lib/libreport.la
dbusabrtconfdir = ${sysconfdir}/dbus-1/system.d/
dist_dbusabrtconf_DATA = dbus-abrt.conf
diff --git a/src/daemon/MiddleWare.cpp b/src/daemon/MiddleWare.cpp
index ee2853b2..2757d84f 100644
--- a/src/daemon/MiddleWare.cpp
+++ b/src/daemon/MiddleWare.cpp
@@ -19,32 +19,21 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "abrtlib.h"
-#include "Daemon.h"
#include "Settings.h"
-#include "abrt_exception.h"
#include "comm_layer_inner.h"
-#include "CommLayerServer.h"
+#include "CommLayerServerDBus.h"
#include "MiddleWare.h"
using namespace std;
/**
- * An instance of CPluginManager. When MiddleWare wants to do something
- * with plugins, it calls the plugin manager.
- * @see PluginManager.h
- */
-CPluginManager* g_pPluginManager;
-
-
-/**
* Get one crash info. If getting is successful,
* then crash info is filled.
* @param dump_dir_name A dump dir containing all necessary data.
* @param pCrashData A crash info.
* @return It return results of operation. See mw_result_t.
*/
-static mw_result_t FillCrashInfo(const char *dump_dir_name,
- map_crash_data_t& pCrashData);
+static crash_data_t *FillCrashInfo(const char *dump_dir_name);
/**
* Transforms a debugdump directory to inner crash
@@ -52,36 +41,53 @@ static mw_result_t FillCrashInfo(const char *dump_dir_name,
* @param dump_dir_name A debugdump dir containing all necessary data.
* @param pCrashData A created crash report.
*/
-static bool DebugDumpToCrashReport(const char *dump_dir_name, map_crash_data_t& pCrashData)
+static crash_data_t *DebugDumpToCrashReport(const char *dump_dir_name)
{
VERB3 log(" DebugDumpToCrashReport('%s')", dump_dir_name);
struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
- return false;
-
+ return NULL;
+
+ static const char *const must_have_files[] = {
+ FILENAME_ARCHITECTURE,
+ FILENAME_KERNEL ,
+ FILENAME_PACKAGE ,
+ FILENAME_COMPONENT ,
+ FILENAME_OS_RELEASE ,
+ FILENAME_EXECUTABLE ,
+ NULL
+ };
const char *const *v = must_have_files;
while (*v)
{
if (!dd_exist(dd, *v))
{
+ /* Old dump dir format compat. Remove in abrt-2.1 */
+ if (strcmp(*v, FILENAME_OS_RELEASE) == 0)
+ if (dd_exist(dd, "release"))
+ goto ok;
+
dd_close(dd);
log("Important file '%s/%s' is missing", dump_dir_name, *v);
- return false;
+ return NULL;
}
+ ok:
v++;
}
- load_crash_data_from_crash_dump_dir(dd, pCrashData);
+ crash_data_t *crash_data = create_crash_data_from_dump_dir(dd);
char *events = list_possible_events(dd, NULL, "");
dd_close(dd);
- add_to_crash_data_ext(pCrashData, CD_EVENTS, CD_SYS, CD_ISNOTEDITABLE, events);
+ add_to_crash_data_ext(crash_data, CD_EVENTS, events,
+ CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);
free(events);
- add_to_crash_data_ext(pCrashData, CD_DUMPDIR, CD_SYS, CD_ISNOTEDITABLE, dump_dir_name);
+ add_to_crash_data_ext(crash_data, CD_DUMPDIR, dump_dir_name,
+ CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);
- return true;
+ return crash_data;
}
static char *do_log_and_update_client(char *log_line, void *param)
@@ -91,22 +97,36 @@ static char *do_log_and_update_client(char *log_line, void *param)
return log_line;
}
+/**
+ * Takes care of getting all additional data needed
+ * for computing UUIDs and creating a report for particular analyzer
+ * plugin. This report could be send somewhere afterwards. If a creation
+ * is successful, then a crash report is filled.
+ * @param pAnalyzer A name of an analyzer plugin.
+ * @param pDebugDumpPath A debugdump dir containing all necessary data.
+ * @param pCrashData A filled crash report.
+ * @return It return results of operation. See mw_result_t.
+ */
/*
* Called in two cases:
* (1) by StartJob dbus call -> CreateReportThread(), in the thread
* (2) by CreateReport dbus call
*/
-mw_result_t CreateCrashReport(const char *dump_dir_name,
+static mw_result_t CreateCrashReport(const char *dump_dir_name,
long caller_uid,
int force,
- map_crash_data_t& pCrashData)
+ crash_data_t **crash_data)
{
- VERB2 log("CreateCrashReport('%s',%ld,result)", dump_dir_name, caller_uid);
+ VERB2 log("CreateCrashReport('%s',%ld)", dump_dir_name, caller_uid);
+
+ *crash_data = NULL;
struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
return MW_NOENT_ERROR;
+ struct run_event_state *run_state;
+ int res;
mw_result_t r = MW_OK;
if (caller_uid != 0) /* not called by root */
@@ -131,63 +151,33 @@ mw_result_t CreateCrashReport(const char *dump_dir_name,
}
dd_close(dd);
- try
+ run_state = new_run_event_state();
+ run_state->logging_callback = do_log_and_update_client;
+ res = run_event_on_dir_name(run_state, dump_dir_name, force ? "reanalyze" : "analyze");
+ free_run_event_state(run_state);
+ if (res != 0)
{
- struct run_event_state *run_state = new_run_event_state();
- run_state->logging_callback = do_log_and_update_client;
- int res = run_event(run_state, dump_dir_name, force ? "reanalyze" : "analyze");
- free_run_event_state(run_state);
- if (res != 0 && res != -1) /* -1 is "nothing was done", here it is ok */
- {
- r = MW_PLUGIN_ERROR;
- goto ret;
- }
-
- /* Do a load_crash_data_from_crash_dump_dir from (possibly updated)
- * crash dump dir
- */
- if (!DebugDumpToCrashReport(dump_dir_name, pCrashData))
- {
- error_msg("Error loading crash data");
- r = MW_ERROR;
- goto ret;
- }
+ r = MW_PLUGIN_ERROR;
+ goto ret;
}
- catch (CABRTException& e)
+
+ /* Do a create_crash_data_from_dump_dir from (possibly updated)
+ * crash dump dir
+ */
+ *crash_data = DebugDumpToCrashReport(dump_dir_name);
+ if (!*crash_data)
{
- r = MW_CORRUPTED;
- error_msg("%s", e.what());
- if (e.type() == EXCEP_PLUGIN)
- {
- r = MW_PLUGIN_ERROR;
- }
+ error_msg("Error loading crash data");
+ r = MW_ERROR;
}
ret:
+ if (*crash_data == NULL)
+ *crash_data = new_crash_data();
VERB3 log("CreateCrashReport() returns %d", r);
return r;
}
-void RunAction(const char *pActionDir,
- const char *pPluginName,
- const char *pPluginArgs)
-{
- CAction* action = g_pPluginManager->GetAction(pPluginName);
- if (!action)
- {
- /* GetAction() already complained */
- return;
- }
- try
- {
- action->Run(pActionDir, pPluginArgs, /*force:*/ 0);
- }
- catch (CABRTException& e)
- {
- error_msg("Execution of '%s' was not successful: %s", pPluginName, e.what());
- }
-}
-
struct logging_state {
char *last_line;
};
@@ -205,43 +195,55 @@ static char *do_log_and_save_line(char *log_line, void *param)
// Do not trust client_report here!
// dbus handler passes it from user without checking
-report_status_t Report(const map_crash_data_t& client_report,
+report_status_t Report(crash_data_t *client_report,
const vector_string_t& events,
const map_map_string_t& settings,
long caller_uid)
{
- // Get ID fields
- const char *UID = get_crash_data_item_content_or_NULL(client_report, FILENAME_UID);
- const char *dump_dir_name = get_crash_data_item_content_or_NULL(client_report, CD_DUMPDIR);
- if (!UID || !dump_dir_name)
+ report_status_t ret;
+ const char *dump_dir_name = get_crash_item_content_or_NULL(client_report, CD_DUMPDIR);
+ if (!dump_dir_name)
{
- throw CABRTException(EXCEP_ERROR, "Report(): UID or DUMPDIR is missing in client's report data");
+ update_client("Reporting error: %s", "DUMPDIR is missing in client's report data");
+ ret[""].push_back("0"); // REPORT_STATUS_IDX_FLAG
+ ret[""].push_back("DUMPDIR is missing in client's report data"); // REPORT_STATUS_IDX_MSG
+ return ret;
}
// Retrieve corresponding stored record
- map_crash_data_t stored_report;
- mw_result_t r = FillCrashInfo(dump_dir_name, stored_report);
- if (r != MW_OK)
+ crash_data_t *stored_report = FillCrashInfo(dump_dir_name);
+ if (!stored_report)
{
return report_status_t();
}
// Is it allowed for this user to report?
- if (caller_uid != 0 // not called by root
- && strcmp(to_string(caller_uid).c_str(), UID) != 0
- ) {
- const char *inform_all = get_crash_data_item_content_or_NULL(stored_report, FILENAME_INFORMALL);
- if (!inform_all || !string_to_bool(inform_all))
- throw CABRTException(EXCEP_ERROR, "Report(): user with uid %ld can't report crash %s",
- caller_uid, dump_dir_name);
+ if (caller_uid != 0) // not called by root
+ {
+ char caller_uid_str[sizeof(long)*3 + 2];
+ sprintf(caller_uid_str, "%ld", caller_uid);
+ if (strcmp(caller_uid_str, get_crash_item_content_or_die(stored_report, FILENAME_UID)) != 0)
+ {
+ const char *inform_all = get_crash_item_content_or_NULL(stored_report, FILENAME_INFORMALL);
+ if (!inform_all || !string_to_bool(inform_all))
+ {
+ free_crash_data(stored_report);
+ char *errmsg = xasprintf("user with uid %ld can't report crash %s", caller_uid, dump_dir_name);
+ update_client("Reporting error: %s", errmsg);
+ ret[""].push_back("0"); // REPORT_STATUS_IDX_FLAG
+ ret[""].push_back(errmsg); // REPORT_STATUS_IDX_MSG
+ free(errmsg);
+ return ret;
+ }
+ }
}
// Save comment, "how to reproduce", backtrace
//TODO: we should iterate through stored_report and modify all
//modifiable fields which have new data in client_report
- const char *comment = get_crash_data_item_content_or_NULL(client_report, FILENAME_COMMENT);
- const char *reproduce = get_crash_data_item_content_or_NULL(client_report, FILENAME_REPRODUCE);
- const char *backtrace = get_crash_data_item_content_or_NULL(client_report, FILENAME_BACKTRACE);
+ const char *comment = get_crash_item_content_or_NULL(client_report, FILENAME_COMMENT);
+ const char *reproduce = get_crash_item_content_or_NULL(client_report, FILENAME_REPRODUCE);
+ const char *backtrace = get_crash_item_content_or_NULL(client_report, FILENAME_BACKTRACE);
if (comment || reproduce || backtrace)
{
struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
@@ -250,45 +252,47 @@ report_status_t Report(const map_crash_data_t& client_report,
if (comment)
{
dd_save_text(dd, FILENAME_COMMENT, comment);
- add_to_crash_data_ext(stored_report, FILENAME_COMMENT, CD_TXT, CD_ISEDITABLE, comment);
+ add_to_crash_data_ext(stored_report, FILENAME_COMMENT, comment, CD_FLAG_TXT + CD_FLAG_ISEDITABLE);
}
if (reproduce)
{
dd_save_text(dd, FILENAME_REPRODUCE, reproduce);
- add_to_crash_data_ext(stored_report, FILENAME_REPRODUCE, CD_TXT, CD_ISEDITABLE, reproduce);
+ add_to_crash_data_ext(stored_report, FILENAME_REPRODUCE, reproduce, CD_FLAG_TXT + CD_FLAG_ISEDITABLE);
}
if (backtrace)
{
dd_save_text(dd, FILENAME_BACKTRACE, backtrace);
- add_to_crash_data_ext(stored_report, FILENAME_BACKTRACE, CD_TXT, CD_ISEDITABLE, backtrace);
+ add_to_crash_data_ext(stored_report, FILENAME_BACKTRACE, backtrace, CD_FLAG_TXT + CD_FLAG_ISEDITABLE);
}
dd_close(dd);
}
}
/* Remove BIN filenames from stored_report if they are not present in client's data */
- map_crash_data_t::const_iterator its = stored_report.begin();
- while (its != stored_report.end())
+ GHashTableIter iter;
+ char *name;
+ struct crash_item *value;
+ g_hash_table_iter_init(&iter, stored_report);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
{
- if (its->second[CD_TYPE] == CD_BIN)
+ if (value->flags & CD_FLAG_BIN)
{
- std::string key = its->first;
- if (get_crash_data_item_content_or_NULL(client_report, key.c_str()) == NULL)
+ if (get_crash_item_content_or_NULL(client_report, name) == NULL)
{
/* client does not have it -> does not want it passed to events */
- VERB3 log("Won't report BIN file %s:'%s'", key.c_str(), its->second[CD_CONTENT].c_str());
- its++; /* move off the element we will erase */
- stored_report.erase(key);
+ VERB3 log("Won't report BIN file %s:'%s'", name, value->content);
+ g_hash_table_iter_remove(&iter);
continue;
}
}
- its++;
}
VERB3 {
- log_map_crash_data(client_report, " client_report");
- log_map_crash_data(stored_report, " stored_report");
+ log_crash_data(client_report, " client_report");
+ log_crash_data(stored_report, " stored_report");
}
+ free_crash_data(stored_report);
+#define stored_report stored_report_must_not_be_used_below
#define client_report client_report_must_not_be_used_below
// Export overridden settings as environment variables
@@ -310,7 +314,6 @@ report_status_t Report(const map_crash_data_t& client_report,
// Run events
bool at_least_one_reporter_succeeded = false;
- report_status_t ret;
std::string message;
struct logging_state l_state;
struct run_event_state *run_state = new_run_event_state();
@@ -321,10 +324,11 @@ report_status_t Report(const map_crash_data_t& client_report,
std::string event = events[i];
l_state.last_line = NULL;
- int r = run_event(run_state, dump_dir_name, event.c_str());
- if (r == -1)
+ int r = run_event_on_dir_name(run_state, dump_dir_name, event.c_str());
+ if (r == 0 && run_state->children_count == 0)
{
l_state.last_line = xasprintf("Error: no processing is specified for event '%s'", event.c_str());
+ r = -1;
}
if (r == 0)
{
@@ -390,6 +394,7 @@ report_status_t Report(const map_crash_data_t& client_report,
}
return ret;
+#undef stored_report
#undef client_report
}
@@ -470,8 +475,7 @@ static char *do_log(char *log_line, void *param)
return log_line;
}
-mw_result_t LoadDebugDump(const char *dump_dir_name,
- map_crash_data_t& pCrashData)
+mw_result_t LoadDebugDump(const char *dump_dir_name, crash_data_t **crash_data)
{
mw_result_t res;
@@ -492,7 +496,7 @@ mw_result_t LoadDebugDump(const char *dump_dir_name,
run_state->post_run_callback = is_crash_a_dup;
run_state->post_run_param = &state;
run_state->logging_callback = do_log;
- int r = run_event(run_state, dump_dir_name, "post-create");
+ int r = run_event_on_dir_name(run_state, dump_dir_name, "post-create");
free_run_event_state(run_state);
//TODO: consider this case:
@@ -527,7 +531,7 @@ mw_result_t LoadDebugDump(const char *dump_dir_name,
dump_dir_name = state.crash_dump_dup_name;
}
- /* Loads pCrashData (from the *first debugdump dir* if this one is a dup)
+ /* Loads crash_data (from the *first debugdump dir* if this one is a dup)
* Returns:
* MW_OCCURRED: "crash count is != 1" (iow: it is > 1 - dup)
* MW_OK: "crash count is 1" (iow: this is a new crash, not a dup)
@@ -548,9 +552,10 @@ mw_result_t LoadDebugDump(const char *dump_dir_name,
dd_save_text(dd, FILENAME_COUNT, new_count_str);
dd_close(dd);
- res = FillCrashInfo(dump_dir_name, pCrashData);
- if (res == MW_OK)
+ *crash_data = FillCrashInfo(dump_dir_name);
+ if (*crash_data != NULL)
{
+ res = MW_OK;
if (count > 1)
{
log("Crash dump is a duplicate of %s", dump_dir_name);
@@ -568,99 +573,89 @@ mw_result_t LoadDebugDump(const char *dump_dir_name,
return res;
}
-static mw_result_t FillCrashInfo(const char *dump_dir_name,
- map_crash_data_t& pCrashData)
+static crash_data_t *FillCrashInfo(const char *dump_dir_name)
{
struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
- return MW_ERROR;
+ return NULL;
- load_crash_data_from_crash_dump_dir(dd, pCrashData);
+ crash_data_t *crash_data = create_crash_data_from_dump_dir(dd);
char *events = list_possible_events(dd, NULL, "");
dd_close(dd);
- add_to_crash_data_ext(pCrashData, CD_EVENTS, CD_SYS, CD_ISNOTEDITABLE, events);
+ add_to_crash_data_ext(crash_data, CD_EVENTS, events,
+ CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);
free(events);
- add_to_crash_data_ext(pCrashData, CD_DUMPDIR, CD_SYS, CD_ISNOTEDITABLE, dump_dir_name);
+ add_to_crash_data_ext(crash_data, CD_DUMPDIR, dump_dir_name,
+ CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);
- return MW_OK;
+ return crash_data;
}
-vector_map_crash_data_t GetCrashInfos(long caller_uid)
+vector_of_crash_data_t *GetCrashInfos(long caller_uid)
{
- vector_map_crash_data_t retval;
+ vector_of_crash_data_t *retval = new_vector_of_crash_data();
log("Getting crash infos...");
DIR *dir = opendir(DEBUG_DUMPS_DIR);
if (dir != NULL)
{
- try
+ struct dirent *dent;
+ while ((dent = readdir(dir)) != NULL)
{
- struct dirent *dent;
- while ((dent = readdir(dir)) != NULL)
- {
- if (dot_or_dotdot(dent->d_name))
- continue; /* skip "." and ".." */
+ if (dot_or_dotdot(dent->d_name))
+ continue; /* skip "." and ".." */
- char *dump_dir_name = concat_path_file(DEBUG_DUMPS_DIR, dent->d_name);
+ char *dump_dir_name = concat_path_file(DEBUG_DUMPS_DIR, dent->d_name);
- struct stat statbuf;
- if (stat(dump_dir_name, &statbuf) != 0
- || !S_ISDIR(statbuf.st_mode)
- ) {
- goto next; /* not a dir, skip */
- }
+ struct stat statbuf;
+ if (stat(dump_dir_name, &statbuf) != 0
+ || !S_ISDIR(statbuf.st_mode)
+ ) {
+ goto next; /* not a dir, skip */
+ }
- /* Skip directories which are not for this uid */
- if (caller_uid != 0) /* not called by root? */
- {
- char *uid;
- char caller_uid_str[sizeof(long) * 3 + 2];
+ /* Skip directories which are not for this uid */
+ if (caller_uid != 0) /* not called by root? */
+ {
+ char *uid;
+ char caller_uid_str[sizeof(long) * 3 + 2];
- struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
- if (!dd)
- goto next;
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ goto next;
- sprintf(caller_uid_str, "%ld", caller_uid);
- uid = dd_load_text(dd, FILENAME_UID);
- if (strcmp(uid, caller_uid_str) != 0)
+ sprintf(caller_uid_str, "%ld", caller_uid);
+ uid = dd_load_text(dd, FILENAME_UID);
+ if (strcmp(uid, caller_uid_str) != 0)
+ {
+ char *inform_all = dd_load_text_ext(dd, FILENAME_INFORMALL, DD_FAIL_QUIETLY);
+ bool for_all = string_to_bool(inform_all);
+ free(inform_all);
+ if (!for_all)
{
- char *inform_all = dd_load_text_ext(dd, FILENAME_INFORMALL, DD_FAIL_QUIETLY);
- bool for_all = string_to_bool(inform_all);
- free(inform_all);
- if (!for_all)
- {
- dd_close(dd);
- goto next;
- }
+ dd_close(dd);
+ goto next;
}
- dd_close(dd);
}
+ dd_close(dd);
+ }
+ {
+ crash_data_t *crash_data = FillCrashInfo(dump_dir_name);
+ if (!crash_data)
{
- map_crash_data_t info;
- mw_result_t res = FillCrashInfo(dump_dir_name, info);
- switch (res)
- {
- case MW_OK:
- retval.push_back(info);
- break;
- case MW_ERROR:
- error_msg("Dump directory %s doesn't exist or misses crucial files, deleting", dump_dir_name);
- delete_crash_dump_dir(dump_dir_name);
- break;
- default:
- break;
- }
+ error_msg("Dump directory %s doesn't exist or misses crucial files, deleting", dump_dir_name);
+ delete_dump_dir(dump_dir_name);
+ }
+ else
+ {
+ g_ptr_array_add(retval, crash_data);
}
- next:
- free(dump_dir_name);
}
- }
- catch (CABRTException& e)
- {
- error_msg("%s", e.what());
+ next:
+ free(dump_dir_name);
}
closedir(dir);
}
@@ -676,14 +671,14 @@ vector_map_crash_data_t GetCrashInfos(long caller_uid)
* StartJob dbus call already did all the processing, and we just retrieve
* the result from dump directory, which is fast.
*/
-void CreateReport(const char* crash_id, long caller_uid, int force, map_crash_data_t& crashReport)
+void CreateReport(const char* crash_id, long caller_uid, int force, crash_data_t **crash_data)
{
/* FIXME: starting from here, any shared data must be protected with a mutex. */
- mw_result_t res = CreateCrashReport(crash_id, caller_uid, force, crashReport);
+ mw_result_t res = CreateCrashReport(crash_id, caller_uid, force, crash_data);
switch (res)
{
case MW_OK:
- VERB2 log_map_crash_data(crashReport, "crashReport");
+ VERB2 log_crash_data(*crash_data, "crashReport");
break;
case MW_NOENT_ERROR:
error_msg("Can't find crash with id '%s'", crash_id);
@@ -715,18 +710,10 @@ static void* create_report(void* arg)
/* Client name is per-thread, need to set it */
set_client_name(thread_data->peer);
- try
- {
- log("Creating report...");
- map_crash_data_t crashReport;
- CreateReport(thread_data->crash_id, thread_data->caller_uid, thread_data->force, crashReport);
- g_pCommLayer->JobDone(thread_data->peer);
- }
- catch (CABRTException& e)
- {
- error_msg("%s", e.what());
- }
- catch (...) {}
+ log("Creating report...");
+ crash_data_t *crash_data = NULL;
+ CreateReport(thread_data->crash_id, thread_data->caller_uid, thread_data->force, &crash_data);
+ send_dbus_sig_JobDone(thread_data->peer);
set_client_name(NULL);
/* free strduped strings */
@@ -805,10 +792,10 @@ void GetPluginsInfo(map_map_string_t &map_of_plugin_info)
struct dirent *dent;
while ((dent = readdir(dir)) != NULL)
{
- if (!is_regular_file(dent, PLUGINS_CONF_DIR))
- continue;
char *ext = strrchr(dent->d_name, '.');
- if (!ext || strcmp(ext + 1, PLUGINS_CONF_EXTENSION) != 0)
+ if (!ext || strcmp(ext + 1, "conf") != 0)
+ continue;
+ if (!is_regular_file(dent, PLUGINS_CONF_DIR))
continue;
VERB3 log("Found %s", dent->d_name);
*ext = '\0';
@@ -853,10 +840,12 @@ void GetPluginsInfo(map_map_string_t &map_of_plugin_info)
closedir(dir);
}
-void GetPluginSettings(const char *plugin_name, map_plugin_settings_t &plugin_settings)
+map_string_h *GetPluginSettings(const char *plugin_name)
{
char *conf_file = xasprintf(PLUGINS_CONF_DIR"/%s.conf", plugin_name);
- if (LoadPluginSettings(conf_file, plugin_settings, /*skip w/o value:*/ false))
+ map_string_h *settings = new_map_string();
+ if (load_conf_file(conf_file, settings, /*skip w/o value:*/ false))
VERB3 log("Loaded %s.conf", plugin_name);
free(conf_file);
+ return settings;
}
diff --git a/src/daemon/MiddleWare.h b/src/daemon/MiddleWare.h
index 808d7be4..fde29978 100644
--- a/src/daemon/MiddleWare.h
+++ b/src/daemon/MiddleWare.h
@@ -23,7 +23,6 @@
#define MIDDLEWARE_H_
#include "abrt_types.h"
-#include "PluginManager.h"
/**
* An enum contains all return codes.
@@ -46,29 +45,6 @@ typedef enum {
/**
- * Takes care of getting all additional data needed
- * for computing UUIDs and creating a report for particular analyzer
- * plugin. This report could be send somewhere afterwards. If a creation
- * is successful, then a crash report is filled.
- * @param pAnalyzer A name of an analyzer plugin.
- * @param pDebugDumpPath A debugdump dir containing all necessary data.
- * @param pCrashData A filled crash report.
- * @return It return results of operation. See mw_result_t.
- */
-mw_result_t CreateCrashReport(const char *dump_dir_name,
- long caller_uid,
- int force,
- map_crash_data_t& pCrashData);
-/**
- * Activates particular action plugin.
- * @param pActionDir A directory, which is passed as working to a action plugin.
- * @param pPluginName An action plugin name.
- * @param pPluginArgs Action plugin's arguments.
- */
-void RunAction(const char *pActionDir,
- const char *pPluginName,
- const char *pPluginArgs);
-/**
* Reports a crash report to particular receiver. It
* takes an user uid, tries to find user config file and load it. If it
* fails, then default config is used. If pUID is emply string, default
@@ -76,16 +52,14 @@ void RunAction(const char *pActionDir,
* ...).
* @param crash_data
* A crash report.
- * @param reporters
- * List of allowed reporters. Which reporters will be used depends
- * on the analyzer of the crash_data. Reporters missing from this list
- * will not be used.
+ * @param events
+ * List of events to run.
* @param caller_uid
* An user uid.
* @return
- * A report status, which reporters ends successfuly with messages.
+ * A report status: which events finished successfully, with messages.
*/
-report_status_t Report(const map_crash_data_t& crash_data,
+report_status_t Report(crash_data_t *crash_data,
const vector_string_t& events,
const map_map_string_t& settings,
long caller_uid);
@@ -97,15 +71,14 @@ report_status_t Report(const map_crash_data_t& crash_data,
* @param pCrashData A crash info.
* @return It return results of operation. See mw_result_t.
*/
-mw_result_t LoadDebugDump(const char *dump_dir_name,
- map_crash_data_t& pCrashData);
+mw_result_t LoadDebugDump(const char *dump_dir_name, crash_data_t **crash_data);
-vector_map_crash_data_t GetCrashInfos(long caller_uid);
+vector_of_crash_data_t *GetCrashInfos(long caller_uid);
int CreateReportThread(const char* dump_dir_name, long caller_uid, int force, const char* pSender);
-void CreateReport(const char* dump_dir_name, long caller_uid, int force, map_crash_data_t&);
+void CreateReport(const char* dump_dir_name, long caller_uid, int force, crash_data_t **crash_data);
int DeleteDebugDump(const char *dump_dir_name, long caller_uid);
void GetPluginsInfo(map_map_string_t &map_of_plugin_info);
-void GetPluginSettings(const char *plugin_name, map_plugin_settings_t &plugin_settings);
+map_string_h *GetPluginSettings(const char *plugin_name);
#endif /*MIDDLEWARE_H_*/
diff --git a/src/daemon/PluginManager.cpp b/src/daemon/PluginManager.cpp
deleted file mode 100644
index 665a4625..00000000
--- a/src/daemon/PluginManager.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- PluginManager.cpp
-
- Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include <dlfcn.h>
-#include "abrtlib.h"
-#include "abrt_exception.h"
-#include "PluginManager.h"
-
-using namespace std;
-
-
-/**
- * CLoadedModule class. A class which contains a loaded plugin.
- */
-class CLoadedModule
-{
- private:
- /* dlopen'ed library */
- void *m_pHandle;
- const plugin_info_t *m_pPluginInfo;
- CPlugin* (*m_pFnPluginNew)();
-
- public:
- CLoadedModule(void *handle, const char *mod_name);
- ~CLoadedModule() { dlclose(m_pHandle); }
- int GetMagicNumber() { return m_pPluginInfo->m_nMagicNumber; }
- const char *GetVersion() { return m_pPluginInfo->m_sVersion; }
- const char *GetName() { return m_pPluginInfo->m_sName; }
- const char *GetDescription() { return m_pPluginInfo->m_sDescription; }
- const char *GetEmail() { return m_pPluginInfo->m_sEmail; }
- const char *GetWWW() { return m_pPluginInfo->m_sWWW; }
- const char *GetGTKBuilder() { return m_pPluginInfo->m_sGTKBuilder; }
- plugin_type_t GetType() { return m_pPluginInfo->m_Type; }
- CPlugin *PluginNew() { return m_pFnPluginNew(); }
-};
-CLoadedModule::CLoadedModule(void *handle, const char *mod_name)
-{
- m_pHandle = handle;
- /* All errors are fatal */
-#define LOADSYM(fp, handle, name) \
- do { \
- fp = (typeof(fp)) (dlsym(handle, name)); \
- if (!fp) \
- error_msg_and_die("'%s' has no %s entry", mod_name, name); \
- } while (0)
-
- LOADSYM(m_pPluginInfo, handle, "plugin_info");
- LOADSYM(m_pFnPluginNew, handle, "plugin_new");
-#undef LOADSYM
-}
-
-
-/**
- * Text representation of plugin types.
- */
-static const char *const plugin_type_str[] = {
- "Analyzer",
- "Action",
- "Reporter",
- "Database"
-};
-
-
-CPluginManager::CPluginManager()
-{}
-
-CPluginManager::~CPluginManager()
-{}
-
-void CPluginManager::LoadPlugins()
-{
- DIR *dir = opendir(PLUGINS_LIB_DIR);
- if (dir != NULL)
- {
- struct dirent *dent;
- while ((dent = readdir(dir)) != NULL)
- {
- if (!is_regular_file(dent, PLUGINS_LIB_DIR))
- continue;
- char *ext = strrchr(dent->d_name, '.');
- if (!ext || strcmp(ext + 1, PLUGINS_LIB_EXTENSION) != 0)
- continue;
- *ext = '\0';
- if (strncmp(dent->d_name, PLUGINS_LIB_PREFIX, sizeof(PLUGINS_LIB_PREFIX)-1) != 0)
- continue;
- LoadPlugin(dent->d_name + sizeof(PLUGINS_LIB_PREFIX)-1, /*enabled_only:*/ true);
- }
- closedir(dir);
- }
-}
-
-void CPluginManager::UnLoadPlugins()
-{
- map_loaded_module_t::iterator it_module;
- while ((it_module = m_mapLoadedModules.begin()) != m_mapLoadedModules.end())
- {
- UnLoadPlugin(it_module->first.c_str());
- }
-}
-
-CPlugin* CPluginManager::LoadPlugin(const char *pName, bool enabled_only)
-{
- map_plugin_t::iterator it_plugin = m_mapPlugins.find(pName);
- if (it_plugin != m_mapPlugins.end())
- {
- return it_plugin->second; /* ok */
- }
-
- map_string_t plugin_info;
- plugin_info["Name"] = pName;
-
- const char *conf_name = pName;
- if (strncmp(pName, "Kerneloops", sizeof("Kerneloops")-1) == 0)
- {
- /* Kerneloops{,Scanner,Reporter} share the same .conf file */
- conf_name = "Kerneloops";
- }
- map_plugin_settings_t pluginSettings;
- string conf_fullname = ssprintf(PLUGINS_CONF_DIR"/%s."PLUGINS_CONF_EXTENSION, conf_name);
- LoadPluginSettings(conf_fullname.c_str(), pluginSettings);
- m_map_plugin_settings[pName] = pluginSettings;
- /* If settings are empty, most likely .conf file does not exist.
- * Don't mislead the user: */
- VERB3 if (!pluginSettings.empty()) log("Loaded %s.conf", conf_name);
-
- if (enabled_only)
- {
- map_plugin_settings_t::iterator it = pluginSettings.find("Enabled");
- if (it == pluginSettings.end() || !string_to_bool(it->second.c_str()))
- {
- plugin_info["Enabled"] = "no";
- string empty;
- plugin_info["Type"] = empty;
- plugin_info["Version"] = empty;
- plugin_info["Description"] = empty;
- plugin_info["Email"] = empty;
- plugin_info["WWW"] = empty;
- plugin_info["GTKBuilder"] = empty;
- VERB3 log("Plugin %s: 'Enabled' is not set, not loading it (yet)", pName);
- return NULL; /* error */
- }
- }
-
- string libPath = ssprintf(PLUGINS_LIB_DIR"/"PLUGINS_LIB_PREFIX"%s."PLUGINS_LIB_EXTENSION, pName);
- void *handle = dlopen(libPath.c_str(), RTLD_NOW);
- if (!handle)
- {
- error_msg("Can't load '%s': %s", libPath.c_str(), dlerror());
- return NULL; /* error */
- }
- CLoadedModule *module = new CLoadedModule(handle, pName);
- if (module->GetMagicNumber() != PLUGINS_MAGIC_NUMBER
- || module->GetType() < 0
- || module->GetType() > MAX_PLUGIN_TYPE
- ) {
- error_msg("Can't load non-compatible plugin %s: magic %d != %d or type %d is not in [0,%d]",
- pName,
- module->GetMagicNumber(), PLUGINS_MAGIC_NUMBER,
- module->GetType(), MAX_PLUGIN_TYPE);
- delete module;
- return NULL; /* error */
- }
- VERB3 log("Loaded plugin %s v.%s", pName, module->GetVersion());
-
- CPlugin *plugin = NULL;
- try
- {
- plugin = module->PluginNew();
- plugin->Init();
- plugin->SetSettings(pluginSettings);
- }
- catch (CABRTException& e)
- {
- error_msg("Can't initialize plugin %s: %s",
- pName,
- e.what()
- );
- delete plugin;
- delete module;
- return NULL; /* error */
- }
-
- plugin_info["Enabled"] = "yes";
- plugin_info["Type"] = plugin_type_str[module->GetType()];
- //plugin_info["Name"] = module->GetName();
- plugin_info["Version"] = module->GetVersion();
- plugin_info["Description"] = module->GetDescription();
- plugin_info["Email"] = module->GetEmail();
- plugin_info["WWW"] = module->GetWWW();
- plugin_info["GTKBuilder"] = module->GetGTKBuilder();
-
- m_mapLoadedModules[pName] = module;
- m_mapPlugins[pName] = plugin;
- log("Registered %s plugin '%s'", plugin_type_str[module->GetType()], pName);
- return plugin; /* ok */
-}
-
-void CPluginManager::UnLoadPlugin(const char *pName)
-{
- map_loaded_module_t::iterator it_module = m_mapLoadedModules.find(pName);
- if (it_module != m_mapLoadedModules.end())
- {
- map_plugin_t::iterator it_plugin = m_mapPlugins.find(pName);
- if (it_plugin != m_mapPlugins.end()) /* always true */
- {
- it_plugin->second->DeInit();
- delete it_plugin->second;
- m_mapPlugins.erase(it_plugin);
- }
- log("UnRegistered %s plugin %s", plugin_type_str[it_module->second->GetType()], pName);
- delete it_module->second;
- m_mapLoadedModules.erase(it_module);
- }
-}
-
-CAction* CPluginManager::GetAction(const char *pName, bool silent)
-{
- CPlugin *plugin = LoadPlugin(pName);
- if (!plugin)
- {
- error_msg("Plugin '%s' is not registered", pName);
- return NULL;
- }
- if (m_mapLoadedModules[pName]->GetType() != ACTION)
- {
- if (!silent)
- error_msg("Plugin '%s' is not an action plugin", pName);
- return NULL;
- }
- return (CAction*)plugin;
-}
-
-plugin_type_t CPluginManager::GetPluginType(const char *pName)
-{
- CPlugin *plugin = LoadPlugin(pName);
- if (!plugin)
- {
- throw CABRTException(EXCEP_PLUGIN, "Plugin '%s' is not registered", pName);
- }
- map_loaded_module_t::iterator it_module = m_mapLoadedModules.find(pName);
- return it_module->second->GetType();
-}
diff --git a/src/daemon/PluginManager.h b/src/daemon/PluginManager.h
deleted file mode 100644
index c5036fbf..00000000
--- a/src/daemon/PluginManager.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- PluginManager.h - header file for plugin manager. it takes care about
- (un)loading plugins
-
- Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef PLUGINMANAGER_H_
-#define PLUGINMANAGER_H_
-
-#include "abrt_types.h"
-#include "plugin.h"
-#include "analyzer.h"
-#include "action.h"
-
-class CLoadedModule; /* opaque */
-
-/**
- * A class. It takes care of loading, registering and manipulating with
- * plugins. When a plugin is loaded, its library is opened, but no plugin
- * instance is created. It is possible after plugin registration.
- */
-class CPluginManager
-{
- private:
- typedef std::map<std::string, CLoadedModule*> map_loaded_module_t;
- typedef std::map<std::string, CPlugin*> map_plugin_t;
-
- /**
- * Loaded plugins. A key is a plugin name.
- */
- map_loaded_module_t m_mapLoadedModules;
- /**
- * Registered plugins. A key is a plugin name.
- */
- map_plugin_t m_mapPlugins;
- /**
- * List of all possible plugins (loaded or not), with some attributes.
- */
- map_map_string_t m_map_plugin_settings;
-
- public:
- /**
- * A constructor.
- * @param pPluginsConfDir A plugins configuration directory.
- * @param pPluginsLibDir A plugins library directory.
- */
- CPluginManager();
- /**
- * A destructor.
- */
- ~CPluginManager();
- /**
- * A method, which loads all plugins in plugins library direcotry.
- */
- void LoadPlugins();
- /**
- * A method, which unregister and unload all loaded plugins.
- */
- void UnLoadPlugins();
- /**
- * A method, which loads particular plugin.
- * @param pName A plugin name.
- */
- CPlugin* LoadPlugin(const char *pName, bool enabled_only = false);
- /**
- * A method, which unloads particular plugin.
- * @param pName A plugin name.
- */
- void UnLoadPlugin(const char *pName);
- /**
- * A method, which returns instance of particular action plugin.
- * @param pName A plugin name.
- * @return An action plugin.
- */
- CAction* GetAction(const char *pName, bool silent = false);
- /**
- * A method, which returns type of particular plugin.
- * @param pName A plugin name.
- * @return A plugin type.
- */
- plugin_type_t GetPluginType(const char *pName);
-};
-
-#endif /*PLUGINMANAGER_H_*/
diff --git a/src/daemon/Settings.cpp b/src/daemon/Settings.cpp
index 5b9972ba..e25b7959 100644
--- a/src/daemon/Settings.cpp
+++ b/src/daemon/Settings.cpp
@@ -19,8 +19,8 @@
#include "abrtlib.h"
#include "Settings.h"
-#define SECTION_COMMON "Common"
-#define SECTION_CRON "Cron"
+#define SECTION_COMMON "Common"
+#define SECTION_LOG_SCANNERS "LogScanners"
/* Conf file has this format:
* [ section_name1 ]
@@ -31,32 +31,28 @@
*/
/* Static data */
-/* Filled by LoadSettings() */
+/* Filled by load_settings() */
/* map["name"] = "value" strings from [ Common ] section.
* If the same name found on more than one line,
* the values are appended, separated by comma: map["name"] = "value1,value2" */
static map_string_t s_mapSectionCommon;
-/* ... from [ Cron ] */
-static map_string_t s_mapSectionCron;
/* Public data */
-/* Written out exactly in this order by SaveSettings() */
/* [ Common ] */
/* one line: "OpenGPGCheck = value" */
bool g_settings_bOpenGPGCheck = false;
/* one line: "OpenGPGPublicKeys = value1,value2" */
-GList *g_settings_setOpenGPGPublicKeys = NULL;
-GList *g_settings_setBlackListedPkgs = NULL;
-GList *g_settings_setBlackListedPaths = NULL;
-char *g_settings_sWatchCrashdumpArchiveDir = NULL;
+GList * g_settings_setOpenGPGPublicKeys = NULL;
+GList * g_settings_setBlackListedPkgs = NULL;
+GList * g_settings_setBlackListedPaths = NULL;
+char * g_settings_sWatchCrashdumpArchiveDir = NULL;
unsigned int g_settings_nMaxCrashReportsSize = 1000;
bool g_settings_bProcessUnpackaged = false;
-/* [ Cron ] */
-/* many lines, one per key: "map_key = aa_first,bb_first(bb_second),cc_first" */
-map_cron_t g_settings_mapCron;
+/* [ LogScanners ] */
+char * g_settings_sLogScanners = NULL;
/*
@@ -80,107 +76,12 @@ static GList *parse_list(const char* list)
}
if (item->len > 0)
- l = g_list_append(l, xstrdup(item->buf));
+ l = g_list_append(l, xstrdup(item->buf));
strbuf_free(item);
return l;
}
-/* Format: name, name(param),name("param with spaces \"and quotes\"") */
-static vector_pair_string_string_t ParseListWithArgs(const char *pValue, int *err)
-{
- VERB3 log(" ParseListWithArgs(%s)", pValue);
-
- vector_pair_string_string_t pluginsWithArgs;
- std::string item;
- std::string action;
- bool is_quote = false;
- bool is_arg = false;
- for (int ii = 0; pValue[ii]; ii++)
- {
- if (is_quote && pValue[ii] == '\\' && pValue[ii + 1])
- {
- ii++;
- item += pValue[ii];
- continue;
- }
- if (pValue[ii] == '"')
- {
- is_quote = !is_quote;
- /*item += pValue[ii]; - wrong! name("param") must be == name(param) */
- continue;
- }
- if (is_quote)
- {
- item += pValue[ii];
- continue;
- }
- if (pValue[ii] == '(')
- {
- if (!is_arg)
- {
- action = item;
- item = "";
- is_arg = true;
- }
- else
- {
- *err = 1;
- error_msg("Parser error: Invalid syntax on column %d in \"%s\"", ii, pValue);
- }
-
- continue;
- }
- if (pValue[ii] == ')')
- {
- if (is_arg)
- {
- VERB3 log(" adding (%s,%s)", action.c_str(), item.c_str());
- pluginsWithArgs.push_back(make_pair(action, item));
- item = "";
- is_arg = false;
- action = "";
- }
- else
- {
- *err = 1;
- error_msg("Parser error: Invalid syntax on column %d in \"%s\"", ii, pValue);
- }
-
- continue;
- }
- if (pValue[ii] == ',' && !is_arg)
- {
- if (item != "")
- {
- VERB3 log(" adding (%s,%s)", item.c_str(), "");
- pluginsWithArgs.push_back(make_pair(item, ""));
- item = "";
- }
- continue;
- }
- item += pValue[ii];
- }
-
- if (is_quote)
- {
- *err = 1;
- error_msg("Parser error: Unclosed quote in \"%s\"", pValue);
- }
-
- if (is_arg)
- {
- *err = 1;
- error_msg("Parser error: Unclosed argument in \"%s\"", pValue);
- }
- else if (item != "")
- {
- VERB3 log(" adding (%s,%s)", item.c_str(), "");
- pluginsWithArgs.push_back(make_pair(item, ""));
- }
- return pluginsWithArgs;
-}
-
static int ParseCommon()
{
map_string_t::const_iterator end = s_mapSectionCommon.end();
@@ -207,7 +108,7 @@ static int ParseCommon()
it = s_mapSectionCommon.find("MaxCrashReportsSize");
if (it != end)
{
- g_settings_nMaxCrashReportsSize = xatoi_u(it->second.c_str());
+ g_settings_nMaxCrashReportsSize = xatoi_positive(it->second.c_str());
}
it = s_mapSectionCommon.find("ProcessUnpackaged");
if (it != end)
@@ -217,20 +118,6 @@ static int ParseCommon()
return 0; /* no error */
}
-static int ParseCron()
-{
- map_string_t::iterator it = s_mapSectionCron.begin();
- for (; it != s_mapSectionCron.end(); it++)
- {
- int err = 0;
- vector_pair_string_string_t actionsAndReporters = ParseListWithArgs(it->second.c_str(), &err);
- if (err)
- return err;
- g_settings_mapCron[it->first] = actionsAndReporters;
- }
- return 0; /* no error */
-}
-
static void LoadGPGKeys()
{
FILE *fp = fopen(CONF_DIR"/gpg_keys", "r");
@@ -277,7 +164,7 @@ static int ReadConfigurationFromFile(FILE *fp)
value += line[ii];
continue;
}
- if (isspace(line[ii]) && !is_quote)
+ if (isspace(line[ii]) && !is_quote && is_key)
{
continue;
}
@@ -304,8 +191,9 @@ static int ReadConfigurationFromFile(FILE *fp)
section += line[ii];
continue;
}
- if (line[ii] == '=' && !is_quote)
+ if (is_key && line[ii] == '=' && !is_quote)
{
+ while (isspace(line[ii + 1])) ii++;
is_key = false;
key = value;
value = "";
@@ -351,11 +239,9 @@ static int ReadConfigurationFromFile(FILE *fp)
s_mapSectionCommon[key] += ",";
s_mapSectionCommon[key] += value;
}
- else if (section == SECTION_CRON)
+ else if (section == SECTION_LOG_SCANNERS)
{
- if (s_mapSectionCron[key] != "")
- s_mapSectionCron[key] += ",";
- s_mapSectionCron[key] += value;
+ g_settings_sLogScanners = xstrdup(value.c_str());
}
else
{
@@ -372,7 +258,7 @@ static int ReadConfigurationFromFile(FILE *fp)
}
/* abrt daemon loads .conf file */
-int LoadSettings()
+int load_settings()
{
int err = 0;
@@ -387,8 +273,6 @@ int LoadSettings()
if (err == 0)
err = ParseCommon();
- if (err == 0)
- err = ParseCron();
if (err == 0)
{
@@ -411,49 +295,36 @@ map_abrt_settings_t GetSettings()
map_abrt_settings_t ABRTSettings;
ABRTSettings[SECTION_COMMON] = s_mapSectionCommon;
- ABRTSettings[SECTION_CRON] = s_mapSectionCron;
return ABRTSettings;
}
-/* dbus call to change some .conf file data */
-void SetSettings(const map_abrt_settings_t& pSettings, const char *dbus_sender)
+///* dbus call to change some .conf file data */
+//void SetSettings(const map_abrt_settings_t& pSettings, const char *dbus_sender)
+//{
+// map_abrt_settings_t::const_iterator it = pSettings.find(SECTION_COMMON);
+// map_abrt_settings_t::const_iterator end = pSettings.end();
+// if (it != end)
+// {
+// s_mapSectionCommon = it->second;
+// ParseCommon();
+// }
+//}
+
+void free_settings()
{
- map_abrt_settings_t::const_iterator it = pSettings.find(SECTION_COMMON);
- map_abrt_settings_t::const_iterator end = pSettings.end();
- if (it != end)
- {
- s_mapSectionCommon = it->second;
- ParseCommon();
- }
- it = pSettings.find(SECTION_CRON);
- if (it != end)
- {
- s_mapSectionCron = it->second;
- ParseCron();
- }
-}
-
-void settings_free()
-{
- for (GList *li = g_settings_setOpenGPGPublicKeys; li != NULL; li = g_list_next(li))
- free((char*)li->data);
-
- g_list_free(g_settings_setOpenGPGPublicKeys);
+ list_free_with_free(g_settings_setOpenGPGPublicKeys);
g_settings_setOpenGPGPublicKeys = NULL;
- for (GList *li = g_settings_setBlackListedPkgs; li != NULL; li = g_list_next(li))
- free((char*)li->data);
-
- g_list_free(g_settings_setBlackListedPkgs);
+ list_free_with_free(g_settings_setBlackListedPkgs);
g_settings_setBlackListedPkgs = NULL;
- for (GList *li = g_settings_setBlackListedPaths; li != NULL; li = g_list_next(li))
- free((char*)li->data);
-
- g_list_free(g_settings_setBlackListedPaths);
+ list_free_with_free(g_settings_setBlackListedPaths);
g_settings_setBlackListedPaths = NULL;
free(g_settings_sWatchCrashdumpArchiveDir);
g_settings_sWatchCrashdumpArchiveDir = NULL;
+
+ free(g_settings_sLogScanners);
+ g_settings_sLogScanners = NULL;
}
diff --git a/src/daemon/Settings.h b/src/daemon/Settings.h
index 71824c74..dce6407d 100644
--- a/src/daemon/Settings.h
+++ b/src/daemon/Settings.h
@@ -20,25 +20,33 @@
#define SETTINGS_H_
#include "abrt_types.h"
-#include <glib.h>
-typedef map_vector_pair_string_string_t map_cron_t;
+#ifdef __cplusplus
+
typedef map_map_string_t map_abrt_settings_t;
+// looks unused to me.
+// Ok to grep for SetSettings and delete after 2011-04-01.
+// void SetSettings(const map_abrt_settings_t& pSettings, const char *dbus_sender);
+map_abrt_settings_t GetSettings();
+
+extern "C" {
+#endif
-extern GList *g_settings_setOpenGPGPublicKeys;
-extern GList *g_settings_setBlackListedPkgs;
-extern GList *g_settings_setBlackListedPaths;
+extern GList * g_settings_setOpenGPGPublicKeys;
+extern GList * g_settings_setBlackListedPkgs;
+extern GList * g_settings_setBlackListedPaths;
extern unsigned int g_settings_nMaxCrashReportsSize;
extern bool g_settings_bOpenGPGCheck;
extern bool g_settings_bProcessUnpackaged;
-extern char *g_settings_sWatchCrashdumpArchiveDir;
-extern map_cron_t g_settings_mapCron;
+extern char * g_settings_sWatchCrashdumpArchiveDir;
-int LoadSettings();
-void SaveSettings();
-void SetSettings(const map_abrt_settings_t& pSettings, const char * dbus_sender);
-map_abrt_settings_t GetSettings();
+extern char * g_settings_sLogScanners;
+
+int load_settings();
+void free_settings();
-void settings_free();
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/src/daemon/abrt-action-save-package-data.cpp b/src/daemon/abrt-action-save-package-data.c
index cb880bd3..5be712cf 100644
--- a/src/daemon/abrt-action-save-package-data.cpp
+++ b/src/daemon/abrt-action-save-package-data.c
@@ -91,7 +91,6 @@ static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
char *package_short_name = NULL;
char *component = NULL;
char *script_name = NULL; /* only if "interpreter /path/to/script" */
- char *dsc = NULL;
/* note: "goto ret" statements below free all the above variables,
* but they don't dd_close(dd) */
@@ -100,7 +99,6 @@ static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
component = xstrdup("kernel");
package_full_name = xstrdup("kernel");
package_short_name = xstrdup("kernel");
- dsc = rpm_get_description(package_short_name);
}
else
{
@@ -124,21 +122,7 @@ static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
if (!dd)
goto ret; /* return 1 (failure) */
dd_save_text(dd, FILENAME_PACKAGE, "");
- dd_save_text(dd, FILENAME_DESCRIPTION, "Crashed executable does not belong to any installed package");
dd_save_text(dd, FILENAME_COMPONENT, "");
-//TODO: move hostname saving to a more logical place
- if (!remote)
- {
- char host[HOST_NAME_MAX + 1];
- int ret = gethostname(host, HOST_NAME_MAX);
- if (ret < 0)
- {
- perror_msg("gethostname");
- host[0] = '\0';
- }
- host[HOST_NAME_MAX] = '\0';
- dd_save_text(dd, FILENAME_HOSTNAME, host);
- }
goto ret0; /* no error */
}
log("Executable '%s' doesn't belong to any package", executable);
@@ -220,7 +204,6 @@ static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
}
component = rpm_get_component(executable);
- dsc = rpm_get_description(package_short_name);
dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
@@ -231,27 +214,10 @@ static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
{
dd_save_text(dd, FILENAME_PACKAGE, package_full_name);
}
- if (dsc)
- {
- dd_save_text(dd, FILENAME_DESCRIPTION, dsc);
- }
if (component)
{
dd_save_text(dd, FILENAME_COMPONENT, component);
}
-//TODO: move hostname saving to a more logical place
- if (!remote)
- {
- char host[HOST_NAME_MAX + 1];
- int ret = gethostname(host, HOST_NAME_MAX);
- if (ret < 0)
- {
- perror_msg("gethostname");
- host[0] = '\0';
- }
- host[HOST_NAME_MAX] = '\0';
- dd_save_text(dd, FILENAME_HOSTNAME, host);
- }
dd_close(dd);
@@ -262,41 +228,40 @@ static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
free(package_short_name);
free(component);
free(script_name);
- free(dsc);
return error;
}
-static const char *dump_dir_name = ".";
-static const char abrt_action_save_package_data_usage[] =
- PROGNAME" [options] -d DIR\n"
- "\n"
- "Query package database and save package name, component, and description";
-enum {
- OPT_v = 1 << 0,
- OPT_d = 1 << 1,
- OPT_s = 1 << 2,
-};
-/* Keep enum above and order of options below in sync! */
-static struct options abrt_action_save_package_data_options[] = {
- OPT__VERBOSE(&g_verbose),
- OPT_STRING('d', NULL, &dump_dir_name, "DIR", "Crash dump directory"),
- OPT_BOOL( 's', NULL, NULL, "Log to syslog"),
- OPT_END()
-};
-
int main(int argc, char **argv)
{
char *env_verbose = getenv("ABRT_VERBOSE");
if (env_verbose)
g_verbose = atoi(env_verbose);
- unsigned opts = parse_opts(argc, argv, abrt_action_save_package_data_options,
- abrt_action_save_package_data_usage);
+ const char *dump_dir_name = ".";
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ PROGNAME" [options] -d DIR\n"
+ "\n"
+ "Query package database and save package name, component, and description"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_s = 1 << 2,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR", _("Crash dump directory")),
+ OPT_BOOL( 's', NULL, NULL , _("Log to syslog")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
msg_prefix = PROGNAME;
-
if (opts & OPT_s)
{
openlog(msg_prefix, 0, LOG_DAEMON);
@@ -304,8 +269,8 @@ int main(int argc, char **argv)
}
VERB1 log("Loading settings");
- if (LoadSettings() != 0)
- return 1; /* syntax error (looged already by LoadSettings) */
+ if (load_settings() != 0)
+ return 1; /* syntax error (logged already by load_settings) */
VERB1 log("Initializing rpm library");
rpm_init();
diff --git a/src/daemon/abrt-handle-crashdump.c b/src/daemon/abrt-handle-crashdump.c
index 2217c67a..e7847c5f 100644
--- a/src/daemon/abrt-handle-crashdump.c
+++ b/src/daemon/abrt-handle-crashdump.c
@@ -38,11 +38,13 @@ int main(int argc, char **argv)
if (env_verbose)
g_verbose = atoi(env_verbose);
- const char *program_usage = _(
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
PROGNAME" [-vs]" /*" [-c CONFFILE]"*/ " -d DIR -e EVENT\n"
" or: "PROGNAME" [-vs]" /*" [-c CONFFILE]"*/ " [-d DIR] -l[PFX]\n"
"\n"
- "Handle crash dump according to rules in abrt_event.conf");
+ "Handle crash dump according to rules in abrt_event.conf"
+ );
enum {
OPT_v = 1 << 0,
OPT_s = 1 << 1,
@@ -61,11 +63,12 @@ int main(int argc, char **argv)
// OPT_STRING( 'c', NULL, &conf_filename, "CONFFILE", _("Configuration file" )),
OPT_END()
};
-
- unsigned opts = parse_opts(argc, argv, program_options, program_usage);
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
if (!(opts & (OPT_e|OPT_l)))
- parse_usage_and_die(program_usage, program_options);
+ show_usage_and_die(program_usage_string, program_options);
+
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
+ msg_prefix = PROGNAME;
if (opts & OPT_s)
{
openlog(msg_prefix, 0, LOG_DAEMON);
@@ -87,17 +90,10 @@ int main(int argc, char **argv)
/* -e EVENT: run event */
- /* Need to add LIBEXEC_DIR to PATH, because otherwise abrt-action-*
- * are not found by exec()
- */
- const char *env_path = getenv("PATH");
- if (!env_path) env_path = "";
- putenv(xasprintf("PATH=%s%s%s", LIBEXEC_DIR, (!env_path[0] ? "" : ":"), env_path));
-
struct run_event_state *run_state = new_run_event_state();
run_state->logging_callback = do_log;
- int r = run_event(run_state, dump_dir_name ? dump_dir_name : ".", event);
- if (r == -1)
+ int r = run_event_on_dir_name(run_state, dump_dir_name ? dump_dir_name : ".", event);
+ if (r == 0 && run_state->children_count == 0)
error_msg_and_die("No actions are found for event '%s'", event);
free_run_event_state(run_state);
diff --git a/src/daemon/abrt-server.c b/src/daemon/abrt-server.c
index fdf66e59..de22f427 100644
--- a/src/daemon/abrt-server.c
+++ b/src/daemon/abrt-server.c
@@ -16,11 +16,10 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "abrtlib.h"
-#include "dump_dir.h"
-#include "abrt_crash_dump.h" /* FILENAME_foo */
#include "hooklib.h"
#include "parse_options.h"
+#define PROGNAME "abrt-server"
/* Maximal length of backtrace. */
#define MAX_BACKTRACE_SIZE (1024*1024)
@@ -108,9 +107,9 @@ static void create_debug_dump()
This directory is renamed to final directory name after
all files have been stored into it.
*/
- char *path = xasprintf(DEBUG_DUMPS_DIR"/%s-%ld-%u.new",
+ char *path = xasprintf(DEBUG_DUMPS_DIR"/%s-%s-%u.new",
dir_basename,
- (long)time(NULL),
+ iso_date_string(NULL),
pid);
/* No need to check the path length, as all variables used are limited, and dd_create()
fails if the path is too long. */
@@ -120,6 +119,7 @@ static void create_debug_dump()
{
error_msg_and_die("Error creating crash dump %s", path);
}
+ dd_create_basic_files(dd, client_uid);
dd_save_text(dd, FILENAME_ANALYZER, analyzer);
dd_save_text(dd, FILENAME_EXECUTABLE, executable);
@@ -275,31 +275,32 @@ static void process_message(const char *message)
static void dummy_handler(int sig_unused) {}
-static const char abrt_server_usage[] = "abrt-server [options]";
-enum {
- OPT_v = 1 << 0,
- OPT_u = 1 << 1,
- OPT_s = 1 << 2,
-};
-/* Keep enum above and order of options below in sync! */
-static struct options abrt_server_options[] = {
- OPT__VERBOSE(&g_verbose),
- OPT_INTEGER( 'u' , 0, &client_uid, "Use UID as client uid"),
- OPT_BOOL( 's' , 0, NULL, "Log to syslog"),
- OPT_END()
-};
-
int main(int argc, char **argv)
{
char *env_verbose = getenv("ABRT_VERBOSE");
if (env_verbose)
g_verbose = atoi(env_verbose);
- unsigned opts = parse_opts(argc, argv, abrt_server_options,
- abrt_server_usage);
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ PROGNAME" [options]"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_u = 1 << 1,
+ OPT_s = 1 << 2,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_INTEGER('u', NULL, &client_uid, _("Use UID as client uid")),
+ OPT_BOOL( 's', NULL, NULL , _("Log to syslog")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
- msg_prefix = xasprintf("abrt-server[%u]", getpid());
+ msg_prefix = xasprintf(PROGNAME"[%u]", getpid());
if (opts & OPT_s)
{
openlog(msg_prefix, 0, LOG_DAEMON);
diff --git a/src/daemon/abrt.conf b/src/daemon/abrt.conf
index 07ea51a0..7f5a9c58 100644
--- a/src/daemon/abrt.conf
+++ b/src/daemon/abrt.conf
@@ -8,7 +8,7 @@ OpenGPGCheck = yes
# Blacklisted packages
#
-BlackList = nspluginwrapper, valgrind, strace
+BlackList = nspluginwrapper,valgrind,strace
# Process crashes in executables which do not belong to any package?
#
@@ -16,7 +16,7 @@ ProcessUnpackaged = no
# Blacklisted executable paths (shell patterns)
#
-BlackListedPaths = /usr/share/doc/*, */example*
+BlackListedPaths = /usr/share/doc/*,*/example*
# Enable this if you want abrtd to auto-unpack crashdump tarballs which appear
# in this directory (for example, uploaded via ftp, scp etc).
@@ -30,12 +30,7 @@ BlackListedPaths = /usr/share/doc/*, */example*
MaxCrashReportsSize = 1000
-# Which Action plugins to run repeatedly
+# So far we support only one line here
#
-[ Cron ]
-# h:m - at h:m
-# s - every s seconds
-
-120 = KerneloopsScanner
-
-#02:00 = FileTransfer
+[ LogScanners ]
+abrt-dump-oops = abrt-dump-oops -drw /var/log/messages
diff --git a/src/daemon/abrt.conf.5 b/src/daemon/abrt.conf.5
index 3f9d8c39..84c8f002 100644
--- a/src/daemon/abrt.conf.5
+++ b/src/daemon/abrt.conf.5
@@ -65,18 +65,6 @@ all the C/C++ programs.
.TP
.B Kerneloops = \fIplugin\fP
This plugin will be used in the case of kernel crashes.
-.SS [ Cron ]
-This section specifies tasks that will be run at some specified time. You can specify
-either a time of day in the format
-.br
-.B hh:mm = \fIplugin\fP
-.br
-or an interval
-.br
-.B ss = \fIplugin2\fP
-.br
-in this case, \fIplugin2\fP will be run every \fIss\fP seconds (this number
-can be greater than 60).
.SH "SEE ALSO"
.IR abrtd (8),
.IR abrt-plugins (7)
diff --git a/src/daemon/abrt_event.conf b/src/daemon/abrt_event.conf
index 45c9017a..23765718 100644
--- a/src/daemon/abrt_event.conf
+++ b/src/daemon/abrt_event.conf
@@ -1,33 +1,49 @@
-# This table specifies which programs should be run
+# This configuration file specifies which programs should be run
# when the specified event occurs in crash dump lifetime.
#
# Example:
# EVENT=post-create { pwd; date; }>/tmp/dt; echo $HOSTNAME `uname -r`
#
-# Each line may have conditions to be checked
-# before the program is run.
+# Rule starts with a line with non-space leading character.
+# All subsequent lines which start with space or tab form one rule.
+# Note that separating newline is *retained*. Example:
+# EVENT=post-create date >/tmp/dt # semicolon is not needed here!
+# echo $HOSTNAME `uname -r`
+#
+# Rules may be commented out with #. One # is sufficient to comment out
+# even a multi-line rule (no need to comment out every line).
+#
+# Rule of the form "include GLOB_PATTERN" recurses to each file which matches
+# GLOB_PATTERN. Example: "include post-create.d/*.conf"
+#
+
+include events.d/*.conf
+
+# Any other rules specify which programs to run on the crash dump.
+#
+# Each rule may have conditions to be checked before the program is run.
#
# Conditions have form VAR=VAL, where VAR is either word "EVENT"
# or a name of crash dump element to be checked (for example,
# "executable", "package", hostname" etc).
#
-# If all conditions match, the program is run in the shell.
+# If all conditions match, the remaining part of the rule
+# (the "program" part) is run in the shell.
# All shell language constructs are valid.
# All stdout and stderr output is captured and passed to abrt
# and possibly to abrt's frontends and shown to the user.
#
-# If the program terminates with nonzero exitcode,
+# If the program terminates with nonzero exit code,
# the event processing is considered unsuccessful and is stopped.
# Last captured output line, if any, is considered to be
# the error message indicating the reason of the failure,
# and may be used by abrt as such.
#
-# If the program terminates successfully, next line is read
+# If the program terminates successfully, next rule is read
# and processed. This process is repeated until the end of this file.
# abrt-action-analyze-c needs package name, save package data first
EVENT=post-create abrt-action-save-package-data
-EVENT=post-create analyzer=CCpp abrt-action-analyze-c
EVENT=post-create analyzer=Python abrt-action-analyze-python
EVENT=post-create analyzer=Kerneloops abrt-action-analyze-oops
# If you want all users (not just root) to be able to see oopses:
@@ -38,24 +54,20 @@ EVENT=post-create analyzer=Kerneloops abrt-action-analyze-oops
# user interaction, uncomment this line:
#EVENT=post-create analyzer=Kerneloops abrt-action-kerneloops
# Example: if you want to save sosreport immediately at the moment of a crash:
-#EVENT=post-create nice sosreport --tmp-dir "$DUMP_DIR" --batch --only=anaconda --only=bootloader --only=devicemapper --only=filesys --only=hardware --only=kernel --only=libraries --only=memory --only=networking --only=nfsserver --only=pam --only=process --only=rpm -k rpm.rpmva=off --only=ssh --only=startup --only=yum && { rm sosreport*.md5; mv sosreport*.tar.bz2 sosreport.tar.bz2; mv sosreport*.tar.xz sosreport.tar.xz; true; } 2>/dev/null
-
-#TODO: implement this (or add this functionality to abrt-action-install-debuginfo):
-#EVENT=analyze analyzer=CCpp backtrace= trim-debuginfo-cache /var/cache/abrt-di 4096m
-# Additional directories to search for debuginfos can be specified
-# in the third argument (its format is CACHEDIR[:DEBUGINFODIR...]).
-# For example, you can specify a network-mounted shared store
-# of all debuginfos this way.
-EVENT=analyze analyzer=CCpp backtrace= abrt-action-install-debuginfo.py "--core=$DUMP_DIR/coredump" "--tmpdir=/var/run/abrt/$$-$RANDOM" --cache=/var/cache/abrt-di
-EVENT=analyze analyzer=CCpp backtrace= abrt-action-generate-backtrace
-
-# Same as "analyze", but executed when user requests "refresh" in GUI
-#EVENT=reanalyze analyzer=CCpp trim-debuginfo-cache /var/cache/abrt-di 4096m
-EVENT=reanalyze analyzer=CCpp abrt-action-install-debuginfo.py "--core=$DUMP_DIR/coredump" "--tmpdir=/var/run/abrt/$$-$RANDOM" --cache=/var/cache/abrt-di
-EVENT=reanalyze analyzer=CCpp abrt-action-generate-backtrace
+#EVENT=post-create
+ nice sosreport --tmp-dir "$DUMP_DIR" --batch \
+ --only=anaconda --only=bootloader --only=devicemapper \
+ --only=filesys --only=hardware --only=kernel --only=libraries \
+ --only=memory --only=networking --only=nfsserver --only=pam \
+ --only=process --only=rpm -k rpm.rpmva=off --only=ssh \
+ --only=startup --only=yum --only=general --only=x11 \
+ && {
+ rm sosreport*.md5
+ mv sosreport*.tar.bz2 sosreport.tar.bz2
+ mv sosreport*.tar.xz sosreport.tar.xz
+ true
+ } 2>/dev/null
EVENT=report analyzer=Kerneloops abrt-action-kerneloops
-EVENT=report_Bugzilla analyzer=CCpp abrt-action-bugzilla -c /etc/abrt/plugins/Bugzilla.conf
-EVENT=report_Logger analyzer=CCpp abrt-action-print -o /var/log/abrt.log
EVENT=report_Bugzilla analyzer=Python abrt-action-bugzilla -c /etc/abrt/plugins/Bugzilla.conf
EVENT=report_Logger analyzer=Python abrt-action-print -o /var/log/abrt.log
diff --git a/src/lib/CommLayerInner.cpp b/src/daemon/comm_layer_inner.cpp
index 3c102d6e..9d5ddfc2 100644
--- a/src/lib/CommLayerInner.cpp
+++ b/src/daemon/comm_layer_inner.cpp
@@ -19,10 +19,9 @@
#include <pthread.h>
#include <map>
#include "abrtlib.h"
+#include "CommLayerServerDBus.h"
#include "comm_layer_inner.h"
-static CObserver *s_pObs;
-
typedef std::map<uint64_t, std::string> map_uint_str_t;
static map_uint_str_t s_mapClientID;
static pthread_mutex_t s_map_mutex;
@@ -31,9 +30,6 @@ static bool s_map_mutex_inited;
/* called via [p]error_msg() */
static void warn_client(const char *msg)
{
- if (!s_pObs)
- return;
-
uint64_t key = uint64_t(pthread_self());
pthread_mutex_lock(&s_map_mutex);
@@ -42,12 +38,13 @@ static void warn_client(const char *msg)
pthread_mutex_unlock(&s_map_mutex);
if (peer)
- s_pObs->Warning(msg, peer);
+ {
+ send_dbus_sig_Warning(msg, peer);
+ }
}
-void init_daemon_logging(CObserver *pObs)
+void init_daemon_logging(void)
{
- s_pObs = pObs;
if (!s_map_mutex_inited)
{
s_map_mutex_inited = true;
@@ -71,9 +68,6 @@ void set_client_name(const char *name)
void update_client(const char *fmt, ...)
{
- if (!s_pObs)
- return;
-
uint64_t key = uint64_t(pthread_self());
pthread_mutex_lock(&s_map_mutex);
@@ -89,6 +83,8 @@ void update_client(const char *fmt, ...)
char *msg = xvasprintf(fmt, p);
va_end(p);
- s_pObs->Status(msg, peer);
+ VERB1 log("Update('%s'): %s", peer, msg);
+ send_dbus_sig_Update(msg, peer);
+
free(msg);
}
diff --git a/src/include/comm_layer_inner.h b/src/daemon/comm_layer_inner.h
index 2cca9add..48eb2010 100644
--- a/src/include/comm_layer_inner.h
+++ b/src/daemon/comm_layer_inner.h
@@ -20,18 +20,11 @@
#define COMMLAYERINNER_H_
#ifdef __cplusplus
-
-#include "observer.h"
-
-void init_daemon_logging(CObserver *pObs);
-
-#endif
-
-
-#ifdef __cplusplus
extern "C" {
#endif
+void init_daemon_logging(void);
+
/*
* Set client's name (dbus ID). NULL unsets it.
*/
diff --git a/src/daemon/rpm.c b/src/daemon/rpm.c
index a726d357..1295211e 100644
--- a/src/daemon/rpm.c
+++ b/src/daemon/rpm.c
@@ -16,7 +16,6 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include <glib.h>
#include "abrtlib.h"
#include "rpm.h"
@@ -150,7 +149,6 @@ bool CheckHash(const char* pPackage, const char* pPath)
goto error;
rpmfi fi = rpmfiNew(ts, header, RPMTAG_BASENAMES, RPMFI_NOHEADER);
- pgpHashAlgo hashAlgo;
std::string headerHash;
char computedHash[1024] = "";
@@ -158,8 +156,8 @@ bool CheckHash(const char* pPackage, const char* pPath)
{
if (strcmp(pPath, rpmfiFN(fi)) == 0)
{
- headerHash = rpmfiFDigestHex(fi, &hashAlgo);
- rpmDoDigest(hashAlgo, pPath, 1, (unsigned char*) computedHash, NULL);
+ headerHash = rpmfiFDigestHex(fi, NULL);
+ rpmDoDigest(rpmfiDigestAlgo(fi), pPath, 1, (unsigned char*) computedHash, NULL);
ret = (headerHash != "" && headerHash == computedHash);
break;
}
@@ -172,27 +170,6 @@ error:
}
*/
-char* rpm_get_description(const char* pkg)
-{
- char *dsc = NULL;
- const char *errmsg = NULL;
- rpmts ts = rpmtsCreate();
-
- rpmdbMatchIterator iter = rpmtsInitIterator(ts, RPMTAG_NAME, pkg, 0);
- Header header = rpmdbNextIterator(iter);
- if (!header)
- goto error;
-
- dsc = headerFormat(header, "%{SUMMARY}\n\n%{DESCRIPTION}", &errmsg);
- if (!dsc && errmsg)
- error_msg("cannot get summary and description. reason: %s", errmsg);
-
-error:
- rpmdbFreeIterator(iter);
- rpmtsFree(ts);
- return dsc;
-}
-
char* rpm_get_component(const char* filename)
{
char *ret = NULL;
diff --git a/src/daemon/rpm.h b/src/daemon/rpm.h
index 12b11ca8..11f02809 100644
--- a/src/daemon/rpm.h
+++ b/src/daemon/rpm.h
@@ -58,12 +58,6 @@ void rpm_load_gpgkey(const char* filename);
int rpm_chk_fingerprint(const char* pkg);
/**
- * Gets a package description.
- * @param pkg A package name.
- * @return A package description.
- */
-char* rpm_get_description(const char* pkg);
-/**
* Gets a package name. This package contains particular
* file. If the file doesn't belong to any package, empty string is
* returned.
diff --git a/src/gui-gtk/Makefile.am b/src/gui-gtk/Makefile.am
new file mode 100644
index 00000000..a0d0de40
--- /dev/null
+++ b/src/gui-gtk/Makefile.am
@@ -0,0 +1,45 @@
+bin_PROGRAMS = abrt-gtk
+
+abrt_gtk_SOURCES = \
+ abrt-gtk.h abrt-gtk.c \
+ main.c
+abrt_gtk_CFLAGS = \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ -DICON_DIR=\"${datadir}/abrt/icons/hicolor/48x48/status\" \
+ $(GTK_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Werror
+# -I/usr/include/glib-2.0
+# -I/usr/lib/glib-2.0/include
+# $(LIBNOTIFY_CFLAGS)
+# $(DBUS_GLIB_CFLAGS)
+abrt_gtk_LDADD = \
+ ../lib/libreport.la \
+ ../lib/libabrt_dbus.la \
+ -lglib-2.0 \
+ -lgthread-2.0 \
+ $(GTK_LIBS) \
+ $(DBUS_LIBS)
+# $(LIBNOTIFY_LIBS)
+
+#test_report_SOURCES = \
+# test_report.c
+#test_report_CPPFLAGS = \
+# -I$(srcdir)/../include/report -I$(srcdir)/../include \
+# $(GLIB_CFLAGS) \
+# -D_GNU_SOURCE \
+# -Wall -Werror
+#test_report_LDADD = \
+# ../lib/libreport.la
+
+DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+
+@INTLTOOL_DESKTOP_RULE@
diff --git a/src/gui-gtk/abrt-gtk.c b/src/gui-gtk/abrt-gtk.c
new file mode 100644
index 00000000..fc945d76
--- /dev/null
+++ b/src/gui-gtk/abrt-gtk.c
@@ -0,0 +1,307 @@
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "abrtlib.h"
+#include "abrt_dbus.h"
+#include "abrt-gtk.h"
+
+static GtkListStore *s_dumps_list_store;
+static GtkWidget *s_treeview;
+
+enum
+{
+ COLUMN_REPORTED,
+ COLUMN_REASON,
+ COLUMN_DIRNAME,
+ COLUMN_LATEST_CRASH_STR,
+ COLUMN_LATEST_CRASH,
+ COLUMN_DUMP_DIR,
+ NUM_COLUMNS
+};
+
+void add_directory_to_dirlist(const char *dirname)
+{
+ struct dump_dir *dd = dd_opendir(dirname, DD_OPEN_READONLY);
+ if (!dd)
+ return;
+
+ time_t time = atoi(dd_load_text(dd, FILENAME_TIME));
+ struct tm *ptm = localtime(&time);
+ char time_buf[60];
+ size_t time_len = strftime(time_buf, sizeof(time_buf)-1, "%c", ptm);
+ time_buf[time_len] = '\0';
+
+ char *msg = dd_load_text_ext(dd, FILENAME_MESSAGE, 0
+ | DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE
+ | DD_FAIL_QUIETLY
+ );
+ const char *reported = (msg ? GTK_STOCK_YES : GTK_STOCK_NO);
+ free(msg);
+ char *reason = dd_load_text(dd, FILENAME_REASON);
+
+ GtkTreeIter iter;
+ gtk_list_store_append(s_dumps_list_store, &iter);
+ gtk_list_store_set(s_dumps_list_store, &iter,
+ COLUMN_REPORTED, reported,
+ COLUMN_REASON, reason,
+ COLUMN_DIRNAME, dd->dd_dirname,
+ //OPTION: time format
+ COLUMN_LATEST_CRASH_STR, time_buf,
+ COLUMN_LATEST_CRASH, (int)time,
+ COLUMN_DUMP_DIR, dirname,
+ -1);
+
+ free(reason);
+
+ dd_close(dd);
+ VERB1 log("added: %s", dirname);
+}
+
+
+/* create_main_window and helpers */
+
+static void on_row_activated_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
+ if (selection)
+ {
+ GtkTreeIter iter;
+ GtkTreeModel *store = gtk_tree_view_get_model(treeview);
+ if (gtk_tree_selection_get_selected(selection, &store, &iter) == TRUE)
+ {
+ GValue d_dir = { 0 };
+ gtk_tree_model_get_value(store, &iter, COLUMN_DUMP_DIR, &d_dir);
+
+ pid_t pid = vfork();
+ if (pid == 0)
+ {
+ /* Undo signal(SIGCHLD, SIG_IGN), or child inherits it and gets terribly confused */
+ signal(SIGCHLD, SIG_DFL);
+
+ const char *dirname= g_value_get_string(&d_dir);
+ VERB1 log("Executing: %s %s", "bug-reporting-wizard", dirname);
+ execlp("bug-reporting-wizard", "bug-reporting-wizard", dirname, NULL);
+ perror_msg_and_die("Can't execute %s", "bug-reporting-wizard");
+ }
+ }
+ }
+}
+
+static gint on_key_press_event_cb(GtkTreeView *treeview, GdkEventKey *key, gpointer unused)
+{
+ int k = key->keyval;
+
+ if (k == GDK_Delete || k == GDK_KP_Delete)
+ {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
+ if (selection)
+ {
+ GtkTreeIter iter;
+ GtkTreeModel *store = gtk_tree_view_get_model(treeview);
+ if (gtk_tree_selection_get_selected(selection, &store, &iter) == TRUE)
+ {
+ GtkTreePath *old_path = gtk_tree_model_get_path(store, &iter);
+
+ GValue d_dir = { 0 };
+ gtk_tree_model_get_value(store, &iter, COLUMN_DUMP_DIR, &d_dir);
+ const char *dump_dir_name = g_value_get_string(&d_dir);
+
+ VERB1 log("Deleting '%s'", dump_dir_name);
+ if (delete_dump_dir_possibly_using_abrtd(dump_dir_name) == 0)
+ {
+ gtk_list_store_remove(s_dumps_list_store, &iter);
+ }
+ else
+ {
+ /* Strange. Deletion did not succeed. Someone else deleted it?
+ * Rescan the whole list */
+ gtk_list_store_clear(s_dumps_list_store);
+ scan_dirs_and_add_to_dirlist();
+ }
+
+ /* Try to retain the same cursor position */
+ sanitize_cursor(old_path);
+ gtk_tree_path_free(old_path);
+ }
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void add_columns(GtkTreeView *treeview)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Reported"),
+ renderer,
+ "stock_id",
+ COLUMN_REPORTED,
+ NULL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sort_column_id(column, COLUMN_REPORTED);
+ gtk_tree_view_append_column(treeview, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Problem"),
+ renderer,
+ "text",
+ COLUMN_REASON,
+ NULL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sort_column_id(column, COLUMN_REASON);
+ gtk_tree_view_append_column(treeview, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Stored in"),
+ renderer,
+ "text",
+ COLUMN_DIRNAME,
+ NULL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sort_column_id(column, COLUMN_DIRNAME);
+ gtk_tree_view_append_column(treeview, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Last occurrence"),
+ renderer,
+ "text",
+ COLUMN_LATEST_CRASH_STR,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, COLUMN_LATEST_CRASH);
+ gtk_tree_view_append_column(treeview, column);
+}
+
+GtkWidget *create_menu(void)
+{
+ /* main bar */
+ GtkWidget *menu = gtk_menu_bar_new();
+ GtkWidget *file_item = gtk_menu_item_new_with_mnemonic(_("_File"));
+ GtkWidget *edit_item = gtk_menu_item_new_with_mnemonic(_("_Edit"));
+ GtkWidget *help_item = gtk_menu_item_new_with_mnemonic(_("_Help"));
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), file_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), edit_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), help_item);
+
+ /* file submenu */
+ GtkWidget *file_submenu = gtk_menu_new();
+ GtkWidget *quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
+ gtk_menu_shell_append(GTK_MENU_SHELL(file_submenu), quit_item);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_item), file_submenu);
+
+ /* edit submenu */
+ GtkWidget *edit_submenu = gtk_menu_new();
+ GtkWidget *plugins_item = gtk_menu_item_new_with_mnemonic(_("_Plugins"));
+ GtkWidget *preferences_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL);
+ gtk_menu_shell_append(GTK_MENU_SHELL(edit_submenu), plugins_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(edit_submenu), preferences_item);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_item), edit_submenu);
+
+ /* help submenu */
+ GtkWidget *help_submenu = gtk_menu_new();
+ GtkWidget *log_item = gtk_menu_item_new_with_mnemonic(_("View _log"));
+ GtkWidget *online_help_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP, NULL);
+ GtkWidget *about_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
+ gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), log_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), online_help_item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), about_item);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(help_item), help_submenu);
+
+ return menu;
+}
+
+GtkWidget *create_main_window(void)
+{
+ /* main window */
+ GtkWidget *main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size(GTK_WINDOW(main_window), 600, 700);
+ gtk_window_set_title(GTK_WINDOW(main_window), _("Automatic Bug Reporting Tool"));
+ gtk_window_set_icon_name(GTK_WINDOW(main_window), "abrt");
+
+ GtkWidget *main_vbox = gtk_vbox_new(false, 0);
+
+ /* scrolled region inside main window */
+ GtkWidget *scroll_win = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_win),
+ GTK_SHADOW_ETCHED_IN);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_box_pack_start(GTK_BOX(main_vbox), create_menu(), false, false, 0);
+ gtk_box_pack_start(GTK_BOX(main_vbox), scroll_win, true, true, 0);
+ gtk_container_add(GTK_CONTAINER(main_window), main_vbox);
+
+ /* tree view inside scrolled region */
+ s_treeview = gtk_tree_view_new();
+ add_columns(GTK_TREE_VIEW(s_treeview));
+ gtk_container_add(GTK_CONTAINER(scroll_win), s_treeview);
+
+ /* Create data store for the list and attach it */
+ s_dumps_list_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, /* reported */
+ G_TYPE_STRING, /* executable */
+ G_TYPE_STRING, /* hostname */
+ G_TYPE_STRING, /* time */
+ G_TYPE_INT, /* unix time - used for sort */
+ G_TYPE_STRING);/* dump dir path */
+ gtk_tree_view_set_model(GTK_TREE_VIEW(s_treeview), GTK_TREE_MODEL(s_dumps_list_store));
+
+ /* Double click/Enter handler */
+ g_signal_connect(s_treeview, "row-activated", G_CALLBACK(on_row_activated_cb), NULL);
+ /* Delete handler */
+ g_signal_connect(s_treeview, "key-press-event", G_CALLBACK(on_key_press_event_cb), NULL);
+ /* Quit when user closes the main window */
+ g_signal_connect(main_window, "destroy", gtk_main_quit, NULL);
+
+ return main_window;
+}
+
+void sanitize_cursor(GtkTreePath *preferred_path)
+{
+ GtkTreePath *path;
+
+ gtk_tree_view_get_cursor(GTK_TREE_VIEW(s_treeview), &path, /* GtkTreeViewColumn** */ NULL);
+ if (path)
+ {
+ /* Cursor exists already */
+ goto ret;
+ }
+
+ if (preferred_path)
+ {
+ /* Try to position cursor on preferred_path */
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(s_treeview), preferred_path,
+ /* GtkTreeViewColumn *focus_column */ NULL, /* start_editing */ false);
+
+ /* Did it work? */
+ gtk_tree_view_get_cursor(GTK_TREE_VIEW(s_treeview), &path, /* GtkTreeViewColumn** */ NULL);
+ if (path) /* yes */
+ {
+ goto ret;
+ }
+ }
+
+ /* Try to position cursor on 1st element */
+ GtkTreeIter iter;
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(s_dumps_list_store), &iter))
+ {
+ /* We have at least one element, put cursor on it */
+
+ /* Get path from iter pointing to 1st element */
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(s_dumps_list_store), &iter);
+
+ /* Use it to set cursor */
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(s_treeview), path,
+ /* GtkTreeViewColumn *focus_column */ NULL, /* start_editing */ false);
+ }
+ /* else we have no elements */
+
+ ret:
+ gtk_tree_path_free(path);
+
+ /* Without this, the *header* of the list gets the focus. Ugly. */
+ gtk_widget_grab_focus(s_treeview);
+}
diff --git a/src/gui-gtk/abrt-gtk.h b/src/gui-gtk/abrt-gtk.h
new file mode 100644
index 00000000..d67ca6ce
--- /dev/null
+++ b/src/gui-gtk/abrt-gtk.h
@@ -0,0 +1,5 @@
+GtkWidget *create_main_window(void);
+void add_directory_to_dirlist(const char *dirname);
+void sanitize_cursor(GtkTreePath *preferred_path);
+
+void scan_dirs_and_add_to_dirlist();
diff --git a/src/gui-gtk/main.c b/src/gui-gtk/main.c
new file mode 100644
index 00000000..d80256e9
--- /dev/null
+++ b/src/gui-gtk/main.c
@@ -0,0 +1,101 @@
+#include <gtk/gtk.h>
+#include "abrtlib.h"
+#include "parse_options.h"
+#include "abrt-gtk.h"
+
+#define PROGNAME "abrt-gtk"
+
+static char **s_dirs;
+
+static void scan_directory_and_add_to_dirlist(const char *path)
+{
+ DIR *dp = opendir(path);
+ if (!dp)
+ {
+ /* We don't want to yell if, say, $HOME/.abrt/spool doesn't exist */
+ //perror_msg("Can't open directory '%s'", path);
+ return;
+ }
+
+ struct dirent *dent;
+ while ((dent = readdir(dp)) != NULL)
+ {
+ if (dot_or_dotdot(dent->d_name))
+ continue; /* skip "." and ".." */
+
+ char *full_name = concat_path_file(path, dent->d_name);
+ struct stat statbuf;
+ if (stat(full_name, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
+ add_directory_to_dirlist(full_name);
+ free(full_name);
+ }
+ closedir(dp);
+}
+
+void scan_dirs_and_add_to_dirlist(void)
+{
+ char **argv = s_dirs;
+ while (*argv)
+ scan_directory_and_add_to_dirlist(*argv++);
+}
+
+int main(int argc, char **argv)
+{
+ /* I18n */
+ setlocale(LC_ALL, "");
+
+ gtk_init(&argc, &argv);
+
+ char *env_verbose = getenv("ABRT_VERBOSE");
+ if (env_verbose)
+ g_verbose = atoi(env_verbose);
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ PROGNAME" [-v] [DIR]...\n\n"
+ "Shows list of ABRT dump directories in specified DIR(s)\n"
+ "(default DIRs: "DEBUG_DUMPS_DIR" $HOME/.abrt/spool)"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_END()
+ };
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
+
+ putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
+
+ GtkWidget *main_window = create_main_window();
+
+ const char *default_dirs[] = {
+ "/var/spool/abrt",
+ NULL,
+ NULL,
+ };
+ argv += optind;
+ if (!argv[0])
+ {
+ char *home = getenv("HOME");
+ if (home)
+ default_dirs[1] = concat_path_file(home, ".abrt/spool");
+ argv = (char**)default_dirs;
+ }
+ s_dirs = argv;
+
+ scan_dirs_and_add_to_dirlist();
+
+ gtk_widget_show_all(main_window);
+
+ sanitize_cursor(NULL);
+
+ /* Prevent zombies when we spawn wizard */
+ signal(SIGCHLD, SIG_IGN);
+
+ /* Enter main loop */
+ gtk_main();
+
+ return 0;
+}
diff --git a/src/gui-wizard-gtk/Makefile.am b/src/gui-wizard-gtk/Makefile.am
new file mode 100644
index 00000000..4281c470
--- /dev/null
+++ b/src/gui-wizard-gtk/Makefile.am
@@ -0,0 +1,60 @@
+# bug-reporting-wizard binary
+bin_PROGRAMS = bug-reporting-wizard
+
+bug_reporting_wizard_SOURCES = \
+ wizard.h wizard.c \
+ main.c \
+ wizard_glade.c
+
+# Required for gtk_builder_connect_signals() to work correctly:
+# -g
+# -Wl,--export-dynamic
+bug_reporting_wizard_CFLAGS = \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DDATA_DIR=\"$(datadir)\" \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ -DICON_DIR=\"${datadir}/abrt/icons/hicolor/48x48/status\" \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Werror
+
+# Required for gtk_builder_connect_signals() to work correctly:
+# -lgmodule-2.0
+# -lgthread-2.0
+bug_reporting_wizard_LDADD = \
+ ../lib/libreport.la \
+ ../lib/libabrt_dbus.la \
+ $(GLIB_LIBS) \
+ $(GTK_LIBS) \
+ $(DBUS_LIBS)
+
+
+# we don't want to install it, just make it part of tarball
+# created by make dist
+GLADE_FILES = wizard.glade
+#pkgdata_DATA = $(GLADE_FILES)
+EXTRA_DIST = $(GLADE_FILES)
+
+
+# For internal glade file storage in the binary:
+wizard.c: wizard_glade.c
+
+wizard_glade.c: wizard.glade
+ { \
+ echo '#define WIZARD_GLADE_CONTENTS "\'; \
+ cat $(srcdir)/wizard.glade | sed -e 's/"/\\"/g' -e 's/?/\\?/g' -e 's/$$/\\/g'; \
+ echo '"'; \
+ } >wizard_glade.c
+
+
+DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+
+@INTLTOOL_DESKTOP_RULE@
diff --git a/src/gui-wizard-gtk/main.c b/src/gui-wizard-gtk/main.c
new file mode 100644
index 00000000..85f962cd
--- /dev/null
+++ b/src/gui-wizard-gtk/main.c
@@ -0,0 +1,80 @@
+#include <gtk/gtk.h>
+#include "abrtlib.h"
+#include "parse_options.h"
+#include "wizard.h"
+
+#define PROGNAME "bug-reporting-wizard"
+
+char *g_glade_file = NULL;
+char *g_dump_dir_name = NULL;
+char *g_analyze_label_selected = NULL;
+char *g_analyze_events = NULL;
+char *g_reanalyze_events = NULL;
+char *g_report_events = NULL;
+crash_data_t *g_cd;
+
+
+void reload_crash_data_from_dump_dir(void)
+{
+ free_crash_data(g_cd);
+ free(g_analyze_events);
+ free(g_reanalyze_events);
+ free(g_report_events);
+
+ struct dump_dir *dd = dd_opendir(g_dump_dir_name, DD_OPEN_READONLY);
+ if (!dd)
+ xfunc_die(); /* dd_opendir already logged error msg */
+ g_cd = create_crash_data_from_dump_dir(dd);
+ g_analyze_events = list_possible_events(dd, NULL, "analyze");
+ g_reanalyze_events = list_possible_events(dd, NULL, "reanalyze");
+ g_report_events = list_possible_events(dd, NULL, "report");
+ dd_close(dd);
+}
+
+int main(int argc, char **argv)
+{
+ gtk_init(&argc, &argv);
+
+ char *env_verbose = getenv("ABRT_VERBOSE");
+ if (env_verbose)
+ g_verbose = atoi(env_verbose);
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ PROGNAME" [-v] [-g GUI_FILE] DIR\n\n"
+ "GUI tool to analyze and report ABRT crash in specified DIR"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_g = 1 << 1,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING('g', NULL, &g_glade_file, "FILE" , _("Alternate GUI file")),
+ OPT_END()
+ };
+
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
+
+ putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
+
+ argv += optind;
+ if (!argv[0] || argv[1]) /* zero or >1 arguments */
+ show_usage_and_die(program_usage_string, program_options);
+
+ g_dump_dir_name = xstrdup(argv[0]);
+
+ create_assistant();
+
+ g_custom_logger = &show_error_as_msgbox;
+
+ reload_crash_data_from_dump_dir();
+
+ update_gui_state_from_crash_data();
+
+ /* Enter main loop */
+ gtk_main();
+
+ return 0;
+}
diff --git a/src/gui-wizard-gtk/wizard.c b/src/gui-wizard-gtk/wizard.c
new file mode 100644
index 00000000..77699480
--- /dev/null
+++ b/src/gui-wizard-gtk/wizard.c
@@ -0,0 +1,968 @@
+#include <gtk/gtk.h>
+#include "abrtlib.h"
+#include "abrt_dbus.h"
+#include "wizard.h"
+
+#define DEFAULT_WIDTH 800
+#define DEFAULT_HEIGHT 500
+
+GtkAssistant *g_assistant;
+
+GtkBox *g_box_analyzers;
+GtkLabel *g_lbl_analyze_log;
+GtkTextView *g_tv_analyze_log;
+GtkBox *g_box_reporters;
+GtkLabel *g_lbl_report_log;
+GtkTextView *g_tv_report_log;
+GtkContainer *g_container_details1;
+GtkContainer *g_container_details2;
+
+GtkLabel *g_lbl_cd_reason;
+GtkTextView *g_tv_backtrace;
+GtkTextView *g_tv_reproduce;
+GtkTextView *g_tv_comment;
+GtkTreeView *g_tv_details;
+GtkListStore *g_ls_details;
+GtkWidget *g_widget_warnings_area;
+GtkBox *g_box_warning_labels;
+GtkToggleButton *g_tb_approve_bt;
+GtkButton *g_btn_refresh;
+
+/* required for search in bt */
+guint g_timeout = 0;
+GtkEntry * g_search_entry_bt;
+
+
+static GtkBuilder *builder;
+static PangoFontDescription *monospace_font;
+
+
+/* THE PAGE FLOW
+ * page_1: analyze action selection
+ * page_2: analyze progress
+ * page_3: reporter selection
+ * page_4: backtrace editor
+ * page_5: how to + user comments
+ * page_6: summary
+ * page_7: reporting progress
+ */
+
+/* Use of arrays (instead of, say, #defines to C strings)
+ * allows cheaper page_obj_t->name == PAGE_FOO comparisons
+ * instead of strcmp.
+ */
+static const gchar PAGE_SUMMARY[] = "page_0";
+static const gchar PAGE_ANALYZE_SELECTOR[] = "page_1";
+static const gchar PAGE_ANALYZE_PROGRESS[] = "page_2";
+static const gchar PAGE_REPORTER_SELECTOR[] = "page_3";
+static const gchar PAGE_BACKTRACE_APPROVAL[] = "page_4";
+static const gchar PAGE_HOWTO[] = "page_5";
+static const gchar PAGE_REPORT[] = "page_6";
+static const gchar PAGE_REPORT_PROGRESS[] = "page_7";
+
+static const gchar *const page_names[] =
+{
+ PAGE_SUMMARY,
+ PAGE_ANALYZE_SELECTOR,
+ PAGE_ANALYZE_PROGRESS,
+ PAGE_REPORTER_SELECTOR,
+ PAGE_BACKTRACE_APPROVAL,
+ PAGE_HOWTO,
+ PAGE_REPORT,
+ PAGE_REPORT_PROGRESS,
+ NULL
+};
+
+typedef struct
+{
+ const gchar *name;
+ const gchar *title;
+ GtkAssistantPageType type;
+ GtkWidget *page_widget;
+} page_obj_t;
+
+static page_obj_t pages[] =
+{
+ /* Page types:
+ * INTRO: only [Fwd] button is shown
+ * CONTENT: normal page
+ * CONFIRM: has [Apply] instead of [Fwd] and emits "apply" signal
+ * PROGRESS: skipped on backward navigation
+ * SUMMARY: has only [Close] button
+ */
+ /* glade element name , on-screen text , type */
+ { PAGE_SUMMARY , "Problem description" , GTK_ASSISTANT_PAGE_CONTENT },
+ { PAGE_ANALYZE_SELECTOR , "Select analyzer" , GTK_ASSISTANT_PAGE_CONFIRM },
+ { PAGE_ANALYZE_PROGRESS , "Analyzing" , GTK_ASSISTANT_PAGE_CONTENT },
+ /* Some reporters don't need backtrace, we can skip bt page for them.
+ * Therefore we want to know reporters _before_ we go to bt page
+ */
+ { PAGE_REPORTER_SELECTOR , "Select reporter" , GTK_ASSISTANT_PAGE_CONTENT },
+ { PAGE_BACKTRACE_APPROVAL , "Review the backtrace" , GTK_ASSISTANT_PAGE_CONTENT },
+ { PAGE_HOWTO , "Provide additional information", GTK_ASSISTANT_PAGE_CONTENT },
+ { PAGE_REPORT , "Confirm data to report", GTK_ASSISTANT_PAGE_CONFIRM },
+ /* Was GTK_ASSISTANT_PAGE_PROGRESS */
+ { PAGE_REPORT_PROGRESS , "Reporting" , GTK_ASSISTANT_PAGE_SUMMARY },
+ { NULL }
+};
+
+
+/* Utility functions */
+
+static void remove_child_widget(GtkWidget *widget, gpointer container)
+{
+ /* Destroy will safely remove it and free the memory
+ * if there are no refs left
+ */
+ gtk_widget_destroy(widget);
+}
+
+static void save_dialog_response(GtkDialog *dialog, gint response_id, gpointer user_data)
+{
+ *(gint*)user_data = response_id;
+}
+
+struct dump_dir *steal_if_needed(struct dump_dir *dd)
+{
+ if (!dd)
+ xfunc_die(); /* error msg was already logged */
+
+ if (dd->locked)
+ return dd;
+
+ dd_close(dd);
+
+ char *HOME = getenv("HOME");
+ if (HOME && HOME[0])
+ HOME = concat_path_file(HOME, ".abrt/spool");
+ else
+ HOME = xstrdup("/tmp");
+
+ GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_assistant),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_OK_CANCEL,
+ _("Need writable directory, but '%s' is not writable."
+ " Move it to '%s' and operate on the moved copy?"),
+ g_dump_dir_name, HOME
+ );
+ gint response = GTK_RESPONSE_CANCEL;
+ g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(save_dialog_response), &response);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ if (response != GTK_RESPONSE_OK)
+ return NULL;
+
+ dd = steal_directory(HOME, g_dump_dir_name);
+ if (!dd)
+ return NULL; /* Stealing failed. Error msg was already logged */
+
+ /* Delete old dir and switch to new one.
+ * Don't want to keep new dd open across deletion,
+ * therefore it's a bit more complicated.
+ */
+ char *old_name = g_dump_dir_name;
+ g_dump_dir_name = xstrdup(dd->dd_dirname);
+ dd_close(dd);
+
+ gtk_window_set_title(GTK_WINDOW(g_assistant), g_dump_dir_name);
+ delete_dump_dir_possibly_using_abrtd(old_name); //TODO: if (deletion_failed) error_msg("BAD")?
+ free(old_name);
+
+ dd = dd_opendir(g_dump_dir_name, 0);
+ if (!dd)
+ xfunc_die(); /* error msg was already logged */
+
+ return dd;
+}
+
+void show_error_as_msgbox(const char *msg)
+{
+ GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g_assistant),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CLOSE,
+ "%s", msg
+ );
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+}
+
+static void load_text_to_text_view(GtkTextView *tv, const char *name)
+{
+ const char *str = g_cd ? get_crash_item_content_or_NULL(g_cd, name) : NULL;
+ gtk_text_buffer_set_text(gtk_text_view_get_buffer(tv), (str ? str : ""), -1);
+}
+
+static gchar *get_malloced_string_from_text_view(GtkTextView *tv)
+{
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer(tv);
+ GtkTextIter start;
+ GtkTextIter end;
+ gtk_text_buffer_get_start_iter(buffer, &start);
+ gtk_text_buffer_get_end_iter(buffer, &end);
+ return gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
+}
+
+static void save_text_if_changed(const char *name, const char *new_value)
+{
+ const char *old_value = g_cd ? get_crash_item_content_or_NULL(g_cd, name) : "";
+ if (!old_value)
+ old_value = "";
+ if (strcmp(new_value, old_value) != 0)
+ {
+ struct dump_dir *dd = dd_opendir(g_dump_dir_name, DD_OPEN_READONLY);
+ dd = steal_if_needed(dd);
+ if (dd && dd->locked)
+ {
+ dd_save_text(dd, name, new_value);
+ add_to_crash_data_ext(g_cd, name, new_value, CD_FLAG_TXT | CD_FLAG_ISEDITABLE);
+ }
+//FIXME: else: what to do with still-unsaved data in the widget??
+ dd_close(dd);
+ }
+}
+
+static void save_text_from_text_view(GtkTextView *tv, const char *name)
+{
+ gchar *new_str = get_malloced_string_from_text_view(tv);
+ save_text_if_changed(name, new_str);
+ free(new_str);
+}
+
+
+/* update_gui_state_from_crash_data */
+
+static void analyze_rb_was_toggled(GtkButton *button, gpointer user_data)
+{
+ const char *label = gtk_button_get_label(button);
+ if (label)
+ {
+ free(g_analyze_label_selected);
+ g_analyze_label_selected = xstrdup(label);
+ }
+}
+
+static void report_tb_was_toggled(GtkButton *button, gpointer user_data)
+{
+ GList *reporters = gtk_container_get_children(GTK_CONTAINER(g_box_reporters));
+ GList *li = reporters;
+ if (reporters)
+ {
+ for (; li; li = li->next)
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(li->data)) == TRUE)
+ break;
+ }
+ g_list_free(reporters);
+ gtk_assistant_set_page_complete(g_assistant,
+ pages[PAGENO_REPORTER_SELECTOR].page_widget,
+ li != NULL /* true if at least one checkbox is active */
+ );
+}
+
+static GtkWidget *add_event_buttons(GtkBox *box, char *event_name, GCallback func, bool radio, const char *prev_selected)
+{
+VERB2 log("removing all buttons from box %p", box);
+ gtk_container_foreach(GTK_CONTAINER(box), &remove_child_widget, box);
+
+ bool have_activated_btn = false;
+ GtkWidget *first_button = NULL;
+ while (event_name[0])
+ {
+ char *event_name_end = strchr(event_name, '\n');
+ *event_name_end = '\0';
+
+VERB2 log("adding button '%s' to box %p", event_name, box);
+ GtkWidget *button = radio
+ ? gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_button), event_name)
+ : gtk_check_button_new_with_label(event_name);
+ if (!first_button)
+ first_button = button;
+
+ if (prev_selected && strcmp(prev_selected, event_name) == 0)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), true);
+ have_activated_btn = true;
+ prev_selected = NULL;
+ }
+
+ *event_name_end = '\n';
+ event_name = event_name_end + 1;
+
+ gtk_box_pack_start(box, button, /*expand*/ false, /*fill*/ false, /*padding*/ 0);
+
+ if (func)
+ g_signal_connect(G_OBJECT(button), "toggled", func, NULL);
+ }
+ return (have_activated_btn ? NULL : first_button);
+}
+
+static void append_item_to_details_ls(gpointer name, gpointer value, gpointer data)
+{
+ crash_item *item = (crash_item*)value;
+ GtkTreeIter iter;
+
+ gtk_list_store_append(g_ls_details, &iter);
+
+ //FIXME: use the value representation here
+ /* If text and not multiline... */
+ if ((item->flags & CD_FLAG_TXT) && !strchr(item->content, '\n'))
+ {
+ gtk_list_store_set(g_ls_details, &iter,
+ DETAIL_COLUMN_NAME, (char *)name,
+ DETAIL_COLUMN_VALUE, item->content,
+ //DETAIL_COLUMN_PATH, xasprintf("%s%s", g_dump_dir_name, name),
+ -1);
+ }
+ else
+ {
+ gtk_list_store_set(g_ls_details, &iter,
+ DETAIL_COLUMN_NAME, (char *)name,
+ DETAIL_COLUMN_VALUE, _("(click here to view/edit)"),
+ //DETAIL_COLUMN_PATH, xasprintf("%s%s", g_dump_dir_name, name),
+ -1);
+ //WARNING: will leak xasprintf results above if uncommented
+ }
+}
+
+void update_gui_state_from_crash_data(void)
+{
+ const char *reason = get_crash_item_content_or_NULL(g_cd, FILENAME_REASON);
+ gtk_label_set_text(g_lbl_cd_reason, reason ? reason : _("(no description)"));
+
+ gtk_list_store_clear(g_ls_details);
+ g_hash_table_foreach(g_cd, append_item_to_details_ls, NULL);
+
+ load_text_to_text_view(g_tv_backtrace, FILENAME_BACKTRACE);
+ load_text_to_text_view(g_tv_reproduce, FILENAME_REPRODUCE);
+ load_text_to_text_view(g_tv_comment, FILENAME_COMMENT);
+
+//Doesn't work: shows empty page
+// if (!g_analyze_events[0])
+// {
+// /* No available analyze events, go to reporter selector page */
+// gtk_assistant_set_current_page(GTK_ASSISTANT(assistant), PAGENO_REPORTER_SELECTOR);
+// }
+
+ /* Update analyze radio buttons */
+ GtkWidget *first_rb = add_event_buttons(g_box_analyzers, g_analyze_events, G_CALLBACK(analyze_rb_was_toggled), /*radio:*/ true, /*prev:*/ g_analyze_label_selected);
+ /* Update the value of currently selected analyzer */
+ if (first_rb)
+ {
+ const char *label = gtk_button_get_label(GTK_BUTTON(first_rb));
+ if (label)
+ {
+ free(g_analyze_label_selected);
+ g_analyze_label_selected = xstrdup(label);
+ }
+ }
+
+ /* Update reporter checkboxes */
+ /* Remember names of selected reporters */
+ GList *old_reporters = gtk_container_get_children(GTK_CONTAINER(g_box_reporters));
+ for (GList *li = old_reporters; li; li = li->next)
+ {
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(li->data)) == TRUE)
+ li->data = xstrdup(gtk_button_get_label(GTK_BUTTON(li->data)));
+ else
+ li->data = NULL;
+ }
+ old_reporters = g_list_remove_all(old_reporters, NULL);
+ /* Delete old checkboxes and create new ones */
+ add_event_buttons(g_box_reporters, g_report_events, /*callback:*/ G_CALLBACK(report_tb_was_toggled), /*radio:*/ false, /*prev:*/ NULL);
+ /* Re-select new reporters which were selected before we deleted them */
+ GList *new_reporters = gtk_container_get_children(GTK_CONTAINER(g_box_reporters));
+ for (GList *li_new = new_reporters; li_new; li_new = li_new->next)
+ {
+ const char *new_name = gtk_button_get_label(GTK_BUTTON(li_new->data));
+ for (GList *li_old = old_reporters; li_old; li_old = li_old->next)
+ {
+ if (strcmp(new_name, li_old->data) == 0)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(li_new->data), true);
+ break;
+ }
+ }
+ }
+ g_list_free(new_reporters);
+ list_free_with_free(old_reporters);
+ /* Update readiness state of reporter selector page */
+ report_tb_was_toggled(NULL, NULL);
+
+ /* We can't just do gtk_widget_show_all once in main:
+ * We created new widgets (buttons). Need to make them visible.
+ */
+ gtk_widget_show_all(GTK_WIDGET(g_assistant));
+
+ if (g_reanalyze_events[0])
+ gtk_widget_show(GTK_WIDGET(g_btn_refresh));
+ else
+ gtk_widget_hide(GTK_WIDGET(g_btn_refresh));
+}
+
+
+/* start_event_run */
+
+struct analyze_event_data
+{
+ struct run_event_state *run_state;
+ const char *event_name;
+ GList *more_events;
+ GtkWidget *page_widget;
+ GtkLabel *status_label;
+ GtkTextView *tv_log;
+ const char *end_msg;
+ GIOChannel *channel;
+ int fd;
+ /*guint event_source_id;*/
+};
+
+static void append_to_textview(GtkTextView *tv, const char *str, int len)
+{
+ GtkTextBuffer *tb = gtk_text_view_get_buffer(tv);
+
+ /* Ensure we insert text at the end */
+ GtkTextIter text_iter;
+ gtk_text_buffer_get_iter_at_offset(tb, &text_iter, -1);
+ gtk_text_buffer_place_cursor(tb, &text_iter);
+
+ gtk_text_buffer_insert_at_cursor(tb, str, len >= 0 ? len : strlen(str));
+
+ /* Scroll so that the end of the log is visible */
+ gtk_text_buffer_get_iter_at_offset(tb, &text_iter, -1);
+ gtk_text_view_scroll_to_iter(tv, &text_iter,
+ /*within_margin:*/ 0.0, /*use_align:*/ FALSE, /*xalign:*/ 0, /*yalign:*/ 0);
+}
+
+static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ struct analyze_event_data *evd = data;
+
+ /* Read and insert the output into the log pane */
+ char buf[256]; /* usually we get one line, no need to have big buf */
+ int r;
+ while ((r = read(evd->fd, buf, sizeof(buf))) > 0)
+ {
+ append_to_textview(evd->tv_log, buf, r);
+ }
+
+ if (r < 0 && errno == EAGAIN)
+ /* We got all data, but fd is still open. Done for now */
+ return TRUE; /* "please don't remove this event (yet)" */
+
+ /* EOF/error. Wait for child to actually exit, collect status */
+ int status;
+ waitpid(evd->run_state->command_pid, &status, 0);
+ int retval = WEXITSTATUS(status);
+ if (WIFSIGNALED(status))
+ retval = WTERMSIG(status) + 128;
+
+ /* Stop if exit code is not 0, or no more commands */
+ if (retval != 0
+ || spawn_next_command(evd->run_state, g_dump_dir_name, evd->event_name) < 0
+ ) {
+ VERB1 log("done running event on '%s': %d", g_dump_dir_name, retval);
+//append_to_textview(evd->tv_log, msg);
+
+ for (;;)
+ {
+ if (!evd->more_events)
+ {
+ char *msg = xasprintf(evd->end_msg, retval);
+ gtk_label_set_text(evd->status_label, msg);
+ free(msg);
+ /* Unfreeze assistant */
+ gtk_assistant_set_page_complete(g_assistant, evd->page_widget, true);
+
+ /*g_source_remove(evd->event_source_id);*/
+ close(evd->fd);
+ free_run_event_state(evd->run_state);
+ free(evd);
+
+ reload_crash_data_from_dump_dir();
+ update_gui_state_from_crash_data();
+
+ return FALSE; /* "please remove this event" */
+ }
+
+ evd->event_name = evd->more_events->data;
+ evd->more_events = g_list_remove(evd->more_events, evd->more_events->data);
+
+ if (prepare_commands(evd->run_state, g_dump_dir_name, evd->event_name) != 0
+ && spawn_next_command(evd->run_state, g_dump_dir_name, evd->event_name) >= 0
+ ) {
+ VERB1 log("running event '%s' on '%s'", evd->event_name, g_dump_dir_name);
+ break;
+ }
+ /* No commands needed?! (This is untypical) */
+//TODO: msg?
+//append_to_textview(evd->tv_log, msg);
+ }
+ }
+
+ /* New command was started. Continue waiting for input */
+
+ /* Transplant cmd's output fd onto old one, so that main loop
+ * is none the wiser that fd it waits on has changed
+ */
+ xmove_fd(evd->run_state->command_out_fd, evd->fd);
+ evd->run_state->command_out_fd = evd->fd; /* just to keep it consistent */
+ ndelay_on(evd->fd);
+
+ return TRUE; /* "please don't remove this event (yet)" */
+}
+
+static void start_event_run(const char *event_name,
+ GList *more_events,
+ GtkWidget *page,
+ GtkTextView *tv_log,
+ GtkLabel *status_label,
+ const char *start_msg,
+ const char *end_msg
+) {
+ /* Start event asyncronously on the dump dir
+ * (syncronous run would freeze GUI until completion)
+ */
+ struct run_event_state *state = new_run_event_state();
+
+ if (prepare_commands(state, g_dump_dir_name, event_name) == 0)
+ {
+ no_cmds:
+ /* No commands needed?! (This is untypical) */
+ free_run_event_state(state);
+//TODO: better msg?
+ char *msg = xasprintf(_("No processing for event '%s' is defined"), event_name);
+ gtk_label_set_text(status_label, msg);
+ free(msg);
+ return;
+ }
+
+ struct dump_dir *dd = dd_opendir(g_dump_dir_name, DD_OPEN_READONLY);
+ dd = steal_if_needed(dd);
+ int locked = (dd && dd->locked);
+ dd_close(dd);
+ if (!locked)
+ return; /* user refused to steal, or write error, etc... */
+
+ if (spawn_next_command(state, g_dump_dir_name, event_name) < 0)
+ goto no_cmds;
+
+ VERB1 log("running event '%s' on '%s'", event_name, g_dump_dir_name);
+
+ /* At least one command is needed, and we started first one.
+ * Hook its output fd to the main loop.
+ */
+ struct analyze_event_data *evd = xzalloc(sizeof(*evd));
+ evd->run_state = state;
+ evd->event_name = event_name;
+ evd->more_events = more_events;
+ evd->page_widget = page;
+ evd->status_label = status_label;
+ evd->tv_log = tv_log;
+ evd->end_msg = end_msg;
+ evd->fd = state->command_out_fd;
+ ndelay_on(evd->fd);
+ evd->channel = g_io_channel_unix_new(evd->fd);
+ /*evd->event_source_id = */ g_io_add_watch(evd->channel,
+ G_IO_IN | G_IO_ERR | G_IO_HUP, /* need HUP to detect EOF w/o any data */
+ consume_cmd_output,
+ evd
+ );
+
+ gtk_label_set_text(status_label, start_msg);
+ /* Freeze assistant so it can't move away from the page until analyzing is done */
+ gtk_assistant_set_page_complete(g_assistant, page, false);
+}
+
+
+/* Backtrace checkbox handling */
+
+static void add_warning(const char *warning)
+{
+ char *label_str = xasprintf("• %s", warning);
+ GtkWidget *warning_lbl = gtk_label_new(label_str);
+ /* should be safe to free it, gtk calls strdup() to copy it */
+ free(label_str);
+
+ gtk_misc_set_alignment(GTK_MISC(warning_lbl), 0.0, 0.0);
+ gtk_label_set_justify(GTK_LABEL(warning_lbl), GTK_JUSTIFY_LEFT);
+ gtk_box_pack_start(g_box_warning_labels, warning_lbl, false, false, 0);
+ gtk_widget_show(warning_lbl);
+}
+
+static void check_backtrace_and_allow_send(void) //TODO: rename, this checks rating, not backtrace
+{
+ bool send = true;
+ bool warn = false;
+
+ /* erase all warnings */
+ gtk_widget_hide(g_widget_warnings_area);
+ gtk_container_foreach(GTK_CONTAINER(g_box_warning_labels), &remove_child_widget, NULL);
+
+ /*
+ * FIXME: this should be bind to a reporter not to a compoment
+ * but so far only oopses doesn't have rating, so for now we
+ * skip the "kernel" manually
+ */
+ const char *component = get_crash_item_content_or_NULL(g_cd, FILENAME_COMPONENT);
+ if (strcmp(component, "kernel") != 0)
+ {
+ const char *rating = get_crash_item_content_or_NULL(g_cd, FILENAME_RATING);
+ if (rating) switch (*rating)
+ {
+ case '4': //bt is ok - no warning here
+ break;
+ case '3': //bt is usable, but not complete, so show a warning
+ add_warning(_("The backtrace is incomplete, please make sure you provide the steps to reproduce."));
+ warn = true;
+ break;
+ case '2':
+ case '1':
+ //FIXME: see CreporterAssistant: 394 for ideas
+ add_warning(_("Reporting disabled because the backtrace is unusable."));
+ send = false;
+ warn = true;
+ break;
+ }
+ }
+
+ if (!gtk_toggle_button_get_active(g_tb_approve_bt))
+ {
+ add_warning(_("You should check the backtrace for sensitive data."));
+ add_warning(_("You must agree with sending the backtrace."));
+ send = false;
+ warn = true;
+ }
+
+ gtk_assistant_set_page_complete(g_assistant,
+ pages[PAGENO_BACKTRACE_APPROVAL].page_widget,
+ send);
+ if (warn)
+ gtk_widget_show(g_widget_warnings_area);
+}
+
+static void on_bt_approve_toggle(GtkToggleButton *togglebutton, gpointer user_data)
+{
+ check_backtrace_and_allow_send();
+}
+
+
+/* Refresh button handling */
+
+static void on_btn_refresh_clicked(GtkButton *button)
+{
+ if (g_reanalyze_events[0])
+ {
+ g_analyze_events = append_to_malloced_string(g_analyze_events, g_reanalyze_events);
+ g_reanalyze_events[0] = '\0';
+ /* Save backtrace text if changed */
+ save_text_from_text_view(g_tv_backtrace, FILENAME_BACKTRACE);
+ /* Refresh GUI so that we see new analyze+reanalyze buttons */
+ update_gui_state_from_crash_data();
+ /* Change page to analyzer selector - let user play with them */
+ gtk_assistant_set_current_page(g_assistant, PAGENO_ANALYZE_SELECTOR);
+ }
+}
+
+
+/* Page navigation handlers */
+
+static void next_page(GtkAssistant *assistant, gpointer user_data)
+{
+ /* page_no is actually the previous page, because this
+ * function is called before assistant goes to the next_page
+ */
+ int page_no = gtk_assistant_get_current_page(assistant);
+ VERB2 log("page_no:%d", page_no);
+
+ if (page_no == PAGENO_ANALYZE_SELECTOR
+ && g_analyze_label_selected != NULL)
+ {
+ start_event_run(/*event_name:*/ g_analyze_label_selected,
+ NULL,
+ pages[PAGENO_ANALYZE_PROGRESS].page_widget,
+ g_tv_analyze_log,
+ g_lbl_analyze_log,
+ _("Analyzing..."),
+ _("Analyzing finished with exit code %d")
+ );
+ }
+
+ if (page_no == PAGENO_REPORT)
+ {
+ GList *reporters = gtk_container_get_children(GTK_CONTAINER(g_box_reporters));
+ if (reporters)
+ {
+ for (GList *li = reporters; li; li = li->next)
+ {
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(li->data)) == TRUE)
+ li->data = (gpointer)gtk_button_get_label(GTK_BUTTON(li->data));
+ else
+ li->data = NULL;
+ }
+ reporters = g_list_remove_all(reporters, NULL);
+ if (reporters)
+ {
+ char *first_event_name = reporters->data;
+ reporters = g_list_remove(reporters, reporters->data);
+ start_event_run(first_event_name,
+ reporters,
+ pages[PAGENO_REPORT_PROGRESS].page_widget,
+ g_tv_report_log,
+ g_lbl_report_log,
+ _("Reporting..."),
+ _("Reporting finished with exit code %d")
+ );
+ }
+ }
+ }
+}
+
+static void on_page_prepare(GtkAssistant *assistant, GtkWidget *page, gpointer user_data)
+{
+ if (pages[PAGENO_BACKTRACE_APPROVAL].page_widget == page)
+ {
+ check_backtrace_and_allow_send();
+ }
+
+ if (pages[PAGENO_HOWTO].page_widget == page)
+ {
+ /* User just pressed [Fwd] on backtrace page. Save backtrace text if changed */
+ save_text_from_text_view(g_tv_backtrace, FILENAME_BACKTRACE);
+ }
+
+ if (pages[PAGENO_REPORT].page_widget == page)
+ {
+ /* User just pressed [Fwd] on comment page. Same as above */
+ save_text_from_text_view(g_tv_reproduce, FILENAME_REPRODUCE);
+ save_text_from_text_view(g_tv_comment, FILENAME_COMMENT);
+ }
+
+ if (pages[PAGENO_SUMMARY].page_widget == page
+ || pages[PAGENO_REPORT].page_widget == page
+ ) {
+ GtkWidget *w = GTK_WIDGET(g_tv_details);
+ GtkContainer *c = GTK_CONTAINER(gtk_widget_get_parent(w));
+ if (c)
+ gtk_container_remove(c, w);
+ gtk_container_add(pages[PAGENO_SUMMARY].page_widget == page ?
+ g_container_details1 : g_container_details2,
+ w
+ );
+ }
+}
+
+static gint next_page_no(gint current_page_no, gpointer data)
+{
+ switch (current_page_no)
+ {
+ case PAGENO_SUMMARY:
+ if (!g_analyze_events[0])
+ {
+ //TODO: if (!g_reporter_events[0]) /* no reporters available */ then what?
+ return PAGENO_REPORTER_SELECTOR; /* skip analyze pages */
+ }
+ break;
+
+ case PAGENO_REPORTER_SELECTOR:
+ if (get_crash_item_content_or_NULL(g_cd, FILENAME_BACKTRACE))
+ break;
+ current_page_no++; /* no backtrace, skip next page */
+ /* fall through */
+
+#if 0
+ case PAGENO_BACKTRACE_APPROVAL:
+ if (get_crash_item_content_or_NULL(g_cd, FILENAME_COMMENT)
+ || get_crash_item_content_or_NULL(g_cd, FILENAME_REPRODUCE)
+ ) {
+ break;
+ }
+ current_page_no++; /* no comment, skip next page */
+ /* fall through */
+#endif
+
+ }
+
+ return current_page_no + 1;
+}
+
+
+static gboolean highlight_search(gpointer user_data)
+{
+ GtkEntry *entry = GTK_ENTRY(user_data);
+ g_print("searching: %s\n", gtk_entry_get_text(entry));
+ //returning will make gtk to remove this event
+ return false;
+}
+
+static void search_timeout(GtkEntry *entry)
+{
+ /* this little hack makes the search start after 500 milisec after
+ * user stops writing into entry box
+ * if this part is removed, then the search will be started on every
+ * char written into the entry
+ */
+ if(g_timeout != 0)
+ g_source_remove(g_timeout);
+ g_timeout = g_timeout_add(500, &highlight_search, (gpointer)entry);
+}
+
+
+/* Initialization */
+
+static void create_details_treeview()
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Name"),
+ renderer,
+ "text",
+ DETAIL_COLUMN_NAME,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, DETAIL_COLUMN_NAME);
+ gtk_tree_view_append_column(g_tv_details, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Value"),
+ renderer,
+ "text",
+ DETAIL_COLUMN_VALUE,
+ NULL);
+ gtk_tree_view_append_column(g_tv_details, column);
+
+ /*
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Path"),
+ renderer,
+ "text",
+ DETAIL_COLUMN_PATH,
+ NULL);
+ gtk_tree_view_append_column(g_tv_details, column);
+ */
+}
+
+/* wizard.glade file as a string WIZARD_GLADE_CONTENTS: */
+#include "wizard_glade.c"
+
+static void add_pages(void)
+{
+ GError *error = NULL;
+ if (!g_glade_file)
+ {
+ /* Load UI from internal string */
+ gtk_builder_add_objects_from_string(builder,
+ WIZARD_GLADE_CONTENTS, sizeof(WIZARD_GLADE_CONTENTS) - 1,
+ (gchar**)page_names,
+ &error);
+ if (error != NULL)
+ error_msg_and_die("Error loading glade data: %s", error->message);
+ }
+ else
+ {
+ /* -g FILE: load IU from it */
+ gtk_builder_add_objects_from_file(builder, g_glade_file, (gchar**)page_names, &error);
+ if (error != NULL)
+ error_msg_and_die("Can't load %s: %s", g_glade_file, error->message);
+ }
+
+ for (int i = 0; page_names[i] != NULL; i++)
+ {
+ GtkWidget *page = GTK_WIDGET(gtk_builder_get_object(builder, page_names[i]));
+ if (page == NULL)
+ continue;
+
+ pages[i].page_widget = page;
+
+ gtk_assistant_append_page(g_assistant, page);
+ /* If we set all pages to complete the wizard thinks there is nothing
+ * to do and shows the button "Last" which allows user to skip all pages
+ * so we need to set them all as incomplete and complete them one by one
+ * on proper place - on_page_prepare() ?
+ */
+ gtk_assistant_set_page_complete(g_assistant, page, true);
+
+ gtk_assistant_set_page_title(g_assistant, page, pages[i].title);
+ gtk_assistant_set_page_type(g_assistant, page, pages[i].type);
+
+ VERB1 log("added page: %s", page_names[i]);
+ }
+ /* Set pointers to objects we might need to work with */
+ g_lbl_cd_reason = GTK_LABEL( gtk_builder_get_object(builder, "lbl_cd_reason"));
+ g_box_analyzers = GTK_BOX( gtk_builder_get_object(builder, "vb_analyzers"));
+ g_lbl_analyze_log = GTK_LABEL( gtk_builder_get_object(builder, "lbl_analyze_log"));
+ g_tv_analyze_log = GTK_TEXT_VIEW( gtk_builder_get_object(builder, "tv_analyze_log"));
+ g_box_reporters = GTK_BOX( gtk_builder_get_object(builder, "vb_reporters"));
+ g_lbl_report_log = GTK_LABEL( gtk_builder_get_object(builder, "lbl_report_log"));
+ g_tv_report_log = GTK_TEXT_VIEW( gtk_builder_get_object(builder, "tv_report_log"));
+ g_tv_backtrace = GTK_TEXT_VIEW( gtk_builder_get_object(builder, "tv_backtrace"));
+ g_tv_reproduce = GTK_TEXT_VIEW( gtk_builder_get_object(builder, "tv_reproduce"));
+ g_tv_comment = GTK_TEXT_VIEW( gtk_builder_get_object(builder, "tv_comment"));
+ g_tv_details = GTK_TREE_VIEW( gtk_builder_get_object(builder, "tv_details"));
+ g_box_warning_labels = GTK_BOX( gtk_builder_get_object(builder, "box_warning_labels"));
+ g_tb_approve_bt = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "cb_approve_bt"));
+ g_widget_warnings_area = GTK_WIDGET( gtk_builder_get_object(builder, "box_warning_area"));
+ g_btn_refresh = GTK_BUTTON( gtk_builder_get_object(builder, "btn_refresh"));
+ g_search_entry_bt = GTK_ENTRY( gtk_builder_get_object(builder, "entry_search_bt"));
+ g_container_details1 = GTK_CONTAINER( gtk_builder_get_object(builder, "container_details1"));
+ g_container_details2 = GTK_CONTAINER( gtk_builder_get_object(builder, "container_details2"));
+
+ gtk_widget_modify_font(GTK_WIDGET(g_tv_analyze_log), monospace_font);
+ gtk_widget_modify_font(GTK_WIDGET(g_tv_report_log), monospace_font);
+ gtk_widget_modify_font(GTK_WIDGET(g_tv_backtrace), monospace_font);
+
+ ///* hide the warnings by default */
+ //gtk_widget_hide(g_widget_warnings_area);
+
+ //gtk_assistant_set_page_complete(g_assistant, pages[PAGENO_REPORTER_SELECTOR].page_widget, false);
+ gtk_assistant_set_page_complete(g_assistant, pages[PAGENO_BACKTRACE_APPROVAL].page_widget,
+ gtk_toggle_button_get_active(g_tb_approve_bt));
+}
+
+void create_assistant()
+{
+ monospace_font = pango_font_description_from_string("monospace");
+
+ builder = gtk_builder_new();
+
+ g_assistant = GTK_ASSISTANT(gtk_assistant_new());
+
+ gtk_assistant_set_forward_page_func(g_assistant, next_page_no, NULL, NULL);
+
+ GtkWindow *wnd_assistant = GTK_WINDOW(g_assistant);
+ gtk_window_set_default_size(wnd_assistant, DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ gtk_window_set_title(wnd_assistant, g_dump_dir_name);
+ gtk_window_set_icon_name(wnd_assistant, "abrt");
+
+ GObject *obj_assistant = G_OBJECT(g_assistant);
+ g_signal_connect(obj_assistant, "cancel", G_CALLBACK(gtk_main_quit), NULL);
+ g_signal_connect(obj_assistant, "close", G_CALLBACK(gtk_main_quit), NULL);
+ g_signal_connect(obj_assistant, "apply", G_CALLBACK(next_page), NULL);
+ g_signal_connect(obj_assistant, "prepare", G_CALLBACK(on_page_prepare), NULL);
+
+ add_pages();
+
+ create_details_treeview();
+ g_ls_details = gtk_list_store_new(DETAIL_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ gtk_tree_view_set_model(g_tv_details, GTK_TREE_MODEL(g_ls_details));
+
+// gtk_builder_connect_signals(builder, NULL);
+
+ g_signal_connect(g_tb_approve_bt, "toggled", G_CALLBACK(on_bt_approve_toggle), NULL);
+ g_signal_connect(g_btn_refresh, "clicked", G_CALLBACK(on_btn_refresh_clicked), NULL);
+
+ /* init search */
+ GtkTextBuffer *backtrace_buf = gtk_text_view_get_buffer(g_tv_backtrace);
+ /* found items background */
+ gtk_text_buffer_create_tag(backtrace_buf, "search_result_bg", "background", "red", NULL);
+ /* current position */
+ gtk_text_buffer_create_tag(backtrace_buf, "current_pos_bg", "background", "yellow", NULL);
+ g_signal_connect(g_search_entry_bt, "changed", G_CALLBACK(search_timeout), NULL);
+
+ gtk_assistant_set_page_complete(g_assistant,
+ pages[PAGENO_BACKTRACE_APPROVAL].page_widget,
+ gtk_toggle_button_get_active(g_tb_approve_bt)
+ );
+}
diff --git a/src/gui-wizard-gtk/wizard.glade b/src/gui-wizard-gtk/wizard.glade
new file mode 100644
index 00000000..9dde4316
--- /dev/null
+++ b/src/gui-wizard-gtk/wizard.glade
@@ -0,0 +1,746 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkWindow" id="window0">
+ <property name="width_request">800</property>
+ <property name="height_request">500</property>
+ <child>
+ <object class="GtkVBox" id="page_0">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="lbl_cd_reason">
+ <property name="width_request">750</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="ypad">5</property>
+ <property name="wrap">True</property>
+ <attributes>
+ <attribute name="style" value="normal"/>
+ <attribute name="weight" value="bold"/>
+ <attribute name="foreground" value="#ffff00000000"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="ypad">5</property>
+ <property name="label" translatable="yes">Press 'Forward' to proceed with analyzing and reporting this problem.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkExpander" id="expander1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="container_details1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="tv_details">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="dump_elements">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Details</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="window1">
+ <property name="width_request">800</property>
+ <property name="height_request">500</property>
+ <child>
+ <object class="GtkVBox" id="page_1">
+ <property name="width_request">750</property>
+ <property name="height_request">400</property>
+ <property name="visible">True</property>
+ <property name="border_width">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="lbl_page5">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Select how you would like to analyze the problem:</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vb_analyzers">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="window2">
+ <property name="width_request">800</property>
+ <property name="height_request">500</property>
+ <child>
+ <object class="GtkVBox" id="page_2">
+ <property name="visible">True</property>
+ <property name="border_width">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="lbl_analyze_log">
+ <property name="width_request">750</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="ypad">5</property>
+ <property name="label" translatable="yes">Analyzing did not start yet</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTextView" id="tv_analyze_log">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="accepts_tab">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="window3">
+ <property name="width_request">800</property>
+ <property name="height_request">500</property>
+ <child>
+ <object class="GtkVBox" id="page_3">
+ <property name="visible">True</property>
+ <property name="border_width">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="lbl_page3">
+ <property name="width_request">750</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Select how you would like to report the problem:</property>
+ <property name="use_markup">True</property>
+ <property name="justify">fill</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vb_reporters">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="window4">
+ <property name="width_request">800</property>
+ <property name="height_request">500</property>
+ <property name="tooltip_text" translatable="yes">Use this button to generate more informative backtrace after you installed additional debug packages</property>
+ <child>
+ <object class="GtkVBox" id="page_4">
+ <property name="visible">True</property>
+ <property name="border_width">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="lbl_page4">
+ <property name="width_request">750</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Backtrace provides developers with details of the crash, helping them track down the source of the problem. Please review it and remove any sensitive data you would rather not share:</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTextView" id="tv_backtrace">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="wrap_mode">word</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <child>
+ <object class="GtkHBox" id="box_warning_area">
+ <property name="visible">True</property>
+ <property name="no_show_all">True</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-warning</property>
+ <property name="icon-size">6</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="box_warning_labels">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkEntry" id="entry_search_bt">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="secondary_icon_stock">gtk-find</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkArrow" id="arrow1">
+ <property name="visible">True</property>
+ <property name="arrow_type">up</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkArrow" id="arrow2">
+ <property name="visible">True</property>
+ <property name="arrow_type">down</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="btn_refresh">
+ <property name="label" translatable="yes">Regenerate backtrace</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Can create more informative backtrace if you installed additional debug packages</property>
+ <signal name="clicked" handler="on_b_refresh_clicked"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="cb_approve_bt">
+ <property name="label" translatable="yes">I agree with submitting the backtrace</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="window5">
+ <property name="width_request">800</property>
+ <property name="height_request">500</property>
+ <child>
+ <object class="GtkVBox" id="page_5">
+ <property name="visible">True</property>
+ <property name="border_width">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="xalign">1.1175871339474952e-09</property>
+ <property name="yalign">0</property>
+ <property name="ypad">5</property>
+ <property name="label" translatable="yes">How did this crash happen (step-by-step)? How can it be reproduced?</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTextView" id="tv_reproduce">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="wrap_mode">word</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes">Are there any comments you would like to share with the software maintainers?</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTextView" id="tv_comment">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="wrap_mode">word</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="ypad">5</property>
+ <property name="label" translatable="yes">&lt;b&gt;Your comments are not private.&lt;/b&gt; They may be included into publicly visible problem reports.</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="window6">
+ <property name="width_request">800</property>
+ <property name="height_request">500</property>
+ <child>
+ <object class="GtkVBox" id="page_6">
+ <property name="visible">True</property>
+ <property name="border_width">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="lbl_page6">
+ <property name="width_request">750</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Below is the information to be reported, please review it.</property>
+ <property name="justify">fill</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="border_width">10</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Size:</property>
+ <property name="justify">right</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"># files:</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">ETA to upload:</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Reporter(s):</property>
+ <property name="justify">right</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Bugzilla, Logger</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">328kB</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">27</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">2 minutes</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="container_details2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkWindow" id="window7">
+ <property name="width_request">800</property>
+ <property name="height_request">500</property>
+ <child>
+ <object class="GtkVBox" id="page_7">
+ <property name="visible">True</property>
+ <property name="border_width">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="lbl_report_log">
+ <property name="width_request">750</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="ypad">5</property>
+ <property name="label" translatable="yes">Reporting did not start yet</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow6">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTextView" id="tv_report_log">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="accepts_tab">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/src/gui-wizard-gtk/wizard.h b/src/gui-wizard-gtk/wizard.h
new file mode 100644
index 00000000..f27fb777
--- /dev/null
+++ b/src/gui-wizard-gtk/wizard.h
@@ -0,0 +1,38 @@
+enum {
+ PAGENO_SUMMARY,
+ PAGENO_ANALYZE_SELECTOR,
+ PAGENO_ANALYZE_PROGRESS,
+ PAGENO_REPORTER_SELECTOR,
+ PAGENO_BACKTRACE_APPROVAL,
+ PAGENO_HOWTO,
+ PAGENO_REPORT,
+ PAGENO_REPORT_PROGRESS,
+};
+extern GtkAssistant *g_assistant;
+extern GtkLabel *g_lbl_cd_reason;
+extern GtkLabel *g_lbl_analyze_log;
+extern GtkBox *g_box_analyzers;
+extern GtkBox *g_box_reporters;
+extern GtkTextView *g_tv_backtrace;
+enum
+{
+ DETAIL_COLUMN_NAME,
+ DETAIL_COLUMN_VALUE,
+ //COLUMN_PATH,
+ DETAIL_NUM_COLUMNS,
+};
+extern GtkTreeView *g_tv_details;
+extern GtkListStore *g_ls_details;
+void create_assistant(void);
+void update_gui_state_from_crash_data(void);
+void show_error_as_msgbox(const char *msg);
+
+
+extern char *g_glade_file;
+extern char *g_dump_dir_name;
+extern char *g_analyze_label_selected;
+extern char *g_analyze_events;
+extern char *g_reanalyze_events;
+extern char *g_report_events;
+extern crash_data_t *g_cd;
+void reload_crash_data_from_dump_dir(void);
diff --git a/src/gui/CCDBusBackend.py b/src/gui/CCDBusBackend.py
index 3fa95eb9..d2b867c7 100644
--- a/src/gui/CCDBusBackend.py
+++ b/src/gui/CCDBusBackend.py
@@ -21,6 +21,7 @@ class DBusManager(gobject.GObject):
# and later with policyKit
bus = None
def __init__(self):
+
session = None
# binds the dbus to glib mainloop
DBusGMainLoop(set_as_default=True)
@@ -60,7 +61,7 @@ class DBusManager(gobject.GObject):
# signal emited to update gui with current status
gobject.signal_new("update", self, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
# signal emited to show gui if user try to run it again
- gobject.signal_new("show", self, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())
+ gobject.signal_new("show_gui", self, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())
gobject.signal_new("daemon-state-changed", self, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
gobject.signal_new("report-done", self, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
@@ -211,11 +212,13 @@ class DBusManager(gobject.GObject):
def getSettings(self):
return self.daemon().GetSettings()
- def setSettings(self, settings):
- # FIXME: STUB!!!!
- log1("setSettings stub")
- retval = self.daemon().SetSettings(self.daemon().GetSettings())
- print ">>>", retval
+ ### looks unused to me.
+ ### Ok to grep for setSettings and delete after 2011-04-01.
+ ### def setSettings(self, settings):
+ ### # FIXME: STUB!!!!
+ ### log1("setSettings stub")
+ ### retval = self.daemon().SetSettings(self.daemon().GetSettings())
+ ### print ">>>", retval
def __del__(self):
log1("CCDBusBackend is about to be deleted")
diff --git a/src/gui/CCDump.py b/src/gui/CCDump.py
index df5c18aa..10d7c2a3 100644
--- a/src/gui/CCDump.py
+++ b/src/gui/CCDump.py
@@ -3,7 +3,7 @@ from datetime import datetime
from abrt_utils import _, init_logging, log, log1, log2
-# Keep in sync with [abrt_]crash_dump.h!
+# Keep in sync with [abrt_]crash_data.h!
CD_TYPE = 0
CD_EDITABLE = 1
CD_CONTENT = 2
@@ -25,10 +25,9 @@ FILENAME_CRASH_FUNCTION = "crash_function"
FILENAME_ARCHITECTURE = "architecture"
FILENAME_KERNEL = "kernel"
FILENAME_TIME = "time"
-FILENAME_RELEASE = "release"
+FILENAME_OS_RELEASE = "os_release"
FILENAME_PACKAGE = "package"
FILENAME_COMPONENT = "component"
-FILENAME_DESCRIPTION = "description"
FILENAME_COMMENT = "comment"
FILENAME_REPRODUCE = "reproduce"
FILENAME_RATING = "rating"
@@ -129,7 +128,11 @@ class Dump():
return self.analyzer
def get_release(self):
- return self.release
+ # Old dump dir format compat. Delete in abrt-2.1:
+ try:
+ return self.os_release
+ except AttributeError:
+ return self.release # old name
def get_reason(self):
return self.reason
diff --git a/src/gui/CCMainWindow.py b/src/gui/CCMainWindow.py
index 651c8e54..c6642f78 100644
--- a/src/gui/CCMainWindow.py
+++ b/src/gui/CCMainWindow.py
@@ -21,7 +21,6 @@ import CCDBusBackend
from CC_gui_functions import *
from CCDumpList import getDumpList
from CCDump import * # FILENAME_xxx, CD_xxx
-from CCReporterDialog import ReporterDialog, ReporterSelector
from CReporterAssistant import ReporterAssistant
from PluginsSettingsDialog import PluginsSettingsDialog
from SettingsDialog import SettingsDialog
@@ -39,6 +38,7 @@ class MainWindow():
self.gladefile = "%s/ccgui.glade" % sys.path[0]
self.wTree = gtk.glade.XML(self.gladefile)
+
#Get the Main Window, and connect the "destroy" event
self.window = self.wTree.get_widget("main_window")
if self.window:
@@ -89,9 +89,10 @@ class MainWindow():
self.wTree.get_widget("bReport").connect("clicked", self.on_bReport_clicked)
self.wTree.get_widget("b_close").connect("clicked", self.on_bQuit_clicked)
self.wTree.get_widget("b_copy").connect("clicked", self.on_b_copy_clicked)
- self.wTree.get_widget("b_help").connect("clicked", self.on_miAbout_clicked)
+ self.wTree.get_widget("b_help").connect("clicked", self.on_miOnlineHelp_clicked)
self.wTree.get_widget("miQuit").connect("activate", self.on_bQuit_clicked)
self.wTree.get_widget("miAbout").connect("activate", self.on_miAbout_clicked)
+ self.wTree.get_widget("miOnlineHelp").connect("activate", self.on_miOnlineHelp_clicked)
self.wTree.get_widget("miPlugins").connect("activate", self.on_miPreferences_clicked)
self.wTree.get_widget("miPreferences").connect("activate", self.on_miSettings_clicked)
self.wTree.get_widget("miReport").connect("activate", self.on_bReport_clicked)
@@ -102,7 +103,7 @@ class MainWindow():
#self.ccdaemon.connect("update", self.update_cb)
# for now, just treat them the same (w/o this, we don't even see daemon warnings in logs!):
#self.ccdaemon.connect("warning", self.update_cb)
- self.ccdaemon.connect("show", self.show_cb)
+ self.ccdaemon.connect("show_gui", self.show_cb)
self.ccdaemon.connect("daemon-state-changed", self.on_daemon_state_changed_cb)
self.ccdaemon.connect("report-done", self.on_report_done_cb)
@@ -130,6 +131,10 @@ class MainWindow():
result = dialog.run()
dialog.hide()
+ def on_miOnlineHelp_clicked(self, widget):
+ # opens default browser and shows ABRT chapter from deployment guide
+ gtk.show_uri(None, "http://docs.fedoraproject.org/en-US/Fedora/14/html/Deployment_Guide/ch-abrt.html", gtk.gdk.CURRENT_TIME)
+
def on_miPreferences_clicked(self, widget):
dialog = PluginsSettingsDialog(self.window,self.ccdaemon)
dialog.hydrate()
@@ -243,6 +248,7 @@ class MainWindow():
# process the labels in sw_details
# hide the fields that are not filled by daemon - e.g. comments
# and how to reproduce
+
for field in dump.not_required_fields:
self.wTree.get_widget("l_%s" % field.lower()).hide()
self.wTree.get_widget("l_%s_heading" % field.lower()).hide()
@@ -342,7 +348,7 @@ class MainWindow():
("Command:", dump.cmdline),
("Reason:", dump.reason),
("Comment:", dump.comment),
- ("Bug Reports:", dump.Message),
+ ("Bug Reports:", dump.message),
]
dumpinfo_text = ""
for line in dumpinfo:
@@ -394,7 +400,7 @@ class MainWindow():
sys.exit()
def show(self):
- self.window.show()
+ self.window.show_all()
def show_cb(self, daemon):
if self.window:
diff --git a/src/gui/CCReporterDialog.py b/src/gui/CCReporterDialog.py
deleted file mode 100644
index b34baf87..00000000
--- a/src/gui/CCReporterDialog.py
+++ /dev/null
@@ -1,578 +0,0 @@
-# -*- coding: utf-8 -*-
-import pygtk
-pygtk.require("2.0")
-import gtk
-import gobject
-import sys
-from CC_gui_functions import *
-import CellRenderers
-from ABRTPlugin import PluginInfo
-from PluginSettingsUI import PluginSettingsUI
-from PluginList import getPluginInfoList
-from CCDump import * # FILENAME_xxx, CD_xxx
-from abrt_utils import _, log, log1, log2, get_verbose_level, g_verbose
-
-# FIXME - create method or smth that returns type|editable|content
-
-# response
-REFRESH = -50
-SHOW_LOG = -60
-
-# default texts
-COMMENT_HINT_TEXT = _("Brief description of how to reproduce this or what you did...")
-HOW_TO_HINT_TEXT = "1.\n2.\n3.\n"
-
-class ReporterDialog():
- """Reporter window"""
- def __init__(self, report, daemon, log=None, parent=None):
- self.editable = []
- self.row_dict = {}
- self.report = report
- #Set the Glade file
- # FIXME add to path
- builderfile = "%s/report.glade" % sys.path[0]
- self.builder = gtk.Builder()
- self.builder.add_from_file(builderfile)
- #Get the Main Window, and connect the "destroy" event
- self.window = self.builder.get_object("reporter_dialog")
- self.window.set_default_size(-1, 800)
- self.window.connect("response", self.on_response, daemon)
- if parent:
- self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
- self.window.set_transient_for(parent)
- self.window.set_modal(True)
- else:
- self.window.set_position(gtk.WIN_POS_CENTER)
-
- # comment textview
- self.tvComment = self.builder.get_object("tvComment")
- self.tvComment.connect("focus-in-event", self.on_comment_focus_cb)
- self.show_hint_comment = 1
-
- # "how to reproduce" textview
- self.tevHowToReproduce = self.builder.get_object("tevHowToReproduce")
-
- self.builder.get_object("fErrors").hide()
- bLog = self.builder.get_object("bLog")
- #if g_verbose > 0: - doesn't work! why?!
- if get_verbose_level() > 0:
- bLog.connect("clicked", self.show_log_cb, log)
- else:
- bLog.unset_flags(gtk.VISIBLE)
- tb_send_bt = self.builder.get_object("cbSendBacktrace")
- tb_send_bt.connect("toggled", self.on_send_backtrace_toggled)
- try:
- tb_send_bt.get_child().modify_fg(gtk.STATE_NORMAL,gtk.gdk.color_parse("red"))
- except Exception, ex:
- # we don't want gui to die if it fails to set the button color
- log(ex)
- self.allow_send()
- self.hydrate()
-
- def check_backtrace(self):
- print "checking backtrace"
-
- def warn_user(self, warnings):
- # FIXME: show in lError
- fErrors = self.builder.get_object("fErrors")
- lErrors = self.builder.get_object("lErrors")
- warning_lbl = None
- for warning in warnings:
- if warning_lbl:
- warning_lbl += "\n* %s" % warning
- else:
- warning_lbl = "* %s" % warning
- lErrors.set_label(warning_lbl)
- fErrors.show_all()
-
- def hide_warning(self):
- fErrors = self.builder.get_object("fErrors")
- lErrors = self.builder.get_object("lErrors")
- fErrors.hide()
-
- def allow_send(self):
- self.hide_warning()
- bSend = self.builder.get_object("bSend")
- SendBacktrace = self.builder.get_object("cbSendBacktrace").get_active()
- send = True
- error_msgs = []
- try:
- rating = int(self.report[FILENAME_RATING][CD_CONTENT])
- except:
- rating = None
- # active buttons acording to required fields
- # if an backtrace has rating use it
- if not SendBacktrace:
- send = False
- error_msgs.append(_("You must check the backtrace for sensitive data."))
- # we have both SendBacktrace and rating
- if rating != None:
- try:
- package = self.report[FILENAME_PACKAGE][CD_CONTENT]
- # if we don't have package for some reason
- except:
- package = None
- # not usable report
- if int(self.report[FILENAME_RATING][CD_CONTENT]) < 3:
- if package:
- error_msgs.append(_("Reporting disabled because the backtrace is unusable.\nPlease try to install debuginfo manually using the command: <b>debuginfo-install %s</b> \nthen use the Refresh button to regenerate the backtrace." % package[0:package.rfind('-',0,package.rfind('-'))]))
- else:
- error_msgs.append(_("The backtrace is unusable, you cannot report this!"))
- send = False
- # probably usable 3
- elif int(self.report[FILENAME_RATING][CD_CONTENT]) < 4:
- error_msgs.append(_("The backtrace is incomplete, please make sure you provide the steps to reproduce."))
-
- if error_msgs:
- self.warn_user(error_msgs)
- bSend.set_sensitive(send)
- if not send:
- bSend.set_tooltip_text(_("Reporting disabled, please fix the problems shown above."))
- else:
- bSend.set_tooltip_text(_("Sends the report using the selected plugin."))
-
- def on_send_backtrace_toggled(self, toggle_button):
- self.allow_send()
-
- def show_log_cb(self, widget, log):
- show_log(log, parent=self.window)
-
- # this callback is called when user press Cancel or Report button in Report dialog
- def on_response(self, dialog, response_id, daemon):
- # the button has been pressed (probably)
- if response_id == gtk.RESPONSE_APPLY:
- if not (self.check_report()):
- dialog.stop_emission("response")
- self.builder.get_object("bSend").stop_emission("clicked")
- if response_id == SHOW_LOG:
- # prevent the report dialog from quitting the run() and closing itself
- dialog.stop_emission("response")
-
- def on_send_toggled(self, cell, path, model):
- model[path][3] = not model[path][3]
-
- def on_comment_focus_cb(self, widget, event):
- if self.show_hint_comment:
- # clear "hint" text by supplying a fresh, empty TextBuffer
- widget.set_buffer(gtk.TextBuffer())
- self.show_hint_comment = 0
-
- def set_label(self, label_widget, text):
- if len(text) > label_widget.get_max_width_chars():
- label_widget.set_tooltip_text(text)
- label_widget.set_text(text)
-
- def hydrate(self):
- self.editable = []
- self.old_comment = COMMENT_HINT_TEXT
- self.old_how_to_reproduce = HOW_TO_HINT_TEXT
- for item in self.report:
- try:
- log2("report[%s]:%s/%s/%s", item, self.report[item][0], self.report[item][1], self.report[item][2][0:20])
- except:
- pass
-
- if item == FILENAME_BACKTRACE:
- buff = gtk.TextBuffer()
- tvBacktrace = self.builder.get_object("tvBacktrace")
- buff.set_text(self.report[item][CD_CONTENT])
- tvBacktrace.set_buffer(buff)
- continue
-
- if item == FILENAME_COMMENT:
- try:
- if self.report[item][CD_CONTENT]:
- self.old_comment = self.report[item][CD_CONTENT]
- except Exception, e:
- pass
- continue
-
- if item == FILENAME_REPRODUCE:
- try:
- if self.report[item][CD_CONTENT]:
- self.old_how_to_reproduce = self.report[item][CD_CONTENT]
- except Exception, e:
- pass
- continue
-
- if self.report[item][CD_TYPE] == CD_SYS:
- continue
-
- # item name 0| value 1| editable? 2| toggled? 3| visible?(attachment)4
- # FIXME: handle editable fields
- if self.report[item][CD_TYPE] == CD_BIN:
- self.builder.get_object("fAttachment").show()
- vbAttachments = self.builder.get_object("vbAttachments")
- toggle = gtk.CheckButton(self.report[item][CD_CONTENT])
- vbAttachments.pack_start(toggle)
- # bind item to checkbox
- toggle.item = item
- #FIXME: temporary workaround, in 1.0.4 reporters don't care
- # about this, they just send what they want to
- # TicketUploader even sends coredump!!
- #toggle.show()
- continue
-
- # It must be CD_TXT field
- item_label = self.builder.get_object("l%s" % item)
- if item_label:
- self.set_label(item_label, self.report[item][CD_CONTENT])
- else:
- # no widget to show this item
- # probably some new item need to adjust the GUI!
- # FIXME: add some window+button to show all the info
- # in raw form (smth like the old report dialog)
- pass
- #end for
-
- buff = gtk.TextBuffer()
- self.show_hint_comment = (self.old_comment == COMMENT_HINT_TEXT)
- if self.show_hint_comment:
- buff.set_text(COMMENT_HINT_TEXT)
- else:
- buff.set_text(self.old_comment)
- self.tvComment.set_buffer(buff)
-
- buff = gtk.TextBuffer()
- if self.old_how_to_reproduce == "":
- buff.set_text(HOW_TO_HINT_TEXT)
- else:
- buff.set_text(self.old_how_to_reproduce)
- self.tevHowToReproduce.set_buffer(buff)
-
- def dehydrate(self):
- ## # handle attachments
- ## vbAttachments = self.builder.get_object("vbAttachments")
- ## for attachment in vbAttachments.get_children():
- ## #print "%s file %s" % (["not sending","sending"][attachment.get_active()], attachment.get_label())
- ## del self.report[attachment.item]
-
- # handle comment
- buff = self.tvComment.get_buffer()
- text = buff.get_text(buff.get_start_iter(), buff.get_end_iter())
- if self.old_comment != text:
- self.report[FILENAME_COMMENT] = [CD_TXT, 'y', text]
- # handle how to reproduce
- buff = self.tevHowToReproduce.get_buffer()
- text = buff.get_text(buff.get_start_iter(), buff.get_end_iter())
- if self.old_how_to_reproduce != text:
- self.report[FILENAME_REPRODUCE] = [CD_TXT, 'y', text]
- # handle backtrace
- tev_backtrace = self.builder.get_object("tvBacktrace")
- buff = tev_backtrace.get_buffer()
- text = buff.get_text(buff.get_start_iter(), buff.get_end_iter())
- self.report[FILENAME_BACKTRACE] = [CD_TXT, 'y', text]
-
- def check_report(self):
- # FIXME: check the report for passwords and some other potentially
- # sensitive info
- self.dehydrate()
- return True
-
- def run(self):
- result = self.window.run()
- self.window.destroy()
- return (result, self.report)
-
-class ReporterSelector():
- def __init__(self, crashdump, daemon, log=None, parent=None):
- self.connected_signals = []
- self.updates = ""
- self.daemon = daemon
- self.dump = crashdump
- self.selected_reporters = []
- #FIXME: cache settings! Create some class to represent it like PluginList
- self.settings = daemon.getSettings()
- pluginlist = getPluginInfoList(daemon)
- self.reporters = []
- AnalyzerActionsAndReporters = self.settings["AnalyzerActionsAndReporters"]
- try:
- reporters = None
- try:
- reporters = AnalyzerActionsAndReporters[self.dump.getAnalyzerName()+":"+self.dump.getPackageName()]
- except KeyError:
- pass
- if not reporters:
- reporters = AnalyzerActionsAndReporters[crashdump.getAnalyzerName()]
- for reporter_name in reporters.split(','):
- reporter = pluginlist.getReporterByName(reporter_name)
- if reporter:
- self.reporters.append(reporter)
- except KeyError:
- # Analyzer has no associated reporters.
- pass
-
- builderfile = "%s/report.glade" % sys.path[0]
- self.builder = gtk.Builder()
- self.builder.add_from_file(builderfile)
- self.window = self.builder.get_object("w_reporters")
- b_cancel = self.builder.get_object("b_close")
-
- if parent:
- self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
- self.window.set_transient_for(parent)
- self.window.set_modal(True)
- self.connect_signal(self.window, "delete-event", self.on_window_delete)
- self.connect_signal(self.window, "destroy-event", self.on_window_delete)
- self.connect_signal(b_cancel, "clicked", self.on_close_clicked)
- else:
- # if we don't have parent we want to quit the mainloop on close
- self.window.set_position(gtk.WIN_POS_CENTER)
- self.connect_signal(self.window, "delete-event", gtk.main_quit)
- self.connect_signal(self.window, "destroy-event", gtk.main_quit)
- self.connect_signal(b_cancel, "clicked", gtk.main_quit)
-
-
- self.pBarWindow = self.builder.get_object("pBarWindow")
- self.pBarWindow.set_transient_for(self.window)
-
- reporters_vbox = self.builder.get_object("vb_reporters")
- for reporter in self.reporters:
- button = gtk.Button(str(reporter))
- self.connect_signal(button, "clicked", self.on_reporter_clicked, data=reporter)
- reporters_vbox.pack_start(button)
-
- # progress bar window to show while bt is being extracted
- self.pBarWindow = self.builder.get_object("pBarWindow")
- if self.pBarWindow:
- self.connect_signal(self.pBarWindow, "delete_event", self.sw_delete_event_cb)
- if parent:
- self.pBarWindow.set_transient_for(parent)
- else:
- self.pBarWindow.set_transient_for(self.window)
- self.pBar = self.builder.get_object("pBar")
-
- # connect handlers for daemon signals
- #self.ccdaemon.connect("abrt-error", self.error_cb)
- self.connect_signal(daemon, "update", self.update_cb)
- # for now, just treat them the same (w/o this, we don't even see daemon warnings in logs!):
- #self.ccdaemon.connect("warning", self.update_cb)
- #self.ccdaemon.connect("show", self.show_cb)
- #self.ccdaemon.connect("daemon-state-changed", self.on_daemon_state_changed_cb)
- self.connect_signal(daemon, "report-done", self.on_report_done_cb)
- self.connect_signal(daemon, "analyze-complete", self.on_analyze_complete_cb, self.pBarWindow)
-
- def connect_signal(self, obj, signal, callback, data=None):
- if data:
- signal_id = obj.connect(signal, callback, data)
- else:
- signal_id = obj.connect(signal, callback)
- self.connected_signals.append((obj, signal_id))
-
- def disconnect_signals(self):
- # we need to disconnect all signals in order to break all references
- # to this object, otherwise python won't destroy this object and the
- # signals emmited by daemon will get caught by multiple instances of
- # this class
- for obj, signal_id in self.connected_signals:
- obj.disconnect(signal_id)
-
- def cleanup_and_exit(self):
- if not self.window.get_property("visible"):
- self.disconnect_signals()
- # if the reporter selector doesn't have a parent
- if not self.window.get_transient_for():
- gtk.main_quit()
-
- def update_cb(self, daemon, message):
- self.updates += message
- if self.updates[-1] != '\n':
- self.updates += '\n'
- message = message.replace('\n',' ')
- self.builder.get_object("lStatus").set_text(message)
- buff = gtk.TextBuffer()
- buff.set_text(self.updates)
- end = buff.get_insert()
- tvUpdates = self.builder.get_object("tvUpdates")
- tvUpdates.set_buffer(buff)
- tvUpdates.scroll_mark_onscreen(end)
-
- def sw_delete_event_cb(self, widget, event, data=None):
- if self.timer:
- gobject.source_remove(self.timer)
- widget.hide()
- return True
-
- def show(self):
- if not self.reporters:
- gui_error_message(_("No reporter plugin available for this type of crash.\n"
- "Please check abrt.conf."))
- elif len(self.reporters) > 1:
- self.builder.get_object("vb_reporters").show_all()
- self.window.show()
- else:
- # we have only one reporter in the list
- self.selected_reporters = [str(self.reporters[0])]
- self.show_report()
-
- def on_config_plugin_clicked(self, button, plugin, image):
- ui = PluginSettingsUI(plugin, parent=self.window)
- ui.hydrate()
- response = ui.run()
- if response == gtk.RESPONSE_APPLY:
- ui.dehydrate()
- if plugin.Settings.check():
- try:
- plugin.save_settings_on_client_side()
- except Exception, e:
- gui_error_message(_("Cannot save plugin settings:\n %s" % e))
- box = image.get_parent()
- im = gtk.Image()
- im.set_from_stock(gtk.STOCK_APPLY, gtk.ICON_SIZE_MENU)
- box.remove(image)
- box.pack_start(im, expand = False, fill = False)
- im.show()
- image.destroy()
- button.set_sensitive(False)
- elif response == gtk.RESPONSE_CANCEL:
- log1("cancel")
- ui.destroy()
-
- def check_settings(self, reporters):
- wrong_conf_plugs = []
- for reporter in reporters:
- if reporter.Settings.check() == False:
- wrong_conf_plugs.append(reporter)
-
- if wrong_conf_plugs:
- gladefile = "%s%ssettings_wizard.glade" % (sys.path[0],"/")
- builder = gtk.Builder()
- builder.add_from_file(gladefile)
- dialog = builder.get_object("WrongSettings")
- vbWrongSettings = builder.get_object("vbWrongSettings")
- for plugin in wrong_conf_plugs:
- hbox = gtk.HBox()
- hbox.set_spacing(6)
- image = gtk.Image()
- image.set_from_stock(gtk.STOCK_CANCEL, gtk.ICON_SIZE_MENU)
- button = gtk.Button(_("Configure %s options" % plugin.getName()))
- button.connect("clicked", self.on_config_plugin_clicked, plugin, image)
- hbox.pack_start(button)
- hbox.pack_start(image, expand = False, fill = False)
- vbWrongSettings.pack_start(hbox)
- vbWrongSettings.show_all()
- dialog.set_transient_for(self.window)
- dialog.set_modal(True)
- response = dialog.run()
- dialog.destroy()
- if response != gtk.RESPONSE_YES:
- # user cancelled reporting
- return False
- return True
-
- def on_reporter_clicked(self, widget, reporter):
- self.selected_reporters = [reporter]
- if self.check_settings(self.selected_reporters):
- self.show_report()
-
- def on_close_clicked(self, widget):
- self.disconnect_signals()
- self.window.destroy()
-
- def on_window_delete(self, window, event):
- self.disconnect_signals()
- return False
-
- def on_report_done_cb(self, daemon, result):
- try:
- gobject.source_remove(self.timer)
- except:
- pass
- self.pBarWindow.hide()
- gui_report_dialog(result, self.window)
-
- self.cleanup_and_exit()
-
- def on_analyze_complete_cb(self, daemon, report, pBarWindow):
- try:
- gobject.source_remove(self.timer)
- except:
- pass
- self.pBarWindow.hide()
-#FIXME - why we need this?? -> timeout warnings
-# try:
-# dumplist = getDumpList(self.daemon)
-# except Exception, e:
-# print e
- if not report:
- gui_error_message(_("Unable to get report!\nIs debuginfo missing?"))
- return
-
- # if we have only one reporter enabled, the window with
- # the selection is not shown, so we can't use it as a parent
- # and we use the mainwindow instead
- if self.window.get_property("visible"):
- parent_window = self.window
- else:
- parent_window = self.window.get_transient_for()
-
- report_dialog = ReporterDialog(report, self.daemon, log=self.updates, parent=parent_window)
- # (response, report)
- response, result = report_dialog.run()
-
- if response == gtk.RESPONSE_APPLY:
- try:
- self.pBarWindow.show_all()
- self.timer = gobject.timeout_add(100, self.progress_update_cb)
- pluginlist = getPluginInfoList(self.daemon)
- reporters_settings = pluginlist.getReporterPluginsSettings()
- log2("Report(result,reporters,settings):")
- log2(" result:%s", str(result))
- # Careful, this will print reporters_settings["Password"] too
- log2(" settings:%s", str(reporters_settings))
- self.daemon.Report(result, self.selected_reporters, reporters_settings)
- log2("Report() returned")
- #self.hydrate()
- except Exception, ex:
- gui_error_message(_("Reporting failed!\n%s" % ex))
- # -50 == REFRESH
- elif response == -50:
- self.refresh_report(report)
- else:
- self.cleanup_and_exit()
-
- # call to update the progressbar
- def progress_update_cb(self, *args):
- self.pBar.pulse()
- return True
-
- def refresh_report(self, report):
- self.updates = ""
- self.pBarWindow.show_all()
- self.timer = gobject.timeout_add(100, self.progress_update_cb)
-
- # show the report window with selected report
- try:
- self.daemon.start_job(report[CD_DUMPDIR][CD_CONTENT], force=1)
- except Exception, ex:
- # FIXME #3 dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoReply: Did not receive a reply
- # do this async and wait for yum to end with debuginfoinstal
- if self.timer:
- gobject.source_remove(self.timer)
- self.pBarWindow.hide()
- gui_error_message(_("Error acquiring the report: %s" % ex))
- return
-
- def show_report(self):
- self.updates = ""
- # FIXME don't duplicate the code, move to function
- #self.pBar.show()
- self.pBarWindow.show_all()
- self.timer = gobject.timeout_add(100, self.progress_update_cb)
-
- # show the report window with selected dump
- # when getReport is done it emits "analyze-complete" and on_analyze_complete_cb is called
- # FIXME: does it make sense to change it to use callback rather then signal emitting?
- try:
- self.daemon.start_job(self.dump.getDumpDir())
- except Exception, ex:
- # FIXME #3 dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoReply: Did not receive a reply
- # do this async and wait for yum to end with debuginfoinstal
- if self.timer:
- gobject.source_remove(self.timer)
- self.pBarWindow.hide()
- gui_error_message(_("Error acquiring the report: %s" % ex))
- return
-
- def __del__(self):
- log1("ReporterSelector: instance is about to be garbage-collected")
diff --git a/src/gui/CReporterAssistant.py b/src/gui/CReporterAssistant.py
index d998b870..300ddc3f 100644
--- a/src/gui/CReporterAssistant.py
+++ b/src/gui/CReporterAssistant.py
@@ -23,12 +23,48 @@ MISSING_BACKTRACE_TEXT = _("Crash info doesn't contain a backtrace")
DEFAULT_WIDTH = 800
DEFAULT_HEIGHT = 500
+
+class Log(gtk.ScrolledWindow):
+ def __init__(self, log=""):
+ gtk.ScrolledWindow.__init__(self)
+ self.tv = gtk.TextView()
+ self.tv.set_editable(False)
+ self.add(self.tv)
+ # FIXME: log shouldn't be None
+ self.buff = gtk.TextBuffer()
+ self.buff.set_text(log)
+ self.tv.set_buffer(self.buff)
+ self.scroll = False
+
+ def append_line(self, text):
+ end_iter = self.buff.get_end_iter()
+ self.buff.insert(end_iter, "%s\n" % text)
+ self.scroll_to_end()
+
+
+ def append_warning(self, text):
+ """ stub """
+ pass
+
+ def append_error(self, text):
+ """ do we need this? shouldn't error be an exception? """
+ pass
+
+ def set_scroll(self, scroll):
+ """ enables/disables scrolling to end, when new text is added """
+ self.scroll = scroll
+
+ def scroll_to_end(self):
+ if self.scroll:
+ mark = self.buff.get_insert()
+ self.tv.scroll_mark_onscreen(mark)
+
class ReporterAssistant():
def __init__(self, report, daemon, log=None, parent=None):
self.connected_signals = []
self.plugins_cb = []
self.daemon = daemon
- self.updates = ""
+ self.updates = []
self.pdict = {}
self.report = report
self.parent = parent
@@ -37,6 +73,8 @@ class ReporterAssistant():
self.report_has_bt = False
self.selected_report_events = []
self.ev_warning = None
+ self.log = Log()
+ self.log.set_scroll(True)
""" create the assistant """
self.assistant = gtk.Assistant()
self.assistant.set_icon_name("abrt")
@@ -61,6 +99,8 @@ class ReporterAssistant():
self.pBarWindow = self.builder.get_object("pBarWindow")
if self.pBarWindow:
self.connect_signal(self.pBarWindow, "delete_event", self.sw_delete_event_cb)
+ pb_log_expander = self.builder.get_object("pb_log_expander")
+ pb_log_expander.add(self.log)
self.connect_signal(daemon, "analyze-complete", self.on_analyze_complete_cb, self.pBarWindow)
self.connect_signal(daemon, "report-done", self.on_report_done_cb)
@@ -82,15 +122,10 @@ class ReporterAssistant():
viewer.set_transient_for(self.assistant)
vbox = gtk.VBox()
viewer.add(vbox)
- log_tev = gtk.TextView()
- log_tev.set_editable(False)
- log_buff = gtk.TextBuffer()
- log_buff.set_text(self.updates)
- log_sw = gtk.ScrolledWindow()
- log_sw.add(log_tev)
- log_sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- log_tev.set_buffer(log_buff)
- vbox.pack_start(log_sw)
+ log = Log()
+ for line in self.updates:
+ log.append_line(line)
+ vbox.pack_start(log)
b_close = gtk.Button(stock=gtk.STOCK_CLOSE)
b_close.connect("clicked",lambda *w: viewer.destroy())
vbox.pack_start(b_close, False)
@@ -159,17 +194,10 @@ class ReporterAssistant():
gtk.main_quit()
def update_cb(self, daemon, message):
- self.updates += message
- if self.updates[-1] != '\n':
- self.updates += '\n'
+ self.updates.append(message)
message = message.replace('\n',' ')
self.builder.get_object("lStatus").set_text(message)
- buff = gtk.TextBuffer()
- buff.set_text(self.updates)
- end = buff.get_insert()
- tvUpdates = self.builder.get_object("tvUpdates")
- tvUpdates.set_buffer(buff)
- tvUpdates.scroll_mark_onscreen(end)
+ self.log.append_line(message)
def sw_delete_event_cb(self, widget, event, data=None):
if self.timer:
@@ -1093,9 +1121,7 @@ class ReporterAssistant():
self.prepare_page_3()
self.prepare_page_4()
self.prepare_page_5()
- self.updates = ""
# FIXME don't duplicate the code, move to function
- #self.pBar.show()
self.show_progress()
self.timer = gobject.timeout_add(100, self.progress_update_cb)
diff --git a/src/gui/CellRenderers.py b/src/gui/CellRenderers.py
deleted file mode 100644
index 155d8ce3..00000000
--- a/src/gui/CellRenderers.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# -*- coding: utf-8 -*-
-import gtk
-
-class CellTextView(gtk.TextView, gtk.CellEditable):
-
- __gtype_name__ = "CellTextView"
-
- def do_editing_done(self, *args):
- self.remove_widget()
-
- def do_remove_widget(self, *args):
- pass
-
- def do_start_editing(self, *args):
- pass
-
- def get_text(self):
- text_buffer = self.get_buffer()
- bounds = text_buffer.get_bounds()
- return text_buffer.get_text(*bounds)
-
- def set_text(self, text):
- self.get_buffer().set_text(text)
-
-
-class MultilineCellRenderer(gtk.CellRendererText):
-
- __gtype_name__ = "MultilineCellRenderer"
-
- def __init__(self):
- gtk.CellRendererText.__init__(self)
- self._in_editor_menu = False
- self.old_text = ""
-
- def _on_editor_focus_out_event(self, editor, event):
- if self._in_editor_menu: return
- editor.remove_widget()
- self.emit("edited", editor.get_data("path"), editor.get_text())
-
- def _on_editor_key_press_event(self, editor, event):
- if event.state & (gtk.gdk.SHIFT_MASK | gtk.gdk.CONTROL_MASK): return
- if event.keyval == gtk.keysyms.Escape:
- editor.set_text(self.old_text)
- editor.remove_widget()
- self.emit("editing-canceled")
-
- def _on_editor_populate_popup(self, editor, menu):
- self._in_editor_menu = True
- def on_menu_unmap(menu, self):
- self._in_editor_menu = False
- menu.connect("unmap", on_menu_unmap, self)
-
- def do_start_editing(self, event, widget, path, bg_area, cell_area, flags):
- editor = CellTextView()
- editor.modify_font(self.props.font_desc)
- self.old_text = self.props.text
- editor.set_text(self.props.text)
- editor.set_size_request(cell_area.width, cell_area.height)
- editor.set_border_width(min(self.props.xpad, self.props.ypad))
- editor.set_data("path", path)
- editor.connect("focus-out-event", self._on_editor_focus_out_event)
- editor.connect("key-press-event", self._on_editor_key_press_event)
- editor.connect("populate-popup", self._on_editor_populate_popup)
- editor.show()
- return editor
-
diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am
index 002b3bc7..61725d6f 100644
--- a/src/gui/Makefile.am
+++ b/src/gui/Makefile.am
@@ -1,8 +1,8 @@
bin_SCRIPTS = abrt-gui
PYTHON_FILES = CCDBusBackend.py CCDumpList.py CCDump.py CC_gui_functions.py \
- CCReporterDialog.py abrt_utils.py \
- CCMainWindow.py CellRenderers.py ABRTExceptions.py \
+ abrt_utils.py \
+ CCMainWindow.py ABRTExceptions.py \
SettingsDialog.py ABRTPlugin.py PluginList.py PluginSettingsUI.py \
PluginsSettingsDialog.py ConfBackend.py CReporterAssistant.py
diff --git a/src/gui/SettingsDialog.py b/src/gui/SettingsDialog.py
index e6d30e99..41fb9829 100644
--- a/src/gui/SettingsDialog.py
+++ b/src/gui/SettingsDialog.py
@@ -27,28 +27,10 @@ class SettingsDialog:
self.window = self.builder.get_object("wGlobalSettings")
self.builder.get_object("bSaveSettings").connect("clicked", self.on_ok_clicked)
self.builder.get_object("bCancelSettings").connect("clicked", self.on_cancel_clicked)
- self.builder.get_object("bAddCronJob").connect("clicked", self.on_bAddCronJob_clicked)
-
- # action plugin list for Cron tab
- self.actionPluginsListStore = gtk.ListStore(str, object)
- self.actionPluginsListStore.append([_("<b>Select plugin</b>"), None])
- # database plugin list
- self.databasePluginsListStore = gtk.ListStore(str, object)
- self.databasePluginsListStore.append([_("<b>Select database backend</b>"), None])
-
- self.dbcombo = self.builder.get_object("cbDatabase")
- self.dbcombo.set_model(self.databasePluginsListStore)
- cell = gtk.CellRendererText()
- self.dbcombo.pack_start(cell)
- self.dbcombo.add_attribute(cell, "markup", 0)
# blacklist edit
self.builder.get_object("bEditBlackList").connect("clicked", self.on_blacklistEdit_clicked)
self.builder.get_object("bOpenGPGPublicKeys").connect("clicked", self.on_GPGKeysEdit_clicked)
- self.builder.get_object("bAddAction").connect("clicked", self.on_bAddAction_clicked)
- # AnalyzerActionsAndReporters
- self.analyzerPluginsListStore = gtk.ListStore(str, object)
- self.analyzerPluginsListStore.append([_("<b>Select plugin</b>"), None])
# GPG keys
self.wGPGKeys = self.builder.get_object("wGPGKeys")
self.GPGKeysListStore = gtk.ListStore(str)
@@ -77,18 +59,6 @@ class SettingsDialog:
except Exception, e:
raise Exception("Comunication with daemon has failed, have you restarted the daemon after update?")
- ## hydrate cron jobs:
- for key,val in self.settings["Cron"].iteritems():
- # actions are separated by ','
- actions = val.split(',')
- self.settings["Cron"][key] = actions
- for plugin in self.pluginlist.getActionPlugins():
- it = self.actionPluginsListStore.append([plugin.getName(), plugin])
- for key,val in self.settings["Cron"].iteritems():
- if plugin.getName() in val:
- cron_job = (key,it)
- self.add_CronJob(cron_job)
- self.settings["Cron"][key].remove(plugin.getName())
# hydrate common
common = self.settings["Common"]
# ensure that all expected keys exist:
@@ -96,11 +66,6 @@ class SettingsDialog:
common["OpenGPGCheck"] = "no" # check unsigned pkgs too
## gpgcheck
self.builder.get_object("cbOpenGPGCheck").set_active(common["OpenGPGCheck"] == 'yes')
- ## database
- for dbplugin in self.pluginlist.getDatabasePlugins():
- it = self.databasePluginsListStore.append([dbplugin.getName(), dbplugin])
- if common["Database"] == dbplugin.getName():
- self.dbcombo.set_active_iter(it)
## MaxCrashSize
self.builder.get_object("sbMaxCrashReportsSize").set_value(float(common["MaxCrashReportsSize"]))
## GPG keys
@@ -114,13 +79,6 @@ class SettingsDialog:
## blacklist
self.builder.get_object("eBlacklist").set_text(common["BlackList"])
- # hydrate AnalyzerActionsAndReporters
- AnalyzerActionsAndReporters = self.settings["AnalyzerActionsAndReporters"]
- for analplugin in self.pluginlist.getAnalyzerPlugins():
- it = self.analyzerPluginsListStore.append([analplugin.getName(), analplugin])
- if analplugin.getName() in AnalyzerActionsAndReporters:
- action = (AnalyzerActionsAndReporters[analplugin.getName()], it)
- self.add_AnalyzerAction(action)
def on_bCancelGPGKeys_clicked(self, button):
self.wGPGKeys.hide()
@@ -147,12 +105,6 @@ class SettingsDialog:
def on_cancel_clicked(self, button):
self.window.hide()
- def on_remove_CronJob_clicked(self, button, job_hbox):
- self.removeHBoxWihtChildren(job_hbox)
-
- def on_remove_Action_clicked(self, button, binding_hbox):
- self.removeHBoxWihtChildren(binding_hbox)
-
def removeHBoxWihtChildren(self, job_hbox):
job_hbox.get_parent().remove(job_hbox)
for child in job_hbox.get_children():
@@ -237,7 +189,10 @@ class SettingsDialog:
self.add_AnalyzerAction()
def dehydrate(self):
- self.ccdaemon.setSettings(self.settings)
+ pass
+ ### looks unused to me.
+ ### Ok to grep for setSettings and delete after 2011-04-01.
+ ### self.ccdaemon.setSettings(self.settings)
def show(self):
self.window.show()
diff --git a/src/gui/ccgui.glade b/src/gui/ccgui.glade
index 4f8c4f02..c2efa148 100644
--- a/src/gui/ccgui.glade
+++ b/src/gui/ccgui.glade
@@ -9,7 +9,6 @@
<property name="window_position">center-on-parent</property>
<property name="icon_name">abrt</property>
<property name="type_hint">dialog</property>
- <property name="has_separator">False</property>
<property name="program_name">ABRT</property>
<property name="version">@VER@</property>
<property name="copyright" translatable="yes">(C) 2009, 2010 Red Hat, Inc.</property>
@@ -128,6 +127,20 @@ M&#xE1;ir&#xED;n Duffy &lt;duffy@redhat.com&gt;</property>
</widget>
</child>
<child>
+ <widget class="GtkImageMenuItem" id="miOnlineHelp">
+ <property name="label" translatable="yes">Online _Help</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">False</property>
+ <child internal-child="image">
+ <widget class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-help</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
<widget class="GtkImageMenuItem" id="miAbout">
<property name="label">gtk-about</property>
<property name="visible">True</property>
@@ -589,11 +602,34 @@ M&#xE1;ir&#xED;n Duffy &lt;duffy@redhat.com&gt;</property>
<property name="homogeneous">True</property>
<child>
<widget class="GtkButton" id="b_help">
- <property name="label">gtk-help</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_stock">True</property>
+ <child>
+ <widget class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="yalign">0</property>
+ <property name="stock">gtk-help</property>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Online _Help</property>
+ <property name="use_underline">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
</widget>
<packing>
<property name="padding">10</property>
diff --git a/src/gui/progress_window.glade b/src/gui/progress_window.glade
index af48ee55..6fae1486 100644
--- a/src/gui/progress_window.glade
+++ b/src/gui/progress_window.glade
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy project-wide -->
@@ -12,7 +12,6 @@
<child>
<object class="GtkVBox" id="vbox9">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="lStatus">
@@ -35,23 +34,11 @@
</packing>
</child>
<child>
- <object class="GtkExpander" id="expander1">
+ <object class="GtkExpander" id="pb_log_expander">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
- <object class="GtkScrolledWindow" id="scrolledwindow4">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
- <property name="shadow_type">etched-in</property>
- <child>
- <object class="GtkTextView" id="tvUpdates">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </object>
- </child>
- </object>
+ <placeholder/>
</child>
<child type="label">
<object class="GtkLabel" id="lUpdates">
diff --git a/src/gui/settings.glade b/src/gui/settings.glade
index b83e6617..9dc1b96f 100644
--- a/src/gui/settings.glade
+++ b/src/gui/settings.glade
@@ -276,7 +276,7 @@
<object class="GtkTable" id="common_table">
<property name="visible">True</property>
<property name="border_width">6</property>
- <property name="n_rows">5</property>
+ <property name="n_rows">4</property>
<property name="n_columns">2</property>
<property name="column_spacing">12</property>
<property name="row_spacing">6</property>
@@ -294,32 +294,6 @@
</packing>
</child>
<child>
- <object class="GtkLabel" id="lDatabasePlugin">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="xpad">5</property>
- <property name="label" translatable="yes">Database backend: </property>
- </object>
- <packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBox" id="cbDatabase">
- <property name="visible">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
<object class="GtkLabel" id="lBlacklist">
<property name="visible">True</property>
<property name="xalign">0</property>
@@ -348,20 +322,6 @@
</packing>
</child>
<child>
- <object class="GtkLabel" id="lOpenGPGPublicKeys">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="xpad">5</property>
- <property name="label" translatable="yes">GPG keys: </property>
- </object>
- <packing>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
<object class="GtkSpinButton" id="sbMaxCrashReportsSize">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -414,6 +374,20 @@
</packing>
</child>
<child>
+ <object class="GtkLabel" id="lOpenGPGPublicKeys">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">5</property>
+ <property name="label" translatable="yes">GPG keys: </property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkHBox" id="gpghbox">
<property name="visible">True</property>
<property name="spacing">6</property>
@@ -445,8 +419,8 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
@@ -461,241 +435,6 @@
<property name="tab_fill">False</property>
</packing>
</child>
- <child>
- <object class="GtkVBox" id="cron_vbox">
- <property name="visible">True</property>
- <property name="border_width">6</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkScrolledWindow" id="swCronJobs">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
- <child>
- <object class="GtkViewport" id="vpCronJobs">
- <property name="visible">True</property>
- <property name="resize_mode">queue</property>
- <property name="shadow_type">none</property>
- <child>
- <object class="GtkVBox" id="cjvbox1">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkHBox" id="cjhbox1">
- <property name="visible">True</property>
- <child>
- <object class="GtkLabel" id="lPlugin">
- <property name="visible">True</property>
- <property name="label" translatable="yes">&lt;b&gt;Plugin&lt;/b&gt;</property>
- <property name="use_markup">True</property>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="lTime">
- <property name="visible">True</property>
- <property name="label" translatable="yes">&lt;b&gt;Time (or period)&lt;/b&gt;</property>
- <property name="use_markup">True</property>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkVBox" id="vbCronJobs">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHButtonBox" id="hbuttonboxy1">
- <property name="visible">True</property>
- <property name="spacing">12</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="bAddCronJob">
- <property name="label">gtk-add</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="pCron">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Cron</property>
- </object>
- <packing>
- <property name="position">1</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkVBox" id="actions_vbox">
- <property name="visible">True</property>
- <property name="border_width">6</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
- <child>
- <object class="GtkViewport" id="viewportA1">
- <property name="visible">True</property>
- <property name="resize_mode">queue</property>
- <property name="shadow_type">none</property>
- <child>
- <object class="GtkVBox" id="vbox1">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkHBox" id="ahbox1">
- <property name="visible">True</property>
- <child>
- <object class="GtkLabel" id="lReporter">
- <property name="visible">True</property>
- <property name="label" translatable="yes">&lt;b&gt;Analyzer plugin&lt;/b&gt;</property>
- <property name="use_markup">True</property>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="lAssociatedActions">
- <property name="visible">True</property>
- <property name="label" translatable="yes">&lt;b&gt;Associated action&lt;/b&gt;</property>
- <property name="use_markup">True</property>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkAlignment" id="action_lbl_align">
- <property name="visible">True</property>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkVBox" id="vbActions">
- <property name="visible">True</property>
- <property name="orientation">vertical</property>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHButtonBox" id="hbuttonbox2">
- <property name="visible">True</property>
- <property name="spacing">12</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="bAddAction">
- <property name="label">gtk-add</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="pAnacre">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Analyzers, Actions, Reporters</property>
- </object>
- <packing>
- <property name="position">2</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
</object>
<packing>
<property name="position">0</property>
diff --git a/src/hooks/Makefile.am b/src/hooks/Makefile.am
index 2f57d2e7..6ebf3628 100644
--- a/src/hooks/Makefile.am
+++ b/src/hooks/Makefile.am
@@ -1,38 +1,19 @@
libexec_PROGRAMS = abrt-hook-ccpp
-bin_PROGRAMS = dumpoops
# abrt-hook-ccpp
abrt_hook_ccpp_SOURCES = \
abrt-hook-ccpp.c
abrt_hook_ccpp_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
-DCONF_DIR=\"$(CONF_DIR)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
- -D_GNU_SOURCE
-abrt_hook_ccpp_LDADD = \
- ../lib/libabrt.la
-
-# dumpoops
-dumpoops_SOURCES = \
- dumpoops.cpp
-dumpoops_CPPFLAGS = \
- -I$(srcdir)/../include \
- -I$(srcdir)/../lib \
- -I$(srcdir)/../plugins \
- -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
- -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
- -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
- -DCONF_DIR=\"$(CONF_DIR)\" \
- -DVAR_RUN=\"$(VAR_RUN)\" \
$(GLIB_CFLAGS) \
+ -Wall \
-D_GNU_SOURCE
-# build will succeed, but at runtime plugins do need ABRT*d*Utils
-dumpoops_LDADD = \
- ../lib/libabrt_daemon.la \
- ../lib/libabrt.la \
- $(GLIB_FLAGS)
+abrt_hook_ccpp_LDADD = \
+ ../lib/libreport.la
python_PYTHON = abrt.pth abrt_exception_handler.py
EXTRA_DIST = abrt_exception_handler.py.in $(man_MANS)
diff --git a/src/hooks/abrt-hook-ccpp.c b/src/hooks/abrt-hook-ccpp.c
index bcdc8547..46d96c91 100644
--- a/src/hooks/abrt-hook-ccpp.c
+++ b/src/hooks/abrt-hook-ccpp.c
@@ -161,9 +161,22 @@ static char* get_cwd(pid_t pid)
return malloc_readlink(buf);
}
-static char core_basename[sizeof("core.%lu") + sizeof(long)*3] = "core";
+/*
+ * %s - signal number
+ * %c - ulimit -c value
+ * %p - pid
+ * %u - uid
+ * %g - gid
+ * %t - UNIX time of dump
+ * %h - hostname
+ * %e - executable filename
+ * %% - output one "%"
+ */
+/* Must match CORE_PATTERN order in daemon! */
+static const char percent_specifiers[] = "%scpugthe";
+static char *core_basename = "core";
-static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid)
+static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid, char **percent_values)
{
struct passwd* pw = getpwuid(uid);
gid_t gid = pw ? pw->pw_gid : uid;
@@ -178,20 +191,63 @@ static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid)
return -1;
}
- /* Mimic "core.PID" if requested */
- char buf[] = "0\n";
- int fd = open("/proc/sys/kernel/core_uses_pid", O_RDONLY);
- if (fd >= 0)
+ if (strcmp(core_basename, "core") == 0)
{
- read(fd, buf, sizeof(buf));
- close(fd);
+ /* Mimic "core.PID" if requested */
+ char buf[] = "0\n";
+ int fd = open("/proc/sys/kernel/core_uses_pid", O_RDONLY);
+ if (fd >= 0)
+ {
+ read(fd, buf, sizeof(buf));
+ close(fd);
+ }
+ if (strcmp(buf, "1\n") == 0)
+ {
+ core_basename = xasprintf("%s.%lu", core_basename, (long)pid);
+ }
}
- if (strcmp(buf, "1\n") == 0)
+ else
{
- sprintf(core_basename, "core.%lu", (long)pid);
+ /* Expand old core pattern, put expanded name in core_basename */
+ core_basename = xstrdup(core_basename);
+ unsigned idx = 0;
+ while (1)
+ {
+ char c = core_basename[idx];
+ if (!c)
+ break;
+ idx++;
+ if (c != '%')
+ continue;
+
+ /* We just copied %, look at following char and expand %c */
+ c = core_basename[idx];
+ unsigned specifier_num = strchrnul(percent_specifiers, c) - percent_specifiers;
+ if (percent_specifiers[specifier_num] != '\0') /* valid %c (might be %% too) */
+ {
+ const char *val = "%";
+ if (specifier_num > 0) /* not %% */
+ val = percent_values[specifier_num - 1];
+ //log("c:'%c'", c);
+ //log("val:'%s'", val);
+
+ /* Replace %c at core_basename[idx] by its value */
+ idx--;
+ char *old = core_basename;
+ core_basename = xasprintf("%.*s%s%s", idx, core_basename, val, core_basename + idx + 2);
+ //log("pos:'%*s|'", idx, "");
+ //log("new:'%s'", core_basename);
+ //log("old:'%s'", old);
+ free(old);
+ idx += strlen(val);
+ }
+ /* else: invalid %c, % is already copied verbatim,
+ * next loop iteration will copy c */
+ }
}
- /* man core:
+ /* Open (create) compat core file.
+ * man core:
* There are various circumstances in which a core dump file
* is not produced:
*
@@ -209,8 +265,7 @@ static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid)
*
* The RLIMIT_CORE or RLIMIT_FSIZE resource limits for the process
* are set to zero.
- * [shouldn't it be checked by kernel? 2.6.30.9-96 doesn't, still
- * calls us even if "ulimit -c 0"]
+ * [we check RLIMIT_CORE, but how can we check RLIMIT_FSIZE?]
*
* The binary being executed by the process does not have
* read permission enabled. [how we can check it here?]
@@ -221,7 +276,6 @@ static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid)
* (However, see the description of the prctl(2) PR_SET_DUMPABLE operation,
* and the description of the /proc/sys/fs/suid_dumpable file in proc(5).)
*/
-
/* Do not O_TRUNC: if later checks fail, we do not want to have file already modified here */
struct stat sb;
errno = 0;
@@ -240,6 +294,7 @@ static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid)
if (ftruncate(user_core_fd, 0) != 0) {
/* perror first, otherwise unlink may trash errno */
perror_msg("truncate %s/%s", user_pwd, core_basename);
+ unlink(core_basename);
return -1;
}
@@ -248,23 +303,28 @@ static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid)
int main(int argc, char** argv)
{
- int i;
struct stat sb;
- if (argc < 5)
+ if (argc < 10) /* no argv[9]? */
{
- error_msg_and_die("Usage: %s: DUMPDIR PID SIGNO UID CORE_SIZE_LIMIT", argv[0]);
+ /* percent specifier: %s %c %p %u %g %t %h %e */
+ /* argv: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] */
+ error_msg_and_die("Usage: %s DUMPDIR SIGNO CORE_SIZE_LIMIT PID UID GID TIME HOSTNAME BINARY_NAME [OLD_PATTERN]", argv[0]);
}
/* Not needed on 2.6.30.
* At least 2.6.18 has a bug where
- * argv[1] = "DUMPDIR PID SIGNO UID CORE_SIZE_LIMIT"
- * argv[2] = "PID SIGNO UID CORE_SIZE_LIMIT"
+ * argv[1] = "DUMPDIR SIGNO CORE_SIZE_LIMIT ..."
+ * argv[2] = "SIGNO CORE_SIZE_LIMIT ..."
* and so on. Fixing it:
*/
- for (i = 1; argv[i]; i++)
+ if (strchr(argv[1], ' '))
{
- strchrnul(argv[i], ' ')[0] = '\0';
+ int i;
+ for (i = 1; argv[i]; i++)
+ {
+ strchrnul(argv[i], ' ')[0] = '\0';
+ }
}
openlog("abrt", LOG_PID, LOG_DAEMON);
@@ -272,19 +332,38 @@ int main(int argc, char** argv)
errno = 0;
const char* dddir = argv[1];
- pid_t pid = xatoi_u(argv[2]);
- const char* signal_str = argv[3];
- int signal_no = xatoi_u(argv[3]);
- uid_t uid = xatoi_u(argv[4]);
- off_t ulimit_c = strtoull(argv[5], NULL, 10);
+ const char* signal_str = argv[2];
+ int signal_no = xatoi_positive(signal_str);
+ off_t ulimit_c = strtoull(argv[3], NULL, 10);
if (ulimit_c < 0) /* unlimited? */
{
/* set to max possible >0 value */
ulimit_c = ~((off_t)1 << (sizeof(off_t)*8-1));
}
+ pid_t pid = xatoi_positive(argv[4]);
+ uid_t uid = xatoi_positive(argv[5]);
if (errno || pid <= 0)
{
- error_msg_and_die("pid '%s' or limit '%s' is bogus", argv[2], argv[5]);
+ perror_msg_and_die("pid '%s' or limit '%s' is bogus", argv[4], argv[3]);
+ }
+ if (argv[10]) /* OLD_PATTERN */
+ {
+ char *buf = (char*) xzalloc(strlen(argv[10]) / 2 + 2);
+ char *end = hex2bin(buf, argv[10], strlen(argv[10]));
+ if (end && end > buf && end[-1] == '\0')
+ {
+ core_basename = buf;
+ //log("core_basename:'%s'", core_basename);
+ }
+ else
+ {
+ /* Until recently, kernels were truncating expanded core pattern.
+ * In this case, we end up here...
+ */
+ error_msg("bad old pattern '%s', ignoring and using 'core'", argv[10]);
+ /* core_basename = "core"; - already is */
+ free(buf);
+ }
}
int src_fd_binary;
@@ -311,8 +390,8 @@ int main(int argc, char** argv)
/* Open a fd to compat coredump, if requested and is possible */
int user_core_fd = -1;
if (setting_MakeCompatCore && ulimit_c != 0)
- /* note: checks "user_pwd == NULL" inside */
- user_core_fd = open_user_core(user_pwd, uid, pid);
+ /* note: checks "user_pwd == NULL" inside, updates core_basename */
+ user_core_fd = open_user_core(user_pwd, uid, pid, &argv[2]);
if (executable == NULL)
{
@@ -340,7 +419,7 @@ int main(int argc, char** argv)
if (!daemon_is_ok())
{
- /* not an error, exit with exitcode 0 */
+ /* not an error, exit with exit code 0 */
log("abrt daemon is not running. If it crashed, "
"/proc/sys/kernel/core_pattern contains a stale value, "
"consider resetting it to 'core'"
@@ -410,16 +489,32 @@ int main(int argc, char** argv)
return 0;
}
- unsigned path_len = snprintf(path, sizeof(path), "%s/ccpp-%ld-%lu.new",
- dddir, (long)time(NULL), (long)pid);
+ unsigned path_len = snprintf(path, sizeof(path), "%s/ccpp-%s-%lu.new",
+ dddir, iso_date_string(NULL), (long)pid);
if (path_len >= (sizeof(path) - sizeof("/"FILENAME_COREDUMP)))
return 1;
struct dump_dir *dd = dd_create(path, uid);
if (dd)
{
+ dd_create_basic_files(dd, uid);
+
+ char source_filename[sizeof("/proc/%lu/smaps") + sizeof(long)*3];
+ int base_name = sprintf(source_filename, "/proc/%lu/smaps", (long)pid);
+ base_name -= strlen("smaps");
+ char *dest_filename = concat_path_file(dd->dd_dirname, FILENAME_SMAPS);
+ copy_file(source_filename, dest_filename, S_IRUSR | S_IRGRP | S_IWUSR);
+ chown(dest_filename, dd->dd_uid, dd->dd_gid);
+ strcpy(source_filename + base_name, "maps");
+ strcpy(strrchr(dest_filename, '/') + 1, FILENAME_MAPS);
+ copy_file(source_filename, dest_filename, S_IRUSR | S_IRGRP | S_IWUSR);
+ chown(dest_filename, dd->dd_uid, dd->dd_gid);
+ free(dest_filename);
+
char *cmdline = get_cmdline(pid); /* never NULL */
- char *reason = xasprintf("Process %s was killed by signal %s (SIG%s)", executable, signal_str, signame ? signame : signal_str);
+ char *reason = xasprintf("Process %s was killed by signal %s (SIG%s)",
+ executable, signal_str, signame ? signame : signal_str);
+
dd_save_text(dd, FILENAME_ANALYZER, "CCpp");
dd_save_text(dd, FILENAME_EXECUTABLE, executable);
dd_save_text(dd, FILENAME_CMDLINE, cmdline);
@@ -498,8 +593,8 @@ int main(int argc, char** argv)
}
/* We close dumpdir before we start catering for crash storm case.
- * Otherwise, delete_crash_dump_dir's from other concurrent
- * CCpp's won't be able to delete our dump (their delete_crash_dump_dir
+ * Otherwise, delete_dump_dir's from other concurrent
+ * CCpp's won't be able to delete our dump (their delete_dump_dir
* will wait for us), and we won't be able to delete their dumps.
* Classic deadlock.
*/
diff --git a/src/hooks/abrt_exception_handler.py.in b/src/hooks/abrt_exception_handler.py.in
index dd6fbaed..d12968e1 100644
--- a/src/hooks/abrt_exception_handler.py.in
+++ b/src/hooks/abrt_exception_handler.py.in
@@ -25,7 +25,6 @@ Module for the ABRT exception handling hook
import sys
import os
import syslog
-import subprocess
import socket
def write_dump(pid, tb):
@@ -61,8 +60,10 @@ def handleMyException((etype, value, tb)):
# restore original exception handler
sys.excepthook = sys.__excepthook__ # pylint: disable-msg=E1101
- # ignore uncaught ctrl-c
- if etype == KeyboardInterrupt:
+ # ignore
+ # - uncaught ctrl-c
+ # - SystemExit rhbz#636913 -> this exception is not an error
+ if etype in [KeyboardInterrupt, SystemExit]:
return sys.__excepthook__(etype, value, tb)
try:
diff --git a/src/hooks/dumpoops.cpp b/src/hooks/dumpoops.cpp
deleted file mode 100644
index c67f8cda..00000000
--- a/src/hooks/dumpoops.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- Copyright (C) 2010 ABRT team
- Copyright (C) 2010 RedHat Inc
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- Authors:
- Denys Vlasenko <dvlasenk@redhat.com>
- Zdenek Prikryl <zprikryl@redhat.com>
-*/
-
-#include "abrtlib.h"
-#include "abrt_exception.h"
-#include "KerneloopsScanner.h"
-#include <dlfcn.h>
-#include <glib.h>
-
-#define LOADSYM(fp, name) \
-do { \
- fp = (typeof(fp)) (dlsym(handle, name)); \
- if (!fp) \
- perror_msg_and_die(PLUGINS_LIB_DIR"/libKerneloopsScanner.so has no %s", name); \
-} while (0)
-
-
-int main(int argc, char **argv)
-{
- char *program_name = strrchr(argv[0], '/');
- program_name = program_name ? program_name + 1 : argv[0];
-
- /* Parse options */
- bool opt_d = 0, opt_s = 0;
- int opt;
- while ((opt = getopt(argc, argv, "dsv")) != -1) {
- switch (opt) {
- case 'd':
- opt_d = 1;
- break;
- case 's':
- opt_s = 1;
- break;
- case 'v':
- /* Kerneloops code uses VERB3, thus: */
- g_verbose = 3;
- break;
- default:
-usage:
- error_msg_and_die(
- "Usage: %s [-dsv] FILE\n\n"
- "Options:\n"
- "\t-d\tCreate ABRT dump for every oops found\n"
- "\t-s\tPrint found oopses on standard output\n"
- "\t-v\tVerbose\n"
- , program_name
- );
- }
- }
- argv += optind;
- if (!argv[0])
- goto usage;
-
- msg_prefix = program_name;
-
- /* Load KerneloopsScanner plugin */
- // const plugin_info_t *plugin_info;
- CPlugin* (*plugin_newf)(void);
- int (*scan_syslog_file)(GList **oopsList, const char *filename, time_t *last_changed_p);
- int (*save_oops_to_debug_dump)(GList **oopsList);
- void *handle;
-
- errno = 0;
- //TODO: use it directly, not via dlopen?
- handle = dlopen(PLUGINS_LIB_DIR"/libKerneloopsScanner.so", RTLD_NOW);
- if (!handle)
- perror_msg_and_die("can't load %s", PLUGINS_LIB_DIR"/libKerneloopsScanner.so");
-
- // LOADSYM(plugin_info, "plugin_info");
- LOADSYM(plugin_newf, "plugin_new");
- LOADSYM(scan_syslog_file, "scan_syslog_file");
- LOADSYM(save_oops_to_debug_dump, "save_oops_to_debug_dump");
-
- // CKerneloopsScanner* scanner = (CKerneloopsScanner*) plugin_newf();
- // scanner->Init();
- // scanner->LoadSettings(path);
-
- /* Use it: parse and dump the oops */
- GList *oopsList = NULL;
- int cnt = scan_syslog_file(&oopsList, argv[0], NULL);
- log("found oopses: %d", cnt);
-
- if (cnt > 0) {
- if (opt_s) {
- int i = 0;
- int length = g_list_length(oopsList);
- while (i < length) {
- printf("\nVersion: %s", (char*)g_list_nth_data(oopsList, i));
- i++;
- }
- }
- if (opt_d) {
- log("dumping oopses");
- int errors = save_oops_to_debug_dump(&oopsList);
- if (errors > 0)
- {
- log("%d errors while dumping oopses", errors);
- return 1;
- }
- }
- }
-
- for (GList *li = oopsList; li != NULL; li = g_list_next(li))
- free((char*)li->data);
-
- g_list_free(oopsList);
- /*dlclose(handle); - why bother?
- * cos we are handsome and good lookin' guys :D
- */
- return 0;
-}
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index 3716d19e..477963c3 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -1,16 +1,12 @@
-HEADER_FILES = \
- abrt_exception.h \
+libreport_includedir = $(includedir)/report
+libreport_include_HEADERS = \
+ report/crash_data.h \
+ report/dump_dir.h \
+ report/run_event.h
+
+libabrt_includedir = $(includedir)/abrt
+libabrt_include_HEADERS = \
abrtlib.h \
abrt_types.h \
- comm_layer_inner.h \
- abrt_crash_dump.h \
- dbus_common.h \
- dump_dir.h \
- observer.h \
- plugin.h \
- action.h \
- analyzer.h \
+ abrt_crash_data.h \
xfuncs.h
-
-lib_includedir=$(includedir)/abrt/
-lib_include_HEADERS = $(HEADER_FILES)
diff --git a/src/include/abrt_crash_dump.h b/src/include/abrt_crash_data.h
index ed7ffe95..b71b046f 100644
--- a/src/include/abrt_crash_dump.h
+++ b/src/include/abrt_crash_data.h
@@ -19,8 +19,12 @@
#ifndef ABRT_CRASH_DUMP_H_
#define ABRT_CRASH_DUMP_H_
+#include "crash_data.h"
#include "abrt_types.h"
+// Text bigger than this usually is attached, not added inline
+#define CD_TEXT_ATT_SIZE (2*1024)
+
// Keep in sync with CCDump.py:
// Filenames in dump directory:
@@ -32,7 +36,8 @@
#define FILENAME_REASON "reason"
#define FILENAME_COREDUMP "coredump"
#define FILENAME_BACKTRACE "backtrace"
-#define FILENAME_MEMORYMAP "memorymap"
+#define FILENAME_MAPS "maps"
+#define FILENAME_SMAPS "smaps"
#define FILENAME_DUPHASH "global_uuid" /* name is compat, to be renamed to "duphash" */
// Name of the function where the application crashed.
// Optional.
@@ -41,11 +46,11 @@
#define FILENAME_ARCHITECTURE "architecture"
#define FILENAME_KERNEL "kernel"
#define FILENAME_TIME "time"
-#define FILENAME_RELEASE "release" /* from /etc/redhat-release */
-// filled by <what?>
+// From /etc/syste-release or /etc/redhat-release
+#define FILENAME_OS_RELEASE "os_release"
+// Filled by <what?>
#define FILENAME_PACKAGE "package"
#define FILENAME_COMPONENT "component"
-#define FILENAME_DESCRIPTION "description" /* package descr (not crash descr) */
#define FILENAME_COMMENT "comment"
#define FILENAME_REPRODUCE "reproduce"
#define FILENAME_RATING "rating"
@@ -67,62 +72,18 @@
#define CD_EVENTS "Events"
-// Crash data is a map of 3-element vectors of strings: type, editable, content
-#define CD_TYPE 0
-#define CD_EDITABLE 1
-#define CD_CONTENT 2
-
-// SYS - system value, should not be displayed
-// BIN - binary data
-// TXT - text data, can be displayed
-#define CD_SYS "s"
-#define CD_BIN "b"
-#define CD_TXT "t"
-// Text bigger than this usually is attached, not added inline
-#define CD_TEXT_ATT_SIZE (2*1024)
-
-#define CD_ISEDITABLE "y"
-#define CD_ISNOTEDITABLE "n"
-
-
#ifdef __cplusplus
extern "C" {
#endif
-extern const char *const must_have_files[];
-
+#define is_editable_file abrt_is_editable_file
bool is_editable_file(const char *file_name);
+#define log_crash_data abrt_log_crash_data
+void log_crash_data(crash_data_t *crash_data, const char *pfx);
+
#ifdef __cplusplus
}
#endif
-
-#ifdef __cplusplus
-
-// <key, data>
-typedef map_vector_string_t map_crash_data_t;
-typedef std::vector<map_crash_data_t> vector_map_crash_data_t;
-
-void add_to_crash_data_ext(map_crash_data_t& pCrashData,
- const char *pItem,
- const char *pType,
- const char *pEditable,
- const char *pContent);
-// Uses type:CD_TXT, editable:CD_ISNOTEDITABLE
-void add_to_crash_data(map_crash_data_t& pCrashData,
- const char *pItem,
- const char *pContent);
-
-void load_crash_data_from_crash_dump_dir(struct dump_dir *dd, map_crash_data_t& data);
-
-const char *get_crash_data_item_content_or_NULL(const map_crash_data_t& crash_data, const char *key);
-// Aborts if key is not found:
-const std::string& get_crash_data_item_content(const map_crash_data_t& crash_data, const char *key);
-
-void log_map_crash_data(const map_crash_data_t& data, const char *name);
-
-#endif /* __cplusplus */
-
-
#endif
diff --git a/src/include/abrt_exception.h b/src/include/abrt_exception.h
deleted file mode 100644
index b826bfa8..00000000
--- a/src/include/abrt_exception.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- Copyright (C) 2010 ABRT team
- Copyright (C) 2010 RedHat Inc
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef ABRTEXCEPTION_H_
-#define ABRTEXCEPTION_H_
-
-#include "abrtlib.h"
-
-typedef enum {
- EXCEP_UNKNOW,
- EXCEP_DD_OPEN,
- EXCEP_DD_LOAD,
- EXCEP_DD_SAVE,
- EXCEP_DD_DELETE,
- EXCEP_DL,
- EXCEP_PLUGIN,
- EXCEP_ERROR,
-} abrt_exception_t;
-
-/* std::exception is a class with virtual members.
- * deriving from it makes our ctor/dtor much more heavy,
- * and those are inlined in every throw and catch site!
- */
-class CABRTException /*: public std::exception*/
-{
- private:
- abrt_exception_t m_type;
- char *m_what;
-
- /* Not defined. You can't use it */
- CABRTException& operator= (const CABRTException&);
-
- public:
- ~CABRTException() { free(m_what); }
- CABRTException(abrt_exception_t type, const char* fmt, ...);
- CABRTException(const CABRTException& rhs);
-
- abrt_exception_t type() { return m_type; }
- const char* what() const { return m_what; }
-};
-
-#endif
diff --git a/src/include/abrt_types.h b/src/include/abrt_types.h
index 38804895..200946e1 100644
--- a/src/include/abrt_types.h
+++ b/src/include/abrt_types.h
@@ -20,6 +20,34 @@
#define ABRT_TYPES_H_
#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* We can't typedef it to map_string_t, since other parts of ABRT
+ * (daemon, cli) still use that name for C++ container. For now,
+ * let's call it map_string_h:
+ */
+typedef GHashTable map_string_h;
+
+#define new_map_string abrt_new_map_string
+map_string_h *new_map_string(void);
+#define free_map_string abrt_free_map_string
+void free_map_string(map_string_h *ms);
+#define get_map_string_item_or_empty abrt_get_map_string_item_or_empty
+const char *get_map_string_item_or_empty(map_string_h *ms, const char *key);
+static inline
+const char *get_map_string_item_or_NULL(map_string_h *ms, const char *key)
+{
+ return (const char*)g_hash_table_lookup(ms, key);
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#ifdef __cplusplus
#include <map>
#include <set>
diff --git a/src/include/abrtlib.h b/src/include/abrtlib.h
index 8cd0664a..61731587 100644
--- a/src/include/abrtlib.h
+++ b/src/include/abrtlib.h
@@ -42,6 +42,7 @@
#ifdef __cplusplus
# include <string>
#endif
+#include <glib.h>
#ifdef HAVE_CONFIG_H
# include "config.h"
@@ -77,74 +78,99 @@ int vdprintf(int d, const char *format, va_list ap);
#include "hash_sha1.h"
#include "hash_md5.h"
-#include "abrt_crash_dump.h"
-#include "dump_dir.h"
+#include "abrt_crash_data.h"
#include "abrt_types.h"
+#include "dump_dir.h"
+#include "run_event.h"
#ifdef __cplusplus
extern "C" {
#endif
+#define prefixcmp abrt_prefixcmp
int prefixcmp(const char *str, const char *prefix);
+#define suffixcmp abrt_suffixcmp
int suffixcmp(const char *str, const char *suffix);
+#define concat_path_file abrt_concat_path_file
char *concat_path_file(const char *path, const char *filename);
+#define append_to_malloced_string abrt_append_to_malloced_string
char *append_to_malloced_string(char *mstr, const char *append);
+#define skip_whitespace abrt_skip_whitespace
char* skip_whitespace(const char *s);
+#define skip_non_whitespace abrt_skip_non_whitespace
char* skip_non_whitespace(const char *s);
/* Like strcpy but can copy overlapping strings. */
+#define overlapping_strcpy abrt_overlapping_strcpy
void overlapping_strcpy(char *dst, const char *src);
/* A-la fgets, but malloced and of unlimited size */
+#define xmalloc_fgets abrt_xmalloc_fgets
char *xmalloc_fgets(FILE *file);
/* Similar, but removes trailing \n */
+#define xmalloc_fgetline abrt_xmalloc_fgetline
char *xmalloc_fgetline(FILE *file);
/* On error, copyfd_XX prints error messages and returns -1 */
enum {
COPYFD_SPARSE = 1 << 0,
};
+#define copyfd_eof abrt_copyfd_eof
off_t copyfd_eof(int src_fd, int dst_fd, int flags);
+#define copyfd_size abrt_copyfd_size
off_t copyfd_size(int src_fd, int dst_fd, off_t size, int flags);
+#define copyfd_exact_size abrt_copyfd_exact_size
void copyfd_exact_size(int src_fd, int dst_fd, off_t size);
+#define copy_file abrt_copy_file
off_t copy_file(const char *src_name, const char *dst_name, int mode);
+#define copy_file_recursive abrt_copy_file_recursive
+int copy_file_recursive(const char *source, const char *dest);
/* Returns malloc'ed block */
+#define encode_base64 abrt_encode_base64
char *encode_base64(const void *src, int length);
+#define xatou abrt_xatou
unsigned xatou(const char *numstr);
+#define xatoi abrt_xatoi
int xatoi(const char *numstr);
/* Using xatoi() instead of naive atoi() is not always convenient -
* in many places people want *non-negative* values, but store them
* in signed int. Therefore we need this one:
- * dies if input is not in [0, INT_MAX] range. Also will reject '-0' etc */
-int xatoi_u(const char *numstr);
+ * dies if input is not in [0, INT_MAX] range. Also will reject '-0' etc.
+ * It should really be named xatoi_nonnegative (since it allows 0),
+ * but that would be too long.
+ */
+#define xatoi_positive abrt_xatoi_positive
+int xatoi_positive(const char *numstr);
-unsigned long long monotonic_ns(void);
-unsigned long long monotonic_us(void);
-unsigned monotonic_sec(void);
+//unused for now
+//unsigned long long monotonic_ns(void);
+//unsigned long long monotonic_us(void);
+//unsigned monotonic_sec(void);
enum {
- /* on return, pipefds[1] is fd to which parent may write
- * and deliver data to child's stdin: */
- EXECFLG_INPUT = 1 << 0,
- /* on return, pipefds[0] is fd from which parent may read
- * child's stdout: */
- EXECFLG_OUTPUT = 1 << 1,
- /* open child's stdin to /dev/null: */
- EXECFLG_INPUT_NUL = 1 << 2,
- /* open child's stdout to /dev/null: */
- EXECFLG_OUTPUT_NUL = 1 << 3,
- /* redirect child's stderr to stdout: */
- EXECFLG_ERR2OUT = 1 << 4,
- /* open child's stderr to /dev/null: */
- EXECFLG_ERR_NUL = 1 << 5,
- /* suppress perror_msg("Can't execute 'foo'") if exec fails */
- EXECFLG_QUIET = 1 << 6,
- EXECFLG_SETGUID = 1 << 7,
- EXECFLG_SETSID = 1 << 8,
+ /* on return, pipefds[1] is fd to which parent may write
+ * and deliver data to child's stdin: */
+ EXECFLG_INPUT = 1 << 0,
+ /* on return, pipefds[0] is fd from which parent may read
+ * child's stdout: */
+ EXECFLG_OUTPUT = 1 << 1,
+ /* open child's stdin to /dev/null: */
+ EXECFLG_INPUT_NUL = 1 << 2,
+ /* open child's stdout to /dev/null: */
+ EXECFLG_OUTPUT_NUL = 1 << 3,
+ /* redirect child's stderr to stdout: */
+ EXECFLG_ERR2OUT = 1 << 4,
+ /* open child's stderr to /dev/null: */
+ EXECFLG_ERR_NUL = 1 << 5,
+ /* suppress perror_msg("Can't execute 'foo'") if exec fails */
+ EXECFLG_QUIET = 1 << 6,
+ EXECFLG_SETGUID = 1 << 7,
+ EXECFLG_SETSID = 1 << 8,
};
/* Returns pid */
+#define fork_execv_on_steroids abrt_fork_execv_on_steroids
pid_t fork_execv_on_steroids(int flags,
char **argv,
int *pipefds,
@@ -153,110 +179,67 @@ pid_t fork_execv_on_steroids(int flags,
uid_t uid);
/* Returns malloc'ed string. NULs are retained, and extra one is appended
* after the last byte (this NUL is not accounted for in *size_p) */
+#define run_in_shell_and_save_output abrt_run_in_shell_and_save_output
char *run_in_shell_and_save_output(int flags,
- const char *cmd,
- const char *dir,
- size_t *size_p);
-
-/* Networking helpers */
-typedef struct len_and_sockaddr {
- socklen_t len;
- union {
- struct sockaddr sa;
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
- } u;
-} len_and_sockaddr;
-enum {
- LSA_LEN_SIZE = offsetof(len_and_sockaddr, u),
- LSA_SIZEOF_SA = sizeof(struct sockaddr) > sizeof(struct sockaddr_in6) ?
- sizeof(struct sockaddr) : sizeof(struct sockaddr_in6),
-};
-void setsockopt_reuseaddr(int fd);
-int setsockopt_broadcast(int fd);
-int setsockopt_bindtodevice(int fd, const char *iface);
-len_and_sockaddr* get_sock_lsa(int fd);
-void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen);
-unsigned lookup_port(const char *port, const char *protocol, unsigned default_port);
-int get_nport(const struct sockaddr *sa);
-void set_nport(len_and_sockaddr *lsa, unsigned port);
-len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af);
-len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t af);
-len_and_sockaddr* host2sockaddr(const char *host, int port);
-len_and_sockaddr* xhost2sockaddr(const char *host, int port);
-len_and_sockaddr* xdotted2sockaddr(const char *host, int port);
-int xsocket_type(len_and_sockaddr **lsap, int family, int sock_type);
-int xsocket_stream(len_and_sockaddr **lsap);
-int create_and_bind_stream_or_die(const char *bindaddr, int port);
-int create_and_bind_dgram_or_die(const char *bindaddr, int port);
-int create_and_connect_stream_or_die(const char *peer, int port);
-int xconnect_stream(const len_and_sockaddr *lsa);
-char* xmalloc_sockaddr2host(const struct sockaddr *sa);
-char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa);
-char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa);
-char* xmalloc_sockaddr2dotted(const struct sockaddr *sa);
-char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa);
+ const char *cmd,
+ const char *dir,
+ size_t *size_p);
/* Random utility functions */
+/* Frees every element'd data using free(),
+ * then frees list itself using g_list_free(list):
+ */
+#define list_free_with_free abrt_list_free_with_free
+void list_free_with_free(GList *list);
+
+#define get_dirsize abrt_get_dirsize
double get_dirsize(const char *pPath);
+#define get_dirsize_find_largest_dir abrt_get_dirsize_find_largest_dir
double get_dirsize_find_largest_dir(
const char *pPath,
char **worst_dir, /* can be NULL */
const char *excluded /* can be NULL */
);
+/* Emit a string of hex representation of bytes */
+#define bin2hex abrt_bin2hex
+char* bin2hex(char *dst, const char *str, int count);
+/* Convert "xxxxxxxx" hex string to binary, no more than COUNT bytes */
+#define hex2bin abrt_hex2bin
+char* hex2bin(char *dst, const char *str, int count);
+
/* Returns command line of running program.
* Caller is responsible to free() the returned value.
* If the pid is not valid or command line can not be obtained,
* empty string is returned.
*/
+#define get_cmdline abrt_get_cmdline
char* get_cmdline(pid_t pid);
/* Returns 1 if abrtd daemon is running, 0 otherwise. */
+#define daemon_is_ok abrt_daemon_is_ok
int daemon_is_ok();
-struct run_event_state {
- int (*post_run_callback)(const char *dump_dir_name, void *param);
- void *post_run_param;
- char* (*logging_callback)(char *log_line, void *param);
- void *logging_param;
-};
-static inline struct run_event_state *new_run_event_state()
- { return (struct run_event_state*)xzalloc(sizeof(struct run_event_state)); }
-static inline void free_run_event_state(struct run_event_state *state)
- { free(state); }
-/* Returns exitcode of first failed action, or first nonzero return value
- * of post_run_callback. If all actions are successful, returns 0.
- * If no actions were run for the event, returns -1.
+/* Takes ptr to time_t, or NULL if you want to use current time.
+ * Returns "YYYY-MM-DD-hh:mm:ss" string.
*/
-int run_event(struct run_event_state *state, const char *dump_dir_name, const char *event);
-char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx);
-
-#ifdef __cplusplus
-}
-#endif
-
-
-/* C++ style stuff */
-#ifdef __cplusplus
-std::string unsigned_to_string(unsigned long long x);
-std::string signed_to_string(long long x);
-template <class T> inline
-std::string to_string(T x)
-{
- if ((T)~(T)0 < (T)0) /* T is a signed type */
- return signed_to_string(x);
- return unsigned_to_string(x);
-}
-
-void parse_args(const char *psArgs, vector_string_t& pArgs, int quote = -1);
-void parse_release(const char *pRelease, char **product, char **version);
-
-char* make_description_bz(const map_crash_data_t& pCrashData);
-char* make_description_reproduce_comment(const map_crash_data_t& pCrashData);
-char* make_description_logger(const map_crash_data_t& pCrashData);
-char* make_description_mailx(const map_crash_data_t& pCrashData);
+#define iso_date_string abrt_iso_date_string
+char *iso_date_string(time_t *pt);
+
+#define make_description_bz abrt_make_description_bz
+char* make_description_bz(crash_data_t *crash_data);
+#define make_description_reproduce_comment abrt_make_description_reproduce_comment
+char* make_description_reproduce_comment(crash_data_t *crash_data);
+#define make_description_logger abrt_make_description_logger
+char* make_description_logger(crash_data_t *crash_data);
+#define make_description_mailx abrt_make_description_mailx
+char* make_description_mailx(crash_data_t *crash_data);
+
+#define parse_release_for_bz abrt_parse_release_for_bz
+void parse_release_for_bz(const char *pRelease, char **product, char **version);
+#define parse_release_for_rhts abrt_parse_release_for_rhts
+void parse_release_for_rhts(const char *pRelease, char **product, char **version);
/**
* Loads settings and stores it in second parameter. On success it
@@ -264,29 +247,21 @@ char* make_description_mailx(const map_crash_data_t& pCrashData);
*
* @param path A path of config file.
* Config file consists of "key=value" lines.
- * @param settings A readed plugin's settings.
+ * @param settings A read plugin's settings.
* @param skipKeysWithoutValue
* If true, lines in format "key=" (without value) are skipped.
* Otherwise empty value "" is inserted into pSettings.
* @return if it success it returns true, otherwise it returns false.
*/
-extern bool LoadPluginSettings(const char *pPath,
- map_plugin_settings_t& pSettings,
- bool skipKeysWithoutValue = true);
+#define load_conf_file abrt_load_conf_file
+bool load_conf_file(const char *pPath, map_string_h *settings, bool skipKeysWithoutValue);
-// TODO: npajkovs: full rewrite ssprintf -> xasprintf
-static inline std::string ssprintf(const char *format, ...)
-{
- va_list p;
- char *string_ptr;
+/* Tries to create a copy of dump_dir_name in base_dir, with same or similar basename.
+ * Returns NULL if copying failed. In this case, logs a message before returning. */
+#define steal_directory abrt_steal_directory
+struct dump_dir *steal_directory(const char *base_dir, const char *dump_dir_name);
- va_start(p, format);
- string_ptr = xvasprintf(format, p);
- va_end(p);
-
- std::string res = string_ptr;
- free(string_ptr);
- return res;
+#ifdef __cplusplus
}
#endif
diff --git a/src/include/action.h b/src/include/action.h
deleted file mode 100644
index 21183366..00000000
--- a/src/include/action.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- Action.h - header file for action plugin
-
- Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef ACTION_H_
-#define ACTION_H_
-
-#include "plugin.h"
-
-/**
- * An abstract class. The class defines an action plugin interface.
- */
-class CAction : public CPlugin
-{
- public:
- /**
- * A Method which performs particular action. As the first parameter it
- * takes an action directory. It could be either a directory of actual
- * crash or it could be a directory contains all crashes. It depends on
- * who call the plugin. The plugin can takes arguments, but the plugin
- * has to parse them by itself.
- * @param pActionDir An actual directory.
- * @param pArgs Plugin's arguments.
- */
- virtual void Run(const char *pActionDir, const char *pArgs, int force) = 0;
-};
-
-#endif
diff --git a/src/include/analyzer.h b/src/include/analyzer.h
deleted file mode 100644
index 1d78d576..00000000
--- a/src/include/analyzer.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- Analyzer.h - header file for analyzer plugin
-
- Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef ANALYZER_H_
-#define ANALYZER_H_
-
-#include <string>
-#include "plugin.h"
-
-/**
- * An abstract class. The class defines an analyzer plugin interface.
- */
-class CAnalyzer : public CPlugin
-{
-};
-
-#endif /*ANALYZER_H_*/
diff --git a/src/include/dbus_common.h b/src/include/dbus_common.h
deleted file mode 100644
index 6c739169..00000000
--- a/src/include/dbus_common.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- Copyright (C) 2009 Jiri Moskovcak (jmoskovc@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef DBUSCOMMON_H_
-#define DBUSCOMMON_H_
-
-#include "abrt_crash_dump.h"
-
-#define ABRTD_DBUS_NAME "com.redhat.abrt"
-#define ABRTD_DBUS_PATH "/com/redhat/abrt"
-#define ABRTD_DBUS_IFACE "com.redhat.abrt"
-
-#endif
diff --git a/src/include/observer.h b/src/include/observer.h
deleted file mode 100644
index 1c8f2355..00000000
--- a/src/include/observer.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- Copyright (C) 2010 ABRT team
- Copyright (C) 2010 RedHat Inc
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef OBSERVER_H_
-#define OBSERVER_H_
-
-#include <string>
-#include "dbus_common.h"
-
-class CObserver {
- public:
- virtual ~CObserver() {}
- virtual void Status(const char *pMessage, const char* peer) = 0;
- virtual void Warning(const char *pMessage, const char* peer) = 0;
-};
-
-#endif
diff --git a/src/include/plugin.h b/src/include/plugin.h
deleted file mode 100644
index 3f652e65..00000000
--- a/src/include/plugin.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- Plugin.h - header file for plugin. It contains mandatory macros
- and common function for plugins
-
- Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef PLUGIN_H_
-#define PLUGIN_H_
-
-#include "abrt_types.h"
-#include "abrt_crash_dump.h"
-
-#define PLUGINS_MAGIC_NUMBER 6
-
-#define PLUGINS_CONF_EXTENSION "conf"
-#define PLUGINS_LIB_EXTENSION "so"
-#define PLUGINS_LIB_PREFIX "lib"
-
-/**
- * An abstract class. The class defines a common plugin interface. If a plugin
- * has some settings, then a *Settings(*) method has to be written.
- */
-class CPlugin
-{
- protected:
- map_plugin_settings_t m_pSettings;
-
- public:
- CPlugin();
- /**
- * A destructor.
- */
- virtual ~CPlugin();
- /**
- * A method, which initializes a plugin. It is not mandatory method.
- */
- virtual void Init();
- /**
- * A method, which deinitializes a plugin. It is not mandatory method.
- */
- virtual void DeInit();
- /**
- * A method, which takes a settings and apply them. It is not a mandatory method.
- * @param pSettings Plugin's settings
- */
- virtual void SetSettings(const map_plugin_settings_t& pSettings);
- /**
- * A method, which return current settings. It is not mandatory method.
- * @return Plugin's settings
- */
-///
-// virtual const map_plugin_settings_t& GetSettings();
-};
-
-/**
- * An enum of plugin types.
- */
-typedef enum {
- ANALYZER, /**< An analyzer plugin*/
- ACTION, /**< An action plugin*/
- REPORTER, /**< A reporter plugin*/
- DATABASE, /**< A database plugin*/
- MAX_PLUGIN_TYPE = DATABASE,
-} plugin_type_t;
-
-/**
- * A struct contains all needed data about particular plugin.
- */
-typedef struct SPluginInfo
-{
- const plugin_type_t m_Type; /**< Plugin type.*/
- const char *const m_sName; /**< Plugin name.*/
- const char *const m_sVersion; /**< Plugin version.*/
- const char *const m_sDescription; /**< Plugin description.*/
- const char *const m_sEmail; /**< Plugin author's email.*/
- const char *const m_sWWW; /**< Plugin's home page.*/
- const char *const m_sGTKBuilder; /**< Plugin's gui description.*/
- const int m_nMagicNumber; /**< Plugin magical number.*/
-} plugin_info_t;
-
-#define PLUGIN_INFO(type, plugin_class, name, version, description, email, www, gtk_builder)\
- extern "C" CPlugin* plugin_new()\
- {\
- return new plugin_class();\
- }\
- extern "C" const plugin_info_t plugin_info =\
- {\
- type,\
- name,\
- version,\
- description,\
- email,\
- www,\
- gtk_builder,\
- PLUGINS_MAGIC_NUMBER,\
- };
-
-#endif
diff --git a/src/include/report/crash_data.h b/src/include/report/crash_data.h
new file mode 100644
index 00000000..3854118a
--- /dev/null
+++ b/src/include/report/crash_data.h
@@ -0,0 +1,101 @@
+/*
+ Copyright (C) 2009 Abrt team.
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef CRASH_DATA_H_
+#define CRASH_DATA_H_
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dump_dir;
+
+enum {
+ CD_FLAG_BIN = (1 << 0),
+ CD_FLAG_TXT = (1 << 1),
+ CD_FLAG_ISEDITABLE = (1 << 2),
+ CD_FLAG_ISNOTEDITABLE = (1 << 3),
+};
+
+struct crash_item {
+ char *content;
+ unsigned flags;
+};
+typedef struct crash_item crash_item;
+
+/* In-memory crash data structure and accessors */
+
+typedef GHashTable crash_data_t;
+
+crash_data_t *new_crash_data(void);
+
+static inline void free_crash_data(crash_data_t *crash_data)
+{
+ if (crash_data)
+ g_hash_table_destroy(crash_data);
+}
+
+void add_to_crash_data_ext(crash_data_t *crash_data,
+ const char *name,
+ const char *content,
+ unsigned flags);
+/* Uses CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE flags */
+void add_to_crash_data(crash_data_t *crash_data,
+ const char *name,
+ const char *content);
+
+static inline struct crash_item *get_crash_data_item_or_NULL(crash_data_t *crash_data, const char *key)
+{
+ return (struct crash_item *)g_hash_table_lookup(crash_data, key);
+}
+const char *get_crash_item_content_or_NULL(crash_data_t *crash_data, const char *key);
+/* Aborts if key is not found: */
+const char *get_crash_item_content_or_die(crash_data_t *crash_data, const char *key);
+
+
+/* Vector of these structures */
+
+typedef GPtrArray vector_of_crash_data_t;
+
+static inline crash_data_t *get_crash_data(vector_of_crash_data_t *vector, unsigned i)
+{
+ return (crash_data_t *)g_ptr_array_index(vector, i);
+}
+
+vector_of_crash_data_t *new_vector_of_crash_data(void);
+static inline void free_vector_of_crash_data(vector_of_crash_data_t *vector)
+{
+ if (vector)
+ g_ptr_array_free(vector, TRUE);
+}
+
+
+/* Conversions between in-memory and on-disk formats */
+
+void load_crash_data_from_dump_dir(crash_data_t *crash_data, struct dump_dir *dd);
+crash_data_t *create_crash_data_from_dump_dir(struct dump_dir *dd);
+
+struct dump_dir *create_dump_dir_from_crash_data(crash_data_t *crash_data, const char *base_dir_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/dump_dir.h b/src/include/report/dump_dir.h
index 7a38f1e4..1376d7f3 100644
--- a/src/include/dump_dir.h
+++ b/src/include/report/dump_dir.h
@@ -21,16 +21,21 @@
#ifndef DUMP_DIR_H_
#define DUMP_DIR_H_
+/* For DIR */
+#include <sys/types.h>
+#include <dirent.h>
+
#ifdef __cplusplus
extern "C" {
#endif
enum {
DD_FAIL_QUIETLY = (1 << 0),
+ DD_OPEN_READONLY = (1 << 1),
};
struct dump_dir {
- char *dd_dir;
+ char *dd_dirname;
DIR *next_dir;
int locked;
uid_t dd_uid;
@@ -40,22 +45,27 @@ struct dump_dir {
void dd_close(struct dump_dir *dd);
struct dump_dir *dd_opendir(const char *dir, int flags);
+/* Pass uid = (uid_t)-1L to disable chown'ing of newly created files
+ * (IOW: if you aren't running under root):
+ */
struct dump_dir *dd_create(const char *dir, uid_t uid);
+void dd_create_basic_files(struct dump_dir *dd, uid_t uid);
int dd_exist(struct dump_dir *dd, const char *path);
DIR *dd_init_next_file(struct dump_dir *dd);
int dd_get_next_file(struct dump_dir *dd, char **short_name, char **full_name);
enum {
/* DD_FAIL_QUIETLY bit is valid for dd_load_text_ext too, */
- DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE = (1 << 1),
+ DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE = (1 << 2),
};
char* dd_load_text_ext(const struct dump_dir *dd, const char *name, unsigned flags);
char* dd_load_text(const struct dump_dir *dd, const char *name);
void dd_save_text(struct dump_dir *dd, const char *name, const char *data);
void dd_save_binary(struct dump_dir *dd, const char *name, const char *data, unsigned size);
-void dd_delete(struct dump_dir *dd);
+/* Returns 0 if directory is deleted or not found */
+int dd_delete(struct dump_dir *dd);
-void delete_crash_dump_dir(const char *dd_dir);
+void delete_dump_dir(const char *dirname);
#ifdef __cplusplus
}
diff --git a/src/include/report/run_event.h b/src/include/report/run_event.h
new file mode 100644
index 00000000..7fd8edea
--- /dev/null
+++ b/src/include/report/run_event.h
@@ -0,0 +1,82 @@
+/*
+ Copyright (C) 2009 Abrt team.
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef RUN_EVENT_H_
+#define RUN_EVENT_H_
+
+#include "crash_data.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dump_dir;
+
+struct run_event_state {
+ int children_count;
+
+ /* Used only for post-create dup detection. TODO: document its API */
+ int (*post_run_callback)(const char *dump_dir_name, void *param);
+ void *post_run_param;
+
+ /* Can take ownership of log_line, which is malloced. In this case, return NULL.
+ * Otherwise should return log_line (it will be freed by caller)
+ */
+ char* (*logging_callback)(char *log_line, void *param);
+ void *logging_param;
+
+ /* Internal data for async command execution */
+ GList *commands;
+ pid_t command_pid;
+ int command_out_fd;
+};
+struct run_event_state *new_run_event_state(void);
+void free_run_event_state(struct run_event_state *state);
+
+/* Asyncronous command execution */
+
+/* Returns 0 if no commands found for this dump_dir_name+event, else >0 */
+int prepare_commands(struct run_event_state *state, const char *dump_dir_name, const char *event);
+/* Returns -1 is no more commands needs to be executed,
+ * else sets state->command_pid and state->command_out_fd and returns >=0
+ */
+int spawn_next_command(struct run_event_state *state, const char *dump_dir_name, const char *event);
+/* Cleans up internal state created in prepare_commands */
+void free_commands(struct run_event_state *state);
+
+/* Syncronous command execution */
+
+/* Returns exit code of first failed action, or first nonzero return value
+ * of post_run_callback. If all actions are successful, returns 0.
+ */
+int run_event_on_dir_name(struct run_event_state *state, const char *dump_dir_name, const char *event);
+int run_event_on_crash_data(struct run_event_state *state, crash_data_t *data, const char *event);
+
+/* Querying for possible events */
+
+/* Scans event.conf for events starting with pfx which are applicable
+ * to dd, or (if dd is NULL), to dump_dir.
+ * Returns a malloced string with '\n'-terminated event names.
+ */
+char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/xfuncs.h b/src/include/xfuncs.h
index 20284564..61188c81 100644
--- a/src/include/xfuncs.h
+++ b/src/include/xfuncs.h
@@ -29,43 +29,69 @@
extern "C" {
#endif
+#define ndelay_on abrt_ndelay_on
int ndelay_on(int fd);
+#define ndelay_off abrt_ndelay_off
int ndelay_off(int fd);
+#define close_on_exec_on abrt_close_on_exec_on
int close_on_exec_on(int fd);
-void* xcalloc(size_t nmemb, size_t size);
+#define xmalloc abrt_xmalloc
void* xmalloc(size_t size);
+#define xrealloc abrt_xrealloc
void* xrealloc(void *ptr, size_t size);
+#define xzalloc abrt_xzalloc
void* xzalloc(size_t size);
+#define xstrdup abrt_xstrdup
char* xstrdup(const char *s);
+#define xstrndup abrt_xstrndup
char* xstrndup(const char *s, int n);
+#define xpipe abrt_xpipe
void xpipe(int filedes[2]);
+#define xdup abrt_xdup
void xdup(int from);
+#define xdup2 abrt_xdup2
void xdup2(int from, int to);
+#define xmove_fd abrt_xmove_fd
void xmove_fd(int from, int to);
+#define xwrite abrt_xwrite
void xwrite(int fd, const void *buf, size_t count);
+#define xwrite_str abrt_xwrite_str
void xwrite_str(int fd, const char *str);
+#define xlseek abrt_xlseek
off_t xlseek(int fd, off_t offset, int whence);
+#define xchdir abrt_xchdir
void xchdir(const char *path);
+#define xvasprintf abrt_xvasprintf
char* xvasprintf(const char *format, va_list p);
+#define xasprintf abrt_xasprintf
char* xasprintf(const char *format, ...);
+#define xsetenv abrt_xsetenv
void xsetenv(const char *key, const char *value);
+#define xsocket abrt_xsocket
int xsocket(int domain, int type, int protocol);
+#define xbind abrt_xbind
void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
+#define xlisten abrt_xlisten
void xlisten(int s, int backlog);
+#define xsendto abrt_xsendto
ssize_t xsendto(int s, const void *buf, size_t len,
const struct sockaddr *to, socklen_t tolen);
+#define xstat abrt_xstat
void xstat(const char *name, struct stat *stat_buf);
+#define xopen3 abrt_xopen3
int xopen3(const char *pathname, int flags, int mode);
+#define xopen abrt_xopen
int xopen(const char *pathname, int flags);
+#define xunlink abrt_xunlink
void xunlink(const char *pathname);
/* Just testing dent->d_type == DT_REG is wrong: some filesystems
@@ -74,18 +100,28 @@ void xunlink(const char *pathname);
* This function handles this case. Note: it returns 0 on symlinks
* even if they point to regular files.
*/
+#define is_regular_file abrt_is_regular_file
int is_regular_file(struct dirent *dent, const char *dirname);
+
+#define dot_or_dotdot abrt_dot_or_dotdot
bool dot_or_dotdot(const char *filename);
+#define last_char_is abrt_last_char_is
char *last_char_is(const char *s, int c);
+#define string_to_bool abrt_string_to_bool
bool string_to_bool(const char *s);
+#define xseteuid abrt_xseteuid
void xseteuid(uid_t euid);
+#define xsetegid abrt_xsetegid
void xsetegid(gid_t egid);
+#define xsetreuid abrt_xsetreuid
void xsetreuid(uid_t ruid, uid_t euid);
+#define xsetregid abrt_xsetregid
void xsetregid(gid_t rgid, gid_t egid);
/* Returns getpwuid(uid)->pw_dir or NULL */
+#define get_home_dir abrt_get_home_dir
const char *get_home_dir(uid_t uid);
#ifdef __cplusplus
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index ad2d2025..bad3e63a 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,69 +1,69 @@
-# libabrt - the stuff shared among most of abrt (like xmalloc, logging)
-# libabrt_daemon - only daemon related things are here
+# libreport - the stuff shared among most of abrt (like xmalloc, logging)
# libabrt_dbus - daemon, cli and applet use this
# libabrt_web - for abrt-action-foo where foo deals with network/web/ftp/...
lib_LTLIBRARIES = \
- libabrt.la \
- libabrt_daemon.la \
+ libreport.la \
libabrt_dbus.la \
libabrt_web.la
-HEADER_DIR = $(srcdir)/../include
-AM_CPPFLAGS = -I$(HEADER_DIR)
-
# Not used just yet:
# time.cpp
# xconnect.cpp
-libabrt_la_SOURCES = \
+libreport_la_SOURCES = \
xfuncs.c \
encbase64.c \
+ binhex.c \
stdio_helpers.c \
hash_md5.c hash_md5.h \
hash_sha1.c hash_sha1.h \
read_write.c read_write.h \
logging.c logging.h \
copyfd.c \
+ copy_file_recursive.c \
concat_path_file.c \
append_to_malloced_string.c \
overlapping_strcpy.c \
skip_whitespace.c \
- stringops.cpp \
+ glib_support.c \
+ iso_date_string.c \
strbuf.c strbuf.h \
- xatonum.c numtoa.cpp \
+ xatonum.c \
spawn.c \
dirsize.c \
dump_dir.c \
get_cmdline.c \
daemon_is_ok.c \
- load_plugin_settings.cpp \
- make_descr.cpp \
+ load_plugin_settings.c \
+ make_descr.c \
run_event.c \
- crash_dump.cpp \
- ABRTException.cpp \
+ crash_data.c \
+ create_dump_dir.c \
+ abrt_types.c \
hooklib.c hooklib.h \
- parse_release.cpp \
- parse_options.c parse_options.h
-libabrt_la_CPPFLAGS = \
+ parse_release.c \
+ parse_options.c parse_options.h \
+ steal_directory.c
+libreport_la_CPPFLAGS = \
-Wall -Werror \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
-DCONF_DIR=\"$(CONF_DIR)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
$(GLIB_CFLAGS) \
-D_GNU_SOURCE
-libabrt_la_LDFLAGS = \
+libreport_la_LDFLAGS = \
-version-info 0:1:0
-libabrt_la_LIBADD = \
+libreport_la_LIBADD = \
$(GLIB_LIBS)
libabrt_dbus_la_SOURCES = \
abrt_dbus.c abrt_dbus.h
libabrt_dbus_la_CPPFLAGS = \
- -Wall -Werror \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
@@ -71,6 +71,7 @@ libabrt_dbus_la_CPPFLAGS = \
-DVAR_RUN=\"$(VAR_RUN)\" \
$(GLIB_CFLAGS) \
$(DBUS_CFLAGS) \
+ -Wall -Werror \
-D_GNU_SOURCE
libabrt_dbus_la_LDFLAGS = \
-version-info 0:1:0
@@ -78,34 +79,18 @@ libabrt_dbus_la_LIBADD = \
$(GLIB_LIBS) \
$(DBUS_LIBS)
-libabrt_daemon_la_SOURCES = \
- $(HEADER_DIR)/comm_layer_inner.h CommLayerInner.cpp \
- $(HEADER_DIR)/plugin.h Plugin.cpp
-libabrt_daemon_la_CPPFLAGS = \
- -Wall \
- -I$(srcdir)/../include \
- -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
- -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
- -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
- -DCONF_DIR=\"$(CONF_DIR)\" \
- -DVAR_RUN=\"$(VAR_RUN)\" \
- -D_GNU_SOURCE
-libabrt_daemon_la_LDFLAGS = \
- -version-info 0:1:0
-libabrt_daemon_la_LIBADD = \
- -ldl
-
libabrt_web_la_SOURCES = \
abrt_curl.h abrt_curl.c \
abrt_xmlrpc.h abrt_xmlrpc.cpp
libabrt_web_la_CPPFLAGS = \
-Wall -Werror \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
-DCONF_DIR=\"$(CONF_DIR)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
+ $(GLIB_CFLAGS) \
$(CURL_CFLAGS) \
$(LIBXML_CFLAGS) \
$(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \
@@ -113,6 +98,7 @@ libabrt_web_la_CPPFLAGS = \
libabrt_web_la_LDFLAGS = \
-version-info 0:1:0
libabrt_web_la_LIBADD = \
+ $(GLIB_LIBS) \
$(CURL_LIBS) \
$(LIBXML_LIBS) \
$(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS)
diff --git a/src/lib/Plugin.cpp b/src/lib/Plugin.cpp
deleted file mode 100644
index 0c2137f5..00000000
--- a/src/lib/Plugin.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include "plugin.h"
-#include "abrtlib.h"
-
-CPlugin::CPlugin() {}
-
-/* class CPlugin's virtuals */
-CPlugin::~CPlugin() {}
-void CPlugin::Init() {}
-void CPlugin::DeInit() {}
-void CPlugin::SetSettings(const map_plugin_settings_t& pSettings)
-{
- m_pSettings = pSettings;
- VERB3
- {
- log("SetSettings:");
- map_plugin_settings_t::const_iterator it = m_pSettings.begin();
- while (it != m_pSettings.end())
- {
- log(" settings[%s]='%s'", it->first.c_str(), it->second.c_str());
- it++;
- }
- }
-}
diff --git a/src/lib/abrt_curl.c b/src/lib/abrt_curl.c
index f23d3949..a17f14d2 100644
--- a/src/lib/abrt_curl.c
+++ b/src/lib/abrt_curl.c
@@ -287,8 +287,10 @@ abrt_post(abrt_post_state_t *state,
// truncates data_size on 32-bit arch. Need xcurl_easy_setopt_long_long()?
// Also, I'm not sure CURLOPT_POSTFIELDSIZE_LARGE special-cases -1.
}
- // Override "Content-Type:"
+
struct curl_slist *httpheader_list = NULL;
+
+ // Override "Content-Type:"
if (data_size != ABRT_POST_DATA_FROMFILE_AS_FORM_DATA)
{
char *content_type_header = xasprintf("Content-Type: %s", content_type);
@@ -300,6 +302,12 @@ abrt_post(abrt_post_state_t *state,
xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPHEADER, httpheader_list);
}
+ // Override "Accept: text/plain": helps convince server to send plain-text
+ // error messages in the body of HTTP error responses [not verified to work]
+ httpheader_list = curl_slist_append(httpheader_list, "Accept: text/plain");
+ if (!httpheader_list)
+ error_msg_and_die("out of memory");
+
// Disabled: was observed to also handle "305 Use proxy" redirect,
// apparently with POST->GET remapping - which server didn't like at all.
// Attempted to suppress remapping on 305 using CURLOPT_POSTREDIR of -1,
@@ -354,6 +362,11 @@ abrt_post(abrt_post_state_t *state,
goto ret;
}
+ // curl-7.20.1 doesn't do it, we get NULL body in the log message below
+ // unless we fflush the body memstream ourself
+ if (body_stream)
+ fflush(body_stream);
+
// Headers/body are already saved (if requested), extract more info
curl_err = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &response_code);
die_if_curl_error(curl_err);
diff --git a/src/lib/abrt_dbus.c b/src/lib/abrt_dbus.c
index 6bc155e3..5d8d861d 100644
--- a/src/lib/abrt_dbus.c
+++ b/src/lib/abrt_dbus.c
@@ -17,7 +17,6 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <dbus/dbus.h>
-#include <glib.h>
#include "abrtlib.h"
#include "abrt_dbus.h"
@@ -156,6 +155,98 @@ void store_string(DBusMessageIter* iter, const char* val)
free((char*)sanitized);
}
+/* Helper for storing map_string */
+void store_map_string(DBusMessageIter* dbus_iter, map_string_h *val)
+{
+ DBusMessageIter sub_iter;
+ /* map_string is a map. map in dbus is an array of two element structs "({...})":
+ * "s" (string) for key and "s" for value (in this case, also string) */
+ if (!dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY, "{ss}", &sub_iter))
+ die_out_of_memory();
+
+ GHashTableIter iter;
+ char *name;
+ char *value;
+ g_hash_table_iter_init(&iter, val);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
+ {
+ DBusMessageIter sub_sub_iter;
+ if (!dbus_message_iter_open_container(&sub_iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub_sub_iter))
+ die_out_of_memory();
+ store_string(&sub_sub_iter, name);
+ store_string(&sub_sub_iter, value);
+ if (!dbus_message_iter_close_container(&sub_iter, &sub_sub_iter))
+ die_out_of_memory();
+ }
+
+ if (!dbus_message_iter_close_container(dbus_iter, &sub_iter))
+ die_out_of_memory();
+}
+
+/* Helpers for storing crash_data */
+
+static void store_crash_item(DBusMessageIter* iter, struct crash_item *val)
+{
+ DBusMessageIter sub_iter;
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub_iter))
+ die_out_of_memory();
+
+ /* Compat with python/cli:
+ * Crash item is represented in dbus as 3-element vector of strings:
+ * type, editable, content.
+ * This doesn't match daemon-side representation: { content, flags } struct
+ */
+ store_string(&sub_iter, (val->flags & CD_FLAG_BIN ? "b" : "t"));
+ store_string(&sub_iter, (val->flags & CD_FLAG_ISEDITABLE ? "y" : "n"));
+ store_string(&sub_iter, val->content);
+
+ if (!dbus_message_iter_close_container(iter, &sub_iter))
+ die_out_of_memory();
+}
+void store_crash_data(DBusMessageIter* dbus_iter, crash_data_t *val)
+{
+ DBusMessageIter sub_iter;
+ /* crash_data is a map. map in dbus is an array of two element structs "({...})":
+ * "s" (string) for key and "as" for value (in this case, array of strings) */
+ if (!dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY, "{sas}", &sub_iter))
+ die_out_of_memory();
+
+ GHashTableIter iter;
+ char *name;
+ struct crash_item *value;
+ g_hash_table_iter_init(&iter, val);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
+ {
+ DBusMessageIter sub_sub_iter;
+ if (!dbus_message_iter_open_container(&sub_iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub_sub_iter))
+ die_out_of_memory();
+ store_string(&sub_sub_iter, name);
+ store_crash_item(&sub_sub_iter, value);
+ if (!dbus_message_iter_close_container(&sub_iter, &sub_sub_iter))
+ die_out_of_memory();
+ }
+
+ if (!dbus_message_iter_close_container(dbus_iter, &sub_iter))
+ die_out_of_memory();
+}
+void store_vector_of_crash_data(DBusMessageIter* iter, vector_of_crash_data_t *val)
+{
+ DBusMessageIter sub_iter;
+ /* "array of maps". map in dbus is an array ("a") of two element structs "({...})":
+ * "s" (string) for key and "as" for value (in this case, array of strings) */
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "a{sas}", &sub_iter))
+ die_out_of_memory();
+
+ for (unsigned i = 0; i < val->len; i++)
+ {
+ crash_data_t *crash_data = get_crash_data(val, i);
+ store_crash_data(&sub_iter, crash_data);
+ }
+
+ if (!dbus_message_iter_close_container(iter, &sub_iter))
+ die_out_of_memory();
+}
+
/*
* Helpers for parsing DBus messages
@@ -217,6 +308,8 @@ int load_uint64(DBusMessageIter* iter, uint64_t *val)
}
int load_charp(DBusMessageIter* iter, const char** val)
{
+ *val = NULL;
+
int type = dbus_message_iter_get_arg_type(iter);
if (type != DBUS_TYPE_STRING)
{
@@ -228,6 +321,157 @@ int load_charp(DBusMessageIter* iter, const char** val)
return dbus_message_iter_next(iter);
}
+/* Helpers for loading crash_data */
+
+static int load_crash_item(DBusMessageIter* iter, struct crash_item *item)
+{
+ int type = dbus_message_iter_get_arg_type(iter);
+ if (type != DBUS_TYPE_ARRAY)
+ {
+ error_msg("array expected in dbus message, but not found ('%c')", type);
+ return -1;
+ }
+
+ /* Compat with python/cli:
+ * Crash item is represented in dbus as 3-element vector of strings:
+ * type, editable, content.
+ * This doesn't match daemon-side representation: { content, flags } struct
+ */
+
+ DBusMessageIter sub_iter;
+ dbus_message_iter_recurse(iter, &sub_iter);
+
+ const char *typestr;
+ int r = load_charp(&sub_iter, &typestr);
+ if (r != ABRT_DBUS_MORE_FIELDS)
+ {
+ error_msg("malformed crash_item element in dbus message");
+ return -1;
+ }
+ const char *editable;
+ r = load_charp(&sub_iter, &editable);
+ if (r != ABRT_DBUS_MORE_FIELDS)
+ {
+ error_msg("malformed crash_item element in dbus message");
+ return -1;
+ }
+ const char *content;
+ r = load_charp(&sub_iter, &content);
+ if (r != ABRT_DBUS_LAST_FIELD)
+ {
+ error_msg("malformed crash_item element in dbus message");
+ return -1;
+ }
+ item->flags = 0;
+ if (typestr[0] == 'b') item->flags |= CD_FLAG_BIN;
+ if (typestr[0] == 't') item->flags |= CD_FLAG_TXT;
+ if (editable[0] == 'y') item->flags |= CD_FLAG_ISEDITABLE;
+ if (editable[0] == 'n') item->flags |= CD_FLAG_ISNOTEDITABLE;
+ item->content = xstrdup(content);
+ return 0;
+}
+int load_crash_data(DBusMessageIter* iter, crash_data_t **val)
+{
+ *val = NULL;
+
+ int type = dbus_message_iter_get_arg_type(iter);
+ if (type != DBUS_TYPE_ARRAY)
+ {
+ error_msg("array expected in dbus message, but not found ('%c')", type);
+ return -1;
+ }
+
+ crash_data_t *result = new_crash_data();
+
+ DBusMessageIter sub_iter;
+ dbus_message_iter_recurse(iter, &sub_iter);
+
+ bool next_exists;
+ int r;
+//int cnt = 0;
+ do {
+ type = dbus_message_iter_get_arg_type(&sub_iter);
+ if (type != DBUS_TYPE_DICT_ENTRY)
+ {
+ /* When the map has 0 elements, we see DBUS_TYPE_INVALID (on the first iteration) */
+ if (type == DBUS_TYPE_INVALID)
+ break;
+ error_msg("sub_iter type is not DBUS_TYPE_DICT_ENTRY (%c)!", type);
+ free_crash_data(result);
+ return -1;
+ }
+
+ DBusMessageIter sub_sub_iter;
+ dbus_message_iter_recurse(&sub_iter, &sub_sub_iter);
+
+ const char *key;
+ r = load_charp(&sub_sub_iter, &key);
+ if (r != ABRT_DBUS_MORE_FIELDS)
+ {
+ if (r == ABRT_DBUS_LAST_FIELD)
+ error_msg("malformed map element in dbus message");
+ free_crash_data(result);
+ return -1;
+ }
+ struct crash_item *value = xzalloc(sizeof(*value));
+ r = load_crash_item(&sub_sub_iter, value);
+ if (r != ABRT_DBUS_LAST_FIELD)
+ {
+ if (r == ABRT_DBUS_MORE_FIELDS)
+ error_msg("malformed map element in dbus message");
+ free(value);
+ free_crash_data(result);
+ return -1;
+ }
+ g_hash_table_replace(result, xstrdup(key), value);
+//cnt++;
+ next_exists = dbus_message_iter_next(&sub_iter);
+ } while (next_exists);
+//log("%s: %d elems", __func__, cnt);
+
+ *val = result;
+ return dbus_message_iter_next(iter); /* note: this can't fail (returns bool, thus never < 0) */
+}
+int load_vector_of_crash_data(DBusMessageIter* iter, vector_of_crash_data_t **val)
+{
+ *val = NULL;
+
+ int type = dbus_message_iter_get_arg_type(iter);
+ if (type != DBUS_TYPE_ARRAY)
+ {
+ error_msg("array expected in dbus message, but not found ('%c')", type);
+ return -1;
+ }
+
+ DBusMessageIter sub_iter;
+ dbus_message_iter_recurse(iter, &sub_iter);
+
+ vector_of_crash_data_t *result = new_vector_of_crash_data();
+
+ int r;
+//int cnt = 0;
+ /* When the vector has 0 elements, we see DBUS_TYPE_INVALID here */
+ type = dbus_message_iter_get_arg_type(&sub_iter);
+ if (type != DBUS_TYPE_INVALID)
+ {
+ do {
+ crash_data_t *cd = NULL;
+//cnt++;
+ r = load_crash_data(&sub_iter, &cd);
+ if (r < 0)
+ {
+ free_vector_of_crash_data(result);
+ return r;
+ }
+ g_ptr_array_add(result, cd);
+ } while (r == ABRT_DBUS_MORE_FIELDS);
+ }
+//log("%s: %d elems", __func__, cnt);
+
+ *val = result;
+ return dbus_message_iter_next(iter); /* note: this can't fail (returns bool, thus never < 0) */
+}
+
/*
* Glib integration machinery
@@ -353,8 +597,29 @@ static void unregister_vtable(DBusConnection *conn, void* data)
VERB3 log("%s()", __func__);
}
+
/*
- * Initialization works as follows:
+ * Simple logging handler for dbus errors.
+ */
+int log_dbus_error(const char *msg, DBusError *err)
+{
+ int ret = 0;
+ if (dbus_error_is_set(err))
+ {
+ error_msg("dbus error: %s", err->message);
+ ret = 1;
+ }
+ if (msg)
+ {
+ error_msg(msg);
+ ret = 1;
+ }
+ return ret;
+}
+
+
+/*
+ * Initialization. Works as follows:
*
* we have a DBusConnection* (say, obtained with dbus_bus_get)
* we call dbus_connection_set_watch_functions
@@ -428,3 +693,172 @@ void attach_dbus_conn_to_glib_main_loop(DBusConnection* conn,
}
}
}
+
+
+/*
+ * Support functions for clients
+ */
+
+/* helpers */
+static DBusMessage* new_call_msg(const char* method)
+{
+ DBusMessage* msg = dbus_message_new_method_call(ABRTD_DBUS_NAME, ABRTD_DBUS_PATH, ABRTD_DBUS_IFACE, method);
+ if (!msg)
+ die_out_of_memory();
+ return msg;
+}
+
+static DBusMessage* send_get_reply_and_unref(DBusMessage* msg)
+{
+ dbus_uint32_t serial;
+ if (TRUE != dbus_connection_send(g_dbus_conn, msg, &serial))
+ error_msg_and_die("Error sending DBus message");
+ dbus_message_unref(msg);
+
+ while (true)
+ {
+ DBusMessage *received = dbus_connection_pop_message(g_dbus_conn);
+ if (!received)
+ {
+ if (FALSE == dbus_connection_read_write(g_dbus_conn, -1))
+ error_msg_and_die("dbus connection closed");
+ continue;
+ }
+
+ int tp = dbus_message_get_type(received);
+ const char *error_str = dbus_message_get_error_name(received);
+#if 0
+ /* Debugging */
+ printf("type:%u (CALL:%u, RETURN:%u, ERROR:%u, SIGNAL:%u)\n", tp,
+ DBUS_MESSAGE_TYPE_METHOD_CALL,
+ DBUS_MESSAGE_TYPE_METHOD_RETURN,
+ DBUS_MESSAGE_TYPE_ERROR,
+ DBUS_MESSAGE_TYPE_SIGNAL
+ );
+ const char *sender = dbus_message_get_sender(received);
+ if (sender)
+ printf("sender: %s\n", sender);
+ const char *path = dbus_message_get_path(received);
+ if (path)
+ printf("path: %s\n", path);
+ const char *member = dbus_message_get_member(received);
+ if (member)
+ printf("member: %s\n", member);
+ const char *interface = dbus_message_get_interface(received);
+ if (interface)
+ printf("interface: %s\n", interface);
+ const char *destination = dbus_message_get_destination(received);
+ if (destination)
+ printf("destination: %s\n", destination);
+ if (error_str)
+ printf("error: '%s'\n", error_str);
+#endif
+
+ DBusError err;
+ dbus_error_init(&err);
+
+ if (dbus_message_is_signal(received, ABRTD_DBUS_IFACE, "Update"))
+ {
+ const char *update_msg;
+ if (!dbus_message_get_args(received, &err,
+ DBUS_TYPE_STRING, &update_msg,
+ DBUS_TYPE_INVALID))
+ {
+ error_msg_and_die("dbus Update message: arguments mismatch");
+ }
+ printf(">> %s\n", update_msg);
+ }
+ else if (dbus_message_is_signal(received, ABRTD_DBUS_IFACE, "Warning"))
+ {
+ const char *warning_msg;
+ if (!dbus_message_get_args(received, &err,
+ DBUS_TYPE_STRING, &warning_msg,
+ DBUS_TYPE_INVALID))
+ {
+ error_msg_and_die("dbus Warning message: arguments mismatch");
+ }
+ log(">! %s\n", warning_msg);
+ }
+ else
+ if (tp == DBUS_MESSAGE_TYPE_METHOD_RETURN
+ && dbus_message_get_reply_serial(received) == serial
+ ) {
+ return received;
+ }
+ else
+ if (tp == DBUS_MESSAGE_TYPE_ERROR
+ && dbus_message_get_reply_serial(received) == serial
+ ) {
+ error_msg_and_die("dbus call returned error: '%s'", error_str);
+ }
+
+ dbus_message_unref(received);
+ }
+}
+
+int32_t call_DeleteDebugDump(const char *dump_dir_name)
+{
+ DBusMessage* msg = new_call_msg(__func__ + 5);
+ dbus_message_append_args(msg,
+ DBUS_TYPE_STRING, &dump_dir_name,
+ DBUS_TYPE_INVALID);
+
+ DBusMessage *reply = send_get_reply_and_unref(msg);
+
+ DBusMessageIter in_iter;
+ dbus_message_iter_init(reply, &in_iter);
+
+ int32_t result;
+ int r = load_int32(&in_iter, &result);
+ if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */
+ error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5);
+
+ dbus_message_unref(reply);
+ return result;
+}
+
+static int connect_to_abrtd_and_call_DeleteDebugDump(const char *dump_dir_name)
+{
+ DBusError err;
+ dbus_error_init(&err);
+ g_dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+ if (log_dbus_error(
+ g_dbus_conn ? NULL :
+ "error requesting system DBus, possible reasons: "
+ "dbus config is incorrect; dbus-daemon is not running, "
+ "or dbus daemon needs to be restarted to reload dbus config",
+ &err
+ )
+ ) {
+ if (g_dbus_conn)
+ dbus_connection_unref(g_dbus_conn);
+ g_dbus_conn = NULL;
+ return 1;
+ }
+
+ int ret = call_DeleteDebugDump(dump_dir_name);
+ if (ret == ENOENT)
+ error_msg("Dump directory '%s' is not found", dump_dir_name);
+ else if (ret != 0)
+ error_msg("Can't delete dump directory '%s'", dump_dir_name);
+
+ dbus_connection_unref(g_dbus_conn);
+ g_dbus_conn = NULL;
+
+ return ret;
+}
+
+int delete_dump_dir_possibly_using_abrtd(const char *dump_dir_name)
+{
+ /* Try to delete it ourselves */
+ struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY);
+ if (dd)
+ {
+ if (dd->locked) /* it is not readonly */
+ return dd_delete(dd);
+ dd_close(dd);
+ }
+
+ VERB1 log("Deleting '%s' via abrtd dbus call", dump_dir_name);
+ return connect_to_abrtd_and_call_DeleteDebugDump(dump_dir_name);
+}
diff --git a/src/lib/abrt_dbus.h b/src/lib/abrt_dbus.h
index cdc963ca..8f559980 100644
--- a/src/lib/abrt_dbus.h
+++ b/src/lib/abrt_dbus.h
@@ -23,6 +23,11 @@
#include "abrtlib.h"
+#define ABRTD_DBUS_NAME "com.redhat.abrt"
+#define ABRTD_DBUS_PATH "/com/redhat/abrt"
+#define ABRTD_DBUS_IFACE "com.redhat.abrt"
+
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -53,6 +58,7 @@ extern DBusConnection* g_dbus_conn;
* // (note: "iface.sig.emitted.from" is not optional for signals!)
* dbus_message_set_destination(msg, "peer"); // optional
* dbus_connection_send(conn, msg, &serial); // &serial can be NULL
+ * dbus_connection_unref(conn); // if you don't want to *stay* connected
*
* - client which receives and processes signals:
* conn = dbus_bus_get(DBUS_BUS_SYSTEM/SESSION, &err);
@@ -73,6 +79,19 @@ void attach_dbus_conn_to_glib_main_loop(DBusConnection* conn,
DBusHandlerResult (*message_received_func)(DBusConnection *conn, DBusMessage *msg, void* data)
);
+/* Log dbus error if err has it set. Then log msg if it's !NULL.
+ * In both cases return 1. Otherwise return 0.
+ */
+int log_dbus_error(const char *msg, DBusError *err);
+
+/* Perform "DeleteDebugDump" call over g_dbus_conn */
+int32_t call_DeleteDebugDump(const char *dump_dir_name);
+
+/* Connect to system bus, find abrtd, perform "DeleteDebugDump" call, close g_dbus_conn */
+/* now static: int connect_to_abrtd_and_call_DeleteDebugDump(const char *dump_dir_name); */
+int delete_dump_dir_possibly_using_abrtd(const char *dump_dir_name);
+
+
/*
* Helpers for building DBus messages
*/
@@ -82,6 +101,9 @@ void store_uint32(DBusMessageIter* iter, uint32_t val);
void store_int64(DBusMessageIter* iter, int64_t val);
void store_uint64(DBusMessageIter* iter, uint64_t val);
void store_string(DBusMessageIter* iter, const char* val);
+void store_crash_data(DBusMessageIter* iter, crash_data_t *val);
+void store_vector_of_crash_data(DBusMessageIter* iter, vector_of_crash_data_t *val);
+void store_map_string(DBusMessageIter* iter, map_string_h *val);
/*
* Helpers for parsing DBus messages
@@ -103,13 +125,18 @@ int load_uint32(DBusMessageIter* iter, uint32_t *val);
int load_int64(DBusMessageIter* iter, int64_t *val);
int load_uint64(DBusMessageIter* iter, uint64_t *val);
int load_charp(DBusMessageIter* iter, const char **val);
+int load_crash_data(DBusMessageIter* iter, crash_data_t **val);
+int load_vector_of_crash_data(DBusMessageIter* iter, vector_of_crash_data_t **val);
#ifdef __cplusplus
}
#endif
-/* C++ style stuff */
+/*
+ * C++ style stuff
+ */
+
#ifdef __cplusplus
#include <map>
@@ -119,6 +146,20 @@ int load_charp(DBusMessageIter* iter, const char **val);
* Helpers for building DBus messages
*/
+static inline std::string ssprintf(const char *format, ...)
+{
+ va_list p;
+ char *string_ptr;
+
+ va_start(p, format);
+ string_ptr = xvasprintf(format, p);
+ va_end(p);
+
+ std::string res = string_ptr;
+ free(string_ptr);
+ return res;
+}
+
//static inline void store_val(DBusMessageIter* iter, bool val) { store_bool(iter, val); }
static inline void store_val(DBusMessageIter* iter, int32_t val) { store_int32(iter, val); }
static inline void store_val(DBusMessageIter* iter, uint32_t val) { store_uint32(iter, val); }
@@ -147,7 +188,7 @@ struct abrt_dbus_type< std::map<K,V> > {
static std::string sig() { return ssprintf("a{%s%s}", ABRT_DBUS_SIG(K), ABRT_DBUS_SIG(V)); }
};
-template<typename E>
+template <typename E>
static void store_vector(DBusMessageIter* iter, const std::vector<E>& val)
{
DBusMessageIter sub_iter;
@@ -170,7 +211,7 @@ static void store_vector(DBus::MessageIter &iter, const std::vector<uint8_t>& va
if we use such vector, MUST add specialized code here (see in dbus-c++ source)
}
*/
-template<typename K, typename V>
+template <typename K, typename V>
static void store_map(DBusMessageIter* iter, const std::map<K,V>& val)
{
DBusMessageIter sub_iter;
@@ -195,9 +236,9 @@ static void store_map(DBusMessageIter* iter, const std::map<K,V>& val)
die_out_of_memory();
}
-template<typename E>
+template <typename E>
static inline void store_val(DBusMessageIter* iter, const std::vector<E>& val) { store_vector(iter, val); }
-template<typename K, typename V>
+template <typename K, typename V>
static inline void store_val(DBusMessageIter* iter, const std::map<K,V>& val) { store_map(iter, val); }
@@ -220,7 +261,7 @@ static inline int load_val(DBusMessageIter* iter, std::string& val)
}
/* Templates for vector and map */
-template<typename E>
+template <typename E>
static int load_vector(DBusMessageIter* iter, std::vector<E>& val)
{
int type = dbus_message_iter_get_arg_type(iter);
@@ -259,7 +300,7 @@ static int load_vector(DBusMessageIter* iter, std::vector<uint8_t>& val)
if we use such vector, MUST add specialized code here (see in dbus-c++ source)
}
*/
-template<typename K, typename V>
+template <typename K, typename V>
static int load_map(DBusMessageIter* iter, std::map<K,V>& val)
{
int type = dbus_message_iter_get_arg_type(iter);
@@ -314,9 +355,9 @@ static int load_map(DBusMessageIter* iter, std::map<K,V>& val)
return dbus_message_iter_next(iter);
}
-template<typename E>
+template <typename E>
static inline int load_val(DBusMessageIter* iter, std::vector<E>& val) { return load_vector(iter, val); }
-template<typename K, typename V>
+template <typename K, typename V>
static inline int load_val(DBusMessageIter* iter, std::map<K,V>& val) { return load_map(iter, val); }
#endif /* __cplusplus */
diff --git a/src/lib/ABRTException.cpp b/src/lib/abrt_types.c
index 0ae5d452..42100075 100644
--- a/src/lib/ABRTException.cpp
+++ b/src/lib/abrt_types.c
@@ -1,6 +1,6 @@
/*
- Copyright (C) 2010 ABRT team
- Copyright (C) 2010 RedHat Inc
+ Copyright (C) 2010 ABRT Team
+ Copyright (C) 2010 RedHat inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,18 +16,22 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "abrt_exception.h"
+#include "abrtlib.h"
-CABRTException::CABRTException(abrt_exception_t type, const char* fmt, ...)
+map_string_h *new_map_string(void)
{
- m_type = type;
- va_list ap;
- va_start(ap, fmt);
- m_what = xvasprintf(fmt, ap);
- va_end(ap);
+ return g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
}
-CABRTException::CABRTException(const CABRTException& rhs):
- m_type(rhs.m_type),
- m_what(xstrdup(rhs.m_what))
-{}
+void free_map_string(map_string_h *ms)
+{
+ if (ms)
+ g_hash_table_destroy(ms);
+}
+
+const char *get_map_string_item_or_empty(map_string_h *ms, const char *key)
+{
+ const char *v = (const char*)g_hash_table_lookup(ms, key);
+ if (!v) v = "";
+ return v;
+}
diff --git a/src/lib/abrt_xmlrpc.cpp b/src/lib/abrt_xmlrpc.cpp
index bf74f05b..e2b8674c 100644
--- a/src/lib/abrt_xmlrpc.cpp
+++ b/src/lib/abrt_xmlrpc.cpp
@@ -18,15 +18,10 @@
*/
#include "abrtlib.h"
#include "abrt_xmlrpc.h"
-#include "abrt_exception.h"
void throw_xml_fault(xmlrpc_env *env)
{
- std::string errmsg = ssprintf("XML-RPC Fault(%d): %s", env->fault_code, env->fault_string);
- xmlrpc_env_clean(env); // this is needed ONLY if fault_occurred
- xmlrpc_env_init(env); // just in case user catches ex and _continues_ to use env
- error_msg("%s", errmsg.c_str()); // show error in daemon log
- throw CABRTException(EXCEP_PLUGIN, errmsg.c_str());
+ error_msg_and_die("XML-RPC Fault(%d): %s", env->fault_code, env->fault_string);
}
void throw_if_xml_fault_occurred(xmlrpc_env *env)
@@ -49,6 +44,17 @@ void abrt_xmlrpc_conn::new_xmlrpc_client(const char* url, bool ssl_verify)
* We do it in abrtd's main */
/* xmlrpc_client_setup_global_const(&env); */
+ /* URL - bugzilla.redhat.com/show_bug.cgi?id=666893 Unable to make sense of
+ * XML-RPC response from server
+ *
+ * By default, XML data from the network may be no larger than 512K.
+ * XMLRPC_XML_SIZE_LIMIT_DEFAULT is #defined to (512*1024) in xmlrpc-c/base.h
+ *
+ * Users reported trouble with 733402 byte long responses, hope raising the
+ * limit to 2*512k is enough
+ */
+ xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 2 * XMLRPC_XML_SIZE_LIMIT_DEFAULT);
+
struct xmlrpc_curl_xportparms curlParms;
memset(&curlParms, 0, sizeof(curlParms));
/* curlParms.network_interface = NULL; - done by memset */
diff --git a/src/lib/abrt_xmlrpc.h b/src/lib/abrt_xmlrpc.h
index ad1a87d3..93c5a9d6 100644
--- a/src/lib/abrt_xmlrpc.h
+++ b/src/lib/abrt_xmlrpc.h
@@ -23,12 +23,12 @@
#include <xmlrpc-c/base.h>
#include <xmlrpc-c/client.h>
+#ifdef __cplusplus
/*
* Simple class holding XMLRPC connection data.
* Used mainly to ensure we always destroy xmlrpc client and server_info
* on return or throw.
*/
-
struct abrt_xmlrpc_conn {
xmlrpc_client* m_pClient;
xmlrpc_server_info* m_pServer_info;
@@ -40,9 +40,19 @@ struct abrt_xmlrpc_conn {
void new_xmlrpc_client(const char* url, bool ssl_verify);
void destroy_xmlrpc_client();
};
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
/* Utility functions */
void throw_xml_fault(xmlrpc_env *env);
void throw_if_xml_fault_occurred(xmlrpc_env *env);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/lib/binhex.c b/src/lib/binhex.c
new file mode 100644
index 00000000..1fcb7445
--- /dev/null
+++ b/src/lib/binhex.c
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "abrtlib.h"
+
+static const char hexdigits_locase[] = "0123456789abcdef";
+
+/* Emit a string of hex representation of bytes */
+char *bin2hex(char *dst, const char *str, int count)
+{
+ while (count) {
+ unsigned char c = *str++;
+ /* put lowercase hex digits */
+ *dst++ = hexdigits_locase[c >> 4];
+ *dst++ = hexdigits_locase[c & 0xf];
+ count--;
+ }
+ return dst;
+}
+
+/* Convert "xxxxxxxx" hex string to binary, no more than COUNT bytes */
+char *hex2bin(char *dst, const char *str, int count)
+{
+ /* Parts commented out with // allow parsing
+ * of strings like "xx:x:x:xx:xx:xx:xxxxxx"
+ * (IPv6, ethernet addresses and the like).
+ */
+ errno = EINVAL;
+ while (*str && count) {
+ uint8_t val;
+ uint8_t c;
+
+ c = *str++;
+ if (isdigit(c))
+ val = c - '0';
+ else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
+ val = (c|0x20) - ('a' - 10);
+ else
+ return NULL;
+ val <<= 4;
+ c = *str;
+ if (isdigit(c))
+ val |= c - '0';
+ else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
+ val |= (c|0x20) - ('a' - 10);
+ //else if (c == ':' || c == '\0')
+ // val >>= 4;
+ else
+ return NULL;
+
+ *dst++ = val;
+ //if (c != '\0')
+ str++;
+ //if (*str == ':')
+ // str++;
+ count--;
+ }
+ errno = (*str ? ERANGE : 0);
+ return dst;
+}
diff --git a/src/lib/copy_file_recursive.c b/src/lib/copy_file_recursive.c
new file mode 100644
index 00000000..c3f021c7
--- /dev/null
+++ b/src/lib/copy_file_recursive.c
@@ -0,0 +1,139 @@
+/*
+ Copyright (C) 2011 ABRT team
+ Copyright (C) 2011 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "abrtlib.h"
+
+int copy_file_recursive(const char *source, const char *dest)
+{
+ /* This is a recursive function, try to minimize stack usage */
+ /* NB: each struct stat is ~100 bytes */
+ struct stat source_stat;
+ struct stat dest_stat;
+ int retval = 0;
+ int dest_exists = 0;
+
+ if (strcmp(source, ".lock") == 0)
+ goto skip;
+
+ if (stat(source, &source_stat) < 0) {
+ perror_msg("Can't stat '%s'", source);
+ return -1;
+ }
+
+ if (lstat(dest, &dest_stat) < 0) {
+ if (errno != ENOENT) {
+ perror_msg("Can't stat '%s'", dest);
+ return -1;
+ }
+ } else {
+ if (source_stat.st_dev == dest_stat.st_dev
+ && source_stat.st_ino == dest_stat.st_ino
+ ) {
+ error_msg("'%s' and '%s' are the same file", source, dest);
+ return -1;
+ }
+ dest_exists = 1;
+ }
+
+ if (S_ISDIR(source_stat.st_mode)) {
+ DIR *dp;
+ struct dirent *d;
+
+ if (dest_exists) {
+ if (!S_ISDIR(dest_stat.st_mode)) {
+ error_msg("Target '%s' is not a directory", dest);
+ return -1;
+ }
+ /* race here: user can substitute a symlink between
+ * this check and actual creation of files inside dest */
+ } else {
+ /* Create DEST */
+ mode_t mode = source_stat.st_mode;
+ /* Allow owner to access new dir (at least for now) */
+ mode |= S_IRWXU;
+ if (mkdir(dest, mode) < 0) {
+ perror_msg("Can't create directory '%s'", dest);
+ return -1;
+ }
+ }
+ /* Recursively copy files in SOURCE */
+ dp = opendir(source);
+ if (dp == NULL) {
+ retval = -1;
+ goto ret;
+ }
+
+ while (retval == 0 && (d = readdir(dp)) != NULL) {
+ char *new_source, *new_dest;
+
+ if (dot_or_dotdot(d->d_name))
+ continue;
+ new_source = concat_path_file(source, d->d_name);
+ new_dest = concat_path_file(dest, d->d_name);
+ if (copy_file_recursive(new_source, new_dest) < 0)
+ retval = -1;
+ free(new_source);
+ free(new_dest);
+ }
+ closedir(dp);
+
+ goto ret;
+ }
+
+ if (S_ISREG(source_stat.st_mode)) {
+ int src_fd;
+ int dst_fd;
+ mode_t new_mode;
+
+ src_fd = open(source, O_RDONLY);
+ if (src_fd < 0) {
+ perror_msg("Can't open '%s'", source);
+ return -1;
+ }
+
+ /* Do not try to open with weird mode fields */
+ new_mode = source_stat.st_mode;
+
+ // security problem versus (sym)link attacks
+ // dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
+ /* safe way: */
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
+ if (dst_fd < 0) {
+ close(src_fd);
+ return -1;
+ }
+
+ if (copyfd_eof(src_fd, dst_fd, COPYFD_SPARSE) == -1)
+ retval = -1;
+ close(src_fd);
+ /* Careful: do check that buffered writes succeeded... */
+ if (close(dst_fd) < 0) {
+ perror_msg("Error writing to '%s'", dest);
+ retval = -1;
+ }
+ goto ret;
+ }
+
+ /* Neither dir not regular file: skip */
+
+ skip:
+ log("Skipping '%s'", source);
+ ret:
+ return retval;
+}
diff --git a/src/lib/crash_dump.cpp b/src/lib/crash_data.c
index 338724d5..1ae8f4f1 100644
--- a/src/lib/crash_dump.cpp
+++ b/src/lib/crash_data.c
@@ -17,20 +17,77 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "abrtlib.h"
-#include "abrt_crash_dump.h"
-
-const char *const must_have_files[] = {
- FILENAME_ARCHITECTURE,
- FILENAME_KERNEL ,
- FILENAME_PACKAGE ,
- FILENAME_COMPONENT ,
- FILENAME_RELEASE ,
- FILENAME_EXECUTABLE ,
- NULL
-};
+
+static void free_crash_item(void *ptr)
+{
+ if (ptr)
+ {
+ struct crash_item *item = (struct crash_item *)ptr;
+ free(item->content);
+ free(item);
+ }
+}
+
+
+/* crash_data["name"] = { "content", CD_FLAG_foo_bits } */
+
+crash_data_t *new_crash_data(void)
+{
+ return g_hash_table_new_full(g_str_hash, g_str_equal,
+ free, free_crash_item);
+}
+
+void add_to_crash_data_ext(crash_data_t *crash_data,
+ const char *name,
+ const char *content,
+ unsigned flags)
+{
+ if (!(flags & (CD_FLAG_BIN|CD_FLAG_TXT)))
+ flags |= CD_FLAG_TXT;
+ if (!(flags & (CD_FLAG_ISEDITABLE|CD_FLAG_ISNOTEDITABLE)))
+ flags |= CD_FLAG_ISNOTEDITABLE;
+
+ struct crash_item *item = (struct crash_item *)xzalloc(sizeof(*item));
+ item->content = xstrdup(content);
+ item->flags = flags;
+ g_hash_table_replace(crash_data, xstrdup(name), item);
+}
+
+void add_to_crash_data(crash_data_t *crash_data,
+ const char *name,
+ const char *content)
+{
+ add_to_crash_data_ext(crash_data, name, content, CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);
+}
+
+const char *get_crash_item_content_or_die(crash_data_t *crash_data, const char *key)
+{
+ struct crash_item *item = get_crash_data_item_or_NULL(crash_data, key);
+ if (!item)
+ error_msg_and_die("Error accessing crash data: no ['%s']", key);
+ return item->content;
+}
+
+const char *get_crash_item_content_or_NULL(crash_data_t *crash_data, const char *key)
+{
+ struct crash_item *item = get_crash_data_item_or_NULL(crash_data, key);
+ if (!item)
+ return NULL;
+ return item->content;
+}
+
+
+/* crash_data_vector[i] = { "name" = { "content", CD_FLAG_foo_bits } } */
+
+vector_of_crash_data_t *new_vector_of_crash_data(void)
+{
+ return g_ptr_array_new_with_free_func((void (*)(void*)) &free_crash_data);
+}
+
+
+/* Miscellaneous helpers */
static const char *const editable_files[] = {
- FILENAME_DESCRIPTION,
FILENAME_COMMENT ,
FILENAME_REPRODUCE ,
FILENAME_BACKTRACE ,
@@ -52,36 +109,6 @@ bool is_editable_file(const char *file_name)
return is_editable(file_name, editable_files);
}
-
-void add_to_crash_data_ext(map_crash_data_t& pCrashData,
- const char *pItem,
- const char *pType,
- const char *pEditable,
- const char *pContent)
-{
- map_crash_data_t::iterator it = pCrashData.find(pItem);
- if (it == pCrashData.end()) {
- vector_string_t& v = pCrashData[pItem]; /* create empty vector */
- v.push_back(pType);
- v.push_back(pEditable);
- v.push_back(pContent);
- return;
- }
- vector_string_t& v = it->second;
- while (v.size() < 3)
- v.push_back("");
- v[CD_TYPE] = pType;
- v[CD_EDITABLE] = pEditable;
- v[CD_CONTENT] = pContent;
-}
-
-void add_to_crash_data(map_crash_data_t& pCrashData,
- const char *pItem,
- const char *pContent)
-{
- add_to_crash_data_ext(pCrashData, pItem, CD_TXT, CD_ISNOTEDITABLE, pContent);
-}
-
static char* is_text_file(const char *name, ssize_t *sz)
{
/* We were using magic.h API to check for file being text, but it thinks
@@ -156,7 +183,7 @@ static char* is_text_file(const char *name, ssize_t *sz)
return NULL; /* it's binary */
}
-void load_crash_data_from_crash_dump_dir(struct dump_dir *dd, map_crash_data_t& data)
+void load_crash_data_from_dump_dir(crash_data_t *crash_data, struct dump_dir *dd)
{
char *short_name;
char *full_name;
@@ -173,13 +200,11 @@ void load_crash_data_from_crash_dump_dir(struct dump_dir *dd, map_crash_data_t&
text = is_text_file(full_name, &sz);
if (!text)
{
- add_to_crash_data_ext(data,
+ add_to_crash_data_ext(crash_data,
short_name,
- CD_BIN,
- CD_ISNOTEDITABLE,
- full_name
+ full_name,
+ CD_FLAG_BIN + CD_FLAG_ISNOTEDITABLE
);
-
free(short_name);
free(full_name);
continue;
@@ -193,11 +218,10 @@ void load_crash_data_from_crash_dump_dir(struct dump_dir *dd, map_crash_data_t&
content = dd_load_text(dd, short_name);
free(text);
- add_to_crash_data_ext(data,
+ add_to_crash_data_ext(crash_data,
short_name,
- CD_TXT,
- editable ? CD_ISEDITABLE : CD_ISNOTEDITABLE,
- content
+ content,
+ (editable ? CD_FLAG_TXT + CD_FLAG_ISEDITABLE : CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE)
);
free(short_name);
free(full_name);
@@ -205,50 +229,25 @@ void load_crash_data_from_crash_dump_dir(struct dump_dir *dd, map_crash_data_t&
}
}
-static const std::string* helper_get_crash_data_item_content(const map_crash_data_t& crash_data, const char *key)
+crash_data_t *create_crash_data_from_dump_dir(struct dump_dir *dd)
{
- map_crash_data_t::const_iterator it = crash_data.find(key);
- if (it == crash_data.end()) {
- return NULL;
- }
- if (it->second.size() <= CD_CONTENT) {
- return NULL;
- }
- return &it->second[CD_CONTENT];
+ crash_data_t *crash_data = new_crash_data();
+ load_crash_data_from_dump_dir(crash_data, dd);
+ return crash_data;
}
-const std::string& get_crash_data_item_content(const map_crash_data_t& crash_data, const char *key)
+void log_crash_data(crash_data_t *crash_data, const char *pfx)
{
- const std::string* sp = helper_get_crash_data_item_content(crash_data, key);
- if (sp == NULL) {
- if (crash_data.find(key) == crash_data.end())
- error_msg_and_die("Error accessing crash data: no ['%s']", key);
- error_msg_and_die("Error accessing crash data: no ['%s'][%d]", key, CD_CONTENT);
- }
- return *sp;
-}
-
-const char *get_crash_data_item_content_or_NULL(const map_crash_data_t& crash_data, const char *key)
-{
- const std::string* sp = helper_get_crash_data_item_content(crash_data, key);
- if (!sp) {
- return NULL;
- }
- return sp->c_str();
-}
-
-void log_map_crash_data(const map_crash_data_t& data, const char *name)
-{
- map_crash_data_t::const_iterator it = data.begin();
- while (it != data.end())
- {
- ssize_t sz = it->second.size();
- log("%s[%s]:%s/%s/'%.20s'",
- name, it->first.c_str(),
- sz > 0 ? it->second[0].c_str() : "<NO [0]>",
- sz > 1 ? it->second[1].c_str() : "<NO [1]>",
- sz > 2 ? it->second[2].c_str() : "<NO [2]>"
- );
- it++;
- }
+ GHashTableIter iter;
+ char *name;
+ struct crash_item *value;
+ g_hash_table_iter_init(&iter, crash_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
+ {
+ log("%s[%s]:'%s' 0x%x",
+ pfx, name,
+ value->content,
+ value->flags
+ );
+ }
}
diff --git a/src/lib/create_dump_dir.c b/src/lib/create_dump_dir.c
new file mode 100644
index 00000000..cbacdab6
--- /dev/null
+++ b/src/lib/create_dump_dir.c
@@ -0,0 +1,85 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "abrtlib.h"
+
+static struct dump_dir *try_dd_create(const char *base_dir_name, const char *dir_name)
+{
+ char *path = concat_path_file(base_dir_name, dir_name);
+ struct dump_dir *dd = dd_create(path, (uid_t)-1L);
+ if (dd)
+ dd_create_basic_files(dd, (uid_t)-1L);
+ free(path);
+ return dd;
+}
+
+struct dump_dir *create_dump_dir_from_crash_data(crash_data_t *crash_data, const char *base_dir_name)
+{
+ char dir_name[sizeof("abrt-tmp-YYYY-MM-DD-HH:MM:SS-%lu") + sizeof(long)*3];
+ sprintf(dir_name, "abrt-tmp-%s-%lu", iso_date_string(NULL), (long)getpid());
+
+ struct dump_dir *dd;
+ if (base_dir_name)
+ dd = try_dd_create(base_dir_name, dir_name);
+ else
+ {
+ /* Try /var/run/abrt */
+ dd = try_dd_create(LOCALSTATEDIR"/run/abrt", dir_name);
+ /* Try $HOME/tmp */
+ if (!dd)
+ {
+ char *home = getenv("HOME");
+ if (home && home[0])
+ {
+ home = concat_path_file(home, "tmp");
+ /*mkdir(home, 0777); - do we want this? */
+ dd = try_dd_create(home, dir_name);
+ free(home);
+ }
+ }
+//TODO: try user's home dir obtained by getpwuid(getuid())?
+ /* Try /tmp */
+ if (!dd)
+ dd = try_dd_create("/tmp", dir_name);
+ }
+ if (!dd)
+ return NULL;
+
+ GHashTableIter iter;
+ char *name;
+ struct crash_item *value;
+ g_hash_table_iter_init(&iter, crash_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
+ {
+ if (name[0] == '.' || strchr(name, '/'))
+ {
+ error_msg("Crash data field name contains disallowed chars: '%s'", name);
+ goto next;
+ }
+
+//FIXME: what to do with CD_FLAG_BINs??
+ if (value->flags & CD_FLAG_BIN)
+ goto next;
+
+ dd_save_text(dd, name, value->content);
+ next: ;
+ }
+
+ return dd;
+}
diff --git a/src/lib/dump_dir.c b/src/lib/dump_dir.c
index 19f86072..a84e2814 100644
--- a/src/lib/dump_dir.c
+++ b/src/lib/dump_dir.c
@@ -1,6 +1,4 @@
/*
- DebugDump.cpp
-
Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
Copyright (C) 2009 RedHat inc.
@@ -22,11 +20,70 @@
#include "abrtlib.h"
#include "strbuf.h"
-// TODO:
+// Locking logic:
+//
+// The directory is locked by creating a symlink named .lock inside it,
+// whose value (where it "points to") is the pid of locking process.
+// We use symlink, not an ordinary file, because symlink creation
+// is an atomic operation.
+//
+// There are two cases where after .lock creation, we might discover
+// that directory is not really free:
+// * another process just created new directory, but didn't manage
+// to lock it before us.
+// * another process is deleting the directory, and we managed to sneak in
+// and create .lock after it deleted all files (including .lock)
+// but before it rmdir'ed the empty directory.
+//
+// Both these cases are detected by the fact that file named "time"
+// is not present (it must be present in any valid dump dir).
+// If after locking the dir we don't see time file, we remove the lock
+// at once and back off. What happens in concurrent processes
+// we interfered with?
+// * "create new dump dir" process just re-tries locking.
+// * "delete dump dir" process just retries rmdir.
//
-// Perhaps dd_opendir should do some sanity checking like
-// "if there is no "uid" file in the directory, it's not a crash dump",
-// and fail.
+// There is another case when we don't find time file:
+// when the directory is not really a *dump* dir - user gave us
+// an ordinary directory name by mistake.
+// We detect it by bailing out of "lock, check time file; sleep
+// and retry if it doesn't exist" loop using a counter.
+//
+// To make locking work reliably, it's important to set timeouts
+// correctly. For example, dd_create should retry locking
+// its newly-created directory much faster than dd_opendir
+// tries to lock the directory it tries to open.
+
+
+// How long to sleep between "symlink fails with EEXIST,
+// readlink fails with ENOENT" tries. Someone just unlocked the dir.
+// We never bail out in this case, we retry forever.
+// The value can be really small:
+#define SYMLINK_RETRY_USLEEP (10*1000)
+
+// How long to sleep when lock file with valid pid is seen by dd_opendir
+// (we are waiting for other process to unlock or die):
+#define WAIT_FOR_OTHER_PROCESS_USLEEP (500*1000)
+
+// How long to sleep when lock file with valid pid is seen by dd_create
+// (some idiot jumped the gun and locked the dir we just created).
+// Must not be the same as WAIT_FOR_OTHER_PROCESS_USLEEP (we depend on this)
+// and should be small (we have the priority in locking, this is OUR dir):
+#define CREATE_LOCK_USLEEP (10*1000)
+
+// How long to sleep after we locked a dir, found no time file
+// (either we are racing with someone, or it's not a dump dir)
+// and unlocked it;
+// and after how many tries to give up and declare it's not a dump dir:
+#define NO_TIME_FILE_USLEEP (50*1000)
+#define NO_TIME_FILE_COUNT 10
+
+// How long to sleep after we unlocked an empty dir, but then rmdir failed
+// (some idiot jumped the gun and locked the dir we are deleting);
+// and after how many tries to give up:
+#define RMDIR_FAIL_USLEEP (10*1000)
+#define RMDIR_FAIL_COUNT 50
+
static char *load_text_file(const char *path, unsigned flags);
@@ -53,12 +110,21 @@ static bool exist_file_dir(const char *path)
return false;
}
-static bool get_and_set_lock(const char* lock_file, const char* pid)
+/* Return values:
+ * -1: error
+ * 0: failed to lock (someone else has it locked)
+ * 1: success
+ */
+static int get_and_set_lock(const char* lock_file, const char* pid)
{
while (symlink(pid, lock_file) != 0)
{
if (errno != EEXIST)
- perror_msg_and_die("Can't create lock file '%s'", lock_file);
+ {
+ if (errno != ENOENT && errno != ENOTDIR)
+ perror_msg("Can't create lock file '%s'", lock_file);
+ return -1;
+ }
char pid_buf[sizeof(pid_t)*3 + 4];
ssize_t r = readlink(lock_file, pid_buf, sizeof(pid_buf) - 1);
@@ -67,54 +133,94 @@ static bool get_and_set_lock(const char* lock_file, const char* pid)
if (errno == ENOENT)
{
/* Looks like lock_file was deleted */
- usleep(10 * 1000); /* avoid CPU eating loop */
+ usleep(SYMLINK_RETRY_USLEEP); /* avoid CPU eating loop */
continue;
}
- perror_msg_and_die("Can't read lock file '%s'", lock_file);
+ perror_msg("Can't read lock file '%s'", lock_file);
+ return -1;
}
pid_buf[r] = '\0';
if (strcmp(pid_buf, pid) == 0)
{
log("Lock file '%s' is already locked by us", lock_file);
- return false;
+ return 0;
}
if (isdigit_str(pid_buf))
{
- char pid_str[sizeof("/proc/") + strlen(pid_buf)];
+ char pid_str[sizeof("/proc/") + sizeof(pid_buf)];
sprintf(pid_str, "/proc/%s", pid_buf);
if (access(pid_str, F_OK) == 0)
{
log("Lock file '%s' is locked by process %s", lock_file, pid_buf);
- return false;
+ return 0;
}
log("Lock file '%s' was locked by process %s, but it crashed?", lock_file, pid_buf);
}
/* The file may be deleted by now by other process. Ignore ENOENT */
if (unlink(lock_file) != 0 && errno != ENOENT)
{
- perror_msg_and_die("Can't remove stale lock file '%s'", lock_file);
+ perror_msg("Can't remove stale lock file '%s'", lock_file);
+ return -1;
}
}
VERB1 log("Locked '%s'", lock_file);
- return true;
+ return 1;
}
-static void dd_lock(struct dump_dir *dd)
+static int dd_lock(struct dump_dir *dd, unsigned sleep_usec)
{
if (dd->locked)
- error_msg_and_die("Locking bug on '%s'", dd->dd_dir);
-
- char lock_buf[strlen(dd->dd_dir) + sizeof(".lock")];
- sprintf(lock_buf, "%s.lock", dd->dd_dir);
+ error_msg_and_die("Locking bug on '%s'", dd->dd_dirname);
char pid_buf[sizeof(long)*3 + 2];
sprintf(pid_buf, "%lu", (long)getpid());
- while ((dd->locked = get_and_set_lock(lock_buf, pid_buf)) != true)
+
+ unsigned dirname_len = strlen(dd->dd_dirname);
+ char lock_buf[dirname_len + sizeof("/.lock")];
+ strcpy(lock_buf, dd->dd_dirname);
+ strcpy(lock_buf + dirname_len, "/.lock");
+
+ unsigned count = NO_TIME_FILE_COUNT;
+ retry:
+ while (1)
{
- sleep(1); /* was 0.5 seconds */
+ int r = get_and_set_lock(lock_buf, pid_buf);
+ if (r < 0)
+ return r; /* error */
+ if (r > 0)
+ break; /* locked successfully */
+ /* Other process has the lock, wait for it to go away */
+ usleep(sleep_usec);
}
+
+ /* Are we called by dd_opendir (as opposed to dd_create)? */
+ if (sleep_usec == WAIT_FOR_OTHER_PROCESS_USLEEP) /* yes */
+ {
+ strcpy(lock_buf + dirname_len, "/time");
+ if (access(lock_buf, F_OK) != 0)
+ {
+ /* time file doesn't exist. We managed to lock the directory
+ * which was just created by somebody else, or is almost deleted
+ * by delete_file_dir.
+ * Unlock and back off.
+ */
+ strcpy(lock_buf + dirname_len, "/.lock");
+ xunlink(lock_buf);
+ VERB1 log("Unlocked '%s' (no time file)", lock_buf);
+ if (--count == 0)
+ {
+ errno = EISDIR; /* "this is an ordinary dir, not dump dir" */
+ return -1;
+ }
+ usleep(NO_TIME_FILE_USLEEP);
+ goto retry;
+ }
+ }
+
+ dd->locked = true;
+ return 0;
}
static void dd_unlock(struct dump_dir *dd)
@@ -122,9 +228,13 @@ static void dd_unlock(struct dump_dir *dd)
if (dd->locked)
{
dd->locked = 0;
- char lock_buf[strlen(dd->dd_dir) + sizeof(".lock")];
- sprintf(lock_buf, "%s.lock", dd->dd_dir);
+
+ unsigned dirname_len = strlen(dd->dd_dirname);
+ char lock_buf[dirname_len + sizeof("/.lock")];
+ strcpy(lock_buf, dd->dd_dirname);
+ strcpy(lock_buf + dirname_len, "/.lock");
xunlink(lock_buf);
+
VERB1 log("Unlocked '%s'", lock_buf);
}
}
@@ -136,7 +246,7 @@ static inline struct dump_dir *dd_init(void)
int dd_exist(struct dump_dir *dd, const char *path)
{
- char *full_path = concat_path_file(dd->dd_dir, path);
+ char *full_path = concat_path_file(dd->dd_dirname, path);
int ret = exist_file_dir(full_path);
free(full_path);
return ret;
@@ -154,7 +264,7 @@ void dd_close(struct dump_dir *dd)
/* free(dd->next_dir); - WRONG! */
}
- free(dd->dd_dir);
+ free(dd->dd_dirname);
free(dd);
}
@@ -170,52 +280,68 @@ struct dump_dir *dd_opendir(const char *dir, int flags)
{
struct dump_dir *dd = dd_init();
- /* Used to use rm_trailing_slashes(dir) here, but with dir = "."
- * or "..", or if the last component is a symlink,
- * then lock file is created in the wrong place.
- * IOW: this breaks locking.
- */
- dd->dd_dir = realpath(dir, NULL);
- if (!dd->dd_dir)
- {
- if (!(flags & DD_FAIL_QUIETLY))
- error_msg("'%s' does not exist", dir);
- dd_close(dd);
- return NULL;
- }
- dir = dd->dd_dir;
-
- dd_lock(dd);
+ dir = dd->dd_dirname = rm_trailing_slashes(dir);
- struct stat stat_buf;
- if (stat(dir, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode))
+ errno = 0;
+ if (dd_lock(dd, WAIT_FOR_OTHER_PROCESS_USLEEP) < 0)
{
- if (!(flags & DD_FAIL_QUIETLY))
- error_msg("'%s' does not exist", dir);
+ if ((flags & DD_OPEN_READONLY) && errno == EACCES)
+ {
+ /* Directory is not writable. If it seems to be readable,
+ * return "read only" dd, not NULL */
+ struct stat stat_buf;
+ if (stat(dir, &stat_buf) == 0
+ && S_ISDIR(stat_buf.st_mode)
+ && access(dir, R_OK) == 0
+ ) {
+ return dd;
+ }
+ }
+ if (errno == EISDIR)
+ {
+ /* EISDIR: dd_lock can lock the dir, but it sees no time file there,
+ * even after it retried many times. It must be an ordinary directory!
+ *
+ * Without this check, e.g. abrt-action-print happily prints any current
+ * directory when run without arguments, because its option -d DIR
+ * defaults to "."!
+ */
+ /*if (!(flags & DD_FAIL_QUIETLY))... - no, DD_FAIL_QUIETLY only means
+ * "it's ok if it doesn exist", not "ok if contents is bogus"!
+ */
+ error_msg("'%s' is not a crash dump directory", dir);
+ dd_close(dd);
+ return NULL;
+ }
+
+ if (errno == ENOENT || errno == ENOTDIR)
+ {
+ if (!(flags & DD_FAIL_QUIETLY))
+ error_msg("'%s' does not exist", dir);
+ }
+ else
+ {
+ perror_msg("Can't access '%s'", dir);
+ }
dd_close(dd);
return NULL;
}
- /* In case caller would want to create more files, he'll need uid:gid */
- dd->dd_uid = stat_buf.st_uid;
- dd->dd_gid = stat_buf.st_gid;
-
- /* Without this check, e.g. abrt-action-print happily prints any current
- * directory when run without arguments, because its option -d DIR
- * defaults to "."! Let's require that at least some crash dump dir
- * specific files exist before we declare open successful:
- */
- char *name = concat_path_file(dir, FILENAME_ANALYZER);
- int bad = (lstat(name, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode));
- free(name);
- if (bad)
+ dd->dd_uid = (uid_t)-1L;
+ dd->dd_gid = (gid_t)-1L;
+ if (geteuid() == 0)
{
- /*if (!(flags & DD_FAIL_QUIETLY))... - no, DD_FAIL_QUIETLY only means
- * "it's ok if it doesn exist", not "ok if contents is bogus"!
- */
- error_msg("'%s' is not a crash dump directory", dir);
- dd_close(dd);
- return NULL;
+ /* In case caller would want to create more files, he'll need uid:gid */
+ struct stat stat_buf;
+ if (stat(dir, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode))
+ {
+ if (!(flags & DD_FAIL_QUIETLY))
+ error_msg("'%s' does not exist", dir);
+ dd_close(dd);
+ return NULL;
+ }
+ dd->dd_uid = stat_buf.st_uid;
+ dd->dd_gid = stat_buf.st_gid;
}
return dd;
@@ -248,7 +374,7 @@ struct dump_dir *dd_create(const char *dir, uid_t uid)
* realpath will always return NULL. We don't really have to:
* dd_opendir(".") makes sense, dd_create(".") does not.
*/
- dir = dd->dd_dir = rm_trailing_slashes(dir);
+ dir = dd->dd_dirname = rm_trailing_slashes(dir);
const char *last_component = strrchr(dir, '/');
if (last_component)
@@ -265,15 +391,38 @@ struct dump_dir *dd_create(const char *dir, uid_t uid)
return NULL;
}
- dd_lock(dd);
-
+ bool created_parents = false;
+ try_again:
/* Was creating it with mode 0700 and user as the owner, but this allows
* the user to replace any file in the directory, changing security-sensitive data
* (e.g. "uid", "analyzer", "executable")
*/
if (mkdir(dir, 0750) == -1)
{
- perror_msg("Can't create dir '%s'", dir);
+ int err = errno;
+ if (!created_parents && errno == ENOENT)
+ {
+ char *p = dd->dd_dirname + 1;
+ while ((p = strchr(p, '/')) != NULL)
+ {
+ *p = '\0';
+ int r = (mkdir(dd->dd_dirname, 0755) == 0 || errno == EEXIST);
+ *p++ = '/';
+ if (!r)
+ goto report_err;
+ }
+ created_parents = true;
+ goto try_again;
+ }
+ report_err:
+ errno = err;
+ perror_msg("Can't create directory '%s'", dir);
+ dd_close(dd);
+ return NULL;
+ }
+
+ if (dd_lock(dd, CREATE_LOCK_USLEEP) < 0)
+ {
dd_close(dd);
return NULL;
}
@@ -281,35 +430,51 @@ struct dump_dir *dd_create(const char *dir, uid_t uid)
/* mkdir's mode (above) can be affected by umask, fix it */
if (chmod(dir, 0750) == -1)
{
- perror_msg("Can't change mode of '%s'", dir);
+ perror_msg("can't change mode of '%s'", dir);
dd_close(dd);
return NULL;
}
- /* Get ABRT's user id */
- /*dd->dd_uid = 0; - dd_init did this already */
- struct passwd *pw = getpwnam("abrt");
- if (pw)
- dd->dd_uid = pw->pw_uid;
- else
- error_msg("User 'abrt' does not exist, using uid 0");
-
- /* Get crashed application's group id */
- /*dd->dd_gid = 0; - dd_init did this already */
- pw = getpwuid(uid);
- if (pw)
- dd->dd_gid = pw->pw_gid;
- else
- error_msg("User %lu does not exist, using gid 0", (long)uid);
-
- if (chown(dir, dd->dd_uid, dd->dd_gid) == -1)
+ dd->dd_uid = (uid_t)-1L;
+ dd->dd_gid = (gid_t)-1L;
+ if (uid != (uid_t)-1L)
{
- perror_msg("Can't change '%s' ownership to %lu:%lu", dir,
- (long)dd->dd_uid, (long)dd->dd_gid);
+ /* Get ABRT's user id */
+ dd->dd_uid = 0;
+ struct passwd *pw = getpwnam("abrt");
+ if (pw)
+ dd->dd_uid = pw->pw_uid;
+ else
+ error_msg("user 'abrt' does not exist, using uid 0");
+
+ /* Get crashed application's group id */
+ /*dd->dd_gid = 0; - dd_init did this already */
+ pw = getpwuid(uid);
+ if (pw)
+ dd->dd_gid = pw->pw_gid;
+ else
+ error_msg("User %lu does not exist, using gid 0", (long)uid);
+
+ if (chown(dir, dd->dd_uid, dd->dd_gid) == -1)
+ {
+ perror_msg("can't change '%s' ownership to %lu:%lu", dir,
+ (long)dd->dd_uid, (long)dd->dd_gid);
+ }
}
+ return dd;
+}
+
+void dd_create_basic_files(struct dump_dir *dd, uid_t uid)
+{
char long_str[sizeof(long) * 3 + 2];
+ time_t t = time(NULL);
+ sprintf(long_str, "%lu", (long)t);
+ dd_save_text(dd, FILENAME_TIME, long_str);
+
+ if (uid == (uid_t)-1)
+ uid = getuid();
sprintf(long_str, "%lu", (long)uid);
dd_save_text(dd, FILENAME_UID, long_str);
@@ -317,54 +482,102 @@ struct dump_dir *dd_create(const char *dir, uid_t uid)
uname(&buf); /* never fails */
dd_save_text(dd, FILENAME_KERNEL, buf.release);
dd_save_text(dd, FILENAME_ARCHITECTURE, buf.machine);
- char *release = load_text_file("/etc/redhat-release", /*flags:*/ 0);
- strchrnul(release, '\n')[0] = '\0';
- dd_save_text(dd, FILENAME_RELEASE, release);
- free(release);
+ dd_save_text(dd, FILENAME_HOSTNAME, buf.nodename);
- time_t t = time(NULL);
- sprintf(long_str, "%lu", (long)t);
- dd_save_text(dd, FILENAME_TIME, long_str);
-
- return dd;
+ char *release = load_text_file("/etc/system-release",
+ DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
+ if (!release)
+ release = load_text_file("/etc/redhat-release", /*flags:*/ 0);
+ dd_save_text(dd, FILENAME_OS_RELEASE, release);
+ free(release);
}
-static void delete_file_dir(const char *dir)
+static int delete_file_dir(const char *dir, bool skip_lock_file)
{
DIR *d = opendir(dir);
if (!d)
- return;
+ {
+ /* The caller expects us to error out only if the directory
+ * still exists (not deleted). If directory
+ * *doesn't exist*, return 0 and clear errno.
+ */
+ if (errno == ENOENT || errno == ENOTDIR)
+ {
+ errno = 0;
+ return 0;
+ }
+ return -1;
+ }
+ bool unlink_lock_file = false;
struct dirent *dent;
while ((dent = readdir(d)) != NULL)
{
if (dot_or_dotdot(dent->d_name))
continue;
+ if (skip_lock_file && strcmp(dent->d_name, ".lock") == 0)
+ {
+ unlink_lock_file = true;
+ continue;
+ }
char *full_path = concat_path_file(dir, dent->d_name);
if (unlink(full_path) == -1 && errno != ENOENT)
{
- if (errno != EISDIR)
+ int err = 0;
+ if (errno == EISDIR)
+ {
+ errno = 0;
+ err = delete_file_dir(full_path, /*skip_lock_file:*/ false);
+ }
+ if (errno || err)
{
- error_msg("Can't remove '%s'", full_path);
+ perror_msg("Can't remove '%s'", full_path);
free(full_path);
closedir(d);
- return;
+ return -1;
}
- delete_file_dir(full_path);
}
free(full_path);
}
closedir(d);
- if (rmdir(dir) == -1)
+
+ /* Here we know for sure that all files/subdirs we found via readdir
+ * were deleted successfully. If rmdir below fails, we assume someone
+ * is racing with us and created a new file.
+ */
+
+ if (unlink_lock_file)
{
- error_msg("Can't remove dir '%s'", dir);
+ char *full_path = concat_path_file(dir, ".lock");
+ xunlink(full_path);
+ free(full_path);
+
+ unsigned cnt = RMDIR_FAIL_COUNT;
+ do {
+ if (rmdir(dir) == 0)
+ return 0;
+ /* Someone locked the dir after unlink, but before rmdir.
+ * This "someone" must be dd_lock().
+ * It detects this (by seeing that there is no time file)
+ * and backs off at once. So we need to just retry rmdir,
+ * with minimal sleep.
+ */
+ usleep(RMDIR_FAIL_USLEEP);
+ } while (--cnt != 0);
}
+
+ int r = rmdir(dir);
+ if (r)
+ perror_msg("Can't remove directory '%s'", dir);
+ return r;
}
-void dd_delete(struct dump_dir *dd)
+int dd_delete(struct dump_dir *dd)
{
- delete_file_dir(dd->dd_dir);
+ int r = delete_file_dir(dd->dd_dirname, /*skip_lock_file:*/ true);
+ dd->locked = 0; /* delete_file_dir already removed .lock */
dd_close(dd);
+ return r;
}
static char *load_text_file(const char *path, unsigned flags)
@@ -411,10 +624,15 @@ static bool save_binary_file(const char *path, const char* data, unsigned size,
perror_msg("Can't open file '%s'", path);
return false;
}
- if (fchown(fd, uid, gid) == -1)
+
+ if (uid != (uid_t)-1L)
{
- perror_msg("can't change '%s' ownership to %lu:%lu", path, (long)uid, (long)gid);
+ if (fchown(fd, uid, gid) == -1)
+ {
+ perror_msg("can't change '%s' ownership to %lu:%lu", path, (long)uid, (long)gid);
+ }
}
+
unsigned r = full_write(fd, data, size);
close(fd);
if (r != size)
@@ -428,10 +646,14 @@ static bool save_binary_file(const char *path, const char* data, unsigned size,
char* dd_load_text_ext(const struct dump_dir *dd, const char *name, unsigned flags)
{
- if (!dd->locked)
- error_msg_and_die("dump_dir is not opened"); /* bug */
+// if (!dd->locked)
+// error_msg_and_die("dump_dir is not opened"); /* bug */
- char *full_path = concat_path_file(dd->dd_dir, name);
+ /* Compat with old abrt dumps. Remove in abrt-2.1 */
+ if (strcmp(name, "release") == 0)
+ name = FILENAME_OS_RELEASE;
+
+ char *full_path = concat_path_file(dd->dd_dirname, name);
char *ret = load_text_file(full_path, flags);
free(full_path);
@@ -448,7 +670,7 @@ void dd_save_text(struct dump_dir *dd, const char *name, const char *data)
if (!dd->locked)
error_msg_and_die("dump_dir is not opened"); /* bug */
- char *full_path = concat_path_file(dd->dd_dir, name);
+ char *full_path = concat_path_file(dd->dd_dirname, name);
save_binary_file(full_path, data, strlen(data), dd->dd_uid, dd->dd_gid);
free(full_path);
}
@@ -458,23 +680,23 @@ void dd_save_binary(struct dump_dir* dd, const char* name, const char* data, uns
if (!dd->locked)
error_msg_and_die("dump_dir is not opened"); /* bug */
- char *full_path = concat_path_file(dd->dd_dir, name);
+ char *full_path = concat_path_file(dd->dd_dirname, name);
save_binary_file(full_path, data, size, dd->dd_uid, dd->dd_gid);
free(full_path);
}
DIR *dd_init_next_file(struct dump_dir *dd)
{
- if (!dd->locked)
- error_msg_and_die("dump_dir is not opened"); /* bug */
+// if (!dd->locked)
+// error_msg_and_die("dump_dir is not opened"); /* bug */
if (dd->next_dir)
closedir(dd->next_dir);
- dd->next_dir = opendir(dd->dd_dir);
+ dd->next_dir = opendir(dd->dd_dirname);
if (!dd->next_dir)
{
- error_msg("Can't open dir '%s'", dd->dd_dir);
+ error_msg("Can't open directory '%s'", dd->dd_dirname);
}
return dd->next_dir;
@@ -488,12 +710,12 @@ int dd_get_next_file(struct dump_dir *dd, char **short_name, char **full_name)
struct dirent *dent;
while ((dent = readdir(dd->next_dir)) != NULL)
{
- if (is_regular_file(dent, dd->dd_dir))
+ if (is_regular_file(dent, dd->dd_dirname))
{
if (short_name)
*short_name = xstrdup(dent->d_name);
if (full_name)
- *full_name = concat_path_file(dd->dd_dir, dent->d_name);
+ *full_name = concat_path_file(dd->dd_dirname, dent->d_name);
return 1;
}
}
@@ -504,9 +726,9 @@ int dd_get_next_file(struct dump_dir *dd, char **short_name, char **full_name)
}
/* Utility function */
-void delete_crash_dump_dir(const char *dd_dir)
+void delete_dump_dir(const char *dirname)
{
- struct dump_dir *dd = dd_opendir(dd_dir, /*flags:*/ 0);
+ struct dump_dir *dd = dd_opendir(dirname, /*flags:*/ 0);
if (dd)
{
dd_delete(dd);
diff --git a/src/daemon/CommLayerServer.cpp b/src/lib/glib_support.c
index 5e250121..feb4c18b 100644
--- a/src/daemon/CommLayerServer.cpp
+++ b/src/lib/glib_support.c
@@ -1,6 +1,6 @@
/*
- Copyright (C) 2010 ABRT team
- Copyright (C) 2010 RedHat Inc
+ Copyright (C) 2011 ABRT team
+ Copyright (C) 2011 RedHat Inc
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,14 +16,11 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "CommLayerServer.h"
-#include "CrashWatcher.h"
+#include "abrtlib.h"
-CCommLayerServer::CCommLayerServer()
-{
- m_init_error = 0;
-}
-
-CCommLayerServer::~CCommLayerServer()
+void list_free_with_free(GList *list)
{
+ for (GList *li = list; li; li = g_list_next(li))
+ free(li->data);
+ g_list_free(list);
}
diff --git a/src/lib/hash_md5.h b/src/lib/hash_md5.h
index cc1d2c43..f7e9f398 100644
--- a/src/lib/hash_md5.h
+++ b/src/lib/hash_md5.h
@@ -24,6 +24,9 @@ typedef struct md5_ctx_t {
uint32_t buflen;
char buffer[128];
} md5_ctx_t;
+#define md5_begin abrt_md5_begin
void md5_begin(md5_ctx_t *ctx);
+#define md5_hash abrt_md5_hash
void md5_hash(const void *data, size_t length, md5_ctx_t *ctx);
+#define md5_end abrt_md5_end
void md5_end(void *resbuf, md5_ctx_t *ctx);
diff --git a/src/lib/hash_sha1.h b/src/lib/hash_sha1.h
index 02978ea4..09f50d12 100644
--- a/src/lib/hash_sha1.h
+++ b/src/lib/hash_sha1.h
@@ -31,8 +31,11 @@ typedef struct sha1_ctx_t {
void (*process_block)(struct sha1_ctx_t*);
} sha1_ctx_t;
+#define sha1_begin abrt_sha1_begin
void sha1_begin(sha1_ctx_t *ctx);
+#define sha1_hash abrt_sha1_hash
void sha1_hash(const void *buffer, size_t len, sha1_ctx_t *ctx);
+#define sha1_end abrt_sha1_end
void sha1_end(void *resbuf, sha1_ctx_t *ctx);
#ifdef __cplusplus
diff --git a/src/lib/hooklib.c b/src/lib/hooklib.c
index 63c8a634..a89ab008 100644
--- a/src/lib/hooklib.c
+++ b/src/lib/hooklib.c
@@ -132,7 +132,7 @@ void trim_debug_dumps(unsigned setting_MaxCrashReportsSize, const char *exclude_
char *d = concat_path_file(DEBUG_DUMPS_DIR, worst_dir);
free(worst_dir);
worst_dir = NULL;
- delete_crash_dump_dir(d);
+ delete_dump_dir(d);
free(d);
}
}
diff --git a/src/lib/hooklib.h b/src/lib/hooklib.h
index ba76efbc..84b31a5f 100644
--- a/src/lib/hooklib.h
+++ b/src/lib/hooklib.h
@@ -20,8 +20,14 @@
extern "C" {
#endif
-void parse_conf(const char *additional_conf, unsigned *setting_MaxCrashReportsSize, bool *setting_MakeCompatCore, bool *setting_SaveBinaryImage);
+#define parse_conf abrt_parse_conf
+void parse_conf(const char *additional_conf,
+ unsigned *setting_MaxCrashReportsSize,
+ bool *setting_MakeCompatCore,
+ bool *setting_SaveBinaryImage);
+#define check_free_space abrt_check_free_space
void check_free_space(unsigned setting_MaxCrashReportsSize);
+#define trim_debug_dumps abrt_trim_debug_dumps
void trim_debug_dumps(unsigned setting_MaxCrashReportsSize, const char *exclude_path);
#ifdef __cplusplus
diff --git a/src/lib/numtoa.cpp b/src/lib/iso_date_string.c
index 061da553..4600ff7f 100644
--- a/src/lib/numtoa.cpp
+++ b/src/lib/iso_date_string.c
@@ -1,8 +1,6 @@
/*
- Number to string conversions
-
- Copyright (C) 2010 ABRT team
- Copyright (C) 2010 RedHat inc.
+ Copyright (C) 2011 ABRT team
+ Copyright (C) 2011 RedHat Inc
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,17 +16,16 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+
#include "abrtlib.h"
-std::string unsigned_to_string(unsigned long long x)
-{
- char buf[sizeof(x)*3];
- sprintf(buf, "%llu", x);
- return buf;
-}
-std::string signed_to_string(long long x)
+char *iso_date_string(time_t *pt)
{
- char buf[sizeof(x)*3];
- sprintf(buf, "%lld", x);
- return buf;
+ static char buf[sizeof("YYYY-MM-DD-HH:MM:SS") + 4];
+
+ time_t t;
+ struct tm *ptm = localtime(pt ? pt : (time(&t), &t));
+ strftime(buf, sizeof(buf), "%Y-%m-%d-%H:%M:%S", ptm);
+
+ return buf;
}
diff --git a/src/lib/load_plugin_settings.cpp b/src/lib/load_plugin_settings.c
index 1052f19e..1e6b31e7 100644
--- a/src/lib/load_plugin_settings.cpp
+++ b/src/lib/load_plugin_settings.c
@@ -18,8 +18,10 @@
*/
#include "abrtlib.h"
-bool LoadPluginSettings(const char *pPath, map_plugin_settings_t& pSettings,
- bool skipKeysWithoutValue /*= true*/)
+/* Returns NULL if open failed.
+ * Returns empty hash if conf file is empty.
+ */
+bool load_conf_file(const char *pPath, map_string_h *settings, bool skipKeysWithoutValue)
{
FILE *fp = stdin;
if (strcmp(pPath, "-") != 0)
@@ -33,11 +35,15 @@ bool LoadPluginSettings(const char *pPath, map_plugin_settings_t& pSettings,
while ((line = xmalloc_fgetline(fp)) != NULL)
{
unsigned ii;
- bool is_value = false;
bool valid = false;
bool in_quote = false;
- std::string key;
- std::string value;
+ /* We are reusing line buffer to form temporary
+ * "key\0value\0..." in its beginning
+ */
+ char *key = line;
+ char *value = line;
+ char *cur = line;
+
for (ii = 0; line[ii] != '\0'; ii++)
{
if (line[ii] == '"')
@@ -48,47 +54,43 @@ bool LoadPluginSettings(const char *pPath, map_plugin_settings_t& pSettings,
{
continue;
}
- if (line[ii] == '#' && !in_quote && key == "")
+ if (line[ii] == '#' && !in_quote && cur == line)
{
break;
}
if (line[ii] == '=' && !in_quote)
{
- is_value = true;
valid = true;
+ *cur++ = '\0'; /* terminate key */
+ value = cur; /* remember where value starts */
continue;
}
- if (!is_value)
- {
- key += line[ii];
- }
- else
- {
- value += line[ii];
- }
+ *cur++ = line[ii]; /* store next key or value char */
}
+ *cur++ = '\0'; /* terminate value */
/* Skip broken or empty lines. */
if (!valid)
goto free_line;
/* Skip lines with empty key. */
- if (key.length() == 0)
+ if (key[0] == '\0')
goto free_line;
- if (skipKeysWithoutValue && value.length() == 0)
+ if (skipKeysWithoutValue && value[0] == '\0')
goto free_line;
/* Skip lines with unclosed quotes. */
if (in_quote)
goto free_line;
- pSettings[key] = value;
+ g_hash_table_replace(settings, xstrdup(key), xstrdup(value));
free_line:
free(line);
}
if (fp != stdin)
fclose(fp);
+
return true;
}
diff --git a/src/lib/logging.h b/src/lib/logging.h
index 8a038bc7..316c1a22 100644
--- a/src/lib/logging.h
+++ b/src/lib/logging.h
@@ -33,14 +33,6 @@ extern "C" {
#define NORETURN __attribute__ ((noreturn))
-/* VERB1 log("what you sometimes want to see, even on a production box") */
-#define VERB1 if (g_verbose >= 1)
-/* VERB2 log("debug message, not going into insanely small details") */
-#define VERB2 if (g_verbose >= 2)
-/* VERB3 log("lots and lots of details") */
-#define VERB3 if (g_verbose >= 3)
-/* there is no level > 3 */
-
enum {
LOGMODE_NONE = 0,
LOGMODE_STDIO = (1 << 0),
@@ -49,26 +41,47 @@ enum {
LOGMODE_CUSTOM = (1 << 2),
};
+#define g_custom_logger abrt_g_custom_logger
extern void (*g_custom_logger)(const char*);
+#define msg_prefix abrt_msg_prefix
extern const char *msg_prefix;
+#define msg_eol abrt_msg_eol
extern const char *msg_eol;
+#define logmode abrt_logmode
extern int logmode;
+#define xfunc_error_retval abrt_xfunc_error_retval
extern int xfunc_error_retval;
/* Verbosity level */
+#define g_verbose abrt_g_verbose
extern int g_verbose;
+/* VERB1 log("what you sometimes want to see, even on a production box") */
+#define VERB1 if (g_verbose >= 1)
+/* VERB2 log("debug message, not going into insanely small details") */
+#define VERB2 if (g_verbose >= 2)
+/* VERB3 log("lots and lots of details") */
+#define VERB3 if (g_verbose >= 3)
+/* there is no level > 3 */
+#define abrt_
+#define xfunc_die abrt_xfunc_die
void xfunc_die(void) NORETURN;
+#define log_msg abrt_log_msg
void log_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
/* It's a macro, not function, since it collides with log() from math.h */
#undef log
#define log(...) log_msg(__VA_ARGS__)
/* error_msg family will use g_custom_logger. log_msg does not. */
+#define error_msg abrt_error_msg
void error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+#define error_msg_and_die abrt_error_msg_and_die
void error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
/* Reports error message with libc's errno error description attached. */
+#define perror_msg abrt_perror_msg
void perror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
+#define perror_msg_and_die abrt_perror_msg_and_die
void perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
+#define die_out_of_memory abrt_die_out_of_memory
void die_out_of_memory(void) NORETURN;
#ifdef __cplusplus
diff --git a/src/lib/make_descr.cpp b/src/lib/make_descr.c
index e11325c8..1ba15203 100644
--- a/src/lib/make_descr.cpp
+++ b/src/lib/make_descr.c
@@ -17,10 +17,6 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "abrtlib.h"
-#include "abrt_crash_dump.h"
-
-
-using namespace std;
// caller is responsible for freeing **dsc
static void add_content(bool *was_multiline, char **dsc, const char *header, const char *content)
@@ -64,7 +60,6 @@ static void add_content(bool *was_multiline, char **dsc, const char *header, con
static const char *const blacklisted_items[] = {
FILENAME_ANALYZER ,
FILENAME_COREDUMP ,
- FILENAME_DESCRIPTION, /* package description - basically useless */
FILENAME_HOSTNAME ,
FILENAME_DUPHASH ,
FILENAME_UUID ,
@@ -75,30 +70,32 @@ static const char *const blacklisted_items[] = {
NULL
};
-char* make_description_mailx(const map_crash_data_t & crash_data)
+char* make_description_mailx(crash_data_t *crash_data)
{
struct strbuf *buf_dsc = strbuf_new();
struct strbuf *buf_additional_files = strbuf_new();
struct strbuf *buf_duphash_file = strbuf_new();
struct strbuf *buf_common_files = strbuf_new();
- map_crash_data_t::const_iterator it;
- for (it = crash_data.begin(); it != crash_data.end(); it++)
+ GHashTableIter iter;
+ char *name;
+ struct crash_item *value;
+ g_hash_table_iter_init(&iter, crash_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
{
- if (it->second[CD_TYPE] == CD_TXT)
+ if (value->flags & CD_FLAG_TXT)
{
- const char *itemname = it->first.c_str();
- if ((strcmp(itemname, FILENAME_DUPHASH) != 0)
- && (strcmp(itemname, FILENAME_ARCHITECTURE) != 0)
- && (strcmp(itemname, FILENAME_KERNEL) != 0)
- && (strcmp(itemname, FILENAME_PACKAGE) != 0)
+ if ((strcmp(name, FILENAME_DUPHASH) != 0)
+ && (strcmp(name, FILENAME_ARCHITECTURE) != 0)
+ && (strcmp(name, FILENAME_KERNEL) != 0)
+ && (strcmp(name, FILENAME_PACKAGE) != 0)
) {
- strbuf_append_strf(buf_additional_files, "%s\n-----\n%s\n\n", itemname, it->second[CD_CONTENT].c_str());
+ strbuf_append_strf(buf_additional_files, "%s\n-----\n%s\n\n", name, value->content);
}
- else if (strcmp(itemname, FILENAME_DUPHASH) == 0)
- strbuf_append_strf(buf_duphash_file, "%s\n-----\n%s\n\n", itemname, it->second[CD_CONTENT].c_str());
+ else if (strcmp(name, FILENAME_DUPHASH) == 0)
+ strbuf_append_strf(buf_duphash_file, "%s\n-----\n%s\n\n", name, value->content);
else
- strbuf_append_strf(buf_common_files, "%s\n-----\n%s\n\n", itemname, it->second[CD_CONTENT].c_str());
+ strbuf_append_strf(buf_common_files, "%s\n-----\n%s\n\n", name, value->content);
}
}
@@ -117,24 +114,27 @@ char* make_description_mailx(const map_crash_data_t & crash_data)
return strbuf_free_nobuf(buf_dsc);
}
-char* make_description_bz(const map_crash_data_t& pCrashData)
+char* make_description_bz(crash_data_t *crash_data)
{
struct strbuf *buf_dsc = strbuf_new();
struct strbuf *buf_long_dsc = strbuf_new();
- map_crash_data_t::const_iterator it = pCrashData.begin();
- for (; it != pCrashData.end(); it++)
+ GHashTableIter iter;
+ char *name;
+ struct crash_item *value;
+ g_hash_table_iter_init(&iter, crash_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
{
- const char *itemname = it->first.c_str();
- const char *type = it->second[CD_TYPE].c_str();
- const char *content = it->second[CD_CONTENT].c_str();
- if (strcmp(type, CD_TXT) == 0)
+ struct stat statbuf;
+ unsigned flags = value->flags;
+ const char *content = value->content;
+ if (flags & CD_FLAG_TXT)
{
/* Skip items we are not interested in */
const char *const *bl = blacklisted_items;
while (*bl)
{
- if (strcmp(itemname, *bl) == 0)
+ if (strcmp(name, *bl) == 0)
break;
bl++;
}
@@ -151,7 +151,7 @@ char* make_description_bz(const map_crash_data_t& pCrashData)
add_content(&was_multiline,
&tmp,
/* "reproduce: blah" looks ugly, fixing: */
- (strcmp(itemname, FILENAME_REPRODUCE) == 0) ? "How to reproduce" : itemname,
+ (strcmp(name, FILENAME_REPRODUCE) == 0) ? "How to reproduce" : name,
content
);
@@ -167,13 +167,33 @@ char* make_description_bz(const map_crash_data_t& pCrashData)
strbuf_append_str(buf_dsc, tmp);
free(tmp);
- } else {
- bool was_multiline = 0;
- char *dsc = NULL;
- add_content(&was_multiline, &dsc, "Attached file", itemname);
- strbuf_append_str(buf_dsc, dsc);
- free(dsc);
}
+ else
+ {
+ statbuf.st_size = strlen(content);
+ goto add_attachment_info;
+ }
+ }
+ if (flags & CD_FLAG_BIN)
+ {
+ /* In many cases, it is useful to know how big binary files are
+ * (for example, helps with diagnosing bug upload problems)
+ */
+ if (stat(content, &statbuf) != 0)
+ statbuf.st_size = (off_t) -1;
+
+ add_attachment_info: ;
+ char *descr;
+ if (statbuf.st_size >= 0)
+ descr = xasprintf("%s, %llu bytes", name, (long long)statbuf.st_size);
+ else
+ descr = xstrdup(name);
+ bool was_multiline = 0;
+ char *tmp = NULL;
+ add_content(&was_multiline, &tmp, "Attached file", descr);
+ free(descr);
+ strbuf_append_str(buf_dsc, tmp);
+ free(tmp);
}
}
@@ -189,25 +209,25 @@ char* make_description_bz(const map_crash_data_t& pCrashData)
return strbuf_free_nobuf(buf_dsc);
}
-char* make_description_logger(const map_crash_data_t& pCrashData)
+char* make_description_logger(crash_data_t *crash_data)
{
struct strbuf *buf_dsc = strbuf_new();
struct strbuf *buf_long_dsc = strbuf_new();
- map_crash_data_t::const_iterator it = pCrashData.begin();
- for (; it != pCrashData.end(); it++)
+ GHashTableIter iter;
+ char *name;
+ struct crash_item *value;
+ g_hash_table_iter_init(&iter, crash_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
{
- const char *filename = it->first.c_str();
- const char *type = it->second[CD_TYPE].c_str();
- const char *content = it->second[CD_CONTENT].c_str();
- if ((strcmp(type, CD_TXT) == 0)
- || (strcmp(type, CD_BIN) == 0)
- ) {
+ const char *content = value->content;
+ if (value->flags & (CD_FLAG_TXT|CD_FLAG_BIN))
+ {
/* Skip items we are not interested in */
const char *const *bl = blacklisted_items;
while (*bl)
{
- if (filename == *bl)
+ if (name == *bl)
break;
bl++;
}
@@ -218,12 +238,12 @@ char* make_description_logger(const map_crash_data_t& pCrashData)
bool was_multiline = 0;
char *tmp = NULL;
- add_content(&was_multiline, &tmp, filename, content);
+ add_content(&was_multiline, &tmp, name, content);
if (was_multiline)
{
if (buf_long_dsc->len != 0)
- strbuf_append_char(buf_long_dsc,'\n');
+ strbuf_append_char(buf_long_dsc, '\n');
strbuf_append_str(buf_long_dsc, tmp);
}
@@ -242,29 +262,27 @@ char* make_description_logger(const map_crash_data_t& pCrashData)
return strbuf_free_nobuf(buf_dsc);
}
-char* make_description_reproduce_comment(const map_crash_data_t& pCrashData)
+char* make_description_reproduce_comment(crash_data_t *crash_data)
{
char *repro = NULL;
char *comment = NULL;
+ struct crash_item *value;
- map_crash_data_t::const_iterator end = pCrashData.end();
- map_crash_data_t::const_iterator it;
-
- it = pCrashData.find(FILENAME_REPRODUCE);
- if (it != end)
+ value = get_crash_data_item_or_NULL(crash_data, FILENAME_REPRODUCE);
+ if (value)
{
- if ((it->second[CD_CONTENT].size() > 0)
- && (it->second[CD_CONTENT] != "1.\n2.\n3.\n"))
- {
- repro = xasprintf("\n\nHow to reproduce\n-----\n%s", it->second[CD_CONTENT].c_str());
+ if (value->content[0]
+ && strcmp(value->content, "1.\n2.\n3.\n") != 0
+ ) {
+ repro = xasprintf("\n\nHow to reproduce\n-----\n%s", value->content);
}
}
- it = pCrashData.find(FILENAME_COMMENT);
- if (it != end)
+ value = get_crash_data_item_or_NULL(crash_data, FILENAME_COMMENT);
+ if (value)
{
- if (it->second[CD_CONTENT].size() > 0)
- comment = xasprintf("\n\nComment\n-----\n%s", it->second[CD_CONTENT].c_str());
+ if (value->content[0])
+ comment = xasprintf("\n\nComment\n-----\n%s", value->content);
}
if (!repro && !comment)
diff --git a/src/lib/parse_options.c b/src/lib/parse_options.c
index c1a2c297..3d631461 100644
--- a/src/lib/parse_options.c
+++ b/src/lib/parse_options.c
@@ -1,3 +1,21 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
#include <getopt.h>
#include "abrtlib.h"
@@ -6,26 +24,17 @@
#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP 2
-void parse_usage_and_die(const char *usage, const struct options *opt)
+void show_usage_and_die(const char *usage, const struct options *opt)
{
fprintf(stderr, _("Usage: %s\n"), usage);
- if (opt->type != OPTION_GROUP)
- fputc('\n', stderr);
+ fputc('\n', stderr);
for (; opt->type != OPTION_END; opt++)
{
size_t pos;
int pad;
- if (opt->type == OPTION_GROUP)
- {
- fputc('\n', stderr);
- if (*opt->help)
- fprintf(stderr, "%s\n", opt->help);
- continue;
- }
-
pos = fprintf(stderr, " ");
if (opt->short_name)
pos += fprintf(stderr, "-%c", opt->short_name);
@@ -92,6 +101,7 @@ unsigned parse_opts(int argc, char **argv, const struct options *opt,
break;
case OPTION_INTEGER:
case OPTION_STRING:
+ case OPTION_LIST:
curopt->has_arg = required_argument;
if (opt[ii].short_name)
strbuf_append_strf(shortopts, "%c:", opt[ii].short_name);
@@ -101,7 +111,6 @@ unsigned parse_opts(int argc, char **argv, const struct options *opt,
if (opt[ii].short_name)
strbuf_append_strf(shortopts, "%c::", opt[ii].short_name);
break;
- case OPTION_GROUP:
case OPTION_END:
break;
}
@@ -144,7 +153,7 @@ unsigned parse_opts(int argc, char **argv, const struct options *opt,
{
free(longopts);
strbuf_free(shortopts);
- parse_usage_and_die(usage, opt);
+ show_usage_and_die(usage, opt);
}
for (ii = 0; ii < size; ++ii)
@@ -157,17 +166,19 @@ unsigned parse_opts(int argc, char **argv, const struct options *opt,
if (opt[ii].value != NULL) switch (opt[ii].type)
{
case OPTION_BOOL:
- *(int*)opt[ii].value += 1;
+ *(int*)(opt[ii].value) += 1;
break;
case OPTION_INTEGER:
- *(int*)opt[ii].value = xatoi(optarg);
+ *(int*)(opt[ii].value) = xatoi(optarg);
break;
case OPTION_STRING:
case OPTION_OPTSTRING:
if (optarg)
- *(char**)opt[ii].value = (char*)optarg;
+ *(char**)(opt[ii].value) = (char*)optarg;
+ break;
+ case OPTION_LIST:
+ *(GList**)(opt[ii].value) = g_list_append(*(GList**)(opt[ii].value), optarg);
break;
- case OPTION_GROUP:
case OPTION_END:
break;
}
diff --git a/src/lib/parse_options.h b/src/lib/parse_options.h
index 105f081c..82f3c6b8 100644
--- a/src/lib/parse_options.h
+++ b/src/lib/parse_options.h
@@ -1,18 +1,34 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
#ifndef PARSE_OPTIONS_H
#define PARSE_OPTIONS_H
-
#ifdef __cplusplus
extern "C" {
#endif
enum parse_opt_type {
OPTION_BOOL,
- OPTION_GROUP,
OPTION_STRING,
OPTION_INTEGER,
OPTION_OPTSTRING,
+ OPTION_LIST,
OPTION_END,
};
@@ -29,22 +45,24 @@ struct options {
* s - short_name
* l - long_name
* v - value
- * a - argh argument help
+ * a - option parameter name (for help text)
* h - help
*/
#define OPT_END() { OPTION_END }
#define OPT_BOOL(s, l, v, h) { OPTION_BOOL, (s), (l), (v), NULL, (h) }
-#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
-#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), "n", (h) }
+#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), "NUM", (h) }
#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) }
#define OPT_OPTSTRING(s, l, v, a, h) { OPTION_OPTSTRING, (s), (l), (v), (a), (h) }
+#define OPT_LIST(s, l, v, a, h) { OPTION_LIST, (s), (l), (v), (a), (h) }
-#define OPT__VERBOSE(v) OPT_BOOL('v', "verbose", (v), "be verbose")
+#define OPT__VERBOSE(v) OPT_BOOL('v', "verbose", (v), _("Be verbose"))
+#define parse_opts abrt_parse_opts
unsigned parse_opts(int argc, char **argv, const struct options *opt,
const char *usage);
-void parse_usage_and_die(const char *usage, const struct options *opt);
+#define show_usage_and_die abrt_show_usage_and_die
+void show_usage_and_die(const char *usage, const struct options *opt);
#ifdef __cplusplus
}
diff --git a/src/lib/parse_release.cpp b/src/lib/parse_release.c
index f9057bfe..b24f928c 100644
--- a/src/lib/parse_release.cpp
+++ b/src/lib/parse_release.c
@@ -19,7 +19,7 @@
#include "abrtlib.h"
// caller is reposible for freeing *product* and *version*
-void parse_release(const char *release, char** product, char** version)
+static void parse_release(const char *release, char** product, char** version, bool append_rhel_version)
{
if (strstr(release, "Rawhide"))
{
@@ -33,21 +33,31 @@ void parse_release(const char *release, char** product, char** version)
if (strstr(release, "Fedora"))
strbuf_append_str(buf_product, "Fedora");
else if (strstr(release, "Red Hat Enterprise Linux"))
- strbuf_append_str(buf_product, "Red Hat Enterprise Linux ");
+ strbuf_append_str(buf_product, "Red Hat Enterprise Linux");
+ else
+ {
+ /* TODO: add logic for parsing other distros' names here */
+ strbuf_append_str(buf_product, release);
+ }
const char *r = strstr(release, "release");
const char *space = r ? strchr(r, ' ') : NULL;
struct strbuf *buf_version = strbuf_new();
- if (space++)
+ if (space)
{
+ space++;
while (*space != '\0' && *space != ' ')
{
/* Eat string like "5.2" */
strbuf_append_char(buf_version, *space);
- if ((strcmp(buf_product->buf, "Red Hat Enterprise Linux ") == 0))
+ if (append_rhel_version
+ && strcmp(buf_product->buf, "Red Hat Enterprise Linux") == 0
+ ) {
+ strbuf_append_char(buf_product, ' ');
strbuf_append_char(buf_product, *space);
-
+ }
+ append_rhel_version = false;
space++;
}
}
@@ -57,3 +67,15 @@ void parse_release(const char *release, char** product, char** version)
VERB3 log("%s: version:'%s' product:'%s'", __func__, *version, *product);
}
+
+void parse_release_for_bz(const char *release, char** product, char** version)
+{
+ /* Fedora/RH bugzilla uses "Red Hat Enterprise Linux N" product RHEL */
+ parse_release(release, product, version, /*append_rhel_version:*/ true);
+}
+
+void parse_release_for_rhts(const char *release, char** product, char** version)
+{
+ /* RHTS uses "Red Hat Enterprise Linux" product for RHEL */
+ parse_release(release, product, version, /*append_rhel_version:*/ false);
+}
diff --git a/src/lib/read_write.h b/src/lib/read_write.h
index 054a1a9a..dc85f33b 100644
--- a/src/lib/read_write.h
+++ b/src/lib/read_write.h
@@ -32,14 +32,20 @@ extern "C" {
// NB: will return short read on error, not -1,
// if some data was read before error occurred
+#define xread abrt_xread
void xread(int fd, void *buf, size_t count);
+#define safe_read abrt_safe_read
ssize_t safe_read(int fd, void *buf, size_t count);
+#define safe_write abrt_safe_write
ssize_t safe_write(int fd, const void *buf, size_t count);
+#define full_read abrt_full_read
ssize_t full_read(int fd, void *buf, size_t count);
+#define full_write abrt_full_write
ssize_t full_write(int fd, const void *buf, size_t count);
+#define full_write_str abrt_full_write_str
ssize_t full_write_str(int fd, const char *buf);
#ifdef __cplusplus
diff --git a/src/lib/run_event.c b/src/lib/run_event.c
index 4ff1070d..e96e762f 100644
--- a/src/lib/run_event.c
+++ b/src/lib/run_event.c
@@ -16,36 +16,78 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <glob.h>
#include "abrtlib.h"
-int run_event(struct run_event_state *state,
+struct run_event_state *new_run_event_state()
+{
+ return xzalloc(sizeof(struct run_event_state));
+}
+
+void free_run_event_state(struct run_event_state *state)
+{
+ if (state)
+ {
+ free_commands(state);
+ free(state);
+ }
+}
+
+
+/* Asyncronous command execution */
+
+/* It is not yet clear whether we need to re-parse event config file
+ * and re-check the elements in dump dir after each comamnd.
+ *
+ * Consider this config file:
+ *
+ * EVENT=e cmd1
+ * EVENT=e foo=bar cmd2
+ * EVENT=e foo=baz cmd3
+ *
+ * Imagine that element foo existed and was equal to bar at the beginning.
+ * After cmd1, should we execute cmd2 if element foo disappeared?
+ * After cmd1/2, should we execute cmd3 if element foo changed value to baz?
+ *
+ * So far, we read entire config file and select a list of commands to execute,
+ * checking all conditions in the beginning. It is a bit more simple to code up.
+ * But we may want to change it later. Therefore list of commands machinery
+ * is encapsulated in struct run_event_state and public async API:
+ * prepare_commands(state, dir, event);
+ * spawn_next_command(state, dir, event);
+ * free_commands(state);
+ * does not expose it.
+ */
+
+static GList *load_event_config(GList *list,
const char *dump_dir_name,
- const char *event
+ const char *event,
+ const char *conf_file_name
) {
- FILE *conffile = fopen(CONF_DIR"/abrt_event.conf", "r");
+ FILE *conffile = fopen(conf_file_name, "r");
if (!conffile)
{
- error_msg("Can't open '%s'", CONF_DIR"/abrt_event.conf");
- return 1;
+ error_msg("Can't open '%s'", conf_file_name);
+ return list;
}
- close_on_exec_on(fileno(conffile));
-
- /* Export some useful environment variables for children */
- /* Just exporting dump_dir_name isn't always ok: it can be "."
- * and some children want to cd to other directory but still
- * be able to find dump directory by using $DUMP_DIR...
- */
- char *full_name = realpath(dump_dir_name, NULL);
- setenv("DUMP_DIR", (full_name ? full_name : dump_dir_name), 1);
- free(full_name);
- setenv("EVENT", event, 1);
- /* Read, match, and execute lines from abrt_event.conf */
- int retval = -1;
+ /* Read, match, and remember commands to execute */
struct dump_dir *dd = NULL;
- char *line;
- while ((line = xmalloc_fgetline(conffile)) != NULL)
+ char *next_line = xmalloc_fgetline(conffile);
+ while (next_line)
{
+ char *line = next_line;
+ while (1)
+ {
+ next_line = xmalloc_fgetline(conffile);
+ if (!next_line || !isblank(next_line[0]))
+ break;
+ char *old_line = line;
+ line = xasprintf("%s\n%s", line, next_line);
+ free(old_line);
+ free(next_line);
+ }
+
/* Line has form: [VAR=VAL]... PROG [ARGS] */
char *p = skip_whitespace(line);
if (*p == '\0' || *p == '#')
@@ -53,6 +95,43 @@ int run_event(struct run_event_state *state,
VERB3 log("%s: line '%s'", __func__, p);
+ if (strncmp(p, "include", strlen("include")) == 0 && isblank(p[strlen("include")]))
+ {
+ /* include GLOB_PATTERN */
+ p = skip_whitespace(p + strlen("include"));
+
+ const char *last_slash;
+ char *name_to_glob;
+ if (*p != '/'
+ && (last_slash = strrchr(conf_file_name, '/')) != NULL
+ )
+ /* GLOB_PATTERN is relative, and this include is in path/to/file.conf
+ * Construct path/to/GLOB_PATTERN:
+ */
+ name_to_glob = xasprintf("%.*s%s", (int)(last_slash - conf_file_name + 1), conf_file_name, p);
+ else
+ /* Either GLOB_PATTERN is absolute, or this include is in file.conf
+ * (no slashes in its name). Use unchanged GLOB_PATTERN:
+ */
+ name_to_glob = xstrdup(p);
+
+ glob_t globbuf;
+ memset(&globbuf, 0, sizeof(globbuf));
+ VERB3 log("%s: globbing '%s'", __func__, name_to_glob);
+ glob(name_to_glob, 0, NULL, &globbuf);
+ free(name_to_glob);
+ char **name = globbuf.gl_pathv;
+ if (name) while (*name)
+ {
+ VERB3 log("%s: recursing into '%s'", __func__, *name);
+ list = load_event_config(list, dump_dir_name, event, *name);
+ VERB3 log("%s: returned from '%s'", __func__, *name);
+ name++;
+ }
+ globfree(&globbuf);
+ goto next_line;
+ }
+
while (1) /* word loop */
{
char *end_word = skip_non_whitespace(p);
@@ -83,7 +162,11 @@ int run_event(struct run_event_state *state,
{
dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
+ {
+ free(line);
+ free(next_line);
goto stop; /* error (note: dd_opendir logged error msg) */
+ }
}
real_val = malloced_val = dd_load_text_ext(dd, p, DD_FAIL_QUIETLY);
}
@@ -104,54 +187,133 @@ int run_event(struct run_event_state *state,
p = next_word;
} /* end of word loop */
- /* Don't keep dump dir locked across program runs */
- dd_close(dd);
- dd = NULL;
+ /* We found matching line, remember its command */
+ VERB1 log("Adding '%s'", p);
+ overlapping_strcpy(line, p);
+ list = g_list_append(list, line);
+ continue;
+
+ next_line:
+ free(line);
+ } /* end of line loop */
+
+ stop:
+ dd_close(dd);
+ fclose(conffile);
+
+ return list;
+}
+
+int prepare_commands(struct run_event_state *state,
+ const char *dump_dir_name,
+ const char *event
+) {
+ free_commands(state);
- /* We found matching line, execute its command(s) in shell */
+ state->children_count = 0;
+
+ GList *commands = load_event_config(NULL, dump_dir_name, event, CONF_DIR"/abrt_event.conf");
+ state->commands = commands;
+ return commands != NULL;
+}
+
+void free_commands(struct run_event_state *state)
+{
+ list_free_with_free(state->commands);
+ state->commands = NULL;
+ state->command_out_fd = -1;
+ state->command_pid = 0;
+}
+
+/* event parameter is unused for now,
+ * but may be needed if we change implementation later
+ */
+int spawn_next_command(struct run_event_state *state,
+ const char *dump_dir_name,
+ const char *event
+) {
+ if (!state->commands)
+ return -1;
+
+ /* We count it even if fork fails. The counter isn't meant
+ * to count *successful* forks, it is meant to let caller know
+ * whether the event we run has *any* handlers configured, or not.
+ */
+ state->children_count++;
+
+ char *cmd = state->commands->data;
+ VERB1 log("Executing '%s'", cmd);
+
+ /* Export some useful environment variables for children */
+ /* Just exporting dump_dir_name isn't always ok: it can be "."
+ * and some children want to cd to other directory but still
+ * be able to find dump directory by using $DUMP_DIR...
+ */
+ char *full_name = realpath(dump_dir_name, NULL);
+ setenv("DUMP_DIR", (full_name ? full_name : dump_dir_name), 1);
+ free(full_name);
+ setenv("EVENT", event, 1);
+//FIXME: set vars in the child, not here! Need to improve fork_execv_on_steroids...
+
+ char *argv[4];
+ argv[0] = (char*)"/bin/sh";
+ argv[1] = (char*)"-c";
+ argv[2] = cmd;
+ argv[3] = NULL;
+
+ int pipefds[2];
+ state->command_pid = fork_execv_on_steroids(
+ EXECFLG_INPUT_NUL + EXECFLG_OUTPUT + EXECFLG_ERR2OUT,
+ argv,
+ pipefds,
+ /* unsetenv_vec: */ NULL,
+ /* dir: */ dump_dir_name,
+ /* uid(unused): */ 0
+ );
+ state->command_out_fd = pipefds[0];
+
+ state->commands = g_list_remove(state->commands, cmd);
+
+ return 0;
+}
+
+
+/* Syncronous command execution:
+ */
+int run_event_on_dir_name(struct run_event_state *state,
+ const char *dump_dir_name,
+ const char *event
+) {
+ prepare_commands(state, dump_dir_name, event);
+
+ /* Execute every command in shell */
+
+ int retval = 0;
+ while (spawn_next_command(state, dump_dir_name, event) >= 0)
+ {
+ /* Consume log from stdout */
+ FILE *fp = fdopen(state->command_out_fd, "r");
+ if (!fp)
+ die_out_of_memory();
+ char *buf;
+ while ((buf = xmalloc_fgetline(fp)) != NULL)
{
- VERB1 log("Executing '%s'", p);
-
- /* /bin/sh -c 'cmd [args]' NULL */
- char *argv[4];
- char **pp = argv;
- *pp++ = (char*)"/bin/sh";
- *pp++ = (char*)"-c";
- *pp++ = (char*)p;
- *pp = NULL;
- int pipefds[2];
- pid_t pid = fork_execv_on_steroids(EXECFLG_INPUT_NUL + EXECFLG_OUTPUT + EXECFLG_ERR2OUT,
- argv,
- pipefds,
- /* unsetenv_vec: */ NULL,
- /* dir: */ dump_dir_name,
- /* uid(unused): */ 0
- );
- free(line);
- line = NULL;
-
- /* Consume log from stdout */
- FILE *fp = fdopen(pipefds[0], "r");
- if (!fp)
- die_out_of_memory();
- char *buf;
- while ((buf = xmalloc_fgetline(fp)) != NULL)
- {
- if (state->logging_callback)
- buf = state->logging_callback(buf, state->logging_param);
- free(buf);
- }
- fclose(fp); /* Got EOF, close. This also closes pipefds[0] */
+ if (state->logging_callback)
+ buf = state->logging_callback(buf, state->logging_param);
+ free(buf);
+ }
+ fclose(fp); /* Got EOF, close. This also closes state->command_out_fd */
- /* Wait for child to actually exit, collect status */
- int status;
- waitpid(pid, &status, 0);
+ /* Wait for child to actually exit, collect status */
+ int status;
+ waitpid(state->command_pid, &status, 0);
- retval = WEXITSTATUS(status);
- if (WIFSIGNALED(status))
- retval = WTERMSIG(status) + 128;
- if (retval != 0)
- break;
+ retval = WEXITSTATUS(status);
+ if (WIFSIGNALED(status))
+ retval = WTERMSIG(status) + 128;
+ if (retval != 0)
+ {
+ break;
}
if (state->post_run_callback)
@@ -160,26 +322,49 @@ int run_event(struct run_event_state *state,
if (retval != 0)
break;
}
+ }
- next_line:
- free(line);
- } /* end of line loop */
-
- stop:
- free(line);
- dd_close(dd);
- fclose(conffile);
+ free_commands(state);
return retval;
}
-char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx)
+int run_event_on_crash_data(struct run_event_state *state, crash_data_t *data, const char *event)
{
- FILE *conffile = fopen(CONF_DIR"/abrt_event.conf", "r");
+ state->children_count = 0;
+
+ struct dump_dir *dd = create_dump_dir_from_crash_data(data, NULL);
+ if (!dd)
+ return -1;
+ char *dir_name = xstrdup(dd->dd_dirname);
+ dd_close(dd);
+
+ int r = run_event_on_dir_name(state, dir_name, event);
+
+ g_hash_table_remove_all(data);
+ dd = dd_opendir(dir_name, 0);
+ free(dir_name);
+ if (dd)
+ {
+ load_crash_data_from_dump_dir(data, dd);
+ dd_delete(dd);
+ }
+ return r;
+}
+
+
+/* TODO: very similar to run_event_helper, try to combine into one fn? */
+static int list_possible_events_helper(struct strbuf *result,
+ struct dump_dir *dd,
+ const char *dump_dir_name,
+ const char *pfx,
+ const char *conf_file_name
+) {
+ FILE *conffile = fopen(conf_file_name, "r");
if (!conffile)
{
- error_msg("Can't open '%s'", CONF_DIR"/abrt_event.conf");
- return NULL;
+ error_msg("Can't open '%s'", conf_file_name);
+ return 0;
}
/* We check "dump_dir_name == NULL" later.
@@ -189,11 +374,23 @@ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const
if (dd)
dump_dir_name = NULL;
+ int error = 0;
unsigned pfx_len = strlen(pfx);
- struct strbuf *result = strbuf_new();
- char *line;
- while ((line = xmalloc_fgetline(conffile)) != NULL)
+ char *next_line = xmalloc_fgetline(conffile);
+ while (next_line)
{
+ char *line = next_line;
+ while (1)
+ {
+ next_line = xmalloc_fgetline(conffile);
+ if (!next_line || !isblank(next_line[0]))
+ break;
+ char *old_line = line;
+ line = xasprintf("%s\n%s", line, next_line);
+ free(old_line);
+ free(next_line);
+ }
+
/* Line has form: [VAR=VAL]... PROG [ARGS] */
char *p = skip_whitespace(line);
if (*p == '\0' || *p == '#')
@@ -201,6 +398,45 @@ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const
VERB3 log("%s: line '%s'", __func__, p);
+ if (strncmp(p, "include", strlen("include")) == 0 && isblank(p[strlen("include")]))
+ {
+ /* include GLOB_PATTERN */
+ p = skip_whitespace(p + strlen("include"));
+
+ const char *last_slash;
+ char *name_to_glob;
+ if (*p != '/'
+ && (last_slash = strrchr(conf_file_name, '/')) != NULL
+ )
+ /* GLOB_PATTERN is relative, and this include is in path/to/file.conf
+ * Construct path/to/GLOB_PATTERN:
+ */
+ name_to_glob = xasprintf("%.*s%s", (int)(last_slash - conf_file_name + 1), conf_file_name, p);
+ else
+ /* Either GLOB_PATTERN is absolute, or this include is in file.conf
+ * (no slashes in its name). Use unchanged GLOB_PATTERN:
+ */
+ name_to_glob = xstrdup(p);
+
+ glob_t globbuf;
+ memset(&globbuf, 0, sizeof(globbuf));
+ VERB3 log("%s: globbing '%s'", __func__, name_to_glob);
+ glob(name_to_glob, 0, NULL, &globbuf);
+ free(name_to_glob);
+ char **name = globbuf.gl_pathv;
+ if (name) while (*name)
+ {
+ VERB3 log("%s: recursing into '%s'", __func__, *name);
+ error = list_possible_events_helper(result, dd, dump_dir_name, pfx, *name);
+ VERB3 log("%s: returned from '%s'", __func__, *name);
+ if (error)
+ break;
+ name++;
+ }
+ globfree(&globbuf);
+ goto next_line;
+ }
+
while (1) /* word loop */
{
char *end_word = skip_non_whitespace(p);
@@ -233,13 +469,21 @@ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const
goto next_word;
dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
+ {
+ error = -1;
+ free(line);
+ free(next_line);
goto stop; /* error (note: dd_opendir logged error msg) */
+ }
}
char *real_val = dd_load_text_ext(dd, p, DD_FAIL_QUIETLY);
/* Does VAL match? */
if (strcmp(real_val, line_val) != 0)
{
- VERB3 log("var '%s': '%s'!='%s', skipping line", p, real_val, line_val);
+ VERB3 log("var '%s': '%.*s'!='%s', skipping line",
+ p,
+ (int)(strchrnul(real_val, '\n') - real_val), real_val,
+ line_val);
free(real_val);
goto next_line; /* no */
}
@@ -265,10 +509,18 @@ char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const
} /* end of line loop */
stop:
- free(line);
if (dump_dir_name != NULL)
dd_close(dd);
fclose(conffile);
+ return error;
+}
+
+char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx)
+{
+ struct strbuf *result = strbuf_new();
+ int error = list_possible_events_helper(result, dd, dump_dir_name, pfx, CONF_DIR"/abrt_event.conf");
+ if (error)
+ strbuf_clear(result);
return strbuf_free_nobuf(result);
}
diff --git a/src/lib/spawn.c b/src/lib/spawn.c
index 068f4ac7..f6b7263c 100644
--- a/src/lib/spawn.c
+++ b/src/lib/spawn.c
@@ -108,7 +108,7 @@ pid_t fork_execv_on_steroids(int flags,
execvp(argv[0], argv);
if (!(flags & EXECFLG_QUIET))
perror_msg("Can't execute '%s'", argv[0]);
- exit(127); /* shell uses this exitcode in this case */
+ exit(127); /* shell uses this exit code in this case */
}
if (flags & EXECFLG_INPUT) {
diff --git a/src/lib/steal_directory.c b/src/lib/steal_directory.c
new file mode 100644
index 00000000..a77861ee
--- /dev/null
+++ b/src/lib/steal_directory.c
@@ -0,0 +1,40 @@
+#include "abrtlib.h"
+
+struct dump_dir *steal_directory(const char *base_dir, const char *dump_dir_name)
+{
+ const char *base_name = strrchr(dump_dir_name, '/');
+ if (base_name)
+ base_name++;
+ else
+ base_name = dump_dir_name;
+
+ struct dump_dir *dd_dst;
+ unsigned count = 100;
+ char *dst_dir_name = concat_path_file(base_dir, base_name);
+ while (1)
+ {
+ dd_dst = dd_create(dst_dir_name, (uid_t)-1);
+ free(dst_dir_name);
+ if (dd_dst)
+ break;
+ if (--count == 0)
+ {
+ error_msg("Can't create new dump dir in '%s'", base_dir);
+ return NULL;
+ }
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ dst_dir_name = xasprintf("%s/%s.%u", base_dir, base_name, (int)tv.tv_usec);
+ }
+
+ VERB1 log("Creating copy in '%s'", dd_dst->dd_dirname);
+ if (copy_file_recursive(dump_dir_name, dd_dst->dd_dirname) < 0)
+ {
+ /* error. copy_file_recursive already emitted error message */
+ /* Don't leave half-copied dir lying around */
+ dd_delete(dd_dst);
+ return NULL;
+ }
+
+ return dd_dst;
+}
diff --git a/src/lib/strbuf.c b/src/lib/strbuf.c
index 04a35998..f56815a0 100644
--- a/src/lib/strbuf.c
+++ b/src/lib/strbuf.c
@@ -37,7 +37,7 @@ int suffixcmp(const char *str, const char *suffix)
return strcmp(str + len_minus_suflen, suffix);
}
-struct strbuf *strbuf_new()
+struct strbuf *strbuf_new(void)
{
struct strbuf *buf = xzalloc(sizeof(*buf));
/*buf->len = 0; - done by xzalloc */
diff --git a/src/lib/strbuf.h b/src/lib/strbuf.h
index dc45a199..44c6599a 100644
--- a/src/lib/strbuf.h
+++ b/src/lib/strbuf.h
@@ -39,13 +39,15 @@ struct strbuf
* It never returns NULL. The returned pointer must be released by
* calling the function strbuf_free().
*/
-struct strbuf *strbuf_new();
+#define strbuf_new abrt_strbuf_new
+struct strbuf *strbuf_new(void);
/**
* Releases the memory held by the string buffer.
* @param strbuf
* If the strbuf is NULL, no operation is performed.
*/
+#define strbuf_free abrt_strbuf_free
void strbuf_free(struct strbuf *strbuf);
/**
@@ -53,24 +55,28 @@ void strbuf_free(struct strbuf *strbuf);
* string buffer is returned. Caller is responsible to release the
* returned memory using free().
*/
+#define strbuf_free_nobuf abrt_strbuf_free_nobuf
char* strbuf_free_nobuf(struct strbuf *strbuf);
/**
* The string content is set to an empty string, erasing any previous
* content and leaving its length at 0 characters.
*/
+#define strbuf_clear abrt_strbuf_clear
void strbuf_clear(struct strbuf *strbuf);
/**
* The current content of the string buffer is extended by adding a
* character c at its end.
*/
+#define strbuf_append_char abrt_strbuf_append_char
struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c);
/**
* The current content of the string buffer is extended by adding a
* string str at its end.
*/
+#define strbuf_append_str abrt_strbuf_append_str
struct strbuf *strbuf_append_str(struct strbuf *strbuf,
const char *str);
@@ -78,6 +84,7 @@ struct strbuf *strbuf_append_str(struct strbuf *strbuf,
* The current content of the string buffer is extended by inserting a
* string str at its beginning.
*/
+#define strbuf_prepend_str abrt_strbuf_prepend_str
struct strbuf *strbuf_prepend_str(struct strbuf *strbuf,
const char *str);
@@ -85,6 +92,7 @@ struct strbuf *strbuf_prepend_str(struct strbuf *strbuf,
* The current content of the string buffer is extended by adding a
* sequence of data formatted as the format argument specifies.
*/
+#define strbuf_append_strf abrt_strbuf_append_strf
struct strbuf *strbuf_append_strf(struct strbuf *strbuf,
const char *format, ...);
@@ -93,6 +101,7 @@ struct strbuf *strbuf_append_strf(struct strbuf *strbuf,
* sequence of data formatted as the format argument specifies at the
* buffer beginning.
*/
+#define strbuf_prepend_strf abrt_strbuf_prepend_strf
struct strbuf *strbuf_prepend_strf(struct strbuf *strbuf,
const char *format, ...);
diff --git a/src/lib/stringops.cpp b/src/lib/stringops.cpp
deleted file mode 100644
index 7bc5413f..00000000
--- a/src/lib/stringops.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- Copyright (C) 2010 ABRT team
- Copyright (C) 2010 RedHat Inc
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include "abrtlib.h"
-
-void parse_args(const char *psArgs, vector_string_t& pArgs, int quote)
-{
- unsigned ii;
- bool inside_quotes = false;
- std::string item;
-
- for (ii = 0; psArgs[ii]; ii++)
- {
- if (quote != -1)
- {
- if (psArgs[ii] == quote)
- {
- inside_quotes = !inside_quotes;
- continue;
- }
- /* inside quotes we support escaping with \x */
- if (inside_quotes && psArgs[ii] == '\\' && psArgs[ii+1])
- {
- ii++;
- item += psArgs[ii];
- continue;
- }
- }
- if (psArgs[ii] == ',' && !inside_quotes)
- {
- pArgs.push_back(item);
- item.clear();
- continue;
- }
- item += psArgs[ii];
- }
-
- if (item.size() != 0)
- {
- pArgs.push_back(item);
- }
-}
diff --git a/src/lib/xatonum.c b/src/lib/xatonum.c
index 3b22071f..1a92db7f 100644
--- a/src/lib/xatonum.c
+++ b/src/lib/xatonum.c
@@ -28,7 +28,7 @@ inval:
error_msg_and_die("invalid number '%s'", numstr);
}
-int xatoi_u(const char *numstr)
+int xatoi_positive(const char *numstr)
{
unsigned r = xatou(numstr);
if (r > (unsigned)INT_MAX)
@@ -41,7 +41,7 @@ int xatoi(const char *numstr)
unsigned r;
if (*numstr != '-')
- return xatoi_u(numstr);
+ return xatoi_positive(numstr);
r = xatou(numstr + 1);
if (r > (unsigned)INT_MAX + 1)
diff --git a/src/lib/xfuncs.c b/src/lib/xfuncs.c
index d5166037..f451693a 100644
--- a/src/lib/xfuncs.c
+++ b/src/lib/xfuncs.c
@@ -26,12 +26,18 @@
/* Turn on nonblocking I/O on a fd */
int ndelay_on(int fd)
{
- return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+ int flags = fcntl(fd, F_GETFL);
+ if (flags & O_NONBLOCK)
+ return 0;
+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
int ndelay_off(int fd)
{
- return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
+ int flags = fcntl(fd, F_GETFL);
+ if (!(flags & O_NONBLOCK))
+ return 0;
+ return fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
int close_on_exec_on(int fd)
@@ -39,16 +45,6 @@ int close_on_exec_on(int fd)
return fcntl(fd, F_SETFD, FD_CLOEXEC);
}
-#if 0 /* unused */
-void *xcalloc(size_t nmemb, size_t size)
-{
- void *ptr = calloc(nmemb, size);
- if (!ptr && nmemb && size)
- die_out_of_memory();
- return ptr;
-}
-#endif
-
// Die if we can't allocate size bytes of memory.
void* xmalloc(size_t size)
{
@@ -290,7 +286,7 @@ int xopen(const char *pathname, int flags)
void xunlink(const char *pathname)
{
if (unlink(pathname))
- perror_msg_and_die("can't remove file '%s'", pathname);
+ perror_msg_and_die("Can't remove file '%s'", pathname);
}
#if 0 //UNUSED
diff --git a/src/plugins/CCpp.cpp b/src/plugins/CCpp.cpp
deleted file mode 100644
index e6807ea7..00000000
--- a/src/plugins/CCpp.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- CCpp.cpp
-
- Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include <set>
-#include "abrtlib.h"
-#include "CCpp.h"
-#include "abrt_exception.h"
-#include "comm_layer_inner.h"
-
-using namespace std;
-
-#define CORE_PATTERN_IFACE "/proc/sys/kernel/core_pattern"
-#define CORE_PATTERN "|"CCPP_HOOK_PATH" "DEBUG_DUMPS_DIR" %p %s %u %c"
-#define CORE_PIPE_LIMIT_IFACE "/proc/sys/kernel/core_pipe_limit"
-/* core_pipe_limit specifies how many dump_helpers might run at the same time
- * 0 - means unlimited, but it's not guaranteed that /proc/<pid> of crashing
- * process will be available for dump_helper
- * 4 - means that 4 dump_helpers can run at the same time (the rest will also
- * run, but they will fail to read /proc/<pid>)
- * This should be enough for ABRT, we can miss some crashes, but what are
- * the odds that more processes crash at the same time?
- * The value of 4 has been recommended by nhorman.
- */
-#define CORE_PIPE_LIMIT "4"
-
-#define DEBUGINFO_CACHE_DIR LOCALSTATEDIR"/cache/abrt-di"
-
-CAnalyzerCCpp::CAnalyzerCCpp() :
- m_bBacktrace(true),
- m_bBacktraceRemotes(false),
- m_bMemoryMap(false),
- m_bInstallDebugInfo(true),
- m_nDebugInfoCacheMB(4000),
- m_nGdbTimeoutSec(60)
-{}
-
-/*
- this is just a workaround until kernel changes it's behavior
- when handling pipes in core_pattern
-*/
-#ifdef HOSTILE_KERNEL
-#define CORE_SIZE_PATTERN "Max core file size=1:unlimited"
-static int isdigit_str(char *str)
-{
- do {
- if (*str < '0' || *str > '9')
- return 0;
- } while (*++str);
- return 1;
-}
-
-static int set_limits()
-{
- DIR *dir = opendir("/proc");
- if (!dir) {
- /* this shouldn't fail, but to be safe.. */
- return 1;
- }
-
- struct dirent *ent;
- while ((ent = readdir(dir)) != NULL) {
- if (!isdigit_str(ent->d_name))
- continue;
-
- char limits_name[sizeof("/proc/%s/limits") + sizeof(long)*3];
- snprintf(limits_name, sizeof(limits_name), "/proc/%s/limits", ent->d_name);
- FILE *limits_fp = fopen(limits_name, "r");
- if (!limits_fp) {
- break;
- }
-
- char line[128];
- char *ulimit_c = NULL;
- while (1) {
- if (fgets(line, sizeof(line)-1, limits_fp) == NULL)
- break;
- if (strncmp(line, "Max core file size", sizeof("Max core file size")-1) == 0) {
- ulimit_c = skip_whitespace(line + sizeof("Max core file size")-1);
- skip_non_whitespace(ulimit_c)[0] = '\0';
- break;
- }
- }
- fclose(limits_fp);
- if (!ulimit_c || ulimit_c[0] != '0' || ulimit_c[1] != '\0') {
- /*process has nonzero ulimit -c, so need to modify it*/
- continue;
- }
- /* echo -n 'Max core file size=1:unlimited' >/proc/PID/limits */
- int fd = open(limits_name, O_WRONLY);
- if (fd >= 0) {
- errno = 0;
- /*full_*/
- ssize_t n = write(fd, CORE_SIZE_PATTERN, sizeof(CORE_SIZE_PATTERN)-1);
- if (n < sizeof(CORE_SIZE_PATTERN)-1)
- log("warning: can't write core_size limit to: %s", limits_name);
- close(fd);
- }
- else
- {
- log("warning: can't open %s for writing", limits_name);
- }
- }
- closedir(dir);
- return 0;
-}
-#endif /* HOSTILE_KERNEL */
-
-void CAnalyzerCCpp::Init()
-{
- FILE *fp = fopen(CORE_PATTERN_IFACE, "r");
- if (fp)
- {
- char line[PATH_MAX];
- if (fgets(line, sizeof(line), fp))
- m_sOldCorePattern = line;
- fclose(fp);
- }
- if (m_sOldCorePattern[0] == '|')
- {
- if (m_sOldCorePattern == CORE_PATTERN)
- {
- log("warning: %s already contains %s, "
- "did abrt daemon crash recently?",
- CORE_PATTERN_IFACE, CORE_PATTERN);
- /* There is no point in "restoring" CORE_PATTERN_IFACE
- * to CORE_PATTERN on exit. Will restore to a default value:
- */
- m_sOldCorePattern = "core";
- } else {
- log("warning: %s was already set to run a crash analyser (%s), "
- "abrt may interfere with it",
- CORE_PATTERN_IFACE, CORE_PATTERN);
- }
- }
-#ifdef HOSTILE_KERNEL
- if (set_limits() != 0)
- log("warning: failed to set core_size limit, ABRT won't detect crashes in"
- "compiled apps");
-#endif
-
- fp = fopen(CORE_PATTERN_IFACE, "w");
- if (fp)
- {
- fputs(CORE_PATTERN, fp);
- fclose(fp);
- }
-
- /* read the core_pipe_limit and change it if it's == 0
- otherwise the abrt-hook-ccpp won't be able to read /proc/<pid>
- of the crashing process
- */
- fp = fopen(CORE_PIPE_LIMIT_IFACE, "r");
- if (fp)
- {
- /* we care only about the first char, if it's
- * not '0' then we don't have to change it,
- * because it means that it's already != 0
- */
- char pipe_limit[2];
- if (!fgets(pipe_limit, sizeof(pipe_limit), fp))
- pipe_limit[0] = '1'; /* not 0 */
- fclose(fp);
- if (pipe_limit[0] == '0')
- {
- fp = fopen(CORE_PIPE_LIMIT_IFACE, "w");
- if (fp)
- {
- fputs(CORE_PIPE_LIMIT, fp);
- fclose(fp);
- }
- else
- {
- log("warning: failed to set core_pipe_limit, ABRT won't detect"
- "crashes in compiled apps if kernel > 2.6.31");
- }
- }
- }
-}
-
-void CAnalyzerCCpp::DeInit()
-{
- /* no need to restore the core_pipe_limit, because it's only used
- when there is s pipe in core_pattern
- */
- FILE *fp = fopen(CORE_PATTERN_IFACE, "w");
- if (fp)
- {
- fputs(m_sOldCorePattern.c_str(), fp);
- fclose(fp);
- }
-}
-
-void CAnalyzerCCpp::SetSettings(const map_plugin_settings_t& pSettings)
-{
- m_pSettings = pSettings;
-
- map_plugin_settings_t::const_iterator end = pSettings.end();
- map_plugin_settings_t::const_iterator it;
- it = pSettings.find("Backtrace");
- if (it != end)
- {
- m_bBacktrace = string_to_bool(it->second.c_str());
- }
- it = pSettings.find("BacktraceRemotes");
- if (it != end)
- {
- m_bBacktraceRemotes = string_to_bool(it->second.c_str());
- }
- it = pSettings.find("MemoryMap");
- if (it != end)
- {
- m_bMemoryMap = string_to_bool(it->second.c_str());
- }
- it = pSettings.find("DebugInfo");
- if (it != end)
- {
- m_sDebugInfo = it->second;
- }
- it = pSettings.find("DebugInfoCacheMB");
- if (it != end)
- {
- m_nDebugInfoCacheMB = xatou(it->second.c_str());
- }
- it = pSettings.find("GdbTimeoutSec");
- if (it != end)
- {
- m_nGdbTimeoutSec = xatoi_u(it->second.c_str());
- }
- it = pSettings.find("InstallDebugInfo");
- if (it == end) //compat, remove after 0.0.11
- it = pSettings.find("InstallDebuginfo");
- if (it != end)
- {
- m_bInstallDebugInfo = string_to_bool(it->second.c_str());
- }
- m_sDebugInfoDirs = DEBUGINFO_CACHE_DIR;
- it = pSettings.find("ReadonlyLocalDebugInfoDirs");
- if (it != end)
- {
- m_sDebugInfoDirs += ':';
- m_sDebugInfoDirs += it->second;
- }
-}
-
-//ok to delete?
-//const map_plugin_settings_t& CAnalyzerCCpp::GetSettings()
-//{
-// m_pSettings["MemoryMap"] = m_bMemoryMap ? "yes" : "no";
-// m_pSettings["DebugInfo"] = m_sDebugInfo;
-// m_pSettings["DebugInfoCacheMB"] = to_string(m_nDebugInfoCacheMB);
-// m_pSettings["InstallDebugInfo"] = m_bInstallDebugInfo ? "yes" : "no";
-//
-// return m_pSettings;
-//}
-
-PLUGIN_INFO(ANALYZER,
- CAnalyzerCCpp,
- "CCpp",
- "0.0.1",
- _("Analyzes crashes in C/C++ programs"),
- "zprikryl@redhat.com",
- "https://fedorahosted.org/abrt/wiki",
- "");
diff --git a/src/plugins/CCpp.h b/src/plugins/CCpp.h
deleted file mode 100644
index e95b4d09..00000000
--- a/src/plugins/CCpp.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- CCpp.h - header file for C/C++ analyzer plugin
- - it can get UUID and memory maps from core files
-
- Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef CCPP_H_
-#define CCPP_H_
-
-#include <string>
-#include "plugin.h"
-#include "analyzer.h"
-
-class CAnalyzerCCpp : public CAnalyzer
-{
- private:
- bool m_bBacktrace;
- bool m_bBacktraceRemotes;
- bool m_bMemoryMap;
- bool m_bInstallDebugInfo;
- unsigned m_nDebugInfoCacheMB;
- unsigned m_nGdbTimeoutSec;
- std::string m_sOldCorePattern;
- std::string m_sDebugInfo;
- std::string m_sDebugInfoDirs;
-
- public:
- CAnalyzerCCpp();
- virtual void Init();
- virtual void DeInit();
- virtual void SetSettings(const map_plugin_settings_t& pSettings);
-};
-
-#endif /* CCPP */
diff --git a/src/plugins/Kerneloops.conf b/src/plugins/Kerneloops.conf
index 67ad07b9..e65e176f 100644
--- a/src/plugins/Kerneloops.conf
+++ b/src/plugins/Kerneloops.conf
@@ -4,10 +4,4 @@ Enabled = yes
# Set to "yes" for compatibility with kerneloops.org tool.
InformAllUsers = yes
-# Kerneloops Scanner configuration
-##################################
-SysLogFile = /var/log/messages
-
-# KerneloopsReporter configuration
-##################################
SubmitURL = http://submit.kerneloops.org/submitoops.php
diff --git a/src/plugins/KerneloopsScanner.cpp b/src/plugins/KerneloopsScanner.cpp
deleted file mode 100644
index 93f37e07..00000000
--- a/src/plugins/KerneloopsScanner.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- Copyright (C) 2010 ABRT team
- Copyright (C) 2010 RedHat Inc
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- Authors:
- Anton Arapov <anton@redhat.com>
- Arjan van de Ven <arjan@linux.intel.com>
-*/
-#include <syslog.h>
-#include <asm/unistd.h> /* __NR_syslog */
-#include <glib.h>
-#include "abrtlib.h"
-#include "abrt_exception.h"
-#include "comm_layer_inner.h"
-#include "KerneloopsSysLog.h"
-#include "KerneloopsScanner.h"
-
-// TODO: https://fedorahosted.org/abrt/ticket/78
-
-static int scan_dmesg(GList **oopsList)
-{
- VERB1 log("Scanning dmesg");
-
- /* syslog(3) - read the last len bytes from the log buffer
- * (non-destructively), but dont read more than was written
- * into the buffer since the last"clear ring buffer" cmd.
- * Returns the number of bytes read.
- */
- char *buffer = (char*)xzalloc(16*1024);
- syscall(__NR_syslog, 3, buffer, 16*1024 - 1); /* always NUL terminated */
- int cnt_FoundOopses = extract_oopses(oopsList, buffer, strlen(buffer));
- free(buffer);
-
- return cnt_FoundOopses;
-}
-
-
-/* "dumpoops" tool uses these two functions too */
-extern "C" {
-
-int scan_syslog_file(GList **oopsList, const char *filename, time_t *last_changed_p)
-{
- VERB1 log("Scanning syslog file '%s'", filename);
-
- char *buffer;
- struct stat statb;
- int fd;
- int cnt_FoundOopses;
- ssize_t sz;
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- return 0;
- statb.st_size = 0; /* paranoia */
- if (fstat(fd, &statb) != 0 || statb.st_size < 1)
- {
- close(fd);
- return 0;
- }
-
- if (last_changed_p != NULL)
- {
- if (*last_changed_p == statb.st_mtime)
- {
- VERB1 log("Syslog file '%s' hasn't changed since last scan, skipping", filename);
- close(fd);
- return 0;
- }
- *last_changed_p = statb.st_mtime;
- }
-
- /*
- * In theory we have a race here, since someone could spew
- * to /var/log/messages before we read it in... we try to
- * deal with it by reading at most 10kbytes extra. If there's
- * more than that.. any oops will be in dmesg anyway.
- * Do not try to allocate an absurd amount of memory; ignore
- * older log messages because they are unlikely to have
- * sufficiently recent data to be useful. 32MB is more
- * than enough; it's not worth looping through more log
- * if the log is larger than that.
- */
- sz = statb.st_size + 10*1024;
- if (statb.st_size > (32*1024*1024 - 10*1024))
- {
- xlseek(fd, statb.st_size - (32*1024*1024 - 10*1024), SEEK_SET);
- sz = 32*1024*1024;
- }
- buffer = (char*)xzalloc(sz);
- sz = full_read(fd, buffer, sz);
- close(fd);
-
- cnt_FoundOopses = 0;
- if (sz > 0)
- cnt_FoundOopses = extract_oopses(oopsList, buffer, sz);
- free(buffer);
-
- return cnt_FoundOopses;
-}
-
-/* returns number of errors */
-int save_oops_to_debug_dump(GList **oopsList)
-{
- unsigned countdown = 16; /* do not report hundreds of oopses */
- unsigned idx = g_list_length(*oopsList);
- time_t t = time(NULL);
- pid_t my_pid = getpid();
-
- VERB1 log("Saving %u oopses as crash dump dirs", idx >= countdown ? countdown-1 : idx);
-
- char *tainted_str = NULL;
- /* once tainted flag is set to 1, only restart can reset the flag to 0 */
- FILE *tainted_fd = fopen("/proc/sys/kernel/tainted", "r");
- if (tainted_fd)
- {
- tainted_str = xmalloc_fgetline(tainted_fd);
- fclose(tainted_fd);
- }
- else
- error_msg("/proc/sys/kernel/tainted does not exist");
-
- int errors = 0;
-
- while (idx != 0 && --countdown != 0)
- {
- char path[sizeof(DEBUG_DUMPS_DIR"/kerneloops-%lu-%lu-%lu") + 3 * sizeof(long)*3];
- sprintf(path, DEBUG_DUMPS_DIR"/kerneloops-%lu-%lu-%lu", (long)t, (long)my_pid, (long)idx);
-
- char *first_line = (char*)g_list_nth_data(*oopsList,--idx);
- char *second_line = (char*)strchr(first_line, '\n'); /* never NULL */
- *second_line++ = '\0';
-
- struct dump_dir *dd = dd_create(path, /*uid:*/ 0);
- if (dd)
- {
- dd_save_text(dd, FILENAME_ANALYZER, "Kerneloops");
- dd_save_text(dd, FILENAME_EXECUTABLE, "kernel");
- dd_save_text(dd, FILENAME_KERNEL, first_line);
- dd_save_text(dd, FILENAME_CMDLINE, "not_applicable");
- dd_save_text(dd, FILENAME_BACKTRACE, second_line);
- /* Optional, makes generated bz more informative */
- strchrnul(second_line, '\n')[0] = '\0';
- dd_save_text(dd, FILENAME_REASON, second_line);
-
- if (tainted_str && tainted_str[0] != '0')
- dd_save_text(dd, FILENAME_TAINTED, tainted_str);
-
- free(tainted_str);
- dd_close(dd);
- }
- else
- errors++;
- }
-
- return errors;
-}
-
-} /* extern "C" */
-
-
-CKerneloopsScanner::CKerneloopsScanner()
-{
- int cnt_FoundOopses;
- m_syslog_last_change = 0;
-
- /* Scan dmesg, on first call only */
- GList *oopsList = NULL;
- cnt_FoundOopses = scan_dmesg(&oopsList);
- if (cnt_FoundOopses > 0)
- {
- int errors = save_oops_to_debug_dump(&oopsList);
- if (errors > 0)
- log("%d errors while dumping oopses", errors);
- }
-}
-
-void CKerneloopsScanner::Run(const char *pActionDir, const char *pArgs, int force)
-{
- const char *syslog_file = "/var/log/messages";
- map_plugin_settings_t::const_iterator it = m_pSettings.find("SysLogFile");
- if (it != m_pSettings.end())
- syslog_file = it->second.c_str();
-
- GList *oopsList = NULL;
- int cnt_FoundOopses = scan_syslog_file(&oopsList, syslog_file, &m_syslog_last_change);
- if (cnt_FoundOopses > 0)
- {
- int errors = save_oops_to_debug_dump(&oopsList);
- if (errors > 0)
- log("%d errors while dumping oopses", errors);
- /*
- * This marker in syslog file prevents us from
- * re-parsing old oopses (any oops before it is
- * ignored by scan_syslog_file()). The only problem
- * is that we can't be sure here that syslog_file
- * is the file where syslog(xxx) stuff ends up.
- */
- openlog("abrt", 0, LOG_KERN);
- syslog(LOG_WARNING,
- "Kerneloops: Reported %u kernel oopses to Abrt",
- cnt_FoundOopses);
- closelog();
- }
-
- for (GList *li = oopsList; li != NULL; li = g_list_next(li))
- free((char*)li->data);
- g_list_free(oopsList);
-}
-
-PLUGIN_INFO(ACTION,
- CKerneloopsScanner,
- "KerneloopsScanner",
- "0.0.1",
- _("Periodically scans for and saves kernel oopses"),
- "anton@redhat.com",
- "http://people.redhat.com/aarapov",
- "");
diff --git a/src/plugins/KerneloopsScanner.h b/src/plugins/KerneloopsScanner.h
deleted file mode 100644
index 2bddb0f4..00000000
--- a/src/plugins/KerneloopsScanner.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2007, Intel Corporation
- * Copyright 2009, Red Hat Inc.
- *
- * This file is part of Abrt.
- *
- * This program file is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program in a file named COPYING; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA
- *
- * Authors:
- * Anton Arapov <anton@redhat.com>
- * Arjan van de Ven <arjan@linux.intel.com>
- */
-#ifndef KERNELOOPSSCANNER_H_
-#define KERNELOOPSSCANNER_H_
-
-#include "abrt_types.h"
-#include "plugin.h"
-#include "action.h"
-
-class CKerneloopsScanner : public CAction
-{
- private:
- time_t m_syslog_last_change;
- public:
- CKerneloopsScanner();
- virtual void Run(const char *pActionDir, const char *pArgs, int force);
-};
-
-#endif
diff --git a/src/plugins/KerneloopsSysLog.cpp b/src/plugins/KerneloopsSysLog.cpp
deleted file mode 100644
index 68f309bc..00000000
--- a/src/plugins/KerneloopsSysLog.cpp
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- Copyright (C) 2010 ABRT team
- Copyright (C) 2010 RedHat Inc
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- Authors:
- Anton Arapov <anton@redhat.com>
- Arjan van de Ven <arjan@linux.intel.com>
- */
-#include "abrtlib.h"
-#include "KerneloopsSysLog.h"
-#include <glib.h>
-
-static void queue_oops(GList **vec, const char *data, const char *version)
-{
- char *ver_data = xasprintf("%s\n%s", version, data);
- *vec = g_list_append(*vec, ver_data);
-}
-
-/*
- * extract_version tries to find the kernel version in given data
- */
-static char *extract_version(const char *linepointer)
-{
- if (strstr(linepointer, "Pid")
- || strstr(linepointer, "comm")
- || strstr(linepointer, "CPU")
- || strstr(linepointer, "REGS")
- || strstr(linepointer, "EFLAGS")
- ) {
- char* start;
- char* end;
-
- start = strstr((char*)linepointer, "2.6.");
- if (start)
- {
- end = strchr(start, ')');
- if (!end)
- end = strchrnul(start, ' ');
- return xstrndup(start, end-start);
- }
- }
-
- return NULL;
-}
-
-/*
- * extract_oops tries to find oops signatures in a log
- */
-struct line_info {
- char *ptr;
- char level;
-};
-
-static int record_oops(GList **oopses, struct line_info* lines_info, int oopsstart, int oopsend)
-{
- int q;
- int len;
- char *oops;
- char *version;
-
- len = 2;
- for (q = oopsstart; q <= oopsend; q++)
- len += strlen(lines_info[q].ptr) + 1;
-
- oops = (char*)xzalloc(len);
-
- version = NULL;
- for (q = oopsstart; q <= oopsend; q++)
- {
- if (!version)
- version = extract_version(lines_info[q].ptr);
-
- if (lines_info[q].ptr[0])
- {
- strcat(oops, lines_info[q].ptr);
- strcat(oops, "\n");
- }
- }
- int rv = 1;
- /* too short oopses are invalid */
- if (strlen(oops) > 100)
- queue_oops(oopses, oops, version ? version : "undefined");
- else
- {
- VERB3 log("Dropped oops: too short");
- rv = 0;
- }
- free(oops);
- free(version);
- return rv;
-}
-#define REALLOC_CHUNK 1000
-int extract_oopses(GList **oopses, char *buffer, size_t buflen)
-{
- char *c;
- int linecount = 0;
- int lines_info_alloc = 0;
- struct line_info *lines_info = NULL;
-
- /* Split buffer into lines */
-
- if (buflen != 0)
- buffer[buflen - 1] = '\n'; /* the buffer usually ends with \n, but let's make sure */
- c = buffer;
- while (c < buffer + buflen)
- {
- char linelevel;
- char *c9;
- char *colon;
-
- c9 = (char*)memchr(c, '\n', buffer + buflen - c); /* a \n will always be found */
- assert(c9);
- *c9 = '\0'; /* turn the \n into a string termination */
- if (c9 == c)
- goto next_line;
-
- /* Is it a syslog file (/var/log/messages or similar)?
- * Even though _usually_ it looks like "Nov 19 12:34:38 localhost kernel: xxx",
- * some users run syslog in non-C locale:
- * "2010-02-22T09:24:08.156534-08:00 gnu-4 gnome-session[2048]: blah blah"
- * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ !!!
- * We detect it by checking for N:NN:NN pattern in first 15 chars
- * (and this still is not good enough... false positive: "pci 0000:15:00.0: PME# disabled")
- */
- colon = strchr(c, ':');
- if (colon && colon > c && colon < c + 15
- && isdigit(colon[-1]) /* N:... */
- && isdigit(colon[1]) /* ...N:NN:... */
- && isdigit(colon[2])
- && colon[3] == ':'
- && isdigit(colon[4]) /* ...N:NN:NN... */
- && isdigit(colon[5])
- ) {
- /* It's syslog file, not a bare dmesg */
-
- /* Skip non-kernel lines */
- char *kernel_str = strstr(c, "kernel: ");
- if (kernel_str == NULL)
- {
- /* if we see our own marker:
- * "hostname abrt: Kerneloops: Reported 1 kernel oopses to Abrt"
- * we know we submitted everything upto here already */
- if (strstr(c, "abrt:") && strstr(c, "Abrt"))
- {
- VERB3 log("Found our marker at line %d, restarting line count from 0", linecount);
- linecount = 0;
- lines_info_alloc = 0;
- free(lines_info);
- lines_info = NULL;
- }
- goto next_line;
- }
- c = kernel_str + sizeof("kernel: ")-1;
- }
-
- linelevel = 0;
- /* store and remove kernel log level */
- if (*c == '<' && c[1] && c[2] == '>')
- {
- linelevel = c[1];
- c += 3;
- }
- /* remove jiffies time stamp counter if present */
- if (*c == '[')
- {
- char *c2 = strchr(c, '.');
- char *c3 = strchr(c, ']');
- if (c2 && c3 && (c2 < c3) && (c3-c) < 14 && (c2-c) < 8)
- {
- c = c3 + 1;
- if (*c == ' ')
- c++;
- }
- }
- if (linecount >= lines_info_alloc)
- {
- lines_info_alloc += REALLOC_CHUNK;
- lines_info = (line_info*)xrealloc(lines_info,
- lines_info_alloc * sizeof(struct line_info));
- }
- lines_info[linecount].ptr = c;
- lines_info[linecount].level = linelevel;
- linecount++;
-next_line:
- c = c9 + 1;
- }
-
- /* Analyze lines */
-
- int i;
- char prevlevel = 0;
- int oopsstart = -1;
- int inbacktrace = 0;
- int oopsesfound = 0;
-
- i = 0;
- while (i < linecount)
- {
- char *curline = lines_info[i].ptr;
-
- if (curline == NULL)
- {
- i++;
- continue;
- }
- while (*curline == ' ')
- curline++;
-
- if (oopsstart < 0)
- {
- /* find start-of-oops markers */
- if (strstr(curline, "general protection fault:"))
- oopsstart = i;
- else if (strstr(curline, "BUG:"))
- oopsstart = i;
- else if (strstr(curline, "kernel BUG at"))
- oopsstart = i;
- else if (strstr(curline, "do_IRQ: stack overflow:"))
- oopsstart = i;
- else if (strstr(curline, "RTNL: assertion failed"))
- oopsstart = i;
- else if (strstr(curline, "Eeek! page_mapcount(page) went negative!"))
- oopsstart = i;
- else if (strstr(curline, "near stack overflow (cur:"))
- oopsstart = i;
- else if (strstr(curline, "double fault:"))
- oopsstart = i;
- else if (strstr(curline, "Badness at"))
- oopsstart = i;
- else if (strstr(curline, "NETDEV WATCHDOG"))
- oopsstart = i;
- else if (strstr(curline, "WARNING: at ")) /* WARN_ON() generated message */
- oopsstart = i;
- else if (strstr(curline, "Unable to handle kernel"))
- oopsstart = i;
- else if (strstr(curline, "sysctl table check failed"))
- oopsstart = i;
- else if (strstr(curline, "INFO: possible recursive locking detected"))
- oopsstart = i;
- // Not needed: "--[ cut here ]--" is always followed
- // by "Badness at", "kernel BUG at", or "WARNING: at" string
- //else if (strstr(curline, "------------[ cut here ]------------"))
- // oopsstart = i;
- else if (strstr(curline, "list_del corruption."))
- oopsstart = i;
- else if (strstr(curline, "list_add corruption."))
- oopsstart = i;
- if (strstr(curline, "Oops:") && i >= 3)
- oopsstart = i-3;
-
- if (oopsstart >= 0)
- {
- /* debug information */
- VERB3 {
- log("Found oops at line %d: '%s'", oopsstart, lines_info[oopsstart].ptr);
- if (oopsstart != i)
- log("Trigger line is %d: '%s'", i, c);
- }
- /* try to find the end marker */
- int i2 = i + 1;
- while (i2 < linecount && i2 < (i+50))
- {
- if (strstr(lines_info[i2].ptr, "---[ end trace"))
- {
- inbacktrace = 1;
- i = i2;
- break;
- }
- i2++;
- }
- }
- }
-
- /* Are we entering a call trace part? */
- /* a call trace starts with "Call Trace:" or with the " [<.......>] function+0xFF/0xAA" pattern */
- if (oopsstart >= 0 && !inbacktrace)
- {
- if (strstr(curline, "Call Trace:"))
- inbacktrace = 1;
- else
- if (strnlen(curline, 9) > 8
- && curline[0] == '[' && curline[1] == '<'
- && strstr(curline, ">]")
- && strstr(curline, "+0x")
- && strstr(curline, "/0x")
- ) {
- inbacktrace = 1;
- }
- }
-
- /* Are we at the end of an oops? */
- else if (oopsstart >= 0 && inbacktrace)
- {
- int oopsend = INT_MAX;
-
- /* line needs to start with " [" or have "] [" if it is still a call trace */
- /* example: "[<ffffffffa006c156>] radeon_get_ring_head+0x16/0x41 [radeon]" */
- if (curline[0] != '['
- && !strstr(curline, "] [")
- && !strstr(curline, "--- Exception")
- && !strstr(curline, "LR =")
- && !strstr(curline, "<#DF>")
- && !strstr(curline, "<IRQ>")
- && !strstr(curline, "<EOI>")
- && !strstr(curline, "<<EOE>>")
- && strncmp(curline, "Code: ", 6) != 0
- && strncmp(curline, "RIP ", 4) != 0
- && strncmp(curline, "RSP ", 4) != 0
- ) {
- oopsend = i-1; /* not a call trace line */
- }
- /* oops lines are always more than 8 chars long */
- else if (strnlen(curline, 8) < 8)
- oopsend = i-1;
- /* single oopses are of the same loglevel */
- else if (lines_info[i].level != prevlevel)
- oopsend = i-1;
- else if (strstr(curline, "Instruction dump:"))
- oopsend = i;
- /* if a new oops starts, this one has ended */
- else if (strstr(curline, "WARNING: at ") && oopsstart != i) /* WARN_ON() generated message */
- oopsend = i-1;
- else if (strstr(curline, "Unable to handle") && oopsstart != i)
- oopsend = i-1;
- /* kernel end-of-oops marker (not including marker itself) */
- else if (strstr(curline, "---[ end trace"))
- oopsend = i-1;
-
- if (oopsend <= i)
- {
- VERB3 log("End of oops at line %d (%d): '%s'", oopsend, i, lines_info[oopsend].ptr);
- if (record_oops(oopses, lines_info, oopsstart, oopsend))
- oopsesfound++;
- oopsstart = -1;
- inbacktrace = 0;
- }
- }
-
- prevlevel = lines_info[i].level;
- i++;
-
- if (oopsstart >= 0)
- {
- /* Do we have a suspiciously long oops? Cancel it */
- if (i-oopsstart > 60)
- {
- inbacktrace = 0;
- oopsstart = -1;
- VERB3 log("Dropped oops, too long");
- continue;
- }
- if (!inbacktrace && i-oopsstart > 40)
- {
- /*inbacktrace = 0; - already is */
- oopsstart = -1;
- VERB3 log("Dropped oops, too long");
- continue;
- }
- }
- } /* while (i < linecount) */
-
- /* process last oops if we have one */
- if (oopsstart >= 0 && inbacktrace)
- {
- int oopsend = i-1;
- VERB3 log("End of oops at line %d (end of file): '%s'", oopsend, lines_info[oopsend].ptr);
- if (record_oops(oopses, lines_info, oopsstart, oopsend))
- oopsesfound++;
- }
-
- free(lines_info);
- return oopsesfound;
-}
diff --git a/src/plugins/KerneloopsSysLog.h b/src/plugins/KerneloopsSysLog.h
deleted file mode 100644
index d8b4d32b..00000000
--- a/src/plugins/KerneloopsSysLog.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2007, Intel Corporation
- * Copyright 2009, Red Hat Inc.
- *
- * This file is part of Abrt.
- *
- * This program file is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program in a file named COPYING; if not, write to the
- * Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA
- *
- * Authors:
- * Anton Arapov <anton@redhat.com>
- * Arjan van de Ven <arjan@linux.intel.com>
- */
-
-#ifndef __INCLUDE_GUARD_KERNELOOPSSYSLOG_H_
-#define __INCLUDE_GUARD_KERNELOOPSSYSLOG_H_
-
-#include "abrt_types.h"
-#include <glib.h>
-
-int extract_oopses(GList **oopses, char *buffer, size_t buflen);
-
-#endif
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 71a2fd6f..207fc860 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -1,13 +1,22 @@
-INC_PATH=$(srcdir)/../include
-UTILS_PATH=$(srcdir)/../lib
-AM_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH)
-pluginslibdir=$(PLUGINS_LIB_DIR)
-libexec_SCRIPTS = \
+pluginslibdir = $(PLUGINS_LIB_DIR)
+
+bin_SCRIPTS = \
abrt-action-install-debuginfo.py
-pluginslib_LTLIBRARIES = \
- libCCpp.la \
- libKerneloopsScanner.la
+bin_PROGRAMS = \
+ abrt-dump-oops \
+ abrt-action-analyze-c \
+ abrt-action-analyze-python \
+ abrt-action-analyze-oops \
+ abrt-action-generate-backtrace \
+ abrt-action-bugzilla \
+ abrt-action-rhtsupport \
+ abrt-action-kerneloops \
+ abrt-action-upload \
+ abrt-action-mailx \
+ abrt-action-print \
+ abrt-action-install-debuginfo \
+ abrt-retrace-client
dist_pluginslib_DATA = \
Logger.glade \
@@ -18,6 +27,7 @@ dist_pluginslib_DATA = \
KerneloopsReporter.glade
pluginsconfdir = $(PLUGINS_CONF_DIR)
+
dist_pluginsconf_DATA = \
CCpp.conf \
Python.conf \
@@ -28,9 +38,13 @@ dist_pluginsconf_DATA = \
RHTSupport.conf \
Upload.conf
+eventsconfdir = $(EVENTS_CONF_DIR)
+
+dist_eventsconf_DATA = \
+ ccpp_events.conf
+
man_MANS = \
abrt-Bugzilla.7 \
- abrt-KerneloopsScanner.7 \
abrt-KerneloopsReporter.7 \
abrt-Logger.7 \
abrt-Mailx.7 \
@@ -48,39 +62,29 @@ install-data-hook: $(DESTDIR)/$(DEBUG_INFO_DIR)
sed 's: = /var/: = $(localstatedir)/:g' -i \
$(DESTDIR)$(sysconfdir)/abrt/plugins/Logger.conf
-# CCpp
-libCCpp_la_SOURCES = CCpp.cpp CCpp.h
-libCCpp_la_LDFLAGS = -avoid-version
-libCCpp_la_CPPFLAGS = -Wall -Werror \
- -I$(INC_PATH) \
- -I$(UTILS_PATH) \
- -DCCPP_HOOK_PATH=\"${libexecdir}/abrt-hook-ccpp\" \
+abrt_dump_oops_SOURCES = \
+ abrt-dump-oops.c
+abrt_dump_oops_CPPFLAGS = \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
-DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
- -DLOCALSTATEDIR='"$(localstatedir)"'
-# -DHOSTILE_KERNEL
-
-# KerneloopsScanner
-libKerneloopsScanner_la_SOURCES = KerneloopsScanner.cpp KerneloopsScanner.h KerneloopsSysLog.cpp KerneloopsSysLog.h
-libKerneloopsScanner_la_LDFLAGS = -avoid-version $(GLIB_LIBS)
-libKerneloopsScanner_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" $(GLIB_CFLAGS)
-
-libexec_PROGRAMS = \
- abrt-action-analyze-c \
- abrt-action-analyze-python \
- abrt-action-analyze-oops \
- abrt-action-generate-backtrace \
- abrt-action-bugzilla \
- abrt-action-rhtsupport \
- abrt-action-kerneloops \
- abrt-action-upload \
- abrt-action-mailx \
- abrt-action-print \
- abrt-retrace-client
+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Werror
+abrt_dump_oops_LDADD = \
+ ../lib/libreport.la
abrt_action_analyze_c_SOURCES = \
abrt-action-analyze-c.c
abrt_action_analyze_c_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -90,15 +94,16 @@ abrt_action_analyze_c_CPPFLAGS = \
-DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
-D_GNU_SOURCE \
-Wall -Werror
abrt_action_analyze_c_LDADD = \
- ../lib/libabrt.la
+ ../lib/libreport.la
abrt_action_analyze_python_SOURCES = \
abrt-action-analyze-python.c
abrt_action_analyze_python_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -108,15 +113,16 @@ abrt_action_analyze_python_CPPFLAGS = \
-DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
-D_GNU_SOURCE \
-Wall -Werror
abrt_action_analyze_python_LDADD = \
- ../lib/libabrt.la
+ ../lib/libreport.la
abrt_action_analyze_oops_SOURCES = \
abrt-action-analyze-oops.c
abrt_action_analyze_oops_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -126,15 +132,16 @@ abrt_action_analyze_oops_CPPFLAGS = \
-DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
-D_GNU_SOURCE \
-Wall -Werror
abrt_action_analyze_oops_LDADD = \
- ../lib/libabrt.la
+ ../lib/libreport.la
abrt_action_generate_backtrace_SOURCES = \
abrt-action-generate-backtrace.c
abrt_action_generate_backtrace_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -144,16 +151,17 @@ abrt_action_generate_backtrace_CPPFLAGS = \
-DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
-D_GNU_SOURCE \
-Wall -Werror
abrt_action_generate_backtrace_LDADD = \
- ../lib/libabrt.la \
+ ../lib/libreport.la \
../btparser/libbtparser.la
abrt_action_bugzilla_SOURCES = \
abrt-action-bugzilla.cpp
abrt_action_bugzilla_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -169,13 +177,13 @@ abrt_action_bugzilla_CPPFLAGS = \
abrt_action_bugzilla_LDADD = \
$(GLIB_LIBS) \
../lib/libabrt_web.la \
- ../lib/libabrt.la
+ ../lib/libreport.la
abrt_action_rhtsupport_SOURCES = \
abrt_rh_support.h abrt_rh_support.c \
- abrt-action-rhtsupport.cpp
+ abrt-action-rhtsupport.c
abrt_action_rhtsupport_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -193,12 +201,12 @@ abrt_action_rhtsupport_LDADD = \
$(GLIB_LIBS) \
$(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) \
../lib/libabrt_web.la \
- ../lib/libabrt.la
+ ../lib/libreport.la
abrt_action_upload_SOURCES = \
- abrt-action-upload.cpp
+ abrt-action-upload.c
abrt_action_upload_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -213,16 +221,15 @@ abrt_action_upload_CPPFLAGS = \
-D_GNU_SOURCE \
-Wall -Werror
abrt_action_upload_LDFLAGS = -ltar
-# Needs libABRTdUtils only for LoadPluginSettings
abrt_action_upload_LDADD = \
$(GLIB_LIBS) \
$(CURL_LIBS) \
- ../lib/libabrt.la
+ ../lib/libreport.la
abrt_action_kerneloops_SOURCES = \
- abrt-action-kerneloops.cpp
+ abrt-action-kerneloops.c
abrt_action_kerneloops_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -235,15 +242,14 @@ abrt_action_kerneloops_CPPFLAGS = \
$(GLIB_CFLAGS) \
-D_GNU_SOURCE \
-Wall -Werror
-# libABRTdUtils is used only because of LoadPluginSettings:
abrt_action_kerneloops_LDADD = \
../lib/libabrt_web.la \
- ../lib/libabrt.la
+ ../lib/libreport.la
abrt_action_mailx_SOURCES = \
- abrt-action-mailx.cpp
+ abrt-action-mailx.c
abrt_action_mailx_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -257,12 +263,12 @@ abrt_action_mailx_CPPFLAGS = \
-D_GNU_SOURCE \
-Wall -Werror
abrt_action_mailx_LDADD = \
- ../lib/libabrt.la
+ ../lib/libreport.la
abrt_action_print_SOURCES = \
- abrt-action-print.cpp
+ abrt-action-print.c
abrt_action_print_CPPFLAGS = \
- -I$(srcdir)/../include \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
-I$(srcdir)/../lib \
-DBIN_DIR=\"$(bindir)\" \
-DVAR_RUN=\"$(VAR_RUN)\" \
@@ -275,9 +281,17 @@ abrt_action_print_CPPFLAGS = \
$(GLIB_CFLAGS) \
-D_GNU_SOURCE \
-Wall -Werror
-# libABRTdUtils is used only because of make_description_logger:
abrt_action_print_LDADD = \
- ../lib/libabrt.la
+ ../lib/libreport.la
+
+abrt_action_install_debuginfo_SOURCES = \
+ abrt-action-install-debuginfo.c
+abrt_action_install_debuginfo_CPPFLAGS = \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -D_GNU_SOURCE \
+ -Wall
+abrt_action_install_debuginfo_LDADD =
abrt_retrace_client_SOURCES = \
abrt-retrace-client.c
diff --git a/src/plugins/abrt-KerneloopsScanner.7 b/src/plugins/abrt-KerneloopsScanner.7
deleted file mode 100644
index ff094847..00000000
--- a/src/plugins/abrt-KerneloopsScanner.7
+++ /dev/null
@@ -1,46 +0,0 @@
-.TH abrt "7" "1 Jun 2009" ""
-.SH NAME
-KerneloopsScanner plugin for abrt(8)
-.SH DESCRIPTION
-.P
-.I abrt
-is a daemon that watches for application crashes. When a crash occurs,
-it collects the crash data and takes action according to
-its configuration. This manual page describes the \fIKerneloopsScanner\fP
-plugin for \fIabrt\fP.
-.P
-This plugin reads the system log file (default /var/log/messages)
-and stores the kernel oops crashes, which were not already
-reported, to abrt's debug dump directory.
-.P
-To distinguish between new crashes and crashes
-that were already reported, the plugin makes its own entry
-in the log file, which acts as a separator.
-.SH INVOCATION
-The plugin is invoked in the \fIabrt.conf\fP configuration file.
-No parameters are necessary.
-.SH CONFIGURATION
-The \fIKerneloopsScanner.conf\fP configuration file contains one entry:
-.SS SysLogFile
-The file to scan. The default is
-.br
-SysLogFile = /var/log/messages
-.SH EXAMPLES
-.P
-This is a snippet from the \fIabrt.conf\fP configuration file.
-Every 10 seconds look if there were any kernel crashes:
-.P
-[common]
-.br
-ActionsAndReporters = Kerneloops, KerneloopsScanner
-.br
-[cron]
-.br
-10 = KerneloopsScanner
-.SH "SEE ALSO"
-.IR abrt (8),
-.IR abrt.conf (5),
-.IR abrt-plugins (7)
-.SH AUTHOR
-Written by Anton Arapov <anton@redhat.com>. Manual
-page by Daniel Novotny <dnovotny@redhat.com>.
diff --git a/src/plugins/abrt-action-analyze-c.c b/src/plugins/abrt-action-analyze-c.c
index 6d8ac1b4..5def9aa1 100644
--- a/src/plugins/abrt-action-analyze-c.c
+++ b/src/plugins/abrt-action-analyze-c.c
@@ -52,7 +52,7 @@ static char *run_unstrip_n(const char *dump_dir_name, unsigned timeout_sec)
return NULL;
char *uid_str = dd_load_text(dd, FILENAME_UID);
dd_close(dd);
- unsigned uid = xatoi_u(uid_str);
+ unsigned uid = xatoi_positive(uid_str);
free(uid_str);
int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETGUID | EXECFLG_SETSID | EXECFLG_QUIET;
@@ -104,7 +104,7 @@ static char *run_unstrip_n(const char *dump_dir_name, unsigned timeout_sec)
if (status != 0)
{
- /* unstrip didnt exit with exitcode 0 */
+ /* unstrip didnt exit with exit code 0 */
strbuf_free(buf_out);
return NULL;
}
@@ -149,28 +149,28 @@ int main(int argc, char **argv)
if (env_verbose)
g_verbose = atoi(env_verbose);
+ const char *dump_dir_name = ".";
+
/* Can't keep these strings/structs static: _() doesn't support that */
const char *program_usage_string = _(
- PROGNAME" [-vs] -d DIR\n\n"
+ PROGNAME" [-v] -d DIR\n\n"
"Calculates and saves UUID of coredumps"
- );
- const char *dump_dir_name = ".";
+ );
enum {
OPT_v = 1 << 0,
OPT_d = 1 << 1,
- OPT_s = 1 << 2,
+// OPT_s = 1 << 2,
};
/* Keep enum above and order of options below in sync! */
struct options program_options[] = {
OPT__VERBOSE(&g_verbose),
OPT_STRING('d', NULL, &dump_dir_name, "DIR", _("Crash dump directory")),
- OPT_BOOL( 's', NULL, NULL, _("Log to syslog" )),
+// OPT_BOOL( 's', NULL, NULL, _("Log to syslog" )),
OPT_END()
};
/*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
-
msg_prefix = PROGNAME;
//Maybe we will want this... later
// if (opts & OPT_s)
diff --git a/src/plugins/abrt-action-analyze-oops.c b/src/plugins/abrt-action-analyze-oops.c
index 24538641..0072c71d 100644
--- a/src/plugins/abrt-action-analyze-oops.c
+++ b/src/plugins/abrt-action-analyze-oops.c
@@ -126,28 +126,28 @@ int main(int argc, char **argv)
if (env_verbose)
g_verbose = atoi(env_verbose);
+ const char *dump_dir_name = ".";
+
/* Can't keep these strings/structs static: _() doesn't support that */
const char *program_usage_string = _(
PROGNAME" [-vs] -d DIR\n\n"
"Calculates and saves UUID and DUPHASH of oops crash dumps"
);
- const char *dump_dir_name = ".";
enum {
OPT_v = 1 << 0,
OPT_d = 1 << 1,
- OPT_s = 1 << 2,
+// OPT_s = 1 << 2,
};
/* Keep enum above and order of options below in sync! */
struct options program_options[] = {
OPT__VERBOSE(&g_verbose),
OPT_STRING('d', NULL, &dump_dir_name, "DIR", _("Crash dump directory")),
- OPT_BOOL( 's', NULL, NULL, _("Log to syslog" )),
+// OPT_BOOL( 's', NULL, NULL, _("Log to syslog" )),
OPT_END()
};
/*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
-
msg_prefix = PROGNAME;
//Maybe we will want this... later
// if (opts & OPT_s)
diff --git a/src/plugins/abrt-action-analyze-python.c b/src/plugins/abrt-action-analyze-python.c
index 9fe563db..feffb439 100644
--- a/src/plugins/abrt-action-analyze-python.c
+++ b/src/plugins/abrt-action-analyze-python.c
@@ -31,28 +31,28 @@ int main(int argc, char **argv)
if (env_verbose)
g_verbose = atoi(env_verbose);
+ const char *dump_dir_name = ".";
+
/* Can't keep these strings/structs static: _() doesn't support that */
const char *program_usage_string = _(
- PROGNAME" [-vs] -d DIR\n\n"
+ PROGNAME" [-v] -d DIR\n\n"
"Calculates and saves UUID and DUPHASH of python crash dumps"
- );
- const char *dump_dir_name = ".";
+ );
enum {
OPT_v = 1 << 0,
OPT_d = 1 << 1,
- OPT_s = 1 << 2,
+// OPT_s = 1 << 2,
};
/* Keep enum above and order of options below in sync! */
struct options program_options[] = {
OPT__VERBOSE(&g_verbose),
OPT_STRING('d', NULL, &dump_dir_name, "DIR", _("Crash dump directory")),
- OPT_BOOL( 's', NULL, NULL, _("Log to syslog" )),
+// OPT_BOOL( 's', NULL, NULL, _("Log to syslog" )),
OPT_END()
};
/*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
-
msg_prefix = PROGNAME;
//Maybe we will want this... later
// if (opts & OPT_s)
diff --git a/src/plugins/abrt-action-bugzilla.cpp b/src/plugins/abrt-action-bugzilla.cpp
index b396e453..2aa32b75 100644
--- a/src/plugins/abrt-action-bugzilla.cpp
+++ b/src/plugins/abrt-action-bugzilla.cpp
@@ -18,8 +18,10 @@
*/
#include "abrtlib.h"
#include "abrt_xmlrpc.h"
-#include "abrt_crash_dump.h"
-#include "abrt_exception.h"
+#include "abrt_crash_data.h"
+#include "parse_options.h"
+
+#define PROGNAME "abrt-action-bugzilla"
#define XML_RPC_SUFFIX "/xmlrpc.cgi"
#define MAX_HOPS 5
@@ -112,8 +114,8 @@ struct ctx: public abrt_xmlrpc_conn {
xmlrpc_int32 get_bug_dup_id(xmlrpc_value* result_xml);
void get_bug_cc(xmlrpc_value* result_xml, struct bug_info* bz);
int add_plus_one_cc(xmlrpc_int32 bug_id, const char* login);
- xmlrpc_int32 new_bug(const map_crash_data_t& pCrashData, int depend_on_bugno);
- int add_attachments(const char* bug_id_str, const map_crash_data_t& pCrashData);
+ xmlrpc_int32 new_bug(crash_data_t *crash_data, int depend_on_bugno);
+ int add_attachments(const char* bug_id_str, crash_data_t *crash_data);
int get_bug_info(struct bug_info* bz, xmlrpc_int32 bug_id);
int add_comment(xmlrpc_int32 bug_id, const char* comment, bool is_private);
@@ -326,7 +328,7 @@ xmlrpc_value* ctx::call_quicksearch_duphash(const char* component, const char* r
{
char *product = NULL;
char *version = NULL;
- parse_release(release, &product, &version);
+ parse_release_for_bz(release, &product, &version);
query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\" product:\"%s\"",
component, duphash, product
);
@@ -442,17 +444,17 @@ static const char *tainted_string(unsigned tainted)
return taint_warnings[idx];
}
-xmlrpc_int32 ctx::new_bug(const map_crash_data_t& pCrashData, int depend_on_bugno)
+xmlrpc_int32 ctx::new_bug(crash_data_t *crash_data, int depend_on_bugno)
{
- const char *package = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_PACKAGE);
- const char *component = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_COMPONENT);
- const char *release = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_RELEASE);
- const char *arch = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_ARCHITECTURE);
- const char *duphash = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_DUPHASH);
- const char *reason = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_REASON);
- const char *function = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_CRASH_FUNCTION);
- const char *analyzer = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_ANALYZER);
- const char *tainted_str = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_TAINTED);
+ const char *package = get_crash_item_content_or_NULL(crash_data, FILENAME_PACKAGE);
+ const char *component = get_crash_item_content_or_NULL(crash_data, FILENAME_COMPONENT);
+ const char *release = get_crash_item_content_or_NULL(crash_data, FILENAME_OS_RELEASE);
+ const char *arch = get_crash_item_content_or_NULL(crash_data, FILENAME_ARCHITECTURE);
+ const char *duphash = get_crash_item_content_or_NULL(crash_data, FILENAME_DUPHASH);
+ const char *reason = get_crash_item_content_or_NULL(crash_data, FILENAME_REASON);
+ const char *function = get_crash_item_content_or_NULL(crash_data, FILENAME_CRASH_FUNCTION);
+ const char *analyzer = get_crash_item_content_or_NULL(crash_data, FILENAME_ANALYZER);
+ const char *tainted_str = get_crash_item_content_or_NULL(crash_data, FILENAME_TAINTED);
struct strbuf *buf_summary = strbuf_new();
strbuf_append_strf(buf_summary, "[abrt] %s", package);
@@ -466,7 +468,7 @@ xmlrpc_int32 ctx::new_bug(const map_crash_data_t& pCrashData, int depend_on_bugn
if (tainted_str && analyzer
&& (strcmp(analyzer, "Kerneloops") == 0)
) {
- unsigned long tainted = xatoi_u(tainted_str);
+ unsigned long tainted = xatoi_positive(tainted_str);
const char *tainted_warning = tainted_string(tainted);
if (tainted_warning)
strbuf_append_strf(buf_summary, ": TAINTED %s", tainted_warning);
@@ -474,13 +476,13 @@ xmlrpc_int32 ctx::new_bug(const map_crash_data_t& pCrashData, int depend_on_bugn
char *status_whiteboard = xasprintf("abrt_hash:%s", duphash);
- char *bz_dsc = make_description_bz(pCrashData);
+ char *bz_dsc = make_description_bz(crash_data);
char *full_dsc = xasprintf("abrt version: "VERSION"\n%s", bz_dsc);
free(bz_dsc);
char *product = NULL;
char *version = NULL;
- parse_release(release, &product, &version);
+ parse_release_for_bz(release, &product, &version);
xmlrpc_value* result = NULL;
char *summary = strbuf_free_nobuf(buf_summary);
@@ -534,23 +536,24 @@ xmlrpc_int32 ctx::new_bug(const map_crash_data_t& pCrashData, int depend_on_bugn
return bug_id;
}
-int ctx::add_attachments(const char* bug_id_str, const map_crash_data_t& pCrashData)
+int ctx::add_attachments(const char* bug_id_str, crash_data_t *crash_data)
{
- map_crash_data_t::const_iterator it = pCrashData.begin();
- for (; it != pCrashData.end(); it++)
+ GHashTableIter iter;
+ char *name;
+ struct crash_item *value;
+ g_hash_table_iter_init(&iter, crash_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
{
- const char *itemname = it->first.c_str();
- const char *type = it->second[CD_TYPE].c_str();
- const char *content = it->second[CD_CONTENT].c_str();
+ const char *content = value->content;
- if ((strcmp(type, CD_TXT) == 0)
- && (strlen(content) > CD_TEXT_ATT_SIZE || (strcmp(itemname, FILENAME_BACKTRACE) == 0))
+ if ((value->flags & CD_FLAG_TXT)
+ && (strlen(content) > CD_TEXT_ATT_SIZE || (strcmp(name, FILENAME_BACKTRACE) == 0))
) {
char *encoded64 = encode_base64(content, strlen(content));
- char *filename = xasprintf("File: %s", itemname);
+ char *filename = xasprintf("File: %s", name);
xmlrpc_value* result = call("bugzilla.addAttachment", "(s{s:s,s:s,s:s,s:s})", bug_id_str,
"description", filename,
- "filename", itemname,
+ "filename", name,
"contenttype", "text/plain",
"data", encoded64
);
@@ -567,7 +570,9 @@ int ctx::add_attachments(const char* bug_id_str, const map_crash_data_t& pCrashD
int ctx::get_bug_info(struct bug_info* bz, xmlrpc_int32 bug_id)
{
- xmlrpc_value* result = call("bugzilla.getBug", "(s)", to_string(bug_id).c_str());
+ char bug_id_str[sizeof(long)*3 + 2];
+ sprintf(bug_id_str, "%lu", (long)bug_id);
+ xmlrpc_value* result = call("bugzilla.getBug", "(s)", bug_id_str);
if (!result)
return -1;
@@ -611,15 +616,10 @@ int ctx::get_bug_info(struct bug_info* bz, xmlrpc_int32 bug_id)
void ctx::login(const char* login, const char* passwd)
{
xmlrpc_value* result = call("User.login", "({s:s,s:s})", "login", login, "password", passwd);
-
if (!result)
- {
- char *errmsg = xasprintf("Can't login. Check Edit->Plugins->Bugzilla and /etc/abrt/plugins/Bugzilla.conf. Server said: %s", env.fault_string);
- error_msg("%s", errmsg); // show error in daemon log
- CABRTException e(EXCEP_PLUGIN, errmsg);
- free(errmsg);
- throw e;
- }
+ error_msg_and_die("Can't login. Check Edit->Plugins->Bugzilla "
+ "and /etc/abrt/plugins/Bugzilla.conf. Server said: %s",
+ env.fault_string);
xmlrpc_DECREF(result);
}
@@ -637,15 +637,12 @@ void ctx::logout()
static void report_to_bugzilla(
const char *dump_dir_name,
- /*const*/ map_plugin_settings_t& settings)
+ map_string_h *settings)
{
struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
- {
- throw CABRTException(EXCEP_PLUGIN, _("Can't open '%s'"), dump_dir_name);
- }
- map_crash_data_t pCrashData;
- load_crash_data_from_crash_dump_dir(dd, pCrashData);
+ xfunc_die(); /* dd_opendir already emitted error msg */
+ crash_data_t *crash_data = create_crash_data_from_dump_dir(dd);
dd_close(dd);
const char *env;
@@ -656,27 +653,27 @@ static void report_to_bugzilla(
bool ssl_verify;
env = getenv("Bugzilla_Login");
- login = env ? env : settings["Login"].c_str();
+ login = env ? env : get_map_string_item_or_empty(settings, "Login");
env = getenv("Bugzilla_Password");
- password = env ? env : settings["Password"].c_str();
+ password = env ? env : get_map_string_item_or_empty(settings, "Password");
if (!login[0] || !password[0])
{
VERB3 log("Empty login and password");
- throw CABRTException(EXCEP_PLUGIN, _("Empty login or password, please check %s"), PLUGINS_CONF_DIR"/Bugzilla.conf");
+ error_msg_and_die(_("Empty login or password, please check %s"), PLUGINS_CONF_DIR"/Bugzilla.conf");
}
env = getenv("Bugzilla_BugzillaURL");
- bugzilla_url = env ? env : settings["BugzillaURL"].c_str();
+ bugzilla_url = env ? env : get_map_string_item_or_empty(settings, "BugzillaURL");
if (!bugzilla_url[0])
bugzilla_url = "https://bugzilla.redhat.com";
bugzilla_xmlrpc = xasprintf("%s"XML_RPC_SUFFIX, bugzilla_url);
env = getenv("Bugzilla_SSLVerify");
- ssl_verify = string_to_bool(env ? env : settings["SSLVerify"].c_str());
+ ssl_verify = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SSLVerify"));
- const char *component = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_COMPONENT);
- const char *duphash = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_DUPHASH);
- const char *release = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_RELEASE);
+ const char *component = get_crash_item_content_or_NULL(crash_data, FILENAME_COMPONENT);
+ const char *duphash = get_crash_item_content_or_NULL(crash_data, FILENAME_DUPHASH);
+ const char *release = get_crash_item_content_or_NULL(crash_data, FILENAME_OS_RELEASE);
ctx bz_server(bugzilla_xmlrpc, ssl_verify);
@@ -687,7 +684,8 @@ static void report_to_bugzilla(
char *product = NULL;
char *version = NULL;
- parse_release(release, &product, &version);
+ parse_release_for_bz(release, &product, &version);
+ free(version);
xmlrpc_value *result;
if (strcmp(product, "Fedora") == 0)
@@ -704,7 +702,7 @@ static void report_to_bugzilla(
if (!all_bugs)
{
throw_if_xml_fault_occurred(&bz_server.env);
- throw CABRTException(EXCEP_PLUGIN, _("Missing mandatory member 'bugs'"));
+ error_msg_and_die(_("Missing mandatory member 'bugs'"));
}
xmlrpc_int32 bug_id = -1;
@@ -723,7 +721,7 @@ static void report_to_bugzilla(
{
bug_info_destroy(&bz);
throw_if_xml_fault_occurred(&bz_server.env);
- throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed. Could not collect all mandatory information"));
+ error_msg_and_die(_("get_bug_info() failed. Could not collect all mandatory information"));
}
if (strcmp(bz.bug_product, product) != 0)
@@ -740,7 +738,7 @@ static void report_to_bugzilla(
if (!all_bugs)
{
throw_if_xml_fault_occurred(&bz_server.env);
- throw CABRTException(EXCEP_PLUGIN, _("Missing mandatory member 'bugs'"));
+ error_msg_and_die(_("Missing mandatory member 'bugs'"));
}
all_bugs_size = bz_server.get_array_size(all_bugs);
@@ -756,7 +754,7 @@ static void report_to_bugzilla(
{
bug_info_destroy(&bz);
throw_if_xml_fault_occurred(&bz_server.env);
- throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed. Could not collect all mandatory information"));
+ error_msg_and_die(_("get_bug_info() failed. Could not collect all mandatory information"));
}
}
else
@@ -764,7 +762,6 @@ static void report_to_bugzilla(
}
}
free(product);
- free(version);
if (all_bugs_size < 0)
{
@@ -773,15 +770,17 @@ static void report_to_bugzilla(
else if (all_bugs_size == 0) // Create new bug
{
log(_("Creating a new bug..."));
- bug_id = bz_server.new_bug(pCrashData, depend_on_bugno);
+ bug_id = bz_server.new_bug(crash_data, depend_on_bugno);
if (bug_id < 0)
{
throw_if_xml_fault_occurred(&bz_server.env);
- throw CABRTException(EXCEP_PLUGIN, _("Bugzilla entry creation failed"));
+ error_msg_and_die(_("Bugzilla entry creation failed"));
}
- log("Adding attachments to bug %d...", bug_id);
- int ret = bz_server.add_attachments(to_string(bug_id).c_str(), pCrashData);
+ log("Adding attachments to bug %ld...", (long)bug_id);
+ char bug_id_str[sizeof(long)*3 + 2];
+ sprintf(bug_id_str, "%ld", (long) bug_id);
+ int ret = bz_server.add_attachments(bug_id_str, crash_data);
if (ret == -1)
{
throw_if_xml_fault_occurred(&bz_server.env);
@@ -816,7 +815,7 @@ static void report_to_bugzilla(
{
VERB3 log("Bugzilla could not find a parent of bug %d", (int)original_bug_id);
bug_info_destroy(&bz);
- throw CABRTException(EXCEP_PLUGIN, _("Bugzilla couldn't find parent of bug %d"), (int)original_bug_id);
+ error_msg_and_die(_("Bugzilla couldn't find parent of bug %d"), (int)original_bug_id);
}
log("Bug %d is a duplicate, using parent bug %d", bug_id, (int)bz.bug_dup_id);
@@ -831,7 +830,7 @@ static void report_to_bugzilla(
{
throw_if_xml_fault_occurred(&bz_server.env);
}
- throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed. Could not collect all mandatory information"));
+ error_msg_and_die(_("get_bug_info() failed. Could not collect all mandatory information"));
}
// found a bug which is not CLOSED as DUPLICATE
@@ -855,13 +854,13 @@ static void report_to_bugzilla(
throw_if_xml_fault_occurred(&bz_server.env);
}
- char *dsc = make_description_reproduce_comment(pCrashData);
+ char *dsc = make_description_reproduce_comment(crash_data);
if (dsc)
{
- const char* package = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_PACKAGE);
- const char* release = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_RELEASE);
- const char* arch = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_ARCHITECTURE);
- const char* is_private = get_crash_data_item_content_or_NULL(pCrashData, "is_private");
+ const char* package = get_crash_item_content_or_NULL(crash_data, FILENAME_PACKAGE);
+ const char* release = get_crash_item_content_or_NULL(crash_data, FILENAME_OS_RELEASE);
+ const char* arch = get_crash_item_content_or_NULL(crash_data, FILENAME_ARCHITECTURE);
+ const char* is_private = get_crash_item_content_or_NULL(crash_data, "is_private");
char *full_dsc = xasprintf("Package: %s\n"
"Architecture: %s\n"
@@ -895,6 +894,7 @@ static void report_to_bugzilla(
(int)bug_id
);
+ free_crash_data(crash_data);
bug_info_destroy(&bz);
}
@@ -904,59 +904,50 @@ int main(int argc, char **argv)
if (env_verbose)
g_verbose = atoi(env_verbose);
- map_plugin_settings_t settings;
-
+ map_string_h *settings = new_map_string();
const char *dump_dir_name = ".";
+ GList *conf_file = NULL;
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ PROGNAME" [-vs] -c CONFFILE -d DIR"
+ "\n"
+ "\nReport a crash to Bugzilla"
+ );
enum {
- OPT_s = (1 << 0),
+ OPT_v = 1 << 0,
+ OPT_s = 1 << 1,
+ OPT_d = 1 << 2,
+ OPT_c = 1 << 3,
};
- int optflags = 0;
- int opt;
- while ((opt = getopt(argc, argv, "c:d:vs")) != -1)
- {
- switch (opt)
- {
- case 'c':
- VERB1 log("Loading settings from '%s'", optarg);
- LoadPluginSettings(optarg, settings);
- VERB3 log("Loaded '%s'", optarg);
- break;
- case 'd':
- dump_dir_name = optarg;
- break;
- case 'v':
- g_verbose++;
- break;
- case 's':
- optflags |= OPT_s;
- break;
- default:
- /* Careful: the string below contains tabs, dont replace with spaces */
- error_msg_and_die(
- "Usage: abrt-action-bugzilla -c CONFFILE -d DIR [-vs]"
- "\n"
- "\nReport a crash to Bugzilla"
- "\n"
- "\nOptions:"
- "\n -c FILE Configuration file (may be given many times)"
- "\n -d DIR Crash dump directory"
- "\n -v Verbose"
- "\n -s Log to syslog"
- );
- }
- }
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_BOOL( 's', NULL, NULL , _("Log to syslog")),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Crash dump directory")),
+ OPT_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file (may be given many times)")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
-
//DONT! our stdout/stderr goes directly to daemon, don't want to have prefix there.
-// msg_prefix = xasprintf("abrt-action-bugzilla[%u]", getpid());
-
- if (optflags & OPT_s)
+// msg_prefix = xasprintf(PROGNAME"[%u]", getpid());
+ if (opts & OPT_s)
{
openlog(msg_prefix, 0, LOG_DAEMON);
logmode = LOGMODE_SYSLOG;
}
+ while (conf_file)
+ {
+ char *fn = (char *)conf_file->data;
+ VERB1 log("Loading settings from '%s'", fn);
+ load_conf_file(fn, settings, /*skip key w/o values:*/ true);
+ VERB3 log("Loaded '%s'", fn);
+ conf_file = g_list_remove(conf_file, fn);
+ }
+
VERB1 log("Initializing XML-RPC library");
xmlrpc_env env;
xmlrpc_env_init(&env);
@@ -965,14 +956,8 @@ int main(int argc, char **argv)
error_msg_and_die("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code);
xmlrpc_env_clean(&env);
- try
- {
- report_to_bugzilla(dump_dir_name, settings);
- }
- catch (CABRTException& e)
- {
- error_msg_and_die("%s", e.what());
- }
+ report_to_bugzilla(dump_dir_name, settings);
+ free_map_string(settings);
return 0;
}
diff --git a/src/plugins/abrt-action-generate-backtrace.c b/src/plugins/abrt-action-generate-backtrace.c
index 64ce4082..a8c18e36 100644
--- a/src/plugins/abrt-action-generate-backtrace.c
+++ b/src/plugins/abrt-action-generate-backtrace.c
@@ -28,8 +28,9 @@
#define DEBUGINFO_CACHE_DIR LOCALSTATEDIR"/cache/abrt-di"
static const char *dump_dir_name = ".";
-static const char *debuginfo_dirs;
-static int exec_timeout_sec = 60;
+static const char *debuginfo_dirs = DEBUGINFO_CACHE_DIR;
+/* 60 seconds was too limiting on slow machines */
+static int exec_timeout_sec = 240;
static void create_hash(char hash_str[SHA1_RESULT_LEN*2 + 1], const char *pInput)
@@ -131,7 +132,7 @@ static char* exec_vp(char **args, uid_t uid, int redirect_stderr, int *status)
static char *get_backtrace(struct dump_dir *dd)
{
char *uid_str = dd_load_text(dd, FILENAME_UID);
- uid_t uid = xatoi_u(uid_str);
+ uid_t uid = xatoi_positive(uid_str);
free(uid_str);
char *executable = dd_load_text(dd, FILENAME_EXECUTABLE);
dd_close(dd);
@@ -244,49 +245,49 @@ static char *get_backtrace(struct dump_dir *dd)
return bt;
}
-static char *i_opt;
-static const char abrt_action_generage_backtrace_usage[] = PROGNAME" [options] -d DIR";
-enum {
- OPT_v = 1 << 0,
- OPT_d = 1 << 1,
- OPT_i = 1 << 2,
- OPT_t = 1 << 3,
- OPT_s = 1 << 4,
-};
-/* Keep enum above and order of options below in sync! */
-static struct options abrt_action_generate_backtrace_options[] = {
- OPT__VERBOSE(&g_verbose),
- OPT_STRING( 'd', NULL, &dump_dir_name, "DIR", "Crash dump directory"),
- OPT_STRING( 'i', NULL, &i_opt, "dir1[:dir2]...", "Additional debuginfo directories"),
- OPT_INTEGER('t', NULL, &exec_timeout_sec, "Kill gdb if it runs for more than N seconds"),
- OPT_BOOL( 's', NULL, NULL, "Log to syslog"),
- OPT_END()
-};
-
int main(int argc, char **argv)
{
char *env_verbose = getenv("ABRT_VERBOSE");
if (env_verbose)
g_verbose = atoi(env_verbose);
- unsigned opts = parse_opts(argc, argv, abrt_action_generate_backtrace_options,
- abrt_action_generage_backtrace_usage);
-
- debuginfo_dirs = DEBUGINFO_CACHE_DIR;
- if (i_opt)
- {
- debuginfo_dirs = xasprintf("%s:%s", DEBUGINFO_CACHE_DIR, i_opt);
- }
+ char *i_opt = NULL;
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ PROGNAME" [options] -d DIR"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_i = 1 << 2,
+ OPT_t = 1 << 3,
+ OPT_s = 1 << 4,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING( 'd', NULL, &dump_dir_name , "DIR" , _("Crash dump directory")),
+ OPT_STRING( 'i', NULL, &i_opt , "dir1[:dir2]...", _("Additional debuginfo directories")),
+ OPT_INTEGER('t', NULL, &exec_timeout_sec, _("Kill gdb if it runs for more than N seconds")),
+ OPT_BOOL( 's', NULL, NULL , _("Log to syslog")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
- msg_prefix = PROGNAME;
-
+ //msg_prefix = PROGNAME;
if (opts & OPT_s)
{
openlog(msg_prefix, 0, LOG_DAEMON);
logmode = LOGMODE_SYSLOG;
}
+ if (i_opt)
+ {
+ debuginfo_dirs = xasprintf("%s:%s", DEBUGINFO_CACHE_DIR, i_opt);
+ }
+
struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
return 1;
@@ -294,49 +295,70 @@ int main(int argc, char **argv)
char *package = dd_load_text(dd, FILENAME_PACKAGE);
char *executable = dd_load_text(dd, FILENAME_EXECUTABLE);
- /* Create and store backtrace */
+ /* Create gdb backtrace */
/* NB: get_backtrace() closes dd */
char *backtrace_str = get_backtrace(dd);
if (!backtrace_str)
{
backtrace_str = xstrdup("");
- VERB3 log("get_backtrace() returns NULL, broken core/gdb?");
+ log("get_backtrace() returns NULL, broken core/gdb?");
}
+ /* Compute backtrace hash */
+ struct btp_location location;
+ btp_location_init(&location);
+ char *backtrace_str_ptr = backtrace_str;
+ struct btp_backtrace *backtrace = btp_backtrace_parse(&backtrace_str_ptr, &location);
+
+ /* Store gdb backtrace */
+
dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
return 1;
-
dd_save_text(dd, FILENAME_BACKTRACE, backtrace_str);
+ /* Don't be completely silent. gdb run takes a few seconds,
+ * it is useful to let user know it (maybe) worked.
+ */
+ log(_("Backtrace is generated and saved, %u bytes"), (int)strlen(backtrace_str));
+ free(backtrace_str);
+
+ /* Store backtrace hash */
- /* Compute and store backtrace hash. */
- struct btp_location location;
- btp_location_init(&location);
- char *backtrace_str_ptr = backtrace_str;
- struct btp_backtrace *backtrace = btp_backtrace_parse(&backtrace_str_ptr, &location);
if (!backtrace)
{
+ /*
+ * The parser failed. Compute the UUID from the executable
+ * and package only. This is not supposed to happen often.
+ */
VERB1 log(_("Backtrace parsing failed for %s"), dump_dir_name);
VERB1 log("%d:%d: %s", location.line, location.column, location.message);
- /* If the parser failed compute the UUID from the executable
- and package only. This is not supposed to happen often.
- Do not store the rating, as we do not know how good the
- backtrace is. */
struct strbuf *emptybt = strbuf_new();
strbuf_prepend_str(emptybt, executable);
strbuf_prepend_str(emptybt, package);
+
char hash_str[SHA1_RESULT_LEN*2 + 1];
create_hash(hash_str, emptybt->buf);
+
dd_save_text(dd, FILENAME_DUPHASH, hash_str);
+ /*
+ * Other parts of ABRT assume that if no rating is available,
+ * it is ok to allow reporting of the bug. To be sure no bad
+ * backtrace is reported, rate the backtrace with the lowest
+ * rating.
+ */
+ dd_save_text(dd, FILENAME_RATING, "0");
strbuf_free(emptybt);
- free(backtrace_str);
free(package);
free(executable);
dd_close(dd);
- return 2;
+
+ /* Report success even if the parser failed, as the backtrace
+ * has been created and rated. The failure is caused by a flaw
+ * in the parser, not in the backtrace.
+ */
+ return 0;
}
- free(backtrace_str);
/* Compute duplication hash. */
char *str_hash_core = btp_backtrace_get_duplication_hash(backtrace);
@@ -344,8 +366,10 @@ int main(int argc, char **argv)
strbuf_append_str(str_hash, package);
strbuf_append_str(str_hash, executable);
strbuf_append_str(str_hash, str_hash_core);
+
char hash_str[SHA1_RESULT_LEN*2 + 1];
create_hash(hash_str, str_hash->buf);
+
dd_save_text(dd, FILENAME_DUPHASH, hash_str);
strbuf_free(str_hash);
free(str_hash_core);
@@ -368,14 +392,14 @@ int main(int argc, char **argv)
/* Get the function name from the crash frame. */
struct btp_frame *crash_frame = btp_backtrace_get_crash_frame(backtrace);
if (crash_frame)
- {
+ {
if (crash_frame->function_name &&
0 != strcmp(crash_frame->function_name, "??"))
{
dd_save_text(dd, FILENAME_CRASH_FUNCTION, crash_frame->function_name);
}
btp_frame_free(crash_frame);
- }
+ }
btp_backtrace_free(backtrace);
dd_close(dd);
diff --git a/src/plugins/abrt-action-install-debuginfo.c b/src/plugins/abrt-action-install-debuginfo.c
new file mode 100644
index 00000000..39915e59
--- /dev/null
+++ b/src/plugins/abrt-action-install-debuginfo.c
@@ -0,0 +1,43 @@
+#include <unistd.h>
+#include <string.h>
+
+#define EXECUTABLE "abrt-action-install-debuginfo.py"
+
+static void error_msg_and_die(const char *msg, const char *arg)
+{
+ write(2, msg, strlen(msg));
+ if (arg)
+ {
+ write(2, " '", 2);
+ write(2, msg, strlen(msg));
+ write(2, "'", 1);
+ }
+ write(2, "\n", 1);
+ exit(1);
+}
+
+
+/* A binary wrapper is needed around python scripts if we want
+ * to run them in sgid/suid mode.
+ *
+ * This is such a wrapper.
+ */
+int main(int argc, char **argv)
+{
+ /*
+ * We disallow passing of arguments which point to writable dirs.
+ * This way, the script will always use default arguments.
+ */
+ char **pp = argv;
+ char *arg;
+ while ((arg = *++pp) != NULL)
+ {
+ if (strncmp(arg, "--cache", 7) == 0)
+ error_msg_and_die("bad option", arg);
+ if (strncmp(arg, "--tmpdir", 8) == 0)
+ error_msg_and_die("bad option", arg);
+ }
+
+ execvp(EXECUTABLE, argv);
+ error_msg_and_die("Can't execute", EXECUTABLE);
+}
diff --git a/src/plugins/abrt-action-install-debuginfo.py b/src/plugins/abrt-action-install-debuginfo.py
index b16cc3e9..9253c87f 100755
--- a/src/plugins/abrt-action-install-debuginfo.py
+++ b/src/plugins/abrt-action-install-debuginfo.py
@@ -6,6 +6,7 @@
from subprocess import Popen, PIPE
import sys
import os
+import time
import getopt
import shutil
from yum import _, YumBase
@@ -51,7 +52,11 @@ def unmute_stdout():
def ask_yes_no(prompt, retries=4):
while True:
- response = raw_input(prompt)
+ try:
+ response = raw_input(prompt)
+ except EOFError:
+ log1("got eof, probably executed from helper, assuming - yes")
+ response = 'y'
if response in ('y', 'Y'):
return True
if response in ('n', 'N', ''):
@@ -65,30 +70,30 @@ def ask_yes_no(prompt, retries=4):
# ..that can lead to: foo.c No such file and directory
# files is not used...
def unpack_rpm(package_nevra, files, tmp_dir, destdir, keeprpm):
- package_file_suffix = ".rpm"
- package_full_path = tmp_dir + "/" + package_nevra + package_file_suffix
+ package_name = package_nevra + ".rpm"
+ package_full_path = tmp_dir + "/" + package_name
log1("Extracting %s to %s" % (package_full_path, destdir))
log2(files)
- print (_("Extracting cpio from %s") % (package_full_path))
+ print _("Extracting cpio from %s") % (package_full_path)
unpacked_cpio_path = tmp_dir + "/unpacked.cpio"
try:
unpacked_cpio = open(unpacked_cpio_path, 'wb')
except IOError, ex:
- print (_("Can't write to:"), (unpacked_cpio_path,ex))
+ print _("Can't write to '%s': %s") % (unpacked_cpio_path, ex)
return RETURN_FAILURE
rpm2cpio = Popen(["rpm2cpio", package_full_path],
- stdout=unpacked_cpio, bufsize=-1)
+ stdout = unpacked_cpio, bufsize = -1)
retcode = rpm2cpio.wait()
if retcode == 0:
log1("cpio written OK")
if not keeprpm:
log1("keeprpms = False, removing %s" % package_full_path)
- print _("Removing the temporary rpm file")
+ #print _("Removing temporary rpm file")
os.unlink(package_full_path)
else:
unpacked_cpio.close()
- print (_("Can't extract package: %s") % package_full_path)
+ print _("Can't extract package '%s'") % package_full_path
return RETURN_FAILURE
# close the file
@@ -96,18 +101,17 @@ def unpack_rpm(package_nevra, files, tmp_dir, destdir, keeprpm):
# and open it for reading
unpacked_cpio = open(unpacked_cpio_path, 'rb')
- print (_("Caching files from %s made from %s") %
- (unpacked_cpio_path, package_full_path))
+ print _("Caching files from %s made from %s") % ("unpacked.cpio", package_name)
cpio = Popen(["cpio","-i", "-d", "--quiet"],
stdin=unpacked_cpio, cwd=destdir, bufsize=-1)
retcode = cpio.wait()
if retcode == 0:
log1("files extracted OK")
- print _("Removing the temporary cpio file")
+ #print _("Removing temporary cpio file")
os.unlink(unpacked_cpio_path)
else:
- print (_("Can't extract files from: %s") % unpacked_cpio_path)
+ print _("Can't extract files from '%s'") % unpacked_cpio_path
return RETURN_FAILURE
class MyDownloadCallback(DownloadBaseCallback):
@@ -115,36 +119,42 @@ class MyDownloadCallback(DownloadBaseCallback):
self.total_pkgs = total_pkgs
self.downloaded_pkgs = 0
self.last_pct = 0
+ self.last_time = 0
DownloadBaseCallback.__init__(self)
def updateProgress(self, name, frac, fread, ftime):
- pct = int( frac*100 )
+ pct = int(frac * 100)
if pct == self.last_pct:
log2("percentage is the same, not updating progress")
return
self.last_pct = pct
- # if run from terminal we can have a fancy output
+ # if run from terminal we can have fancy output
if sys.stdout.isatty():
- sys.stdout.write("\033[sDownloading (%i of %i) %.30s : %.3s %%\033[u"
- % (self.downloaded_pkgs + 1, self.total_pkgs,
- name, pct)
- )
+ sys.stdout.write("\033[sDownloading (%i of %i) %s: %3u%%\033[u"
+ % (self.downloaded_pkgs + 1, self.total_pkgs, name, pct)
+ )
if pct == 100:
- print _("Downloading (%i of %i) %.30s : %.3s %%"
- % (self.downloaded_pkgs + 1, self.total_pkgs,
- name, pct)
- )
+ print (_("Downloading (%i of %i) %s: %3u%%")
+ % (self.downloaded_pkgs + 1, self.total_pkgs, name, pct)
+ )
# but we want machine friendly output when spawned from abrt-server
else:
- print (_("Downloading (%i of %i) %.30s : %.3s %%")
- % (self.downloaded_pkgs + 1, self.total_pkgs, name, pct)
- )
+ t = time.time()
+ if self.last_time == 0:
+ self.last_time = t
+ # update only every 10 seconds
+ if pct == 100 or self.last_time > t or t - self.last_time >= 10:
+ print (_("Downloading (%i of %i) %s: %3u%%")
+ % (self.downloaded_pkgs + 1, self.total_pkgs, name, pct)
+ )
+ self.last_time = t
+ if pct == 100:
+ self.last_time = 0
sys.stdout.flush()
class DebugInfoDownload(YumBase):
- """abrt-debuginfo-install --core=CORE tmpdir cachedir"""
def __init__(self, cache, tmp, keep_rpms=False):
self.cachedir = cache
self.tmpdir = tmp
@@ -166,13 +176,13 @@ class DebugInfoDownload(YumBase):
if not files:
return
- print _("Searching the missing debuginfo packages")
- # this suppress yum messages about setting up repositories
- mute_stdout()
+ if verbose == 0:
+ # this suppress yum messages about setting up repositories
+ mute_stdout()
# make yumdownloader work as non root user.
if not self.setCacheDir():
- self.logger.error("Error: Could not make cachedir, exiting")
+ self.logger.error("Error: can't make cachedir, exiting")
sys.exit(50)
# disable all not needed
@@ -191,13 +201,14 @@ class DebugInfoDownload(YumBase):
self.repos.populateSack(mdtype='metadata', cacheonly=1)
self.repos.populateSack(mdtype='filelists', cacheonly=1)
- # re-enable the output to stdout
- unmute_stdout()
+ if verbose == 0:
+ # re-enable the output to stdout
+ unmute_stdout()
not_found = []
package_files_dict = {}
for debuginfo_path in files:
- log2("yum whatprovides %s" %debuginfo_path)
+ log2("yum whatprovides %s" % debuginfo_path)
pkg = self.pkgSack.searchFiles(debuginfo_path)
# sometimes one file is provided by more rpms, we can use either of
# them, so let's use the first match
@@ -210,29 +221,23 @@ class DebugInfoDownload(YumBase):
installed_size += float(pkg[0].installedsize)
total_pkgs += 1
- log2("found pkg for %s" % debuginfo_path)
+ log2("found pkg for %s: %s" % (debuginfo_path, pkg[0]))
else:
log2("not found pkg for %s" % debuginfo_path)
not_found.append(debuginfo_path)
# connect our progress update callback
dnlcb = MyDownloadCallback(total_pkgs)
- self.repos.setProgressBar( dnlcb )
-
- log1("%i files in %i packages" % (len(files), total_pkgs))
+ self.repos.setProgressBar(dnlcb)
- print (_("To download: (%.2f) M / Installed size: %.2f M" %
- (
- todownload_size / (1024**2),
- installed_size / (1024**2))
- )
- )
- #print _("%i debug infos not found" % len(not_found))
-
- log1("packages: %i\nTo download: %f \nUnpacked size: %f" %
- (total_pkgs,
- todownload_size / (1024**2),
- installed_size / (1024**2)))
+ if verbose != 0 or len(not_found) != 0:
+ print _("Can't find packages for %u debuginfo files") % len(not_found)
+ if verbose != 0 or total_pkgs != 0:
+ print _("Found %u packages to download") % total_pkgs
+ print _("Downloading %.2fMb, installed size: %.2fMb") % (
+ todownload_size / (1024**2),
+ installed_size / (1024**2)
+ )
# ask only if we have terminal, because for now we don't have a way
# how to pass the question to gui and the response back
@@ -271,13 +276,13 @@ class DebugInfoDownload(YumBase):
downloaded_pkgs += 1
- if not self.keeprpms:
+ if not self.keeprpms and os.path.exists(self.tmpdir):
print (_("All downloaded packages have been extracted, removing %s")
% self.tmpdir)
try:
os.rmdir(self.tmpdir)
except OSError:
- print _("Can't remove %s, probably contains an error log")
+ print _("Can't remove %s, probably contains an error log" % self.tmpdir)
verbose = 0
def log1(message):
@@ -302,8 +307,7 @@ def extract_info_from_core(corefile):
#SEP = 3
EXECUTABLE = 4
- print (_("Analyzing corefile: %(corefile_path)s") %
- {"corefile_path":corefile})
+ print _("Analyzing corefile '%s'") % corefile
eu_unstrip_OUT = Popen(["eu-unstrip","--core=%s" % corefile, "-n"], stdout=PIPE, bufsize=-1).communicate()[0]
# parse eu_unstrip_OUT and return the list of build_ids
@@ -323,7 +327,7 @@ def extract_info_from_core(corefile):
#print eu_unstrip_OUT
# we failed to get build ids from the core -> die
if not eu_unstrip_OUT:
- log1("can't get build ids from the core")
+ print "Can't get build ids from %s" % corefile
return RETURN_FAILURE
lines = eu_unstrip_OUT.split('\n')
@@ -344,7 +348,7 @@ def extract_info_from_core(corefile):
libraries.add(library)
build_ids.add(build_id)
else:
- log2("skipping line %s" % line)
+ log2("skipping line '%s'" % line)
log1("Found %i build_ids" % len(build_ids))
log1("Found %i libs" % len(libraries))
return build_ids
@@ -354,7 +358,7 @@ def build_ids_to_path(build_ids):
build_id1=${build_id:0:2}
build_id2=${build_id:2}
file="usr/lib/debug/.build-id/$build_id1/$build_id2.debug"
- """
+ """
return ["/usr/lib/debug/.build-id/%s/%s.debug" % (b_id[:2], b_id[2:]) for b_id in build_ids]
# beware this finds only missing libraries, but not the executable itself ..
@@ -381,9 +385,7 @@ def clean_up():
try:
shutil.rmtree(tmpdir)
except OSError, ex:
- print (_("Can't remove %(tmpdir_path)s: %(reason)s")
- % {"tmpdir_path":tmpdir, "reason": ex }
- )
+ print _("Can't remove '%s': %s") % (tmpdir, ex)
def sigterm_handler(signum, frame):
clean_up()
@@ -391,10 +393,11 @@ def sigterm_handler(signum, frame):
def sigint_handler(signum, frame):
clean_up()
- print "\n", _("Exiting on user Command")
+ print "\n", _("Exiting on user command")
exit(RETURN_OK)
import signal
+
if __name__ == "__main__":
# abrt-server can send SIGTERM to abort the download
signal.signal(signal.SIGTERM, sigterm_handler)
@@ -404,15 +407,14 @@ if __name__ == "__main__":
cachedir = None
tmpdir = None
keeprpms = False
- result = RETURN_OK
noninteractive = False
# localization
init_gettext()
- help_text = _("Usage: %s --core=<COREFILE> "
- "--tmpdir=<TMPDIR> "
- "--cachedir=<CACHEDIR>") % sys.argv[0]
+ help_text = _("Usage: %s --core=COREFILE "
+ "--tmpdir=TMPDIR "
+ "--cache=CACHEDIR") % sys.argv[0]
try:
opts, args = getopt.getopt(sys.argv[1:], "vyhc:", ["help", "core=",
"cache=", "tmpdir=",
@@ -426,7 +428,7 @@ if __name__ == "__main__":
verbose += 1
elif opt == "-y":
noninteractive = True
- elif opt in ("--core","-c"):
+ elif opt in ("--core", "-c"):
core = arg
elif opt in ("--cache"):
cachedir = arg
@@ -443,30 +445,26 @@ if __name__ == "__main__":
print help_text
exit(RETURN_FAILURE)
if not cachedir:
- print _("You have to specify the path to cachedir.")
- print help_text
- exit(RETURN_FAILURE)
+ cachedir = "/var/cache/abrt-di"
if not tmpdir:
- print _("You have to specify the path to tmpdir.")
- print help_text
- exit(RETURN_FAILURE)
+ # security people prefer temp subdirs in app's private dir, like /var/run/abrt
+ # for now, we use /tmp...
+ tmpdir = "/tmp/abrt-tmp-debuginfo-%s.%u" % (time.strftime("%Y-%m-%d-%H:%M:%S"), os.getpid())
b_ids = extract_info_from_core(core)
if b_ids == RETURN_FAILURE:
exit(RETURN_FAILURE)
+
missing = filter_installed_debuginfos(b_ids, cachedir)
if missing:
log2(missing)
+ print _("Coredump references %u debuginfo files, %u of them are not installed") % (len(b_ids), len(missing))
downloader = DebugInfoDownload(cache=cachedir, tmp=tmpdir)
result = downloader.download(missing)
- else:
- print _("All debuginfo seems to be available")
- exit(RETURN_OK)
-
- missing = filter_installed_debuginfos(b_ids, cachedir)
- for bid in missing:
- log1("MISSING:%s" % bid)
-
- print _("Complete!")
- exit(result)
+ missing = filter_installed_debuginfos(b_ids, cachedir)
+ for bid in missing:
+ print _("Missing debuginfo file: %s") % bid
+ exit(result)
+ print _("All %u debuginfo files are available") % len(b_ids)
+ exit(RETURN_OK)
diff --git a/src/plugins/abrt-action-kerneloops.cpp b/src/plugins/abrt-action-kerneloops.c
index dea6df17..8d00da52 100644
--- a/src/plugins/abrt-action-kerneloops.cpp
+++ b/src/plugins/abrt-action-kerneloops.c
@@ -19,8 +19,7 @@
#include <curl/curl.h>
#include "abrtlib.h"
-#include "abrt_crash_dump.h"
-#include "abrt_exception.h"
+#include "parse_options.h"
#define PROGNAME "abrt-action-kerneloops"
@@ -85,26 +84,21 @@ static CURLcode http_post_to_kerneloops_site(const char *url, const char *oopsda
static void report_to_kerneloops(
const char *dump_dir_name,
- const map_plugin_settings_t& settings)
+ map_string_h *settings)
{
struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
exit(1); /* error msg is already logged */
- map_crash_data_t pCrashData;
- load_crash_data_from_crash_dump_dir(dd, pCrashData);
+ crash_data_t *crash_data = create_crash_data_from_dump_dir(dd);
dd_close(dd);
- const char *backtrace = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_BACKTRACE);
+ const char *backtrace = get_crash_item_content_or_NULL(crash_data, FILENAME_BACKTRACE);
if (!backtrace)
error_msg_and_die("Error sending kernel oops due to missing backtrace");
- map_plugin_settings_t::const_iterator end = settings.end();
- map_plugin_settings_t::const_iterator it;
-
const char *env = getenv("KerneloopsReporter_SubmitURL");
- it = settings.find("SubmitURL");
- const char *submitURL = (env ? env : it == end ? "" : it->second.c_str());
+ const char *submitURL = (env ? env : get_map_string_item_or_empty(settings, "SubmitURL"));
if (!submitURL[0])
submitURL = "http://submit.kerneloops.org/submitoops.php";
@@ -114,6 +108,8 @@ static void report_to_kerneloops(
if (ret != CURLE_OK)
error_msg_and_die("Kernel oops has not been sent due to %s", curl_easy_strerror(ret));
+ free_crash_data(crash_data);
+
/* Server replies with:
* 200 thank you for submitting the kernel oops information
* RemoteIP: 34192fd15e34bf60fac6a5f01bba04ddbd3f0558
@@ -128,67 +124,52 @@ int main(int argc, char **argv)
if (env_verbose)
g_verbose = atoi(env_verbose);
- map_plugin_settings_t settings;
-
+ map_string_h *settings = new_map_string();
const char *dump_dir_name = ".";
+ GList *conf_file = NULL;
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ PROGNAME" [-vs] -c CONFFILE -d DIR"
+ "\n"
+ "\nReport a kernel oops to kerneloops.org (or similar) site"
+ );
enum {
- OPT_s = (1 << 0),
+ OPT_v = 1 << 0,
+ OPT_s = 1 << 1,
+ OPT_d = 1 << 2,
+ OPT_c = 1 << 3,
};
- int optflags = 0;
- int opt;
- while ((opt = getopt(argc, argv, "c:d:vs")) != -1)
- {
- switch (opt)
- {
- case 'c':
- VERB1 log("Loading settings from '%s'", optarg);
- LoadPluginSettings(optarg, settings);
- VERB3 log("Loaded '%s'", optarg);
- break;
- case 'd':
- dump_dir_name = optarg;
- break;
- case 'v':
- g_verbose++;
- break;
- case 's':
- optflags |= OPT_s;
- break;
- default:
- /* Careful: the string below contains tabs, dont replace with spaces */
- error_msg_and_die(
- "Usage: "PROGNAME" -c CONFFILE -d DIR [-vs]"
- "\n"
- "\nReport a kernel oops to kerneloops.org (or similar) site"
- "\n"
- "\nOptions:"
- "\n -c FILE Configuration file (may be given many times)"
- "\n -d DIR Crash dump directory"
- "\n -v Verbose"
- "\n -s Log to syslog"
- );
- }
- }
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_BOOL( 's', NULL, NULL , _("Log to syslog")),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Crash dump directory")),
+ OPT_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file (may be given many times)")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
-
//DONT! our stdout/stderr goes directly to daemon, don't want to have prefix there.
// msg_prefix = xasprintf(PROGNAME"[%u]", getpid());
-
- if (optflags & OPT_s)
+ if (opts & OPT_s)
{
openlog(msg_prefix, 0, LOG_DAEMON);
logmode = LOGMODE_SYSLOG;
}
- try
+ while (conf_file)
{
- report_to_kerneloops(dump_dir_name, settings);
- }
- catch (CABRTException& e)
- {
- error_msg_and_die("%s", e.what());
+ char *fn = (char *)conf_file->data;
+ VERB1 log("Loading settings from '%s'", fn);
+ load_conf_file(fn, settings, /*skip key w/o values:*/ true);
+ VERB3 log("Loaded '%s'", fn);
+ conf_file = g_list_remove(conf_file, fn);
}
+ report_to_kerneloops(dump_dir_name, settings);
+
+ free_map_string(settings);
return 0;
}
diff --git a/src/plugins/abrt-action-mailx.cpp b/src/plugins/abrt-action-mailx.c
index fa7fd8a0..3debf449 100644
--- a/src/plugins/abrt-action-mailx.cpp
+++ b/src/plugins/abrt-action-mailx.c
@@ -21,8 +21,6 @@
#include "abrtlib.h"
#include "parse_options.h"
-#include "abrt_crash_dump.h"
-#include "abrt_exception.h"
#define PROGNAME "abrt-action-mailx"
@@ -47,73 +45,71 @@ static void exec_and_feed_input(uid_t uid, const char* text, char **args)
error_msg_and_die("Error running '%s'", args[0]);
}
-static char** append_str_to_vector(char **vec, unsigned &size, const char *str)
+static char** append_str_to_vector(char **vec, unsigned *size_p, const char *str)
{
//log("old vec: %p", vec);
+ unsigned size = *size_p;
vec = (char**) xrealloc(vec, (size+2) * sizeof(vec[0]));
vec[size] = xstrdup(str);
//log("new vec: %p, added [%d] %p", vec, size, vec[size]);
size++;
vec[size] = NULL;
+ *size_p = size;
return vec;
}
static void create_and_send_email(
const char *dump_dir_name,
- const map_plugin_settings_t& settings)
+ map_string_h *settings)
{
struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
exit(1); /* error msg is already logged by dd_opendir */
- map_crash_data_t pCrashData;
- load_crash_data_from_crash_dump_dir(dd, pCrashData);
+ crash_data_t *crash_data = create_crash_data_from_dump_dir(dd);
dd_close(dd);
char* env;
- map_plugin_settings_t::const_iterator end = settings.end();
- map_plugin_settings_t::const_iterator it;
env = getenv("Mailx_Subject");
- it = settings.find("Subject");
- const char *subject = xstrdup(env ? env : (it != end ? it->second.c_str() : "[abrt] full crash report"));
+ const char *subject = (env ? env : get_map_string_item_or_NULL(settings, "Subject") ? : "[abrt] full crash report");
env = getenv("Mailx_EmailFrom");
- it = settings.find("EmailFrom");
- const char *email_from = (env ? env : (it != end ? it->second.c_str() : "user@localhost"));
+ const char *email_from = (env ? env : get_map_string_item_or_NULL(settings, "EmailFrom") ? : "user@localhost");
env = getenv("Mailx_EmailTo");
- it = settings.find("EmailTo");
- const char *email_to = (env ? env : (it != end ? it->second.c_str() : "root@localhost"));
+ const char *email_to = (env ? env : get_map_string_item_or_NULL(settings, "EmailTo") ? : "root@localhost");
env = getenv("Mailx_SendBinaryData");
- it = settings.find("SendBinaryData");
- bool send_binary_data = string_to_bool(env ? env : (it != end ? it->second.c_str() : "0"));
+ bool send_binary_data = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SendBinaryData"));
char **args = NULL;
unsigned arg_size = 0;
- args = append_str_to_vector(args, arg_size, "/bin/mailx");
+ args = append_str_to_vector(args, &arg_size, "/bin/mailx");
- char *dsc = make_description_mailx(pCrashData);
+ char *dsc = make_description_mailx(crash_data);
if (send_binary_data)
{
- map_crash_data_t::const_iterator it_cd;
- for (it_cd = pCrashData.begin(); it_cd != pCrashData.end(); it_cd++)
+ GHashTableIter iter;
+ char *name;
+ struct crash_item *value;
+ g_hash_table_iter_init(&iter, crash_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
{
- if (it_cd->second[CD_TYPE] == CD_BIN)
+ if (value->flags & CD_FLAG_BIN)
{
- args = append_str_to_vector(args, arg_size, "-a");
- args = append_str_to_vector(args, arg_size, it_cd->second[CD_CONTENT].c_str());
+ args = append_str_to_vector(args, &arg_size, "-a");
+ args = append_str_to_vector(args, &arg_size, value->content);
}
}
}
- args = append_str_to_vector(args, arg_size, "-s");
- args = append_str_to_vector(args, arg_size, subject);
- args = append_str_to_vector(args, arg_size, "-r");
- args = append_str_to_vector(args, arg_size, email_from);
- args = append_str_to_vector(args, arg_size, email_to);
+ args = append_str_to_vector(args, &arg_size, "-s");
+ args = append_str_to_vector(args, &arg_size, subject);
+ args = append_str_to_vector(args, &arg_size, "-r");
+ args = append_str_to_vector(args, &arg_size, email_from);
+ args = append_str_to_vector(args, &arg_size, email_to);
log(_("Sending an email..."));
- const char *uid_str = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_UID);
- exec_and_feed_input(xatoi_u(uid_str), dsc, args);
+ const char *uid_str = get_crash_item_content_or_NULL(crash_data, FILENAME_UID);
+ exec_and_feed_input(xatoi_positive(uid_str), dsc, args);
free(dsc);
@@ -122,6 +118,8 @@ static void create_and_send_email(
args -= arg_size;
free(args);
+ free_crash_data(crash_data);
+
log("Email was sent to: %s", email_to);
}
@@ -134,7 +132,8 @@ int main(int argc, char **argv)
const char *dump_dir_name = ".";
const char *conf_file = NULL;
- const char *program_usage = _(
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
PROGNAME" [-v] -d DIR [-c CONFFILE]\n"
"\n"
"Upload compressed tarball of crash dump"
@@ -151,29 +150,22 @@ int main(int argc, char **argv)
OPT_STRING('c', NULL, &conf_file , "CONFFILE", _("Config file")),
OPT_END()
};
-
- /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage);
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
//msg_prefix = PROGNAME;
- //if (optflags & OPT_s)
+ //if (opts & OPT_s)
//{
// openlog(msg_prefix, 0, LOG_DAEMON);
// logmode = LOGMODE_SYSLOG;
//}
- map_plugin_settings_t settings;
+ map_string_h *settings = new_map_string();
if (conf_file)
- LoadPluginSettings(conf_file, settings);
+ load_conf_file(conf_file, settings, /*skip key w/o values:*/ true);
- try
- {
- create_and_send_email(dump_dir_name, settings);
- }
- catch (CABRTException& e)
- {
- error_msg_and_die("%s", e.what());
- }
+ create_and_send_email(dump_dir_name, settings);
+ free_map_string(settings);
return 0;
}
diff --git a/src/plugins/abrt-action-print.cpp b/src/plugins/abrt-action-print.c
index 303c05f7..cc7fbb34 100644
--- a/src/plugins/abrt-action-print.cpp
+++ b/src/plugins/abrt-action-print.c
@@ -20,8 +20,6 @@
*/
#include "abrtlib.h"
#include "parse_options.h"
-#include "abrt_crash_dump.h"
-#include "abrt_exception.h"
#define PROGNAME "abrt-action-print"
@@ -35,10 +33,12 @@ int main(int argc, char **argv)
if (env_verbose)
g_verbose = atoi(env_verbose);
- const char *program_usage = _(
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
PROGNAME" [-v] [-o FILE] -d DIR\n"
"\n"
- "Print information about the crash to standard output");
+ "Print information about the crash to standard output"
+ );
enum {
OPT_v = 1 << 0,
OPT_d = 1 << 1,
@@ -51,8 +51,7 @@ int main(int argc, char **argv)
OPT_STRING('o', NULL, &output_file , "FILE", _("Output file")),
OPT_END()
};
-
- /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage);
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
//msg_prefix = PROGNAME;
@@ -75,25 +74,17 @@ int main(int argc, char **argv)
}
}
- try
- {
- struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
- if (!dd)
- return 1; /* error message is already logged */
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ return 1; /* error message is already logged */
- map_crash_data_t pCrashData;
- load_crash_data_from_crash_dump_dir(dd, pCrashData);
- dd_close(dd);
+ crash_data_t *crash_data = create_crash_data_from_dump_dir(dd);
+ dd_close(dd);
- char *dsc = make_description_logger(pCrashData);
- fputs(dsc, stdout);
- free(dsc);
- }
- catch (CABRTException& e)
- {
- log("%s", e.what());
- return 1;
- }
+ char *dsc = make_description_logger(crash_data);
+ fputs(dsc, stdout);
+ free(dsc);
+ free_crash_data(crash_data);
if (output_file)
{
diff --git a/src/plugins/abrt-action-rhtsupport.cpp b/src/plugins/abrt-action-rhtsupport.c
index eb69489d..94523e08 100644
--- a/src/plugins/abrt-action-rhtsupport.cpp
+++ b/src/plugins/abrt-action-rhtsupport.c
@@ -22,21 +22,19 @@
#include "abrt_curl.h"
#include "abrt_xmlrpc.h"
#include "abrt_rh_support.h"
-#include "abrt_crash_dump.h"
-#include "abrt_exception.h"
+#include "parse_options.h"
#define PROGNAME "abrt-action-rhtsupport"
static void report_to_rhtsupport(
const char *dump_dir_name,
- const map_plugin_settings_t& settings)
+ map_string_h *settings)
{
struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
exit(1); /* error msg is already logged by dd_opendir */
- map_crash_data_t pCrashData;
- load_crash_data_from_crash_dump_dir(dd, pCrashData);
+ crash_data_t *crash_data = create_crash_data_from_dump_dir(dd);
dd_close(dd);
/* Gzipping e.g. 0.5gig coredump takes a while. Let client know what we are doing */
@@ -54,34 +52,31 @@ static void report_to_rhtsupport(
const char* package;
char* env;
- map_plugin_settings_t::const_iterator end = settings.end();
- map_plugin_settings_t::const_iterator it;
-
env = getenv("RHTSupport_URL");
- it = settings.find("URL");
- char *url = xstrdup(env ? env : it == end ? "https://api.access.redhat.com/rs" : it->second.c_str());
+ char *url = xstrdup(env ? env : (get_map_string_item_or_NULL(settings, "URL") ? : "https://api.access.redhat.com/rs"));
env = getenv("RHTSupport_Login");
- it = settings.find("Login");
- char *login = xstrdup(env ? env : it == end ? "" : it->second.c_str());
+ char *login = xstrdup(env ? env : get_map_string_item_or_empty(settings, "Login"));
env = getenv("RHTSupport_Password");
- it = settings.find("Password");
- char *password = xstrdup(env ? env : it == end ? "" : it->second.c_str());
+ char *password = xstrdup(env ? env : get_map_string_item_or_empty(settings, "Password"));
env = getenv("RHTSupport_SSLVerify");
- it = settings.find("SSLVerify");
- bool ssl_verify = string_to_bool(env ? env : it == end ? "1" : it->second.c_str());
+ bool ssl_verify = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SSLVerify"));
if (!login[0] || !password[0])
{
- errmsg = _("Empty login or password, please check RHTSupport.conf");
- goto ret;
+ free_crash_data(crash_data);
+ free(url);
+ free(login);
+ free(password);
+ error_msg_and_die(_("Empty login or password, please check RHTSupport.conf"));
+ return;
}
- package = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_PACKAGE);
- reason = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_REASON);
- function = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_CRASH_FUNCTION);
+ package = get_crash_item_content_or_NULL(crash_data, FILENAME_PACKAGE);
+ reason = get_crash_item_content_or_NULL(crash_data, FILENAME_REASON);
+ function = get_crash_item_content_or_NULL(crash_data, FILENAME_CRASH_FUNCTION);
{
struct strbuf *buf_summary = strbuf_new();
@@ -92,7 +87,7 @@ static void report_to_rhtsupport(
strbuf_append_strf(buf_summary, ": %s", reason);
summary = strbuf_free_nobuf(buf_summary);
- char *bz_dsc = make_description_bz(pCrashData);
+ char *bz_dsc = make_description_bz(crash_data);
dsc = xasprintf("abrt version: "VERSION"\n%s", bz_dsc);
free(bz_dsc);
}
@@ -100,7 +95,7 @@ static void report_to_rhtsupport(
file = new_reportfile();
/* SELinux guys are not happy with /tmp, using /var/run/abrt */
- tempfile = xasprintf(LOCALSTATEDIR"/run/abrt/tmp-%lu-%lu.tar.gz", (long)getpid(), (long)time(NULL));
+ tempfile = xasprintf(LOCALSTATEDIR"/run/abrt/tmp-%s-%lu.tar.gz", iso_date_string(NULL), (long)getpid());
int pipe_from_parent_to_child[2];
xpipe(pipe_from_parent_to_child);
@@ -124,21 +119,23 @@ static void report_to_rhtsupport(
}
{
- map_crash_data_t::const_iterator it = pCrashData.begin();
- for (; it != pCrashData.end(); it++)
+ GHashTableIter iter;
+ char *name;
+ struct crash_item *value;
+ g_hash_table_iter_init(&iter, crash_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
{
- if (it->first == FILENAME_COUNT) continue;
- if (it->first == CD_DUMPDIR) continue;
- if (it->first == FILENAME_INFORMALL) continue;
- if (it->first == FILENAME_MESSAGE) continue; // plugin's status message (if we already reported it yesterday)
- if (it->first == FILENAME_DESCRIPTION) continue; // package description
-
- const char *content = it->second[CD_CONTENT].c_str();
- if (it->second[CD_TYPE] == CD_TXT)
+ if (strcmp(name, FILENAME_COUNT) == 0) continue;
+ if (strcmp(name, CD_DUMPDIR) == 0) continue;
+ if (strcmp(name, FILENAME_INFORMALL) == 0) continue;
+ if (strcmp(name, FILENAME_MESSAGE) == 0) continue; // plugin's status message (if we already reported it yesterday)
+
+ const char *content = value->content;
+ if (value->flags & CD_FLAG_TXT)
{
- reportfile_add_binding_from_string(file, it->first.c_str(), content);
+ reportfile_add_binding_from_string(file, name, content);
}
- else if (it->second[CD_TYPE] == CD_BIN)
+ else if (value->flags & CD_FLAG_BIN)
{
const char *basename = strrchr(content, '/');
if (basename)
@@ -148,7 +145,7 @@ static void report_to_rhtsupport(
char *xml_name = concat_path_file("content", basename);
reportfile_add_binding_from_namedfile(file,
/*on_disk_filename */ content,
- /*binding_name */ it->first.c_str(),
+ /*binding_name */ name,
/*recorded_filename*/ xml_name,
/*binary */ 1);
if (tar_append_file(tar, (char*)content, xml_name) != 0)
@@ -248,6 +245,7 @@ static void report_to_rhtsupport(
free(url);
free(login);
free(password);
+ free_crash_data(crash_data);
if (errmsg)
error_msg_and_die("%s", errmsg);
@@ -259,59 +257,50 @@ int main(int argc, char **argv)
if (env_verbose)
g_verbose = atoi(env_verbose);
- map_plugin_settings_t settings;
-
+ map_string_h *settings = new_map_string();
const char *dump_dir_name = ".";
+ GList *conf_file = NULL;
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ PROGNAME" [-vs] -c CONFFILE -d DIR"
+ "\n"
+ "\nReport a crash to RHTSupport"
+ );
enum {
- OPT_s = (1 << 0),
+ OPT_v = 1 << 0,
+ OPT_s = 1 << 1,
+ OPT_d = 1 << 2,
+ OPT_c = 1 << 3,
};
- int optflags = 0;
- int opt;
- while ((opt = getopt(argc, argv, "c:d:vs")) != -1)
- {
- switch (opt)
- {
- case 'c':
- VERB1 log("Loading settings from '%s'", optarg);
- LoadPluginSettings(optarg, settings);
- VERB3 log("Loaded '%s'", optarg);
- break;
- case 'd':
- dump_dir_name = optarg;
- break;
- case 'v':
- g_verbose++;
- break;
- case 's':
- optflags |= OPT_s;
- break;
- default:
- /* Careful: the string below contains tabs, dont replace with spaces */
- error_msg_and_die(
- "Usage: "PROGNAME" -c CONFFILE -d DIR [-vs]"
- "\n"
- "\nReport a crash to RHTSupport"
- "\n"
- "\nOptions:"
- "\n -c FILE Configuration file (may be given many times)"
- "\n -d DIR Crash dump directory"
- "\n -v Verbose"
- "\n -s Log to syslog"
- );
- }
- }
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_BOOL( 's', NULL, NULL , _("Log to syslog")),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Crash dump directory")),
+ OPT_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file (may be given many times)")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
-
//DONT! our stdout/stderr goes directly to daemon, don't want to have prefix there.
// msg_prefix = xasprintf(PROGNAME"[%u]", getpid());
-
- if (optflags & OPT_s)
+ if (opts & OPT_s)
{
openlog(msg_prefix, 0, LOG_DAEMON);
logmode = LOGMODE_SYSLOG;
}
+ while (conf_file)
+ {
+ char *fn = (char *)conf_file->data;
+ VERB1 log("Loading settings from '%s'", fn);
+ load_conf_file(fn, settings, /*skip key w/o values:*/ true);
+ VERB3 log("Loaded '%s'", fn);
+ conf_file = g_list_remove(conf_file, fn);
+ }
+
VERB1 log("Initializing XML-RPC library");
xmlrpc_env env;
xmlrpc_env_init(&env);
@@ -320,14 +309,8 @@ int main(int argc, char **argv)
error_msg_and_die("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code);
xmlrpc_env_clean(&env);
- try
- {
- report_to_rhtsupport(dump_dir_name, settings);
- }
- catch (CABRTException& e)
- {
- error_msg_and_die("%s", e.what());
- }
+ report_to_rhtsupport(dump_dir_name, settings);
+ free_map_string(settings);
return 0;
}
diff --git a/src/plugins/abrt-action-upload.cpp b/src/plugins/abrt-action-upload.c
index 9741f543..88380bd7 100644
--- a/src/plugins/abrt-action-upload.cpp
+++ b/src/plugins/abrt-action-upload.c
@@ -21,8 +21,6 @@
#include <curl/curl.h>
#include "abrtlib.h"
#include "parse_options.h"
-#include "abrt_crash_dump.h"
-#include "abrt_exception.h"
#define PROGNAME "abrt-action-upload"
@@ -104,7 +102,7 @@ static int send_file(const char *url, const char *filename)
static int create_and_upload_archive(
const char *dump_dir_name,
- const map_plugin_settings_t& settings)
+ map_string_h *settings)
{
int result = 0;
@@ -125,16 +123,12 @@ static int create_and_upload_archive(
//ArchiveType = .tar.bz2
//ExcludeFiles = foo,bar*,b*z
char* env;
- map_plugin_settings_t::const_iterator end = settings.end();
- map_plugin_settings_t::const_iterator it;
-
env = getenv("Upload_URL");
- it = settings.find("URL");
- const char *url = (env ? env : (it == end ? NULL : it->second.c_str()));
+ const char *url = (env ? env : get_map_string_item_or_empty(settings, "URL"));
/* Create a child gzip which will compress the data */
/* SELinux guys are not happy with /tmp, using /var/run/abrt */
- tempfile = xasprintf(LOCALSTATEDIR"/run/abrt/tmp-%lu-%lu.tar.gz", (long)getpid(), (long)time(NULL));
+ tempfile = xasprintf(LOCALSTATEDIR"/run/abrt/upload-%s-%lu.tar.gz", iso_date_string(NULL), (long)getpid());
int pipe_from_parent_to_child[2];
xpipe(pipe_from_parent_to_child);
child = fork();
@@ -167,7 +161,6 @@ static int create_and_upload_archive(
if (strcmp(short_name, CD_DUMPDIR) == 0) goto next;
if (strcmp(short_name, FILENAME_INFORMALL) == 0) goto next;
if (strcmp(short_name, FILENAME_MESSAGE) == 0) goto next; // plugin's status message (if we already reported it yesterday)
- if (strcmp(short_name, FILENAME_DESCRIPTION) == 0) goto next; // package description
// dd_get_next_file guarantees this:
//struct stat stbuf;
//if (stat(full_name, &stbuf) != 0)
@@ -249,7 +242,8 @@ int main(int argc, char **argv)
const char *conf_file = NULL;
const char *url = NULL;
- const char *program_usage = _(
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
PROGNAME" [-v] -d DIR [-c CONFFILE] [-u URL]\n"
"\n"
"Upload compressed tarball of crash dump"
@@ -268,32 +262,24 @@ int main(int argc, char **argv)
OPT_STRING('u', NULL, &url , "URL" , _("Base URL to upload to")),
OPT_END()
};
-
- /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage);
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
//msg_prefix = PROGNAME;
- //if (optflags & OPT_s)
+ //if (opts & OPT_s)
//{
// openlog(msg_prefix, 0, LOG_DAEMON);
// logmode = LOGMODE_SYSLOG;
//}
- map_plugin_settings_t settings;
+ map_string_h *settings = new_map_string();
if (url)
- settings["URL"] = url;
+ g_hash_table_replace(settings, xstrdup("URL"), xstrdup(url));
if (conf_file)
- LoadPluginSettings(conf_file, settings);
+ load_conf_file(conf_file, settings, /*skip key w/o values:*/ true);
- int result = 0;
- try
- {
- result = create_and_upload_archive(dump_dir_name, settings);
- }
- catch (CABRTException& e)
- {
- error_msg_and_die("%s", e.what());
- }
+ int result = create_and_upload_archive(dump_dir_name, settings);
+ free_map_string(settings);
return result;
}
diff --git a/src/plugins/abrt-dump-oops.c b/src/plugins/abrt-dump-oops.c
new file mode 100644
index 00000000..5b6a2062
--- /dev/null
+++ b/src/plugins/abrt-dump-oops.c
@@ -0,0 +1,710 @@
+/*
+ Copyright (C) 2011 ABRT team
+ Copyright (C) 2011 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ Authors:
+ Anton Arapov <anton@redhat.com>
+ Arjan van de Ven <arjan@linux.intel.com>
+ */
+#include <syslog.h>
+#include <asm/unistd.h> /* __NR_syslog */
+#include <sys/inotify.h>
+#include <sys/ioctl.h> /* ioctl(FIONREAD) */
+#include "abrtlib.h"
+#include "parse_options.h"
+
+#define PROGNAME "abrt-dump-oops"
+
+static void queue_oops(GList **vec, const char *data, const char *version)
+{
+ char *ver_data = xasprintf("%s\n%s", version, data);
+ *vec = g_list_append(*vec, ver_data);
+}
+
+/*
+ * extract_version tries to find the kernel version in given data
+ */
+static char *extract_version(const char *linepointer)
+{
+ if (strstr(linepointer, "Pid")
+ || strstr(linepointer, "comm")
+ || strstr(linepointer, "CPU")
+ || strstr(linepointer, "REGS")
+ || strstr(linepointer, "EFLAGS")
+ ) {
+ char* start;
+ char* end;
+
+ start = strstr((char*)linepointer, "2.6.");
+ if (start)
+ {
+ end = strchr(start, ')');
+ if (!end)
+ end = strchrnul(start, ' ');
+ return xstrndup(start, end-start);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * extract_oops tries to find oops signatures in a log
+ */
+struct line_info {
+ char *ptr;
+ char level;
+};
+
+static int record_oops(GList **oopses, struct line_info* lines_info, int oopsstart, int oopsend)
+{
+ int q;
+ int len;
+ char *oops;
+ char *version;
+
+ len = 2;
+ for (q = oopsstart; q <= oopsend; q++)
+ len += strlen(lines_info[q].ptr) + 1;
+
+ oops = (char*)xzalloc(len);
+
+ version = NULL;
+ for (q = oopsstart; q <= oopsend; q++)
+ {
+ if (!version)
+ version = extract_version(lines_info[q].ptr);
+
+ if (lines_info[q].ptr[0])
+ {
+ strcat(oops, lines_info[q].ptr);
+ strcat(oops, "\n");
+ }
+ }
+ int rv = 1;
+ /* too short oopses are invalid */
+ if (strlen(oops) > 100)
+ queue_oops(oopses, oops, version ? version : "undefined");
+ else
+ {
+ VERB3 log("Dropped oops: too short");
+ rv = 0;
+ }
+ free(oops);
+ free(version);
+ return rv;
+}
+
+#define REALLOC_CHUNK 1000
+static int extract_oopses(GList **oopses, char *buffer, size_t buflen)
+{
+ char *c;
+ int linecount = 0;
+ int lines_info_alloc = 0;
+ struct line_info *lines_info = NULL;
+
+ /* Split buffer into lines */
+
+ if (buflen != 0)
+ buffer[buflen - 1] = '\n'; /* the buffer usually ends with \n, but let's make sure */
+ c = buffer;
+ while (c < buffer + buflen)
+ {
+ char linelevel;
+ char *c9;
+ char *colon;
+
+ c9 = (char*)memchr(c, '\n', buffer + buflen - c); /* a \n will always be found */
+ assert(c9);
+ *c9 = '\0'; /* turn the \n into a string termination */
+ if (c9 == c)
+ goto next_line;
+
+ /* Is it a syslog file (/var/log/messages or similar)?
+ * Even though _usually_ it looks like "Nov 19 12:34:38 localhost kernel: xxx",
+ * some users run syslog in non-C locale:
+ * "2010-02-22T09:24:08.156534-08:00 gnu-4 gnome-session[2048]: blah blah"
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ !!!
+ * We detect it by checking for N:NN:NN pattern in first 15 chars
+ * (and this still is not good enough... false positive: "pci 0000:15:00.0: PME# disabled")
+ */
+ colon = strchr(c, ':');
+ if (colon && colon > c && colon < c + 15
+ && isdigit(colon[-1]) /* N:... */
+ && isdigit(colon[1]) /* ...N:NN:... */
+ && isdigit(colon[2])
+ && colon[3] == ':'
+ && isdigit(colon[4]) /* ...N:NN:NN... */
+ && isdigit(colon[5])
+ ) {
+ /* It's syslog file, not a bare dmesg */
+
+ /* Skip non-kernel lines */
+ char *kernel_str = strstr(c, "kernel: ");
+ if (kernel_str == NULL)
+ {
+ /* if we see our own marker:
+ * "hostname abrt: Kerneloops: Reported 1 kernel oopses to Abrt"
+ * we know we submitted everything upto here already */
+ if (strstr(c, "abrt:") && strstr(c, "Abrt"))
+ {
+ VERB3 log("Found our marker at line %d, restarting line count from 0", linecount);
+ linecount = 0;
+ lines_info_alloc = 0;
+ free(lines_info);
+ lines_info = NULL;
+ }
+ goto next_line;
+ }
+ c = kernel_str + sizeof("kernel: ")-1;
+ }
+
+ linelevel = 0;
+ /* store and remove kernel log level */
+ if (*c == '<' && c[1] && c[2] == '>')
+ {
+ linelevel = c[1];
+ c += 3;
+ }
+ /* remove jiffies time stamp counter if present */
+ if (*c == '[')
+ {
+ char *c2 = strchr(c, '.');
+ char *c3 = strchr(c, ']');
+ if (c2 && c3 && (c2 < c3) && (c3-c) < 14 && (c2-c) < 8)
+ {
+ c = c3 + 1;
+ if (*c == ' ')
+ c++;
+ }
+ }
+ if (linecount >= lines_info_alloc)
+ {
+ lines_info_alloc += REALLOC_CHUNK;
+ lines_info = (struct line_info*)xrealloc(lines_info,
+ lines_info_alloc * sizeof(lines_info[0]));
+ }
+ lines_info[linecount].ptr = c;
+ lines_info[linecount].level = linelevel;
+ linecount++;
+next_line:
+ c = c9 + 1;
+ }
+
+ /* Analyze lines */
+
+ int i;
+ char prevlevel = 0;
+ int oopsstart = -1;
+ int inbacktrace = 0;
+ int oopsesfound = 0;
+
+ i = 0;
+ while (i < linecount)
+ {
+ char *curline = lines_info[i].ptr;
+
+ if (curline == NULL)
+ {
+ i++;
+ continue;
+ }
+ while (*curline == ' ')
+ curline++;
+
+ if (oopsstart < 0)
+ {
+ /* Find start-of-oops markers */
+ /* In some comparisons, we skip 1st letter, to avoid dealing with
+ * changes in capitalization in kernel. For example, I see that
+ * current kernel git (at 2011-01-01) has both "kernel BUG at ..."
+ * and "Kernel BUG at ..." messages, and I don't want to change
+ * the code below whenever kernel is changed to use "K" (or "k")
+ * uniformly.
+ */
+ if (strstr(curline, /*g*/ "eneral protection fault:"))
+ oopsstart = i;
+ else if (strstr(curline, "BUG:"))
+ oopsstart = i;
+ else if (strstr(curline, /*k*/ "ernel BUG at"))
+ oopsstart = i;
+ else if (strstr(curline, "do_IRQ: stack overflow:"))
+ oopsstart = i;
+ else if (strstr(curline, "RTNL: assertion failed"))
+ oopsstart = i;
+ else if (strstr(curline, /*e*/ "eek! page_mapcount(page) went negative!"))
+ oopsstart = i;
+ else if (strstr(curline, /*n*/ "ear stack overflow (cur:"))
+ oopsstart = i;
+ else if (strstr(curline, /*d*/ "ouble fault:"))
+ oopsstart = i;
+ else if (strstr(curline, /*b*/ "adness at"))
+ oopsstart = i;
+ else if (strstr(curline, "NETDEV WATCHDOG"))
+ oopsstart = i;
+ else if (strstr(curline, "WARNING: at ")) /* WARN_ON() generated message */
+ oopsstart = i;
+ else if (strstr(curline, /*u*/ "nable to handle kernel"))
+ oopsstart = i;
+ else if (strstr(curline, /*s*/ "ysctl table check failed"))
+ oopsstart = i;
+ else if (strstr(curline, "INFO: possible recursive locking detected"))
+ oopsstart = i;
+ // Not needed: "--[ cut here ]--" is always followed
+ // by "Badness at", "kernel BUG at", or "WARNING: at" string
+ //else if (strstr(curline, "------------[ cut here ]------------"))
+ // oopsstart = i;
+ else if (strstr(curline, "list_del corruption"))
+ oopsstart = i;
+ else if (strstr(curline, "list_add corruption"))
+ oopsstart = i;
+
+ if (i >= 3 && strstr(curline, "Oops:"))
+ oopsstart = i-3;
+
+ if (oopsstart >= 0)
+ {
+ /* debug information */
+ VERB3 {
+ log("Found oops at line %d: '%s'", oopsstart, lines_info[oopsstart].ptr);
+ if (oopsstart != i)
+ log("Trigger line is %d: '%s'", i, c);
+ }
+ /* try to find the end marker */
+ int i2 = i + 1;
+ while (i2 < linecount && i2 < (i+50))
+ {
+ if (strstr(lines_info[i2].ptr, "---[ end trace"))
+ {
+ inbacktrace = 1;
+ i = i2;
+ break;
+ }
+ i2++;
+ }
+ }
+ }
+
+ /* Are we entering a call trace part? */
+ /* a call trace starts with "Call Trace:" or with the " [<.......>] function+0xFF/0xAA" pattern */
+ if (oopsstart >= 0 && !inbacktrace)
+ {
+ if (strstr(curline, "Call Trace:"))
+ inbacktrace = 1;
+ else
+ if (strnlen(curline, 9) > 8
+ && curline[0] == '[' && curline[1] == '<'
+ && strstr(curline, ">]")
+ && strstr(curline, "+0x")
+ && strstr(curline, "/0x")
+ ) {
+ inbacktrace = 1;
+ }
+ }
+
+ /* Are we at the end of an oops? */
+ else if (oopsstart >= 0 && inbacktrace)
+ {
+ int oopsend = INT_MAX;
+
+ /* line needs to start with " [" or have "] [" if it is still a call trace */
+ /* example: "[<ffffffffa006c156>] radeon_get_ring_head+0x16/0x41 [radeon]" */
+ if (curline[0] != '['
+ && !strstr(curline, "] [")
+ && !strstr(curline, "--- Exception")
+ && !strstr(curline, "LR =")
+ && !strstr(curline, "<#DF>")
+ && !strstr(curline, "<IRQ>")
+ && !strstr(curline, "<EOI>")
+ && !strstr(curline, "<<EOE>>")
+ && strncmp(curline, "Code: ", 6) != 0
+ && strncmp(curline, "RIP ", 4) != 0
+ && strncmp(curline, "RSP ", 4) != 0
+ ) {
+ oopsend = i-1; /* not a call trace line */
+ }
+ /* oops lines are always more than 8 chars long */
+ else if (strnlen(curline, 8) < 8)
+ oopsend = i-1;
+ /* single oopses are of the same loglevel */
+ else if (lines_info[i].level != prevlevel)
+ oopsend = i-1;
+ else if (strstr(curline, "Instruction dump:"))
+ oopsend = i;
+ /* if a new oops starts, this one has ended */
+ else if (strstr(curline, "WARNING: at ") && oopsstart != i) /* WARN_ON() generated message */
+ oopsend = i-1;
+ else if (strstr(curline, "Unable to handle") && oopsstart != i)
+ oopsend = i-1;
+ /* kernel end-of-oops marker (not including marker itself) */
+ else if (strstr(curline, "---[ end trace"))
+ oopsend = i-1;
+
+ if (oopsend <= i)
+ {
+ VERB3 log("End of oops at line %d (%d): '%s'", oopsend, i, lines_info[oopsend].ptr);
+ if (record_oops(oopses, lines_info, oopsstart, oopsend))
+ oopsesfound++;
+ oopsstart = -1;
+ inbacktrace = 0;
+ }
+ }
+
+ prevlevel = lines_info[i].level;
+ i++;
+
+ if (oopsstart >= 0)
+ {
+ /* Do we have a suspiciously long oops? Cancel it */
+ if (i-oopsstart > 60)
+ {
+ inbacktrace = 0;
+ oopsstart = -1;
+ VERB3 log("Dropped oops, too long");
+ continue;
+ }
+ if (!inbacktrace && i-oopsstart > 40)
+ {
+ /*inbacktrace = 0; - already is */
+ oopsstart = -1;
+ VERB3 log("Dropped oops, too long");
+ continue;
+ }
+ }
+ } /* while (i < linecount) */
+
+ /* process last oops if we have one */
+ if (oopsstart >= 0 && inbacktrace)
+ {
+ int oopsend = i-1;
+ VERB3 log("End of oops at line %d (end of file): '%s'", oopsend, lines_info[oopsend].ptr);
+ if (record_oops(oopses, lines_info, oopsstart, oopsend))
+ oopsesfound++;
+ }
+
+ free(lines_info);
+ return oopsesfound;
+}
+
+#define MAX_SCAN_BLOCK (4*1024*1024)
+#define READ_AHEAD (10*1024)
+
+static void scan_dmesg(GList **oops_list)
+{
+ VERB1 log("Scanning dmesg");
+
+ /* syslog(3) - read the last len bytes from the log buffer
+ * (non-destructively), but dont read more than was written
+ * into the buffer since the last "clear ring buffer" cmd.
+ * Returns the number of bytes read.
+ */
+ char *buffer = xzalloc(16*1024);
+ syscall(__NR_syslog, 3, buffer, 16*1024 - 1); /* always NUL terminated */
+ extract_oopses(oops_list, buffer, strlen(buffer));
+ free(buffer);
+}
+
+static int scan_syslog_file(GList **oops_list, int fd, struct stat *statbuf, int partial_line_len)
+{
+ /* fstat(fd, &statbuf) was just done by caller */
+
+ off_t cur_pos = lseek(fd, 0, SEEK_CUR);
+ if (statbuf->st_size <= cur_pos)
+ return partial_line_len; /* we are at EOF, nothing to do */
+
+ VERB3 log("File grew by %llu bytes, from %llu to %llu",
+ (long long)(statbuf->st_size - cur_pos),
+ (long long)(cur_pos),
+ (long long)(statbuf->st_size));
+
+ /* Do not try to allocate an absurd amount of memory. */
+ int sz = MAX_SCAN_BLOCK - READ_AHEAD;
+ if (sz > statbuf->st_size - cur_pos)
+ sz = statbuf->st_size - cur_pos;
+
+ /* Rewind to the beginning of the current line */
+ if (partial_line_len > 0 && lseek(fd, -partial_line_len, SEEK_CUR) != (off_t)-1)
+ {
+ VERB3 log("Went back %u bytes", partial_line_len);
+ cur_pos -= partial_line_len;
+ sz += partial_line_len;
+ }
+
+ /*
+ * In theory we have a race here, since someone can spew
+ * to /var/log/messages before we read it in...
+ * We try to deal with it by reading READ_AHEAD extra.
+ */
+ sz += READ_AHEAD;
+ char *buffer = xzalloc(sz);
+
+ partial_line_len = 0;
+ do {
+ int r = full_read(fd, buffer, sz-1);
+ if (r <= 0)
+ break;
+ VERB3 log("Read %u bytes", r);
+
+ /* For future scans, try to find where last (incomplete) line starts */
+ partial_line_len = 0;
+ char *last_newline = memrchr(buffer, '\n', r) ? : buffer-1;
+ partial_line_len = buffer+r - (last_newline+1);
+ if (partial_line_len > 500) /* cap it */
+ partial_line_len = 500;
+
+ extract_oopses(oops_list, buffer, r);
+ cur_pos += r;
+ } while (cur_pos < statbuf->st_size);
+
+ free(buffer);
+
+ return partial_line_len;
+}
+
+/* returns number of errors */
+static int save_oops_to_dump_dir(GList *oops_list, unsigned oops_cnt)
+{
+ unsigned countdown = 16; /* do not report hundreds of oopses */
+ unsigned idx = oops_cnt;
+ time_t t = time(NULL);
+ pid_t my_pid = getpid();
+
+ VERB1 log("Saving %u oopses as crash dump dirs", idx >= countdown ? countdown-1 : idx);
+
+ char *tainted_str = NULL;
+ /* once tainted flag is set to 1, only restart can reset the flag to 0 */
+ FILE *tainted_fp = fopen("/proc/sys/kernel/tainted", "r");
+ if (tainted_fp)
+ {
+ tainted_str = xmalloc_fgetline(tainted_fp);
+ fclose(tainted_fp);
+ }
+ else
+ error_msg("/proc/sys/kernel/tainted does not exist");
+
+ int errors = 0;
+
+ while (idx != 0 && --countdown != 0)
+ {
+ char path[sizeof(DEBUG_DUMPS_DIR"/kerneloops-%lu-%lu-%lu") + 3 * sizeof(long)*3];
+ sprintf(path, DEBUG_DUMPS_DIR"/kerneloops-%lu-%lu-%lu", (long)t, (long)my_pid, (long)idx);
+
+ char *first_line = (char*)g_list_nth_data(oops_list, --idx);
+ char *second_line = (char*)strchr(first_line, '\n'); /* never NULL */
+ *second_line++ = '\0';
+
+ struct dump_dir *dd = dd_create(path, /*uid:*/ 0);
+ if (dd)
+ {
+ dd_create_basic_files(dd, /*uid:*/ 0);
+ dd_save_text(dd, FILENAME_ANALYZER, "Kerneloops");
+ dd_save_text(dd, FILENAME_EXECUTABLE, "kernel");
+ dd_save_text(dd, FILENAME_KERNEL, first_line);
+ dd_save_text(dd, FILENAME_CMDLINE, "not_applicable");
+ dd_save_text(dd, FILENAME_BACKTRACE, second_line);
+ /* Optional, makes generated bz more informative */
+ strchrnul(second_line, '\n')[0] = '\0';
+ dd_save_text(dd, FILENAME_REASON, second_line);
+
+ if (tainted_str && tainted_str[0] != '0')
+ dd_save_text(dd, FILENAME_TAINTED, tainted_str);
+
+ free(tainted_str);
+ dd_close(dd);
+ }
+ else
+ errors++;
+ }
+
+ return errors;
+}
+
+int main(int argc, char **argv)
+{
+ char *env_verbose = getenv("ABRT_VERBOSE");
+ if (env_verbose)
+ g_verbose = atoi(env_verbose);
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ PROGNAME" [-vsrdow] FILE\n"
+ "\n"
+ "Extract oops from syslog/dmesg file"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_s = 1 << 1,
+ OPT_r = 1 << 2,
+ OPT_d = 1 << 3,
+ OPT_o = 1 << 4,
+ OPT_w = 1 << 5,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_BOOL('s', NULL, NULL, _("Log to syslog")),
+ OPT_BOOL('r', NULL, NULL, _("Parse kernel's message buffer before parsing FILE")),
+ OPT_BOOL('d', NULL, NULL, _("Create ABRT dump for every oops found")),
+ OPT_BOOL('o', NULL, NULL, _("Print found oopses on standard output")),
+ OPT_BOOL('w', NULL, NULL, _("Do not exit, watch the file for new oopses")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
+
+ putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
+ msg_prefix = PROGNAME;
+ if ((opts & OPT_s)
+ || getenv("ABRT_SYSLOG")
+ ) {
+ openlog(msg_prefix, 0, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ argv += optind;
+ if (!argv[0])
+ show_usage_and_die(program_usage_string, program_options);
+ const char *filename = argv[0];
+
+ int inotify_fd = -1;
+ if (opts & OPT_w)
+ {
+ inotify_fd = inotify_init();
+ if (inotify_fd == -1)
+ perror_msg_and_die("inotify_init failed");
+ //close_on_exec_on(inotify_fd);
+ }
+
+ GList *oops_list = NULL;
+ if (opts & OPT_r)
+ /* Scan dmesg (only once even with -w) */
+ scan_dmesg(&oops_list);
+
+ int partial_line_len = 0;
+ struct stat statbuf;
+ int file_fd = -1;
+ int wd = -1;
+
+ while (1) /* loops only if -w */
+ {
+ /* If file is already opened, parse oopses from current pos */
+ if (file_fd >= 0)
+ {
+ memset(&statbuf, 0, sizeof(statbuf));
+ fstat(file_fd, &statbuf);
+ partial_line_len = scan_syslog_file(&oops_list, file_fd, &statbuf, partial_line_len);
+
+ /* Was file deleted or replaced? */
+ ino_t fd_ino = statbuf.st_ino;
+ if (stat(filename, &statbuf) != 0 || statbuf.st_ino != fd_ino) /* yes */
+ {
+ VERB2 log("Can't stat '%s', closing fd", filename);
+ close(file_fd);
+ if (wd >= 0)
+ inotify_rm_watch(inotify_fd, wd);
+ file_fd = -1;
+ wd = -1;
+ partial_line_len = 0;
+ }
+ }
+
+ /* If file isn't opened, try to open it and parse oopses */
+ if (file_fd < 0)
+ {
+ file_fd = open(filename, O_RDONLY);
+ if (file_fd < 0)
+ {
+ if (!(opts & OPT_w))
+ perror_msg_and_die("Can't open '%s'", filename);
+ /* with -w, we ignore open errors */
+ }
+ else
+ {
+ VERB2 log("Opened '%s'", filename);
+ /* For -w case, if we don't have inotify watch yet, open one */
+ if ((opts & OPT_w) && wd < 0)
+ {
+ wd = inotify_add_watch(inotify_fd, filename, IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF);
+ if (wd < 0)
+ perror_msg("inotify_add_watch failed on '%s'", filename);
+ else
+ VERB2 log("Added inotify watch for '%s'", filename);
+ }
+ if (fstat(file_fd, &statbuf) == 0)
+ {
+ /* If file is large, skip the beginning.
+ * IOW: ignore old log messages because they are unlikely
+ * to have sufficiently recent data to be useful.
+ */
+ if (statbuf.st_size > (MAX_SCAN_BLOCK - READ_AHEAD))
+ lseek(file_fd, statbuf.st_size - (MAX_SCAN_BLOCK - READ_AHEAD), SEEK_SET);
+ /* Note that statbuf is filled by fstat by now,
+ * scan_syslog_file needs that
+ */
+ partial_line_len = scan_syslog_file(&oops_list, file_fd, &statbuf, partial_line_len);
+ }
+ }
+ }
+
+ /* Print and/or save oopses */
+ int oops_cnt = g_list_length(oops_list);
+ if (!(opts & OPT_w) || oops_cnt != 0)
+ log("Found oopses: %d", oops_cnt);
+ if (oops_cnt != 0)
+ {
+ if (opts & OPT_o)
+ {
+ int i = 0;
+ while (i < oops_cnt)
+ printf("\nVersion: %s", (char*)g_list_nth_data(oops_list, i++));
+ }
+ if (opts & OPT_d)
+ {
+ log("Creating dump directories");
+ int errors = save_oops_to_dump_dir(oops_list, oops_cnt);
+ if (errors > 0)
+ log("%d errors while dumping oopses", errors);
+ }
+ }
+ list_free_with_free(oops_list);
+ oops_list = NULL;
+
+ /* Done if no -w */
+ if (!(opts & OPT_w))
+ break;
+
+ /* Even if log file grows all the time, say, a new line every 5 ms,
+ * we don't want to scan it all the time. Sleep a bit and let it grow
+ * in bigger increments.
+ * Sleep longer if file does not exist.
+ */
+ sleep(file_fd >= 0 ? 1 : 59);
+
+ /* Now wait for it to change, be moved or deleted */
+ if (wd >= 0)
+ {
+ char buf[4096];
+ VERB3 log("Waiting for '%s' to change", filename);
+ /* We block here: */
+ int len = read(inotify_fd, buf, sizeof(buf));
+ if (len < 0 && errno != EINTR) /* I saw EINTR here on strace attach */
+ perror_msg("Error reading inotify fd");
+ /* we don't actually check what happened to file -
+ * the code will handle all possibilities.
+ */
+ VERB3 log("Change in '%s' detected", filename);
+ }
+
+ } /* while (1) */
+
+ return 0;
+}
diff --git a/src/plugins/abrt-plugins.7 b/src/plugins/abrt-plugins.7
index 28de8f02..d8027057 100644
--- a/src/plugins/abrt-plugins.7
+++ b/src/plugins/abrt-plugins.7
@@ -33,7 +33,6 @@ stored in the \fI/etc/abrt/plugins\fP directory.
.IR abrt-Bugzilla (7),
.IR abrt-Upload (7),
.IR abrt-KerneloopsReporter (7),
-.IR abrt-KerneloopsScanner (7),
.IR abrt-Logger (7),
.IR abrt-Mailx (7),
.SH AUTHOR
diff --git a/src/plugins/abrt_rh_support.c b/src/plugins/abrt_rh_support.c
index 04e2c8ef..9a48485b 100644
--- a/src/plugins/abrt_rh_support.c
+++ b/src/plugins/abrt_rh_support.c
@@ -212,69 +212,6 @@ reportfile_free(reportfile_t* file)
//
-// post_signature()
-//
-char*
-post_signature(const char* baseURL, bool ssl_verify, const char* signature)
-{
- char *URL = concat_path_file(baseURL, "/signatures");
-
- abrt_post_state_t *state = new_abrt_post_state(0
- + ABRT_POST_WANT_HEADERS
- + ABRT_POST_WANT_BODY
- + ABRT_POST_WANT_ERROR_MSG
- + (ssl_verify ? ABRT_POST_WANT_SSL_VERIFY : 0)
- );
- int http_resp_code = abrt_post_string(state, URL, "application/xml", signature);
- free(URL);
-
- char *retval;
- const char *strata_msg;
- switch (http_resp_code)
- {
- case 200:
- case 201:
- if (state->body)
- {
- retval = state->body;
- state->body = NULL;
- break;
- }
- strata_msg = find_header_in_abrt_post_state(state, "Strata-Message:");
- if (strata_msg && strcmp(strata_msg, "CREATED") != 0) {
- retval = xstrdup(strata_msg);
- break;
- }
- retval = xstrdup("Signature submitted successfully");
- break;
-
- default:
- strata_msg = find_header_in_abrt_post_state(state, "Strata-Message:");
- if (strata_msg)
- {
- retval = xasprintf("Error (HTTP response %d): %s",
- http_resp_code,
- strata_msg);
- break;
- }
- if (state->curl_error_msg)
- {
- if (http_resp_code >= 0)
- retval = xasprintf("Error (HTTP response %d): %s", http_resp_code, state->curl_error_msg);
- else
- retval = xasprintf("Error in HTTP transaction: %s", state->curl_error_msg);
- break;
- }
- retval = xasprintf("Error (HTTP response %d), body:\n%s", http_resp_code, state->body);
- break;
- }
-
- free_abrt_post_state(state);
- return retval;
-}
-
-
-//
// send_report_to_new_case()
//
@@ -391,6 +328,15 @@ send_report_to_new_case(const char* baseURL,
char *case_location = find_header_in_abrt_post_state(case_state, "Location:");
switch (case_state->http_resp_code)
{
+ case 404:
+ /* Not strictly necessary (default branch would deal with it too),
+ * but makes this typical error less cryptic:
+ * instead of returning html-encoded body, we show short concise message,
+ * and show offending URL (typos in which is a typical cause) */
+ retval = xasprintf("error in case creation, "
+ "HTTP code: 404 (Not found), URL:'%s'", case_url);
+ break;
+
case 301: /* "301 Moved Permanently" (for example, used to move http:// to https://) */
case 302: /* "302 Found" (just in case) */
case 305: /* "305 Use Proxy" */
@@ -401,27 +347,16 @@ send_report_to_new_case(const char* baseURL,
free_abrt_post_state(case_state);
goto redirect_case;
}
- goto bad_resp_code;
-
- case 404:
- /* Not strictly necessary, but makes this typical error less cryptic:
- * instead of returning html-encoded body, we show short concise message,
- * and show offending URL (typos in which is a typical cause) */
- retval = xasprintf("error in case creation, "
- "HTTP code: 404 (Not found), URL:'%s'", case_url);
- break;
+ /* fall through */
default:
- bad_resp_code:
errmsg = case_state->curl_error_msg;
- if (errmsg)
+ if (errmsg && errmsg[0])
retval = xasprintf("error in case creation: %s", errmsg);
else
{
- errmsg = find_header_in_abrt_post_state(case_state, "Strata-Message:");
- if ((!errmsg || !errmsg[0]) && case_state->body && case_state->body[0])
- errmsg = case_state->body;
- if (errmsg)
+ errmsg = case_state->body;
+ if (errmsg && errmsg[0])
retval = xasprintf("error in case creation, HTTP code: %d, server says: '%s'",
case_state->http_resp_code, errmsg);
else
@@ -467,9 +402,7 @@ send_report_to_new_case(const char* baseURL,
default:
/* Case Creation Succeeded, attachement FAILED */
- errmsg = find_header_in_abrt_post_state(atch_state, "Strata-Message:");
- if (!errmsg || !errmsg[0])
- errmsg = atch_state->curl_error_msg;
+ errmsg = atch_state->curl_error_msg;
if (atch_state->body && atch_state->body[0])
{
if (errmsg && errmsg[0]
diff --git a/src/plugins/ccpp_events.conf b/src/plugins/ccpp_events.conf
new file mode 100644
index 00000000..1ad57608
--- /dev/null
+++ b/src/plugins/ccpp_events.conf
@@ -0,0 +1,17 @@
+EVENT=post-create analyzer=CCpp abrt-action-analyze-c
+
+#TODO: implement this (or add this functionality to abrt-action-install-debuginfo):
+#EVENT=analyze analyzer=CCpp backtrace= trim-debuginfo-cache /var/cache/abrt-di 4096m
+
+#TODO: can we still specify additional directories to search for debuginfos,
+# or was this ability lost with move to python installer?
+EVENT=analyze analyzer=CCpp backtrace= abrt-action-install-debuginfo --core="$DUMP_DIR/coredump"
+EVENT=analyze analyzer=CCpp backtrace= abrt-action-generate-backtrace
+
+# Same as "analyze", but executed when user requests "refresh" in GUI
+#EVENT=reanalyze analyzer=CCpp trim-debuginfo-cache /var/cache/abrt-di 4096m
+EVENT=reanalyze analyzer=CCpp abrt-action-install-debuginfo --core="$DUMP_DIR/coredump"
+EVENT=reanalyze analyzer=CCpp abrt-action-generate-backtrace
+
+EVENT=report_Bugzilla analyzer=CCpp abrt-action-bugzilla -c /etc/abrt/plugins/Bugzilla.conf
+EVENT=report_Logger analyzer=CCpp abrt-action-print -o /var/log/abrt.log
diff --git a/src/report-python/Makefile.am b/src/report-python/Makefile.am
new file mode 100644
index 00000000..a00e2d7b
--- /dev/null
+++ b/src/report-python/Makefile.am
@@ -0,0 +1,42 @@
+pyreportexecdir = $(pyexecdir)/report
+
+pyreportexec_PYTHON = \
+ __init__.py \
+ accountmanager.py
+
+pyreportexec_LTLIBRARIES = _pyreport.la
+
+_pyreport_la_SOURCES = \
+ reportmodule.c \
+ crash_data.c \
+ dump_dir.c \
+ run_event.c \
+ common.h
+_pyreport_la_CPPFLAGS = \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ $(GLIB_CFLAGS) \
+ $(PYTHON_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Werror
+_pyreport_la_LDFLAGS = \
+ -module \
+ -avoid-version \
+ -export-symbols-regex init_pyreport
+_pyreport_la_LIBADD = \
+ ../lib/libreport.la
+
+# report compat:
+
+pyreportioexecdir = $(pyexecdir)/report/io
+
+pyreportioexec_PYTHON = \
+ io/__init__.py \
+ io/GTKIO.py \
+ io/NewtIO.py \
+ io/TextIO.py
diff --git a/src/report-python/__init__.py b/src/report-python/__init__.py
new file mode 100644
index 00000000..0b0f5685
--- /dev/null
+++ b/src/report-python/__init__.py
@@ -0,0 +1,130 @@
+from _pyreport import *
+
+
+#Compatibility with report package:
+
+import os
+
+def createAlertSignature(component, hashmarkername, hashvalue, summary, alertSignature):
+
+ SYSTEM_RELEASE_PATHS = ["/etc/system-release","/etc/redhat-release"]
+ ####SYSTEM_RELEASE_DEPS = ["system-release", "redhat-release"]
+
+ _hardcoded_default_product = ""
+ _hardcoded_default_version = ""
+
+ ####def getProduct_fromPRODUCT():
+ #### try:
+ #### import product
+ #### return product.productName
+ #### except:
+ #### return ""
+
+ ####def getVersion_fromPRODUCT():
+ #### try:
+ #### import product
+ #### return product.productVersion
+ #### except:
+ #### return ""
+
+ ####def getProduct_fromRPM():
+ #### try:
+ #### import rpm
+ #### ts = rpm.TransactionSet()
+ #### for each_dep in SYSTEM_RELEASE_DEPS:
+ #### mi = ts.dbMatch('provides', each_dep)
+ #### for h in mi:
+ #### if h['name']:
+ #### return h['name'].split("-")[0].capitalize()
+ ####
+ #### return ""
+ #### except:
+ #### return ""
+
+ ####def getVersion_fromRPM():
+ #### try:
+ #### import rpm
+ #### ts = rpm.TransactionSet()
+ #### for each_dep in SYSTEM_RELEASE_DEPS:
+ #### mi = ts.dbMatch('provides', each_dep)
+ #### for h in mi:
+ #### if h['version']:
+ #### return str(h['version'])
+ #### return ""
+ #### except:
+ #### return ""
+
+ def getProduct_fromFILE():
+ for each_path in SYSTEM_RELEASE_PATHS:
+ try:
+ file = open(each_path, "r")
+ content = file.read()
+ if content.startswith("Red Hat Enterprise Linux"):
+ return "Red Hat Enterprise Linux"
+ if content.startswith("Fedora"):
+ return "Fedora"
+ i = content.find(" release")
+ if i > -1:
+ return content[0:i]
+ except:
+ pass
+ return ""
+
+ def getVersion_fromFILE():
+ for each_path in SYSTEM_RELEASE_PATHS:
+ try:
+ file = open(each_path, "r")
+ content = file.read()
+ if content.find("Rawhide") > -1:
+ return "rawhide"
+ clist = content.split(" ")
+ i = clist.index("release")
+ return clist[i+1]
+ except:
+ pass
+ return ""
+
+ def getProduct():
+ ####product = getProduct_fromPRODUCT()
+ ####if product:
+ #### return product
+ product = getProduct_fromFILE()
+ if product:
+ return product
+ ####product = getProduct_fromRPM()
+ ####if product:
+ #### return product
+ return _hardcoded_default_product
+
+ def getVersion():
+ ####version = getVersion_fromPRODUCT()
+ ####if version:
+ #### return version
+ version = getVersion_fromFILE()
+ if version:
+ return version
+ ####version = getVersion_fromRPM()
+ ####if version:
+ #### return version
+ return _hardcoded_default_version
+
+ cd = crash_data()
+ cd.add("component", component)
+ cd.add("hashmarkername", hashmarkername)
+ cd.add("localhash", hashvalue)
+ cd.add("summary", summary)
+ cd.add("description", alertSignature)
+ cd.add("product", getProduct())
+ cd.add("version", getVersion())
+ return cd
+
+def report(cd, io_unused):
+ #dd = cd.create_dump_dir()
+ #dir_name = dd.name
+ #dd.close()
+ #r = os.spawnlp(P_WAIT, "abrt-handle-crashdump", "abrt-handle-crashdump", "-d", dirname, "-e" , "report");
+ ### Silmpler alternative:
+ state = run_event_state()
+ #state.logging_callback = logfunc
+ r = state.run_event_on_crash_data(cd, "report")
+ return r
diff --git a/src/report-python/accountmanager.py b/src/report-python/accountmanager.py
new file mode 100644
index 00000000..db8ed117
--- /dev/null
+++ b/src/report-python/accountmanager.py
@@ -0,0 +1,6 @@
+"""
+ Compatibility with report package
+"""
+
+class AccountManager:
+ pass
diff --git a/src/daemon/Daemon.h b/src/report-python/common.h
index 158b48fb..d6d209e9 100644
--- a/src/daemon/Daemon.h
+++ b/src/report-python/common.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2009 Denys Vlasenko (dvlasenk@redhat.com)
+ Copyright (C) 2009 Abrt team.
Copyright (C) 2009 RedHat inc.
This program is free software; you can redistribute it and/or modify
@@ -16,21 +16,32 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef DAEMON_H_
-#define DAEMON_H_
-
-#include <pthread.h>
-#include "abrt_types.h"
-#include "abrt_crash_dump.h"
-
-class CCrashWatcher;
-class CCommLayerServer;
-class CPluginManager;
-
-/* Used for sending dbus signals */
-extern CCommLayerServer *g_pCommLayer;
-
-/* Collection of loaded plugins */
-extern CPluginManager* g_pPluginManager;
-
-#endif
+#include <Python.h>
+
+#include "dump_dir.h"
+#include "crash_data.h"
+#include "run_event.h"
+
+/* exception object */
+extern PyObject *ReportError;
+
+/* type objects */
+extern PyTypeObject p_crash_data_type;
+extern PyTypeObject p_dump_dir_type;
+extern PyTypeObject p_run_event_state_type;
+
+/* module-level functions */
+PyObject *p_dd_opendir(PyObject *module, PyObject *args);
+PyObject *p_dd_create(PyObject *module, PyObject *args);
+PyObject *p_delete_dump_dir(PyObject *pself, PyObject *args);
+
+/* python objects' struct defs */
+typedef struct {
+ PyObject_HEAD
+ struct dump_dir *dd;
+} p_dump_dir;
+
+typedef struct {
+ PyObject_HEAD
+ crash_data_t *cd;
+} p_crash_data;
diff --git a/src/report-python/crash_data.c b/src/report-python/crash_data.c
new file mode 100644
index 00000000..217560e5
--- /dev/null
+++ b/src/report-python/crash_data.c
@@ -0,0 +1,137 @@
+/*
+ Copyright (C) 2010 Abrt team.
+ Copyright (C) 2010 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <Python.h>
+#include <structmember.h>
+
+#include <errno.h>
+#include "common.h"
+
+static void
+p_crash_data_dealloc(PyObject *pself)
+{
+ p_crash_data *self = (p_crash_data*)pself;
+ free_crash_data(self->cd);
+ self->cd = NULL;
+ self->ob_type->tp_free(pself);
+}
+
+static PyObject *
+p_crash_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ p_crash_data *self = (p_crash_data *)type->tp_alloc(type, 0);
+ if (self)
+ self->cd = new_crash_data();
+ return (PyObject *)self;
+}
+
+//static int
+//p_crash_data_init(PyObject *pself, PyObject *args, PyObject *kwds)
+//{
+// return 0;
+//}
+
+/*
+void add_to_crash_data_ext(crash_data_t *crash_data,
+ const char *name,
+ const char *content,
+ unsigned flags);
+*/
+static PyObject *p_crash_data_add(PyObject *pself, PyObject *args)
+{
+ p_crash_data *self = (p_crash_data*)pself;
+
+ const char *name;
+ const char *content;
+ int flags = 0;
+ if (!PyArg_ParseTuple(args, "ss|i", &name, &content, &flags))
+ {
+ /* PyArg_ParseTuple raises the exception saying why it fails
+ * eg: TypeError: function takes exactly 2 arguments (1 given)
+ */
+ return NULL;
+ }
+ add_to_crash_data_ext(self->cd, name, content, flags);
+
+ /* every function returns PyObject, to return void we need to do this */
+ Py_RETURN_NONE;
+}
+
+/* struct crash_item *get_crash_data_item_or_NULL(crash_data_t *crash_data, const char *key); */
+static PyObject *p_get_crash_data_item(PyObject *pself, PyObject *args)
+{
+ p_crash_data *self = (p_crash_data*)pself;
+ const char *key;
+ if (!PyArg_ParseTuple(args, "s", &key))
+ {
+ return NULL;
+ }
+ struct crash_item *ci = get_crash_data_item_or_NULL(self->cd, key);
+ if (ci == NULL)
+ {
+ Py_RETURN_NONE;
+ }
+ return Py_BuildValue("sI", ci->content, ci->flags);
+}
+
+/* struct dump_dir *create_dump_dir_from_crash_data(crash_data_t *crash_data, const char *base_dir_name); */
+static PyObject *p_create_dump_dir_from_crash_data(PyObject *pself, PyObject *args)
+{
+ p_crash_data *self = (p_crash_data*)pself;
+ const char *base_dir_name = NULL;
+ if (!PyArg_ParseTuple(args, "|s", &base_dir_name))
+ {
+ return NULL;
+ }
+ p_dump_dir *new_dd = PyObject_New(p_dump_dir, &p_dump_dir_type);
+ if (!new_dd)
+ return NULL;
+ struct dump_dir *dd = create_dump_dir_from_crash_data(self->cd, base_dir_name);
+ if (!dd)
+ {
+ PyObject_Del((PyObject*)new_dd);
+ PyErr_SetString(ReportError, "Can't create the dump dir");
+ return NULL;
+ }
+ new_dd->dd = dd;
+ return (PyObject*)new_dd;
+}
+
+//static PyMemberDef p_crash_data_members[] = {
+// { NULL }
+//};
+
+static PyMethodDef p_crash_data_methods[] = {
+ /* method_name, func, flags, doc_string */
+ { "add" , p_crash_data_add , METH_VARARGS },
+ { "get" , p_get_crash_data_item , METH_VARARGS },
+ { "create_dump_dir", p_create_dump_dir_from_crash_data, METH_VARARGS },
+ { NULL }
+};
+
+PyTypeObject p_crash_data_type = {
+ PyObject_HEAD_INIT(NULL)
+ .tp_name = "report.crash_data",
+ .tp_basicsize = sizeof(p_crash_data),
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = p_crash_data_new,
+ .tp_dealloc = p_crash_data_dealloc,
+ //.tp_init = p_crash_data_init,
+ //.tp_members = p_crash_data_members,
+ .tp_methods = p_crash_data_methods,
+};
diff --git a/src/report-python/dump_dir.c b/src/report-python/dump_dir.c
new file mode 100644
index 00000000..c8ff3798
--- /dev/null
+++ b/src/report-python/dump_dir.c
@@ -0,0 +1,264 @@
+/*
+ On-disk storage of crash dumps
+
+ Copyright (C) 2010 Abrt team
+ Copyright (C) 2010 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <Python.h>
+#include <structmember.h>
+
+#include <errno.h>
+#include "common.h"
+
+/*** init/cleanup ***/
+
+static PyObject *
+p_dump_dir_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ p_dump_dir *self = (p_dump_dir *)type->tp_alloc(type, 0);
+ if (self)
+ self->dd = NULL;
+ return (PyObject *)self;
+}
+
+static void
+p_dump_dir_dealloc(PyObject *pself)
+{
+ p_dump_dir *self = (p_dump_dir*)pself;
+ dd_close(self->dd);
+ self->dd = NULL;
+ self->ob_type->tp_free(pself);
+}
+
+//static int
+//p_dump_dir_init(PyObject *pself, PyObject *args, PyObject *kwds)
+//{
+// return 0;
+//}
+
+
+/*** methods ***/
+
+/* void dd_close(struct dump_dir *dd); */
+static PyObject *p_dd_close(PyObject *pself, PyObject *args)
+{
+ p_dump_dir *self = (p_dump_dir*)pself;
+ dd_close(self->dd);
+ self->dd = NULL;
+ Py_RETURN_NONE;
+}
+
+/* void dd_delete(struct dump_dir *dd); */
+static PyObject *p_dd_delete(PyObject *pself, PyObject *args)
+{
+ p_dump_dir *self = (p_dump_dir*)pself;
+//Do we want to disallow delete() on non-opened dd?
+// if (!self->dd)
+// {
+// PyErr_SetString(ReportError, "dump dir is not open");
+// return NULL;
+// }
+ dd_delete(self->dd);
+ self->dd = NULL;
+ Py_RETURN_NONE;
+}
+
+/* int dd_exist(struct dump_dir *dd, const char *path); */
+static PyObject *p_dd_exist(PyObject *pself, PyObject *args)
+{
+ p_dump_dir *self = (p_dump_dir*)pself;
+ if (!self->dd)
+ {
+ PyErr_SetString(ReportError, "dump dir is not open");
+ return NULL;
+ }
+ const char *path;
+ if (!PyArg_ParseTuple(args, "s", &path))
+ {
+ return NULL;
+ }
+ return Py_BuildValue("i", dd_exist(self->dd, path));
+}
+
+/* DIR *dd_init_next_file(struct dump_dir *dd); */
+//static PyObject *p_dd_init_next_file(PyObject *pself, PyObject *args);
+/* int dd_get_next_file(struct dump_dir *dd, char **short_name, char **full_name); */
+//static PyObject *p_dd_get_next_file(PyObject *pself, PyObject *args);
+
+/* char* dd_load_text_ext(const struct dump_dir *dd, const char *name, unsigned flags); */
+/* char* dd_load_text(const struct dump_dir *dd, const char *name); */
+static PyObject *p_dd_load_text(PyObject *pself, PyObject *args)
+{
+ p_dump_dir *self = (p_dump_dir*)pself;
+ if (!self->dd)
+ {
+ PyErr_SetString(ReportError, "dump dir is not open");
+ return NULL;
+ }
+ const char *name;
+ int flags = 0;
+ if (!PyArg_ParseTuple(args, "s|i", &name, &flags))
+ {
+ return NULL;
+ }
+ char *val = dd_load_text_ext(self->dd, name, flags);
+ PyObject *obj = Py_BuildValue("s", val); /* NB: if val is NULL, obj is None */
+ free(val);
+ return obj;
+}
+
+/* void dd_save_text(struct dump_dir *dd, const char *name, const char *data); */
+static PyObject *p_dd_save_text(PyObject *pself, PyObject *args)
+{
+ p_dump_dir *self = (p_dump_dir*)pself;
+ if (!self->dd)
+ {
+ PyErr_SetString(ReportError, "dump dir is not open");
+ return NULL;
+ }
+ const char *name;
+ const char *data;
+ if (!PyArg_ParseTuple(args, "ss", &name, &data))
+ {
+ return NULL;
+ }
+ dd_save_text(self->dd, name, data);
+ Py_RETURN_NONE;
+}
+
+/* void dd_save_binary(struct dump_dir *dd, const char *name, const char *data, unsigned size); */
+static PyObject *p_dd_save_binary(PyObject *pself, PyObject *args)
+{
+ p_dump_dir *self = (p_dump_dir*)pself;
+ if (!self->dd)
+ {
+ PyErr_SetString(ReportError, "dump dir is not open");
+ return NULL;
+ }
+ const char *name;
+ const char *data;
+ unsigned size;
+ if (!PyArg_ParseTuple(args, "ssI", &name, &data, &size))
+ {
+ return NULL;
+ }
+ dd_save_binary(self->dd, name, data, size);
+ Py_RETURN_NONE;
+}
+
+
+/*** attribute getters/setters ***/
+
+static PyObject *get_name(PyObject *pself, void *unused)
+{
+ p_dump_dir *self = (p_dump_dir*)pself;
+ if (self->dd)
+ return Py_BuildValue("s", self->dd->dd_dirname);
+ Py_RETURN_NONE;
+}
+
+//static PyObject *set_name(PyObject *pself, void *unused)
+//{
+// PyErr_SetString(ReportError, "dump dir name is not settable");
+// Py_RETURN_NONE;
+//}
+
+
+/*** type object ***/
+
+static PyMethodDef p_dump_dir_methods[] = {
+ /* method_name, func, flags, doc_string */
+ { "close" , p_dd_close, METH_NOARGS, NULL },
+ { "delete" , p_dd_delete, METH_NOARGS, NULL },
+ { "exist" , p_dd_exist, METH_VARARGS, NULL },
+ { "load_text" , p_dd_load_text, METH_VARARGS, NULL },
+ { "save_text" , p_dd_save_text, METH_VARARGS, NULL },
+ { "save_binary", p_dd_save_binary, METH_VARARGS, NULL },
+ { NULL }
+};
+
+static PyGetSetDef p_dump_dir_getset[] = {
+ /* attr_name, getter_func, setter_func, doc_string, void_param */
+ { "name", get_name, NULL /*set_name*/ },
+ { NULL }
+};
+
+/* Support for "dd = dd_opendir(...); if [not] dd: ..." */
+static int p_dd_is_non_null(PyObject *pself)
+{
+ p_dump_dir *self = (p_dump_dir*)pself;
+ return self->dd != NULL;
+}
+static PyNumberMethods p_dump_dir_number_methods = {
+ .nb_nonzero = p_dd_is_non_null,
+};
+
+PyTypeObject p_dump_dir_type = {
+ PyObject_HEAD_INIT(NULL)
+ .tp_name = "report.dump_dir",
+ .tp_basicsize = sizeof(p_dump_dir),
+ /* Py_TPFLAGS_BASETYPE means "can be subtyped": */
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = p_dump_dir_new,
+ .tp_dealloc = p_dump_dir_dealloc,
+ //.tp_init = p_dump_dir_init,
+ //.tp_members = p_dump_dir_members,
+ .tp_methods = p_dump_dir_methods,
+ .tp_as_number = &p_dump_dir_number_methods,
+ .tp_getset = p_dump_dir_getset,
+};
+
+
+/*** module-level functions ***/
+
+/* struct dump_dir *dd_opendir(const char *dir, int flags); */
+PyObject *p_dd_opendir(PyObject *module, PyObject *args)
+{
+ const char *dir;
+ int flags = 0;
+ if (!PyArg_ParseTuple(args, "s|i", &dir, &flags))
+ return NULL;
+ p_dump_dir *new_dd = PyObject_New(p_dump_dir, &p_dump_dir_type);
+ if (!new_dd)
+ return NULL;
+ new_dd->dd = dd_opendir(dir, flags);
+ return (PyObject*)new_dd;
+}
+
+/* struct dump_dir *dd_create(const char *dir, uid_t uid); */
+PyObject *p_dd_create(PyObject *module, PyObject *args)
+{
+ const char *dir;
+ int uid = -1;
+ if (!PyArg_ParseTuple(args, "s|i", &dir, &uid))
+ return NULL;
+ p_dump_dir *new_dd = PyObject_New(p_dump_dir, &p_dump_dir_type);
+ if (!new_dd)
+ return NULL;
+ new_dd->dd = dd_create(dir, uid);
+ return (PyObject*)new_dd;
+}
+
+/* void delete_dump_dir(const char *dirname); */
+PyObject *p_delete_dump_dir(PyObject *pself, PyObject *args)
+{
+ const char *dirname;
+ if (!PyArg_ParseTuple(args, "s", &dirname))
+ return NULL;
+ delete_dump_dir(dirname);
+ Py_RETURN_NONE;
+}
diff --git a/src/report-python/io/GTKIO.py b/src/report-python/io/GTKIO.py
new file mode 100644
index 00000000..4cc8766e
--- /dev/null
+++ b/src/report-python/io/GTKIO.py
@@ -0,0 +1,11 @@
+"""
+ Compatibility with report package
+"""
+
+class GTKIO:
+ def __init__(self, loginManager = None):
+ pass
+
+#class FailDialog():
+# def __init__(self, title, message):
+# pass
diff --git a/src/report-python/io/NewtIO.py b/src/report-python/io/NewtIO.py
new file mode 100644
index 00000000..10eae284
--- /dev/null
+++ b/src/report-python/io/NewtIO.py
@@ -0,0 +1,7 @@
+"""
+ Compatibility with report package
+"""
+
+class NewtIO:
+ def __init__(self, screen = None):
+ pass
diff --git a/src/report-python/io/TextIO.py b/src/report-python/io/TextIO.py
new file mode 100644
index 00000000..6162fb8b
--- /dev/null
+++ b/src/report-python/io/TextIO.py
@@ -0,0 +1,6 @@
+"""
+ Compatibility with report package
+"""
+
+class TextIO:
+ pass
diff --git a/src/report-python/io/__init__.py b/src/report-python/io/__init__.py
new file mode 100644
index 00000000..2ae28399
--- /dev/null
+++ b/src/report-python/io/__init__.py
@@ -0,0 +1 @@
+pass
diff --git a/src/report-python/reportmodule.c b/src/report-python/reportmodule.c
new file mode 100644
index 00000000..fd58a3bd
--- /dev/null
+++ b/src/report-python/reportmodule.c
@@ -0,0 +1,83 @@
+/*
+ Copyright (C) 2010 Abrt team.
+ Copyright (C) 2010 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <Python.h>
+
+#include "common.h"
+
+PyObject *ReportError;
+
+static PyMethodDef module_methods[] = {
+ /* method_name, func, flags, doc_string */
+ { "dd_opendir" , p_dd_opendir , METH_VARARGS },
+ { "dd_create" , p_dd_create , METH_VARARGS },
+ { "delete_dump_dir", p_delete_dump_dir, METH_VARARGS },
+ { NULL }
+};
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+init_pyreport(void)
+{
+ if (PyType_Ready(&p_crash_data_type) < 0)
+ {
+ printf("PyType_Ready(&p_crash_data_type) < 0\n");
+ return;
+ }
+ if (PyType_Ready(&p_dump_dir_type) < 0)
+ {
+ printf("PyType_Ready(&p_dump_dir_type) < 0\n");
+ return;
+ }
+ if (PyType_Ready(&p_run_event_state_type) < 0)
+ {
+ printf("PyType_Ready(&p_run_event_state_type) < 0\n");
+ return;
+ }
+
+ PyObject *m = Py_InitModule("_pyreport", module_methods);
+ //m = Py_InitModule3("_pyreport", module_methods, "Python wrapper for libreport");
+ if (!m)
+ {
+ printf("m == NULL\n");
+ return;
+ }
+
+ /* init the exception object */
+ ReportError = PyErr_NewException("_pyreport.error", NULL, NULL);
+ Py_INCREF(ReportError);
+ PyModule_AddObject(m, "error", ReportError);
+
+ /* init type objects */
+ Py_INCREF(&p_crash_data_type);
+ PyModule_AddObject(m, "crash_data", (PyObject *)&p_crash_data_type);
+ PyModule_AddObject(m, "CD_FLAG_BIN" , Py_BuildValue("i", CD_FLAG_BIN ));
+ PyModule_AddObject(m, "CD_FLAG_TXT" , Py_BuildValue("i", CD_FLAG_TXT ));
+ PyModule_AddObject(m, "CD_FLAG_ISEDITABLE" , Py_BuildValue("i", CD_FLAG_ISEDITABLE ));
+ PyModule_AddObject(m, "CD_FLAG_ISNOTEDITABLE", Py_BuildValue("i", CD_FLAG_ISNOTEDITABLE));
+
+ Py_INCREF(&p_dump_dir_type);
+ PyModule_AddObject(m, "dump_dir", (PyObject *)&p_dump_dir_type);
+ PyModule_AddObject(m, "DD_FAIL_QUIETLY" , Py_BuildValue("i", DD_FAIL_QUIETLY ));
+ PyModule_AddObject(m, "DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE", Py_BuildValue("i", DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE));
+
+ Py_INCREF(&p_run_event_state_type);
+ PyModule_AddObject(m, "run_event_state", (PyObject *)&p_run_event_state_type);
+}
diff --git a/src/report-python/run_event.c b/src/report-python/run_event.c
new file mode 100644
index 00000000..6131df8e
--- /dev/null
+++ b/src/report-python/run_event.c
@@ -0,0 +1,220 @@
+/*
+ Copyright (C) 2010 Abrt team.
+ Copyright (C) 2010 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <Python.h>
+#include <structmember.h>
+
+#include <errno.h>
+#include "common.h"
+#include "crash_data.h"
+#include "run_event.h"
+
+typedef struct {
+ PyObject_HEAD
+ struct run_event_state *state;
+ PyObject *post_run_callback;
+ PyObject *logging_callback;
+} p_run_event_state;
+
+
+/*** init/cleanup ***/
+
+static int post_run_callback(const char *dump_dir_name, void *param);
+static char *logging_callback(char *log_line, void *param);
+
+static PyObject *p_run_event_state_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ p_run_event_state *self = (p_run_event_state *)type->tp_alloc(type, 0);
+ if (self)
+ {
+ self->state = new_run_event_state();
+ self->state->post_run_callback = post_run_callback;
+ self->state->logging_callback = logging_callback;
+ self->state->post_run_param = self;
+ self->state->logging_param = self;
+ }
+ return (PyObject *)self;
+}
+
+static void p_run_event_state_dealloc(PyObject *pself)
+{
+ p_run_event_state *self = (p_run_event_state*)pself;
+ free_run_event_state(self->state);
+ self->state = NULL;
+ Py_XDECREF(self->post_run_callback);
+ self->post_run_callback = NULL;
+ Py_XDECREF(self->logging_callback);
+ self->logging_callback = NULL;
+ self->ob_type->tp_free(pself);
+}
+
+//static int
+//p_run_event_state_init(PyObject *pself, PyObject *args, PyObject *kwds)
+//{
+// return 0;
+//}
+
+
+/*** methods ***/
+
+/* First, C-level callback helpers for run_event_on_FOO(): */
+static int post_run_callback(const char *dump_dir_name, void *param)
+{
+ PyObject *obj = (PyObject*)param;
+ PyObject *ret = PyObject_CallMethod(obj, "post_run_callback", "(s)", dump_dir_name);
+ int r = 0;
+ if (ret)
+ {
+ r = PyInt_AsLong(ret);
+ Py_DECREF(ret);
+ }
+ // TODO: handle exceptions: if (PyErr_Occurred()) ...
+ return r;
+}
+static char *logging_callback(char *log_line, void *param)
+{
+ PyObject *obj = (PyObject*)param;
+ PyObject *ret = PyObject_CallMethod(obj, "logging_callback", "(s)", log_line);
+ Py_XDECREF(ret);
+ // TODO: handle exceptions: if (PyErr_Occurred()) ...
+ return log_line; /* signaling to caller that we didnt consume the string */
+}
+
+/* int run_event_on_dir_name(struct run_event_state *state, const char *dump_dir_name, const char *event); */
+static PyObject *p_run_event_on_dir_name(PyObject *pself, PyObject *args)
+{
+ p_run_event_state *self = (p_run_event_state*)pself;
+ const char *dump_dir_name;
+ const char *event;
+ if (!PyArg_ParseTuple(args, "ss", &dump_dir_name, &event))
+ {
+ return NULL;
+ }
+ int r = run_event_on_dir_name(self->state, dump_dir_name, event);
+ PyObject *obj = Py_BuildValue("i", r);
+ return obj;
+}
+
+/* int run_event_on_crash_data(struct run_event_state *state, crash_data_t *data, const char *event); */
+static PyObject *p_run_event_on_crash_data(PyObject *pself, PyObject *args)
+{
+ p_run_event_state *self = (p_run_event_state*)pself;
+ p_crash_data *cd;
+ const char *event;
+ if (!PyArg_ParseTuple(args, "O!s", &p_crash_data_type, &cd, &event))
+ {
+ return NULL;
+ }
+ int r = run_event_on_crash_data(self->state, cd->cd, event);
+ PyObject *obj = Py_BuildValue("i", r);
+ return obj;
+}
+
+/* TODO: char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx); */
+
+
+/*** attribute getters/setters ***/
+
+static PyObject *get_post_run_callback(PyObject *pself, void *unused)
+{
+ p_run_event_state *self = (p_run_event_state*)pself;
+ if (self->post_run_callback)
+ {
+ Py_INCREF(self->post_run_callback);
+ return self->post_run_callback;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *get_logging_callback(PyObject *pself, void *unused)
+{
+ p_run_event_state *self = (p_run_event_state*)pself;
+ if (self->logging_callback)
+ {
+ Py_INCREF(self->logging_callback);
+ return self->logging_callback;
+ }
+ Py_RETURN_NONE;
+}
+
+static int set_post_run_callback(PyObject *pself, PyObject *callback, void *unused)
+{
+ p_run_event_state *self = (p_run_event_state*)pself;
+//WRONG: we aren't a Python function, calling convention is different
+// PyObject *callback;
+// if (!PyArg_ParseTuple(args, "O", &callback))
+// return -1;
+ if (!PyCallable_Check(callback))
+ {
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+ return -1;
+ }
+ Py_INCREF(callback);
+ Py_XDECREF(self->post_run_callback);
+ self->post_run_callback = callback;
+ return 0;
+}
+
+static int set_logging_callback(PyObject *pself, PyObject *callback, void *unused)
+{
+ p_run_event_state *self = (p_run_event_state*)pself;
+ if (!PyCallable_Check(callback))
+ {
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+ return -1;
+ }
+ Py_INCREF(callback);
+ Py_XDECREF(self->logging_callback);
+ self->logging_callback = callback;
+ return 0;
+}
+
+
+/*** type object ***/
+
+//static PyMemberDef p_run_event_state_members[] = {
+// { NULL }
+//};
+
+static PyMethodDef p_run_event_state_methods[] = {
+ /* method_name, func, flags, doc_string */
+ { "run_event_on_dir_name" , p_run_event_on_dir_name , METH_VARARGS },
+ { "run_event_on_crash_data", p_run_event_on_crash_data, METH_VARARGS },
+ { NULL }
+};
+
+static PyGetSetDef p_run_event_state_getset[] = {
+ /* attr_name, getter_func, setter_func, doc_string, void_param */
+ { "post_run_callback", get_post_run_callback, set_post_run_callback },
+ { "logging_callback" , get_logging_callback , set_logging_callback },
+ { NULL }
+};
+
+PyTypeObject p_run_event_state_type = {
+ PyObject_HEAD_INIT(NULL)
+ .tp_name = "report.run_event_state",
+ .tp_basicsize = sizeof(p_run_event_state),
+ /* Py_TPFLAGS_BASETYPE means "can be subtyped": */
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = p_run_event_state_new,
+ .tp_dealloc = p_run_event_state_dealloc,
+ //.tp_init = p_run_event_state_init,
+ //.tp_members = p_run_event_state_members,
+ .tp_methods = p_run_event_state_methods,
+ .tp_getset = p_run_event_state_getset,
+};
diff --git a/src/report-python/test_crash_data b/src/report-python/test_crash_data
new file mode 100755
index 00000000..6f719a8f
--- /dev/null
+++ b/src/report-python/test_crash_data
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+from report import *
+
+cd = crash_data()
+cd.add("foo", "bar")
+
+dd = cd.create_dump_dir()
+
+if dd:
+ print "dd is nonzero"
+else:
+ print "dd is zero"
+
+print "closing"
+dd.close()
+
+if dd:
+ print "dd is nonzero"
+else:
+ print "dd is zero"
diff --git a/src/report-python/test_crash_data2 b/src/report-python/test_crash_data2
new file mode 100755
index 00000000..2594f863
--- /dev/null
+++ b/src/report-python/test_crash_data2
@@ -0,0 +1,10 @@
+#!/usr/bin/python
+
+from report import *
+
+cd = crash_data()
+cd.add("foo", "bar")
+
+print "foo:", cd.get("foo")
+print "nonexistent:", cd.get("nonexistent")
+print "done"
diff --git a/src/report-python/test_dd_create b/src/report-python/test_dd_create
new file mode 100755
index 00000000..4da29b11
--- /dev/null
+++ b/src/report-python/test_dd_create
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+from report import *
+
+dd = dd_create("testdir")
+print dd
+
+if dd:
+ print "dd is nonzero"
+else:
+ print "dd is zero"
+
+print "name:", dd.name
+print "closing"
+dd.close()
+
+if dd:
+ print "dd is nonzero"
+else:
+ print "dd is zero"
+
+# Should fail here
+dd.name = "qwe"
+
+print "Done"
diff --git a/src/report-python/test_full b/src/report-python/test_full
new file mode 100755
index 00000000..103535dd
--- /dev/null
+++ b/src/report-python/test_full
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import sys
+from report import *
+
+def run_event_on_crash_data(cd, event, log_function = None):
+ dd = cd.create_dump_dir("/tmp")
+ dir_name = dd.name
+ print "Created dump_dir:", dir_name
+ dd.close()
+ run_state = run_event_state()
+ if log_function: # maybe if callable(log_function)?
+ run_state.logging_callback = log_function
+ print "Running event:", event
+ r = run_state.run_event_on_dir_name(dir_name, event)
+ print "Deleting:", dir_name
+ delete_dump_dir(dir_name)
+ return r;
+
+def log_function(line):
+ print "LOG:", line
+
+cd = crash_data()
+cd.add("foo", "bar")
+cd.add("analyzer", "baz", CD_FLAG_ISNOTEDITABLE)
+r = run_event_on_crash_data(cd, "post-create", log_function)
+print "Result:", r
diff --git a/src/report-python/test_full2 b/src/report-python/test_full2
new file mode 100755
index 00000000..734946eb
--- /dev/null
+++ b/src/report-python/test_full2
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+
+import sys
+from report import *
+
+def log_function(line):
+ print "LOG:", line
+
+cd = crash_data()
+cd.add("foo", "bar")
+cd.add("analyzer", "baz", CD_FLAG_ISNOTEDITABLE)
+
+st = run_event_state()
+st.logging_callback = log_function
+r = st.run_event_on_crash_data(cd, "post-create")
+
+print "Result:", r
diff --git a/src/report-python/test_run_event_state b/src/report-python/test_run_event_state
new file mode 100755
index 00000000..3e391407
--- /dev/null
+++ b/src/report-python/test_run_event_state
@@ -0,0 +1,13 @@
+#!/usr/bin/python
+
+from report import *
+
+def func():
+ return 0
+
+res = run_event_state()
+print res
+print res.post_run_callback
+res.post_run_callback = func
+res.logging_callback = func
+print res.post_run_callback
diff --git a/src/report-python/test_run_event_state1 b/src/report-python/test_run_event_state1
new file mode 100755
index 00000000..6c3584fe
--- /dev/null
+++ b/src/report-python/test_run_event_state1
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import sys
+from report import *
+
+def post_run_callback(dump_dir_name):
+ return 0
+
+def logging_callback(line):
+ print "LOG:", line
+ return
+
+res = run_event_state()
+res.post_run_callback = post_run_callback
+res.logging_callback = logging_callback
+
+dd = dd_create("testdir")
+if not dd:
+ sys.exit(1)
+dd.save_text("analyzer", "foo")
+dd.close()
+
+res.run_event_on_dir_name("testdir", "post-create")
+
+dd = dd_opendir("testdir")
+dd.delete()
+dd.close()
diff --git a/src/report-python/test_setroubleshoot_example b/src/report-python/test_setroubleshoot_example
new file mode 100755
index 00000000..74428f16
--- /dev/null
+++ b/src/report-python/test_setroubleshoot_example
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+import report
+import report.io
+import report.io.GTKIO
+import report.accountmanager
+
+accounts = report.accountmanager.AccountManager()
+
+signature = report.createAlertSignature("selinux-policy",
+ "setroubleshoot",
+ "self.siginfo.get_hash()",
+ "self.summary",
+ "content")
+
+rc = report.report(signature, report.io.GTKIO.GTKIO(accounts))
+
+print "rc:", rc
diff --git a/src/report-python/test_setroubleshoot_example2 b/src/report-python/test_setroubleshoot_example2
new file mode 100755
index 00000000..8aebcdfe
--- /dev/null
+++ b/src/report-python/test_setroubleshoot_example2
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import report
+import report.io
+import report.io.GTKIO
+import report.accountmanager
+
+accounts = report.accountmanager.AccountManager()
+
+signature = report.createAlertSignature("selinux-policy",
+ "setroubleshoot",
+ "self.siginfo.get_hash()",
+ "self.summary",
+ "content")
+
+# Won't send log anywhere:
+#rc = report.report(signature, report.io.GTKIO.GTKIO(accounts))
+
+# report.report() + logging:
+def logging_callback(line):
+ print "LOG:", line
+ return
+state = report.run_event_state()
+state.logging_callback = logging_callback
+rc = state.run_event_on_crash_data(signature, "report")
+
+print "rc:", rc