/** @file * * XPCOM server process hepler module implementation functions */ /* * Copyright (C) 2006-2007 Sun Microsystems, Inc. * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) 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. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 USA or visit http://www.sun.com if you need * additional information or have any questions. */ #ifdef RT_OS_OS2 # include #endif #include #include #include #include #include #include #include #include #include #include #include #include // official XPCOM headers don't define it yet #define IPC_DCONNECTSERVICE_CONTRACTID \ "@mozilla.org/ipc/dconnect-service;1" // generated file #include #include "xpcom/server.h" #include "Logging.h" #include #include #include #include #include #include #include #if defined(RT_OS_SOLARIS) # include #endif /// @todo move this to RT headers (and use them in MachineImpl.cpp as well) #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) #define HOSTSUFF_EXE ".exe" #else /* !RT_OS_WINDOWS */ #define HOSTSUFF_EXE "" #endif /* !RT_OS_WINDOWS */ /** Name of the server executable. */ const char VBoxSVC_exe[] = RTPATH_SLASH_STR "VBoxSVC" HOSTSUFF_EXE; enum { /** Amount of time to wait for the server to establish a connection, ms */ VBoxSVC_Timeout = 30000, /** How often to perform a connection check, ms */ VBoxSVC_WaitSlice = 100, }; /** * Full path to the VBoxSVC executable. */ static char VBoxSVCPath [RTPATH_MAX]; static bool IsVBoxSVCPathSet = false; /* * The following macros define the method necessary to provide a list of * interfaces implemented by the VirtualBox component. Note that this must be * in sync with macros used for VirtualBox in server.cpp for the same purpose. */ NS_DECL_CLASSINFO (VirtualBox) NS_IMPL_CI_INTERFACE_GETTER1 (VirtualBox, IVirtualBox) /** * VirtualBox component constructor. * * This constructor is responsible for starting the VirtualBox server * process, connecting to it, and redirecting the constructor request to the * VirtualBox component defined on the server. */ static NS_IMETHODIMP VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, void **aResult) { LogFlowFuncEnter(); nsresult rc = NS_OK; int vrc = VINF_SUCCESS; do { *aResult = NULL; if (NULL != aOuter) { rc = NS_ERROR_NO_AGGREGATION; break; } if (!IsVBoxSVCPathSet) { /* Get the directory containing XPCOM components -- the VBoxSVC * executable is expected in the parent directory. */ nsCOMPtr dirServ = do_GetService (NS_DIRECTORY_SERVICE_CONTRACTID, &rc); if (NS_SUCCEEDED(rc)) { nsCOMPtr componentDir; rc = dirServ->Get (NS_XPCOM_COMPONENT_DIR, NS_GET_IID (nsIFile), getter_AddRefs (componentDir)); if (NS_SUCCEEDED(rc)) { nsCAutoString path; componentDir->GetNativePath (path); LogFlowFunc (("component directory = \"%s\"\n", path.get())); AssertBreakStmt (path.Length() + strlen (VBoxSVC_exe) < RTPATH_MAX, rc = NS_ERROR_FAILURE); #if defined(RT_OS_SOLARIS) && defined(VBOX_WITH_HARDENING) char achKernArch[128]; int cbKernArch = sysinfo (SI_ARCHITECTURE_K, achKernArch, sizeof(achKernArch)); if (cbKernArch > 0) { sprintf(VBoxSVCPath, "/opt/VirtualBox/%s%s", achKernArch, VBoxSVC_exe); IsVBoxSVCPathSet = true; } else rc = NS_ERROR_UNEXPECTED; #else strcpy (VBoxSVCPath, path.get()); RTPathStripFilename (VBoxSVCPath); strcat (VBoxSVCPath, VBoxSVC_exe); IsVBoxSVCPathSet = true; #endif } } if (NS_FAILED (rc)) break; } nsCOMPtr ipcServ = do_GetService (IPC_SERVICE_CONTRACTID, &rc); if (NS_FAILED (rc)) break; /* connect to the VBoxSVC server process */ bool startedOnce = false; unsigned timeLeft = VBoxSVC_Timeout; do { LogFlowFunc (("Resolving server name \"%s\"...\n", VBOXSVC_IPC_NAME)); PRUint32 serverID = 0; rc = ipcServ->ResolveClientName (VBOXSVC_IPC_NAME, &serverID); if (NS_FAILED (rc)) { LogFlowFunc (("Starting server \"%s\"...\n", VBoxSVCPath)); startedOnce = true; #ifdef RT_OS_OS2 char * const args[] = { VBoxSVCPath, "--automate", 0 }; /* use NSPR because we want the process to be detached right * at startup (it isn't possible to detach it later on), * RTProcCreate() isn't yet capable of doing that. */ PRStatus rv = PR_CreateProcessDetached (VBoxSVCPath, args, NULL, NULL); if (rv != PR_SUCCESS) { rc = NS_ERROR_FAILURE; break; } #else const char *args[] = { VBoxSVCPath, "--automate", 0 }; RTPROCESS pid = NIL_RTPROCESS; vrc = RTProcCreate (VBoxSVCPath, args, RTENV_DEFAULT, 0, &pid); if (RT_FAILURE(vrc)) { rc = NS_ERROR_FAILURE; break; } /* need to wait for the pid to avoid zombie VBoxSVC. * ignore failure since it just means we'll have a zombie * VBoxSVC until we exit */ int vrc2 = RTProcWaitNoResume(pid, RTPROCWAIT_FLAGS_BLOCK, NULL); AssertRC(vrc2); #endif /* wait for the server process to establish a connection */ do { RTThreadSleep (VBoxSVC_WaitSlice); rc = ipcServ->ResolveClientName (VBOXSVC_IPC_NAME, &serverID); if (NS_SUCCEEDED(rc)) break; if (timeLeft <= VBoxSVC_WaitSlice) { timeLeft = 0; break; } timeLeft -= VBoxSVC_WaitSlice; } while (1); if (!timeLeft) { rc = IPC_ERROR_WOULD_BLOCK; break; } } LogFlowFunc (("Connecting to server (ID=%d)...\n", serverID)); nsCOMPtr dconServ = do_GetService (IPC_DCONNECTSERVICE_CONTRACTID, &rc); if (NS_FAILED (rc)) break; rc = dconServ->CreateInstance (serverID, (nsCID) NS_VIRTUALBOX_CID, aIID, aResult); if (NS_SUCCEEDED(rc)) break; LogFlowFunc (("Failed to connect (rc=%Rhrc (%#08x))\n", rc, rc)); /* It's possible that the server gets shut down after we * successfully resolve the server name but before it * receives our CreateInstance() request. So, check for the * name again, and restart the cycle if it fails. */ if (!startedOnce) { nsresult rc2 = ipcServ->ResolveClientName (VBOXSVC_IPC_NAME, &serverID); if (NS_SUCCEEDED(rc2)) break; LogFlowFunc (("Server seems to have terminated before " "receiving our request. Will try again.\n")); } else break; } while (1); } while (0); LogFlowFunc (("rc=%Rhrc (%#08x), vrc=%Rrc\n", rc, rc, vrc)); LogFlowFuncLeave(); return rc; } #if 0 /// @todo not really necessary for the moment /** * * @param aCompMgr * @param aPath * @param aLoaderStr * @param aType * @param aInfo * * @return */ static NS_IMETHODIMP VirtualBoxRegistration (nsIComponentManager *aCompMgr, nsIFile *aPath, const char *aLoaderStr, const char *aType, const nsModuleComponentInfo *aInfo) { nsCAutoString modulePath; aPath->GetNativePath (modulePath); nsCAutoString moduleTarget; aPath->GetNativeTarget (moduleTarget); LogFlowFunc (("aPath=%s, aTarget=%s, aLoaderStr=%s, aType=%s\n", modulePath.get(), moduleTarget.get(), aLoaderStr, aType)); nsresult rc = NS_OK; return rc; } #endif /** * Component definition table. * Lists all components defined in this module. */ static const nsModuleComponentInfo components[] = { { "VirtualBox component", // description NS_VIRTUALBOX_CID, NS_VIRTUALBOX_CONTRACTID, // CID/ContractID VirtualBoxConstructor, // constructor function NULL, /* VirtualBoxRegistration, */ // registration function NULL, // deregistration function NULL, // destructor function /// @todo NS_CI_INTERFACE_GETTER_NAME(VirtualBox), // interfaces function NULL, // language helper /// @todo &NS_CLASSINFO_NAME(VirtualBox) // global class info & flags } }; NS_IMPL_NSGETMODULE (VirtualBox_Server_Module, components)