VirtualBox

source: vbox/trunk/src/VBox/Main/xpcom/server.cpp@ 33281

Last change on this file since 33281 was 33112, checked in by vboxsync, 14 years ago

Main/xpcom: Start VBoxSVC just like VBoxXPCOMIPCD directly as a detached process, avoids the extra re-exec, a useless log file for debug builds and simplifies the code. We can still go back to the old approach as VBoxSVC is still capable of daemonizing itself.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.7 KB
Line 
1/* $Id: server.cpp 33112 2010-10-13 17:34:05Z vboxsync $ */
2/** @file
3 * XPCOM server process (VBoxSVC) start point.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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#include <ipcIService.h>
19#include <ipcCID.h>
20
21#include <nsIComponentRegistrar.h>
22
23#ifdef XPCOM_GLUE
24# include <nsXPCOMGlue.h>
25#endif
26
27#include <nsEventQueueUtils.h>
28#include <nsGenericFactory.h>
29
30#include "prio.h"
31#include "prproces.h"
32
33#include "xpcom/server.h"
34
35#include "Logging.h"
36
37#include <VBox/param.h>
38#include <VBox/version.h>
39
40#include <iprt/buildconfig.h>
41#include <iprt/initterm.h>
42#include <iprt/critsect.h>
43#include <iprt/getopt.h>
44#include <iprt/message.h>
45#include <iprt/stream.h>
46#include <iprt/path.h>
47#include <iprt/timer.h>
48#include <iprt/env.h>
49
50#include <signal.h> // for the signal handler
51#include <stdlib.h>
52#include <unistd.h>
53#include <errno.h>
54#include <fcntl.h>
55#include <sys/stat.h>
56#include <sys/resource.h>
57
58/////////////////////////////////////////////////////////////////////////////
59// VirtualBox component instantiation
60/////////////////////////////////////////////////////////////////////////////
61
62#include <nsIGenericFactory.h>
63
64#include <VirtualBox_XPCOM.h>
65#include <VirtualBoxImpl.h>
66#include <MachineImpl.h>
67#include <VFSExplorerImpl.h>
68#include <ApplianceImpl.h>
69#include <SnapshotImpl.h>
70#include <MediumImpl.h>
71#include <MediumFormatImpl.h>
72#include <ProgressCombinedImpl.h>
73#include <ProgressProxyImpl.h>
74#include <VRDPServerImpl.h>
75#include <SharedFolderImpl.h>
76#include <HostImpl.h>
77#include <HostNetworkInterfaceImpl.h>
78#include <GuestOSTypeImpl.h>
79#include <NetworkAdapterImpl.h>
80#include <NATEngineImpl.h>
81#include <SerialPortImpl.h>
82#include <ParallelPortImpl.h>
83#include <USBControllerImpl.h>
84#include "DHCPServerRunner.h"
85#include "DHCPServerImpl.h"
86#ifdef VBOX_WITH_USB
87# include "USBDeviceFilterImpl.h"
88# include <HostUSBDeviceImpl.h>
89# include <USBDeviceImpl.h>
90#endif
91#include <StorageControllerImpl.h>
92#include <AudioAdapterImpl.h>
93#include <SystemPropertiesImpl.h>
94
95/* implement nsISupports parts of our objects with support for nsIClassInfo */
96
97NS_DECL_CLASSINFO(VirtualBox)
98NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VirtualBox, IVirtualBox)
99
100NS_DECL_CLASSINFO(Machine)
101NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Machine, IMachine)
102
103NS_DECL_CLASSINFO(VFSExplorer)
104NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VFSExplorer, IVFSExplorer)
105
106NS_DECL_CLASSINFO(Appliance)
107NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Appliance, IAppliance)
108
109NS_DECL_CLASSINFO(VirtualSystemDescription)
110NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VirtualSystemDescription, IVirtualSystemDescription)
111
112NS_DECL_CLASSINFO(SessionMachine)
113NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
114
115NS_DECL_CLASSINFO(SnapshotMachine)
116NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
117
118NS_DECL_CLASSINFO(Snapshot)
119NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Snapshot, ISnapshot)
120
121NS_DECL_CLASSINFO(Medium)
122NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Medium, IMedium)
123
124NS_DECL_CLASSINFO(MediumFormat)
125NS_IMPL_THREADSAFE_ISUPPORTS1_CI(MediumFormat, IMediumFormat)
126
127NS_DECL_CLASSINFO(MediumAttachment)
128NS_IMPL_THREADSAFE_ISUPPORTS1_CI(MediumAttachment, IMediumAttachment)
129
130NS_DECL_CLASSINFO(Progress)
131NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Progress, IProgress)
132
133NS_DECL_CLASSINFO(CombinedProgress)
134NS_IMPL_THREADSAFE_ISUPPORTS1_CI(CombinedProgress, IProgress)
135
136NS_DECL_CLASSINFO(ProgressProxy)
137NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ProgressProxy, IProgress)
138
139NS_DECL_CLASSINFO(SharedFolder)
140NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SharedFolder, ISharedFolder)
141
142#ifdef VBOX_WITH_VRDP
143NS_DECL_CLASSINFO(VRDPServer)
144NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VRDPServer, IVRDPServer)
145#endif
146
147NS_DECL_CLASSINFO(Host)
148NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Host, IHost)
149
150NS_DECL_CLASSINFO(HostNetworkInterface)
151NS_IMPL_THREADSAFE_ISUPPORTS1_CI(HostNetworkInterface, IHostNetworkInterface)
152
153NS_DECL_CLASSINFO(DHCPServer)
154NS_IMPL_THREADSAFE_ISUPPORTS1_CI(DHCPServer, IDHCPServer)
155
156NS_DECL_CLASSINFO(GuestOSType)
157NS_IMPL_THREADSAFE_ISUPPORTS1_CI(GuestOSType, IGuestOSType)
158
159NS_DECL_CLASSINFO(NetworkAdapter)
160NS_IMPL_THREADSAFE_ISUPPORTS1_CI(NetworkAdapter, INetworkAdapter)
161
162NS_DECL_CLASSINFO(NATEngine)
163NS_IMPL_THREADSAFE_ISUPPORTS1_CI(NATEngine, INATEngine)
164
165
166NS_DECL_CLASSINFO(SerialPort)
167NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SerialPort, ISerialPort)
168
169NS_DECL_CLASSINFO(ParallelPort)
170NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ParallelPort, IParallelPort)
171
172NS_DECL_CLASSINFO(USBController)
173NS_IMPL_THREADSAFE_ISUPPORTS1_CI(USBController, IUSBController)
174
175NS_DECL_CLASSINFO(StorageController)
176NS_IMPL_THREADSAFE_ISUPPORTS1_CI(StorageController, IStorageController)
177
178#ifdef VBOX_WITH_USB
179NS_DECL_CLASSINFO(USBDeviceFilter)
180NS_IMPL_THREADSAFE_ISUPPORTS1_CI(USBDeviceFilter, IUSBDeviceFilter)
181
182NS_DECL_CLASSINFO(HostUSBDevice)
183NS_IMPL_THREADSAFE_ISUPPORTS2_CI(HostUSBDevice, IUSBDevice, IHostUSBDevice)
184
185NS_DECL_CLASSINFO(HostUSBDeviceFilter)
186NS_IMPL_THREADSAFE_ISUPPORTS2_CI(HostUSBDeviceFilter, IUSBDeviceFilter, IHostUSBDeviceFilter)
187#endif
188
189NS_DECL_CLASSINFO(AudioAdapter)
190NS_IMPL_THREADSAFE_ISUPPORTS1_CI(AudioAdapter, IAudioAdapter)
191
192NS_DECL_CLASSINFO(SystemProperties)
193NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SystemProperties, ISystemProperties)
194
195#ifdef VBOX_WITH_RESOURCE_USAGE_API
196NS_DECL_CLASSINFO(PerformanceCollector)
197NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PerformanceCollector, IPerformanceCollector)
198NS_DECL_CLASSINFO(PerformanceMetric)
199NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PerformanceMetric, IPerformanceMetric)
200#endif /* VBOX_WITH_RESOURCE_USAGE_API */
201
202NS_DECL_CLASSINFO(BIOSSettings)
203NS_IMPL_THREADSAFE_ISUPPORTS1_CI(BIOSSettings, IBIOSSettings)
204
205////////////////////////////////////////////////////////////////////////////////
206
207enum
208{
209 /* Delay before shutting down the VirtualBox server after the last
210 * VirtualBox instance is released, in ms */
211 VBoxSVC_ShutdownDelay = 5000
212};
213
214static bool gAutoShutdown = false;
215
216static nsIEventQueue *gEventQ = nsnull;
217static PRBool volatile gKeepRunning = PR_TRUE;
218static PRBool volatile gAllowSigUsrQuit = PR_TRUE;
219
220/////////////////////////////////////////////////////////////////////////////
221
222/**
223 * Simple but smart PLEvent wrapper.
224 *
225 * @note Instances must be always created with <tt>operator new</tt>!
226 */
227class MyEvent
228{
229public:
230
231 MyEvent()
232 {
233 mEv.that = NULL;
234 };
235
236 /**
237 * Posts this event to the given message queue. This method may only be
238 * called once. @note On success, the event will be deleted automatically
239 * after it is delivered and handled. On failure, the event will delete
240 * itself before this method returns! The caller must not delete it in
241 * either case.
242 */
243 nsresult postTo(nsIEventQueue *aEventQ)
244 {
245 AssertReturn(mEv.that == NULL, NS_ERROR_FAILURE);
246 AssertReturn(aEventQ, NS_ERROR_FAILURE);
247 nsresult rv = aEventQ->InitEvent(&mEv.e, NULL,
248 eventHandler, eventDestructor);
249 if (NS_SUCCEEDED(rv))
250 {
251 mEv.that = this;
252 rv = aEventQ->PostEvent(&mEv.e);
253 if (NS_SUCCEEDED(rv))
254 return rv;
255 }
256 delete this;
257 return rv;
258 }
259
260 virtual void *handler() = 0;
261
262private:
263
264 struct Ev
265 {
266 PLEvent e;
267 MyEvent *that;
268 } mEv;
269
270 static void *PR_CALLBACK eventHandler(PLEvent *self)
271 {
272 return reinterpret_cast<Ev *>(self)->that->handler();
273 }
274
275 static void PR_CALLBACK eventDestructor(PLEvent *self)
276 {
277 delete reinterpret_cast<Ev *>(self)->that;
278 }
279};
280
281////////////////////////////////////////////////////////////////////////////////
282
283/**
284 * VirtualBox class factory that destroys the created instance right after
285 * the last reference to it is released by the client, and recreates it again
286 * when necessary (so VirtualBox acts like a singleton object).
287 */
288class VirtualBoxClassFactory : public VirtualBox
289{
290public:
291
292 virtual ~VirtualBoxClassFactory()
293 {
294 LogFlowFunc(("Deleting VirtualBox...\n"));
295
296 FinalRelease();
297 sInstance = NULL;
298
299 LogFlowFunc(("VirtualBox object deleted.\n"));
300 RTPrintf("Informational: VirtualBox object deleted.\n");
301 }
302
303 NS_IMETHOD_(nsrefcnt) Release()
304 {
305 /* we overload Release() to guarantee the VirtualBox destructor is
306 * always called on the main thread */
307
308 nsrefcnt count = VirtualBox::Release();
309
310 if (count == 1)
311 {
312 /* the last reference held by clients is being released
313 * (see GetInstance()) */
314
315 PRBool onMainThread = PR_TRUE;
316 if (gEventQ)
317 gEventQ->IsOnCurrentThread(&onMainThread);
318
319 PRBool timerStarted = PR_FALSE;
320
321 /* sTimer is null if this call originates from FactoryDestructor()*/
322 if (sTimer != NULL)
323 {
324 LogFlowFunc(("Last VirtualBox instance was released.\n"));
325 LogFlowFunc(("Scheduling server shutdown in %d ms...\n",
326 VBoxSVC_ShutdownDelay));
327
328 /* make sure the previous timer (if any) is stopped;
329 * otherwise RTTimerStart() will definitely fail. */
330 RTTimerLRStop(sTimer);
331
332 int vrc = RTTimerLRStart(sTimer, uint64_t(VBoxSVC_ShutdownDelay) * 1000000);
333 AssertRC(vrc);
334 timerStarted = SUCCEEDED(vrc);
335 }
336 else
337 {
338 LogFlowFunc(("Last VirtualBox instance was released "
339 "on XPCOM shutdown.\n"));
340 Assert(onMainThread);
341 }
342
343 gAllowSigUsrQuit = PR_TRUE;
344
345 if (!timerStarted)
346 {
347 if (!onMainThread)
348 {
349 /* Failed to start the timer, post the shutdown event
350 * manually if not on the main thread alreay. */
351 ShutdownTimer(NULL, NULL, 0);
352 }
353 else
354 {
355 /* Here we come if:
356 *
357 * a) gEventQ is 0 which means either FactoryDestructor() is called
358 * or the IPC/DCONNECT shutdown sequence is initiated by the
359 * XPCOM shutdown routine (NS_ShutdownXPCOM()), which always
360 * happens on the main thread.
361 *
362 * b) gEventQ has reported we're on the main thread. This means
363 * that DestructEventHandler() has been called, but another
364 * client was faster and requested VirtualBox again.
365 *
366 * In either case, there is nothing to do.
367 *
368 * Note: case b) is actually no more valid since we don't
369 * call Release() from DestructEventHandler() in this case
370 * any more. Thus, we assert below.
371 */
372
373 Assert(gEventQ == NULL);
374 }
375 }
376 }
377
378 return count;
379 }
380
381 class MaybeQuitEvent : public MyEvent
382 {
383 /* called on the main thread */
384 void *handler()
385 {
386 LogFlowFunc(("\n"));
387
388 Assert(RTCritSectIsInitialized(&sLock));
389
390 /* stop accepting GetInstance() requests on other threads during
391 * possible destruction */
392 RTCritSectEnter(&sLock);
393
394 nsrefcnt count = 0;
395
396 /* sInstance is NULL here if it was deleted immediately after
397 * creation due to initialization error. See GetInstance(). */
398 if (sInstance != NULL)
399 {
400 /* Release the guard reference added in GetInstance() */
401 count = sInstance->Release();
402 }
403
404 if (count == 0)
405 {
406 if (gAutoShutdown)
407 {
408 Assert(sInstance == NULL);
409 LogFlowFunc(("Terminating the server process...\n"));
410 /* make it leave the event loop */
411 gKeepRunning = PR_FALSE;
412 }
413 }
414 else
415 {
416 /* This condition is quite rare: a new client happened to
417 * connect after this event has been posted to the main queue
418 * but before it started to process it. */
419 LogFlowFunc(("Destruction is canceled (refcnt=%d).\n", count));
420 }
421
422 RTCritSectLeave(&sLock);
423
424 return NULL;
425 }
426 };
427
428 static void ShutdownTimer(RTTIMERLR hTimerLR, void *pvUser, uint64_t /*iTick*/)
429 {
430 NOREF(hTimerLR);
431 NOREF(pvUser);
432
433 /* A "too late" event is theoretically possible if somebody
434 * manually ended the server after a destruction has been scheduled
435 * and this method was so lucky that it got a chance to run before
436 * the timer was killed. */
437 AssertReturnVoid(gEventQ);
438
439 /* post a quit event to the main queue */
440 MaybeQuitEvent *ev = new MaybeQuitEvent();
441 nsresult rv = ev->postTo(gEventQ);
442 NOREF(rv);
443
444 /* A failure above means we've been already stopped (for example
445 * by Ctrl-C). FactoryDestructor() (NS_ShutdownXPCOM())
446 * will do the job. Nothing to do. */
447 }
448
449 static NS_IMETHODIMP FactoryConstructor()
450 {
451 LogFlowFunc(("\n"));
452
453 /* create a critsect to protect object construction */
454 if (RT_FAILURE(RTCritSectInit(&sLock)))
455 return NS_ERROR_OUT_OF_MEMORY;
456
457 int vrc = RTTimerLRCreateEx(&sTimer, 0, 0, ShutdownTimer, NULL);
458 if (RT_FAILURE(vrc))
459 {
460 LogFlowFunc(("Failed to create a timer! (vrc=%Rrc)\n", vrc));
461 return NS_ERROR_FAILURE;
462 }
463
464 return NS_OK;
465 }
466
467 static NS_IMETHODIMP FactoryDestructor()
468 {
469 LogFlowFunc(("\n"));
470
471 RTTimerLRDestroy(sTimer);
472 sTimer = NULL;
473
474 RTCritSectDelete(&sLock);
475
476 if (sInstance != NULL)
477 {
478 /* Either posting a destruction event falied for some reason (most
479 * likely, the quit event has been received before the last release),
480 * or the client has terminated abnormally w/o releasing its
481 * VirtualBox instance (so NS_ShutdownXPCOM() is doing a cleanup).
482 * Release the guard reference we added in GetInstance(). */
483 sInstance->Release();
484 }
485
486 return NS_OK;
487 }
488
489 static nsresult GetInstance(VirtualBox **inst)
490 {
491 LogFlowFunc(("Getting VirtualBox object...\n"));
492
493 RTCritSectEnter(&sLock);
494
495 if (!gKeepRunning)
496 {
497 LogFlowFunc(("Process termination requested first. Refusing.\n"));
498
499 RTCritSectLeave(&sLock);
500
501 /* this rv is what CreateInstance() on the client side returns
502 * when the server process stops accepting events. Do the same
503 * here. The client wrapper should attempt to start a new process in
504 * response to a failure from us. */
505 return NS_ERROR_ABORT;
506 }
507
508 nsresult rv = NS_OK;
509
510 if (sInstance == NULL)
511 {
512 LogFlowFunc (("Creating new VirtualBox object...\n"));
513 sInstance = new VirtualBoxClassFactory();
514 if (sInstance != NULL)
515 {
516 /* make an extra AddRef to take the full control
517 * on the VirtualBox destruction (see FinalRelease()) */
518 sInstance->AddRef();
519
520 sInstance->AddRef(); /* protect FinalConstruct() */
521 rv = sInstance->FinalConstruct();
522 RTPrintf("Informational: VirtualBox object created (rc=%Rhrc).\n", rv);
523 if (NS_FAILED(rv))
524 {
525 /* On failure diring VirtualBox initialization, delete it
526 * immediately on the current thread by releasing all
527 * references in order to properly schedule the server
528 * shutdown. Since the object is fully deleted here, there
529 * is a chance to fix the error and request a new
530 * instantiation before the server terminates. However,
531 * the main reason to maintain the shoutdown delay on
532 * failure is to let the front-end completely fetch error
533 * info from a server-side IVirtualBoxErrorInfo object. */
534 sInstance->Release();
535 sInstance->Release();
536 Assert(sInstance == NULL);
537 }
538 else
539 {
540 /* On success, make sure the previous timer is stopped to
541 * cancel a scheduled server termination (if any). */
542 gAllowSigUsrQuit = PR_FALSE;
543 RTTimerLRStop(sTimer);
544 }
545 }
546 else
547 {
548 rv = NS_ERROR_OUT_OF_MEMORY;
549 }
550 }
551 else
552 {
553 LogFlowFunc(("Using existing VirtualBox object...\n"));
554 nsrefcnt count = sInstance->AddRef();
555 Assert(count > 1);
556
557 if (count == 2)
558 {
559 LogFlowFunc(("Another client has requested a reference to VirtualBox, canceling detruction...\n"));
560
561 /* make sure the previous timer is stopped */
562 gAllowSigUsrQuit = PR_FALSE;
563 RTTimerLRStop(sTimer);
564 }
565 }
566
567 *inst = sInstance;
568
569 RTCritSectLeave(&sLock);
570
571 return rv;
572 }
573
574private:
575
576 /* Don't be confused that sInstance is of the *ClassFactory type. This is
577 * actually a singleton instance (*ClassFactory inherits the singleton
578 * class; we combined them just for "simplicity" and used "static" for
579 * factory methods. *ClassFactory here is necessary for a couple of extra
580 * methods. */
581
582 static VirtualBoxClassFactory *sInstance;
583 static RTCRITSECT sLock;
584
585 static RTTIMERLR sTimer;
586};
587
588VirtualBoxClassFactory *VirtualBoxClassFactory::sInstance = NULL;
589RTCRITSECT VirtualBoxClassFactory::sLock;
590
591RTTIMERLR VirtualBoxClassFactory::sTimer = NIL_RTTIMERLR;
592
593NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR_WITH_RC(VirtualBox, VirtualBoxClassFactory::GetInstance)
594
595////////////////////////////////////////////////////////////////////////////////
596
597typedef NSFactoryDestructorProcPtr NSFactoryConsructorProcPtr;
598
599/**
600 * Enhanced module component information structure.
601 *
602 * nsModuleComponentInfo lacks the factory construction callback, here we add
603 * it. This callback is called by NS_NewGenericFactoryEx() after a
604 * nsGenericFactory instance is successfully created.
605 */
606struct nsModuleComponentInfoEx : nsModuleComponentInfo
607{
608 nsModuleComponentInfoEx() {}
609 nsModuleComponentInfoEx(int) {}
610
611 nsModuleComponentInfoEx(
612 const char* aDescription,
613 const nsCID& aCID,
614 const char* aContractID,
615 NSConstructorProcPtr aConstructor,
616 NSRegisterSelfProcPtr aRegisterSelfProc,
617 NSUnregisterSelfProcPtr aUnregisterSelfProc,
618 NSFactoryDestructorProcPtr aFactoryDestructor,
619 NSGetInterfacesProcPtr aGetInterfacesProc,
620 NSGetLanguageHelperProcPtr aGetLanguageHelperProc,
621 nsIClassInfo ** aClassInfoGlobal,
622 PRUint32 aFlags,
623 NSFactoryConsructorProcPtr aFactoryConstructor)
624 {
625 mDescription = aDescription;
626 mCID = aCID;
627 mContractID = aContractID;
628 mConstructor = aConstructor;
629 mRegisterSelfProc = aRegisterSelfProc;
630 mUnregisterSelfProc = aUnregisterSelfProc;
631 mFactoryDestructor = aFactoryDestructor;
632 mGetInterfacesProc = aGetInterfacesProc;
633 mGetLanguageHelperProc = aGetLanguageHelperProc;
634 mClassInfoGlobal = aClassInfoGlobal;
635 mFlags = aFlags;
636 mFactoryConstructor = aFactoryConstructor;
637 }
638
639 /** (optional) Factory Construction Callback */
640 NSFactoryConsructorProcPtr mFactoryConstructor;
641};
642
643////////////////////////////////////////////////////////////////////////////////
644
645static const nsModuleComponentInfoEx components[] =
646{
647 nsModuleComponentInfoEx(
648 "VirtualBox component",
649 CLSID_VirtualBox,
650 NS_VIRTUALBOX_CONTRACTID,
651 VirtualBoxConstructor, // constructor funcion
652 NULL, // registration function
653 NULL, // deregistration function
654 VirtualBoxClassFactory::FactoryDestructor, // factory destructor function
655 NS_CI_INTERFACE_GETTER_NAME(VirtualBox),
656 NULL, // language helper
657 &NS_CLASSINFO_NAME(VirtualBox),
658 0, // flags
659 VirtualBoxClassFactory::FactoryConstructor // factory constructor function
660 )
661};
662
663/////////////////////////////////////////////////////////////////////////////
664
665/**
666 * Extends NS_NewGenericFactory() by immediately calling
667 * nsModuleComponentInfoEx::mFactoryConstructor before returning to the
668 * caller.
669 */
670nsresult
671NS_NewGenericFactoryEx(nsIGenericFactory **result,
672 const nsModuleComponentInfoEx *info)
673{
674 AssertReturn(result, NS_ERROR_INVALID_POINTER);
675
676 nsresult rv = NS_NewGenericFactory(result, info);
677 if (NS_SUCCEEDED(rv) && info && info->mFactoryConstructor)
678 {
679 rv = info->mFactoryConstructor();
680 if (NS_FAILED(rv))
681 NS_RELEASE(*result);
682 }
683
684 return rv;
685}
686
687/////////////////////////////////////////////////////////////////////////////
688
689/**
690 * Helper function to register self components upon start-up
691 * of the out-of-proc server.
692 */
693static nsresult
694RegisterSelfComponents(nsIComponentRegistrar *registrar,
695 const nsModuleComponentInfoEx *aComponents,
696 PRUint32 count)
697{
698 nsresult rc = NS_OK;
699 const nsModuleComponentInfoEx *info = aComponents;
700 for (PRUint32 i = 0; i < count && NS_SUCCEEDED(rc); i++, info++)
701 {
702 /* skip components w/o a constructor */
703 if (!info->mConstructor)
704 continue;
705 /* create a new generic factory for a component and register it */
706 nsIGenericFactory *factory;
707 rc = NS_NewGenericFactoryEx(&factory, info);
708 if (NS_SUCCEEDED(rc))
709 {
710 rc = registrar->RegisterFactory(info->mCID,
711 info->mDescription,
712 info->mContractID,
713 factory);
714 factory->Release();
715 }
716 }
717 return rc;
718}
719
720/////////////////////////////////////////////////////////////////////////////
721
722static ipcIService *gIpcServ = nsnull;
723static const char *g_pszPidFile = NULL;
724
725class ForceQuitEvent : public MyEvent
726{
727 void *handler()
728 {
729 LogFlowFunc(("\n"));
730
731 gKeepRunning = PR_FALSE;
732
733 if (g_pszPidFile)
734 RTFileDelete(g_pszPidFile);
735
736 return NULL;
737 }
738};
739
740static void signal_handler(int sig)
741{
742 if (gEventQ && gKeepRunning)
743 {
744 if (sig == SIGUSR1)
745 {
746 if (gAllowSigUsrQuit)
747 {
748 VirtualBoxClassFactory::MaybeQuitEvent *ev = new VirtualBoxClassFactory::MaybeQuitEvent();
749 ev->postTo(gEventQ);
750 }
751 /* else do nothing */
752 }
753 else
754 {
755 /* post a force quit event to the queue */
756 ForceQuitEvent *ev = new ForceQuitEvent();
757 ev->postTo(gEventQ);
758 }
759 }
760}
761
762static nsresult vboxsvcSpawnDaemonByReExec(const char *pszPath)
763{
764 PRFileDesc *readable = nsnull, *writable = nsnull;
765 PRProcessAttr *attr = nsnull;
766 nsresult rv = NS_ERROR_FAILURE;
767 PRFileDesc *devNull;
768 // The ugly casts are necessary because the PR_CreateProcessDetached has
769 // a const array of writable strings as a parameter. It won't write. */
770 char * const args[] = { (char *)pszPath, (char *)"--auto-shutdown", 0 };
771
772 // Use a pipe to determine when the daemon process is in the position
773 // to actually process requests. The daemon will write "READY" to the pipe.
774 if (PR_CreatePipe(&readable, &writable) != PR_SUCCESS)
775 goto end;
776 PR_SetFDInheritable(writable, PR_TRUE);
777
778 attr = PR_NewProcessAttr();
779 if (!attr)
780 goto end;
781
782 if (PR_ProcessAttrSetInheritableFD(attr, writable, VBOXSVC_STARTUP_PIPE_NAME) != PR_SUCCESS)
783 goto end;
784
785 devNull = PR_Open("/dev/null", PR_RDWR, 0);
786 if (!devNull)
787 goto end;
788
789 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, devNull);
790 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, devNull);
791 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardError, devNull);
792
793 if (PR_CreateProcessDetached(pszPath, args, nsnull, attr) != PR_SUCCESS)
794 goto end;
795
796 // Close /dev/null
797 PR_Close(devNull);
798 // Close the child end of the pipe to make it the only owner of the
799 // file descriptor, so that unexpected closing can be detected.
800 PR_Close(writable);
801 writable = nsnull;
802
803 char msg[10];
804 memset(msg, '\0', sizeof(msg));
805 if ( PR_Read(readable, msg, sizeof(msg)-1) != 5
806 || strcmp(msg, "READY"))
807 goto end;
808
809 rv = NS_OK;
810
811end:
812 if (readable)
813 PR_Close(readable);
814 if (writable)
815 PR_Close(writable);
816 if (attr)
817 PR_DestroyProcessAttr(attr);
818 return rv;
819}
820
821int main(int argc, char **argv)
822{
823 /*
824 * Initialize the VBox runtime without loading
825 * the support driver
826 */
827 RTR3Init();
828
829 static const RTGETOPTDEF s_aOptions[] =
830 {
831 { "--automate", 'a', RTGETOPT_REQ_NOTHING },
832 { "--auto-shutdown", 'A', RTGETOPT_REQ_NOTHING },
833 { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
834 { "--pidfile", 'p', RTGETOPT_REQ_STRING },
835 };
836
837 bool fDaemonize = false;
838 PRFileDesc *daemon_pipe_wr = nsnull;
839
840 RTGETOPTSTATE GetOptState;
841 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
842 AssertRC(vrc);
843
844 RTGETOPTUNION ValueUnion;
845 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
846 {
847 switch (vrc)
848 {
849 case 'a':
850 {
851 /* --automate mode means we are started by XPCOM on
852 * demand. Daemonize ourselves and activate
853 * auto-shutdown. */
854 gAutoShutdown = true;
855 fDaemonize = true;
856 break;
857 }
858
859 /* Used together with '-P', see below. Internal use only. */
860 case 'A':
861 {
862 gAutoShutdown = true;
863 break;
864 }
865
866 case 'd':
867 {
868 fDaemonize = true;
869 break;
870 }
871
872 case 'p':
873 {
874 g_pszPidFile = ValueUnion.psz;
875 break;
876 }
877
878 case 'h':
879 {
880 RTPrintf("no help\n");
881 return 1;
882 }
883
884 case 'V':
885 {
886 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
887 return 0;
888 }
889
890 default:
891 return RTGetOptPrintError(vrc, &ValueUnion);
892 }
893 }
894
895 if (fDaemonize)
896 {
897 vboxsvcSpawnDaemonByReExec(argv[0]);
898 exit(126);
899 }
900
901 nsresult rc;
902
903 daemon_pipe_wr = PR_GetInheritedFD(VBOXSVC_STARTUP_PIPE_NAME);
904 RTEnvUnset("NSPR_INHERIT_FDS");
905
906 do
907 {
908 rc = com::Initialize();
909 if (NS_FAILED(rc))
910 {
911 RTMsgError("Failed to initialize XPCOM! (rc=%Rhrc)\n", rc);
912 break;
913 }
914
915 nsCOMPtr <nsIComponentRegistrar> registrar;
916 rc = NS_GetComponentRegistrar(getter_AddRefs(registrar));
917 if (NS_FAILED(rc))
918 {
919 RTMsgError("Failed to get component registrar! (rc=%Rhrc)", rc);
920 break;
921 }
922
923 registrar->AutoRegister(nsnull);
924 rc = RegisterSelfComponents(registrar, components,
925 NS_ARRAY_LENGTH (components));
926 if (NS_FAILED(rc))
927 {
928 RTMsgError("Failed to register server components! (rc=%Rhrc)", rc);
929 break;
930 }
931
932 /* get the main thread's event queue (afaik, the dconnect service always
933 * gets created upon XPCOM startup, so it will use the main (this)
934 * thread's event queue to receive IPC events) */
935 rc = NS_GetMainEventQ(&gEventQ);
936 if (NS_FAILED(rc))
937 {
938 RTMsgError("Failed to get the main event queue! (rc=%Rhrc)", rc);
939 break;
940 }
941
942 nsCOMPtr<ipcIService> ipcServ (do_GetService(IPC_SERVICE_CONTRACTID, &rc));
943 if (NS_FAILED(rc))
944 {
945 RTMsgError("Failed to get IPC service! (rc=%Rhrc)", rc);
946 break;
947 }
948
949 NS_ADDREF(gIpcServ = ipcServ);
950
951 LogFlowFunc(("Will use \"%s\" as server name.\n", VBOXSVC_IPC_NAME));
952
953 rc = gIpcServ->AddName(VBOXSVC_IPC_NAME);
954 if (NS_FAILED(rc))
955 {
956 LogFlowFunc(("Failed to register the server name (rc=%Rhrc (%08X))!\n"
957 "Is another server already running?\n", rc, rc));
958
959 RTMsgError("Failed to register the server name \"%s\" (rc=%Rhrc)!\n"
960 "Is another server already running?\n",
961 VBOXSVC_IPC_NAME, rc);
962 NS_RELEASE(gIpcServ);
963 break;
964 }
965
966 {
967 /* setup signal handling to convert some signals to a quit event */
968 struct sigaction sa;
969 sa.sa_handler = signal_handler;
970 sigemptyset(&sa.sa_mask);
971 sa.sa_flags = 0;
972 sigaction(SIGINT, &sa, NULL);
973 sigaction(SIGQUIT, &sa, NULL);
974 sigaction(SIGTERM, &sa, NULL);
975 sigaction(SIGTRAP, &sa, NULL);
976 sigaction(SIGUSR1, &sa, NULL);
977 }
978
979 {
980 char szBuf[80];
981 int iSize;
982
983 iSize = RTStrPrintf(szBuf, sizeof(szBuf),
984 VBOX_PRODUCT" XPCOM Server Version "
985 VBOX_VERSION_STRING);
986 for (int i = iSize; i > 0; i--)
987 putchar('*');
988 RTPrintf("\n%s\n", szBuf);
989 RTPrintf("(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
990 "All rights reserved.\n");
991#ifdef DEBUG
992 RTPrintf("Debug version.\n");
993#endif
994 }
995
996 if (daemon_pipe_wr != nsnull)
997 {
998 RTPrintf("\nStarting event loop....\n[send TERM signal to quit]\n");
999 /* now we're ready, signal the parent process */
1000 PR_Write(daemon_pipe_wr, "READY", strlen("READY"));
1001 /* close writing end of the pipe, its job is done */
1002 PR_Close(daemon_pipe_wr);
1003 }
1004 else
1005 RTPrintf("\nStarting event loop....\n[press Ctrl-C to quit]\n");
1006
1007 if (g_pszPidFile)
1008 {
1009 RTFILE hPidFile = NIL_RTFILE;
1010 vrc = RTFileOpen(&hPidFile, g_pszPidFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
1011 if (RT_SUCCESS(vrc))
1012 {
1013 char szBuf[32];
1014 const char *lf = "\n";
1015 RTStrFormatNumber(szBuf, getpid(), 10, 0, 0, 0);
1016 RTFileWrite(hPidFile, szBuf, strlen(szBuf), NULL);
1017 RTFileWrite(hPidFile, lf, strlen(lf), NULL);
1018 RTFileClose(hPidFile);
1019 }
1020 }
1021
1022 // Increase the file table size to 10240 or as high as possible.
1023 struct rlimit lim;
1024 if (getrlimit(RLIMIT_NOFILE, &lim) == 0)
1025 {
1026 if ( lim.rlim_cur < 10240
1027 && lim.rlim_cur < lim.rlim_max)
1028 {
1029 lim.rlim_cur = RT_MIN(lim.rlim_max, 10240);
1030 if (setrlimit(RLIMIT_NOFILE, &lim) == -1)
1031 RTPrintf("WARNING: failed to increase file descriptor limit. (%d)\n", errno);
1032 }
1033 }
1034 else
1035 RTPrintf("WARNING: failed to obtain per-process file-descriptor limit (%d).\n", errno);
1036
1037 PLEvent *ev;
1038 while (gKeepRunning)
1039 {
1040 gEventQ->WaitForEvent(&ev);
1041 gEventQ->HandleEvent(ev);
1042 }
1043
1044 /* stop accepting new events. Clients that happen to resolve our
1045 * name and issue a CreateInstance() request after this point will
1046 * get NS_ERROR_ABORT once we hande the remaining messages. As a
1047 * result, they should try to start a new server process. */
1048 gEventQ->StopAcceptingEvents();
1049
1050 /* unregister ourselves. After this point, clients will start a new
1051 * process because they won't be able to resolve the server name.*/
1052 gIpcServ->RemoveName(VBOXSVC_IPC_NAME);
1053
1054 /* process any remaining events. These events may include
1055 * CreateInstance() requests received right before we called
1056 * StopAcceptingEvents() above. We will detect this case below,
1057 * restore gKeepRunning and continue to serve. */
1058 gEventQ->ProcessPendingEvents();
1059
1060 RTPrintf("Terminated event loop.\n");
1061 }
1062 while (0); // this scopes the nsCOMPtrs
1063
1064 NS_IF_RELEASE(gIpcServ);
1065 NS_IF_RELEASE(gEventQ);
1066
1067 /* no nsCOMPtrs are allowed to be alive when you call com::Shutdown(). */
1068
1069 LogFlowFunc(("Calling com::Shutdown()...\n"));
1070 rc = com::Shutdown();
1071 LogFlowFunc(("Finished com::Shutdown() (rc=%Rhrc)\n", rc));
1072
1073 if (NS_FAILED(rc))
1074 RTMsgError("Failed to shutdown XPCOM! (rc=%Rhrc)", rc);
1075
1076 RTPrintf("XPCOM server has shutdown.\n");
1077
1078 if (g_pszPidFile)
1079 RTFileDelete(g_pszPidFile);
1080
1081 return 0;
1082}
Note: See TracBrowser for help on using the repository browser.

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