diff options
author | Gunnar Wrobel <wrobel@gentoo.org> | 2007-04-27 17:54:03 +0000 |
---|---|---|
committer | Gunnar Wrobel <wrobel@gentoo.org> | 2007-04-27 17:54:03 +0000 |
commit | f5de4747faf7eb4b836da7b99e032443809b0c5d (patch) | |
tree | 3f6fdb7b78151215244fb438a7c48f7617473ff2 | |
parent | Do not error out if there are unexpanded variables in the configuration. In t... (diff) | |
download | webapp-config-f5de4747faf7eb4b836da7b99e032443809b0c5d.tar.gz webapp-config-f5de4747faf7eb4b836da7b99e032443809b0c5d.tar.bz2 webapp-config-f5de4747faf7eb4b836da7b99e032443809b0c5d.zip |
Merged revision 8 (sandbox fixes) on trunk for testing.
svn path=/trunk/webapp-config/; revision=17
-rw-r--r-- | WebappConfig/ebuild.py | 25 | ||||
-rw-r--r-- | WebappConfig/sandbox.py | 187 |
2 files changed, 129 insertions, 83 deletions
diff --git a/WebappConfig/ebuild.py b/WebappConfig/ebuild.py index 9c08d3d..857e78e 100644 --- a/WebappConfig/ebuild.py +++ b/WebappConfig/ebuild.py @@ -117,11 +117,8 @@ class Ebuild: sandbox = Sandbox(self.config) - if sandbox.start(): - OUT.debug('Disabling hooks') - return - - self.run_vars(server) + # save list of environment variables to set + env_map = self.run_vars(server) if os.path.isdir(self.__hooksd): for x in os.listdir(self.__hooksd): @@ -131,23 +128,7 @@ class Ebuild: OUT.debug('Running hook script', 7) - fi, fo, fe = os.popen3(self.__hooksd + '/' + x + ' ' - + type) - fi.close() - result_lines = fo.readlines() - error_lines = fe.readlines() - fo.close() - fe.close() - - if result_lines: - for i in result_lines: - OUT.notice(i) - - if error_lines: - for i in error_lines: - OUT.warn(i) - - sandbox.stop() + sandbox.spawn(self.__hooksd + '/' + x + ' ' + type, env_map) def show_post(self, filename, ptype, server = None): ''' diff --git a/WebappConfig/sandbox.py b/WebappConfig/sandbox.py index f010bd0..c3df234 100644 --- a/WebappConfig/sandbox.py +++ b/WebappConfig/sandbox.py @@ -6,7 +6,7 @@ # # Originally written for the Gentoo Linux distribution # -# Copyright (c) 1999-2006 Gentoo Foundation +# Copyright (c) 1999-2005 Gentoo Foundation # Released under v2 of the GNU GPL # # Author(s) Stuart Herbert <stuart@gentoo.org> @@ -24,38 +24,36 @@ __version__ = "$Id: permissions.py 129 2005-11-06 12:46:31Z wrobel $" # Dependencies # ------------------------------------------------------------------------ -import os, string, time - -from WebappConfig.wrapper import config_libdir +import os, os.path, string, time, sys from WebappConfig.debug import OUT +# stolen from portage +if os.path.isdir("/proc/%i/fd" % os.getpid()): + def get_open_fds(): + return map(int, [fd for fd in os.listdir("/proc/%i/fd" % os.getpid()) if fd.isdigit()]) +else: + def get_open_fds(): + return xrange(max_fd_limit) + + class Sandbox: ''' Basic class for handling sandbox stuff - Concept stolen from app-shells/sandboxshell ''' def __init__(self, config): self.config = config - self.__path = [config_libdir + '/libsandbox.so', - '/usr/lib/libsandbox.so', '/lib/libsandbox.so'] - self.__export = {} self.__write = ['g_installdir', 'g_htdocsdir', 'g_cgibindir', 'vhost_root'] - self.__read = '/' - self.__syswrite = ':/dev/tty:/dev/pts:/dev/null:/tmp' - self.sandbox = '' + self.sandbox_binary = '/usr/bin/sandbox' - self.log = '/tmp/w-c.sandbox-' \ - + time.strftime("%Y-%m-%d-%H.%M.%S",time.gmtime())\ - + '.log' - self.debug_log = self.log + '.debug' + self.env = {'SANDBOX_WRITE' : self.get_write() } def get_write(self): '''Return write paths.''' @@ -66,49 +64,116 @@ class Sandbox: ''' Return a config option.''' return self.config.config.get('USER', option) - def start(self): - ''' - Start sandbox. Return 1 if failed. - ''' - - OUT.debug('Initializing sandbox', 7) - for i in self.__path: - if os.access(i, os.R_OK): - self.sandbox = i - break - - if not self.sandbox: - OUT.warn("Could not find a sandbox, disabling hooks") - return 1 - - try: - self.__ld = os.environ['LD_PRELOAD'] - except KeyError, e: - self.__ld = "" - - self.__export = {'LD_PRELOAD' : self.sandbox, - 'SANDBOX_WRITE' : self.get_write(), - 'SANDBOX_READ' : self.__read, - 'SANDBOX_LOG' : self.log, - 'SANDBOX_DEBUG_LOG' : self.debug_log, - 'SANDBOX_ON' : "1", - 'SANDBOX_ACTIVE' : "armedandready"} - self.run_vars() - - def stop(self): - '''Stop sandbox''' - - self.__export = {'LD_PRELOAD' : self.__ld, - 'SANDBOX_WRITE' : "", - 'SANDBOX_READ' : "", - 'SANDBOX_LOG' : "", - 'SANDBOX_DEBUG_LOG' : "", - 'SANDBOX_ON' : "0", - 'SANDBOX_ACTIVE' : ""} - self.run_vars() - - def run_vars(self): - - for i in self.__export.keys(): - value = self.__export[i] - os.putenv(i, value) + # stolen from portage + def spawn(self, mycommand, full_env): + """ + Spawns a given command. + + @param mycommand: the command to execute + @type mycommand: String or List (Popen style list) + @param full_env: A dict of Key=Value pairs for env variables + @type full_env: Dictionary + """ + + # Default to propagating our stdin, stdout and stderr. + fd_pipes = {0:0, 1:1, 2:2} + + # mypids will hold the pids of all processes created. + mypids = [] + + command = [] + command.append(self.sandbox_binary) + command.append(mycommand) + + # merge full_env (w-c variables) with env (write path) + self.env.update(full_env) + for a in self.env.keys(): + if not self.env[a]: + self.env[a] = '' + + pid = os.fork() + + if not pid: + try: + self._exec(command, self.env, fd_pipes) + except Exception, e: + # We need to catch _any_ exception so that it doesn't + # propagate out of this function and cause exiting + # with anything other than os._exit() + sys.stderr.write("%s:\n %s\n" % (e, " ".join(command))) + sys.stderr.flush() + os._exit(1) + + # Add the pid to our list + mypids.append(pid) + + # Clean up processes + while mypids: + + # Pull the last reader in the pipe chain. If all processes + # in the pipe are well behaved, it will die when the process + # it is reading from dies. + pid = mypids.pop(0) + + # and wait for it. + retval = os.waitpid(pid, 0)[1] + + if retval: + # If it failed, kill off anything else that + # isn't dead yet. + for pid in mypids: + if os.waitpid(pid, os.WNOHANG) == (0,0): + os.kill(pid, signal.SIGTERM) + os.waitpid(pid, 0) + + # If it got a signal, return the signal that was sent. + if (retval & 0xff): + return ((retval & 0xff) << 8) + + # Otherwise, return its exit code. + return (retval >> 8) + + # Everything succeeded + return 0 + + # stolen from portage + def _exec(self, binary, env, fd_pipes): + + """ + Execute a given binary with options in a sandbox + + @param binary: Name of program to execute + @type binary: String + @param env: Key,Value mapping for Environmental Variables + @type env: Dictionary + @param fd_pipes: Mapping pipes to destination; { 0:0, 1:1, 2:2 } + @type fd_pipes: Dictionary + @rtype: None + @returns: Never returns (calls os.execve) + """ + + # If the process we're creating hasn't been given a name + # assign it the name of the executable. + opt_name = os.path.basename(binary[0]) + + # Set up the command's pipes. + my_fds = {} + # To protect from cases where direct assignment could + # clobber needed fds ({1:2, 2:1}) we first dupe the fds + # into unused fds. + for fd in fd_pipes: + my_fds[fd] = os.dup(fd_pipes[fd]) + # Then assign them to what they should be. + for fd in my_fds: + os.dup2(my_fds[fd], fd) + # Then close _all_ fds that haven't been explictly + # requested to be kept open. + for fd in get_open_fds(): + if fd not in my_fds: + try: + os.close(fd) + except OSError: + pass + + # And switch to the new process. + os.execve(binary[0], binary, env) |