VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/win/svcmain.cpp@ 65525

Last change on this file since 65525 was 65525, checked in by vboxsync, 8 years ago

Main/VBoxSVC: fix for ticketref:16409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.0 KB
Line 
1/* $Id: svcmain.cpp 65525 2017-01-30 18:38:26Z vboxsync $ */
2/** @file
3 *
4 * SVCMAIN - COM out-of-proc server main entry
5 */
6
7/*
8 * Copyright (C) 2004-2016 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/win/windows.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <tchar.h>
23
24#include "VBox/com/defs.h"
25
26#include "VBox/com/com.h"
27
28#include "VBox/com/VirtualBox.h"
29
30#include "VirtualBoxImpl.h"
31#include "Logging.h"
32
33#include "svchlp.h"
34
35#include <VBox/err.h>
36#include <iprt/buildconfig.h>
37#include <iprt/initterm.h>
38#include <iprt/string.h>
39#include <iprt/uni.h>
40#include <iprt/path.h>
41#include <iprt/getopt.h>
42#include <iprt/message.h>
43#include <iprt\asm.h>
44
45class CExeModule : public ATL::CComModule
46{
47public:
48 LONG Unlock();
49 DWORD dwThreadID;
50 HANDLE hEventShutdown;
51 void MonitorShutdown();
52 bool StartMonitor();
53 bool HasActiveConnection();
54 bool bActivity;
55};
56
57/* Normal timeout usually used in Shutdown Monitor */
58const DWORD dwNormalTimeout = 5000;
59volatile uint32_t dwTimeOut = dwNormalTimeout; /* time for EXE to be idle before shutting down. Can be decreased at system shutdown phase. */
60
61/* Passed to CreateThread to monitor the shutdown event */
62static DWORD WINAPI MonitorProc(void* pv)
63{
64 CExeModule* p = (CExeModule*)pv;
65 p->MonitorShutdown();
66 return 0;
67}
68
69LONG CExeModule::Unlock()
70{
71 LONG l = ATL::CComModule::Unlock();
72 if (l == 0)
73 {
74 bActivity = true;
75 SetEvent(hEventShutdown); /* tell monitor that we transitioned to zero */
76 }
77 return l;
78}
79
80bool CExeModule::HasActiveConnection()
81{
82 return bActivity || GetLockCount() > 0;
83}
84
85/* Monitors the shutdown event */
86void CExeModule::MonitorShutdown()
87{
88 while (1)
89 {
90 WaitForSingleObject(hEventShutdown, INFINITE);
91 DWORD dwWait=0;
92 do
93 {
94 bActivity = false;
95 dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut);
96 } while (dwWait == WAIT_OBJECT_0);
97 /* timed out */
98 if (!HasActiveConnection()) /* if no activity let's really bail */
99 {
100 /* Disable log rotation at this point, worst case a log file
101 * becomes slightly bigger than it should. Avoids quirks with
102 * log rotation: there might be another API service process
103 * running at this point which would rotate the logs concurrently,
104 * creating a mess. */
105 PRTLOGGER pReleaseLogger = RTLogRelGetDefaultInstance();
106 if (pReleaseLogger)
107 {
108 char szDest[1024];
109 int rc = RTLogGetDestinations(pReleaseLogger, szDest, sizeof(szDest));
110 if (RT_SUCCESS(rc))
111 {
112 rc = RTStrCat(szDest, sizeof(szDest), " nohistory");
113 if (RT_SUCCESS(rc))
114 {
115 rc = RTLogDestinations(pReleaseLogger, szDest);
116 AssertRC(rc);
117 }
118 }
119 }
120#if _WIN32_WINNT >= 0x0400
121 CoSuspendClassObjects();
122 if (!HasActiveConnection())
123#endif
124 break;
125 }
126 }
127 CloseHandle(hEventShutdown);
128 PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
129}
130
131bool CExeModule::StartMonitor()
132{
133 hEventShutdown = CreateEvent(NULL, false, false, NULL);
134 if (hEventShutdown == NULL)
135 return false;
136 DWORD dwThreadID;
137 HANDLE h = CreateThread(NULL, 0, MonitorProc, this, 0, &dwThreadID);
138 return (h != NULL);
139}
140
141
142BEGIN_OBJECT_MAP(ObjectMap)
143 OBJECT_ENTRY(CLSID_VirtualBox, VirtualBox)
144END_OBJECT_MAP()
145
146CExeModule* g_pModule = NULL;
147HWND g_hMainWindow = NULL;
148HINSTANCE g_hInstance = NULL;
149#define MAIN_WND_CLASS L"VirtualBox Interface"
150
151/*
152* Wrapper for Win API function ShutdownBlockReasonCreate
153* This function defined starting from Vista only.
154*/
155BOOL ShutdownBlockReasonCreateAPI(HWND hWnd,LPCWSTR pwszReason)
156{
157 BOOL fResult = FALSE;
158 typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONCREATE)(HWND hWnd, LPCWSTR pwszReason);
159
160 PFNSHUTDOWNBLOCKREASONCREATE pfn = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(
161 GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
162 _ASSERTE(pfn);
163 if (pfn)
164 fResult = pfn(hWnd, pwszReason);
165 return fResult;
166}
167
168/*
169* Wrapper for Win API function ShutdownBlockReasonDestroy
170* This function defined starting from Vista only.
171*/
172BOOL ShutdownBlockReasonDestroyAPI(HWND hWnd)
173{
174 BOOL fResult = FALSE;
175 typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONDESTROY)(HWND hWnd);
176
177 PFNSHUTDOWNBLOCKREASONDESTROY pfn = (PFNSHUTDOWNBLOCKREASONDESTROY)GetProcAddress(
178 GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonDestroy");
179 _ASSERTE(pfn);
180 if (pfn)
181 fResult = pfn(hWnd);
182 return fResult;
183}
184
185
186LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
187{
188 LRESULT rc = 0;
189 switch (msg)
190 {
191 case WM_QUERYENDSESSION:
192 {
193 if (g_pModule)
194 {
195 rc = !g_pModule->HasActiveConnection();
196 if (!rc)
197 {
198 /* place the VBoxSVC into system shutdown list */
199 ShutdownBlockReasonCreateAPI(hwnd, L"Has active connections.");
200 /* decrease a latency of MonitorShutdown loop */
201 ASMAtomicXchgU32(&dwTimeOut, 100);
202 Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: VBoxSvc has active connections. bActivity = %d. Loc count = %d\n",
203 g_pModule->bActivity, g_pModule->GetLockCount()));
204 }
205 }
206 else
207 {
208 Assert(g_pModule);
209 Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: Error: g_pModule is NULL"));
210 }
211 } break;
212 case WM_ENDSESSION:
213 {
214 /* Restore timeout of Monitor Shutdown if user canceled system shutdown */
215 if (wParam == FALSE)
216 {
217 ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout);
218 Log(("VBoxSVCWinMain: user canceled system shutdown.\n"));
219 }
220 } break;
221 case WM_DESTROY:
222 {
223 ShutdownBlockReasonDestroyAPI(hwnd);
224 PostQuitMessage(0);
225 } break;
226
227 default:
228 {
229 rc = DefWindowProc(hwnd, msg, wParam, lParam);
230 }
231 }
232 return rc;
233}
234
235
236int CreateMainWindow()
237{
238 int rc = VINF_SUCCESS;
239 _ASSERTE(g_hMainWindow == NULL);
240
241 LogFlow(("CreateMainWindow\n"));
242
243 g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
244
245 /* Register the Window Class. */
246 WNDCLASS wc;
247 RT_ZERO(wc);
248
249 wc.style = CS_NOCLOSE;
250 wc.lpfnWndProc = WinMainWndProc;
251 wc.hInstance = g_hInstance;
252 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
253 wc.lpszClassName = MAIN_WND_CLASS;
254
255
256 ATOM atomWindowClass = RegisterClass(&wc);
257
258 if (atomWindowClass == 0)
259 {
260 Log(("Failed to register main window class\n"));
261 rc = VERR_NOT_SUPPORTED;
262 }
263 else
264 {
265 /* Create the window. */
266 g_hMainWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
267 MAIN_WND_CLASS, MAIN_WND_CLASS,
268 WS_POPUPWINDOW,
269 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
270
271 if (g_hMainWindow == NULL)
272 {
273 Log(("Failed to create main window\n"));
274 rc = VERR_NOT_SUPPORTED;
275 }
276 else
277 {
278 SetWindowPos(g_hMainWindow, HWND_TOPMOST, -200, -200, 0, 0,
279 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
280
281 }
282 }
283 return 0;
284}
285
286
287void DestroyMainWindow()
288{
289 _ASSERTE(g_hMainWindow != NULL);
290 Log(("SVCMain: DestroyMainWindow \n"));
291 if (g_hMainWindow != NULL)
292 {
293 DestroyWindow(g_hMainWindow);
294 g_hMainWindow = NULL;
295
296 if (g_hInstance != NULL)
297 {
298 UnregisterClass(MAIN_WND_CLASS, g_hInstance);
299 g_hInstance = NULL;
300 }
301 }
302}
303
304
305/////////////////////////////////////////////////////////////////////////////
306//
307int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
308{
309 int argc = __argc;
310 char **argv = __argv;
311
312 /*
313 * Need to parse the command line before initializing the VBox runtime so we can
314 * change to the user home directory before logs are being created.
315 */
316 for (int i = 1; i < argc; i++)
317 if ( (argv[i][0] == '/' || argv[i][0] == '-')
318 && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
319 {
320 /* %HOMEDRIVE%%HOMEPATH% */
321 wchar_t wszHome[RTPATH_MAX];
322 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
323 if (cEnv && cEnv < RTPATH_MAX)
324 {
325 DWORD cwc = cEnv; /* doesn't include NUL */
326 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
327 if (cEnv && cEnv < RTPATH_MAX - cwc)
328 {
329 /* If this fails there is nothing we can do. Ignore. */
330 SetCurrentDirectory(wszHome);
331 }
332 }
333 }
334
335 /*
336 * Initialize the VBox runtime without loading
337 * the support driver.
338 */
339 RTR3InitExe(argc, &argv, 0);
340
341
342 /* Note that all options are given lowercase/camel case/uppercase to
343 * approximate case insensitive matching, which RTGetOpt doesn't offer. */
344 static const RTGETOPTDEF s_aOptions[] =
345 {
346 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
347 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
348 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
349 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
350 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
351 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
352 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
353 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
354 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
355 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
356 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
357 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
358 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
359 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
360 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
361 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
362 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
363 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
364 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
365 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
366 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
367 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
368 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
369 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
370 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
371 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
372 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
373 };
374
375 bool fRun = true;
376 bool fRegister = false;
377 bool fUnregister = false;
378 const char *pszPipeName = NULL;
379 const char *pszLogFile = NULL;
380 uint32_t cHistory = 10; // enable log rotation, 10 files
381 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
382 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
383
384 RTGETOPTSTATE GetOptState;
385 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
386 AssertRC(vrc);
387
388 RTGETOPTUNION ValueUnion;
389 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
390 {
391 switch (vrc)
392 {
393 case 'e':
394 /* already handled above */
395 break;
396
397 case 'u':
398 fUnregister = true;
399 fRun = false;
400 break;
401
402 case 'r':
403 fRegister = true;
404 fRun = false;
405 break;
406
407 case 'f':
408 fUnregister = true;
409 fRegister = true;
410 fRun = false;
411 break;
412
413 case 'H':
414 pszPipeName = ValueUnion.psz;
415 if (!pszPipeName)
416 pszPipeName = "";
417 fRun = false;
418 break;
419
420 case 'F':
421 pszLogFile = ValueUnion.psz;
422 break;
423
424 case 'R':
425 cHistory = ValueUnion.u32;
426 break;
427
428 case 'S':
429 uHistoryFileSize = ValueUnion.u64;
430 break;
431
432 case 'I':
433 uHistoryFileTime = ValueUnion.u32;
434 break;
435
436 case 'h':
437 {
438 TCHAR txt[]= L"Options:\n\n"
439 L"/RegServer:\tregister COM out-of-proc server\n"
440 L"/UnregServer:\tunregister COM out-of-proc server\n"
441 L"/ReregServer:\tunregister and register COM server\n"
442 L"no options:\trun the server";
443 TCHAR title[]=_T("Usage");
444 fRun = false;
445 MessageBox(NULL, txt, title, MB_OK);
446 return 0;
447 }
448
449 case 'V':
450 {
451 char *psz = NULL;
452 RTStrAPrintf(&psz, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
453 PRTUTF16 txt = NULL;
454 RTStrToUtf16(psz, &txt);
455 TCHAR title[]=_T("Version");
456 fRun = false;
457 MessageBox(NULL, txt, title, MB_OK);
458 RTStrFree(psz);
459 RTUtf16Free(txt);
460 return 0;
461 }
462
463 default:
464 /** @todo this assumes that stderr is visible, which is not
465 * true for standard Windows applications. */
466 /* continue on command line errors... */
467 RTGetOptPrintError(vrc, &ValueUnion);
468 }
469 }
470
471 /* Only create the log file when running VBoxSVC normally, but not when
472 * registering/unregistering or calling the helper functionality. */
473 if (fRun)
474 {
475 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
476 char szLogFile[RTPATH_MAX];
477 if (!pszLogFile)
478 {
479 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
480 if (RT_SUCCESS(vrc))
481 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
482 }
483 else
484 {
485 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
486 vrc = VERR_NO_MEMORY;
487 }
488 if (RT_FAILURE(vrc))
489 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
490
491 char szError[RTPATH_MAX + 128];
492 vrc = com::VBoxLogRelCreate("COM Server", szLogFile,
493 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
494 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
495 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
496 cHistory, uHistoryFileTime, uHistoryFileSize,
497 szError, sizeof(szError));
498 if (RT_FAILURE(vrc))
499 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, vrc);
500 }
501
502 /* Set up a build identifier so that it can be seen from core dumps what
503 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
504 static char saBuildID[48];
505 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
506 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
507
508 int nRet = 0;
509 HRESULT hRes = com::Initialize(false /*fGui*/, fRun /*fAutoRegUpdate*/);
510 _ASSERTE(SUCCEEDED(hRes));
511
512 g_pModule = new CExeModule();
513 if(g_pModule == NULL)
514 return RTMsgErrorExit(RTEXITCODE_FAILURE, "not enough memory to create ExeModule.");
515 g_pModule->Init(ObjectMap, hInstance, &LIBID_VirtualBox);
516 g_pModule->dwThreadID = GetCurrentThreadId();
517
518 if (!fRun)
519 {
520#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
521 if (fUnregister)
522 {
523 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
524 nRet = g_pModule->UnregisterServer(TRUE);
525 }
526 if (fRegister)
527 {
528 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
529 nRet = g_pModule->RegisterServer(TRUE);
530 }
531#endif
532 if (pszPipeName)
533 {
534 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
535
536 if (!*pszPipeName)
537 vrc = VERR_INVALID_PARAMETER;
538
539 if (RT_SUCCESS(vrc))
540 {
541 /* do the helper job */
542 SVCHlpServer server;
543 vrc = server.open(pszPipeName);
544 if (RT_SUCCESS(vrc))
545 vrc = server.run();
546 }
547 if (RT_FAILURE(vrc))
548 {
549 Log(("SVCMAIN: Failed to process Helper request (%Rrc).", vrc));
550 nRet = 1;
551 }
552 }
553 }
554 else
555 {
556 g_pModule->StartMonitor();
557#if _WIN32_WINNT >= 0x0400
558 hRes = g_pModule->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
559 _ASSERTE(SUCCEEDED(hRes));
560 hRes = CoResumeClassObjects();
561#else
562 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
563#endif
564 _ASSERTE(SUCCEEDED(hRes));
565
566 if (RT_SUCCESS(CreateMainWindow()))
567 {
568 Log(("SVCMain: Main window succesfully created\n"));
569 }
570 else
571 {
572 Log(("SVCMain: Failed to create main window\n"));
573 }
574
575 MSG msg;
576 while (GetMessage(&msg, 0, 0, 0) > 0)
577 {
578 DispatchMessage(&msg);
579 TranslateMessage(&msg);
580 }
581
582 DestroyMainWindow();
583
584 g_pModule->RevokeClassObjects();
585 }
586
587 g_pModule->Term();
588
589 com::Shutdown();
590
591 if(g_pModule)
592 delete g_pModule;
593 g_pModule = NULL;
594
595 Log(("SVCMAIN: Returning, COM server process ends.\n"));
596 return nRet;
597}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette