VirtualBox

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

Last change on this file since 90441 was 89202, checked in by vboxsync, 4 years ago

VBoxSVC: Add a bit more release logging around WM_QUERYENDSESSION.
Make the monitoring window truely hidden. Add placeholder for
SetConsoleCtrlHandler - we won't get window events in the context of
an autostart session. bugref:8161.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.6 KB
Line 
1/* $Id: svcmain.cpp 89202 2021-05-20 14:32:08Z vboxsync $ */
2/** @file
3 * SVCMAIN - COM out-of-proc server main entry
4 */
5
6/*
7 * Copyright (C) 2004-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_VBOXSVC
23#include <iprt/win/windows.h>
24#ifdef DEBUG_bird
25# include <RpcAsync.h>
26#endif
27
28#include "VBox/com/defs.h"
29#include "VBox/com/com.h"
30#include "VBox/com/VirtualBox.h"
31
32#include "VirtualBoxImpl.h"
33#include "LoggingNew.h"
34
35#include "svchlp.h"
36
37#include <iprt/errcore.h>
38#include <iprt/buildconfig.h>
39#include <iprt/initterm.h>
40#include <iprt/string.h>
41#include <iprt/path.h>
42#include <iprt/getopt.h>
43#include <iprt/message.h>
44#include <iprt/asm.h>
45
46
47/*********************************************************************************************************************************
48* Defined Constants And Macros *
49*********************************************************************************************************************************/
50#define MAIN_WND_CLASS L"VirtualBox Interface"
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56class CExeModule : public ATL::CComModule
57{
58public:
59 LONG Unlock();
60 DWORD dwThreadID;
61 HANDLE hEventShutdown;
62 void MonitorShutdown();
63 bool StartMonitor();
64 bool HasActiveConnection();
65 bool bActivity;
66 static bool isIdleLockCount(LONG cLocks);
67};
68
69
70/*********************************************************************************************************************************
71* Global Variables *
72*********************************************************************************************************************************/
73BEGIN_OBJECT_MAP(ObjectMap)
74 OBJECT_ENTRY(CLSID_VirtualBox, VirtualBox)
75END_OBJECT_MAP()
76
77CExeModule *g_pModule = NULL;
78HWND g_hMainWindow = NULL;
79HINSTANCE g_hInstance = NULL;
80#ifdef VBOX_WITH_SDS
81/** This is set if we're connected to SDS.
82 *
83 * It means that we should discount a server lock that it is holding when
84 * deciding whether we're idle or not.
85 *
86 * Also, when set we deregister with SDS during class factory destruction. We
87 * exploit this to prevent attempts to deregister during or after COM shutdown.
88 */
89bool g_fRegisteredWithVBoxSDS = false;
90#endif
91
92/* Normal timeout usually used in Shutdown Monitor */
93const DWORD dwNormalTimeout = 5000;
94volatile uint32_t dwTimeOut = dwNormalTimeout; /* time for EXE to be idle before shutting down. Can be decreased at system shutdown phase. */
95
96
97
98/** Passed to CreateThread to monitor the shutdown event. */
99static DWORD WINAPI MonitorProc(void *pv) RT_NOTHROW_DEF
100{
101 CExeModule *p = (CExeModule *)pv;
102 p->MonitorShutdown();
103 return 0;
104}
105
106LONG CExeModule::Unlock()
107{
108 LONG cLocks = ATL::CComModule::Unlock();
109 if (isIdleLockCount(cLocks))
110 {
111 bActivity = true;
112 SetEvent(hEventShutdown); /* tell monitor that we transitioned to zero */
113 }
114 return cLocks;
115}
116
117bool CExeModule::HasActiveConnection()
118{
119 return bActivity || !isIdleLockCount(GetLockCount());
120}
121
122/**
123 * Checks if @a cLocks signifies an IDLE server lock load.
124 *
125 * This takes VBoxSDS into account (i.e. ignores it).
126 */
127/*static*/ bool CExeModule::isIdleLockCount(LONG cLocks)
128{
129#ifdef VBOX_WITH_SDS
130 if (g_fRegisteredWithVBoxSDS)
131 return cLocks <= 1;
132#endif
133 return cLocks <= 0;
134}
135
136/* Monitors the shutdown event */
137void CExeModule::MonitorShutdown()
138{
139 while (1)
140 {
141 WaitForSingleObject(hEventShutdown, INFINITE);
142 DWORD dwWait;
143 do
144 {
145 bActivity = false;
146 dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut);
147 } while (dwWait == WAIT_OBJECT_0);
148 /* timed out */
149 if (!HasActiveConnection()) /* if no activity let's really bail */
150 {
151 /* Disable log rotation at this point, worst case a log file
152 * becomes slightly bigger than it should. Avoids quirks with
153 * log rotation: there might be another API service process
154 * running at this point which would rotate the logs concurrently,
155 * creating a mess. */
156 PRTLOGGER pReleaseLogger = RTLogRelGetDefaultInstance();
157 if (pReleaseLogger)
158 {
159 char szDest[1024];
160 int rc = RTLogGetDestinations(pReleaseLogger, szDest, sizeof(szDest));
161 if (RT_SUCCESS(rc))
162 {
163 rc = RTStrCat(szDest, sizeof(szDest), " nohistory");
164 if (RT_SUCCESS(rc))
165 {
166 rc = RTLogDestinations(pReleaseLogger, szDest);
167 AssertRC(rc);
168 }
169 }
170 }
171#if _WIN32_WINNT >= 0x0400
172 CoSuspendClassObjects();
173 if (!HasActiveConnection())
174#endif
175 break;
176 }
177 }
178 CloseHandle(hEventShutdown);
179 PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
180}
181
182bool CExeModule::StartMonitor()
183{
184 hEventShutdown = CreateEvent(NULL, false, false, NULL);
185 if (hEventShutdown == NULL)
186 return false;
187 DWORD idThreadIgnored;
188 HANDLE h = CreateThread(NULL, 0, MonitorProc, this, 0, &idThreadIgnored);
189 return (h != NULL);
190}
191
192
193#ifdef VBOX_WITH_SDS
194
195class VBoxSVCRegistration;
196
197/**
198 * Custom class factory for the VirtualBox singleton.
199 *
200 * The implementation of CreateInstance is found in win/svcmain.cpp.
201 */
202class VirtualBoxClassFactory : public ATL::CComClassFactory
203{
204private:
205 /** Tri state: 0=uninitialized or initializing; 1=success; -1=failure.
206 * This will be updated after both m_hrcCreate and m_pObj have been set. */
207 volatile int32_t m_iState;
208 /** The result of the instantiation attempt. */
209 HRESULT m_hrcCreate;
210 /** The IUnknown of the VirtualBox object/interface we're working with. */
211 IUnknown *m_pObj;
212 /** Pointer to the IVBoxSVCRegistration implementation that VBoxSDS works with. */
213 VBoxSVCRegistration *m_pVBoxSVC;
214 /** The VBoxSDS interface. */
215 ComPtr<IVirtualBoxSDS> m_ptrVirtualBoxSDS;
216
217public:
218 VirtualBoxClassFactory() : m_iState(0), m_hrcCreate(S_OK), m_pObj(NULL), m_pVBoxSVC(NULL)
219 { }
220
221 virtual ~VirtualBoxClassFactory()
222 {
223 if (m_pObj)
224 {
225 m_pObj->Release();
226 m_pObj = NULL;
227 }
228
229 /* We usually get here during g_pModule->Term() via CoRevokeClassObjec, so COM
230 probably working well enough to talk to SDS when we get here. */
231 if (g_fRegisteredWithVBoxSDS)
232 i_deregisterWithSds();
233 }
234
235 // IClassFactory
236 STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj);
237
238 /** Worker for VBoxSVCRegistration::getVirtualBox. */
239 HRESULT i_getVirtualBox(IUnknown **ppResult);
240
241private:
242 HRESULT i_registerWithSds(IUnknown **ppOtherVirtualBox);
243 void i_deregisterWithSds(void);
244
245 friend VBoxSVCRegistration;
246};
247
248
249/**
250 * The VBoxSVC class is handed to VBoxSDS so it can call us back and ask for the
251 * VirtualBox object when the next VBoxSVC for this user registers itself.
252 */
253class VBoxSVCRegistration : public IVBoxSVCRegistration
254{
255private:
256 /** Number of references. */
257 uint32_t volatile m_cRefs;
258
259public:
260 /** Pointer to the factory. */
261 VirtualBoxClassFactory *m_pFactory;
262
263public:
264 VBoxSVCRegistration(VirtualBoxClassFactory *pFactory)
265 : m_cRefs(1), m_pFactory(pFactory)
266 { }
267 virtual ~VBoxSVCRegistration()
268 {
269 if (m_pFactory)
270 {
271 if (m_pFactory->m_pVBoxSVC)
272 m_pFactory->m_pVBoxSVC = NULL;
273 m_pFactory = NULL;
274 }
275 }
276 RTMEMEF_NEW_AND_DELETE_OPERATORS();
277
278 // IUnknown
279 STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject)
280 {
281 if (riid == __uuidof(IUnknown))
282 *ppvObject = (void *)(IUnknown *)this;
283 else if (riid == __uuidof(IVBoxSVCRegistration))
284 *ppvObject = (void *)(IVBoxSVCRegistration *)this;
285 else
286 {
287 return E_NOINTERFACE;
288 }
289 AddRef();
290 return S_OK;
291
292 }
293
294 STDMETHOD_(ULONG,AddRef)(void)
295 {
296 uint32_t cRefs = ASMAtomicIncU32(&m_cRefs);
297 return cRefs;
298 }
299
300 STDMETHOD_(ULONG,Release)(void)
301 {
302 uint32_t cRefs = ASMAtomicDecU32(&m_cRefs);
303 if (cRefs == 0)
304 delete this;
305 return cRefs;
306 }
307
308 // IVBoxSVCRegistration
309 STDMETHOD(GetVirtualBox)(IUnknown **ppResult)
310 {
311 if (m_pFactory)
312 return m_pFactory->i_getVirtualBox(ppResult);
313 return E_FAIL;
314 }
315};
316
317
318HRESULT VirtualBoxClassFactory::i_registerWithSds(IUnknown **ppOtherVirtualBox)
319{
320# ifdef DEBUG_bird
321 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
322 RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
323 LogRel(("i_registerWithSds: RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n",
324 rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus,
325 CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid));
326# endif
327
328 /*
329 * Connect to VBoxSDS.
330 */
331 HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS,
332 (void **)m_ptrVirtualBoxSDS.asOutParam());
333 if (SUCCEEDED(hrc))
334 {
335 /*
336 * Create VBoxSVCRegistration object and hand that to VBoxSDS.
337 */
338 m_pVBoxSVC = new VBoxSVCRegistration(this);
339 hrc = m_ptrVirtualBoxSDS->RegisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId(), ppOtherVirtualBox);
340 if (SUCCEEDED(hrc))
341 {
342 g_fRegisteredWithVBoxSDS = !*ppOtherVirtualBox;
343 return hrc;
344 }
345 m_pVBoxSVC->Release();
346 }
347 m_ptrVirtualBoxSDS.setNull();
348 m_pVBoxSVC = NULL;
349 *ppOtherVirtualBox = NULL;
350 return hrc;
351}
352
353
354void VirtualBoxClassFactory::i_deregisterWithSds(void)
355{
356 Log(("VirtualBoxClassFactory::i_deregisterWithSds\n"));
357
358 if (m_ptrVirtualBoxSDS.isNotNull())
359 {
360 if (m_pVBoxSVC)
361 {
362 HRESULT hrc = m_ptrVirtualBoxSDS->DeregisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId());
363 NOREF(hrc);
364 }
365 m_ptrVirtualBoxSDS.setNull();
366 g_fRegisteredWithVBoxSDS = false;
367 }
368 if (m_pVBoxSVC)
369 {
370 m_pVBoxSVC->m_pFactory = NULL;
371 m_pVBoxSVC->Release();
372 m_pVBoxSVC = NULL;
373 }
374}
375
376
377HRESULT VirtualBoxClassFactory::i_getVirtualBox(IUnknown **ppResult)
378{
379# ifdef DEBUG_bird
380 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
381 RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
382 LogRel(("i_getVirtualBox: RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n",
383 rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus,
384 CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid));
385# endif
386 IUnknown *pObj = m_pObj;
387 if (pObj)
388 {
389 /** @todo Do we need to do something regarding server locking? Hopefully COM
390 * deals with that........... */
391 pObj->AddRef();
392 *ppResult = pObj;
393 Log(("VirtualBoxClassFactory::GetVirtualBox: S_OK - %p\n", pObj));
394 return S_OK;
395 }
396 *ppResult = NULL;
397 Log(("VirtualBoxClassFactory::GetVirtualBox: E_FAIL\n"));
398 return E_FAIL;
399}
400
401
402/**
403 * Custom instantiation of CComObjectCached.
404 *
405 * This catches certain QueryInterface callers for the purpose of watching for
406 * abnormal client process termination (@bugref{3300}).
407 *
408 * @todo just merge this into class VirtualBox VirtualBoxImpl.h
409 */
410class VirtualBoxObjectCached : public VirtualBox
411{
412public:
413 VirtualBoxObjectCached(void * = NULL)
414 : VirtualBox()
415 {
416 }
417
418 virtual ~VirtualBoxObjectCached()
419 {
420 m_iRef = LONG_MIN / 2; /* Catch refcount screwups by setting refcount something insane. */
421 FinalRelease();
422 }
423
424 /** @name IUnknown implementation for VirtualBox
425 * @{ */
426
427 STDMETHOD_(ULONG, AddRef)() throw()
428 {
429 ULONG cRefs = InternalAddRef();
430 if (cRefs == 2)
431 {
432 AssertMsg(ATL::_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n"));
433 ATL::_pAtlModule->Lock();
434 }
435 return cRefs;
436 }
437
438 STDMETHOD_(ULONG, Release)() throw()
439 {
440 ULONG cRefs = InternalRelease();
441 if (cRefs == 0)
442 delete this;
443 else if (cRefs == 1)
444 {
445 AssertMsg(ATL::_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n"));
446 ATL::_pAtlModule->Unlock();
447 }
448 return cRefs;
449 }
450
451 STDMETHOD(QueryInterface)(REFIID iid, void **ppvObj) throw()
452 {
453 HRESULT hrc = _InternalQueryInterface(iid, ppvObj);
454#ifdef VBOXSVC_WITH_CLIENT_WATCHER
455 i_logCaller("QueryInterface %RTuuid -> %Rhrc %p", &iid, hrc, *ppvObj);
456#endif
457 return hrc;
458 }
459
460 /** @} */
461
462 static HRESULT WINAPI CreateInstance(VirtualBoxObjectCached **ppObj) throw()
463 {
464 AssertReturn(ppObj, E_POINTER);
465 *ppObj = NULL;
466
467 HRESULT hrc = E_OUTOFMEMORY;
468 VirtualBoxObjectCached *p = new (std::nothrow) VirtualBoxObjectCached();
469 if (p)
470 {
471 p->SetVoid(NULL);
472 p->InternalFinalConstructAddRef();
473 hrc = p->_AtlInitialConstruct();
474 if (SUCCEEDED(hrc))
475 hrc = p->FinalConstruct();
476 p->InternalFinalConstructRelease();
477 if (FAILED(hrc))
478 delete p;
479 else
480 *ppObj = p;
481 }
482 return hrc;
483 }
484};
485
486
487/**
488 * Custom class factory impl for the VirtualBox singleton.
489 *
490 * This will consult with VBoxSDS on whether this VBoxSVC instance should
491 * provide the actual VirtualBox instance or just forward the instance from
492 * some other SVC instance.
493 *
494 * @param pUnkOuter This must be NULL.
495 * @param riid Reference to the interface ID to provide.
496 * @param ppvObj Where to return the pointer to the riid instance.
497 *
498 * @return COM status code.
499 */
500STDMETHODIMP VirtualBoxClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj)
501{
502# ifdef VBOXSVC_WITH_CLIENT_WATCHER
503 VirtualBox::i_logCaller("VirtualBoxClassFactory::CreateInstance: %RTuuid", riid);
504# endif
505 HRESULT hrc = E_POINTER;
506 if (ppvObj != NULL)
507 {
508 *ppvObj = NULL;
509 // no aggregation for singletons
510 AssertReturn(pUnkOuter == NULL, CLASS_E_NOAGGREGATION);
511
512 /*
513 * We must make sure there is only one instance around.
514 * So, we check without locking and then again after locking.
515 */
516 if (ASMAtomicReadS32(&m_iState) == 0)
517 {
518 Lock();
519 __try
520 {
521 if (ASMAtomicReadS32(&m_iState) == 0)
522 {
523 /*
524 * lock the module to indicate activity
525 * (necessary for the monitor shutdown thread to correctly
526 * terminate the module in case when CreateInstance() fails)
527 */
528 ATL::_pAtlModule->Lock();
529 __try
530 {
531 /*
532 * Now we need to connect to VBoxSDS to register ourselves.
533 */
534 IUnknown *pOtherVirtualBox = NULL;
535 m_hrcCreate = hrc = i_registerWithSds(&pOtherVirtualBox);
536 if (SUCCEEDED(hrc) && pOtherVirtualBox)
537 m_pObj = pOtherVirtualBox;
538 else if (SUCCEEDED(hrc))
539 {
540 ATL::_pAtlModule->Lock();
541 VirtualBoxObjectCached *p;
542 m_hrcCreate = hrc = VirtualBoxObjectCached::CreateInstance(&p);
543 if (SUCCEEDED(hrc))
544 {
545 m_hrcCreate = hrc = p->QueryInterface(IID_IUnknown, (void **)&m_pObj);
546 if (SUCCEEDED(hrc))
547 RTLogClearFileDelayFlag(RTLogRelGetDefaultInstance(), NULL);
548 else
549 {
550 delete p;
551 i_deregisterWithSds();
552 m_pObj = NULL;
553 }
554 }
555 }
556 ASMAtomicWriteS32(&m_iState, SUCCEEDED(hrc) ? 1 : -1);
557 }
558 __finally
559 {
560 ATL::_pAtlModule->Unlock();
561 }
562 }
563 }
564 __finally
565 {
566 if (ASMAtomicReadS32(&m_iState) == 0)
567 {
568 ASMAtomicWriteS32(&m_iState, -1);
569 if (SUCCEEDED(m_hrcCreate))
570 m_hrcCreate = E_FAIL;
571 }
572 Unlock();
573 }
574 }
575
576 /*
577 * Query the requested interface from the IUnknown one we're keeping around.
578 */
579 if (m_hrcCreate == S_OK)
580 hrc = m_pObj->QueryInterface(riid, ppvObj);
581 else
582 hrc = m_hrcCreate;
583 }
584 return hrc;
585}
586
587#endif // VBOX_WITH_SDS
588
589
590/*
591* Wrapper for Win API function ShutdownBlockReasonCreate
592* This function defined starting from Vista only.
593*/
594static BOOL ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason)
595{
596 typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNSHUTDOWNBLOCKREASONCREATE,(HWND hWnd, LPCWSTR pwszReason));
597
598 PFNSHUTDOWNBLOCKREASONCREATE pfn
599 = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
600 AssertPtr(pfn);
601
602 BOOL fResult = FALSE;
603 if (pfn)
604 fResult = pfn(hWnd, pwszReason);
605 return fResult;
606}
607
608/*
609* Wrapper for Win API function ShutdownBlockReasonDestroy
610* This function defined starting from Vista only.
611*/
612static BOOL ShutdownBlockReasonDestroyAPI(HWND hWnd)
613{
614 typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNSHUTDOWNBLOCKREASONDESTROY,(HWND hWnd));
615 PFNSHUTDOWNBLOCKREASONDESTROY pfn
616 = (PFNSHUTDOWNBLOCKREASONDESTROY)GetProcAddress(GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonDestroy");
617 AssertPtr(pfn);
618
619 BOOL fResult = FALSE;
620 if (pfn)
621 fResult = pfn(hWnd);
622 return fResult;
623}
624
625static LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
626{
627 LRESULT lResult = 0;
628
629 switch (msg)
630 {
631 case WM_QUERYENDSESSION:
632 {
633 LogRel(("WM_QUERYENDSESSION:%s%s%s%s (0x%08lx)\n",
634 lParam == 0 ? " shutdown" : "",
635 lParam & ENDSESSION_CRITICAL ? " critical" : "",
636 lParam & ENDSESSION_LOGOFF ? " logoff" : "",
637 lParam & ENDSESSION_CLOSEAPP ? " close" : "",
638 (unsigned long)lParam));
639 if (g_pModule)
640 {
641 bool fActiveConnection = g_pModule->HasActiveConnection();
642 if (fActiveConnection)
643 {
644 lResult = FALSE;
645 LogRel(("VBoxSvc has active connections:"
646 " bActivity = %RTbool, lock count = %d\n",
647 g_pModule->bActivity, g_pModule->GetLockCount()));
648
649 /* place the VBoxSVC into system shutdown list */
650 ShutdownBlockReasonCreateAPI(hwnd, L"Has active connections.");
651 /* decrease a latency of MonitorShutdown loop */
652 ASMAtomicXchgU32(&dwTimeOut, 100);
653 Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: VBoxSvc has active connections."
654 " bActivity = %d. Lock count = %d\n",
655 g_pModule->bActivity, g_pModule->GetLockCount()));
656 }
657 else
658 {
659 LogRel(("No active connections:"
660 " bActivity = %RTbool, lock count = %d\n",
661 g_pModule->bActivity, g_pModule->GetLockCount()));
662 lResult = TRUE;
663 }
664 }
665 else
666 AssertMsgFailed(("VBoxSVCWinMain: WM_QUERYENDSESSION: Error: g_pModule is NULL"));
667 break;
668 }
669 case WM_ENDSESSION:
670 {
671 LogRel(("WM_ENDSESSION:%s%s%s%s%s (%s/0x%08lx)\n",
672 lParam == 0 ? " shutdown" : "",
673 lParam & ENDSESSION_CRITICAL ? " critical" : "",
674 lParam & ENDSESSION_LOGOFF ? " logoff" : "",
675 lParam & ENDSESSION_CLOSEAPP ? " close" : "",
676 wParam == FALSE ? " cancelled" : "",
677 wParam ? "TRUE" : "FALSE",
678 (unsigned long)lParam));
679
680 /* Restore timeout of Monitor Shutdown if user canceled system shutdown */
681 if (wParam == FALSE)
682 {
683 Log(("VBoxSVCWinMain: user canceled system shutdown.\n"));
684 ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout);
685 ShutdownBlockReasonDestroyAPI(hwnd);
686 }
687 break;
688 }
689
690 case WM_DESTROY:
691 {
692 ShutdownBlockReasonDestroyAPI(hwnd);
693 PostQuitMessage(0);
694 break;
695 }
696
697 default:
698 {
699 lResult = DefWindowProc(hwnd, msg, wParam, lParam);
700 break;
701 }
702 }
703 return lResult;
704}
705
706static int CreateMainWindow()
707{
708 int rc = VINF_SUCCESS;
709 Assert(g_hMainWindow == NULL);
710
711 LogFlow(("CreateMainWindow\n"));
712
713 g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
714
715 /* Register the Window Class. */
716 WNDCLASS wc;
717 RT_ZERO(wc);
718
719 wc.style = CS_NOCLOSE;
720 wc.lpfnWndProc = WinMainWndProc;
721 wc.hInstance = g_hInstance;
722 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
723 wc.lpszClassName = MAIN_WND_CLASS;
724
725 ATOM atomWindowClass = RegisterClass(&wc);
726 if (atomWindowClass == 0)
727 {
728 LogRel(("Failed to register window class for session monitoring\n"));
729 rc = VERR_NOT_SUPPORTED;
730 }
731 else
732 {
733 /* Create the window. */
734 g_hMainWindow = CreateWindowEx(0, MAIN_WND_CLASS, MAIN_WND_CLASS, 0,
735 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
736 if (g_hMainWindow == NULL)
737 {
738 LogRel(("Failed to create window for session monitoring\n"));
739 rc = VERR_NOT_SUPPORTED;
740 }
741 }
742 return rc;
743}
744
745
746static void DestroyMainWindow()
747{
748 Assert(g_hMainWindow != NULL);
749 Log(("SVCMain: DestroyMainWindow \n"));
750 if (g_hMainWindow != NULL)
751 {
752 DestroyWindow(g_hMainWindow);
753 g_hMainWindow = NULL;
754 if (g_hInstance != NULL)
755 {
756 UnregisterClass(MAIN_WND_CLASS, g_hInstance);
757 g_hInstance = NULL;
758 }
759 }
760}
761
762
763static const char * const ctrl_event_names[] = {
764 "CTRL_C_EVENT",
765 "CTRL_BREAK_EVENT",
766 "CTRL_CLOSE_EVENT",
767 /* reserved, not used */
768 "<console control event 3>",
769 "<console control event 4>",
770 /* not sent to processes that load gdi32.dll or user32.dll */
771 "CTRL_LOGOFF_EVENT",
772 "CTRL_SHUTDOWN_EVENT",
773};
774
775/** @todo r=uwe placeholder */
776BOOL WINAPI
777ConsoleCtrlHandler(DWORD dwCtrlType) RT_NOTHROW_DEF
778{
779 const char *signame;
780 char namebuf[48];
781 // int rc;
782
783 if (dwCtrlType < RT_ELEMENTS(ctrl_event_names))
784 signame = ctrl_event_names[dwCtrlType];
785 else
786 {
787 /* should not happen, but be prepared */
788 RTStrPrintf(namebuf, sizeof(namebuf),
789 "<console control event %lu>", (unsigned long)dwCtrlType);
790 signame = namebuf;
791 }
792 LogRel(("Got %s\n", signame));
793
794 if (RT_UNLIKELY(g_pModule == NULL))
795 {
796 LogRel(("%s: g_pModule == NULL\n", __FUNCTION__));
797 return TRUE;
798 }
799
800 /* decrease latency of the MonitorShutdown loop */
801 ASMAtomicXchgU32(&dwTimeOut, 100);
802
803 bool fHasClients = g_pModule->HasActiveConnection();
804 if (!fHasClients)
805 {
806 LogRel(("No clients, closing the shop.\n"));
807 return TRUE;
808 }
809
810 LogRel(("VBoxSvc has clients: bActivity = %RTbool, lock count = %d\n",
811 g_pModule->bActivity, g_pModule->GetLockCount()));
812
813 /** @todo r=uwe wait for clients to disconnect */
814 return TRUE;
815}
816
817
818
819/** Special export that make VBoxProxyStub not register this process as one that
820 * VBoxSDS should be watching.
821 */
822extern "C" DECLEXPORT(void) VBOXCALL Is_VirtualBox_service_process_like_VBoxSDS_And_VBoxSDS(void)
823{
824 /* never called, just need to be here */
825}
826
827
828/////////////////////////////////////////////////////////////////////////////
829//
830int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
831{
832 int argc = __argc;
833 char **argv = __argv;
834
835 /*
836 * Need to parse the command line before initializing the VBox runtime so we can
837 * change to the user home directory before logs are being created.
838 */
839 for (int i = 1; i < argc; i++)
840 if ( (argv[i][0] == '/' || argv[i][0] == '-')
841 && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
842 {
843 /* %HOMEDRIVE%%HOMEPATH% */
844 wchar_t wszHome[RTPATH_MAX];
845 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
846 if (cEnv && cEnv < RTPATH_MAX)
847 {
848 DWORD cwc = cEnv; /* doesn't include NUL */
849 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
850 if (cEnv && cEnv < RTPATH_MAX - cwc)
851 {
852 /* If this fails there is nothing we can do. Ignore. */
853 SetCurrentDirectory(wszHome);
854 }
855 }
856 }
857
858 /*
859 * Initialize the VBox runtime without loading
860 * the support driver.
861 */
862 RTR3InitExe(argc, &argv, 0);
863
864 static const RTGETOPTDEF s_aOptions[] =
865 {
866 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
867 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
868 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
869 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
870 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
871 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
872 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
873 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
874 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
875 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
876 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
877 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
878 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
879 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
880 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
881 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
882 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
883 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
884 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
885 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
886 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
887 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
888 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
889 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
890 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
891 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
892 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
893 };
894
895 bool fRun = true;
896 bool fRegister = false;
897 bool fUnregister = false;
898 const char *pszPipeName = NULL;
899 const char *pszLogFile = NULL;
900 uint32_t cHistory = 10; // enable log rotation, 10 files
901 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
902 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
903
904 RTGETOPTSTATE GetOptState;
905 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
906 AssertRC(vrc);
907
908 RTGETOPTUNION ValueUnion;
909 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
910 {
911 switch (vrc)
912 {
913 case 'e':
914 /* already handled above */
915 break;
916
917 case 'u':
918 fUnregister = true;
919 fRun = false;
920 break;
921
922 case 'r':
923 fRegister = true;
924 fRun = false;
925 break;
926
927 case 'f':
928 fUnregister = true;
929 fRegister = true;
930 fRun = false;
931 break;
932
933 case 'H':
934 pszPipeName = ValueUnion.psz;
935 if (!pszPipeName)
936 pszPipeName = "";
937 fRun = false;
938 break;
939
940 case 'F':
941 pszLogFile = ValueUnion.psz;
942 break;
943
944 case 'R':
945 cHistory = ValueUnion.u32;
946 break;
947
948 case 'S':
949 uHistoryFileSize = ValueUnion.u64;
950 break;
951
952 case 'I':
953 uHistoryFileTime = ValueUnion.u32;
954 break;
955
956 case 'h':
957 {
958 static const WCHAR s_wszText[] = L"Options:\n\n"
959 L"/RegServer:\tregister COM out-of-proc server\n"
960 L"/UnregServer:\tunregister COM out-of-proc server\n"
961 L"/ReregServer:\tunregister and register COM server\n"
962 L"no options:\trun the server";
963 static const WCHAR s_wszTitle[] = L"Usage";
964 fRun = false;
965 MessageBoxW(NULL, s_wszText, s_wszTitle, MB_OK);
966 return 0;
967 }
968
969 case 'V':
970 {
971 static const WCHAR s_wszTitle[] = L"Version";
972 char *pszText = NULL;
973 RTStrAPrintf(&pszText, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
974 PRTUTF16 pwszText = NULL;
975 RTStrToUtf16(pszText, &pwszText);
976 RTStrFree(pszText);
977 MessageBoxW(NULL, pwszText, s_wszTitle, MB_OK);
978 RTUtf16Free(pwszText);
979 fRun = false;
980 return 0;
981 }
982
983 default:
984 /** @todo this assumes that stderr is visible, which is not
985 * true for standard Windows applications. */
986 /* continue on command line errors... */
987 RTGetOptPrintError(vrc, &ValueUnion);
988 }
989 }
990
991 /* Only create the log file when running VBoxSVC normally, but not when
992 * registering/unregistering or calling the helper functionality. */
993 if (fRun)
994 {
995 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
996 char szLogFile[RTPATH_MAX];
997 if (!pszLogFile || !*pszLogFile)
998 {
999 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
1000 if (RT_SUCCESS(vrc))
1001 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
1002 if (RT_FAILURE(vrc))
1003 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to construct release log filename, rc=%Rrc", vrc);
1004 pszLogFile = szLogFile;
1005 }
1006
1007 RTERRINFOSTATIC ErrInfo;
1008 vrc = com::VBoxLogRelCreate("COM Server", pszLogFile,
1009 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
1010 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
1011#ifdef VBOX_WITH_SDS
1012 RTLOGDEST_FILE | RTLOGDEST_F_DELAY_FILE,
1013#else
1014 RTLOGDEST_FILE,
1015#endif
1016 UINT32_MAX /* cMaxEntriesPerGroup */, cHistory, uHistoryFileTime, uHistoryFileSize,
1017 RTErrInfoInitStatic(&ErrInfo));
1018 if (RT_FAILURE(vrc))
1019 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
1020 }
1021
1022 /* Set up a build identifier so that it can be seen from core dumps what
1023 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
1024 static char saBuildID[48];
1025 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
1026 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
1027
1028 AssertCompile(VBOX_COM_INIT_F_DEFAULT == VBOX_COM_INIT_F_AUTO_REG_UPDATE);
1029 HRESULT hRes = com::Initialize(fRun ? VBOX_COM_INIT_F_AUTO_REG_UPDATE : 0);
1030 AssertLogRelMsg(SUCCEEDED(hRes), ("SVCMAIN: init failed: %Rhrc\n", hRes));
1031
1032 g_pModule = new CExeModule();
1033 if(g_pModule == NULL)
1034 return RTMsgErrorExit(RTEXITCODE_FAILURE, "not enough memory to create ExeModule.");
1035 g_pModule->Init(ObjectMap, hInstance, &LIBID_VirtualBox);
1036 g_pModule->dwThreadID = GetCurrentThreadId();
1037
1038 int nRet = 0;
1039 if (!fRun)
1040 {
1041#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
1042 if (fUnregister)
1043 {
1044 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
1045 nRet = g_pModule->UnregisterServer(TRUE);
1046 }
1047 if (fRegister)
1048 {
1049 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
1050 nRet = g_pModule->RegisterServer(TRUE);
1051 }
1052#endif
1053 if (pszPipeName)
1054 {
1055 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
1056
1057 if (!*pszPipeName)
1058 vrc = VERR_INVALID_PARAMETER;
1059
1060 if (RT_SUCCESS(vrc))
1061 {
1062 /* do the helper job */
1063 SVCHlpServer server;
1064 vrc = server.open(pszPipeName);
1065 if (RT_SUCCESS(vrc))
1066 vrc = server.run();
1067 }
1068 if (RT_FAILURE(vrc))
1069 {
1070 Log(("SVCMAIN: Failed to process Helper request (%Rrc).\n", vrc));
1071 nRet = 1;
1072 }
1073 }
1074 }
1075 else
1076 {
1077
1078 g_pModule->StartMonitor();
1079#if _WIN32_WINNT >= 0x0400
1080 hRes = g_pModule->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
1081 _ASSERTE(SUCCEEDED(hRes));
1082 hRes = CoResumeClassObjects();
1083#else
1084 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
1085#endif
1086 _ASSERTE(SUCCEEDED(hRes));
1087
1088 /*
1089 * Register windows console signal handler to react to Ctrl-C,
1090 * Ctrl-Break, Close; but more importantly - to get notified
1091 * about shutdown when we are running in the context of the
1092 * autostart service - we won't get WM_ENDSESSION in that
1093 * case.
1094 */
1095 ::SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
1096
1097
1098 if (RT_SUCCESS(CreateMainWindow()))
1099 Log(("SVCMain: Main window succesfully created\n"));
1100 else
1101 Log(("SVCMain: Failed to create main window\n"));
1102
1103 MSG msg;
1104 while (GetMessage(&msg, 0, 0, 0) > 0)
1105 {
1106 TranslateMessage(&msg);
1107 DispatchMessage(&msg);
1108 }
1109
1110 DestroyMainWindow();
1111
1112 g_pModule->RevokeClassObjects();
1113 }
1114
1115 g_pModule->Term();
1116
1117#ifdef VBOX_WITH_SDS
1118 g_fRegisteredWithVBoxSDS = false; /* Don't trust COM LPC to work right from now on. */
1119#endif
1120 com::Shutdown();
1121
1122 if(g_pModule)
1123 delete g_pModule;
1124 g_pModule = NULL;
1125
1126 Log(("SVCMAIN: Returning, COM server process ends.\n"));
1127 return nRet;
1128}
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