#!/usr/bin/python3 -u # WARNING: python -u means unbuffered I/O. Without it the messages are # passed to the parent asynchronously which looks bad in clients. # # This script wraps reporter-ureport client and keeps number of sent # uReports to a server consistent with number of problem ocurrences. import sys import os import getopt import augeas from report import dd_opendir, DD_FAIL_QUIETLY_ENOENT, run_event_on_problem_dir from reportclient import set_verbosity, error_msg_and_die, error_msg, log1, log_warning GETTEXT_PROGNAME = "abrt" import locale import gettext _ = lambda x: gettext.gettext(x) def init_gettext(): try: locale.setlocale(locale.LC_ALL, "") except locale.Error: os.environ['LC_ALL'] = 'C' locale.setlocale(locale.LC_ALL, "") # Defeat "AttributeError: 'module' object has no attribute 'nl_langinfo'" try: gettext.bind_textdomain_codeset(GETTEXT_PROGNAME, locale.nl_langinfo(locale.CODESET)) except AttributeError: pass gettext.bindtextdomain(GETTEXT_PROGNAME, '/usr/share/locale') gettext.textdomain(GETTEXT_PROGNAME) def get_augeas(module, file_path): """ A function for efficient configuration of Augeas. Augeas modules are placed in /usr/share/augeas/lenses/dist """ aug_obj = augeas.Augeas(flags=augeas.Augeas.NO_MODL_AUTOLOAD) aug_obj.set("/augeas/load/{0}/lens".format(module), "{0}.lns".format(module)) aug_obj.set("/augeas/load/{0}/incl".format(module), file_path) aug_obj.load() return aug_obj def spawn_and_wait(prog, args=None): if args is None: args = [prog] else: args.insert(0, prog) try: return os.spawnvpe(os.P_WAIT, prog, args, os.environ) except OSError as err: error_msg(_("Unable to start '%s', error message was: '%s'"), " ".join(args), err) return -1 def try_parse_number(dd, filename): try: n = dd.load_text(filename, DD_FAIL_QUIETLY_ENOENT) if n == "": return 0 return int(n) except: error_msg(_("Not a number in file '%s'"), filename) return 0 def get_bugzilla_reports(reported_to): bugs = set() for line in reported_to.split("\n"): if line.startswith("Bugzilla:"): bugs.add(line) return bugs def run_event(event_name, dump_dir_name): state, ret = run_event_on_problem_dir(dump_dir_name, event_name) if ret == 0 and state.children_count == 0: log1("Didn't find definition of event '%s'", event_name) if __name__ == "__main__": # localization init_gettext() verbose = 0 ABRT_VERBOSE = os.getenv("ABRT_VERBOSE") if ABRT_VERBOSE: try: verbose = int(ABRT_VERBOSE) except: pass progname = os.path.basename(sys.argv[0]) help_text = _("Usage: %s [-v]") % progname try: opts, args = getopt.getopt(sys.argv[1:], "vh", ["help"]) except getopt.GetoptError as err: error_msg(err) # prints something like "option -a not recognized" error_msg_and_die(help_text) for opt, arg in opts: if opt in ("-h", "--help"): print(help_text) sys.exit(0) if opt == "-v": verbose += 1 set_verbosity(verbose) os.environ["ABRT_VERBOSE"] = str(verbose) # getcwd might fail if cwd was deleted try: dirname = os.getcwd() except OSError as err: error_msg_and_die(_("Unable to get current working directory as" " it was probably deleted")) dd = dd_opendir(dirname, 0) if not dd: sys.exit(1) report_type = dd.load_text("type", DD_FAIL_QUIETLY_ENOENT) # because of backward compatibility if not report_type: report_type = dd.load_text("analyzer", 0) core_backtrace_exists = dd.exist("core_backtrace") packaged = dd.exist("package") reported_to = dd.load_text("reported_to", DD_FAIL_QUIETLY_ENOENT) ureports_counter = try_parse_number(dd, "ureports_counter") count = try_parse_number(dd, "count") dd.close() # Send only if the problem is not yet reported # if the count file is corrupted or # if the number of ureports is lower then the number of occurrences if ureports_counter != 0 and count != 0 and ureports_counter >= count: log1("uReport has been already sent: '%s'", dirname) if reported_to and reported_to != "": bugs = get_bugzilla_reports(reported_to) if bugs: log_warning(_("A bug was already filed about this problem:")) bugs = sorted(bugs) for bug in bugs: print(bug) sys.exit(70) # EXIT_STOP_EVENT_RUN log1("Bug for '%s' not yet filed. Continuing.", dirname) sys.exit(0) else: log1("'%s/reported_to' doesn't exist", dirname) log_warning(_("uReport was already sent, not sending it again")) sys.exit(0) if report_type == "CCpp" and not core_backtrace_exists: exitcode = spawn_and_wait("abrt-action-generate-core-backtrace") if exitcode != 0: log1("uReport can't be sent without core_backtrace. Exiting.") sys.exit(1) if not packaged: log1("Problem comes from unpackaged executable.") process_unpackaged_str = os.getenv("uReport_ProcessUnpackaged") if not process_unpackaged_str: augeas_conf = get_augeas("libreport", "/etc/libreport/plugins/ureport.conf") process_unpackaged_str = augeas_conf.get("/files/etc/libreport/plugins/ureport.conf/ProcessUnpackaged") process_unpackaged = False if process_unpackaged_str: process_unpackaged = process_unpackaged_str.lower() in ['yes', '1', 'on'] if not process_unpackaged: log_warning(_("Problem comes from unpackaged executable. Unable to create uReport.")) sys.exit(1) exitcode = spawn_and_wait("reporter-ureport") if exitcode == 0 or exitcode == 70: dd = dd_opendir(dirname, 0) if not dd: sys.exit(1) dd.save_text("ureports_counter", str(ureports_counter + 1)) reported_to = dd.load_text("reported_to", DD_FAIL_QUIETLY_ENOENT) dd.close() watch = os.getenv("uReport_WatchReportedBugs") or "" if exitcode == 70 and watch.lower() in ["yes", "on", "1"]: if reported_to and reported_to != "" and get_bugzilla_reports(reported_to): log_warning(_("Adding you to CC List of the existing bugzilla bug")) run_event("watch_Bugzilla", dirname) email = os.getenv("uReport_ContactEmail") if not email: augeas_conf = get_augeas("libreport", "/etc/libreport/plugins/ureport.conf") email = augeas_conf.get("/files/etc/libreport/plugins/ureport.conf/ContactEmail") if email: log1("Attaching ContactEmail: " + email) spawn_and_wait("reporter-ureport", ["-A", "-E"]) sys.exit(exitcode) else: log1(_("reporter-ureport failed with exit code %d" % exitcode)) sys.exit(exitcode)