diff options
author | Jiri Moskovcak <jmoskovc@redhat.com> | 2010-11-10 17:01:31 +0100 |
---|---|---|
committer | Jiri Moskovcak <jmoskovc@redhat.com> | 2010-11-10 18:27:05 +0100 |
commit | 3fce116492052267e5e1a634e5404b1b518f7ef3 (patch) | |
tree | 3160a2d429987573004fb8166d40542b76a7315b /src | |
parent | 9d2cb4518c3a8a72ccc714ddbc131aaa84506092 (diff) | |
download | abrt-3fce116492052267e5e1a634e5404b1b518f7ef3.tar.gz abrt-3fce116492052267e5e1a634e5404b1b518f7ef3.tar.xz abrt-3fce116492052267e5e1a634e5404b1b518f7ef3.zip |
move files from lib/plugins to src/plugins
Diffstat (limited to 'src')
68 files changed, 5968 insertions, 184 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 00227a47..f6117250 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1 +1 @@ -SUBDIRS = hooks btparser daemon applet gui cli +SUBDIRS = hooks btparser daemon applet gui cli plugins diff --git a/src/daemon/Daemon.cpp b/src/daemon/Daemon.cpp index a30b2d97..72bb54bf 100644 --- a/src/daemon/Daemon.cpp +++ b/src/daemon/Daemon.cpp @@ -843,6 +843,11 @@ int main(int argc, char** argv) if (!getenv("PATH")) putenv((char*)"PATH=/usr/sbin:/usr/bin:/sbin:/bin"); + /* need to add LIBEXEC_DIR to PATH, because otherwise abrt-action-* + * is not found by exec() + */ + putenv(xasprintf("PATH=%s:%s", LIBEXEC_DIR, getenv("PATH"))); + putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); msg_prefix = "abrtd"; /* for log(), error_msg() and such */ diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index a939798c..072f7f17 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -1,21 +1,13 @@ bin_SCRIPTS = \ - abrt-action-install-debuginfo \ abrt-handle-upload - -sbin_PROGRAMS = abrtd \ - abrt-server \ - abrt-action-analyze-c \ - abrt-action-analyze-python \ - abrt-action-analyze-oops \ - abrt-action-generate-backtrace \ +libexec_PROGRAMS = \ abrt-action-save-package-data bin_PROGRAMS = \ - abrt-handle-crashdump \ - abrt-action-bugzilla \ - abrt-action-rhtsupport \ - abrt-action-kerneloops \ - abrt-action-print + abrt-handle-crashdump + +sbin_PROGRAMS = abrtd \ + abrt-server abrtd_SOURCES = \ PluginManager.h PluginManager.cpp \ @@ -35,6 +27,7 @@ abrtd_CPPFLAGS = \ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ + -DLIBEXEC_DIR=\"$(LIBEXEC_DIR)\" \ $(GLIB_CFLAGS) \ $(DBUS_CFLAGS) \ $(ENABLE_SOCKET_OR_DBUS) \ @@ -63,45 +56,9 @@ abrt_server_CPPFLAGS = \ abrt_server_LDADD = \ ../../lib/utils/libABRTUtils.la -abrt_action_analyze_c_SOURCES = \ - abrt-action-analyze-c.c -abrt_action_analyze_c_CPPFLAGS = \ - -I$(srcdir)/../../inc \ - -I$(srcdir)/../../lib/utils \ - -DBIN_DIR=\"$(bindir)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ - -DCONF_DIR=\"$(CONF_DIR)\" \ - -DLOCALSTATEDIR='"$(localstatedir)"' \ - -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ - -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ - -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ - -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ - -D_GNU_SOURCE \ - -Wall -Werror -abrt_action_analyze_c_LDADD = \ - ../../lib/utils/libABRTUtils.la - -abrt_action_analyze_python_SOURCES = \ - abrt-action-analyze-python.c -abrt_action_analyze_python_CPPFLAGS = \ - -I$(srcdir)/../../inc \ - -I$(srcdir)/../../lib/utils \ - -DBIN_DIR=\"$(bindir)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ - -DCONF_DIR=\"$(CONF_DIR)\" \ - -DLOCALSTATEDIR='"$(localstatedir)"' \ - -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ - -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ - -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ - -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ - -D_GNU_SOURCE \ - -Wall -Werror -abrt_action_analyze_python_LDADD = \ - ../../lib/utils/libABRTUtils.la - -abrt_action_analyze_oops_SOURCES = \ - abrt-action-analyze-oops.c -abrt_action_analyze_oops_CPPFLAGS = \ +abrt_handle_crashdump_SOURCES = \ + abrt-handle-crashdump.c +abrt_handle_crashdump_CPPFLAGS = \ -I$(srcdir)/../../inc \ -I$(srcdir)/../../lib/utils \ -DBIN_DIR=\"$(bindir)\" \ @@ -114,28 +71,9 @@ abrt_action_analyze_oops_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ -D_GNU_SOURCE \ -Wall -Werror -abrt_action_analyze_oops_LDADD = \ +abrt_handle_crashdump_LDADD = \ ../../lib/utils/libABRTUtils.la -abrt_action_generate_backtrace_SOURCES = \ - abrt-action-generate-backtrace.c -abrt_action_generate_backtrace_CPPFLAGS = \ - -I$(srcdir)/../../inc \ - -I$(srcdir)/../../lib/utils \ - -DBIN_DIR=\"$(bindir)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ - -DCONF_DIR=\"$(CONF_DIR)\" \ - -DLOCALSTATEDIR='"$(localstatedir)"' \ - -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ - -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ - -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ - -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ - -D_GNU_SOURCE \ - -Wall -Werror -abrt_action_generate_backtrace_LDADD = \ - ../../lib/utils/libABRTUtils.la \ - ../btparser/libbtparser.la - abrt_action_save_package_data_SOURCES = \ rpm.h rpm.c \ Settings.h Settings.cpp \ @@ -160,115 +98,6 @@ abrt_action_save_package_data_LDADD = \ ../../lib/utils/libABRTdUtils.la \ ../../lib/utils/libABRTUtils.la -abrt_handle_crashdump_SOURCES = \ - abrt-handle-crashdump.c -abrt_handle_crashdump_CPPFLAGS = \ - -I$(srcdir)/../../inc \ - -I$(srcdir)/../../lib/utils \ - -DBIN_DIR=\"$(bindir)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ - -DCONF_DIR=\"$(CONF_DIR)\" \ - -DLOCALSTATEDIR='"$(localstatedir)"' \ - -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ - -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ - -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ - -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ - -D_GNU_SOURCE \ - -Wall -Werror -abrt_handle_crashdump_LDADD = \ - ../../lib/utils/libABRTUtils.la - -abrt_action_bugzilla_SOURCES = \ - abrt-action-bugzilla.cpp -abrt_action_bugzilla_CPPFLAGS = \ - -I$(srcdir)/../../inc \ - -I$(srcdir)/../../lib/utils \ - -DBIN_DIR=\"$(bindir)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ - -DCONF_DIR=\"$(CONF_DIR)\" \ - -DLOCALSTATEDIR='"$(localstatedir)"' \ - -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ - -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_bugzilla_LDADD = \ - $(GLIB_LIBS) \ - ../../lib/utils/libABRT_web_utils.la \ - ../../lib/utils/libABRTdUtils.la \ - ../../lib/utils/libABRTUtils.la - -abrt_action_rhtsupport_SOURCES = \ - abrt_rh_support.h abrt_rh_support.c \ - abrt-action-rhtsupport.cpp -abrt_action_rhtsupport_CPPFLAGS = \ - -I$(srcdir)/../../inc \ - -I$(srcdir)/../../lib/utils \ - -DBIN_DIR=\"$(bindir)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ - -DCONF_DIR=\"$(CONF_DIR)\" \ - -DLOCALSTATEDIR='"$(localstatedir)"' \ - -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ - -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ - -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ - -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ - $(GLIB_CFLAGS) \ - $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \ - -D_GNU_SOURCE \ - -Wall -Werror -abrt_action_rhtsupport_LDFLAGS = -ltar -abrt_action_rhtsupport_LDADD = \ - $(GLIB_LIBS) \ - $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) \ - ../../lib/utils/libABRT_web_utils.la \ - ../../lib/utils/libABRTdUtils.la \ - ../../lib/utils/libABRTUtils.la - -abrt_action_kerneloops_SOURCES = \ - abrt-action-kerneloops.cpp -abrt_action_kerneloops_CPPFLAGS = \ - -I$(srcdir)/../../inc \ - -I$(srcdir)/../../lib/utils \ - -DBIN_DIR=\"$(bindir)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ - -DCONF_DIR=\"$(CONF_DIR)\" \ - -DLOCALSTATEDIR='"$(localstatedir)"' \ - -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ - -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 -# libABRTdUtils is used only because of LoadPluginSettings: -abrt_action_kerneloops_LDADD = \ - ../../lib/utils/libABRT_web_utils.la \ - ../../lib/utils/libABRTdUtils.la \ - ../../lib/utils/libABRTUtils.la - -abrt_action_print_SOURCES = \ - abrt-action-print.cpp -abrt_action_print_CPPFLAGS = \ - -I$(srcdir)/../../inc \ - -I$(srcdir)/../../lib/utils \ - -DBIN_DIR=\"$(bindir)\" \ - -DVAR_RUN=\"$(VAR_RUN)\" \ - -DCONF_DIR=\"$(CONF_DIR)\" \ - -DLOCALSTATEDIR='"$(localstatedir)"' \ - -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ - -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 -# libABRTdUtils is used only because of make_description_logger: -abrt_action_print_LDADD = \ - ../../lib/utils/libABRTdUtils.la \ - ../../lib/utils/libABRTUtils.la - dbusabrtconfdir = ${sysconfdir}/dbus-1/system.d/ dist_dbusabrtconf_DATA = dbus-abrt.conf @@ -286,7 +115,7 @@ dist_comredhatabrtservice_DATA = com.redhat.abrt.service man_MANS = abrtd.8 abrt.conf.5 -EXTRA_DIST = $(man_MANS) abrt-action-install-debuginfo abrt-handle-upload +EXTRA_DIST = $(man_MANS) abrt-handle-upload if HAVE_SYSTEMD dist_systemdsystemunit_DATA = \ diff --git a/src/hooks/Makefile.am b/src/hooks/Makefile.am index 52c151ba..d20fad91 100644 --- a/src/hooks/Makefile.am +++ b/src/hooks/Makefile.am @@ -20,7 +20,7 @@ dumpoops_SOURCES = \ dumpoops_CPPFLAGS = \ -I$(srcdir)/../../inc \ -I$(srcdir)/../../lib/utils \ - -I$(srcdir)/../../lib/plugins \ + -I$(srcdir)/../plugins \ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ diff --git a/src/plugins/Bugzilla.conf b/src/plugins/Bugzilla.conf new file mode 100644 index 00000000..76e0d1d8 --- /dev/null +++ b/src/plugins/Bugzilla.conf @@ -0,0 +1,12 @@ +# Description: Reports bugs to bugzilla + +Enabled = yes + +# Bugzilla URL +BugzillaURL = https://bugzilla.redhat.com/ +# yes means that ssl certificates will be checked +SSLVerify = yes +# your login has to exist, if you don have any, please create one +Login = +# your password +Password = diff --git a/src/plugins/Bugzilla.cpp b/src/plugins/Bugzilla.cpp new file mode 100644 index 00000000..452d7a58 --- /dev/null +++ b/src/plugins/Bugzilla.cpp @@ -0,0 +1,155 @@ +/* + 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" +#include "crash_types.h" +#include "abrt_exception.h" +#include "comm_layer_inner.h" +#include "Bugzilla.h" + +using namespace std; + +CReporterBugzilla::CReporterBugzilla() +{ + m_pSettings["BugzillaURL"] = "https://bugzilla.redhat.com"; + m_pSettings["Login"] = ""; + m_pSettings["Password"] = ""; + m_pSettings["SSLVerify"] = "yes"; + m_pSettings["RatingRequired"] = "yes"; +} + +CReporterBugzilla::~CReporterBugzilla() +{ +} + +void CReporterBugzilla::SetSettings(const map_plugin_settings_t& pSettings) +{ +//BUG! This gets called when user's keyring contains login data, +//then it takes precedence over /etc/abrt/plugins/Bugzilla.conf. +//I got a case when keyring had a STALE password, and there was no way +//for me to know that it is being used. Moreover, when I discovered it +//(by hacking abrt source!), I don't know how to purge it from the keyring. +//At the very least, log("SOMETHING") here. + + /* Can't simply do this: + + m_pSettings = pSettings; + + * - it will erase keys which aren't present in pSettings. + * Example: if Bugzilla.conf doesn't have "Login = foo", + * then there's no pSettings["Login"] and m_pSettings = pSettings + * will nuke default m_pSettings["Login"] = "", + * making GUI think that we have no "Login" key at all + * and thus never overriding it - even if it *has* an override! + */ + + map_plugin_settings_t::iterator it = m_pSettings.begin(); + while (it != m_pSettings.end()) + { + map_plugin_settings_t::const_iterator override = pSettings.find(it->first); + if (override != pSettings.end()) + { + VERB3 log(" 3 settings[%s]='%s'", it->first.c_str(), it->second.c_str()); + it->second = override->second; + } + it++; + } +} + +string CReporterBugzilla::Report(const map_crash_data_t& crash_data, + const map_plugin_settings_t& settings, + const char *args) +{ + /* abrt-action-bugzilla [-s] -c /etc/arbt/Bugzilla.conf -c - -d pCrashData.dir NULL */ + char *argv[9]; + char **pp = argv; + *pp++ = (char*)"abrt-action-bugzilla"; + +//We want to consume output, so don't redirect to syslog. +// if (logmode & LOGMODE_SYSLOG) +// *pp++ = (char*)"-s"; +//TODO: the actions<->daemon interaction will be changed anyway... + + *pp++ = (char*)"-c"; + *pp++ = (char*)(PLUGINS_CONF_DIR"/Bugzilla."PLUGINS_CONF_EXTENSION); + *pp++ = (char*)"-c"; + *pp++ = (char*)"-"; + *pp++ = (char*)"-d"; + *pp++ = (char*)get_crash_data_item_content_or_NULL(crash_data, CD_DUMPDIR); + *pp = NULL; + int pipefds[2]; + pid_t pid = fork_execv_on_steroids(EXECFLG_INPUT + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, + argv, + pipefds, + /* unsetenv_vec: */ NULL, + /* dir: */ NULL, + /* uid(unused): */ 0 + ); + + /* Write the configuration to stdin */ + map_plugin_settings_t::const_iterator it = settings.begin(); + while (it != settings.end()) + { + full_write_str(pipefds[1], it->first.c_str()); + full_write_str(pipefds[1], "="); + full_write_str(pipefds[1], it->second.c_str()); + full_write_str(pipefds[1], "\n"); + it++; + } + close(pipefds[1]); + + FILE *fp = fdopen(pipefds[0], "r"); + if (!fp) + die_out_of_memory(); + + /* Consume log from stdout */ + string bug_status; + char *buf; + while ((buf = xmalloc_fgetline(fp)) != NULL) + { + if (strncmp(buf, "STATUS:", 7) == 0) + bug_status = buf + 7; + else + if (strncmp(buf, "EXCEPT:", 7) == 0) + { + CABRTException e(EXCEP_PLUGIN, "%s", buf + 7); + free(buf); + fclose(fp); + waitpid(pid, NULL, 0); + throw e; + } + update_client("%s", buf); + free(buf); + } + + fclose(fp); /* this also closes pipefds[0] */ + /* wait for child to actually exit, and prevent leaving a zombie behind */ + waitpid(pid, NULL, 0); + + return bug_status; +} + +PLUGIN_INFO(REPORTER, + CReporterBugzilla, + "Bugzilla", + "0.0.4", + _("Reports bugs to bugzilla"), + "npajkovs@redhat.com", + "https://fedorahosted.org/abrt/wiki", + PLUGINS_LIB_DIR"/Bugzilla.glade"); diff --git a/src/plugins/Bugzilla.glade b/src/plugins/Bugzilla.glade new file mode 100644 index 00000000..cabdd06a --- /dev/null +++ b/src/plugins/Bugzilla.glade @@ -0,0 +1,246 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.14"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkDialog" id="PluginDialog"> + <property name="border_width">12</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="window_position">center-on-parent</property> + <property name="icon_name">abrt</property> + <property name="type_hint">normal</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">5</property> + <property name="n_columns">2</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <object class="GtkLabel" id="lBugzillaURL"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Bugzilla URL:</property> + </object> + <packing> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="lLogin"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Login(email):</property> + </object> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="lPassword"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Password:</property> + </object> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_BugzillaURL"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_Login"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</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="GtkEntry" id="conf_Password"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="visibility">False</property> + <property name="invisible_char">●</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="GtkCheckButton" id="cb_Password"> + <property name="label" translatable="yes">Show password</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</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> + <child> + <object class="GtkLabel" id="label3"> + <property name="xalign">0</property> + </object> + <packing> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="conf_SSLVerify"> + <property name="label" translatable="yes">SSL verify</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Bugzilla plugin configuration</b></property> + <property name="use_markup">True</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <child> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="label" translatable="yes">Don't have an account yet?</property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="xpad">5</property> + <property name="label" translatable="yes">You can create it <a href="https://bugzilla.redhat.com/createaccount.cgi">here</a></property> + <property name="use_markup">True</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="button2"> + <property name="label">gtk-cancel</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> + <child> + <object class="GtkButton" id="bApply"> + <property name="label">gtk-apply</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">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-6">button2</action-widget> + <action-widget response="-10">bApply</action-widget> + </action-widgets> + </object> + <object class="GtkAction" id="action1"/> +</interface> diff --git a/src/plugins/Bugzilla.h b/src/plugins/Bugzilla.h new file mode 100644 index 00000000..d7f3acf0 --- /dev/null +++ b/src/plugins/Bugzilla.h @@ -0,0 +1,37 @@ +/* + 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 BUGZILLA_H_ +#define BUGZILLA_H_ + +#include "plugin.h" +#include "reporter.h" + +class CReporterBugzilla : public CReporter +{ + public: + CReporterBugzilla(); + virtual ~CReporterBugzilla(); + + virtual std::string Report(const map_crash_data_t& pCrashData, + const map_plugin_settings_t& pSettings, + const char *pArgs); + virtual void SetSettings(const map_plugin_settings_t& pSettings); +}; + +#endif /* BUGZILLA_H_ */ diff --git a/src/plugins/CCpp.conf b/src/plugins/CCpp.conf new file mode 100644 index 00000000..4af91470 --- /dev/null +++ b/src/plugins/CCpp.conf @@ -0,0 +1,44 @@ +# Configuration file for CCpp hook and plugin +Enabled = yes + +# If you also want to dump file named "core" +# in crashed process' current dir, set to "yes" +MakeCompatCore = yes + +# Do you want a copy of crashed binary be saved? +# (useful, for example, when _deleted binary_ segfaults) +SaveBinaryImage = no + +# Generate backtrace +Backtrace = yes +# How long to wait for gdb to finish. Default is 60 seconds. +GdbTimeoutSec = 120 + +# Generate backtrace for crashes uploaded from remote machines. +# Note that for reliable backtrace generation, your local machine +# needs to have the crashed executable and all libraries it uses, +# and they need to be the same versions as on remote machines. +# If you cannot ensure that, it's better to set this option to "no" +BacktraceRemotes = no + +# Generate memory map too (IGNORED FOR NOW) +MemoryMap = no + +# How to get debuginfo: install, mount +## install - download and install debuginfo packages +## mount - mount fedora NFS with debug info +## (IGNORED FOR NOW) +DebugInfo = install + +# If this option is set to "yes", +# debuginfos will be installed to @@LOCALSTATEDIR@@/cache/abrt-di +InstallDebugInfo = yes + +# Additional directories to search for debuginfos. +# For example, you can list a network-mounted shared store +# of all debuginfos here. +# ReadonlyLocalDebugInfoDirs = /path1:/path2:... + +# Keep @@LOCALSTATEDIR@@/cache/abrt-di +# from growing out-of-bounds. +DebugInfoCacheMB = 4000 diff --git a/src/plugins/CCpp.cpp b/src/plugins/CCpp.cpp new file mode 100644 index 00000000..fad9cf62 --- /dev/null +++ b/src/plugins/CCpp.cpp @@ -0,0 +1,279 @@ +/* + 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" +#include "Polkit.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 the it's not guaranteed that /proc/<pid> of crashing +process might not be available for dump_helper +4 - means that 4 dump_helpers can run at the same time, which should be enough +for ABRT, we can miss some crashes, but what are the odds that more processes +crash at the same time? This value 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 new file mode 100644 index 00000000..e95b4d09 --- /dev/null +++ b/src/plugins/CCpp.h @@ -0,0 +1,49 @@ +/* + 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/FileTransfer.conf b/src/plugins/FileTransfer.conf new file mode 100644 index 00000000..111c1c4b --- /dev/null +++ b/src/plugins/FileTransfer.conf @@ -0,0 +1,35 @@ +# Configuration of the FileTransfer reporter plugin. +Enabled = yes + +# The plugin is invoked in the abrt.conf file, usually in the +# ActionsAndReporters option and/or the [cron] section. +# There are two modes of invocation: +# +# * Specify FileTransfer(one) in ActionsAndReporters directive. +# Immediately after crash is detected, the plugin transfers +# crash data to the server specified via URL directive in this file. +# +# * Specify FileTransfer(store) in ActionsAndReporters directive +# and add "HH:MM = FileTransfer" line in [cron] section. +# At the time of the crash, the plugin stores a record of it +# in its internal list. When specified time is reached, +# the plugin iterates through its internal list and sends +# every recorded crash to the specified URL. +# After that, the internal list is cleared. + + +# URL to upload the files to +# supported: ftp, ftps, http, https, scp, sftp, tftp, file +# for example: ftp://user:password@server.name/directory +# or: scp://user:password@server.name:port/directory etc. +# for testing: file:///tmp/test_directory +URL = + +# Archive type, one of .zip, .tar.gz or .tar.bz2 +ArchiveType = .tar.gz + +# How many times we try to upload the file +RetryCount = 3 + +# How long we wait between we retry the upload (in seconds) +RetryDelay = 20 diff --git a/src/plugins/FileTransfer.cpp b/src/plugins/FileTransfer.cpp new file mode 100644 index 00000000..d964bc9d --- /dev/null +++ b/src/plugins/FileTransfer.cpp @@ -0,0 +1,367 @@ +/* + FileTransfer.cpp + + Copyright (C) 2009 Daniel Novotny (dnovotny@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 _GNU_SOURCE +# define _GNU_SOURCE +#endif +#include <libtar.h> +#include <bzlib.h> +#include <zlib.h> +#include "abrtlib.h" +#include "abrt_curl.h" +#include "FileTransfer.h" +#include "abrt_exception.h" +#include "comm_layer_inner.h" + +using namespace std; + +#define HBLEN 255 +#define FILETRANSFER_DIRLIST DEBUG_DUMPS_DIR "/FileTransferDirlist.txt" + +CFileTransfer::CFileTransfer() +: + m_sArchiveType(".tar.gz"), + m_nRetryCount(3), + m_nRetryDelay(20) +{ +} + +void CFileTransfer::SendFile(const char *pURL, const char *pFilename) +{ + int len = strlen(pURL); + if (len == 0) + { + error_msg(_("FileTransfer: URL not specified")); + return; + } + + update_client(_("Sending archive %s to %s"), pFilename, pURL); + + char *whole_url = concat_path_file(pURL, strrchr(pFilename, '/') ? : pFilename); + + int count = m_nRetryCount; + while (1) + { + FILE *f = fopen(pFilename, "r"); + if (!f) + { + free(whole_url); + throw CABRTException(EXCEP_PLUGIN, "Can't open archive file '%s'", pFilename); + } + + struct stat buf; + fstat(fileno(f), &buf); /* never fails */ + + CURL *curl = xcurl_easy_init(); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + /* specify target */ + curl_easy_setopt(curl, CURLOPT_URL, whole_url); + /* FILE handle: passed to the default callback, it will fread() it */ + curl_easy_setopt(curl, CURLOPT_READDATA, f); + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)buf.st_size); + + /* everything is done here; result 0 means success */ + int result = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + fclose(f); + if (result == 0 || --count <= 0) + break; + /* retry the upload if not succesful, wait a bit before next try */ + sleep(m_nRetryDelay); + } + free(whole_url); +} + +static void create_tar(const char *archive_name, const char *directory) +{ + TAR *tar; + + if (tar_open(&tar, (char *)archive_name, NULL, O_WRONLY | O_CREAT, 0644, TAR_GNU) != 0) + { + return; + } + tar_append_tree(tar, (char *)directory, (char*)"."); + tar_close(tar); +} + +static void create_targz(const char *archive_name, const char *directory) +{ + char *name_without_gz = xstrdup(archive_name); + strrchr(name_without_gz, '.')[0] = '\0'; + + create_tar(name_without_gz, directory); + + int fd = open(name_without_gz, O_RDONLY); + if (fd < 0) + { + remove(name_without_gz); + free(name_without_gz); + return; + } + + gzFile gz = gzopen(archive_name, "w"); + if (gz == NULL) + { + close(fd); + remove(name_without_gz); + free(name_without_gz); + return; + } + + char buf[BUFSIZ]; + ssize_t bytesRead; + while ((bytesRead = full_read(fd, buf, BUFSIZ)) > 0) + { + gzwrite(gz, buf, bytesRead); // TODO: check that return value == bytesRead + } + + gzclose(gz); + close(fd); + remove(name_without_gz); + free(name_without_gz); +} + +static void create_tarbz2(const char * archive_name, const char * directory) +{ + char *name_without_bz2 = xstrdup(archive_name); + strrchr(name_without_bz2, '.')[0] = '\0'; + + create_tar(name_without_bz2, directory); + + int tarFD = open(name_without_bz2, O_RDONLY); + if (tarFD == -1) + { + remove(name_without_bz2); + free(name_without_bz2); + return; + } + FILE *f = fopen(archive_name, "w"); + if (f == NULL) + { + close(tarFD); + remove(name_without_bz2); + free(name_without_bz2); + return; + } + int bzError; + BZFILE *bz = BZ2_bzWriteOpen(&bzError, f, /*BLOCK_MULTIPLIER:*/ 7, 0, 0); + if (bz == NULL) + { + fclose(f); + close(tarFD); + remove(name_without_bz2); + free(name_without_bz2); + return; + } + + char buf[BUFSIZ]; + ssize_t bytesRead; + while ((bytesRead = read(tarFD, buf, BUFSIZ)) > 0) + { + BZ2_bzWrite(&bzError, bz, buf, bytesRead); + } + + BZ2_bzWriteClose(&bzError, bz, 0, NULL, NULL); + fclose(f); + close(tarFD); + remove(name_without_bz2); + free(name_without_bz2); +} + +void CFileTransfer::CreateArchive(const char *pArchiveName, const char *pDir) +{ + if (m_sArchiveType == ".tar") + { + create_tar(pArchiveName, pDir); + } + else if (m_sArchiveType == ".tar.gz") + { + create_targz(pArchiveName, pDir); + } + else if (m_sArchiveType == ".tar.bz2") + { + create_tarbz2(pArchiveName, pDir); + } + else + { + throw CABRTException(EXCEP_PLUGIN, "Unknown/unsupported archive type %s", m_sArchiveType.c_str()); + } +} + +/* Returns the last component of the directory path. + * Careful to not return "" on "/path/path2/", but "path2". + */ +static string DirBase(const char *pStr) +{ + int end = strlen(pStr); + if (end > 1 && pStr[end-1] == '/') + { + end--; + } + int beg = end; + while (beg > 0 && pStr[beg-1] != '/') + { + beg--; + } + return string(pStr + beg, end - beg); +} + +void CFileTransfer::Run(const char *pActionDir, const char *pArgs, int force) +{ + if (strcmp(pArgs, "store") == 0) + { + /* Remember pActiveDir for later sending */ + FILE *dirlist = fopen(FILETRANSFER_DIRLIST, "a"); + if (!dirlist) + { + throw CABRTException(EXCEP_PLUGIN, "Can't open "FILETRANSFER_DIRLIST); + } + fprintf(dirlist, "%s\n", pActionDir); + fclose(dirlist); + VERB3 log("Remembered '%s' for future file transfer", pActionDir); + return; + } + + update_client(_("FileTransfer: Creating a report...")); + + char hostname[HBLEN]; + gethostname(hostname, HBLEN-1); + hostname[HBLEN-1] = '\0'; + + char tmpdir_name[] = "/tmp/abrtuploadXXXXXX"; + /* mkdtemp does mkdir(xxx, 0700), should be safe (is it?) */ + if (mkdtemp(tmpdir_name) == NULL) + { + throw CABRTException(EXCEP_PLUGIN, "Can't mkdir a temporary directory in /tmp"); + } + + if (strcmp(pArgs, "one") == 0) + { + /* Just send one archive */ + string archivename = ssprintf("%s/%s-%s%s", tmpdir_name, hostname, DirBase(pActionDir).c_str(), m_sArchiveType.c_str()); + try + { + CreateArchive(archivename.c_str(), pActionDir); + SendFile(m_sURL.c_str(), archivename.c_str()); + } + catch (CABRTException& e) + { + error_msg(_("Cannot create and send an archive: %s"), e.what()); + } + unlink(archivename.c_str()); + } + else + { + /* Tar up and send all remebered directories */ + FILE *dirlist = fopen(FILETRANSFER_DIRLIST, "r"); + if (!dirlist) + { + /* not an error */ + VERB3 log("No saved crashes to transfer"); + goto del_tmp_dir; + } + + char *dirname; + while ((dirname = xmalloc_fgetline(dirlist)) != NULL) + { + string archivename = ssprintf("%s/%s-%s%s", tmpdir_name, hostname, DirBase(dirname).c_str(), m_sArchiveType.c_str()); + try + { + VERB3 log("Creating archive '%s' of dir '%s'", archivename.c_str(), dirname); + CreateArchive(archivename.c_str(), dirname); + VERB3 log("Sending archive to '%s'", m_sURL.c_str()); + SendFile(m_sURL.c_str(), archivename.c_str()); + } + catch (CABRTException& e) + { + error_msg(_("Cannot create and send an archive: %s"), e.what()); + } + VERB3 log("Deleting archive '%s'", archivename.c_str()); + unlink(archivename.c_str()); + free(dirname); + } + + fclose(dirlist); + /* all the files we're able to send should be sent now, + starting over with clean table */ + unlink(FILETRANSFER_DIRLIST); + } + + del_tmp_dir: + rmdir(tmpdir_name); +} + +void CFileTransfer::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("URL"); + if (it != end) + { + m_sURL = it->second; + } + + it = pSettings.find("RetryCount"); + if (it != end) + { + m_nRetryCount = xatoi_u(it->second.c_str()); + } + + it = pSettings.find("RetryDelay"); + if (it != end) + { + m_nRetryDelay = xatoi_u(it->second.c_str()); + } + + it = pSettings.find("ArchiveType"); + if (it != end) + { + /* currently supporting .tar, .tar.gz, .tar.bz2 and .zip */ + m_sArchiveType = it->second; + if (m_sArchiveType[0] != '.') + { + m_sArchiveType = "." + m_sArchiveType; + } + } +} + +//ok to delete? +//const map_plugin_settings_t& CFileTransfer::GetSettings() +//{ +// m_pSettings["URL"] = m_sURL; +// m_pSettings["RetryCount"] = to_string(m_nRetryCount); +// m_pSettings["RetryDelay"] = to_string(m_nRetryDelay); +// m_pSettings["ArchiveType"] = m_sArchiveType; +// +// return m_pSettings; +//} + +PLUGIN_INFO(ACTION, + CFileTransfer, + "FileTransfer", + "0.0.6", + _("Sends a report via FTP or SCTP"), + "dnovotny@redhat.com", + "https://fedorahosted.org/abrt/wiki", + ""); diff --git a/src/plugins/FileTransfer.h b/src/plugins/FileTransfer.h new file mode 100644 index 00000000..17bebf3d --- /dev/null +++ b/src/plugins/FileTransfer.h @@ -0,0 +1,46 @@ +/* + FileTransfer.h - header file for the file transfer plugin + - it uploads the file via ftp or sctp + + Copyright (C) 2009 Daniel Novotny (dnovotny@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 FILETRANSFER_H_ +#define FILETRANSFER_H_ + +#include <string> +#include "plugin.h" +#include "action.h" + +class CFileTransfer : public CAction +{ + private: + std::string m_sURL; + std::string m_sArchiveType; + int m_nRetryCount; + int m_nRetryDelay; + + void CreateArchive(const char *pArchiveName, const char *pDir); + void SendFile(const char *pURL, const char *pFilename); + + public: + CFileTransfer(); + virtual void SetSettings(const map_plugin_settings_t& pSettings); + virtual void Run(const char *pActionDir, const char *pArgs, int force); +}; + +#endif /* FILETRANSFER_H_ */ diff --git a/src/plugins/Kerneloops.conf b/src/plugins/Kerneloops.conf new file mode 100644 index 00000000..67ad07b9 --- /dev/null +++ b/src/plugins/Kerneloops.conf @@ -0,0 +1,13 @@ +Enabled = yes + +# Do we want kernel oopses to be visible to any user? +# 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/Kerneloops.cpp b/src/plugins/Kerneloops.cpp new file mode 100644 index 00000000..37cab992 --- /dev/null +++ b/src/plugins/Kerneloops.cpp @@ -0,0 +1,31 @@ +/* + 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 "Kerneloops.h" +#include "abrt_exception.h" + +PLUGIN_INFO(ANALYZER, + CAnalyzerKerneloops, + "Kerneloops", + "0.0.2", + _("Analyzes kernel oopses"), + "anton@redhat.com", + "https://people.redhat.com/aarapov", + ""); diff --git a/src/plugins/Kerneloops.h b/src/plugins/Kerneloops.h new file mode 100644 index 00000000..914f1fc8 --- /dev/null +++ b/src/plugins/Kerneloops.h @@ -0,0 +1,38 @@ +/* + * 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_KERNELOOPS_H_ +#define __INCLUDE_GUARD_KERNELOOPS_H_ + +#include "plugin.h" +#include "analyzer.h" +#include <string> + +class CAnalyzerKerneloops : public CAnalyzer +{ +}; + +#endif diff --git a/src/plugins/KerneloopsReporter.cpp b/src/plugins/KerneloopsReporter.cpp new file mode 100644 index 00000000..ae459737 --- /dev/null +++ b/src/plugins/KerneloopsReporter.cpp @@ -0,0 +1,143 @@ +/* + 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 "comm_layer_inner.h" +#include "abrt_exception.h" +#include "KerneloopsReporter.h" + +using namespace std; + +CKerneloopsReporter::CKerneloopsReporter() +{ + m_pSettings["SubmitURL"] = "http://submit.kerneloops.org/submitoops.php"; +} + +CKerneloopsReporter::~CKerneloopsReporter() +{ +} + +void CKerneloopsReporter::SetSettings(const map_plugin_settings_t& pSettings) +{ + /* Can't simply do this: + + m_pSettings = pSettings; + + * - it will erase keys which aren't present in pSettings. + * Example: if Bugzilla.conf doesn't have "Login = foo", + * then there's no pSettings["Login"] and m_pSettings = pSettings + * will nuke default m_pSettings["Login"] = "", + * making GUI think that we have no "Login" key at all + * and thus never overriding it - even if it *has* an override! + */ + + map_plugin_settings_t::iterator it = m_pSettings.begin(); + while (it != m_pSettings.end()) + { + map_plugin_settings_t::const_iterator override = pSettings.find(it->first); + if (override != pSettings.end()) + { + VERB3 log(" kerneloops settings[%s]='%s'", it->first.c_str(), it->second.c_str()); + it->second = override->second; + } + it++; + } +} + +string CKerneloopsReporter::Report(const map_crash_data_t& crash_data, + const map_plugin_settings_t& settings, + const char *args) +{ + /* abrt-action-kerneloops [-s] -c /etc/arbt/Kerneloops.conf -c - -d pCrashData.dir NULL */ + char *argv[9]; + char **pp = argv; + *pp++ = (char*)"abrt-action-kerneloops"; + +//We want to consume output, so don't redirect to syslog. +// if (logmode & LOGMODE_SYSLOG) +// *pp++ = (char*)"-s"; +//TODO: the actions<->daemon interaction will be changed anyway... + + *pp++ = (char*)"-c"; + *pp++ = (char*)(PLUGINS_CONF_DIR"/Kerneloops."PLUGINS_CONF_EXTENSION); + *pp++ = (char*)"-c"; + *pp++ = (char*)"-"; + *pp++ = (char*)"-d"; + *pp++ = (char*)get_crash_data_item_content_or_NULL(crash_data, CD_DUMPDIR); + *pp = NULL; + int pipefds[2]; + pid_t pid = fork_execv_on_steroids(EXECFLG_INPUT + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, + argv, + pipefds, + /* unsetenv_vec: */ NULL, + /* dir: */ NULL, + /* uid(unused): */ 0 + ); + + /* Write the configuration to stdin */ + map_plugin_settings_t::const_iterator it = settings.begin(); + while (it != settings.end()) + { + full_write_str(pipefds[1], it->first.c_str()); + full_write_str(pipefds[1], "="); + full_write_str(pipefds[1], it->second.c_str()); + full_write_str(pipefds[1], "\n"); + it++; + } + close(pipefds[1]); + + FILE *fp = fdopen(pipefds[0], "r"); + if (!fp) + die_out_of_memory(); + + /* Consume log from stdout */ + string bug_status; + char *buf; + while ((buf = xmalloc_fgetline(fp)) != NULL) + { + if (strncmp(buf, "STATUS:", 7) == 0) + bug_status = buf + 7; + else + if (strncmp(buf, "EXCEPT:", 7) == 0) + { + CABRTException e(EXCEP_PLUGIN, "%s", buf + 7); + free(buf); + fclose(fp); + waitpid(pid, NULL, 0); + throw e; + } + update_client("%s", buf); + free(buf); + } + + fclose(fp); /* this also closes pipefds[0] */ + /* wait for child to actually exit, and prevent leaving a zombie behind */ + waitpid(pid, NULL, 0); + + return bug_status; +} + +PLUGIN_INFO(REPORTER, + CKerneloopsReporter, + "KerneloopsReporter", + "0.0.1", + _("Sends kernel oops information to kerneloops.org"), + "anton@redhat.com", + "http://people.redhat.com/aarapov", + PLUGINS_LIB_DIR"/KerneloopsReporter.glade"); diff --git a/src/plugins/KerneloopsReporter.glade b/src/plugins/KerneloopsReporter.glade new file mode 100644 index 00000000..1ba287b8 --- /dev/null +++ b/src/plugins/KerneloopsReporter.glade @@ -0,0 +1,118 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.14"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkDialog" id="PluginDialog"> + <property name="border_width">12</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="icon_name">abrt</property> + <property name="type_hint">normal</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_columns">2</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <object class="GtkLabel" id="lSubmitURL"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Submit URL:</property> + </object> + <packing> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_SubmitURL"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Kerneloops Reporter plugin configuration</b></property> + <property name="use_markup">True</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="button2"> + <property name="label">gtk-cancel</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> + <child> + <object class="GtkButton" id="button1"> + <property name="label">gtk-apply</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">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-6">button2</action-widget> + <action-widget response="-10">button1</action-widget> + </action-widgets> + </object> +</interface> diff --git a/src/plugins/KerneloopsReporter.h b/src/plugins/KerneloopsReporter.h new file mode 100644 index 00000000..e0f4a1bb --- /dev/null +++ b/src/plugins/KerneloopsReporter.h @@ -0,0 +1,47 @@ +/* + * 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_KERNELOOPSREPORTER_H_ +#define __INCLUDE_GUARD_KERNELOOPSREPORTER_H_ + +#include "plugin.h" +#include "reporter.h" + +#include <string> + +class CKerneloopsReporter : public CReporter +{ + public: + CKerneloopsReporter(); + ~CKerneloopsReporter(); + + virtual void SetSettings(const map_plugin_settings_t& pSettings); + virtual std::string Report(const map_crash_data_t& pCrashData, + const map_plugin_settings_t& pSettings, + const char *pArgs); +}; + +#endif diff --git a/src/plugins/KerneloopsScanner.cpp b/src/plugins/KerneloopsScanner.cpp new file mode 100644 index 00000000..d187daa9 --- /dev/null +++ b/src/plugins/KerneloopsScanner.cpp @@ -0,0 +1,214 @@ +/* + 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); + + 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); + 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 new file mode 100644 index 00000000..2bddb0f4 --- /dev/null +++ b/src/plugins/KerneloopsScanner.h @@ -0,0 +1,42 @@ +/* + * 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 new file mode 100644 index 00000000..68f309bc --- /dev/null +++ b/src/plugins/KerneloopsSysLog.cpp @@ -0,0 +1,383 @@ +/* + 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 new file mode 100644 index 00000000..d8b4d32b --- /dev/null +++ b/src/plugins/KerneloopsSysLog.h @@ -0,0 +1,35 @@ +/* + * 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/Logger.conf b/src/plugins/Logger.conf new file mode 100644 index 00000000..aadd3515 --- /dev/null +++ b/src/plugins/Logger.conf @@ -0,0 +1,8 @@ +# Description: Writes report to a file + +# Configuration for Logger plugin +Enabled = yes + +LogPath = /var/log/abrt.log + +AppendLogs = yes diff --git a/src/plugins/Logger.glade b/src/plugins/Logger.glade new file mode 100644 index 00000000..a0a909a4 --- /dev/null +++ b/src/plugins/Logger.glade @@ -0,0 +1,135 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.14"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkDialog" id="PluginDialog"> + <property name="border_width">12</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="window_position">center-on-parent</property> + <property name="icon_name">abrt</property> + <property name="type_hint">normal</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <object class="GtkLabel" id="lLogPath"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Logger file:</property> + </object> + <packing> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_LogPath"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="conf_AppendLogs"> + <property name="label" translatable="yes">Append new logs</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Logger plugin configuration</b></property> + <property name="use_markup">True</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="button2"> + <property name="label">gtk-cancel</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> + <child> + <object class="GtkButton" id="button1"> + <property name="label">gtk-apply</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">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-6">button2</action-widget> + <action-widget response="-10">button1</action-widget> + </action-widgets> + </object> +</interface> diff --git a/src/plugins/Mailx.conf b/src/plugins/Mailx.conf new file mode 100644 index 00000000..1d946427 --- /dev/null +++ b/src/plugins/Mailx.conf @@ -0,0 +1,17 @@ +# Description: Sends an email with a report (using mailx command) + +# Configuration to Email reporter plugin +Enabled = yes + +# In abrt.conf, plugin takes one parameter: subject (in "" if you need to embed spaces). +# If it isn't specified, then a default subject is taken from this file +Subject = "[abrt] crash report" + +# Your Email +EmailFrom = user@localhost + +# Email To +EmailTo = root@localhost + +# Warning! enabling this may cause sending a lot of MB via email +SendBinaryData = no diff --git a/src/plugins/Mailx.cpp b/src/plugins/Mailx.cpp new file mode 100644 index 00000000..255c873d --- /dev/null +++ b/src/plugins/Mailx.cpp @@ -0,0 +1,153 @@ +/* + Mailx.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 "abrtlib.h" +#include "Mailx.h" +#include "abrt_exception.h" +#include "comm_layer_inner.h" + +#define MAILX_COMMAND "/bin/mailx" + +CMailx::CMailx() +{ + m_email_from = xstrdup("user@localhost"); + m_email_to = xstrdup("root@localhost"); + m_subject = xstrdup("[abrt] full crash report"); + m_send_binary_data = false; +} + +CMailx::~CMailx() +{ + free(m_email_from); + free(m_email_to); + free(m_subject); +} + +static void exec_and_feed_input(uid_t uid, const char* text, char **args) +{ + int pipein[2]; + + pid_t child = fork_execv_on_steroids( + EXECFLG_INPUT | EXECFLG_QUIET | EXECFLG_SETGUID, + args, + pipein, + /*unsetenv_vec:*/ NULL, + /*dir:*/ NULL, + uid); + + full_write_str(pipein[1], text); + close(pipein[1]); + + waitpid(child, NULL, 0); /* wait for command completion */ +} + +static char** append_str_to_vector(char **vec, unsigned &size, const char *str) +{ + //log("old vec: %p", vec); + 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; + return vec; +} + +std::string CMailx::Report(const map_crash_data_t& pCrashData, + const map_plugin_settings_t& pSettings, + const char *pArgs) +{ + SetSettings(pSettings); + char **args = NULL; + unsigned arg_size = 0; + args = append_str_to_vector(args, arg_size, MAILX_COMMAND); + + char *dsc = make_dsc_mailx(pCrashData); + + map_crash_data_t::const_iterator it; + for (it = pCrashData.begin(); it != pCrashData.end(); it++) + { + if (it->second[CD_TYPE] == CD_BIN && m_send_binary_data) + { + args = append_str_to_vector(args, arg_size, "-a"); + args = append_str_to_vector(args, arg_size, it->second[CD_CONTENT].c_str()); + } + } + + args = append_str_to_vector(args, arg_size, "-s"); + args = append_str_to_vector(args, arg_size, (pArgs[0] != '\0' ? pArgs : m_subject)); + args = append_str_to_vector(args, arg_size, "-r"); + args = append_str_to_vector(args, arg_size, m_email_from); + args = append_str_to_vector(args, arg_size, m_email_to); + + update_client(_("Sending an email...")); + const char *uid_str = get_crash_data_item_content_or_NULL(pCrashData, CD_UID); + exec_and_feed_input(xatoi_u(uid_str), dsc, args); + + free(dsc); + + while (*args) + { + free(*args++); + } + args -= arg_size; + free(args); + + return ssprintf("Email was sent to: %s", m_email_to); +} + +void CMailx::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("Subject"); + if (it != end) + { + free(m_subject); + m_subject = xstrdup(it->second.c_str()); + } + it = pSettings.find("EmailFrom"); + if (it != end) + { + free(m_email_from); + m_email_from = xstrdup(it->second.c_str()); + } + it = pSettings.find("EmailTo"); + if (it != end) + { + free(m_email_to); + m_email_to = xstrdup(it->second.c_str()); + } + it = pSettings.find("SendBinaryData"); + if (it != end) + { + m_send_binary_data = string_to_bool(it->second.c_str()); + } +} + +PLUGIN_INFO(REPORTER, + CMailx, + "Mailx", + "0.0.2", + _("Sends an email with a report (via mailx command)"), + "zprikryl@redhat.com", + "https://fedorahosted.org/abrt/wiki", + PLUGINS_LIB_DIR"/Mailx.glade"); diff --git a/src/plugins/Mailx.glade b/src/plugins/Mailx.glade new file mode 100644 index 00000000..656204b5 --- /dev/null +++ b/src/plugins/Mailx.glade @@ -0,0 +1,184 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.14"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkDialog" id="PluginDialog"> + <property name="border_width">12</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="window_position">center-on-parent</property> + <property name="icon_name">abrt</property> + <property name="type_hint">normal</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkTable" id="table1"> + <property name="visible">True</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> + <child> + <object class="GtkLabel" id="lSubject"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Subject:</property> + </object> + <packing> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_Subject"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_EmailFrom"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</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="lEmailFrom"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Your Email:</property> + </object> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="lEmailTo"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Recipient's Email:</property> + </object> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_EmailTo"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</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="GtkCheckButton" id="conf_SendBinaryData"> + <property name="label" translatable="yes">Send Binary Data</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="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Mailx plugin configuration</b></property> + <property name="use_markup">True</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="button2"> + <property name="label">gtk-cancel</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> + <child> + <object class="GtkButton" id="button1"> + <property name="label">gtk-apply</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">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-6">button2</action-widget> + <action-widget response="-10">button1</action-widget> + </action-widgets> + </object> +</interface> diff --git a/src/plugins/Mailx.h b/src/plugins/Mailx.h new file mode 100644 index 00000000..326a6371 --- /dev/null +++ b/src/plugins/Mailx.h @@ -0,0 +1,48 @@ +/* + Mailx.h - header file for Mailx reporter plugin + - it simple sends an email to specific address via mailx command + + 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 MAILX_H_ +#define MAILX_H_ + +#include <string> +#include "plugin.h" +#include "reporter.h" + +class CMailx : public CReporter +{ + private: + char *m_email_from; + char *m_email_to; + char *m_subject; + bool m_send_binary_data; + + public: + CMailx(); + ~CMailx(); + + virtual void SetSettings(const map_plugin_settings_t& pSettings); + + virtual std::string Report(const map_crash_data_t& pCrashData, + const map_plugin_settings_t& pSettings, + const char *pArgs); +}; + +#endif diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am new file mode 100644 index 00000000..2cb85709 --- /dev/null +++ b/src/plugins/Makefile.am @@ -0,0 +1,316 @@ +INC_PATH=$(srcdir)/../../inc +UTILS_PATH=$(srcdir)/../../lib/utils +AM_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) +pluginslibdir=$(PLUGINS_LIB_DIR) +libexec_SCRIPTS = \ + abrt-action-install-debuginfo + +pluginslib_LTLIBRARIES = \ + libCCpp.la \ + libMailx.la \ + libSQLite3.la \ + libKerneloopsScanner.la\ + libKerneloops.la \ + libKerneloopsReporter.la \ + libSOSreport.la \ + libBugzilla.la \ + libRHTSupport.la \ + libReportUploader.la \ + libPython.la \ + libFileTransfer.la + +dist_pluginslib_DATA = \ + Logger.glade \ + Mailx.glade \ + Bugzilla.glade \ + RHTSupport.glade \ + ReportUploader.glade \ + KerneloopsReporter.glade + +pluginsconfdir = $(PLUGINS_CONF_DIR) +dist_pluginsconf_DATA = \ + CCpp.conf \ + Mailx.conf \ + SQLite3.conf \ + Logger.conf \ + Kerneloops.conf \ + Bugzilla.conf \ + RHTSupport.conf \ + ReportUploader.conf \ + FileTransfer.conf \ + Python.conf \ + SOSreport.conf + +man_MANS = \ + abrt-FileTransfer.7 \ + abrt-Bugzilla.7 \ + abrt-KerneloopsScanner.7 \ + abrt-KerneloopsReporter.7 \ + abrt-Logger.7 \ + abrt-Mailx.7 \ + abrt-plugins.7 \ + abrt-SQLite3.7 \ + abrt-ReportUploader.7 + +EXTRA_DIST = $(man_MANS) abrt-action-install-debuginfo + +$(DESTDIR)/$(DEBUG_INFO_DIR): + $(mkdir_p) '$@' + +install-data-hook: $(DESTDIR)/$(DEBUG_INFO_DIR) + sed 's: = /var/: = $(localstatedir)/:g' -i \ + $(DESTDIR)$(sysconfdir)/abrt/plugins/SQLite3.conf \ + $(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\" \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' +# -DHOSTILE_KERNEL + +# Kerneloops +libKerneloops_la_SOURCES = Kerneloops.cpp Kerneloops.h +libKerneloops_la_LDFLAGS = -avoid-version +libKerneloops_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) + +# KerneloopsReporter +libKerneloopsReporter_la_SOURCES = KerneloopsReporter.cpp KerneloopsReporter.h +libKerneloopsReporter_la_LDFLAGS = -avoid-version +libKerneloopsReporter_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" + +# 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) + +# Mailx +libMailx_la_SOURCES = Mailx.cpp Mailx.h +libMailx_la_LDFLAGS = -avoid-version +libMailx_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" -DLOCALSTATEDIR='"$(localstatedir)"' + +# SQLite3 +libSQLite3_la_SOURCES = SQLite3.cpp SQLite3.h +libSQLite3_la_LDFLAGS = -avoid-version +libSQLite3_la_LIBADD = $(SQLITE3_LIBS) $(GLIB_LIBS) +libSQLite3_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) $(SQLITE3_CFLAGS) -DLOCALSTATEDIR='"$(localstatedir)"' $(GLIB_CFLAGS) + +# SOSreport +libSOSreport_la_SOURCES = SOSreport.cpp SOSreport.h +libSOSreport_la_LDFLAGS = -avoid-version + +# Bugzilla +libBugzilla_la_SOURCES = Bugzilla.h Bugzilla.cpp +libBugzilla_la_LIBADD = +libBugzilla_la_LDFLAGS = -avoid-version +libBugzilla_la_CPPFLAGS = \ + -I$(INC_PATH) -I$(UTILS_PATH) \ + -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" + +# RHTSupport +libRHTSupport_la_SOURCES = RHTSupport.h RHTSupport.cpp +libRHTSupport_la_LIBADD = +libRHTSupport_la_LDFLAGS = -avoid-version +libRHTSupport_la_CPPFLAGS = \ + -I$(INC_PATH) -I$(UTILS_PATH) \ + -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' + +# ReportUploader +libReportUploader_la_SOURCES = ReportUploader.h ReportUploader.cpp +libReportUploader_la_LDFLAGS = -avoid-version +libReportUploader_la_LIBADD = $(CURL_LIBS) +libReportUploader_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) $(CURL_CFLAGS) -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" + +# Python +libPython_la_SOURCES = Python.h Python.cpp +#libPython_la_LIBADD = $(NSS_LIBS) +libPython_la_LDFLAGS = -avoid-version +libPython_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) + +# FileTrasfer +libFileTransfer_la_SOURCES = FileTransfer.cpp FileTransfer.h +libFileTransfer_la_LDFLAGS = -avoid-version -ltar -lbz2 -lz +libFileTransfer_la_LIBADD = $(CURL_LIBS) +libFileTransfer_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) $(CURL_CFLAGS) -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" + +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-print + +abrt_action_analyze_c_SOURCES = \ + abrt-action-analyze-c.c +abrt_action_analyze_c_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -DBIN_DIR=\"$(bindir)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ + -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ + -D_GNU_SOURCE \ + -Wall -Werror +abrt_action_analyze_c_LDADD = \ + ../../lib/utils/libABRTUtils.la + +abrt_action_analyze_python_SOURCES = \ + abrt-action-analyze-python.c +abrt_action_analyze_python_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -DBIN_DIR=\"$(bindir)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ + -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ + -D_GNU_SOURCE \ + -Wall -Werror +abrt_action_analyze_python_LDADD = \ + ../../lib/utils/libABRTUtils.la + +abrt_action_analyze_oops_SOURCES = \ + abrt-action-analyze-oops.c +abrt_action_analyze_oops_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -DBIN_DIR=\"$(bindir)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ + -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ + -D_GNU_SOURCE \ + -Wall -Werror +abrt_action_analyze_oops_LDADD = \ + ../../lib/utils/libABRTUtils.la + +abrt_action_generate_backtrace_SOURCES = \ + abrt-action-generate-backtrace.c +abrt_action_generate_backtrace_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -DBIN_DIR=\"$(bindir)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ + -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ + -D_GNU_SOURCE \ + -Wall -Werror +abrt_action_generate_backtrace_LDADD = \ + ../../lib/utils/libABRTUtils.la \ + ../btparser/libbtparser.la + +abrt_action_bugzilla_SOURCES = \ + abrt-action-bugzilla.cpp +abrt_action_bugzilla_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -DBIN_DIR=\"$(bindir)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -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_bugzilla_LDADD = \ + $(GLIB_LIBS) \ + ../../lib/utils/libABRT_web_utils.la \ + ../../lib/utils/libABRTdUtils.la \ + ../../lib/utils/libABRTUtils.la + +abrt_action_rhtsupport_SOURCES = \ + abrt_rh_support.h abrt_rh_support.c \ + abrt-action-rhtsupport.cpp +abrt_action_rhtsupport_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -DBIN_DIR=\"$(bindir)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ + -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ + $(GLIB_CFLAGS) \ + $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \ + -D_GNU_SOURCE \ + -Wall -Werror +abrt_action_rhtsupport_LDFLAGS = -ltar +abrt_action_rhtsupport_LDADD = \ + $(GLIB_LIBS) \ + $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) \ + ../../lib/utils/libABRT_web_utils.la \ + ../../lib/utils/libABRTdUtils.la \ + ../../lib/utils/libABRTUtils.la + +abrt_action_kerneloops_SOURCES = \ + abrt-action-kerneloops.cpp +abrt_action_kerneloops_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -DBIN_DIR=\"$(bindir)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -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 +# libABRTdUtils is used only because of LoadPluginSettings: +abrt_action_kerneloops_LDADD = \ + ../../lib/utils/libABRT_web_utils.la \ + ../../lib/utils/libABRTdUtils.la \ + ../../lib/utils/libABRTUtils.la + +abrt_action_print_SOURCES = \ + abrt-action-print.cpp +abrt_action_print_CPPFLAGS = \ + -I$(srcdir)/../../inc \ + -I$(srcdir)/../../lib/utils \ + -DBIN_DIR=\"$(bindir)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -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 +# libABRTdUtils is used only because of make_description_logger: +abrt_action_print_LDADD = \ + ../../lib/utils/libABRTdUtils.la \ + ../../lib/utils/libABRTUtils.la + +DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ diff --git a/src/plugins/Python.conf b/src/plugins/Python.conf new file mode 100644 index 00000000..3201c6da --- /dev/null +++ b/src/plugins/Python.conf @@ -0,0 +1 @@ +Enabled = yes diff --git a/src/plugins/Python.cpp b/src/plugins/Python.cpp new file mode 100644 index 00000000..e955b5fb --- /dev/null +++ b/src/plugins/Python.cpp @@ -0,0 +1,30 @@ +/* + 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" +#include "Python.h" +#include "abrt_exception.h" + +PLUGIN_INFO(ANALYZER, + CAnalyzerPython, + "Python", + "0.0.1", + _("Analyzes crashes in Python programs"), + "zprikryl@redhat.com, jmoskovc@redhat.com", + "https://fedorahosted.org/abrt/wiki", + ""); diff --git a/src/plugins/Python.h b/src/plugins/Python.h new file mode 100644 index 00000000..3f01d2c6 --- /dev/null +++ b/src/plugins/Python.h @@ -0,0 +1,30 @@ +/* + 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 PYTHON_H_ +#define PYTHON_H_ + +#include <string> +#include "plugin.h" +#include "analyzer.h" + +class CAnalyzerPython : public CAnalyzer +{ +}; + +#endif /* PYTHON_H_ */ diff --git a/src/plugins/RHTSupport.conf b/src/plugins/RHTSupport.conf new file mode 100644 index 00000000..ecd5992a --- /dev/null +++ b/src/plugins/RHTSupport.conf @@ -0,0 +1,11 @@ +# Description: Reports crashes to Red Hat support + +Enabled = yes + +URL = https://api.access.redhat.com/rs +# No means that ssl certificates will not be checked +SSLVerify = yes +# Your login has to exist +Login = +# Your password +Password = diff --git a/src/plugins/RHTSupport.cpp b/src/plugins/RHTSupport.cpp new file mode 100644 index 00000000..3732afe3 --- /dev/null +++ b/src/plugins/RHTSupport.cpp @@ -0,0 +1,147 @@ +/* + 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" +#include "crash_types.h" +#include "abrt_exception.h" +#include "comm_layer_inner.h" +#include "RHTSupport.h" + +using namespace std; + +CReporterRHticket::CReporterRHticket() +{ + m_pSettings["URL"] = "https://api.access.redhat.com/rs"; + m_pSettings["Login"] = ""; + m_pSettings["Password"] = ""; + m_pSettings["SSLVerify"] = "yes"; +} + +CReporterRHticket::~CReporterRHticket() +{ +} + +void CReporterRHticket::SetSettings(const map_plugin_settings_t& pSettings) +{ + /* Can't simply do this: + + m_pSettings = pSettings; + + * - it will erase keys which aren't present in pSettings. + * Example: if Bugzilla.conf doesn't have "Login = foo", + * then there's no pSettings["Login"] and m_pSettings = pSettings + * will nuke default m_pSettings["Login"] = "", + * making GUI think that we have no "Login" key at all + * and thus never overriding it - even if it *has* an override! + */ + + map_plugin_settings_t::iterator it = m_pSettings.begin(); + while (it != m_pSettings.end()) + { + map_plugin_settings_t::const_iterator override = pSettings.find(it->first); + if (override != pSettings.end()) + { + VERB3 log(" rhtsupport settings[%s]='%s'", it->first.c_str(), it->second.c_str()); + it->second = override->second; + } + it++; + } +} + +string CReporterRHticket::Report(const map_crash_data_t& crash_data, + const map_plugin_settings_t& settings, + const char *args) +{ + /* abrt-action-rhtsupport [-s] -c /etc/arbt/RHTSupport.conf -c - -d pCrashData.dir NULL */ + char *argv[9]; + char **pp = argv; + *pp++ = (char*)"abrt-action-rhtsupport"; + +//We want to consume output, so don't redirect to syslog. +// if (logmode & LOGMODE_SYSLOG) +// *pp++ = (char*)"-s"; +//TODO: the actions<->daemon interaction will be changed anyway... + + *pp++ = (char*)"-c"; + *pp++ = (char*)(PLUGINS_CONF_DIR"/RHTSupport."PLUGINS_CONF_EXTENSION); + *pp++ = (char*)"-c"; + *pp++ = (char*)"-"; + *pp++ = (char*)"-d"; + *pp++ = (char*)get_crash_data_item_content_or_NULL(crash_data, CD_DUMPDIR); + *pp = NULL; + int pipefds[2]; + pid_t pid = fork_execv_on_steroids(EXECFLG_INPUT + EXECFLG_OUTPUT + EXECFLG_ERR2OUT, + argv, + pipefds, + /* unsetenv_vec: */ NULL, + /* dir: */ NULL, + /* uid(unused): */ 0 + ); + + /* Write the configuration to stdin */ + map_plugin_settings_t::const_iterator it = settings.begin(); + while (it != settings.end()) + { + full_write_str(pipefds[1], it->first.c_str()); + full_write_str(pipefds[1], "="); + full_write_str(pipefds[1], it->second.c_str()); + full_write_str(pipefds[1], "\n"); + it++; + } + close(pipefds[1]); + + FILE *fp = fdopen(pipefds[0], "r"); + if (!fp) + die_out_of_memory(); + + /* Consume log from stdout */ + string bug_status; + char *buf; + while ((buf = xmalloc_fgetline(fp)) != NULL) + { + if (strncmp(buf, "STATUS:", 7) == 0) + bug_status = buf + 7; + else + if (strncmp(buf, "EXCEPT:", 7) == 0) + { + CABRTException e(EXCEP_PLUGIN, "%s", buf + 7); + free(buf); + fclose(fp); + waitpid(pid, NULL, 0); + throw e; + } + update_client("%s", buf); + free(buf); + } + + fclose(fp); /* this also closes pipefds[0] */ + /* wait for child to actually exit, and prevent leaving a zombie behind */ + waitpid(pid, NULL, 0); + + return bug_status; +} + +PLUGIN_INFO(REPORTER, + CReporterRHticket, + "RHticket", + "0.0.4", + _("Reports bugs to Red Hat support"), + "Denys Vlasenko <dvlasenk@redhat.com>", + "https://fedorahosted.org/abrt/wiki", + PLUGINS_LIB_DIR"/RHTSupport.glade"); diff --git a/src/plugins/RHTSupport.glade b/src/plugins/RHTSupport.glade new file mode 100644 index 00000000..64fd6c26 --- /dev/null +++ b/src/plugins/RHTSupport.glade @@ -0,0 +1,213 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.14"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkDialog" id="PluginDialog"> + <property name="border_width">12</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="window_position">center-on-parent</property> + <property name="icon_name">abrt</property> + <property name="type_hint">normal</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">5</property> + <property name="n_columns">2</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <object class="GtkLabel" id="lURL"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">RHTSupport URL:</property> + </object> + <packing> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="lLogin"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Login:</property> + </object> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="lPassword"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Password:</property> + </object> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_URL"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_Login"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</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="GtkEntry" id="conf_Password"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="visibility">False</property> + <property name="invisible_char">●</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="GtkCheckButton" id="cb_Password"> + <property name="label" translatable="yes">Show password</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</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> + <child> + <object class="GtkLabel" id="label3"> + <property name="xalign">0</property> + </object> + <packing> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="conf_SSLVerify"> + <property name="label" translatable="yes">SSL verify</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>RHTSupport plugin configuration</b></property> + <property name="use_markup">True</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="button2"> + <property name="label">gtk-cancel</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> + <child> + <object class="GtkButton" id="bApply"> + <property name="label">gtk-apply</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">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-6">button2</action-widget> + <action-widget response="-10">bApply</action-widget> + </action-widgets> + </object> + <object class="GtkAction" id="action1"/> +</interface> diff --git a/src/plugins/RHTSupport.h b/src/plugins/RHTSupport.h new file mode 100644 index 00000000..2338732f --- /dev/null +++ b/src/plugins/RHTSupport.h @@ -0,0 +1,38 @@ +/* + 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 RHTICKET_H_ +#define RHTICKET_H_ + +#include "plugin.h" +#include "reporter.h" + +class CReporterRHticket: public CReporter +{ + public: + CReporterRHticket(); + virtual ~CReporterRHticket(); + + virtual std::string Report(const map_crash_data_t& pCrashData, + const map_plugin_settings_t& pSettings, + const char *pArgs); + + virtual void SetSettings(const map_plugin_settings_t& pSettings); +}; + +#endif diff --git a/src/plugins/ReportUploader.conf b/src/plugins/ReportUploader.conf new file mode 100644 index 00000000..7a7b9133 --- /dev/null +++ b/src/plugins/ReportUploader.conf @@ -0,0 +1,23 @@ +# Description: Packs crash data into .tar.gz file, optionally uploads it via FTP/SCP/etc + +Enabled = yes + +# Customer = "Example Inc." +# Ticket = IT12345 +# Encrypt = yes +# If set to "no" or commented out, +# compressed ticket data will be copied to /tmp: +# Upload = yes + +# If "Upload = yes", URL to upload the files to. +# supported: ftp, ftps, http, https, scp, sftp, tftp, file +# for example: ftp://user:password@server.name/directory +# or: scp://user:password@server.name:port/directory etc. +# for testing: file:///tmp/test_directory +# URL = + +# How many times we try to upload the file +# RetryCount = 3 + +# How long we wait between we retry the upload (in seconds) +# RetryDelay = 20 diff --git a/src/plugins/ReportUploader.cpp b/src/plugins/ReportUploader.cpp new file mode 100644 index 00000000..4100e996 --- /dev/null +++ b/src/plugins/ReportUploader.cpp @@ -0,0 +1,517 @@ +/* + ReportUploader.cpp + + 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 "abrt_curl.h" +#include "ReportUploader.h" +#include "abrt_exception.h" +#include "comm_layer_inner.h" + +using namespace std; + + +CReportUploader::CReportUploader() : + m_bEncrypt(false), + m_bUpload(false), + m_nRetryCount(3), + m_nRetryDelay(20) +{} + +CReportUploader::~CReportUploader() +{} + + +static void RunCommand(const char *cmd) +{ + int retcode = system(cmd); + if (retcode) + { + throw CABRTException(EXCEP_PLUGIN, "'%s' exited with %d", cmd, retcode); + } +} + +static string ReadCommand(const char *cmd) +{ + FILE* fp = popen(cmd, "r"); + if (!fp) + { + throw CABRTException(EXCEP_PLUGIN, "Error running '%s'", cmd); + } + + string result; + char *buff; + while ((buff = xmalloc_fgetline(fp)) != NULL) + { + result += buff; + free(buff); + } + + int retcode = pclose(fp); + if (retcode) + { + throw CABRTException(EXCEP_PLUGIN, "'%s' exited with %d", cmd, retcode); + } + + return result; +} + +static void WriteCommand(const char *cmd, const char *input) +{ + FILE* fp = popen(cmd, "w"); + if (!fp) + { + throw CABRTException(EXCEP_PLUGIN, "error running '%s'", cmd); + } + + /* Hoping it's not too big to get us forever blocked... */ + fputs(input, fp); + + int retcode = pclose(fp); + if (retcode) + { + throw CABRTException(EXCEP_PLUGIN, "'%s' exited with %d", cmd, retcode); + } +} + +void CReportUploader::SendFile(const char *pURL, const char *pFilename, int retry_count, int retry_delay) +{ + if (pURL[0] == '\0') + { + error_msg(_("FileTransfer: URL not specified")); + return; + } + + update_client(_("Sending archive %s to %s"), pFilename, pURL); + + const char *base = (strrchr(pFilename, '/') ? : pFilename-1) + 1; + char *whole_url = concat_path_file(pURL, base); + int count = retry_count; + int result; + while (1) + { + FILE* f = fopen(pFilename, "r"); + if (!f) + { + free(whole_url); + throw CABRTException(EXCEP_PLUGIN, "Can't open archive file '%s'", pFilename); + } + struct stat buf; + fstat(fileno(f), &buf); /* never fails */ + CURL* curl = xcurl_easy_init(); + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + /* specify target */ + curl_easy_setopt(curl, CURLOPT_URL, whole_url); + curl_easy_setopt(curl, CURLOPT_READDATA, f); + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)buf.st_size); + /* everything is done here; result 0 means success */ + result = curl_easy_perform(curl); + /* goodbye */ + curl_easy_cleanup(curl); + fclose(f); + if (result != 0) + { + update_client(_("Sending failed, trying again. %s"), curl_easy_strerror((CURLcode)result)); + } + if (result == 0 || --count <= 0) + break; + /* retry the upload if not succesful, wait a bit before next try */ + sleep(retry_delay); + } + free(whole_url); + + if (count <= 0 && result != 0) + { + throw CABRTException(EXCEP_PLUGIN, "Curl can not send a ticket"); + } +} + + +static void write_str_to_file(const char *str, const char *path, const char *fname) +{ + char *ofile_name = concat_path_file(path, fname); + FILE *ofile = fopen(ofile_name, "w"); + if (!ofile) + { + CABRTException e(EXCEP_PLUGIN, "Can't open '%s'", ofile_name); + free(ofile_name); + throw e; + } + free(ofile_name); + fputs(str, ofile); + fclose(ofile); +} + +string CReportUploader::Report(const map_crash_data_t& pCrashData, + const map_plugin_settings_t& pSettings, + const char *pArgs) +{ + string customer_name; + string ticket_name; + string upload_url; + bool do_encrypt; + bool do_upload; + int retry_count; + int retry_delay; + + /* if parse_settings fails it returns an empty map so we need to use defaults */ + map_plugin_settings_t settings = parse_settings(pSettings); + // Get ticket name, customer name, and do_encrypt from config settings + if (!settings.empty()) + { + customer_name = settings["Customer"]; + ticket_name = settings["Ticket"]; + upload_url = settings["URL"]; + do_encrypt = string_to_bool(settings["Encrypt"].c_str()); + do_upload = string_to_bool(settings["Upload"].c_str()); + retry_count = xatoi_u(settings["RetryCount"].c_str()); + retry_delay = xatoi_u(settings["RetryDelay"].c_str()); + } + else + { + customer_name = m_sCustomer; + ticket_name = m_sTicket; + upload_url = m_sURL; + do_encrypt = m_bEncrypt; + do_upload = m_bUpload; + retry_count = m_nRetryCount; + retry_delay = m_nRetryDelay; + } + update_client(_("Creating a ReportUploader report...")); + + bool have_ticket_name = (ticket_name != ""); + if (!have_ticket_name) + { + ticket_name = "ReportUploader-newticket"; + } + + // Format the time to add to the file name + char timebuf[256]; + time_t curtime = time(NULL); + strftime(timebuf, sizeof(timebuf), "-%Y%m%d%H%M%S", gmtime(&curtime)); + + // Create a tmp work directory, and within that + // create the "<ticketname>-yyyymmddhhmmss" directory + // which will be the root of the tarball + string file_name = ticket_name + timebuf; + + char tmpdir_name[] = "/tmp/abrtuploadXXXXXX"; + if (mkdtemp(tmpdir_name) == NULL) + { + throw CABRTException(EXCEP_PLUGIN, "Can't mkdir a temporary directory in /tmp"); + } + + char *tmptar_name = concat_path_file(tmpdir_name, file_name.c_str()); + if (mkdir(tmptar_name, 0700)) + { + CABRTException e(EXCEP_PLUGIN, "Can't mkdir '%s'", tmptar_name); + free(tmptar_name); + throw e; + } + + // Copy each entry into the tarball root. + // Files are simply copied, strings are written to a file + // TODO: some files are totally useless: + // "Reported", "Message" (plugin's output), "DumpDir", + // "Description" (package description) - maybe skip those? + map_crash_data_t::const_iterator it; + for (it = pCrashData.begin(); it != pCrashData.end(); it++) + { + const char *content = it->second[CD_CONTENT].c_str(); + if (it->second[CD_TYPE] == CD_TXT) + { + write_str_to_file(content, tmptar_name, it->first.c_str()); + } + else if (it->second[CD_TYPE] == CD_BIN) + { + char *ofile_name = concat_path_file(tmptar_name, it->first.c_str()); + if (copy_file(content, ofile_name, 0644) < 0) + { + CABRTException e(EXCEP_PLUGIN, + "Can't copy '%s' to '%s'", + content, ofile_name + ); + free(tmptar_name); + free(ofile_name); + throw e; + } + free(ofile_name); + } + } + + // add ticket_name and customer name to tarball + if (have_ticket_name) + { + write_str_to_file(ticket_name.c_str(), tmptar_name, "TICKET"); + } + if (customer_name != "") + { + write_str_to_file(customer_name.c_str(), tmptar_name, "CUSTOMER"); + } + + // Create the compressed tarball + string outfile_basename = file_name + ".tar.gz"; + char *outfile_name = concat_path_file(tmpdir_name, outfile_basename.c_str()); + string cmd = ssprintf("tar -C %s --create --gzip --file=%s %s", tmpdir_name, outfile_name, file_name.c_str()); + RunCommand(cmd.c_str()); + + // encrypt if requested + string key; + if (do_encrypt) + { + key = ReadCommand("openssl rand -base64 48"); + + string infile_name = outfile_name; + outfile_basename += ".aes"; + outfile_name = append_to_malloced_string(outfile_name, ".aes"); + + cmd = ssprintf("openssl aes-128-cbc -in %s -out %s -pass stdin", infile_name.c_str(), outfile_name); + WriteCommand(cmd.c_str(), key.c_str()); + } + + // generate md5sum + cmd = ssprintf("cd %s; md5sum <%s", tmpdir_name, outfile_basename.c_str()); + string md5sum = ReadCommand(cmd.c_str()); + + // upload or cp to /tmp + if (do_upload) + { + // FIXME: SendFile isn't working sometime (scp) + SendFile(upload_url.c_str(), outfile_name, retry_count, retry_delay); + } + else + { + cmd = ssprintf("cp %s /tmp/", outfile_name); + RunCommand(cmd.c_str()); + } + + // generate a reciept telling md5sum and encryption key + // note: do not internationalize these strings! + string msg; + if (have_ticket_name) + { + msg += "Please copy this into ticket: "; + msg += ticket_name; + msg += '\n'; + msg += "========cut here========\n"; + } + else + { + msg += "Please send this to your technical support:\n"; + msg += "========cut here========\n"; + } + if (do_upload) + { + msg += "RHUPLOAD: This report was sent to "; + msg += upload_url; + msg += '\n'; + } + else + { + msg += "RHUPLOAD: This report was copied into /tmp/:\n"; + } + if (have_ticket_name) + { + msg += "TICKET: "; + msg += ticket_name; + msg += '\n'; + } + msg += "FILE: "; + msg += outfile_basename; + msg += "\nMD5SUM: "; + msg += md5sum; + msg += '\n'; + if (do_encrypt) + { + msg += "KEY: aes-128-cbc\n"; + msg += key; + msg += '\n'; + } + msg += "==========end===========\n"; + + // warn the client (why _warn_? it's not an error, maybe update_client?): + //error_msg("%s", msg.c_str()); + + // delete the temporary directory + cmd = ssprintf("rm -rf %s", tmpdir_name); + RunCommand(cmd.c_str()); + + free(tmptar_name); + free(outfile_name); + + return msg; +} + +static bool is_string_safe(const char *str) +{ + const char *p = str; + while (*p) + { + unsigned char c = *p; + if ((c < '0' || c > '9') + && c != '_' + && c != '-' + ) { + c |= 0x20; // tolower + if (c < 'a' || c > 'z') + { + return false; + } + } + // only 0-9, -, _, A-Z, a-z reach this point + p++; + } + return true; +} + +void CReportUploader::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("Customer"); + if (it != end) + { + m_sCustomer = it->second; + } + // We use m_sTicket as part of filename, + // and we use resulting filename in system("cd %s; ...", filename) etc, + // so we are very paraniod about allowed chars + it = pSettings.find("Ticket"); + if (it != end && is_string_safe(it->second.c_str())) + { + m_sTicket = it->second; + } + it = pSettings.find("URL"); + if (it != end) + { + m_sURL = it->second; + } + it = pSettings.find("Encrypt"); + if (it != end) + { + m_bEncrypt = string_to_bool(it->second.c_str()); + } + it = pSettings.find("Upload"); + if (it != end) + { + m_bUpload = string_to_bool(it->second.c_str()); + } + it = pSettings.find("RetryCount"); + if (it != end) + { + m_nRetryCount = xatoi_u(it->second.c_str()); + } + it = pSettings.find("RetryDelay"); + if (it != end) + { + m_nRetryDelay = xatoi_u(it->second.c_str()); + } +} + +const map_plugin_settings_t& CReportUploader::GetSettings() +{ + m_pSettings["Customer"] = m_sCustomer; + m_pSettings["Ticket"] = m_sTicket; + m_pSettings["URL"] = m_sURL; + m_pSettings["Encrypt"] = m_bEncrypt ? "yes" : "no"; + m_pSettings["Upload"] = m_bUpload ? "yes" : "no"; + m_pSettings["RetryCount"] = to_string(m_nRetryCount); + m_pSettings["RetryDelay"] = to_string(m_nRetryDelay); + + return m_pSettings; +} + +//todo: make static +map_plugin_settings_t CReportUploader::parse_settings(const map_plugin_settings_t& pSettings) +{ + map_plugin_settings_t plugin_settings; + + map_plugin_settings_t::const_iterator end = pSettings.end(); + map_plugin_settings_t::const_iterator it; + + it = pSettings.find("Customer"); + if (it == end) + { + plugin_settings.clear(); + return plugin_settings; + } + plugin_settings["Customer"] = it->second; + + it = pSettings.find("Ticket"); + if (it == end) + { + plugin_settings.clear(); + return plugin_settings; + } + plugin_settings["Ticket"] = it->second; + + it = pSettings.find("URL"); + if (it == end) + { + plugin_settings.clear(); + return plugin_settings; + } + plugin_settings["URL"] = it->second; + + it = pSettings.find("Encrypt"); + if (it == end) + { + plugin_settings.clear(); + return plugin_settings; + } + plugin_settings["Encrypt"] = it->second; + + it = pSettings.find("Upload"); + if (it == end) + { + plugin_settings.clear(); + return plugin_settings; + } + plugin_settings["Upload"] = it->second; + + it = pSettings.find("RetryCount"); + if (it == end) + { + plugin_settings.clear(); + return plugin_settings; + } + plugin_settings["RetryCount"] = it->second; + + it = pSettings.find("RetryDelay"); + if (it == end) + { + plugin_settings.clear(); + return plugin_settings; + } + plugin_settings["RetryDelay"] = it->second; + + VERB1 log("User settings ok, using them instead of defaults"); + return plugin_settings; +} + +PLUGIN_INFO(REPORTER, + CReportUploader, + "ReportUploader", + "0.0.1", + _("Packs crash data into .tar.gz file, optionally uploads it via FTP/SCP/etc"), + "gavin@redhat.com", + "https://fedorahosted.org/abrt/wiki", + PLUGINS_LIB_DIR"/ReportUploader.glade"); diff --git a/src/plugins/ReportUploader.glade b/src/plugins/ReportUploader.glade new file mode 100644 index 00000000..c2bbd470 --- /dev/null +++ b/src/plugins/ReportUploader.glade @@ -0,0 +1,249 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.14"/> + <!-- interface-naming-policy project-wide --> + <object class="GtkDialog" id="PluginDialog"> + <property name="border_width">5</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="window_position">center-on-parent</property> + <property name="icon_name">abrt</property> + <property name="type_hint">normal</property> + <property name="has_separator">False</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child> + <object class="GtkFrame" id="frame1"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <child> + <object class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">7</property> + <property name="n_columns">2</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Customer:</property> + </object> + <packing> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Ticket:</property> + </object> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">URL:</property> + </object> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_Customer"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_Ticket"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</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="GtkEntry" id="conf_URL"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</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="label6"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Retry count:</property> + </object> + <packing> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_RetryCount"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</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> + <child> + <object class="GtkLabel" id="label7"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Retry delay:</property> + </object> + <packing> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="conf_RetryDelay"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <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> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="conf_Encrypt"> + <property name="label" translatable="yes">Use encryption</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="right_attach">2</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="conf_Upload"> + <property name="label" translatable="yes">Upload</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="right_attach">2</property> + <property name="top_attach">6</property> + <property name="bottom_attach">7</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Report Uploader plugin configuration</b></property> + <property name="use_markup">True</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="button2"> + <property name="label">gtk-cancel</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> + <child> + <object class="GtkButton" id="bApply"> + <property name="label">gtk-apply</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">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-6">button2</action-widget> + <action-widget response="-10">bApply</action-widget> + </action-widgets> + </object> + <object class="GtkAction" id="action1"/> +</interface> diff --git a/src/plugins/ReportUploader.h b/src/plugins/ReportUploader.h new file mode 100644 index 00000000..4ff780b8 --- /dev/null +++ b/src/plugins/ReportUploader.h @@ -0,0 +1,55 @@ +/* + ReportUploader.h + + Attach a configureable Ticket Number and Customer name to a report. + Create a compressed, optionally encrypted, tarball. + Upload tarball to configureable URL. + + 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 REPORTUPLOADER_H_ +#define REPORTUPLAODER_H_ + +#include "plugin.h" +#include "reporter.h" + +class CReportUploader : public CReporter +{ + private: + std::string m_sCustomer; + std::string m_sTicket; + std::string m_sURL; + bool m_bEncrypt; + bool m_bUpload; + int m_nRetryCount; + int m_nRetryDelay; + + void SendFile(const char *pURL, const char *pFilename, int retry_count, int retry_delay); + map_plugin_settings_t parse_settings(const map_plugin_settings_t& pSettings); + + public: + CReportUploader(); + virtual ~CReportUploader(); + virtual const map_plugin_settings_t& GetSettings(); + virtual void SetSettings(const map_plugin_settings_t& pSettings); + + virtual std::string Report(const map_crash_data_t& pCrashData, + const map_plugin_settings_t& pSettings, + const char *pArgs); +}; + +#endif diff --git a/src/plugins/SOSreport.conf b/src/plugins/SOSreport.conf new file mode 100644 index 00000000..3201c6da --- /dev/null +++ b/src/plugins/SOSreport.conf @@ -0,0 +1 @@ +Enabled = yes diff --git a/src/plugins/SOSreport.cpp b/src/plugins/SOSreport.cpp new file mode 100644 index 00000000..36a768fd --- /dev/null +++ b/src/plugins/SOSreport.cpp @@ -0,0 +1,169 @@ +/* + SOSreport.cpp + + 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 "abrt_exception.h" +#include "SOSreport.h" +#include "abrt_exception.h" +#include "comm_layer_inner.h" + +using namespace std; + +static char *ParseFilename(char *p) +{ + /* + the sosreport's filename is embedded in sosreport's output. + It appears on the line after the string in 'sosreport_filename_marker', + it has leading spaces, and a trailing newline. This function trims + any leading and trailing whitespace from the filename. + */ + static const char sosreport_filename_marker[] = + "Your sosreport has been generated and saved in:"; + + p = strstr(p, sosreport_filename_marker); + if (!p) + return p; + p = skip_whitespace(p + sizeof(sosreport_filename_marker)-1); + char *end = strchrnul(p, '\n'); + while (end > p && isspace(*end)) + *end-- = '\0'; + return p[0] == '/' ? p : NULL; +} + +void CActionSOSreport::Run(const char *pActionDir, const char *pArgs, int force) +{ + if (!force) + { + struct dump_dir *dd = dd_opendir(pActionDir, /*flags:*/ 0); + if (!dd) + return; + + bool bt_exists = dd_exist(dd, "sosreport.tar.bz2") || dd_exist(dd, "sosreport.tar.xz"); + if (bt_exists) + { + VERB3 log("%s already exists, not regenerating", "sosreport.tar.bz2"); + return; + } + dd_close(dd); + } + + static const char command_default[] = + "cd -- '%s' || exit 1;" + "nice sosreport --tmp-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 2>&1;" + "rm sosreport*.md5 2>/dev/null;" + "mv sosreport*.tar.bz2 sosreport.tar.bz2 2>/dev/null;" + "mv sosreport*.tar.xz sosreport.tar.xz 2>/dev/null;" + ; + static const char command_prefix[] = + "cd -- '%s' || exit 1;" + "nice sosreport --tmp-dir . --batch %s 2>&1;" + "rm sosreport*.md5 2>/dev/null;" + "mv sosreport*.tar.bz2 sosreport.tar.bz2 2>/dev/null;" + "mv sosreport*.tar.xz sosreport.tar.xz 2>/dev/null;" + ; + string command; + + vector_string_t args; + parse_args(pArgs, args, '"'); + + if (args.size() == 0 || args[0] == "") + { + command = ssprintf(command_default, pActionDir); + } + else + { + command = ssprintf(command_prefix, pActionDir, args[0].c_str()); + } + + update_client(_("Running sosreport: %s"), command.c_str()); + string output = command; + output += '\n'; + char *command_out = run_in_shell_and_save_output(/*flags:*/ 0, command.c_str(), /*dir:*/ NULL, /*size_p:*/ NULL); + output += command_out; + update_client(_("Finished running sosreport")); + VERB3 log("sosreport output:'%s'", output.c_str()); + +// Not needed: now we use "sosreport --tmp-dir DUMPDIR" +#if 0 + // Parse: + // "Your sosreport has been generated and saved in: + // /tmp/sosreport-XXXX.tar.bz2" + // Note: ParseFilename modifies its parameter and returns pointer + // which points somewhere inside it. + char *sosreport_filename = xstrdup(ParseFilename(command_out)); + free(command_out); + if (!sosreport_filename) + { + throw CABRTException(EXCEP_PLUGIN, "Can't find filename in sosreport output"); + } + + char *sosreport_dd_filename = concat_path_file(pActionDir, "sosreport.tar"); + char *ext = strrchr(sosreport_filename, '.'); + if (ext && strcmp(ext, ".tar") != 0) + { + // Assuming it's .bz2, .gz or some such + sosreport_dd_filename = append_to_malloced_string(sosreport_dd_filename, ext); + } + + CDebugDump dd; + if (!dd.Open(pActionDir)) + { + VERB1 log(_("Unable to open debug dump '%s'"), pDebugDumpDir); + free(sosreport_filename); + free(sosreport_dd_filename); + return; + } + //Not useful: dd.SaveText("sosreportoutput", output); + off_t sz = copy_file(sosreport_filename, sosreport_dd_filename, 0644); + + // don't want to leave sosreport-XXXX.tar.bz2 in /tmp + unlink(sosreport_filename); + // sosreport-XXXX.tar.bz2.md5 too + sosreport_filename = append_to_malloced_string(sosreport_filename, ".md5"); + unlink(sosreport_filename); + + dd.Close(); + if (sz < 0) + { + CABRTException e(EXCEP_PLUGIN, + "Can't copy '%s' to '%s'", + sosreport_filename, sosreport_dd_filename + ); + free(sosreport_filename); + free(sosreport_dd_filename); + throw e; + } + free(sosreport_filename); + free(sosreport_dd_filename); +#endif +} + +PLUGIN_INFO(ACTION, + CActionSOSreport, + "SOSreport", + "0.0.2", + _("Runs sosreport, saves the output"), + "gavin@redhat.com", + "https://fedorahosted.org/abrt/wiki", + ""); diff --git a/src/plugins/SOSreport.h b/src/plugins/SOSreport.h new file mode 100644 index 00000000..4b32940f --- /dev/null +++ b/src/plugins/SOSreport.h @@ -0,0 +1,31 @@ +/* + SOSreport.h - Attach an sosreport to a crash dump + + 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 SOSREPORT_H_ +#define SOSREPORT_H_ + +#include "action.h" + +class CActionSOSreport : public CAction +{ + public: + virtual void Run(const char *pActionDir, const char *pArgs, int force); +}; + +#endif diff --git a/src/plugins/SQLite3.conf b/src/plugins/SQLite3.conf new file mode 100644 index 00000000..a7617a90 --- /dev/null +++ b/src/plugins/SQLite3.conf @@ -0,0 +1,4 @@ +# Configuration file for database plugin SQLite3 + +# DB path +DBPath = /var/spool/abrt/abrt-db diff --git a/src/plugins/SQLite3.cpp b/src/plugins/SQLite3.cpp new file mode 100644 index 00000000..2ed3f9fd --- /dev/null +++ b/src/plugins/SQLite3.cpp @@ -0,0 +1,742 @@ +/* + SQLite3.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 <sqlite3.h> +#include "abrtlib.h" +#include "SQLite3.h" + +using namespace std; + +#define ABRT_TABLE_VERSION 4 +#define ABRT_TABLE_VERSION_STR "4" +#define ABRT_TABLE "abrt_v"ABRT_TABLE_VERSION_STR +#define ABRT_REPRESULT_TABLE "abrt_v"ABRT_TABLE_VERSION_STR"_reportresult" +#define SQLITE3_MASTER_TABLE "sqlite_master" + +#define COL_UID "UID" +#define COL_UUID "UUID" +#define COL_INFORMALL "InformAll" +#define COL_DEBUG_DUMP_PATH "DebugDumpPath" +#define COL_COUNT "Count" +#define COL_REPORTED "Reported" +#define COL_TIME "Time" +#define COL_MESSAGE "Message" + +#define COL_REPORTER "Reporter" + +#define NUM_COL 8 + +/* Is this string safe wrt SQL injection? + * PHP's mysql_real_escape_string() treats \, ', ", \x00, \n, \r, and \x1a as special. + * We are a bit more paranoid and disallow any control chars. + */ +static bool is_string_safe(const char *str) +{ +// Apparently SQLite allows unescaped newlines. More surprisingly, +// it does not unescape escaped ones - I see lines ending with \ when I do it. +// I wonder whether this is a bug in SQLite, and whether using unescaped +// newlines is a danger with other SQL servers. +// For now, I disabled newline escaping, and also allowed double quote. + const char *p = str; + while (*p) + { + unsigned char c = *p; +// if (c == '\\' && p[1] != '\0') +// { +// p += 2; +// continue; +// } + if ((c < ' ' && c != '\n') + || strchr("\\\'", c) //was: "\\\"\'" + ) { + error_msg("Probable SQL injection: '%s'", str); + return false; + } + p++; + } + return true; +} + +#ifdef UNUSED_FOR_NOW +/* Escape \n */ +static string sql_escape(const char *str) +{ + const char *s = str; + unsigned len = 0; + do + { + if (*s == '\n') + len++; + len++; + } while (*s++); + + char buf[len]; + s = str; + char *d = buf; + do + { + if (*s == '\n') + *d++ = '\\'; + *d++ = *s; + } while (*s++); + + return buf; +} +#endif + +/* Note: + * expects "SELECT * FROM ...", not "SELECT <only some fields> FROM ..." + */ +static GList *vget_table(sqlite3 *db, const char *fmt, va_list p) +{ + char *sql = xvasprintf(fmt, p); + + char **table; + int ncol, nrow; + char *err = NULL; + int ret = sqlite3_get_table(db, sql, &table, &nrow, &ncol, &err); + if (ret != SQLITE_OK) + { + error_msg("Error in SQL:'%s' error: %s", sql, err); + free(sql); + sqlite3_free(err); + return (GList*)ERR_PTR; + } + VERB2 log("%s: %d rows returned by SQL:%s", __func__, nrow, sql); + free(sql); + + if (nrow > 0 && ncol < NUM_COL) + error_msg_and_die("Unexpected number of columns: %d", ncol); + + GList *rows = NULL; + int ii; + for (ii = 0; ii < nrow; ii++) + { + int jj; + struct db_row *row = (struct db_row*)xzalloc(sizeof(struct db_row)); + for (jj = 0; jj < ncol; jj++) + { + char *val = table[jj + (ncol*ii) + ncol]; + switch (jj) + { + case 0: row->db_uuid = xstrdup(val); break; + case 1: row->db_uid = xstrdup(val); break; + case 2: row->db_inform_all = xstrdup(val); break; + case 3: row->db_dump_dir = xstrdup(val); break; + case 4: row->db_count = xstrdup(val); break; + case 5: row->db_reported = xstrdup(val); break; + case 6: row->db_time = xstrdup(val); break; + case 7: row->db_message = xstrdup(val); break; + } + } + + VERB3 log("%s: row->db_uuid = '%s'", __func__, row->db_uuid); + VERB3 log("%s: row->db_uid = '%s'", __func__, row->db_uid); + VERB3 log("%s: row->db_inform_all = '%s'", __func__, row->db_inform_all); + VERB3 log("%s: row->db_dump_dir = '%s'", __func__, row->db_dump_dir); + VERB3 log("%s: row->db_count = '%s'", __func__, row->db_count); + VERB3 log("%s: row->db_reported = '%s'", __func__, row->db_reported); + VERB3 log("%s: row->db_time = '%s'", __func__, row->db_time); + VERB3 log("%s: row->db_message = '%s'", __func__, row->db_message); + rows = g_list_append(rows, row); + + } + sqlite3_free_table(table); + + return rows; +} + +static GList *get_table_or_die(sqlite3 *db, const char *fmt, ...) +{ + va_list p; + va_start(p, fmt); + GList *table = vget_table(db, fmt, p); + va_end(p); + + if (table == (GList*)ERR_PTR) + xfunc_die(); + + return table; +} + +static int vexecute_sql(sqlite3 *db, const char *fmt, va_list p) +{ + char *sql = xvasprintf(fmt, p); + + char *err = NULL; + int ret = sqlite3_exec(db, sql, /*callback:*/ NULL, /*callback param:*/ NULL, &err); + if (ret != SQLITE_OK) + { + error_msg("Error in SQL:'%s' error: %s", sql, err); + free(sql); + sqlite3_free(err); + return -1; + } + int affected = sqlite3_changes(db); + VERB2 log("%d rows affected by SQL:%s", affected, sql); + free(sql); + + return affected; +} + +static int execute_sql_or_die(sqlite3 *db, const char *fmt, ...) +{ + va_list p; + va_start(p, fmt); + int ret = vexecute_sql(db, fmt, p); + va_end(p); + + if (ret < 0) + xfunc_die(); + + return ret; +} + +static bool exists_uuid_uid(sqlite3 *db, const char *UUID, const char *UID) +{ + GList *table = get_table_or_die(db, "SELECT * FROM "ABRT_TABLE + " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';", + UUID, UID + ); + + if (!table) + return false; + + db_list_free(table); + + return true; +} + +static void update_from_old_ver(sqlite3 *db, int old_version) +{ + static const char *const update_sql_commands[] = { + // v0 -> v1 + NULL, + // v1 -> v2 + "BEGIN TRANSACTION;" + "CREATE TABLE abrt_v2 (" + COL_UUID" VARCHAR NOT NULL," + COL_UID" VARCHAR NOT NULL," + COL_DEBUG_DUMP_PATH" VARCHAR NOT NULL," + COL_COUNT" INT NOT NULL DEFAULT 1," + COL_REPORTED" INT NOT NULL DEFAULT 0," + COL_TIME" VARCHAR NOT NULL DEFAULT 0," + COL_MESSAGE" VARCHAR NOT NULL DEFAULT ''," + "PRIMARY KEY ("COL_UUID","COL_UID"));" + "INSERT INTO abrt_v2 " + "SELECT "COL_UUID"," + COL_UID"," + COL_DEBUG_DUMP_PATH"," + COL_COUNT"," + COL_REPORTED"," + COL_TIME"," + COL_MESSAGE + " FROM abrt;" + "DROP TABLE abrt;" + "COMMIT;", + // v2 -> v3 + "BEGIN TRANSACTION;" + "CREATE TABLE abrt_v3 (" + COL_UUID" VARCHAR NOT NULL," + COL_UID" VARCHAR NOT NULL," + COL_DEBUG_DUMP_PATH" VARCHAR NOT NULL," + COL_COUNT" INT NOT NULL DEFAULT 1," + COL_REPORTED" INT NOT NULL DEFAULT 0," + COL_TIME" VARCHAR NOT NULL DEFAULT 0," + COL_MESSAGE" VARCHAR NOT NULL DEFAULT ''," + "PRIMARY KEY ("COL_UUID","COL_UID"));" + "INSERT INTO abrt_v3 " + "SELECT "COL_UUID"," + COL_UID"," + COL_DEBUG_DUMP_PATH"," + COL_COUNT"," + COL_REPORTED"," + COL_TIME"," + COL_MESSAGE + " FROM abrt_v2;" + "DROP TABLE abrt_v2;" + "CREATE TABLE abrt_v3_reportresult (" + COL_UUID" VARCHAR NOT NULL," + COL_UID" VARCHAR NOT NULL," + COL_REPORTER" VARCHAR NOT NULL," + COL_MESSAGE" VARCHAR NOT NULL DEFAULT ''," + "PRIMARY KEY ("COL_UUID","COL_UID","COL_REPORTER"));" + "COMMIT;", + // v3-> v4 + "BEGIN TRANSACTION;" + "CREATE TABLE abrt_v4(" + COL_UUID" VARCHAR NOT NULL," + COL_UID" VARCHAR NOT NULL," + COL_INFORMALL" INT NOT NULL DEFAULT 0," + COL_DEBUG_DUMP_PATH" VARCHAR NOT NULL," + COL_COUNT" INT NOT NULL DEFAULT 1," + COL_REPORTED" INT NOT NULL DEFAULT 0," + COL_TIME" VARCHAR NOT NULL DEFAULT 0," + COL_MESSAGE" VARCHAR NOT NULL DEFAULT ''," + "PRIMARY KEY ("COL_UUID","COL_UID"));" + "INSERT INTO abrt_v4 " + "SELECT "COL_UUID"," + COL_UID"," + "0," /* COL_INFORMALL */ + COL_DEBUG_DUMP_PATH"," + COL_COUNT"," + COL_REPORTED"," + COL_TIME"," + COL_MESSAGE + " FROM abrt_v3;" + "DROP TABLE abrt_v3;" + "UPDATE abrt_v4" + " SET "COL_UID"='0', "COL_INFORMALL"=1" + " WHERE "COL_UID"='-1';" + "CREATE TABLE abrt_v4_reportresult (" + COL_UUID" VARCHAR NOT NULL," + COL_UID" VARCHAR NOT NULL," + COL_REPORTER" VARCHAR NOT NULL," + COL_MESSAGE" VARCHAR NOT NULL DEFAULT ''," + "PRIMARY KEY ("COL_UUID","COL_UID","COL_REPORTER"));" + "INSERT INTO abrt_v4_reportresult " + "SELECT * FROM abrt_v3_reportresult;" + "DROP TABLE abrt_v3_reportresult;" + "COMMIT;", + }; + + while (old_version < ABRT_TABLE_VERSION) + { + execute_sql_or_die(db, update_sql_commands[old_version]); + old_version++; + } +} + +static bool check_table(sqlite3 *db) +{ + const char *command = "SELECT NAME FROM "SQLITE3_MASTER_TABLE" " + "WHERE TYPE='table' AND NAME like 'abrt_v%';"; + char **table; + int ncol, nrow; + char *err; + int ret = sqlite3_get_table(db, command, &table, &nrow, &ncol, &err); + if (ret != SQLITE_OK) + { + /* Should never happen */ + error_msg_and_die("SQLite3 database is corrupted"); + } + if (!nrow) + { + sqlite3_free_table(table); + return false; + } + + // table format: + // table[0]:"NAME" // table[1]:"SQL" <== field names from SELECT + // table[2]:"abrt_vNN" // table[3]:"sql" + char *tableName = table[0 + ncol]; + char *underscore = strchr(tableName, '_'); + if (underscore) + { + // It can be "abrt_vNN_something", thus using atoi(), not xatoi() + int tableVersion = atoi(underscore + 2); + sqlite3_free_table(table); + if (tableVersion < ABRT_TABLE_VERSION) + { + update_from_old_ver(db, tableVersion); + } + return true; + } + sqlite3_free_table(table); + update_from_old_ver(db, 1); + return true; +} + + +CSQLite3::CSQLite3() : + m_sDBPath(LOCALSTATEDIR "/spool/abrt/abrt-db"), + m_pDB(NULL) +{} + +CSQLite3::~CSQLite3() +{ + /* Paranoia. In C++, destructor will abort() if it was called while unwinding + * the stack and it throws an exception. + */ + try + { + DisConnect(); + m_sDBPath.clear(); + } + catch (...) + { + error_msg_and_die("Internal error"); + } +} + +void CSQLite3::DisConnect() +{ + if (m_pDB) + { + sqlite3_close(m_pDB); + m_pDB = NULL; + } +} + +void CSQLite3::Connect() +{ + int ret = sqlite3_open_v2(m_sDBPath.c_str(), + &m_pDB, + SQLITE_OPEN_READWRITE, + NULL + ); + + if (ret != SQLITE_OK) + { + if (ret != SQLITE_CANTOPEN) + { + error_msg_and_die("Can't open database '%s': %s", m_sDBPath.c_str(), sqlite3_errmsg(m_pDB)); + } + + ret = sqlite3_open_v2(m_sDBPath.c_str(), + &m_pDB, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + NULL + ); + if (ret != SQLITE_OK) + { + error_msg_and_die("Can't create database '%s': %s", m_sDBPath.c_str(), sqlite3_errmsg(m_pDB)); + } + } + + if (!check_table(m_pDB)) + { + execute_sql_or_die(m_pDB, + "CREATE TABLE "ABRT_TABLE" (" + COL_UUID" VARCHAR NOT NULL," + COL_UID" VARCHAR NOT NULL," + COL_INFORMALL" INT NOT NULL DEFAULT 0," + COL_DEBUG_DUMP_PATH" VARCHAR NOT NULL," + COL_COUNT" INT NOT NULL DEFAULT 1," + COL_REPORTED" INT NOT NULL DEFAULT 0," + COL_TIME" VARCHAR NOT NULL DEFAULT 0," + COL_MESSAGE" VARCHAR NOT NULL DEFAULT ''," + "PRIMARY KEY ("COL_UUID","COL_UID"));" + ); + execute_sql_or_die(m_pDB, + "CREATE TABLE "ABRT_REPRESULT_TABLE" (" + COL_UUID" VARCHAR NOT NULL," + COL_UID" VARCHAR NOT NULL," + COL_REPORTER" VARCHAR NOT NULL," + COL_MESSAGE" VARCHAR NOT NULL DEFAULT ''," + "PRIMARY KEY ("COL_UUID","COL_UID","COL_REPORTER"));" + ); + } +} + +void CSQLite3::Insert_or_Update(const char *crash_id, + bool inform_all_users, + const char *pDebugDumpPath, + const char *pTime) +{ + const char *UUID = strchr(crash_id, ':'); + if (!UUID + || !is_string_safe(crash_id) + || !is_string_safe(pDebugDumpPath) + || !is_string_safe(pTime) + ) { + return; + } + + /* Split crash_id into UID:UUID */ + unsigned uid_len = UUID - crash_id; + UUID++; + char UID[uid_len + 1]; + strncpy(UID, crash_id, uid_len); + UID[uid_len] = '\0'; + + if (!exists_uuid_uid(m_pDB, UUID, UID)) + { + execute_sql_or_die(m_pDB, + "INSERT INTO "ABRT_TABLE" (" + COL_UUID"," + COL_UID"," + COL_INFORMALL"," + COL_DEBUG_DUMP_PATH"," + COL_TIME + ")" + " VALUES ('%s','%s',%u,'%s','%s');", + UUID, UID, (unsigned)inform_all_users, pDebugDumpPath, pTime + ); + } + else + { + execute_sql_or_die(m_pDB, + "UPDATE "ABRT_TABLE + " SET "COL_COUNT"="COL_COUNT"+1,"COL_TIME"='%s'" + " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';", + pTime, + UUID, UID + ); + } +} + +void CSQLite3::DeleteRow(const char *crash_id) +{ + const char *UUID = strchr(crash_id, ':'); + if (!UUID + || !is_string_safe(crash_id) + ) { + return; + } + + /* Split crash_id into UID:UUID */ + unsigned uid_len = UUID - crash_id; + UUID++; + char UID[uid_len + 1]; + strncpy(UID, crash_id, uid_len); + UID[uid_len] = '\0'; + + if (exists_uuid_uid(m_pDB, UUID, UID)) + { + execute_sql_or_die(m_pDB, "DELETE FROM "ABRT_TABLE + " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';", + UUID, UID + ); + execute_sql_or_die(m_pDB, "DELETE FROM "ABRT_REPRESULT_TABLE + " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';", + UUID, UID + ); + } + else + { + error_msg("crash_id %s is not found in DB", crash_id); + } +} + +void CSQLite3::DeleteRows_by_dir(const char *dump_dir) +{ + if (!is_string_safe(dump_dir)) + { + return; + } + + /* Get UID:UUID pair(s) to delete */ + GList *table = get_table_or_die(m_pDB, "SELECT * FROM "ABRT_TABLE + " WHERE "COL_DEBUG_DUMP_PATH"='%s';", + dump_dir + ); + + if (!table) + { + return; + } + + struct db_row *row = NULL; + /* Delete from both tables */ + for (GList *li = table; li != NULL; li = g_list_next(li)) + { + row = (struct db_row*)li->data; + execute_sql_or_die(m_pDB, + "DELETE FROM "ABRT_REPRESULT_TABLE + " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';", + row->db_uuid, row->db_uid + ); + } + execute_sql_or_die(m_pDB, + "DELETE FROM "ABRT_TABLE + " WHERE "COL_DEBUG_DUMP_PATH"='%s'", + dump_dir + ); +} + +void CSQLite3::SetReported(const char *crash_id, const char *pMessage) +{ + const char *UUID = strchr(crash_id, ':'); + if (!UUID + || !is_string_safe(crash_id) + || !is_string_safe(pMessage) + ) { + return; + } + + /* Split crash_id into UID:UUID */ + unsigned uid_len = UUID - crash_id; + UUID++; + char UID[uid_len + 1]; + strncpy(UID, crash_id, uid_len); + UID[uid_len] = '\0'; + + if (exists_uuid_uid(m_pDB, UUID, UID)) + { + execute_sql_or_die(m_pDB, + "UPDATE "ABRT_TABLE + " SET "COL_REPORTED"=1" + " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';", + UUID, UID + ); + execute_sql_or_die(m_pDB, + "UPDATE "ABRT_TABLE + " SET "COL_MESSAGE"='%s'" + " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';", + pMessage, UUID, UID + ); + } + else + { + error_msg("crash_id %s is not found in DB", crash_id); + } +} + +void CSQLite3::SetReportedPerReporter(const char *crash_id, + const char *reporter, + const char *pMessage) +{ + const char *UUID = strchr(crash_id, ':'); + if (!UUID + || !is_string_safe(crash_id) + || !is_string_safe(reporter) + || !is_string_safe(pMessage) + ) { + return; + } + + /* Split crash_id into UID:UUID */ + unsigned uid_len = UUID - crash_id; + UUID++; + char UID[uid_len + 1]; + strncpy(UID, crash_id, uid_len); + UID[uid_len] = '\0'; + + int affected_rows = execute_sql_or_die(m_pDB, + "UPDATE "ABRT_REPRESULT_TABLE + " SET "COL_MESSAGE"='%s'" + " WHERE "COL_UUID"='%s' AND "COL_UID"='%s' AND "COL_REPORTER"='%s'", + pMessage, + UUID, UID, reporter + ); + if (!affected_rows) + { + execute_sql_or_die(m_pDB, + "INSERT INTO "ABRT_REPRESULT_TABLE + " ("COL_UUID","COL_UID","COL_REPORTER","COL_MESSAGE")" + " VALUES ('%s','%s','%s','%s');", + UUID, UID, reporter, pMessage + ); + } +} + +GList *CSQLite3::GetUIDData(long caller_uid) +{ + GList *table = NULL; + + if (caller_uid == 0) + { + table = get_table_or_die(m_pDB, "SELECT * FROM "ABRT_TABLE";"); + } + else + { + table = get_table_or_die(m_pDB, "SELECT * FROM "ABRT_TABLE + " WHERE "COL_UID"='%ld' OR "COL_INFORMALL"=1;", + caller_uid + ); + } + return table; +} + +struct db_row *CSQLite3::GetRow(const char *crash_id) +{ + const char *UUID = strchr(crash_id, ':'); + if (!UUID + || !is_string_safe(crash_id) + ) { + return NULL; + } + + /* Split crash_id into UID:UUID */ + unsigned uid_len = UUID - crash_id; + UUID++; + char UID[uid_len + 1]; + strncpy(UID, crash_id, uid_len); + UID[uid_len] = '\0'; + + GList *table = get_table_or_die(m_pDB, "SELECT * FROM "ABRT_TABLE + " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';", + UUID, UID + ); + + if (!table) + { + return NULL; + } + + GList *first = g_list_first(table); + struct db_row *row = db_rowcpy_from_list(first); + + db_list_free(table); + + return row; +} + +struct db_row *CSQLite3::GetRow_by_dir(const char *dir) +{ + if (!is_string_safe(dir)) + return NULL; + + GList *table = get_table_or_die(m_pDB, "SELECT * FROM "ABRT_TABLE + " WHERE "COL_DEBUG_DUMP_PATH"='%s';", + dir + ); + + if (!table) + return NULL; + + GList *first = g_list_first(table); + struct db_row *row = db_rowcpy_from_list(first); + + db_list_free(table); + + return row; +} + +void CSQLite3::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("DBPath"); + if (it != end) + { + m_sDBPath = it->second; + } +} + +//ok to delete? +//const map_plugin_settings_t& CSQLite3::GetSettings() +//{ +// m_pSettings["DBPath"] = m_sDBPath; +// +// return m_pSettings; +//} + +PLUGIN_INFO(DATABASE, + CSQLite3, + "SQLite3", + "0.0.2", + _("Keeps SQLite3 database about all crashes"), + "zprikryl@redhat.com,jmoskovc@redhat.com", + "https://fedorahosted.org/abrt/wiki", + ""); diff --git a/src/plugins/SQLite3.h b/src/plugins/SQLite3.h new file mode 100644 index 00000000..5750d92e --- /dev/null +++ b/src/plugins/SQLite3.h @@ -0,0 +1,58 @@ +/* + SQLite3.h + + 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 SQLITE3_H_ +#define SQLITE3_H_ + +#include "plugin.h" +#include "database.h" + +class CSQLite3 : public CDatabase +{ + private: + std::string m_sDBPath; + sqlite3* m_pDB; + + public: + CSQLite3(); + ~CSQLite3(); + + virtual void Connect(); + virtual void DisConnect(); + + virtual void Insert_or_Update(const char *crash_id, + bool inform_all_users, + const char *pDebugDumpPath, + const char *pTime); + virtual void DeleteRow(const char *crash_id); + virtual void DeleteRows_by_dir(const char *dump_dir); + virtual void SetReported(const char *crash_id, const char *pMessage); + virtual void SetReportedPerReporter(const char *crash_id, + const char *reporter, + const char *pMessage); + virtual GList *GetUIDData(long caller_uid); + virtual struct db_row *GetRow(const char *crash_id); + virtual struct db_row *GetRow_by_dir(const char *dir); + + virtual void SetSettings(const map_plugin_settings_t& pSettings); +}; + + +#endif diff --git a/src/plugins/abrt-Bugzilla.7 b/src/plugins/abrt-Bugzilla.7 new file mode 100644 index 00000000..99bb60d1 --- /dev/null +++ b/src/plugins/abrt-Bugzilla.7 @@ -0,0 +1,43 @@ +.TH abrt "7" "1 Jun 2009" "" +.SH NAME +Bugzilla 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 \fIBugzilla\fP plugin +for \fIabrt\fP. +.P +This plugin is used to report the crash to a Bugzilla instance. The +plugin will determine the package name and distribution version. The +crash data is attached to the bug report. +.SH INVOCATION +The plugin is invoked in the \fIabrt.conf\fP configuration file. +No parameters are necessary. +.SH CONFIGURATION +The \fIBugzilla.conf\fP configuration file contains several +entries in the format "Option = Value". The options are: +.SS BugzillaURL +The URL of the Bugzilla instance that you want to use, including the +path to the xmlrpc. The default is https://bugzilla.redhat.com/xmlrpc.cgi +.SS Login +Your Bugzilla login. If you have no Bugzilla account, you cannot +use the plugin. +.SS Password +Your Bugzilla password. +.SH EXAMPLES +.P +This is a snippet from the \fIabrt.conf\fP configuration file. +When something crashes, use the Bugzilla plugin: +.P +[common] +.br +ActionsAndReporters = Bugzilla +.SH "SEE ALSO" +.IR abrt (8), +.IR abrt.conf (5), +.IR abrt-plugins (7) +.SH AUTHOR +Written by Zdenek Prikryl <zprikryl@redhat.com>. +Manual page written by Daniel Novotny <dnovotny@redhat.com>. diff --git a/src/plugins/abrt-FileTransfer.7 b/src/plugins/abrt-FileTransfer.7 new file mode 100644 index 00000000..a721dd81 --- /dev/null +++ b/src/plugins/abrt-FileTransfer.7 @@ -0,0 +1,72 @@ +.TH abrt "7" "1 Jun 2009" "" +.SH NAME +FileTransfer 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 \fIFileTransfer\fP plugin +for \fIabrt\fP. +.P +This plugin is used to transfer the crash report to another +machine using a file transfer protocol. The protocols supported +are FTP, FTPS, HTTP, HTTPS, SCP, SFTP, and TFTP. +.SH INVOCATION +.P +The plugin is invoked in the \fIabrt.conf\fP file, usually in the +\fIActionsAndReporters\fP option and/or the \fI[cron]\fP section. +There are two modes of invocation: +.P +* Specify \fIFileTransfer(one)\fP in ActionsAndReporters directive. +Immediately after crash is detected, the plugin transfers crash data +to the server specified in the \fIFileTransfer.conf\fP configuration file. +.P +* Specify \fIFileTransfer(store)\fP in ActionsAndReporters directive +and add \fIHH:MM = FileTransfer\fP line in [cron] section. +At the time of the crash, +the plugin stores a record of it in its internal list. +When specified time is reached, the plugin iterates through +its internal list and sends every recorded crash to the specified URL. +After that, the internal list is cleared. +.SH CONFIGURATION +The \fIFileTransfer.conf\fP configuration file contains +several entries in the format "Option = Value". The options are: +.SS URL +The URL of the server, where the crash should +be transfered, specifying the protocol, the path, +the user name and the password, for example: +.br +URL = ftp://user:passwd@server.com/path +.SS ArchiveType +The type of the archive in which to pack the crash data. +Currently, \fI.tar\fP, \fI.tar.gz\fP, \fI.tar.bz2\fP and \fI.zip\fP +are supported. The default is \fI.tar.gz\fP +.SS RetryCount +This specifies how many times the plugin will try to resend +the file if the transfer was not succesful. The plugin +waits a while before it retries the transfer: see \fIRetryDelay\fP. +The default is 3 +.SS RetryDelay +If the transfer was not succesful, the plugin will +wait some time before sending the file again. This configuration +option specifies the time in seconds. The default is 20. +.SH EXAMPLES +.P +Typical configuration in \fIabrt.conf\fP. The crash is stored +each time it happens and at midnight, all the crash data +is transferred to a central server. +.P +[common] +.br +ActionsAndReporters = FileTransfer(store) +.br +[cron] +.br +00:00 = FileTransfer +.SH "SEE ALSO" +.IR abrt (8), +.IR abrt.conf (5), +.IR abrt-plugins (7) +.SH AUTHOR +Written by Daniel Novotny <dnovotny@redhat.com>. diff --git a/src/plugins/abrt-KerneloopsReporter.7 b/src/plugins/abrt-KerneloopsReporter.7 new file mode 100644 index 00000000..98bd3874 --- /dev/null +++ b/src/plugins/abrt-KerneloopsReporter.7 @@ -0,0 +1,40 @@ +.TH abrt "7" "1 Jun 2009" "" +.SH NAME +KerneloopsReporter 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 \fIKerneloopsReporter\fP +plugin for \fIabrt\fP. +.P +This plugin is used to report the crash to the Kerneloops tracker. +.SH INVOCATION +The plugin is invoked in the \fIabrt.conf\fP configuration file. +No parameters are necessary. +.SH CONFIGURATION +The \fIKerneloopsReporter.conf\fP configuration file contains one entry: +.SS SubmitURL +The URL of the kerneloops tracker, the default is +.br +SubmitURL = http://submit.kerneloops.org/submitoops.php +.SH EXAMPLES +.P +This is a snippet from the \fIabrt.conf\fP configuration file. +Each time a kernel oops is detected, run KerneloopsReporter: +.P +[common] +.br +ActionsAndReporters = Kerneloops, KerneloopsReporter +.br +[AnalyzerActionsAndReporters] +.br +Kerneloops = KerneloopsReporter +.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-KerneloopsScanner.7 b/src/plugins/abrt-KerneloopsScanner.7 new file mode 100644 index 00000000..ff094847 --- /dev/null +++ b/src/plugins/abrt-KerneloopsScanner.7 @@ -0,0 +1,46 @@ +.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-Logger.7 b/src/plugins/abrt-Logger.7 new file mode 100644 index 00000000..8ae679f8 --- /dev/null +++ b/src/plugins/abrt-Logger.7 @@ -0,0 +1,44 @@ +.TH abrt "7" "1 Jun 2009" "" +.SH NAME +Logger 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 \fILogger\fP plugin +for \fIabrt\fP. +.P +This plugin is used to log the crash to a file. +.P +The log will contain all the file names as well as their +content. It also contains "duplicity check": the ID +of the crash, which is used to tell whether the same +crash has happened previously. +.SH INVOCATION +The plugin is invoked in the \fIabrt.conf\fP configuration file. +No parameters are necessary. +.SH CONFIGURATION +The \fILogger.conf\fP configuration file contains +several entries in a format "Option = Value". The options are: +.SS LogPath +The path to the log file. +.SS AppendLogs +If set to "yes" (the default) \fILogger\fP will append +the report to the file, otherwise it will overwrite the file (so +only the last crash will be stored). +.SH EXAMPLES +.P +This is a snippet from the \fIabrt.conf\fP configuration file. +Log all the C/C++ application crashes: +.P +[AnalyzerActionsAndReporters] +.br +CCpp = Logger +.SH "SEE ALSO" +.IR abrt (8), +.IR abrt.conf (5), +.IR abrt-plugins (7) +.SH AUTHOR +Written by Zdenek Prikryl <zprikryl@redhat.com>. Manual +page by Daniel Novotny <dnovotny@redhat.com>. diff --git a/src/plugins/abrt-Mailx.7 b/src/plugins/abrt-Mailx.7 new file mode 100644 index 00000000..90a8bbce --- /dev/null +++ b/src/plugins/abrt-Mailx.7 @@ -0,0 +1,57 @@ +.TH abrt "7" "1 Jun 2009" "" +.SH NAME +Mailx 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 \fIMailx\fP plugin +for \fIabrt\fP. +.P +This plugin is used to mail the data about the crash +to a specified mail address. +.SH INVOCATION +The plugin is invoked in the \fIabrt.conf\fP configuration file. It can take +one parameter, a subject of the mail (if it differs from the +one specified in the \fIMailx.conf\fP configuration file). +.SH CONFIGURATION +The \fIMailx.conf\fP configuration file contains +several entries in a format "Option = Value". The options are: +.SS Subject +The subject of the mail. +.SS Parameters +The \fIMailx\fP plugin executes the external "mailx" command to +send the mail. This option defines some additional command line +parameters, which should be added to the program invocation, if any. +.SS EmailFrom +The address from which the email is sent. +.SS EmailTo +The address to which the email is sent. +.SS SendBinaryData +Can be "yes" or "no". If set to "yes", the email will also +contain the binary files associated with the crash. Warning: +this can cause the emails to be large! (several MB) +.SH EXAMPLES +.P +These are snippets from the \fIabrt.conf\fP configuration file. +.P +1) Each time a crash happens, a mail is sent +.PP +[common] +.br +ActionsAndReporters = Mailx("[abrt] a crash occurs") +.P +2) When a program in a specific package (in this case "httpd") crashes, +send a mail about it. +.PP +[AnalyzerActionsAndReporters] +.br +CCpp:httpd = Mailx("[abrt] Apache crash") +.SH "SEE ALSO" +.IR abrt (8), +.IR abrt.conf (5), +.IR abrt-plugins (7) +.SH AUTHOR +Written by Zdenek Prikryl <zprikryl@redhat.com>. Manual +page by Daniel Novotny <dnovotny@redhat.com>. diff --git a/src/plugins/abrt-ReportUploader.7 b/src/plugins/abrt-ReportUploader.7 new file mode 100644 index 00000000..bd91f266 --- /dev/null +++ b/src/plugins/abrt-ReportUploader.7 @@ -0,0 +1,55 @@ +.TH abrt "7" "9 July 2009" "" +.SH NAME +ReportUploader plugin for abrt(8) +.SH DESCRIPTION +.P +.I abrt +is a daemon which watches for application crashes. When a crash occurs, +it collects the crash data and performs some actions according to +the configuration. This manual page describes the \fIReportUploader\fP plugin +for \fIabrt\fP. +.P +This plugin will send a report to an anonymous FTP site. It's intended +for use in cases where a ticketing system is associated with the FTP site, +but the ticketing system has no way to automatically create new tickets, +or add to existing tickets. Customer Name is put in config file. +Ticket name (or number) is also put in config file. If no ticket +name is configured, assume ticketing system should create a new ticket. +This information is added to the report, the report is copied into a +compressed, optionally encrypted, tarball. Then the tarball is FTP'd +to the upload site. Then a status string is displayed to the user +showing the name of the file on the FTP site, it's MD5SUM, and +it's encryption key. This information can be pasted into a ticket +in the ticketing system. +.SH INVOCATION +The plugin is invoked in the \fIabrt.conf\fP configuration file. +No parameters are necessary. +.SH CONFIGURATION +The \fIReportUploader.conf\fP configuration file contains +entries in a format "Option = Value". The options are: +.SS Customer +This is the customer's name or other customer identifier. +.SS Ticket +This is the ticket name or number. +.SS Encrypt +"yes" for encrypt upload, anything else for not. +.SS Upload +"yes" for for upload to FTP site, anything else for copy to local /tmp. +.SS URL +URL of upload site (ie. ftp://support.com/upload). +.SH EXAMPLES +.P +This is a snippet from the \fIabrt.conf\fP configuration file. +Log all the C/C++ application crashes: +.P +[AnalyzerActionsAndReporters] +.br +CCpp = RHUpload +.SH "SEE ALSO" +.IR abrt (8), +.IR abrt.conf (5), +.IR abrt-plugins (7) +.SH AUTHOR +Plugin and man page by Gavin Romig-Koch <gavin@redhat.com>. + + diff --git a/src/plugins/abrt-SQLite3.7 b/src/plugins/abrt-SQLite3.7 new file mode 100644 index 00000000..c2b39d86 --- /dev/null +++ b/src/plugins/abrt-SQLite3.7 @@ -0,0 +1,36 @@ +.TH abrt "7" "1 Jun 2009" "" +.SH NAME +SQLite3 database 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 \fISQLite3\fP database plugin +for \fIabrt\fP. +.P +This is a database plugin: \fIabrt\fP needs a database in which to store +its metadata. You can choose one by specifying "Database" in +the \fIabrt.conf\fP configuration file. Currently SQLite3 is +the only choice supported. +.SH INVOCATION +The plugin is invoked in the \fIabrt.conf\fP configuration file, like +this: +.br +[common] +.br +Database = SQLite3 +.SH CONFIGURATION +The \fISQLite3.conf\fP configuration file contains one entry: +.SS DBPath +The path to the database. +.SH EXAMPLES +see \fBINVOCATION\fP +.SH "SEE ALSO" +.IR abrt (8), +.IR abrt.conf (5), +.IR abrt-plugins (7) +.SH AUTHOR +Written by Zdenek Prikryl <zprikryl@redhat.com> and Jiri +Moskovcak <jmoskovc@redhat.com>. Manual +page by Daniel Novotny <dnovotny@redhat.com>. diff --git a/src/daemon/abrt-action-analyze-c.c b/src/plugins/abrt-action-analyze-c.c index 60e08372..60e08372 100644 --- a/src/daemon/abrt-action-analyze-c.c +++ b/src/plugins/abrt-action-analyze-c.c diff --git a/src/daemon/abrt-action-analyze-oops.c b/src/plugins/abrt-action-analyze-oops.c index 354ec6fd..354ec6fd 100644 --- a/src/daemon/abrt-action-analyze-oops.c +++ b/src/plugins/abrt-action-analyze-oops.c diff --git a/src/daemon/abrt-action-analyze-python.c b/src/plugins/abrt-action-analyze-python.c index bb5722ec..bb5722ec 100644 --- a/src/daemon/abrt-action-analyze-python.c +++ b/src/plugins/abrt-action-analyze-python.c diff --git a/src/daemon/abrt-action-bugzilla.cpp b/src/plugins/abrt-action-bugzilla.cpp index a3c2f0b0..a3c2f0b0 100644 --- a/src/daemon/abrt-action-bugzilla.cpp +++ b/src/plugins/abrt-action-bugzilla.cpp diff --git a/src/daemon/abrt-action-generate-backtrace.c b/src/plugins/abrt-action-generate-backtrace.c index 8f1642d7..8f1642d7 100644 --- a/src/daemon/abrt-action-generate-backtrace.c +++ b/src/plugins/abrt-action-generate-backtrace.c diff --git a/src/daemon/abrt-action-install-debuginfo b/src/plugins/abrt-action-install-debuginfo index c1b8fdb9..c1b8fdb9 100755 --- a/src/daemon/abrt-action-install-debuginfo +++ b/src/plugins/abrt-action-install-debuginfo diff --git a/src/daemon/abrt-action-kerneloops.cpp b/src/plugins/abrt-action-kerneloops.cpp index 4c820081..4c820081 100644 --- a/src/daemon/abrt-action-kerneloops.cpp +++ b/src/plugins/abrt-action-kerneloops.cpp diff --git a/src/daemon/abrt-action-print.cpp b/src/plugins/abrt-action-print.cpp index a4db373a..a4db373a 100644 --- a/src/daemon/abrt-action-print.cpp +++ b/src/plugins/abrt-action-print.cpp diff --git a/src/daemon/abrt-action-rhtsupport.cpp b/src/plugins/abrt-action-rhtsupport.cpp index d1854541..d1854541 100644 --- a/src/daemon/abrt-action-rhtsupport.cpp +++ b/src/plugins/abrt-action-rhtsupport.cpp diff --git a/src/plugins/abrt-plugins.7 b/src/plugins/abrt-plugins.7 new file mode 100644 index 00000000..6c07e65a --- /dev/null +++ b/src/plugins/abrt-plugins.7 @@ -0,0 +1,43 @@ +.TH abrt "8" "28 May 2009" "" +.SH NAME +abrt-plugins \- plugins for the abrt crash reporter program +.SH DESCRIPTION +.P +.I abrt +is a daemon that watches for application crashes. When a crash occurs, +it collects the crash data (core file, application's command line etc.) +and takes action according to the type of application that +crashed and according to the configuration specified in the +.I abrt.conf +configuration file. +.P +Plugins allow abrt to perform various actions: for example, +to report the crash to Bugzilla, to mail the report, to transfer +the report via FTP or SCP, or to run a program that you specify. +.P +This manual page provides a list of all the manual pages for +these plugins. +.P +If you want to create your own plugin, refer to the PLUGINS-HOWTO +file in the documentation directory. +.SH INVOCATION +Each plugin is invoked in the \fIabrt.conf\fP configuration +file, in the section that is appropriate to what you +want abrt to do. +.SH CONFIGURATION +Almost every plugin has its configuration file, +stored in the \fI/etc/abrt/plugins\fP directory. +.SH "SEE ALSO" +.IR abrt (8), +.IR abrt.conf (5), +.IR abrt-Bugzilla (7), +.IR abrt-FileTransfer (7), +.IR abrt-KerneloopsReporter (7), +.IR abrt-KerneloopsScanner (7), +.IR abrt-Logger (7), +.IR abrt-Mailx (7), +.IR abrt-SQLite3 (7) +.SH AUTHOR +\fIabrt\fP written by Zdeněk Přikryl <zprikryl@redhat.com> and +Jiří Moskovčák <jmoskovc@redhat.com>. Manual page written by Daniel +Novotný <dnovotny@redhat.com>. diff --git a/src/daemon/abrt_rh_support.c b/src/plugins/abrt_rh_support.c index 04e2c8ef..04e2c8ef 100644 --- a/src/daemon/abrt_rh_support.c +++ b/src/plugins/abrt_rh_support.c diff --git a/src/daemon/abrt_rh_support.h b/src/plugins/abrt_rh_support.h index db6e9cd7..db6e9cd7 100644 --- a/src/daemon/abrt_rh_support.h +++ b/src/plugins/abrt_rh_support.h |