mirror of
https://github.com/munki/munki.git
synced 2026-04-23 04:59:17 -05:00
installer.py: new copyFromDMG method that copies an arbitrary list of items from a mounted DMG to specified locations.
New removeCopiedItems method that removes the same list of items from the startup disk. updatecheck.py: support for new copy_from_dmg and remove_copied_items methods. makepkginfo: support for making pkginfo with new copy_from_dmg method, git-svn-id: http://munki.googlecode.com/svn/trunk@603 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
+120
-39
@@ -36,6 +36,7 @@ import sys
|
||||
import os
|
||||
import re
|
||||
import optparse
|
||||
from optparse import OptionValueError
|
||||
from distutils import version
|
||||
import subprocess
|
||||
import hashlib
|
||||
@@ -63,7 +64,7 @@ def DMGhasSLA(dmgpath):
|
||||
return hasSLA
|
||||
|
||||
|
||||
def getCatalogInfoFromDmg(dmgpath, pkgname='', appname=''):
|
||||
def getCatalogInfoFromDmg(dmgpath, options):
|
||||
"""
|
||||
* Mounts a disk image
|
||||
* Gets catalog info for the first installer item found at the root level.
|
||||
@@ -84,13 +85,13 @@ def getCatalogInfoFromDmg(dmgpath, pkgname='', appname=''):
|
||||
print >>sys.stderr, "Could not mount %s!" % dmgpath
|
||||
exit(-1)
|
||||
|
||||
if pkgname:
|
||||
if options.pkgname:
|
||||
pkgpath = os.path.join(mountpoints[0], pkgname)
|
||||
if os.path.exists(pkgpath):
|
||||
cataloginfo = munkicommon.getPackageMetaData(pkgpath)
|
||||
if cataloginfo:
|
||||
cataloginfo['package_path'] = pkgname
|
||||
elif not appname:
|
||||
elif not options.item:
|
||||
# search for first package at root
|
||||
for fsitem in os.listdir(mountpoints[0]):
|
||||
itempath = os.path.join(mountpoints[0], fsitem)
|
||||
@@ -114,39 +115,61 @@ def getCatalogInfoFromDmg(dmgpath, pkgname='', appname=''):
|
||||
else:
|
||||
# maybe an Adobe installer/updater/patcher?
|
||||
cataloginfo = adobeutils.getAdobeCatalogInfo(mountpoints[0],
|
||||
pkgname)
|
||||
options.pkgname or '')
|
||||
|
||||
if not cataloginfo:
|
||||
# maybe this is an appdmg
|
||||
# look for an app at the top level of the dmg
|
||||
appinfo = {}
|
||||
if appname:
|
||||
itempath = os.path.join(mountpoints[0], appname)
|
||||
if munkicommon.isApplication(itempath):
|
||||
appinfo = getiteminfo(itempath)
|
||||
# maybe this is a drag-n-drop dmg
|
||||
# look for given item or an app at the top level of the dmg
|
||||
iteminfo = {}
|
||||
if options.item:
|
||||
item = options.item
|
||||
itempath = os.path.join(mountpoints[0], item)
|
||||
if os.path.exists(itempath):
|
||||
iteminfo = getiteminfo(itempath)
|
||||
else:
|
||||
print >>sys.stderr, \
|
||||
"%s not found on disk image." % item
|
||||
else:
|
||||
appname = ''
|
||||
for item in os.listdir(mountpoints[0]):
|
||||
itempath = os.path.join(mountpoints[0], item)
|
||||
# no item specified; look for an application at root of
|
||||
# mounted dmg
|
||||
item = ''
|
||||
for itemname in os.listdir(mountpoints[0]):
|
||||
itempath = os.path.join(mountpoints[0], itemname)
|
||||
if munkicommon.isApplication(itempath):
|
||||
appname = item
|
||||
appinfo = getiteminfo(itempath)
|
||||
if appinfo:
|
||||
item = itemname
|
||||
iteminfo = getiteminfo(itempath)
|
||||
if iteminfo:
|
||||
break
|
||||
|
||||
if appinfo:
|
||||
appinfo['path'] = os.path.join("/Applications", appname)
|
||||
if iteminfo:
|
||||
if options.destinationpath:
|
||||
iteminfo['path'] = os.path.join(options.destinationpath,
|
||||
item)
|
||||
else:
|
||||
iteminfo['path'] = os.path.join("/Applications", item)
|
||||
cataloginfo = {}
|
||||
cataloginfo['name'] = appinfo.get('CFBundleName',
|
||||
os.path.splitext(appname)[0])
|
||||
cataloginfo['name'] = iteminfo.get('CFBundleName',
|
||||
os.path.splitext(item)[0])
|
||||
cataloginfo['version'] = \
|
||||
munkicommon.padVersionString(
|
||||
appinfo.get('CFBundleShortVersionString', "0")
|
||||
iteminfo.get('CFBundleShortVersionString', "0")
|
||||
,5)
|
||||
cataloginfo['installs'] = [appinfo]
|
||||
cataloginfo['installer_type'] = "appdmg"
|
||||
cataloginfo['installs'] = [iteminfo]
|
||||
cataloginfo['installer_type'] = "copy_from_dmg"
|
||||
item_to_copy = {}
|
||||
item_to_copy['source_item'] = item
|
||||
item_to_copy['destination_path'] = options.destinationpath or \
|
||||
"/Applications"
|
||||
if options.user:
|
||||
item_to_copy['user'] = options.user
|
||||
if options.user:
|
||||
item_to_copy['group'] = options.group
|
||||
if options.user:
|
||||
item_to_copy['mode'] = options.mode
|
||||
|
||||
cataloginfo['items_to_copy'] = [item_to_copy]
|
||||
cataloginfo['uninstallable'] = True
|
||||
cataloginfo['uninstall_method'] = "remove_app"
|
||||
cataloginfo['uninstall_method'] = "remove_copied_items"
|
||||
|
||||
#eject the dmg
|
||||
munkicommon.unmountdmg(mountpoints[0])
|
||||
@@ -240,7 +263,20 @@ def getiteminfo(itempath):
|
||||
if os.path.isfile(itempath):
|
||||
infodict['md5checksum'] = getmd5hash(itempath)
|
||||
return infodict
|
||||
|
||||
|
||||
|
||||
def check_mode(option, opt, value, parser):
|
||||
# callback to check --mode options
|
||||
modes = value.lower().replace(',',' ').split()
|
||||
value = None
|
||||
rex = re.compile("[augo]+[=+-][rstwxXugo]+")
|
||||
for mode in modes:
|
||||
if rex.match(mode):
|
||||
value = mode if not value else (value + "," + mode)
|
||||
else:
|
||||
raise OptionValueError("option %s: invalid mode: %s" %
|
||||
(opt, mode))
|
||||
setattr(parser.values, option.dest, value)
|
||||
|
||||
|
||||
def main():
|
||||
@@ -267,14 +303,23 @@ def main():
|
||||
|
||||
If this flag is missing, the AdobeUber* files should
|
||||
be at the top level of the mounted dmg.''')
|
||||
p.add_option('--appname', '-a',
|
||||
p.add_option('--itemname', '-i', '--appname', '-a',
|
||||
metavar='ITEM',
|
||||
dest='item',
|
||||
help='''Optional flag.
|
||||
|
||||
-If the installer item is a disk image with a
|
||||
drag-and-drop application, APPNAME is the name or
|
||||
relative path of the application to be installed.
|
||||
Useful if there is more than one application at the
|
||||
drag-and-drop item, ITEMNAME is the name or
|
||||
relative path of the item to be installed.
|
||||
Useful if there is more than one item at the
|
||||
root of the dmg.''')
|
||||
p.add_option('--destinationpath', '-d',
|
||||
help='''Optional flag.
|
||||
|
||||
If the installer item is a disk image with a
|
||||
drag-and-drop item, this is the path to which
|
||||
the item should be copied. Defaults to
|
||||
"/Applications".''')
|
||||
p.add_option('--uninstallerdmg', '-u',
|
||||
help='''Optional flag.
|
||||
|
||||
@@ -301,6 +346,46 @@ def main():
|
||||
If the installer item is a disk image containing an
|
||||
Adobe CS5 product, SERIALNUMBER is the product
|
||||
serial number.''')
|
||||
p.add_option('--catalog', '-c', action="append",
|
||||
help='''Optional flag.
|
||||
|
||||
Specifies in which catalog the item should appear. The
|
||||
default is 'testing'. Can be specified multiple times
|
||||
to add the item to multiple catalogs.''')
|
||||
p.add_option('-o','--owner',
|
||||
metavar='USER',
|
||||
dest='user',
|
||||
help='''Optional flag.
|
||||
|
||||
If the installer item is a disk image with a
|
||||
drag-and-drop application, this sets the owner
|
||||
of the application. This flag may be either a
|
||||
UID or a symbolic name. The owner will be
|
||||
set recursively on the application.''')
|
||||
p.add_option('-g','--group',
|
||||
metavar='GROUP',
|
||||
dest='group',
|
||||
help='''Optional flag.
|
||||
|
||||
If the installer item is a disk image with a
|
||||
drag-and-drop application, this sets the group
|
||||
of the application. This flag may be either a
|
||||
GID or a symbolic name. The group will be
|
||||
set recursively on the application.''')
|
||||
p.add_option('-m','--mode',
|
||||
metavar='MODE',
|
||||
dest='mode',
|
||||
action='callback',
|
||||
type='string',
|
||||
callback=check_mode,
|
||||
help='''Optional flag.
|
||||
|
||||
If the installer item is a disk image with a
|
||||
drag-and-drop application, this sets the mode
|
||||
of the application. The specified mode must
|
||||
be in symbolic form, see the manpage for
|
||||
chmod(1) for more information. The mode is
|
||||
applied recursively.''')
|
||||
|
||||
options, arguments = p.parse_args()
|
||||
|
||||
@@ -335,14 +420,7 @@ def main():
|
||||
itemsize += int(os.lstat(filename).st_size)
|
||||
|
||||
if item.endswith('.dmg'):
|
||||
pkgname = ''
|
||||
appname = ''
|
||||
if options.pkgname:
|
||||
pkgname = options.pkgname
|
||||
if options.appname:
|
||||
appname = options.appname
|
||||
|
||||
catinfo = getCatalogInfoFromDmg(item, pkgname, appname)
|
||||
catinfo = getCatalogInfoFromDmg(item, options)
|
||||
if not catinfo:
|
||||
print >>sys.stderr, \
|
||||
"Could not find a supported installer item in %s!" % \
|
||||
@@ -402,7 +480,10 @@ def main():
|
||||
uninstallerpath
|
||||
|
||||
# some metainfo
|
||||
catinfo['catalogs'] = ['testing']
|
||||
if options.catalog:
|
||||
catinfo['catalogs'] = options.catalog
|
||||
else:
|
||||
catinfo['catalogs'] = ['testing']
|
||||
if catinfo.get('receipts',None):
|
||||
catinfo['uninstallable'] = True
|
||||
catinfo['uninstall_method'] = "removepackages"
|
||||
|
||||
@@ -945,7 +945,7 @@ def getAdobeInstallInfo(mountpoint=None,
|
||||
return adobeInstallInfo
|
||||
|
||||
|
||||
def getAdobeCatalogInfo(mountpoint, pkgname):
|
||||
def getAdobeCatalogInfo(mountpoint, pkgname=""):
|
||||
'''Used by makepkginfo to build pkginfo data for Adobe
|
||||
installers/updaters'''
|
||||
|
||||
|
||||
+284
-126
@@ -23,7 +23,6 @@ munki module to automatically install pkgs, mpkgs, and dmgs
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
#import tempfile
|
||||
|
||||
import adobeutils
|
||||
import munkicommon
|
||||
@@ -309,7 +308,152 @@ def copyAppFromDMG(dmgpath):
|
||||
os.path.basename(dmgpath))
|
||||
return -1
|
||||
|
||||
|
||||
def copyFromDMG(dmgpath, itemlist):
|
||||
# copies items from DMG
|
||||
if not itemlist:
|
||||
munkicommon.display_error("No items to copy!")
|
||||
return -1
|
||||
|
||||
munkicommon.display_status("Mounting disk image %s" %
|
||||
os.path.basename(dmgpath))
|
||||
mountpoints = munkicommon.mountdmg(dmgpath)
|
||||
if mountpoints:
|
||||
mountpoint = mountpoints[0]
|
||||
retcode = 0
|
||||
for item in itemlist:
|
||||
itemname = item.get("source_item")
|
||||
if not itemname:
|
||||
munkicommon.display_error("Missing name of item to copy!")
|
||||
retcode = -1
|
||||
|
||||
if retcode == 0:
|
||||
itempath = os.path.join(mountpoint, itemname)
|
||||
if os.path.exists(itempath):
|
||||
destpath = item.get("destination_path")
|
||||
if os.path.exists(destpath):
|
||||
# remove item if it already exists
|
||||
olditem = os.path.join(destpath, itemname)
|
||||
if os.path.exists(olditem):
|
||||
retcode = subprocess.call(
|
||||
["/bin/rm", "-rf", olditem])
|
||||
if retcode:
|
||||
munkicommon.display_error(
|
||||
"Error removing existing %s" % olditem)
|
||||
else:
|
||||
munkicommon.display_error(
|
||||
"Destination path %s does not exist!" % destpath)
|
||||
retcode = -1
|
||||
else:
|
||||
munkicommon.display_error(
|
||||
"Source item %s does not exist!" % itemname)
|
||||
retcode = -1
|
||||
|
||||
if retcode == 0:
|
||||
munkicommon.display_status(
|
||||
"Copying %s to %s" % (itemname, destpath))
|
||||
retcode = subprocess.call(["/bin/cp", "-R",
|
||||
itempath, destpath])
|
||||
if retcode:
|
||||
munkicommon.display_error(
|
||||
"Error copying %s to %s" %
|
||||
(itempath, destpath))
|
||||
|
||||
destitem = os.path.join(destpath, itemname)
|
||||
if (retcode == 0) and ('user' in item):
|
||||
munkicommon.display_detail(
|
||||
"Setting owner for '%s'" % destitem)
|
||||
cmd = ['/usr/sbin/chown', '-R', item['user'], destitem]
|
||||
retcode = subprocess.call(cmd)
|
||||
if retcode:
|
||||
munkicommon.display_error("Error setting owner for %s" %
|
||||
(destitem))
|
||||
|
||||
if (retcode == 0) and ('group' in item):
|
||||
munkicommon.display_detail(
|
||||
"Setting group for '%s'" % destitem)
|
||||
cmd = ['/usr/bin/chgrp', '-R', item['group'], destitem]
|
||||
retcode = subprocess.call(cmd)
|
||||
if retcode:
|
||||
munkicommon.display_error("Error setting group for %s" %
|
||||
(destitem))
|
||||
|
||||
if (retcode == 0) and ('mode' in item):
|
||||
munkicommon.display_detail("Setting mode for '%s'" % destitem)
|
||||
cmd = ['/bin/chmod', '-R', options['mode'], destitem]
|
||||
retcode = subprocess.call(cmd)
|
||||
if retcode:
|
||||
munkicommon.display_error("Error setting mode for %s" %
|
||||
(destitem))
|
||||
|
||||
if retcode == 0:
|
||||
# remove com.apple.quarantine attribute from copied item
|
||||
cmd = ["/usr/bin/xattr", destitem]
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
(out, err) = p.communicate()
|
||||
if out:
|
||||
xattrs = out.splitlines()
|
||||
if "com.apple.quarantine" in xattrs:
|
||||
err = subprocess.call(["/usr/bin/xattr", "-d",
|
||||
"com.apple.quarantine",
|
||||
destitem])
|
||||
|
||||
if retcode:
|
||||
# we encountered an error on this iteration;
|
||||
# should not continue.
|
||||
break
|
||||
|
||||
if retcode == 0:
|
||||
# let the user know we completed successfully
|
||||
munkicommon.display_status(
|
||||
"The software was successfully installed.")
|
||||
munkicommon.unmountdmg(mountpoint)
|
||||
return retcode
|
||||
else:
|
||||
munkicommon.display_error("No mountable filesystems on %s" %
|
||||
os.path.basename(dmgpath))
|
||||
return -1
|
||||
|
||||
|
||||
def removeCopiedItems(itemlist):
|
||||
# removes filesystem items based on info in itemlist
|
||||
# typically installed via DMG
|
||||
|
||||
retcode = 0
|
||||
if not itemlist:
|
||||
munkicommon.display_error("Nothing to remove!")
|
||||
return -1
|
||||
|
||||
for item in itemlist:
|
||||
itemname = item.get("source_item")
|
||||
if not itemname:
|
||||
munkicommon.display_error("Missing item name to remove.")
|
||||
retcode = -1
|
||||
break
|
||||
destpath = item.get("destination_path")
|
||||
if not destpath:
|
||||
munkicommon.display_error("Missing path for item to remove.")
|
||||
retcode = -1
|
||||
break
|
||||
path_to_remove = os.path.join(destpath, itemname)
|
||||
if os.path.exists(path_to_remove):
|
||||
munkicommon.display_status("Removing %s" % path_to_remove)
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", path_to_remove])
|
||||
if retcode:
|
||||
munkicommon.display_error("Removal error for %s" %
|
||||
path_to_remove)
|
||||
break
|
||||
else:
|
||||
# path_to_remove doesn't exist
|
||||
# note it, but not an error
|
||||
munkicommon.display_detail("Path %s doesn't exist." %
|
||||
path_to_remove)
|
||||
|
||||
return retcode
|
||||
|
||||
def installWithInfo(dirpath, installlist):
|
||||
"""
|
||||
Uses the installlist to install items in the
|
||||
@@ -345,8 +489,17 @@ def installWithInfo(dirpath, installlist):
|
||||
installer_type = item.get("installer_type","")
|
||||
if installer_type.startswith("Adobe"):
|
||||
retcode = adobeutils.doAdobeInstall(item)
|
||||
elif installer_type == "copy_from_dmg":
|
||||
retcode = copyFromDMG(itempath, item.get('items_to_copy'))
|
||||
elif installer_type == "appdmg":
|
||||
retcode = copyAppFromDMG(itempath)
|
||||
retcode = copyAppFromDMG(itempath,
|
||||
item.get('installer_options',{}))
|
||||
elif installer_type != "":
|
||||
# we've encountered an installer type
|
||||
# we don't know how to handle
|
||||
munkicommon.log("Unsupported install type: %s" %
|
||||
installer_type)
|
||||
retcode = -99
|
||||
else:
|
||||
# must be Apple installer package
|
||||
suppressBundleRelocation = item.get(
|
||||
@@ -472,134 +625,139 @@ def processRemovals(removallist):
|
||||
for item in removallist:
|
||||
if munkicommon.stopRequested():
|
||||
return restartFlag
|
||||
if 'installed' in item:
|
||||
if item['installed']:
|
||||
index += 1
|
||||
name = item.get('display_name') or item.get('name') or \
|
||||
item.get('manifestitem')
|
||||
if munkicommon.munkistatusoutput:
|
||||
munkistatus.message("Removing %s (%s of %s)..." %
|
||||
(name, index, len(removallist)))
|
||||
munkistatus.detail("")
|
||||
munkistatus.percent(-1)
|
||||
else:
|
||||
munkicommon.display_status("Removing %s (%s of %s)..." %
|
||||
(name, index, len(removallist)))
|
||||
|
||||
if 'uninstall_method' in item:
|
||||
uninstallmethod = item['uninstall_method'].split(' ')
|
||||
if uninstallmethod[0] == "removepackages":
|
||||
if 'packages' in item:
|
||||
if item.get('RestartAction') == "RequireRestart":
|
||||
restartFlag = True
|
||||
retcode = removepackages(item['packages'],
|
||||
forcedeletebundles=True)
|
||||
if retcode:
|
||||
if retcode == -128:
|
||||
message = ("Uninstall of %s was "
|
||||
"cancelled." % name)
|
||||
else:
|
||||
message = "Uninstall of %s failed." % name
|
||||
munkicommon.display_error(message)
|
||||
else:
|
||||
munkicommon.log("Uninstall of %s was "
|
||||
"successful." % name)
|
||||
|
||||
elif uninstallmethod[0].startswith("Adobe"):
|
||||
retcode = adobeutils.doAdobeRemoval(item)
|
||||
|
||||
elif uninstallmethod[0] == "remove_app":
|
||||
remove_app_info = item.get('remove_app_info',None)
|
||||
if remove_app_info:
|
||||
path_to_remove = remove_app_info['path']
|
||||
munkicommon.display_status("Removing %s" %
|
||||
path_to_remove)
|
||||
retcode = subprocess.call(["/bin/rm", "-rf",
|
||||
path_to_remove])
|
||||
if retcode:
|
||||
munkicommon.display_error("Removal error "
|
||||
"for %s" %
|
||||
path_to_remove)
|
||||
if not item.get('installed'):
|
||||
# not installed, so skip it
|
||||
continue
|
||||
|
||||
index += 1
|
||||
name = item.get('display_name') or item.get('name') or \
|
||||
item.get('manifestitem')
|
||||
if munkicommon.munkistatusoutput:
|
||||
munkistatus.message("Removing %s (%s of %s)..." %
|
||||
(name, index, len(removallist)))
|
||||
munkistatus.detail("")
|
||||
munkistatus.percent(-1)
|
||||
else:
|
||||
munkicommon.display_status("Removing %s (%s of %s)..." %
|
||||
(name, index, len(removallist)))
|
||||
|
||||
if 'uninstall_method' in item:
|
||||
uninstallmethod = item['uninstall_method'].split(' ')
|
||||
if uninstallmethod[0] == "removepackages":
|
||||
if 'packages' in item:
|
||||
if item.get('RestartAction') == "RequireRestart":
|
||||
restartFlag = True
|
||||
retcode = removepackages(item['packages'],
|
||||
forcedeletebundles=True)
|
||||
if retcode:
|
||||
if retcode == -128:
|
||||
message = ("Uninstall of %s was "
|
||||
"cancelled." % name)
|
||||
else:
|
||||
munkicommon.display_error("Application removal "
|
||||
"info 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":
|
||||
restartFlag = True
|
||||
|
||||
cmd = uninstallmethod
|
||||
uninstalleroutput = []
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
while (p.poll() == None):
|
||||
msg = p.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")
|
||||
if munkicommon.munkistatusoutput:
|
||||
# do nothing with the output
|
||||
pass
|
||||
else:
|
||||
print msg
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
message = "Uninstall of %s failed." % name
|
||||
print >>sys.stderr, message
|
||||
munkicommon.log(message)
|
||||
message = \
|
||||
"-------------------------------------------------"
|
||||
print >>sys.stderr, message
|
||||
munkicommon.log(message)
|
||||
for line in uninstalleroutput:
|
||||
print >>sys.stderr, " ", line.rstrip("\n")
|
||||
munkicommon.log(line.rstrip("\n"))
|
||||
message = \
|
||||
"-------------------------------------------------"
|
||||
print >>sys.stderr, message
|
||||
munkicommon.log(message)
|
||||
else:
|
||||
munkicommon.log("Uninstall of %s was "
|
||||
"successful." % name)
|
||||
|
||||
if munkicommon.munkistatusoutput:
|
||||
# clear indeterminate progress bar
|
||||
munkistatus.percent(0)
|
||||
|
||||
munkicommon.display_error(message)
|
||||
else:
|
||||
munkicommon.log("Uninstall of %s failed because "
|
||||
"there was no valid uninstall "
|
||||
"method." % name)
|
||||
retcode = -99
|
||||
munkicommon.log("Uninstall of %s was "
|
||||
"successful." % name)
|
||||
|
||||
elif uninstallmethod[0].startswith("Adobe"):
|
||||
retcode = adobeutils.doAdobeRemoval(item)
|
||||
|
||||
elif uninstallmethod[0] == "remove_copied_items":
|
||||
retcode = removeCopiedItems(item.get('items_to_remove'))
|
||||
|
||||
elif uninstallmethod[0] == "remove_app":
|
||||
remove_app_info = item.get('remove_app_info',None)
|
||||
if remove_app_info:
|
||||
path_to_remove = remove_app_info['path']
|
||||
munkicommon.display_status("Removing %s" %
|
||||
path_to_remove)
|
||||
retcode = subprocess.call(["/bin/rm", "-rf",
|
||||
path_to_remove])
|
||||
if retcode:
|
||||
munkicommon.display_error("Removal error "
|
||||
"for %s" %
|
||||
path_to_remove)
|
||||
else:
|
||||
munkicommon.display_error("Application removal "
|
||||
"info 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":
|
||||
restartFlag = True
|
||||
|
||||
cmd = uninstallmethod
|
||||
uninstalleroutput = []
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
while (p.poll() == None):
|
||||
msg = p.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")
|
||||
if munkicommon.munkistatusoutput:
|
||||
# do nothing with the output
|
||||
pass
|
||||
else:
|
||||
print msg
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
message = "Uninstall of %s failed." % name
|
||||
print >>sys.stderr, message
|
||||
munkicommon.log(message)
|
||||
message = \
|
||||
"-------------------------------------------------"
|
||||
print >>sys.stderr, message
|
||||
munkicommon.log(message)
|
||||
for line in uninstalleroutput:
|
||||
print >>sys.stderr, " ", line.rstrip("\n")
|
||||
munkicommon.log(line.rstrip("\n"))
|
||||
message = \
|
||||
"-------------------------------------------------"
|
||||
print >>sys.stderr, message
|
||||
munkicommon.log(message)
|
||||
else:
|
||||
munkicommon.log("Uninstall of %s was "
|
||||
"successful." % name)
|
||||
|
||||
# record removal success/failure
|
||||
if retcode == 0:
|
||||
success_msg = "Removal of %s: SUCCESSFUL" % name
|
||||
munkicommon.log(success_msg, "Install.log")
|
||||
munkicommon.report[
|
||||
'RemovalResults'].append(success_msg)
|
||||
else:
|
||||
failure_msg = "Removal of %s: " % name + \
|
||||
" FAILED with return code: %s" % retcode
|
||||
munkicommon.log(failure_msg, "Install.log")
|
||||
munkicommon.report[
|
||||
'RemovalResults'].append(failure_msg)
|
||||
|
||||
if munkicommon.munkistatusoutput:
|
||||
# clear indeterminate progress bar
|
||||
munkistatus.percent(0)
|
||||
|
||||
else:
|
||||
munkicommon.log("Uninstall of %s failed because "
|
||||
"there was no valid uninstall "
|
||||
"method." % name)
|
||||
retcode = -99
|
||||
|
||||
# record removal success/failure
|
||||
if retcode == 0:
|
||||
success_msg = "Removal of %s: SUCCESSFUL" % name
|
||||
munkicommon.log(success_msg, "Install.log")
|
||||
munkicommon.report[
|
||||
'RemovalResults'].append(success_msg)
|
||||
else:
|
||||
failure_msg = "Removal of %s: " % name + \
|
||||
" FAILED with return code: %s" % retcode
|
||||
munkicommon.log(failure_msg, "Install.log")
|
||||
munkicommon.report[
|
||||
'RemovalResults'].append(failure_msg)
|
||||
|
||||
return restartFlag
|
||||
|
||||
|
||||
|
||||
@@ -1327,6 +1327,9 @@ def processInstall(manifestitem, cataloglist, installinfo):
|
||||
iteminfo['adobe_package_name'] = pl['adobe_package_name']
|
||||
if 'package_path' in pl:
|
||||
iteminfo['package_path'] = pl['package_path']
|
||||
if 'items_to_copy' in pl:
|
||||
# used with copy_from_dmg installer type
|
||||
iteminfo['items_to_copy'] = pl['items_to_copy']
|
||||
installinfo['managed_installs'].append(iteminfo)
|
||||
if nameAndVersion(manifestitemname)[1] == '':
|
||||
# didn't specify a specific version, so
|
||||
@@ -1566,6 +1569,8 @@ 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':
|
||||
uninstall_item = item
|
||||
else:
|
||||
@@ -1714,6 +1719,8 @@ def processRemoval(manifestitem, cataloglist, installinfo):
|
||||
"uninstaller for %s"
|
||||
% iteminfo["name"])
|
||||
return False
|
||||
elif uninstallmethod == "remove_copied_items":
|
||||
iteminfo['items_to_remove'] = item.get('items_to_copy',[])
|
||||
elif uninstallmethod == "remove_app":
|
||||
if uninstall_item.get('installs',None):
|
||||
iteminfo['remove_app_info'] = uninstall_item['installs'][0]
|
||||
|
||||
Reference in New Issue
Block a user