VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/xpcom/server_module.cpp@ 102052

Last change on this file since 102052 was 102048, checked in by vboxsync, 18 months ago

Main/XPCOM: Convert re-spawning VBoxSVC from NSPR to IPRT, bugref:10545

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 11.3 KB
Line 
1/* $Id: server_module.cpp 102048 2023-11-09 19:47:10Z vboxsync $ */
2/** @file
3 * XPCOM server process helper module implementation functions
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_VBOXSVC
29#include <nsMemory.h>
30#include <nsString.h>
31#include <nsCOMPtr.h>
32#include <nsIFile.h>
33#include <nsIGenericFactory.h>
34#include <nsIServiceManagerUtils.h>
35#include <nsICategoryManager.h>
36#include <nsDirectoryServiceDefs.h>
37
38#include <ipcIService.h>
39#include <ipcIDConnectService.h>
40#include <ipcCID.h>
41#include <ipcdclient.h>
42
43#include "prio.h"
44
45// official XPCOM headers don't define it yet
46#define IPC_DCONNECTSERVICE_CONTRACTID \
47 "@mozilla.org/ipc/dconnect-service;1"
48
49// generated file
50#include <VBox/com/VirtualBox.h>
51
52#include "server.h"
53#include "LoggingNew.h"
54
55#include <iprt/errcore.h>
56
57#include <iprt/assert.h>
58#include <iprt/param.h>
59#include <iprt/path.h>
60#include <iprt/pipe.h>
61#include <iprt/process.h>
62#include <iprt/env.h>
63#include <iprt/string.h>
64#include <iprt/thread.h>
65
66#if defined(RT_OS_SOLARIS)
67# include <sys/systeminfo.h>
68#endif
69
70/// @todo move this to RT headers (and use them in MachineImpl.cpp as well)
71#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
72#define HOSTSUFF_EXE ".exe"
73#else /* !RT_OS_WINDOWS */
74#define HOSTSUFF_EXE ""
75#endif /* !RT_OS_WINDOWS */
76
77
78/** Name of the server executable. */
79const char VBoxSVC_exe[] = RTPATH_SLASH_STR "VBoxSVC" HOSTSUFF_EXE;
80
81enum
82{
83 /** Amount of time to wait for the server to establish a connection, ms */
84 VBoxSVC_Timeout = 30000,
85 /** How often to perform a connection check, ms */
86 VBoxSVC_WaitSlice = 100
87};
88
89/**
90 * Full path to the VBoxSVC executable.
91 */
92static char VBoxSVCPath[RTPATH_MAX];
93static bool IsVBoxSVCPathSet = false;
94
95/*
96 * The following macros define the method necessary to provide a list of
97 * interfaces implemented by the VirtualBox component. Note that this must be
98 * in sync with macros used for VirtualBox in server.cpp for the same purpose.
99 */
100
101NS_DECL_CLASSINFO(VirtualBoxWrap)
102NS_IMPL_CI_INTERFACE_GETTER1(VirtualBoxWrap, IVirtualBox)
103
104static nsresult vboxsvcSpawnDaemon(void)
105{
106 /*
107 * Setup an anonymous pipe that we can use to determine when the daemon
108 * process has started up. the daemon will write a char to the pipe, and
109 * when we read it, we'll know to proceed with trying to connect to the
110 * daemon.
111 */
112 RTPIPE hPipeWr = NIL_RTPIPE;
113 RTPIPE hPipeRd = NIL_RTPIPE;
114 int vrc = RTPipeCreate(&hPipeRd, &hPipeWr, RTPIPE_C_INHERIT_WRITE);
115 if (RT_SUCCESS(vrc))
116 {
117 char szPipeInheritFd[32]; RT_ZERO(szPipeInheritFd);
118 const char *apszArgs[] =
119 {
120 VBoxSVCPath,
121 "--auto-shutdown",
122 "--inherit-startup-pipe",
123 &szPipeInheritFd[0],
124 NULL
125 };
126
127 ssize_t cch = RTStrFormatU32(&szPipeInheritFd[0], sizeof(szPipeInheritFd),
128 (uint32_t)RTPipeToNative(hPipeWr), 10 /*uiBase*/,
129 0 /*cchWidth*/, 0 /*cchPrecision*/, 0 /*fFlags*/);
130 Assert(cch > 0);
131
132 RTHANDLE hStdNil;
133 hStdNil.enmType = RTHANDLETYPE_FILE;
134 hStdNil.u.hFile = NIL_RTFILE;
135
136 vrc = RTProcCreateEx(VBoxSVCPath, apszArgs, RTENV_DEFAULT,
137 RTPROC_FLAGS_DETACHED, &hStdNil, &hStdNil, &hStdNil,
138 NULL /* pszAsUser */, NULL /* pszPassword */, NULL /* pExtraData */,
139 NULL /* phProcess */);
140 if (RT_SUCCESS(vrc))
141 {
142 vrc = RTPipeClose(hPipeWr); AssertRC(vrc); RT_NOREF(vrc);
143 hPipeWr = NIL_RTPIPE;
144
145 size_t cbRead = 0;
146 char msg[10];
147 memset(msg, '\0', sizeof(msg));
148 vrc = RTPipeReadBlocking(hPipeRd, &msg[0], sizeof(msg) - 1, &cbRead);
149 if ( RT_SUCCESS(vrc)
150 && cbRead == 5
151 && !strcmp(msg, "READY"))
152 {
153 RTPipeClose(hPipeRd);
154 return NS_OK;
155 }
156 }
157
158 if (hPipeWr != NIL_RTPIPE)
159 RTPipeClose(hPipeWr);
160 RTPipeClose(hPipeRd);
161 }
162
163 return NS_ERROR_FAILURE;
164}
165
166
167/**
168 * VirtualBox component constructor.
169 *
170 * This constructor is responsible for starting the VirtualBox server
171 * process, connecting to it, and redirecting the constructor request to the
172 * VirtualBox component defined on the server.
173 */
174static NS_IMETHODIMP
175VirtualBoxConstructor(nsISupports *aOuter, REFNSIID aIID,
176 void **aResult)
177{
178 LogFlowFuncEnter();
179
180 nsresult rc = NS_OK;
181
182 do
183 {
184 *aResult = NULL;
185 if (NULL != aOuter)
186 {
187 rc = NS_ERROR_NO_AGGREGATION;
188 break;
189 }
190
191 if (!IsVBoxSVCPathSet)
192 {
193 /* Get the directory containing XPCOM components -- the VBoxSVC
194 * executable is expected in the parent directory. */
195 nsCOMPtr<nsIProperties> dirServ = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rc);
196 if (NS_SUCCEEDED(rc))
197 {
198 nsCOMPtr<nsIFile> componentDir;
199 rc = dirServ->Get(NS_XPCOM_COMPONENT_DIR,
200 NS_GET_IID(nsIFile), getter_AddRefs(componentDir));
201
202 if (NS_SUCCEEDED(rc))
203 {
204 nsCAutoString path;
205 componentDir->GetNativePath(path);
206
207 LogFlowFunc(("component directory = \"%s\"\n", path.get()));
208 AssertBreakStmt(path.Length() + strlen(VBoxSVC_exe) < RTPATH_MAX,
209 rc = NS_ERROR_FAILURE);
210
211#if defined(RT_OS_SOLARIS) && defined(VBOX_WITH_HARDENING)
212 char achKernArch[128];
213 int cbKernArch = sysinfo(SI_ARCHITECTURE_K, achKernArch, sizeof(achKernArch));
214 if (cbKernArch > 0)
215 {
216 sprintf(VBoxSVCPath, "/opt/VirtualBox/%s%s", achKernArch, VBoxSVC_exe);
217 IsVBoxSVCPathSet = true;
218 }
219 else
220 rc = NS_ERROR_UNEXPECTED;
221#else
222 strcpy(VBoxSVCPath, path.get());
223 RTPathStripFilename(VBoxSVCPath);
224 strcat(VBoxSVCPath, VBoxSVC_exe);
225
226 IsVBoxSVCPathSet = true;
227#endif
228 }
229 }
230 if (NS_FAILED(rc))
231 break;
232 }
233
234 nsCOMPtr<ipcIService> ipcServ = do_GetService(IPC_SERVICE_CONTRACTID, &rc);
235 if (NS_FAILED(rc))
236 break;
237
238 /* connect to the VBoxSVC server process */
239
240 bool startedOnce = false;
241 unsigned timeLeft = VBoxSVC_Timeout;
242
243 do
244 {
245 LogFlowFunc(("Resolving server name \"%s\"...\n", VBOXSVC_IPC_NAME));
246
247 PRUint32 serverID = 0;
248 rc = ipcServ->ResolveClientName(VBOXSVC_IPC_NAME, &serverID);
249 if (NS_FAILED(rc))
250 {
251 LogFlowFunc(("Starting server \"%s\"...\n", VBoxSVCPath));
252
253 startedOnce = true;
254
255 rc = vboxsvcSpawnDaemon();
256 if (NS_FAILED(rc))
257 break;
258
259 /* wait for the server process to establish a connection */
260 do
261 {
262 RTThreadSleep(VBoxSVC_WaitSlice);
263 rc = ipcServ->ResolveClientName(VBOXSVC_IPC_NAME, &serverID);
264 if (NS_SUCCEEDED(rc))
265 break;
266 if (timeLeft <= VBoxSVC_WaitSlice)
267 {
268 timeLeft = 0;
269 break;
270 }
271 timeLeft -= VBoxSVC_WaitSlice;
272 }
273 while (1);
274
275 if (!timeLeft)
276 {
277 rc = IPC_ERROR_WOULD_BLOCK;
278 break;
279 }
280 }
281
282 LogFlowFunc(("Connecting to server (ID=%d)...\n", serverID));
283
284 nsCOMPtr<ipcIDConnectService> dconServ =
285 do_GetService(IPC_DCONNECTSERVICE_CONTRACTID, &rc);
286 if (NS_FAILED(rc))
287 break;
288
289 rc = dconServ->CreateInstance(serverID,
290 CLSID_VirtualBox,
291 aIID, aResult);
292 if (NS_SUCCEEDED(rc))
293 break;
294
295 LogFlowFunc(("Failed to connect (rc=%Rhrc (%#08x))\n", rc, rc));
296
297 /* It's possible that the server gets shut down after we
298 * successfully resolve the server name but before it
299 * receives our CreateInstance() request. So, check for the
300 * name again, and restart the cycle if it fails. */
301 if (!startedOnce)
302 {
303 nsresult rc2 =
304 ipcServ->ResolveClientName(VBOXSVC_IPC_NAME, &serverID);
305 if (NS_SUCCEEDED(rc2))
306 break;
307
308 LogFlowFunc(("Server seems to have terminated before receiving our request. Will try again.\n"));
309 }
310 else
311 break;
312 }
313 while (1);
314 }
315 while (0);
316
317 LogFlowFunc(("rc=%Rhrc (%#08x)\n", rc, rc));
318 LogFlowFuncLeave();
319
320 return rc;
321}
322
323#if 0
324/// @todo not really necessary for the moment
325/**
326 *
327 * @param aCompMgr
328 * @param aPath
329 * @param aLoaderStr
330 * @param aType
331 * @param aInfo
332 *
333 * @return
334 */
335static NS_IMETHODIMP
336VirtualBoxRegistration(nsIComponentManager *aCompMgr,
337 nsIFile *aPath,
338 const char *aLoaderStr,
339 const char *aType,
340 const nsModuleComponentInfo *aInfo)
341{
342 nsCAutoString modulePath;
343 aPath->GetNativePath(modulePath);
344 nsCAutoString moduleTarget;
345 aPath->GetNativeTarget(moduleTarget);
346
347 LogFlowFunc(("aPath=%s, aTarget=%s, aLoaderStr=%s, aType=%s\n",
348 modulePath.get(), moduleTarget.get(), aLoaderStr, aType));
349
350 nsresult rc = NS_OK;
351
352 return rc;
353}
354#endif
355
356/**
357 * Component definition table.
358 * Lists all components defined in this module.
359 */
360static const nsModuleComponentInfo components[] =
361{
362 {
363 "VirtualBox component", // description
364 NS_VIRTUALBOX_CID, NS_VIRTUALBOX_CONTRACTID, // CID/ContractID
365 VirtualBoxConstructor, // constructor function
366 NULL, /* VirtualBoxRegistration, */ // registration function
367 NULL, // deregistration function
368 NULL, // destructor function
369 /// @todo
370 NS_CI_INTERFACE_GETTER_NAME(VirtualBoxWrap), // interfaces function
371 NULL, // language helper
372 /// @todo
373 &NS_CLASSINFO_NAME(VirtualBoxWrap) // global class info & flags
374 }
375};
376
377NS_IMPL_NSGETMODULE(VirtualBox_Server_Module, components)
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