From 10576cbcbc54fb58c3e88422be3c3fee762ec6fb Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Tue, 11 Jun 2013 05:22:12 -0700 Subject: [PATCH] Mac installer: To avoid invalidating our code signing: use a metapackage instead of modifying the property list file to trigger a system restart when needed. --- clientgui/mac/SetVersion.cpp | 64 +++++++++++++++- mac_installer/Installer.cpp | 129 ++++++++++----------------------- mac_installer/PostInstall.cpp | 32 ++++++-- mac_installer/release_boinc.sh | 19 +++++ 4 files changed, 144 insertions(+), 100 deletions(-) diff --git a/clientgui/mac/SetVersion.cpp b/clientgui/mac/SetVersion.cpp index 215359ffea..9ad6d6e6b8 100644 --- a/clientgui/mac/SetVersion.cpp +++ b/clientgui/mac/SetVersion.cpp @@ -40,6 +40,7 @@ int file_exists(const char* path); int FixInfoPlistFile(char* name); int FixInfoPlist_Strings(char* myPath, char* name); int MakeBOINCPackageInfoPlistFile(char* myPath, char* brand); +int MakeBOINCRestartPackageInfoPlistFile(char* myPath, char* brand); int MakeMetaPackageInfoPlistFile(char* myPath, char* brand); int main(int argc, char** argv) { @@ -91,6 +92,9 @@ int main(int argc, char** argv) { err = MakeBOINCPackageInfoPlistFile("./Pkg-Info.plist", "BOINC Manager"); if (err) retval = err; + err = MakeBOINCRestartPackageInfoPlistFile("./Pkg_Restart-Info.plist", "BOINC Manager"); + if (err) retval = err; + err = MakeMetaPackageInfoPlistFile("./Mpkg-Info.plist", "BOINC Manager"); return retval; } @@ -284,7 +288,7 @@ int MakeBOINCPackageInfoPlistFile(char* myPath, char* brand) { retval = fclose(f); } else { - puts("Error creating file Pkg-Info.plist\n"); + printf("Error creating file %s\n", myPath); retval = -1; } @@ -292,6 +296,58 @@ int MakeBOINCPackageInfoPlistFile(char* myPath, char* brand) { } +// Create a MetaPackage whcih runs only BOINC,pkg but specifies Restart Required +int MakeBOINCRestartPackageInfoPlistFile(char* myPath, char* brand) { + int retval = 0; + FILE *f; + + if (IsFileCurrent(myPath)) + return 0; + + f = fopen(myPath, "w"); + if (f) + { + fprintf(f, "\n"); + fprintf(f, "\n"); + fprintf(f, "\n\n"); + fprintf(f, "\tCFBundleGetInfoString\n"); + fprintf(f, "\t%s %s\n", brand, BOINC_VERSION_STRING); + fprintf(f, "\tCFBundleIdentifier\n\tedu.berkeley.boinc_r\n"); + fprintf(f, "\tCFBundleShortVersionString\n"); + fprintf(f, "\t%s\n", BOINC_VERSION_STRING); + fprintf(f, "\tIFMajorVersion\n\t%d\n", BOINC_MAJOR_VERSION); + fprintf(f, "\tIFMinorVersion\n\t%d\n", BOINC_MINOR_VERSION); + fprintf(f, "\tIFPkgFlagAllowBackRev\n\t1\n"); + fprintf(f, "\tIFPkgFlagAuthorizationAction\n\tAdminAuthorization\n"); + fprintf(f, "\tIFPkgFlagRestartAction\n\tRequiredRestart\n"); + fprintf(f, "\tIFPkgFlagRootVolumeOnly\n\t1\n"); + fprintf(f, "\tIFPkgFlagComponentDirectory\n\t../\n"); + + fprintf(f, "\tIFPkgFlagPackageList\n"); + + fprintf(f, "\t\n"); + fprintf(f, "\t\t\n"); + fprintf(f, "\t\t\tIFPkgFlagPackageLocation\n\t\t\tBOINC.pkg\n"); + fprintf(f, "\t\t\tIFPkgFlagPackageSelection\n\t\t\trequired\n"); + fprintf(f, "\t\t\n"); + fprintf(f, "\t\n"); + + fprintf(f, "\tIFPkgFormatVersion\n\t0.10000000149011612\n"); + fprintf(f, "\n\n"); + + fflush(f); + retval = fclose(f); + } + else { + printf("Error creating file %s\n", myPath); + retval = -1; + } + + return retval; +} + + +// Make a MetaPackage to install both BOINC and VirtualBox int MakeMetaPackageInfoPlistFile(char* myPath, char* brand) { int retval = 0; FILE *f; @@ -313,7 +369,9 @@ int MakeMetaPackageInfoPlistFile(char* myPath, char* brand) { fprintf(f, "\tIFMajorVersion\n\t%d\n", BOINC_MAJOR_VERSION); fprintf(f, "\tIFMinorVersion\n\t%d\n", BOINC_MINOR_VERSION); fprintf(f, "\tIFPkgFlagAllowBackRev\n\t1\n"); - fprintf(f, "\tIFPkgFlagAuthorizationAction\n\tRootAuthorization\n"); + fprintf(f, "\tIFPkgFlagAuthorizationAction\n\tAdminAuthorization\n"); + fprintf(f, "\tIFPkgFlagRestartAction\n\tNoRestart\n"); + fprintf(f, "\tIFPkgFlagRootVolumeOnly\n\t1\n"); fprintf(f, "\tIFPkgFlagComponentDirectory\n\t../\n"); fprintf(f, "\tIFPkgFlagPackageList\n"); @@ -337,7 +395,7 @@ int MakeMetaPackageInfoPlistFile(char* myPath, char* brand) { retval = fclose(f); } else { - puts("Error creating file Mpkg-Info.plist\n"); + printf("Error creating file %s\n", myPath); retval = -1; } diff --git a/mac_installer/Installer.cpp b/mac_installer/Installer.cpp index 6b79cb982b..d8770cc279 100644 --- a/mac_installer/Installer.cpp +++ b/mac_installer/Installer.cpp @@ -40,7 +40,7 @@ void Initialize(void); /* function prototypes */ Boolean IsUserMemberOfGroup(const char *userName, const char *groupName); -OSStatus GetFinalAction(CFStringRef *restartValue); +Boolean IsRestartNeeded(); OSErr FindProcess (OSType typeToFind, OSType creatorToFind, ProcessSerialNumberPtr processSN); static OSErr QuitAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, UInt32 refcon); void print_to_log_file(const char *format, ...); @@ -52,14 +52,17 @@ void strip_cr(char *buf); Boolean gQuitFlag = false; /* global */ +#if 0 CFStringRef valueRestartRequired = CFSTR("RequiredRestart"); CFStringRef valueLogoutRequired = CFSTR("RequiredLogout"); CFStringRef valueNoRestart = CFSTR("NoRestart"); - +#endif int main(int argc, char *argv[]) { - char pkgPath[MAXPATHLEN], infoPlistPath[MAXPATHLEN], MetaPkgPath[MAXPATHLEN]; + char pkgPath[MAXPATHLEN], pkgRestartPath[MAXPATHLEN]; + char infoPlistPath[MAXPATHLEN]; + char MetaPkgPath[MAXPATHLEN], MetaPkgRestartPath[MAXPATHLEN]; char brand[64], s[256]; char *p; ProcessSerialNumber ourPSN, installerPSN; @@ -67,17 +70,10 @@ int main(int argc, char *argv[]) long response; short itemHit; pid_t installerPID = 0; - FSRef infoPlistFileRef; - Boolean isDirectory, result; - CFURLRef xmlURL = NULL; - CFDataRef xmlDataIn = NULL; - CFDataRef xmlDataOut = NULL; - CFPropertyListRef propertyListRef = NULL; - CFStringRef restartKey = CFSTR("IFPkgFlagRestartAction"); - CFStringRef currentValue = NULL, desiredValue = NULL; - CFStringRef errorString = NULL; OSStatus err = noErr; struct stat stat_buf; + Boolean restartNeeded = true; + FILE *restartNeededFile; Initialize(); @@ -109,6 +105,13 @@ int main(int argc, char *argv[]) strlcpy(MetaPkgPath, pkgPath, sizeof(MetaPkgPath)); strlcat(MetaPkgPath, "+VirtualBox.mpkg", sizeof(MetaPkgPath)); + + strlcpy(MetaPkgRestartPath, pkgPath, sizeof(MetaPkgRestartPath)); + strlcat(MetaPkgRestartPath, "+VirtualBox .mpkg", sizeof(MetaPkgRestartPath)); + + strlcpy(pkgRestartPath, pkgPath, sizeof(MetaPkgPath)); + strlcat(pkgRestartPath, ".mpkg", sizeof(pkgPath)); + strlcat(pkgPath, ".pkg", sizeof(pkgPath)); err = Gestalt(gestaltSystemVersion, &response); if (err != noErr) @@ -132,78 +135,29 @@ int main(int argc, char *argv[]) } // Remove previous installer package receipt so we can run installer again + // (affects only older versions of OS X and fixes a bug in those versions) // "rm -rf /Library/Receipts/GridRepublic.pkg" sprintf(s, "rm -rf \"/Library/Receipts/%s.pkg\"", brand); system (s); + restartNeeded = IsRestartNeeded(); + + // Write a temp file to tell our PostInstall.app whether restart is needed + restartNeededFile = fopen("/tmp/BOINC_restart_flag", "w"); + if (restartNeededFile) { + fputs(restartNeeded ? "1\n" : "0\n", restartNeededFile); + fclose(restartNeededFile); + } + err = Gestalt(gestaltSystemVersion, &response); if (err != noErr) return err; - err = GetFinalAction(&desiredValue); - - strlcpy(infoPlistPath, pkgPath, sizeof(infoPlistPath)); - strlcat(infoPlistPath, "/Contents/Info.plist", sizeof(infoPlistPath)); - - err = FSPathMakeRef((UInt8*)infoPlistPath, &infoPlistFileRef, &isDirectory); - if (err) - return err; - - xmlURL = CFURLCreateFromFSRef(NULL, &infoPlistFileRef); - if (xmlURL == NULL) - return -1; - - // Read XML Data from file - result = CFURLCreateDataAndPropertiesFromResource(NULL, xmlURL, &xmlDataIn, NULL, NULL, &err); - if (err == noErr) - if (!result) - err = coreFoundationUnknownErr; - - if (err == noErr) { // Convert XML Data to internal CFPropertyListRef / CFDictionaryRef format - propertyListRef = CFPropertyListCreateFromXMLData(NULL, xmlDataIn, kCFPropertyListMutableContainersAndLeaves, &errorString); - if (propertyListRef == NULL) - err = coreFoundationUnknownErr; - } - - if (err == noErr) { // Get current value for our key - currentValue = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)propertyListRef, restartKey); - if (currentValue == NULL) - err = coreFoundationUnknownErr; - } - - if (err == noErr) { - if (CFStringCompare(currentValue, desiredValue, 0) != kCFCompareEqualTo) { // If current value != desired value - // Replace value for key with desired value - CFDictionaryReplaceValue((CFMutableDictionaryRef)propertyListRef, restartKey, desiredValue); - - // Convert internal CFPropertyListRef / CFDictionaryRef format to XML data - xmlDataOut = CFPropertyListCreateXMLData(NULL, propertyListRef); - if (xmlDataOut == NULL) - err = coreFoundationUnknownErr; - - if (err == noErr) { // Write revised XML Data back to the file - result = CFURLWriteDataAndPropertiesToResource (xmlURL, xmlDataOut, NULL, &err); - if (err == noErr) - if (!result) - err = coreFoundationUnknownErr; - } - } - } - - if (xmlURL) - CFRelease(xmlURL); - if (xmlDataIn) - CFRelease(xmlDataIn); - if (xmlDataOut) - CFRelease(xmlDataOut); - if (propertyListRef) - CFRelease(propertyListRef); - if (err == noErr) { if ((response < 0x1050) || stat(MetaPkgPath, &stat_buf)) { // stat() returns zero on success - sprintf(infoPlistPath, "open \"%s\" &", pkgPath); + sprintf(infoPlistPath, "open \"%s\" &", restartNeeded ? pkgRestartPath : pkgPath); } else { - sprintf(infoPlistPath, "open \"%s\" &", MetaPkgPath); + sprintf(infoPlistPath, "open \"%s\" &", restartNeeded ? MetaPkgRestartPath : MetaPkgPath); } system(infoPlistPath); } @@ -234,7 +188,7 @@ Boolean IsUserMemberOfGroup(const char *userName, const char *groupName) { } -OSStatus GetFinalAction(CFStringRef *restartValue) +Boolean IsRestartNeeded() { passwd *pw = NULL; group *grp = NULL; @@ -244,61 +198,58 @@ OSStatus GetFinalAction(CFStringRef *restartValue) long response; char loginName[256]; - *restartValue = valueRestartRequired; - grp = getgrnam(boinc_master_group_name); if (grp == NULL) - return noErr; // Group boinc_master does not exist + return true; // Group boinc_master does not exist boinc_master_gid = grp->gr_gid; grp = getgrnam(boinc_project_group_name); if (grp == NULL) - return noErr; // Group boinc_project does not exist + return true; // Group boinc_project does not exist boinc_project_gid = grp->gr_gid; pw = getpwnam(boinc_master_user_name); if (pw == NULL) - return noErr; // User boinc_master does not exist + return true; // User boinc_master does not exist boinc_master_uid = pw->pw_uid; if (pw->pw_gid != boinc_master_gid) - return noErr; // User boinc_master does not have group boinc_master as its primary group + return true; // User boinc_master does not have group boinc_master as its primary group pw = getpwnam(boinc_project_user_name); if (pw == NULL) - return noErr; // User boinc_project does not exist + return true; // User boinc_project does not exist boinc_project_uid = pw->pw_uid; if (pw->pw_gid != boinc_project_gid) - return noErr; // User boinc_project does not have group boinc_project as its primary group + return true; // User boinc_project does not have group boinc_project as its primary group err = Gestalt(gestaltSystemVersion, &response); if ((err == noErr) && (response >= 0x1050)) { if (boinc_master_gid < 501) - return noErr; // We will change boinc_master_gid to a value > 501 + return true; // We will change boinc_master_gid to a value > 501 if (boinc_project_gid < 501) - return noErr; // We will change boinc_project_gid to a value > 501 + return true; // We will change boinc_project_gid to a value > 501 if (boinc_master_uid < 501) - return noErr; // We will change boinc_master_uid to a value > 501 + return true; // We will change boinc_master_uid to a value > 501 if (boinc_project_uid < 501) - return noErr; // We will change boinc_project_uid to a value > 501 + return true; // We will change boinc_project_uid to a value > 501 } #ifdef SANDBOX strncpy(loginName, getenv("USER"), sizeof(loginName)-1); if (loginName[0]) { if (IsUserMemberOfGroup(loginName, boinc_master_group_name)) { - *restartValue = valueNoRestart; - return noErr; // Logged in user is already a member of group boinc_master + return false; // Logged in user is already a member of group boinc_master } } #endif // SANDBOX - return noErr; + return true; } diff --git a/mac_installer/PostInstall.cpp b/mac_installer/PostInstall.cpp index 4718e06909..0aaadee127 100644 --- a/mac_installer/PostInstall.cpp +++ b/mac_installer/PostInstall.cpp @@ -92,6 +92,7 @@ void Initialize(void); /* function prototypes */ Boolean myFilterProc(DialogRef theDialog, EventRecord *theEvent, DialogItemIndex *itemHit); int DeleteReceipt(void); OSStatus CheckLogoutRequirement(int *finalAction); +Boolean IsRestartNeeded(); void CheckUserAndGroupConflicts(); Boolean SetLoginItemOSAScript(long brandID, Boolean deleteLogInItem, char *userName); Boolean SetLoginItemAPI(long brandID, Boolean deleteLogInItem); @@ -424,14 +425,14 @@ int main(int argc, char *argv[]) pid_t waitPermissionsPID = 0; uid_t saved_euid, saved_uid, b_m_uid; passwd *pw; - int finalInstallAction; + Boolean restartNeeded; DialogRef theWin; - err = CheckLogoutRequirement(&finalInstallAction); - printf("CheckLogoutRequirement returned %d\n", finalInstallAction); + restartNeeded = IsRestartNeeded(); + printf("IsRestartNeeded() returned %d\n", (int)restartNeeded); fflush(stdout); - if (finalInstallAction == launchWhenDone) { + if (!restartNeeded) { // Wait for BOINC's RPC socket address to become available to user boinc_master, in // case we are upgrading from a version which did not run as user boinc_master. @@ -536,7 +537,7 @@ int DeleteReceipt() int i; pid_t installerPID = 0; OSStatus err; - int finalInstallAction; + Boolean restartNeeded = true; FSRef fileRef; char s[256]; struct stat sbuf; @@ -544,8 +545,8 @@ int DeleteReceipt() Initialize(); - err = CheckLogoutRequirement(&finalInstallAction); -// print_to_log_file("CheckLogoutRequirement returned %d\n", finalInstallAction); + restartNeeded = IsRestartNeeded(); +// print_to_log_file("IsRestartNeeded() returned %d\n", (int)restartNeeded); brandID = GetBrandID(); @@ -556,7 +557,7 @@ int DeleteReceipt() // err_fsref = FSPathMakeRef((StringPtr)"/Applications/GridRepublic Desktop.app", &fileRef, NULL); err_fsref = FSPathMakeRef((StringPtr)appPath[brandID], &fileRef, NULL); - if (finalInstallAction == launchWhenDone) { + if (!restartNeeded) { err = FindProcess ('APPL', 'xins', &installerPSN); if (err == noErr) { @@ -586,6 +587,21 @@ int DeleteReceipt() } +// BOINC Installer.app wrote a file to tell us whether a restart is required +Boolean IsRestartNeeded() { + FILE *restartNeededFile; + int value; + + restartNeededFile = fopen("/tmp/BOINC_restart_flag", "r"); + if (restartNeededFile) { + fscanf(restartNeededFile,"%d", &value); + fclose(restartNeededFile); + return (value != 0); + } + + return true; +} + OSStatus CheckLogoutRequirement(int *finalAction) { *finalAction = restartRequired; diff --git a/mac_installer/release_boinc.sh b/mac_installer/release_boinc.sh index d14e40281b..841cde8d7e 100644 --- a/mac_installer/release_boinc.sh +++ b/mac_installer/release_boinc.sh @@ -275,6 +275,19 @@ if [ ! -d ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BO fi cp -fp mac_installer/Description.plist ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC\ Installer.app/Contents/Resources/BOINC.pkg/Contents/Resources/en.lproj/ +# Build the "BOINC.mpkg" metapackage (used if installer.app determines +# that we need user to restart OS X after installation) +mkdir -p "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/BOINC.mpkg/Contents/Resources" +cp -fp mac_build/Pkg_Restart-Info.plist ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC\ Installer.app/Contents/Resources/BOINC.mpkg/Contents/Info.plist +cp -fp mac_installer/License.rtf ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC\ Installer.app/Contents/Resources/BOINC.mpkg/Contents/Resources/ +cp -fp ../BOINC_Installer/Installer\ Resources/ReadMe.rtf ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC\ Installer.app/Contents/Resources/BOINC.mpkg/Contents/Resources/ +cat >> ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC\ Installer.app/Contents/Resources/BOINC.mpkg/Contents/Resources/package_version << ENDOFFILE +major: $1 +minor: $2 +ENDOFFILE +echo "pmkrpkg1" > ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC\ Installer.app/Contents/Resources/BOINC.mpkg/Contents/PkgInfo + + # Build the BOINC+VirtualBox.mpkg metapackage if VirtualBox.pkg exists VirtualBoxPackageName="VirtualBox.pkg" @@ -290,6 +303,12 @@ ENDOFFILE echo "pmkrpkg1" > ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC\ Installer.app/Contents/Resources/BOINC+VirtualBox.mpkg/Contents/PkgInfo cp -fpR mac_installer/${VirtualBoxPackageName} ../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC\ Installer.app/Contents/Resources/ + + # Now create the "BOINC+VirtualBox .mpkg" metapackage (used if installer.app + # determines that we need user to restart OS X after installation) + cp -fpR "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/BOINC+VirtualBox.mpkg" "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/BOINC+VirtualBox .mpkg" + # Change "NoRestart" to "RequiredRestart" in BOINC+VirtualBox .mpkg + sed -i "" s/"NoRestart"/"RequiredRestart"/g "../BOINC_Installer/New_Release_$1_$2_$3/boinc_$1.$2.$3_macOSX_$arch/BOINC Installer.app/Contents/Resources/BOINC+VirtualBox .mpkg/Contents/Info.plist" fi # Build the stand-alone client distribution