VirtualBox

source: vbox/trunk/src/VBox/Main/src-global/win/VBoxSDS.cpp@ 76082

最後變更 在這個檔案從76082是 76082,由 vboxsync 提交於 6 年 前

VBoxSDS: Implemented (disabled) monitoring client (VBoxSVC) process termination to bypass the 5 min denalty for abended clients. Really handy for development. bugref:3300

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 36.3 KB
 
1/* $Id: VBoxSDS.cpp 76082 2018-12-09 19:25:00Z vboxsync $ */
2/** @file
3 * VBoxSDS - COM global service main entry (System Directory Service)
4 */
5
6/*
7 * Copyright (C) 2017-2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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/** @page pg_VBoxSDS VBoxSDS - Per user CLSID_VirtualBox coordinater
20 *
21 * VBoxSDS is short for VirtualBox System Directory Service (SDS). Its purpose
22 * is to make sure there is only one CLSID_VirtualBox object running for each
23 * user using VirtualBox on a Windows host system.
24 *
25 *
26 * @section sec_vboxsds_backgroud Background
27 *
28 * COM is desktop oriented when it comes to activate-as-activator (AAA) COM
29 * servers. This means that if the users has two logins to the same box (e.g.
30 * physical console, RDP, SSHD) and tries to use an AAA COM server, a new server
31 * will be instantiated for each login. With the introduction of User Account
32 * Control (UAC) in Windows Vista, this was taken a step further and a user
33 * would talk different AAA COM server instances depending on the elevation
34 * level too.
35 *
36 * VBoxSVC is a service affected by this issue. Using VirtualBox across logins
37 * or between user elevation levels was impossible to do simultaneously. This
38 * was confusing and illogical to the user.
39 *
40 *
41 * @section sec_vboxsds_how How it works
42 *
43 * VBoxSDS assists in working around this problem by tracking which VBoxSVC
44 * server is currently providing CLSID_VirtualBox for a user. Each VBoxSVC
45 * instance will register itself with VBoxSDS when the CLSID_VirtualBox object
46 * is requested via their class factory. The first VBoxSVC registering for a
47 * given user will be allowed to instantate CLSID_VirtualBox. We will call this
48 * the chosen one. Subsequent VBoxSVC instance for the given user, regardless
49 * of elevation, session, windows station, or whatever else, will be told to use
50 * the instance from the first VBoxSVC.
51 *
52 * The registration call passes along an IVBoxSVCRegistration interface from
53 * VBoxSVC. VBoxSDS keeps this around for the chosen one only. When other
54 * VBoxSVC instances for the same user tries to register, VBoxSDS will ask the
55 * choosen one for its CLSID_VirtualBox object and return it to the new
56 * registrant.
57 *
58 * The chosen one will deregister with VBoxSDS before it terminates. Should it
59 * terminate abnormally, VBoxSDS will (probably) notice the next time it tries
60 * to request CLSID_VirtualBox from it and replace it as the chosen one with the
61 * new registrant.
62 *
63 *
64 * @section sec_vboxsds_locking Locking
65 *
66 * VBoxSDS stores data in a map indexed by the stringified secure identifier
67 * (SID) for each user. The map is protected by a shared critical section, so
68 * only inserting new users requires exclusive access.
69 *
70 * Each user data entry has it own lock (regular, not shared), so that it won't
71 * be necessary to hold down the map lock while accessing per user data. Thus
72 * preventing a user from blocking all others from using VirtualBox by
73 * suspending or debugging their chosen VBoxSVC process.
74 *
75 */
76
77
78/*********************************************************************************************************************************
79* Header Files *
80*********************************************************************************************************************************/
81#include <iprt/win/windows.h>
82#include <iprt/win/shlobj.h>
83
84#include "VBox/com/defs.h"
85#include "VBox/com/com.h"
86#include "VBox/com/VirtualBox.h"
87
88#include "VirtualBoxSDSImpl.h"
89#include "Logging.h"
90
91#include <VBox/err.h>
92#include <iprt/asm.h>
93#include <iprt/buildconfig.h>
94#include <iprt/dir.h>
95#include <iprt/env.h>
96#include <iprt/getopt.h>
97#include <iprt/initterm.h>
98#include <iprt/path.h>
99#include <iprt/message.h>
100#include <iprt/string.h>
101
102#include <VBox/com/microatl.h>
103
104#define _ATL_FREE_THREADED /** @todo r=bird: WTF? */
105
106/**
107 * Implements Windows Service
108 */
109class ATL_NO_VTABLE CWindowsServiceModule
110{
111protected:
112 // data members
113 WCHAR m_wszServiceName[256];
114 WCHAR m_wszServiceDisplayName[256];
115 WCHAR m_wszServiceDescription[256];
116 SERVICE_STATUS_HANDLE m_hServiceStatus;
117 SERVICE_STATUS m_Status;
118 DWORD m_dwThreadID;
119
120 /** Pointer to the instance, for use by staticServiceMain and staticHandler. */
121 static CWindowsServiceModule *s_pInstance;
122
123public:
124 CWindowsServiceModule() throw()
125 {
126 // set up the initial service status
127 m_hServiceStatus = NULL;
128 m_Status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
129 m_Status.dwCurrentState = SERVICE_STOPPED;
130 m_Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
131 m_Status.dwWin32ExitCode = 0;
132 m_Status.dwServiceSpecificExitCode = 0;
133 m_Status.dwCheckPoint = 0;
134 m_Status.dwWaitHint = 3000;
135
136 s_pInstance = this;
137 }
138
139 virtual ~CWindowsServiceModule()
140 {
141 s_pInstance = NULL;
142 }
143
144 HRESULT startService(int /*nShowCmd*/) throw()
145 {
146 SERVICE_TABLE_ENTRY aServiceTable[] =
147 {
148 { m_wszServiceName, staticServiceMain },
149 { NULL, NULL }
150 };
151
152 if (::StartServiceCtrlDispatcher(aServiceTable) == 0)
153 {
154 m_Status.dwWin32ExitCode = ::GetLastError();
155 LogRelFunc(("Error: Cannot start service in console mode. Code: %u\n", m_Status.dwWin32ExitCode));
156 }
157
158 return m_Status.dwWin32ExitCode;
159 }
160
161 virtual HRESULT registerService() throw()
162 {
163 HRESULT hrc;
164 if (uninstallService())
165 {
166 hrc = onRegisterService();
167 if (SUCCEEDED(hrc))
168 {
169 if (installService())
170 hrc = S_OK;
171 else
172 hrc = E_FAIL;
173 }
174 }
175 else
176 hrc = E_FAIL;
177 return hrc;
178 }
179
180 virtual HRESULT unregisterService() throw()
181 {
182 HRESULT hrc = E_FAIL;
183 if (uninstallService())
184 hrc = onUnregisterService();
185 return hrc;
186 }
187
188private:
189 void serviceMain(DWORD, LPTSTR *) throw()
190 {
191 LogFunc(("Enter into serviceMain\n"));
192 // Register the control request handler
193 m_Status.dwCurrentState = SERVICE_START_PENDING;
194 m_dwThreadID = ::GetCurrentThreadId();
195 m_hServiceStatus = ::RegisterServiceCtrlHandler(m_wszServiceName, staticHandler);
196 if (m_hServiceStatus == NULL)
197 {
198 LogWarnFunc(("Handler not installed\n"));
199 return;
200 }
201 setServiceStatus(SERVICE_START_PENDING);
202
203 m_Status.dwWin32ExitCode = S_OK;
204 m_Status.dwCheckPoint = 0;
205 m_Status.dwWaitHint = 0;
206
207 // When the Run function returns, the service has stopped.
208 m_Status.dwWin32ExitCode = runService(SW_HIDE);
209
210 setServiceStatus(SERVICE_STOPPED);
211 LogFunc(("Windows Service stopped\n"));
212 }
213
214 /** Service table callback. */
215 static void WINAPI staticServiceMain(DWORD cArgs, LPTSTR *papwszArgs) throw()
216 {
217 AssertPtrReturnVoid(s_pInstance);
218 s_pInstance->serviceMain(cArgs, papwszArgs);
219 }
220
221 HRESULT runService(int nShowCmd = SW_HIDE) throw()
222 {
223 HRESULT hr = preMessageLoop(nShowCmd);
224
225 if (hr == S_OK)
226 runMessageLoop();
227
228 if (SUCCEEDED(hr))
229 hr = postMessageLoop();
230
231 return hr;
232 }
233
234protected:
235 /** Hook that's called before the message loop starts.
236 * Must return S_OK for it to start. */
237 virtual HRESULT preMessageLoop(int /*nShowCmd*/) throw()
238 {
239 LogFunc(("Enter\n"));
240 if (::InterlockedCompareExchange(&m_Status.dwCurrentState, SERVICE_RUNNING, SERVICE_START_PENDING) == SERVICE_START_PENDING)
241 {
242 LogFunc(("VBoxSDS Service started/resumed without delay\n"));
243 ::SetServiceStatus(m_hServiceStatus, &m_Status);
244 }
245 return S_OK;
246 }
247
248 /** Your typical windows message loop. */
249 virtual void runMessageLoop()
250 {
251 MSG msg;
252 while (::GetMessage(&msg, 0, 0, 0) > 0)
253 {
254 ::TranslateMessage(&msg);
255 ::DispatchMessage(&msg);
256 }
257 }
258
259 /** Hook that's called after the message loop ends. */
260 virtual HRESULT postMessageLoop()
261 {
262 return S_OK;
263 }
264
265 /** @name Overridable status change handlers
266 * @{ */
267 virtual void onStop() throw()
268 {
269 setServiceStatus(SERVICE_STOP_PENDING);
270 ::PostThreadMessage(m_dwThreadID, WM_QUIT, 0, 0);
271 LogFunc(("Windows Service stopped\n"));
272 }
273
274 virtual void onPause() throw()
275 {
276 }
277
278 virtual void onContinue() throw()
279 {
280 }
281
282 virtual void onInterrogate() throw()
283 {
284 }
285
286 virtual void onShutdown() throw()
287 {
288 }
289
290 virtual void onUnknownRequest(DWORD dwOpcode) throw()
291 {
292 LogRelFunc(("Bad service request: %u (%#x)\n", dwOpcode, dwOpcode));
293 }
294
295 virtual HRESULT onRegisterService()
296 {
297 return S_OK;
298 }
299
300 virtual HRESULT onUnregisterService()
301 {
302 return S_OK;
303 }
304 /** @} */
305
306private:
307 void handler(DWORD dwOpcode) throw()
308 {
309
310 switch (dwOpcode)
311 {
312 case SERVICE_CONTROL_STOP:
313 onStop();
314 break;
315 case SERVICE_CONTROL_PAUSE:
316 onPause();
317 break;
318 case SERVICE_CONTROL_CONTINUE:
319 onContinue();
320 break;
321 case SERVICE_CONTROL_INTERROGATE:
322 onInterrogate();
323 break;
324 case SERVICE_CONTROL_SHUTDOWN:
325 onShutdown();
326 break;
327 default:
328 onUnknownRequest(dwOpcode);
329 }
330 }
331
332 static void WINAPI staticHandler(DWORD dwOpcode) throw()
333 {
334 AssertPtrReturnVoid(s_pInstance);
335 s_pInstance->handler(dwOpcode);
336 }
337
338protected:
339 void setServiceStatus(DWORD dwState) throw()
340 {
341 uint32_t const uPrevState = ASMAtomicXchgU32((uint32_t volatile *)&m_Status.dwCurrentState, dwState);
342 if (!::SetServiceStatus(m_hServiceStatus, &m_Status))
343 LogRel(("Error: SetServiceStatus(%p, %u) failed: %u (uPrevState=%u)\n",
344 m_hServiceStatus, dwState, GetLastError(), uPrevState));
345 }
346
347
348public:
349 /** @note unused */
350 BOOL IsInstalled() throw()
351 {
352 BOOL fResult = FALSE;
353
354 SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
355 if (hSCM != NULL)
356 {
357 SC_HANDLE hService = ::OpenService(hSCM, m_wszServiceName, SERVICE_QUERY_CONFIG);
358 if (hService != NULL)
359 {
360 fResult = TRUE;
361 ::CloseServiceHandle(hService);
362 }
363 ::CloseServiceHandle(hSCM);
364 }
365
366 return fResult;
367 }
368
369 BOOL installService() throw()
370 {
371 BOOL fResult = FALSE;
372 SC_HANDLE hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
373 if (hSCM != NULL)
374 {
375 SC_HANDLE hService = ::OpenService(hSCM, m_wszServiceName, SERVICE_QUERY_CONFIG);
376 if (hService != NULL)
377 {
378 fResult = TRUE; /* Already installed. */
379
380 ::CloseServiceHandle(hService);
381 }
382 else
383 {
384 // Get the executable file path and quote it.
385 const int QUOTES_SPACE = 2;
386 WCHAR wszFilePath[MAX_PATH + QUOTES_SPACE];
387 DWORD cwcFilePath = ::GetModuleFileNameW(NULL, wszFilePath + 1, MAX_PATH);
388 if (cwcFilePath != 0 && cwcFilePath < MAX_PATH)
389 {
390 wszFilePath[0] = L'\"';
391 wszFilePath[cwcFilePath + 1] = L'\"';
392 wszFilePath[cwcFilePath + 2] = L'\0';
393
394 SC_HANDLE hService = ::CreateServiceW(hSCM, m_wszServiceName, m_wszServiceDisplayName,
395 SERVICE_CHANGE_CONFIG,
396 SERVICE_WIN32_OWN_PROCESS,
397 SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
398 wszFilePath, NULL, NULL, L"RPCSS\0", NULL, NULL);
399 if (hService != NULL)
400 {
401 SERVICE_DESCRIPTIONW sd;
402 sd.lpDescription = m_wszServiceDescription;
403 if (!::ChangeServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION, &sd))
404 AssertLogRelMsgFailed(("Error: could not set service description: %u\n", GetLastError()));
405
406 fResult = TRUE;
407
408 ::CloseServiceHandle(hService);
409 }
410 else
411 AssertLogRelMsgFailed(("Error: Could not create service '%ls': %u\n", m_wszServiceName, GetLastError()));
412 }
413 else
414 AssertLogRelMsgFailed(("Error: GetModuleFileNameW returned %u: %u\n", cwcFilePath, GetLastError()));
415 }
416 }
417 else
418 AssertLogRelMsgFailed(("Error: Could not open the service control manager: %u\n", GetLastError()));
419 return fResult;
420 }
421
422 BOOL uninstallService() throw()
423 {
424 BOOL fResult = FALSE;
425 SC_HANDLE hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
426 if (hSCM != NULL)
427 {
428 SC_HANDLE hService = ::OpenService(hSCM, m_wszServiceName, SERVICE_STOP | DELETE);
429 if (hService == NULL)
430 {
431 DWORD dwErr = GetLastError();
432 hService = ::OpenService(hSCM, m_wszServiceName, SERVICE_QUERY_CONFIG);
433 if (hService == NULL)
434 fResult = TRUE; /* Probably not installed or some access problem. */
435 else
436 {
437 ::CloseServiceHandle(hService);
438 AssertLogRelMsgFailed(("Error: Failed to open '%ls' for stopping and deletion: %u\n", m_wszServiceName, dwErr));
439 }
440 }
441 else
442 {
443 /* Try stop it. */
444 SERVICE_STATUS status;
445 RT_ZERO(status);
446 if (!::ControlService(hService, SERVICE_CONTROL_STOP, &status))
447 {
448 DWORD dwErr = GetLastError();
449 AssertLogRelMsg( dwErr == ERROR_SERVICE_NOT_ACTIVE
450 || ( dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL
451 && status.dwCurrentState == SERVICE_STOP_PENDING)
452 , ("Error: Failed to stop serive '%ls': dwErr=%u dwCurrentState=%u\n",
453 m_wszServiceName, dwErr, status.dwCurrentState));
454 }
455
456 /* Try delete it. */
457 fResult = ::DeleteService(hService);
458 AssertLogRelMsg(fResult, ("Error: Failed to delete serivce '%ls': %u\n", m_wszServiceName, GetLastError()));
459
460 ::CloseServiceHandle(hService);
461 }
462 ::CloseServiceHandle(hSCM);
463 }
464 else
465 AssertLogRelMsgFailed(("Error: Could not open the service control manager: %u\n", GetLastError()));
466 return fResult;
467 }
468};
469
470/*static*/ CWindowsServiceModule *CWindowsServiceModule::s_pInstance = NULL;
471
472
473/**
474 * Implements COM Module that used within Windows Service.
475 *
476 * It is derived from ComModule to intercept Unlock() and derived from
477 * CWindowsServiceModule to implement Windows Service
478 */
479class CComServiceModule : public CWindowsServiceModule, public ATL::CComModule
480{
481private:
482 /** Tracks whether Init() has been called for debug purposes. */
483 bool m_fInitialized;
484 /** Tracks COM init status for no visible purpose other than debugging. */
485 bool m_fComInitialized;
486 /** Part of the shutdown monitoring logic. */
487 bool volatile m_fActivity;
488#ifdef WITH_WATCHER
489 /** Part of the shutdown monitoring logic. */
490 bool volatile m_fHasClients;
491#endif
492 /** Auto reset event for communicating with the shutdown thread.
493 * This is created by startMonitor(). */
494 HANDLE m_hEventShutdown;
495 /** The main thread ID.
496 * The monitorShutdown code needs this to post a WM_QUIT message. */
497 DWORD m_dwMainThreadID;
498
499public:
500 /** Time for EXE to be idle before shutting down.
501 * Can be decreased at system shutdown phase. */
502 volatile uint32_t m_cMsShutdownTimeOut;
503
504 /** The service module instance. */
505 static CComServiceModule * volatile s_pInstance;
506
507public:
508 /**
509 * Constructor.
510 *
511 * @param cMsShutdownTimeout Number of milliseconds to idle without clients
512 * before autoamtically shutting down the service.
513 *
514 * The default is 2 seconds, because VBoxSVC (our
515 * only client) already does 5 seconds making the
516 * effective idle time 7 seconds from clients like
517 * VBoxManage's point of view. We consider single
518 * user and development as the dominant usage
519 * patterns here, not configuration activity by
520 * multiple users via VBoxManage.
521 */
522 CComServiceModule(DWORD cMsShutdownTimeout = 2000)
523 : m_fInitialized(false)
524 , m_fComInitialized(false)
525 , m_fActivity(false)
526#ifdef WITH_WATCHER
527 , m_fHasClients(false)
528#endif
529 , m_cMsShutdownTimeOut(cMsShutdownTimeout)
530 , m_hEventShutdown(INVALID_HANDLE_VALUE)
531 , m_dwMainThreadID(~(DWORD)42)
532 {
533 }
534
535 /**
536 * Initialization function.
537 */
538 HRESULT init(ATL::_ATL_OBJMAP_ENTRY *p, HINSTANCE h, const GUID *pLibID,
539 wchar_t const *p_wszServiceName, wchar_t const *p_wszDisplayName, wchar_t const *p_wszDescription)
540 {
541 HRESULT hrc = ATL::CComModule::Init(p, h, pLibID);
542 if (SUCCEEDED(hrc))
543 {
544 // copy service name
545 int rc = ::RTUtf16Copy(m_wszServiceName, sizeof(m_wszServiceName), p_wszServiceName);
546 AssertRCReturn(rc, E_NOT_SUFFICIENT_BUFFER);
547 rc = ::RTUtf16Copy(m_wszServiceDisplayName, sizeof(m_wszServiceDisplayName), p_wszDisplayName);
548 AssertRCReturn(rc, E_NOT_SUFFICIENT_BUFFER);
549 rc = ::RTUtf16Copy(m_wszServiceDescription, sizeof(m_wszServiceDescription), p_wszDescription);
550 AssertRCReturn(rc, E_NOT_SUFFICIENT_BUFFER);
551
552 m_fInitialized = true;
553 }
554
555 return hrc;
556 }
557
558 /**
559 * Overload CAtlModule::Unlock to trigger delayed automatic shutdown action.
560 */
561 virtual LONG Unlock() throw()
562 {
563 LONG cLocks = ATL::CComModule::Unlock();
564 LogFunc(("Unlock() called. Ref=%d\n", cLocks));
565 if (cLocks == 0)
566 {
567 ::ASMAtomicWriteBool(&m_fActivity, true);
568 ::SetEvent(m_hEventShutdown); // tell monitor that we transitioned to zero
569 }
570 return cLocks;
571 }
572
573 /**
574 * Overload CAtlModule::Lock to untrigger automatic shutdown.
575 */
576 virtual LONG Lock() throw()
577 {
578 LONG cLocks = ATL::CComModule::Lock();
579 LogFunc(("Lock() called. Ref=%d\n", cLocks));
580#ifdef WITH_WATCHER
581 ::ASMAtomicWriteBool(&m_fActivity, true);
582 ::SetEvent(m_hEventShutdown); /* reset the timeout interval */
583#endif
584 return cLocks;
585 }
586
587#ifdef WITH_WATCHER
588
589 /** Called to start the automatic shutdown behaviour based on client count
590 * rather than lock count.. */
591 void notifyZeroClientConnections()
592 {
593 m_fHasClients = false;
594 ::ASMAtomicWriteBool(&m_fActivity, true);
595 ::SetEvent(m_hEventShutdown);
596 }
597
598 /** Called to make sure automatic shutdown is cancelled. */
599 void notifyHasClientConnections()
600 {
601 m_fHasClients = true;
602 ::ASMAtomicWriteBool(&m_fActivity, true);
603 }
604
605#endif /* WITH_WATCHER */
606
607protected:
608
609 bool hasActiveConnection()
610 {
611#ifdef WITH_WATCHER
612 return m_fActivity || (m_fHasClients && GetLockCount() > 0);
613#else
614 return m_fActivity || GetLockCount() > 0;
615#endif
616 }
617
618 void monitorShutdown() throw()
619 {
620 for (;;)
621 {
622 ::WaitForSingleObject(m_hEventShutdown, INFINITE);
623 DWORD dwWait;
624 do
625 {
626 m_fActivity = false;
627 dwWait = ::WaitForSingleObject(m_hEventShutdown, m_cMsShutdownTimeOut);
628 } while (dwWait == WAIT_OBJECT_0);
629
630 /* timed out */
631 if (!hasActiveConnection()) /* if no activity let's really bail */
632 {
633 ::CoSuspendClassObjects();
634
635 /* Disable log rotation at this point, worst case a log file becomes slightly
636 bigger than it should. Avoids quirks with log rotation: There might be
637 another API service process running at this point which would rotate the
638 logs concurrently, creating a mess. */
639 PRTLOGGER pReleaseLogger = ::RTLogRelGetDefaultInstance();
640 if (pReleaseLogger)
641 {
642 char szDest[1024];
643 int rc = ::RTLogGetDestinations(pReleaseLogger, szDest, sizeof(szDest));
644 if (RT_SUCCESS(rc))
645 {
646 rc = ::RTStrCat(szDest, sizeof(szDest), " nohistory");
647 if (RT_SUCCESS(rc))
648 {
649 rc = ::RTLogDestinations(pReleaseLogger, szDest);
650 AssertRC(rc);
651 }
652 }
653 }
654
655 if (!hasActiveConnection())
656 break;
657 LogRel(("Still got active connection(s)...\n"));
658 }
659 }
660
661 LogRel(("Shutting down\n"));
662 if (m_hEventShutdown)
663 {
664 ::CloseHandle(m_hEventShutdown);
665 m_hEventShutdown = NULL;
666 }
667 ::PostThreadMessage(m_dwMainThreadID, WM_QUIT, 0, 0);
668 }
669
670 static DECLCALLBACK(int) monitorThreadProc(RTTHREAD hThreadSelf, void *pvUser) throw()
671 {
672 RT_NOREF(hThreadSelf);
673 CComServiceModule *p = static_cast<CComServiceModule *>(pvUser);
674 p->monitorShutdown();
675 return VINF_SUCCESS;
676 }
677
678 void startMonitor()
679 {
680 m_dwMainThreadID = ::GetCurrentThreadId();
681 m_hEventShutdown = ::CreateEvent(NULL, false, false, NULL);
682 AssertLogRelMsg(m_hEventShutdown != NULL, ("GetLastError => %u\n", GetLastError()));
683
684 int vrc = RTThreadCreate(NULL, monitorThreadProc, this, 0 /*cbStack*/, RTTHREADTYPE_DEFAULT, 0 /*fFlags*/, "MonShdwn");
685 if (RT_FAILURE(vrc))
686 {
687 ::CloseHandle(m_hEventShutdown);
688 m_hEventShutdown = NULL;
689 LogRel(("Error: RTThreadCreate failed to create shutdown monitor thread: %Rrc\n", vrc));
690 }
691 }
692
693 virtual HRESULT preMessageLoop(int nShowCmd)
694 {
695 Assert(m_fInitialized);
696 LogFunc(("Enter\n"));
697
698 HRESULT hrc = com::Initialize();
699 if (SUCCEEDED(hrc))
700 {
701 m_fComInitialized = true;
702 hrc = ATL::CComModule::RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
703 if (SUCCEEDED(hrc))
704 {
705 // Start Shutdown monitor here
706 startMonitor();
707
708 hrc = CWindowsServiceModule::preMessageLoop(nShowCmd);
709 if (FAILED(hrc))
710 LogRelFunc(("Warning: preMessageLoop failed: %Rhrc\n", hrc));
711
712 hrc = CoResumeClassObjects();
713 if (FAILED(hrc))
714 {
715 ATL::CComModule::RevokeClassObjects();
716 LogRelFunc(("Error: CoResumeClassObjects failed: %Rhrc\n", hrc));
717 }
718 }
719 else
720 LogRel(("Error: ATL::CComModule::RegisterClassObjects: %Rhrc\n", hrc));
721 }
722 else
723 LogRel(("Error: com::Initialize failed\n", hrc));
724 return hrc;
725 }
726
727 virtual HRESULT postMessageLoop()
728 {
729 com::Shutdown();
730 m_fComInitialized = false;
731 return S_OK;
732 }
733};
734
735/*static*/ CComServiceModule * volatile CComServiceModule::s_pInstance = NULL;
736
737
738#ifdef WITH_WATCHER
739/**
740 * Go-between for CComServiceModule and VirtualBoxSDS.
741 */
742void VBoxSDSNotifyClientCount(uint32_t cClients)
743{
744 CComServiceModule *pInstance = CComServiceModule::s_pInstance;
745 if (pInstance)
746 {
747 if (cClients == 0)
748 pInstance->notifyZeroClientConnections();
749 else
750 pInstance->notifyHasClientConnections();
751 }
752}
753#endif
754
755
756/** Special export that make VBoxProxyStub not register this process as one that
757 * VBoxSDS should be watching.
758 */
759extern "C" DECLEXPORT(void) VBOXCALL Is_VirtualBox_service_process_like_VBoxSDS_And_VBoxSDS(void)
760{
761 /* never called, just need to be here */
762}
763
764
765/**
766 * Main function for the VBoxSDS process.
767 *
768 * @param hInstance The process instance.
769 * @param hPrevInstance Previous instance (not used here).
770 * @param nShowCmd The show flags.
771 * @param lpCmdLine The command line (not used here, we get it from the
772 * C runtime library).
773 *
774 * @return Exit code
775 */
776int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
777{
778 RT_NOREF(hPrevInstance, lpCmdLine);
779 int argc = __argc;
780 char **argv = __argv;
781
782 /*
783 * Initialize the VBox runtime without loading the support driver.
784 */
785 RTR3InitExe(argc, &argv, 0);
786
787 static const RTGETOPTDEF s_aOptions[] =
788 {
789 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
790 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
791 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
792 { "--unregservice", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
793 { "-unregservice", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
794 { "/unregservice", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
795 { "--regservice", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
796 { "-regservice", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
797 { "/regservice", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
798 { "--reregservice", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
799 { "-reregservice", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
800 { "/reregservice", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
801 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
802 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
803 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
804 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
805 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
806 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
807 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
808 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
809 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
810 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
811 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
812 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
813 };
814
815 bool fRun = true;
816 bool fRegister = false;
817 bool fUnregister = false;
818 const char *pszLogFile = NULL;
819 uint32_t cHistory = 10; // enable log rotation, 10 files
820 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
821 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
822
823 RTGETOPTSTATE GetOptState;
824 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
825 AssertRC(vrc);
826
827 RTGETOPTUNION ValueUnion;
828 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
829 {
830 switch (vrc)
831 {
832 case 'e':
833 break;
834
835 case 'u':
836 fUnregister = true;
837 fRun = false;
838 break;
839
840 case 'r':
841 fRegister = true;
842 fRun = false;
843 break;
844
845 case 'f':
846 fUnregister = true;
847 fRegister = true;
848 fRun = false;
849 break;
850
851 case 'F':
852 pszLogFile = ValueUnion.psz;
853 break;
854
855 case 'R':
856 cHistory = ValueUnion.u32;
857 break;
858
859 case 'S':
860 uHistoryFileSize = ValueUnion.u64;
861 break;
862
863 case 'I':
864 uHistoryFileTime = ValueUnion.u32;
865 break;
866
867 case 'h':
868 {
869 static WCHAR const s_wszHelpText[] =
870 L"Options:\n"
871 L"\n"
872 L"/RegService\t" L"register COM out-of-proc service\n"
873 L"/UnregService\t" L"unregister COM out-of-proc service\n"
874 L"/ReregService\t" L"unregister and register COM service\n"
875 L"no options\t" L"run the service";
876 MessageBoxW(NULL, s_wszHelpText, L"VBoxSDS - Usage", MB_OK);
877 return 0;
878 }
879
880 case 'V':
881 {
882 char *pszText = NULL;
883 RTStrAPrintf(&pszText, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
884
885 PRTUTF16 pwszText = NULL;
886 RTStrToUtf16(pszText, &pwszText);
887
888 MessageBoxW(NULL, pwszText, L"VBoxSDS - Version", MB_OK);
889
890 RTStrFree(pszText);
891 RTUtf16Free(pwszText);
892 return 0;
893 }
894
895 default:
896 {
897 char szTmp[256];
898 RTGetOptFormatError(szTmp, sizeof(szTmp), vrc, &ValueUnion);
899
900 PRTUTF16 pwszText = NULL;
901 RTStrToUtf16(szTmp, &pwszText);
902
903 MessageBoxW(NULL, pwszText, L"VBoxSDS - Syntax error", MB_OK | MB_ICONERROR);
904
905 RTUtf16Free(pwszText);
906 return RTEXITCODE_SYNTAX;
907 }
908 }
909 }
910
911 /*
912 * Default log location is %ProgramData%\VirtualBox\VBoxSDS.log, falling back
913 * on %_CWD%\VBoxSDS.log (where _CWD typicaly is 'C:\Windows\System32').
914 *
915 * We change the current directory to %ProgramData%\VirtualBox\ if possible.
916 *
917 * We only create the log file when running VBoxSDS normally, but not
918 * when registering/unregistering, at least for now.
919 */
920 if (fRun)
921 {
922 char szLogFile[RTPATH_MAX];
923 if (!pszLogFile || !*pszLogFile)
924 {
925 WCHAR wszAppData[MAX_PATH + 16];
926 if (SHGetSpecialFolderPathW(NULL, wszAppData, CSIDL_COMMON_APPDATA, TRUE /*fCreate*/))
927 {
928 char *pszConv = szLogFile;
929 vrc = RTUtf16ToUtf8Ex(wszAppData, RTSTR_MAX, &pszConv, sizeof(szLogFile) - 12, NULL);
930 }
931 else
932 vrc = RTEnvGetUtf8("ProgramData", szLogFile, sizeof(szLogFile) - sizeof("VBoxSDS.log"), NULL);
933 if (RT_SUCCESS(vrc))
934 {
935 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VirtualBox\\");
936 if (RT_SUCCESS(vrc))
937 {
938 /* Make sure it exists. */
939 if (!RTDirExists(szLogFile))
940 vrc = RTDirCreate(szLogFile, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET);
941 if (RT_SUCCESS(vrc))
942 {
943 /* Change into it. */
944 RTPathSetCurrent(szLogFile);
945 }
946 }
947 }
948 if (RT_FAILURE(vrc)) /* ignore any failure above */
949 szLogFile[0] = '\0';
950 vrc = RTStrCat(szLogFile, sizeof(szLogFile), "VBoxSDS.log");
951 if (RT_FAILURE(vrc))
952 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct release log filename: %Rrc", vrc);
953 pszLogFile = szLogFile;
954 }
955
956 RTERRINFOSTATIC ErrInfo;
957 vrc = com::VBoxLogRelCreate("COM Service", pszLogFile,
958 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
959 VBOXSDS_LOG_DEFAULT, "VBOXSDS_RELEASE_LOG",
960 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
961 cHistory, uHistoryFileTime, uHistoryFileSize,
962 RTErrInfoInitStatic(&ErrInfo));
963 if (RT_FAILURE(vrc))
964 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
965 }
966
967
968 /*
969 * Initialize COM.
970 */
971 HRESULT hrcExit = com::Initialize();
972 if (SUCCEEDED(hrcExit))
973 {
974 HRESULT hrcSec = CoInitializeSecurity(NULL,
975 -1,
976 NULL,
977 NULL,
978 RPC_C_AUTHN_LEVEL_DEFAULT,
979 RPC_C_IMP_LEVEL_IMPERSONATE,//RPC_C_IMP_LEVEL_IMPERSONATE, RPC_C_IMP_LEVEL_DELEGATE
980 NULL,
981 EOAC_NONE, //EOAC_DYNAMIC_CLOAKING,//EOAC_STATIC_CLOAKING, //EOAC_NONE,
982 NULL);
983 LogRelFunc(("VBoxSDS: InitializeSecurity: %x\n", hrcSec));
984
985 /*
986 * Instantiate our COM service class.
987 */
988 CComServiceModule *pServiceModule = new CComServiceModule();
989 if (pServiceModule)
990 {
991 BEGIN_OBJECT_MAP(s_aObjectMap)
992 OBJECT_ENTRY(CLSID_VirtualBoxSDS, VirtualBoxSDS)
993 END_OBJECT_MAP()
994 hrcExit = pServiceModule->init(s_aObjectMap, hInstance, &LIBID_VirtualBox,
995 L"VBoxSDS",
996 L"VirtualBox system service",
997 L"Used as a COM server for VirtualBox API.");
998
999 if (SUCCEEDED(hrcExit))
1000 {
1001 if (!fRun)
1002 {
1003 /*
1004 * Do registration work and quit.
1005 */
1006 /// @todo The VBoxProxyStub should do all work for COM registration
1007 if (fUnregister)
1008 hrcExit = pServiceModule->unregisterService();
1009 if (fRegister)
1010 hrcExit = pServiceModule->registerService();
1011 }
1012 else
1013 {
1014 /*
1015 * Run service.
1016 */
1017 CComServiceModule::s_pInstance = pServiceModule;
1018 hrcExit = pServiceModule->startService(nShowCmd);
1019 LogRelFunc(("VBoxSDS: Calling _ServiceModule.RevokeClassObjects()...\n"));
1020 CComServiceModule::s_pInstance = NULL;
1021 pServiceModule->RevokeClassObjects();
1022 }
1023
1024 LogRelFunc(("VBoxSDS: Calling _ServiceModule.Term()...\n"));
1025 pServiceModule->Term();
1026 }
1027 else
1028 LogRelFunc(("VBoxSDS: new CComServiceModule::Init failed: %Rhrc\n", hrcExit));
1029
1030 LogRelFunc(("VBoxSDS: deleting pServiceModule (%p)\n", pServiceModule));
1031 delete pServiceModule;
1032 pServiceModule = NULL;
1033 }
1034 else
1035 LogRelFunc(("VBoxSDS: new CComServiceModule() failed\n"));
1036
1037 LogRelFunc(("VBoxSDS: Calling com::Shutdown\n"));
1038 com::Shutdown();
1039 }
1040 else
1041 LogRelFunc(("VBoxSDS: COM initialization failed: %Rrc\n", hrcExit));
1042
1043 LogRelFunc(("VBoxSDS: COM service process ends: hrcExit=%Rhrc (%#x)\n", hrcExit, hrcExit));
1044 return (int)hrcExit;
1045}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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