VirtualBox

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

Last change on this file since 104149 was 104149, checked in by vboxsync, 8 months ago

VBoxSDS: Added Windows-specific help command line parameters ('/?', '/h', '/help'), to match the rest of the syntax.

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