|
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 |
|