VirtualBox

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

Last change on this file since 72118 was 71716, checked in by vboxsync, 7 years ago

VBoxSDS,VBoxSVC: Replaced the enviornment variable hack (VBOX_SERVICE_PROCESS) for preventing VBoxSDS and VBoxSVC from getting watched as clients, with a special export in the executable (Is_VirtualBox_service_process_like_VBoxSDS_And_VBoxSDS). Some cleanups here and there.

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