Add support for 'installcheck_script' in pkginfo

This commit is contained in:
Greg Neagle
2012-06-28 11:08:22 -07:00
parent 391fda8c23
commit 2ac7f26720
3 changed files with 158 additions and 112 deletions
+9 -98
View File
@@ -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
+96 -2
View File
@@ -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."""
+53 -12
View File
@@ -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'):