// Berkeley Open Infrastructure for Network Computing // http://boinc.berkeley.edu // Copyright (C) 2005 University of California // // This 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 2.1 of the License, or (at your option) any later version. // // This software 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. // // To view the GNU Lesser General Public License visit // http://www.gnu.org/copyleft/lesser.html // or write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #if defined(__GNUG__) && !defined(__APPLE__) #pragma implementation "BOINCClientManager.h" #endif #include "stdwx.h" #include "diagnostics.h" #include "LogBOINC.h" #include "BOINCGUIApp.h" #include "MainDocument.h" #include "BOINCBaseFrame.h" #include "AdvancedFrame.h" #include "BOINCClientManager.h" #ifdef __WXMAC__ #include "filesys.h" #include "util.h" enum { NewStyleDaemon = 1, OldStyleDaemon }; #endif #ifdef __WXMSW__ EXTERN_C BOOL IsBOINCServiceInstalled(); EXTERN_C BOOL IsBOINCServiceStarting(); EXTERN_C BOOL IsBOINCServiceRunning(); EXTERN_C BOOL IsBOINCServiceStopping(); EXTERN_C BOOL IsBOINCServiceStopped(); EXTERN_C BOOL StartBOINCService(); EXTERN_C BOOL StopBOINCService(); #endif CBOINCClientManager::CBOINCClientManager() { wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::CBOINCClientManager - Function Begin")); m_bBOINCStartedByManager = false; m_lBOINCCoreProcessId = 0; #ifdef __WXMSW__ m_hBOINCCoreProcess = NULL; #endif wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::CBOINCClientManager - Function End")); } CBOINCClientManager::~CBOINCClientManager() { wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::~CBOINCClientManager - Function Begin")); wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::~CBOINCClientManager - Function End")); } bool CBOINCClientManager::AutoRestart() { if (IsBOINCCoreRunning()) return true; #ifndef __WXMAC__ // Mac can restart Client as a daemon if (! m_bBOINCStartedByManager) return false; #endif m_lBOINCCoreProcessId = 0; StartupBOINCCore(); return true; } bool CBOINCClientManager::IsSystemBooting() { bool bReturnValue = false; #if defined(__WXMSW__) if (GetTickCount() < (1000*60*5)) bReturnValue = true; // If system has been up for less than 5 minutes #elif defined(__WXMAC__) if (TickCount() < (120*60)) bReturnValue = true; // If system has been up for less than 2 minutes #endif return bReturnValue; } int CBOINCClientManager::IsBOINCConfiguredAsDaemon() { bool bReturnValue = false; #if defined(__WXMSW__) if (IsBOINCServiceInstalled()) bReturnValue = 1; #elif defined(__WXMAC__) if ( boinc_file_exists("/Library/LaunchDaemons/edu.berkeley.boinc.plist")) { bReturnValue = NewStyleDaemon; // New-style daemon uses launchd } if (boinc_file_exists("/Library/StartupItems/boinc/boinc") ) { bReturnValue = OldStyleDaemon; // Old-style daemon uses StartupItem } #endif return bReturnValue; } bool CBOINCClientManager::IsBOINCCoreRunning() { wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Function Begin")); int retval; bool running = false; HOST_INFO hostinfo; RPC_CLIENT rpc; #ifdef __WXMSW__ if (IsBOINCServiceInstalled()) { running = (FALSE != IsBOINCServiceStarting()) || (FALSE != IsBOINCServiceRunning()); } else { #endif // If set up to run as a daemon, allow time for daemon to start up for (int i=0; i<10; i++) { retval = rpc.init("localhost"); // synchronous is OK since local wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Connecting to core client returned '%d'"), retval); retval = rpc.get_host_info(hostinfo); wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Requesting host info... retval '%d'"), retval); running = (retval == 0); rpc.close(); if (running) break; if (!IsBOINCConfiguredAsDaemon()) break; wxSleep(1); } #ifdef __WXMSW__ } #endif wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Returning '%d'"), retval); wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Function End")); return running; } bool CBOINCClientManager::StartupBOINCCore() { wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::StartupBOINCCore - Function Begin")); bool bReturnValue = false; wxString strExecute = wxEmptyString; if (IsBOINCCoreRunning()) return true; #if defined(__WXMSW__) LPTSTR szExecute = NULL; LPTSTR szDataDirectory = NULL; if (IsBOINCConfiguredAsDaemon()) { return (StartBOINCService()); } // Append boinc.exe to the end of the strExecute string and get ready to rock strExecute.Printf( wxT("\"%s\\boinc.exe\" --redirectio --launched_by_manager %s"), wxGetApp().GetRootDirectory().c_str(), wxGetApp().GetArguments().c_str() ); PROCESS_INFORMATION pi; STARTUPINFO si; BOOL bProcessStarted; memset(&pi, 0, sizeof(pi)); memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; szExecute = (LPTSTR)strExecute.c_str(); if (wxGetApp().GetDataDirectory().empty()) { szDataDirectory = NULL; } else { szDataDirectory = (LPTSTR)wxGetApp().GetDataDirectory().c_str(); } wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szExecute '%s'\n"), szExecute); wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szDataDirectory '%s'\n"), szDataDirectory); bProcessStarted = CreateProcess( NULL, szExecute, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP|CREATE_NO_WINDOW, NULL, szDataDirectory, &si, &pi ); if (bProcessStarted) { m_lBOINCCoreProcessId = pi.dwProcessId; m_hBOINCCoreProcess = pi.hProcess; } #elif defined(__WXMAC__) wxChar buf[1024]; wxChar *argv[5]; ProcessSerialNumber ourPSN; FSRef ourFSRef; OSErr err; if (IsBOINCConfiguredAsDaemon() == NewStyleDaemon) { system ("launchctl load /Library/LaunchDaemons/edu.berkeley.boinc.plist"); system ("launchctl start edu.berkeley.boinc"); bReturnValue = IsBOINCCoreRunning(); } else { // Get the full path to core client inside this application's bundle err = GetCurrentProcess (&ourPSN); if (err == noErr) { err = GetProcessBundleLocation(&ourPSN, &ourFSRef); } if (err == noErr) { err = FSRefMakePath (&ourFSRef, (UInt8*)buf, sizeof(buf)); } if (err == noErr) { #if 0 // The Mac version of wxExecute(wxString& ...) crashes if there is a space in the path strExecute = wxT("\""); strExecute += wxT(buf); strExecute += wxT("/Contents/Resources/boinc\" --redirectio --launched_by_manager"); m_lBOINCCoreProcessId = ::wxExecute(strExecute); #else // Use wxExecute(wxChar **argv ...) instead of wxExecute(wxString& ...) strcat(buf, "/Contents/Resources/boinc"); argv[0] = buf; argv[1] = "--redirectio"; argv[2] = "--launched_by_manager"; argv[3] = NULL; #ifdef SANDBOX if (!g_use_sandbox) { argv[3] = "--insecure"; argv[4] = NULL; } #endif // Under wxMac-2.8.0, wxExecute starts a separate thread // to wait for child's termination. // That wxProcessTerminationThread uses a huge amount of processor // time (about 11% of one CPU on 2GHz Intel Dual-Core Mac). // m_lBOINCCoreProcessId = ::wxExecute(argv); run_program( "/Library/Application Support/BOINC Data", buf, argv[3] ? 4 : 3, argv, 0.0, m_lBOINCCoreProcessId ); #endif } else { buf[0] = '\0'; } } #else // Unix based systems // Append boinc.exe to the end of the strExecute string and get ready to rock strExecute = ::wxGetCwd() + wxT("/boinc --redirectio --launched_by_manager"); if (!g_use_sandbox) { strExecute += wxT(" --insecure"); } wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szExecute '%s'\n"), strExecute.c_str()); wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szDataDirectory '%s'\n"), ::wxGetCwd().c_str()); m_lBOINCCoreProcessId = ::wxExecute(strExecute); #endif if (0 != m_lBOINCCoreProcessId) { m_bBOINCStartedByManager = true; bReturnValue = true; } wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::StartupBOINCCore - Function End")); return bReturnValue; } #if defined(__WXMSW__) void CBOINCClientManager::ShutdownBOINCCore() { wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::ShutdownBOINCCore - Function Begin")); CMainDocument* pDoc = wxGetApp().GetDocument(); wxInt32 iCount = 0; DWORD dwExitCode = 0; bool bClientQuit = false; wxString strConnectedCompter = wxEmptyString; wxString strPassword = wxEmptyString; wxASSERT(pDoc); wxASSERT(wxDynamicCast(pDoc, CMainDocument)); if (m_bBOINCStartedByManager) { pDoc->GetConnectedComputerName(strConnectedCompter); if (!pDoc->IsComputerNameLocal(strConnectedCompter)) { RPC_CLIENT rpc; if (!rpc.init("localhost")) { pDoc->m_pNetworkConnection->GetLocalPassword(strPassword); rpc.authorize((const char*)strPassword.mb_str()); if (GetExitCodeProcess(m_hBOINCCoreProcess, &dwExitCode)) { if (STILL_ACTIVE == dwExitCode) { rpc.quit(); for (iCount = 0; iCount <= 10; iCount++) { if (!bClientQuit && GetExitCodeProcess(m_hBOINCCoreProcess, &dwExitCode)) { if (STILL_ACTIVE != dwExitCode) { wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - (localhost) Application Exit Detected")); bClientQuit = true; break; } } wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - (localhost) Application Exit NOT Detected, Sleeping...")); ::wxSleep(1); } } } } rpc.close(); } else { if (GetExitCodeProcess(m_hBOINCCoreProcess, &dwExitCode)) { if (STILL_ACTIVE == dwExitCode) { pDoc->CoreClientQuit(); for (iCount = 0; iCount <= 10; iCount++) { if (!bClientQuit && GetExitCodeProcess(m_hBOINCCoreProcess, &dwExitCode)) { if (STILL_ACTIVE != dwExitCode) { wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - Application Exit Detected")); bClientQuit = true; break; } } wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - Application Exit NOT Detected, Sleeping...")); ::wxSleep(1); } } } } if (!bClientQuit) { ::wxKill(m_lBOINCCoreProcessId); } m_lBOINCCoreProcessId = 0; } wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::ShutdownBOINCCore - Function End")); } #elif defined(__WXMAC__) static char * PersistentFGets(char *buf, size_t buflen, FILE *f) { char *p = buf; size_t len = buflen; size_t datalen = 0; *buf = '\0'; while (datalen < (buflen - 1)) { fgets(p, len, f); if (feof(f)) break; if (ferror(f) && (errno != EINTR)) break; if (strchr(buf, '\n')) break; datalen = strlen(buf); p = buf + datalen; len -= datalen; } return (buf[0] ? buf : NULL); } bool CBOINCClientManager::ProcessExists(pid_t thePID) { FILE *f; char buf[256]; pid_t aPID; f = popen("ps -a -x -c -o pid,state", "r"); if (f == NULL) return false; while (PersistentFGets(buf, sizeof(buf), f)) { aPID = atol(buf); if (aPID == thePID) { if (strchr(buf, 'Z')) // A 'zombie', stopped but waiting break; // for us (its parent) to quit pclose(f); return true; } } pclose(f); return false; } // wxProcess::Exists and wxKill are unimplemented in WxMac-2.6.0 void CBOINCClientManager::ShutdownBOINCCore() { CMainDocument* pDoc = wxGetApp().GetDocument(); wxInt32 iCount = 0; wxString strConnectedCompter = wxEmptyString; wxString strPassword = wxEmptyString; wxASSERT(pDoc); wxASSERT(wxDynamicCast(pDoc, CMainDocument)); if (m_bBOINCStartedByManager) { pDoc->GetConnectedComputerName(strConnectedCompter); if (!pDoc->IsComputerNameLocal(strConnectedCompter)) { RPC_CLIENT rpc; if (!rpc.init("localhost")) { pDoc->m_pNetworkConnection->GetLocalPassword(strPassword); rpc.authorize((const char*)strPassword.mb_str()); if (ProcessExists(m_lBOINCCoreProcessId)) { rpc.quit(); for (iCount = 0; iCount <= 10; iCount++) { if (!ProcessExists(m_lBOINCCoreProcessId)) return; ::wxSleep(1); } } } rpc.close(); } else { if (ProcessExists(m_lBOINCCoreProcessId)) { pDoc->CoreClientQuit(); for (iCount = 0; iCount <= 10; iCount++) { if (!ProcessExists(m_lBOINCCoreProcessId)) return; ::wxSleep(1); } } } // Client did not quit after 10 seconds so kill it kill(m_lBOINCCoreProcessId, SIGKILL); } m_lBOINCCoreProcessId = 0; } #else void CBOINCClientManager::ShutdownBOINCCore() { CMainDocument* pDoc = wxGetApp().GetDocument(); wxInt32 iCount = 0; bool bClientQuit = false; wxString strConnectedCompter = wxEmptyString; wxString strPassword = wxEmptyString; wxASSERT(pDoc); wxASSERT(wxDynamicCast(pDoc, CMainDocument)); if (m_bBOINCStartedByManager) { pDoc->GetConnectedComputerName(strConnectedCompter); if (!pDoc->IsComputerNameLocal(strConnectedCompter)) { RPC_CLIENT rpc; if (!rpc.init("localhost")) { pDoc->m_pNetworkConnection->GetLocalPassword(strPassword); rpc.authorize((const char*)strPassword.mb_str()); if (wxProcess::Exists(m_lBOINCCoreProcessId)) { rpc.quit(); for (iCount = 0; iCount <= 10; iCount++) { if (!bClientQuit && !wxProcess::Exists(m_lBOINCCoreProcessId)) { bClientQuit = true; break; } ::wxSleep(1); } } } rpc.close(); } else { if (wxProcess::Exists(m_lBOINCCoreProcessId)) { pDoc->CoreClientQuit(); for (iCount = 0; iCount <= 10; iCount++) { if (!bClientQuit && !wxProcess::Exists(m_lBOINCCoreProcessId)) { bClientQuit = true; break; } ::wxSleep(1); } } } if (!bClientQuit) { ::wxKill(m_lBOINCCoreProcessId); } } } #endif