bool CBOINCGUIApp::OnInit() { // Initialize globals #ifdef SANDBOX g_use_sandbox = true; #else g_use_sandbox = false; #endif s_bSkipExitConfirmation = false; m_bFilterEvents = false; m_bAboutDialogIsOpen = false; // Initialize class variables m_pLocale = NULL; m_pSkinManager = NULL; m_pFrame = NULL; m_pDocument = NULL; m_pTaskBarIcon = NULL; m_pEventLog = NULL; #ifdef __WXMAC__ m_pMacDockIcon = NULL; #endif m_strBOINCMGRExecutableName = wxEmptyString; m_strBOINCMGRRootDirectory = wxEmptyString; m_strBOINCMGRDataDirectory = wxEmptyString; m_strHostNameArg = wxEmptyString; m_strPasswordArg = wxEmptyString; m_iRPCPortArg = GUI_RPC_PORT; m_strBOINCArguments = wxEmptyString; m_bGUIVisible = true; m_bDebugSkins = false; m_bMultipleInstancesOK = false; m_bBOINCMGRAutoStarted = false; m_iBOINCMGRDisableAutoStart = 0; m_iShutdownCoreClient = 0; m_iDisplayExitDialog = 1; m_iGUISelected = BOINC_SIMPLEGUI; m_bSafeMessageBoxDisplayed = 0; #ifdef __WXMSW__ m_hClientLibraryDll = NULL; #endif // Initialize local variables int iErrorCode = 0; int iSelectedLanguage = 0; bool bOpenEventLog = false; wxString strDesiredSkinName = wxEmptyString; wxString strDialogMessage = wxEmptyString; bool success = false; // Configure wxWidgets platform specific code #ifdef __WXMSW__ wxSystemOptions::SetOption(wxT("msw.staticbox.optimized-paint"), 0); #endif #ifdef __WXMAC__ // In wxMac-2.8.7, default wxListCtrl::RefreshItem() does not work // so use traditional generic implementation. // This has been fixed in wxMac-2.8.8, but the Mac native implementation: // - takes 3 times the CPU time as the Mac generic version. // - seems to always redraw entire control even if asked to refresh only one row. // - causes major flicker of progress bars, (probably due to full redraws.) wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1); AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP((AEEventHandlerProcPtr)QuitAppleEventHandler), 0, false ); // Cache the current process serial number GetCurrentProcess(&m_psnCurrentProcess); #endif // Commandline parsing is done in wxApp::OnInit() if (!wxApp::OnInit()) { return false; } if (g_use_sandbox) { wxCHANGE_UMASK(2); // Set file creation mask to be writable by both user and group // Our umask will be inherited by all our child processes } // Setup application and company information SetAppName(wxT("BOINC Manager")); SetVendorName(wxT("Space Sciences Laboratory, U.C. Berkeley")); // Initialize the configuration storage module m_pConfig = new wxConfig(GetAppName()); wxConfigBase::Set(m_pConfig); wxASSERT(m_pConfig); // Restore Application State m_pConfig->SetPath(wxT("/")); m_pConfig->Read(wxT("AutomaticallyShutdownClient"), &m_iShutdownCoreClient, 0L); m_pConfig->Read(wxT("DisplayShutdownClientDialog"), &m_iDisplayExitDialog, 1L); m_pConfig->Read(wxT("DisableAutoStart"), &m_iBOINCMGRDisableAutoStart, 0L); m_pConfig->Read(wxT("Language"), &iSelectedLanguage, 0L); m_pConfig->Read(wxT("GUISelection"), &m_iGUISelected, BOINC_SIMPLEGUI); m_pConfig->Read(wxT("EventLogOpen"), &bOpenEventLog); // Should we abort the BOINC Manager startup process? if (m_bBOINCMGRAutoStarted && m_iBOINCMGRDisableAutoStart) { return false; } // Detect where BOINC Manager executable name. DetectExecutableName(); // Detect where BOINC Manager was installed too. DetectRootDirectory(); // Detect where the BOINC Data files are. DetectDataDirectory(); // Switch the current directory to the BOINC Data directory if (!GetDataDirectory().IsEmpty()) { success = wxSetWorkingDirectory(GetDataDirectory()); if (!success) { if (!g_use_sandbox) { if (!wxDirExists(GetDataDirectory())) { success = wxMkdir(GetDataDirectory(), 0777); // Does nothing if dir exists } } } } if (!success) iErrorCode = -1016; // Initialize the BOINC Diagnostics Framework int dwDiagnosticsFlags = BOINC_DIAG_DUMPCALLSTACKENABLED | BOINC_DIAG_HEAPCHECKENABLED | BOINC_DIAG_MEMORYLEAKCHECKENABLED | #if defined(__WXMSW__) || defined(__WXMAC__) BOINC_DIAG_REDIRECTSTDERR | BOINC_DIAG_REDIRECTSTDOUT | #endif BOINC_DIAG_TRACETOSTDOUT; diagnostics_init( dwDiagnosticsFlags, "stdoutgui", "stderrgui" ); // Enable Logging and Trace Masks m_pLog = new wxLogBOINC(); wxLog::SetActiveTarget(m_pLog); m_pLog->AddTraceMask(wxT("Function Start/End")); m_pLog->AddTraceMask(wxT("Function Status")); // Initialize the internationalization module #ifdef __WXMSW__ // On Windows, set all locales for this thread on a per-thread basis _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); #endif m_pLocale = new wxLocale(); wxASSERT(m_pLocale); // Look for the localization files by absolute and relative locations. // preference given to the absolute location. m_pLocale->Init(iSelectedLanguage); if (!m_strBOINCMGRRootDirectory.IsEmpty()) { m_pLocale->AddCatalogLookupPathPrefix( wxString(m_strBOINCMGRRootDirectory + wxT("locale")) ); } m_pLocale->AddCatalogLookupPathPrefix(wxT("locale")); m_pLocale->AddCatalog(wxT("BOINC-Manager")); m_pLocale->AddCatalog(wxT("BOINC-Client")); m_pLocale->AddCatalog(wxT("BOINC-Web")); InitSupportedLanguages(); language = m_pLocale->GetCanonicalName(); // Note: JAWS for Windows will only speak the context-sensitive // help if you use this help provider: wxHelpProvider::Set(new wxHelpControllerHelpProvider()); // Enable known image types wxInitAllImageHandlers(); // Initialize the skin manager m_pSkinManager = new CSkinManager(m_bDebugSkins); wxASSERT(m_pSkinManager); // Load desired manager skin m_pConfig->Read(wxT("Skin"), &strDesiredSkinName, m_pSkinManager->GetDefaultSkinName()); m_pSkinManager->ReloadSkin(strDesiredSkinName); #ifdef SANDBOX // Make sure owners, groups and permissions are correct for the current setting of g_use_sandbox char path_to_error[MAXPATHLEN]; path_to_error[0] = '\0'; if (!iErrorCode) { #if (defined(__WXMAC__) && defined(_DEBUG)) // TODO: implement this for other platforms // GDB can't attach to applications which are running as a different user // or group, so fix up data with current user and group during debugging if (check_security(g_use_sandbox, true, NULL, 0)) { CreateBOINCUsersAndGroups(); SetBOINCDataOwnersGroupsAndPermissions(); SetBOINCAppOwnersGroupsAndPermissions(NULL); } #endif iErrorCode = check_security( g_use_sandbox, true, path_to_error, sizeof(path_to_error) ); } if (iErrorCode) { ShowApplication(true); if (iErrorCode == -1099) { strDialogMessage.Printf( _("You currently are not authorized to manage the client.\n\nTo run %s as this user, please:\n - reinstall %s answering \"Yes\" to the question about\n non-administrative users\n or\n - contact your administrator to add you to the 'boinc_master'\n user group."), m_pSkinManager->GetAdvanced()->GetApplicationShortName().c_str(), m_pSkinManager->GetAdvanced()->GetApplicationShortName().c_str() ); } else { strDialogMessage.Printf( _("%s ownership or permissions are not set properly; please reinstall %s.\n(Error code %d"), m_pSkinManager->GetAdvanced()->GetApplicationShortName().c_str(), m_pSkinManager->GetAdvanced()->GetApplicationShortName().c_str(), iErrorCode ); if (path_to_error[0]) { strDialogMessage += _(" at "); strDialogMessage += wxString::FromUTF8(path_to_error); } strDialogMessage += _(")"); fprintf(stderr, "%s\n", (const char*)strDialogMessage.utf8_str()); } wxMessageDialog* pDlg = new wxMessageDialog( NULL, strDialogMessage, m_pSkinManager->GetAdvanced()->GetApplicationName(), wxOK ); pDlg->ShowModal(); if (pDlg) pDlg->Destroy(); return false; } #endif // SANDBOX #ifdef __WXMSW__ // Perform any last minute checks that should keep the manager // from starting up. wxString strRebootPendingFile = GetRootDirectory() + wxFileName::GetPathSeparator() + wxT("RebootPending.txt"); if (wxFile::Exists(strRebootPendingFile)) { wxMessageDialog dialog( NULL, _("A reboot is required in order for BOINC to run properly.\nPlease reboot your computer and try again."), _("BOINC Manager"), wxOK|wxICON_ERROR ); dialog.ShowModal(); return false; } #endif // Detect if BOINC Manager is already running, if so, bring it into the // foreground and then exit. if (!m_bMultipleInstancesOK) { if (DetectDuplicateInstance()) { return false; } } // Initialize the main document m_pDocument = new CMainDocument(); wxASSERT(m_pDocument); m_pDocument->OnInit(); // Is there a condition in which the Simple GUI should not be used? if (BOINC_SIMPLEGUI == m_iGUISelected) { // Screen too small? if (wxGetDisplaySize().GetHeight() < 600) { m_iGUISelected = BOINC_ADVANCEDGUI; } } // Initialize the task bar icon m_pTaskBarIcon = new CTaskBarIcon( m_pSkinManager->GetAdvanced()->GetApplicationName(), m_pSkinManager->GetAdvanced()->GetApplicationIcon(), m_pSkinManager->GetAdvanced()->GetApplicationDisconnectedIcon(), m_pSkinManager->GetAdvanced()->GetApplicationSnoozeIcon() #ifdef __WXMAC__ , wxTBI_CUSTOM_STATUSITEM #endif ); #ifdef __WXMAC__ m_pMacDockIcon = new CTaskBarIcon( m_pSkinManager->GetAdvanced()->GetApplicationName(), m_pSkinManager->GetAdvanced()->GetApplicationIcon(), m_pSkinManager->GetAdvanced()->GetApplicationDisconnectedIcon(), m_pSkinManager->GetAdvanced()->GetApplicationSnoozeIcon() , wxTBI_DOCK ); wxASSERT(m_pMacDockIcon); #endif wxASSERT(m_pTaskBarIcon); // Startup the System Idle Detection code IdleTrackerAttach(); #ifdef __WXMAC__ ProcessSerialNumber psn; ProcessInfoRec pInfo; OSStatus err; memset(&pInfo, 0, sizeof(pInfo)); pInfo.processInfoLength = sizeof( ProcessInfoRec ); err = GetProcessInformation(&m_psnCurrentProcess, &pInfo); if (!err) { psn = pInfo.processLauncher; memset(&pInfo, 0, sizeof(pInfo)); pInfo.processInfoLength = sizeof( ProcessInfoRec ); err = GetProcessInformation(&psn, &pInfo); } // Don't open main window if we were started automatically at login if (pInfo.processSignature == 'lgnw') { // Login Window app m_bGUIVisible = false; // If the system was just started, we usually get a "Connection // failed" error if we try to connect too soon, so delay a bit. sleep(10); } #endif // Show the UI SetActiveGUI(m_iGUISelected, m_bGUIVisible); if (!m_bGUIVisible) { #ifdef __WXMAC__ // We don't call Hide() or Show(false) for the main frame // under wxCocoa 2.9.5 because it bounces the Dock icon // (as in notification.) We work around this by moving // the main window/frame off screen here. // The position will be restored in one of these methods: // CBOINCGUIApp::OnActivateApp(), CSimpleFrame::SaveState() // or CAdvancedFrame::SaveWindowDimensions(). if (m_pFrame) { m_pFrame->MoveFrameOffScreen(); m_pFrame->Show(); } #endif ShowApplication(false); } if (bOpenEventLog) { DisplayEventLog(m_bGUIVisible); if (m_bGUIVisible && m_pFrame) { m_pFrame->Raise(); } } return true; }
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; }