Blame src/plugins/abrt-action-install-debuginfo.in

Packit 8ea169
#!/usr/bin/python3 -u
Packit 8ea169
# -*- coding: utf-8 -*-
Packit 8ea169
# WARNING: python -u means unbuffered I/O without it the messages are
Packit 8ea169
# passed to the parent asynchronously which looks bad in clients.
Packit 8ea169
Packit 8ea169
PROGNAME = "abrt-action-install-debuginfo"
Packit 8ea169
Packit 8ea169
import sys
Packit 8ea169
import os
Packit 8ea169
import errno
Packit 8ea169
import getopt
Packit 8ea169
import reportclient
Packit 8ea169
from subprocess import Popen, PIPE
Packit 8ea169
from reportclient import verbose, log_warning, log1, log2, set_verbosity, error_msg_and_die, error_msg
Packit 8ea169
import time
Packit 8ea169
from reportclient.debuginfo import DebugInfoDownload, filter_installed_debuginfos, build_ids_to_path, clean_up
Packit 8ea169
import problem
Packit 8ea169
Packit 8ea169
# everything was ok
Packit 8ea169
RETURN_OK = 0
Packit 8ea169
# serious problem, should be logged somewhere
Packit 8ea169
RETURN_FAILURE = 2
Packit 8ea169
# path to tmp directory has to be global because of clean_up()
Packit 8ea169
TMPDIR = None
Packit 8ea169
Packit 8ea169
GETTEXT_PROGNAME = "abrt"
Packit 8ea169
import locale
Packit 8ea169
import gettext
Packit 8ea169
Packit 8ea169
_ = lambda x: gettext.gettext(x)
Packit 8ea169
Packit 8ea169
def init_gettext():
Packit 8ea169
    try:
Packit 8ea169
        locale.setlocale(locale.LC_ALL, "")
Packit 8ea169
    except locale.Error:
Packit 8ea169
        os.environ['LC_ALL'] = 'C'
Packit 8ea169
        locale.setlocale(locale.LC_ALL, "")
Packit 8ea169
    # Defeat "AttributeError: 'module' object has no attribute 'nl_langinfo'"
Packit 8ea169
    try:
Packit 8ea169
        gettext.bind_textdomain_codeset(GETTEXT_PROGNAME, locale.nl_langinfo(locale.CODESET))
Packit 8ea169
    except AttributeError:
Packit 8ea169
        pass
Packit 8ea169
    gettext.bindtextdomain(GETTEXT_PROGNAME, '/usr/share/locale')
Packit 8ea169
    gettext.textdomain(GETTEXT_PROGNAME)
Packit 8ea169
Packit 8ea169
def sigterm_handler(signum, frame):
Packit 8ea169
    clean_up(TMPDIR, silent=True)
Packit 8ea169
    exit(RETURN_OK)
Packit 8ea169
Packit 8ea169
def sigint_handler(signum, frame):
Packit 8ea169
    clean_up(TMPDIR)
Packit 8ea169
    print("\n{0}".format(_("Exiting on user command")))
Packit 8ea169
    sys.stdout.flush()
Packit 8ea169
    # ??! without "sys.", I am getting segv!
Packit 8ea169
    sys.exit(RETURN_OK)
Packit 8ea169
Packit 8ea169
import signal
Packit 8ea169
Packit 8ea169
if __name__ == "__main__":
Packit 8ea169
    # abrt-server can send SIGTERM to abort the download
Packit 8ea169
    signal.signal(signal.SIGTERM, sigterm_handler)
Packit 8ea169
    # ctrl-c
Packit 8ea169
    signal.signal(signal.SIGINT, sigint_handler)
Packit 8ea169
    fbuild_ids = "build_ids"
Packit 8ea169
    cachedirs = []
Packit 8ea169
    size_mb = 4096
Packit 8ea169
    keeprpms = False
Packit 8ea169
    noninteractive = False
Packit 8ea169
    b_ids = []
Packit 8ea169
    exact_fls = False
Packit 8ea169
    missing = None
Packit 8ea169
    repo_pattern = "*debug*"
Packit 8ea169
    pkgmgr = None
Packit 8ea169
    releasever = None
Packit 8ea169
Packit 8ea169
    # localization
Packit 8ea169
    init_gettext()
Packit 8ea169
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
    PROGNAME = os.path.basename(sys.argv[0])
Packit 8ea169
    # ____________________________________________________________________________________ 7
Packit 8ea169
    # ______01234567890123456789012345678901234567890123456789012345678901234567890123456789
Packit 8ea169
    help_text = _(
Packit 8ea169
            "Usage: %s [-vy] [--ids=BUILD_IDS_FILE] [--pkgmgr=(yum|dnf)]\n"
Packit 8ea169
            "       [--tmpdir=TMPDIR] [--cache=CACHEDIR[:DEBUGINFODIR1:DEBUGINFODIR2...]] [--size_mb=SIZE]\n"
Packit 8ea169
            "       [-e, --exact=PATH[:PATH]...]\n"
Packit 8ea169
            "       [--releasever=RELEASEVER]\n"
Packit 8ea169
            "\n"
Packit 8ea169
            "Installs debuginfos for all build-ids listed in BUILD_IDS_FILE\n"
Packit 8ea169
            "to CACHEDIR, using TMPDIR as temporary staging area.\n"
Packit 8ea169
            "Old files in CACHEDIR are deleted until it is smaller than SIZE.\n"
Packit 8ea169
            "\n"
Packit 8ea169
            "Reads configuration from /etc/abrt/plugins/CCpp.conf\n"
Packit 8ea169
            "\n"
Packit 8ea169
            "    -v          Be verbose\n"
Packit 8ea169
            "    -y          Noninteractive, assume 'Yes' to all questions\n"
Packit 8ea169
            "    --ids       Default: build_ids\n"
Packit 8ea169
            "    --tmpdir    Default: @LARGE_DATA_TMP_DIR@/abrt-tmp-debuginfo-RANDOM_SUFFIX\n"
Packit 8ea169
            "    --cache     Colon separated list of directories. The first one is used for\n"
Packit 8ea169
            "                saving installed debuginfos.\n"
Packit 8ea169
            "                Default: /var/cache/abrt-dir\n"
Packit 8ea169
            "    --size_mb   Default: 4096\n"
Packit 8ea169
            "    --pkgmgr   Default: PackageManager from CCpp.conf or 'dnf'\n"
Packit 8ea169
            "    -e,--exact  Download only specified files\n"
Packit 8ea169
            "    --repo      Pattern to use when searching for repos.\n"
Packit 8ea169
            "                Default: *debug*\n"
Packit 8ea169
            "    --releasever RELEASEVER\n"
Packit 8ea169
            "                Pass this OS version to package managers.\n"
Packit 8ea169
            # --keeprpms is not documented yet because it's a NOP so far
Packit 8ea169
    ) % PROGNAME
Packit 8ea169
Packit 8ea169
    try:
Packit 8ea169
        opts, args = getopt.getopt(sys.argv[1:], "vyhe",
Packit 8ea169
                ["help", "ids=", "cache=", "size_mb=", "tmpdir=", "keeprpms",
Packit 8ea169
                 "exact=", "repo=", "pkgmgr=", "releasever="])
Packit 8ea169
    except getopt.GetoptError as err:
Packit 8ea169
        print(err) # prints something like "option -a not recognized"
Packit 8ea169
        exit(RETURN_FAILURE)
Packit 8ea169
Packit 8ea169
    for opt, arg in opts:
Packit 8ea169
        if opt in ("-h", "--help"):
Packit 8ea169
            print(help_text)
Packit 8ea169
            exit(0)
Packit 8ea169
        elif opt == "-v":
Packit 8ea169
            verbose += 1
Packit 8ea169
        elif opt == "-y":
Packit 8ea169
            noninteractive = True
Packit 8ea169
        elif opt == "--ids":
Packit 8ea169
            fbuild_ids = arg
Packit 8ea169
        elif opt == "--cache":
Packit 8ea169
            cachedirs = arg.split(':')
Packit 8ea169
        elif opt == "--size_mb":
Packit 8ea169
            try:
Packit 8ea169
                size_mb = int(arg)
Packit 8ea169
            except:
Packit 8ea169
                pass
Packit 8ea169
        elif opt == "--tmpdir":
Packit 8ea169
            TMPDIR = arg
Packit 8ea169
        elif opt == "--keeprpms":
Packit 8ea169
            keeprpms = True
Packit 8ea169
        # --exact takes precedence over --ids
Packit 8ea169
        elif opt in ("-e", "--exact"):
Packit 8ea169
            missing = arg.split(':')
Packit 8ea169
            exact_fls = True
Packit 8ea169
        elif opt == "--repo":
Packit 8ea169
            repo_pattern = arg
Packit 8ea169
        elif opt == "--pkgmgr":
Packit 8ea169
            pkgmgr = arg
Packit 8ea169
        elif opt == "--releasever":
Packit 8ea169
            releasever = arg
Packit 8ea169
Packit 8ea169
    set_verbosity(verbose)
Packit 8ea169
Packit 8ea169
    if not cachedirs:
Packit 8ea169
        cachedirs = ["/var/cache/abrt-di"]
Packit 8ea169
        try:
Packit 8ea169
            conf = problem.load_plugin_conf_file("CCpp.conf")
Packit 8ea169
        except OSError as ex:
Packit 8ea169
            print(ex)
Packit 8ea169
        else:
Packit 8ea169
            cachedirs = conf.get("DebuginfoLocation", cachedirs[0]).split(":")
Packit 8ea169
Packit 8ea169
    if not TMPDIR:
Packit 8ea169
        # security people prefer temp subdirs in app's private dir, like /var/run/abrt
Packit 8ea169
        # and we switched to /tmp but Fedora feature tmp-on-tmpfs appeared, hence we must
Packit 8ea169
        # not use /tmp for potential big data anymore
Packit 8ea169
        TMPDIR = "@LARGE_DATA_TMP_DIR@/abrt-tmp-debuginfo-%s.%u" % (time.strftime("%Y-%m-%d-%H:%M:%S"), os.getpid())
Packit 8ea169
Packit 8ea169
Packit 8ea169
    if missing == None:
Packit 8ea169
        fin = sys.stdin
Packit 8ea169
        if fbuild_ids != "-":
Packit 8ea169
            try:
Packit 8ea169
                fin = open(fbuild_ids, "r")
Packit 8ea169
            except IOError as ex:
Packit 8ea169
                error_msg_and_die(_("Can't open {0}: {1}").format(fbuild_ids, ex))
Packit 8ea169
        for line in fin.readlines():
Packit 8ea169
            b_ids.append(line.strip('\n'))
Packit 8ea169
Packit 8ea169
        if not b_ids:
Packit 8ea169
            exit(RETURN_FAILURE)
Packit 8ea169
Packit 8ea169
        # Delete oldest/biggest files from cachedir.
Packit 8ea169
        # (Note that we need to do it before we check for missing debuginfos)
Packit 8ea169
        #
Packit 8ea169
        # We can do it as a separate step in report_event.conf, but this
Packit 8ea169
        # would require setuid'ing abrt-action-trim-files to abrt:abrt.
Packit 8ea169
        # Since we (via abrt-action-install-debuginfo-to-abrt-cache)
Packit 8ea169
        # are already running setuid,
Packit 8ea169
        # it makes sense to NOT setuid abrt-action-trim-files too,
Packit 8ea169
        # but instead run it as our child:
Packit 8ea169
        sys.stdout.flush()
Packit 8ea169
        try:
Packit 8ea169
            pid = os.fork()
Packit 8ea169
            if pid == 0:
Packit 8ea169
                argv = ["abrt-action-trim-files", "-f", "%um:%s" % (size_mb, cachedirs[0]), "--"]
Packit 8ea169
                argv.extend(build_ids_to_path(cachedirs[0], b_ids))
Packit 8ea169
                log2("abrt-action-trim-files %s", argv);
Packit 8ea169
                os.execvp("abrt-action-trim-files", argv);
Packit 8ea169
                error_msg_and_die("Can't execute '%s'", "abrt-action-trim-files");
Packit 8ea169
            if pid > 0:
Packit 8ea169
                os.waitpid(pid, 0);
Packit 8ea169
        except Exception as e:
Packit 8ea169
            error_msg("Can't execute abrt-action-trim-files: %s", e);
Packit 8ea169
Packit 8ea169
        missing = filter_installed_debuginfos(b_ids, cachedirs)
Packit 8ea169
Packit 8ea169
    exact_file_missing = False
Packit 8ea169
    result = RETURN_OK
Packit 8ea169
    if missing:
Packit 8ea169
        log2("%s", missing)
Packit 8ea169
        if len(b_ids) > 0:
Packit 8ea169
            print(_("Coredump references {0} debuginfo files, {1} of them are not installed").format(len(b_ids), len(missing)))
Packit 8ea169
        else:
Packit 8ea169
            # Only --exact FILE[:FILE2]... was specified
Packit 8ea169
            print(_("{0} of debuginfo files are not installed").format(len(missing)))
Packit 8ea169
Packit 8ea169
        if pkgmgr is None:
Packit 8ea169
            try:
Packit 8ea169
                conf = problem.load_plugin_conf_file("CCpp.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
            pkgmgr = conf.get("PackageManager", "dnf").lower()
Packit 8ea169
Packit 8ea169
        download_class = None
Packit 8ea169
        if pkgmgr == "dnf":
Packit 8ea169
            from reportclient.dnfdebuginfo import DNFDebugInfoDownload
Packit 8ea169
            download_class = DNFDebugInfoDownload
Packit 8ea169
        elif pkgmgr == "yum":
Packit 8ea169
            from reportclient.yumdebuginfo import YumDebugInfoDownload
Packit 8ea169
            download_class = YumDebugInfoDownload
Packit 8ea169
        else:
Packit 8ea169
            sys.stderr.write(_("Invalid configuration of CCpp addon, unsupported Package manager: '%s'") % (pkgmgr))
Packit 8ea169
            sys.exit(RETURN_FAILURE)
Packit 8ea169
Packit 8ea169
        # TODO: should we pass keep_rpms=keeprpms to DebugInfoDownload here??
Packit 8ea169
        try:
Packit 8ea169
            downloader = download_class(cache=cachedirs[0], tmp=TMPDIR,
Packit 8ea169
                                    noninteractive=noninteractive,
Packit 8ea169
                                    repo_pattern=repo_pattern,
Packit 8ea169
                                    releasever=releasever)
Packit 8ea169
            result = downloader.download(missing, download_exact_files=exact_fls)
Packit 8ea169
Packit 8ea169
            # make sure that all downloaded directories are writeable by abrt group
Packit 8ea169
            for root, dirs, files in os.walk(cachedirs[0]):
Packit 8ea169
                for walked_dir in dirs:
Packit 8ea169
                    os.chmod(os.path.join(root, walked_dir), 0o775)
Packit 8ea169
Packit 8ea169
        except OSError as ex:
Packit 8ea169
            if ex.errno == errno.EPIPE:
Packit 8ea169
                clean_up(TMPDIR, silent=True)
Packit 8ea169
                exit(RETURN_FAILURE)
Packit 8ea169
            error_msg_and_die("Can't download debuginfos: %s", ex)
Packit 8ea169
Packit 8ea169
        if exact_fls:
Packit 8ea169
            for bid in missing:
Packit 8ea169
                if not os.path.isfile(bid):
Packit 8ea169
                    print(_("Missing requested file: {0}").format(bid))
Packit 8ea169
                    exact_file_missing = True
Packit 8ea169
Packit 8ea169
        missing = filter_installed_debuginfos(b_ids, cachedirs)
Packit 8ea169
        for bid in missing:
Packit 8ea169
            print(_("Missing debuginfo file: {0}").format(bid))
Packit 8ea169
Packit 8ea169
    if not missing and not exact_file_missing:
Packit 8ea169
        print(_("All debuginfo files are available"))
Packit 8ea169
Packit 8ea169
    if exact_file_missing:
Packit 8ea169
        result = RETURN_FAILURE
Packit 8ea169
Packit 8ea169
    exit(result)