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