Add support for uninstall scripts embedded into pkginfo.

git-svn-id: http://munki.googlecode.com/svn/trunk@925 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
Greg Neagle
2010-11-22 20:00:15 +00:00
parent a77c1329fb
commit f790b71aea
2 changed files with 99 additions and 53 deletions

View File

@@ -657,6 +657,60 @@ def installWithInfo(dirpath, installlist, only_forced=False):
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 runUninstallScript(name, path):
if munkicommon.munkistatusoutput:
munkistatus.message("Running uninstall script "
"for %s..." % name)
munkistatus.detail("")
# set indeterminate progress bar
munkistatus.percent(-1)
uninstalleroutput = []
proc = subprocess.Popen(path, shell=False, bufsize=1,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
while (proc.poll() == None):
msg = proc.stdout.readline().decode('UTF-8')
# save all uninstaller output in case there is
# an error so we can dump it to the log
uninstalleroutput.append(msg)
msg = msg.rstrip("\n")
munkicommon.display_info(msg)
retcode = proc.poll()
if retcode:
munkicommon.display_error(
"Uninstall of %s failed." % name)
munkicommon.display_error("-"*78)
for line in uninstalleroutput:
munkicommon.display_error("\t%s" % line.rstrip("\n"))
munkicommon.display_error("-"*78)
else:
munkicommon.log("Uninstall of %s was "
"successful." % name)
if munkicommon.munkistatusoutput:
# clear indeterminate progress bar
munkistatus.percent(0)
return retcode
def processRemovals(removallist, only_forced=False):
'''processes removals from the removal list'''
restartFlag = False
@@ -735,50 +789,38 @@ def processRemovals(removallist, only_forced=False):
"info missing from %s" %
name)
elif uninstallmethod[0] == "uninstall_script":
uninstall_script = item.get('uninstall_script')
if uninstall_script:
scriptpath = os.path.join(munkicommon.tmpdir,
"uninstallscript")
if writefile(uninstall_script, scriptpath):
cmd = ['/bin/chmod', '-R', 'o+x', scriptpath]
retcode = subprocess.call(cmd)
if retcode:
munkicommon.display_error("Error setting mode "
"for %s" % scriptpath)
else:
retcode = runUninstallScript(name, scriptpath)
if (retcode == 0 and item.get(
'RestartAction') == "RequireRestart"):
restartFlag = True
os.unlink(scriptpath)
else:
munkicommon.display_error("Cannot write uninstall "
"script for %s" % name)
else:
munkicommon.display_error("Uninstall script missing "
"from %s" % name)
elif os.path.exists(uninstallmethod[0]) and \
os.access(uninstallmethod[0], os.X_OK):
# it's a script or program to uninstall
if munkicommon.munkistatusoutput:
munkistatus.message("Running uninstall script "
"for %s..." % name)
munkistatus.detail("")
# set indeterminate progress bar
munkistatus.percent(-1)
if item.get('RestartAction') == "RequireRestart":
retcode = runUninstallScript(name, uninstallmethod[0])
if (retcode == 0 and
item.get('RestartAction') == "RequireRestart"):
restartFlag = True
cmd = uninstallmethod
uninstalleroutput = []
proc = subprocess.Popen(cmd, shell=False, bufsize=1,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
while (proc.poll() == None):
msg = proc.stdout.readline().decode('UTF-8')
# save all uninstaller output in case there is
# an error so we can dump it to the log
uninstalleroutput.append(msg)
msg = msg.rstrip("\n")
munkicommon.display_info(msg)
retcode = proc.poll()
if retcode:
munkicommon.display_error(
"Uninstall of %s failed." % name)
munkicommon.display_error("-"*78)
for line in uninstalleroutput:
munkicommon.display_error("\t%s" % line.rstrip("\n"))
munkicommon.display_error("-"*78)
else:
munkicommon.log("Uninstall of %s was "
"successful." % name)
if munkicommon.munkistatusoutput:
# clear indeterminate progress bar
munkistatus.percent(0)
else:
munkicommon.log("Uninstall of %s failed because "
"there was no valid uninstall "

View File

@@ -758,7 +758,6 @@ def getAllItemsWithName(name, cataloglist):
if itemlist:
# sort so latest version is first
itemlist.sort(compare_item_versions)
return itemlist
@@ -1070,16 +1069,8 @@ def evidenceThisIsInstalled(item_pl):
This is used when determining if we can remove the item, thus
the attention given to the uninstall method.
"""
if item_pl.get('uninstall_method') == 'removepackages':
# we're supposed to use receipt info to remove
# this, so we should check for relevent receipts
if item_pl.get('receipts'):
if PKGDATA == {}:
# build our database of installed packages
analyzeInstalledPkgs()
if item_pl['name'] in PKGDATA['installed_names']:
return True
elif 'installs' in item_pl:
if ('installs' in item_pl and
item_pl.get('uninstall_method') != 'removepackages'):
installitems = item_pl['installs']
foundallinstallitems = True
for item in installitems:
@@ -1092,6 +1083,12 @@ def evidenceThisIsInstalled(item_pl):
foundallinstallitems = False
if foundallinstallitems:
return True
if item_pl.get('receipts'):
if PKGDATA == {}:
# build our database of installed packages
analyzeInstalledPkgs()
if item_pl['name'] in PKGDATA['installed_names']:
return True
# if we got this far, we failed all the tests, so the item
# must not be installed (or we dont't have the right info...)
@@ -1643,9 +1640,14 @@ def processRemoval(manifestitem, cataloglist, installinfo):
installEvidence = False
for item in infoitems:
munkicommon.display_debug2('Considering item %s-%s for removal info'
% (item['name'], item['version']))
if evidenceThisIsInstalled(item):
installEvidence = True
break
else:
munkicommon.display_debug2('%s-%s not installed.'
% (item['name'], item['version']))
if not installEvidence:
munkicommon.display_detail('%s doesn\'t appear to be installed.' %
@@ -1673,9 +1675,9 @@ def processRemoval(manifestitem, cataloglist, installinfo):
elif uninstallmethod.startswith('Adobe'):
# Adobe CS3/CS4/CS5 product
uninstall_item = item
elif uninstallmethod == 'remove_copied_items':
uninstall_item = item
elif uninstallmethod == 'remove_app':
elif uninstallmethod in ['remove_copied_items',
'remove_app',
'uninstall_script']:
uninstall_item = item
else:
# uninstall_method is a local script.
@@ -1824,6 +1826,8 @@ def processRemoval(manifestitem, cataloglist, installinfo):
elif uninstallmethod == 'remove_app':
if uninstall_item.get('installs', None):
iteminfo['remove_app_info'] = uninstall_item['installs'][0]
elif uninstallmethod == 'uninstall_script':
iteminfo['uninstall_script'] = item.get('uninstall_script','')
# before we add this removal to the list,
# check for installed updates and add them to the