1
// This file is part of BOINC.
2
// http://boinc.berkeley.edu
3
// Copyright (C) 2008 University of California
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.
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.
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/>.
18
#include "boinc_win.h"
19
#include "diagnostics.h"
20
#include "error_numbers.h"
28
#include "client_state.h"
29
#include "log_flags.h"
30
#include "client_msgs.h"
31
#include "http_curl.h"
35
#include "net_stats.h"
37
#include "sysmon_win.h"
40
static HANDLE g_hWindowsMonitorSystemPowerThread = NULL;
41
static HWND g_hWndWindowsMonitorSystemPower = NULL;
42
static HANDLE g_hWindowsMonitorSystemProxyThread = NULL;
45
// The following 3 functions are called in a separate thread,
46
// so we can't do anything directly.
47
// Set flags telling the main thread what to do.
51
static void quit_client() {
52
gstate.requested_exit = true;
55
if (gstate.cleanup_completed) break;
59
// Suspend client operations
60
static void suspend_client(bool wait) {
61
gstate.requested_suspend = true;
65
if (!gstate.active_tasks.is_task_executing()) break;
70
// Resume client operations
71
static void resume_client() {
72
gstate.requested_resume = true;
75
// Process console messages sent by the system
76
static BOOL WINAPI console_control_handler( DWORD dwCtrlType ){
77
BOOL bReturnStatus = FALSE;
78
BOINCTRACE("***** Console Event Detected *****\n");
80
case CTRL_LOGOFF_EVENT:
81
BOINCTRACE("Event: CTRL-LOGOFF Event\n");
82
if (!gstate.executing_as_daemon) {
88
case CTRL_BREAK_EVENT:
89
BOINCTRACE("Event: CTRL-C or CTRL-BREAK Event\n");
93
case CTRL_CLOSE_EVENT:
94
case CTRL_SHUTDOWN_EVENT:
95
BOINCTRACE("Event: CTRL-CLOSE or CTRL-SHUTDOWN Event\n");
102
// Trap events on Windows so we can clean ourselves up.
103
static LRESULT CALLBACK WindowsMonitorSystemPowerWndProc(
104
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
107
// On Windows power events are broadcast via the WM_POWERBROADCAST
108
// window message. It has the following parameters:
109
// PBT_APMQUERYSUSPEND
110
// PBT_APMQUERYSUSPENDFAILED
112
// PBT_APMRESUMECRITICAL
113
// PBT_APMRESUMESUSPEND
115
// PBT_APMPOWERSTATUSCHANGE
117
// PBT_APMRESUMEAUTOMATIC
118
case WM_POWERBROADCAST:
120
// System is preparing to suspend. This is valid on
121
// Windows versions older than Vista
122
case PBT_APMQUERYSUSPEND:
126
// System is resuming from a failed request to suspend
127
// activity. This is only valid on Windows versions
129
case PBT_APMQUERYSUSPENDFAILED:
133
// System is critically low on battery power. This is
134
// only valid on Windows versions older than Vista
135
case PBT_APMBATTERYLOW:
136
msg_printf(NULL, MSG_INFO, "Critical battery alarm, Windows is suspending operations");
137
suspend_client(true);
140
// System is suspending
142
msg_printf(NULL, MSG_INFO, "Windows is suspending operations");
143
suspend_client(true);
146
// System is resuming from a normal power event
147
case PBT_APMRESUMESUSPEND:
148
msg_printf(NULL, MSG_INFO, "Windows is resuming operations");
151
working_proxy_info.need_autodetect_proxy_settings = true;
160
return (DefWindowProc(hWnd, uMsg, wParam, lParam));
163
// Create a thread to monitor system events
164
static DWORD WINAPI WindowsMonitorSystemPowerThread( LPVOID ) {
168
wc.style = CS_GLOBALCLASS;
169
wc.lpfnWndProc = (WNDPROC)WindowsMonitorSystemPowerWndProc;
175
wc.hbrBackground = NULL;
176
wc.lpszMenuName = NULL;
177
wc.lpszClassName = "BOINCWindowsMonitorSystemPower";
179
if (!RegisterClass(&wc)) {
180
log_message_error("Failed to register the WindowsMonitorSystem window class.");
184
g_hWndWindowsMonitorSystemPower = CreateWindow(
186
"BOINC Monitor System (Power)",
187
WS_OVERLAPPEDWINDOW & ~WS_VISIBLE,
197
if (!g_hWndWindowsMonitorSystemPower) {
198
log_message_error("Failed to create the WindowsMonitorSystem window.");
202
while (GetMessage(&msg, NULL, 0, 0)) {
203
TranslateMessage(&msg);
204
DispatchMessage(&msg);
209
// Detect any proxy configuration settings automatically.
210
static void windows_detect_autoproxy_settings() {
211
if (log_flags.proxy_debug) {
212
msg_printf(NULL, MSG_INFO, "[proxy] automatic proxy check in progress");
215
HMODULE hModWinHttp = LoadLibrary("winhttp.dll");
219
pfnWinHttpOpen pWinHttpOpen =
220
(pfnWinHttpOpen)GetProcAddress(hModWinHttp, "WinHttpOpen");
224
pfnWinHttpCloseHandle pWinHttpCloseHandle =
225
(pfnWinHttpCloseHandle)(GetProcAddress(hModWinHttp, "WinHttpCloseHandle"));
226
if (!pWinHttpCloseHandle) {
229
pfnWinHttpGetProxyForUrl pWinHttpGetProxyForUrl =
230
(pfnWinHttpGetProxyForUrl)(GetProcAddress(hModWinHttp, "WinHttpGetProxyForUrl"));
231
if (!pWinHttpGetProxyForUrl) {
235
HINTERNET hWinHttp = NULL;
236
WINHTTP_AUTOPROXY_OPTIONS autoproxy_options;
237
WINHTTP_PROXY_INFO proxy_info;
239
std::wstring network_test_url;
243
memset(&autoproxy_options, 0, sizeof(autoproxy_options));
244
memset(&proxy_info, 0, sizeof(proxy_info));
246
autoproxy_options.dwFlags =
247
WINHTTP_AUTOPROXY_AUTO_DETECT;
248
autoproxy_options.dwAutoDetectFlags =
249
WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
250
autoproxy_options.fAutoLogonIfChallenged = TRUE;
252
network_test_url = A2W(config.network_test_url).c_str();
254
hWinHttp = pWinHttpOpen(
256
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
257
WINHTTP_NO_PROXY_NAME,
258
WINHTTP_NO_PROXY_BYPASS,
262
if (pWinHttpGetProxyForUrl(hWinHttp, network_test_url.c_str(), &autoproxy_options, &proxy_info)) {
264
if (log_flags.proxy_debug) {
265
msg_printf(NULL, MSG_INFO, "[proxy] successfully executed proxy check", hWinHttp);
268
// Apparently there are some conditions where WinHttpGetProxyForUrl can return
269
// success but where proxy_info.lpszProxy is null. Maybe related to UPNP?
271
// For the time being check to see if proxy_info.lpszProxy is non-null.
273
if (proxy_info.lpszProxy) {
274
std::string proxy(W2A(std::wstring(proxy_info.lpszProxy)));
275
std::string new_proxy;
277
if (log_flags.proxy_debug) {
278
msg_printf(NULL, MSG_INFO, "[proxy] proxy list '%s'", proxy.c_str());
281
if (!proxy.empty()) {
282
// Trim string if more than one proxy is defined
283
// proxy list is defined as:
284
// ([<scheme>=][<scheme>"://"]<server>[":"<port>])
286
// Find and erase first delimeter type.
287
pos = proxy.find(';');
289
new_proxy = proxy.erase(pos);
293
// Find and erase second delimeter type.
294
pos = proxy.find(' ');
296
new_proxy = proxy.erase(pos);
300
// Parse the remaining url
301
parse_url(proxy.c_str(), purl);
303
// Store the results for future use.
304
if (0 != strcmp(working_proxy_info.autodetect_server_name, purl.host)) {
305
// Reset clients connection error detection path
306
net_status.need_physical_connection = false;
308
working_proxy_info.autodetect_protocol = purl.protocol;
309
strcpy(working_proxy_info.autodetect_server_name, purl.host);
310
working_proxy_info.autodetect_port = purl.port;
313
if (log_flags.proxy_debug) {
314
msg_printf(NULL, MSG_INFO,
315
"[proxy] automatic proxy detected %s:%d",
323
if (proxy_info.lpszProxy) GlobalFree(proxy_info.lpszProxy);
324
if (proxy_info.lpszProxyBypass) GlobalFree(proxy_info.lpszProxyBypass);
326
// We can get here if the user is switching from a network that
327
// requires a proxy to one that does not require a proxy.
328
working_proxy_info.autodetect_protocol = 0;
329
strcpy(working_proxy_info.autodetect_server_name, "");
330
working_proxy_info.autodetect_port = 0;
331
if (log_flags.proxy_debug) {
332
msg_printf(NULL, MSG_INFO, "[proxy] no automatic proxy detected");
335
if (hWinHttp) pWinHttpCloseHandle(hWinHttp);
336
FreeLibrary(hModWinHttp);
337
if (log_flags.proxy_debug) {
338
msg_printf(NULL, MSG_INFO, "[proxy] automatic proxy check finished");
342
static DWORD WINAPI WindowsMonitorSystemProxyThread( LPVOID ) {
344
// notify the main client thread that detecting proxies is
346
working_proxy_info.autodetect_proxy_supported = true;
350
if (working_proxy_info.need_autodetect_proxy_settings) {
351
working_proxy_info.have_autodetect_proxy_settings = false;
352
windows_detect_autoproxy_settings();
353
working_proxy_info.need_autodetect_proxy_settings = false;
354
working_proxy_info.have_autodetect_proxy_settings = true;
363
// Setup the client software to monitor various system events
364
int initialize_system_monitor(int /*argc*/, char** /*argv*/) {
366
// Windows: install console controls
367
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)console_control_handler, TRUE)){
368
log_message_error("Failed to register the console control handler.");
372
// Create a window to receive system power events.
373
g_hWindowsMonitorSystemPowerThread = CreateThread(
376
WindowsMonitorSystemPowerThread,
381
if (!g_hWindowsMonitorSystemPowerThread) {
382
g_hWindowsMonitorSystemPowerThread = NULL;
383
g_hWndWindowsMonitorSystemPower = NULL;
386
// Create a thread to handle proxy auto-detection.
387
g_hWindowsMonitorSystemProxyThread = CreateThread(
390
WindowsMonitorSystemProxyThread,
395
if (!g_hWindowsMonitorSystemProxyThread) {
396
g_hWindowsMonitorSystemProxyThread = NULL;
402
// Cleanup the system event monitor
403
int cleanup_system_monitor() {
405
if (g_hWindowsMonitorSystemPowerThread) {
406
TerminateThread(g_hWindowsMonitorSystemPowerThread, 0);
407
CloseHandle(g_hWindowsMonitorSystemPowerThread);
408
g_hWindowsMonitorSystemPowerThread = NULL;
409
g_hWndWindowsMonitorSystemPower = NULL;
412
if (g_hWindowsMonitorSystemProxyThread) {
413
TerminateThread(g_hWindowsMonitorSystemProxyThread, 0);
414
CloseHandle(g_hWindowsMonitorSystemProxyThread);
415
g_hWindowsMonitorSystemProxyThread = NULL;
421
// internal variables for managing the service
422
SERVICE_STATUS ssStatus; // current status of the service
423
SERVICE_STATUS_HANDLE sshStatusHandle;
427
SERVICE_TABLE_ENTRY service_dispatch_table[] = {
428
{ TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)BOINCServiceMain },
432
// Inform the service control manager that the service is about to
434
int initialize_service_dispatcher(int /*argc*/, char** /*argv*/) {
435
fprintf(stdout, "\nStartServiceCtrlDispatcher being called.\n");
436
fprintf(stdout, "This may take several seconds. Please wait.\n");
438
if (!StartServiceCtrlDispatcher(service_dispatch_table)) {
439
log_message_error("StartServiceCtrlDispatcher failed.");
446
// FUNCTION: BOINCServiceMain
448
// PURPOSE: To perform actual initialization of the service
451
// dwArgc - number of command line arguments
452
// lpszArgv - array of command line arguments
458
// This routine performs the service initialization and then calls
459
// the user defined main() routine to perform majority
462
void WINAPI BOINCServiceMain(DWORD /*dwArgc*/, LPTSTR * /*lpszArgv*/)
464
// SERVICE_STATUS members that don't change in example
466
ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
467
ssStatus.dwControlsAccepted = SERVICE_ACCEPTED_ACTIONS;
468
ssStatus.dwServiceSpecificExitCode = 0;
471
// register our service control handler:
473
sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), BOINCServiceCtrl);
474
if (!sshStatusHandle)
478
SERVICE_RUNNING, // service state
479
ERROR_SUCCESS, // exit code
483
dwErr = boinc_main_loop();
487
// try to report the stopped status to the service control manager.
498
// FUNCTION: BOINCServiceCtrl
500
// PURPOSE: This function is called by the SCM whenever
501
// ControlService() is called on this service.
504
// dwCtrlCode - type of control requested
511
VOID WINAPI BOINCServiceCtrl(DWORD dwCtrlCode)
513
// Handle the requested control code.
519
// SERVICE_STOP_PENDING should be reported before
520
// setting the Stop Event - hServerStopEvent - in
521
// ServiceStop(). This avoids a race condition
522
// which may result in a 1053 - The Service did not respond...
524
case SERVICE_CONTROL_STOP:
525
case SERVICE_CONTROL_SHUTDOWN:
526
ReportStatus(SERVICE_STOP_PENDING, ERROR_SUCCESS, 30000);
530
// Pause the service.
532
case SERVICE_CONTROL_PAUSE:
533
ReportStatus(SERVICE_PAUSE_PENDING, ERROR_SUCCESS, 10000);
534
suspend_client(true);
535
ReportStatus(SERVICE_PAUSED, ERROR_SUCCESS, 10000);
538
// Continue the service.
540
case SERVICE_CONTROL_CONTINUE:
541
ReportStatus(SERVICE_CONTINUE_PENDING, ERROR_SUCCESS, 10000);
543
ReportStatus(SERVICE_RUNNING, ERROR_SUCCESS, 10000);
546
// Update the service status.
548
case SERVICE_CONTROL_INTERROGATE:
551
// invalid control code
558
ReportStatus(ssStatus.dwCurrentState, ERROR_SUCCESS, 1000);
563
// FUNCTION: ReportStatus()
565
// PURPOSE: Sets the current status of the service and
566
// reports it to the Service Control Manager
569
// dwCurrentState - the state of the service
570
// dwWin32ExitCode - error code to report
571
// dwWaitHint - worst case estimate to next checkpoint
579
BOOL ReportStatus(DWORD dwCurrentState,
580
DWORD dwWin32ExitCode,
583
static DWORD dwCheckPoint = 1;
586
if (dwCurrentState == SERVICE_START_PENDING)
587
ssStatus.dwControlsAccepted = 0;
589
ssStatus.dwControlsAccepted = SERVICE_ACCEPTED_ACTIONS;
591
ssStatus.dwCurrentState = dwCurrentState;
592
ssStatus.dwWin32ExitCode = dwWin32ExitCode;
593
ssStatus.dwWaitHint = dwWaitHint;
595
if ( ( dwCurrentState == SERVICE_RUNNING ) ||
596
( dwCurrentState == SERVICE_STOPPED ) )
597
ssStatus.dwCheckPoint = 0;
599
ssStatus.dwCheckPoint = dwCheckPoint++;
602
// Report the status of the service to the service control manager.
604
fResult = SetServiceStatus( sshStatusHandle, &ssStatus);
606
LogEventErrorMessage(TEXT("SetServiceStatus"));
614
// FUNCTION: LogEventErrorMessage(LPTSTR lpszMsg)
616
// PURPOSE: Allows any thread to log an error message
619
// lpszMsg - text for message
626
VOID LogEventErrorMessage(LPTSTR lpszMsg)
630
LPTSTR lpszStrings[2];
632
dwErr = GetLastError();
634
// Use event logging to log the error.
636
hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
638
_stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
639
lpszStrings[0] = szMsg;
640
lpszStrings[1] = lpszMsg;
642
if (hEventSource != NULL) {
643
ReportEvent(hEventSource, // handle of event source
644
EVENTLOG_ERROR_TYPE, // event type
647
NULL, // current user's SID
648
2, // strings in lpszStrings
649
0, // no bytes of raw data
650
(LPCSTR*)lpszStrings, // array of error strings
651
NULL); // no raw data
653
(VOID) DeregisterEventSource(hEventSource);
659
// FUNCTION: LogEventWarningMessage(LPTSTR lpszMsg)
661
// PURPOSE: Allows any thread to log an warning message
664
// lpszMsg - text for message
671
VOID LogEventWarningMessage(LPTSTR lpszMsg)
674
LPTSTR lpszStrings[2];
676
// Use event logging to log the error.
678
hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
680
lpszStrings[0] = lpszMsg;
681
lpszStrings[1] = '\0';
683
if (hEventSource != NULL) {
684
ReportEvent(hEventSource, // handle of event source
685
EVENTLOG_WARNING_TYPE,// event type
688
NULL, // current user's SID
689
2, // strings in lpszStrings
690
0, // no bytes of raw data
691
(LPCSTR*)lpszStrings, // array of error strings
692
NULL); // no raw data
694
(VOID) DeregisterEventSource(hEventSource);
700
// FUNCTION: LogEventInfoMessage(LPTSTR lpszMsg)
702
// PURPOSE: Allows any thread to log an info message
705
// lpszMsg - text for message
712
VOID LogEventInfoMessage(LPTSTR lpszMsg)
715
LPTSTR lpszStrings[2];
717
// Use event logging to log the error.
719
hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
721
lpszStrings[0] = lpszMsg;
722
lpszStrings[1] = '\0';
724
if (hEventSource != NULL) {
725
ReportEvent(hEventSource, // handle of event source
726
EVENTLOG_INFORMATION_TYPE,// event type
729
NULL, // current user's SID
730
2, // strings in lpszStrings
731
0, // no bytes of raw data
732
(LPCSTR*)lpszStrings, // array of error strings
733
NULL); // no raw data
735
(VOID) DeregisterEventSource(hEventSource);