1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17
18 #if defined(__GNUG__) && !defined(__APPLE__)
19 #pragma implementation "MainDocument.h"
20 #endif
21
22 #include "stdwx.h"
23 #include <wx/numformatter.h>
24
25 #include "error_numbers.h"
26 #include "str_replace.h"
27 #include "util.h"
28 #ifdef __WXMAC__
29 #include "mac_util.h"
30 #endif
31 #ifdef _WIN32
32 #include "proc_control.h"
33 #endif
34
35 #include "BOINCGUIApp.h"
36 #include "MainDocument.h"
37 #include "BOINCBaseFrame.h"
38 #include "AdvancedFrame.h"
39 #include "BOINCClientManager.h"
40 #include "BOINCTaskBar.h"
41 #include "DlgEventLog.h"
42 #include "Events.h"
43 #include "SkinManager.h"
44
45 #ifndef _WIN32
46 #include <sys/wait.h>
47 #endif
48
49 #ifdef SANDBOX
50 #include <grp.h>
51 #endif
52
53 #define USE_CACHE_TIMEOUTS 0
54
55 // If get_results RPC takes x seconds, do it no more often than
56 // once every (x * GET_RESULTS_FREQUENCY_FACTOR) seconds
57 #define GET_RESULTS_FREQUENCY_FACTOR 10
58
59 // *** RPC update intervals in seconds ***
60 // m_dtCachedCCStatusTimestamp
61 #define CCSTATUS_RPC_INTERVAL 1
62
63 //m_dtCachedStateTimestamp
64 #define STATERPC_INTERVAL 3600
65
66 //m_dtNoticesTimeStamp
67 #define NOTICESBACKGROUNDRPC_INTERVAL 60
68
69 //m_dtProjectsStatusTimestamp
70 #define PROJECTSTATUSRPC_INTERVAL 1
71
72 //m_dtResultsTimestamp
73 #define RESULTSRPC_INTERVAL 1
74
75 //m_dtFileTransfersTimestamp
76 #define FILETRANSFERSRPC_INTERVAL 1
77
78 // m_dtStatisticsStatusTimestamp
79 #define STATISTICSSTATUSRPC_INTERVAL 60
80
81 // m_dtDiskUsageTimestamp
82 #define DISKUSAGERPC_INTERVAL 60
83
84 // m_dtCachedSimpleGUITimestamp
85 #define CACHEDSIMPLEGUIRPC_INTERVAL 1
86
87 // m_dtCachedAcctMgrInfoTimestamp
88 #define CACHEDACCTMGRINFORPC_INTERVAL 600
89
90 // m_dtLasAsyncRPCDlgTime
91 #define DELAYAFTERASYNCRPC_DLG 1
92
93 bool g_use_sandbox = false;
94
95 extern bool s_bSkipExitConfirmation;
96
97
98 using std::string;
99
CNetworkConnection(CMainDocument * pDocument)100 CNetworkConnection::CNetworkConnection(CMainDocument* pDocument) :
101 wxObject() {
102 m_pDocument = pDocument;
103
104 m_strConnectedComputerName = wxEmptyString;
105 m_strConnectedComputerPassword = wxEmptyString;
106 m_strNewComputerName = wxEmptyString;
107 m_strNewComputerPassword = wxEmptyString;
108 m_bFrameShutdownDetected = false;
109 m_bConnectEvent = false;
110 m_bConnected = false;
111 m_bReconnecting = false;
112 m_bForceReconnect = false;
113 m_bReconnectOnError = false;
114 m_bNewConnection = false;
115 m_bUseDefaultPassword = false;
116 m_bUsedDefaultPassword = false;
117 m_iPort = GUI_RPC_PORT,
118 m_iReadGUIRPCAuthFailure = 0;
119 }
120
121
~CNetworkConnection()122 CNetworkConnection::~CNetworkConnection() {
123 }
124
125
GetLocalPassword(wxString & strPassword)126 int CNetworkConnection::GetLocalPassword(wxString& strPassword){
127 char buf[256];
128 safe_strcpy(buf, "");
129
130 FILE* f = fopen("gui_rpc_auth.cfg", "r");
131 if (!f) return errno;
132 fgets(buf, 256, f);
133 fclose(f);
134 int n = (int)strlen(buf);
135 if (n) {
136 n--;
137 if (buf[n]=='\n') {
138 buf[n] = 0;
139 }
140 }
141
142 strPassword = wxString(buf, wxConvUTF8);
143 return 0;
144 }
145
146
Poll()147 void CNetworkConnection::Poll() {
148 int retval;
149 wxString strComputer = wxEmptyString;
150 wxString strComputerPassword = wxEmptyString;
151
152 if (IsReconnecting()) {
153 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - Reconnection Detected"));
154 retval = m_pDocument->rpcClient.init_poll();
155 if (!retval) {
156 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - init_poll() returned ERR_CONNECT, now authorizing..."));
157
158 // Wait until we can establish a connection to the core client before reading
159 // the password so that the client has time to create one when it needs to.
160 if (m_bUseDefaultPassword) {
161 m_iReadGUIRPCAuthFailure = 0;
162 m_bUseDefaultPassword = FALSE;
163 m_bUsedDefaultPassword = true;
164
165 m_iReadGUIRPCAuthFailure = GetLocalPassword(m_strNewComputerPassword);
166 }
167
168 retval = m_pDocument->rpc.authorize(m_strNewComputerPassword.mb_str());
169 if (!retval) {
170 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - Connection Success"));
171 SetStateSuccess(m_strNewComputerName, m_strNewComputerPassword);
172 } else if (ERR_AUTHENTICATOR == retval) {
173 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - RPC Authorization - ERR_AUTHENTICATOR"));
174 SetStateErrorAuthentication();
175 } else {
176 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - RPC Authorization Failed '%d'"), retval);
177 SetStateError();
178 }
179 m_bUsedDefaultPassword = false;
180 } else if (ERR_RETRY != retval) {
181 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - RPC Connection Failed '%d'"), retval);
182 SetStateError();
183 }
184 } else if (IsConnectEventSignaled() || m_bReconnectOnError) {
185 if ((m_bForceReconnect) || (!IsConnected() && m_bReconnectOnError)) {
186 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - Resetting Document State"));
187 m_pDocument->ResetState();
188 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - Setting connection state to reconnecting"));
189 SetStateReconnecting();
190 }
191
192 if (!IsConnected()) {
193 // determine computer name and password to use.
194 // NOTE: Initial connection case.
195 if (!m_strNewComputerName.empty()) {
196 strComputer = m_strNewComputerName;
197 strComputerPassword = m_strNewComputerPassword;
198 } else {
199 // NOTE: Reconnect after a disconnect case.
200 // Values are stored after the first successful connect to the host.
201 // See: SetStateSuccess()
202 if (!m_strConnectedComputerName.empty()) {
203 strComputer = m_strConnectedComputerName;
204 strComputerPassword = m_strConnectedComputerPassword;
205 }
206 }
207
208 // a host value of NULL is special cased as binding to the localhost and
209 // if we are connecting to the localhost we need to retry the connection
210 // for awhile so that the users can respond to firewall prompts.
211 //
212 // use a timeout of 60 seconds so that slow machines do not get a
213 // timeout event right after boot-up.
214 //
215 if (IsComputerNameLocal(strComputer)) {
216 retval = m_pDocument->rpcClient.init_asynch(NULL, 60.0, true, m_iPort);
217 } else {
218 retval = m_pDocument->rpcClient.init_asynch(strComputer.mb_str(), 60.0, false, m_iPort);
219 }
220
221 if (!retval) {
222 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - RPC Initialization Called"));
223 } else {
224 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - RPC Initialization Failed '%d'"), retval);
225 SetStateError();
226 }
227 }
228 }
229 }
230
231
FrameShutdownDetected()232 int CNetworkConnection::FrameShutdownDetected() {
233 m_bFrameShutdownDetected = true;
234 return 0;
235 }
236
GetConnectedComputerName(wxString & strMachine)237 int CNetworkConnection::GetConnectedComputerName(wxString& strMachine) {
238 strMachine = m_strConnectedComputerName;
239 return 0;
240 }
241
242
GetConnectedComputerVersion(wxString & strVersion)243 int CNetworkConnection::GetConnectedComputerVersion(wxString& strVersion) {
244 strVersion = m_strConnectedComputerVersion;
245 return 0;
246 }
247
248
GetConnectingComputerName(wxString & strMachine)249 int CNetworkConnection::GetConnectingComputerName(wxString& strMachine) {
250 strMachine = m_strNewComputerName;
251 return 0;
252 }
253
254
IsComputerNameLocal(const wxString & strMachine)255 bool CNetworkConnection::IsComputerNameLocal(const wxString& strMachine) {
256 static wxString strHostName = wxEmptyString;
257 static wxString strFullHostName = wxEmptyString;
258
259 if (strHostName.empty()) {
260 strHostName = ::wxGetHostName().Lower();
261 }
262 if (strFullHostName.empty()) {
263 strFullHostName = ::wxGetFullHostName().Lower();
264 }
265
266 if (strMachine.empty()) {
267 return true;
268 } else if (wxT("localhost") == strMachine.Lower()) {
269 return true;
270 } else if (wxT("localhost.localdomain") == strMachine.Lower()) {
271 return true;
272 } else if (strHostName == strMachine.Lower()) {
273 return true;
274 } else if (strFullHostName == strMachine.Lower()) {
275 return true;
276 }
277 return false;
278 }
279
280
SetComputer(const wxString & szComputer,const int iPort,const wxString & szPassword,const bool bUseDefaultPassword)281 int CNetworkConnection::SetComputer(
282 const wxString& szComputer, const int iPort, const wxString& szPassword,
283 const bool bUseDefaultPassword
284 ) {
285 m_strNewComputerName.Empty();
286 m_strNewComputerPassword.Empty();
287 m_bUseDefaultPassword = FALSE;
288
289 m_bNewConnection = true;
290 m_strNewComputerName = szComputer;
291 m_iPort = iPort;
292 m_strNewComputerPassword = szPassword;
293 m_bUseDefaultPassword = bUseDefaultPassword;
294 return 0;
295 }
296
297
SetStateErrorAuthentication()298 void CNetworkConnection::SetStateErrorAuthentication() {
299 CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
300 if (pFrame && !m_bFrameShutdownDetected) {
301 wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
302 m_bConnected = false;
303 m_bReconnecting = false;
304 m_bReconnectOnError = false;
305
306 m_bConnectEvent = false;
307
308 pFrame->ShowConnectionBadPasswordAlert(m_bUsedDefaultPassword, m_iReadGUIRPCAuthFailure);
309 }
310 }
311
312
SetStateError()313 void CNetworkConnection::SetStateError() {
314 CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
315 if (pFrame && !m_bFrameShutdownDetected) {
316 wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
317 m_bConnected = false;
318 m_bReconnecting = false;
319 m_bReconnectOnError = false;
320
321 m_bConnectEvent = false;
322
323 pFrame->ShowConnectionFailedAlert();
324 }
325 }
326
327
SetStateReconnecting()328 void CNetworkConnection::SetStateReconnecting() {
329 CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
330 if (pFrame && !m_bFrameShutdownDetected) {
331 wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
332 m_bConnected = false;
333 m_bReconnectOnError = false;
334 m_bForceReconnect = false;
335 m_bReconnecting = true;
336 if (!m_bNewConnection) {
337 m_strNewComputerName = m_strConnectedComputerName;
338 m_strNewComputerPassword = m_strConnectedComputerPassword;
339 }
340 pFrame->FireRefreshView();
341 }
342 }
343
344
SetStateSuccess(wxString & strComputer,wxString & strComputerPassword)345 void CNetworkConnection::SetStateSuccess(wxString& strComputer, wxString& strComputerPassword) {
346 CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
347 if (pFrame && !m_bFrameShutdownDetected) {
348 wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
349
350 m_bConnected = true;
351 m_bReconnecting = false;
352 m_bReconnectOnError = true;
353 m_strConnectedComputerName = strComputer;
354 m_strConnectedComputerPassword = strComputerPassword;
355 m_strNewComputerName = wxEmptyString;
356 m_strNewComputerPassword = wxEmptyString;
357 m_bNewConnection = false;
358
359 // Prevent a race condition where OnFrameRender() causes SetStateDisconnected()
360 // to be called due to a previous RPC error before we reconnected.
361 m_pDocument->RefreshRPCs(true);
362
363 // Get the version of the client and cache it
364 VERSION_INFO vi;
365 m_pDocument->rpc.exchange_versions(vi);
366 m_strConnectedComputerVersion.Printf(
367 wxT("%d.%d.%d"),
368 vi.major, vi.minor, vi.release
369 );
370
371 m_bConnectEvent = false;
372 m_pDocument->ResetMessageState();
373
374 pFrame->FireConnect();
375 }
376 }
377
378
SetStateDisconnected()379 void CNetworkConnection::SetStateDisconnected() {
380 CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
381 if (pFrame && !m_bFrameShutdownDetected) {
382 wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
383 m_bConnected = false;
384 m_bReconnecting = false;
385 m_pDocument->results.clear();
386 }
387 }
388
389
IMPLEMENT_DYNAMIC_CLASS(CMainDocument,wxObject)390 IMPLEMENT_DYNAMIC_CLASS(CMainDocument, wxObject)
391
392
393 CMainDocument::CMainDocument() : rpc(this) {
394
395 #ifdef __WIN32__
396 int retval;
397 WSADATA wsdata;
398
399 retval = WSAStartup(MAKEWORD(1, 1), &wsdata);
400 if (retval) {
401 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CMainDocument - Winsock Initialization Failure '%d'"), retval);
402 }
403 #endif
404
405 safe_strcpy(m_szLanguage, "");
406
407 m_bClientStartCheckCompleted = false;
408
409 m_ActiveTasksOnly = false;
410
411 m_fProjectTotalResourceShare = 0.0;
412
413 m_iLastMessageSequenceNumber = 0;
414 m_iFirstMessageSequenceNumber = -1;
415
416 m_iNoticeSequenceNumber = 0;
417 m_iLastReadNoticeSequenceNumber = -1;
418 m_dLastReadNoticeArrivalTime = 0.0;
419 m_bWaitingForGetNoticesRPC = false;
420
421 m_dtCachedStateTimestamp = wxDateTime((time_t)0);
422 m_iGet_state_rpc_result = 0;
423
424 m_dtCachedCCStatusTimestamp = wxDateTime((time_t)0);
425 m_iGet_status_rpc_result = 0;
426
427 m_dtNoticesTimeStamp = wxDateTime((time_t)0);;
428 m_iGet_notices_rpc_result = -1;
429
430 m_dtProjectsStatusTimestamp = wxDateTime((time_t)0);
431 m_iGet_project_status1_rpc_result = -1;
432
433 m_dtResultsTimestamp = wxDateTime((time_t)0);
434 m_iGet_results_rpc_result = -1;
435
436 m_fResultsRPCExecutionTime = 0;
437
438 m_dtKillInactiveGfxTimestamp = wxDateTime((time_t)0);
439 m_dtFileTransfersTimestamp = wxDateTime((time_t)0);
440 m_iGet_file_transfers_rpc_result = 0;
441
442 m_iGet_messages_rpc_result = -1;
443
444 m_dtDiskUsageTimestamp = wxDateTime((time_t)0);
445 m_iGet_dsk_usage_rpc_result = -1;
446
447 m_dtStatisticsStatusTimestamp = wxDateTime((time_t)0);
448 m_iGet_statistics_rpc_result = -1;
449
450 m_dtCachedSimpleGUITimestamp = wxDateTime((time_t)0);
451 m_iGet_simple_gui2_rpc_result = -1;
452
453 m_dtCachedAcctMgrInfoTimestamp = wxDateTime((time_t)0);
454 m_iAcct_mgr_info_rpc_result = -1;
455
456 m_dtLasAsyncRPCDlgTime = wxDateTime((time_t)0);
457 m_dtLastFrameViewRefreshRPCTime = wxDateTime((time_t)0);
458
459 status.max_event_log_lines = 0;
460 }
461
462
~CMainDocument()463 CMainDocument::~CMainDocument() {
464 KillAllRunningGraphicsApps();
465 #ifdef __WIN32__
466 WSACleanup();
467 #endif
468 }
469
470
OnInit()471 int CMainDocument::OnInit() {
472 int iRetVal = -1;
473
474 m_pNetworkConnection = new CNetworkConnection(this);
475 wxASSERT(m_pNetworkConnection);
476
477 m_pClientManager = new CBOINCClientManager();
478 wxASSERT(m_pClientManager);
479
480 m_RPCWaitDlg = NULL;
481 m_bWaitingForRPC = false;
482 m_bNeedRefresh = false;
483 m_bNeedTaskBarRefresh = false;
484 m_bRPCThreadIsReady = false;
485 m_bShutDownRPCThread = false;
486 current_rpc_request.clear();
487
488 m_pRPC_Thread_Mutex = new BOINC_Mutex();
489 wxASSERT(m_pRPC_Thread_Mutex);
490
491 m_pRPC_Thread_Condition = new BOINC_Condition(*m_pRPC_Thread_Mutex);
492 wxASSERT(m_pRPC_Thread_Condition);
493
494 m_pRPC_Request_Mutex = new BOINC_Mutex();
495 wxASSERT(m_pRPC_Request_Mutex);
496
497 m_pRPC_Request_Condition = new BOINC_Condition(*m_pRPC_Request_Mutex);
498 wxASSERT(m_pRPC_Request_Condition);
499
500 m_RPCThread = new RPCThread(this,
501 m_pRPC_Thread_Mutex,
502 m_pRPC_Thread_Condition,
503 m_pRPC_Request_Mutex,
504 m_pRPC_Request_Condition
505 );
506 wxASSERT(m_RPCThread);
507
508 iRetVal = m_RPCThread->Create();
509 wxASSERT(!iRetVal);
510
511 m_RPCThread->Run();
512 for (int i=0; i<100; i++) {
513 if (!m_bRPCThreadIsReady) {
514 boinc_sleep(0.01); // Allow RPC thread to initialize itself
515 }
516 }
517
518 return iRetVal;
519 }
520
521
OnExit()522 int CMainDocument::OnExit() {
523 int iRetVal = 0;
524
525 if (m_pClientManager) {
526 if (wxGetApp().ShouldShutdownCoreClient()) {
527 // Shut down only local clients on Manager exit
528 if (!wxGetApp().IsMgrMultipleInstance()) {
529 m_pClientManager->ShutdownBOINCCore(true);
530 }
531 }
532
533 delete m_pClientManager;
534 m_pClientManager = NULL;
535 }
536
537 if (m_RPCThread) {
538 KillRPCThread();
539 m_RPCThread = NULL;
540 }
541
542 delete m_pRPC_Thread_Mutex;
543 m_pRPC_Thread_Mutex = NULL;
544
545 delete m_pRPC_Thread_Condition;
546 m_pRPC_Thread_Condition = NULL;
547
548 delete m_pRPC_Request_Mutex;
549 m_pRPC_Request_Mutex = NULL;
550
551 delete m_pRPC_Request_Condition;
552 m_pRPC_Request_Condition = NULL;
553
554 rpcClient.close();
555
556 if (m_pNetworkConnection) {
557 delete m_pNetworkConnection;
558 m_pNetworkConnection = NULL;
559 }
560
561 return iRetVal;
562 }
563
564
OnPoll()565 int CMainDocument::OnPoll() {
566 CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
567 int iRetVal = 0;
568 wxString hostName = wxGetApp().GetClientHostNameArg();
569 wxString password = wxGetApp().GetClientPasswordArg();
570 int portNum = wxGetApp().GetClientRPCPortArg();
571
572 wxASSERT(wxDynamicCast(m_pClientManager, CBOINCClientManager));
573 wxASSERT(wxDynamicCast(m_pNetworkConnection, CNetworkConnection));
574
575 if (!m_bClientStartCheckCompleted && pFrame) {
576 m_bClientStartCheckCompleted = true;
577
578 if (IsComputerNameLocal(hostName)) {
579 if (wxGetApp().IsAnotherInstanceRunning()) {
580 if (!pFrame->SelectComputer(hostName, portNum, password, true)) {
581 s_bSkipExitConfirmation = true;
582 wxCommandEvent event;
583 pFrame->OnExit(event); // Exit if Select Computer dialog cancelled
584 }
585 }
586 }
587
588 if (wxGetApp().GetNeedRunDaemon()) {
589 if (IsComputerNameLocal(hostName)) {
590 if (m_pClientManager->StartupBOINCCore()) {
591 Connect(wxT("localhost"), portNum, password, TRUE, TRUE);
592 } else {
593 m_pNetworkConnection->ForceDisconnect();
594 pFrame->ShowDaemonStartFailedAlert();
595 }
596 } else {
597 Connect(hostName, portNum, password, TRUE, password.IsEmpty());
598 }
599 }
600 }
601
602 // Check connection state, connect if needed.
603 m_pNetworkConnection->Poll();
604
605 // Every 10 seconds, kill any running graphics apps
606 // whose associated worker tasks are no longer running
607 wxTimeSpan ts(wxDateTime::Now() - m_dtKillInactiveGfxTimestamp);
608 if (ts.GetSeconds() > 10) {
609 m_dtKillInactiveGfxTimestamp = wxDateTime::Now();
610 KillInactiveGraphicsApps();
611 }
612
613 return iRetVal;
614 }
615
616
OnRefreshState()617 int CMainDocument::OnRefreshState() {
618 if (IsConnected()) {
619 CachedStateUpdate();
620 }
621 return 0;
622 }
623
624
CachedStateUpdate()625 int CMainDocument::CachedStateUpdate() {
626 // Most of this is now handled by RunPeriodicRPCs() and ForceCacheUpdate()
627 int retval = 0;
628
629 if (m_iGet_state_rpc_result) retval = m_iGet_state_rpc_result;
630
631 if (retval) m_pNetworkConnection->SetStateDisconnected();
632
633 return retval;
634 }
635
636
ResetState()637 int CMainDocument::ResetState() {
638 rpcClient.close();
639 state.clear();
640 results.clear();
641 ft.clear();
642 statistics_status.clear();
643 disk_usage.clear();
644 proxy_info.clear();
645
646 ForceCacheUpdate();
647 return 0;
648 }
649
650
Connect(const wxString & szComputer,int iPort,const wxString & szComputerPassword,const bool bDisconnect,const bool bUseDefaultPassword)651 int CMainDocument::Connect(const wxString& szComputer, int iPort, const wxString& szComputerPassword, const bool bDisconnect, const bool bUseDefaultPassword) {
652 if (IsComputerNameLocal(szComputer)) {
653 // Restart client if not already running
654 m_pClientManager->AutoRestart();
655 }
656
657 if (bDisconnect) {
658 m_pNetworkConnection->ForceReconnect();
659 }
660
661 m_pNetworkConnection->SetComputer(szComputer, iPort, szComputerPassword, bUseDefaultPassword);
662 m_pNetworkConnection->FireReconnectEvent();
663
664 ResetMessageState();
665 ResetNoticeState();
666 return 0;
667 }
668
669
Reconnect()670 int CMainDocument::Reconnect() {
671 m_pNetworkConnection->ForceReconnect();
672 m_pNetworkConnection->FireReconnectEvent();
673 ResetMessageState();
674 ResetNoticeState();
675 return 0;
676 }
677
678
GetConnectedComputerName(wxString & strMachine)679 int CMainDocument::GetConnectedComputerName(wxString& strMachine) {
680 m_pNetworkConnection->GetConnectedComputerName(strMachine);
681 return 0;
682 }
683
684
GetConnectedComputerVersion(wxString & strVersion)685 int CMainDocument::GetConnectedComputerVersion(wxString& strVersion) {
686 m_pNetworkConnection->GetConnectedComputerVersion(strVersion);
687 return 0;
688 }
689
690
GetConnectingComputerName(wxString & strMachine)691 int CMainDocument::GetConnectingComputerName(wxString& strMachine) {
692 m_pNetworkConnection->GetConnectingComputerName(strMachine);
693 return 0;
694 }
695
696
IsComputerNameLocal(const wxString & strMachine)697 bool CMainDocument::IsComputerNameLocal(const wxString& strMachine) {
698 return m_pNetworkConnection->IsComputerNameLocal(strMachine);
699 }
700
701
IsConnected()702 bool CMainDocument::IsConnected() {
703 return m_pNetworkConnection->IsConnected();
704 }
705
706
IsReconnecting()707 bool CMainDocument::IsReconnecting() {
708 return m_pNetworkConnection->IsReconnecting();
709 }
710
711
ForceDisconnect()712 void CMainDocument::ForceDisconnect() {
713 return m_pNetworkConnection->ForceDisconnect();
714 }
715
716
FrameShutdownDetected()717 int CMainDocument::FrameShutdownDetected() {
718 return m_pNetworkConnection->FrameShutdownDetected();
719 }
720
721
722 // It is _not_ enough to just reset m_dtCachedCCStatusTimestamp
723 // and let RunPeriodicRPCs() update the state for these routines
724 // (which need immediate results):
725 // CMainDocument::SetActivityRunMode()
726 // CMainDocument::SetNetworkRunMode()
727 // Otherwise the Snooze task bar menu item and SimpleGUI Pause
728 // button do not work properly.
729 //
GetCoreClientStatus(CC_STATUS & ccs,bool bForce)730 int CMainDocument::GetCoreClientStatus(CC_STATUS& ccs, bool bForce) {
731 wxString strMachine = wxEmptyString;
732 int iRetVal = 0;
733
734 if (IsConnected()) {
735 if (!m_bWaitingForRPC) { // Prevent recursive entry of RequestRPC()
736 #if USE_CACHE_TIMEOUTS
737 wxTimeSpan ts(wxDateTime::Now() - m_dtCachedCCStatusTimestamp);
738 if (ts.GetSeconds() >= (10 * CCSTATUS_RPC_INTERVAL)) bForce = true;
739 #endif
740 if (m_dtCachedCCStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) bForce = true;
741 }
742 if (bForce) {
743 m_dtCachedCCStatusTimestamp = wxDateTime::Now();
744
745 m_iGet_status_rpc_result = rpc.get_cc_status(ccs);
746 if (0 == iRetVal) {
747 status = ccs;
748 } else {
749 iRetVal = m_iGet_status_rpc_result;
750 }
751 } else {
752 ccs = status;
753 iRetVal = m_iGet_status_rpc_result;
754 }
755
756 if (m_iGet_status_rpc_result) {
757 m_pNetworkConnection->SetStateDisconnected();
758 } else {
759 if (ccs.manager_must_quit) {
760 GetConnectedComputerName(strMachine);
761 if (IsComputerNameLocal(strMachine)) {
762 CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
763 wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
764 pFrame->Close(true);
765 }
766 }
767 }
768 } else {
769 iRetVal = -1;
770 }
771
772 return iRetVal;
773 }
774
775
SetActivityRunMode(int iMode,int iTimeout)776 int CMainDocument::SetActivityRunMode(int iMode, int iTimeout) {
777 int iRetVal = 0;
778 CC_STATUS ccs;
779
780 if (IsConnected()) {
781 iRetVal = rpc.set_run_mode(iMode, iTimeout);
782 if (0 == iRetVal) {
783 if (RUN_MODE_RESTORE == iMode) {
784 GetCoreClientStatus(ccs, true);
785 } else {
786 status.task_mode = iMode;
787 }
788 }
789 }
790
791 return iRetVal;
792 }
793
794
SetGPURunMode(int iMode,int iTimeout)795 int CMainDocument::SetGPURunMode(int iMode, int iTimeout) {
796 CC_STATUS ccs;
797
798 if (IsConnected()) {
799 int retval = rpc.set_gpu_mode(iMode, iTimeout);
800 if (retval) return retval;
801 if (iMode == RUN_MODE_RESTORE) {
802 GetCoreClientStatus(ccs, true);
803 } else {
804 status.gpu_mode = iMode;
805 }
806 }
807 return 0;
808 }
809
SetNetworkRunMode(int iMode,int iTimeout)810 int CMainDocument::SetNetworkRunMode(int iMode, int iTimeout) {
811 int iRetVal = 0;
812 CC_STATUS ccs;
813
814 if (IsConnected()) {
815 iRetVal = rpc.set_network_mode(iMode, iTimeout);
816 if (0 == iRetVal) {
817 if (RUN_MODE_RESTORE == iMode) {
818 GetCoreClientStatus(ccs, true);
819 } else {
820 status.network_mode = iMode;
821 }
822 }
823 }
824
825 return iRetVal;
826 }
827
828
829 // We use 0 to indicate that the RPC has never been called yet, so
830 // set last update time to (time_t)1 here rather than to (time_t)0,
831 // and only if it is currently not zero.
RefreshRPCs(bool fullReset)832 void CMainDocument::RefreshRPCs(bool fullReset) {
833 wxDateTime t = fullReset ? wxDateTime((time_t)0) : wxDateTime((time_t)1);
834
835 if (!m_dtCachedCCStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
836 m_dtCachedCCStatusTimestamp = t;
837 // m_iGet_status_rpc_result = -1;
838 }
839
840 if (!m_dtProjectsStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
841 m_dtProjectsStatusTimestamp = t;
842 // m_iGet_project_status1_rpc_result = -1;
843 }
844
845 if (!m_dtResultsTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
846 m_dtResultsTimestamp = t;
847 // m_iGet_results_rpc_result = -1;
848 }
849 m_fResultsRPCExecutionTime = 0;
850
851 if (!m_dtFileTransfersTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
852 m_dtFileTransfersTimestamp = t;
853 // m_iGet_file_transfers_rpc_result = 0;
854 }
855
856 // m_iGet_messages_rpc_result = -1;
857
858 if (!m_dtDiskUsageTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
859 m_dtDiskUsageTimestamp = t;
860 // m_iGet_dsk_usage_rpc_result = -1;
861 }
862
863 if (!m_dtStatisticsStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
864 m_dtStatisticsStatusTimestamp = t;
865 // m_iGet_statistics_rpc_result = -1;
866 }
867
868 if (!m_dtCachedSimpleGUITimestamp.IsEqualTo(wxDateTime((time_t)0))) {
869 m_dtCachedSimpleGUITimestamp = t;
870 // m_iGet_simple_gui2_rpc_result = -1;
871 }
872
873 if (!m_dtCachedAcctMgrInfoTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
874 m_dtCachedAcctMgrInfoTimestamp = t;
875 m_iAcct_mgr_info_rpc_result = -1;
876 }
877
878 // m_iGet_state_rpc_result = -1;
879 }
880
881
RunPeriodicRPCs(int frameRefreshRate)882 void CMainDocument::RunPeriodicRPCs(int frameRefreshRate) {
883 ASYNC_RPC_REQUEST request;
884 wxTimeSpan ts;
885
886 // Timer events are handled while the RPC Wait dialog is shown
887 // which may cause unintended recursion and repeatedly posting
888 // the same RPC requests from timer routines.
889 if (WaitingForRPC()) return;
890
891 CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
892 if (!pFrame) return;
893
894 int currentTabView = pFrame->GetCurrentViewPage();
895
896 // If the client is heavily loaded (e.g, very many tasks), the
897 // RPC Wait dialog could appear continuously. To prevent this,
898 // delay periodic RPCs for 1 second after the dialog closes.
899 wxDateTime dtNow(wxDateTime::Now());
900 if ((currentTabView & (VW_STAT | VW_DISK)) == 0) {
901 ts = dtNow - m_dtLasAsyncRPCDlgTime;
902 if (ts.GetSeconds()<= DELAYAFTERASYNCRPC_DLG) {
903 return;
904 }
905 }
906
907 wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
908
909 if (!IsConnected()) {
910 CFrameEvent event(wxEVT_FRAME_REFRESHVIEW, pFrame);
911 pFrame->GetEventHandler()->AddPendingEvent(event);
912 CTaskBarIcon* pTaskbar = wxGetApp().GetTaskBarIcon();
913 if (pTaskbar) {
914 CTaskbarEvent event(wxEVT_TASKBAR_REFRESH, pTaskbar);
915 pTaskbar->AddPendingEvent(event);
916 }
917 CDlgEventLog* eventLog = wxGetApp().GetEventLog();
918 if (eventLog) {
919 eventLog->OnRefresh();
920 }
921 return;
922 }
923
924 // Several functions (such as Abort, Reset, Detach) display an
925 // "Are you sure?" dialog before passing a pointer to a result
926 // or project in a demand RPC call. If Periodic RPCs continue
927 // to run during these dialogs, that pointer may no longer be
928 // valid by the time the demand RPC is executed. So we suspend
929 // periodic RPCs during certain modal dialogs.
930 //
931 // Note that this depends on using wxGetApp().SafeMessageBox()
932 // instead of wxMessageBox in all tab views and anywhere else
933 // where a periodic RPC could cause a similar problem.
934 if (wxGetApp().IsSafeMesageBoxDisplayed()) {
935 return;
936 }
937
938 // SET_LANGUAGE
939
940 static bool first = true;
941 if (first) {
942 first = false;
943 safe_strcpy(m_szLanguage, wxGetApp().GetISOLanguageCode().mb_str());
944 request.clear();
945 request.which_rpc = RPC_SET_LANGUAGE;
946 request.arg1 = (void*)(const char*)&m_szLanguage;
947 request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH;
948 RequestRPC(request);
949 }
950
951 // *********** RPC_GET_CC_STATUS **************
952
953 ts = dtNow - m_dtCachedCCStatusTimestamp;
954 if (ts.GetSeconds() >= CCSTATUS_RPC_INTERVAL) {
955 request.clear();
956 request.which_rpc = RPC_GET_CC_STATUS;
957 request.arg1 = &async_status_buf;
958 request.exchangeBuf = &status;
959 request.rpcType = RPC_TYPE_ASYNC_WITH_UPDATE_TASKBAR_ICON_AFTER;
960 request.completionTime = &m_dtCachedCCStatusTimestamp;
961 request.resultPtr = &m_iGet_status_rpc_result;
962
963 RequestRPC(request);
964 }
965
966 // *********** RPC_GET_MESSAGES **************
967
968 // We must keep getting messages even if the Event Log is not open
969 // due to the limited size of the client's buffer, or some may be
970 // lost, causing gaps when the Event Log is later opened.
971 //
972 request.clear();
973 request.which_rpc = RPC_GET_MESSAGES;
974 // m_iLastMessageSequenceNumber could change between request and execution
975 // of RPC, so pass in a pointer rather than its value
976 request.arg1 = &m_iLastMessageSequenceNumber;
977 request.arg2 = &messages;
978 static bool _true = true;
979 request.arg3 = &_true;
980 request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_EVENT_LOG_AFTER;
981 request.completionTime = NULL;
982 request.resultPtr = &m_iGet_messages_rpc_result;
983
984 RequestRPC(request);
985
986 // *********** RPC_GET_NOTICES **************
987
988 // We must keep getting notices even if the Notices Tab is not open
989 // so we can notify the user when new notices become available.
990 ts = dtNow - m_dtNoticesTimeStamp;
991 if ((currentTabView & VW_NOTIF) ||
992 (ts.GetSeconds() >= NOTICESBACKGROUNDRPC_INTERVAL)) {
993 // Don't request another get_notices RPC until we have
994 // updated m_iNoticeSequenceNumber from the previous
995 // one; otherwise we will get duplicate notices
996 if (!m_bWaitingForGetNoticesRPC) {
997 m_bWaitingForGetNoticesRPC = true;
998 request.clear();
999 request.which_rpc = RPC_GET_NOTICES;
1000 // m_iNoticeSequenceNumber could change between request and execution
1001 // of RPC, so pass in a pointer rather than its value
1002 request.arg1 = &m_iNoticeSequenceNumber;
1003 request.arg2 = ¬ices;
1004 request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1005 if (!pFrame->IsShown()
1006 #ifdef __WXMAC__
1007 || (!wxGetApp().IsApplicationVisible())
1008 #endif
1009 ) {
1010 request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH;
1011 }
1012
1013 request.completionTime = &m_dtNoticesTimeStamp;
1014 request.resultPtr = &m_iGet_notices_rpc_result;
1015
1016 RequestRPC(request);
1017 }
1018 }
1019
1020 ts = dtNow - m_dtCachedStateTimestamp;
1021 if (ts.GetSeconds() >= STATERPC_INTERVAL) {
1022
1023 // *********** RPC_GET_STATE **************
1024
1025 request.clear();
1026 request.which_rpc = RPC_GET_STATE;
1027 request.arg1 = &async_state_buf;
1028 request.exchangeBuf = &state;
1029 request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH;
1030 request.completionTime = &m_dtCachedStateTimestamp;
1031 request.resultPtr = &m_iGet_state_rpc_result;
1032
1033 RequestRPC(request);
1034 }
1035
1036 // **** All periodic RPCs after this point are used only
1037 // **** when refreshing Advanced Frame or Simple Frame views.
1038 // **** If the Event Log is shown, the Periodic RPC Timer is
1039 // **** set for 1 second even though the Frame View may need
1040 // **** less frequent update.
1041 // **** The argument frameRefreshRate is 0 if an immediate
1042 // **** update is needed due to some user action, etc.
1043 // **** Otherwise frameRefreshRate is the rate at which the
1044 // **** the current Frame View should be updated.
1045 ts = dtNow - m_dtLastFrameViewRefreshRPCTime;
1046 if (ts.GetMilliseconds() < (frameRefreshRate - 500)) return;
1047
1048 // Don't do periodic RPC calls when hidden / minimized
1049 if (!pFrame->IsShown()) return;
1050 #ifdef __WXMAC__
1051 if (!wxGetApp().IsApplicationVisible()) return;
1052 #endif
1053
1054 m_dtLastFrameViewRefreshRPCTime = dtNow;
1055
1056 // *********** RPC_GET_PROJECT_STATUS1 **************
1057
1058 if (currentTabView & VW_PROJ) {
1059 ts = dtNow - m_dtProjectsStatusTimestamp;
1060 if (ts.GetSeconds() >= PROJECTSTATUSRPC_INTERVAL) {
1061 request.clear();
1062 request.which_rpc = RPC_GET_PROJECT_STATUS1;
1063 request.arg1 = &async_projects_update_buf;
1064 request.arg2 = &state;
1065 request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1066 request.completionTime = &m_dtProjectsStatusTimestamp;
1067 request.resultPtr = &m_iGet_project_status1_rpc_result;
1068
1069 RequestRPC(request);
1070 }
1071 }
1072
1073 // *********** RPC_GET_RESULTS **************
1074
1075 if (currentTabView & VW_TASK) {
1076 ts = dtNow - m_dtResultsTimestamp;
1077 wxLongLong secondsSinceLastRPC = ts.GetSeconds();
1078 if (secondsSinceLastRPC >= RESULTSRPC_INTERVAL) {
1079 if (secondsSinceLastRPC >= (m_fResultsRPCExecutionTime * GET_RESULTS_FREQUENCY_FACTOR)) {
1080 request.clear();
1081 request.which_rpc = RPC_GET_RESULTS;
1082 request.arg1 = &async_results_buf;
1083 request.arg2 = &m_ActiveTasksOnly;
1084 request.exchangeBuf = &results;
1085 request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1086 request.completionTime = &m_dtResultsTimestamp;
1087 request.RPCExecutionTime = &m_fResultsRPCExecutionTime;
1088 request.resultPtr = &m_iGet_results_rpc_result;
1089
1090 RequestRPC(request);
1091 }
1092 }
1093 }
1094
1095 // *********** RPC_GET_FILE_TRANSFERS **************
1096
1097 if (currentTabView & VW_XFER) {
1098 ts = dtNow - m_dtFileTransfersTimestamp;
1099 if (ts.GetSeconds() >= FILETRANSFERSRPC_INTERVAL) {
1100 request.clear();
1101 request.which_rpc = RPC_GET_FILE_TRANSFERS;
1102 request.arg1 = &async_ft_buf;
1103 request.exchangeBuf = &ft;
1104 request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1105 request.completionTime = &m_dtFileTransfersTimestamp;
1106 request.resultPtr = &m_iGet_file_transfers_rpc_result;
1107
1108 RequestRPC(request);
1109 }
1110 }
1111
1112 // *********** RPC_GET_STATISTICS **************
1113
1114 if (currentTabView & VW_STAT) {
1115 ts = dtNow - m_dtStatisticsStatusTimestamp;
1116 if (ts.GetSeconds() >= STATISTICSSTATUSRPC_INTERVAL) {
1117 request.clear();
1118 request.which_rpc = RPC_GET_STATISTICS;
1119 request.arg1 = &async_statistics_status_buf;
1120 request.exchangeBuf = &statistics_status;
1121 request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1122 request.completionTime = &m_dtStatisticsStatusTimestamp;
1123 request.resultPtr = &m_iGet_statistics_rpc_result;
1124
1125 RequestRPC(request);
1126 }
1127 }
1128
1129 // *********** RPC_GET_DISK_USAGE **************
1130
1131 if (currentTabView & VW_DISK) {
1132 ts = dtNow - m_dtDiskUsageTimestamp;
1133 if ((ts.GetSeconds() >= DISKUSAGERPC_INTERVAL) || disk_usage.projects.empty()) {
1134 request.clear();
1135 request.which_rpc = RPC_GET_DISK_USAGE;
1136 request.arg1 = &async_disk_usage_buf;
1137 request.exchangeBuf = &disk_usage;
1138 request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1139 request.completionTime = &m_dtDiskUsageTimestamp;
1140 request.resultPtr = &m_iGet_dsk_usage_rpc_result;
1141
1142 RequestRPC(request);
1143 }
1144 }
1145
1146 // *********** GET_SIMPLE_GUI_INFO2 **************
1147 if (currentTabView & VW_SGUI) {
1148 ts = dtNow - m_dtCachedSimpleGUITimestamp;
1149 if (ts.GetSeconds() >= CACHEDSIMPLEGUIRPC_INTERVAL) {
1150 request.clear();
1151 request.which_rpc = RPC_GET_SIMPLE_GUI_INFO2;
1152 request.arg1 = &async_projects_update_buf;
1153 request.arg2 = &state;
1154 request.arg3 = &async_results_buf;
1155 request.arg4 = &m_ActiveTasksOnly;
1156 request.exchangeBuf = &results;
1157 request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1158 request.completionTime = &m_dtCachedSimpleGUITimestamp;
1159 request.resultPtr = &m_iGet_simple_gui2_rpc_result;
1160
1161 RequestRPC(request);
1162 }
1163 }
1164 // *********** RPC_ACCT_MGR_INFO **************
1165
1166 if (currentTabView & VW_SGUI) {
1167 ts = dtNow - m_dtCachedAcctMgrInfoTimestamp;
1168 if (ts.GetSeconds() >= CACHEDACCTMGRINFORPC_INTERVAL) {
1169 request.clear();
1170 request.which_rpc = RPC_ACCT_MGR_INFO;
1171 request.arg1 = &async_ami_buf;
1172 request.exchangeBuf = &ami;
1173 request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH;
1174 request.completionTime = &m_dtCachedAcctMgrInfoTimestamp;
1175 request.resultPtr = &m_iAcct_mgr_info_rpc_result;
1176
1177 RequestRPC(request);
1178 }
1179 }
1180 }
1181
1182
1183 // TODO: Is it enough to just reset m_dtCachedStateTimestamp
1184 // and let RunPeriodicRPCs() update the state? This would avoid
1185 // displaying the "Please wait" dialog on multi-processor computers.
1186 // Possible exceptions might be when ForceCacheUpdate() is called
1187 // from these routines (which may need immediate results):
1188 // CAdvancedFrame::OnConnect()
1189 // CDlgItemProperties::FormatApplicationName()
1190 // WorkunitNotebook::AddTab()
1191 // CMainDocument::CachedProjectStatusUpdate()
1192 // CMainDocument::CachedSimpleGUIUpdate()
1193 //
1194 // Currently, no calls to ForceCacheUpdate pass false as the arg.
1195 //
ForceCacheUpdate(bool immediate)1196 int CMainDocument::ForceCacheUpdate(bool immediate) {
1197 wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::ForceCacheUpdate - Function Begin"));
1198
1199 if (!immediate) {
1200 m_dtCachedStateTimestamp = wxDateTime((time_t)0);
1201 return m_iGet_state_rpc_result;
1202 }
1203
1204 int retval = 0;
1205
1206 if (IsConnected()) {
1207 m_dtCachedStateTimestamp = wxDateTime::Now();
1208 m_iGet_state_rpc_result = rpc.get_state(state);
1209 if (m_iGet_state_rpc_result) {
1210 retval = m_iGet_state_rpc_result;
1211 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::ForceCacheUpdate - Get State Failed '%d'"), retval);
1212 m_pNetworkConnection->SetStateDisconnected();
1213 }
1214
1215 } else {
1216 retval = -1;
1217 }
1218
1219 wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::ForceCacheUpdate - Function End"));
1220 return retval;
1221 }
1222
1223
RunBenchmarks()1224 int CMainDocument::RunBenchmarks() {
1225 return rpc.run_benchmarks();
1226 }
1227
1228
CoreClientQuit()1229 int CMainDocument::CoreClientQuit() {
1230 return rpc.quit();
1231 }
1232
1233
IsUserAuthorized()1234 bool CMainDocument::IsUserAuthorized() {
1235 #ifndef _WIN32
1236 #ifdef SANDBOX
1237 #ifndef __WXMAC__ // Currently unauthorized users can't run Manager, so this would be redundant
1238 static bool sIsAuthorized = false;
1239 group *grp;
1240 gid_t rgid, boinc_master_gid;
1241 char *userName, *groupMember;
1242 int i;
1243
1244 if (g_use_sandbox) {
1245 if (sIsAuthorized)
1246 return true; // We already checked and OK'd current user
1247
1248 grp = getgrnam(BOINC_MASTER_GROUP_NAME);
1249 if (grp) {
1250 boinc_master_gid = grp->gr_gid;
1251
1252 rgid = getgid();
1253 if (rgid == boinc_master_gid) {
1254 sIsAuthorized = true; // User's primary group is boinc_master
1255 return true;
1256 }
1257
1258 userName = getlogin();
1259 if (userName) {
1260 for (i=0; ; i++) { // Step through all users in group boinc_master
1261 groupMember = grp->gr_mem[i];
1262 if (groupMember == NULL)
1263 break; // User is not a member of group boinc_master
1264 if (strcmp(userName, groupMember) == 0) {
1265 sIsAuthorized = true; // User is a member of group boinc_master
1266 return true;
1267 }
1268 } // for (i)
1269 } // if (userName)
1270 } // if grp
1271
1272 #ifdef __WXMAC__
1273 if (Mac_Authorize()) { // Run Mac Authentication dialog
1274 sIsAuthorized = true; // Authenticated by password
1275 return true;
1276 }
1277 #endif // __WXMAC__
1278
1279 return false;
1280
1281 } // if (g_use_sandbox)
1282 #endif // SANDBOX
1283 #endif // #ifndef _WIN32
1284 #endif // #ifndef __WXMAC__
1285
1286 return true;
1287 }
1288
CheckForVersionUpdate(bool showMessage)1289 void CMainDocument::CheckForVersionUpdate(bool showMessage) {
1290 std::string version, url;
1291 wxString message, title;
1292 title.Printf(_("Version Update"));
1293 wxString applicationName = wxGetApp().GetSkinManager()->GetAdvanced()->GetApplicationName();
1294 if (IsConnected()) {
1295 rpc.get_newer_version(version, url);
1296
1297 if (!showMessage)
1298 return;
1299
1300 if (!version.empty() && !url.empty()) {
1301 message.Printf(_("A new version of %s is available. You can download it here: %s"), applicationName, url);
1302 }
1303 else {
1304 message.Printf(_("There is no new version of %s available for download."), applicationName);
1305 }
1306 }
1307 else {
1308 message.Printf(_("%s is not connected to the client"), applicationName);
1309 }
1310 if (showMessage) {
1311 wxGetApp().SafeMessageBox(message, title);
1312 }
1313 }
1314
CachedProjectStatusUpdate(bool bForce)1315 int CMainDocument::CachedProjectStatusUpdate(bool bForce) {
1316 int i = 0;
1317
1318 if (! IsConnected()) return -1;
1319
1320 #if USE_CACHE_TIMEOUTS
1321 wxTimeSpan ts(wxDateTime::Now() - m_dtProjecStatusTimestamp);
1322 if (ts.GetSeconds() >= (2 * PROJECTSTATUSRPC_INTERVAL)) bForce = true;
1323 #endif
1324 if (m_dtProjectsStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) bForce = true;
1325
1326 if (bForce) {
1327 m_dtProjectsStatusTimestamp = wxDateTime::Now();
1328 m_iGet_project_status1_rpc_result = rpc.get_project_status(async_projects_update_buf, state);
1329 }
1330
1331 if (m_iGet_project_status1_rpc_result) {
1332 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedProjectStatusUpdate - Get Project Status Failed '%d'"), m_iGet_project_status1_rpc_result);
1333 ForceCacheUpdate();
1334 // return m_iGet_project_status1_rpc_result;
1335 }
1336
1337 m_fProjectTotalResourceShare = 0.0;
1338 for (i=0; i < (long)state.projects.size(); i++) {
1339 m_fProjectTotalResourceShare += state.projects.at(i)->resource_share;
1340 }
1341 if (!m_fProjectTotalResourceShare) m_fProjectTotalResourceShare = 1;
1342
1343 return m_iGet_project_status1_rpc_result;
1344 }
1345
1346
project(unsigned int i)1347 PROJECT* CMainDocument::project(unsigned int i) {
1348 PROJECT* pProject = NULL;
1349
1350 try {
1351 if (!state.projects.empty())
1352 pProject = state.projects.at(i);
1353 }
1354 catch (std::out_of_range e) {
1355 pProject = NULL;
1356 }
1357
1358 return pProject;
1359 }
1360
1361
project(char * url)1362 PROJECT* CMainDocument::project(char* url) {
1363 for (unsigned int i=0; i< state.projects.size(); i++) {
1364 PROJECT* tp = state.projects[i];
1365 if (!strcmp(url, tp->master_url)) return tp;
1366 }
1367 return NULL;
1368 }
1369
1370
GetProjectCount()1371 int CMainDocument::GetProjectCount() {
1372 int iCount = -1;
1373
1374 CachedProjectStatusUpdate();
1375 CachedStateUpdate();
1376
1377 if (!state.projects.empty())
1378 iCount = (int)state.projects.size();
1379
1380 return iCount;
1381 }
1382
1383
ProjectDetach(int iIndex)1384 int CMainDocument::ProjectDetach(int iIndex) {
1385 PROJECT* pProject = NULL;
1386 int iRetVal = -1;
1387
1388 pProject = project(iIndex);
1389
1390 if (pProject)
1391 iRetVal = rpc.project_op((*pProject), "detach");
1392
1393 return iRetVal;
1394 }
1395
ProjectUpdate(int iIndex)1396 int CMainDocument::ProjectUpdate(int iIndex) {
1397 PROJECT* pProject = NULL;
1398 int iRetVal = -1;
1399
1400 pProject = project(iIndex);
1401
1402 if (pProject)
1403 iRetVal = rpc.project_op((*pProject), "update");
1404
1405 return iRetVal;
1406 }
1407
ProjectReset(int iIndex)1408 int CMainDocument::ProjectReset(int iIndex) {
1409 PROJECT* pProject = NULL;
1410 int iRetVal = -1;
1411
1412 pProject = project(iIndex);
1413
1414 if (pProject)
1415 iRetVal = rpc.project_op((*pProject), "reset");
1416
1417 return iRetVal;
1418 }
1419
ProjectSuspend(int iIndex)1420 int CMainDocument::ProjectSuspend(int iIndex) {
1421 PROJECT* pProject = NULL;
1422 int iRetVal = -1;
1423
1424 pProject = project(iIndex);
1425
1426 if (pProject)
1427 iRetVal = rpc.project_op((*pProject), "suspend");
1428
1429 return iRetVal;
1430 }
1431
ProjectResume(int iIndex)1432 int CMainDocument::ProjectResume(int iIndex) {
1433 PROJECT* pProject = NULL;
1434 int iRetVal = -1;
1435
1436 pProject = project(iIndex);
1437
1438 if (pProject)
1439 iRetVal = rpc.project_op((*pProject), "resume");
1440
1441 return iRetVal;
1442 }
1443
ProjectNoMoreWork(int iIndex)1444 int CMainDocument::ProjectNoMoreWork(int iIndex) {
1445 PROJECT* pProject = NULL;
1446 int iRetVal = -1;
1447
1448 pProject = project(iIndex);
1449
1450 if (pProject)
1451 iRetVal = rpc.project_op((*pProject), "nomorework");
1452
1453 return iRetVal;
1454 }
1455
ProjectAllowMoreWork(int iIndex)1456 int CMainDocument::ProjectAllowMoreWork(int iIndex) {
1457 PROJECT* pProject = NULL;
1458 int iRetVal = -1;
1459
1460 pProject = project(iIndex);
1461
1462 if (pProject)
1463 iRetVal = rpc.project_op((*pProject), "allowmorework");
1464
1465 return iRetVal;
1466 }
1467
CachedResultsStatusUpdate()1468 int CMainDocument::CachedResultsStatusUpdate() {
1469 if (! IsConnected()) return -1;
1470 bool active_tasks_only = false;
1471 bool immediate = false;
1472
1473 CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
1474 if (pFrame) {
1475 wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
1476
1477 if (pFrame->GetCurrentViewPage() & VW_TASK) {
1478 active_tasks_only = m_ActiveTasksOnly;
1479 }
1480 }
1481
1482 #if USE_CACHE_TIMEOUTS
1483 wxTimeSpan ts(wxDateTime::Now() - m_dtResultsTimestamp);
1484 if (ts.GetSeconds() >= (2 * RESULTSRPC_INTERVAL)) immediate = true;
1485 #endif
1486 if (m_dtResultsTimestamp.IsEqualTo(wxDateTime((time_t)0))) immediate = true;
1487
1488 if (immediate) {
1489 m_dtResultsTimestamp = wxDateTime::Now();
1490 m_iGet_results_rpc_result = rpc.get_results(results, active_tasks_only);
1491 }
1492
1493 if (m_iGet_results_rpc_result) {
1494 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedResultsStatusUpdate - Get Result Status Failed '%d'"), m_iGet_results_rpc_result);
1495 ForceCacheUpdate();
1496 }
1497
1498 return m_iGet_results_rpc_result;
1499 }
1500
1501
result(unsigned int i)1502 RESULT* CMainDocument::result(unsigned int i) {
1503 RESULT* pResult = NULL;
1504
1505 try {
1506 if (!results.results.empty())
1507 pResult = results.results.at(i);
1508 }
1509 catch (std::out_of_range e) {
1510 pResult = NULL;
1511 }
1512
1513 return pResult;
1514 }
1515
1516 // get the result not by index, but by name
1517 //
result(const wxString & name,const wxString & project_url)1518 RESULT* CMainDocument::result(const wxString& name, const wxString& project_url) {
1519 RESULT* pResult = NULL;
1520
1521 try {
1522 if (!results.results.empty()) {
1523 //iterating over the vector and find the right result
1524 for(unsigned int i=0; i< results.results.size();i++) {
1525 RESULT* tResult = results.results.at(i);
1526 wxString resname(tResult->name, wxConvUTF8);
1527 if(resname.IsSameAs(name)){
1528 wxString resurl(tResult->project_url, wxConvUTF8);
1529 if(resurl.IsSameAs(project_url)){
1530 pResult = tResult;
1531 break;
1532 }
1533 }
1534 }
1535 }
1536 }
1537 catch (std::out_of_range e) {
1538 pResult = NULL;
1539 }
1540
1541 return pResult;
1542 }
1543
GetWorkCount()1544 int CMainDocument::GetWorkCount() {
1545 int iCount = -1;
1546
1547 CachedResultsStatusUpdate();
1548 CachedStateUpdate();
1549
1550 if (!results.results.empty())
1551 iCount = (int)results.results.size();
1552
1553 return iCount;
1554 }
1555
1556
WorkSuspend(char * url,char * name)1557 int CMainDocument::WorkSuspend(char* url, char* name) {
1558 int iRetVal = 0;
1559
1560 RESULT* pStateResult = state.lookup_result(url, name);
1561 if (pStateResult) {
1562 iRetVal = rpc.result_op((*pStateResult), "suspend");
1563 } else {
1564 ForceCacheUpdate();
1565 }
1566
1567 return iRetVal;
1568 }
1569
1570
WorkResume(char * url,char * name)1571 int CMainDocument::WorkResume(char* url, char* name) {
1572 int iRetVal = 0;
1573
1574 RESULT* pStateResult = state.lookup_result(url, name);
1575 if (pStateResult) {
1576 iRetVal = rpc.result_op((*pStateResult), "resume");
1577 } else {
1578 ForceCacheUpdate();
1579 }
1580
1581 return iRetVal;
1582 }
1583
1584
1585 // If the graphics application for the current task is already
1586 // running, return a pointer to its RUNNING_GFX_APP struct.
1587 //
GetRunningGraphicsApp(RESULT * rp,int slot)1588 RUNNING_GFX_APP* CMainDocument::GetRunningGraphicsApp(
1589 RESULT* rp, int slot
1590 ) {
1591 bool exited = false;
1592 std::vector<RUNNING_GFX_APP>::iterator gfx_app_iter;
1593
1594 for( gfx_app_iter = m_running_gfx_apps.begin();
1595 gfx_app_iter != m_running_gfx_apps.end();
1596 ++gfx_app_iter
1597 ) {
1598 if ((slot >= 0) && ((*gfx_app_iter).slot != slot)) continue;
1599
1600 #ifdef _WIN32
1601 unsigned long exit_code;
1602 if (GetExitCodeProcess((*gfx_app_iter).pid, &exit_code)) {
1603 if (exit_code != STILL_ACTIVE) {
1604 exited = true;
1605 }
1606 }
1607 #else
1608 if (waitpid((*gfx_app_iter).pid, 0, WNOHANG) == (*gfx_app_iter).pid) {
1609 exited = true;
1610 }
1611 #endif
1612 if (! exited) {
1613 if ((rp->name == (*gfx_app_iter).name) &&
1614 (rp->project_url == (*gfx_app_iter).project_url)
1615 ) {
1616 return &(*gfx_app_iter);
1617 }
1618
1619 // Graphics app is still running but the slot now has a different task
1620 KillGraphicsApp((*gfx_app_iter).pid);
1621 }
1622
1623 // Either the graphics app had already exited or we just killed it
1624 (*gfx_app_iter).name.clear();
1625 (*gfx_app_iter).project_url.clear();
1626 m_running_gfx_apps.erase(gfx_app_iter);
1627 return NULL;
1628 }
1629 return NULL;
1630 }
1631
1632
1633 // Kill any running graphics apps whose worker tasks aren't running
KillInactiveGraphicsApps()1634 void CMainDocument::KillInactiveGraphicsApps() {
1635 /*
1636 std::vector<RUNNING_GFX_APP>::iterator gfx_app_iter;
1637 unsigned int i;
1638 bool bStillRunning;
1639
1640 if (m_running_gfx_apps.size() <= 0) return;
1641
1642 // If none of the Tasks displays are visible, we need to update
1643 // the results vector. This call does nothing if recently updated
1644 // by a call from CViewWork or CViewTabPage.
1645 CachedResultsStatusUpdate();
1646
1647 gfx_app_iter = m_running_gfx_apps.begin();
1648 while (gfx_app_iter != m_running_gfx_apps.end()) {
1649 bStillRunning = false;
1650
1651 for (i=0; i<results.results.size(); i++) {
1652 if ((results.results.at(i))->state != RESULT_FILES_DOWNLOADED) continue;
1653 if (!(results.results.at(i))->active_task) continue;
1654 if ((results.results.at(i))->scheduler_state != CPU_SCHED_SCHEDULED) continue;
1655 if ((results.results.at(i))->name != (*gfx_app_iter).name) continue;
1656 if ((results.results.at(i))->project_url != (*gfx_app_iter).project_url) continue;
1657 bStillRunning = true;
1658 break;
1659 }
1660
1661 if (!bStillRunning) {
1662 KillGraphicsApp((*gfx_app_iter).pid);
1663 gfx_app_iter = m_running_gfx_apps.erase(gfx_app_iter);
1664 } else {
1665 gfx_app_iter++;
1666 }
1667 }
1668 */
1669 }
1670
1671
KillAllRunningGraphicsApps()1672 void CMainDocument::KillAllRunningGraphicsApps()
1673 {
1674 size_t i, n;
1675 std::vector<RUNNING_GFX_APP>::iterator gfx_app_iter;
1676
1677 n = m_running_gfx_apps.size();
1678 for (i=0; i<n; i++) {
1679 gfx_app_iter = m_running_gfx_apps.begin();
1680 KillGraphicsApp((*gfx_app_iter).pid);
1681 (*gfx_app_iter).name.clear();
1682 (*gfx_app_iter).project_url.clear();
1683 m_running_gfx_apps.erase(gfx_app_iter);
1684 }
1685 }
1686
1687
1688 #ifdef _WIN32
KillGraphicsApp(HANDLE pid)1689 void CMainDocument::KillGraphicsApp(HANDLE pid) {
1690 kill_program(pid);
1691 }
1692 #else
KillGraphicsApp(int pid)1693 void CMainDocument::KillGraphicsApp(int pid) {
1694 char* argv[6];
1695 char currentDir[1024];
1696 char thePIDbuf[20];
1697 int id, iRetVal;
1698
1699
1700 if (g_use_sandbox) {
1701 snprintf(thePIDbuf, sizeof(thePIDbuf), "%d", pid);
1702 argv[0] = "switcher";
1703 argv[1] = "/bin/kill";
1704 argv[2] = "kill";
1705 argv[3] = "-KILL";
1706 argv[4] = thePIDbuf;
1707 argv[5] = 0;
1708
1709 iRetVal = run_program(
1710 getcwd(currentDir, sizeof(currentDir)),
1711 "./switcher/switcher",
1712 5,
1713 argv,
1714 0,
1715 id
1716 );
1717 } else {
1718 kill_program(pid);
1719 }
1720 }
1721 #endif
1722
WorkShowGraphics(RESULT * rp)1723 int CMainDocument::WorkShowGraphics(RESULT* rp) {
1724 int iRetVal = 0;
1725
1726 if (strlen(rp->web_graphics_url)) {
1727 wxString url(rp->web_graphics_url, wxConvUTF8);
1728 wxLaunchDefaultBrowser(url);
1729 return 0;
1730 }
1731 if (strlen(rp->graphics_exec_path)) {
1732 // V6 Graphics
1733 RUNNING_GFX_APP gfx_app;
1734 RUNNING_GFX_APP* previous_gfx_app;
1735 char *p;
1736 int slot;
1737 #ifdef __WXMSW__
1738 HANDLE id;
1739 #else
1740 int id;
1741 #endif
1742
1743 p = strrchr((char*)rp->slot_path, '/');
1744 if (!p) return ERR_INVALID_PARAM;
1745 slot = atoi(p+1);
1746
1747 // See if we are already running the graphics application for this task
1748 previous_gfx_app = GetRunningGraphicsApp(rp, slot);
1749
1750 #ifndef __WXMSW__
1751 char* argv[4];
1752
1753 if (previous_gfx_app) {
1754 #ifdef __WXMAC__
1755 // If this graphics app is already running,
1756 // just bring it to the front
1757 //
1758 BringAppWithPidToFront(previous_gfx_app->pid);
1759 #endif
1760 // If graphics app is already running, don't launch a second instance
1761 //
1762 return 0;
1763 }
1764 argv[0] = "switcher";
1765 // For unknown reasons on Macs, the graphics application
1766 // exits with "RegisterProcess failed (error = -50)" unless
1767 // we pass its full path twice in the argument list to execv.
1768 //
1769 argv[1] = (char *)rp->graphics_exec_path;
1770 argv[2] = (char *)rp->graphics_exec_path;
1771 argv[3] = 0;
1772
1773 if (g_use_sandbox) {
1774 iRetVal = run_program(
1775 rp->slot_path,
1776 "../../switcher/switcher",
1777 3,
1778 argv,
1779 0,
1780 id
1781 );
1782 } else {
1783 iRetVal = run_program(
1784 rp->slot_path,
1785 rp->graphics_exec_path,
1786 1,
1787 &argv[2],
1788 0,
1789 id
1790 );
1791 }
1792 #else
1793 char* argv[2];
1794
1795 // If graphics app is already running, don't launch a second instance
1796 //
1797 if (previous_gfx_app) return 0;
1798 argv[0] = 0;
1799
1800 iRetVal = run_program(
1801 rp->slot_path,
1802 rp->graphics_exec_path,
1803 0,
1804 argv,
1805 0,
1806 id
1807 );
1808 #endif
1809 if (!iRetVal) {
1810 gfx_app.slot = slot;
1811 gfx_app.project_url = rp->project_url;
1812 gfx_app.name = rp->name;
1813 gfx_app.pid = id;
1814 m_running_gfx_apps.push_back(gfx_app);
1815 }
1816 }
1817 return iRetVal;
1818 }
1819
1820
WorkShowVMConsole(RESULT * res)1821 int CMainDocument::WorkShowVMConsole(RESULT* res) {
1822 int iRetVal = 0;
1823
1824 if (strlen(res->remote_desktop_addr)) {
1825 wxString strConnection(res->remote_desktop_addr, wxConvUTF8);
1826 wxString strCommand;
1827
1828 #if defined(__WXMSW__)
1829 strCommand = wxT("mstsc.exe /v:") + strConnection;
1830 wxExecute(strCommand);
1831 #elif defined(__WXGTK__)
1832 strCommand = wxT("rdesktop-vrdp ") + strConnection;
1833 wxExecute(strCommand);
1834 #elif defined(__WXMAC__)
1835 OSStatus status = noErr;
1836 char pathToCoRD[MAXPATHLEN];
1837
1838 // I have found no reliable way to pass the IP address and port to Microsoft's
1839 // Remote Desktop Connection application for the Mac, so I'm using CoRD.
1840 // Unfortunately, CoRD does not seem as reliable as I would like either.
1841 //
1842 // First try to find the CoRD application by Bundle ID and Creator Code
1843 status = GetPathToAppFromID('RDC#', CFSTR("net.sf.cord"), pathToCoRD, MAXPATHLEN);
1844 if (status != noErr) {
1845 CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
1846 if (pFrame) {
1847 pFrame->ShowAlert(
1848 _("Missing application"),
1849 _("Please download and install the CoRD application from http://cord.sourceforge.net"),
1850 wxOK | wxICON_INFORMATION,
1851 false
1852 );
1853 }
1854 return ERR_FILE_MISSING;
1855 }
1856
1857 strCommand = wxT("osascript -e 'tell application \"CoRD\"' -e 'activate' -e 'open location \"rdp://") + strConnection + wxT("\"' -e 'end tell'");
1858 strCommand.Replace(wxT("localhost"), wxT("127.0.0.1"));
1859 system(strCommand.char_str());
1860 #endif
1861 }
1862
1863 return iRetVal;
1864 }
1865
1866
WorkAbort(char * url,char * name)1867 int CMainDocument::WorkAbort(char* url, char* name) {
1868 int iRetVal = 0;
1869
1870 RESULT* pStateResult = state.lookup_result(url, name);
1871 if (pStateResult) {
1872 iRetVal = rpc.result_op((*pStateResult), "abort");
1873 } else {
1874 ForceCacheUpdate();
1875 }
1876
1877 return iRetVal;
1878 }
1879
1880
1881 // Call this only when notice buffer is stable
1882 // Note: This must not call any rpcs.
1883 // This is now called after each get_notices RPC from
1884 // CMainDocument::HandleCompletedRPC() .
CachedNoticeUpdate()1885 int CMainDocument::CachedNoticeUpdate() {
1886 static bool in_this_func = false;
1887
1888 if (in_this_func) return 0;
1889 in_this_func = true;
1890
1891 if (IsConnected()) {
1892 // Can't look up previous last read message until we know machine name
1893 if (!strlen(state.host_info.domain_name)) {
1894 goto done;
1895 }
1896
1897 // rpc.get_notices is now called from RunPeriodicRPCs()
1898 if (m_iGet_notices_rpc_result) {
1899 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedNoticeUpdate - Get Notices Failed '%d'"), m_iGet_notices_rpc_result);
1900 m_pNetworkConnection->SetStateDisconnected();
1901 goto done;
1902 }
1903
1904 if (notices.notices.size() > 0) {
1905 m_iNoticeSequenceNumber = notices.notices[0]->seqno;
1906
1907 if (m_iLastReadNoticeSequenceNumber < 0) {
1908 m_iLastReadNoticeSequenceNumber = 0; // Do this only once
1909 RestoreUnreadNoticeInfo();
1910 }
1911 }
1912 }
1913
1914 done:
1915 in_this_func = false;
1916 return 0;
1917 }
1918
1919
SaveUnreadNoticeInfo()1920 void CMainDocument::SaveUnreadNoticeInfo() {
1921 static double dLastSavedArrivalTime = 0.0;
1922
1923 if (dLastSavedArrivalTime != m_dLastReadNoticeArrivalTime) {
1924 wxString strBaseConfigLocation = wxString(wxT("/Notices/"));
1925 wxConfigBase* pConfig = wxConfigBase::Get(FALSE);
1926 wxString strDomainName = wxString(state.host_info.domain_name, wxConvUTF8, strlen(state.host_info.domain_name));
1927 wxString strArrivalTime = wxEmptyString;
1928
1929 pConfig->SetPath(strBaseConfigLocation + strDomainName);
1930 // wxConfigBase::Write(const wxString& key, double value) has
1931 // insufficient precision so write a wxString.
1932 strArrivalTime.Printf(wxT("%f"), m_dLastReadNoticeArrivalTime);
1933 pConfig->Write(wxT("lastReadNoticeTime"), strArrivalTime);
1934 dLastSavedArrivalTime = m_dLastReadNoticeArrivalTime;
1935 }
1936 }
1937
1938
RestoreUnreadNoticeInfo()1939 void CMainDocument::RestoreUnreadNoticeInfo() {
1940 wxString strBaseConfigLocation = wxString(wxT("/Notices/"));
1941 wxConfigBase* pConfig = wxConfigBase::Get(FALSE);
1942 wxString strDomainName = wxString(state.host_info.domain_name, wxConvUTF8, strlen(state.host_info.domain_name));
1943 double dLastReadNoticeTime;
1944 wxString strArrivalTime = wxEmptyString;
1945 int i, n = (int)notices.notices.size();
1946
1947 pConfig->SetPath(strBaseConfigLocation + strDomainName);
1948
1949 if (pConfig->Read(wxT("LastReadNoticeTime"), &strArrivalTime)) {
1950 strArrivalTime.ToDouble(&dLastReadNoticeTime);
1951 // To avoid problems caused by rounding in save & restore operation, test in
1952 // reverse order (oldest first) and for arrival time <= dLastReadNoticeTime
1953 for (i=n-1; i>=0; --i) {
1954 if (notices.notices[i]->arrival_time <= dLastReadNoticeTime) {
1955 m_iLastReadNoticeSequenceNumber = notices.notices[i]->seqno;
1956 m_dLastReadNoticeArrivalTime = notices.notices[i]->arrival_time;
1957 }
1958 }
1959 }
1960 }
1961
1962
notice(unsigned int i)1963 NOTICE* CMainDocument::notice(unsigned int i) {
1964 NOTICE* pNotice = NULL;
1965
1966 try {
1967 if (!notices.notices.empty())
1968 pNotice = notices.notices.at(i);
1969 }
1970 catch (std::out_of_range e) {
1971 pNotice = NULL;
1972 }
1973
1974 return pNotice;
1975 }
1976
1977
GetNoticeCount()1978 int CMainDocument::GetNoticeCount() {
1979 int iCount = -1;
1980
1981 // CachedNoticeUpdate() is now called from CMainDocument::OnRPCComplete()
1982 // only after a get_notices RPC completes so notices buffer is stable.
1983 CachedStateUpdate();
1984
1985 if (notices.received) {
1986 iCount = (int)notices.notices.size();
1987 }
1988
1989 return iCount;
1990 }
1991
1992
GetUnreadNoticeCount()1993 int CMainDocument::GetUnreadNoticeCount() {
1994 int iCount = 0;
1995 if (!notices.notices.empty()) {
1996 for (unsigned int i = 0; i < notices.notices.size(); i++) {
1997 if (notices.notices[i]->arrival_time > m_dLastReadNoticeArrivalTime) {
1998 iCount++;
1999 }
2000 }
2001 }
2002 return iCount;
2003 }
2004
2005
UpdateUnreadNoticeState()2006 void CMainDocument::UpdateUnreadNoticeState() {
2007 if (!notices.notices.empty()) {
2008 m_iLastReadNoticeSequenceNumber = notices.notices[0]->seqno;
2009 m_dLastReadNoticeArrivalTime = notices.notices[0]->arrival_time;
2010 SaveUnreadNoticeInfo();
2011 }
2012 }
2013
2014
ResetNoticeState()2015 int CMainDocument::ResetNoticeState() {
2016 notices.clear();
2017 m_iNoticeSequenceNumber = 0;
2018 m_iLastReadNoticeSequenceNumber = -1;
2019 m_dLastReadNoticeArrivalTime = 0.0;
2020 return 0;
2021 }
2022
2023
2024 // Replace CRLFs and LFs with HTML breaks.
2025 //
eol_to_br(wxString & strMessage)2026 void eol_to_br(wxString& strMessage) {
2027 strMessage.Replace(wxT("\r\n"), wxT("<BR>"));
2028 strMessage.Replace(wxT("\n"), wxT("<BR>"));
2029 }
2030
2031 // Remove CRLFs and LFs
2032 //
remove_eols(wxString & strMessage)2033 void remove_eols(wxString& strMessage) {
2034 strMessage.Replace(wxT("\r\n"), wxT(""));
2035 strMessage.Replace(wxT("\n"), wxT(""));
2036 }
2037
2038 // Replace https:// with http://
2039 //
https_to_http(wxString & strMessage)2040 void https_to_http(wxString& strMessage) {
2041 strMessage.Replace(wxT("https://"), wxT("http://"));
2042 }
2043
2044 // replace substrings of the form _("X") with the translation of X
2045 //
localize(wxString & strMessage)2046 void localize(wxString& strMessage) {
2047 wxString strBuffer = wxEmptyString;
2048 wxString strStart = wxString(wxT("_(\""));
2049 wxString strEnd = wxString(wxT("\")"));
2050
2051 while (strMessage.Find(strStart.c_str()) != wxNOT_FOUND) {
2052 strBuffer = strMessage.SubString(
2053 strMessage.Find(strStart.c_str()) + strStart.Length(),
2054 strMessage.Find(strEnd.c_str()) - (strEnd.Length() - 1)
2055 );
2056 strMessage.Replace(
2057 wxString(strStart + strBuffer + strEnd).c_str(),
2058 wxGetTranslation(strBuffer.c_str())
2059 );
2060 }
2061 }
2062
2063
2064 // Call this only when message buffer is stable
2065 // Note: This must not call any rpcs.
2066 // This is now called after each get_messages RPC from
2067 // CMainDocument::HandleCompletedRPC() .
2068 //
CachedMessageUpdate()2069 int CMainDocument::CachedMessageUpdate() {
2070 static bool in_this_func = false;
2071 size_t last_ind;
2072
2073 if (in_this_func) return 0;
2074 in_this_func = true;
2075
2076 if (IsConnected()) {
2077 // rpc.get_messages is now called from RunPeriodicRPCs()
2078 // retval = rpc.get_messages(m_iLastMessageSequenceNumber, messages);
2079 if (m_iGet_messages_rpc_result) {
2080 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedMessageUpdate - Get Messages Failed '%d'"), m_iGet_messages_rpc_result);
2081 m_pNetworkConnection->SetStateDisconnected();
2082 goto done;
2083 }
2084 if (messages.messages.size() != 0) {
2085 last_ind = messages.messages.size()-1;
2086 m_iLastMessageSequenceNumber = messages.messages[last_ind]->seqno;
2087
2088 // status.max_event_log_lines <= 0 means no limit
2089 if ((status.max_event_log_lines > 0) &&
2090 (last_ind >= (unsigned)status.max_event_log_lines)
2091 ) {
2092 // Remove oldest messages if we have too many
2093 while (messages.messages.size() > (unsigned)status.max_event_log_lines) {
2094 delete messages.messages.front();
2095 messages.messages.pop_front();
2096 }
2097 m_iFirstMessageSequenceNumber = messages.messages[0]->seqno;
2098 } else if (m_iFirstMessageSequenceNumber < 0) {
2099 m_iFirstMessageSequenceNumber = messages.messages[0]->seqno;
2100 }
2101 }
2102 }
2103
2104 done:
2105 in_this_func = false;
2106 return 0;
2107 }
2108
2109
message(unsigned int i)2110 MESSAGE* CMainDocument::message(unsigned int i) {
2111 MESSAGE* pMessage = NULL;
2112
2113 try {
2114 if (!messages.messages.empty())
2115 pMessage = messages.messages.at(i);
2116 }
2117 catch (std::out_of_range e) {
2118 pMessage = NULL;
2119 }
2120
2121 return pMessage;
2122 }
2123
2124
GetMessageCount()2125 int CMainDocument::GetMessageCount() {
2126 int iCount = -1;
2127
2128 // CachedMessageUpdate() is now called from CMainDocument::OnRPCComplete()
2129 // only after a get_messages RPC completes so messages buffer is stable.
2130 CachedStateUpdate();
2131
2132 if (!messages.messages.empty()) {
2133 iCount = (int)messages.messages.size();
2134 }
2135
2136 return iCount;
2137 }
2138
2139
ResetMessageState()2140 int CMainDocument::ResetMessageState() {
2141 messages.clear();
2142 m_iLastMessageSequenceNumber = 0;
2143 m_iFirstMessageSequenceNumber = -1;
2144 return 0;
2145 }
2146
2147
CachedFileTransfersUpdate()2148 int CMainDocument::CachedFileTransfersUpdate() {
2149 bool immediate = false;
2150
2151 if (! IsConnected()) return -1;
2152
2153 #if USE_CACHE_TIMEOUTS
2154 wxTimeSpan ts(wxDateTime::Now() - m_dtFileTransfersTimestamp);
2155 if (ts.GetSeconds() >= (2* FILETRANSFERSRPC_INTERVAL)) immediate = true;
2156 #endif
2157 if (m_dtFileTransfersTimestamp.IsEqualTo(wxDateTime((time_t)0))) immediate = true;
2158
2159 if (immediate) {
2160 m_dtFileTransfersTimestamp = wxDateTime::Now();
2161 m_iGet_file_transfers_rpc_result = rpc.get_file_transfers(ft);
2162 }
2163
2164 if (m_iGet_file_transfers_rpc_result) {
2165 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedFileTransfersUpdate - Get File Transfers Failed '%d'"), m_iGet_file_transfers_rpc_result);
2166 ForceCacheUpdate();
2167 }
2168
2169 return m_iGet_file_transfers_rpc_result;
2170 }
2171
2172
file_transfer(unsigned int i)2173 FILE_TRANSFER* CMainDocument::file_transfer(unsigned int i) {
2174 FILE_TRANSFER* pFT = NULL;
2175
2176 try {
2177 if (!ft.file_transfers.empty())
2178 pFT = ft.file_transfers.at(i);
2179 }
2180 catch (std::out_of_range e) {
2181 pFT = NULL;
2182 }
2183
2184 return pFT;
2185 }
2186
file_transfer(const wxString & fileName,const wxString & project_url)2187 FILE_TRANSFER* CMainDocument::file_transfer(const wxString& fileName, const wxString& project_url) {
2188 FILE_TRANSFER* pFT = NULL;
2189
2190 try {
2191 if (!ft.file_transfers.empty()) {
2192 for(unsigned int i=0; i< ft.file_transfers.size();i++) {
2193 FILE_TRANSFER* tFT = ft.file_transfers.at(i);
2194 wxString fname(tFT->name.c_str(),wxConvUTF8);
2195 if(fname.IsSameAs(fileName)) {
2196 wxString furl(tFT->project_url.c_str(),wxConvUTF8);
2197 if(furl.IsSameAs(project_url)){
2198 pFT = tFT;
2199 break;
2200 }
2201 }
2202 }
2203 }
2204 }
2205 catch (std::out_of_range e) {
2206 pFT = NULL;
2207 }
2208
2209 return pFT;
2210 }
2211
2212
GetTransferCount()2213 int CMainDocument::GetTransferCount() {
2214 int iCount = 0;
2215
2216 CachedFileTransfersUpdate();
2217 CachedStateUpdate();
2218
2219 if (!ft.file_transfers.empty())
2220 iCount = (int)ft.file_transfers.size();
2221
2222 return iCount;
2223 }
2224
2225
TransferRetryNow(int iIndex)2226 int CMainDocument::TransferRetryNow(int iIndex) {
2227 FILE_TRANSFER* pFT = NULL;
2228 int iRetVal = 0;
2229
2230 pFT = file_transfer(iIndex);
2231
2232 if (pFT)
2233 iRetVal = rpc.file_transfer_op((*pFT), "retry");
2234
2235 return iRetVal;
2236 }
2237
TransferRetryNow(const wxString & fileName,const wxString & project_url)2238 int CMainDocument::TransferRetryNow(const wxString& fileName, const wxString& project_url) {
2239 FILE_TRANSFER* pFT = NULL;
2240 int iRetVal = 0;
2241
2242 pFT = file_transfer(fileName, project_url);
2243
2244 if (pFT)
2245 iRetVal = rpc.file_transfer_op((*pFT), "retry");
2246
2247 return iRetVal;
2248 }
2249
2250
TransferAbort(int iIndex)2251 int CMainDocument::TransferAbort(int iIndex) {
2252 FILE_TRANSFER* pFT = NULL;
2253 int iRetVal = 0;
2254
2255 pFT = file_transfer(iIndex);
2256
2257 if (pFT)
2258 iRetVal = rpc.file_transfer_op((*pFT), "abort");
2259
2260 return iRetVal;
2261 }
2262
TransferAbort(const wxString & fileName,const wxString & project_url)2263 int CMainDocument::TransferAbort(const wxString& fileName, const wxString& project_url) {
2264 FILE_TRANSFER* pFT = NULL;
2265 int iRetVal = 0;
2266
2267 pFT = file_transfer(fileName, project_url);
2268
2269 if (pFT)
2270 iRetVal = rpc.file_transfer_op((*pFT), "abort");
2271
2272 return iRetVal;
2273 }
2274
CachedDiskUsageUpdate()2275 int CMainDocument::CachedDiskUsageUpdate() {
2276 bool immediate = true;
2277
2278 if (!IsConnected()) return -1;
2279
2280 // don't get disk usage more than once per minute
2281 // unless we just connected to a client
2282 //
2283 #if USE_CACHE_TIMEOUTS
2284 wxTimeSpan ts(wxDateTime::Now() - m_dtDiskUsageTimestamp);
2285 if (ts.GetSeconds() >= (2 * DISKUSAGERPC_INTERVAL)) immediate = true;
2286 #endif
2287 if (disk_usage.projects.empty()) immediate = true;
2288 if (m_dtDiskUsageTimestamp.IsEqualTo(wxDateTime((time_t)0))) immediate = true;
2289
2290 if (immediate) {
2291 m_dtDiskUsageTimestamp = wxDateTime::Now();
2292 m_iGet_dsk_usage_rpc_result = rpc.get_disk_usage(disk_usage);
2293 }
2294
2295 if (m_iGet_dsk_usage_rpc_result) {
2296 wxLogTrace(wxT("Function Status"), wxT("Get Disk Usage Failed '%d'"), m_iGet_dsk_usage_rpc_result);
2297 ForceCacheUpdate();
2298 }
2299
2300 return m_iGet_dsk_usage_rpc_result;
2301 }
2302
2303
DiskUsageProject(unsigned int i)2304 PROJECT* CMainDocument::DiskUsageProject(unsigned int i) {
2305 PROJECT* pProject = NULL;
2306
2307 try {
2308 if (!disk_usage.projects.empty()) {
2309 pProject = disk_usage.projects.at(i);
2310 }
2311 }
2312 catch (std::out_of_range e) {
2313 pProject = NULL;
2314 }
2315
2316 return pProject;
2317 }
2318
CachedStatisticsStatusUpdate()2319 int CMainDocument::CachedStatisticsStatusUpdate() {
2320 bool immediate = false;
2321
2322 if (! IsConnected()) return -1;
2323
2324 #if USE_CACHE_TIMEOUTS
2325 wxTimeSpan ts(wxDateTime::Now() - m_dtStatisticsStatusTimestamp);
2326 if (ts.GetSeconds() >= (2 * STATISTICSSTATUSRPC_INTERVAL)) immediate = true;
2327 #endif
2328 if (statistics_status.projects.empty()) immediate = true;
2329 if (m_dtStatisticsStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) immediate = true;
2330
2331 if (immediate) {
2332 m_dtStatisticsStatusTimestamp = wxDateTime::Now();
2333 m_dtStatisticsStatusTimestamp = rpc.get_statistics(statistics_status);
2334 }
2335
2336 if (m_iGet_statistics_rpc_result) {
2337 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedStatisticsStatusUpdate - Get Statistics Failed '%d'"), m_iGet_statistics_rpc_result);
2338 ForceCacheUpdate();
2339 }
2340
2341 return m_iGet_state_rpc_result;
2342 }
2343
2344
statistic(unsigned int i)2345 PROJECT* CMainDocument::statistic(unsigned int i) {
2346 PROJECT* pProject = NULL;
2347
2348
2349 try {
2350 if (!statistics_status.projects.empty())
2351 pProject = statistics_status.projects.at(i);
2352 }
2353 catch (std::out_of_range e) {
2354 pProject = NULL;
2355 }
2356
2357
2358 return pProject;
2359 }
2360
2361
GetStatisticsCount()2362 int CMainDocument::GetStatisticsCount() {
2363 int iCount = -1;
2364
2365 CachedStatisticsStatusUpdate();
2366 CachedStateUpdate();
2367
2368 if (!statistics_status.projects.empty())
2369 iCount = (int)statistics_status.projects.size();
2370
2371 return iCount;
2372 }
2373
2374
GetProxyConfiguration()2375 int CMainDocument::GetProxyConfiguration() {
2376 int iRetVal = 0;
2377
2378 iRetVal = rpc.get_proxy_settings(proxy_info);
2379 if (iRetVal) {
2380 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::GetProxyInfo - Get Proxy Info Failed '%d'"), iRetVal);
2381 }
2382
2383 return iRetVal;
2384 }
2385
2386
SetProxyConfiguration()2387 int CMainDocument::SetProxyConfiguration() {
2388 int iRetVal = 0;
2389
2390 if (!proxy_info.http_user_name.empty() || !proxy_info.http_user_passwd.empty())
2391 proxy_info.use_http_authentication = true;
2392
2393 iRetVal = rpc.set_proxy_settings(proxy_info);
2394 if (iRetVal) {
2395 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::SetProxyInfo - Set Proxy Info Failed '%d'"), iRetVal);
2396 }
2397
2398 return iRetVal;
2399 }
2400
2401
CachedSimpleGUIUpdate(bool bForce)2402 int CMainDocument::CachedSimpleGUIUpdate(bool bForce) {
2403 int i = 0;
2404
2405 if (! IsConnected()) return -1;
2406
2407 #if USE_CACHE_TIMEOUTS
2408 wxTimeSpan ts(wxDateTime::Now() - m_dtCachedSimpleGUITimestamp);
2409 if (ts.GetSeconds() >= (2 * CACHEDSIMPLEGUIRPC_INTERVAL)) bForce = true;
2410 #endif
2411 if (m_dtCachedSimpleGUITimestamp.IsEqualTo(wxDateTime((time_t)0))) bForce = true;
2412 if (bForce) {
2413 m_dtCachedSimpleGUITimestamp = wxDateTime::Now();
2414 m_iGet_simple_gui2_rpc_result = rpc.get_simple_gui_info(async_projects_update_buf, state, results, m_ActiveTasksOnly);
2415 }
2416
2417 if (m_iGet_simple_gui2_rpc_result) {
2418 wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedSimpleGUIUpdate - Get Simple GUI Failed '%d'"), m_iGet_simple_gui2_rpc_result);
2419 ForceCacheUpdate();
2420 }
2421
2422 m_fProjectTotalResourceShare = 0.0;
2423 for (i=0; i < (long)state.projects.size(); i++) {
2424 m_fProjectTotalResourceShare += state.projects.at(i)->resource_share;
2425 }
2426 if (!m_fProjectTotalResourceShare) m_fProjectTotalResourceShare = 1;
2427
2428 return m_iGet_simple_gui2_rpc_result;
2429 }
2430
2431
GetSimpleProjectCount()2432 int CMainDocument::GetSimpleProjectCount() {
2433 int iCount = -1;
2434
2435 CachedSimpleGUIUpdate();
2436 CachedStateUpdate();
2437
2438 if (!state.projects.empty())
2439 iCount = (int)state.projects.size();
2440
2441 return iCount;
2442 }
2443
2444
GetSimpleGUIWorkCount()2445 int CMainDocument::GetSimpleGUIWorkCount() {
2446 int iCount = 0;
2447 unsigned int i = 0;
2448
2449 CachedSimpleGUIUpdate();
2450 CachedStateUpdate();
2451
2452 for(i=0; i<results.results.size(); i++) {
2453 if (results.results[i]->active_task) {
2454 iCount++;
2455 }
2456 }
2457 return iCount;
2458 }
2459
suspend_reason_wxstring(int reason)2460 wxString suspend_reason_wxstring(int reason) {
2461 switch (reason) {
2462 case SUSPEND_REASON_BATTERIES: return _("on batteries");
2463 case SUSPEND_REASON_USER_ACTIVE: return _("computer is in use");
2464 case SUSPEND_REASON_USER_REQ: return _("user request");
2465 case SUSPEND_REASON_TIME_OF_DAY: return _("time of day");
2466 case SUSPEND_REASON_BENCHMARKS: return _("CPU benchmarks in progress");
2467 case SUSPEND_REASON_DISK_SIZE: return _("need disk space - check preferences");
2468 case SUSPEND_REASON_NO_RECENT_INPUT: return _("computer is not in use");
2469 case SUSPEND_REASON_INITIAL_DELAY: return _("starting up");
2470 case SUSPEND_REASON_EXCLUSIVE_APP_RUNNING: return _("an exclusive app is running");
2471 case SUSPEND_REASON_CPU_USAGE: return _("CPU is busy");
2472 case SUSPEND_REASON_NETWORK_QUOTA_EXCEEDED: return _("network bandwidth limit exceeded");
2473 case SUSPEND_REASON_OS: return _("requested by operating system");
2474 }
2475 return _("unknown reason");
2476 }
2477
uses_gpu(RESULT * r)2478 bool uses_gpu(RESULT* r) {
2479 // kludge. But r->avp isn't populated.
2480 return (strstr(r->resources, "GPU") != NULL);
2481 }
2482
result_description(RESULT * result,bool show_resources)2483 wxString result_description(RESULT* result, bool show_resources) {
2484 CMainDocument* doc = wxGetApp().GetDocument();
2485 PROJECT* project;
2486 CC_STATUS status;
2487 int retval;
2488 wxString strBuffer= wxEmptyString;
2489
2490 strBuffer.Clear();
2491 retval = doc->GetCoreClientStatus(status);
2492 if (retval || !result) {
2493 return strBuffer;
2494 }
2495
2496 if (result->coproc_missing) {
2497 strBuffer += _("GPU missing, ");
2498 }
2499
2500 project = doc->state.lookup_project(result->project_url);
2501 int throttled = status.task_suspend_reason & SUSPEND_REASON_CPU_THROTTLE;
2502 switch(result->state) {
2503 case RESULT_NEW:
2504 strBuffer += _("New");
2505 break;
2506 case RESULT_FILES_DOWNLOADING:
2507 if (result->ready_to_report) {
2508 strBuffer += _("Download failed");
2509 } else {
2510 strBuffer += _("Downloading");
2511 if (status.network_suspend_reason) {
2512 strBuffer += _(" (suspended - ");
2513 strBuffer += suspend_reason_wxstring(status.network_suspend_reason);
2514 strBuffer += _(")");
2515 }
2516 }
2517 break;
2518 case RESULT_FILES_DOWNLOADED:
2519 if (result->project_suspended_via_gui) {
2520 strBuffer += _("Project suspended by user");
2521 } else if (result->suspended_via_gui) {
2522 strBuffer += _("Task suspended by user");
2523 } else if (status.task_suspend_reason && !throttled && result->active_task_state != PROCESS_EXECUTING) {
2524 // an NCI process can be running even though computation is suspended
2525 // (because of <dont_suspend_nci>
2526 //
2527 strBuffer += _("Suspended - ");
2528 strBuffer += suspend_reason_wxstring(status.task_suspend_reason);
2529 } else if (status.gpu_suspend_reason && uses_gpu(result)) {
2530 strBuffer += _("GPU suspended - ");
2531 strBuffer += suspend_reason_wxstring(status.gpu_suspend_reason);
2532 } else if (result->active_task) {
2533 if (result->too_large) {
2534 strBuffer += _("Waiting for memory");
2535 } else if (result->needs_shmem) {
2536 strBuffer += _("Waiting for shared memory");
2537 } else if (result->scheduler_state == CPU_SCHED_SCHEDULED) {
2538 strBuffer += _("Running");
2539 if (project && project->non_cpu_intensive) {
2540 strBuffer += _(" (non-CPU-intensive)");
2541 }
2542 } else if (result->scheduler_state == CPU_SCHED_PREEMPTED) {
2543 strBuffer += _("Waiting to run");
2544 } else if (result->scheduler_state == CPU_SCHED_UNINITIALIZED) {
2545 strBuffer += _("Ready to start");
2546 }
2547 } else {
2548 strBuffer += _("Ready to start");
2549 }
2550 if (result->scheduler_wait) {
2551 if (strlen(result->scheduler_wait_reason)) {
2552 strBuffer = _("Postponed: ");
2553 strBuffer += wxString(result->scheduler_wait_reason, wxConvUTF8);
2554 } else {
2555 strBuffer = _("Postponed");
2556 }
2557 }
2558 if (result->network_wait) {
2559 strBuffer = _("Waiting for network access");
2560 }
2561 break;
2562 case RESULT_COMPUTE_ERROR:
2563 strBuffer += _("Computation error");
2564 break;
2565 case RESULT_FILES_UPLOADING:
2566 if (result->ready_to_report) {
2567 strBuffer += _("Upload failed");
2568 } else {
2569 strBuffer += _("Uploading");
2570 if (status.network_suspend_reason) {
2571 strBuffer += _(" (suspended - ");
2572 strBuffer += suspend_reason_wxstring(status.network_suspend_reason);
2573 strBuffer += _(")");
2574 }
2575 }
2576 break;
2577 case RESULT_ABORTED:
2578 switch(result->exit_status) {
2579 case EXIT_ABORTED_VIA_GUI:
2580 strBuffer += _("Aborted by user");
2581 break;
2582 case EXIT_ABORTED_BY_PROJECT:
2583 strBuffer += _("Aborted by project");
2584 break;
2585 case EXIT_UNSTARTED_LATE:
2586 strBuffer += _("Aborted: not started by deadline");
2587 break;
2588 case EXIT_DISK_LIMIT_EXCEEDED:
2589 strBuffer += _("Aborted: task disk limit exceeded");
2590 break;
2591 case EXIT_TIME_LIMIT_EXCEEDED:
2592 strBuffer += _("Aborted: run time limit exceeded");
2593 break;
2594 case EXIT_MEM_LIMIT_EXCEEDED:
2595 strBuffer += _("Aborted: memory limit exceeded");
2596 break;
2597 default:
2598 strBuffer += _("Aborted");
2599 }
2600 break;
2601 default:
2602 if (result->got_server_ack) {
2603 strBuffer += _("Acknowledged");
2604 } else if (result->ready_to_report) {
2605 strBuffer += _("Ready to report");
2606 } else {
2607 strBuffer.Format(_("Error: invalid state '%d'"), result->state);
2608 }
2609 break;
2610 }
2611 if (strlen(result->resources)>1 && show_resources) {
2612 strBuffer += wxString(wxT(" (")) + wxString(result->resources, wxConvUTF8) + wxString(wxT(")"));
2613 }
2614 return strBuffer;
2615 }
2616
hsv2rgb(double h,double s,double v,double & r,double & g,double & b)2617 static void hsv2rgb(
2618 double h, double s, double v, double& r, double& g, double& b
2619 ) {
2620 double m, n, f;
2621 int i = floor(h);
2622 f = h - i;
2623 if (!(i&1)) f = 1 - f;
2624 m = v * (1 - s);
2625 n = v * (1 - s*f);
2626 switch (i) {
2627 case 6:
2628 case 0: r = v; g = n; b = m; return;
2629 case 1: r = n; g = v; b = m; return;
2630 case 2: r = m; g = v; b = n; return;
2631 case 3: r = m; g = n; b = v; return;
2632 case 4: r = n; g = m; b = v; return;
2633 case 5: r = v; g = m; b = n; return;
2634 }
2635 }
2636
2637 // return the ith out of n maximally distinct colors
2638 //
color_cycle(int i,int n,wxColour & color)2639 void color_cycle(int i, int n, wxColour& color) {
2640 double h = (double)i/(double)n;
2641 double r, g, b;
2642 double v = .75;
2643 if (n > 6) v = .6 + (i % 3)*.1;
2644 // cycle through 3 different brightnesses
2645 hsv2rgb(h*6, .5, v, r, g, b);
2646 unsigned char cr = (unsigned char) (r*256);
2647 unsigned char cg = (unsigned char) (g*256);
2648 unsigned char cb = (unsigned char) (b*256);
2649 color = wxColour(cr, cg, cb);
2650 }
2651
2652 #ifdef __WXMSW__
2653 static double XDPIScaleFactor = 0.0;
2654 static double YDPIScaleFactor = 0.0;
2655
GetDPIScaling()2656 void GetDPIScaling() {
2657 XDPIScaleFactor = 1.0;
2658 YDPIScaleFactor = 1.0;
2659 // SetProcessDPIAware() requires Windows Vista or later
2660 HMODULE hUser32 = LoadLibrary(_T("user32.dll"));
2661 typedef BOOL (*SetProcessDPIAwareFunc)();
2662 SetProcessDPIAwareFunc setDPIAware = (SetProcessDPIAwareFunc)GetProcAddress(hUser32, "SetProcessDPIAware");
2663 if (setDPIAware) {
2664 setDPIAware();
2665 HWND hWnd = GetForegroundWindow();
2666 HDC hdc = GetDC(hWnd);
2667 XDPIScaleFactor = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f;
2668 YDPIScaleFactor = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0f;
2669 ReleaseDC(hWnd, hdc);
2670 }
2671 FreeLibrary(hUser32);
2672 }
2673
GetXDPIScaling()2674 double GetXDPIScaling() {
2675 if (XDPIScaleFactor == 0.0) {
2676 GetDPIScaling();
2677 }
2678 return XDPIScaleFactor;
2679 }
2680
GetYDPIScaling()2681 double GetYDPIScaling() {
2682 if (YDPIScaleFactor == 0.0) {
2683 GetDPIScaling();
2684 }
2685 return YDPIScaleFactor;
2686 }
2687 #endif
2688
2689 // TODO: Choose from multiple size images if provided, else resize the closest one
GetScaledBitmapFromXPMData(const char ** XPMData)2690 wxBitmap GetScaledBitmapFromXPMData(const char** XPMData) {
2691 #ifdef __WXMSW__
2692 if ((GetXDPIScaling() > 1.05) || (GetYDPIScaling() > 1.05)) {
2693 wxImage img = wxImage(XPMData);
2694 img.Rescale((int) (img.GetWidth()*GetXDPIScaling()),
2695 (int) (img.GetHeight()*GetYDPIScaling()),
2696 wxIMAGE_QUALITY_BILINEAR
2697 );
2698 wxBitmap *bm = new wxBitmap(img);
2699 return *bm;
2700 }
2701 #endif
2702 return wxBitmap(XPMData);
2703 }
2704
FormatTime(double secs)2705 wxString FormatTime(double secs) {
2706 if (secs <= 0) {
2707 return wxT("---");
2708 }
2709 wxInt32 iHour = (wxInt32)(secs / (60 * 60));
2710 wxInt32 iMin = (wxInt32)(secs / 60) % 60;
2711 wxInt32 iSec = (wxInt32)(secs) % 60;
2712 wxTimeSpan ts = wxTimeSpan(iHour, iMin, iSec);
2713 return ts.Format((secs>=86400)?"%Dd %H:%M:%S":"%H:%M:%S");
2714 }
2715
format_number(double x,int nprec)2716 wxString format_number(double x, int nprec) {
2717 return wxNumberFormatter::ToString(x, nprec);
2718
2719 }
2720