1 | /* $Id: MachineLaunchVMCommonWorker.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * VirtualBox Main - VM process launcher helper for VBoxSVC & VBoxSDS.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2011-2022 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 aEnvironmentChanges Additional environment variables in the putenv style
|
---|
62 | * (VAR=VAL for setting, VAR for unsetting) 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 | */
|
---|
74 | int MachineLaunchVMCommonWorker(const Utf8Str &aNameOrId,
|
---|
75 | const Utf8Str &aComment,
|
---|
76 | const Utf8Str &aFrontend,
|
---|
77 | const std::vector<com::Utf8Str> &aEnvironmentChanges,
|
---|
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 (!aEnvironmentChanges.empty())
|
---|
108 | {
|
---|
109 | #ifdef IN_VBOXSVC
|
---|
110 | /* VBoxSVC: clone the current environment */
|
---|
111 | vrc = RTEnvClone(&hEnv, RTENV_DEFAULT);
|
---|
112 | #else
|
---|
113 | /* VBoxSDS: Create a change record environment since RTProcCreateEx has to
|
---|
114 | build the final environment from the profile of the VBoxSDS caller. */
|
---|
115 | aFlags |= RTPROC_FLAGS_ENV_CHANGE_RECORD;
|
---|
116 | vrc = RTEnvCreateChangeRecord(&hEnv);
|
---|
117 | #endif
|
---|
118 | AssertRCReturn(vrc, vrc);
|
---|
119 |
|
---|
120 | /* Apply the specified environment changes. */
|
---|
121 | for (std::vector<com::Utf8Str>::const_iterator itEnv = aEnvironmentChanges.begin();
|
---|
122 | itEnv != aEnvironmentChanges.end();
|
---|
123 | ++itEnv)
|
---|
124 | {
|
---|
125 | vrc = RTEnvPutEx(hEnv, itEnv->c_str());
|
---|
126 | AssertRCReturnStmt(vrc, RTEnvDestroy(hEnv), vrc);
|
---|
127 | }
|
---|
128 | }
|
---|
129 |
|
---|
130 | #ifdef VBOX_WITH_QTGUI
|
---|
131 | if ( !aFrontend.compare("gui", Utf8Str::CaseInsensitive)
|
---|
132 | || !aFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
|
---|
133 | || !aFrontend.compare("separate", Utf8Str::CaseInsensitive)
|
---|
134 | || !aFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
|
---|
135 | || !aFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
|
---|
136 | {
|
---|
137 | # ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
|
---|
138 |
|
---|
139 | # define OSX_APP_NAME "VirtualBoxVM"
|
---|
140 | # define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
|
---|
141 | # define OSX_APP_PATH_WITH_NAME "/Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM"
|
---|
142 |
|
---|
143 | /* Modify the base path so that we don't need to use ".." below. */
|
---|
144 | RTPathStripTrailingSlash(szPath);
|
---|
145 | RTPathStripFilename(szPath);
|
---|
146 | cbBufLeft = strlen(szPath);
|
---|
147 | pszNamePart = &szPath[cbBufLeft]; Assert(!*pszNamePart);
|
---|
148 | cbBufLeft = sizeof(szPath) - cbBufLeft;
|
---|
149 |
|
---|
150 | if (aFilename.isNotEmpty() && strpbrk(aFilename.c_str(), "./\\:") == NULL)
|
---|
151 | {
|
---|
152 | ssize_t cch = RTStrPrintf2(pszNamePart, cbBufLeft, OSX_APP_PATH_FMT, aFilename.c_str());
|
---|
153 | AssertReturn(cch > 0, VERR_FILENAME_TOO_LONG);
|
---|
154 | /* there is a race, but people using this deserve the failure */
|
---|
155 | if (!RTFileExists(szPath))
|
---|
156 | *pszNamePart = '\0';
|
---|
157 | }
|
---|
158 | if (!*pszNamePart)
|
---|
159 | {
|
---|
160 | vrc = RTStrCopy(pszNamePart, cbBufLeft, OSX_APP_PATH_WITH_NAME);
|
---|
161 | AssertRCReturn(vrc, vrc);
|
---|
162 | }
|
---|
163 | # else
|
---|
164 | static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
|
---|
165 | vrc = RTStrCopy(pszNamePart, cbBufLeft, s_szVirtualBox_exe);
|
---|
166 | AssertRCReturn(vrc, vrc);
|
---|
167 | # endif
|
---|
168 |
|
---|
169 | const char *apszArgs[] =
|
---|
170 | {
|
---|
171 | szPath,
|
---|
172 | "--comment", aComment.c_str(),
|
---|
173 | "--startvm", aNameOrId.c_str(),
|
---|
174 | "--no-startvm-errormsgbox",
|
---|
175 | NULL, /* For "--separate". */
|
---|
176 | NULL, /* For "--sup-startup-log". */
|
---|
177 | NULL
|
---|
178 | };
|
---|
179 | unsigned iArg = 6;
|
---|
180 | if (fSeparate)
|
---|
181 | apszArgs[iArg++] = "--separate";
|
---|
182 | if (aExtraArg.isNotEmpty())
|
---|
183 | apszArgs[iArg++] = aExtraArg.c_str();
|
---|
184 |
|
---|
185 | vrc = RTProcCreateEx(szPath, apszArgs, hEnv, aFlags, NULL, NULL, NULL, NULL, NULL, aExtraData, &aPid);
|
---|
186 | }
|
---|
187 | #else /* !VBOX_WITH_QTGUI */
|
---|
188 | if (0)
|
---|
189 | ;
|
---|
190 | #endif /* VBOX_WITH_QTGUI */
|
---|
191 |
|
---|
192 | else
|
---|
193 |
|
---|
194 | #ifdef VBOX_WITH_VBOXSDL
|
---|
195 | if ( !aFrontend.compare("sdl", Utf8Str::CaseInsensitive)
|
---|
196 | || !aFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
|
---|
197 | || !aFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
|
---|
198 | || !aFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
|
---|
199 | {
|
---|
200 | static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
|
---|
201 | vrc = RTStrCopy(pszNamePart, cbBufLeft, s_szVBoxSDL_exe);
|
---|
202 | AssertRCReturn(vrc, vrc);
|
---|
203 |
|
---|
204 | const char *apszArgs[] =
|
---|
205 | {
|
---|
206 | szPath,
|
---|
207 | "--comment", aComment.c_str(),
|
---|
208 | "--startvm", aNameOrId.c_str(),
|
---|
209 | NULL, /* For "--separate". */
|
---|
210 | NULL, /* For "--sup-startup-log". */
|
---|
211 | NULL
|
---|
212 | };
|
---|
213 | unsigned iArg = 5;
|
---|
214 | if (fSeparate)
|
---|
215 | apszArgs[iArg++] = "--separate";
|
---|
216 | if (aExtraArg.isNotEmpty())
|
---|
217 | apszArgs[iArg++] = aExtraArg.c_str();
|
---|
218 |
|
---|
219 | vrc = RTProcCreateEx(szPath, apszArgs, hEnv, aFlags, NULL, NULL, NULL, NULL, NULL, aExtraData, &aPid);
|
---|
220 | }
|
---|
221 | #else /* !VBOX_WITH_VBOXSDL */
|
---|
222 | if (0)
|
---|
223 | ;
|
---|
224 | #endif /* !VBOX_WITH_VBOXSDL */
|
---|
225 |
|
---|
226 | else
|
---|
227 |
|
---|
228 | #ifdef VBOX_WITH_HEADLESS
|
---|
229 | if ( !aFrontend.compare("headless", Utf8Str::CaseInsensitive)
|
---|
230 | || !aFrontend.compare("capture", Utf8Str::CaseInsensitive)
|
---|
231 | || !aFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
|
---|
232 | )
|
---|
233 | {
|
---|
234 | /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
|
---|
235 | * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
|
---|
236 | * and a VM works even if the server has not been installed.
|
---|
237 | * So in 4.0 the "headless" behavior remains the same for default VBox installations.
|
---|
238 | * Only if a VRDE has been installed and the VM enables it, the "headless" will work
|
---|
239 | * differently in 4.0 and 3.x.
|
---|
240 | */
|
---|
241 | static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
|
---|
242 | vrc = RTStrCopy(pszNamePart, cbBufLeft, s_szVBoxHeadless_exe);
|
---|
243 | AssertRCReturn(vrc, vrc);
|
---|
244 |
|
---|
245 | const char *apszArgs[] =
|
---|
246 | {
|
---|
247 | szPath,
|
---|
248 | "--comment", aComment.c_str(),
|
---|
249 | "--startvm", aNameOrId.c_str(),
|
---|
250 | "--vrde", "config",
|
---|
251 | NULL, /* For "--capture". */
|
---|
252 | NULL, /* For "--sup-startup-log". */
|
---|
253 | NULL
|
---|
254 | };
|
---|
255 | unsigned iArg = 7;
|
---|
256 | if (!aFrontend.compare("capture", Utf8Str::CaseInsensitive))
|
---|
257 | apszArgs[iArg++] = "--capture";
|
---|
258 | if (aExtraArg.isNotEmpty())
|
---|
259 | apszArgs[iArg++] = aExtraArg.c_str();
|
---|
260 |
|
---|
261 | # ifdef RT_OS_WINDOWS
|
---|
262 | aFlags |= RTPROC_FLAGS_NO_WINDOW;
|
---|
263 | # endif
|
---|
264 | vrc = RTProcCreateEx(szPath, apszArgs, hEnv, aFlags, NULL, NULL, NULL, NULL, NULL, aExtraData, &aPid);
|
---|
265 | }
|
---|
266 | #else /* !VBOX_WITH_HEADLESS */
|
---|
267 | if (0)
|
---|
268 | ;
|
---|
269 | #endif /* !VBOX_WITH_HEADLESS */
|
---|
270 | else
|
---|
271 | vrc = VERR_INVALID_PARAMETER;
|
---|
272 |
|
---|
273 | RTEnvDestroy(hEnv);
|
---|
274 |
|
---|
275 | if (RT_FAILURE(vrc))
|
---|
276 | return vrc;
|
---|
277 |
|
---|
278 | return VINF_SUCCESS;
|
---|
279 | }
|
---|