VirtualBox

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

Last change on this file since 20626 was 20598, checked in by vboxsync, 15 years ago

Main: implmented waitForEvents(aTimeout) API for XPCOM targets, added command to VBox shell using this API

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