VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/MachineLaunchVMCommonWorker.cpp@ 80774

Last change on this file since 80774 was 80614, checked in by vboxsync, 5 years ago

Main/MachineLaunchVMCommonWorker: Simplified darwin specific code for kicking off VirtualVM or override. Was making too many unnecessary string copies. [fix] bugref:9341

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.0 KB
Line 
1/* $Id: MachineLaunchVMCommonWorker.cpp 80614 2019-09-05 22:24:44Z vboxsync $ */
2/** @file
3 * VirtualBox Main - VM process launcher helper for VBoxSVC & VBoxSDS.
4 */
5
6/*
7 * Copyright (C) 2011-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <iprt/dir.h>
23#include <iprt/env.h>
24#include <iprt/err.h>
25#include <iprt/file.h>
26#include <iprt/log.h>
27#include <iprt/path.h>
28#include <iprt/process.h>
29#include "MachineLaunchVMCommonWorker.h"
30
31
32/*********************************************************************************************************************************
33* Defined Constants And Macros *
34*********************************************************************************************************************************/
35#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
36# define HOSTSUFF_EXE ".exe"
37#else
38# define HOSTSUFF_EXE ""
39#endif
40
41
42/**
43 * Launch a VM process.
44 *
45 * The function starts the new VM process. It is a caller's responsibility
46 * to make any checks before and after calling the function.
47 * The function is a part of both VBoxSVC and VBoxSDS, so any calls to IVirtualBox
48 * and IMachine interfaces are performed using the client API.
49 *
50 * @returns VBox status code.
51 * @retval VINF_SUCCESS when new VM process started.
52 * @retval VERR_INVALID_PARAMETER when either aMachine is not Machine interface
53 * or invalid aFrontend is specified. Hmm. Come to think of it, it
54 * could also be returned in some other cases, especially if the code
55 * is buggy, so I wouldn't rely on any exact meaning here!
56 * @retval VERR_INTERNAL_ERROR when something wrong.
57 *
58 * @param aNameOrId The Machine name or id interface the VM will start for.
59 * @param aComment The comment for new VM process.
60 * @param aFrontend The desired frontend for started VM.
61 * @param aEnvironment Additional environment variables=value pairs
62 * separated by newlines for new VM process.
63 * @param aExtraArg Extra argument for the VM process. Ignored if
64 * empty string.
65 * @param aFilename Start new VM using specified filename. Only filename
66 * without path is allowed. Default filename is used if
67 * empty.
68 * @param aFlags Flags for RTProcCreateEx functions family if
69 * required (RTPROC_FLAGS_XXX).
70 * @param aExtraData Additional data for RTProcCreateX functions family
71 * if required. Content is defined by the flags.
72 * @param aPid The PID of created process is returned here
73 */
74int MachineLaunchVMCommonWorker(const Utf8Str &aNameOrId,
75 const Utf8Str &aComment,
76 const Utf8Str &aFrontend,
77 const Utf8Str &aEnvironment,
78 const Utf8Str &aExtraArg,
79 const Utf8Str &aFilename,
80 uint32_t aFlags,
81 void *aExtraData,
82 RTPROCESS &aPid)
83{
84 NOREF(aNameOrId);
85 NOREF(aComment);
86 NOREF(aFlags);
87 NOREF(aExtraData);
88 NOREF(aExtraArg);
89 NOREF(aFilename);
90
91 /* Get the path to the executable directory w/ trailing slash: */
92 char szPath[RTPATH_MAX];
93 int vrc = RTPathAppPrivateArch(szPath, sizeof(szPath));
94 AssertRCReturn(vrc, vrc);
95 size_t cbBufLeft = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath));
96 AssertReturn(cbBufLeft > 0, VERR_FILENAME_TOO_LONG);
97 char *pszNamePart = &szPath[cbBufLeft]; NOREF(pszNamePart);
98 cbBufLeft = sizeof(szPath) - cbBufLeft;
99
100 /* The process started when launching a VM with separate UI/VM processes is always
101 * the UI process, i.e. needs special handling as it won't claim the session. */
102 bool fSeparate = aFrontend.endsWith("separate", Utf8Str::CaseInsensitive); NOREF(fSeparate);
103
104 aPid = NIL_RTPROCESS;
105
106 RTENV hEnv = RTENV_DEFAULT;
107 if (!aEnvironment.isEmpty())
108 {
109 Utf8Str strEnvCopy(aEnvironment); /* auto release trick */
110
111#ifdef IN_VBOXSVC
112 /* VBoxSVC: clone the current environment */
113 vrc = RTEnvClone(&hEnv, RTENV_DEFAULT);
114#else
115 /* VBoxSDS: Create a change record environment since RTProcCreateEx has to
116 build the final environment from the profile of the VBoxSDS caller. */
117 aFlags |= RTPROC_FLAGS_ENV_CHANGE_RECORD;
118 vrc = RTEnvCreateChangeRecord(&hEnv);
119#endif
120 AssertRCReturn(vrc, vrc);
121
122 /* Apply the specified environment changes (ignoring empty variable names
123 as RTEnv intentionally does not support that). */
124 char *pszEnvMutable = strEnvCopy.mutableRaw();
125 char *pszVar = pszEnvMutable;
126 for (char *psz = pszEnvMutable; ; ++psz)
127 {
128 /** @todo r=bird: Broken escaping rule, how to end a variable with '\\'?
129 * E.g. TMP=C:\\TEMP\\ */
130 char const ch = *psz;
131 if ( (ch == '\n' && (psz == pszEnvMutable || psz[-1] != '\\'))
132 || ch == '\0')
133 {
134 *psz = '\0';
135 if (*pszVar)
136 {
137 char *val = strchr(pszVar, '=');
138 if (val)
139 {
140 *val++ = '\0';
141 vrc = RTEnvSetEx(hEnv, pszVar, val);
142 }
143 else
144 vrc = RTEnvUnsetEx(hEnv, pszVar);
145 if (RT_FAILURE(vrc))
146 {
147 RTEnvDestroy(hEnv);
148 return vrc;
149 }
150 }
151 if (!ch)
152 break;
153 pszVar = psz + 1;
154 }
155 }
156 }
157
158#ifdef VBOX_WITH_QTGUI
159 if ( !aFrontend.compare("gui", Utf8Str::CaseInsensitive)
160 || !aFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
161 || !aFrontend.compare("separate", Utf8Str::CaseInsensitive)
162 || !aFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
163 || !aFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
164 {
165# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
166
167# define OSX_APP_NAME "VirtualBoxVM"
168# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
169# define OSX_APP_PATH_WITH_NAME "/Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM"
170
171 /* Modify the base path so that we don't need to use ".." below. */
172 RTPathStripTrailingSlash(szPath);
173 RTPathStripFilename(szPath);
174 cbBufLeft = strlen(szPath);
175 pszNamePart = &szPath[cbBufLeft]; Assert(!*pszNamePart);
176 cbBufLeft = sizeof(szPath) - cbBufLeft;
177
178 if (aFilename.isNotEmpty() && strpbrk(aFilename.c_str(), "./\\:") == NULL)
179 {
180 ssize_t cch = RTStrPrintf2(pszNamePart, cbBufLeft, OSX_APP_PATH_FMT, aFilename.c_str());
181 AssertReturn(cch > 0, VERR_FILENAME_TOO_LONG);
182 /* there is a race, but people using this deserve the failure */
183 if (!RTFileExists(szPath))
184 *pszNamePart = '\0';
185 }
186 if (!*pszNamePart)
187 {
188 vrc = RTStrCopy(pszNamePart, cbBufLeft, OSX_APP_PATH_WITH_NAME);
189 AssertRCReturn(vrc, vrc);
190 }
191# else
192 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
193 vrc = RTStrCopy(pszNamePart, cbBufLeft, s_szVirtualBox_exe);
194 AssertRCReturn(vrc, vrc);
195# endif
196
197 const char *apszArgs[] =
198 {
199 szPath,
200 "--comment", aComment.c_str(),
201 "--startvm", aNameOrId.c_str(),
202 "--no-startvm-errormsgbox",
203 NULL, /* For "--separate". */
204 NULL, /* For "--sup-startup-log". */
205 NULL
206 };
207 unsigned iArg = 6;
208 if (fSeparate)
209 apszArgs[iArg++] = "--separate";
210 if (aExtraArg.isNotEmpty())
211 apszArgs[iArg++] = aExtraArg.c_str();
212
213 vrc = RTProcCreateEx(szPath, apszArgs, hEnv, aFlags, NULL, NULL, NULL, NULL, NULL, aExtraData, &aPid);
214 }
215#else /* !VBOX_WITH_QTGUI */
216 if (0)
217 ;
218#endif /* VBOX_WITH_QTGUI */
219
220 else
221
222#ifdef VBOX_WITH_VBOXSDL
223 if ( !aFrontend.compare("sdl", Utf8Str::CaseInsensitive)
224 || !aFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
225 || !aFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
226 || !aFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
227 {
228 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
229 vrc = RTStrCopy(pszNamePart, cbBufLeft, s_szVBoxSDL_exe);
230 AssertRCReturn(vrc, vrc);
231
232 const char *apszArgs[] =
233 {
234 szPath,
235 "--comment", aComment.c_str(),
236 "--startvm", aNameOrId.c_str(),
237 NULL, /* For "--separate". */
238 NULL, /* For "--sup-startup-log". */
239 NULL
240 };
241 unsigned iArg = 5;
242 if (fSeparate)
243 apszArgs[iArg++] = "--separate";
244 if (aExtraArg.isNotEmpty())
245 apszArgs[iArg++] = aExtraArg.c_str();
246
247 vrc = RTProcCreateEx(szPath, apszArgs, hEnv, aFlags, NULL, NULL, NULL, NULL, NULL, aExtraData, &aPid);
248 }
249#else /* !VBOX_WITH_VBOXSDL */
250 if (0)
251 ;
252#endif /* !VBOX_WITH_VBOXSDL */
253
254 else
255
256#ifdef VBOX_WITH_HEADLESS
257 if ( !aFrontend.compare("headless", Utf8Str::CaseInsensitive)
258 || !aFrontend.compare("capture", Utf8Str::CaseInsensitive)
259 || !aFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
260 )
261 {
262 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
263 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
264 * and a VM works even if the server has not been installed.
265 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
266 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
267 * differently in 4.0 and 3.x.
268 */
269 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
270 vrc = RTStrCopy(pszNamePart, cbBufLeft, s_szVBoxHeadless_exe);
271 AssertRCReturn(vrc, vrc);
272
273 const char *apszArgs[] =
274 {
275 szPath,
276 "--comment", aComment.c_str(),
277 "--startvm", aNameOrId.c_str(),
278 "--vrde", "config",
279 NULL, /* For "--capture". */
280 NULL, /* For "--sup-startup-log". */
281 NULL
282 };
283 unsigned iArg = 7;
284 if (!aFrontend.compare("capture", Utf8Str::CaseInsensitive))
285 apszArgs[iArg++] = "--capture";
286 if (aExtraArg.isNotEmpty())
287 apszArgs[iArg++] = aExtraArg.c_str();
288
289# ifdef RT_OS_WINDOWS
290 aFlags |= RTPROC_FLAGS_NO_WINDOW;
291# endif
292 vrc = RTProcCreateEx(szPath, apszArgs, hEnv, aFlags, NULL, NULL, NULL, NULL, NULL, aExtraData, &aPid);
293 }
294#else /* !VBOX_WITH_HEADLESS */
295 if (0)
296 ;
297#endif /* !VBOX_WITH_HEADLESS */
298 else
299 vrc = VERR_INVALID_PARAMETER;
300
301 RTEnvDestroy(hEnv);
302
303 if (RT_FAILURE(vrc))
304 return vrc;
305
306 return VINF_SUCCESS;
307}
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