server.cpp revision 9d3a6dcdf5659c385fd193a59927d74c92ce401d
/** @file
*
* XPCOM server module implementation
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
#include <ipcIService.h>
#include <ipcCID.h>
#include <nsIServiceManager.h>
#include <nsIComponentRegistrar.h>
#include <nsXPCOMGlue.h>
#include <nsEventQueueUtils.h>
// for NS_InitXPCOM2 with bin dir parameter
#include <nsEmbedString.h>
#include <nsIFile.h>
#include <nsILocalFile.h>
#include "Logging.h"
#include <iprt/critsect.h>
// for nsMyFactory
#include "nsIGenericFactory.h"
#include "nsIClassInfo.h"
#include <stdio.h>
// for the signal handler
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <getopt.h>
// for the backtrace signal handler
# define USE_BACKTRACE
#endif
#if defined(USE_BACKTRACE)
# include <execinfo.h>
// get REG_EIP/RIP from ucontext.h
# ifndef __USE_GNU
# define __USE_GNU
# endif
# include <ucontext.h>
# ifdef __AMD64__
# else
# endif
#endif
/////////////////////////////////////////////////////////////////////////////
// VirtualBox component instantiation
/////////////////////////////////////////////////////////////////////////////
#include <nsIGenericFactory.h>
#include <VirtualBox_XPCOM.h>
#include <VirtualBoxImpl.h>
#include <MachineImpl.h>
#include <SnapshotImpl.h>
#include <HardDiskImpl.h>
#include <ProgressImpl.h>
#include <DVDDriveImpl.h>
#include <FloppyDriveImpl.h>
#include <VRDPServerImpl.h>
#include <DVDImageImpl.h>
#include <FloppyImageImpl.h>
#include <SharedFolderImpl.h>
#include <HostImpl.h>
#include <HostDVDDriveImpl.h>
#include <HostFloppyDriveImpl.h>
#include <HostUSBDeviceImpl.h>
#include <GuestOSTypeImpl.h>
#include <NetworkAdapterImpl.h>
#include <USBControllerImpl.h>
#include <USBDeviceImpl.h>
#include <AudioAdapterImpl.h>
#include <SystemPropertiesImpl.h>
#include <Collection.h>
// implement nsISupports parts of our objects with support for nsIClassInfo
#ifdef VBOX_VRDP
#endif
// collections and enumerators
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/**
* VirtualBox class factory that destroys the created instance right after
* the last reference to it is released by the client, and recreates it again
* when necessary (so VirtualBox acts like a singleton object).
*/
class VirtualBoxClassFactory : public VirtualBox
{
public:
virtual ~VirtualBoxClassFactory()
{
Log (("VirtualBoxClassFactory: deleting VirtualBox...\n"));
FinalRelease();
sInstance = 0;
Log (("VirtualBoxClassFactory: VirtualBox object deleted.\n"));
printf ("Informational: VirtualBox object deleted.\n");
}
{
// we overload Release() to guarantee the VirtualBox destructor is
// always called on the main thread
if (count == 1)
{
// the last reference held by clients is being released
// (see GetInstance())
if (gEventQ)
if (!onMainThread)
{
// post a destruction event to the main thread to release the
// extra reference added in GetInstance()
LogFlow (("VirtualBoxClassFactory: the last VirtualBox reference "
"is being released, scheduling the destruction...\n"));
{
// this means we've been already stopped (for example
// by Ctrl-C). FactoryDestructor() (NS_ShutdownXPCOM())
// will do the job.
}
}
else
{
// Here we come if:
//
// a) gEventQ is 0 which means either FactoryDestructor() is called
// XPCOM shutdown routine (NS_ShutdownXPCOM()), which always
// happens on the main thread.
//
// b) gEventQ has reported we're on the main thread. This means
// that DestructEventHandler() has been called, but another
// client was faster and requested VirtualBox again.
//
// We have nothing to do in these cases.
}
}
return count;
}
{
// stop accepting GetInstance() requests during possible destruction
RTCritSectEnter (&sLock);
// release the reference we added in GetInstance()
// (will call the destructor if nobody referenced us again)
if (count != 0)
{
LogFlow (("VirtualBoxClassFactory: destruciton is canceled\n"));
}
RTCritSectLeave (&sLock);
return 0;
}
{
delete self;
}
static NS_IMETHODIMP FactoryConstructor()
{
LogFlow (("VirtualBoxClassFactory::FactoryConstructor()\n"));
// create a critsect to protect object construction
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
static NS_IMETHODIMP FactoryDestructor()
{
LogFlow (("VirtualBoxClassFactory::FactoryDestructor()\n"));
if (sInstance)
{
// Either posting a destruction event falied for some reason (most
// likely, the quit event has been received before the last release),
// or the client has terminated abnormally w/o releasing its
// VirtualBox instance (so NS_ShutdownXPCOM() is doing a cleanup).
// Release the extra reference we added in GetInstance().
}
return NS_OK;
}
{
Log (("VirtualBoxClassFactory: getting VirtualBox object...\n"));
RTCritSectEnter (&sLock);
if (sInstance == 0)
{
Log (("VirtualBoxClassFactory: creating new VirtualBox object...\n"));
sInstance = new VirtualBoxClassFactory();
if (sInstance)
{
// make an extra AddRef to take the full control
// on the VirtualBox destruction (see FinalRelease())
{
// on failure diring VirtualBox initialization, delete it
// immediately on the current thread, ignoring the reference
// count (VirtualBox should be aware of that meaning that it
// has already completely unintialized itself in this case)
Log (("VirtualBoxClassFactory: VirtualBox creation failed "
"(rc=%08X), deleting immediately...\n", rv));
delete sInstance;
sInstance = 0;
}
}
else
{
}
}
else
{
Log (("VirtualBoxClassFactory: using existing VirtualBox object...\n"));
if (count == 2)
{
LogFlow (("VirtualBoxClassFactory: another client has requested "
"a reference of VirtualBox scheduled for destruction, "
"canceling detruction...\n"));
// add a reference to compensate one that DestructEventHandler()
// will release
}
}
RTCritSectLeave (&sLock);
return rv;
}
private:
static VirtualBox *sInstance;
static RTCRITSECT sLock;
};
)
////////////////////////////////////////////////////////////////////////////////
/**
* Enhanced module component information structure.
* nsModuleComponentInfo lacks the factory construction callback,
* here we add it. This callback is called by NS_NewMyFactory() after
* a nsMyFactory instance is successfully created.
*/
{
nsMyModuleComponentInfo () {}
nsMyModuleComponentInfo (int) {}
const char* aDescription,
const char* aContractID,
{
}
/** (optional) Factory Construction Callback */
};
////////////////////////////////////////////////////////////////////////////////
static const nsMyModuleComponentInfo components[] =
{
"VirtualBox component",
VirtualBoxConstructor, // constructor funcion
NULL, // registration function
NULL, // deregistration function
NULL, // language helper
0, // flags
)
};
/////////////////////////////////////////////////////////////////////////////
/**
* Generic component factory.
*
* The code below is stolen from nsGenericFactory.h / nsGenericFactory.cpp,
* because we get a segmentation fault for some unknown reason when VBoxSVC
* starts up (somewhere during the initialization of the libipcdc.so module)
* when we just reference XPCOM's NS_NewGenericFactory() from here (i.e. even
* before actually calling it) and run VBoxSVC using the debug XPCOM libraries.
*/
public:
/* nsIGenericFactory methods */
private:
~nsMyFactory();
const nsModuleComponentInfo *mInfo;
};
{
}
{
if (mInfo) {
if (mInfo->mFactoryDestructor)
if (mInfo->mClassInfoGlobal)
*mInfo->mClassInfoGlobal = 0;
}
}
{
if (mInfo->mConstructor)
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
{
// XXX do we care if (mInfo->mFlags & THREADSAFE)?
return NS_OK;
}
{
if (!mInfo->mGetInterfacesProc) {
*countp = 0;
return NS_OK;
}
}
{
if (mInfo->mGetLanguageHelperProc)
return NS_OK;
}
{
if (mInfo->mContractID) {
if (!*aContractID)
return NS_ERROR_OUT_OF_MEMORY;
} else {
*aContractID = nsnull;
}
return NS_OK;
}
{
if (mInfo->mDescription) {
*aClassDescription = (char *)
if (!*aClassDescription)
return NS_ERROR_OUT_OF_MEMORY;
} else {
}
return NS_OK;
}
{
*aClassID =
if (! *aClassID)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
{
return NS_OK;
}
{
return NS_OK;
}
{
return NS_OK;
}
// nsIGenericFactory: component-info accessors
{
*mInfo->mClassInfoGlobal = 0;
return NS_OK;
}
{
return NS_OK;
}
{
// sorry, aggregation not spoken here.
delete factory;
} else {
}
}
return res;
}
/**
* Instantiates a new factory and calls
* nsMyModuleComponentInfo::mFactoryConstructor.
*/
const nsMyModuleComponentInfo *info)
{
}
return rv;
return rv;
}
/////////////////////////////////////////////////////////////////////////////
// a helper function to register self components upon start-up
// of the out-of-proc server
static nsresult RegisterSelfComponents (
{
// skip components w/o a constructor
if (!info->mConstructor) continue;
// create a new generic factory for a component and register it
// rc = NS_NewGenericFactory (&factory, info);
if (NS_SUCCEEDED( rc )) {
);
}
}
return rc;
}
/////////////////////////////////////////////////////////////////////////////
#define VIRTUALBOXSERVER_IPCNAME "VirtualBoxServer"
static char *pszPidFile = NULL;
void signal_handler (int sig)
{
if (gEventQ && gKeepRunning)
{
// post a quit event to the queue
}
if (pszPidFile)
{
}
};
#if defined(USE_BACKTRACE)
/**
* the signal handler that prints out a backtrace of the call stack.
* the code is taken from http://www.linuxjournal.com/article/6391.
*/
{
void *trace[16];
int i, trace_size = 0;
// Do something useful with siginfo_t
Log (("Got signal %d, faulty address is %p, from %p\n",
else
// overwrite sigaction with caller's address
// skip first stack frame (points here)
Log (("[bt] Execution path:\n"));
for (i = 1; i < trace_size; ++i)
exit (0);
}
#endif
{
{
};
int c;
bool fDaemonize = false;
for (;;)
{
if (c == -1)
break;
switch (c)
{
case 'd':
{
fDaemonize = true;
break;
}
case 'p':
{
pszPidFile = optarg;
break;
}
default:
break;
}
}
static int daemon_pipe_fds[2];
if (fDaemonize)
{
/* create a pipe for communication between child and parent */
if (pipe(daemon_pipe_fds) < 0)
{
return 1;
}
if (childpid == -1)
{
printf("Error failed to fork!\n");
return 1;
}
if (childpid != 0)
{
/* we're the parent process */
bool fSuccess = false;
/* close the writing end of the pipe */
/* try to read a message from the pipe */
{
{
Log (("child process signalled ready\n"));
fSuccess = true;
}
else
{
Log (("unknown message from child\n"));
}
}
else
{
Log (("0 bytes read from child process\n"));
}
/* close the reading end of the pipe as well and exit */
close(daemon_pipe_fds[0]);
return fSuccess ? 0 : 1;
}
/* we're the child process */
/* close the reading end of the pipe */
close(daemon_pipe_fds[0]);
}
#if defined(USE_BACKTRACE)
{
// install our signal handler to backtrace the call stack
}
#endif
/*
* Initialize the VBox runtime without loading
* the support driver
*/
RTR3Init(false);
do
{
// get the path to the executable
char buf [RTPATH_MAX];
#if defined(DEBUG)
if (appPath)
else
#endif
{
if (NS_SUCCEEDED (rc))
}
{
break;
}
if (!servMan)
{
printf ("ERROR: failed to get service manager!\n");
break;
}
if (!registrar)
{
printf ("ERROR: failed to get component registrar!\n");
break;
}
{
break;
}
// get the main thread's event queue (afaik, the dconnect service always
// gets created upon XPCOM startup, so it will use the main (this)
// thread's event queue to receive IPC events)
{
break;
}
{
break;
}
{
break;
}
{
// setup signal handling to convert some signals to a quit event
}
{
char szBuf[80];
int iSize;
"InnoTek VirtualBox XPCOM Server Version %s",
for (int i=iSize; i>0; i--)
putchar('*');
printf ("(C) 2004-2007 InnoTek Systemberatung GmbH\n");
printf ("All rights reserved.\n");
#ifdef DEBUG
printf ("Debug version.\n");
#endif
#if 0
/* in my opinion two lines enclosing the text look better */
for (int i=iSize; i>0; i--)
putchar('*');
putchar('\n');
#endif
}
if (fDaemonize)
{
printf ("\nStarting event loop....\n[send TERM signal to quit]\n");
/* now we're ready, signal the parent process */
}
else
{
printf ("\nStarting event loop....\n[press Ctrl-C to quit]\n");
}
if (pszPidFile)
{
char szBuf[32];
char *lf = "\n";
}
while (gKeepRunning)
{
}
// stop accepting new events
// process any remaining events
printf ("Terminated event loop.\n");
}
while (0); // this scopes the nsCOMPtrs
// no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
LogFlow (("VBoxSVC::main(): calling NS_ShutdownXPCOM()...\n"));
printf ("XPCOM server has shutdown.\n");
if (pszPidFile)
{
}
if (fDaemonize)
{
/* close writing end of the pipe as well */
}
return 0;
}