// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2020 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 . // Screensaver coordinator. // Alternates between a "default screensaver" // and application graphics for running jobs. // Periods are configurable via config file "ss_config.xml". // See http://boinc.berkeley.edu/trac/wiki/ScreensaverEnhancements #ifdef _WIN32 #include "boinc_win.h" #endif #ifdef __APPLE__ #define VERBOSE 0 #include #include #include #include #include #include extern pthread_mutex_t saver_mutex; #endif // Common application includes // #include "diagnostics.h" #include "common_defs.h" #include "util.h" #include "common_defs.h" #include "filesys.h" #include "error_numbers.h" #include "gui_rpc_client.h" #include "str_util.h" #include "str_replace.h" #include "screensaver.h" #ifdef __APPLE__ #undef BOINCTRACE #if VERBOSE #define BOINCTRACE print_to_log_file #else #define BOINCTRACE(...) #endif #define _T #endif // Platform specific application includes // #if defined(_WIN32) #include "screensaver_win.h" typedef HANDLE GFXAPP_ID; #define DataMgmtProcType DWORD WINAPI #elif defined(__APPLE__) #include "Mac_Saver_Module.h" #include "shmem.h" typedef int GFXAPP_ID; #define DataMgmtProcType void* #endif #ifdef _WIN32 // Allow for Unicode wide characters #define PATH_SEPARATOR (_T("\\")) #define THE_DEFAULT_SS_EXECUTABLE (_T(DEFAULT_SS_EXECUTABLE)) #define THE_SS_CONFIG_FILE (_T(SS_CONFIG_FILE)) #define DEFAULT_GFX_CANT_CONNECT ERR_CONNECT #else // Using (_T()) here causes compiler errors on Mac #define PATH_SEPARATOR "/" #define THE_DEFAULT_SS_EXECUTABLE DEFAULT_SS_EXECUTABLE #define THE_SS_CONFIG_FILE SS_CONFIG_FILE #define DEFAULT_GFX_CANT_CONNECT (ERR_CONNECT & 0xff) #endif // Flags for testing & debugging #define SIMULATE_NO_GRAPHICS 0 RESULT* graphics_app_result_ptr = NULL; #ifdef __APPLE__ pid_t* pid_from_shmem = NULL; #endif bool CScreensaver::is_same_task(RESULT* taska, RESULT* taskb) { if ((taska == NULL) || (taskb == NULL)) return false; if (strcmp(taska->name, taskb->name)) return false; if (strcmp(taska->project_url, taskb->project_url)) return false; return true; } int CScreensaver::count_active_graphic_apps(RESULTS& res, RESULT* exclude) { int i = 0; unsigned int graphics_app_count = 0; // Count the number of active graphics-capable apps excluding the specified result. // If exclude is NULL, don't exclude any results. // for (i = res.results.size()-1; i >=0 ; i--) { BOINCTRACE(_T("count_active_graphic_apps -- active task detected\n")); BOINCTRACE( _T("count_active_graphic_apps -- name = '%s', path = '%s'\n"), res.results[i]->name, res.results[i]->graphics_exec_path ); if (!strlen(res.results[i]->graphics_exec_path)) continue; if (is_same_task(res.results[i], exclude)) continue; #ifdef __APPLE__ // Remove it from the vector if incompatible with current version of OS X if (isIncompatible(res.results[i]->graphics_exec_path)) { BOINCTRACE( _T("count_active_graphic_apps -- removing incompatible name = '%s', path = '%s'\n"), res.results[i]->name, res.results[i]->graphics_exec_path ); RESULT *rp = res.results[i]; res.results.erase(res.results.begin()+i); delete rp; continue; } #endif BOINCTRACE(_T("count_active_graphic_apps -- active task detected w/graphics\n")); graphics_app_count++; } return graphics_app_count; } // Choose a random graphics application out of the vector. // Exclude the specified result unless it is the only candidate. // If exclude is NULL or an empty string, don't exclude any results. // RESULT* CScreensaver::get_random_graphics_app( RESULTS& res, RESULT* exclude ) { RESULT* rp = NULL; unsigned int i = 0; unsigned int graphics_app_count = 0; unsigned int random_selection = 0; unsigned int current_counter = 0; RESULT *avoid = exclude; BOINCTRACE(_T("get_random_graphics_app -- Function Start\n")); graphics_app_count = count_active_graphic_apps(res, avoid); BOINCTRACE(_T("get_random_graphics_app -- graphics_app_count = '%d'\n"), graphics_app_count); // If no graphics app found other than the one excluded, count again without excluding any if ((0 == graphics_app_count) && (avoid != NULL)) { avoid = NULL; graphics_app_count = count_active_graphic_apps(res, avoid); } // If no graphics app was found, return NULL if (0 == graphics_app_count) { goto CLEANUP; } // Choose which application to display. // random_selection = (rand() % graphics_app_count) + 1; BOINCTRACE(_T("get_random_graphics_app -- random_selection = '%d'\n"), random_selection); // find the chosen graphics application. // for (i = 0; i < res.results.size(); i++) { if (!strlen(res.results[i]->graphics_exec_path)) continue; if (is_same_task(res.results[i], avoid)) continue; current_counter++; if (current_counter == random_selection) { rp = res.results[i]; break; } } CLEANUP: BOINCTRACE(_T("get_random_graphics_app -- Function End\n")); return rp; } #ifdef __APPLE__ void CScreensaver::markAsIncompatible(char *gfxAppPath) { char *buf = (char *)malloc(strlen(gfxAppPath)+1); if (buf) { strlcpy(buf, gfxAppPath, malloc_size(buf)); m_vIncompatibleGfxApps.push_back(buf); BOINCTRACE(_T("markAsIncompatible -- path = '%s'\n"), gfxAppPath); } } bool CScreensaver::isIncompatible(char *appPath) { unsigned int i = 0; for (i = 0; i < m_vIncompatibleGfxApps.size(); i++) { BOINCTRACE( _T("isIncompatible -- comparing incompatible path '%s' to candidate path %s\n"), m_vIncompatibleGfxApps[i], appPath ); if (strcmp(m_vIncompatibleGfxApps[i], appPath) == 0) { return true; } } return false; } #endif // Launch a project (science) graphics application // int CScreensaver::launch_screensaver(RESULT* rp, GFXAPP_ID& graphics_application) { int retval = 0; if (strlen(rp->graphics_exec_path)) { // V6 Graphics #ifdef __APPLE__ if (gIsCatalina) { // As of OS 10.15 (Catalina) screensavers can no longer: // - launch apps that run setuid or setgid // - launch apps downloaded from the Internet which have not been // specifically approved by the user via Gatekeeper. // So instead of launching graphics apps via gfx_switcher, we send an // RPC to the client asking the client to launch them via gfx_switcher. // See comments in gfx_switcher.cpp for a more detailed explanation. // We have tested this on OS 10.13 High Sierra and it works there, too // retval = rpc->run_graphics_app("runfullscreen", rp->slot, gUserName); for (int i=0; i<800; i++) { boinc_sleep(0.01); // Wait 8 seconds max if (*pid_from_shmem != 0) { graphics_application = *pid_from_shmem; break; } } // fprintf(stderr, "launch_screensaver got pid %d\n", graphics_application); // Inform our helper app what we launched fprintf(m_gfx_Cleanup_IPC, "%d\n", graphics_application); fflush(m_gfx_Cleanup_IPC); } else { // For sandbox security, use gfx_switcher to launch gfx app // as user boinc_project and group boinc_project. // // For unknown reasons, the graphics application exits with // "RegisterProcess failed (error = -50)" unless we pass its // full path twice in the argument list to execv. char* argv[5]; argv[0] = "gfx_Switcher"; argv[1] = "-launch_gfx"; argv[2] = strrchr(rp->slot_path, '/'); if (*argv[2]) argv[2]++; // Point to the slot number in ascii argv[3] = "--fullscreen"; argv[4] = 0; retval = run_program( rp->slot_path, m_gfx_Switcher_Path, 4, argv, 0, graphics_application ); } if (graphics_application) { launchedGfxApp(rp->graphics_exec_path, graphics_application, rp->slot); } #else char* argv[3]; argv[0] = rp->graphics_exec_path; argv[1] = "--fullscreen"; argv[2] = 0; retval = run_program( rp->slot_path, rp->graphics_exec_path, 2, argv, 0, graphics_application ); #endif } return retval; } // Terminate any screensaver graphics application // int CScreensaver::terminate_v6_screensaver(GFXAPP_ID& graphics_application, RESULT* rp) { int retval = 0; int i; #ifdef __APPLE__ pid_t thePID; if (gIsCatalina) { // As of OS 10.15 (Catalina) screensavers can no longer launch apps // that run setuid or setgid. So instead of killing graphics apps // via gfx_switcher, we send an RPC to the client asking the client // to kill them via switcher. // We have tested this on OS 10.13 High Sierra and it works there, too // int ignore; if (graphics_application == 0) return 0; // MUTEX may help prevent crashes when terminating an older gfx app which // we were displaying using CGWindowListCreateImage under OS X >= 10.13 // Also prevents reentry when called from our other thread pthread_mutex_lock(&saver_mutex); thePID = graphics_application; // fprintf(stderr, "stopping pid %d\n", thePID); retval = rpc->run_graphics_app("stop", thePID, gUserName); //kill_program(graphics_application); // Inform our helper app that we have stopped current graphics app fprintf(m_gfx_Cleanup_IPC, "0\n"); fflush(m_gfx_Cleanup_IPC); launchedGfxApp("", 0, -1); for (i=0; i<200; i++) { boinc_sleep(0.01); // Wait 2 seconds max if (HasProcessExited(graphics_application, ignore)) { break; } } pthread_mutex_unlock(&saver_mutex); } else { // Under sandbox security, use gfx_switcher to kill default gfx app // as user boinc_master and group boinc_master (for default gfx app) // or user boinc_project and group boinc_project (for project gfx // apps.) The man page for kill() says the user ID of the process // sending the signal must match that of the target process, though // in practice that seems not to be true on the Mac. char current_dir[PATH_MAX]; char gfx_pid[16]; if (graphics_application == 0) return 0; // MUTEX may help prevent crashes when terminating an older gfx app which // we were displaying using CGWindowListCreateImage under OS X >= 10.13 // Also prevents reentry when called from our other thread pthread_mutex_lock(&saver_mutex); sprintf(gfx_pid, "%d", graphics_application); getcwd( current_dir, sizeof(current_dir)); char* argv[4]; argv[0] = "gfx_switcher"; argv[1] = "-kill_gfx"; argv[2] = gfx_pid; argv[3] = 0; retval = run_program( current_dir, m_gfx_Switcher_Path, 3, argv, 0, thePID ); if (graphics_application) { launchedGfxApp("", 0, -1); } for (i=0; i<200; i++) { boinc_sleep(0.01); // Wait 2 seconds max // Prevent gfx_switcher from becoming a zombie if (waitpid(thePID, 0, WNOHANG) == thePID) { break; } } pthread_mutex_unlock(&saver_mutex); } #endif #ifdef _WIN32 HWND hBOINCGraphicsWindow = FindWindow(BOINC_WINDOW_CLASS_NAME, NULL); if (hBOINCGraphicsWindow) { CloseWindow(hBOINCGraphicsWindow); Sleep(1000); hBOINCGraphicsWindow = FindWindow(BOINC_WINDOW_CLASS_NAME, NULL); if (hBOINCGraphicsWindow) { kill_program(graphics_application); } } #endif // For safety, call kill_program even under Apple sandbox security kill_program(graphics_application); return retval; } // Terminate the project (science) graphics application // int CScreensaver::terminate_screensaver(GFXAPP_ID& graphics_application, RESULT* rp) { int retval = 0; if (graphics_application) { // V6 Graphics if (m_bScience_gfx_running) { terminate_v6_screensaver(graphics_application, rp); } } return retval; } // Launch the default graphics application // int CScreensaver::launch_default_screensaver(char *dir_path, GFXAPP_ID& graphics_application) { int retval = 0; int num_args; #ifdef __APPLE__ if (gIsCatalina) { // As of OS 10.15 (Catalina) screensavers can no longer: // - launch apps that run setuid or setgid // - launch apps downloaded from the Internet which have not been // specifically approved by the user via Gatekeeper. // So instead of launching graphics apps via gfx_switcher, we send an // RPC to the client asking the client to launch them via gfx_switcher. // See comments in gfx_switcher.cpp for a more detailed explanation. // We have tested this on OS 10.13 High Sierra and it works there, too // int thePID = -1; retval = rpc->run_graphics_app("runfullscreen", thePID, gUserName); for (int i=0; i<800; i++) { boinc_sleep(0.01); // Wait 8 seconds max if (*pid_from_shmem != 0) { graphics_application = *pid_from_shmem; break; } } // fprintf(stderr, "launch_screensaver got pid %d\n", graphics_application); // Inform our helper app what we launched fprintf(m_gfx_Cleanup_IPC, "%d\n", graphics_application); fflush(m_gfx_Cleanup_IPC); } else { // For sandbox security, use gfx_switcher to launch default // gfx app as user boinc_master and group boinc_master. char* argv[6]; argv[0] = "gfx_switcher"; argv[1] = "-default_gfx"; argv[2] = THE_DEFAULT_SS_EXECUTABLE; // Will be changed by gfx_switcher argv[3] = "--fullscreen"; argv[4] = 0; argv[5] = 0; if (!m_bConnected) { BOINCTRACE(_T("launch_default_screensaver using --retry_connect argument\n")); argv[4] = "--retry_connect"; num_args = 5; } else { num_args = 4; } retval = run_program( dir_path, m_gfx_Switcher_Path, num_args, argv, 0, graphics_application ); } if (graphics_application) { launchedGfxApp("boincscr", graphics_application, -1); } BOINCTRACE(_T("launch_default_screensaver returned %d\n"), retval); #else char* argv[4]; char full_path[1024]; strlcpy(full_path, dir_path, sizeof(full_path)); strlcat(full_path, PATH_SEPARATOR, sizeof(full_path)); strlcat(full_path, THE_DEFAULT_SS_EXECUTABLE, sizeof(full_path)); argv[0] = full_path; argv[1] = "--fullscreen"; argv[2] = 0; argv[3] = 0; if (!m_bConnected) { BOINCTRACE(_T("launch_default_screensaver using --retry_connect argument\n")); argv[2] = "--retry_connect"; num_args = 3; } else { num_args = 2; } retval = run_program( dir_path, full_path, num_args, argv, 0, graphics_application ); BOINCTRACE(_T("launch_default_screensaver %s returned %d\n"), full_path, retval); #endif return retval; } // Terminate the default graphics application // int CScreensaver::terminate_default_screensaver(GFXAPP_ID& graphics_application) { int retval = 0; if (! graphics_application) return 0; retval = terminate_v6_screensaver(graphics_application, NULL); return retval; } // If we cannot connect to the core client: // - we retry connecting every 10 seconds // - we launch the default graphics application with the argument --retry_connect, so // it will continue running and will also retry connecting every 10 seconds. // // If we successfully connected to the core client, launch the default graphics application // without the argument --retry_connect. If it can't connect, it will return immediately // with the exit code ERR_CONNECT. In that case, we assume it was blocked by a firewall // and so we run only project (science) graphics. DataMgmtProcType CScreensaver::DataManagementProc() { int retval = 0; int suspend_reason = 0; RESULT* theResult = NULL; RESULT previous_result; // previous_result_ptr = &previous_result when previous_result is valid, else NULL RESULT* previous_result_ptr = NULL; int iResultCount = 0; int iIndex = 0; double default_phase_start_time = 0.0; double science_phase_start_time = 0.0; double last_change_time = 0.0; // If we run default screensaver during science phase because no science graphics // are available, then shorten next default graphics phase by that much time. double default_saver_start_time_in_science_phase = 0.0; double default_saver_duration_in_science_phase = 0.0; SS_PHASE ss_phase = DEFAULT_SS_PHASE; bool switch_to_default_gfx = false; bool killing_default_gfx = false; int exit_status = 0; char* default_ss_dir_path = NULL; char full_path[1024]; BOINCTRACE(_T("CScreensaver::DataManagementProc - Display screen saver loading message\n")); SetError(TRUE, SCRAPPERR_BOINCSCREENSAVERLOADING); // No GFX App is running: show moving BOINC logo #ifdef _WIN32 m_tThreadCreateTime = time(0); // Set the starting point for iterating through the results m_iLastResultShown = 0; m_tLastResultChangeTime = 0; #endif m_bDefault_ss_exists = false; m_bScience_gfx_running = false; m_bDefault_gfx_running = false; m_bShow_default_ss_first = false; graphics_app_result_ptr = NULL; #ifdef __APPLE__ m_vIncompatibleGfxApps.clear(); default_ss_dir_path = "/Library/Application Support/BOINC Data"; if (gIsCatalina) { char shmem_name[MAXPATHLEN]; snprintf(shmem_name, sizeof(shmem_name), "/tmp/boinc_ss_%s", gUserName); retval = create_shmem_mmap(shmem_name, sizeof(int), (void**)&pid_from_shmem); // make sure user/group RW permissions are set, but not other. // if (retval == 0) { chmod(shmem_name, 0666); retval = attach_shmem_mmap(shmem_name, (void**)&pid_from_shmem); } if (retval == 0) { *pid_from_shmem = 0; } } #else default_ss_dir_path = (char*)m_strBOINCInstallDirectory.c_str(); #endif strlcpy(full_path, default_ss_dir_path, sizeof(full_path)); strlcat(full_path, PATH_SEPARATOR, sizeof(full_path)); strlcat(full_path, THE_DEFAULT_SS_EXECUTABLE, sizeof(full_path)); if (boinc_file_exists(full_path)) { m_bDefault_ss_exists = true; } else { SetError(TRUE, SCRAPPERR_CANTLAUNCHDEFAULTGFXAPP); // No GFX App is running: show moving BOINC logo } if (m_bDefault_ss_exists && m_bShow_default_ss_first) { ss_phase = DEFAULT_SS_PHASE; default_phase_start_time = dtime(); science_phase_start_time = 0; switch_to_default_gfx = true; } else { ss_phase = SCIENCE_SS_PHASE; default_phase_start_time = 0; science_phase_start_time = dtime(); } while (true) { for (int i = 0; i < 4; i++) { // *** // *** Things that should be run frequently. // *** 4 times per second. // *** // Are we supposed to exit the screensaver? if (m_bQuitDataManagementProc) { // If main thread has requested we exit BOINCTRACE(_T("CScreensaver::DataManagementProc - Thread told to stop\n")); if (m_hGraphicsApplication || graphics_app_result_ptr) { if (m_bDefault_gfx_running) { BOINCTRACE(_T("CScreensaver::DataManagementProc - Terminating default screensaver\n")); terminate_default_screensaver(m_hGraphicsApplication); } else { BOINCTRACE(_T("CScreensaver::DataManagementProc - Terminating screensaver\n")); terminate_screensaver(m_hGraphicsApplication, graphics_app_result_ptr); } graphics_app_result_ptr = NULL; previous_result_ptr = NULL; m_hGraphicsApplication = 0; } BOINCTRACE(_T("CScreensaver::DataManagementProc - Stopping...\n")); m_bDataManagementProcStopped = true; // Tell main thread that we exited return 0; // Exit the thread } boinc_sleep(0.25); } // *** // *** Things that should be run less frequently. // *** 1 time per second. // *** // Blank screen saver? if ((m_dwBlankScreen) && (time(0) > m_dwBlankTime) && (m_dwBlankTime > 0)) { BOINCTRACE(_T("CScreensaver::DataManagementProc - Time to blank\n")); SetError(FALSE, SCRAPPERR_SCREENSAVERBLANKED); // Blanked - hide moving BOINC logo m_bQuitDataManagementProc = true; continue; // Code above will exit the thread } BOINCTRACE(_T("CScreensaver::DataManagementProc - ErrorMode = '%d', ErrorCode = '%x'\n"), m_bErrorMode, m_hrError); if (!m_bConnected) { HandleRPCError(); } if (m_bConnected) { // Do we need to get the core client state? if (m_bResetCoreState) { // Try and get the current state of the CC retval = rpc->get_state(state); if (retval) { // CC may not yet be running HandleRPCError(); continue; } else { m_bResetCoreState = false; } } // Update our task list retval = rpc->get_screensaver_tasks(suspend_reason, results); if (retval) { // rpc call returned error HandleRPCError(); m_bResetCoreState = true; continue; } } else { results.clear(); } // Time to switch to default graphics phase? if (m_bDefault_ss_exists && (ss_phase == SCIENCE_SS_PHASE) && (m_fGFXDefaultPeriod > 0)) { if (science_phase_start_time && ((dtime() - science_phase_start_time) > m_fGFXSciencePeriod)) { if (!m_bDefault_gfx_running) { switch_to_default_gfx = true; } ss_phase = DEFAULT_SS_PHASE; default_phase_start_time = dtime(); science_phase_start_time = 0; if (m_bDefault_gfx_running && default_saver_start_time_in_science_phase) { // Remember how long default graphics ran during science phase default_saver_duration_in_science_phase += (dtime() - default_saver_start_time_in_science_phase); } default_saver_start_time_in_science_phase = 0; } } // Time to switch to science graphics phase? if ((ss_phase == DEFAULT_SS_PHASE) && m_bConnected && (m_fGFXSciencePeriod > 0)) { if (default_phase_start_time && ((dtime() - default_phase_start_time + default_saver_duration_in_science_phase) > m_fGFXDefaultPeriod)) { // BOINCTRACE(_T("CScreensaver::Ending Default phase: now=%f, default_phase_start_time=%f, default_saver_duration_in_science_phase=%f\n"), // dtime(), default_phase_start_time, default_saver_duration_in_science_phase); ss_phase = SCIENCE_SS_PHASE; default_phase_start_time = 0; default_saver_duration_in_science_phase = 0; science_phase_start_time = dtime(); if (m_bDefault_gfx_running) { default_saver_start_time_in_science_phase = science_phase_start_time; } switch_to_default_gfx = false; } } // Core client suspended? // We ignore SUSPEND_REASON_CPU_USAGE in SS coordinator, so it won't kill graphics apps for // short-term CPU usage spikes (such as anti-virus.) Added 9 April 2010 if (suspend_reason && !(suspend_reason & (SUSPEND_REASON_CPU_THROTTLE | SUSPEND_REASON_CPU_USAGE))) { if (!m_bDefault_gfx_running) { SetError(TRUE, m_hrError); // No GFX App is running: show moving BOINC logo if (m_bDefault_ss_exists) { switch_to_default_gfx = true; } } } if (switch_to_default_gfx) { if (m_bScience_gfx_running) { if (m_hGraphicsApplication || previous_result_ptr) { // use previous_result_ptr because graphics_app_result_ptr may no longer be valid terminate_screensaver(m_hGraphicsApplication, previous_result_ptr); if (m_hGraphicsApplication == 0) { graphics_app_result_ptr = NULL; m_bScience_gfx_running = false; } else { // HasProcessExited() test will clear m_hGraphicsApplication and graphics_app_result_ptr } previous_result_ptr = NULL; } } else { if (!m_bDefault_gfx_running) { switch_to_default_gfx = false; retval = launch_default_screensaver(default_ss_dir_path, m_hGraphicsApplication); if (retval) { m_hGraphicsApplication = 0; previous_result_ptr = NULL; graphics_app_result_ptr = NULL; m_bDefault_gfx_running = false; SetError(TRUE, SCRAPPERR_CANTLAUNCHDEFAULTGFXAPP); // No GFX App is running: show moving BOINC logo } else { m_bDefault_gfx_running = true; if (ss_phase == SCIENCE_SS_PHASE) { default_saver_start_time_in_science_phase = dtime(); } SetError(FALSE, SCRAPPERR_BOINCSCREENSAVERLOADING); // A GFX App is running: hide moving BOINC logo } } } } if ((ss_phase == SCIENCE_SS_PHASE) && !switch_to_default_gfx) { #if SIMULATE_NO_GRAPHICS /* FOR TESTING */ if (!m_bDefault_gfx_running) { SetError(TRUE, m_hrError); // No GFX App is running: show moving BOINC logo if (m_bDefault_ss_exists) { switch_to_default_gfx = true; } } #else /* NORMAL OPERATION */ if (m_bScience_gfx_running) { // Is the current graphics app's associated task still running? if ((m_hGraphicsApplication) || (graphics_app_result_ptr)) { iResultCount = (int)results.results.size(); graphics_app_result_ptr = NULL; // Find the current task in the new results vector (if it still exists) for (iIndex = 0; iIndex < iResultCount; iIndex++) { theResult = results.results.at(iIndex); if (is_same_task(theResult, previous_result_ptr)) { graphics_app_result_ptr = theResult; previous_result = *theResult; previous_result_ptr = &previous_result; break; } } // V6 graphics only: if worker application has stopped running, terminate_screensaver if ((graphics_app_result_ptr == NULL) && (m_hGraphicsApplication != 0)) { if (previous_result_ptr) { BOINCTRACE(_T("CScreensaver::DataManagementProc - %s finished\n"), previous_result.graphics_exec_path ); } terminate_screensaver(m_hGraphicsApplication, previous_result_ptr); previous_result_ptr = NULL; if (m_hGraphicsApplication == 0) { graphics_app_result_ptr = NULL; m_bScience_gfx_running = false; // Save previous_result and previous_result_ptr for get_random_graphics_app() call } else { // HasProcessExited() test will clear m_hGraphicsApplication and graphics_app_result_ptr } } if (last_change_time && (m_fGFXChangePeriod > 0) && ((dtime() - last_change_time) > m_fGFXChangePeriod) ) { if (count_active_graphic_apps(results, previous_result_ptr) > 0) { if (previous_result_ptr) { BOINCTRACE(_T("CScreensaver::DataManagementProc - time to change: %s / %s\n"), previous_result.name, previous_result.graphics_exec_path ); } terminate_screensaver(m_hGraphicsApplication, graphics_app_result_ptr); if (m_hGraphicsApplication == 0) { graphics_app_result_ptr = NULL; m_bScience_gfx_running = false; // Save previous_result and previous_result_ptr for get_random_graphics_app() call } else { // HasProcessExited() test will clear m_hGraphicsApplication and graphics_app_result_ptr } } last_change_time = dtime(); } } } // End if (m_bScience_gfx_running) // If no current graphics app, pick an active task at random // and launch its graphics app // if ((m_bDefault_gfx_running || (m_hGraphicsApplication == 0)) && (graphics_app_result_ptr == NULL)) { if (suspend_reason && !(suspend_reason & (SUSPEND_REASON_CPU_THROTTLE | SUSPEND_REASON_CPU_USAGE))) { graphics_app_result_ptr = NULL; } else { graphics_app_result_ptr = get_random_graphics_app(results, previous_result_ptr); previous_result_ptr = NULL; } if (graphics_app_result_ptr) { if (m_bDefault_gfx_running) { terminate_default_screensaver(m_hGraphicsApplication); killing_default_gfx = true; // Remember how long default graphics ran during science phase if (default_saver_start_time_in_science_phase) { default_saver_duration_in_science_phase += (dtime() - default_saver_start_time_in_science_phase); //BOINCTRACE(_T("CScreensaver::During Science phase: now=%f, default_saver_start_time=%f, default_saver_duration=%f\n"), // dtime(), default_saver_start_time_in_science_phase, default_saver_duration_in_science_phase); } default_saver_start_time_in_science_phase = 0; // HasProcessExited() test will clear // m_hGraphicsApplication and graphics_app_result_ptr } else { retval = launch_screensaver(graphics_app_result_ptr, m_hGraphicsApplication); if (retval) { m_hGraphicsApplication = 0; previous_result_ptr = NULL; graphics_app_result_ptr = NULL; m_bScience_gfx_running = false; } else { // A GFX App is running: hide moving BOINC logo // SetError(FALSE, SCRAPPERR_BOINCSCREENSAVERLOADING); last_change_time = dtime(); m_bScience_gfx_running = true; // Make a local copy of current result, since original pointer // may have been freed by the time we perform later tests previous_result = *graphics_app_result_ptr; previous_result_ptr = &previous_result; if (previous_result_ptr) { BOINCTRACE(_T("CScreensaver::DataManagementProc - launching %s\n"), previous_result.graphics_exec_path ); } } } } else { if (!m_bDefault_gfx_running) { // We can't run a science graphics app, so run the default graphics if available SetError(TRUE, m_hrError); if (m_bDefault_ss_exists) { switch_to_default_gfx = true; } } } // End if no science graphics available } // End if no current science graphics app is running #endif // ! SIMULATE_NO_GRAPHICS if (switch_to_default_gfx) { switch_to_default_gfx = false; if (!m_bDefault_gfx_running) { retval = launch_default_screensaver(default_ss_dir_path, m_hGraphicsApplication); if (retval) { m_hGraphicsApplication = 0; previous_result_ptr = NULL; graphics_app_result_ptr = NULL; m_bDefault_gfx_running = false; SetError(TRUE, SCRAPPERR_CANTLAUNCHDEFAULTGFXAPP); // No GFX App is running: show BOINC logo } else { m_bDefault_gfx_running = true; default_saver_start_time_in_science_phase = dtime(); SetError(FALSE, SCRAPPERR_BOINCSCREENSAVERLOADING); // Default GFX App is running: hide moving BOINC logo } } } } // End if ((ss_phase == SCIENCE_SS_PHASE) && !switch_to_default_gfx) // Is the graphics app still running? if (m_hGraphicsApplication) { if (HasProcessExited(m_hGraphicsApplication, exit_status)) { // Something has happened to the previously selected screensaver // application. Start a different one. BOINCTRACE(_T("CScreensaver::DataManagementProc - Graphics application isn't running, start a new one.\n")); if (m_bDefault_gfx_running) { // If we were able to connect to core client // but gfx app can't, don't use it. // BOINCTRACE(_T("CScreensaver::DataManagementProc - Default graphics application exited with code %d.\n"), exit_status); if (!killing_default_gfx) { // If this is an unexpected exit if (exit_status == DEFAULT_GFX_CANT_CONNECT) { SetError(TRUE, SCRAPPERR_DEFAULTGFXAPPCANTCONNECT); // No GFX App is running: show moving BOINC logo } else { SetError(TRUE, SCRAPPERR_DEFAULTGFXAPPCRASHED); // No GFX App is running: show moving BOINC logo } m_bDefault_ss_exists = false; ss_phase = SCIENCE_SS_PHASE; } killing_default_gfx = false; } SetError(TRUE, SCRAPPERR_BOINCNOGRAPHICSAPPSEXECUTING); // No GFX App is running: show moving BOINC logo m_hGraphicsApplication = 0; graphics_app_result_ptr = NULL; m_bDefault_gfx_running = false; m_bScience_gfx_running = false; #ifdef __APPLE__ launchedGfxApp("", 0, -1); #endif continue; } } } // end while(true) } #ifdef _WIN32 BOOL CScreensaver::HasProcessExited(HANDLE pid_handle, int &exitCode) { unsigned long status = 1; if (GetExitCodeProcess(pid_handle, &status)) { if (status == STILL_ACTIVE) { exitCode = 0; return false; } } exitCode = (int)status; return true; } #else bool CScreensaver::HasProcessExited(pid_t pid, int &exitCode) { int status; pid_t p; if (gIsCatalina) { // Only the process which launched an app can use waitpid() to test // whether that app is still running. If we sent an RPC to the client // asking the client to launch a graphics app via switcher, we must // send another RPC to the client to call waitpid() for that app. // if (pid_from_shmem) { //fprintf(stderr, "screensaver HasProcessExited got pid_from_shmem = %d\n", *pid_from_shmem); if (*pid_from_shmem != 0) return false; } return true; } p = waitpid(pid, &status, WNOHANG); exitCode = WEXITSTATUS(status); if (p == pid) return true; // process has exited if (p == -1) return true; // PID doesn't exist exitCode = 0; return false; } #endif void CScreensaver::GetDefaultDisplayPeriods(struct ss_periods &periods) { char* default_data_dir_path = NULL; char buf[1024]; FILE* f; MIOFILE mf; periods.GFXDefaultPeriod = GFX_DEFAULT_PERIOD; periods.GFXSciencePeriod = GFX_SCIENCE_PERIOD; periods.GFXChangePeriod = GFX_CHANGE_PERIOD; periods.Show_default_ss_first = false; #ifdef __APPLE__ default_data_dir_path = "/Library/Application Support/BOINC Data"; #else default_data_dir_path = (char*)m_strBOINCDataDirectory.c_str(); #endif strlcpy(buf, default_data_dir_path, sizeof(buf)); strlcat(buf, PATH_SEPARATOR, sizeof(buf)); strlcat(buf, THE_SS_CONFIG_FILE, sizeof(buf)); f = boinc_fopen(buf, "r"); if (!f) return; mf.init_file(f); XML_PARSER xp(&mf); while (!xp.get_tag()) { if (xp.parse_bool("default_ss_first", periods.Show_default_ss_first)) continue; if (xp.parse_double("default_gfx_duration", periods.GFXDefaultPeriod)) continue; if (xp.parse_double("science_gfx_duration", periods.GFXSciencePeriod)) continue; if (xp.parse_double("science_gfx_change_interval", periods.GFXChangePeriod)) continue; } fclose(f); BOINCTRACE( _T("CScreensaver::GetDefaultDisplayPeriods: m_bShow_default_ss_first=%d, m_fGFXDefaultPeriod=%f, m_fGFXSciencePeriod=%f, m_fGFXChangePeriod=%f\n"), (int)periods.Show_default_ss_first, periods.GFXDefaultPeriod, periods.GFXSciencePeriod, periods.GFXChangePeriod ); }