summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/xpcom/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/xpcom/server.cpp')
-rw-r--r--src/VBox/Main/src-server/xpcom/server.cpp988
1 files changed, 988 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/xpcom/server.cpp b/src/VBox/Main/src-server/xpcom/server.cpp
new file mode 100644
index 00000000..99e90d03
--- /dev/null
+++ b/src/VBox/Main/src-server/xpcom/server.cpp
@@ -0,0 +1,988 @@
+/* $Id: server.cpp $ */
+/** @file
+ * XPCOM server process (VBoxSVC) start point.
+ */
+
+/*
+ * Copyright (C) 2004-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_VBOXSVC
+#include <ipcIService.h>
+#include <ipcCID.h>
+
+#include <nsIComponentRegistrar.h>
+
+#include <nsGenericFactory.h>
+
+#include "prio.h"
+#include "prproces.h"
+
+#include "server.h"
+
+#include "LoggingNew.h"
+
+#include <VBox/param.h>
+#include <VBox/version.h>
+
+#include <iprt/buildconfig.h>
+#include <iprt/initterm.h>
+#include <iprt/critsect.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/path.h>
+#include <iprt/timer.h>
+#include <iprt/env.h>
+
+#include <signal.h> // for the signal handler
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+
+/////////////////////////////////////////////////////////////////////////////
+// VirtualBox component instantiation
+/////////////////////////////////////////////////////////////////////////////
+
+#include <nsIGenericFactory.h>
+#include <VBox/com/VirtualBox.h>
+
+#include "VBox/com/NativeEventQueue.h"
+
+#include "ApplianceImpl.h"
+#include "AudioAdapterImpl.h"
+#include "BandwidthControlImpl.h"
+#include "BandwidthGroupImpl.h"
+#include "NetworkServiceRunner.h"
+#include "DHCPServerImpl.h"
+#include "GuestOSTypeImpl.h"
+#include "HostImpl.h"
+#include "HostNetworkInterfaceImpl.h"
+#include "MachineImpl.h"
+#include "MediumFormatImpl.h"
+#include "MediumImpl.h"
+#include "NATEngineImpl.h"
+#include "NetworkAdapterImpl.h"
+#include "ParallelPortImpl.h"
+#include "ProgressProxyImpl.h"
+#include "SerialPortImpl.h"
+#include "SharedFolderImpl.h"
+#include "SnapshotImpl.h"
+#include "StorageControllerImpl.h"
+#include "SystemPropertiesImpl.h"
+#include "USBControllerImpl.h"
+#include "USBDeviceFiltersImpl.h"
+#include "VFSExplorerImpl.h"
+#include "VirtualBoxImpl.h"
+#include "VRDEServerImpl.h"
+#ifdef VBOX_WITH_USB
+# include "HostUSBDeviceImpl.h"
+# include "USBDeviceFilterImpl.h"
+# include "USBDeviceImpl.h"
+#endif
+#ifdef VBOX_WITH_EXTPACK
+# include "ExtPackManagerImpl.h"
+#endif
+# include "NATNetworkImpl.h"
+
+// This needs to stay - it is needed by the service registration below, and
+// is defined in the automatically generated VirtualBoxWrap.cpp
+extern nsIClassInfo *NS_CLASSINFO_NAME(VirtualBoxWrap);
+NS_DECL_CI_INTERFACE_GETTER(VirtualBoxWrap)
+
+////////////////////////////////////////////////////////////////////////////////
+
+static bool gAutoShutdown = false;
+/** Delay before shutting down the VirtualBox server after the last
+ * VirtualBox instance is released, in ms */
+static uint32_t gShutdownDelayMs = 5000;
+
+static com::NativeEventQueue *gEventQ = NULL;
+static PRBool volatile gKeepRunning = PR_TRUE;
+static PRBool volatile gAllowSigUsrQuit = PR_TRUE;
+
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * 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()
+ {
+ LogFlowFunc(("Deleting VirtualBox...\n"));
+
+ FinalRelease();
+ sInstance = NULL;
+
+ LogFlowFunc(("VirtualBox object deleted.\n"));
+ RTPrintf("Informational: VirtualBox object deleted.\n");
+ }
+
+ NS_IMETHOD_(nsrefcnt) Release()
+ {
+ /* we overload Release() to guarantee the VirtualBox destructor is
+ * always called on the main thread */
+
+ nsrefcnt count = VirtualBox::Release();
+
+ if (count == 1)
+ {
+ /* the last reference held by clients is being released
+ * (see GetInstance()) */
+
+ bool onMainThread = RTThreadIsMain(RTThreadSelf());
+ PRBool timerStarted = PR_FALSE;
+
+ /* sTimer is null if this call originates from FactoryDestructor()*/
+ if (sTimer != NULL)
+ {
+ LogFlowFunc(("Last VirtualBox instance was released.\n"));
+ LogFlowFunc(("Scheduling server shutdown in %u ms...\n",
+ gShutdownDelayMs));
+
+ /* make sure the previous timer (if any) is stopped;
+ * otherwise RTTimerStart() will definitely fail. */
+ RTTimerLRStop(sTimer);
+
+ int vrc = RTTimerLRStart(sTimer, gShutdownDelayMs * RT_NS_1MS_64);
+ AssertRC(vrc);
+ timerStarted = RT_BOOL(RT_SUCCESS(vrc));
+ }
+ else
+ {
+ LogFlowFunc(("Last VirtualBox instance was released "
+ "on XPCOM shutdown.\n"));
+ Assert(onMainThread);
+ }
+
+ gAllowSigUsrQuit = PR_TRUE;
+
+ if (!timerStarted)
+ {
+ if (!onMainThread)
+ {
+ /* Failed to start the timer, post the shutdown event
+ * manually if not on the main thread already. */
+ ShutdownTimer(NULL, NULL, 0);
+ }
+ else
+ {
+ /* Here we come if:
+ *
+ * a) gEventQ is 0 which means either FactoryDestructor() is called
+ * or the IPC/DCONNECT shutdown sequence is initiated by the
+ * 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.
+ *
+ * In either case, there is nothing to do.
+ *
+ * Note: case b) is actually no more valid since we don't
+ * call Release() from DestructEventHandler() in this case
+ * any more. Thus, we assert below.
+ */
+
+ Assert(!gEventQ);
+ }
+ }
+ }
+
+ return count;
+ }
+
+ class MaybeQuitEvent : public NativeEvent
+ {
+ public:
+ MaybeQuitEvent() :
+ m_fSignal(false)
+ {
+ }
+
+ MaybeQuitEvent(bool fSignal) :
+ m_fSignal(fSignal)
+ {
+ }
+
+ private:
+ /* called on the main thread */
+ void *handler()
+ {
+ LogFlowFuncEnter();
+
+ Assert(RTCritSectIsInitialized(&sLock));
+
+ /* stop accepting GetInstance() requests on other threads during
+ * possible destruction */
+ RTCritSectEnter(&sLock);
+
+ nsrefcnt count = 1;
+
+ /* sInstance is NULL here if it was deleted immediately after
+ * creation due to initialization error. See GetInstance(). */
+ if (sInstance != NULL)
+ {
+ /* Safe way to get current refcount is by first increasing and
+ * then decreasing. Keep in mind that the Release is overloaded
+ * (see VirtualBoxClassFactory::Release) and will start the
+ * timer again if the returned count is 1. It won't do harm,
+ * but also serves no purpose, so stop it ASAP. */
+ sInstance->AddRef();
+ count = sInstance->Release();
+ if (count == 1)
+ {
+ RTTimerLRStop(sTimer);
+ /* Release the guard reference added in GetInstance() */
+ sInstance->Release();
+ }
+ }
+
+ if (count == 1)
+ {
+ if (gAutoShutdown || m_fSignal)
+ {
+ Assert(sInstance == NULL);
+ LogFlowFunc(("Terminating the server process...\n"));
+ /* make it leave the event loop */
+ gKeepRunning = PR_FALSE;
+ }
+ else
+ LogFlowFunc(("No automatic shutdown.\n"));
+ }
+ else
+ {
+ /* This condition is quite rare: a new client happened to
+ * connect after this event has been posted to the main queue
+ * but before it started to process it. */
+ LogRel(("Destruction is canceled (refcnt=%d).\n", count));
+ }
+
+ RTCritSectLeave(&sLock);
+
+ LogFlowFuncLeave();
+ return NULL;
+ }
+
+ bool m_fSignal;
+ };
+
+ static DECLCALLBACK(void) ShutdownTimer(RTTIMERLR hTimerLR, void *pvUser, uint64_t /*iTick*/)
+ {
+ NOREF(hTimerLR);
+ NOREF(pvUser);
+
+ /* A "too late" event is theoretically possible if somebody
+ * manually ended the server after a destruction has been scheduled
+ * and this method was so lucky that it got a chance to run before
+ * the timer was killed. */
+ com::NativeEventQueue *q = gEventQ;
+ AssertReturnVoid(q);
+
+ /* post a quit event to the main queue */
+ MaybeQuitEvent *ev = new MaybeQuitEvent(false /* fSignal */);
+ if (!q->postEvent(ev))
+ delete ev;
+
+ /* A failure above means we've been already stopped (for example
+ * by Ctrl-C). FactoryDestructor() (NS_ShutdownXPCOM())
+ * will do the job. Nothing to do. */
+ }
+
+ static NS_IMETHODIMP FactoryConstructor()
+ {
+ LogFlowFunc(("\n"));
+
+ /* create a critsect to protect object construction */
+ if (RT_FAILURE(RTCritSectInit(&sLock)))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ int vrc = RTTimerLRCreateEx(&sTimer, 0, 0, ShutdownTimer, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ LogFlowFunc(("Failed to create a timer! (vrc=%Rrc)\n", vrc));
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ static NS_IMETHODIMP FactoryDestructor()
+ {
+ LogFlowFunc(("\n"));
+
+ RTTimerLRDestroy(sTimer);
+ sTimer = NULL;
+
+ if (sInstance != NULL)
+ {
+ /* Either posting a destruction event failed 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 guard reference we added in GetInstance(). */
+ sInstance->Release();
+ }
+
+ /* Destroy lock after releasing the VirtualBox instance, otherwise
+ * there are races with cleanup. */
+ RTCritSectDelete(&sLock);
+
+ return NS_OK;
+ }
+
+ static nsresult GetInstance(VirtualBox **inst)
+ {
+ LogFlowFunc(("Getting VirtualBox object...\n"));
+
+ RTCritSectEnter(&sLock);
+
+ if (!gKeepRunning)
+ {
+ LogFlowFunc(("Process termination requested first. Refusing.\n"));
+
+ RTCritSectLeave(&sLock);
+
+ /* this rv is what CreateInstance() on the client side returns
+ * when the server process stops accepting events. Do the same
+ * here. The client wrapper should attempt to start a new process in
+ * response to a failure from us. */
+ return NS_ERROR_ABORT;
+ }
+
+ nsresult rv = NS_OK;
+
+ if (sInstance == NULL)
+ {
+ LogFlowFunc(("Creating new VirtualBox object...\n"));
+ sInstance = new VirtualBoxClassFactory();
+ if (sInstance != NULL)
+ {
+ /* make an extra AddRef to take the full control
+ * on the VirtualBox destruction (see FinalRelease()) */
+ sInstance->AddRef();
+
+ sInstance->AddRef(); /* protect FinalConstruct() */
+ rv = sInstance->FinalConstruct();
+ RTPrintf("Informational: VirtualBox object created (rc=%Rhrc).\n", rv);
+ if (NS_FAILED(rv))
+ {
+ /* On failure diring VirtualBox initialization, delete it
+ * immediately on the current thread by releasing all
+ * references in order to properly schedule the server
+ * shutdown. Since the object is fully deleted here, there
+ * is a chance to fix the error and request a new
+ * instantiation before the server terminates. However,
+ * the main reason to maintain the shutdown delay on
+ * failure is to let the front-end completely fetch error
+ * info from a server-side IVirtualBoxErrorInfo object. */
+ sInstance->Release();
+ sInstance->Release();
+ Assert(sInstance == NULL);
+ }
+ else
+ {
+ /* On success, make sure the previous timer is stopped to
+ * cancel a scheduled server termination (if any). */
+ gAllowSigUsrQuit = PR_FALSE;
+ RTTimerLRStop(sTimer);
+ }
+ }
+ else
+ {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ else
+ {
+ LogFlowFunc(("Using existing VirtualBox object...\n"));
+ nsrefcnt count = sInstance->AddRef();
+ Assert(count > 1);
+
+ if (count >= 2)
+ {
+ LogFlowFunc(("Another client has requested a reference to VirtualBox, canceling destruction...\n"));
+
+ /* make sure the previous timer is stopped */
+ gAllowSigUsrQuit = PR_FALSE;
+ RTTimerLRStop(sTimer);
+ }
+ }
+
+ *inst = sInstance;
+
+ RTCritSectLeave(&sLock);
+
+ return rv;
+ }
+
+private:
+
+ /* Don't be confused that sInstance is of the *ClassFactory type. This is
+ * actually a singleton instance (*ClassFactory inherits the singleton
+ * class; we combined them just for "simplicity" and used "static" for
+ * factory methods. *ClassFactory here is necessary for a couple of extra
+ * methods. */
+
+ static VirtualBoxClassFactory *sInstance;
+ static RTCRITSECT sLock;
+
+ static RTTIMERLR sTimer;
+};
+
+VirtualBoxClassFactory *VirtualBoxClassFactory::sInstance = NULL;
+RTCRITSECT VirtualBoxClassFactory::sLock;
+
+RTTIMERLR VirtualBoxClassFactory::sTimer = NIL_RTTIMERLR;
+
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR_WITH_RC(VirtualBox, VirtualBoxClassFactory::GetInstance)
+
+////////////////////////////////////////////////////////////////////////////////
+
+typedef NSFactoryDestructorProcPtr NSFactoryConstructorProcPtr;
+
+/**
+ * Enhanced module component information structure.
+ *
+ * nsModuleComponentInfo lacks the factory construction callback, here we add
+ * it. This callback is called straight after a nsGenericFactory instance is
+ * successfully created in RegisterSelfComponents.
+ */
+struct nsModuleComponentInfoPlusFactoryConstructor
+{
+ /** standard module component information */
+ const nsModuleComponentInfo *mpModuleComponentInfo;
+ /** (optional) Factory Construction Callback */
+ NSFactoryConstructorProcPtr mFactoryConstructor;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Helper function to register self components upon start-up
+ * of the out-of-proc server.
+ */
+static nsresult
+RegisterSelfComponents(nsIComponentRegistrar *registrar,
+ const nsModuleComponentInfoPlusFactoryConstructor *aComponents,
+ PRUint32 count)
+{
+ nsresult rc = NS_OK;
+ const nsModuleComponentInfoPlusFactoryConstructor *info = aComponents;
+ for (PRUint32 i = 0; i < count && NS_SUCCEEDED(rc); i++, info++)
+ {
+ /* skip components w/o a constructor */
+ if (!info->mpModuleComponentInfo->mConstructor)
+ continue;
+ /* create a new generic factory for a component and register it */
+ nsIGenericFactory *factory;
+ rc = NS_NewGenericFactory(&factory, info->mpModuleComponentInfo);
+ if (NS_SUCCEEDED(rc) && info->mFactoryConstructor)
+ {
+ rc = info->mFactoryConstructor();
+ if (NS_FAILED(rc))
+ NS_RELEASE(factory);
+ }
+ if (NS_SUCCEEDED(rc))
+ {
+ rc = registrar->RegisterFactory(info->mpModuleComponentInfo->mCID,
+ info->mpModuleComponentInfo->mDescription,
+ info->mpModuleComponentInfo->mContractID,
+ factory);
+ NS_RELEASE(factory);
+ }
+ }
+ return rc;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static ipcIService *gIpcServ = nsnull;
+static const char *g_pszPidFile = NULL;
+
+class ForceQuitEvent : public NativeEvent
+{
+ void *handler()
+ {
+ LogFlowFunc(("\n"));
+
+ gKeepRunning = PR_FALSE;
+
+ if (g_pszPidFile)
+ RTFileDelete(g_pszPidFile);
+
+ return NULL;
+ }
+};
+
+static void signal_handler(int sig)
+{
+ com::NativeEventQueue *q = gEventQ;
+ if (q && gKeepRunning)
+ {
+ if (sig == SIGUSR1)
+ {
+ if (gAllowSigUsrQuit)
+ {
+ /* terminate the server process if it is idle */
+ VirtualBoxClassFactory::MaybeQuitEvent *ev = new VirtualBoxClassFactory::MaybeQuitEvent(true /* fSignal */);
+ if (!q->postEvent(ev))
+ delete ev;
+ }
+ /* else do nothing */
+ }
+ else
+ {
+ /* post a force quit event to the queue */
+ ForceQuitEvent *ev = new ForceQuitEvent();
+ if (!q->postEvent(ev))
+ delete ev;
+ }
+ }
+}
+
+static nsresult vboxsvcSpawnDaemonByReExec(const char *pszPath, bool fAutoShutdown, const char *pszPidFile)
+{
+ PRFileDesc *readable = nsnull, *writable = nsnull;
+ PRProcessAttr *attr = nsnull;
+ nsresult rv = NS_ERROR_FAILURE;
+ PRFileDesc *devNull;
+ unsigned args_index = 0;
+ // The ugly casts are necessary because the PR_CreateProcessDetached has
+ // a const array of writable strings as a parameter. It won't write. */
+ char * args[1 + 1 + 2 + 1];
+ args[args_index++] = (char *)pszPath;
+ if (fAutoShutdown)
+ args[args_index++] = (char *)"--auto-shutdown";
+ if (pszPidFile)
+ {
+ args[args_index++] = (char *)"--pidfile";
+ args[args_index++] = (char *)pszPidFile;
+ }
+ args[args_index++] = 0;
+
+ // Use a pipe to determine when the daemon process is in the position
+ // to actually process requests. The daemon will write "READY" to the pipe.
+ if (PR_CreatePipe(&readable, &writable) != PR_SUCCESS)
+ goto end;
+ PR_SetFDInheritable(writable, PR_TRUE);
+
+ attr = PR_NewProcessAttr();
+ if (!attr)
+ goto end;
+
+ if (PR_ProcessAttrSetInheritableFD(attr, writable, VBOXSVC_STARTUP_PIPE_NAME) != PR_SUCCESS)
+ goto end;
+
+ devNull = PR_Open("/dev/null", PR_RDWR, 0);
+ if (!devNull)
+ goto end;
+
+ PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, devNull);
+ PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, devNull);
+ PR_ProcessAttrSetStdioRedirect(attr, PR_StandardError, devNull);
+
+ if (PR_CreateProcessDetached(pszPath, (char * const *)args, nsnull, attr) != PR_SUCCESS)
+ goto end;
+
+ // Close /dev/null
+ PR_Close(devNull);
+ // Close the child end of the pipe to make it the only owner of the
+ // file descriptor, so that unexpected closing can be detected.
+ PR_Close(writable);
+ writable = nsnull;
+
+ char msg[10];
+ memset(msg, '\0', sizeof(msg));
+ if ( PR_Read(readable, msg, sizeof(msg)-1) != 5
+ || strcmp(msg, "READY"))
+ goto end;
+
+ rv = NS_OK;
+
+end:
+ if (readable)
+ PR_Close(readable);
+ if (writable)
+ PR_Close(writable);
+ if (attr)
+ PR_DestroyProcessAttr(attr);
+ return rv;
+}
+
+static void showUsage(const char *pcszFileName)
+{
+ RTPrintf(VBOX_PRODUCT " VBoxSVC "
+ VBOX_VERSION_STRING "\n"
+ "Copyright (C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
+ RTPrintf("By default the service will be started in the background.\n"
+ "\n");
+ RTPrintf("Usage:\n"
+ "\n");
+ RTPrintf(" %s\n", pcszFileName);
+ RTPrintf("\n");
+ RTPrintf("Options:\n");
+ RTPrintf(" -a, --automate Start XPCOM on demand and daemonize.\n");
+ RTPrintf(" -A, --auto-shutdown Shuts down service if no longer in use.\n");
+ RTPrintf(" -d, --daemonize Starts service in background.\n");
+ RTPrintf(" -D, --shutdown-delay <ms> Sets shutdown delay in ms.\n");
+ RTPrintf(" -h, --help Displays this help.\n");
+ RTPrintf(" -p, --pidfile <path> Uses a specific pidfile.\n");
+ RTPrintf(" -F, --logfile <path> Uses a specific logfile.\n");
+ RTPrintf(" -R, --logrotate <count> Number of old log files to keep.\n");
+ RTPrintf(" -S, --logsize <bytes> Maximum size of a log file before rotating.\n");
+ RTPrintf(" -I, --loginterval <s> Maximum amount of time to put in a log file.\n");
+
+ RTPrintf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ /*
+ * Initialize the VBox runtime without loading
+ * the support driver
+ */
+ int vrc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(vrc))
+ return RTMsgInitFailure(vrc);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--automate", 'a', RTGETOPT_REQ_NOTHING },
+ { "--auto-shutdown", 'A', RTGETOPT_REQ_NOTHING },
+ { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
+ { "--help", 'h', RTGETOPT_REQ_NOTHING },
+ { "--shutdown-delay", 'D', RTGETOPT_REQ_UINT32 },
+ { "--pidfile", 'p', RTGETOPT_REQ_STRING },
+ { "--logfile", 'F', RTGETOPT_REQ_STRING },
+ { "--logrotate", 'R', RTGETOPT_REQ_UINT32 },
+ { "--logsize", 'S', RTGETOPT_REQ_UINT64 },
+ { "--loginterval", 'I', RTGETOPT_REQ_UINT32 }
+ };
+
+ const char *pszLogFile = NULL;
+ uint32_t cHistory = 10; // enable log rotation, 10 files
+ uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
+ uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
+ bool fDaemonize = false;
+ PRFileDesc *daemon_pipe_wr = nsnull;
+
+ RTGETOPTSTATE GetOptState;
+ vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
+ AssertRC(vrc);
+
+ RTGETOPTUNION ValueUnion;
+ while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
+ {
+ switch (vrc)
+ {
+ case 'a':
+ /* --automate mode means we are started by XPCOM on
+ * demand. Daemonize ourselves and activate
+ * auto-shutdown. */
+ gAutoShutdown = true;
+ fDaemonize = true;
+ break;
+
+ case 'A':
+ /* --auto-shutdown mode means we're already daemonized. */
+ gAutoShutdown = true;
+ break;
+
+ case 'd':
+ fDaemonize = true;
+ break;
+
+ case 'D':
+ gShutdownDelayMs = ValueUnion.u32;
+ break;
+
+ case 'p':
+ g_pszPidFile = ValueUnion.psz;
+ break;
+
+ case 'F':
+ pszLogFile = ValueUnion.psz;
+ break;
+
+ case 'R':
+ cHistory = ValueUnion.u32;
+ break;
+
+ case 'S':
+ uHistoryFileSize = ValueUnion.u64;
+ break;
+
+ case 'I':
+ uHistoryFileTime = ValueUnion.u32;
+ break;
+
+ case 'h':
+ showUsage(argv[0]);
+ return RTEXITCODE_SYNTAX;
+
+ case 'V':
+ RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
+ return RTEXITCODE_SUCCESS;
+
+ default:
+ return RTGetOptPrintError(vrc, &ValueUnion);
+ }
+ }
+
+ if (fDaemonize)
+ {
+ vboxsvcSpawnDaemonByReExec(argv[0], gAutoShutdown, g_pszPidFile);
+ exit(126);
+ }
+
+ nsresult rc;
+
+ /** @todo Merge this code with svcmain.cpp (use Logging.cpp?). */
+ char szLogFile[RTPATH_MAX];
+ if (!pszLogFile)
+ {
+ vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
+ if (RT_SUCCESS(vrc))
+ vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
+ }
+ else
+ {
+ if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
+ vrc = VERR_NO_MEMORY;
+ }
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
+
+ RTERRINFOSTATIC ErrInfo;
+ vrc = com::VBoxLogRelCreate("XPCOM Server", szLogFile,
+ RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
+ VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
+ RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
+ cHistory, uHistoryFileTime, uHistoryFileSize,
+ RTErrInfoInitStatic(&ErrInfo));
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
+
+ /* Set up a build identifier so that it can be seen from core dumps what
+ * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
+ static char saBuildID[48];
+ RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
+ "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
+
+ daemon_pipe_wr = PR_GetInheritedFD(VBOXSVC_STARTUP_PIPE_NAME);
+ RTEnvUnset("NSPR_INHERIT_FDS");
+
+ const nsModuleComponentInfo VirtualBoxInfo = {
+ "VirtualBox component",
+ NS_VIRTUALBOX_CID,
+ NS_VIRTUALBOX_CONTRACTID,
+ VirtualBoxConstructor, // constructor function
+ NULL, // registration function
+ NULL, // deregistration function
+ VirtualBoxClassFactory::FactoryDestructor, // factory destructor function
+ NS_CI_INTERFACE_GETTER_NAME(VirtualBoxWrap),
+ NULL, // language helper
+ &NS_CLASSINFO_NAME(VirtualBoxWrap),
+ 0 // flags
+ };
+
+ const nsModuleComponentInfoPlusFactoryConstructor components[] = {
+ {
+ &VirtualBoxInfo,
+ VirtualBoxClassFactory::FactoryConstructor // factory constructor function
+ }
+ };
+
+ do /* goto avoidance only */
+ {
+ rc = com::Initialize();
+ if (NS_FAILED(rc))
+ {
+ RTMsgError("Failed to initialize XPCOM! (rc=%Rhrc)\n", rc);
+ break;
+ }
+
+ nsCOMPtr<nsIComponentRegistrar> registrar;
+ rc = NS_GetComponentRegistrar(getter_AddRefs(registrar));
+ if (NS_FAILED(rc))
+ {
+ RTMsgError("Failed to get component registrar! (rc=%Rhrc)", rc);
+ break;
+ }
+
+ registrar->AutoRegister(nsnull);
+ rc = RegisterSelfComponents(registrar, components,
+ NS_ARRAY_LENGTH(components));
+ if (NS_FAILED(rc))
+ {
+ RTMsgError("Failed to register server components! (rc=%Rhrc)", rc);
+ break;
+ }
+
+ nsCOMPtr<ipcIService> ipcServ(do_GetService(IPC_SERVICE_CONTRACTID, &rc));
+ if (NS_FAILED(rc))
+ {
+ RTMsgError("Failed to get IPC service! (rc=%Rhrc)", rc);
+ break;
+ }
+
+ NS_ADDREF(gIpcServ = ipcServ);
+
+ LogFlowFunc(("Will use \"%s\" as server name.\n", VBOXSVC_IPC_NAME));
+
+ rc = gIpcServ->AddName(VBOXSVC_IPC_NAME);
+ if (NS_FAILED(rc))
+ {
+ LogFlowFunc(("Failed to register the server name (rc=%Rhrc (%08X))!\n"
+ "Is another server already running?\n", rc, rc));
+
+ RTMsgError("Failed to register the server name \"%s\" (rc=%Rhrc)!\n"
+ "Is another server already running?\n",
+ VBOXSVC_IPC_NAME, rc);
+ NS_RELEASE(gIpcServ);
+ break;
+ }
+
+ {
+ /* setup signal handling to convert some signals to a quit event */
+ struct sigaction sa;
+ sa.sa_handler = signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+// XXX Temporary allow release assertions to terminate VBoxSVC
+// sigaction(SIGTRAP, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+ }
+
+ {
+ char szBuf[80];
+ size_t cSize;
+
+ cSize = RTStrPrintf(szBuf, sizeof(szBuf),
+ VBOX_PRODUCT" XPCOM Server Version "
+ VBOX_VERSION_STRING);
+ for (size_t i = cSize; i > 0; i--)
+ putchar('*');
+ RTPrintf("\n%s\n", szBuf);
+ RTPrintf("Copyright (C) 2004-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
+#ifdef DEBUG
+ RTPrintf("Debug version.\n");
+#endif
+ }
+
+ if (daemon_pipe_wr != nsnull)
+ {
+ RTPrintf("\nStarting event loop....\n[send TERM signal to quit]\n");
+ /* now we're ready, signal the parent process */
+ PR_Write(daemon_pipe_wr, RT_STR_TUPLE("READY"));
+ /* close writing end of the pipe, its job is done */
+ PR_Close(daemon_pipe_wr);
+ }
+ else
+ RTPrintf("\nStarting event loop....\n[press Ctrl-C to quit]\n");
+
+ if (g_pszPidFile)
+ {
+ RTFILE hPidFile = NIL_RTFILE;
+ vrc = RTFileOpen(&hPidFile, g_pszPidFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
+ if (RT_SUCCESS(vrc))
+ {
+ char szBuf[64];
+ size_t cchToWrite = RTStrPrintf(szBuf, sizeof(szBuf), "%ld\n", (long)getpid());
+ RTFileWrite(hPidFile, szBuf, cchToWrite, NULL);
+ RTFileClose(hPidFile);
+ }
+ }
+
+ // Increase the file table size to 10240 or as high as possible.
+ struct rlimit lim;
+ if (getrlimit(RLIMIT_NOFILE, &lim) == 0)
+ {
+ if ( lim.rlim_cur < 10240
+ && lim.rlim_cur < lim.rlim_max)
+ {
+ lim.rlim_cur = RT_MIN(lim.rlim_max, 10240);
+ if (setrlimit(RLIMIT_NOFILE, &lim) == -1)
+ RTPrintf("WARNING: failed to increase file descriptor limit. (%d)\n", errno);
+ }
+ }
+ else
+ RTPrintf("WARNING: failed to obtain per-process file-descriptor limit (%d).\n", errno);
+
+ /* get the main thread's event queue */
+ gEventQ = com::NativeEventQueue::getMainEventQueue();
+ if (!gEventQ)
+ {
+ RTMsgError("Failed to get the main event queue! (rc=%Rhrc)", rc);
+ break;
+ }
+
+ while (gKeepRunning)
+ {
+ vrc = gEventQ->processEventQueue(RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
+ {
+ LogRel(("Failed to wait for events! (rc=%Rrc)", vrc));
+ break;
+ }
+ }
+
+ gEventQ = NULL;
+ RTPrintf("Terminated event loop.\n");
+
+ /* unregister ourselves. After this point, clients will start a new
+ * process because they won't be able to resolve the server name.*/
+ gIpcServ->RemoveName(VBOXSVC_IPC_NAME);
+ }
+ while (0); // this scopes the nsCOMPtrs
+
+ NS_IF_RELEASE(gIpcServ);
+
+ /* no nsCOMPtrs are allowed to be alive when you call com::Shutdown(). */
+
+ LogFlowFunc(("Calling com::Shutdown()...\n"));
+ rc = com::Shutdown();
+ LogFlowFunc(("Finished com::Shutdown() (rc=%Rhrc)\n", rc));
+
+ if (NS_FAILED(rc))
+ RTMsgError("Failed to shutdown XPCOM! (rc=%Rhrc)", rc);
+
+ RTPrintf("XPCOM server has shutdown.\n");
+
+ if (g_pszPidFile)
+ RTFileDelete(g_pszPidFile);
+
+ return RTEXITCODE_SUCCESS;
+}