int main(int argc, char *argv[]) { char pkgPath[MAXPATHLEN]; char postInstallAppPath[MAXPATHLEN]; char temp[MAXPATHLEN], temp2[MAXPATHLEN]; char brand[64], s[256]; char *p; OSStatus err = noErr; Boolean restartNeeded = true; FILE *restartNeededFile; FILE *f; long oldBrandID; if (!check_branding_arrays(temp, sizeof(temp))) { ShowMessage((char *)_("Branding array has too few entries: %s"), temp); return -1; } if (Initialize() != noErr) { return 0; } strncpy(loginName, getenv("USER"), sizeof(loginName)-1); if (loginName[0] == '\0') { ShowMessage((char *)_("Could not get user login name")); return 0; } snprintf(tempDirName, sizeof(tempDirName), "InstallBOINC-%s", loginName); snprintf(temp, sizeof(temp), "/tmp/%s", tempDirName); mkdir(temp, 0777); chmod(temp, 0777); // Needed because mkdir sets permissions restricted by umask (022) snprintf(temp, sizeof(temp), "rm -dfR /tmp/%s/BOINC_Installer_Errors", tempDirName); err = callPosixSpawn(temp); snprintf(Catalogs_Dir, sizeof(Catalogs_Dir), "/tmp/%s/BOINC_payload/Library/Application Support/BOINC Data/locale/", tempDirName); // Get the full path to Installer package inside this application's bundle getPathToThisApp(pkgPath, sizeof(pkgPath)); strlcpy(temp, pkgPath, sizeof(temp)); strlcat(pkgPath, "/Contents/Resources/", sizeof(pkgPath)); strlcpy(postInstallAppPath, pkgPath, sizeof(postInstallAppPath)); strlcat(postInstallAppPath, "PostInstall.app", sizeof(postInstallAppPath)); p = strrchr(temp, '/'); // Point to name of this application (e.g., "BOINC Installer.app") if (p == NULL) { p = temp - 1; } else { *p = '\0'; } // Delete any old project auto-attach key file from our temp directory snprintf(temp2, sizeof(temp2), "rm -dfR \"/tmp/%s/%s\"", tempDirName, ACCOUNT_DATA_FILENAME); err = callPosixSpawn(temp2); REPORT_ERROR(err); // Write a file containing the project auto-attach key into our temp // directory because the BOINC Data directory may not yet exist. // PostInstall.app will copy it into the BOINC Data directory laer snprintf(temp2, sizeof(temp2), "%s/%s", temp, ACCOUNT_DATA_FILENAME); if (boinc_file_exists(temp2)) { // If the project server put account_data.txt file in the same // parent directory as this installer, copy it into our temp directory snprintf(temp2, sizeof(temp2), "cp \"%s/%s\" \"/tmp/%s/%s\"", temp, ACCOUNT_DATA_FILENAME, tempDirName, ACCOUNT_DATA_FILENAME); err = callPosixSpawn(temp2); REPORT_ERROR(err); } else { // Create an account_data.txt file containing our // installer's filename and put it in our temp directory snprintf(temp2, sizeof(temp2), "/tmp/%s/%s", tempDirName, ACCOUNT_DATA_FILENAME); f = fopen(temp2, "w"); fputs(p+1, f); fclose(f); } // Write a temp file to tell our PostInstall.app the previous branding, if any oldBrandID = GetOldBrandID(); snprintf(temp, sizeof(temp), "/tmp/%s/OldBranding", tempDirName); f = fopen(temp, "w"); if (!f) { REPORT_ERROR(true); } else { fprintf(f, "BrandId=%ld\n", oldBrandID); fclose(f); } // To allow for branding, assume name of installer package inside bundle corresponds to name of this application strlcpy(brand, p+1, sizeof(brand)); strlcat(pkgPath, p+1, sizeof(pkgPath)); p = strrchr(pkgPath, ' '); // Strip off last space character and everything following if (p) *p = '\0'; p = strrchr(brand, ' '); // Strip off last space character and everything following if (p) *p = '\0'; strlcat(pkgPath, ".pkg", sizeof(pkgPath)); // In the unlikely situation that /tmp has files from an earlier attempt to install // BOINC by a different user, we won't have permission to delete or overwrite them, // so include the current user's name as part of the paths to our temporary files. // Expand the installer package snprintf(temp, sizeof(temp), "rm -dfR /tmp/%s/BOINC.pkg", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); snprintf(temp, sizeof(temp), "rm -dfR /tmp/%s/expanded_BOINC.pkg", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); snprintf(temp, sizeof(temp), "rm -dfR /tmp/%s/PostInstall.app", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); snprintf(temp, sizeof(temp), "rm -f /tmp/%s/BOINC_preferred_languages", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); snprintf(temp, sizeof(temp), "rm -f /tmp/%s/BOINC_restart_flag", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); sprintf(temp, "cp -fpR \"%s\" /tmp/%s/PostInstall.app", postInstallAppPath, tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); sprintf(temp, "pkgutil --expand \"%s\" /tmp/%s/expanded_BOINC.pkg", pkgPath, tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); if (err == noErr) { GetPreferredLanguages(); } if (compareOSVersionTo(10, 6) < 0) { LoadPreferredLanguages(); BringAppToFront(); p = strrchr(brand, ' '); // Strip off last space character and everything following if (p) *p = '\0'; ShowMessage((char *)_("Sorry, this version of %s requires system 10.6 or higher."), brand); snprintf(temp, sizeof(temp), "rm -dfR /tmp/%s/BOINC_payload", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); return -1; } snprintf(temp, sizeof(temp), "rm -dfR /tmp/%s/BOINC_payload", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); // 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); err = callPosixSpawn (s); REPORT_ERROR(err); restartNeeded = IsRestartNeeded(); // Write a temp file to tell our PostInstall.app whether restart is needed snprintf(temp, sizeof(temp), "/tmp/%s/BOINC_restart_flag", tempDirName); restartNeededFile = fopen(temp, "w"); if (restartNeededFile) { fputs(restartNeeded ? "1\n" : "0\n", restartNeededFile); fclose(restartNeededFile); } if (restartNeeded) { if (err == noErr) { // Change onConclusion="none" to onConclusion="RequireRestart" snprintf(temp, sizeof(temp), "sed -i \".bak\" s/onConclusion=\"none\"/onConclusion=\"RequireRestart\"/g /tmp/%s/expanded_BOINC.pkg/Distribution", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); } if (err == noErr) { snprintf(temp, sizeof(temp), "rm -dfR /tmp/%s/expanded_BOINC.pkg/Distribution.bak", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); // Flatten the installer package sprintf(temp, "pkgutil --flatten /tmp/%s/expanded_BOINC.pkg /tmp/%s/%s.pkg", tempDirName, tempDirName, brand); err = callPosixSpawn(temp); REPORT_ERROR(err); } if (err == noErr) { snprintf(temp, sizeof(temp), "rm -dfR /tmp/%s/expanded_BOINC.pkg", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); sprintf(temp, "open \"/tmp/%s/%s.pkg\"", tempDirName, brand); err = callPosixSpawn(temp); REPORT_ERROR(err); return err; } } snprintf(temp, sizeof(temp), "rm -dfR /tmp/%s/expanded_BOINC.pkg", tempDirName); err = callPosixSpawn(temp); REPORT_ERROR(err); sprintf(temp, "open \"%s\"", pkgPath); err = callPosixSpawn(temp); REPORT_ERROR(err); return err; }
int main(int argc, char *argv[]) { long brandID = 0; Boolean AddUsers = false; Boolean SetSavers = false; Boolean isBMGroupMember, isBPGroupMember; Boolean saverIsSet = false; passwd *pw; uid_t saved_uid; group grpBOINC_master, *grpBOINC_masterPtr; group grpBOINC_project, *grpBOINC_projectPtr; char bmBuf[32768]; char bpBuf[32768]; char loginName[256]; short index, i; FILE *f; int flag; char *p; char s[1024], buf[1024]; OSStatus err; brandID = GetBrandID(); #ifndef _DEBUG if (getuid() != 0) { printf("This program must be run as root\n"); printUsage(brandID); return 0; } #endif saved_uid = geteuid(); if (argc < 3) { printUsage(brandID); return 0; } if (strcmp(argv[1], "-a") == 0) { AddUsers = true; } else if (strcmp(argv[1], "-s") == 0) { AddUsers = true; SetSavers = true; } else if (strcmp(argv[1], "-r") != 0) { printUsage(brandID); return 0; } printf("\n"); if (!check_branding_arrays(s, sizeof(s))) { printf("Branding array has too few entries: %s\n", s); return -1; } loginName[0] = '\0'; strncpy(loginName, getenv("USER"), sizeof(loginName)-1); err = getgrnam_r("boinc_master", &grpBOINC_master, bmBuf, sizeof(bmBuf), &grpBOINC_masterPtr); if (err) { // Should never happen unless buffer too small puts("getgrnam(\"boinc_master\") failed\n"); return -1; } err = getgrnam_r("boinc_project", &grpBOINC_project, bpBuf, sizeof(bpBuf), &grpBOINC_projectPtr); if (err) { // Should never happen unless buffer too small puts("getgrnam(\"boinc_project\") failed\n"); return -1; } for (index=2; index<argc; index++) { // getpwnam works with either the full / login name (pw->pw_gecos) // or the short / Posix name (pw->pw_name) pw = getpwnam(argv[index]); if ((pw == NULL) || (pw->pw_uid < 501)) { printf("User %s not found.\n\n", argv[index]); continue; } flag = 0; sprintf(s, "dscl . -read \"/Users/%s\" NFSHomeDirectory", pw->pw_name); f = popen(s, "r"); if (!f) { flag = 1; } else { while (PersistentFGets(buf, sizeof(buf), f)) { p = strrchr(buf, ' '); if (p) { if (strstr(p, "/var/empty") != NULL) { flag = 1; break; } } } pclose(f); } if (flag) { sprintf(s, "dscl . -read \"/Users/%s\" UserShell", pw->pw_name); f = popen(s, "r"); if (!f) { flag |= 2; } else { while (PersistentFGets(buf, sizeof(buf), f)) { p = strrchr(buf, ' '); if (p) { if (strstr(p, "/usr/bin/false") != NULL) { flag |= 2; break; } } } pclose(f); } } if (flag == 3) { // if (Home Directory == "/var/empty") && (UserShell == "/usr/bin/false") printf("%s is not a valid user name.\n\n", argv[index]); continue; } printf("%s user %s (/Users/%s)\n", AddUsers? "Adding" : "Removing", pw->pw_gecos, pw->pw_name); isBMGroupMember = false; i = 0; while ((p = grpBOINC_master.gr_mem[i]) != NULL) { // Step through all users in group boinc_master if (strcmp(p, pw->pw_name) == 0) { // Only the short / Posix names are in the list // User is a member of group boinc_master isBMGroupMember = true; break; } ++i; } isBPGroupMember = false; i = 0; while ((p = grpBOINC_project.gr_mem[i]) != NULL) { // Step through all users in group boinc_project if (strcmp(p, pw->pw_name) == 0) { // Only the short / Posix names are in the list // User is a member of group boinc_master isBPGroupMember = true; break; } ++i; } if ((!isBMGroupMember) && AddUsers) { sprintf(s, "dscl . -merge /groups/boinc_master GroupMembership %s", pw->pw_name); callPosixSpawn(s); } if ((!isBPGroupMember) && AddUsers) { sprintf(s, "dscl . -merge /groups/boinc_project GroupMembership %s", pw->pw_name); callPosixSpawn(s); } if (isBMGroupMember && (!AddUsers)) { sprintf(s, "dscl . -delete /Groups/boinc_master GroupMembership %s", pw->pw_name); callPosixSpawn(s); } if (isBPGroupMember && (!AddUsers)) { sprintf(s, "dscl . -delete /Groups/boinc_project GroupMembership %s", pw->pw_name); callPosixSpawn(s); } if (!AddUsers) { // Delete per-user BOINC Manager and screensaver files sprintf(s, "rm -fR \"/Users/%s/Library/Application Support/BOINC\"", pw->pw_name); callPosixSpawn (s); } // Set or remove login item for this user bool useOSASript = false; if ((compareOSVersionTo(10, 13) < 0) || (strcmp(loginName, pw->pw_name) == 0) || (strcmp(loginName, pw->pw_gecos) == 0)) { useOSASript = true; } #if USE_OSASCRIPT_FOR_ALL_LOGGED_IN_USERS if (! useOSASript) { useOSASript = IsUserLoggedIn(pw->pw_name); } #endif if (useOSASript) { snprintf(s, sizeof(s), "/Users/%s/Library/LaunchAgents/edu.berkeley.boinc.plist", pw->pw_name); boinc_delete_file(s); SetLoginItemOSAScript(brandID, !AddUsers, pw->pw_name); } else { SetLoginItemLaunchAgent(brandID, !AddUsers, pw); } saverIsSet = false; err = GetCurrentScreenSaverSelection(pw, s, sizeof(s) -1); #if VERBOSE fprintf(stderr, "Current Screensaver Selection for user %s is: \"%s\"\n", pw->pw_name, s); #endif if (err == noErr) { if (!strcmp(s, saverName[brandID])) { saverIsSet = true; } } if (SetSavers) { if (saverIsSet) { printf("Screensaver already set to %s for user %s (/Users/%s)\n", saverName[brandID], pw->pw_gecos, pw->pw_name); } else { printf("Setting screensaver to %s for user %s (/Users/%s)\n", saverName[brandID], pw->pw_gecos, pw->pw_name); } } if ((!saverIsSet) && SetSavers) { seteuid(pw->pw_uid); // Temporarily set effective uid to this user sprintf(s, "/Library/Screen Savers/%s.saver", saverName[brandID]); err = SetScreenSaverSelection(pw, saverName[brandID], s, 0); #if VERBOSE fprintf(stderr, "SetScreenSaverSelection for user %s (uid %d) to \"%s\" returned error %d\n", pw->pw_name, geteuid(), saverName[brandID], err); #endif seteuid(saved_uid); // Set effective uid back to privileged user // This seems to work also: // sprintf(buf, "su -l \"%s\" -c 'defaults -currentHost write com.apple.screensaver moduleDict -dict moduleName \"%s\" path \"%s\ type 0'", pw->pw_name, saverName[brandID], s); // callPosixSpawn(s); } if (saverIsSet && (!AddUsers)) { printf("Setting screensaver to Flurry for user %s (/Users/%s)\n", pw->pw_gecos, pw->pw_name); seteuid(pw->pw_uid); // Temporarily set effective uid to this user err = SetScreenSaverSelection(pw, "Flurry", "/System/Library/Screen Savers/Flurry.saver", 0); #if VERBOSE fprintf(stderr, "SetScreenSaverSelection for user %s (%d) to Flurry returned error %d\n", pw->pw_name, geteuid(), err); #endif seteuid(saved_uid); // Set effective uid back to privileged user } seteuid(saved_uid); // Set effective uid back to privileged user printf("\n"); } printf("WARNING: Changes may require a system restart to take effect.\n"); return 0; }