~ubuntu-branches/ubuntu/precise/boinc/precise

« back to all changes in this revision

Viewing changes to clientgui/BOINCClientManager.cpp

Tags: 6.12.8+dfsg-1
* New upstream release.
* Simplified debian/rules

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// Berkeley Open Infrastructure for Network Computing
2
 
// http://boinc.berkeley.edu
3
 
// Copyright (C) 2005 University of California
4
 
//
5
 
// This is free software; you can redistribute it and/or
6
 
// modify it under the terms of the GNU Lesser General Public
7
 
// License as published by the Free Software Foundation;
8
 
// either version 2.1 of the License, or (at your option) any later version.
9
 
//
10
 
// This software 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
 
// To view the GNU Lesser General Public License visit
16
 
// http://www.gnu.org/copyleft/lesser.html
17
 
// or write to the Free Software Foundation, Inc.,
18
 
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19
 
 
20
 
#if defined(__GNUG__) && !defined(__APPLE__)
21
 
#pragma implementation "BOINCClientManager.h"
22
 
#endif
23
 
 
24
 
#include "stdwx.h"
25
 
#include "diagnostics.h"
26
 
#include "LogBOINC.h"
27
 
#include "BOINCGUIApp.h"
28
 
#include "MainDocument.h"
29
 
#include "BOINCBaseFrame.h"
30
 
#include "AdvancedFrame.h"
31
 
#include "BOINCClientManager.h"
32
 
 
33
 
#ifdef __WXMAC__
34
 
#include "filesys.h"
35
 
#include "util.h"
36
 
 
37
 
enum {
38
 
    NewStyleDaemon = 1,
39
 
    OldStyleDaemon
40
 
};
41
 
#endif
42
 
 
43
 
#ifdef __WXMSW__
44
 
EXTERN_C BOOL  IsBOINCServiceInstalled();
45
 
EXTERN_C BOOL  IsBOINCServiceStarting();
46
 
EXTERN_C BOOL  IsBOINCServiceRunning();
47
 
EXTERN_C BOOL  IsBOINCServiceStopping();
48
 
EXTERN_C BOOL  IsBOINCServiceStopped();
49
 
EXTERN_C BOOL  StartBOINCService();
50
 
EXTERN_C BOOL  StopBOINCService();
51
 
#endif
52
 
 
53
 
CBOINCClientManager::CBOINCClientManager() {
54
 
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::CBOINCClientManager - Function Begin"));
55
 
 
56
 
    m_bBOINCStartedByManager = false;
57
 
    m_lBOINCCoreProcessId = 0;
58
 
 
59
 
#ifdef __WXMSW__
60
 
    m_hBOINCCoreProcess = NULL;
61
 
#endif
62
 
 
63
 
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::CBOINCClientManager - Function End"));
64
 
}
65
 
 
66
 
 
67
 
CBOINCClientManager::~CBOINCClientManager() {
68
 
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::~CBOINCClientManager - Function Begin"));
69
 
 
70
 
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::~CBOINCClientManager - Function End"));
71
 
}
72
 
 
73
 
 
74
 
bool CBOINCClientManager::AutoRestart() {
75
 
    if (IsBOINCCoreRunning()) return true;
76
 
#ifndef __WXMAC__       // Mac can restart Client as a daemon
77
 
    if (! m_bBOINCStartedByManager) return false;
78
 
#endif
79
 
    m_lBOINCCoreProcessId = 0;
80
 
    StartupBOINCCore();
81
 
    return true;
82
 
}
83
 
 
84
 
 
85
 
bool CBOINCClientManager::IsSystemBooting() {
86
 
    bool bReturnValue = false;
87
 
#if   defined(__WXMSW__)
88
 
    if (GetTickCount() < (1000*60*5)) bReturnValue = true;  // If system has been up for less than 5 minutes 
89
 
#elif defined(__WXMAC__)
90
 
    if (TickCount() < (120*60)) bReturnValue = true;        // If system has been up for less than 2 minutes 
91
 
#endif
92
 
    return bReturnValue;
93
 
}
94
 
 
95
 
 
96
 
int CBOINCClientManager::IsBOINCConfiguredAsDaemon() {
97
 
    bool bReturnValue = false;
98
 
#if   defined(__WXMSW__)
99
 
    if (IsBOINCServiceInstalled()) bReturnValue = 1;
100
 
#elif defined(__WXMAC__)
101
 
    if ( boinc_file_exists("/Library/LaunchDaemons/edu.berkeley.boinc.plist")) {
102
 
        bReturnValue = NewStyleDaemon;                      // New-style daemon uses launchd
103
 
    }
104
 
    if (boinc_file_exists("/Library/StartupItems/boinc/boinc") ) {
105
 
        bReturnValue = OldStyleDaemon;                      // Old-style daemon uses StartupItem
106
 
    }
107
 
#endif
108
 
    return bReturnValue;
109
 
}
110
 
 
111
 
 
112
 
bool CBOINCClientManager::IsBOINCCoreRunning() {
113
 
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Function Begin"));
114
 
 
115
 
    int retval;
116
 
    bool running = false;
117
 
    HOST_INFO hostinfo;
118
 
    RPC_CLIENT rpc;
119
 
 
120
 
#ifdef __WXMSW__
121
 
    if (IsBOINCServiceInstalled()) {
122
 
        running = (FALSE != IsBOINCServiceStarting()) || (FALSE != IsBOINCServiceRunning());
123
 
    } else {
124
 
#endif
125
 
    // If set up to run as a daemon, allow time for daemon to start up
126
 
    for (int i=0; i<10; i++) {
127
 
        retval = rpc.init("localhost");  // synchronous is OK since local
128
 
        wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Connecting to core client returned '%d'"), retval);
129
 
        retval = rpc.get_host_info(hostinfo);
130
 
        wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Requesting host info... retval '%d'"), retval);
131
 
        running = (retval == 0);
132
 
        rpc.close();
133
 
        if (running) break;
134
 
        if (!IsBOINCConfiguredAsDaemon()) break;
135
 
        wxSleep(1);
136
 
    }
137
 
#ifdef __WXMSW__
138
 
    }
139
 
#endif
140
 
 
141
 
    wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Returning '%d'"), retval);
142
 
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Function End"));
143
 
    return running;
144
 
}
145
 
 
146
 
 
147
 
bool CBOINCClientManager::StartupBOINCCore() {
148
 
    wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::StartupBOINCCore - Function Begin"));
149
 
 
150
 
    bool                bReturnValue = false;
151
 
    wxString            strExecute = wxEmptyString;
152
 
 
153
 
    if (IsBOINCCoreRunning()) return true;
154
 
 
155
 
#if   defined(__WXMSW__)
156
 
    LPTSTR  szExecute = NULL;
157
 
    LPTSTR  szDataDirectory = NULL;
158
 
 
159
 
    if (IsBOINCConfiguredAsDaemon()) {
160
 
        StartBOINCService();
161
 
 
162
 
        m_bBOINCStartedByManager = true;
163
 
        bReturnValue = IsBOINCCoreRunning();
164
 
    } else {
165
 
 
166
 
        // Append boinc.exe to the end of the strExecute string and get ready to rock
167
 
        strExecute.Printf(
168
 
            wxT("\"%s\\boinc.exe\" --redirectio --launched_by_manager %s"),
169
 
            wxGetApp().GetRootDirectory().c_str(),
170
 
            wxGetApp().GetArguments().c_str()
171
 
        );
172
 
 
173
 
        PROCESS_INFORMATION pi;
174
 
        STARTUPINFO         si;
175
 
        BOOL                bProcessStarted;
176
 
 
177
 
        memset(&pi, 0, sizeof(pi));
178
 
        memset(&si, 0, sizeof(si));
179
 
 
180
 
        si.cb = sizeof(si);
181
 
        si.dwFlags = STARTF_USESHOWWINDOW;
182
 
        si.wShowWindow = SW_HIDE;
183
 
 
184
 
        szExecute = (LPTSTR)strExecute.c_str();
185
 
        if (wxGetApp().GetDataDirectory().empty()) {
186
 
            szDataDirectory = NULL;
187
 
        } else {
188
 
            szDataDirectory = (LPTSTR)wxGetApp().GetDataDirectory().c_str();
189
 
        }
190
 
 
191
 
        wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szExecute '%s'\n"), szExecute);
192
 
        wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szDataDirectory '%s'\n"), szDataDirectory);
193
 
 
194
 
        bProcessStarted = CreateProcess(
195
 
            NULL,
196
 
            szExecute,
197
 
            NULL,
198
 
            NULL,
199
 
            FALSE,
200
 
            CREATE_NEW_PROCESS_GROUP|CREATE_NO_WINDOW,
201
 
            NULL,
202
 
            szDataDirectory,
203
 
            &si,
204
 
            &pi
205
 
        );
206
 
        if (bProcessStarted) {
207
 
            m_lBOINCCoreProcessId = pi.dwProcessId;
208
 
            m_hBOINCCoreProcess = pi.hProcess;
209
 
        }
210
 
    }
211
 
 
212
 
#elif defined(__WXMAC__)
213
 
 
214
 
    wxChar buf[1024];
215
 
    wxChar *argv[5];
216
 
    ProcessSerialNumber ourPSN;
217
 
    FSRef ourFSRef;
218
 
    OSErr err;
219
 
 
220
 
    if (IsBOINCConfiguredAsDaemon() == NewStyleDaemon) {
221
 
        system ("launchctl load /Library/LaunchDaemons/edu.berkeley.boinc.plist");
222
 
        system ("launchctl start edu.berkeley.boinc");
223
 
        bReturnValue = IsBOINCCoreRunning();
224
 
    } else {
225
 
        
226
 
        // Get the full path to core client inside this application's bundle
227
 
        err = GetCurrentProcess (&ourPSN);
228
 
        if (err == noErr) {
229
 
            err = GetProcessBundleLocation(&ourPSN, &ourFSRef);
230
 
        }
231
 
        if (err == noErr) {
232
 
            err = FSRefMakePath (&ourFSRef, (UInt8*)buf, sizeof(buf));
233
 
        }
234
 
        if (err == noErr) {
235
 
#if 0   // The Mac version of wxExecute(wxString& ...) crashes if there is a space in the path
236
 
            strExecute = wxT("\"");            
237
 
            strExecute += wxT(buf);
238
 
            strExecute += wxT("/Contents/Resources/boinc\" --redirectio --launched_by_manager");
239
 
            m_lBOINCCoreProcessId = ::wxExecute(strExecute);
240
 
#else   // Use wxExecute(wxChar **argv ...) instead of wxExecute(wxString& ...)
241
 
            strcat(buf, "/Contents/Resources/boinc");
242
 
            argv[0] = buf;
243
 
            argv[1] = "--redirectio";
244
 
            argv[2] = "--launched_by_manager";
245
 
            argv[3] = NULL;
246
 
#ifdef SANDBOX
247
 
            if (!g_use_sandbox) {
248
 
                argv[3] = "--insecure";
249
 
                argv[4] = NULL;
250
 
            }
251
 
#endif
252
 
            // Under wxMac-2.8.0, wxExecute starts a separate thread
253
 
            // to wait for child's termination.
254
 
            // That wxProcessTerminationThread uses a huge amount of processor
255
 
            // time (about 11% of one CPU on 2GHz Intel Dual-Core Mac).
256
 
//                m_lBOINCCoreProcessId = ::wxExecute(argv);
257
 
            run_program(
258
 
                "/Library/Application Support/BOINC Data",
259
 
                buf, argv[3] ? 4 : 3, argv, 0.0, m_lBOINCCoreProcessId
260
 
            );
261
 
#endif
262
 
        } else {
263
 
            buf[0] = '\0';
264
 
        }
265
 
    }
266
 
 
267
 
#else   // Unix based systems
268
 
 
269
 
    // Append boinc.exe to the end of the strExecute string and get ready to rock
270
 
    strExecute = ::wxGetCwd() + wxT("/boinc --redirectio --launched_by_manager");
271
 
 
272
 
    wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szExecute '%s'\n"), strExecute.c_str());
273
 
    wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szDataDirectory '%s'\n"), ::wxGetCwd().c_str());
274
 
 
275
 
    m_lBOINCCoreProcessId = ::wxExecute(strExecute);
276
 
    
277
 
#endif
278
 
 
279
 
    if (0 != m_lBOINCCoreProcessId) {
280
 
        m_bBOINCStartedByManager = true;
281
 
        bReturnValue = true;
282
 
    }
283
 
 
284
 
    wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::StartupBOINCCore - Function End"));
285
 
    return bReturnValue;
286
 
}
287
 
 
288
 
 
289
 
#if defined(__WXMSW__)
290
 
 
291
 
void CBOINCClientManager::ShutdownBOINCCore() {
292
 
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::ShutdownBOINCCore - Function Begin"));
293
 
 
294
 
    CMainDocument*     pDoc = wxGetApp().GetDocument();
295
 
    wxInt32            iCount = 0;
296
 
    DWORD              dwExitCode = 0;
297
 
    bool               bClientQuit = false;
298
 
    wxString           strConnectedCompter = wxEmptyString;
299
 
    wxString           strPassword = wxEmptyString;
300
 
 
301
 
    wxASSERT(pDoc);
302
 
    wxASSERT(wxDynamicCast(pDoc, CMainDocument));
303
 
 
304
 
    if (m_bBOINCStartedByManager) {
305
 
        if (IsBOINCConfiguredAsDaemon()) {
306
 
            StopBOINCService();
307
 
            bClientQuit = true;
308
 
        } else {
309
 
            pDoc->GetConnectedComputerName(strConnectedCompter);
310
 
            if (!pDoc->IsComputerNameLocal(strConnectedCompter)) {
311
 
                RPC_CLIENT rpc;
312
 
                if (!rpc.init("localhost")) {
313
 
                    pDoc->m_pNetworkConnection->GetLocalPassword(strPassword);
314
 
                    rpc.authorize((const char*)strPassword.mb_str());
315
 
                    if (GetExitCodeProcess(m_hBOINCCoreProcess, &dwExitCode)) {
316
 
                        if (STILL_ACTIVE == dwExitCode) {
317
 
                            rpc.quit();
318
 
                            for (iCount = 0; iCount <= 10; iCount++) {
319
 
                                if (!bClientQuit && GetExitCodeProcess(m_hBOINCCoreProcess, &dwExitCode)) {
320
 
                                    if (STILL_ACTIVE != dwExitCode) {
321
 
                                        wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - (localhost) Application Exit Detected"));
322
 
                                        bClientQuit = true;
323
 
                                        break;
324
 
                                    }
325
 
                                }
326
 
                                wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - (localhost) Application Exit NOT Detected, Sleeping..."));
327
 
                                ::wxSleep(1);
328
 
                            }
329
 
                        }
330
 
                    }
331
 
                }
332
 
                rpc.close();
333
 
            } else {
334
 
                if (GetExitCodeProcess(m_hBOINCCoreProcess, &dwExitCode)) {
335
 
                    if (STILL_ACTIVE == dwExitCode) {
336
 
                        pDoc->CoreClientQuit();
337
 
                        for (iCount = 0; iCount <= 10; iCount++) {
338
 
                            if (!bClientQuit && GetExitCodeProcess(m_hBOINCCoreProcess, &dwExitCode)) {
339
 
                                if (STILL_ACTIVE != dwExitCode) {
340
 
                                    wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - Application Exit Detected"));
341
 
                                    bClientQuit = true;
342
 
                                    break;
343
 
                                }
344
 
                            }
345
 
                            wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - Application Exit NOT Detected, Sleeping..."));
346
 
                            ::wxSleep(1);
347
 
                        }
348
 
                    }
349
 
                }
350
 
            }
351
 
        }
352
 
 
353
 
        if (!bClientQuit) {
354
 
            ::wxKill(m_lBOINCCoreProcessId);
355
 
        }
356
 
        m_lBOINCCoreProcessId = 0;
357
 
    }
358
 
 
359
 
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::ShutdownBOINCCore - Function End"));
360
 
}
361
 
 
362
 
#elif defined(__WXMAC__)
363
 
 
364
 
static char * PersistentFGets(char *buf, size_t buflen, FILE *f) {
365
 
    char *p = buf;
366
 
    size_t len = buflen;
367
 
    size_t datalen = 0;
368
 
 
369
 
    *buf = '\0';
370
 
    while (datalen < (buflen - 1)) {
371
 
        fgets(p, len, f);
372
 
        if (feof(f)) break;
373
 
        if (ferror(f) && (errno != EINTR)) break;
374
 
        if (strchr(buf, '\n')) break;
375
 
        datalen = strlen(buf);
376
 
        p = buf + datalen;
377
 
        len -= datalen;
378
 
    }
379
 
    return (buf[0] ? buf : NULL);
380
 
}
381
 
 
382
 
bool CBOINCClientManager::ProcessExists(pid_t thePID)
383
 
{
384
 
    FILE *f;
385
 
    char buf[256];
386
 
    pid_t aPID;
387
 
 
388
 
    f = popen("ps -a -x -c -o pid,state", "r");
389
 
    if (f == NULL)
390
 
        return false;
391
 
    
392
 
    while (PersistentFGets(buf, sizeof(buf), f)) {
393
 
        aPID = atol(buf);
394
 
        if (aPID == thePID) {
395
 
            if (strchr(buf, 'Z'))   // A 'zombie', stopped but waiting
396
 
                break;              // for us (its parent) to quit
397
 
            pclose(f);
398
 
            return true;
399
 
        }
400
 
    }
401
 
    pclose(f);
402
 
    return false;
403
 
}
404
 
 
405
 
// wxProcess::Exists and wxKill are unimplemented in WxMac-2.6.0
406
 
void CBOINCClientManager::ShutdownBOINCCore() {
407
 
    CMainDocument*     pDoc = wxGetApp().GetDocument();
408
 
    wxInt32            iCount = 0;
409
 
    wxString           strConnectedCompter = wxEmptyString;
410
 
    wxString           strPassword = wxEmptyString;
411
 
 
412
 
    wxASSERT(pDoc);
413
 
    wxASSERT(wxDynamicCast(pDoc, CMainDocument));
414
 
 
415
 
    if (m_bBOINCStartedByManager) {
416
 
        pDoc->GetConnectedComputerName(strConnectedCompter);
417
 
        if (!pDoc->IsComputerNameLocal(strConnectedCompter)) {
418
 
            RPC_CLIENT rpc;
419
 
            if (!rpc.init("localhost")) {
420
 
                pDoc->m_pNetworkConnection->GetLocalPassword(strPassword);
421
 
                rpc.authorize((const char*)strPassword.mb_str());
422
 
                if (ProcessExists(m_lBOINCCoreProcessId)) {
423
 
                    rpc.quit();
424
 
                    for (iCount = 0; iCount <= 10; iCount++) {
425
 
                        if (!ProcessExists(m_lBOINCCoreProcessId))
426
 
                            return;
427
 
                        ::wxSleep(1);
428
 
                    }
429
 
                }
430
 
            }
431
 
            rpc.close();
432
 
        } else {
433
 
            if (ProcessExists(m_lBOINCCoreProcessId)) {
434
 
                pDoc->CoreClientQuit();
435
 
                for (iCount = 0; iCount <= 10; iCount++) {
436
 
                    if (!ProcessExists(m_lBOINCCoreProcessId))
437
 
                        return;
438
 
 
439
 
                    ::wxSleep(1);
440
 
                }
441
 
            }
442
 
        }
443
 
        
444
 
        // Client did not quit after 10 seconds so kill it
445
 
        kill(m_lBOINCCoreProcessId, SIGKILL);
446
 
    }
447
 
    m_lBOINCCoreProcessId = 0;
448
 
}
449
 
 
450
 
#else
451
 
 
452
 
void CBOINCClientManager::ShutdownBOINCCore() {
453
 
    CMainDocument*     pDoc = wxGetApp().GetDocument();
454
 
    wxInt32            iCount = 0;
455
 
    bool               bClientQuit = false;
456
 
    wxString           strConnectedCompter = wxEmptyString;
457
 
    wxString           strPassword = wxEmptyString;
458
 
 
459
 
    wxASSERT(pDoc);
460
 
    wxASSERT(wxDynamicCast(pDoc, CMainDocument));
461
 
 
462
 
    if (m_bBOINCStartedByManager) {
463
 
        pDoc->GetConnectedComputerName(strConnectedCompter);
464
 
        if (!pDoc->IsComputerNameLocal(strConnectedCompter)) {
465
 
            RPC_CLIENT rpc;
466
 
            if (!rpc.init("localhost")) {
467
 
                pDoc->m_pNetworkConnection->GetLocalPassword(strPassword);
468
 
                rpc.authorize((const char*)strPassword.mb_str());
469
 
                if (wxProcess::Exists(m_lBOINCCoreProcessId)) {
470
 
                    rpc.quit();
471
 
                    for (iCount = 0; iCount <= 10; iCount++) {
472
 
                        if (!bClientQuit && !wxProcess::Exists(m_lBOINCCoreProcessId)) {
473
 
                            bClientQuit = true;
474
 
                            break;
475
 
                        }
476
 
                        ::wxSleep(1);
477
 
                    }
478
 
                }
479
 
            }
480
 
            rpc.close();
481
 
        } else {
482
 
            if (wxProcess::Exists(m_lBOINCCoreProcessId)) {
483
 
                pDoc->CoreClientQuit();
484
 
                for (iCount = 0; iCount <= 10; iCount++) {
485
 
                    if (!bClientQuit && !wxProcess::Exists(m_lBOINCCoreProcessId)) {
486
 
                        bClientQuit = true;
487
 
                        break;
488
 
                    }
489
 
                    ::wxSleep(1);
490
 
                }
491
 
            }
492
 
        }
493
 
 
494
 
        if (!bClientQuit) {
495
 
            ::wxKill(m_lBOINCCoreProcessId);
496
 
        }
497
 
    }
498
 
}
499
 
 
500
 
#endif
501
 
 
 
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 "BOINCClientManager.h"
 
20
#endif
 
21
 
 
22
#include "stdwx.h"
 
23
#include "diagnostics.h"
 
24
#include "miofile.h"
 
25
#include "LogBOINC.h"
 
26
#include "BOINCGUIApp.h"
 
27
#include "SkinManager.h"
 
28
#include "MainDocument.h"
 
29
#include "BOINCBaseFrame.h"
 
30
#include "AdvancedFrame.h"
 
31
#include "BOINCClientManager.h"
 
32
#include "error_numbers.h"
 
33
#include "procinfo.h"
 
34
#include "filesys.h"
 
35
#include "daemonmgt.h"
 
36
#include "util.h"
 
37
#include "Events.h"
 
38
#include "version.h"
 
39
 
 
40
// Alert user if Client crashes 3 times in 30 minutes
 
41
#define CLIENT_3_CRASH_MAX_TIME 30
 
42
 
 
43
#ifdef __WXMAC__
 
44
enum {
 
45
    NewStyleDaemon = 1,
 
46
    OldStyleDaemon
 
47
};
 
48
 
 
49
#elif defined(__WXMSW__)
 
50
 
 
51
#include "win_util.h"
 
52
#include "diagnostics_win.h"
 
53
 
 
54
extern int diagnostics_get_process_information(PVOID* ppBuffer, PULONG pcbBuffer);
 
55
 
 
56
#else
 
57
#include <sys/wait.h>
 
58
#endif
 
59
 
 
60
CBOINCClientManager::CBOINCClientManager() {
 
61
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::CBOINCClientManager - Function Begin"));
 
62
 
 
63
    m_bBOINCStartedByManager = false;
 
64
    m_lBOINCCoreProcessId = 0;
 
65
    m_fAutoRestart1Time = 0;
 
66
    m_fAutoRestart2Time = 0;
 
67
 
 
68
#ifdef __WXMSW__
 
69
    m_hBOINCCoreProcess = NULL;
 
70
#endif
 
71
 
 
72
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::CBOINCClientManager - Function End"));
 
73
}
 
74
 
 
75
 
 
76
CBOINCClientManager::~CBOINCClientManager() {
 
77
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::~CBOINCClientManager - Function Begin"));
 
78
 
 
79
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::~CBOINCClientManager - Function End"));
 
80
}
 
81
 
 
82
 
 
83
bool CBOINCClientManager::AutoRestart() {
 
84
    double timeNow, timeDiff;
 
85
    if (IsBOINCCoreRunning()) return true;
 
86
#if ! (defined(__WXMAC__) || defined(__WXMSW__)) 
 
87
// Mac and Windows can restart Client as a daemon, but 
 
88
// Linux may not know Client's location if it didn't start the Client
 
89
    if (!m_bBOINCStartedByManager) return false;
 
90
#endif
 
91
    // Alert user if Client crashes 3 times in CLIENT_3_CRASH_MAX_TIME
 
92
    timeNow = dtime();
 
93
    timeDiff = timeNow - m_fAutoRestart1Time;
 
94
    if ((timeDiff) < (CLIENT_3_CRASH_MAX_TIME * 60)) {
 
95
        int                 response;
 
96
        ClientCrashDlg      *dlg = new ClientCrashDlg(timeDiff);
 
97
        if (dlg) {
 
98
            CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
 
99
            if (!pFrame->IsShown()) {
 
100
                pFrame->Show();
 
101
            }
 
102
            response = dlg->ShowModal();
 
103
            dlg->Destroy();
 
104
            if (response == wxID_CANCEL) return false;
 
105
            timeNow = 0;
 
106
            m_fAutoRestart1Time = 0;
 
107
            m_fAutoRestart2Time = 0;
 
108
        }
 
109
    }
 
110
    m_lBOINCCoreProcessId = 0;
 
111
    m_fAutoRestart1Time = m_fAutoRestart2Time;
 
112
    m_fAutoRestart2Time = timeNow;
 
113
    StartupBOINCCore();
 
114
    return true;
 
115
}
 
116
 
 
117
 
 
118
bool CBOINCClientManager::IsSystemBooting() {
 
119
    bool bReturnValue = false;
 
120
#if   defined(__WXMSW__)
 
121
    if (GetTickCount() < (1000*60*5)) bReturnValue = true;  // If system has been up for less than 5 minutes 
 
122
#elif defined(__WXMAC__)
 
123
    if (TickCount() < (120*60)) bReturnValue = true;        // If system has been up for less than 2 minutes 
 
124
#endif
 
125
    return bReturnValue;
 
126
}
 
127
 
 
128
 
 
129
int CBOINCClientManager::IsBOINCConfiguredAsDaemon() {
 
130
    bool bReturnValue = false;
 
131
#if   defined(__WXMSW__)
 
132
    if (is_daemon_installed()) bReturnValue = 1;
 
133
#elif defined(__WXMAC__)
 
134
    if ( boinc_file_exists("/Library/LaunchDaemons/edu.berkeley.boinc.plist")) {
 
135
        bReturnValue = NewStyleDaemon;                      // New-style daemon uses launchd
 
136
    }
 
137
    if (boinc_file_exists("/Library/StartupItems/boinc/boinc") ) {
 
138
        bReturnValue = OldStyleDaemon;                      // Old-style daemon uses StartupItem
 
139
    }
 
140
#endif
 
141
    return bReturnValue;
 
142
}
 
143
 
 
144
 
 
145
bool CBOINCClientManager::IsBOINCCoreRunning() {
 
146
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Function Begin"));
 
147
    bool running = false;
 
148
 
 
149
#ifdef __WXMSW__
 
150
    char buf[MAX_PATH] = "";
 
151
    
 
152
    if (is_daemon_installed()) {
 
153
        running = (FALSE != is_daemon_starting()) || (FALSE != is_daemon_running());
 
154
    } else {
 
155
        // Global mutex on Win2k and later
 
156
        //
 
157
        if (IsWindows2000Compatible()) {
 
158
            strcpy(buf, "Global\\");
 
159
        }
 
160
        strcat( buf, RUN_MUTEX);
 
161
 
 
162
        HANDLE h = CreateMutexA(NULL, true, buf);
 
163
        DWORD err = GetLastError();
 
164
        if ((h==0) || (err == ERROR_ALREADY_EXISTS)) {
 
165
            running = true;
 
166
        }
 
167
        if (h) {
 
168
            CloseHandle(h);
 
169
        }
 
170
    }
 
171
#elif defined(__WXMAC__)
 
172
    char path[1024];
 
173
    static FILE_LOCK file_lock;
 
174
    
 
175
    sprintf(path, "%s/%s", (const char *)wxGetApp().GetDataDirectory().mb_str(), LOCK_FILE_NAME);
 
176
    if (boinc_file_exists(path)) {   // If there is no lock file, core is not running
 
177
        if (file_lock.lock(path)) {
 
178
            running = true;
 
179
        } else {
 
180
            file_lock.unlock(path);
 
181
        }
 
182
    }
 
183
#else
 
184
    std::vector<PROCINFO> piv;
 
185
    int retval;
 
186
 
 
187
    if (m_lBOINCCoreProcessId) {
 
188
        // Prevent client from being a zombie
 
189
        if (waitpid(m_lBOINCCoreProcessId, 0, WNOHANG) == m_lBOINCCoreProcessId) {
 
190
            m_lBOINCCoreProcessId = 0;
 
191
        }
 
192
    }
 
193
 
 
194
    // Look for BOINC Client in list of all running processes
 
195
    retval = procinfo_setup(piv);
 
196
    if (retval) return false;     // Should never happen
 
197
    
 
198
    for (unsigned int i=0; i<piv.size(); i++) {
 
199
        PROCINFO& pi = piv[i];
 
200
        if (!strcmp(pi.command, "boinc")) {
 
201
            running = true;
 
202
            break;
 
203
        }
 
204
        if (!strcmp(pi.command, "boinc_client")) {
 
205
            running = true;
 
206
            break;
 
207
        }
 
208
    }
 
209
#endif
 
210
    wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Returning '%d'"), (int)running);
 
211
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::IsBOINCCoreRunning - Function End"));
 
212
    return running;
 
213
}
 
214
 
 
215
 
 
216
bool CBOINCClientManager::StartupBOINCCore() {
 
217
    wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::StartupBOINCCore - Function Begin"));
 
218
 
 
219
    bool                bReturnValue = false;
 
220
    wxString            strExecute = wxEmptyString;
 
221
 
 
222
    if (IsBOINCCoreRunning()) return true;
 
223
 
 
224
#if defined(__WXMSW__)
 
225
    LPTSTR  szExecute = NULL;
 
226
    LPTSTR  szDataDirectory = NULL;
 
227
 
 
228
    if (IsBOINCConfiguredAsDaemon()) {
 
229
        start_daemon_via_daemonctrl();
 
230
 
 
231
        m_bBOINCStartedByManager = true;
 
232
        bReturnValue = IsBOINCCoreRunning();
 
233
    } else {
 
234
 
 
235
        // Append boinc.exe to the end of the strExecute string and get ready to rock
 
236
        strExecute.Printf(
 
237
            wxT("\"%s\\boinc.exe\" --redirectio --launched_by_manager %s"),
 
238
            wxGetApp().GetRootDirectory().c_str(),
 
239
            wxGetApp().GetArguments().c_str()
 
240
        );
 
241
 
 
242
        PROCESS_INFORMATION pi;
 
243
        STARTUPINFO         si;
 
244
        BOOL                bProcessStarted;
 
245
 
 
246
        memset(&pi, 0, sizeof(pi));
 
247
        memset(&si, 0, sizeof(si));
 
248
 
 
249
        si.cb = sizeof(si);
 
250
        si.dwFlags = STARTF_USESHOWWINDOW;
 
251
        si.wShowWindow = SW_HIDE;
 
252
 
 
253
        szExecute = (LPTSTR)strExecute.c_str();
 
254
        if (wxGetApp().GetDataDirectory().empty()) {
 
255
            szDataDirectory = NULL;
 
256
        } else {
 
257
            szDataDirectory = (LPTSTR)wxGetApp().GetDataDirectory().c_str();
 
258
        }
 
259
 
 
260
        wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szExecute '%s'\n"), szExecute);
 
261
        wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szDataDirectory '%s'\n"), szDataDirectory);
 
262
 
 
263
        bProcessStarted = CreateProcess(
 
264
            NULL,
 
265
            szExecute,
 
266
            NULL,
 
267
            NULL,
 
268
            FALSE,
 
269
            CREATE_NEW_PROCESS_GROUP|CREATE_NO_WINDOW,
 
270
            NULL,
 
271
            szDataDirectory,
 
272
            &si,
 
273
            &pi
 
274
        );
 
275
 
 
276
        if (bProcessStarted) {
 
277
            m_lBOINCCoreProcessId = pi.dwProcessId;
 
278
            m_hBOINCCoreProcess = pi.hProcess;
 
279
        }
 
280
    }
 
281
 
 
282
#elif defined(__WXMAC__)
 
283
 
 
284
#if 0   // The Mac version of wxExecute(wxString& ...) crashes if there is a space in the path
 
285
    wxChar buf[1024];
 
286
    wxChar *argv[5];
 
287
#else
 
288
    char buf[1024];
 
289
    char *argv[5];
 
290
#endif
 
291
    ProcessSerialNumber ourPSN;
 
292
    FSRef ourFSRef;
 
293
    OSErr err;
 
294
 
 
295
    if (IsBOINCConfiguredAsDaemon() == NewStyleDaemon) {
 
296
        system ("launchctl load /Library/LaunchDaemons/edu.berkeley.boinc.plist");
 
297
        system ("launchctl start edu.berkeley.boinc");
 
298
        bReturnValue = IsBOINCCoreRunning();
 
299
    } else {
 
300
        
 
301
        // Get the full path to core client inside this application's bundle
 
302
        err = GetCurrentProcess (&ourPSN);
 
303
        if (err == noErr) {
 
304
            err = GetProcessBundleLocation(&ourPSN, &ourFSRef);
 
305
        }
 
306
        if (err == noErr) {
 
307
            err = FSRefMakePath (&ourFSRef, (UInt8*)buf, sizeof(buf));
 
308
        }
 
309
        if (err == noErr) {
 
310
#if 0   // The Mac version of wxExecute(wxString& ...) crashes if there is a space in the path
 
311
            strExecute = wxT("\"");            
 
312
            strExecute += wxT(buf);
 
313
            strExecute += wxT("/Contents/Resources/boinc\" --redirectio --launched_by_manager");
 
314
            m_lBOINCCoreProcessId = ::wxExecute(strExecute);
 
315
#else   // Use wxExecute(wxChar **argv ...) instead of wxExecute(wxString& ...)
 
316
            strcat(buf, "/Contents/Resources/boinc");
 
317
            argv[0] = buf;
 
318
            argv[1] = "--redirectio";
 
319
            argv[2] = "--launched_by_manager";
 
320
            argv[3] = NULL;
 
321
#ifdef SANDBOX
 
322
            if (!g_use_sandbox) {
 
323
                argv[3] = "--insecure";
 
324
                argv[4] = NULL;
 
325
            }
 
326
#endif
 
327
            // Under wxMac-2.8.0, wxExecute starts a separate thread
 
328
            // to wait for child's termination.
 
329
            // That wxProcessTerminationThread uses a huge amount of processor
 
330
            // time (about 11% of one CPU on 2GHz Intel Dual-Core Mac).
 
331
//                m_lBOINCCoreProcessId = ::wxExecute(argv);
 
332
            run_program(
 
333
                "/Library/Application Support/BOINC Data",
 
334
                buf, argv[3] ? 4 : 3, argv, 0.0, m_lBOINCCoreProcessId
 
335
            );
 
336
#endif
 
337
        } else {
 
338
            buf[0] = '\0';
 
339
        }
 
340
    }
 
341
 
 
342
#else   // Unix based systems
 
343
    wxString savedWD = ::wxGetCwd();
 
344
    
 
345
    wxSetWorkingDirectory(wxGetApp().GetDataDirectory());
 
346
    
 
347
    // Append boinc.exe to the end of the strExecute string and get ready to rock
 
348
    strExecute = wxGetApp().GetRootDirectory() + wxT("boinc --redirectio --launched_by_manager");
 
349
#ifdef SANDBOX
 
350
    if (!g_use_sandbox) {
 
351
        strExecute += wxT(" --insecure");
 
352
    }
 
353
#endif
 
354
 
 
355
    wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szExecute '%s'\n"), strExecute.c_str());
 
356
    wxLogTrace(wxT("Function Status"), wxT("CMainDocument::StartupBOINCCore - szDataDirectory '%s'\n"), wxGetApp().GetDataDirectory().c_str());
 
357
 
 
358
    m_lBOINCCoreProcessId = ::wxExecute(strExecute);
 
359
    
 
360
    wxSetWorkingDirectory(savedWD);
 
361
#endif
 
362
 
 
363
    if (0 != m_lBOINCCoreProcessId) {
 
364
        m_bBOINCStartedByManager = true;
 
365
        bReturnValue = true;
 
366
        // Allow time for daemon to start up so we don't keep relaunching it
 
367
        for (int i=0; i<100; i++) {     // Wait up to 1 seccond in 10 ms increments
 
368
            boinc_sleep(0.01);
 
369
            if (IsBOINCCoreRunning()) break;
 
370
        }
 
371
    }
 
372
 
 
373
    wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::StartupBOINCCore - Function End"));
 
374
    return bReturnValue;
 
375
}
 
376
 
 
377
 
 
378
#ifdef __WXMSW__
 
379
static tstring downcase_string(tstring& orig) {
 
380
    tstring retval = orig;
 
381
    for (size_t i=0; i < retval.length(); i++) {
 
382
        retval[i] = tolower(retval[i]);
 
383
    }
 
384
    return retval;
 
385
}
 
386
 
 
387
 
 
388
void CBOINCClientManager::KillClient() {
 
389
    ULONG                   cbBuffer = 128*1024;    // 128k initial buffer
 
390
    PVOID                   pBuffer = NULL;
 
391
    PSYSTEM_PROCESSES       pProcesses = NULL;
 
392
 
 
393
    if (m_hBOINCCoreProcess != NULL) {
 
394
        kill_program(m_hBOINCCoreProcess);
 
395
        return;
 
396
    }
 
397
 
 
398
    // Get a snapshot of the process and thread information.
 
399
    diagnostics_get_process_information(&pBuffer, &cbBuffer);
 
400
 
 
401
    // Lets start walking the structures to find the good stuff.
 
402
    pProcesses = (PSYSTEM_PROCESSES)pBuffer;
 
403
    do {
 
404
        if (pProcesses->ProcessId) {
 
405
            tstring strProcessName = pProcesses->ProcessName.Buffer;
 
406
            if (downcase_string(strProcessName) == tstring(_T("boinc.exe"))) {
 
407
                TerminateProcessById(pProcesses->ProcessId);
 
408
                break;
 
409
           }
 
410
        }
 
411
 
 
412
        // Move to the next structure if one exists
 
413
        if (!pProcesses->NextEntryDelta) {
 
414
            break;
 
415
        }
 
416
        pProcesses = (PSYSTEM_PROCESSES)(((LPBYTE)pProcesses) + pProcesses->NextEntryDelta);
 
417
    } while (pProcesses);
 
418
 
 
419
    // Release resources
 
420
    if (pBuffer) HeapFree(GetProcessHeap(), NULL, pBuffer);
 
421
}
 
422
 
 
423
#else
 
424
 
 
425
void CBOINCClientManager::KillClient() {
 
426
    std::vector<PROCINFO> piv;
 
427
    int retval;
 
428
    
 
429
    if (m_lBOINCCoreProcessId) {
 
430
        kill_program(m_lBOINCCoreProcessId);
 
431
        return;
 
432
    }
 
433
 
 
434
    retval = procinfo_setup(piv);
 
435
        if (retval) return;     // Should never happen
 
436
    
 
437
    for (unsigned int i=0; i<piv.size(); i++) {
 
438
        PROCINFO& pi = piv[i];
 
439
        if (!strcmp(pi.command, "boinc")) {
 
440
            kill_program(pi.id);
 
441
            break;
 
442
        }
 
443
    }
 
444
}
 
445
#endif
 
446
 
 
447
 
 
448
void CBOINCClientManager::ShutdownBOINCCore() {
 
449
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::ShutdownBOINCCore - Function Begin"));
 
450
 
 
451
    CMainDocument*     pDoc = wxGetApp().GetDocument();
 
452
    wxInt32            iCount = 0;
 
453
    bool               bClientQuit = false;
 
454
    wxString           strConnectedCompter = wxEmptyString;
 
455
    wxString           strPassword = wxEmptyString;
 
456
 
 
457
    wxASSERT(pDoc);
 
458
    wxASSERT(wxDynamicCast(pDoc, CMainDocument));
 
459
 
 
460
#ifdef __WXMAC__
 
461
    // Mac Manager shuts down client only if Manager started client
 
462
    if (!m_bBOINCStartedByManager) return;
 
463
#endif
 
464
 
 
465
#ifdef __WXMSW__
 
466
    if (IsBOINCConfiguredAsDaemon()) {
 
467
        stop_daemon_via_daemonctrl();
 
468
        bClientQuit = true;
 
469
    } else
 
470
#endif
 
471
    {
 
472
        pDoc->GetConnectedComputerName(strConnectedCompter);
 
473
        if (!pDoc->IsComputerNameLocal(strConnectedCompter)) {
 
474
            RPC_CLIENT rpc;
 
475
            if (!rpc.init("localhost")) {
 
476
                pDoc->m_pNetworkConnection->GetLocalPassword(strPassword);
 
477
                rpc.authorize((const char*)strPassword.mb_str());
 
478
                if (IsBOINCCoreRunning()) {
 
479
                    rpc.quit();
 
480
                    for (iCount = 0; iCount <= 10; iCount++) {
 
481
                        if (!bClientQuit && !IsBOINCCoreRunning()) {
 
482
                            wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - (localhost) Application Exit Detected"));
 
483
                            bClientQuit = true;
 
484
                            break;
 
485
                        }
 
486
                        wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - (localhost) Application Exit NOT Detected, Sleeping..."));
 
487
                        ::wxSleep(1);
 
488
                    }
 
489
                } else {
 
490
                    bClientQuit = true;
 
491
                }
 
492
            }
 
493
            rpc.close();
 
494
        } else {
 
495
            if (IsBOINCCoreRunning()) {
 
496
                pDoc->CoreClientQuit();
 
497
                for (iCount = 0; iCount <= 10; iCount++) {
 
498
                    if (!bClientQuit && !IsBOINCCoreRunning()) {
 
499
                        wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - Application Exit Detected"));
 
500
                        bClientQuit = true;
 
501
                        break;
 
502
                    }
 
503
                    wxLogTrace(wxT("Function Status"), wxT("CBOINCClientManager::ShutdownBOINCCore - Application Exit NOT Detected, Sleeping..."));
 
504
                    ::wxSleep(1);
 
505
                }
 
506
            } else {
 
507
                bClientQuit = true;
 
508
            }
 
509
        }
 
510
    }
 
511
 
 
512
    if (!bClientQuit) {
 
513
        KillClient();
 
514
    }
 
515
    m_lBOINCCoreProcessId = 0;
 
516
 
 
517
    wxLogTrace(wxT("Function Start/End"), wxT("CBOINCClientManager::ShutdownBOINCCore - Function End"));
 
518
}
 
519
 
 
520
 
 
521
BEGIN_EVENT_TABLE(ClientCrashDlg, wxDialog)
 
522
    EVT_BUTTON(wxID_HELP, ClientCrashDlg::OnHelp)
 
523
END_EVENT_TABLE()
 
524
 
 
525
IMPLEMENT_CLASS(ClientCrashDlg, wxDialog)
 
526
 
 
527
ClientCrashDlg::ClientCrashDlg(double timeDiff) : wxDialog( NULL, wxID_ANY, wxT(""), wxDefaultPosition ) {
 
528
    wxString            strDialogTitle = wxEmptyString;
 
529
    wxString            strDialogMessage = wxEmptyString;
 
530
    int                 minutes = wxMax((int)((timeDiff + 59.) / 60.), 2);
 
531
    CSkinAdvanced*      pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
 
532
    wxASSERT(pSkinAdvanced);
 
533
    
 
534
    // %s is the application name
 
535
    //    i.e. 'BOINC Manager', 'GridRepublic Manager'
 
536
    strDialogTitle.Printf(
 
537
        _("%s - Unexpected Exit"),
 
538
        pSkinAdvanced->GetApplicationName().c_str()
 
539
    );
 
540
    SetTitle(strDialogTitle.c_str());
 
541
 
 
542
    // 1st %s is the application name
 
543
    //    i.e. 'BOINC Manager', 'GridRepublic Manager'
 
544
    // 2st %s is the project name
 
545
    //    i.e. 'BOINC', 'GridRepublic'
 
546
    strDialogMessage.Printf(
 
547
        _("The %s client has exited unexpectedly 3 times within the last %d minutes.\nWould you like to restart it again?"),
 
548
        pSkinAdvanced->GetApplicationShortName().c_str(),
 
549
        minutes        
 
550
    );
 
551
            
 
552
    wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL );
 
553
    wxBoxSizer *icon_text = new wxBoxSizer( wxHORIZONTAL );
 
554
 
 
555
    icon_text->Add( CreateTextSizer( strDialogMessage ), 0, wxALIGN_CENTER | wxLEFT, 10 );
 
556
    topsizer->Add( icon_text, 1, wxCENTER | wxLEFT|wxRIGHT|wxTOP, 10 );
 
557
    
 
558
    wxStdDialogButtonSizer *sizerBtn = CreateStdDialogButtonSizer(wxYES | wxNO | wxHELP);
 
559
    SetEscapeId(wxID_NO);   // Changes return value of NO button to wxID_CANCEL
 
560
    
 
561
    if ( sizerBtn )
 
562
        topsizer->Add(sizerBtn, 0, wxEXPAND | wxALL, 10 );
 
563
 
 
564
    SetAutoLayout( true );
 
565
    SetSizer( topsizer );
 
566
 
 
567
    topsizer->SetSizeHints( this );
 
568
    topsizer->Fit( this );
 
569
    wxSize size( GetSize() );
 
570
    if (size.x < size.y*3/2)
 
571
    {
 
572
        size.x = size.y*3/2;
 
573
        SetSize( size );
 
574
    }
 
575
 
 
576
    Centre( wxBOTH | wxCENTER_FRAME);
 
577
}
 
578
 
 
579
 
 
580
void ClientCrashDlg::OnHelp(wxCommandEvent& WXUNUSED(eventUnused)) {
 
581
    wxString strURL = wxGetApp().GetSkinManager()->GetAdvanced()->GetOrganizationHelpUrl();
 
582
 
 
583
    wxString wxurl;
 
584
    wxurl.Printf(
 
585
        wxT("%s?target=crash_detection&version=%s&controlid=%d"),
 
586
        strURL.c_str(),
 
587
        wxString(BOINC_VERSION_STRING, wxConvUTF8).c_str(),
 
588
        ID_HELPBOINC
 
589
    );
 
590
    wxLaunchDefaultBrowser(wxurl);
 
591
}