VirtualBox

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

Last change on this file since 95274 was 95274, checked in by vboxsync, 2 years ago

FE/VBoxAutostart/adi: Added experimental support for running VBoxSVC session 0 (service session). Did some minor cleanup for the patch posted at comment 95. Disabled by default for now. bugref:9341

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.8 KB
Line 
1/* $Id: svcmain.cpp 95274 2022-06-14 10:48:33Z vboxsync $ */
2/** @file
3 * SVCMAIN - COM out-of-proc server main entry
4 */
5
6/*
7 * Copyright (C) 2004-2022 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() throw();
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() throw()
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 = RTLogQueryDestinations(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 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
336 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
337 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
338 service to access the files. */
339 hrc = CoSetProxyBlanket(m_ptrVirtualBoxSDS,
340 RPC_C_AUTHN_DEFAULT,
341 RPC_C_AUTHZ_DEFAULT,
342 COLE_DEFAULT_PRINCIPAL,
343 RPC_C_AUTHN_LEVEL_DEFAULT,
344 RPC_C_IMP_LEVEL_IMPERSONATE,
345 NULL,
346 EOAC_DEFAULT);
347 if (SUCCEEDED(hrc))
348 {
349 /*
350 * Create VBoxSVCRegistration object and hand that to VBoxSDS.
351 */
352 m_pVBoxSVC = new VBoxSVCRegistration(this);
353 hrc = E_PENDING;
354 /* we try to register IVirtualBox 10 times */
355 for (int regTimes = 0; hrc == E_PENDING && regTimes < 10; --regTimes)
356 {
357 hrc = m_ptrVirtualBoxSDS->RegisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId(), ppOtherVirtualBox);
358 if (SUCCEEDED(hrc))
359 {
360 g_fRegisteredWithVBoxSDS = !*ppOtherVirtualBox;
361 return hrc;
362 }
363 /* sleep to give a time for windows session 0 registration */
364 if (hrc == E_PENDING)
365 RTThreadSleep(1000);
366 }
367 m_pVBoxSVC->Release();
368 }
369 }
370 m_ptrVirtualBoxSDS.setNull();
371 m_pVBoxSVC = NULL;
372 *ppOtherVirtualBox = NULL;
373 return hrc;
374}
375
376
377void VirtualBoxClassFactory::i_deregisterWithSds(void)
378{
379 Log(("VirtualBoxClassFactory::i_deregisterWithSds\n"));
380
381 if (m_ptrVirtualBoxSDS.isNotNull())
382 {
383 if (m_pVBoxSVC)
384 {
385 HRESULT hrc = m_ptrVirtualBoxSDS->DeregisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId());
386 NOREF(hrc);
387 }
388 m_ptrVirtualBoxSDS.setNull();
389 g_fRegisteredWithVBoxSDS = false;
390 }
391 if (m_pVBoxSVC)
392 {
393 m_pVBoxSVC->m_pFactory = NULL;
394 m_pVBoxSVC->Release();
395 m_pVBoxSVC = NULL;
396 }
397}
398
399
400HRESULT VirtualBoxClassFactory::i_getVirtualBox(IUnknown **ppResult)
401{
402# ifdef DEBUG_bird
403 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
404 RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
405 LogRel(("i_getVirtualBox: RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n",
406 rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus,
407 CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid));
408# endif
409 IUnknown *pObj = m_pObj;
410 if (pObj)
411 {
412 /** @todo Do we need to do something regarding server locking? Hopefully COM
413 * deals with that........... */
414 pObj->AddRef();
415 *ppResult = pObj;
416 Log(("VirtualBoxClassFactory::GetVirtualBox: S_OK - %p\n", pObj));
417 return S_OK;
418 }
419 *ppResult = NULL;
420 Log(("VirtualBoxClassFactory::GetVirtualBox: E_FAIL\n"));
421 return E_FAIL;
422}
423
424
425/**
426 * Custom instantiation of CComObjectCached.
427 *
428 * This catches certain QueryInterface callers for the purpose of watching for
429 * abnormal client process termination (@bugref{3300}).
430 *
431 * @todo just merge this into class VirtualBox VirtualBoxImpl.h
432 */
433class VirtualBoxObjectCached : public VirtualBox
434{
435public:
436 VirtualBoxObjectCached(void * = NULL)
437 : VirtualBox()
438 {
439 }
440
441 virtual ~VirtualBoxObjectCached()
442 {
443 m_iRef = LONG_MIN / 2; /* Catch refcount screwups by setting refcount something insane. */
444 FinalRelease();
445 }
446
447 /** @name IUnknown implementation for VirtualBox
448 * @{ */
449
450 STDMETHOD_(ULONG, AddRef)() throw()
451 {
452 ULONG cRefs = InternalAddRef();
453 if (cRefs == 2)
454 {
455 AssertMsg(ATL::_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n"));
456 ATL::_pAtlModule->Lock();
457 }
458 return cRefs;
459 }
460
461 STDMETHOD_(ULONG, Release)() throw()
462 {
463 ULONG cRefs = InternalRelease();
464 if (cRefs == 0)
465 delete this;
466 else if (cRefs == 1)
467 {
468 AssertMsg(ATL::_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n"));
469 ATL::_pAtlModule->Unlock();
470 }
471 return cRefs;
472 }
473
474 STDMETHOD(QueryInterface)(REFIID iid, void **ppvObj) throw()
475 {
476 HRESULT hrc = _InternalQueryInterface(iid, ppvObj);
477#ifdef VBOXSVC_WITH_CLIENT_WATCHER
478 i_logCaller("QueryInterface %RTuuid -> %Rhrc %p", &iid, hrc, *ppvObj);
479#endif
480 return hrc;
481 }
482
483 /** @} */
484
485 static HRESULT WINAPI CreateInstance(VirtualBoxObjectCached **ppObj) throw()
486 {
487 AssertReturn(ppObj, E_POINTER);
488 *ppObj = NULL;
489
490 HRESULT hrc = E_OUTOFMEMORY;
491 VirtualBoxObjectCached *p = new (std::nothrow) VirtualBoxObjectCached();
492 if (p)
493 {
494 p->SetVoid(NULL);
495 p->InternalFinalConstructAddRef();
496 hrc = p->_AtlInitialConstruct();
497 if (SUCCEEDED(hrc))
498 hrc = p->FinalConstruct();
499 p->InternalFinalConstructRelease();
500 if (FAILED(hrc))
501 delete p;
502 else
503 *ppObj = p;
504 }
505 return hrc;
506 }
507};
508
509
510/**
511 * Custom class factory impl for the VirtualBox singleton.
512 *
513 * This will consult with VBoxSDS on whether this VBoxSVC instance should
514 * provide the actual VirtualBox instance or just forward the instance from
515 * some other SVC instance.
516 *
517 * @param pUnkOuter This must be NULL.
518 * @param riid Reference to the interface ID to provide.
519 * @param ppvObj Where to return the pointer to the riid instance.
520 *
521 * @return COM status code.
522 */
523STDMETHODIMP VirtualBoxClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj)
524{
525# ifdef VBOXSVC_WITH_CLIENT_WATCHER
526 VirtualBox::i_logCaller("VirtualBoxClassFactory::CreateInstance: %RTuuid", riid);
527# endif
528 HRESULT hrc = E_POINTER;
529 if (ppvObj != NULL)
530 {
531 *ppvObj = NULL;
532 // no aggregation for singletons
533 AssertReturn(pUnkOuter == NULL, CLASS_E_NOAGGREGATION);
534
535 /*
536 * We must make sure there is only one instance around.
537 * So, we check without locking and then again after locking.
538 */
539 if (ASMAtomicReadS32(&m_iState) == 0)
540 {
541 Lock();
542 __try
543 {
544 if (ASMAtomicReadS32(&m_iState) == 0)
545 {
546 /*
547 * lock the module to indicate activity
548 * (necessary for the monitor shutdown thread to correctly
549 * terminate the module in case when CreateInstance() fails)
550 */
551 ATL::_pAtlModule->Lock();
552 __try
553 {
554 /*
555 * Now we need to connect to VBoxSDS to register ourselves.
556 */
557 IUnknown *pOtherVirtualBox = NULL;
558 m_hrcCreate = hrc = i_registerWithSds(&pOtherVirtualBox);
559 if (SUCCEEDED(hrc) && pOtherVirtualBox)
560 m_pObj = pOtherVirtualBox;
561 else if (SUCCEEDED(hrc))
562 {
563 ATL::_pAtlModule->Lock();
564 VirtualBoxObjectCached *p;
565 m_hrcCreate = hrc = VirtualBoxObjectCached::CreateInstance(&p);
566 if (SUCCEEDED(hrc))
567 {
568 m_hrcCreate = hrc = p->QueryInterface(IID_IUnknown, (void **)&m_pObj);
569 if (SUCCEEDED(hrc))
570 RTLogClearFileDelayFlag(RTLogRelGetDefaultInstance(), NULL);
571 else
572 {
573 delete p;
574 i_deregisterWithSds();
575 m_pObj = NULL;
576 }
577 }
578 }
579 ASMAtomicWriteS32(&m_iState, SUCCEEDED(hrc) ? 1 : -1);
580 }
581 __finally
582 {
583 ATL::_pAtlModule->Unlock();
584 }
585 }
586 }
587 __finally
588 {
589 if (ASMAtomicReadS32(&m_iState) == 0)
590 {
591 ASMAtomicWriteS32(&m_iState, -1);
592 if (SUCCEEDED(m_hrcCreate))
593 m_hrcCreate = E_FAIL;
594 }
595 Unlock();
596 }
597 }
598
599 /*
600 * Query the requested interface from the IUnknown one we're keeping around.
601 */
602 if (m_hrcCreate == S_OK)
603 hrc = m_pObj->QueryInterface(riid, ppvObj);
604 else
605 hrc = m_hrcCreate;
606 }
607 return hrc;
608}
609
610#endif // VBOX_WITH_SDS
611
612
613/*
614* Wrapper for Win API function ShutdownBlockReasonCreate
615* This function defined starting from Vista only.
616*/
617static BOOL ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason)
618{
619 typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNSHUTDOWNBLOCKREASONCREATE,(HWND hWnd, LPCWSTR pwszReason));
620
621 PFNSHUTDOWNBLOCKREASONCREATE pfn
622 = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
623 AssertPtr(pfn);
624
625 BOOL fResult = FALSE;
626 if (pfn)
627 fResult = pfn(hWnd, pwszReason);
628 return fResult;
629}
630
631/*
632* Wrapper for Win API function ShutdownBlockReasonDestroy
633* This function defined starting from Vista only.
634*/
635static BOOL ShutdownBlockReasonDestroyAPI(HWND hWnd)
636{
637 typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNSHUTDOWNBLOCKREASONDESTROY,(HWND hWnd));
638 PFNSHUTDOWNBLOCKREASONDESTROY pfn
639 = (PFNSHUTDOWNBLOCKREASONDESTROY)GetProcAddress(GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonDestroy");
640 AssertPtr(pfn);
641
642 BOOL fResult = FALSE;
643 if (pfn)
644 fResult = pfn(hWnd);
645 return fResult;
646}
647
648static LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
649{
650 LRESULT lResult = 0;
651
652 switch (msg)
653 {
654 case WM_QUERYENDSESSION:
655 {
656 LogRel(("WM_QUERYENDSESSION:%s%s%s%s (0x%08lx)\n",
657 lParam == 0 ? " shutdown" : "",
658 lParam & ENDSESSION_CRITICAL ? " critical" : "",
659 lParam & ENDSESSION_LOGOFF ? " logoff" : "",
660 lParam & ENDSESSION_CLOSEAPP ? " close" : "",
661 (unsigned long)lParam));
662 if (g_pModule)
663 {
664 bool fActiveConnection = g_pModule->HasActiveConnection();
665 if (fActiveConnection)
666 {
667 lResult = FALSE;
668 LogRel(("VBoxSvc has active connections:"
669 " bActivity = %RTbool, lock count = %d\n",
670 g_pModule->bActivity, g_pModule->GetLockCount()));
671
672 /* place the VBoxSVC into system shutdown list */
673 ShutdownBlockReasonCreateAPI(hwnd, L"Has active connections.");
674 /* decrease a latency of MonitorShutdown loop */
675 ASMAtomicXchgU32(&dwTimeOut, 100);
676 Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: VBoxSvc has active connections."
677 " bActivity = %d. Lock count = %d\n",
678 g_pModule->bActivity, g_pModule->GetLockCount()));
679 }
680 else
681 {
682 LogRel(("No active connections:"
683 " bActivity = %RTbool, lock count = %d\n",
684 g_pModule->bActivity, g_pModule->GetLockCount()));
685 lResult = TRUE;
686 }
687 }
688 else
689 AssertMsgFailed(("VBoxSVCWinMain: WM_QUERYENDSESSION: Error: g_pModule is NULL"));
690 break;
691 }
692 case WM_ENDSESSION:
693 {
694 LogRel(("WM_ENDSESSION:%s%s%s%s%s (%s/0x%08lx)\n",
695 lParam == 0 ? " shutdown" : "",
696 lParam & ENDSESSION_CRITICAL ? " critical" : "",
697 lParam & ENDSESSION_LOGOFF ? " logoff" : "",
698 lParam & ENDSESSION_CLOSEAPP ? " close" : "",
699 wParam == FALSE ? " cancelled" : "",
700 wParam ? "TRUE" : "FALSE",
701 (unsigned long)lParam));
702
703 /* Restore timeout of Monitor Shutdown if user canceled system shutdown */
704 if (wParam == FALSE)
705 {
706 Log(("VBoxSVCWinMain: user canceled system shutdown.\n"));
707 ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout);
708 ShutdownBlockReasonDestroyAPI(hwnd);
709 }
710 break;
711 }
712 case WM_DESTROY:
713 {
714 ShutdownBlockReasonDestroyAPI(hwnd);
715 PostQuitMessage(0);
716 break;
717 }
718
719 default:
720 {
721 lResult = DefWindowProc(hwnd, msg, wParam, lParam);
722 break;
723 }
724 }
725 return lResult;
726}
727
728static int CreateMainWindow()
729{
730 int rc = VINF_SUCCESS;
731 Assert(g_hMainWindow == NULL);
732
733 LogFlow(("CreateMainWindow\n"));
734
735 g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
736
737 /* Register the Window Class. */
738 WNDCLASS wc;
739 RT_ZERO(wc);
740
741 wc.style = CS_NOCLOSE;
742 wc.lpfnWndProc = WinMainWndProc;
743 wc.hInstance = g_hInstance;
744 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
745 wc.lpszClassName = MAIN_WND_CLASS;
746
747 ATOM atomWindowClass = RegisterClass(&wc);
748 if (atomWindowClass == 0)
749 {
750 LogRel(("Failed to register window class for session monitoring\n"));
751 rc = VERR_NOT_SUPPORTED;
752 }
753 else
754 {
755 /* Create the window. */
756 g_hMainWindow = CreateWindowEx(0, MAIN_WND_CLASS, MAIN_WND_CLASS, 0,
757 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
758 if (g_hMainWindow == NULL)
759 {
760 LogRel(("Failed to create window for session monitoring\n"));
761 rc = VERR_NOT_SUPPORTED;
762 }
763 }
764 return rc;
765}
766
767
768static void DestroyMainWindow()
769{
770 Assert(g_hMainWindow != NULL);
771 Log(("SVCMain: DestroyMainWindow \n"));
772 if (g_hMainWindow != NULL)
773 {
774 DestroyWindow(g_hMainWindow);
775 g_hMainWindow = NULL;
776 if (g_hInstance != NULL)
777 {
778 UnregisterClass(MAIN_WND_CLASS, g_hInstance);
779 g_hInstance = NULL;
780 }
781 }
782}
783
784
785static const char * const ctrl_event_names[] = {
786 "CTRL_C_EVENT",
787 "CTRL_BREAK_EVENT",
788 "CTRL_CLOSE_EVENT",
789 /* reserved, not used */
790 "<console control event 3>",
791 "<console control event 4>",
792 /* not sent to processes that load gdi32.dll or user32.dll */
793 "CTRL_LOGOFF_EVENT",
794 "CTRL_SHUTDOWN_EVENT",
795};
796
797/** @todo r=uwe placeholder */
798BOOL WINAPI
799ConsoleCtrlHandler(DWORD dwCtrlType) RT_NOTHROW_DEF
800{
801 const char *signame;
802 char namebuf[48];
803 // int rc;
804
805 if (dwCtrlType < RT_ELEMENTS(ctrl_event_names))
806 signame = ctrl_event_names[dwCtrlType];
807 else
808 {
809 /* should not happen, but be prepared */
810 RTStrPrintf(namebuf, sizeof(namebuf),
811 "<console control event %lu>", (unsigned long)dwCtrlType);
812 signame = namebuf;
813 }
814 LogRel(("Got %s\n", signame));
815
816 if (RT_UNLIKELY(g_pModule == NULL))
817 {
818 LogRel(("%s: g_pModule == NULL\n", __FUNCTION__));
819 return TRUE;
820 }
821
822 /* decrease latency of the MonitorShutdown loop */
823 ASMAtomicXchgU32(&dwTimeOut, 100);
824
825 bool fHasClients = g_pModule->HasActiveConnection();
826 if (!fHasClients)
827 {
828 LogRel(("No clients, closing the shop.\n"));
829 return TRUE;
830 }
831
832 LogRel(("VBoxSvc has clients: bActivity = %RTbool, lock count = %d\n",
833 g_pModule->bActivity, g_pModule->GetLockCount()));
834
835 /** @todo r=uwe wait for clients to disconnect */
836 return TRUE;
837}
838
839
840
841/** Special export that make VBoxProxyStub not register this process as one that
842 * VBoxSDS should be watching.
843 */
844extern "C" DECLEXPORT(void) VBOXCALL Is_VirtualBox_service_process_like_VBoxSDS_And_VBoxSDS(void)
845{
846 /* never called, just need to be here */
847}
848
849
850/* thread for registering the VBoxSVC started in session 0 */
851static DWORD WINAPI threadRegisterVirtualBox(LPVOID lpParam) throw()
852{
853 HANDLE hEvent = (HANDLE)lpParam;
854 HRESULT hrc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
855 if (SUCCEEDED(hrc))
856 {
857 /* create IVirtualBox instance */
858 ComPtr<IVirtualBox> pVirtualBox;
859 hrc = CoCreateInstance(CLSID_VirtualBox, NULL, CLSCTX_INPROC_SERVER /*CLSCTX_LOCAL_SERVER */, IID_IVirtualBox,
860 (void **)pVirtualBox.asOutParam());
861 if (SUCCEEDED(hrc))
862 {
863 /* wait a minute allowing clients to connect to the instance */
864 WaitForSingleObject(hEvent, 60 * 1000);
865 /* remove reference. If anybody connected to IVirtualBox it will stay alive. */
866 pVirtualBox.setNull();
867 }
868 CoUninitialize();
869 }
870 return 0L;
871}
872
873
874/////////////////////////////////////////////////////////////////////////////
875//
876int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
877{
878 int argc = __argc;
879 char **argv = __argv;
880
881 /*
882 * Need to parse the command line before initializing the VBox runtime so we can
883 * change to the user home directory before logs are being created.
884 */
885 for (int i = 1; i < argc; i++)
886 if ( (argv[i][0] == '/' || argv[i][0] == '-')
887 && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
888 {
889 /* %HOMEDRIVE%%HOMEPATH% */
890 wchar_t wszHome[RTPATH_MAX];
891 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
892 if (cEnv && cEnv < RTPATH_MAX)
893 {
894 DWORD cwc = cEnv; /* doesn't include NUL */
895 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
896 if (cEnv && cEnv < RTPATH_MAX - cwc)
897 {
898 /* If this fails there is nothing we can do. Ignore. */
899 SetCurrentDirectory(wszHome);
900 }
901 }
902 }
903
904 /*
905 * Initialize the VBox runtime without loading
906 * the support driver.
907 */
908 RTR3InitExe(argc, &argv, 0);
909
910 static const RTGETOPTDEF s_aOptions[] =
911 {
912 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
913 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
914 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
915 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
916 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
917 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
918 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
919 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
920 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
921 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
922 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
923 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
924 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
925 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
926 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
927 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
928 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
929 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
930 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
931 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
932 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
933 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
934 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
935 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
936 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
937 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
938 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
939 { "--registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
940 { "-registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
941 { "/registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
942 };
943
944 bool fRun = true;
945 bool fRegister = false;
946 bool fUnregister = false;
947 const char *pszPipeName = NULL;
948 const char *pszLogFile = NULL;
949 uint32_t cHistory = 10; // enable log rotation, 10 files
950 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
951 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
952 bool fRegisterVBox = false;
953
954 RTGETOPTSTATE GetOptState;
955 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
956 AssertRC(vrc);
957
958 RTGETOPTUNION ValueUnion;
959 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
960 {
961 switch (vrc)
962 {
963 case 'e':
964 /* already handled above */
965 break;
966
967 case 'u':
968 fUnregister = true;
969 fRun = false;
970 break;
971
972 case 'r':
973 fRegister = true;
974 fRun = false;
975 break;
976
977 case 'f':
978 fUnregister = true;
979 fRegister = true;
980 fRun = false;
981 break;
982
983 case 'H':
984 pszPipeName = ValueUnion.psz;
985 if (!pszPipeName)
986 pszPipeName = "";
987 fRun = false;
988 break;
989
990 case 'F':
991 pszLogFile = ValueUnion.psz;
992 break;
993
994 case 'R':
995 cHistory = ValueUnion.u32;
996 break;
997
998 case 'S':
999 uHistoryFileSize = ValueUnion.u64;
1000 break;
1001
1002 case 'I':
1003 uHistoryFileTime = ValueUnion.u32;
1004 break;
1005
1006 case 'h':
1007 {
1008 static const WCHAR s_wszText[] = L"Options:\n\n"
1009 L"/RegServer:\tregister COM out-of-proc server\n"
1010 L"/UnregServer:\tunregister COM out-of-proc server\n"
1011 L"/ReregServer:\tunregister and register COM server\n"
1012 L"no options:\trun the server";
1013 static const WCHAR s_wszTitle[] = L"Usage";
1014 fRun = false;
1015 MessageBoxW(NULL, s_wszText, s_wszTitle, MB_OK);
1016 return 0;
1017 }
1018
1019 case 'V':
1020 {
1021 static const WCHAR s_wszTitle[] = L"Version";
1022 char *pszText = NULL;
1023 RTStrAPrintf(&pszText, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1024 PRTUTF16 pwszText = NULL;
1025 RTStrToUtf16(pszText, &pwszText);
1026 RTStrFree(pszText);
1027 MessageBoxW(NULL, pwszText, s_wszTitle, MB_OK);
1028 RTUtf16Free(pwszText);
1029 fRun = false;
1030 return 0;
1031 }
1032
1033 case 'b':
1034 fRegisterVBox = true;
1035 break;
1036
1037 default:
1038 /** @todo this assumes that stderr is visible, which is not
1039 * true for standard Windows applications. */
1040 /* continue on command line errors... */
1041 RTGetOptPrintError(vrc, &ValueUnion);
1042 }
1043 }
1044
1045 /* Only create the log file when running VBoxSVC normally, but not when
1046 * registering/unregistering or calling the helper functionality. */
1047 if (fRun)
1048 {
1049 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
1050 char szLogFile[RTPATH_MAX];
1051 if (!pszLogFile || !*pszLogFile)
1052 {
1053 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
1054 if (RT_SUCCESS(vrc))
1055 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
1056 if (RT_FAILURE(vrc))
1057 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to construct release log filename, rc=%Rrc", vrc);
1058 pszLogFile = szLogFile;
1059 }
1060
1061 RTERRINFOSTATIC ErrInfo;
1062 vrc = com::VBoxLogRelCreate("COM Server", pszLogFile,
1063 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
1064 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
1065#ifdef VBOX_WITH_SDS
1066 RTLOGDEST_FILE | RTLOGDEST_F_DELAY_FILE,
1067#else
1068 RTLOGDEST_FILE,
1069#endif
1070 UINT32_MAX /* cMaxEntriesPerGroup */, cHistory, uHistoryFileTime, uHistoryFileSize,
1071 RTErrInfoInitStatic(&ErrInfo));
1072 if (RT_FAILURE(vrc))
1073 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
1074 }
1075
1076 /* Set up a build identifier so that it can be seen from core dumps what
1077 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
1078 static char saBuildID[48];
1079 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
1080 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
1081
1082 AssertCompile(VBOX_COM_INIT_F_DEFAULT == VBOX_COM_INIT_F_AUTO_REG_UPDATE);
1083 HRESULT hRes = com::Initialize(fRun ? VBOX_COM_INIT_F_AUTO_REG_UPDATE : 0);
1084 AssertLogRelMsg(SUCCEEDED(hRes), ("SVCMAIN: init failed: %Rhrc\n", hRes));
1085
1086 g_pModule = new CExeModule();
1087 if(g_pModule == NULL)
1088 return RTMsgErrorExit(RTEXITCODE_FAILURE, "not enough memory to create ExeModule.");
1089 g_pModule->Init(ObjectMap, hInstance, &LIBID_VirtualBox);
1090 g_pModule->dwThreadID = GetCurrentThreadId();
1091
1092 int nRet = 0;
1093 if (!fRun)
1094 {
1095#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
1096 if (fUnregister)
1097 {
1098 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
1099 nRet = g_pModule->UnregisterServer(TRUE);
1100 }
1101 if (fRegister)
1102 {
1103 g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
1104 nRet = g_pModule->RegisterServer(TRUE);
1105 }
1106#endif
1107 if (pszPipeName)
1108 {
1109 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
1110
1111 if (!*pszPipeName)
1112 vrc = VERR_INVALID_PARAMETER;
1113
1114 if (RT_SUCCESS(vrc))
1115 {
1116 /* do the helper job */
1117 SVCHlpServer server;
1118 vrc = server.open(pszPipeName);
1119 if (RT_SUCCESS(vrc))
1120 vrc = server.run();
1121 }
1122 if (RT_FAILURE(vrc))
1123 {
1124 Log(("SVCMAIN: Failed to process Helper request (%Rrc).\n", vrc));
1125 nRet = 1;
1126 }
1127 }
1128 }
1129 else
1130 {
1131
1132 g_pModule->StartMonitor();
1133#if _WIN32_WINNT >= 0x0400
1134 hRes = g_pModule->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
1135 _ASSERTE(SUCCEEDED(hRes));
1136 hRes = CoResumeClassObjects();
1137#else
1138 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
1139#endif
1140 _ASSERTE(SUCCEEDED(hRes));
1141
1142 /*
1143 * Register windows console signal handler to react to Ctrl-C,
1144 * Ctrl-Break, Close; but more importantly - to get notified
1145 * about shutdown when we are running in the context of the
1146 * autostart service - we won't get WM_ENDSESSION in that
1147 * case.
1148 */
1149 ::SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
1150
1151
1152 if (RT_SUCCESS(CreateMainWindow()))
1153 Log(("SVCMain: Main window succesfully created\n"));
1154 else
1155 Log(("SVCMain: Failed to create main window\n"));
1156
1157 /* create thread to register IVirtualBox in VBoxSDS
1158 * It is used for starting the VBoxSVC in the windows
1159 * session 0. */
1160 HANDLE hWaitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1161 HANDLE hRegisterVBoxThread = NULL;
1162 if (fRegisterVBox)
1163 {
1164 DWORD dwThreadId = 0;
1165 hRegisterVBoxThread = CreateThread(NULL, 0, threadRegisterVirtualBox, (LPVOID)hWaitEvent,
1166 0, &dwThreadId);
1167 }
1168
1169 MSG msg;
1170 while (GetMessage(&msg, 0, 0, 0) > 0)
1171 {
1172 TranslateMessage(&msg);
1173 DispatchMessage(&msg);
1174 }
1175
1176 DestroyMainWindow();
1177
1178 if (fRegisterVBox)
1179 {
1180 SetEvent(hWaitEvent);
1181 WaitForSingleObject(hRegisterVBoxThread, INFINITE);
1182 CloseHandle(hRegisterVBoxThread);
1183 CloseHandle(hWaitEvent);
1184 }
1185
1186 g_pModule->RevokeClassObjects();
1187 }
1188
1189 g_pModule->Term();
1190
1191#ifdef VBOX_WITH_SDS
1192 g_fRegisteredWithVBoxSDS = false; /* Don't trust COM LPC to work right from now on. */
1193#endif
1194 com::Shutdown();
1195
1196 if(g_pModule)
1197 delete g_pModule;
1198 g_pModule = NULL;
1199
1200 Log(("SVCMAIN: Returning, COM server process ends.\n"));
1201 return nRet;
1202}
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