From c80bc9e37c4e461033d1457f953d09f7439fea39 Mon Sep 17 00:00:00 2001 From: Richard Marko Date: Tue, 18 Feb 2014 15:18:43 +0100 Subject: [PATCH 1/2] abrt-addon-python3 Related to rhbz#1047085 Signed-off-by: Richard Marko Conflicts: src/hooks/Makefile.am --- src/hooks/Makefile.am | 19 ++- src/hooks/abrt3.pth | 1 + src/hooks/abrt_exception_handler3.py.in | 226 ++++++++++++++++++++++++++++++++ src/hooks/python3.conf | 5 + 4 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 src/hooks/abrt3.pth create mode 100644 src/hooks/abrt_exception_handler3.py.in create mode 100644 src/hooks/python3.conf diff --git a/src/hooks/Makefile.am b/src/hooks/Makefile.am index 650a771..85502a0 100644 --- a/src/hooks/Makefile.am +++ b/src/hooks/Makefile.am @@ -1,13 +1,15 @@ confdir = $(CONF_DIR) pyhookdir = $(pyexecdir) +py3hookdir = $(py3execdir) pluginsconfdir = $(PLUGINS_CONF_DIR) dist_pluginsconf_DATA = \ CCpp.conf \ python.conf \ - vmcore.conf \ oops.conf + python3.conf \ + vmcore.conf defaultpluginsconfdir = $(DEFAULT_PLUGINS_CONF_DIR) dist_defaultpluginsconf_DATA = $(dist_pluginsconf_DATA) @@ -62,7 +64,12 @@ pyhook_PYTHON = \ abrt_exception_handler.py \ abrt.pth +py3hook_PYTHON = \ + abrt_exception_handler3.py \ + abrt3.pth + EXTRA_DIST = abrt_exception_handler.py.in \ + abrt_exception_handler3.py.in \ abrt-install-ccpp-hook.in \ abrt_harvest_vmcore.py.in \ abrt-harvest-pstoreoops.in @@ -70,7 +77,7 @@ EXTRA_DIST = abrt_exception_handler.py.in \ CLEANFILES := $(notdir $(wildcard *~)) $(notdir $(wildcard *\#)) $(notdir $(wildcard \.\#*)) $(notdir $(wildcard *.pyc)) $(man1_MANS) # Generate on build -all: abrt_exception_handler.py +all: abrt_exception_handler.py abrt_exception_handler3.py # Must be synchronized with another sed call below. abrt_exception_handler.py: abrt_exception_handler.py.in @@ -78,11 +85,19 @@ abrt_exception_handler.py: abrt_exception_handler.py.in -e s,\@CONF_DIR\@,\"$(CONF_DIR)\",g \ abrt_exception_handler.py.in >abrt_exception_handler.py +abrt_exception_handler3.py: abrt_exception_handler3.py.in + sed -e s,\@VAR_RUN\@,\"$(VAR_RUN)\",g \ + -e s,\@CONF_DIR\@,\"$(CONF_DIR)\",g \ + abrt_exception_handler3.py.in >abrt_exception_handler3.py + # RPM fix: we need to regenerate abrt_exception_handler.py, because it has the default ddir install-data-local: sed -e s,\@VAR_RUN\@,\"$(VAR_RUN)\",g \ -e s,\@CONF_DIR\@,\"$(CONF_DIR)\",g \ abrt_exception_handler.py.in >abrt_exception_handler.py + sed -e s,\@VAR_RUN\@,\"$(VAR_RUN)\",g \ + -e s,\@CONF_DIR\@,\"$(CONF_DIR)\",g \ + abrt_exception_handler3.py.in >abrt_exception_handler3.py abrt-install-ccpp-hook: abrt-install-ccpp-hook.in sed -e s,\@VAR_RUN\@,$(VAR_RUN),g \ diff --git a/src/hooks/abrt3.pth b/src/hooks/abrt3.pth new file mode 100644 index 0000000..7f75de0 --- /dev/null +++ b/src/hooks/abrt3.pth @@ -0,0 +1 @@ +import abrt_exception_handler3 diff --git a/src/hooks/abrt_exception_handler3.py.in b/src/hooks/abrt_exception_handler3.py.in new file mode 100644 index 0000000..b20e6f5 --- /dev/null +++ b/src/hooks/abrt_exception_handler3.py.in @@ -0,0 +1,226 @@ +#:mode=python: +# -*- coding: utf-8 -*- +## Copyright (C) 2014 Red Hat, 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, Suite 500, Boston, MA 02110-1335 USA + +""" +Module for the ABRT exception handling hook +""" + +import sys +import os + + +def syslog(msg): + """Log message to system logger (journal)""" + + from systemd import journal + + # required as a workaround for rhbz#1023041 + # where journal tries to log into non-existent log + # and fails (during %check in mock) + # + # try/except block should be removed when the bug is fixed + + try: + journal.send(msg) + except: + pass + + +def send(data): + """Send data to abrtd""" + + response = "" + + try: + import socket + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.settimeout(5) + s.connect(@VAR_RUN@ + "/abrt/abrt.socket") + pre = "POST / HTTP/1.1\r\n\r\ntype=Python\0" + s.sendall(pre.encode()) + s.sendall(data.encode()) + + s.sendall("\0".encode()) + s.shutdown(socket.SHUT_WR) + + while True: + buf = s.recv(256) + if not buf: + break + response += buf.decode() + + s.close() + + except socket.timeout as ex: + syslog("communication with ABRT daemon failed: {0}".format(ex)) + + except Exception as ex: + syslog("can't communicate with ABRT daemon, is it running? {0}" + .format(ex)) + + return response + + +def write_dump(tb_text, tb): + if sys.argv[0][0] == "/": + executable = os.path.abspath(sys.argv[0]) + else: + # We don't know the path. + # (BTW, we *can't* assume the script is in current directory.) + executable = sys.argv[0] + + data = "pid={0}\0".format(os.getpid()) + data += "executable={0}\0".format(executable) + data += "reason={0}\0".format(tb_text.splitlines()[0]) + data += "backtrace={0}\0".format(tb_text) + + data += "environ=" + for k, v in os.environ.items(): + data += "{0}={1}\n".format(k, v) + + response = send(data) + parts = response.split() + if (len(parts) < 2 + or (not parts[0].startswith("HTTP/")) + or (not parts[1].isdigit()) + or (int(parts[1]) >= 400)): + syslog("error sending data to ABRT daemon: {0}".format(response)) + + +def conf_enabled(var_name): + import problem + try: + conf = problem.load_plugin_conf_file("python3.conf") + except: + return -1 + else: + conf.get(var_name, -1) + + +def handle_exception(etype, value, tb): + """ + The exception handling function. + + progname - the name of the application + version - the version of the application + """ + + try: + # Restore original exception handler + sys.excepthook = sys.__excepthook__ # pylint: disable-msg=E1101 + + import errno + + # Ignore Ctrl-C + # SystemExit rhbz#636913 -> this exception is not an error + if etype in [KeyboardInterrupt, SystemExit]: + return sys.__excepthook__(etype, value, tb) + + # Ignore EPIPE: it happens all the time + # Testcase: script.py | true, where script.py is: + ## #!/usr/bin/python + ## import os + ## import time + ## time.sleep(1) + ## os.write(1, "Hello\n") # print "Hello" wouldn't be the same + # + if etype == IOError or etype == OSError: + if value.errno == errno.EPIPE: + return sys.__excepthook__(etype, value, tb) + + # Ignore interactive Python and similar + # Check for first "-" is meant to catch "-c" which appears in this case: + ## $ python -c 'import sys; print "argv0 is:%s" % sys.argv[0]' + ## argv0 is:-c + # Are there other cases when sys.argv[0][0] is "-"? + if not sys.argv[0] or sys.argv[0][0] == "-": + syslog("detected unhandled Python exception") + raise Exception + + # Ignore scripts with relative path unless "RequireAbsolutePath = no". + # (In this case we can't reliably determine package) + syslog("detected unhandled Python exception in '{0}'" + .format(sys.argv[0])) + + if sys.argv[0][0] != "/": + if conf_enabled("RequireAbsolutePath") != 0: + raise Exception + + import traceback + + elist = traceback.format_exception(etype, value, tb) + + if tb is not None and etype != IndentationError: + tblast = traceback.extract_tb(tb, limit=None) + if len(tblast): + tblast = tblast[len(tblast) - 1] + extxt = traceback.format_exception_only(etype, value) + if tblast and len(tblast) > 3: + ll = [] + ll.extend(tblast[:3]) + ll[0] = os.path.basename(tblast[0]) + tblast = ll + + text = "" + for t in tblast: + text += "{0}:".format(t) + + text += "{0}\n{1}".format(extxt[0], "".join(elist)) + + trace = tb + while trace.tb_next: + trace = trace.tb_next + frame = trace.tb_frame + text += ("\nLocal variables in innermost frame:\n") + try: + for (key, val) in frame.f_locals.items(): + text += "{0}: {1}\n".format(key, repr(val)) + except: + pass + else: + text = "{0}\n\n{1}".format(value, "".join(elist)) + + # Send data to the daemon + write_dump(text, tb) + + except: + # Silently ignore any error in this hook, + # to not interfere with other scripts + pass + + return sys.__excepthook__(etype, value, tb) + + +def install_handler(): + """ + Install the exception handling function. + """ + sys.excepthook = lambda etype, value, tb: \ + handle_exception(etype, value, tb) + +# install the exception handler when the abrt_exception_handler +# module is imported +try: + install_handler() +except Exception as e: + pass + +if __name__ == '__main__': + # test exception raised to show the effect + div0 = 1 / 0 # pylint: disable-msg=W0612 + sys.exit(0) diff --git a/src/hooks/python3.conf b/src/hooks/python3.conf new file mode 100644 index 0000000..ac739a6 --- /dev/null +++ b/src/hooks/python3.conf @@ -0,0 +1,5 @@ +# If set to 'no', unhandled python exceptions will be caught +# and saved even in scripts which are run without full path +# in sys.argv[0]. +# Default is 'yes': do not save them. +#RequireAbsolutePath = yes -- 1.8.3.1