// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 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 .
#if defined(__GNUG__) && !defined(__APPLE__)
#pragma implementation "BOINCClientManager.h"
#endif
#include "stdwx.h"
#include "diagnostics.h"
#include "miofile.h"
#include "LogBOINC.h"
#include "BOINCGUIApp.h"
#include "SkinManager.h"
#include "MainDocument.h"
#include "BOINCBaseFrame.h"
#include "AdvancedFrame.h"
#include "BOINCClientManager.h"
#include "error_numbers.h"
#include "procinfo.h"
#include "filesys.h"
#include "daemonmgt.h"
#include "util.h"
#include "Events.h"
#include "version.h"
// Alert user if Client crashes 3 times in 30 minutes
#define CLIENT_3_CRASH_MAX_TIME 30
#ifdef __WXMAC__
enum {
NewStyleDaemon = 1,
OldStyleDaemon
};
#elif defined(__WXMSW__)
#include "win_util.h"
#include "diagnostics_win.h"
extern int diagnostics_get_process_information(PVOID* ppBuffer, PULONG pcbBuffer);
#else
#include
#endif
CBOINCClientManager::CBOINCClientManager() {
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::CBOINCClientManager - Function Begin"));
m_bBOINCStartedByManager = false;
m_lBOINCCoreProcessId = 0;
m_fAutoRestart1Time = 0;
m_fAutoRestart2Time = 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() {
double timeNow, timeDiff;
if (IsBOINCCoreRunning()) return true;
#if ! (defined(__WXMAC__) || defined(__WXMSW__))
// Mac and Windows can restart Client as a daemon, but
// Linux may not know Client's location if it didn't start the Client
if (!m_bBOINCStartedByManager) return false;
#endif
// Alert user if Client crashes 3 times in CLIENT_3_CRASH_MAX_TIME
timeNow = dtime();
timeDiff = timeNow - m_fAutoRestart1Time;
if ((timeDiff) < (CLIENT_3_CRASH_MAX_TIME * 60)) {
int response;
ClientCrashDlg *dlg = new ClientCrashDlg(timeDiff);
if (dlg) {
CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
if (!pFrame->IsShown()) {
pFrame->Show();
}
response = dlg->ShowModal();
dlg->Destroy();
if (response == wxID_CANCEL) return false;
timeNow = 0;
m_fAutoRestart1Time = 0;
m_fAutoRestart2Time = 0;
}
}
m_lBOINCCoreProcessId = 0;
m_fAutoRestart1Time = m_fAutoRestart2Time;
m_fAutoRestart2Time = timeNow;
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 (is_daemon_installed()) 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"));
bool running = false;
#ifdef __WXMSW__
char buf[MAX_PATH] = "";
if (is_daemon_installed()) {
running = (FALSE != is_daemon_starting()) || (FALSE != is_daemon_running());
} else {
// Global mutex on Win2k and later
//
if (IsWindows2000Compatible()) {
strcpy(buf, "Global\\");
}
strcat( buf, RUN_MUTEX);
HANDLE h = CreateMutexA(NULL, true, buf);
DWORD err = GetLastError();
if ((h==0) || (err == ERROR_ALREADY_EXISTS)) {
running = true;
}
if (h) {
CloseHandle(h);
}
}
#elif defined(__WXMAC__)
char path[1024];
static FILE_LOCK file_lock;
sprintf(path, "%s/%s", (const char *)wxGetApp().GetDataDirectory().mb_str(), LOCK_FILE_NAME);
if (boinc_file_exists(path)) { // If there is no lock file, core is not running
if (file_lock.lock(path)) {
running = true;
} else {
file_lock.unlock(path);
}
}
#else
std::vector piv;
int retval;
if (m_lBOINCCoreProcessId) {
// Prevent client from being a zombie
if (waitpid(m_lBOINCCoreProcessId, 0, WNOHANG) == m_lBOINCCoreProcessId) {
m_lBOINCCoreProcessId = 0;
}
}
// Look for BOINC Client in list of all running processes
retval = procinfo_setup(piv);
if (retval) return false; // Should never happen
for (unsigned int i=0; iProcessId) {
tstring strProcessName = pProcesses->ProcessName.Buffer;
if (downcase_string(strProcessName) == tstring(_T("boinc.exe"))) {
TerminateProcessById(pProcesses->ProcessId);
break;
}
}
// Move to the next structure if one exists
if (!pProcesses->NextEntryDelta) {
break;
}
pProcesses = (PSYSTEM_PROCESSES)(((LPBYTE)pProcesses) + pProcesses->NextEntryDelta);
} while (pProcesses);
// Release resources
if (pBuffer) HeapFree(GetProcessHeap(), NULL, pBuffer);
}
#else
void CBOINCClientManager::KillClient() {
std::vector piv;
int retval;
if (m_lBOINCCoreProcessId) {
kill_program(m_lBOINCCoreProcessId);
return;
}
retval = procinfo_setup(piv);
if (retval) return; // Should never happen
for (unsigned int i=0; iGetConnectedComputerName(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 (IsBOINCCoreRunning()) {
rpc.quit();
for (iCount = 0; iCount <= 10; iCount++) {
if (!bClientQuit && !IsBOINCCoreRunning()) {
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);
}
} else {
bClientQuit = true;
}
}
rpc.close();
} else {
if (IsBOINCCoreRunning()) {
pDoc->CoreClientQuit();
for (iCount = 0; iCount <= 10; iCount++) {
if (!bClientQuit && !IsBOINCCoreRunning()) {
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);
}
} else {
bClientQuit = true;
}
}
}
if (!bClientQuit) {
KillClient();
}
m_lBOINCCoreProcessId = 0;
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::ShutdownBOINCCore - Function End"));
}
BEGIN_EVENT_TABLE(ClientCrashDlg, wxDialog)
EVT_BUTTON(wxID_HELP, ClientCrashDlg::OnHelp)
END_EVENT_TABLE()
IMPLEMENT_CLASS(ClientCrashDlg, wxDialog)
ClientCrashDlg::ClientCrashDlg(double timeDiff) : wxDialog( NULL, wxID_ANY, wxT(""), wxDefaultPosition ) {
wxString strDialogTitle = wxEmptyString;
wxString strDialogMessage = wxEmptyString;
int minutes = wxMax((int)((timeDiff + 59.) / 60.), 2);
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
wxASSERT(pSkinAdvanced);
// %s is the application name
// i.e. 'BOINC Manager', 'GridRepublic Manager'
strDialogTitle.Printf(
_("%s - Unexpected Exit"),
pSkinAdvanced->GetApplicationName().c_str()
);
SetTitle(strDialogTitle.c_str());
// 1st %s is the application name
// i.e. 'BOINC Manager', 'GridRepublic Manager'
// 2st %s is the project name
// i.e. 'BOINC', 'GridRepublic'
strDialogMessage.Printf(
_("The %s client has exited unexpectedly 3 times within the last %d minutes.\nWould you like to restart it again?"),
pSkinAdvanced->GetApplicationShortName().c_str(),
minutes
);
wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer *icon_text = new wxBoxSizer( wxHORIZONTAL );
icon_text->Add( CreateTextSizer( strDialogMessage ), 0, wxALIGN_CENTER | wxLEFT, 10 );
topsizer->Add( icon_text, 1, wxCENTER | wxLEFT|wxRIGHT|wxTOP, 10 );
wxStdDialogButtonSizer *sizerBtn = CreateStdDialogButtonSizer(wxYES | wxNO | wxHELP);
SetEscapeId(wxID_NO); // Changes return value of NO button to wxID_CANCEL
if ( sizerBtn )
topsizer->Add(sizerBtn, 0, wxEXPAND | wxALL, 10 );
SetAutoLayout( true );
SetSizer( topsizer );
topsizer->SetSizeHints( this );
topsizer->Fit( this );
wxSize size( GetSize() );
if (size.x < size.y*3/2)
{
size.x = size.y*3/2;
SetSize( size );
}
Centre( wxBOTH | wxCENTER_FRAME);
}
void ClientCrashDlg::OnHelp(wxCommandEvent& WXUNUSED(eventUnused)) {
wxString strURL = wxGetApp().GetSkinManager()->GetAdvanced()->GetOrganizationHelpUrl();
wxString wxurl;
wxurl.Printf(
wxT("%s?target=crash_detection&version=%s&controlid=%d"),
strURL.c_str(),
wxString(BOINC_VERSION_STRING, wxConvUTF8).c_str(),
ID_HELPBOINC
);
wxLaunchDefaultBrowser(wxurl);
}