VirtualBox

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

Last change on this file since 98110 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 11.2 KB
Line 
1/* $Id: server_module.cpp 98103 2023-01-17 14:15:46Z 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#ifdef RT_OS_OS2
30# include <prproces.h>
31#endif
32
33#include <nsMemory.h>
34#include <nsString.h>
35#include <nsCOMPtr.h>
36#include <nsIFile.h>
37#include <nsIGenericFactory.h>
38#include <nsIServiceManagerUtils.h>
39#include <nsICategoryManager.h>
40#include <nsDirectoryServiceDefs.h>
41
42#include <ipcIService.h>
43#include <ipcIDConnectService.h>
44#include <ipcCID.h>
45#include <ipcdclient.h>
46
47#include "prio.h"
48#include "prproces.h"
49
50// official XPCOM headers don't define it yet
51#define IPC_DCONNECTSERVICE_CONTRACTID \
52 "@mozilla.org/ipc/dconnect-service;1"
53
54// generated file
55#include <VBox/com/VirtualBox.h>
56
57#include "server.h"
58#include "LoggingNew.h"
59
60#include <iprt/errcore.h>
61
62#include <iprt/assert.h>
63#include <iprt/param.h>
64#include <iprt/path.h>
65#include <iprt/process.h>
66#include <iprt/env.h>
67#include <iprt/string.h>
68#include <iprt/thread.h>
69
70#if defined(RT_OS_SOLARIS)
71# include <sys/systeminfo.h>
72#endif
73
74/// @todo move this to RT headers (and use them in MachineImpl.cpp as well)
75#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
76#define HOSTSUFF_EXE ".exe"
77#else /* !RT_OS_WINDOWS */
78#define HOSTSUFF_EXE ""
79#endif /* !RT_OS_WINDOWS */
80
81
82/** Name of the server executable. */
83const char VBoxSVC_exe[] = RTPATH_SLASH_STR "VBoxSVC" HOSTSUFF_EXE;
84
85enum
86{
87 /** Amount of time to wait for the server to establish a connection, ms */
88 VBoxSVC_Timeout = 30000,
89 /** How often to perform a connection check, ms */
90 VBoxSVC_WaitSlice = 100
91};
92
93/**
94 * Full path to the VBoxSVC executable.
95 */
96static char VBoxSVCPath[RTPATH_MAX];
97static bool IsVBoxSVCPathSet = false;
98
99/*
100 * The following macros define the method necessary to provide a list of
101 * interfaces implemented by the VirtualBox component. Note that this must be
102 * in sync with macros used for VirtualBox in server.cpp for the same purpose.
103 */
104
105NS_DECL_CLASSINFO(VirtualBoxWrap)
106NS_IMPL_CI_INTERFACE_GETTER1(VirtualBoxWrap, IVirtualBox)
107
108static nsresult vboxsvcSpawnDaemon(void)
109{
110 PRFileDesc *readable = nsnull, *writable = nsnull;
111 PRProcessAttr *attr = nsnull;
112 nsresult rv = NS_ERROR_FAILURE;
113 PRFileDesc *devNull;
114 // The ugly casts are necessary because the PR_CreateProcessDetached has
115 // a const array of writable strings as a parameter. It won't write. */
116 char * const args[] = { (char *)VBoxSVCPath, (char *)"--auto-shutdown", 0 };
117
118 // Use a pipe to determine when the daemon process is in the position
119 // to actually process requests. The daemon will write "READY" to the pipe.
120 if (PR_CreatePipe(&readable, &writable) != PR_SUCCESS)
121 goto end;
122 PR_SetFDInheritable(writable, PR_TRUE);
123
124 attr = PR_NewProcessAttr();
125 if (!attr)
126 goto end;
127
128 if (PR_ProcessAttrSetInheritableFD(attr, writable, VBOXSVC_STARTUP_PIPE_NAME) != PR_SUCCESS)
129 goto end;
130
131 devNull = PR_Open("/dev/null", PR_RDWR, 0);
132 if (!devNull)
133 goto end;
134
135 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, devNull);
136 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, devNull);
137 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardError, devNull);
138
139 if (PR_CreateProcessDetached(VBoxSVCPath, args, nsnull, attr) != PR_SUCCESS)
140 goto end;
141
142 // Close /dev/null
143 PR_Close(devNull);
144 // Close the child end of the pipe to make it the only owner of the
145 // file descriptor, so that unexpected closing can be detected.
146 PR_Close(writable);
147 writable = nsnull;
148
149 char msg[10];
150 RT_ZERO(msg);
151 if ( PR_Read(readable, msg, sizeof(msg)-1) != 5
152 || strcmp(msg, "READY"))
153 {
154 /* If several clients start VBoxSVC simultaneously only one can
155 * succeed. So treat this as success as well. */
156 rv = NS_OK;
157 goto end;
158 }
159
160 rv = NS_OK;
161
162end:
163 if (readable)
164 PR_Close(readable);
165 if (writable)
166 PR_Close(writable);
167 if (attr)
168 PR_DestroyProcessAttr(attr);
169 return rv;
170}
171
172
173/**
174 * VirtualBox component constructor.
175 *
176 * This constructor is responsible for starting the VirtualBox server
177 * process, connecting to it, and redirecting the constructor request to the
178 * VirtualBox component defined on the server.
179 */
180static NS_IMETHODIMP
181VirtualBoxConstructor(nsISupports *aOuter, REFNSIID aIID,
182 void **aResult)
183{
184 LogFlowFuncEnter();
185
186 nsresult rc = NS_OK;
187
188 do
189 {
190 *aResult = NULL;
191 if (NULL != aOuter)
192 {
193 rc = NS_ERROR_NO_AGGREGATION;
194 break;
195 }
196
197 if (!IsVBoxSVCPathSet)
198 {
199 /* Get the directory containing XPCOM components -- the VBoxSVC
200 * executable is expected in the parent directory. */
201 nsCOMPtr<nsIProperties> dirServ = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rc);
202 if (NS_SUCCEEDED(rc))
203 {
204 nsCOMPtr<nsIFile> componentDir;
205 rc = dirServ->Get(NS_XPCOM_COMPONENT_DIR,
206 NS_GET_IID(nsIFile), getter_AddRefs(componentDir));
207
208 if (NS_SUCCEEDED(rc))
209 {
210 nsCAutoString path;
211 componentDir->GetNativePath(path);
212
213 LogFlowFunc(("component directory = \"%s\"\n", path.get()));
214 AssertBreakStmt(path.Length() + strlen(VBoxSVC_exe) < RTPATH_MAX,
215 rc = NS_ERROR_FAILURE);
216
217#if defined(RT_OS_SOLARIS) && defined(VBOX_WITH_HARDENING)
218 char achKernArch[128];
219 int cbKernArch = sysinfo(SI_ARCHITECTURE_K, achKernArch, sizeof(achKernArch));
220 if (cbKernArch > 0)
221 {
222 sprintf(VBoxSVCPath, "/opt/VirtualBox/%s%s", achKernArch, VBoxSVC_exe);
223 IsVBoxSVCPathSet = true;
224 }
225 else
226 rc = NS_ERROR_UNEXPECTED;
227#else
228 strcpy(VBoxSVCPath, path.get());
229 RTPathStripFilename(VBoxSVCPath);
230 strcat(VBoxSVCPath, VBoxSVC_exe);
231
232 IsVBoxSVCPathSet = true;
233#endif
234 }
235 }
236 if (NS_FAILED(rc))
237 break;
238 }
239
240 nsCOMPtr<ipcIService> ipcServ = do_GetService(IPC_SERVICE_CONTRACTID, &rc);
241 if (NS_FAILED(rc))
242 break;
243
244 /* connect to the VBoxSVC server process */
245
246 bool startedOnce = false;
247 unsigned timeLeft = VBoxSVC_Timeout;
248
249 do
250 {
251 LogFlowFunc(("Resolving server name \"%s\"...\n", VBOXSVC_IPC_NAME));
252
253 PRUint32 serverID = 0;
254 rc = ipcServ->ResolveClientName(VBOXSVC_IPC_NAME, &serverID);
255 if (NS_FAILED(rc))
256 {
257 LogFlowFunc(("Starting server \"%s\"...\n", VBoxSVCPath));
258
259 startedOnce = true;
260
261 rc = vboxsvcSpawnDaemon();
262 if (NS_FAILED(rc))
263 break;
264
265 /* wait for the server process to establish a connection */
266 do
267 {
268 RTThreadSleep(VBoxSVC_WaitSlice);
269 rc = ipcServ->ResolveClientName(VBOXSVC_IPC_NAME, &serverID);
270 if (NS_SUCCEEDED(rc))
271 break;
272 if (timeLeft <= VBoxSVC_WaitSlice)
273 {
274 timeLeft = 0;
275 break;
276 }
277 timeLeft -= VBoxSVC_WaitSlice;
278 }
279 while (1);
280
281 if (!timeLeft)
282 {
283 rc = IPC_ERROR_WOULD_BLOCK;
284 break;
285 }
286 }
287
288 LogFlowFunc(("Connecting to server (ID=%d)...\n", serverID));
289
290 nsCOMPtr<ipcIDConnectService> dconServ =
291 do_GetService(IPC_DCONNECTSERVICE_CONTRACTID, &rc);
292 if (NS_FAILED(rc))
293 break;
294
295 rc = dconServ->CreateInstance(serverID,
296 CLSID_VirtualBox,
297 aIID, aResult);
298 if (NS_SUCCEEDED(rc))
299 break;
300
301 LogFlowFunc(("Failed to connect (rc=%Rhrc (%#08x))\n", rc, rc));
302
303 /* It's possible that the server gets shut down after we
304 * successfully resolve the server name but before it
305 * receives our CreateInstance() request. So, check for the
306 * name again, and restart the cycle if it fails. */
307 if (!startedOnce)
308 {
309 nsresult rc2 =
310 ipcServ->ResolveClientName(VBOXSVC_IPC_NAME, &serverID);
311 if (NS_SUCCEEDED(rc2))
312 break;
313
314 LogFlowFunc(("Server seems to have terminated before receiving our request. Will try again.\n"));
315 }
316 else
317 break;
318 }
319 while (1);
320 }
321 while (0);
322
323 LogFlowFunc(("rc=%Rhrc (%#08x)\n", rc, rc));
324 LogFlowFuncLeave();
325
326 return rc;
327}
328
329#if 0
330/// @todo not really necessary for the moment
331/**
332 *
333 * @param aCompMgr
334 * @param aPath
335 * @param aLoaderStr
336 * @param aType
337 * @param aInfo
338 *
339 * @return
340 */
341static NS_IMETHODIMP
342VirtualBoxRegistration(nsIComponentManager *aCompMgr,
343 nsIFile *aPath,
344 const char *aLoaderStr,
345 const char *aType,
346 const nsModuleComponentInfo *aInfo)
347{
348 nsCAutoString modulePath;
349 aPath->GetNativePath(modulePath);
350 nsCAutoString moduleTarget;
351 aPath->GetNativeTarget(moduleTarget);
352
353 LogFlowFunc(("aPath=%s, aTarget=%s, aLoaderStr=%s, aType=%s\n",
354 modulePath.get(), moduleTarget.get(), aLoaderStr, aType));
355
356 nsresult rc = NS_OK;
357
358 return rc;
359}
360#endif
361
362/**
363 * Component definition table.
364 * Lists all components defined in this module.
365 */
366static const nsModuleComponentInfo components[] =
367{
368 {
369 "VirtualBox component", // description
370 NS_VIRTUALBOX_CID, NS_VIRTUALBOX_CONTRACTID, // CID/ContractID
371 VirtualBoxConstructor, // constructor function
372 NULL, /* VirtualBoxRegistration, */ // registration function
373 NULL, // deregistration function
374 NULL, // destructor function
375 /// @todo
376 NS_CI_INTERFACE_GETTER_NAME(VirtualBoxWrap), // interfaces function
377 NULL, // language helper
378 /// @todo
379 &NS_CLASSINFO_NAME(VirtualBoxWrap) // global class info & flags
380 }
381};
382
383NS_IMPL_NSGETMODULE(VirtualBox_Server_Module, components)
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