VirtualBox

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

Last change on this file since 26163 was 26089, checked in by vboxsync, 15 years ago

Branding: Moved the Product & Vendor strings to kBuild, so it could be used
there as well. Added a Copyright year define which points to the current year.
All this should be used on more places. For now the help strings of the
Frontends and most strings of the Mac OS X installer are updated.

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