Hello, all.
I'm trying to deploy an older version of Wacom tablet driver to several workstations with tablets not supported by the version that was included in our image. The first part of this process is removing the current driver so that I can install the older one. I've extracted the Wacom-developed uninstall script from the Wacom Tablet Utility and setup a policy that runs it and nothing else. Here is the log from the JSS. You can see that the script exit code is zero, yet it is still showing that the policy failed:
/usr/sbin/jamf is version 8.52
Executing Policy Wacom Separate Uninstall Script...
Mounting smb://casper.acad.ccad.edu/JSS_Repository to /Volumes/JSS_Repository...
Running Script wacomTablet_uninstall.pl...
Script Exit Code:0
Script Result: Deleting StartupItems - Driver:
Deleting Driver.kext:
Can't exec "/private/tmp/SystemLoginItemTool": No such file or directory at /private/tmp/wacomTablet_uninstall.pl line 262.
Tablet Driver not running.:
Unloading KEXTs:
(kernel) Kext com.Wacom.iokit.TabletDriver not found for unload request.
Failed to unload com.Wacom.iokit.TabletDriver - (libkern/kext) not found.
(kernel) Kext com.wacom.driver.HIDTablet not found for unload request.
Failed to unload com.wacom.driver.HIDTablet - (libkern/kext) not found.
Deleting Preferences:
readdir() attempted on invalid dirhandle TABLETDIR at /private/tmp/wacomTablet_uninstall.pl line 413.
closedir() attempted on invalid dirhandle TABLETDIR at /private/tmp/wacomTablet_uninstall.pl line 422.
Removing /Users/student/Library/Preferences/com.wacom.wacomtablet.prefs
Removing /Users/student/Library/Preferences/com.wacom.wacomtouch.prefs
Deleting Control Panels:
No receipt for 'com.wacom.installwacomtablet' found at '/'.
Deleting Frameworks
Deleting Internet Plugins:
Done.:
Unmounting file server...
The script itself is below. I don't really do perl, but I see the main routine ends with exit 0, and as previously mentioned, the JSS is picking up on that exit code. Can anyone help me figure out how I can get a successful result with the policy so that I have an accurate listing of where this script has run successfully?
Thanks,
Eric
#! /usr/bin/perl -w
#-----------------------------------------------------------------------------
#
# FILE NAME
#
# uninstall_base.pl
#
# PURPOSE
#
# Remove Tablet Driver From System. Also allows removing preferences for the
# current user or all users.
#
# COPYRIGHT
#
# Copyright WACOM Technology, Inc. 2003 - 2009
# All rights reserved.
#
#-----------------------------------------------------------------------------
use strict;
use Env;
use utf8;
use File::Basename;
use File::Path "rmtree";
use Getopt::Long;
use Data::Dumper;
# Make %ENV safer; this script runs as root
# removes search paths, so all commands must be issued with fully-qualified paths
$ENV{PATH} = "";
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
##############################################################################
# Prototypes
sub killRunning;
sub deleteReciepts;
sub deleteProLanguageFiles;
sub deleteSystemFiles;
sub deleteWacomApplications;
sub deleteCurrentUserPreferenceFiles;
sub deleteAllPreferenceFiles;
sub deleteWacomInternetPlugins;
sub removeDirIfEmpty;
sub removeFileIfExists;
sub deleteUserDefaultsFile;
sub createRestorePrefsFile;
sub quitPreferences;
sub quitAllPreferences;
##############################################################################
# Globals
my $gDriverExecutable = "WacomTabletDriver";
my $gDriverApp = "$gDriverExecutable.app";
my $gDriverSpringboard = "WacomTabletSpringboard";
my $gPrefPane = "WacomTablet.prefpane";
my $gKext = "Wacom Tablet.kext";
my $gLaunchAgent = "com.wacom.wacomtablet.plist";
my $gPrefFile = "com.wacom.wacomtablet.prefs";
my $gCFPlugin = "TabletDriverCFPlugin.bundle";
my $gAppFolder = "/Applications/Wacom Tablet.localized";
my $gUninstaller = "Wacom Tablet Utility.app";
my $gUninstaller_Old = "Removed Wacom Tablet.app";
my $gDocReceipt = "Wacom Tablet Docs.txt";
my $gInstallerPkg = "Install Wacom Tablet.pkg";
my $gDriverAppPath = "/Library/Application Support/Tablet";
my $gOtherDriverKext = "Pen Tablet.kext";
my $gSafariPlugin = "WacomSafari.plugin";
my $gNetscapePlugin = "WacomNetscape.plugin";
my $gWacomTabletPlugin = "WacomTabletPlugin.plugin";
my $gTouchPrefFile = "com.wacom.wacomtouch.prefs";
my $gTouchUserDefaults = "com.wacomtouchtablet.userdefaults.xml";
my $gDriverUserDefaults = "com.wacomtablet.userdefaults.xml";
my $gTouchRestorePrefFile = "com.wacom.wacomtouch.restore.prefs";
my $gPenRestorePrefFile = "com.wacom.wacomtablet.restore.prefs";
# Bundle identifiers
my $gInstallerBundleIdentifier = "com.wacom.installwacomtablet";
my $gKextIdentifier = "com.wacom.kext.wacomtablet";
# Xtras
my $gXtrasFolder = "";
my $gWacomXtraFile = "";
my $gWacomDataXtraFile = "";
#------------------
# This script is customized based on whether it removes Consumer or Professional
# drivers. All the variables declaring the filenames are spliced into this
# script at build time. We can't just include them because this script runs
# under root privileges, and inclusion is consequently disabled as a security
# risk.
my $gCoordinatorExecutable = "TabletDriver";
my $gOldPrefFile = "com.wacom.tabletpreferences";
my $gSysPrefsExecutable = "System Preferences";
my $wacomLog = "wacomlog.txt";
#command line options & hash table
my $prefsOnly = 0;
my $currentPrefsOnly = "";
my $restartDrivers = 0;
my %commandOpts = ("prefsonly" => $prefsOnly, "currentprefsonly=s" => $currentPrefsOnly, "restartdrivers" => $restartDrivers);
my @processList = `/bin/ps -axcopid,command`;
my @currentUserProcessList = `/bin/ps -xcopid,command`;
##############################################################################
# main
##############################################################################
sub main
{
GetOptions(%commandOpts);
if ($prefsOnly) { quitAllPreferences(); deleteAllPreferenceFiles(); hupDriver(); } elsif($currentPrefsOnly) #Remove current user's prefs only { quitPreferences(); deleteCurrentUserPreferenceFiles($currentPrefsOnly); hupDriver(); } elsif($restartDrivers) #restart drivers (called for resetting preferences) { quitPreferences(); #if the preference pane is running, quit hupDriver(); } else #Remove everything (uninstall) { quitAllPreferences();
# Delete running programs, *then* kill them so launchd won't relaunch them # even if KeepAlive is enabled. deleteSystemFiles(); killRunning();
deleteAllPreferenceFiles(); deleteWacomApplications(); deleteReceipts(); deleteFrameworks(); deleteWacomInternetPlugins(); deleteUserDefaultsFile(); }
print "Done.: ";
exit 0;
}
##############################################################################
# The Sub Routines
##############################################################################
##############################################################################
sub hupDriver
{
# defect 5670 It's problematic to restart driver for inactive user sessions.
# All driver processes would be competing for resources such as connection
# to the tablet and the current driver process may lose.
# So we only restart the driver in the current session.
# The inactive sessions will pick up the change after logout and back in.
foreach my $process (@currentUserProcessList)
{
if($process =~ m/^s*(d+)s+$gDriverExecutable$/)
{
print "Restarting Driver $1
";
kill "HUP", $1;
return;
}
}
}
##############################################################################
sub quitPreferences
{
my @processUserListPre = `/bin/ps -xcopid,command`;
my @processUserListPost = ();
my $script = qq| tell application "System Preferences" to quit|;
# Defect 6001 removal of prefs while System Preferences is running # was causing System Preferences to crash. Problem fixed by # quitting System Preferences before prefs file is removed.
foreach my $process (@processUserListPre) { if($process =~ m/^s*(d+)s+$gSysPrefsExecutable$/) { system(qq|/usr/bin/osascript -e '$script'|); return; } }
@processUserListPost = `/bin/ps -xcopid,command`;
foreach my $process (@processUserListPost)
{
if($process =~ m/^s*(d+)s+$gSysPrefsExecutable$/)
{
kill "TERM", $1;
}
}
}
##############################################################################
sub quitAllPreferences
{
my @allProcessList = ();
quitPreferences();
# Defect 6001 removal of prefs while System Preferences is running # was causing System Preferences to crash. Problem fixed by # quitting System Preferences before prefs file is removed.
@allProcessList = `/bin/ps -axcopid,command`;
foreach my $process (@allProcessList)
{
if($process =~ m/^s*(d+)s+$gSysPrefsExecutable$/)
{
kill "TERM", $1;
}
}
}
##############################################################################
# killRunning
#
# Purpose: Terminating running executables or loaded code.
#
sub killRunning
{
my @killPIDs;
#delete System login item entry my $sysLogItemTool = dirname($0)."/SystemLoginItemTool"; $sysLogItemTool =~ m/^(.*)$/; $sysLogItemTool = $1; #get around perl security
# SystemLoginItem tool can not run link on 10.1.5 # so ignore any errors from it. That's ok because it's not needed on 10.1.5 anyway. eval { system($sysLogItemTool, "-d", "$gDriverAppPath/$gDriverApp"); };
# Find PIDs of processes to kill foreach my $process (@processList) { # Driver if( $process =~ m/^s*(d+)s+$gDriverExecutable$/ ) { push @killPIDs, $1; }
# Coordinator (only if the other driver isn't running) if( isOtherDriverInstalled() == 0 && $process =~ m/^s*(d+)s+$gCoordinatorExecutable$/ ) { push @killPIDs, $1; } }
# Kill 'em if ( scalar @killPIDs ) { print "Killing Tablet Driver: @killPIDs "; kill "QUIT", @killPIDs; } else { print "Tablet Driver not running.: "; }
print "Unloading KEXTs: "; `/sbin/kextunload -m com.Wacom.iokit.TabletDriver`; `/sbin/kextunload -m com.wacom.driver.HIDTablet`;
}#end killRunning
##############################################################################
# deleteReceipts
#
# Purpose: Delete the evidence that this install ever existed. Now when
# running the installer again, it won't complain about
# pre-existing versions.
#
sub deleteReceipts
{
my $delFile;
# Receipt tracking has been an evolving field: # 10.4: Installed packages, stripped of contents, are put in /Library/Receipts # 10.5: Some (newer?) packages are registered in a database format which can # be manipulated with the new pkgutil command-line tool. Traditional # receipts are still written too, and are the exclusive means of # tracking older package formats such as Wacom's installer. # 10.6: Now everything goes to the database. You must use pkgutil.
# Snow Leopard: try pkgutil. system("/usr/sbin/pkgutil", "--forget", $gInstallerBundleIdentifier);
# Earlier: delete the files rmtree( "/Library/Receipts/$gInstallerPkg" ); rmtree( "/Library/Receipts/$gDocReceipt" );
}#end deleteReceipts
##############################################################################
sub deleteSystemFiles
{
my $extensionsDir = "/System/Library/Extensions";
my $launchAgents = "/Library/LaunchAgents";
my $xtrasFolderLength = length($gXtrasFolder);
print "Deleting StartupItems - Driver: ";
# Tablet Driver rmtree("$gDriverAppPath/$gDriverApp"); rmtree("$gDriverAppPath/$gDriverSpringboard");
# if installer happened to have failed make sure the Xtras temp folder gets deleted. if($xtrasFolderLength > 0) { rmtree(["$gDriverAppPath/$gXtrasFolder/$gWacomXtraFile"], 0, 0); rmtree(["$gDriverAppPath/$gXtrasFolder/$gWacomDataXtraFile"], 0, 0);
removeDirIfEmpty("$gDriverAppPath/$gXtrasFolder"); } removeDirIfEmpty("$gDriverAppPath");
# launchd agents rmtree(["$launchAgents/$gLaunchAgent"], 0, 0);
# Kexts print "Deleting Driver.kext: "; rmtree(["$extensionsDir/$gKext"], 0, 0);
# The TabletDriverCFPlugin is shared between the two drivers, so only remove
# it if we are removing the last driver.
if( isOtherDriverInstalled() == 0 )
{
rmtree(["$extensionsDir/$gCFPlugin"], 0, 0);
}
}
##############################################################################
sub deleteWacomApplications
{
my $prefpanesDir = "/Library/PreferencePanes";
my $tutorialLog = "$gAppFolder/$wacomLog";
print "Deleting Control Panels: ";
# Get the path to the RemoveTablet.app's folder my $manifestFile;
my @applications = ( "$prefpanesDir/$gPrefPane", "$gAppFolder/$gUninstaller_Old", "$gAppFolder/$gUninstaller" );
map { removeFileIfExists($_); } @applications;
removeFileIfExists($tutorialLog);
# Delete installed docs listed in manifest file if (open MANIFEST, "<", "/Library/Receipts/$gDocReceipt") { while(<MANIFEST>) { chomp $; $manifestFile = $; $manifestFile =~ m/^(.*)$/; $manifestFile = $1; #get around perl security
removeFileIfExists($manifestFile) } close MANIFEST; } else { # no manifest file }
# Delete tablet folder if it's empty my $isEmpty = 1; opendir TABLETDIR, $gAppFolder; my @dirlist = readdir TABLETDIR; my $dirEntry; foreach $dirEntry (@dirlist) { if ( $dirEntry !~ m/^./) { $isEmpty = 0; } } closedir TABLETDIR;
# We used to check the empty status, but when we switched to installing
# manuals through the control panel, that method wouldn't leave a receipt
# that could be used to remove it, so now we're removing the Wacom Tablet
# folder and any files contained therein without checking.
#if ($isEmpty == 1)
#{
rmtree(["$gAppFolder"], 0, 0);
#}
}
##############################################################################
sub deleteCurrentUserPreferenceFiles
{
print "Deleting Preferences:
";
my $userName = shift; my $prefFile; my $nameLength = length($gTouchPrefFile); my $prefsFolder = "/Users/$userName/Library/Preferences";
$prefFile = "$prefsFolder/$gOldPrefFile"; removeFileIfExists($prefFile);
$prefFile = "$prefsFolder/$gPrefFile"; removeFileIfExists($prefFile);
if ($nameLength > 0) { $prefFile = "$prefsFolder/$gTouchPrefFile"; removeFileIfExists($prefFile); }
# creating restore files to signal the pen and touch driver to restore to defaults
createRestorePrefsFile($prefsFolder, $gPenRestorePrefFile);
createRestorePrefsFile($prefsFolder, $gTouchRestorePrefFile);
}
##############################################################################
# deleteAllPreferenceFiles
#
# Purpose: Deletes preferences files for all users.
#
# Note that this routine will leave a set of "restore.pref" files in other
# user accounts. This is done because the driver can't be restarted in other
# user accounts without causing problems (see comments under hupDriver())
#
sub deleteAllPreferenceFiles
{
print "Deleting Preferences:
";
my @homeList; my $homeRegEx; my $homeLine; my $prefFile;
my $nameLength = length($gTouchPrefFile);
if(-e "/usr/bin/niutil") { # MacOS X Server @homeList = `/usr/bin/niutil -list . /users home`; } else { # MacOS X @homeList = `/usr/bin/dscl localhost -list /Search/Users home`; }
$homeRegEx = 's(/Users/.*)';
#delete each user's preference file foreach $homeLine (@homeList) { next if not $homeLine =~ /$homeRegEx/;
my $homePath = $1; my $prefsFolder = "$homePath/Library/Preferences";
#delete legacy pref file $prefFile = "$prefsFolder/$gOldPrefFile"; removeFileIfExists($prefFile);
#delete pen pref file $prefFile = "$prefsFolder/$gPrefFile"; removeFileIfExists($prefFile);
#delete touch pref file if ($nameLength > 0) { $prefFile = "$prefsFolder/$gTouchPrefFile"; removeFileIfExists($prefFile); }
if ($prefsOnly) { # we're just removing prefs; not uninstalling. # create empty restore file so driver will pick up the changes upon restart createRestorePrefsFile($prefsFolder, $gPenRestorePrefFile); createRestorePrefsFile($prefsFolder, $gTouchRestorePrefFile); } else { # this is uninstall # remove the restore files if(length($gPenRestorePrefFile) > 0) { removeFileIfExists("$prefsFolder/$gPenRestorePrefFile"); }
if(length($gTouchRestorePrefFile) > 0) { removeFileIfExists("$prefsFolder/$gTouchRestorePrefFile"); } } }
#delete global preference file my $globalPrefsFolder = "/Library/Preferences/Tablet"; removeFileIfExists("$globalPrefsFolder/$gOldPrefFile"); removeFileIfExists("$globalPrefsFolder/$gPrefFile");
if ($nameLength > 0) { removeFileIfExists("$globalPrefsFolder/$gTouchPrefFile"); }
#delete global Tablet preference folder if empty
removeDirIfEmpty($globalPrefsFolder);
}
##############################################################################
sub deleteFrameworks
{
print "Deleting Frameworks
";
rmtree("/Library/Frameworks/WacomMultiTouch.framework");
}
##############################################################################
sub deleteWacomInternetPlugins
{
print "Deleting Internet Plugins:
";
# The web plugins are shared between the two drivers, so only remove them if
# we are removing the last driver.
if( isOtherDriverInstalled() == 0 )
{
deleteInternetPlugin($gSafariPlugin);
deleteInternetPlugin($gNetscapePlugin);
deleteInternetPlugin($gWacomTabletPlugin);
}
}
##############################################################################
# deleteInternetPlugin
#
# Purpose: Deletes an Internet plugin of the given name from the standard
# Internet Plugins folder.
#
# Parameters: $[0] = the plugin file name
#
sub deleteInternetPlugin
{
my $pluginName = $[0]; #first argument.
my $nameLength = length($pluginName);
if ($nameLength > 0) { my $pluginPath = "/Library/Internet Plug-Ins/$pluginName"; removeFileIfExists($pluginPath); }
}#end deleteInternetPlugin
##############################################################################
# deleteUserDefaultsFile
#
# Purpose: Delete the user defaults files for both touch and pen if they
# exist.
#
sub deleteUserDefaultsFile
{
my $userDriverLength = length($gDriverUserDefaults);
my $userTouchLength = length($gTouchUserDefaults);
if($userDriverLength > 0) { my $userDriverDefaultsPath = "/Library/Preferences/$gDriverUserDefaults"; removeFileIfExists($userDriverDefaultsPath); }
if($userTouchLength > 0)
{
my $usersTouchDefaultsPath = "/Library/Preferences/$gTouchUserDefaults";
removeFileIfExists($usersTouchDefaultsPath);
}
}
##############################################################################
# isOtherDriverInstalled
#
# Purpose: Returns true if the other Wacom driver is installed, such as if
# this is a pro uninstaller and the consumer driver is installed.
#
sub isOtherDriverInstalled
{
my $otherDriverIsPresent = 0;
# check for the presence of an indicative file. if(-e "/System/Library/Extensions/$gOtherDriverKext") { $otherDriverIsPresent = 1; }
return $otherDriverIsPresent;
}#end isOtherDriverInstalled
##############################################################################
sub removeDirIfEmpty
{
my $dirToRemove = shift;
my $isEmpty = 1;
opendir SOMEDIR, $dirToRemove or return;
my @dirlist = readdir SOMEDIR;
my $dirEntry;
foreach $dirEntry (@dirlist)
{
if ( $dirEntry !~ m/^./)
{
$isEmpty = 0;
}
}
closedir SOMEDIR;
if ($isEmpty == 1)
{
rmtree([$dirToRemove], 0, 0);
}
}
##############################################################################
sub removeFileIfExists
{
my $full_path = shift;
if(-e $full_path)
{
print "Removing $full_path
";
rmtree([$full_path], 0, 0);
}
}
##############################################################################
# createRestorePrefsFile
#
# Purpose: Create an empty file with the given file name under user's
# Preferences folder.
#
# Parameters: $[0] = prefs folder path, file name
#
sub createRestorePrefsFile
{
my ($prefsFolder, $fileName) = @;
if (length($fileName) > 0) { my $filePath = "$prefsFolder/$fileName";
# remove the existing one if exists removeFileIfExists($filePath);
# create the file open(RESTOREFILE, ">$filePath") or warn "Could not create $filePath .";
# done; close the file close RESTOREFILE;
# set file permission to -rw-rw-r--
system("/bin/chmod", "664", $filePath);
}
}#end createRestorePrefsFile
##############################################################################
main();
