From 93bd0aa4164dd6fe1447fec679c0c9f4a694bd9c Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Sat, 9 Apr 2011 20:24:00 +0200 Subject: [PATCH] Move chroot code into own file. --- pakfire/builder.py | 5 +- pakfire/chroot.py | 161 +++++++++++++++++++++++++++++++++++++++++++++ pakfire/util.py | 158 -------------------------------------------- 3 files changed, 164 insertions(+), 160 deletions(-) create mode 100644 pakfire/chroot.py diff --git a/pakfire/builder.py b/pakfire/builder.py index a9fb3f1a9..79ae93f92 100644 --- a/pakfire/builder.py +++ b/pakfire/builder.py @@ -13,6 +13,7 @@ import time import uuid import base +import chroot import depsolve import packages import repository @@ -397,13 +398,13 @@ class Builder(object): self.log.debug("Mounting environment") for cmd, mountpoint in self.mountpoints: cmd = "%s %s" % (cmd, self.chrootPath(mountpoint)) - util.do(cmd, shell=True) + chroot.do(cmd, shell=True) def _umountall(self): self.log.debug("Umounting environment") for cmd, mountpoint in self.mountpoints: cmd = "umount -n %s" % self.chrootPath(mountpoint) - util.do(cmd, raiseExc=0, shell=True) + chroot.do(cmd, raiseExc=0, shell=True) @property def mountpoints(self): diff --git a/pakfire/chroot.py b/pakfire/chroot.py new file mode 100644 index 000000000..170b2e389 --- /dev/null +++ b/pakfire/chroot.py @@ -0,0 +1,161 @@ +#!/usr/bin/python + +import ctypes +import fcntl +import os +import select +import subprocess +import time + +_libc = ctypes.cdll.LoadLibrary(None) +_libc.personality.argtypes = [ctypes.c_ulong] +_libc.personality.restype = ctypes.c_int + +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: + if line == '': continue + 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, personality=None, logger=None, env=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 "/")) + + try: + child = None + + # Create new child process + child = subprocess.Popen( + command, + shell=shell, + bufsize=0, close_fds=True, + stdin=open("/dev/null", "r"), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + preexec_fn = preexec, + env=env + ) + + # use select() to poll for output so we dont block + output = logOutput([child.stdout, child.stderr], 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. + """ + # taken from sys/personality.h + personality_defs = { + "linux64": 0x0000, + "linux32": 0x0008, + } + + 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: + res = _libc.personality(self.personality) + if res == -1: + raise OSError, "Could not set personality" + + # Change into new root. + if self.chrootPath: + os.chdir(self.chrootPath) + os.chroot(self.chrootPath) + + # Change to cwd. + if self.cwd: + os.chdir(self.cwd) diff --git a/pakfire/util.py b/pakfire/util.py index 5f5269782..575d5bbfa 100644 --- a/pakfire/util.py +++ b/pakfire/util.py @@ -1,24 +1,16 @@ #!/usr/bin/python -import ctypes -import fcntl import os import progressbar import random -import select import shutil import string -import subprocess import sys import time from errors import Error from packages.util import calc_hash1, format_size -_libc = ctypes.cdll.LoadLibrary(None) -_libc.personality.argtypes = [ctypes.c_ulong] -_libc.personality.restype = ctypes.c_int - def cli_is_interactive(): """ Say weather a shell is interactive or not. @@ -81,153 +73,3 @@ def rm(path, *args, **kargs): os.system("chattr -R -i %s" % path) else: raise - -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: - if line == '': continue - 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, personality=None, logger=None, env=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 "/")) - - try: - child = None - - # Create new child process - child = subprocess.Popen( - command, - shell=shell, - bufsize=0, close_fds=True, - stdin=open("/dev/null", "r"), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - preexec_fn = preexec, - env=env - ) - - # use select() to poll for output so we dont block - output = logOutput([child.stdout, child.stderr], 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. - """ - # taken from sys/personality.h - personality_defs = { - "linux64": 0x0000, - "linux32": 0x0008, - } - - 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: - res = _libc.personality(self.personality) - if res == -1: - raise OSError, "Could not set personality" - - # Change into new root. - if self.chrootPath: - os.chdir(self.chrootPath) - os.chroot(self.chrootPath) - - # Change to cwd. - if self.cwd: - os.chdir(self.cwd) - -- 2.39.5