VirtualBox

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

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

Main/svcmain: coding style

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

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