From: Michael Tremer Date: Wed, 24 Oct 2012 19:18:51 +0000 (+0200) Subject: Rewrite code that forks subprocesses. X-Git-Tag: 0.9.24~55 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=64ffcd37932a3fe0761df79bf4beb44dc54b9e11;p=pakfire.git Rewrite code that forks subprocesses. There is nothing too new here, but the old code has been cleaned up and been put into a shiny new class. :) --- diff --git a/python/pakfire/actions.py b/python/pakfire/actions.py index b8d0fb3e0..854198ca9 100644 --- a/python/pakfire/actions.py +++ b/python/pakfire/actions.py @@ -22,8 +22,8 @@ import os import sys -import chroot import packages +import shell import util import logging @@ -98,7 +98,7 @@ class Action(object): """ return self.pakfire.repos.local - def do(self, cmd, **kwargs): + def execute(self, command, **kwargs): # If we are running in /, we do not need to chroot there. chroot_path = None if not self.pakfire.path == "/": @@ -118,7 +118,6 @@ class Action(object): args = { "cwd" : cwd, - "logger" : log, "personality" : self.pakfire.distro.personality, "shell" : False, "timeout" : SCRIPTLET_TIMEOUT, @@ -128,11 +127,11 @@ class Action(object): args.update(kwargs) # You can never overwrite chrootPath. - args.update({ - "chrootPath" : chroot_path, - }) + args["chroot_path"] = chroot_path - return chroot.do(cmd, **args) + # Execute command. + shellenv = shell.ShellExecuteEnvironment(command, **args) + shellenv.execute() class ActionScript(Action): @@ -248,9 +247,9 @@ class ActionScript(Action): command = [script_file_chroot] + self.args try: - self.do(command) + self.execute(command) - except Error, e: + except ShellEnvironmentError, e: raise ActionError, _("The scriptlet returned an error:\n%s" % e) except commandTimeoutExpired: @@ -398,7 +397,7 @@ class ActionInstall(Action): ldconfig = os.path.join(self.pakfire.path, LDCONFIG[1:]) if os.path.exists(ldconfig) and os.access(ldconfig, os.X_OK): - self.do(LDCONFIG) + self.execute(LDCONFIG) else: log.debug("ldconfig is not present or not executable.") diff --git a/python/pakfire/builder.py b/python/pakfire/builder.py index a766f166c..ea74f7da1 100644 --- a/python/pakfire/builder.py +++ b/python/pakfire/builder.py @@ -33,12 +33,12 @@ import uuid import base import cgroup -import chroot import logger import packages import packages.file import packages.packager import repository +import shell import util import _pakfire @@ -204,6 +204,7 @@ class BuildEnviron(object): "enable_ccache" : True, "enable_icecream" : False, "sign_packages" : False, + "buildroot_tmpfs" : False, } #self.settings.update(settings) @@ -271,6 +272,14 @@ class BuildEnviron(object): """ return self.distro.arch + @property + def personality(self): + """ + Gets the personality from the distribution configuration. + """ + if self.distro: + return self.distro.personality + @property def info(self): return { @@ -528,9 +537,7 @@ class BuildEnviron(object): if not os.path.exists(mountpoint): os.makedirs(mountpoint) - cmd = "mount -n -t %s %s %s %s" % \ - (fs, options, src, mountpoint) - chroot.do(cmd, shell=True) + self.execute_root("mount -n -t %s %s %s %s" % (fs, options, src, mountpoint), shell=True) def _umountall(self): self.log.debug("Umounting environment") @@ -543,16 +550,20 @@ class BuildEnviron(object): for dest in mountpoints: mountpoint = self.chrootPath(dest) - chroot.do("umount -n %s" % mountpoint, raiseExc=0, shell=True) + try: + self.execute_root("umount -n %s" % mountpoint, shell=True) + except ShellEnvironmentError: + pass @property def mountpoints(self): mountpoints = [] - # Make root as a tmpfs. - #mountpoints += [ - # ("pakfire_root", "/", "tmpfs", "defaults"), - #] + # Make root as a tmpfs if enabled. + if self.settings.get("buildroot_tmpfs"): + mountpoints += [ + ("pakfire_root", "/", "tmpfs", "defaults"), + ] mountpoints += [ # src, dest, fs, options @@ -669,9 +680,14 @@ class BuildEnviron(object): f.write("\n".join(conf)) f.close() - def do(self, command, shell=True, personality=None, logger=None, *args, **kwargs): - ret = None + @property + def pkg_makefile(self): + return os.path.join(self.build_dir, "%s.%s" % (self.pkg.name, MAKEFILE_EXTENSION)) + def execute(self, command, logger=None, **kwargs): + """ + Executes the given command in the build chroot. + """ # Environment variables env = self.environ @@ -682,42 +698,42 @@ class BuildEnviron(object): for k, v in sorted(env.items()): self.log.debug(" %s=%s" % (k, v)) - # Update personality it none was set - if not personality: - personality = self.distro.personality - # Make every shell to a login shell because we set a lot of # environment things there. - if shell: - command = ["bash", "--login", "-c", command] - - if not kwargs.has_key("chrootPath"): - kwargs["chrootPath"] = self.chrootPath() - - if not kwargs.has_key("cgroup"): - kwargs["cgroup"] = self.cgroup - - ret = chroot.do( - command, - personality=personality, - shell=False, - env=env, - logger=logger, - *args, - **kwargs - ) + command = ["bash", "--login", "-c", command] + + args = { + "chroot_path" : self.chrootPath(), + "cgroup" : self.cgroup, + "env" : env, + "logger" : logger, + "personality" : self.personality, + "shell" : False, + } + args.update(kwargs) - return ret + # Run the shit. + shellenv = shell.ShellExecuteEnvironment(command, **args) + shellenv.execute() + + return shellenv + + def execute_root(self, command, **kwargs): + """ + Executes the given command outside the build chroot. + """ + shellenv = shell.ShellExecuteEnvironment(command, **kwargs) + shellenv.execute() + + return shellenv def build(self, install_test=True, prepare=False): if not self.pkg: raise BuildError, _("You cannot run a build when no package was given.") # Search for the package file in build_dir and raise BuildError if it is not present. - pkgfile = os.path.join(self.build_dir, "%s.%s" % (self.pkg.name, MAKEFILE_EXTENSION)) - if not os.path.exists(pkgfile): - raise BuildError, _("Could not find makefile in build root: %s") % pkgfile - pkgfile = "/%s" % os.path.relpath(pkgfile, self.chrootPath()) + if not os.path.exists(self.pkg_makefile): + raise BuildError, _("Could not find makefile in build root: %s") % self.pkg_makefile # Write pakfire configuration into the chroot. self.write_config() @@ -727,41 +743,49 @@ class BuildEnviron(object): "/usr/lib/pakfire/builder", "--offline", "build", - pkgfile, + "/%s" % os.path.relpath(self.pkg_makefile, self.chrootPath()), "--arch", self.arch, "--nodeps", "--resultdir=/result", ] + build_command = " ".join(build_command) + # Check if only the preparation stage should be run. if prepare: build_command.append("--prepare") + error = False try: - self.do(" ".join(build_command), logger=self.log) + self.execute(build_command, logger=self.log) # Perform the install test after the actual build. if install_test and not prepare: self.install_test() - except Error: + except ShellEnvironmentError: + error = True + self.log.error(_("Build failed")) + + # Catch all other errors. + except: + error = True self.log.error(_("Build failed."), exc_info=True) - raise BuildError, _("The build command failed. See logfile for details.") + else: + # Don't sign packages in prepare mode. + if prepare: + return - # Don't sign packages in prepare mode. - if prepare: - return + # Sign all built packages with the host key (if available). + self.sign_packages() - # Sign all built packages with the host key (if available). - if self.settings.get("sign_packages"): - host_key = self.keyring.get_host_key_id() - assert host_key + # Dump package information. + self.dump() - # Do the signing... - self.sign(host_key) + return - # Dump package information. - self.dump() + # End here in case of an error. + raise BuildError, _("The build command failed. See logfile for details.") def install_test(self): self.log.info(_("Running installation test...")) @@ -801,8 +825,14 @@ class BuildEnviron(object): shell = os.system(command) return os.WEXITSTATUS(shell) - def sign(self, keyfp): - assert self.keyring.get_key(keyfp), "Key for signing does not exist" + def sign_packages(self, keyfp=None): + # Do nothing if signing is not requested. + if not self.settings.get("sign_packages"): + return + + # Get key, that should be used for signing. + if not keyfp: + keyfp = self.keyring.get_host_key_id() # Find all files to process. files = self.find_result_packages() @@ -982,42 +1012,33 @@ class Builder(object): return environ - def do(self, command, shell=True, *args, **kwargs): - try: - logger = kwargs["logger"] - except KeyError: + def execute(self, command, logger=None, **kwargs): + if logger is None: logger = logging.getLogger("pakfire") - kwargs["logger"] = logger - - # Environment variables - log.debug("Environment:") - for k, v in sorted(self.environ.items()): - log.debug(" %s=%s" % (k, v)) - - # Update personality it none was set - if not kwargs.has_key("personality"): - kwargs["personality"] = self.distro.personality - - if not kwargs.has_key("cwd"): - kwargs["cwd"] = "/%s" % LOCAL_TMP_PATH # Make every shell to a login shell because we set a lot of # environment things there. - if shell: - command = ["bash", "--login", "-c", command] - kwargs["shell"] = False - - kwargs["env"] = self.environ + command = ["bash", "--login", "-c", command] + + args = { + "cwd" : "/%s" % LOCAL_TMP_PATH, + "env" : self.environ, + "logger" : logger, + "personality" : self.distro.personality, + "shell" : False, + } + args.update(kwargs) try: - return chroot.do(command, *args, **kwargs) - except Error: - if not logger: - logger = logging.getLogger("pakfire") + shellenv = shell.ShellExecuteEnvironment(command, **args) + shellenv.execute() + except ShellEnvironmentError: logger.error("Command exited with an error: %s" % command) raise + return shellenv + def run_script(self, script, *args): if not script.startswith("/"): script = os.path.join(SCRIPT_DIR, script) @@ -1031,15 +1052,24 @@ class Builder(object): # Returns the output of the command, but the output won't get # logged. - return self.do(cmd, returnOutput=True, logger=None) + exe = self.execute(cmd, record_output=True, log_output=False) + + # Return the output of the command. + if exe.exitcode == 0: + return exe.output def create_icecream_toolchain(self): try: - out = self.do("icecc --build-native 2>/dev/null", returnOutput=True, cwd="/tmp") - except Error: + exe = self.execute( + "icecc --build-native 2>/dev/null", + record_output=True, record_stderr=False, + log_output=False, log_errors=False, + cwd="/tmp", + ) + except ShellEnvironmentError: return - for line in out.splitlines(): + for line in exe.output.splitlines(): m = re.match(r"^creating ([a-z0-9]+\.tar\.gz)", line) if m: self._environ["ICECC_VERSION"] = "/tmp/%s" % m.group(1) @@ -1107,7 +1137,7 @@ class Builder(object): log.info(_("Running stage %s:") % stage) try: - self.do(buildscript, shell=False) + self.execute(buildscript) finally: # Remove the buildscript. @@ -1119,15 +1149,15 @@ class Builder(object): keep_libs = keep_libs.split() try: - self.do("%s/remove-static-libs %s %s" % \ + self.execute("%s/remove-static-libs %s %s" % \ (SCRIPT_DIR, self.buildroot, " ".join(keep_libs))) - except Error, e: + except ShellEnvironmentError, e: log.warning(_("Could not remove static libraries: %s") % e) def post_compress_man_pages(self): try: - self.do("%s/compress-man-pages %s" % (SCRIPT_DIR, self.buildroot)) - except Error, e: + self.execute("%s/compress-man-pages %s" % (SCRIPT_DIR, self.buildroot)) + except ShellEnvironmentError, e: log.warning(_("Compressing man pages did not complete successfully.")) def post_extract_debuginfo(self): @@ -1146,8 +1176,8 @@ class Builder(object): args += options.split() try: - self.do("%s/extract-debuginfo %s %s" % (SCRIPT_DIR, " ".join(args), self.pkg.buildroot)) - except Error, e: + self.execute("%s/extract-debuginfo %s %s" % (SCRIPT_DIR, " ".join(args), self.pkg.buildroot)) + except ShellEnvironmentError, e: log.error(_("Extracting debuginfo did not complete with success. Aborting build.")) raise diff --git a/python/pakfire/chroot.py b/python/pakfire/chroot.py deleted file mode 100644 index c08bbdb39..000000000 --- a/python/pakfire/chroot.py +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/python -############################################################################### -# # -# Pakfire - The IPFire package management system # -# Copyright (C) 2011 Pakfire development team # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see . # -# # -############################################################################### - -import fcntl -import os -import select -import subprocess -import time - -from _pakfire import PERSONALITY_LINUX, PERSONALITY_LINUX32 - -import pakfire.util as util -from errors import * - -def logOutput(fds, logger, returnOutput=1, start=0, timeout=0): - output="" - done = 0 - - # set all fds to nonblocking - for fd in fds: - flags = fcntl.fcntl(fd, fcntl.F_GETFL) - if not fd.closed: - fcntl.fcntl(fd, fcntl.F_SETFL, flags| os.O_NONBLOCK) - - tail = "" - while not done: - if (time.time() - start) > timeout and timeout != 0: - done = 1 - break - - i_rdy, o_rdy, e_rdy = select.select(fds,[],[],1) - - for s in i_rdy: - # slurp as much input as is ready - input = s.read() - - if input == "": - done = 1 - break - - if logger is not None: - lines = input.split("\n") - if tail: - lines[0] = tail + lines[0] - - # we may not have all of the last line - tail = lines.pop() - - for line in lines: - logger.info(line) - - for h in logger.handlers: - h.flush() - - if returnOutput: - output += input - - if tail and logger is not None: - logger.info(tail) - - return output - - -def do(command, shell=False, chrootPath=None, cwd=None, timeout=0, raiseExc=True, returnOutput=0, logstderr=1, personality=None, logger=None, env=None, cgroup=None, *args, **kargs): - # Save the output of command - output = "" - - # Save time when command was started - start = time.time() - - # Create preexecution thingy for command - preexec = ChildPreExec(personality, chrootPath, cwd) - - if logger: - logger.debug("Executing command: %s in %s" % (command, chrootPath or "/")) - - child = None - - if logstderr: - stderr = subprocess.PIPE - else: - stderr = open("/dev/null", "w") - - try: - # Create new child process - child = subprocess.Popen( - command, - shell=shell, - bufsize=0, close_fds=True, - stdin=open("/dev/null", "r"), - stdout=subprocess.PIPE, - stderr=stderr, - preexec_fn = preexec, - env=env - ) - - # If cgroup is given, attach the subprocess. - if cgroup: - cgroup.attach_task(child.pid) - - # use select() to poll for output so we dont block - fds = [child.stdout,] - if logstderr: - fds.append(child.stderr) - - output = logOutput(fds, logger, returnOutput, start, timeout) - - except: - # kill children if they aren't done - if child and child.returncode is None: - os.killpg(child.pid, 9) - try: - if child: - os.waitpid(child.pid, 0) - except: - pass - raise - - # wait until child is done, kill it if it passes timeout - niceExit=1 - while child.poll() is None: - if (time.time() - start) > timeout and timeout != 0: - niceExit = 0 - os.killpg(child.pid, 15) - if (time.time() - start) > (timeout+1) and timeout != 0: - niceExit = 0 - os.killpg(child.pid, 9) - - if not niceExit: - raise commandTimeoutExpired, ("Timeout(%s) expired for command:\n # %s\n%s" % (timeout, command, output)) - - if logger: - logger.debug("Child returncode was: %s" % str(child.returncode)) - - if raiseExc and child.returncode: - if returnOutput: - raise Error, ("Command failed: \n # %s\n%s" % (command, output), child.returncode) - else: - raise Error, ("Command failed. See logs for output.\n # %s" % (command,), child.returncode) - - return output - -class ChildPreExec(object): - def __init__(self, personality, chrootPath, cwd): - self._personality = personality - self.chrootPath = chrootPath - self.cwd = cwd - - @property - def personality(self): - """ - Return personality value if supported. - Otherwise return None. - """ - personality_defs = { - "linux64": PERSONALITY_LINUX, - "linux32": PERSONALITY_LINUX32, - } - - try: - return personality_defs[self._personality] - except KeyError: - pass - - def __call__(self, *args, **kargs): - # Set a new process group - os.setpgrp() - - # Set new personality if we got one. - if self.personality: - util.personality(self.personality) - - # Change into new root. - if self.chrootPath: - os.chdir(self.chrootPath) - os.chroot(self.chrootPath) - - # Change to cwd. - if self.cwd: - if not os.path.exists(self.cwd): - os.makedirs(self.cwd) - - os.chdir(self.cwd) diff --git a/python/pakfire/errors.py b/python/pakfire/errors.py index 3af257d53..3ac7b3c1f 100644 --- a/python/pakfire/errors.py +++ b/python/pakfire/errors.py @@ -86,6 +86,10 @@ class PakfireContainerError(Error): message = _("Running pakfire-build in a pakfire container?") +class ShellEnvironmentError(Error): + pass + + class SignatureError(Error): pass diff --git a/python/pakfire/packages/lexer.py b/python/pakfire/packages/lexer.py index 38445d833..c31ffd69b 100644 --- a/python/pakfire/packages/lexer.py +++ b/python/pakfire/packages/lexer.py @@ -6,7 +6,7 @@ import re from pakfire.constants import * from pakfire.i18n import _ -import pakfire.chroot +import pakfire.shell import logging #log = logging.getLogger("pakfire.lexer") @@ -237,17 +237,20 @@ class Lexer(object): return # Do we need to chroot and change personality? + shellenv = pakfire.shell.ShellExecuteEnvironment(command, + shell=True, record_output=True, log_output=False, record_stderr=False) + try: - output = pakfire.chroot.do(command, shell=True, returnOutput=1, logstderr=False) + shellenv.execute() - except Error: + except ShellEnvironmentError: return # Strip newline. - if output: - output = output.rstrip("\n") + if shellenv.output: + return shellenv.output.rstrip("\n") - return output + return shellenv.output def get_var(self, key, default=None, raw=False): definitions = {} diff --git a/python/pakfire/packages/make.py b/python/pakfire/packages/make.py index 764cb9cea..37d9c5e5c 100644 --- a/python/pakfire/packages/make.py +++ b/python/pakfire/packages/make.py @@ -36,7 +36,6 @@ import packager import logging log = logging.getLogger("pakfire") -import pakfire.chroot as chroot import pakfire.downloader as downloader import pakfire.util as util diff --git a/python/pakfire/shell.py b/python/pakfire/shell.py new file mode 100644 index 000000000..e1f7600a8 --- /dev/null +++ b/python/pakfire/shell.py @@ -0,0 +1,291 @@ +#!/usr/bin/python +############################################################################### +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2012 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +import fcntl +import os +import select +import subprocess +import time + +from _pakfire import PERSONALITY_LINUX, PERSONALITY_LINUX32 + +from pakfire.i18n import _ +import pakfire.util as util +from errors import * + +class ShellExecuteEnvironment(object): + def __init__(self, command, cwd=None, chroot_path=None, personality=None, shell=False, timeout=0, env=None, + cgroup=None, logger=None, log_output=True, log_errors=True, record_output=False, record_stdout=True, record_stderr=True): + # The given command that should be executed. + self.command = command + + # Change into current working dir. + self.cwd = cwd + + # Chroot into this directory. + self.chroot_path = chroot_path + + # The logger where all the output goes. + self.logger = logger + + # Set timeout. + self.timeout = timeout + + # Personality. + self.personality = personality + + # Shell. + self.shell = shell + self.env = env + + # cgroup to which the newly created process should be attached. + self.cgroup = cgroup + + # Timestamp, when execution has been started and ended. + self.time_start = None + self.time_end = None + + # Output, that has to be returned. + self.output = "" + self.record_output = record_output + self.record_stdout = record_stdout + self.record_stderr = record_stderr + + # Log the output and errors? + self.log_errors = log_errors + self.log_output = log_output + + # Exit code of command. + self.exitcode = None + + def execute(self): + # Save start time. + self.time_start = time.time() + + if self.logger: + self.logger.debug(_("Executing command: %s in %s") % (self.command, self.chroot_path or "/")) + + child = None + try: + # Create new child process + child = self.create_subprocess() + + # Record the output. + self.tee_log(child) + except: + # In case there has been an error, kill children if they aren't done + if child and child.returncode is None: + os.killpg(child.pid, 9) + + try: + if child: + os.waitpid(child.pid, 0) + except: + pass + + # Raise original exception. + raise + + finally: + # Save end time. + self.time_end = time.time() + + # wait until child is done, kill it if it passes timeout + nice_exit = True + while child.poll() is None: + if self.timeout_has_been_exceeded(): + nice_exit = False + os.killpg(child.pid, 15) + + if self.timeout_has_been_exceeded(3): + nice_exit = False + os.killpg(child.pid, 9) + + if not nice_exit: + raise commandTimeoutExpired, (_("Command exceeded timeout (%(timeout)d): %(command)s") % (self.timeout, self.command)) + + # Save exitcode. + self.exitcode = child.returncode + + if self.logger: + self.logger.debug(_("Child returncode was: %s") % self.exitcode) + + if self.exitcode and self.log_errors: + raise ShellEnvironmentError, (_("Command failed: %s") % self.command, self.exitcode) + + return self.exitcode + + def create_subprocess(self): + # Create preexecution thingy for command + preexec_fn = ChildPreExec(self.personality, self.chroot_path, self.cwd) + + kwargs = { + "bufsize" : 0, + "close_fds" : True, + "env" : self.env, + "preexec_fn" : preexec_fn, + "shell" : self.shell, + } + + # File descriptors. + stdin = open("/dev/null", "r") + + if self.record_stdout: + stdout = subprocess.PIPE + else: + stdout = open("/dev/null", "w") + + if self.record_stderr: + stderr = subprocess.PIPE + else: + stderr = open("/dev/null", "w") + + kwargs.update({ + "stdin" : stdin, + "stdout" : stdout, + "stderr" : stderr, + }) + + child = subprocess.Popen(self.command, **kwargs) + + # If cgroup is given, attach the subprocess. + if self.cgroup: + self.cgroup.attach_task(child.pid) + + return child + + def timeout_has_been_exceeded(self, offset=0): + """ + Returns true when the command has been running + for more than 'timeout' seconds. + """ + # If no timeout has been configured, it can never be exceeded. + if not self.timeout: + return False + + # Check if the command has already been started. + if not self.time_start: + return False + + return (time.time() - self.time_start - offset) > self.timeout + + def tee_log(self, child): + fds = [] + + if self.record_stdout: + fds.append(child.stdout) + + if self.record_stderr: + fds.append(child.stderr) + + # Set all file descriptors as non-blocking. + for fd in fds: + # Skip already closed file descriptors. + if fd.closed: + continue + + flags = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) + + done = False + tail = "" + while not done: + # Check if timeout has been hit. + if self.timeout_has_been_exceeded(): + done = True + break + + # Start the select() call. + i_rdy, o_rdy, e_rdy = select.select(fds, [], [], 1) + + # Process output. + for s in i_rdy: + # Read as much data as possible. + input = s.read() + + if input == "": + done = True + break + + if self.record_output: + self.output += input + + if self.log_output and self.logger: + lines = input.split("\n") + if tail: + lines[0] = tail + lines[0] + + # We may not have got all the characters of the last line. + tail = lines.pop() + + for line in lines: + self.logger.info(line) + + # Flush all handlers of the logger. + for h in self.logger.handlers: + h.flush() + + # Log the rest of the last line. + if tail and self.log_output and self.logger: + self.logger.info(tail) + + +class ChildPreExec(object): + def __init__(self, personality, chroot_path, cwd): + self._personality = personality + self.chroot_path = chroot_path + self.cwd = cwd + + @property + def personality(self): + """ + Return personality value if supported. + Otherwise return None. + """ + personality_defs = { + "linux64": PERSONALITY_LINUX, + "linux32": PERSONALITY_LINUX32, + } + + try: + return personality_defs[self._personality] + except KeyError: + pass + + def __call__(self, *args, **kargs): + # Set a new process group + os.setpgrp() + + # Set new personality if we got one. + if self.personality: + util.personality(self.personality) + + # Change into new root. + if self.chroot_path: + os.chdir(self.chroot_path) + os.chroot(self.chroot_path) + + # Change to cwd. + if self.cwd: + if not os.path.exists(self.cwd): + os.makedirs(self.cwd) + + os.chdir(self.cwd)