// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2022 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . /* * SetVersion.cpp * boinc * */ // Important: To ensure that the relevant *info.plist and *InfoPlist.strings // files are available by the time they are needed for building a target: // [1] include SetVersion in that target's "Target Dependencies" phase, // [2] if a target is dependent on a file created by SetVersion, make sure // the file is listed as an Output File for SetVersion's script phase. // Otherwise, the build may fail due to a race condition. // // Also, make sure the template used by SetVersion to create the file exists // in clientgui/mac/templates. // // Set STAND_ALONE TRUE if testing as a separate applicaiton #define STAND_ALONE 0 #define VERBOSE_SPAWN 0 /* for debugging callPosixSpawn */ #include #include #include #include #include #include #include // for MAXPATHLEN #include #include "version.h" 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 callPosixSpawn(const char *cmd); int main(int argc, char** argv) { int retval = 0, err; #if STAND_ALONE char myPath[1024]; getcwd(myPath, sizeof(myPath)); printf("%s\n", myPath); // For debugging #endif if (!file_exists("./English.lproj")) { retval = mkdir("./English.lproj", 0755); if (retval) { printf("Error %d creating directory English.lproj\n", retval); } } // BOINC Manager err = FixInfoPlist_Strings("./English.lproj/InfoPlist.strings", "BOINC Manager"); if (err) retval = err; err = FixInfoPlistFile("Info.plist"); if (err) retval = err; // BOINC Installer err = FixInfoPlist_Strings("./English.lproj/Installer-InfoPlist.strings", "BOINC Installer"); if (err) retval = err; err = FixInfoPlistFile("Installer-Info.plist"); if (err) retval = err; // BOINC PostInstall app err = FixInfoPlist_Strings("./English.lproj/PostInstall-InfoPlist.strings", "Install BOINC"); if (err) retval = err; err = FixInfoPlistFile("PostInstall-Info.plist"); if (err) retval = err; // BOINC Screen Saver err = FixInfoPlist_Strings("./English.lproj/ScreenSaver-InfoPlist.strings", "BOINC Screen Saver"); if (err) retval = err; err = FixInfoPlistFile("ScreenSaver-Info.plist"); if (err) retval = err; // BOINC Uninstaller err = FixInfoPlist_Strings("./English.lproj/Uninstaller-InfoPlist.strings", "Uninstall BOINC"); if (err) retval = err; err = FixInfoPlistFile("Uninstaller-Info.plist"); if (err) retval = err; // SystemMenu is not currently used err = FixInfoPlistFile("SystemMenu-Info.plist"); if (err) retval = err; // BOINCCmd err = FixInfoPlistFile("BoincCmd-Info.plist"); if (err) retval = err; // WaitPermissions is not currently used err = FixInfoPlistFile("WaitPermissions-Info.plist"); if (err) retval = err; // The following are not used by Xcode, and are probably obsolete 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; } int file_exists(const char* path) { struct stat buf; if (stat(path, &buf)) { return false; // stat() returns zero on success } return true; } int FixInfoPlist_Strings(char* myPath, char* name) { int retval = 0; FILE *f; time_t cur_time; struct tm *time_data; cur_time = time(NULL); time_data = localtime( &cur_time ); f = fopen(myPath, "w"); if (f) { fprintf(f, "/* Localized versions of Info.plist keys */\n\n"); fprintf(f, "CFBundleName = \"%s\";\n", name); fprintf(f, "CFBundleShortVersionString = \"%s version %s\";\n", name, BOINC_VERSION_STRING); fprintf(f, "CFBundleGetInfoString = \"%s version %s, Copyright %d University of California.\";\n", name, BOINC_VERSION_STRING, time_data->tm_year+1900); fflush(f); retval = fclose(f); } else { printf("Error creating file %s\n", myPath); retval = -1; } return retval; } int FixInfoPlistFile(char* name) { int retval = 0; FILE *fin = NULL, *fout = NULL; char *c, a, buf[1024]; char srcPath[MAXPATHLEN], dstPath[MAXPATHLEN]; strcpy(dstPath, "./"); strcat(dstPath, name); strcpy(srcPath, "../clientgui/mac/templates/"); strcat(srcPath, name); // Save the old file in case there is an error updating it if (file_exists(dstPath)) { rename(dstPath, "./temp"); } fin = fopen(srcPath, "r"); if (fin == NULL) goto bail; fout = fopen(dstPath, "w"); if (fout == NULL) { goto bail; } // Copy everything up to version number for (;;) { c = fgets(buf, sizeof(buf), fin); if (c == NULL) goto bail; // EOF c = strstr(buf, "CFBundleVersion"); if (c) break; // Found "CFBundleVersion" fputs(buf, fout); } c = strstr(buf, ""); if (c == NULL) { fputs(buf, fout); c = fgets(buf, sizeof(buf), fin); if (c == NULL) goto bail; // EOF c = strstr(buf, ""); if (c == NULL) goto bail; // "CFBundleVersion" not followed by "" } a = *(c+8); *(c+8) = '\0'; // Put terminator after "" fputs(buf, fout); // Copy up to end of "" fputs(BOINC_VERSION_STRING, fout); // Write the current version number *(c+8) = a; // Undo terminator we inserted c = strstr(buf, ""); // Skip over old version number in input fputs(c, fout); // Copy rest of input line // Copy rest of file for (;;) { c = fgets(buf, sizeof(buf), fin); if (c == NULL) break; // EOF fputs(buf, fout); } fclose(fin); fflush(fout); fclose(fout); unlink("temp"); return retval; bail: if (fin) fclose(fin); if (fout) fclose(fout); if (file_exists("./temp")) { rename("./temp", dstPath); // sprintf(buf, "mv -f temp %s", myPath); // retval = callPosixSpawn(buf); } else { sprintf(buf, "cp -f %s %s", srcPath, dstPath); retval = callPosixSpawn(buf); } printf("Error updating version number in file %s\n", dstPath); return -1; } int MakeBOINCPackageInfoPlistFile(char* myPath, char* brand) { int retval = 0; FILE *f; 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\n"); fprintf(f, "\tCFBundleShortVersionString\n"); fprintf(f, "\t%s\n", BOINC_VERSION_STRING); fprintf(f, "\tIFPkgFlagAllowBackRev\n\t1\n"); fprintf(f, "\tIFPkgFlagAuthorizationAction\n\tAdminAuthorization\n"); fprintf(f, "\tIFPkgFlagDefaultLocation\n\t/\n"); fprintf(f, "\tIFPkgFlagFollowLinks\n\t0\n"); fprintf(f, "\tIFPkgFlagInstallFat\n\t0\n"); fprintf(f, "\tIFPkgFlagInstalledSize\n\t6680\n"); fprintf(f, "\tIFPkgFlagIsRequired\n\t0\n"); fprintf(f, "\tIFPkgFlagOverwritePermissions\n\t0\n"); fprintf(f, "\tIFPkgFlagRelocatable\n\t0\n"); fprintf(f, "\tIFPkgFlagRestartAction\n\tNoRestart\n"); fprintf(f, "\tIFPkgFlagRootVolumeOnly\n\t1\n"); fprintf(f, "\tIFPkgFlagUpdateInstalledLanguages\n\t0\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; } // Create a MetaPackage which runs only BOINC,pkg but specifies Restart Required int MakeBOINCRestartPackageInfoPlistFile(char* myPath, char* brand) { int retval = 0; FILE *f; 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; 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 + VirtualBox\n", brand, BOINC_VERSION_STRING); fprintf(f, "\tCFBundleIdentifier\n\tedu.berkeley.boinc+vbox\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\tNoRestart\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\t\n"); fprintf(f, "\t\t\tIFPkgFlagPackageLocation\n\t\t\tVirtualBox.pkg\n"); fprintf(f, "\t\t\tIFPkgFlagPackageSelection\n\t\t\tselected\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; } #define NOT_IN_TOKEN 0 #define IN_SINGLE_QUOTED_TOKEN 1 #define IN_DOUBLE_QUOTED_TOKEN 2 #define IN_UNQUOTED_TOKEN 3 static int parse_posix_spawn_command_line(char* p, char** argv) { int state = NOT_IN_TOKEN; int argc=0; while (*p) { switch(state) { case NOT_IN_TOKEN: if (isspace(*p)) { } else if (*p == '\'') { p++; argv[argc++] = p; state = IN_SINGLE_QUOTED_TOKEN; break; } else if (*p == '\"') { p++; argv[argc++] = p; state = IN_DOUBLE_QUOTED_TOKEN; break; } else { argv[argc++] = p; state = IN_UNQUOTED_TOKEN; } break; case IN_SINGLE_QUOTED_TOKEN: if (*p == '\'') { if (*(p-1) == '\\') break; *p = 0; state = NOT_IN_TOKEN; } break; case IN_DOUBLE_QUOTED_TOKEN: if (*p == '\"') { if (*(p-1) == '\\') break; *p = 0; state = NOT_IN_TOKEN; } break; case IN_UNQUOTED_TOKEN: if (isspace(*p)) { *p = 0; state = NOT_IN_TOKEN; } break; } p++; } argv[argc] = 0; return argc; } #include int callPosixSpawn(const char *cmdline) { char command[1024]; char progName[1024]; char progPath[MAXPATHLEN]; char* argv[100]; int argc __attribute__((unused)) = 0; char *p; pid_t thePid = 0; int result = 0; int status = 0; extern char **environ; // Make a copy of cmdline because parse_posix_spawn_command_line modifies it strlcpy(command, cmdline, sizeof(command)); argc = parse_posix_spawn_command_line(const_cast(command), argv); strlcpy(progPath, argv[0], sizeof(progPath)); strlcpy(progName, argv[0], sizeof(progName)); p = strrchr(progName, '/'); if (p) { argv[0] = p+1; } else { argv[0] = progName; } #if VERBOSE_SPAWN printf("***********"); for (int i=0; i