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.

This commit is contained in:
Charlie Fenton 2013-06-11 05:22:12 -07:00
parent c518170452
commit 10576cbcbc
4 changed files with 144 additions and 100 deletions

View File

@ -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, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
fprintf(f, "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
fprintf(f, "<plist version=\"1.0\">\n<dict>\n");
fprintf(f, "\t<key>CFBundleGetInfoString</key>\n");
fprintf(f, "\t<string>%s %s</string>\n", brand, BOINC_VERSION_STRING);
fprintf(f, "\t<key>CFBundleIdentifier</key>\n\t<string>edu.berkeley.boinc_r</string>\n");
fprintf(f, "\t<key>CFBundleShortVersionString</key>\n");
fprintf(f, "\t<string>%s</string>\n", BOINC_VERSION_STRING);
fprintf(f, "\t<key>IFMajorVersion</key>\n\t<integer>%d</integer>\n", BOINC_MAJOR_VERSION);
fprintf(f, "\t<key>IFMinorVersion</key>\n\t<integer>%d</integer>\n", BOINC_MINOR_VERSION);
fprintf(f, "\t<key>IFPkgFlagAllowBackRev</key>\n\t<integer>1</integer>\n");
fprintf(f, "\t<key>IFPkgFlagAuthorizationAction</key>\n\t<string>AdminAuthorization</string>\n");
fprintf(f, "\t<key>IFPkgFlagRestartAction</key>\n\t<string>RequiredRestart</string>\n");
fprintf(f, "\t<key>IFPkgFlagRootVolumeOnly</key>\n\t<integer>1</integer>\n");
fprintf(f, "\t<key>IFPkgFlagComponentDirectory</key>\n\t<string>../</string>\n");
fprintf(f, "\t<key>IFPkgFlagPackageList</key>\n");
fprintf(f, "\t<array>\n");
fprintf(f, "\t\t<dict>\n");
fprintf(f, "\t\t\t<key>IFPkgFlagPackageLocation</key>\n\t\t\t<string>BOINC.pkg</string>\n");
fprintf(f, "\t\t\t<key>IFPkgFlagPackageSelection</key>\n\t\t\t<string>required</string>\n");
fprintf(f, "\t\t</dict>\n");
fprintf(f, "\t</array>\n");
fprintf(f, "\t<key>IFPkgFormatVersion</key>\n\t<real>0.10000000149011612</real>\n");
fprintf(f, "</dict>\n</plist>\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, "\t<key>IFMajorVersion</key>\n\t<integer>%d</integer>\n", BOINC_MAJOR_VERSION);
fprintf(f, "\t<key>IFMinorVersion</key>\n\t<integer>%d</integer>\n", BOINC_MINOR_VERSION);
fprintf(f, "\t<key>IFPkgFlagAllowBackRev</key>\n\t<integer>1</integer>\n");
fprintf(f, "\t<key>IFPkgFlagAuthorizationAction</key>\n\t<string>RootAuthorization</string>\n");
fprintf(f, "\t<key>IFPkgFlagAuthorizationAction</key>\n\t<string>AdminAuthorization</string>\n");
fprintf(f, "\t<key>IFPkgFlagRestartAction</key>\n\t<string>NoRestart</string>\n");
fprintf(f, "\t<key>IFPkgFlagRootVolumeOnly</key>\n\t<integer>1</integer>\n");
fprintf(f, "\t<key>IFPkgFlagComponentDirectory</key>\n\t<string>../</string>\n");
fprintf(f, "\t<key>IFPkgFlagPackageList</key>\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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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