# Copyright 1999-2020 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import logging from _emerge.SpawnProcess import SpawnProcess import portage from portage.localization import _ from portage.util.compression_probe import ( compression_probe, _compressors, ) from portage.util.futures.compat_coroutine import coroutine from portage.process import find_binary from portage.util import ( shlex_split, varexpand, ) import signal import subprocess import tarfile class BinpkgExtractorAsync(SpawnProcess): __slots__ = ("features", "image_dir", "pkg", "pkg_path") _shell_binary = portage.const.BASH_BINARY def _start(self): self.scheduler.run_until_complete(self._async_start()) @coroutine def _async_start(self): tar_options = "" if "xattr" in self.features: process = subprocess.Popen(["tar", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) output = process.communicate()[0] if b"--xattrs" in output: tar_options = ["--xattrs", "--xattrs-include='*'"] for x in portage.util.shlex_split(self.env.get("PORTAGE_XATTR_EXCLUDE", "")): tar_options.append(portage._shell_quote("--xattrs-exclude=%s" % x)) tar_options = " ".join(tar_options) decomp = _compressors.get(compression_probe(self.pkg_path)) if decomp is not None: decomp_cmd = decomp.get("decompress") elif tarfile.is_tarfile(portage._unicode_encode(self.pkg_path, encoding=portage._encodings['fs'], errors='strict')): decomp_cmd = 'cat' decomp = { 'compress': 'cat', 'package': 'sys-apps/coreutils', } else: decomp_cmd = None if decomp_cmd is None: self.scheduler.output("!!! %s\n" % _("File compression header unrecognized: %s") % self.pkg_path, log_path=self.logfile, background=self.background, level=logging.ERROR) self.returncode = 1 self._async_wait() return try: decompression_binary = shlex_split(varexpand(decomp_cmd, mydict=self.env))[0] except IndexError: decompression_binary = "" if find_binary(decompression_binary) is None: # Try alternative command if it exists if decomp.get("decompress_alt"): decomp_cmd = decomp.get("decompress_alt") try: decompression_binary = shlex_split(varexpand(decomp_cmd, mydict=self.env))[0] except IndexError: decompression_binary = "" if find_binary(decompression_binary) is None: missing_package = decomp.get("package") self.scheduler.output("!!! %s\n" % _("File compression unsupported %s.\n Command was: %s.\n Maybe missing package: %s") % (self.pkg_path, varexpand(decomp_cmd, mydict=self.env), missing_package), log_path=self.logfile, background=self.background, level=logging.ERROR) self.returncode = 1 self._async_wait() return pkg_xpak = portage.xpak.tbz2(self.pkg_path) pkg_xpak.scan() # SIGPIPE handling (128 + SIGPIPE) should be compatible with # assert_sigpipe_ok() that's used by the ebuild unpack() helper. self.args = [self._shell_binary, "-c", ("cmd0=(head -c %d -- %s) cmd1=(%s) cmd2=(tar -xp %s -C %s -f -); " + \ '"${cmd0[@]}" | "${cmd1[@]}" | "${cmd2[@]}"; ' + \ "p=(${PIPESTATUS[@]}) ; for i in {0..2}; do " + \ "if [[ ${p[$i]} != 0 && ${p[$i]} != %d ]] ; then " + \ "echo command $(eval \"echo \\\"'\\${cmd$i[*]}'\\\"\") " + \ "failed with status ${p[$i]} ; exit ${p[$i]} ; fi ; done; " + \ "if [ ${p[$i]} != 0 ] ; then " + \ "echo command $(eval \"echo \\\"'\\${cmd$i[*]}'\\\"\") " + \ "failed with status ${p[$i]} ; exit ${p[$i]} ; fi ; " + \ "exit 0 ;") % \ (pkg_xpak.filestat.st_size - pkg_xpak.xpaksize, portage._shell_quote(self.pkg_path), decomp_cmd, tar_options, portage._shell_quote(self.image_dir), 128 + signal.SIGPIPE)] yield SpawnProcess._async_start(self)