VirtualBox

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

Last change on this file since 94660 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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