|
Packit |
8ea169 |
#!/usr/bin/python3 -u
|
|
Packit |
8ea169 |
# Called by abrtd when a new file is noticed in upload directory.
|
|
Packit |
8ea169 |
# The task of this script is to unpack the file and move
|
|
Packit |
8ea169 |
# problem data found in it to abrtd spool directory.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
import sys
|
|
Packit |
8ea169 |
import stat
|
|
Packit |
8ea169 |
import os
|
|
Packit |
8ea169 |
import getopt
|
|
Packit |
8ea169 |
import tempfile
|
|
Packit |
8ea169 |
import shutil
|
|
Packit |
8ea169 |
import datetime
|
|
Packit |
8ea169 |
import grp
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
from reportclient import set_verbosity, error_msg_and_die, error_msg, log_warning
|
|
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 |
|
|
Packit |
8ea169 |
import problem
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
def write_bytes_to(filename, b, uid, gid, mode):
|
|
Packit |
8ea169 |
fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, mode)
|
|
Packit |
8ea169 |
if fd >= 0:
|
|
Packit |
8ea169 |
os.fchown(fd, uid, gid)
|
|
Packit |
8ea169 |
os.write(fd, b)
|
|
Packit |
8ea169 |
os.close(fd)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
def validate_transform_move_and_notify(uploaded_dir_path, problem_dir_path, dest=None):
|
|
Packit |
8ea169 |
fsuid = 0
|
|
Packit |
8ea169 |
fsgid = 0
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
gabrt = grp.getgrnam("abrt")
|
|
Packit |
8ea169 |
fsgid = gabrt.gr_gid
|
|
Packit |
8ea169 |
except KeyError as ex:
|
|
Packit |
8ea169 |
error_msg("Failed to get GID of 'abrt' (using 0 instead): {0}'".format(str(ex)))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
# give the uploaded directory to 'root:abrt' or 'root:root'
|
|
Packit |
8ea169 |
os.chown(uploaded_dir_path, fsuid, fsgid)
|
|
Packit |
8ea169 |
# set the right permissions for this machine
|
|
Packit |
8ea169 |
# (allow the owner and the group to access problem elements,
|
|
Packit |
8ea169 |
# the default dump dir mode lacks x bit for both)
|
|
Packit |
8ea169 |
os.chmod(uploaded_dir_path, @DEFAULT_DUMP_DIR_MODE@ | stat.S_IXUSR | stat.S_IXGRP)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# sanitize problem elements
|
|
Packit |
8ea169 |
for item in os.listdir(uploaded_dir_path):
|
|
Packit |
8ea169 |
apath = os.path.join(uploaded_dir_path, item)
|
|
Packit |
8ea169 |
if os.path.islink(apath):
|
|
Packit |
8ea169 |
# remove symbolic links
|
|
Packit |
8ea169 |
os.remove(apath)
|
|
Packit |
8ea169 |
elif os.path.isdir(apath):
|
|
Packit |
8ea169 |
# remove directories
|
|
Packit |
8ea169 |
shutil.rmtree(apath)
|
|
Packit |
8ea169 |
elif os.path.isfile(apath):
|
|
Packit |
8ea169 |
# set file ownership to 'root:abrt' or 'root:root'
|
|
Packit |
8ea169 |
os.chown(apath, fsuid, fsgid)
|
|
Packit |
8ea169 |
# set the right file permissions for this machine
|
|
Packit |
8ea169 |
os.chmod(apath, @DEFAULT_DUMP_DIR_MODE@)
|
|
Packit |
8ea169 |
else:
|
|
Packit |
8ea169 |
# remove things that are neither files, symlinks nor directories
|
|
Packit |
8ea169 |
os.remove(apath)
|
|
Packit |
8ea169 |
except OSError as ex:
|
|
Packit |
8ea169 |
error_msg("Removing uploaded dir '{0}': '{1}'".format(uploaded_dir_path, str(ex)))
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
shutil.rmtree(uploaded_dir_path)
|
|
Packit |
8ea169 |
except OSError as ex2:
|
|
Packit |
8ea169 |
error_msg_and_die("Failed to clean up dir '{0}': '{1}'".format(uploaded_dir_path, str(ex2)))
|
|
Packit |
8ea169 |
return
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# overwrite remote if it exists
|
|
Packit |
8ea169 |
remote_path = os.path.join(uploaded_dir_path, "remote")
|
|
Packit |
8ea169 |
write_bytes_to(remote_path, b"1", fsuid, fsgid, @DEFAULT_DUMP_DIR_MODE@)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# abrtd would increment count value and abrt-server refuses to process
|
|
Packit |
8ea169 |
# problem directories containing 'count' element when PrivateReports is on.
|
|
Packit |
8ea169 |
count_path = os.path.join(uploaded_dir_path, "count")
|
|
Packit |
8ea169 |
if os.path.exists(count_path):
|
|
Packit |
8ea169 |
# overwrite remote_count if it exists
|
|
Packit |
8ea169 |
remote_count_path = os.path.join(uploaded_dir_path, "remote_count")
|
|
Packit |
8ea169 |
os.rename(count_path, remote_count_path)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if not dest:
|
|
Packit |
8ea169 |
dest = problem_dir_path
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
shutil.move(uploaded_dir_path, dest)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
problem.notify_new_path(problem_dir_path)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if __name__ == "__main__":
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# Helper: exit with cleanup
|
|
Packit |
8ea169 |
die_exitcode = 1
|
|
Packit |
8ea169 |
delete_on_exit = None
|
|
Packit |
8ea169 |
def print_clean_and_die(fmt, *args):
|
|
Packit |
8ea169 |
sys.stderr.write("%s\n" % (fmt % args))
|
|
Packit |
8ea169 |
if delete_on_exit:
|
|
Packit |
8ea169 |
shutil.rmtree(delete_on_exit, True) # True: ignore_errors
|
|
Packit |
8ea169 |
sys.exit(die_exitcode)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# localization
|
|
Packit |
8ea169 |
init_gettext()
|
|
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 |
progname = os.path.basename(sys.argv[0])
|
|
Packit |
8ea169 |
help_text = _(
|
|
Packit |
8ea169 |
"Usage: %s [-vd] ABRT_SPOOL_DIR UPLOAD_DIR FILENAME"
|
|
Packit |
8ea169 |
"\n"
|
|
Packit |
8ea169 |
"\n -v - Verbose"
|
|
Packit |
8ea169 |
"\n -d - Delete uploaded archive"
|
|
Packit |
8ea169 |
"\n ABRT_SPOOL_DIR - Directory where valid uploaded archives are unpacked to"
|
|
Packit |
8ea169 |
"\n UPLOAD_DIR - Directory where uploaded archives are stored"
|
|
Packit |
8ea169 |
"\n FILENAME - Uploaded archive file name"
|
|
Packit |
8ea169 |
"\n"
|
|
Packit |
8ea169 |
) % progname
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
opts, args = getopt.getopt(sys.argv[1:], "vdh", ["help"])
|
|
Packit |
8ea169 |
except getopt.GetoptError as err:
|
|
Packit |
8ea169 |
error_msg(err) # prints something like "option -a not recognized"
|
|
Packit |
8ea169 |
error_msg_and_die(help_text)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
delete_archive = False
|
|
Packit |
8ea169 |
for opt, arg in opts:
|
|
Packit |
8ea169 |
if opt in ("-h", "--help"):
|
|
Packit |
8ea169 |
print(help_text)
|
|
Packit |
8ea169 |
sys.exit(0)
|
|
Packit |
8ea169 |
if opt == "-v":
|
|
Packit |
8ea169 |
verbose += 1
|
|
Packit |
8ea169 |
if opt == "-d":
|
|
Packit |
8ea169 |
delete_archive = True
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
set_verbosity(verbose)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if len(args) < 3:
|
|
Packit |
8ea169 |
error_msg_and_die(help_text)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_dir = args[0]
|
|
Packit |
8ea169 |
upload_dir = args[1]
|
|
Packit |
8ea169 |
archive = args[2]
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if not os.path.isdir(abrt_dir):
|
|
Packit |
8ea169 |
error_msg_and_die(_("Not a directory: '{0}'").format(abrt_dir))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if not os.path.isdir(upload_dir):
|
|
Packit |
8ea169 |
error_msg_and_die(_("Not a directory: '{0}'").format(upload_dir))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if archive[0] == "/":
|
|
Packit |
8ea169 |
error_msg_and_die(_("Skipping: '{0}' (starts with slash)").format(archive))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if archive[0] == ".":
|
|
Packit |
8ea169 |
error_msg_and_die(_("Skipping: '{0}' (starts with dot)").format(archive))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if ".." in archive:
|
|
Packit |
8ea169 |
error_msg_and_die(_("Skipping: '{0}' (contains ..)").format(archive))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if " " in archive:
|
|
Packit |
8ea169 |
error_msg_and_die(_("Skipping: '{0}' (contains space)").format(archive))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if "\t" in archive:
|
|
Packit |
8ea169 |
error_msg_and_die(_("Skipping: '{0}' (contains tab)").format(archive))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
os.chdir(upload_dir)
|
|
Packit |
8ea169 |
except OSError:
|
|
Packit |
8ea169 |
error_msg_and_die(_("Can't change directory to '{0}'").format(upload_dir))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if archive.endswith(".tar.gz"):
|
|
Packit |
8ea169 |
unpacker = "gunzip"
|
|
Packit |
8ea169 |
elif archive.endswith(".tgz"):
|
|
Packit |
8ea169 |
unpacker = "gunzip"
|
|
Packit |
8ea169 |
elif archive.endswith(".tar.bz2"):
|
|
Packit |
8ea169 |
unpacker = "bunzip2"
|
|
Packit |
8ea169 |
elif archive.endswith(".tar.xz"):
|
|
Packit |
8ea169 |
unpacker = "unxz"
|
|
Packit |
8ea169 |
else:
|
|
Packit |
8ea169 |
error_msg_and_die(_("Unknown file type: '{0}'").format(archive))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
working_dir = tempfile.mkdtemp(prefix="abrt_handle_upload.", dir="@LARGE_DATA_TMP_DIR@")
|
|
Packit |
8ea169 |
except OSError:
|
|
Packit |
8ea169 |
error_msg_and_die(_("Can't create working directory in '{0}'").format("@LARGE_DATA_TMP_DIR@"))
|
|
Packit |
8ea169 |
delete_on_exit = working_dir
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
tempdir = working_dir + "/remote." + datetime.datetime.now().strftime("%Y-%m-%d-%H:%M:%S.%f.") + str(os.getpid())
|
|
Packit |
8ea169 |
working_archive = working_dir + "/" + archive
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if delete_archive:
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
shutil.move(archive, working_archive)
|
|
Packit |
8ea169 |
except IOError:
|
|
Packit |
8ea169 |
print_clean_and_die(_("Can't move '{0}' to '{1}'").format(archive, working_archive))
|
|
Packit |
8ea169 |
else:
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
shutil.copy(archive, working_archive)
|
|
Packit |
8ea169 |
except IOError:
|
|
Packit |
8ea169 |
print_clean_and_die(_("Can't copy '{0}' to '{1}'").format(archive, working_archive))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
ex = os.spawnlp(os.P_WAIT, unpacker, unpacker, "-t", "--", working_archive)
|
|
Packit |
8ea169 |
if ex != 0:
|
|
Packit |
8ea169 |
print_clean_and_die(_("Verification error on '{0}'").format(archive))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_warning(_("Unpacking '{0}'").format(archive))
|
|
Packit |
8ea169 |
try:
|
|
Packit |
8ea169 |
os.mkdir(tempdir)
|
|
Packit |
8ea169 |
except OSError:
|
|
Packit |
8ea169 |
print_clean_and_die(_("Can't create '{0}' directory").format(tempdir))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
ex = os.system(unpacker+" <"+working_archive+" | tar xf - -C "+tempdir)
|
|
Packit |
8ea169 |
if ex != 0:
|
|
Packit |
8ea169 |
print_clean_and_die(_("Can't unpack '{0}'").format(archive))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
# The archive can contain either plain dump files
|
|
Packit |
8ea169 |
# or one or more complete problem data directories.
|
|
Packit |
8ea169 |
# Checking second possibility first.
|
|
Packit |
8ea169 |
if (os.path.exists(tempdir+"/analyzer") or os.path.exists(tempdir+"/type")) and os.path.exists(tempdir+"/time"):
|
|
Packit |
8ea169 |
validate_transform_move_and_notify(tempdir, abrt_dir+"/"+os.path.basename(tempdir), dest=abrt_dir)
|
|
Packit |
8ea169 |
else:
|
|
Packit |
8ea169 |
for d in os.listdir(tempdir):
|
|
Packit |
8ea169 |
if not os.path.isdir(tempdir+"/"+d):
|
|
Packit |
8ea169 |
continue
|
|
Packit |
8ea169 |
dst = abrt_dir+"/"+d
|
|
Packit |
8ea169 |
if os.path.exists(dst):
|
|
Packit |
8ea169 |
dst += "."+str(os.getpid())
|
|
Packit |
8ea169 |
if os.path.exists(dst):
|
|
Packit |
8ea169 |
continue
|
|
Packit |
8ea169 |
validate_transform_move_and_notify(tempdir+"/"+d, dst)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
die_exitcode = 0
|
|
Packit |
8ea169 |
# This deletes working_dir (== delete_on_exit)
|
|
Packit |
8ea169 |
print_clean_and_die(_("'{0}' processed successfully").format(archive))
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
except:
|
|
Packit |
8ea169 |
if delete_on_exit:
|
|
Packit |
8ea169 |
shutil.rmtree(delete_on_exit, True) # True: ignore_errors
|
|
Packit |
8ea169 |
raise
|