VirtualBox

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

Last change on this file since 69500 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • 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 69500 2017-10-28 15:14:05Z vboxsync $ */
2/** @file
3 *
4 * SVCMAIN - COM out-of-proc server main entry
5 */
6
7/*
8 * Copyright (C) 2004-2017 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#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*/
153static BOOL 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 AssertPtr(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*/
170static BOOL 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 AssertPtr(pfn);
178 if (pfn)
179 fResult = pfn(hWnd);
180 return fResult;
181}
182
183static LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
184{
185 LRESULT rc = 0;
186 switch (msg)
187 {
188 case WM_QUERYENDSESSION:
189 {
190 if (g_pModule)
191 {
192 bool fActiveConnection = g_pModule->HasActiveConnection();
193 if (fActiveConnection)
194 {
195 /* place the VBoxSVC into system shutdown list */
196 ShutdownBlockReasonCreateAPI(hwnd, L"Has active connections.");
197 /* decrease a latency of MonitorShutdown loop */
198 ASMAtomicXchgU32(&dwTimeOut, 100);
199 Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: VBoxSvc has active connections. bActivity = %d. Loc count = %d\n",
200 g_pModule->bActivity, g_pModule->GetLockCount()));
201 }
202 rc = !fActiveConnection;
203 }
204 else
205 AssertMsgFailed(("VBoxSVCWinMain: WM_QUERYENDSESSION: Error: g_pModule is NULL"));
206 break;
207 }
208 case WM_ENDSESSION:
209 {
210 /* Restore timeout of Monitor Shutdown if user canceled system shutdown */
211 if (wParam == FALSE)
212 {
213 ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout);
214 Log(("VBoxSVCWinMain: user canceled system shutdown.\n"));
215 }
216 break;
217 }
218 case WM_DESTROY:
219 {
220 ShutdownBlockReasonDestroyAPI(hwnd);
221 PostQuitMessage(0);
222 break;
223 }
224 default:
225 {
226 rc = DefWindowProc(hwnd, msg, wParam, lParam);
227 }
228 }
229 return rc;
230}
231
232static int CreateMainWindow()
233{
234 int rc = VINF_SUCCESS;
235 Assert(g_hMainWindow == NULL);
236
237 LogFlow(("CreateMainWindow\n"));
238
239 g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
240
241 /* Register the Window Class. */
242 WNDCLASS wc;
243 RT_ZERO(wc);
244
245 wc.style = CS_NOCLOSE;
246 wc.lpfnWndProc = WinMainWndProc;
247 wc.hInstance = g_hInstance;
248 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
249 wc.lpszClassName = MAIN_WND_CLASS;
250
251 ATOM atomWindowClass = RegisterClass(&wc);
252 if (atomWindowClass == 0)
253 {
254 Log(("Failed to register main window class\n"));
255 rc = VERR_NOT_SUPPORTED;
256 }
257 else
258 {
259 /* Create the window. */
260 g_hMainWindow = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
261 MAIN_WND_CLASS, MAIN_WND_CLASS,
262 WS_POPUPWINDOW,
263 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
264 if (g_hMainWindow == NULL)
265 {
266 Log(("Failed to create main window\n"));
267 rc = VERR_NOT_SUPPORTED;
268 }
269 else
270 {
271 SetWindowPos(g_hMainWindow, HWND_TOPMOST, -200, -200, 0, 0,
272 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
273
274 }
275 }
276 return 0;
277}
278
279
280static void DestroyMainWindow()
281{
282 Assert(g_hMainWindow != NULL);
283 Log(("SVCMain: DestroyMainWindow \n"));
284 if (g_hMainWindow != NULL)
285 {
286 DestroyWindow(g_hMainWindow);
287 g_hMainWindow = NULL;
288 if (g_hInstance != NULL)
289 {
290 UnregisterClass(MAIN_WND_CLASS, g_hInstance);
291 g_hInstance = NULL;
292 }
293 }
294}
295
296
297/////////////////////////////////////////////////////////////////////////////
298//
299int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
300{
301 int argc = __argc;
302 char **argv = __argv;
303
304 /*
305 * Need to parse the command line before initializing the VBox runtime so we can
306 * change to the user home directory before logs are being created.
307 */
308 for (int i = 1; i < argc; i++)
309 if ( (argv[i][0] == '/' || argv[i][0] == '-')
310 && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
311 {
312 /* %HOMEDRIVE%%HOMEPATH% */
313 wchar_t wszHome[RTPATH_MAX];
314 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
315 if (cEnv && cEnv < RTPATH_MAX)
316 {
317 DWORD cwc = cEnv; /* doesn't include NUL */
318 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
319 if (cEnv && cEnv < RTPATH_MAX - cwc)
320 {
321 /* If this fails there is nothing we can do. Ignore. */
322 SetCurrentDirectory(wszHome);
323 }
324 }
325 }
326
327 /*
328 * Initialize the VBox runtime without loading
329 * the support driver.
330 */
331 RTR3InitExe(argc, &argv, 0);
332
333 static const RTGETOPTDEF s_aOptions[] =
334 {
335 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
336 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
337 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
338 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
339 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
340 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
341 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
342 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
343 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
344 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
345 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
346 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
347 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
348 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
349 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
350 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
351 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
352 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
353 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
354 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
355 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
356 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
357 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
358 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
359 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
360 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
361 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
362 };
363
364 bool fRun = true;
365 bool fRegister = false;
366 bool fUnregister = false;
367 const char *pszPipeName = NULL;
368 const char *pszLogFile = NULL;
369 uint32_t cHistory = 10; // enable log rotation, 10 files
370 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
371 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
372
373 RTGETOPTSTATE GetOptState;
374 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
375 AssertRC(vrc);
376
377 RTGETOPTUNION ValueUnion;
378 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
379 {
380 switch (vrc)
381 {
382 case 'e':
383 /* already handled above */
384 break;
385
386 case 'u':
387 fUnregister = true;
388 fRun = false;
389 break;
390
391 case 'r':
392 fRegister = true;
393 fRun = false;
394 break;
395
396 case 'f':
397 fUnregister = true;
398 fRegister = true;
399 fRun = false;
400 break;
401
402 case 'H':
403 pszPipeName = ValueUnion.psz;
404 if (!pszPipeName)
405 pszPipeName = "";
406 fRun = false;
407 break;
408
409 case 'F':
410 pszLogFile = ValueUnion.psz;
411 break;
412
413 case 'R':
414 cHistory = ValueUnion.u32;
415 break;
416
417 case 'S':
418 uHistoryFileSize = ValueUnion.u64;
419 break;
420
421 case 'I':
422 uHistoryFileTime = ValueUnion.u32;
423 break;
424
425 case 'h':
426 {
427 TCHAR txt[]= L"Options:\n\n"
428 L"/RegServer:\tregister COM out-of-proc server\n"
429 L"/UnregServer:\tunregister COM out-of-proc server\n"
430 L"/ReregServer:\tunregister and register COM server\n"
431 L"no options:\trun the server";
432 TCHAR title[]=_T("Usage");
433 fRun = false;
434 MessageBox(NULL, txt, title, MB_OK);
435 return 0;
436 }
437
438 case 'V':
439 {
440 char *psz = NULL;
441 RTStrAPrintf(&psz, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
442 PRTUTF16 txt = NULL;
443 RTStrToUtf16(psz, &txt);
444 TCHAR title[]=_T("Version");
445 fRun = false;
446 MessageBox(NULL, txt, title, MB_OK);
447 RTStrFree(psz);
448 RTUtf16Free(txt);
449 return 0;
450 }
451
452 default:
453 /** @todo this assumes that stderr is visible, which is not
454 * true for standard Windows applications. */
455 /* continue on command line errors... */
456 RTGetOptPrintError(vrc, &ValueUnion);
457 }
458 }
459
460 /* Only create the log file when running VBoxSVC normally, but not when
461 * registering/unregistering or calling the helper functionality. */
462 if (fRun)
463 {
464 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
465 char szLogFile[RTPATH_MAX];
466 if (!pszLogFile)
467 {
468 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
469 if (RT_SUCCESS(vrc))
470 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
471 }
472 else
473 {
474 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
475 vrc = VERR_NO_MEMORY;
476 }
477 if (RT_FAILURE(vrc))
478 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
479
480 char szError[RTPATH_MAX + 128];
481 vrc = com::VBoxLogRelCreate("COM Server", szLogFile,
482 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
483 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
484 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
485 cHistory, uHistoryFileTime, uHistoryFileSize,
486 szError, sizeof(szError));
487 if (RT_FAILURE(vrc))
488 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, vrc);
489 }
490
491 /* Set up a build identifier so that it can be seen from core dumps what
492 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
493 static char saBuildID[48];
494 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
495 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
496
497 int nRet = 0;
498 HRESULT hRes = com::Initialize(false /*fGui*/, fRun /*fAutoRegUpdate*/);
499 AssertLogRelMsg(SUCCEEDED(hRes), ("SVCMAIN: init failed: %Rhrc\n", hRes));
500
501 g_pModule = new CExeModule();
502 if(g_pModule == NULL)
503 return RTMsgErrorExit(RTEXITCODE_FAILURE, "not enough memory to create ExeModule.");
504 g_pModule->Init(ObjectMap, hInstance, &LIBID_VirtualBox);
505 g_pModule->dwThreadID = GetCurrentThreadId();
506
507 if (!fRun)
508 {
509#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
510 if (fUnregister)
511 {
512 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
513 nRet = g_pModule->UnregisterServer(TRUE);
514 }
515 if (fRegister)
516 {
517 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
518 nRet = g_pModule->RegisterServer(TRUE);
519 }
520#endif
521 if (pszPipeName)
522 {
523 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
524
525 if (!*pszPipeName)
526 vrc = VERR_INVALID_PARAMETER;
527
528 if (RT_SUCCESS(vrc))
529 {
530 /* do the helper job */
531 SVCHlpServer server;
532 vrc = server.open(pszPipeName);
533 if (RT_SUCCESS(vrc))
534 vrc = server.run();
535 }
536 if (RT_FAILURE(vrc))
537 {
538 Log(("SVCMAIN: Failed to process Helper request (%Rrc).", vrc));
539 nRet = 1;
540 }
541 }
542 }
543 else
544 {
545 g_pModule->StartMonitor();
546#if _WIN32_WINNT >= 0x0400
547 hRes = g_pModule->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
548 _ASSERTE(SUCCEEDED(hRes));
549 hRes = CoResumeClassObjects();
550#else
551 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
552#endif
553 _ASSERTE(SUCCEEDED(hRes));
554
555 if (RT_SUCCESS(CreateMainWindow()))
556 Log(("SVCMain: Main window succesfully created\n"));
557 else
558 Log(("SVCMain: Failed to create main window\n"));
559
560 MSG msg;
561 while (GetMessage(&msg, 0, 0, 0) > 0)
562 {
563 DispatchMessage(&msg);
564 TranslateMessage(&msg);
565 }
566
567 DestroyMainWindow();
568
569 g_pModule->RevokeClassObjects();
570 }
571
572 g_pModule->Term();
573
574 com::Shutdown();
575
576 if(g_pModule)
577 delete g_pModule;
578 g_pModule = NULL;
579
580 Log(("SVCMAIN: Returning, COM server process ends.\n"));
581 return nRet;
582}
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