trg_fd = [0, 1, 2] # wax all open descriptors that weren't requested be left open. for x in range(0, max_fd_limit): if x not in trg_fd: try: os.close(x) except SystemExit, e: raise except: pass # note this order must be preserved- can't change gid/groups if you change uid first. if selinux_capable and selinux_context: import selinux selinux.setexec(selinux_context) if gid: os.setgid(gid) if groups: os.setgroups(groups) if uid: os.setuid(uid) if umask: os.umask(umask) try: #print "execing", myc, myargs if func_call: # either use a passed in func for interpretting the results, or return if no exception. # note the passed in list, and dict are expanded. if len(mycommand) == 4:
def spawn(mycommand,env={},raw_exit_code=False,opt_name=None,fd_pipes=None,returnpid=False,\ uid=None,gid=None,groups=None,umask=None,logfile=None,path_lookup=True,\ selinux_context=None, raise_signals=False, func_call=False): """base fork/execve function. mycommand is the desired command- if you need a command to execute in a bash/sandbox/fakeroot environment, use the appropriate spawn call. This is a straight fork/exec code path. Can either have a tuple, or a string passed in. If uid/gid/groups/umask specified, it changes the forked process to said value. If path_lookup is on, a non-absolute command will be converted to an absolute command, otherwise it returns None. selinux_context is the desired context, dependant on selinux being available. opt_name controls the name the processor goes by. fd_pipes controls which file descriptor numbers are left open in the forked process- it's a dict of current fd's raw fd #, desired #. func_call is a boolean for specifying to execute a python function- use spawn_func instead. raise_signals is questionable. Basically throw an exception if signal'd. No exception is thrown if raw_input is on. logfile overloads the specified fd's to write to a tee process which logs to logfile returnpid returns the relevant pids (a list, including the logging process if logfile is on). non-returnpid calls to spawn will block till the process has exited, returning the exitcode/signal raw_exit_code controls whether the actual waitpid result is returned, or intrepretted.""" myc='' if not func_call: if type(mycommand)==bytes: mycommand=mycommand.split() myc = mycommand[0] if not os.access(myc, os.X_OK): if not path_lookup: return None myc = find_binary(myc) if myc == None: return None mypid=[] if logfile: pr,pw=os.pipe() mypid.extend(spawn(('tee','-i','-a',logfile),returnpid=True,fd_pipes={0:pr,1:1,2:2})) retval=os.waitpid(mypid[-1],os.WNOHANG)[1] if retval != 0: # he's dead jim. if raw_exit_code: return retval return process_exit_code(retval) if fd_pipes == None: fd_pipes={} fd_pipes[0] = 0 fd_pipes[1]=pw fd_pipes[2]=pw if not opt_name: opt_name = mycommand[0] myargs=[opt_name] myargs.extend(mycommand[1:]) global spawned_pids mypid.append(os.fork()) if mypid[-1] != 0: #log the bugger. spawned_pids.extend(mypid) if mypid[-1] == 0: if func_call: spawned_pids = [] # this may look ugly, but basically it moves file descriptors around to ensure no # handles that are needed are accidentally closed during the final dup2 calls. trg_fd=[] if type(fd_pipes)==dict: src_fd=[] k=list(fd_pipes.keys()) k.sort() #build list of which fds will be where, and where they are at currently for x in k: trg_fd.append(x) src_fd.append(fd_pipes[x]) # run through said list dup'ing descriptors so that they won't be waxed # by other dup calls. for x in range(0,len(trg_fd)): if trg_fd[x] == src_fd[x]: continue if trg_fd[x] in src_fd[x+1:]: new=os.dup2(trg_fd[x],max(src_fd) + 1) os.close(trg_fd[x]) try: while True: src_fd[s.index(trg_fd[x])]=new except SystemExit as e: raise except: pass # transfer the fds to their final pre-exec position. for x in range(0,len(trg_fd)): if trg_fd[x] != src_fd[x]: os.dup2(src_fd[x], trg_fd[x]) else: trg_fd=[0,1,2] # wax all open descriptors that weren't requested be left open. for x in range(0,max_fd_limit): if x not in trg_fd: try: os.close(x) except SystemExit as e: raise except: pass # note this order must be preserved- can't change gid/groups if you change uid first. if selinux_capable and selinux_context: import selinux selinux.setexec(selinux_context) if gid: os.setgid(gid) if groups: os.setgroups(groups) if uid: os.setuid(uid) if umask: os.umask(umask) try: #print "execing", myc, myargs if func_call: # either use a passed in func for interpretting the results, or return if no exception. # note the passed in list, and dict are expanded. if len(mycommand) == 4: os._exit(mycommand[3](mycommand[0](*mycommand[1],**mycommand[2]))) try: mycommand[0](*mycommand[1],**mycommand[2]) except Exception as e: print("caught exception",e," in forked func",mycommand[0]) sys.exit(0) #os.execvp(myc,myargs) os.execve(myc,myargs,env) except SystemExit as e: raise except Exception as e: if not func_call: raise MetroError(str(e)+":\n "+myc+" "+string.join(myargs)) print("func call failed") # If the execve fails, we need to report it, and exit # *carefully* --- report error here os._exit(1) sys.exit(1) return # should never get reached # if we were logging, kill the pipes. if logfile: os.close(pr) os.close(pw) if returnpid: return mypid # loop through pids (typically one, unless logging), either waiting on their death, or waxing them # if the main pid (mycommand) returned badly. while len(mypid): try: retval=os.waitpid(mypid[-1],0)[1] except KeyboardInterrupt: print("Keyboard interrupt detected, aborting script...") os.kill(mypid[-1],signal.SIGINT) continue if retval != 0: cleanup(mypid[0:-1],block_exceptions=False) # at this point we've killed all other kid pids generated via this call. # return now. if raw_exit_code: return retval return process_exit_code(retval,throw_signals=raise_signals) else: mypid.pop(-1) cleanup(mypid) return 0
trg_fd=[0,1,2] # wax all open descriptors that weren't requested be left open. for x in range(0,max_fd_limit): if x not in trg_fd: try: os.close(x) except SystemExit, e: raise except: pass # note this order must be preserved- can't change gid/groups if you change uid first. if selinux_capable and selinux_context: import selinux selinux.setexec(selinux_context) if gid: os.setgid(gid) if groups: os.setgroups(groups) if uid: os.setuid(uid) if umask: os.umask(umask) else: os.umask(022) try: #msg("execing", myc, myargs) if func_call: # either use a passed in func for interpretting the results, or return if no exception.