# encoding: utf-8 # # Copyright 2009-2017 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an 'AS IS' BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ scriptutils.py Created by Greg Neagle on 2016-12-14. Functions to run scripts inside Munki """ import os import subprocess from . import osutils from . import display from . import munkilog from . import munkistatus def _writefile(stringdata, path): '''Writes string data to path. Returns the path on success, empty string on failure.''' try: fileobject = open(path, mode='w', buffering=1) # write line-by-line to ensure proper UNIX line-endings for line in stringdata.splitlines(): print >> fileobject, line.encode('UTF-8') fileobject.close() return path except (OSError, IOError): display.display_error("Couldn't write %s" % stringdata) return "" def run_embedded_script(scriptname, pkginfo_item, suppress_error=False): '''Runs a script embedded in the pkginfo. Returns the result code.''' # get the script text from the pkginfo script_text = pkginfo_item.get(scriptname) itemname = pkginfo_item.get('name') if not script_text: display.display_error( 'Missing script %s for %s' % (scriptname, itemname)) return -1 # write the script to a temp file scriptpath = os.path.join(osutils.tmpdir(), scriptname) if _writefile(script_text, scriptpath): cmd = ['/bin/chmod', '-R', 'o+x', scriptpath] retcode = subprocess.call(cmd) if retcode: display.display_error( 'Error setting script mode in %s for %s' % (scriptname, itemname)) return -1 else: display.display_error( 'Cannot write script %s for %s' % (scriptname, itemname)) return -1 # now run the script return run_script( itemname, scriptpath, scriptname, suppress_error=suppress_error) def run_script(itemname, path, scriptname, suppress_error=False): '''Runs a script, Returns return code.''' if suppress_error: display.display_detail( 'Running %s for %s ' % (scriptname, itemname)) else: display.display_status_minor( 'Running %s for %s ' % (scriptname, itemname)) if display.munkistatusoutput: # set indeterminate progress bar munkistatus.percent(-1) scriptoutput = [] try: proc = subprocess.Popen(path, shell=False, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except OSError, err: display.display_error( 'Error executing script %s: %s' % (scriptname, str(err))) return -1 while True: msg = proc.stdout.readline().decode('UTF-8') if not msg and (proc.poll() != None): break # save all script output in case there is # an error so we can dump it to the log scriptoutput.append(msg) msg = msg.rstrip("\n") display.display_info(msg) retcode = proc.poll() if retcode and not suppress_error: display.display_error( 'Running %s for %s failed.' % (scriptname, itemname)) display.display_error("-"*78) for line in scriptoutput: display.display_error("\t%s" % line.rstrip("\n")) display.display_error("-"*78) elif not suppress_error: munkilog.log( 'Running %s for %s was successful.' % (scriptname, itemname)) if display.munkistatusoutput: # clear indeterminate progress bar munkistatus.percent(0) return retcode if __name__ == '__main__': print 'This is a library of support tools for the Munki Suite.'