Blame src/plugins/abrt-action-notify

Packit 8ea169
#!/usr/bin/python3
Packit 8ea169
# This program is free software; you can redistribute it and/or modify
Packit 8ea169
# it under the terms of the GNU General Public License as published by
Packit 8ea169
# the Free Software Foundation; either version 2 of the License, or
Packit 8ea169
# (at your option) any later version.
Packit 8ea169
#
Packit 8ea169
# This program is distributed in the hope that it will be useful,
Packit 8ea169
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8ea169
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8ea169
# GNU General Public License for more details.
Packit 8ea169
#
Packit 8ea169
# You should have received a copy of the GNU General Public License
Packit 8ea169
# along with this program; if not, write to the Free Software
Packit 8ea169
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 8ea169
Packit 8ea169
import os
Packit 8ea169
import sys
Packit 8ea169
from argparse import ArgumentParser
Packit 8ea169
from subprocess import call
Packit 8ea169
Packit 8ea169
import dbus
Packit 8ea169
import dbus.lowlevel
Packit 8ea169
Packit 8ea169
import problem
Packit 8ea169
Packit 8ea169
import report
Packit 8ea169
from reportclient import (RETURN_OK,
Packit 8ea169
                          RETURN_FAILURE,
Packit 8ea169
                          RETURN_CANCEL_BY_USER,
Packit 8ea169
                          RETURN_STOP_EVENT_RUN,
Packit 8ea169
                          log1,
Packit 8ea169
                          set_verbosity)
Packit 8ea169
Packit 8ea169
CD_DUMPDIR = "Directory"
Packit 8ea169
FILENAME_PACKAGE = "package"
Packit 8ea169
FILENAME_UID = "uid"
Packit 8ea169
FILENAME_UUID = "uuid"
Packit 8ea169
FILENAME_DUPHASH = "duphash"
Packit 8ea169
Packit 8ea169
def run_event(event_name, dump_dir_name):
Packit 8ea169
    '''
Packit 8ea169
    Run event with `event_name` on problem directory `dump_dir_name`
Packit 8ea169
    '''
Packit 8ea169
Packit 8ea169
    state, ret = report.run_event_on_problem_dir(dump_dir_name, event_name)
Packit 8ea169
Packit 8ea169
    if ret == 0 and state.children_count == 0:
Packit 8ea169
        log1("Didn't find definition of event '%s'", event_name)
Packit 8ea169
        return False
Packit 8ea169
Packit 8ea169
    return True
Packit 8ea169
Packit 8ea169
def run_autoreport(problem_data, event_name):
Packit 8ea169
    """Runs autoreporting event
Packit 8ea169
Packit 8ea169
    Requires CD_DUMPDIR key in problem_data.
Packit 8ea169
Packit 8ea169
    Keyword arguments:
Packit 8ea169
    problem_data -- problem data of notified problems
Packit 8ea169
Packit 8ea169
    Returns None as it raises an exception on error
Packit 8ea169
Packit 8ea169
    Raises:
Packit 8ea169
    KeyError -- if any of required elements is missing
Packit 8ea169
    RuntimeError -- if event run fails
Packit 8ea169
    """
Packit 8ea169
Packit 8ea169
    dir_name = problem_data.get(CD_DUMPDIR)
Packit 8ea169
    if dir_name is None:
Packit 8ea169
        raise KeyError(CD_DUMPDIR)
Packit 8ea169
Packit 8ea169
    log1("Running autoreporting event: '{0}'".format(event_name))
Packit 8ea169
Packit 8ea169
    res, ret  = report.run_event_on_problem_dir(dir_name[0], event_name)
Packit 8ea169
Packit 8ea169
    if res.children_count == 0 and ret == 0:
Packit 8ea169
        raise RuntimeError("No processing is specified for event '{0}'"
Packit 8ea169
                .format(event_name))
Packit 8ea169
Packit 8ea169
    if not ret in [RETURN_OK, RETURN_CANCEL_BY_USER, RETURN_STOP_EVENT_RUN]:
Packit 8ea169
        raise RuntimeError("Event '{0}' exited with {1}"
Packit 8ea169
                .format(event_name, ret))
Packit 8ea169
Packit 8ea169
def emit_crash_dbus_signal(problem_data):
Packit 8ea169
    """Emits a Crash signal on D-Bus Problem bus
Packit 8ea169
Packit 8ea169
    Emits a signal with 5 members:
Packit 8ea169
        package -- value of 'package' element in problem_data
Packit 8ea169
        problem_id -- value of 'Directory' element in problem_data
Packit 8ea169
        uid -- empty string if 'uid' element is not present in problem_data
Packit 8ea169
        uuid -- empty string if 'uuid' element is not present in problem_data
Packit 8ea169
        duphash -- empty string if 'duphash' element is not present in problem_data
Packit 8ea169
Packit 8ea169
    Keyword arguments:
Packit 8ea169
    problem_data -- problem data of notified problems
Packit 8ea169
Packit 8ea169
    Returns None as it raises an exception on error
Packit 8ea169
Packit 8ea169
    Raises:
Packit 8ea169
    RuntimeError -- for all D-Bus related errors
Packit 8ea169
    KeyError -- if any of required elements is missing
Packit 8ea169
    """
Packit 8ea169
Packit 8ea169
    bus = None
Packit 8ea169
    try:
Packit 8ea169
        bus = dbus.SystemBus()
Packit 8ea169
        msg = dbus.lowlevel.SignalMessage("/org/freedesktop/problems",
Packit 8ea169
                "org.freedesktop.problems", "Crash")
Packit 8ea169
Packit 8ea169
        # List of tuples where the first member is element name and the second
Packit 8ea169
        # member is a Boolean flag which is True if the element is required
Packit 8ea169
        arguments = ((FILENAME_PACKAGE, False), (CD_DUMPDIR, True),
Packit 8ea169
                (FILENAME_UID, False), (FILENAME_UUID, False),
Packit 8ea169
                (FILENAME_DUPHASH, False))
Packit 8ea169
Packit 8ea169
        for elem in arguments:
Packit 8ea169
            itm = problem_data.get(elem[0])
Packit 8ea169
Packit 8ea169
            if itm is None:
Packit 8ea169
                if elem[1]:
Packit 8ea169
                    raise KeyError(elem[0])
Packit 8ea169
Packit 8ea169
                msg.append("", signature="s")
Packit 8ea169
            else:
Packit 8ea169
                msg.append(itm[0], signature="s")
Packit 8ea169
Packit 8ea169
Packit 8ea169
        bus.send_message(msg)
Packit 8ea169
    except dbus.exceptions.DBusException as ex:
Packit 8ea169
        raise RuntimeError("Failed to emit D-Bus Crash signal: {0}"
Packit 8ea169
                .format(str(ex)))
Packit 8ea169
    finally:
Packit 8ea169
        if bus is not None:
Packit 8ea169
            bus.close()
Packit 8ea169
Packit 8ea169
def build_notification_problem_data(problem_dir):
Packit 8ea169
    """Loads all necessary problem elements
Packit 8ea169
Packit 8ea169
    Keyword arguments:
Packit 8ea169
    problem_dir -- an absolute file system path problem directory
Packit 8ea169
Packit 8ea169
    Returns an instance of report.problem_data
Packit 8ea169
Packit 8ea169
    Raises:
Packit 8ea169
    ValueError -- if problem_dir is not an absolute path, if problem_dir cannot
Packit 8ea169
        be opened and if any required problem element is missing.
Packit 8ea169
    """
Packit 8ea169
Packit 8ea169
    if not os.path.isabs(problem_dir):
Packit 8ea169
        raise ValueError("problem directory must be absolute path")
Packit 8ea169
Packit 8ea169
    prblm_dt = report.problem_data()
Packit 8ea169
Packit 8ea169
    try:
Packit 8ea169
        dump_dir = report.dd_opendir(problem_dir, report.DD_OPEN_READONLY)
Packit 8ea169
        if not dump_dir:
Packit 8ea169
            raise ValueError("cannot open problem directory")
Packit 8ea169
Packit 8ea169
        dd_load_flag = (report.DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE
Packit 8ea169
                | report.DD_FAIL_QUIETLY_ENOENT)
Packit 8ea169
Packit 8ea169
        pd_add_flag = report.CD_FLAG_TXT | report.CD_FLAG_ISNOTEDITABLE
Packit 8ea169
Packit 8ea169
        package = dump_dir.load_text(FILENAME_PACKAGE, dd_load_flag)
Packit 8ea169
        if package:
Packit 8ea169
            prblm_dt.add(FILENAME_PACKAGE, package, pd_add_flag)
Packit 8ea169
        prblm_dt.add(CD_DUMPDIR, problem_dir, pd_add_flag)
Packit 8ea169
Packit 8ea169
        for element in (FILENAME_UID, FILENAME_UUID, FILENAME_DUPHASH):
Packit 8ea169
            val = dump_dir.load_text(element, dd_load_flag)
Packit 8ea169
            if val is not None:
Packit 8ea169
                prblm_dt.add(element, val, pd_add_flag)
Packit 8ea169
    finally:
Packit 8ea169
        dump_dir.close()
Packit 8ea169
Packit 8ea169
    return prblm_dt
Packit 8ea169
Packit 8ea169
Packit 8ea169
if __name__ == "__main__":
Packit 8ea169
    CMDARGS = ArgumentParser(
Packit 8ea169
            description=("Announce a new or duplicated problem via"
Packit 8ea169
                " all accessible channels"),
Packit 8ea169
            epilog=("Reads the default configuration from 'abrt.conf' file"))
Packit 8ea169
    CMDARGS.add_argument("-d", "--problem-dir",
Packit 8ea169
            type=str, required=True,
Packit 8ea169
            help="An absolute path to a new or duplicated problem directory")
Packit 8ea169
    CMDARGS.add_argument("-v", "--verbose",
Packit 8ea169
            action="count", dest="verbose", default=0,
Packit 8ea169
            help="Be verbose")
Packit 8ea169
    CMDARGS.add_argument("-a", "--autoreporting",
Packit 8ea169
            action="store_true", dest="autoreporting", default=False,
Packit 8ea169
            help="Force to run autoreporting event")
Packit 8ea169
    CMDARGS.add_argument("-e", "--autoreporting-event",
Packit 8ea169
            type=str, dest="autoreporting_event",
Packit 8ea169
            help="Overwrite autoreporting event name")
Packit 8ea169
Packit 8ea169
    OPTIONS = CMDARGS.parse_args()
Packit 8ea169
Packit 8ea169
    DIR_PATH = OPTIONS.problem_dir
Packit 8ea169
Packit 8ea169
    verbose = 0
Packit 8ea169
    ABRT_VERBOSE = os.getenv("ABRT_VERBOSE")
Packit 8ea169
    if ABRT_VERBOSE:
Packit 8ea169
        try:
Packit 8ea169
            verbose = int(ABRT_VERBOSE)
Packit 8ea169
        except:
Packit 8ea169
            pass
Packit 8ea169
Packit 8ea169
    verbose += OPTIONS.verbose
Packit 8ea169
    set_verbosity(verbose)
Packit 8ea169
    os.environ["ABRT_VERBOSE"] = str(verbose)
Packit 8ea169
Packit 8ea169
    try:
Packit 8ea169
        conf = problem.load_conf_file("abrt.conf")
Packit 8ea169
    except OSError as ex:
Packit 8ea169
        sys.stderr.write("{0}".format(str(ex)))
Packit 8ea169
        sys.exit(RETURN_FAILURE)
Packit 8ea169
Packit 8ea169
    try:
Packit 8ea169
        PD = build_notification_problem_data(DIR_PATH)
Packit 8ea169
    except ValueError as ex:
Packit 8ea169
        sys.stderr.write("Cannot notify '{0}': {1}\n".
Packit 8ea169
                format(DIR_PATH, str(ex)))
Packit 8ea169
        sys.exit(RETURN_FAILURE)
Packit 8ea169
Packit 8ea169
    # The execution must continue because we should try to notify via all
Packit 8ea169
    # configured channels. One of them might work properly.
Packit 8ea169
    return_status = RETURN_OK
Packit 8ea169
    try:
Packit 8ea169
        emit_crash_dbus_signal(PD)
Packit 8ea169
    except RuntimeError as ex:
Packit 8ea169
        sys.stderr.write("Cannot notify '{0}' via D-Bus: {1}\n".
Packit 8ea169
                format(DIR_PATH, str(ex)))
Packit 8ea169
        return_status = RETURN_FAILURE
Packit 8ea169
    except KeyError as ex:
Packit 8ea169
        # this is a bug in build_notification_problem_data()
Packit 8ea169
        sys.stderr.write("BUG: problem data misses required element '{0}'"
Packit 8ea169
                         " required for D-Bus notification\n"
Packit 8ea169
                         .format(str(ex)))
Packit 8ea169
Packit 8ea169
        return_status = RETURN_FAILURE
Packit 8ea169
Packit 8ea169
    if OPTIONS.autoreporting or conf.get("AutoreportingEnabled", "no") == "yes":
Packit 8ea169
        event_name = OPTIONS.autoreporting_event
Packit 8ea169
        if not event_name:
Packit 8ea169
            if "AutoreportingEvent" in conf:
Packit 8ea169
                event_name = conf["AutoreportingEvent"]
Packit 8ea169
            else:
Packit 8ea169
                sys.stderr.write("Autoreporting event is not configured\n")
Packit 8ea169
                return_status = RETURN_FAILURE
Packit 8ea169
Packit 8ea169
        if event_name:
Packit 8ea169
            try:
Packit 8ea169
                run_autoreport(PD, event_name)
Packit 8ea169
            except RuntimeError as ex:
Packit 8ea169
                sys.stderr.write("Cannot notify '{0}' via uReport: {1}\n".
Packit 8ea169
                        format(DIR_PATH, str(ex)))
Packit 8ea169
                return_status = RETURN_FAILURE
Packit 8ea169
            except KeyError as ex:
Packit 8ea169
                # this is a bug in build_notification_problem_data()
Packit 8ea169
                sys.stderr.write(
Packit 8ea169
                    "BUG: problem data misses required element '{0}'"
Packit 8ea169
                    " required for uReport notification\n".format(str(ex)))
Packit 8ea169
Packit 8ea169
                return_status = RETURN_FAILURE
Packit 8ea169
Packit 8ea169
    # log problem to systemd journal with SYSLOG_IDENTIFIER=abrt-notification
Packit 8ea169
    os.environ["REPORTER_JOURNAL_SYSLOG_ID"] = "abrt-notification"
Packit 8ea169
    if not run_event("report_systemd-journal", DIR_PATH):
Packit 8ea169
        # if report_systemd-journal is not defined, run 'reporter-systemd-journald'
Packit 8ea169
        try:
Packit 8ea169
            call(["reporter-systemd-journald", "-d " + DIR_PATH])
Packit 8ea169
        except FileNotFoundError as ex:
Packit 8ea169
            # we expect the reporter doesn't have to be installed
Packit 8ea169
            log1("reporter-systemd-journald was not found")
Packit 8ea169
Packit 8ea169
    sys.exit(return_status)
Packit 8ea169