From b9e73c2379447185b71c260165d2871b3f114bc0 Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Thu, 7 Jun 2007 12:24:29 +0000 Subject: [PATCH] Further work on BONC Mac uninstaller svn path=/trunk/boinc/; revision=12843 --- checkin_notes | 14 +- mac_build/Uninstaller-Info.plist | 8 + mac_build/boinc.xcodeproj/project.pbxproj | 2 - mac_installer/uninstall.cpp | 214 +++++++++++++++++++--- 4 files changed, 208 insertions(+), 30 deletions(-) diff --git a/checkin_notes b/checkin_notes index 0a80908939..1593ffcd90 100755 --- a/checkin_notes +++ b/checkin_notes @@ -5792,7 +5792,7 @@ Charlie 6 June 2007 - Mac: Create "Uninstall BOINC" application, modify build scripts to include it in release package with BOINC Manager. - Mac: Fix a bug in release_GridRepublic.sh build script which - created a boogus " BOINC Data/locale" directory (with a bad + created a bogus " BOINC Data/locale" directory (with a bad leading space character.) mac_build/ @@ -5871,3 +5871,15 @@ Rom 6 June 2007 boinccas.dll boinccas95.dll +Charlie 7 June 2007 + - Mac: Further work on "Uninstall BOINC" application: use various + techniques to try to locate BOINC Manager in non-standard + directories, use branding-independent search techniques. + to include it in release package with BOINC Manager. + + mac_build/ + boinc.xcodeproj/ + project.pbxproj + Uninstaller-Info.plist + mac_installer/ + uninstall.cpp diff --git a/mac_build/Uninstaller-Info.plist b/mac_build/Uninstaller-Info.plist index e41651c9aa..3686bc4089 100644 --- a/mac_build/Uninstaller-Info.plist +++ b/mac_build/Uninstaller-Info.plist @@ -6,6 +6,8 @@ English CFBundleExecutable ${EXECUTABLE_NAME} + CFBundleGetInfoString + Uninstall BOINC version 5.10.2, Copyright 2007 University of California. CFBundleIdentifier edu.berkeley.boinc.Uninstaller CFBundleInfoDictionaryVersion @@ -16,5 +18,11 @@ ???? CFBundleVersion 5.10.2 + CFBundleName + Uninstall BOINC + CFBundleShortVersionString + Uninstall BOINC version 5.10.2 + CFBundleDevelopmentRegion + English diff --git a/mac_build/boinc.xcodeproj/project.pbxproj b/mac_build/boinc.xcodeproj/project.pbxproj index 95a88cd2fb..0dbe99a7de 100755 --- a/mac_build/boinc.xcodeproj/project.pbxproj +++ b/mac_build/boinc.xcodeproj/project.pbxproj @@ -239,7 +239,6 @@ DDAEC9FF07FA5A5C00A7BC36 /* SetVersion.C in Sources */ = {isa = PBXBuildFile; fileRef = DDAEC9E707FA58A000A7BC36 /* SetVersion.C */; }; DDB693500ABFE9C600689FD8 /* procinfo_mac.C in Sources */ = {isa = PBXBuildFile; fileRef = DDB6934F0ABFE9C600689FD8 /* procinfo_mac.C */; }; DDBD51760C169B770074905B /* LoginItemAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = DD1277BE081F3E73007B5DE1 /* LoginItemAPI.c */; }; - DDBD52BD0C16C7180074905B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F51BDF4903086C46012012A7 /* InfoPlist.strings */; }; DDBE74390C03B1C600453BB8 /* cs_platforms.C in Sources */ = {isa = PBXBuildFile; fileRef = DDBE74380C03B1C600453BB8 /* cs_platforms.C */; }; DDCDCDF60B532433009BB03C /* ViewProjectsGrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DDCDCDF20B532433009BB03C /* ViewProjectsGrid.cpp */; }; DDD095490A3EDF2D00C95BA4 /* switcher.C in Sources */ = {isa = PBXBuildFile; fileRef = DDD095480A3EDF2D00C95BA4 /* switcher.C */; }; @@ -2252,7 +2251,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - DDBD52BD0C16C7180074905B /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/mac_installer/uninstall.cpp b/mac_installer/uninstall.cpp index 76c99a831f..e3682cfea3 100644 --- a/mac_installer/uninstall.cpp +++ b/mac_installer/uninstall.cpp @@ -35,16 +35,17 @@ #include "LoginItemAPI.h" //please take a look at LoginItemAPI.h for an explanation of the routines available to you. static OSStatus DoUninstall(void); -static OSErr CleanupAllVisibleUsers(void); +static OSStatus GetpathToBOINCManagerApp(char* path, int maxLen, FSRef *theFSRef); +static OSStatus CleanupAllVisibleUsers(void); +static OSStatus DeleteOurBundlesFromDirectory(CFStringRef bundleID, char *extension, char *dirPath); static OSStatus GetAuthorization(AuthorizationRef * authRef, const char *pathToTool, char *brandName); static OSStatus DoPrivilegedExec(char *brandName, const char *pathToTool, char *arg1, char *arg2, char *arg3, char *arg4, char *arg5); static void DeleteLoginItem(void); static char * PersistentFGets(char *buf, size_t buflen, FILE *f); static pid_t FindProcessPID(char* name, pid_t thePID); -static OSErr QuitBOINCManager(OSType signature); +static OSStatus QuitBOINCManager(OSType signature); static void SleepTicks(UInt32 ticksToSleep); static void ShowMessage(const char *format, ...); -static pascal Boolean ErrorDlgFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *theItemHit); int main(int argc, char *argv[]) @@ -91,7 +92,7 @@ int main(int argc, char *argv[]) strlcat(pathToSelf, "/Contents/MacOS/", sizeof(pathToSelf)); strlcat(pathToSelf, appName, sizeof(pathToSelf)); - p = strrchr(appName, ' '); + p = strchr(appName, ' '); p += 1; // Point to brand name following "Uninstall " err = DoPrivilegedExec(p, pathToSelf, "--privileged", NULL, NULL, NULL, NULL); @@ -115,6 +116,11 @@ int main(int argc, char *argv[]) static OSStatus DoUninstall(void) { pid_t coreClientPID = 0; + char myRmCommand[MAXPATHLEN+10], plistRmCommand[MAXPATHLEN+10], *p; + char notBoot[] = "/Volumes/"; + FSRef theFSRef; + int pathOffset, i; + OSStatus err = noErr; #if TESTING ShowMessage("Permission OK after relaunch"); @@ -128,32 +134,199 @@ static OSStatus DoUninstall(void) { if (coreClientPID) kill(coreClientPID, SIGTERM); // boinc catches SIGTERM & exits gracefully + // Phase 1: try to find all our applications using LaunchServices + for (i=0; i<100; i++) { + strlcpy(myRmCommand, "rm -rf \"", 10); + pathOffset = strlen(myRmCommand); + + err = GetpathToBOINCManagerApp(myRmCommand+pathOffset, MAXPATHLEN, &theFSRef); + if (err) + break; + + strlcat(myRmCommand, "\"", sizeof(myRmCommand)); + +#if TESTING + ShowMessage("manager: %s", myRmCommand); +#endif + + p = strstr(myRmCommand, notBoot); + + if (p == myRmCommand+pathOffset) { +#if TESTING + ShowMessage("Not on boot volume: %s", myRmCommand); +#endif + break; + } else { + + // First delete just the application's info.plist file and update the + // LaunchServices Database;; otherwise LSFindApplicationForInfo might + // return this application again after it's been deleted. + strlcpy(plistRmCommand, myRmCommand, sizeof(plistRmCommand)); + strlcat(plistRmCommand, "/Contents/info.plist", sizeof(plistRmCommand)); +#if TESTING + ShowMessage("Deleting info.plist: %s", plistRmCommand); +#endif + system(plistRmCommand); + err = LSRegisterFSRef(&theFSRef, true); +#if TESTING + if (err) + ShowMessage("LSRegisterFSRef returned error %d", err); +#endif + system(myRmCommand); + } + } + + // Phase 2: step through default Applications directory searching for our applications + err = DeleteOurBundlesFromDirectory(CFSTR("edu.berkeley.boinc"), "app", "/Applications"); + + // Phase 3: step through default Screen Savers directory searching for our screen savers + err = DeleteOurBundlesFromDirectory(CFSTR("edu.berkeley.boincsaver"), "saver", "/Library/Screen Savers"); + + // Phase 4: Delete our files and directories at our installer's default locations // Remove everything we've installed, whether BOINC or GridRepublic + // These first 4 should already have been deleted by the above code, but do them anyway for safety system ("rm -rf /Applications/BOINCManager.app"); system ("rm -rf \"/Library/Screen Savers/BOINCSaver.saver\""); - system ("rm -rf /Library/Receipts/BOINC.pkg"); system ("rm -rf \"/Applications/GridRepublic Desktop.app\""); system ("rm -rf \"/Library/Screen Savers/GridRepublic.saver\""); + + system ("rm -rf /Library/Receipts/GridRepublic.pkg"); + system ("rm -rf /Library/Receipts/BOINC.pkg"); // We don't customize BOINC Data directory name for branding system ("rm -rf \"/Library/Application Support/BOINC Data\""); + // Phase 5: step through all users and do user-specific cleanup CleanupAllVisibleUsers(); system ("dscl . -delete /users/boinc_master"); system ("dscl . -delete /groups/boinc_master"); system ("dscl . -delete /users/boinc_project"); system ("dscl . -delete /groups/boinc_project"); - - } + +static OSStatus GetpathToBOINCManagerApp(char* path, int maxLen, FSRef *theFSRef) +{ + CFStringRef bundleID = CFSTR("edu.berkeley.boinc"); + OSType creator = 'BNC!'; + OSStatus status = noErr; + + status = LSFindApplicationForInfo(creator, bundleID, NULL, theFSRef, NULL); + if (status) { +#if TESTING + ShowMessage("LSFindApplicationForInfo returned error %d", status); +#endif + return status; + } + + status = FSRefMakePath(theFSRef, (unsigned char *)path, maxLen); +#if TESTING + if (status) + ShowMessage("GetpathToBOINCManagerApp FSRefMakePath returned error %d, %s", status, path); +#endif + + return status; +} + + +static OSStatus DeleteOurBundlesFromDirectory(CFStringRef bundleID, char *extension, char *dirPath) { + DIR *dirp; + dirent *dp; + CFStringRef urlStringRef = NULL; + int index; + CFStringRef thisID = NULL; + CFBundleRef thisBundle = NULL; + CFURLRef bundleURLRef = NULL; + char myRmCommand[MAXPATHLEN+10], *p; + int pathOffset; + + dirp = opendir(dirPath); + if (dirp == NULL) { // Should never happen + ShowMessage("opendir(\"%s\") failed", dirPath); + return -1; + } + + index = -1; + while (true) { + index++; + + dp = readdir(dirp); + if (dp == NULL) + break; // End of list + + p = strrchr(dp->d_name, '.'); + if (p == NULL) + continue; + + if (strcmp(p+1, extension)) + continue; + + strlcpy(myRmCommand, "rm -rf \"", 10); + pathOffset = strlen(myRmCommand); + strlcat(myRmCommand, dirPath, sizeof(myRmCommand)); + strlcat(myRmCommand, "/", sizeof(myRmCommand)); + strlcat(myRmCommand, dp->d_name, sizeof(myRmCommand)); + urlStringRef = CFStringCreateWithCString(kCFAllocatorDefault, myRmCommand+pathOffset, CFStringGetSystemEncoding()); + if (urlStringRef) { + bundleURLRef = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, urlStringRef, kCFURLPOSIXPathStyle, false); + if (bundleURLRef) { + thisBundle = CFBundleCreate( kCFAllocatorDefault, bundleURLRef ); + if (thisBundle) { + thisID = CFBundleGetIdentifier(thisBundle); + if (thisID) { + strlcat(myRmCommand, "\"", sizeof(myRmCommand)); + if (CFStringCompare(thisID, bundleID, 0) == kCFCompareEqualTo) { +#if TESTING + ShowMessage("Bundles: %s", myRmCommand); +#endif + + system(myRmCommand); + } else { +#if TESTING +// ShowMessage("Bundles: Not deleting %s", myRmCommand+pathOffset); +#endif + } + + + } // if (thisID) +#if TESTING + else + ShowMessage("CFBundleGetIdentifier failed for index %d", index); +#endif + CFRelease(thisBundle); + } //if (thisBundle) +#if TESTING + else + ShowMessage("CFBundleCreate failed for index %d", index); +#endif + CFRelease(bundleURLRef); + } // if (bundleURLRef) +#if TESTING + else + ShowMessage("CFURLCreateWithFileSystemPath failed"); +#endif + + CFRelease(urlStringRef); + } // if (urlStringRef) +#if TESTING + else + ShowMessage("CFStringCreateWithCString failed"); +#endif + } // while true + + closedir(dirp); + + return noErr; +} + + // Find all visible users and delete their login item to launch BOINC Manager. // Remove each user from groups boinc_master and boinc_project. // Delete user's BOINC Preferences file. -static OSErr CleanupAllVisibleUsers(void) +static OSStatus CleanupAllVisibleUsers(void) { DIR *dirp; dirent *dp; @@ -414,11 +587,11 @@ static pid_t FindProcessPID(char* name, pid_t thePID) } -static OSErr QuitBOINCManager(OSType signature) { +static OSStatus QuitBOINCManager(OSType signature) { bool done = false; ProcessSerialNumber thisPSN; ProcessInfoRec thisPIR; - OSErr err = noErr; + OSStatus err = noErr; Str63 thisProcessName; AEAddressDesc thisPSNDesc; AppleEvent thisQuitEvent, thisReplyEvent; @@ -497,7 +670,7 @@ static void ShowMessage(const char *format, ...) { char s[1024]; short itemHit; AlertStdAlertParamRec alertParams; - ModalFilterUPP ErrorDlgFilterProcUPP; +// ModalFilterUPP ErrorDlgFilterProcUPP; ProcessSerialNumber ourProcess; @@ -505,11 +678,12 @@ static void ShowMessage(const char *format, ...) { s[0] = vsprintf(s+1, format, args); va_end(args); - ErrorDlgFilterProcUPP = NewModalFilterUPP(ErrorDlgFilterProc); +// ErrorDlgFilterProcUPP = NewModalFilterUPP(ErrorDlgFilterProc); alertParams.movable = true; alertParams.helpButton = false; - alertParams.filterProc = ErrorDlgFilterProcUPP; +// alertParams.filterProc = ErrorDlgFilterProcUPP; + alertParams.filterProc = NULL; alertParams.defaultText = "\pOK"; alertParams.cancelText = NULL; alertParams.otherText = NULL; @@ -521,18 +695,4 @@ static void ShowMessage(const char *format, ...) { ::SetFrontProcess(&ourProcess); StandardAlert (kAlertStopAlert, (StringPtr)s, NULL, &alertParams, &itemHit); - - DisposeModalFilterUPP(ErrorDlgFilterProcUPP); } - - -static pascal Boolean ErrorDlgFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *theItemHit) { - // We need this because this is a command-line application so it does not get normal events - if (Button()) { - *theItemHit = kStdOkItemIndex; - return true; - } - - return StdFilterProc(theDialog, theEvent, theItemHit); -} -