mirror of
https://github.com/munki/munki.git
synced 2026-04-22 12:38:23 -05:00
Add support for 'installcheck_script' in pkginfo
This commit is contained in:
@@ -582,7 +582,7 @@ def installWithInfo(
|
||||
|
||||
retcode = 0
|
||||
if 'preinstall_script' in item:
|
||||
retcode = runEmbeddedScript('preinstall_script', item)
|
||||
retcode = munkicommon.runEmbeddedScript('preinstall_script', item)
|
||||
|
||||
if retcode == 0 and 'installer_item' in item:
|
||||
display_name = item.get('display_name') or item.get('name')
|
||||
@@ -708,7 +708,8 @@ def installWithInfo(
|
||||
if retcode == 0 and 'postinstall_script' in item:
|
||||
# only run embedded postinstall script if the install did not
|
||||
# return a failure code
|
||||
retcode = runEmbeddedScript('postinstall_script', item)
|
||||
retcode = munkicommon.runEmbeddedScript(
|
||||
'postinstall_script', item)
|
||||
if retcode:
|
||||
# we won't consider postinstall script failures as fatal
|
||||
# since the item has been installed via package/disk image
|
||||
@@ -806,98 +807,6 @@ def installWithInfo(
|
||||
return (restartflag, skipped_installs)
|
||||
|
||||
|
||||
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)
|
||||
print >> fileobject, stringdata.encode('UTF-8')
|
||||
fileobject.close()
|
||||
return path
|
||||
except (OSError, IOError):
|
||||
munkicommon.display_error("Couldn't write %s" % stringdata)
|
||||
return ""
|
||||
|
||||
|
||||
def runEmbeddedScript(scriptname, pkginfo_item):
|
||||
'''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:
|
||||
munkicommon.display_error(
|
||||
'Missing script %s for %s' % (scriptname, itemname))
|
||||
return -1
|
||||
|
||||
# write the script to a temp file
|
||||
scriptpath = os.path.join(munkicommon.tmpdir, scriptname)
|
||||
if writefile(script_text, scriptpath):
|
||||
cmd = ['/bin/chmod', '-R', 'o+x', scriptpath]
|
||||
retcode = subprocess.call(cmd)
|
||||
if retcode:
|
||||
munkicommon.display_error(
|
||||
'Error setting script mode in %s for %s'
|
||||
% (scriptname, itemname))
|
||||
return -1
|
||||
else:
|
||||
munkicommon.display_error(
|
||||
'Cannot write script %s for %s' % (scriptname, itemname))
|
||||
return -1
|
||||
|
||||
# now run the script
|
||||
return runScript(itemname, scriptpath, scriptname)
|
||||
|
||||
|
||||
def runScript(itemname, path, scriptname):
|
||||
'''Runs a script, Returns return code.'''
|
||||
munkicommon.display_status_minor(
|
||||
'Running %s for %s ' % (scriptname, itemname))
|
||||
if munkicommon.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, e:
|
||||
munkicommon.display_error(
|
||||
'Error executing script %s: %s' % (scriptname, str(e)))
|
||||
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")
|
||||
munkicommon.display_info(msg)
|
||||
|
||||
retcode = proc.poll()
|
||||
if retcode:
|
||||
munkicommon.display_error(
|
||||
'Running %s for %s failed.' % (scriptname, itemname))
|
||||
munkicommon.display_error("-"*78)
|
||||
for line in scriptoutput:
|
||||
munkicommon.display_error("\t%s" % line.rstrip("\n"))
|
||||
munkicommon.display_error("-"*78)
|
||||
else:
|
||||
munkicommon.log(
|
||||
'Running %s for %s was successful.' % (scriptname, itemname))
|
||||
|
||||
if munkicommon.munkistatusoutput:
|
||||
# clear indeterminate progress bar
|
||||
munkistatus.percent(0)
|
||||
|
||||
return retcode
|
||||
|
||||
|
||||
def skippedItemsThatRequireThisItem(item, skipped_items):
|
||||
'''Looks for items in the skipped_items that require or are update_for
|
||||
the current item. Returns a list of matches.'''
|
||||
@@ -963,7 +872,7 @@ def processRemovals(removallist, only_unattended=False):
|
||||
retcode = 0
|
||||
# run preuninstall_script if it exists
|
||||
if 'preuninstall_script' in item:
|
||||
retcode = runEmbeddedScript('preuninstall_script', item)
|
||||
retcode = munkicommon.runEmbeddedScript('preuninstall_script', item)
|
||||
|
||||
if retcode == 0 and 'uninstall_method' in item:
|
||||
uninstallmethod = item['uninstall_method']
|
||||
@@ -1008,7 +917,8 @@ def processRemovals(removallist, only_unattended=False):
|
||||
name)
|
||||
|
||||
elif uninstallmethod == 'uninstall_script':
|
||||
retcode = runEmbeddedScript('uninstall_script', item)
|
||||
retcode = munkicommon.runEmbeddedScript(
|
||||
'uninstall_script', item)
|
||||
if (retcode == 0 and
|
||||
item.get('RestartAction') == "RequireRestart"):
|
||||
restartFlag = True
|
||||
@@ -1016,7 +926,7 @@ def processRemovals(removallist, only_unattended=False):
|
||||
elif os.path.exists(uninstallmethod) and \
|
||||
os.access(uninstallmethod, os.X_OK):
|
||||
# it's a script or program to uninstall
|
||||
retcode = runScript(
|
||||
retcode = munkicommon.runScript(
|
||||
name, uninstallmethod, 'uninstall script')
|
||||
if (retcode == 0 and
|
||||
item.get('RestartAction') == "RequireRestart"):
|
||||
@@ -1029,7 +939,8 @@ def processRemovals(removallist, only_unattended=False):
|
||||
retcode = -99
|
||||
|
||||
if retcode == 0 and item.get('postuninstall_script'):
|
||||
retcode = runEmbeddedScript('postuninstall_script', item)
|
||||
retcode = munkicommon.runEmbeddedScript(
|
||||
'postuninstall_script', item)
|
||||
if retcode:
|
||||
# we won't consider postuninstall script failures as fatal
|
||||
# since the item has been uninstalled
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2011 Greg Neagle.
|
||||
# Copyright 2009-2012 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -549,7 +549,7 @@ def saveappdata():
|
||||
os.path.join(
|
||||
pref('ManagedInstallDir'), 'ApplicationInventory.plist'))
|
||||
except FoundationPlist.NSPropertyListSerializationException, err:
|
||||
munkicommon.display_warning(
|
||||
display_warning(
|
||||
'Unable to save inventory report: %s' % err)
|
||||
|
||||
|
||||
@@ -2290,6 +2290,100 @@ def findProcesses(user=None, exe=None):
|
||||
return pids
|
||||
|
||||
|
||||
# utility functions for running scripts from pkginfo files
|
||||
# used by updatecheck.py and installer.py
|
||||
|
||||
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)
|
||||
print >> fileobject, stringdata.encode('UTF-8')
|
||||
fileobject.close()
|
||||
return path
|
||||
except (OSError, IOError):
|
||||
display_error("Couldn't write %s" % stringdata)
|
||||
return ""
|
||||
|
||||
|
||||
def runEmbeddedScript(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_error(
|
||||
'Missing script %s for %s' % (scriptname, itemname))
|
||||
return -1
|
||||
|
||||
# write the script to a temp file
|
||||
scriptpath = os.path.join(tmpdir, scriptname)
|
||||
if writefile(script_text, scriptpath):
|
||||
cmd = ['/bin/chmod', '-R', 'o+x', scriptpath]
|
||||
retcode = subprocess.call(cmd)
|
||||
if retcode:
|
||||
display_error(
|
||||
'Error setting script mode in %s for %s'
|
||||
% (scriptname, itemname))
|
||||
return -1
|
||||
else:
|
||||
display_error(
|
||||
'Cannot write script %s for %s' % (scriptname, itemname))
|
||||
return -1
|
||||
|
||||
# now run the script
|
||||
return runScript(
|
||||
itemname, scriptpath, scriptname, suppress_error=suppress_error)
|
||||
|
||||
|
||||
def runScript(itemname, path, scriptname, suppress_error=False):
|
||||
'''Runs a script, Returns return code.'''
|
||||
display_status_minor(
|
||||
'Running %s for %s ' % (scriptname, itemname))
|
||||
if 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, e:
|
||||
display_error(
|
||||
'Error executing script %s: %s' % (scriptname, str(e)))
|
||||
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_info(msg)
|
||||
|
||||
retcode = proc.poll()
|
||||
if retcode and not suppress_error:
|
||||
display_error(
|
||||
'Running %s for %s failed.' % (scriptname, itemname))
|
||||
display_error("-"*78)
|
||||
for line in scriptoutput:
|
||||
display_error("\t%s" % line.rstrip("\n"))
|
||||
display_error("-"*78)
|
||||
elif not suppress_error:
|
||||
log('Running %s for %s was successful.' % (scriptname, itemname))
|
||||
|
||||
if munkistatusoutput:
|
||||
# clear indeterminate progress bar
|
||||
munkistatus.percent(0)
|
||||
|
||||
return retcode
|
||||
|
||||
|
||||
def forceLogoutNow():
|
||||
"""Force the logout of interactive GUI users and spawn MSU."""
|
||||
|
||||
@@ -22,18 +22,12 @@ Created by Greg Neagle on 2008-11-13.
|
||||
"""
|
||||
|
||||
# standard libs
|
||||
#import calendar
|
||||
#import errno
|
||||
import datetime
|
||||
import os
|
||||
#import re
|
||||
#import shutil
|
||||
import subprocess
|
||||
import socket
|
||||
#import time
|
||||
import urllib2
|
||||
import urlparse
|
||||
#import xattr
|
||||
from OpenSSL.crypto import load_certificate, FILETYPE_PEM
|
||||
|
||||
# our libs
|
||||
@@ -328,12 +322,14 @@ def analyzeInstalledPkgs():
|
||||
if not name in references[pkgid]:
|
||||
references[pkgid].append(name)
|
||||
|
||||
#PKGDATA['itemname_to_pkgid'] = itemname_to_pkgid
|
||||
#PKGDATA['pkgid_to_itemname'] = pkgid_to_itemname
|
||||
PKGDATA['receipts_for_name'] = installedpkgsmatchedtoname
|
||||
PKGDATA['installed_names'] = installed
|
||||
#PKGDATA['partiallyinstalled_names'] = partiallyinstalled
|
||||
PKGDATA['pkg_references'] = references
|
||||
|
||||
# left here for future debugging/testing use....
|
||||
#PKGDATA['itemname_to_pkgid'] = itemname_to_pkgid
|
||||
#PKGDATA['pkgid_to_itemname'] = pkgid_to_itemname
|
||||
#PKGDATA['partiallyinstalled_names'] = partiallyinstalled
|
||||
#PKGDATA['orphans'] = orphans
|
||||
#PKGDATA['matched_orphans'] = matched_orphans
|
||||
#ManagedInstallDir = munkicommon.pref('ManagedInstallDir')
|
||||
@@ -1157,7 +1153,20 @@ def installedState(item_pl):
|
||||
Returns 0 otherwise.
|
||||
"""
|
||||
foundnewer = False
|
||||
|
||||
|
||||
if item_pl.get('installcheck_script'):
|
||||
retcode = munkicommon.runEmbeddedScript(
|
||||
'installcheck_script', item)
|
||||
munkicommon.display_debug1(
|
||||
'installcheck_script returned %s' % retcode)
|
||||
# retcode 0 means install is needed
|
||||
if retcode == 0:
|
||||
return 0
|
||||
# non-zero could be an error or successfully indicating
|
||||
# that an install is not needed
|
||||
# return 1 so we're marked as not needing to be installed
|
||||
return 1
|
||||
|
||||
if item_pl.get('softwareupdatename'):
|
||||
availableAppleUpdates = appleupdates.softwareUpdateList()
|
||||
munkicommon.display_debug2(
|
||||
@@ -1174,8 +1183,8 @@ def installedState(item_pl):
|
||||
item_pl['softwareupdatename'])
|
||||
# return 1 so we're marked as not needing to be installed
|
||||
return 1
|
||||
|
||||
# does 'installs' exist and is it non-empty?
|
||||
|
||||
# does 'installs' exist and is it non-empty?
|
||||
if item_pl.get('installs', None):
|
||||
installitems = item_pl['installs']
|
||||
for item in installitems:
|
||||
@@ -1223,7 +1232,23 @@ def someVersionInstalled(item_pl):
|
||||
|
||||
Args:
|
||||
item_pl: item plist for the item to check for version of.
|
||||
|
||||
Returns a boolean.
|
||||
"""
|
||||
if item_pl.get('installcheck_script'):
|
||||
retcode = munkicommon.runEmbeddedScript(
|
||||
'installcheck_script', item)
|
||||
munkicommon.display_debug1(
|
||||
'installcheck_script returned %s' % retcode)
|
||||
# retcode 0 means install is needed
|
||||
# (ie, item is not installed)
|
||||
if retcode == 0:
|
||||
return False
|
||||
# non-zero could be an error or successfully indicating
|
||||
# that an install is not needed
|
||||
# return True
|
||||
return True
|
||||
|
||||
# does 'installs' exist and is it non-empty?
|
||||
if item_pl.get('installs'):
|
||||
installitems = item_pl['installs']
|
||||
@@ -1264,7 +1289,23 @@ def evidenceThisIsInstalled(item_pl):
|
||||
If any tests pass, the item might be installed.
|
||||
This is used when determining if we can remove the item, thus
|
||||
the attention given to the uninstall method.
|
||||
|
||||
Returns a boolean.
|
||||
"""
|
||||
if item_pl.get('installcheck_script'):
|
||||
retcode = munkicommon.runEmbeddedScript(
|
||||
'installcheck_script', item)
|
||||
munkicommon.display_debug1(
|
||||
'installcheck_script returned %s' % retcode)
|
||||
# retcode 0 means install is needed
|
||||
# (ie, item is not installed)
|
||||
if retcode == 0:
|
||||
return False
|
||||
# non-zero could be an error or successfully indicating
|
||||
# that an install is not needed
|
||||
# return True
|
||||
return True
|
||||
|
||||
foundallinstallitems = False
|
||||
if ('installs' in item_pl and
|
||||
item_pl.get('uninstall_method') != 'removepackages'):
|
||||
|
||||
Reference in New Issue
Block a user