示例#1
0
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;
}
示例#2
0
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;
}