#!/usr/bin/env python # used in a script with shebang in 1 of 2 ways # #!/usr/bin/env re-l relative_interpreter ... # this requires support for more than 1 parameter to the intpreter (not on linux) # # will set SCRIPT_FILE and SCRIPT_PATH # example: # #!/usr/bin/env re-l python # # these assume the script is in the same folder as the python interpreter. other deeper locations are also valid, like: # #!/usr/bin/env re-l ../Resources/Python.app/Contents/MacOS/Python # # Many systems only allow 1 parameter with Shebang. On these systems, re-l # can be used in a proprietary way as follows: # #!/usr/bin/env re-l # #re-l:relative_interpreter ... # # #Created by jpgarcia. #Copyright (c) 2015-2018 University of Wisconsin SSEC. All rights reserved. # import os,sys import logging logger=logging.getLogger(__name__) LOG = logger.debug PY2 = sys.version_info[0] == 2 if not PY2: ropenmode="r" openkwargs=dict(newline=None,encoding="utf-8") else: ropenmode="rU" openkwargs=dict() def getLine(f,linenumber=1):#returns native string (ascii or unicode) if not os.path.exists(f): return None fil=open(f,mode=ropenmode,**openkwargs) v='' for x in range(linenumber): try: v=fil.readline(512).strip() except UnicodeDecodeError: v='' return v def blacklist_env(): if len(os.environ.get('RE_L_NOBLACKLIST',''))>0: return tuple() if 'darwin' in sys.platform: return('LD_LIBRARY_PATH','LD_PRELOAD','DYLD_INSERT_LIBRARIES','DYLD_LIBRARY_PATH','DYLD_PRELOAD') return('LD_LIBRARY_PATH','LD_PRELOAD') #deterimine if the script is a single-parameter env-based script file def isRelTriageScript(filepath): return getLine(filepath,linenumber=1)=='#!/usr/bin/env re-l' #extract the interpreter from a scriptfile def extractRelEmbeddedInterpreter(filepath): if not isRelTriageScript(filepath): raise RuntimeError("Not a re-l script!") for line in range(2,5): l=getLine(filepath,linenumber=line) if l.startswith('#re-l:') or l.startswith('#:'): return l.split(':',1)[1].strip() raise RuntimeError("Couldn't find re-lative interpreter line (starts with '#re-l:')") #executes a scriptfile using a relative interpreter with given commandline args def relexec(scriptrelativeinterpreter,scriptfile,*_args): args=[None,None]+list(_args) scriptGoes=1 interp_and_args=scriptrelativeinterpreter.strip().split() if len(interp_and_args)>1: scriptrelativeinterpreter=interp_and_args[0] for interparg in interp_and_args[1:]: args.insert(scriptGoes,interparg) scriptGoes=scriptGoes+1 else: while len(_args)>0 and (not os.path.exists(scriptfile) or os.path.isdir(scriptfile)): args.insert(scriptGoes,scriptfile) scriptfile=_args.pop(0) scriptGoes=scriptGoes+1 firstline=getLine(scriptfile) assert(firstline.startswith('#!/usr/bin/env re-l')) if firstline.startswith('#!/usr/bin/env re-l '): assert(len(firstline)>0 and scriptfile not in firstline and ('#!/usr/bin/env re-l '+(' '.join([scriptrelativeinterpreter]+args[1:scriptGoes]))) == firstline) #else: # assert(getLine(scriptfile,2)==) LOG('%s %s' % (scriptrelativeinterpreter,scriptfile)) scriptpath=os.path.dirname(os.path.realpath(scriptfile)) interpreter=os.path.normpath(os.path.join(scriptpath,scriptrelativeinterpreter)) LOG('%s %s' % (scriptpath,interpreter)) args[0]=interpreter args[scriptGoes]=scriptfile #print replacements LOG('running interpreter '+interpreter+' with parameters '+repr(args)) LOG('script_path=%s' % (scriptpath)) assert(os.path.exists(interpreter) and not os.path.isdir(interpreter)) assert(os.path.exists(scriptfile) and not os.path.isdir(scriptfile)) assert(os.path.exists(scriptpath) and os.path.isdir(scriptpath)) env=os.environ.copy() env['SCRIPT_FILE']=scriptfile env['SCRIPT_PATH']=scriptpath for bad_env in blacklist_env(): LOG('Checking blacklisted environment variable ' + bad_env) if bad_env in env: logger.warning('Dangerous environment variable "' + bad_env + '" set to "' + env[bad_env] + '". Cleared before execution.') del env[bad_env] os.execve(interpreter,args,env) raise RuntimeError('Exec Failed') def main(): if len(sys.argv)<2 or (len(sys.argv)<3 and not isRelTriageScript(sys.argv[1])): print('re-l relative interpreter proxy script') print('Usage: with re-l anywhere in the path modify any script shebang to be:') if 'darwin' in sys.platform: print('\t#!/usr/bin/env re-l relative/path/to/interpeter ...') else: print('\t#!/usr/bin/env re-l') print('and then for the second or third line, add the line:') print('\t#re-l:relative/path/to/interpeter ...') print('where the interpreter is identified relative to the script file itself') sys.exit(0) if len(sys.argv)==2 or isRelTriageScript(sys.argv[1]): assert(isRelTriageScript(sys.argv[1])) relexec(extractRelEmbeddedInterpreter(sys.argv[1]),*sys.argv[1:]) else: relexec(*sys.argv[1:]) if __name__ == '__main__': main()