// After installation has completed, delete the installer receipt. // If we don't need to logout the user, also launch BOINC Manager. int DeleteReceipt() { ProcessSerialNumber installerPSN; long brandID = 0; int i; pid_t installerPID = 0; OSStatus err; int finalInstallAction; FSRef fileRef; char s[256]; struct stat sbuf; OSStatus err_fsref; Initialize(); err = CheckLogoutRequirement(&finalInstallAction); err = FindProcess ('APPL', 'xins', &installerPSN); if (err == noErr) err = GetProcessPID(&installerPSN , &installerPID); // Launch BOINC Manager when user closes installer or after 15 seconds for (i=0; i<15; i++) { // Wait 15 seconds max for installer to quit sleep (1); if (err == noErr) if (FindProcessPID(NULL, installerPID) == 0) break; } brandID = GetBrandID(); // Remove installer package receipt so we can run installer again if needed to fix permissions // "rm -rf /Library/Receipts/GridRepublic.pkg" sprintf(s, "rm -rf %s", receiptNameEscaped[brandID]); system (s); // err_fsref = FSPathMakeRef((StringPtr)"/Applications/GridRepublic Desktop.app", &fileRef, NULL); err_fsref = FSPathMakeRef((StringPtr)appName[brandID], &fileRef, NULL); if (finalInstallAction == launchWhenDone) { // If system is set up to run BOINC Client as a daemon using launchd, launch it // as a daemon and allow time for client to start before launching BOINC Manager. err = stat("/Library/LaunchDaemons/edu.berkeley.boinc.plist", &sbuf); if (err == noErr) { system("launchctl unload /Library/LaunchDaemons/edu.berkeley.boinc.plist"); i = system("launchctl load /Library/LaunchDaemons/edu.berkeley.boinc.plist"); if (i == 0) sleep (2); } err = LSOpenFSRef(&fileRef, NULL); } return 0; }
Boolean myFilterProc(DialogRef theDialog, EventRecord *theEvent, DialogItemIndex *itemHit) { static time_t lastCheckTime = 0; time_t now = time(NULL); pid_t waitPermissionsPID = 0; if (now != lastCheckTime) { waitPermissionsPID = FindProcessPID("WaitPermissions", 0); if (waitPermissionsPID == 0) { *itemHit = kStdOkItemIndex; return true; } lastCheckTime = now; // Limit delay to 3 minutes if ((now - waitPermissionsStartTime) > 180) { *itemHit = kStdOkItemIndex; return true; } } return false; }
void CScreensaver::HandleRPCError() { static time_t last_RPC_retry = 0; time_t now = time(0); // Attempt to restart BOINC Client if needed, reinitialize the RPC client and state rpc->close(); m_bConnected = false; if (saverState == SaverState_CantLaunchCoreClient) { if ((now - last_RPC_retry) < RPC_RETRY_INTERVAL) { return; } last_RPC_retry = now; } else { // There is a possible race condition where the Core Client was in the // process of shutting down just as ScreenSaver started, so initBOINCApp() // found it already running but now it has shut down. This code takes // care of that and other situations where the Core Client quits unexpectedy. // Code in initBOINC_App() limits # launch retries to 3 to prevent thrashing. if (FindProcessPID("boinc", 0) == 0) { saverState = SaverState_RelaunchCoreClient; m_bResetCoreState = true; } } // If Core Client is hung, it might cause RPCs to hang, preventing us from // shutting down the Data Management Thread, so don't reinitialize the RPC // client if we have told the Data Management Thread to exit. if (m_bQuitDataManagementProc) { return; } // Otherwise just reinitialize the RPC client and state and keep trying if (!rpc->init(NULL)) { m_bConnected = true; } // Error message after timeout? }
int main(int argc, char *argv[]) { Boolean Success; ProcessSerialNumber ourProcess, installerPSN; short itemHit; long brandID = 0; int i; pid_t installerPID = 0, coreClientPID = 0, waitPermissionsPID = 0; FSRef fileRef; OSStatus err, err_fsref; FILE *f; char s[256]; char *q; #ifdef SANDBOX uid_t saved_euid, saved_uid, b_m_uid; passwd *pw; int finalInstallAction; DialogRef theWin; #else // SANDBOX group *grp; #endif // SANDBOX appName[0] = "/Applications/BOINCManager.app"; appNameEscaped[0] = "/Applications/BOINCManager.app"; brandName[0] = "BOINC"; saverName[0] = "BOINCSaver"; saverNameEscaped[0] = "BOINCSaver"; receiptNameEscaped[0] = "/Library/Receipts/BOINC.pkg"; appName[1] = "/Applications/GridRepublic Desktop.app"; appNameEscaped[1] = "/Applications/GridRepublic\\ Desktop.app"; brandName[1] = "GridRepublic"; saverName[1] = "GridRepublic"; saverNameEscaped[1] = "GridRepublic"; receiptNameEscaped[1] = "/Library/Receipts/GridRepublic.pkg"; appName[2] = "/Applications/Progress Thru Processors Desktop.app"; appNameEscaped[2] = "/Applications/Progress\\ Thru\\ Processors\\ Desktop.app"; brandName[2] = "Progress Thru Processors"; saverName[2] = "Progress Thru Processors"; saverNameEscaped[2] = "Progress\\ Thru\\ Processors"; receiptNameEscaped[2] = "/Library/Receipts/Progress\\ Thru\\ Processors.pkg"; ::GetCurrentProcess (&ourProcess); // getlogin() gives unreliable results under OS 10.6.2, so use environment strncpy(loginName, getenv("USER"), sizeof(loginName)-1); err = Gestalt(gestaltSystemVersion, &OSVersion); if (err != noErr) return err; for (i=0; i<argc; i++) { if (strcmp(argv[i], "-part2") == 0) return DeleteReceipt(); } Initialize(); QuitBOINCManager('BNC!'); // Quit any old instance of BOINC manager sleep(2); // Core Client may still be running if it was started without Manager coreClientPID = FindProcessPID("boinc", 0); if (coreClientPID) kill(coreClientPID, SIGTERM); // boinc catches SIGTERM & exits gracefully err = FindProcess ('APPL', 'xins', &installerPSN); if (err == noErr) err = GetProcessPID(&installerPSN , &installerPID); brandID = GetBrandID(); if ((brandID < 0) || (brandID >= NUMBRANDS)) { // Safety check brandID = 0; } if (OSVersion < 0x1040) { ::SetFrontProcess(&ourProcess); // Remove everything we've installed // "\pSorry, this version of GridRepublic requires system 10.4.0 or higher." s[0] = sprintf(s+1, "Sorry, this version of %s requires system 10.4.0 or higher.", brandName[brandID]); StandardAlert (kAlertStopAlert, (StringPtr)s, NULL, NULL, &itemHit); // "rm -rf /Applications/GridRepublic\\ Desktop.app" sprintf(s, "rm -rf %s", appNameEscaped[brandID]); system (s); // "rm -rf /Library/Screen\\ Savers/GridRepublic.saver" sprintf(s, "rm -rf /Library/Screen\\ Savers/%s.saver", saverNameEscaped[brandID]); system (s); // "rm -rf /Library/Receipts/GridRepublic.pkg" sprintf(s, "rm -rf %s", receiptNameEscaped[brandID]); system (s); // We don't customize BOINC Data directory name for branding system ("rm -rf /Library/Application\\ Support/BOINC\\ Data"); err = kill(installerPID, SIGKILL); ExitToShell(); } sleep (2); // Install all_projects_list.xml file, but only if one doesn't // already exist, since a pre-existing one is probably newer. f = fopen("/Library/Application Support/BOINC Data/all_projects_list.xml", "r"); if (f) { fclose(f); // Already exists } else { system ("cp -fp Contents/Resources/all_projects_list.xml /Library/Application\\ Support/BOINC\\ Data/"); system ("chmod a-x /Library/Application\\ Support/BOINC\\ Data/all_projects_list.xml"); } Success = false; #ifdef SANDBOX CheckUserAndGroupConflicts(); for (i=0; i<5; ++i) { err = CreateBOINCUsersAndGroups(); if (err != noErr) { // print_to_log_file("CreateBOINCUsersAndGroups returned %d (repetition=%d)", err, i); continue; } // err = SetBOINCAppOwnersGroupsAndPermissions("/Applications/GridRepublic Desktop.app"); err = SetBOINCAppOwnersGroupsAndPermissions(appName[brandID]); if (err != noErr) { // print_to_log_file("SetBOINCAppOwnersGroupsAndPermissions returned %d (repetition=%d)", err, i); continue; } err = SetBOINCDataOwnersGroupsAndPermissions(); if (err != noErr) { // print_to_log_file("SetBOINCDataOwnersGroupsAndPermissions returned %d (repetition=%d)", err, i); continue; } err = check_security(appName[brandID], "/Library/Application Support/BOINC Data", true, false); if (err == noErr) break; // print_to_log_file("check_security returned %d (repetition=%d)", err, i); } #else // ! defined(SANDBOX) // The BOINC Manager and Core Client have the set-user-ID-on-execution // flag set, so their ownership is important and must match the // ownership of the BOINC Data directory. // Find an appropriate admin user to set as owner of installed files // First, try the user currently logged in grp = getgrnam("admin"); i = 0; while ((p = grp->gr_mem[i]) != NULL) { // Step through all users in group admin if (strcmp(p, loginName) == 0) { Success = true; // Logged in user is a member of group admin break; } ++i; } // If currently logged in user is not admin, use first non-root admin user if (!Success) { i = 0; while ((p = grp->gr_mem[i]) != NULL) { // Step through all users in group admin if (strcmp(p, "root") != 0) break; ++i; } } // Set owner of branded BOINCManager and contents, including core client // "chown -Rf username /Applications/GridRepublic\\ Desktop.app" sprintf(s, "chown -Rf %s %s", p, appNameEscaped[brandID]); system (s); // Set owner of BOINC Screen Saver // "chown -Rf username /Library/Screen\\ Savers/GridRepublic.saver" sprintf(s, "chown -Rf %s /Library/Screen\\ Savers/%s.saver", p, saverNameEscaped[brandID]); system (s); // We don't customize BOINC Data directory name for branding // "chown -Rf username /Library/Application\\ Support/BOINC\\ Data" sprintf(s, "chown -Rf %s /Library/Application\\ Support/BOINC\\ Data", p); system (s); // "chmod -R a+s /Applications/GridRepublic\\ Desktop.app" sprintf(s, "chmod -R a+s %s", appNameEscaped[brandID]); system (s); #endif // ! defined(SANDBOX) // Remove any branded versions of BOINC other than ours (i.e., old versions) for (i=0; i< NUMBRANDS; i++) { if (i == brandID) continue; // "rm -rf /Applications/GridRepublic\\ Desktop.app" sprintf(s, "rm -rf %s", appNameEscaped[i]); system (s); // "rm -rf /Library/Screen\\ Savers/GridRepublic.saver" sprintf(s, "rm -rf /Library/Screen\\ Savers/%s.saver", saverNameEscaped[i]); system (s); } if (brandID == 0) { // Installing generic BOINC system ("rm -f /Library/Application\\ Support/BOINC\\ Data/Branding"); } // err_fsref = FSPathMakeRef((StringPtr)"/Applications/GridRepublic Desktop.app", &fileRef, NULL); err_fsref = FSPathMakeRef((StringPtr)appName[brandID], &fileRef, NULL); if (err_fsref == noErr) err = LSRegisterFSRef(&fileRef, true); err = UpdateAllVisibleUsers(brandID); if (err != noErr) return err; #ifdef SANDBOX err = CheckLogoutRequirement(&finalInstallAction); if (finalInstallAction == launchWhenDone) { // 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. saved_uid = getuid(); saved_euid = geteuid(); pw = getpwnam("boinc_master"); b_m_uid = pw->pw_uid; seteuid(b_m_uid); for (i=0; i<120; i++) { err = TestRPCBind(); if (err == noErr) break; sleep(1); } seteuid(saved_euid); ProcessSerialNumber ourPSN; ProcessInfoRec pInfo; FSRef ourFSRef, theFSRef; char thePath[MAXPATHLEN]; // Get the full path to this PostInstall application's bundle err = GetCurrentProcess (&ourPSN); if (err) return -1000; // Should never happen memset(&pInfo, 0, sizeof(pInfo)); pInfo.processInfoLength = sizeof( ProcessInfoRec ); err = GetProcessInformation(&ourPSN, &pInfo); if (err) return -1001; // Should never happen err = GetProcessBundleLocation(&ourPSN, &ourFSRef); if (err) return -1002; // Should never happen err = FSRefMakePath (&ourFSRef, (UInt8*)thePath, sizeof(thePath)); if (err) return -1003; // Should never happen q = strrchr(thePath, '/'); if (q == NULL) return -1004; // Should never happen *++q = '\0'; strlcat(thePath, "WaitPermissions.app", sizeof(thePath)); err = FSPathMakeRef((StringPtr)thePath, &theFSRef, NULL); // When we first create the boinc_master group and add the current user to the // new group, there is a delay before the new group membership is recognized. // If we launch the BOINC Manager too soon, it will fail with a -1037 permissions // error, so we wait until the current user can access the switcher application. // Apparently, in order to get the changed permissions / group membership, we must // launch a new process belonging to the user. It may also need to be in a new // process group or new session. Neither system() nor popen() works, even after // setting the uid and euid back to the logged in user, but LSOpenFSRef() does. // The WaitPermissions application loops until it can access the switcher // application. err = LSOpenFSRef(&theFSRef, NULL); waitPermissionsStartTime = time(NULL); for (i=0; i<15; i++) { // Show "Please wait..." alert after 15 seconds waitPermissionsPID = FindProcessPID("WaitPermissions", 0); if (waitPermissionsPID == 0) { return 0; } sleep(1); } CreateStandardAlert(kAlertNoteAlert, CFSTR("Finishing install. Please wait ..."), CFSTR("This may take a few more minutes."), NULL, &theWin); HideDialogItem(theWin, kStdOkItemIndex); RemoveDialogItems(theWin, kStdOkItemIndex, 1, false); RunStandardAlert(theWin, &myFilterProc, &itemHit); } #endif // SANDBOX return 0; }
// Returns new desired Animation Frequency (per second) or 0 for no change int CScreensaver::getSSMessage(char **theMessage, int* coveredFreq) { int newFrequency = TEXTLOGOFREQUENCY; *coveredFreq = 0; pid_t myPid; CC_STATE ccstate; OSStatus err; if (ScreenIsBlanked) { setSSMessageText(0); // No text message *theMessage = m_MessageText; return NOTEXTLOGOFREQUENCY; } CheckDualGPUStatus(); switch (saverState) { case SaverState_RelaunchCoreClient: err = initBOINCApp(); break; case SaverState_LaunchingCoreClient: if (m_wasAlreadyRunning) { setSSMessageText(ConnectingCCMsg); } else { setSSMessageText(LaunchingCCMsg); } myPid = FindProcessPID(NULL, m_CoreClientPID); if (myPid) { saverState = SaverState_CoreClientRunning; if (!rpc->init(NULL)) { // Initialize communications with Core Client m_bConnected = true; if (IsDualGPUMacbook) { ccstate.clear(); ccstate.global_prefs.init_bools(); int result = rpc->get_state(ccstate); if (!result) { OKToRunOnBatteries = ccstate.global_prefs.run_on_batteries; } else { OKToRunOnBatteries = false; } if (OKToRunOnBatteries) { SetDiscreteGPU(true); } } } // Set up a separate thread for communicating with Core Client // and running screensaver graphics CreateDataManagementThread(); // ToDo: Add a timeout after which we display error message } else // Take care of the possible race condition where the Core Client was in the // process of shutting down just as ScreenSaver started, so initBOINCApp() // found it already running but now it has shut down. if (m_wasAlreadyRunning) { // If we launched it, then just wait for it to start saverState = SaverState_RelaunchCoreClient; } break; case SaverState_CoreClientRunning: if (IsDualGPUMacbook && RunningOnBattery && !OKToRunOnBatteries) { setSSMessageText(RunningOnBatteryMsg); break; } // RPC called in DataManagementProc() setSSMessageText(ConnectingCCMsg); if (! m_bResetCoreState) { saverState = SaverState_ConnectedToCoreClient; } break; case SaverState_ConnectedToCoreClient: if (IsDualGPUMacbook && RunningOnBattery && !OKToRunOnBatteries) { setSSMessageText(RunningOnBatteryMsg); break; } switch (m_hrError) { case 0: setSSMessageText(ConnectedCCMsg); break; // No status response yet from DataManagementProc case SCRAPPERR_SCREENSAVERBLANKED: setSSMessageText(0); // No text message ScreenIsBlanked = true; if (IsDualGPUMacbook && (GPUSelectConnect != IO_OBJECT_NULL)) { IOServiceClose(GPUSelectConnect); GPUSelectConnect = IO_OBJECT_NULL; } break; #if 0 // Not currently used case SCRAPPERR_QUITSCREENSAVERREQUESTED: // setSSMessageText(BOINCExitedSaverMode); // Wait 1 second to allow ScreenSaver engine to close us down if (++gQuitCounter > (m_MessageText[0] ? TEXTLOGOFREQUENCY : NOTEXTLOGOFREQUENCY)) { closeBOINCSaver(); KillScreenSaver(); // Stop the ScreenSaver Engine } break; #endif case SCRAPPERR_CANTLAUNCHDEFAULTGFXAPP: setSSMessageText(CantLaunchDefaultGFXAppMsg); break; case SCRAPPERR_DEFAULTGFXAPPCANTCONNECT: setSSMessageText(DefaultGFXAppCantRPCMsg); break; case SCRAPPERR_DEFAULTGFXAPPCRASHED: setSSMessageText(DefaultGFXAppCrashedMsg); break; default: // m_bErrorMode is TRUE if we should display moving logo (no graphics app is running) // m_bErrorMode is FALSE if a graphics app was launched and has not exit if (! m_bErrorMode) { // NOTE: My tests seem to confirm that the top window is always the first // window returned by NSWindowList under OS 10.5 and the second window // returned by NSWindowList under OS 10.3.9 and OS 10.4. However, Apple's // documentation is unclear whether we can depend on this. So I have // added some safety by doing two things: // [1] Only use the NSWindowList test when we have started project or default // graphics. // [2] Assume that our window is covered 45 seconds after starting project // graphics even if the NSWindowList test did not indicate that is so. // // The -animateOneFrame method in Mac_SaverModuleView.m does the NSWindowList test // only if we return a non-zero value for coveredFreq. // // Tell the calling routine to set the frame rate to NOTEXTLOGOFREQUENCY if // NSWindowList indicates that science app graphics window has covered our window. *coveredFreq = NOTEXTLOGOFREQUENCY; if (m_iGraphicsStartingMsgCounter > 0) { // Show ScreenSaverAppStartingMsg for GFX_STARTING_MSG_DURATION seconds or until // NSWindowList indicates that science app graphics window has covered our window setSSMessageText(ScreenSaverAppStartingMsg); m_iGraphicsStartingMsgCounter--; } else { // Don't waste CPU cycles when the science app is drawing over our window setSSMessageText(0); // No text message } } // End if (! m_bErrorMode) break; // End default case of switch (m_hrError) } // end switch (m_hrError) break; // End case SaverState_ConnectedToCoreClient of switch (saverState) case SaverState_ControlPanelTestMode: setSSMessageText(BOINCTestModeMsg); break; case SaverState_UnrecoverableError: setSSMessageText(BOINCUnrecoverableErrorMsg); break; case SaverState_CantLaunchCoreClient: if (IsDualGPUMacbook && RunningOnBattery && !OKToRunOnBatteries) { setSSMessageText(RunningOnBatteryMsg); break; } setSSMessageText(CantLaunchCCMsg); // Set up a separate thread for running screensaver graphics // even if we can't communicate with core client CreateDataManagementThread(); break; case SaverState_Idle: break; // Should never get here; fixes compiler warning } // end switch (saverState) if (IsDualGPUMacbook && RunningOnBattery && !OKToRunOnBatteries) { if ((m_dwBlankScreen) && (time(0) > m_dwBlankTime) && (m_dwBlankTime > 0)) { setSSMessageText(0); // No text message ScreenIsBlanked = true; } } if (m_MessageText[0]) { newFrequency = TEXTLOGOFREQUENCY; } else { newFrequency = NOTEXTLOGOFREQUENCY; } *theMessage = m_MessageText; return newFrequency; }
OSStatus CScreensaver::initBOINCApp() { char boincPath[2048]; pid_t myPid; int status; OSStatus err; long brandId = 0; saverState = SaverState_CantLaunchCoreClient; brandId = GetBrandID(); switch(brandId) { case 1: m_BrandText = "GridRepublic Desktop"; break; case 2: m_BrandText = "Progress Thru Processors Desktop"; break; case 3: m_BrandText = "Charity Engine Desktop"; break; default: m_BrandText = "BOINC"; break; } m_CoreClientPID = FindProcessPID("boinc", 0); if (m_CoreClientPID) { m_wasAlreadyRunning = true; saverState = SaverState_LaunchingCoreClient; retryCount = 0; return noErr; } m_wasAlreadyRunning = false; if (++retryCount > 3) // Limit to 3 relaunches to prevent thrashing return -1; // Find boinc client within BOINCManager.app // First, try default path strcpy(boincPath, "/Applications/"); if (brandId) { strcat(boincPath, m_BrandText); } else { strcat(boincPath, "BOINCManager"); } strcat(boincPath, ".app/Contents/Resources/boinc"); // If not at default path, search for it by creator code and bundle identifier if (!boinc_file_exists(boincPath)) { err = GetpathToBOINCManagerApp(boincPath, sizeof(boincPath)); if (err) { saverState = SaverState_CantLaunchCoreClient; return err; } else { strcat(boincPath, "/Contents/Resources/boinc"); } } if ( (myPid = fork()) < 0) return -1; else if (myPid == 0) // child { // We don't customize BOINC Data directory name for branding #if 0 // Code for separate data in each user's private directory char buf[256]; safe_strcpy(buf, getenv("HOME")); safe_strcat(buf, "/Library/Application Support/BOINC Data"); status = chdir(buf); #else // All users share the same data status = chdir("/Library/Application Support/BOINC Data"); #endif if (status) { perror("chdir"); fflush(NULL); _exit(status); } status = execl(boincPath, boincPath, "-redirectio", "-saver", (char *) 0); fflush(NULL); _exit(127); // execl error (execl should never return) } else { m_CoreClientPID = myPid; // make this available globally saverState = SaverState_LaunchingCoreClient; } return noErr; }
Boolean SetLoginItemOSAScript(long brandID, Boolean deleteLogInItem, char *userName) { int i, j; char cmd[2048]; char systemEventsPath[1024]; pid_t systemEventsPID; OSErr err = 0, err2 = 0; #if USE_OSASCRIPT_FOR_ALL_LOGGED_IN_USERS // NOTE: It may not be necessary to kill and relaunch the // System Events application for each logged in user under High Sierra Boolean isHighSierraOrLater = (compareOSVersionTo(10, 13) >= 0); #endif #if VERBOSE fprintf(stderr, "Adjusting login items for user %s\n", userName); fflush(stderr); #endif // We must launch the System Events application for the target user err = noErr; systemEventsPath[0] = '\0'; err = GetPathToAppFromID(kSystemEventsCreator, kSystemEventsBundleID, systemEventsPath, sizeof(systemEventsPath)); #if VERBOSE if (err == noErr) { fprintf(stderr, "SystemEvents is at %s\n", systemEventsPath); } else { fprintf(stderr, "GetPathToAppFromID(kSystemEventsCreator, kSystemEventsBundleID) returned error %d ", (int) err); } #endif if (err == noErr) { // Find SystemEvents process. If found, quit it in case // it is running under a different user. #if VERBOSE fprintf(stderr, "Telling System Events to quit (at start of SetLoginItemOSAScript)\n"); fflush(stderr); #endif systemEventsPID = FindProcessPID(systemEventsAppName, 0); if (systemEventsPID != 0) { err = kill(systemEventsPID, SIGKILL); } #if VERBOSE if (err != noErr) { fprintf(stderr, "(systemEventsPID, SIGKILL) returned error %d \n", (int) err); fflush(stderr); } #endif // Wait for the process to be gone for (i=0; i<50; ++i) { // 5 seconds max delay SleepSeconds(0.1); // 1/10 second systemEventsPID = FindProcessPID(systemEventsAppName, 0); if (systemEventsPID == 0) break; } if (i >= 50) { #if VERBOSE fprintf(stderr, "Failed to make System Events quit\n"); fflush(stderr); #endif err = noErr; goto cleanupSystemEvents; } sleep(4); } if (systemEventsPath[0] != '\0') { #if VERBOSE fprintf(stderr, "Launching SystemEvents for user %s\n", userName); fflush(stderr); #endif for (j=0; j<5; ++j) { sprintf(cmd, "sudo -u \"%s\" -b \"%s/Contents/MacOS/System Events\" &", userName, systemEventsPath); err = callPosixSpawn(cmd); #if VERBOSE if (err) { fprintf(stderr, "Command: %s returned error %d (try %d of 5)\n", cmd, (int) err, j); fflush(stderr); } #endif // Wait for the process to start for (i=0; i<50; ++i) { // 5 seconds max delay SleepSeconds(0.1); // 1/10 second systemEventsPID = FindProcessPID(systemEventsAppName, 0); if (systemEventsPID != 0) break; } if (i < 50) break; // Exit j loop on success } if (j >= 5) { #if VERBOSE fprintf(stderr, "Failed to launch System Events for user %s\n", userName); fflush(stderr); #endif err = noErr; goto cleanupSystemEvents; } } sleep(2); #if VERBOSE fprintf(stderr, "Deleting any login items containing %s for user %s\n", appName[brandID], userName); fflush(stderr); #endif #if USE_OSASCRIPT_FOR_ALL_LOGGED_IN_USERS if (isHighSierraOrLater) { sprintf(cmd, "su -l \"%s\" -c 'osascript -e \"tell application \\\"System Events\\\" to delete login item \\\"%s\\\"\"'", userName, appName[i]); } else #endif { sprintf(cmd, "sudo -u \"%s\" osascript -e 'tell application \"System Events\" to delete login item \"%s\"'", userName, appName[i]); } err = callPosixSpawn(cmd); #if VERBOSE if (err) { fprintf(stderr, "Command: %s\n", cmd); fprintf(stderr, "Delete login item containing %s returned error %d\n", appName[brandID], err); fflush(stderr); } #endif if (deleteLogInItem) { err = noErr; goto cleanupSystemEvents; } #if VERBOSE fprintf(stderr, "Making new login item %s for user %s\n", appName[brandID], userName); fflush(stderr); #endif #if USE_OSASCRIPT_FOR_ALL_LOGGED_IN_USERS if (isHighSierraOrLater) { sprintf(cmd, "su -l \"%s\" -c 'osascript -e \"tell application \\\"System Events\\\" to make new login item at end with properties {path:\\\"%s\\\", hidden:true, name:\\\"%s\\\"}\"'", userName, appPath[brandID], appName[brandID]); } else #endif { sprintf(cmd, "sudo -u \"%s\" osascript -e 'tell application \"System Events\" to make new login item at end with properties {path:\"%s\", hidden:true, name:\"%s\"}'", userName, appPath[brandID], appName[brandID]); } err = callPosixSpawn(cmd); #if VERBOSE if (err) { fprintf(stderr, "Command: %s\n", cmd); printf("[Make login item for %s returned error %d\n", appPath[brandID], err); } #endif fflush(stderr); cleanupSystemEvents: // Clean up in case this was our last user #if VERBOSE fprintf(stderr, "Telling System Events to quit (at end of SetLoginItemOSAScript)\n"); fflush(stderr); #endif systemEventsPID = FindProcessPID(systemEventsAppName, 0); err2 = noErr; if (systemEventsPID != 0) { err2 = kill(systemEventsPID, SIGKILL); } #if VERBOSE if (err2 != noErr) { fprintf(stderr, "kill(systemEventsPID, SIGKILL) returned error %d \n", (int) err2); fflush(stderr); } #endif // Wait for the process to be gone for (i=0; i<50; ++i) { // 5 seconds max delay SleepSeconds(0.1); // 1/10 second systemEventsPID = FindProcessPID(systemEventsAppName, 0); if (systemEventsPID == 0) break; } #if VERBOSE if (i >= 50) { fprintf(stderr, "Failed to make System Events quit\n"); fflush(stderr); } #endif sleep(4); return (err == noErr); }