VirtualBox

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

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

Main: Make it possible to cancel the starting of a teleporation target.

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