VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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