mirror of https://github.com/BOINC/boinc.git
628 lines
21 KiB
C++
628 lines
21 KiB
C++
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
#if defined(__GNUG__) && !defined(__APPLE__)
|
|
#pragma implementation "BOINCClientManager.h"
|
|
#endif
|
|
|
|
#include "stdwx.h"
|
|
|
|
#include "diagnostics.h"
|
|
#include "miofile.h"
|
|
#include "str_replace.h"
|
|
#include "util.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 "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 <sys/wait.h>
|
|
#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
|
|
//
|
|
safe_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
|
|
PROC_MAP pm;
|
|
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(pm);
|
|
if (retval) return false; // Should never happen
|
|
|
|
PROC_MAP::iterator i;
|
|
for (i=pm.begin(); i!=pm.end(); ++i) {
|
|
PROCINFO& pi = i->second;
|
|
if (!strcmp(pi.command, "boinc")) {
|
|
running = true;
|
|
break;
|
|
}
|
|
if (!strcmp(pi.command, "boinc_client")) {
|
|
running = true;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Returning '%d'"), (int)running);
|
|
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;
|
|
wxString strDataDir = wxEmptyString;
|
|
|
|
if (IsBOINCCoreRunning()) return true;
|
|
|
|
#if defined(__WXMSW__)
|
|
const char* pszExecute = NULL;
|
|
const char* pszDataDirectory = NULL;
|
|
|
|
if (IsBOINCConfiguredAsDaemon()) {
|
|
start_daemon_via_daemonctrl();
|
|
|
|
m_bBOINCStartedByManager = true;
|
|
bReturnValue = IsBOINCCoreRunning();
|
|
} else {
|
|
|
|
// Append boinc.exe to the end of the strExecute string and get ready to rock
|
|
strExecute.Printf(
|
|
wxT("\"%sboinc.exe\" --redirectio --launched_by_manager %s"),
|
|
wxGetApp().GetRootDirectory().c_str(),
|
|
wxGetApp().GetArguments().c_str()
|
|
);
|
|
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFOA 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;
|
|
|
|
pszExecute = (const char*)strExecute.mb_str();
|
|
if (wxGetApp().GetDataDirectory().empty()) {
|
|
pszDataDirectory = NULL;
|
|
} else {
|
|
strDataDir = wxGetApp().GetDataDirectory();
|
|
pszDataDirectory = (const char*)strDataDir.mb_str();
|
|
}
|
|
|
|
wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - pszExecute '%s'\n"), pszExecute);
|
|
wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - pszDataDirectory '%s'\n"), pszDataDirectory);
|
|
|
|
bProcessStarted = CreateProcessA(
|
|
NULL,
|
|
(LPSTR)pszExecute,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
CREATE_NEW_PROCESS_GROUP|CREATE_NO_WINDOW,
|
|
NULL,
|
|
(LPSTR)pszDataDirectory,
|
|
&si,
|
|
&pi
|
|
);
|
|
|
|
if (bProcessStarted) {
|
|
m_lBOINCCoreProcessId = pi.dwProcessId;
|
|
m_hBOINCCoreProcess = pi.hProcess;
|
|
}
|
|
}
|
|
|
|
#elif defined(__WXMAC__)
|
|
|
|
#if 0 // The Mac version of wxExecute(wxString& ...) crashes if there is a space in the path
|
|
wxChar buf[1024];
|
|
wxChar *argv[5];
|
|
#else
|
|
char buf[1024];
|
|
char *argv[5];
|
|
#endif
|
|
ProcessSerialNumber ourPSN;
|
|
FSRef ourFSRef;
|
|
OSErr err;
|
|
|
|
if (IsBOINCConfiguredAsDaemon() == NewStyleDaemon) {
|
|
system ("launchctl load /Library/LaunchDaemons/edu.berkeley.boinc.plist");
|
|
system ("launchctl start edu.berkeley.boinc");
|
|
for (int i=0; i<100; i++) { // Wait up to 1 seccond in 10 ms increments
|
|
boinc_sleep(0.01);
|
|
if (IsBOINCCoreRunning()) {
|
|
bReturnValue = true;
|
|
break;
|
|
}
|
|
}
|
|
} 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
|
|
wxString savedWD = ::wxGetCwd();
|
|
|
|
wxSetWorkingDirectory(wxGetApp().GetDataDirectory());
|
|
|
|
// Append boinc.exe to the end of the strExecute string and get ready to rock
|
|
strExecute = wxGetApp().GetRootDirectory() + wxT("boinc --redirectio --launched_by_manager");
|
|
#ifdef SANDBOX
|
|
if (!g_use_sandbox) {
|
|
strExecute += wxT(" --insecure");
|
|
}
|
|
#endif
|
|
|
|
wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szExecute '%s'\n"), strExecute.c_str());
|
|
wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szDataDirectory '%s'\n"), wxGetApp().GetDataDirectory().c_str());
|
|
|
|
m_lBOINCCoreProcessId = ::wxExecute(strExecute);
|
|
|
|
wxSetWorkingDirectory(savedWD);
|
|
#endif
|
|
|
|
if (0 != m_lBOINCCoreProcessId) {
|
|
m_bBOINCStartedByManager = true;
|
|
bReturnValue = true;
|
|
// Allow time for daemon to start up so we don't keep relaunching it
|
|
for (int i=0; i<100; i++) { // Wait up to 1 seccond in 10 ms increments
|
|
boinc_sleep(0.01);
|
|
if (IsBOINCCoreRunning()) break;
|
|
}
|
|
}
|
|
|
|
wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::StartupBOINCCore - Function End"));
|
|
return bReturnValue;
|
|
}
|
|
|
|
|
|
#ifdef __WXMSW__
|
|
static tstring downcase_string(tstring& orig) {
|
|
tstring retval = orig;
|
|
for (size_t i=0; i < retval.length(); i++) {
|
|
retval[i] = tolower(retval[i]);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
void CBOINCClientManager::KillClient() {
|
|
ULONG cbBuffer = 128*1024; // 128k initial buffer
|
|
PVOID pBuffer = NULL;
|
|
PSYSTEM_PROCESSES pProcesses = NULL;
|
|
|
|
if (m_hBOINCCoreProcess != NULL) {
|
|
kill_program(m_hBOINCCoreProcess);
|
|
return;
|
|
}
|
|
|
|
// Get a snapshot of the process and thread information.
|
|
diagnostics_get_process_information(&pBuffer, &cbBuffer);
|
|
|
|
// Lets start walking the structures to find the good stuff.
|
|
pProcesses = (PSYSTEM_PROCESSES)pBuffer;
|
|
do {
|
|
if (pProcesses->ProcessId) {
|
|
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() {
|
|
PROC_MAP pm;
|
|
int retval;
|
|
|
|
if (m_lBOINCCoreProcessId) {
|
|
kill_program(m_lBOINCCoreProcessId);
|
|
return;
|
|
}
|
|
|
|
retval = procinfo_setup(pm);
|
|
if (retval) return; // Should never happen
|
|
|
|
PROC_MAP::iterator i;
|
|
for (i=pm.begin(); i!=pm.end(); ++i) {
|
|
PROCINFO& procinfo = i->second;
|
|
if (!strcmp(procinfo.command, "boinc")) {
|
|
kill_program(procinfo.id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void CBOINCClientManager::ShutdownBOINCCore(bool ShuttingDownManager) {
|
|
wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::ShutdownBOINCCore - Function Begin"));
|
|
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
wxInt32 iCount = 0;
|
|
bool bClientQuit = false;
|
|
wxString strConnectedCompter = wxEmptyString;
|
|
wxString strPassword = wxEmptyString;
|
|
double startTime = 0;
|
|
wxDateTime zeroTime = wxDateTime((time_t)0);
|
|
wxDateTime rpcCompletionTime = zeroTime;
|
|
ASYNC_RPC_REQUEST request;
|
|
int quit_result;
|
|
|
|
wxASSERT(pDoc);
|
|
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
|
|
|
|
#ifdef __WXMAC__
|
|
// Mac Manager shuts down client only if Manager started client
|
|
if (!m_bBOINCStartedByManager) return;
|
|
#endif
|
|
|
|
#ifdef __WXMSW__
|
|
if (IsBOINCConfiguredAsDaemon()) {
|
|
stop_daemon_via_daemonctrl();
|
|
bClientQuit = true;
|
|
} else
|
|
#endif
|
|
{
|
|
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 (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()) {
|
|
if (ShuttingDownManager) {
|
|
// Set event filtering to allow RPC completion
|
|
// events but not events which start new RPCs
|
|
wxGetApp().SetEventFiltering(true);
|
|
}
|
|
quit_result = -1;
|
|
request.clear();
|
|
request.which_rpc = RPC_QUIT;
|
|
request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH;
|
|
request.completionTime = &rpcCompletionTime;
|
|
request.resultPtr = &quit_result;
|
|
pDoc->RequestRPC(request); // Issue an asynchronous Quit RPC
|
|
|
|
// Client needs time to shut down project applications, so don't wait
|
|
// for it to shut down; assume it will exit OK if Quit RPC succeeds.
|
|
startTime = dtime();
|
|
while ((dtime() - startTime) < 10.0) { // Allow 10 seconds
|
|
boinc_sleep(0.25); // Check 4 times per second
|
|
wxSafeYield(NULL, true); // To allow handling RPC completion events
|
|
if (!bClientQuit && (rpcCompletionTime != zeroTime)) {
|
|
// If Quit RPC finished, check its returned value
|
|
if (quit_result) {
|
|
break; // Quit RPC returned an error
|
|
}
|
|
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..."));
|
|
}
|
|
} 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);
|
|
}
|