summaryrefslogtreecommitdiffstats
path: root/src/retrace
diff options
context:
space:
mode:
authorKarel Klic <kklic@redhat.com>2011-03-01 11:32:49 +0100
committerKarel Klic <kklic@redhat.com>2011-03-01 11:32:49 +0100
commitfb52104af74bbf6eeda394880666df40b4354aba (patch)
tree52b3318f050284cbac8ec96cac1701a1bf28f921 /src/retrace
parent5e89938bd01a92dd2166f78e5a3541c185bae10f (diff)
downloadabrt-fb52104af74bbf6eeda394880666df40b4354aba.tar.gz
abrt-fb52104af74bbf6eeda394880666df40b4354aba.tar.xz
abrt-fb52104af74bbf6eeda394880666df40b4354aba.zip
moved retrace server code to src/retrace
Diffstat (limited to 'src/retrace')
-rw-r--r--src/retrace/Makefile.am27
-rwxr-xr-xsrc/retrace/abrt-retrace-cleanup.py43
-rwxr-xr-xsrc/retrace/abrt-retrace-reposync58
-rw-r--r--src/retrace/backtrace.wsgi42
-rwxr-xr-xsrc/retrace/coredump2packages.py293
-rw-r--r--src/retrace/create.wsgi96
-rwxr-xr-xsrc/retrace/install.sh244
-rw-r--r--src/retrace/log.wsgi42
-rw-r--r--src/retrace/retrace-local.repo143
-rw-r--r--src/retrace/retrace.conf33
-rw-r--r--src/retrace/retrace.py232
-rw-r--r--src/retrace/retrace.repo199
-rw-r--r--src/retrace/retrace_httpd.conf4
-rw-r--r--src/retrace/status.wsgi38
-rwxr-xr-xsrc/retrace/test-uploader.py139
-rw-r--r--src/retrace/worker.c51
-rwxr-xr-xsrc/retrace/worker.py298
17 files changed, 1982 insertions, 0 deletions
diff --git a/src/retrace/Makefile.am b/src/retrace/Makefile.am
new file mode 100644
index 00000000..887466eb
--- /dev/null
+++ b/src/retrace/Makefile.am
@@ -0,0 +1,27 @@
+bin_PROGRAMS = abrt-retrace-worker
+abrt_retrace_worker_SOURCES = worker.c
+
+dist_bin_SCRIPTS=abrt-retrace-reposync
+
+retrace_PYTHON = retrace.py
+retracedir = $(datadir)/abrt-retrace
+
+interface_PYTHON = backtrace.wsgi create.wsgi log.wsgi status.wsgi
+# interfacedir should probably be $$(pkgdatadir)/retrace
+interfacedir = $(datadir)/abrt-retrace
+
+repo_DATA = retrace.repo retrace-local.repo
+repodir = ${sysconfdir}/yum.repos.d
+
+retraceconf_DATA = retrace.conf
+retraceconfdir = ${sysconfdir}/abrt
+
+httpdconf_DATA = retrace_httpd.conf
+httpdconfdir = ${sysconfdir}/httpd/conf.d
+
+EXTRA_DIST = retrace.conf retrace_httpd.conf retrace.repo retrace-local.repo
+
+# Apache config files can be owned by root, httpd just needs read
+# access.
+#install-data-hook:
+# chown apache:apache $(DESTDIR)$(httpdconfdir)/retrace_httpd.conf
diff --git a/src/retrace/abrt-retrace-cleanup.py b/src/retrace/abrt-retrace-cleanup.py
new file mode 100755
index 00000000..9286889f
--- /dev/null
+++ b/src/retrace/abrt-retrace-cleanup.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+
+import os
+import shutil
+import sys
+import time
+from retrace import *
+
+if __name__ == "__main__":
+ now = int(time.time())
+
+ logfile = "%s/cleanup.log" % CONFIG["LogDir"]
+
+ try:
+ log = open(logfile, "a")
+ log.write(time.strftime("[%Y-%m-%d %H:%M:%S] Running cleanup\n"))
+
+ files = os.listdir(CONFIG["WorkDir"])
+ except IOError, ex:
+ print "Error opening log file: %s" % ex
+ sys.exit(1)
+ except OSError, ex:
+ log.write("Unable to list work directory: %s" % ex)
+ sys.exit(2)
+
+ for filename in files:
+ filepath = "%s/%s" % (CONFIG["WorkDir"], filename)
+ if os.path.isdir(filepath):
+ try:
+ if (now - os.path.getctime(filepath)) / 3600 >= CONFIG["DeleteTaskAfter"]:
+ log.write("Deleting directory '%s'\n" % filepath)
+ shutil.rmtree(filepath)
+ except OSError, ex:
+ log.write("Error deleting directory: %s\n" % (filepath, ex))
+ except IOError, ex:
+ print "Unable to write to log file: %s" % ex
+ sys.exit(3)
+
+ try:
+ log.close()
+ except IOError, ex:
+ print "Error closing log file: %s" % ex
+ sys.exit(4)
diff --git a/src/retrace/abrt-retrace-reposync b/src/retrace/abrt-retrace-reposync
new file mode 100755
index 00000000..0e75a7e1
--- /dev/null
+++ b/src/retrace/abrt-retrace-reposync
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+if [ $3 ] && [ ! $4 ] \
+ && [ $1 = "fedora" ] \
+ && ( [ $3 = "i686" ] || [ $3 = "x86_64" ] )
+then
+ LOG="/var/log/abrt-retrace/$1-$2-$3.log"
+ LOCK="/var/lock/subsys/abrt-retrace-$1-$2-$3"
+ REPODIR="/var/cache/abrt-retrace/"
+
+ REPODIR_CUSTOM=`cat "/etc/abrt/retrace.conf" | grep "RepoDir" | sed "s/^ *RepoDir *= *//"`
+ if [ -d $REPODIR_CUSTOM ] || mkdir -p $REPODIR_CUSTOM > /dev/null 2>&1
+ then
+ REPODIR="$REPODIR_CUSTOM"
+ fi
+
+ date >> "$LOG"
+
+ if [ -f "$LOCK" ]
+ then
+ echo "The repository synchronization is running at the moment." >> "$LOG"
+ exit 2
+ fi
+
+ if ! touch "$LOCK"
+ then
+ echo "Unable to set lock."
+ exit 3
+ fi
+
+ cd "$REPODIR"
+
+ reposync -a $3 \
+ --repoid="$1-$2-$3-updates" \
+ --repoid="$1-$2-$3-updates-debuginfo" \
+ --repoid="$1-$2-$3-updates-testing" \
+ --repoid="$1-$2-$3-updates-testing-debuginfo" \
+ | grep "Downloading" \
+ | sed -e "s/^\[\([^:]*\).*\] Downloading /\1 /" \
+ | sed -e "s/Packages\///" \
+ >> "$LOG"
+
+ createrepo "$1-$2-$3-updates" > /dev/null
+ createrepo "$1-$2-$3-updates-debuginfo" > /dev/null
+ createrepo "$1-$2-$3-updates-testing" > /dev/null
+ createrepo "$1-$2-$3-updates-testing-debuginfo" > /dev/null
+
+ rm -f "$LOCK"
+else
+ echo "Usage: $0 distribution version architecture"
+ echo
+ echo "where"
+ echo "distributuon = [fedora]"
+ echo "version = release version"
+ echo "architecture = [i686|x86_64]"
+
+ exit 1
+fi
diff --git a/src/retrace/backtrace.wsgi b/src/retrace/backtrace.wsgi
new file mode 100644
index 00000000..d6a8686a
--- /dev/null
+++ b/src/retrace/backtrace.wsgi
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+import sys
+sys.path = ["/usr/share/abrt-retrace"] + sys.path
+
+from retrace import *
+
+def application(environ, start_response):
+ request = Request(environ)
+
+ match = URL_PARSER.match(request.script_name)
+ if not match:
+ return response(start_response, "404 Not Found")
+
+ taskdir = "%s/%s" % (CONFIG["SaveDir"], match.group(1))
+
+ if not os.path.isdir(taskdir):
+ return response(start_response, "404 Not Found")
+
+ pwdpath = "%s/password" % taskdir
+ try:
+ pwdfile = open(pwdpath, "r")
+ pwd = pwdfile.read()
+ pwdfile.close()
+ except:
+ return response(start_response, "500 Internal Server Error", "Unable to verify password")
+
+ if not "X-Task-Password" in request.headers or request.headers["X-Task-Password"] != pwd:
+ return response(start_response, "403 Forbidden")
+
+ btpath = "%s/retrace_backtrace" % taskdir
+ if not os.path.isfile(btpath):
+ return response(start_response, "404 Not Found")
+
+ try:
+ btfile = open(btpath, "r")
+ output = btfile.read()
+ btfile.close()
+ except:
+ return response(start_response, "500 Internal Server Error", "Unable to read backtrace file at server")
+
+ return response(start_response, "200 OK", output)
diff --git a/src/retrace/coredump2packages.py b/src/retrace/coredump2packages.py
new file mode 100755
index 00000000..ac2db9f2
--- /dev/null
+++ b/src/retrace/coredump2packages.py
@@ -0,0 +1,293 @@
+#! /usr/bin/python
+# -*- coding:utf-8;mode:python -*-
+# Gets list of packages necessary for processing of a coredump.
+# Uses eu-unstrip and yum.
+
+import subprocess
+import yum
+import sys
+import argparse
+
+parser = argparse.ArgumentParser(description='Get packages for coredump processing.')
+parser.add_argument('--repos', default='*', metavar='WILDCARD',
+ help='Yum repository wildcard to be enabled')
+parser.add_argument('coredump', help='Coredump')
+parser.add_argument('--log', metavar='FILENAME',
+ help='Store debug output to a file')
+args = parser.parse_args()
+
+if args.log:
+ log = open(args.log, "w")
+else:
+ log = open("/dev/null", "w")
+
+#
+# Initialize yum, enable only repositories specified via command line
+# --repos option.
+#
+stdout = sys.stdout
+sys.stdout = log
+yumbase = yum.YumBase()
+yumbase.doConfigSetup()
+if not yumbase.setCacheDir():
+ exit(2)
+log.write("Closing all enabled repositories...\n")
+for repo in yumbase.repos.listEnabled():
+ log.write(" - {0}\n".format(repo.name))
+ repo.close()
+ yumbase.repos.disableRepo(repo.id)
+log.write("Enabling repositories matching \'{0}\'...\n".format(args.repos))
+for repo in yumbase.repos.findRepos(args.repos):
+ log.write(" - {0}\n".format(repo.name))
+ repo.enable()
+ repo.skip_if_unavailable = True
+yumbase.repos.doSetup()
+yumbase.repos.populateSack(mdtype='metadata', cacheonly=1)
+yumbase.repos.populateSack(mdtype='filelists', cacheonly=1)
+sys.stdout = stdout
+
+#
+# Get eu-unstrip output, which contains build-ids and binary object
+# paths
+#
+log.write("Running eu-unstrip...\n")
+unstrip_args = ['eu-unstrip', '--core={0}'.format(args.coredump), '-n']
+unstrip_proc = subprocess.Popen(unstrip_args, stdout=subprocess.PIPE)
+unstrip = unstrip_proc.communicate()[0]
+log.write("{0}\n".format(unstrip))
+if not unstrip:
+ exit(1)
+
+def binary_packages_from_debuginfo_package(debuginfo_package, binobj_path):
+ """
+ Returns a list of packages corresponding to the provided debuginfo
+ package. One of the packages in the list contains the binary
+ specified in binobj_path; this is a list because if binobj_patch
+ is not specified (and sometimes it is not, binobj_path might
+ contain just '-'), we do not know which package contains the
+ binary, we know only packages from the same SRPM as the debuginfo
+ package.
+ """
+ package_list = []
+ if binobj_path == '-': # [exe] without binary name
+ log.write(" Yum search for [exe] without binary name, "
+ "packages with NVR {0}:{1}-{2}.{3}...\n".format(debuginfo_package.epoch,
+ debuginfo_package.ver,
+ debuginfo_package.rel,
+ debuginfo_package.arch))
+ # Append all packages with the same base package name.
+ # Other possibility is to download the debuginfo RPM,
+ # unpack it, and get the name of the binary from the
+ # /usr/lib/debug/.build-id/xx/yyyyyy symlink.
+ evra_list = yumbase.pkgSack.searchNevra(epoch=debuginfo_package.epoch,
+ ver=debuginfo_package.ver,
+ rel=debuginfo_package.rel,
+ arch=debuginfo_package.arch)
+ for package in evra_list:
+ log.write(" - {0}: base name \"{1}\"\n".format(str(package), package.base_package_name))
+ if package.base_package_name != debuginfo_package.base_package_name:
+ continue
+ package_list.append(package)
+ else:
+ log.write(" Yum search for {0}...\n".format(binobj_path))
+ binobj_package_list = yumbase.pkgSack.searchFiles(binobj_path)
+ for binobj_package in binobj_package_list:
+ log.write(" - {0}".format(str(binobj_package)))
+ if 0 != binobj_package.returnEVR().compare(debuginfo_package.returnEVR()):
+ log.write(": NVR doesn't match\n")
+ continue
+ log.write(": NVR matches\n")
+ package_list.append(binobj_package)
+ return package_list
+
+def process_unstrip_entry(build_id, binobj_path):
+ """
+ Returns a tuple of two items.
+
+ First item is a list of packages which we found to be associated
+ with the unstrip entry defined by build_id and binobj_path.
+
+ Second item is a list of package versions (same package name,
+ different epoch-version-release), which contain the binary object
+ (an executable or shared library) corresponding to this unstrip
+ entry. If this method failed to find an unique package name (with
+ only different versions), this list contains the list of base
+ package names. This item can be used to associate a coredump with
+ some crashing package.
+ """
+ package_list = []
+ coredump_package_list = []
+ coredump_base_package_list = []
+ # Ask for a known path from debuginfo package.
+ debuginfo_path = "/usr/lib/debug/.build-id/{0}/{1}.debug".format(build_id[:2], build_id[2:])
+ log.write("Yum search for {0}...\n".format(debuginfo_path))
+ debuginfo_package_list = yumbase.pkgSack.searchFiles(debuginfo_path)
+
+ # A problem here is that some libraries lack debuginfo. Either
+ # they were stripped during build, or they were not stripped by
+ # /usr/lib/rpm/find-debuginfo.sh because of wrong permissions or
+ # something. The proper solution is to detect such libraries and
+ # fix the packages.
+ for debuginfo_package in debuginfo_package_list:
+ log.write(" - {0}\n".format(str(debuginfo_package)))
+ package_list.append(debuginfo_package)
+ binary_packages = binary_packages_from_debuginfo_package(debuginfo_package, binobj_path)
+ coredump_base_package_list.append(debuginfo_package.base_package_name)
+ if len(binary_packages) == 1:
+ coredump_package_list.append(str(binary_packages[0]))
+ package_list.extend(binary_packages)
+ if len(coredump_package_list) == len(coredump_base_package_list):
+ return package_list, coredump_package_list
+ else:
+ return package_list, coredump_base_package_list
+
+
+def process_unstrip_output():
+ """
+ Parse the eu-unstrip output, and search for packages via yum.
+
+ Returns a tuple containing three items:
+ - a list of package objects
+ - a list of missing buildid entries
+ - a list of coredump package adepts
+ """
+ # List of packages found in yum repositories and matching the
+ # coredump.
+ package_list = []
+ # List of pairs (library/executable path, build id) which were not
+ # found via yum.
+ missing_buildid_list = []
+ # coredump package adepts
+ coredump_package_list = []
+ first_entry = True
+ for line in unstrip.split('\n'):
+ parts = line.split()
+ if not parts or len(parts) < 3:
+ continue
+ build_id = parts[1].split('@')[0]
+ binobj_path = parts[2]
+ if binobj_path[0] != '/' and parts[4] != '[exe]':
+ continue
+ entry_package_list, entry_coredump_package_list = process_unstrip_entry(build_id, binobj_path)
+ if first_entry:
+ coredump_package_list = entry_coredump_package_list
+ first_entry = False
+ if len(entry_package_list) == 0:
+ missing_buildid_list.append([binobj_path, build_id])
+ else:
+ for entry_package in entry_package_list:
+ found = False
+ for package in package_list:
+ if str(entry_package) == str(package):
+ found = True
+ break
+ if not found:
+ package_list.append(entry_package)
+ return package_list, missing_buildid_list, coredump_package_list
+
+package_list, missing_buildid_list, coredump_package_list = process_unstrip_output()
+
+#
+# The package list might contain multiple packages with the same name,
+# but different version. This happens because some binary had the same
+# build id over multiple package releases.
+#
+def find_duplicates(package_list):
+ for p1 in range(0, len(package_list) - 1):
+ package1 = package_list[p1]
+ for p2 in range(p1 + 1, len(package_list)):
+ package2 = package_list[p2]
+ if package1.name == package2.name:
+ return package1, package2
+ return None, None
+
+def count_removals(package_list, base_package_name, epoch, ver, rel, arch):
+ count = 0
+ for package in package_list:
+ if package.base_package_name != base_package_name:
+ continue
+ if package.epoch != epoch or package.ver != ver or package.rel != rel or package.arch != arch:
+ continue
+ count += 1
+ return count
+
+log.write("Checking for duplicates...\n")
+while True:
+ package1, package2 = find_duplicates(package_list)
+ if package1 is None:
+ break
+ p1removals = count_removals(package_list,
+ package1.base_package_name,
+ package1.epoch,
+ package1.ver,
+ package1.rel,
+ package1.arch)
+ p2removals = count_removals(package_list,
+ package2.base_package_name,
+ package2.epoch,
+ package2.ver,
+ package2.rel,
+ package2.arch)
+
+ log.write(" - {0}".format(package1.base_package_name))
+ if package1.base_package_name != package2.base_package_name:
+ log.write(" {0}\n".format(package2.base_package_name))
+ else:
+ log.write("\n")
+ log.write(" - {0}:{1}-{2}.{3} ({4} dependent packages)\n".format(package1.epoch,
+ package1.ver,
+ package1.rel,
+ package1.arch,
+ p1removals))
+ log.write(" - {0}:{1}-{2}.{3} ({4} dependent packages)\n".format(package2.epoch,
+ package2.ver,
+ package2.rel,
+ package2.arch,
+ p2removals))
+
+ removal_candidate = package1
+ if p1removals == p2removals:
+ # Remove older if we can choose
+ if package1.returnEVR().compare(package2.returnEVR()) > 0:
+ removal_candidate = package2
+ log.write(" - decided to remove {0}:{1}-{2}.{3} because it's older\n".format(removal_candidate.epoch,
+ removal_candidate.ver,
+ removal_candidate.rel,
+ removal_candidate.arch))
+ else:
+ if p1removals > p2removals:
+ removal_candidate = package2
+ log.write(" - decided to remove {0}:{1}-{2}.{3} because has fewer dependencies\n".format(removal_candidate.epoch,
+ removal_candidate.ver,
+ removal_candidate.rel,
+ removal_candidate.arch))
+ # Remove the removal_candidate packages from the package list
+ for package in package_list[:]:
+ if package.base_package_name == removal_candidate.base_package_name and \
+ 0 == package.returnEVR().compare(removal_candidate.returnEVR()):
+ package_list.remove(package)
+
+# Clean coredump_package_list:
+for coredump_package in coredump_package_list[:]:
+ found = False
+ for package in package_list:
+ if str(package) == coredump_package or package.base_package_name == coredump_package:
+ found = True
+ break
+ if not found:
+ coredump_package_list.remove(coredump_package)
+
+#
+# Print names of found packages first, then a newline separator, and
+# then objects for which the packages were not found.
+#
+if len(coredump_package_list) == 1:
+ print coredump_package_list[0]
+else:
+ print "-"
+print
+for package in sorted(package_list):
+ print str(package)
+print
+for path, build_id in missing_buildid_list:
+ print "{0} {1}".format(path, build_id)
diff --git a/src/retrace/create.wsgi b/src/retrace/create.wsgi
new file mode 100644
index 00000000..1c560d45
--- /dev/null
+++ b/src/retrace/create.wsgi
@@ -0,0 +1,96 @@
+#!/usr/bin/python
+
+import sys
+sys.path = ["/usr/share/abrt-retrace"] + sys.path
+
+from retrace import *
+from tempfile import *
+
+def application(environ, start_response):
+ request = Request(environ)
+
+ if request.scheme != "https":
+ return response(start_response, "403 Forbidden", "You must use HTTPS.")
+
+ if len(get_active_tasks()) >= CONFIG["MaxParallelTasks"]:
+ return response(start_response, "503 Service Unavailable")
+
+ if request.method != "POST":
+ return response(start_response, "405 Method Not Allowed")
+
+ if not request.content_type in ["application/x-xz", "application/x-xz-compressed-tar", "application/x-gzip", "application/x-tar"]:
+ return response(start_response, "415 Unsupported Media Type")
+
+ if not request.content_length:
+ return response(start_response, "411 Length Required")
+
+ if request.content_length > CONFIG["MaxPackedSize"] * 1048576:
+ return response(start_response, "413 Request Entity Too Large")
+
+ if CONFIG["UseWorkDir"]:
+ workdir = CONFIG["WorkDir"]
+ else:
+ workdir = CONFIG["SaveDir"]
+
+ if not os.path.isdir(workdir):
+ try:
+ os.makedirs(workdir)
+ except:
+ return response(start_response, "500 Internal Server Error", "Unable to create working directory")
+
+ space = free_space(workdir)
+
+ if not space:
+ return response(start_response, "500 Internal Server Error", "Unable to obtain disk free space")
+
+ if space - request.content_length < CONFIG["MinStorageLeft"] * 1048576:
+ return response(start_response, "507 Insufficient Storage")
+
+ try:
+ archive = NamedTemporaryFile(mode="wb", delete=False, suffix=".tar.xz")
+ archive.write(request.body)
+ archive.close()
+ except:
+ return response(start_response, "500 Internal Server Error", "Unable to save archive")
+
+ size = unpacked_size(archive.name)
+ if not size:
+ os.unlink(archive.name)
+ return response(start_response, "500 Internal Server Error", "Unable to obtain unpacked size")
+
+ if size > CONFIG["MaxUnpackedSize"] * 1048576:
+ os.unlink(archive.name)
+ return response(start_response, "413 Request Entity Too Large")
+
+ if space - size < CONFIG["MinStorageLeft"] * 1048576:
+ os.unlink(archive.name)
+ return response(start_response, "507 Insufficient Storage")
+
+ taskid, taskpass, taskdir = new_task()
+ if not taskid or not taskpass or not taskdir:
+ return response(start_response, "500 Internal Server Error", "Unable to create new task")
+
+ try:
+ os.mkdir("%s/crash/" % taskdir)
+ os.chdir("%s/crash/" % taskdir)
+ unpack_retcode = unpack(archive.name)
+ os.unlink(archive.name)
+
+ if unpack_retcode != 0:
+ raise Exception
+ except:
+ os.chdir("/")
+ Popen(["rm", "-rf", taskdir])
+ return response(start_response, "500 Internal Server Error", "Unable to unpack archive")
+
+ files = os.listdir(".")
+
+ for required_file in REQUIRED_FILES:
+ if not required_file in files:
+ os.chdir("/")
+ Popen(["rm", "-rf", taskdir])
+ return response(start_response, "403 Forbidden")
+
+ Popen(["/usr/sbin/abrt-retrace-worker", "%d" % taskid])
+
+ return response(start_response, "201 Created", "", [("X-Task-Id", "%d" % taskid), ("X-Task-Password", taskpass)])
diff --git a/src/retrace/install.sh b/src/retrace/install.sh
new file mode 100755
index 00000000..ca88a1ec
--- /dev/null
+++ b/src/retrace/install.sh
@@ -0,0 +1,244 @@
+#!/bin/bash
+
+ABRTDIR="/etc/abrt"
+LOGDIR="/var/log/abrt-retrace"
+REPODIR="/var/cache/abrt-retrace"
+SCRIPTDIR="/usr/share/abrt-retrace"
+SRCDIR="."
+WORKDIR="/var/spool/abrt-retrace"
+
+INTFILES="$SRCDIR/interface/create.wsgi $SRCDIR/interface/status.wsgi \
+ $SRCDIR/interface/log.wsgi $SRCDIR/interface/backtrace.wsgi"
+LIBFILES="$SRCDIR/lib/retrace.py"
+REPOSYNCFILES="$SRCDIR/reposync/abrt-retrace-reposync"
+WORKERFILES="$SRCDIR/worker/worker.py $SRCDIR/worker/coredump2packages.py"
+
+if [ ! $EUID = "0" ]
+then
+ echo "You must run '$0' with root permissions."
+ exit 1
+fi
+
+if ! rpm -q httpd > /dev/null 2>&1
+then
+ echo "httpd package is required to install Retrace Server."
+ exit 2
+fi
+
+if ! rpm -q mod_wsgi > /dev/null 2>&1
+then
+ echo "mod_wsgi package is required to install Retrace Server"
+ exit 3
+fi
+
+if ! rpm -q mod_ssl > /dev/null 2>&1
+then
+ echo "mod_ssl package is required to install Retrace Server"
+ exit 4
+fi
+
+if ! rpm -q python-webob > /dev/null 2>&1
+then
+ echo "python-webob package is required to install Retrace Server"
+ exit 5
+fi
+
+if ! rpm -q yum-utils > /dev/null 2>&1
+then
+ echo "yum-utils package is required to install Retrace Server"
+ exit 6
+fi
+
+if ! rpm -q createrepo > /dev/null 2>&1
+then
+ echo "createrepo package is required to install Retrace Server"
+ exit 7
+fi
+
+if ! rpm -q mock > /dev/null 2>&1
+then
+ echo "mock package is required to install Retrace Server"
+ exit 8
+fi
+
+if ! rpm -q xz > /dev/null 2>&1
+then
+ echo "xz package is required to install Retrace Server"
+ exit 9
+fi
+
+if ! rpm -q gcc > /dev/null 2>&1
+then
+ echo "gcc package is required to install Retrace Server"
+ exit 10
+fi
+
+if usermod -G mock root
+then
+ echo "User 'root' added to 'mock' group"
+else
+ echo "Unable to add user 'root' to group 'mock'"
+ exit 11
+fi
+
+if [ ! -d "$ABRTDIR" ]
+then
+ if mkdir "$ABRTDIR"
+ then
+ echo "Created directory '$ABRTDIR'"
+ else
+ echo "Error creating directory '$ABRTDIR'"
+ exit 12
+ fi
+fi
+
+if [ ! -d "$SCRIPTDIR" ]
+then
+ if mkdir "$SCRIPTDIR"
+ then
+ echo "Created directory '$SCRIPTDIR'"
+ else
+ echo "Error creating directory '$SCRIPTDIR'"
+ exit 13
+ fi
+fi
+
+if [ ! -d "$WORKDIR" ]
+then
+ if mkdir "$WORKDIR"
+ then
+ echo "Created directory '$WORKDIR'"
+ if chown apache "$WORKDIR" && chgrp apache "$WORKDIR"
+ then
+ echo "$WORKDIR owner and group changed to 'apache'"
+ else
+ echo "$WORKDIR unable to change owner or group"
+ exit 14
+ fi
+ else
+ echo "Error creating directory '$WORKDIR'"
+ exit 15
+ fi
+fi
+
+if [ ! -d "$REPODIR" ]
+then
+ if mkdir "$REPODIR"
+ then
+ echo "Created directory '$REPODIR'"
+ else
+ echo "Error creating directory '$REPODIR'"
+ exit 16
+ fi
+fi
+
+if [ ! -d "$LOGDIR" ]
+then
+ if mkdir "$LOGDIR"
+ then
+ echo "Created directory '$LOGDIR'"
+ else
+ echo "Error creating directory '$LOGDIR'"
+ exit 17
+ fi
+fi
+
+if ! gcc -pedantic -Wall -Wextra -Werror -o "/usr/sbin/abrt-retrace-worker" "$SRCDIR/worker/worker.c" \
+ || ! chmod u+s "/usr/sbin/abrt-retrace-worker"
+then
+ echo "Error compiling abrt-retrace-worker"
+ exit 18
+fi
+
+echo "abrt-retrace-worker compiled"
+
+for FILE in $LIBFILES
+do
+ if cp "$FILE" "$SCRIPTDIR"
+ then
+ echo "Installed '$FILE'"
+ else
+ echo "Error installing '$FILE'"
+ exit 19
+ fi
+done
+
+for FILE in $INTFILES
+do
+ if cp "$FILE" "$SCRIPTDIR"
+ then
+ echo "Installed '$FILE'"
+ else
+ echo "Error installing '$FILE'"
+ exit 20
+ fi
+done
+
+for FILE in $WORKERFILES
+do
+ if cp "$FILE" "$SCRIPTDIR"
+ then
+ echo "Installed '$FILE'"
+ else
+ echo "Error installing '$FILE'"
+ exit 21
+ fi
+done
+
+for FILE in $REPOSYNCFILES
+do
+ if cp "$FILE" "$SCRIPTDIR"
+ then
+ echo "Installed '$FILE'"
+ else
+ echo "Error installing '$FILE'"
+ exit 22
+ fi
+done
+
+if cp "$SRCDIR/config/retrace.conf" "/etc/abrt/retrace.conf"
+then
+ echo "Copied '$SRCDIR/config/retrace.conf' to '/etc/abrt/retrace.conf'"
+else
+ echo "Error copying '$SRCDIR/config/retrace.conf'"
+ exit 23
+fi
+
+if cp "$SRCDIR/config/retrace.repo" "/etc/yum.repos.d/retrace.repo" \
+ && cp "$SRCDIR/config/retrace-local.repo" "/etc/yum.repos.d/retrace-local.repo"
+then
+ echo "Copied '$SRCDIR/config/retrace.repo' to '/etc/yum.repos.d/retrace.repo'"
+ echo "Running initial repository download. This will take some time."
+ "$SCRIPTDIR/abrt-retrace-reposync" fedora 14 i686
+ createrepo "$REPODIR/fedora-14-i686" > /dev/null
+ createrepo "$REPODIR/fedora-14-i686-debuginfo" > /dev/null
+ "$SCRIPTDIR/abrt-retrace-reposync" fedora 14 x86_64
+ createrepo "$REPODIR/fedora-14-x86_64" > /dev/null
+ createrepo "$REPODIR/fedora-14-x86_64-debuginfo" > /dev/null
+# "$SCRIPTDIR/abrt-retrace-reposync" fedora 15 i686
+# createrepo "$REPODIR/fedora-15-i686"
+# createrepo "$REPODIR/fedora-15-i686-debuginfo"
+# "$SCRIPTDIR/abrt-retrace-reposync" fedora 15 x86_64
+# createrepo "$REPODIR/fedora-15-x86_64"
+# createrepo "$REPODIR/fedora-15-x86_64-debuginfo"
+else
+ echo "Error copying '$SRCDIR/config/retrace.repo'"
+ exit 24
+fi
+
+if cp "$SRCDIR/config/retrace_httpd.conf" "/etc/httpd/conf.d/retrace.conf"
+then
+ echo "Copied '$SRCDIR/config/retrace_httpd.conf' to '/etc/httpd/conf.d/retrace.conf'"
+ service httpd restart
+else
+ echo "Error copying '$SRCDIR/config/retrace_httpd.conf'"
+ exit 25
+fi
+
+echo
+echo "Retrace Server setup OK."
+echo "You should set up cron to periodically synchronize local repositories. The recommended configuration is:"
+echo "0 0,8,16 * * * $SCRIPTDIR/abrt-retrace-reposync fedora 14 i686"
+echo "0 2,10,18 * * * $SCRIPTDIR/abrt-retrace-reposync fedora 14 x86_64"
+#echo "0 4,12,20 * * * $SCRIPTDIR/abrt-retrace-reposync fedora 15 i686"
+#echo "0 6,14,22 * * * $SCRIPTDIR/abrt-retrace-reposync fedora 15 x86_64"
diff --git a/src/retrace/log.wsgi b/src/retrace/log.wsgi
new file mode 100644
index 00000000..bc20a98b
--- /dev/null
+++ b/src/retrace/log.wsgi
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+import sys
+sys.path = ["/usr/share/abrt-retrace"] + sys.path
+
+from retrace import *
+
+def application(environ, start_response):
+ request = Request(environ)
+
+ match = URL_PARSER.match(request.script_name)
+ if not match:
+ return response(start_response, "404 Not Found")
+
+ taskdir = "%s/%s" % (CONFIG["SaveDir"], match.group(1))
+
+ if not os.path.isdir(taskdir):
+ return response(start_response, "404 Not Found")
+
+ pwdpath = "%s/password" % taskdir
+ try:
+ pwdfile = open(pwdpath, "r")
+ pwd = pwdfile.read()
+ pwdfile.close()
+ except:
+ return response(start_response, "500 Internal Server Error", "Unable to verify password")
+
+ if not "X-Task-Password" in request.headers or request.headers["X-Task-Password"] != pwd:
+ return response(start_response, "403 Forbidden")
+
+ logpath = "%s/retrace_log" % taskdir
+ if not os.path.isfile(logpath):
+ return response(start_response, "404 Not Found")
+
+ try:
+ logfile = open(logpath, "r")
+ output = logfile.read()
+ logfile.close()
+ except:
+ return response(start_response, "500 Internal Server Error", "Unable to read log file at server")
+
+ return response(start_response, "200 OK", output)
diff --git a/src/retrace/retrace-local.repo b/src/retrace/retrace-local.repo
new file mode 100644
index 00000000..fb079ca5
--- /dev/null
+++ b/src/retrace/retrace-local.repo
@@ -0,0 +1,143 @@
+[retrace-fedora-13-i686]
+name=Fedora 13 - i686
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-i686/
+enabled=0
+
+[retrace-fedora-13-i686-debuginfo]
+name=Fedora 13 - i686 - Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-i686-debuginfo/
+enabled=0
+
+[retrace-fedora-13-i686-updates]
+name=Fedora 13 - i686 - Updates
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-i686-updates/
+enabled=0
+
+[retrace-fedora-13-i686-updates-debuginfo]
+name=Fedora 13 - i686 - Updates - Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-i686-updates-debuginfo/
+enabled=0
+
+[retrace-fedora-13-i686-updates-testing]
+name=Fedora 13 - i686 - Test Updates
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-i686-updates-testing/
+enabled=0
+
+[retrace-fedora-13-i686-updates-testing-debuginfo]
+name=Fedora 13 - i686 - Test Updates Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-i686-updates-testing-debuginfo/
+enabled=0
+
+[retrace-fedora-13-x86_64]
+name=Fedora 13 - x86_64
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-x86_64/
+enabled=0
+
+[retrace-fedora-13-x86_64-debuginfo]
+name=Fedora 13 - x86_64 - Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-x86_64-debuginfo/
+enabled=0
+
+[retrace-fedora-13-x86_64-updates]
+name=Fedora 13 - x86_64 - Updates
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-x86_64-updates/
+enabled=0
+
+[retrace-fedora-13-x86_64-updates-debuginfo]
+name=Fedora 13 - x86_64 - Updates - Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-x86_64-updates-debuginfo/
+enabled=0
+
+[retrace-fedora-13-x86_64-updates-testing]
+name=Fedora 13 - x86_64 - Test Updates
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-x86_64-updates-testing/
+enabled=0
+
+[retrace-fedora-13-x86_64-updates-testing-debuginfo]
+name=Fedora 13 - x86_64 - Test Updates Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-13-x86_64-updates-testing-debuginfo/
+enabled=0
+
+[retrace-fedora-14-i686]
+name=Fedora 14 - i686
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-i686/
+enabled=0
+
+[retrace-fedora-14-i686-debuginfo]
+name=Fedora 14 - i686 - Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-i686-debuginfo/
+enabled=0
+
+[retrace-fedora-14-i686-updates]
+name=Fedora 14 - i686 - Updates
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-i686-updates/
+enabled=0
+
+[retrace-fedora-14-i686-updates-debuginfo]
+name=Fedora 14 - i686 - Updates - Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-i686-updates-debuginfo/
+enabled=0
+
+[retrace-fedora-14-i686-updates-testing]
+name=Fedora 14 - i686 - Test Updates
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-i686-updates-testing/
+enabled=0
+
+[retrace-fedora-14-i686-updates-testing-debuginfo]
+name=Fedora 14 - i686 - Test Updates Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-i686-updates-testing-debuginfo/
+enabled=0
+
+[retrace-fedora-14-x86_64]
+name=Fedora 14 - x86_64
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-x86_64/
+enabled=0
+
+[retrace-fedora-14-x86_64-debuginfo]
+name=Fedora 14 - x86_64 - Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-x86_64-debuginfo/
+enabled=0
+
+[retrace-fedora-14-x86_64-updates]
+name=Fedora 14 - x86_64 - Updates
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-x86_64-updates/
+enabled=0
+
+[retrace-fedora-14-x86_64-updates-debuginfo]
+name=Fedora 14 - x86_64 - Updates - Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-x86_64-updates-debuginfo/
+enabled=0
+
+[retrace-fedora-14-x86_64-updates-testing]
+name=Fedora 14 - x86_64 - Test Updates
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-x86_64-updates-testing/
+enabled=0
+
+[retrace-fedora-14-x86_64-updates-testing-debuginfo]
+name=Fedora 14 - x86_64 - Test Updates Debug
+failovermethod=priority
+baseurl=file:///var/cache/abrt-retrace/fedora-14-x86_64-updates-testing-debuginfo/
+enabled=0
diff --git a/src/retrace/retrace.conf b/src/retrace/retrace.conf
new file mode 100644
index 00000000..e97e7cfc
--- /dev/null
+++ b/src/retrace/retrace.conf
@@ -0,0 +1,33 @@
+[retrace]
+# Maximum tasks running at one moment
+MaxParallelTasks = 5
+
+# Maximum size of archive uploaded by user (MB)
+MaxPackedSize = 50
+
+# Maximum size of archive contents (MB)
+MaxUnpackedSize = 1024
+
+# Minimal storage left on WorkDir FS after unpacking archive (MB)
+MinStorageLeft = 1024
+
+# Delete old tasks after (hours)
+DeleteTaskAfter = 120
+
+# SQLite statistics DB filename
+DBFile = stats.db
+
+# Log directory
+LogDir = /var/log/abrt-retrace
+
+# Local repos directory
+RepoDir = /var/cache/abrt-retrace
+
+# Directory where the crashes and results are saved
+SaveDir = /var/spool/abrt-retrace
+
+# Whether to use explicit working directory, otherwise SaveDir is used
+UseWorkDir = 0
+
+# Working directory
+WorkDir = /tmp/abrt-retrace
diff --git a/src/retrace/retrace.py b/src/retrace/retrace.py
new file mode 100644
index 00000000..1c741ec7
--- /dev/null
+++ b/src/retrace/retrace.py
@@ -0,0 +1,232 @@
+#!/usr/bin/python
+
+import os
+import re
+import ConfigParser
+import random
+import sqlite3
+from webob import Request
+from subprocess import *
+
+REQUIRED_FILES = ["architecture", "coredump", "release"]
+
+DF_BIN = "/bin/df"
+DU_BIN = "/usr/bin/du"
+TAR_BIN = "/bin/tar"
+XZ_BIN = "/usr/bin/xz"
+
+TASKID_PARSER = re.compile("^.*/([0-9]+)/*$")
+PACKAGE_PARSER = re.compile("^(.+)-([0-9]+(\.[0-9]+)*-[0-9]+)\.([^-]+)$")
+DF_OUTPUT_PARSER = re.compile("^([^ ^\t]*)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+%)[ \t]+(.*)$")
+DU_OUTPUT_PARSER = re.compile("^([0-9]+)")
+XZ_OUTPUT_PARSER = re.compile("^totals[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+\.[0-9]+)[ \t]+([^ ^\t]+)[ \t]+([0-9]+)")
+URL_PARSER = re.compile("^/([0-9]+)/?")
+RELEASE_PARSERS = {
+ "fedora": re.compile("^Fedora[^0-9]+([0-9]+)[^\(]\(([^\)]+)\)$"),
+}
+
+TASKPASS_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+CONFIG_FILE = "/etc/abrt/retrace.conf"
+CONFIG = {
+ "TaskIdLength": 9,
+ "TaskPassLength": 32,
+ "MaxParallelTasks": 10,
+ "MaxPackedSize": 30,
+ "MaxUnpackedSize": 600,
+ "MinStorageLeft": 10240,
+ "DeleteTaskAfter": 120,
+ "LogDir": "/var/log/abrt-retrace",
+ "RepoDir": "/var/cache/abrt-retrace",
+ "SaveDir": "/var/spool/abrt-retrace",
+ "WorkDir": "/tmp/abrt-retrace",
+ "UseWorkDir": False,
+ "DBFile": "stats.db",
+}
+
+def read_config():
+ parser = ConfigParser.ConfigParser()
+ parser.read(CONFIG_FILE)
+ for key in CONFIG.keys():
+ vartype = type(CONFIG[key])
+ if vartype is int:
+ get = parser.getint
+ elif vartype is bool:
+ get = parser.getboolean
+ elif vartype is float:
+ get = parser.getfloat
+ else:
+ get = parser.get
+
+ try:
+ CONFIG[key] = get("retrace", key)
+ except:
+ pass
+
+def free_space(path):
+ pipe = Popen([DF_BIN, path], stdout=PIPE).stdout
+ for line in pipe.readlines():
+ match = DF_OUTPUT_PARSER.match(line)
+ if match:
+ pipe.close()
+ return 1024 * int(match.group(4))
+
+ pipe.close()
+ return None
+
+def dir_size(path):
+ pipe = Popen([DU_BIN, "-s", path], stdout=PIPE).stdout
+ for line in pipe.readlines():
+ match = DU_OUTPUT_PARSER.match(line)
+ if match:
+ pipe.close()
+ return 1024 * int(match.group(1))
+
+ pipe.close()
+ return 0
+
+def unpacked_size(archive):
+ pipe = Popen([XZ_BIN, "--list", "--robot", archive], stdout=PIPE).stdout
+ for line in pipe.readlines():
+ match = XZ_OUTPUT_PARSER.match(line)
+ if match:
+ pipe.close()
+ return int(match.group(4))
+
+ pipe.close()
+ return None
+
+def gen_task_password(taskdir):
+ generator = random.SystemRandom()
+ taskpass = ""
+ for j in xrange(CONFIG["TaskPassLength"]):
+ taskpass += generator.choice(TASKPASS_ALPHABET)
+
+ try:
+ passfile = open("%s/password" % taskdir, "w")
+ passfile.write(taskpass)
+ passfile.close()
+ except:
+ return None
+
+ return taskpass
+
+def get_task_est_time(taskdir):
+ return 180
+
+def new_task():
+ i = 0
+ newdir = CONFIG["SaveDir"]
+ while os.path.exists(newdir) and i < 50:
+ i += 1
+ taskid = random.randint(pow(10, CONFIG["TaskIdLength"] - 1), pow(10, CONFIG["TaskIdLength"]) - 1)
+ newdir = "%s/%d" % (CONFIG["SaveDir"], taskid)
+
+ try:
+ os.mkdir(newdir)
+ taskpass = gen_task_password(newdir)
+ if not taskpass:
+ Popen(["rm", "-rf", newdir])
+ raise Exception
+
+ return taskid, taskpass, newdir
+ except:
+ return None, None, None
+
+def unpack(archive):
+ pipe = Popen([TAR_BIN, "xJf", archive])
+ pipe.wait()
+ return pipe.returncode
+
+def response(start_response, status, body="", extra_headers=[]):
+ start_response(status, [("Content-Type", "text/plain"), ("Content-Length", "%d" % len(body))] + extra_headers)
+ return [body]
+
+def get_active_tasks():
+ tasks = []
+ if CONFIG["UseWorkDir"]:
+ tasksdir = CONFIG["WorkDir"]
+ else:
+ tasksdir = CONFIG["SaveDir"]
+
+ for filename in os.listdir(tasksdir):
+ if len(filename) != CONFIG["TaskIdLength"]:
+ continue
+
+ try:
+ taskid = int(filename)
+ except:
+ continue
+
+ path = "%s/%s" % (tasksdir, filename)
+ if os.path.isdir(path) and not os.path.isfile("%s/retrace_log" % path):
+ tasks.append(taskid)
+
+ return tasks
+
+def init_crashstats_db():
+ try:
+ con = sqlite3.connect("%s/%s" % (CONFIG["SaveDir"], CONFIG["DBFile"]))
+ query = con.cursor()
+ query.execute("""
+ CREATE TABLE IF NOT EXISTS
+ retracestats(
+ taskid INT NOT NULL,
+ package VARCHAR(255) NOT NULL,
+ version VARCHAR(16) NOT NULL,
+ release VARCHAR(16) NOT NULL,
+ arch VARCHAR(8) NOT NULL,
+ starttime INT NOT NULL,
+ duration INT NOT NULL,
+ prerunning TINYINT NOT NULL,
+ postrunning TINYINT NOT NULL,
+ chrootsize BIGINT NOT NULL
+ )
+ """)
+ con.commit()
+ con.close()
+
+ return True
+ except:
+ return False
+
+def save_crashstats(crashstats):
+ try:
+ con = sqlite3.connect("%s/%s" % (CONFIG["SaveDir"], CONFIG["DBFile"]))
+ query = con.cursor()
+ query.execute("""
+ INSERT INTO retracestats(taskid, package, version, release, arch,
+ starttime, duration, prerunning, postrunning, chrootsize)
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ """,
+ (crashstats["taskid"], crashstats["package"], crashstats["version"],
+ crashstats["release"], crashstats["arch"], crashstats["starttime"],
+ crashstats["duration"], crashstats["prerunning"],
+ crashstats["postrunning"], crashstats["chrootsize"])
+ )
+ con.commit()
+ con.close()
+
+ return True
+ except:
+ return False
+
+class logger():
+ def __init__(self, taskid):
+ "Starts logging into savedir."
+ self._logfile = open("%s/%s/log" % (CONFIG["SaveDir"], taskid), "w")
+
+ def write(self, msg):
+ "Writes msg into log file."
+ if not self._logfile.closed:
+ self._logfile.write(msg)
+ self._logfile.flush()
+
+ def close(self):
+ "Finishes logging and renames file to retrace_log."
+ if not self._logfile.closed:
+ self._logfile.close()
+ os.rename(self._logfile.name, self._logfile.name.replace("/log", "/retrace_log"))
+
+### read config on import ###
+read_config()
diff --git a/src/retrace/retrace.repo b/src/retrace/retrace.repo
new file mode 100644
index 00000000..19c409e2
--- /dev/null
+++ b/src/retrace/retrace.repo
@@ -0,0 +1,199 @@
+[fedora-13-i686]
+name=Fedora 13 - i686
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-13&arch=i386
+enabled=0
+metadata_expire=6h
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-13-i686-debuginfo]
+name=Fedora 13 - i686 - Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-debug-13&arch=i386
+enabled=0
+metadata_expire=6h
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-13-i686-updates]
+name=Fedora 13 - i686 - Updates
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-released-f13&arch=i386
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-13-i686-updates-debuginfo]
+name=Fedora 13 - i686 - Updates - Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-released-debug-f13&arch=i386
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-13-i686-updates-testing]
+name=Fedora 13 - i686 - Test Updates
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-testing-f13&arch=i386
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-13-i686-updates-testing-debuginfo]
+name=Fedora 13 - i686 - Test Updates Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-testing-debug-f13&arch=i386
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-13-x86_64]
+name=Fedora 13 - x86_64
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-13&arch=x86_64
+enabled=0
+metadata_expire=6h
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-13-x86_64-debuginfo]
+name=Fedora 13 - x86_64 - Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-debug-13&arch=x86_64
+enabled=0
+metadata_expire=6h
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-13-x86_64-updates]
+name=Fedora 13 - x86_64 - Updates
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-released-f13&arch=x86_64
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-13-x86_64-updates-debuginfo]
+name=Fedora 13 - x86_64 - Updates - Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-released-debug-f13&arch=x86_64
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-13-x86_64-updates-testing]
+name=Fedora 13 - x86_64 - Test Updates
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-testing-f13&arch=x86_64
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-13-x86_64-updates-testing-debuginfo]
+name=Fedora 13 - x86_64 - Test Updates Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-testing-debug-f13&arch=x86_64
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-14-i686]
+name=Fedora 14 - i686
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-14&arch=i386
+enabled=0
+metadata_expire=6h
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-14-i686-debuginfo]
+name=Fedora 14 - i686 - Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-debug-14&arch=i386
+enabled=0
+metadata_expire=6h
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-14-i686-updates]
+name=Fedora 14 - i686 - Updates
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-released-f14&arch=i386
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-14-i686-updates-debuginfo]
+name=Fedora 14 - i686 - Updates - Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-released-debug-f14&arch=i386
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-14-i686-updates-testing]
+name=Fedora 14 - i686 - Test Updates
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-testing-f14&arch=i386
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-14-i686-updates-testing-debuginfo]
+name=Fedora 14 - i686 - Test Updates Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-testing-debug-f14&arch=i386
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-i386
+
+[fedora-14-x86_64]
+name=Fedora 14 - x86_64
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-14&arch=x86_64
+enabled=0
+metadata_expire=6h
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-14-x86_64-debuginfo]
+name=Fedora 14 - x86_64 - Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=fedora-debug-14&arch=x86_64
+enabled=0
+metadata_expire=6h
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-14-x86_64-updates]
+name=Fedora 14 - x86_64 - Updates
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-released-f14&arch=x86_64
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-14-x86_64-updates-debuginfo]
+name=Fedora 14 - x86_64 - Updates - Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-released-debug-f14&arch=x86_64
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-14-x86_64-updates-testing]
+name=Fedora 14 - x86_64 - Test Updates
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-testing-f14&arch=x86_64
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
+
+[fedora-14-x86_64-updates-testing-debuginfo]
+name=Fedora 14 - x86_64 - Test Updates Debug
+failovermethod=priority
+mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=updates-testing-debug-f14&arch=x86_64
+enabled=0
+gpgcheck=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-x86_64
diff --git a/src/retrace/retrace_httpd.conf b/src/retrace/retrace_httpd.conf
new file mode 100644
index 00000000..b896f86f
--- /dev/null
+++ b/src/retrace/retrace_httpd.conf
@@ -0,0 +1,4 @@
+WSGIScriptAliasMatch ^/create$ /usr/share/abrt-retrace/create.wsgi
+WSGIScriptAliasMatch ^/[0-9]+/?$ /usr/share/abrt-retrace/status.wsgi
+WSGIScriptAliasMatch ^/[0-9]+/log$ /usr/share/abrt-retrace/log.wsgi
+WSGIScriptAliasMatch ^/[0-9]+/backtrace$ /usr/share/abrt-retrace/backtrace.wsgi
diff --git a/src/retrace/status.wsgi b/src/retrace/status.wsgi
new file mode 100644
index 00000000..eb153b05
--- /dev/null
+++ b/src/retrace/status.wsgi
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+import sys
+sys.path = ["/usr/share/abrt-retrace"] + sys.path
+
+from retrace import *
+
+def application(environ, start_response):
+ request = Request(environ)
+
+ match = URL_PARSER.match(request.script_name)
+ if not match:
+ return response(start_response, "404 Not Found")
+
+ taskdir = "%s/%s" % (CONFIG["SaveDir"], match.group(1))
+
+ if not os.path.isdir(taskdir):
+ return response(start_response, "404 Not Found")
+
+ pwdpath = "%s/password" % taskdir
+ try:
+ pwdfile = open(pwdpath, "r")
+ pwd = pwdfile.read()
+ pwdfile.close()
+ except:
+ return response(start_response, "500 Internal Server Error", "Unable to verify password")
+
+ if not "X-Task-Password" in request.headers or request.headers["X-Task-Password"] != pwd:
+ return response(start_response, "403 Forbidden")
+
+ status = "PENDING"
+ if os.path.isfile("%s/retrace_log" % taskdir):
+ if os.path.isfile("%s/retrace_backtrace" % taskdir):
+ status = "FINISHED_SUCCESS"
+ else:
+ status = "FINISHED_FAILURE"
+
+ return response(start_response, "200 OK", status, [("X-Task-Status", status)])
diff --git a/src/retrace/test-uploader.py b/src/retrace/test-uploader.py
new file mode 100755
index 00000000..db75da16
--- /dev/null
+++ b/src/retrace/test-uploader.py
@@ -0,0 +1,139 @@
+#!/usr/bin/python
+import os
+import socket
+import ssl
+import sys
+import tempfile
+
+from subprocess import *
+
+REQUIRED_FILES = ["analyzer", "architecture", "coredump", "executable", "package", "release", "uid"]
+CONTENT_TYPE = "application/x-xz-compressed-tar"
+
+if __name__ == "__main__":
+ argc = len(sys.argv)
+ if argc < 2 or argc > 3:
+ print "ABRT Retrace Uploader"
+ print "For test purposes only"
+ print "Usage: '" + sys.argv[0] + " crash_directory server_address'"
+ print " Crash directory is the directory created by ABRT (default /var/spool/abrt/crash_directory/)."
+ print " Crash directory must contain analyzer, architecture, coredump, executable, package, release and uid files."
+ print " If no server address is specified, default testing machine is used."
+ print " Only binary crashes (caught by CCpp) need retrace."
+ print " The script shows raw HTTP output including X-Task-Id and X-Task-Password headers."
+ print " The tester is supposed to know what he's uploading and should handle task id and password on his own."
+ sys.exit(1)
+
+ crashdir = sys.argv[1]
+ if argc == 2:
+ server_addr = "retrace01.fedoraproject.org"
+ else:
+ server_addr = sys.argv[2]
+
+ print "Checking crash directory...",
+ sys.stdout.flush()
+
+ for required_file in REQUIRED_FILES:
+ if not os.path.isfile(crashdir + "/" + required_file):
+ print "Error"
+ sys.exit(3)
+
+ print "OK"
+ print "Checking analyzer...",
+ sys.stdout.flush()
+
+ try:
+ anfile = open(crashdir + "/analyzer", "r")
+ an = anfile.read()
+ anfile.close()
+ if an != "CCpp":
+ raise
+ except:
+ print "Error"
+ sys.exit(4)
+
+ print "OK"
+ print "Checking architecture...",
+ sys.stdout.flush()
+
+ print "OK"
+
+ print "Compressing crash into .tar.xz archive...",
+ sys.stdout.flush()
+
+ archive = tempfile.NamedTemporaryFile(delete = False, suffix = ".tar.xz")
+ archive.close()
+
+ os.chdir(crashdir)
+ compress = Popen(["tar", "cJf", archive.name] + REQUIRED_FILES)
+ compress.wait()
+ if compress.returncode != 0:
+ print "Error"
+ sys.exit(7)
+
+ print "OK"
+ print "Building request...",
+ sys.stdout.flush()
+
+ try:
+ f = open(archive.name, "rb")
+ data = f.read()
+ f.close()
+ request = "POST /create HTTP/1.1\r\n" \
+ + "Host: " + server_addr + "\r\n" \
+ + "Content-Type: " + CONTENT_TYPE + "\r\n" \
+ + "Content-Length: " + str(len(data)) + "\r\n" \
+ + "Connection: close\r\n" \
+ + "\r\n"
+
+ print "OK"
+ print "---------"
+ print request + "[raw data]"
+ print "---------"
+ except:
+ print "Error"
+ sys.exit(8)
+
+ print "Connecting to retrace server @ ssl://" + server_addr + ":443...",
+ sys.stdout.flush()
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sslsock = ssl.wrap_socket(sock)
+ try:
+ sslsock.connect((server_addr, 443))
+ print "OK"
+ print "Sending request...",
+ sys.stdout.flush()
+
+ sslsock.write(request + data)
+
+ print "OK"
+ print "Receiving response...",
+ sys.stdout.flush()
+
+ response = ""
+ block = sslsock.read()
+ while (block):
+ response += block
+ block = sslsock.read()
+
+ sslsock.close()
+
+ print "OK"
+ print "----------"
+ print response,
+ print "----------"
+ except:
+ print "Error"
+ sys.exit(9)
+
+ print "Cleanup...",
+ sys.stdout.flush()
+
+ try:
+ os.unlink(archive.name)
+ except:
+ print "Error"
+ sys.exit(10)
+
+ print "OK"
diff --git a/src/retrace/worker.c b/src/retrace/worker.c
new file mode 100644
index 00000000..2020627d
--- /dev/null
+++ b/src/retrace/worker.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ Launches Retrace Server worker (worker.py) with root permissions.
+ Binary needs to be owned by root and needs to set SUID bit.
+*/
+
+int main(int argc, char **argv)
+{
+ char command[256];
+ FILE *pipe;
+ int i;
+
+ if (argc != 2)
+ {
+ fprintf(stderr, "Usage: %s task_id\n", argv[0]);
+ return 1;
+ }
+
+ if (setuid(0) != 0)
+ {
+ fprintf(stderr, "You must run %s with root permissions.\n", argv[0]);
+ return 2;
+ }
+
+ for (i = 0; argv[1][i]; ++i)
+ if (!isdigit(argv[1][i]))
+ {
+ fputs("Task ID may only contain digits.", stderr);
+ return 3;
+ }
+
+ /* needs to be set to make mock work properly */
+ setenv("SUDO_USER", "root", 1);
+ setenv("SUDO_UID", "0", 1);
+ setenv("SUDO_GID", "0", 1);
+
+ /* launch worker.py */
+ sprintf(command, "/usr/bin/python /usr/share/abrt-retrace/worker.py \"%s\"", argv[1]);
+ pipe = popen(command, "r");
+ if (pipe == NULL)
+ {
+ fputs("Unable to run 'worker.py'.", stderr);
+ return 4;
+ }
+
+ return pclose(pipe) >> 8;
+}
diff --git a/src/retrace/worker.py b/src/retrace/worker.py
new file mode 100755
index 00000000..4b56c992
--- /dev/null
+++ b/src/retrace/worker.py
@@ -0,0 +1,298 @@
+#!/usr/bin/python
+
+import sys
+import time
+from retrace import *
+
+LOG = None
+
+def retrace_run(errorcode, cmd):
+ "Runs cmd using subprocess.Popen and kills script with errorcode on failure"
+ try:
+ process = Popen(cmd, stdout=PIPE, stderr=STDOUT)
+ process.wait()
+ output = process.stdout.read()
+ process.stdout.close()
+ except Exception as ex:
+ process = None
+ output = "An unhandled exception occured: %s" % ex
+
+ if not process or process.returncode != 0:
+ LOG.write("Error %d:\n=== OUTPUT ===\n%s\n" % (errorcode, output))
+ LOG.close()
+ sys.exit(errorcode)
+
+ return output
+
+if __name__ == "__main__":
+ starttime = time.time()
+
+ if len(sys.argv) != 2:
+ sys.stderr.write("Usage: %s task_id\n" % sys.argv[0])
+ sys.exit(11)
+
+ taskid = sys.argv[1]
+ try:
+ int(taskid)
+ except:
+ sys.stderr.write("Task ID may only contain digits.\n")
+ sys.exit(12)
+
+ savedir = workdir = "%s/%s" % (CONFIG["SaveDir"], taskid)
+
+ if CONFIG["UseWorkDir"]:
+ workdir = "%s/%s" % (CONFIG["WorkDir"], taskid)
+
+ if not os.path.isdir(savedir):
+ sys.stderr.write("Task '%s' does not exist.\n" % workdir)
+ sys.exit(13)
+
+ try:
+ LOG = logger(taskid)
+ except Exception as ex:
+ sys.stderr.write("Unable to start logging for task '%s': %s.\n" % (taskid, ex))
+ sys.exit(14)
+
+ # check the crash directory for required files
+ for required_file in REQUIRED_FILES:
+ if not os.path.isfile("%s/crash/%s" % (savedir, required_file)):
+ LOG.write("Crash directory does not contain required file '%s'.\n" % required_file)
+ LOG.close()
+ sys.exit(15)
+
+ # read architecture file
+ try:
+ arch_file = open("%s/crash/architecture" % savedir, "r")
+ arch = repoarch = arch_file.read()
+ arch_file.close()
+ except Exception as ex:
+ LOG.write("Unable to read architecture from 'architecture' file: %s.\n" % ex)
+ LOG.close()
+ sys.exit(16)
+
+ # required hack for public repos
+ if arch == "i686":
+ repoarch = "i386"
+
+ # read release, distribution and version from release file
+ try:
+ release_file = open("%s/crash/release" % savedir, "r")
+ release = release_file.read()
+ release_file.close()
+ except Exception as ex:
+ LOG.write("Unable to read distribution and version from 'release' file: %s.\n" % ex)
+ LOG.close()
+ sys.exit(17)
+
+ version = distribution = None
+ for distro in RELEASE_PARSERS.keys():
+ match = RELEASE_PARSERS[distro].match(release)
+ if match:
+ version = match.group(1)
+ distribution = distro
+ break
+
+ if not version or not distribution:
+ LOG.write("Release '%s' is not supported.\n" % release)
+ LOG.close()
+ sys.exit(18)
+
+ # read package file
+ try:
+ package_file = open("%s/crash/package" % savedir, "r")
+ crash_package = package_file.read()
+ package_file.close()
+ except Exception as ex:
+ LOG.write("Unable to read crash package from 'package' file: %s.\n" % ex)
+ LOG.close()
+ sys.exit(19)
+
+ # read required packages from coredump
+ packages = "%s.%s" % (crash_package, arch)
+ try:
+ # ToDo: deal with not found build-ids
+ pipe = Popen(["/usr/share/abrt-retrace/coredump2packages.py", "%s/crash/coredump" % savedir, "--repos=retrace-%s-%s-%s*" % (distribution, version, arch)], stdout=PIPE).stdout
+ section = 0
+ crash_package_or_component = None
+ for line in pipe.readlines():
+ if line == "\n":
+ section += 1
+ continue
+ elif 0 == section:
+ crash_package_or_component = line.strip()
+ elif 1 == section:
+ packages += " %s" % line.rstrip("\n")
+ elif 2 == section:
+ # Missing build ids
+ pass
+ pipe.close()
+ except Exception as ex:
+ LOG.write("Unable to obtain packages from 'coredump' file: %s.\n" % ex)
+ LOG.close()
+ sys.exit(20)
+
+ # create mock config file
+ try:
+ mockcfg = open("%s/mock.cfg" % savedir, "w")
+ mockcfg.write("config_opts['root'] = 'chroot'\n")
+ mockcfg.write("config_opts['target_arch'] = '%s'\n" % arch)
+ mockcfg.write("config_opts['chroot_setup_cmd'] = 'install %s shadow-utils abrt-addon-ccpp gdb'\n" % packages)
+ mockcfg.write("config_opts['basedir'] = '%s'\n" % workdir)
+ mockcfg.write("config_opts['plugin_conf']['ccache_enable'] = False\n")
+ mockcfg.write("config_opts['plugin_conf']['yum_cache_enable'] = False\n")
+ mockcfg.write("config_opts['plugin_conf']['root_cache_enable'] = False\n")
+ mockcfg.write("\n")
+ mockcfg.write("config_opts['yum.conf'] = \"\"\"\n")
+ mockcfg.write("[main]\n")
+ mockcfg.write("cachedir=/var/cache/yum\n")
+ mockcfg.write("debuglevel=1\n")
+ mockcfg.write("reposdir=/dev/null\n")
+ mockcfg.write("logfile=/var/log/yum.log\n")
+ mockcfg.write("retries=20\n")
+ mockcfg.write("obsoletes=1\n")
+ mockcfg.write("gpgcheck=0\n")
+ mockcfg.write("assumeyes=1\n")
+ mockcfg.write("syslog_ident=mock\n")
+ mockcfg.write("syslog_device=\n")
+ mockcfg.write("\n")
+ mockcfg.write("#repos\n")
+ mockcfg.write("\n")
+ mockcfg.write("[fedora]\n")
+ mockcfg.write("name=fedora\n")
+ mockcfg.write("baseurl=file://%s/%s-%s-%s/\n" % (CONFIG["RepoDir"], distribution, version, arch))
+ mockcfg.write("failovermethod=priority\n")
+ mockcfg.write("\n")
+ mockcfg.write("[fedora-debuginfo]\n")
+ mockcfg.write("name=fedora-debuginfo\n")
+ mockcfg.write("baseurl=file://%s/%s-%s-%s-debuginfo/\n" % (CONFIG["RepoDir"], distribution, version, arch))
+ mockcfg.write("failovermethod=priority\n")
+ mockcfg.write("\n")
+ mockcfg.write("[updates]\n")
+ mockcfg.write("name=updates\n")
+ mockcfg.write("baseurl=file://%s/%s-%s-%s-updates/\n" % (CONFIG["RepoDir"], distribution, version, arch))
+ mockcfg.write("failovermethod=priority\n")
+ mockcfg.write("\n")
+ mockcfg.write("[updates-debuginfo]\n")
+ mockcfg.write("name=updates-debuginfo\n")
+ mockcfg.write("baseurl=file://%s/%s-%s-%s-updates-debuginfo/\n" % (CONFIG["RepoDir"], distribution, version, arch))
+ mockcfg.write("failovermethod=priority\n")
+ mockcfg.write("\n")
+ mockcfg.write("[updates-testing]\n")
+ mockcfg.write("name=updates-testing\n")
+ mockcfg.write("baseurl=file://%s/%s-%s-%s-updates-testing/\n" % (CONFIG["RepoDir"], distribution, version, arch))
+ mockcfg.write("failovermethod=priority\n")
+ mockcfg.write("\n")
+ mockcfg.write("[updates-testing-debuginfo]\n")
+ mockcfg.write("name=updates-testing-debuginfo\n")
+ mockcfg.write("baseurl=file://%s/%s-%s-%s-updates-testing-debuginfo/\n" % (CONFIG["RepoDir"], distribution, version, arch))
+ mockcfg.write("failovermethod=priority\n")
+ mockcfg.write("\n")
+ # custom ABRT repo with ABRT 2.0 binaries - obsolete after release of ABRT 2.0
+ mockcfg.write("[abrt]\n")
+ mockcfg.write("name=abrt\n")
+ mockcfg.write("baseurl=http://repos.fedorapeople.org/repos/mtoman/abrt20/%s-%s/%s/\n" % (distribution, version, repoarch))
+ mockcfg.write("failovermethod=priority\n")
+ mockcfg.write("\n")
+ mockcfg.write("\"\"\"\n")
+ mockcfg.close()
+ except Exception as ex:
+ LOG.write("Unable to create mock config file: %s.\n" % ex)
+ LOG.close()
+ sys.exit(21)
+
+ # get count of tasks running before starting
+ prerunning = len(get_active_tasks()) - 1
+
+ # run retrace
+ mockr = "../../%s/mock" % savedir
+
+ LOG.write("Initializing virtual root... ")
+
+ retrace_run(25, ["mock", "init", "-r", mockr])
+ retrace_run(26, ["mock", "-r", mockr, "--copyin", "%s/crash" % savedir, "/var/spool/abrt/crash"])
+ retrace_run(27, ["touch", "%s/chroot/root/var/spool/abrt/crash/time" % workdir])
+
+ LOG.write("OK\n")
+
+ try:
+ rootfile = open("%s/chroot/result/root.log" % workdir, "r")
+ rootlog = rootfile.read()
+ rootfile.close()
+ except Exception as ex:
+ LOG.write("Error reading root log: %s.\n" % ex)
+ rootlog = "Not found"
+
+ # generate backtrace
+ LOG.write("Generating backtrace... ")
+
+ retrace_run(28, ["mock", "shell", "-r", mockr, "--", "/usr/bin/abrt-action-generate-backtrace", "-d", "/var/spool/abrt/crash/"])
+ retrace_run(29, ["mock", "-r", mockr, "--copyout", "/var/spool/abrt/crash/backtrace", savedir])
+ retrace_run(30, ["chmod", "a+r", "%s/backtrace" % savedir])
+
+ LOG.write("OK\n")
+
+ chroot_size = dir_size("%s/chroot/root" % workdir)
+
+ # clean up temporary data
+ LOG.write("Cleaning up... ")
+
+ retrace_run(31, ["mock", "-r", mockr, "--scrub=all"])
+ retrace_run(32, ["rm", "-rf", "%s/mock.cfg" % savedir, "%s/crash" % savedir])
+
+ # ignore error: workdir = savedir => workdir is not empty
+ if CONFIG["UseWorkDir"]:
+ try:
+ os.rmdir(workdir)
+ except:
+ pass
+
+ LOG.write("OK\n")
+
+ # save crash statistics
+ LOG.write("Saving crash statistics... ")
+
+ duration = int(time.time() - starttime)
+
+ package_match = PACKAGE_PARSER.match(crash_package)
+ if not package_match:
+ package = crash_package
+ version = "unknown"
+ release = "unknown"
+ else:
+ package = package_match.group(1)
+ version = package_match.group(2)
+ release = package_match.group(4)
+
+ crashstats = {
+ "taskid": int(taskid),
+ "package": package,
+ "version": version,
+ "release": release,
+ "arch": arch,
+ "starttime": int(starttime),
+ "duration": duration,
+ "prerunning": prerunning,
+ "postrunning": len(get_active_tasks()) - 1,
+ "chrootsize": chroot_size
+ }
+
+ if not init_crashstats_db() or not save_crashstats(crashstats):
+ LOG.write("Error: %s\n" % crashstats)
+ else:
+ LOG.write("OK\n")
+
+ # publish bactkrace and log
+ LOG.write("Finishing task... ")
+
+ try:
+ os.rename("%s/backtrace" % savedir, "%s/retrace_backtrace" % savedir)
+ except Exception as ex:
+ LOG.write("Error: %s\n" % ex)
+ LOG.close()
+ sys.exit(35)
+
+ LOG.write("OK\n")
+ LOG.write("Retrace took %d seconds.\n" % duration)
+
+ LOG.write("\n=== ROOT LOG ===\n%s\n" % rootlog)
+ LOG.close()