VirtualBox

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

Last change on this file since 23702 was 23223, checked in by vboxsync, 15 years ago

API: big medium handling change and lots of assorted other cleanups and fixes

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