This closes another unfixed detail of issue #657 "divide abrtd into two parts to get rid of inotify"
Signed-off-by: Denys Vlasenko dvlasenk@redhat.com --- src/daemon/abrt-handle-upload.in | 267 ++++++++++++++++++++++++++------------- 1 file changed, 180 insertions(+), 87 deletions(-)
diff --git a/src/daemon/abrt-handle-upload.in b/src/daemon/abrt-handle-upload.in index e727db6..e258c3b 100755 --- a/src/daemon/abrt-handle-upload.in +++ b/src/daemon/abrt-handle-upload.in @@ -1,90 +1,183 @@ -#!/bin/sh +#!/usr/bin/python -u # Called by abrtd when a new file is noticed in upload directory. # The task of this script is to unpack the file and move # problem data found in it to abrtd spool directory. -# -# Usage: abrt-handle-upload [-d] ABRT_SPOOL_DIR UPLOAD_DIR FILENAME -# -# -d - deletes an uploaded archive -# ABRT_SPOOL_DIR - a directory where valid uploaded archives are unpacked to -# UPLOAD_DIR - a directory where uploaded archives are stored in -# FILENAME - an uploaded archive file name - -#echo "Started: $0 $*" - -print_clean_and_die() -{ - printf "%s\n" "$*" - #echo delete_on_exit="$delete_on_exit" - test "$delete_on_exit" && rm -rf -- $delete_on_exit - exit $die_exitcode -} - -die_exitcode=1 -delete_on_exit="" - -delete_archive=false -if test x"$1" == x"-d"; then delete_archive=true; shift; fi - -abrt_dir="$1" -upload_dir="$2" -archive="$3" - -test -d "$abrt_dir" || print_clean_and_die "Not a directory: '$abrt_dir'" -test -d "$upload_dir" || print_clean_and_die "Not a directory: '$upload_dir'" -test x"${archive#/}" != x"$archive" && print_clean_and_die "Skipping: '$archive' (starts with slash)" -test x"${archive#.}" != x"$archive" && print_clean_and_die "Skipping: '$archive' (starts with dot)" -test x"${archive#*..}" != x"$archive" && print_clean_and_die "Skipping: '$archive' (contains ..)" -test x"${archive#* }" != x"$archive" && print_clean_and_die "Skipping: '$archive' (contains space)" -# Note: next line has a tab! -test x"${archive#* }" != x"$archive" && print_clean_and_die "Skipping: '$archive' (contains tab)" - -cd -- "$upload_dir" || print_clean_and_die "Can't chdir to '$upload_dir'" - -unpacker="" -test x"${archive%.tar.gz}" != x"$archive" && unpacker="gunzip" -test x"${archive%.tar.bz2}" != x"$archive" && unpacker="bunzip2" -test x"${archive%.tar.xz}" != x"$archive" && unpacker="unxz" - -test "$unpacker" || print_clean_and_die "Unknown file type: '$archive'" - -workingdir=`mktemp -d "abrt_handle_upload.XXXXXXXXXX" --tmpdir=@LARGE_DATA_TMP_DIR@` || print_clean_and_die "Can't create working directory" -delete_on_exit="$workingdir" - -tempdir="$workingdir/remote.`date +%Y-%m-%d-%H:%M:%S.%N`.$$" -working_archive="$workingdir/$archive" - -if $delete_archive; then - mv -- "$archive" "$working_archive" || print_clean_and_die "Can't move '$archive' to '$working_archive'" -else - cp -- "$archive" "$working_archive" || print_clean_and_die "Can't copy '$archive' to '$working_archive'" -fi - -$unpacker -t -- "$working_archive" || print_clean_and_die "Verification error on '$archive'" - -echo "Unpacking '$archive'" -mkdir "$tempdir" || print_clean_and_die "Can't create '$tempdir' directory" -$unpacker <"$working_archive" | tar xf - -C "$tempdir" || print_clean_and_die "Can't unpack '$archive'" - -# The archive can contain either plain dump files -# or one or more complete problem data directories. -# Checking second possibility first. -if test -f "$tempdir/analyzer" && test -f "$tempdir/time" && test -f "$tempdir/uid"; then - printf "1" >"$tempdir/remote" - mv -- "$tempdir" "$abrt_dir" -else - ( - cd -- "$tempdir" || exit - for d in *; do - test -d "$d" || continue - printf "1" >"$d/remote" - dst="$abrt_dir/$d" - test -e "$dst" && dst="$abrt_dir/$d.$$" - test -e "$dst" && continue - mv -- "$d" "$dst" - done - ) -fi - -die_exitcode=0 -print_clean_and_die "'$archive' processed successfully" + +import sys +import os +import getopt +import tempfile +import shutil +import datetime + +from reportclient import _, set_verbosity, error_msg_and_die, error_msg, log1, log +import problem + +def spawn_and_wait(prog): + return os.spawnlp(os.P_WAIT, prog, prog) + +def write_str_to(filename, s): + fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0666) + if fd >= 0: + os.write(fd, s) + os.close(fd) + +if __name__ == "__main__": + + # Helper: exit with cleanup + die_exitcode = 1 + delete_on_exit = None + def print_clean_and_die(fmt, *args): + sys.stderr.write("%s\n" % (fmt % args)) + if delete_on_exit: + shutil.rmtree(delete_on_exit, True) # True: ignore_errors + sys.exit(die_exitcode) + + # localization + #init_gettext() - done by reportclient module init + + verbose = 0 + ABRT_VERBOSE = os.getenv("ABRT_VERBOSE") + if ABRT_VERBOSE: + try: + verbose = int(ABRT_VERBOSE) + except: + pass + + progname = os.path.basename(sys.argv[0]) + help_text = _( + "Usage: %s [-vd] ABRT_SPOOL_DIR UPLOAD_DIR FILENAME" + "\n" + "\n -v - Verbose" + "\n -d - Delete uploaded archive" + "\n ABRT_SPOOL_DIR - Directory where valid uploaded archives are unpacked to" + "\n UPLOAD_DIR - Directory where uploaded archives are stored" + "\n FILENAME - Uploaded archive file name" + "\n" + ) % progname + + try: + opts, args = getopt.getopt(sys.argv[1:], "vdh", ["help"]) + except getopt.GetoptError as err: + error_msg(err) # prints something like "option -a not recognized" + error_msg_and_die(help_text) + + delete_archive = False + for opt, arg in opts: + if opt in ("-h", "--help"): + print help_text + sys.exit(0) + if opt == "-v": + verbose += 1 + if opt == "-d": + delete_archive = True + + set_verbosity(verbose) + + if len(args) < 3: + error_msg_and_die(help_text) + + abrt_dir = args[0] + upload_dir = args[1] + archive = args[2] + + if not os.path.isdir(abrt_dir): + error_msg_and_die("Not a directory: '%s'", abrt_dir) + + if not os.path.isdir(upload_dir): + error_msg_and_die("Not a directory: '%s'", upload_dir) + + if archive[0] == "/": + error_msg_and_die("Skipping: '%s' (starts with slash)", archive) + + if archive[0] == ".": + error_msg_and_die("Skipping: '%s' (starts with dot)", archive) + + if ".." in archive: + error_msg_and_die("Skipping: '%s' (contains ..)", archive) + + if " " in archive: + error_msg_and_die("Skipping: '%s' (contains space)", archive) + + if "\t" in archive: + error_msg_and_die("Skipping: '%s' (contains tab)", archive) + + try: + os.chdir(upload_dir) + except OSError: + error_msg_and_die("Can't change directory to '%s'", upload_dir) + + if archive.endswith(".tar.gz"): + unpacker = "gunzip" + elif archive.endswith(".tgz"): + unpacker = "gunzip" + elif archive.endswith(".tar.bz2"): + unpacker = "bunzip2" + elif archive.endswith(".tar.xz"): + unpacker = "unxz" + else: + error_msg_and_die("Unknown file type: '%s'", archive) + + try: + working_dir = tempfile.mkdtemp(prefix="abrt_handle_upload.", dir="@LARGE_DATA_TMP_DIR@") + except OSError: + error_msg_and_die("Can't create working directory in '%s'", "@LARGE_DATA_TMP_DIR@") + delete_on_exit = working_dir + + try: + tempdir = working_dir + "/remote." + datetime.datetime.now().strftime("%Y-%m-%d-%H:%M:%S.%f.") + str(os.getpid()) + working_archive = working_dir + "/" + archive + + if delete_archive: + try: + shutil.move(archive, working_archive) + except IOError: + print_clean_and_die("Can't move '%s' to '%s'", archive, working_archive) + else: + try: + shutil.copy(archive, working_archive) + except IOError: + print_clean_and_die("Can't copy '%s' to '%s'", archive, working_archive) + + ex = os.spawnlp(os.P_WAIT, unpacker, unpacker, "-t", "--", working_archive) + if ex != 0: + print_clean_and_die("Verification error on '%s'", archive) + + log("Unpacking '%s'", archive) + try: + os.mkdir(tempdir) + except OSError: + print_clean_and_die("Can't create '%s' directory", tempdir) + + ex = os.system(unpacker+" <"+working_archive+" | tar xf - -C "+tempdir) + if ex != 0: + print_clean_and_die("Can't unpack '%s'", archive) + + # The archive can contain either plain dump files + # or one or more complete problem data directories. + # Checking second possibility first. + if os.path.exists(tempdir+"/analyzer") and os.path.exists(tempdir+"/time"): + write_str_to(tempdir+"/remote", "1") + shutil.move(tempdir, abrt_dir) + problem.notify_new_path(abrt_dir+"/"+os.path.basename(tempdir)) + else: + for d in os.listdir(tempdir): + if not os.path.isdir(tempdir+"/"+d): + continue + write_str_to(tempdir+"/"+d+"/remote", "1") + dst = abrt_dir+"/"+d + if os.path.exists(dst): + dst += "."+str(os.getpid()) + if os.path.exists(dst): + continue + shutil.move(tempdir+"/"+d, dst) + problem.notify_new_path(dst) + + die_exitcode = 0 + # This deletes working_dir (== delete_on_exit) + print_clean_and_die("'%s' processed successfully", archive) + + except: + if delete_on_exit: + shutil.rmtree(delete_on_exit, True) # True: ignore_errors + raise