VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp@ 105266

Last change on this file since 105266 was 105266, checked in by vboxsync, 3 months ago

Recording: Implemented support for a dedicated progress object, which is exposed to API clients. This can be used for better tracking the recording progress as well as for error reporting. The RecordingSettings API also now has a dedicated start() method to start recording, as well as support for attaching to an already ongoing recording by retrieving the progress object at a later time. Adapted FE/Qt (draft, see @todos), FE/VBoxManage and the Validation Kit testdriver to the new APIs. VBoxManage also can attach to an ongoing recording now. The recording progress object also will have multiple operations to get the recording progress for convenience. bugref:10718

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.4 KB
Line 
1/* $Id: VBoxManageControlVM.cpp 105266 2024-07-11 07:49:37Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of the controlvm command.
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
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <VBox/com/com.h>
33#include <VBox/com/string.h>
34#include <VBox/com/Guid.h>
35#include <VBox/com/array.h>
36#include <VBox/com/ErrorInfo.h>
37#include <VBox/com/errorprint.h>
38#include <VBox/com/VirtualBox.h>
39
40#include <iprt/ctype.h>
41#include <iprt/getopt.h>
42#include <iprt/stream.h>
43#include <iprt/string.h>
44#include <iprt/thread.h>
45#include <iprt/uuid.h>
46#include <iprt/file.h>
47#include <VBox/log.h>
48
49#include "VBoxManage.h"
50#include "VBoxManageUtils.h"
51
52#include <list>
53
54DECLARE_TRANSLATION_CONTEXT(ControlVM);
55
56/**
57 * Parses a number.
58 *
59 * @returns Valid number on success.
60 * @returns 0 if invalid number. All necessary bitching has been done.
61 * @param psz Pointer to the nic number.
62 */
63static unsigned parseNum(const char *psz, unsigned cMaxNum, const char *name)
64{
65 uint32_t u32;
66 char *pszNext;
67 int vrc = RTStrToUInt32Ex(psz, &pszNext, 10, &u32);
68 if ( RT_SUCCESS(vrc)
69 && *pszNext == '\0'
70 && u32 >= 1
71 && u32 <= cMaxNum)
72 return (unsigned)u32;
73 errorArgument(ControlVM::tr("Invalid %s number '%s'."), name, psz);
74 return 0;
75}
76
77#define KBDCHARDEF_MOD_NONE 0x00
78#define KBDCHARDEF_MOD_SHIFT 0x01
79
80typedef struct KBDCHARDEF
81{
82 uint8_t u8Scancode;
83 uint8_t u8Modifiers;
84} KBDCHARDEF;
85
86static const KBDCHARDEF g_aASCIIChars[0x80] =
87{
88 /* 0x00 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
89 /* 0x01 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
90 /* 0x02 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
91 /* 0x03 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
92 /* 0x04 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
93 /* 0x05 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
94 /* 0x06 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
95 /* 0x07 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
96 /* 0x08 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
97 /* 0x09 ' ' */ {0x0f, KBDCHARDEF_MOD_NONE},
98 /* 0x0A ' ' */ {0x1c, KBDCHARDEF_MOD_NONE},
99 /* 0x0B ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
100 /* 0x0C ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
101 /* 0x0D ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
102 /* 0x0E ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
103 /* 0x0F ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
104 /* 0x10 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
105 /* 0x11 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
106 /* 0x12 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
107 /* 0x13 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
108 /* 0x14 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
109 /* 0x15 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
110 /* 0x16 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
111 /* 0x17 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
112 /* 0x18 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
113 /* 0x19 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
114 /* 0x1A ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
115 /* 0x1B ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
116 /* 0x1C ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
117 /* 0x1D ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
118 /* 0x1E ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
119 /* 0x1F ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
120 /* 0x20 ' ' */ {0x39, KBDCHARDEF_MOD_NONE},
121 /* 0x21 '!' */ {0x02, KBDCHARDEF_MOD_SHIFT},
122 /* 0x22 '"' */ {0x28, KBDCHARDEF_MOD_SHIFT},
123 /* 0x23 '#' */ {0x04, KBDCHARDEF_MOD_SHIFT},
124 /* 0x24 '$' */ {0x05, KBDCHARDEF_MOD_SHIFT},
125 /* 0x25 '%' */ {0x06, KBDCHARDEF_MOD_SHIFT},
126 /* 0x26 '&' */ {0x08, KBDCHARDEF_MOD_SHIFT},
127 /* 0x27 ''' */ {0x28, KBDCHARDEF_MOD_NONE},
128 /* 0x28 '(' */ {0x0a, KBDCHARDEF_MOD_SHIFT},
129 /* 0x29 ')' */ {0x0b, KBDCHARDEF_MOD_SHIFT},
130 /* 0x2A '*' */ {0x09, KBDCHARDEF_MOD_SHIFT},
131 /* 0x2B '+' */ {0x0d, KBDCHARDEF_MOD_SHIFT},
132 /* 0x2C ',' */ {0x33, KBDCHARDEF_MOD_NONE},
133 /* 0x2D '-' */ {0x0c, KBDCHARDEF_MOD_NONE},
134 /* 0x2E '.' */ {0x34, KBDCHARDEF_MOD_NONE},
135 /* 0x2F '/' */ {0x35, KBDCHARDEF_MOD_NONE},
136 /* 0x30 '0' */ {0x0b, KBDCHARDEF_MOD_NONE},
137 /* 0x31 '1' */ {0x02, KBDCHARDEF_MOD_NONE},
138 /* 0x32 '2' */ {0x03, KBDCHARDEF_MOD_NONE},
139 /* 0x33 '3' */ {0x04, KBDCHARDEF_MOD_NONE},
140 /* 0x34 '4' */ {0x05, KBDCHARDEF_MOD_NONE},
141 /* 0x35 '5' */ {0x06, KBDCHARDEF_MOD_NONE},
142 /* 0x36 '6' */ {0x07, KBDCHARDEF_MOD_NONE},
143 /* 0x37 '7' */ {0x08, KBDCHARDEF_MOD_NONE},
144 /* 0x38 '8' */ {0x09, KBDCHARDEF_MOD_NONE},
145 /* 0x39 '9' */ {0x0a, KBDCHARDEF_MOD_NONE},
146 /* 0x3A ':' */ {0x27, KBDCHARDEF_MOD_SHIFT},
147 /* 0x3B ';' */ {0x27, KBDCHARDEF_MOD_NONE},
148 /* 0x3C '<' */ {0x33, KBDCHARDEF_MOD_SHIFT},
149 /* 0x3D '=' */ {0x0d, KBDCHARDEF_MOD_NONE},
150 /* 0x3E '>' */ {0x34, KBDCHARDEF_MOD_SHIFT},
151 /* 0x3F '?' */ {0x35, KBDCHARDEF_MOD_SHIFT},
152 /* 0x40 '@' */ {0x03, KBDCHARDEF_MOD_SHIFT},
153 /* 0x41 'A' */ {0x1e, KBDCHARDEF_MOD_SHIFT},
154 /* 0x42 'B' */ {0x30, KBDCHARDEF_MOD_SHIFT},
155 /* 0x43 'C' */ {0x2e, KBDCHARDEF_MOD_SHIFT},
156 /* 0x44 'D' */ {0x20, KBDCHARDEF_MOD_SHIFT},
157 /* 0x45 'E' */ {0x12, KBDCHARDEF_MOD_SHIFT},
158 /* 0x46 'F' */ {0x21, KBDCHARDEF_MOD_SHIFT},
159 /* 0x47 'G' */ {0x22, KBDCHARDEF_MOD_SHIFT},
160 /* 0x48 'H' */ {0x23, KBDCHARDEF_MOD_SHIFT},
161 /* 0x49 'I' */ {0x17, KBDCHARDEF_MOD_SHIFT},
162 /* 0x4A 'J' */ {0x24, KBDCHARDEF_MOD_SHIFT},
163 /* 0x4B 'K' */ {0x25, KBDCHARDEF_MOD_SHIFT},
164 /* 0x4C 'L' */ {0x26, KBDCHARDEF_MOD_SHIFT},
165 /* 0x4D 'M' */ {0x32, KBDCHARDEF_MOD_SHIFT},
166 /* 0x4E 'N' */ {0x31, KBDCHARDEF_MOD_SHIFT},
167 /* 0x4F 'O' */ {0x18, KBDCHARDEF_MOD_SHIFT},
168 /* 0x50 'P' */ {0x19, KBDCHARDEF_MOD_SHIFT},
169 /* 0x51 'Q' */ {0x10, KBDCHARDEF_MOD_SHIFT},
170 /* 0x52 'R' */ {0x13, KBDCHARDEF_MOD_SHIFT},
171 /* 0x53 'S' */ {0x1f, KBDCHARDEF_MOD_SHIFT},
172 /* 0x54 'T' */ {0x14, KBDCHARDEF_MOD_SHIFT},
173 /* 0x55 'U' */ {0x16, KBDCHARDEF_MOD_SHIFT},
174 /* 0x56 'V' */ {0x2f, KBDCHARDEF_MOD_SHIFT},
175 /* 0x57 'W' */ {0x11, KBDCHARDEF_MOD_SHIFT},
176 /* 0x58 'X' */ {0x2d, KBDCHARDEF_MOD_SHIFT},
177 /* 0x59 'Y' */ {0x15, KBDCHARDEF_MOD_SHIFT},
178 /* 0x5A 'Z' */ {0x2c, KBDCHARDEF_MOD_SHIFT},
179 /* 0x5B '[' */ {0x1a, KBDCHARDEF_MOD_NONE},
180 /* 0x5C '\' */ {0x2b, KBDCHARDEF_MOD_NONE},
181 /* 0x5D ']' */ {0x1b, KBDCHARDEF_MOD_NONE},
182 /* 0x5E '^' */ {0x07, KBDCHARDEF_MOD_SHIFT},
183 /* 0x5F '_' */ {0x0c, KBDCHARDEF_MOD_SHIFT},
184 /* 0x60 '`' */ {0x28, KBDCHARDEF_MOD_NONE},
185 /* 0x61 'a' */ {0x1e, KBDCHARDEF_MOD_NONE},
186 /* 0x62 'b' */ {0x30, KBDCHARDEF_MOD_NONE},
187 /* 0x63 'c' */ {0x2e, KBDCHARDEF_MOD_NONE},
188 /* 0x64 'd' */ {0x20, KBDCHARDEF_MOD_NONE},
189 /* 0x65 'e' */ {0x12, KBDCHARDEF_MOD_NONE},
190 /* 0x66 'f' */ {0x21, KBDCHARDEF_MOD_NONE},
191 /* 0x67 'g' */ {0x22, KBDCHARDEF_MOD_NONE},
192 /* 0x68 'h' */ {0x23, KBDCHARDEF_MOD_NONE},
193 /* 0x69 'i' */ {0x17, KBDCHARDEF_MOD_NONE},
194 /* 0x6A 'j' */ {0x24, KBDCHARDEF_MOD_NONE},
195 /* 0x6B 'k' */ {0x25, KBDCHARDEF_MOD_NONE},
196 /* 0x6C 'l' */ {0x26, KBDCHARDEF_MOD_NONE},
197 /* 0x6D 'm' */ {0x32, KBDCHARDEF_MOD_NONE},
198 /* 0x6E 'n' */ {0x31, KBDCHARDEF_MOD_NONE},
199 /* 0x6F 'o' */ {0x18, KBDCHARDEF_MOD_NONE},
200 /* 0x70 'p' */ {0x19, KBDCHARDEF_MOD_NONE},
201 /* 0x71 'q' */ {0x10, KBDCHARDEF_MOD_NONE},
202 /* 0x72 'r' */ {0x13, KBDCHARDEF_MOD_NONE},
203 /* 0x73 's' */ {0x1f, KBDCHARDEF_MOD_NONE},
204 /* 0x74 't' */ {0x14, KBDCHARDEF_MOD_NONE},
205 /* 0x75 'u' */ {0x16, KBDCHARDEF_MOD_NONE},
206 /* 0x76 'v' */ {0x2f, KBDCHARDEF_MOD_NONE},
207 /* 0x77 'w' */ {0x11, KBDCHARDEF_MOD_NONE},
208 /* 0x78 'x' */ {0x2d, KBDCHARDEF_MOD_NONE},
209 /* 0x79 'y' */ {0x15, KBDCHARDEF_MOD_NONE},
210 /* 0x7A 'z' */ {0x2c, KBDCHARDEF_MOD_NONE},
211 /* 0x7B '{' */ {0x1a, KBDCHARDEF_MOD_SHIFT},
212 /* 0x7C '|' */ {0x2b, KBDCHARDEF_MOD_SHIFT},
213 /* 0x7D '}' */ {0x1b, KBDCHARDEF_MOD_SHIFT},
214 /* 0x7E '~' */ {0x29, KBDCHARDEF_MOD_SHIFT},
215 /* 0x7F ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
216};
217
218static HRESULT keyboardPutScancodes(IKeyboard *pKeyboard, const std::list<LONG> &llScancodes)
219{
220 /* Send scancodes to the VM. */
221 com::SafeArray<LONG> saScancodes(llScancodes);
222
223 HRESULT hrc = S_OK;
224 size_t i;
225 for (i = 0; i < saScancodes.size(); ++i)
226 {
227 hrc = pKeyboard->PutScancode(saScancodes[i]);
228 if (FAILED(hrc))
229 {
230 RTMsgError(ControlVM::tr("Failed to send a scancode."));
231 break;
232 }
233
234 RTThreadSleep(10); /* "Typing" too fast causes lost characters. */
235 }
236
237 return hrc;
238}
239
240static void keyboardCharsToScancodes(const char *pch, size_t cchMax, std::list<LONG> &llScancodes, bool *pfShift)
241{
242 size_t cchProcessed = 0;
243 const char *p = pch;
244 while (cchProcessed < cchMax)
245 {
246 ++cchProcessed;
247 const uint8_t c = (uint8_t)*p++;
248 if (c < RT_ELEMENTS(g_aASCIIChars))
249 {
250 const KBDCHARDEF *d = &g_aASCIIChars[c];
251 if (d->u8Scancode)
252 {
253 const bool fNeedShift = RT_BOOL(d->u8Modifiers & KBDCHARDEF_MOD_SHIFT);
254 if (*pfShift != fNeedShift)
255 {
256 *pfShift = fNeedShift;
257 /* Press or release the SHIFT key. */
258 llScancodes.push_back(0x2a | (fNeedShift? 0x00: 0x80));
259 }
260
261 llScancodes.push_back(d->u8Scancode);
262 llScancodes.push_back(d->u8Scancode | 0x80);
263 }
264 }
265 }
266}
267
268static HRESULT keyboardPutString(IKeyboard *pKeyboard, int argc, char **argv)
269{
270 std::list<LONG> llScancodes;
271 bool fShift = false;
272
273 /* Convert command line string(s) to the en-us keyboard scancodes. */
274 int i;
275 for (i = 1 + 1; i < argc; ++i)
276 {
277 if (!llScancodes.empty())
278 {
279 /* Insert a SPACE before the next string. */
280 llScancodes.push_back(0x39);
281 llScancodes.push_back(0x39 | 0x80);
282 }
283
284 keyboardCharsToScancodes(argv[i], strlen(argv[i]), llScancodes, &fShift);
285 }
286
287 /* Release SHIFT if pressed. */
288 if (fShift)
289 llScancodes.push_back(0x2a | 0x80);
290
291 return keyboardPutScancodes(pKeyboard, llScancodes);
292}
293
294static HRESULT keyboardPutFile(IKeyboard *pKeyboard, const char *pszFilename)
295{
296 std::list<LONG> llScancodes;
297 bool fShift = false;
298
299 RTFILE File = NIL_RTFILE;
300 int vrc = RTFileOpen(&File, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
301 if (RT_SUCCESS(vrc))
302 {
303 uint64_t cbFile = 0;
304 vrc = RTFileQuerySize(File, &cbFile);
305 if (RT_SUCCESS(vrc))
306 {
307 const uint64_t cbFileMax = _64K;
308 if (cbFile <= cbFileMax)
309 {
310 const size_t cbBuffer = _4K;
311 char *pchBuf = (char *)RTMemAlloc(cbBuffer);
312 if (pchBuf)
313 {
314 size_t cbRemaining = (size_t)cbFile;
315 while (cbRemaining > 0)
316 {
317 const size_t cbToRead = cbRemaining > cbBuffer ? cbBuffer : cbRemaining;
318
319 size_t cbRead = 0;
320 vrc = RTFileRead(File, pchBuf, cbToRead, &cbRead);
321 if (RT_FAILURE(vrc) || cbRead == 0)
322 break;
323
324 keyboardCharsToScancodes(pchBuf, cbRead, llScancodes, &fShift);
325 cbRemaining -= cbRead;
326 }
327
328 RTMemFree(pchBuf);
329 }
330 else
331 RTMsgError(ControlVM::tr("Out of memory allocating %d bytes.", "", cbBuffer), cbBuffer);
332 }
333 else
334 RTMsgError(ControlVM::tr("File size %RI64 is greater than %RI64: '%s'."), cbFile, cbFileMax, pszFilename);
335 }
336 else
337 RTMsgError(ControlVM::tr("Cannot get size of file '%s': %Rrc."), pszFilename, vrc);
338
339 RTFileClose(File);
340 }
341 else
342 RTMsgError(ControlVM::tr("Cannot open file '%s': %Rrc."), pszFilename, vrc);
343
344 /* Release SHIFT if pressed. */
345 if (fShift)
346 llScancodes.push_back(0x2a | 0x80);
347
348 return keyboardPutScancodes(pKeyboard, llScancodes);
349}
350
351
352RTEXITCODE handleControlVM(HandlerArg *a)
353{
354 using namespace com;
355 bool fNeedsSaving = false;
356 HRESULT hrc;
357
358 if (a->argc < 2)
359 return errorSyntax(ControlVM::tr("Not enough parameters."));
360
361 /* try to find the given machine */
362 ComPtr<IMachine> machine;
363 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
364 machine.asOutParam()));
365 if (FAILED(hrc))
366 return RTEXITCODE_FAILURE;
367
368 /* open a session for the VM */
369 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
370
371 ComPtr<IConsole> console;
372 ComPtr<IMachine> sessionMachine;
373
374 do
375 {
376 /* get the associated console */
377 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
378 if (!console)
379 return RTMsgErrorExit(RTEXITCODE_FAILURE, ControlVM::tr("Machine '%s' is not currently running."), a->argv[0]);
380
381 /* ... and session machine */
382 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
383
384 /* which command? */
385 if (!strcmp(a->argv[1], "pause"))
386 {
387 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_PAUSE);
388 CHECK_ERROR_BREAK(console, Pause());
389 }
390 else if (!strcmp(a->argv[1], "resume"))
391 {
392 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RESUME);
393 CHECK_ERROR_BREAK(console, Resume());
394 }
395 else if (!strcmp(a->argv[1], "reset"))
396 {
397 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RESET);
398 CHECK_ERROR_BREAK(console, Reset());
399 }
400 else if (!strcmp(a->argv[1], "unplugcpu"))
401 {
402 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_UNPLUGCPU);
403 if (a->argc <= 1 + 1)
404 {
405 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
406 hrc = E_FAIL;
407 break;
408 }
409
410 unsigned n = parseNum(a->argv[2], 32, "CPU");
411
412 CHECK_ERROR_BREAK(sessionMachine, HotUnplugCPU(n));
413 }
414 else if (!strcmp(a->argv[1], "plugcpu"))
415 {
416 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_PLUGCPU);
417 if (a->argc <= 1 + 1)
418 {
419 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
420 hrc = E_FAIL;
421 break;
422 }
423
424 unsigned n = parseNum(a->argv[2], 32, "CPU");
425
426 CHECK_ERROR_BREAK(sessionMachine, HotPlugCPU(n));
427 }
428 else if (!strcmp(a->argv[1], "cpuexecutioncap"))
429 {
430 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_CPUEXECUTIONCAP);
431 if (a->argc <= 1 + 1)
432 {
433 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
434 hrc = E_FAIL;
435 break;
436 }
437
438 unsigned n = parseNum(a->argv[2], 100, "ExecutionCap");
439
440 CHECK_ERROR_BREAK(sessionMachine, COMSETTER(CPUExecutionCap)(n));
441 }
442 else if (!strcmp(a->argv[1], "audioin"))
443 {
444 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUDIOIN);
445
446 ComPtr<IAudioSettings> audioSettings;
447 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioSettings)(audioSettings.asOutParam()));
448 ComPtr<IAudioAdapter> adapter;
449 CHECK_ERROR_BREAK(audioSettings, COMGETTER(Adapter)(adapter.asOutParam()));
450 if (adapter)
451 {
452 bool fEnabled;
453 if (RT_FAILURE(parseBool(a->argv[2], &fEnabled)))
454 {
455 errorSyntax(ControlVM::tr("Invalid value '%s'."), a->argv[2]);
456 hrc = E_FAIL;
457 break;
458 }
459 CHECK_ERROR_RET(adapter, COMSETTER(EnabledIn)(fEnabled), RTEXITCODE_FAILURE);
460 fNeedsSaving = true;
461 }
462 else
463 {
464 errorSyntax(ControlVM::tr("Audio adapter not enabled in VM configuration."));
465 hrc = E_FAIL;
466 break;
467 }
468 }
469 else if (!strcmp(a->argv[1], "audioout"))
470 {
471 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUDIOOUT);
472
473 ComPtr<IAudioSettings> audioSettings;
474 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioSettings)(audioSettings.asOutParam()));
475 ComPtr<IAudioAdapter> adapter;
476 CHECK_ERROR_BREAK(audioSettings, COMGETTER(Adapter)(adapter.asOutParam()));
477 if (adapter)
478 {
479 bool fEnabled;
480 if (RT_FAILURE(parseBool(a->argv[2], &fEnabled)))
481 {
482 errorSyntax(ControlVM::tr("Invalid value '%s'."), a->argv[2]);
483 hrc = E_FAIL;
484 break;
485 }
486 CHECK_ERROR_RET(adapter, COMSETTER(EnabledOut)(fEnabled), RTEXITCODE_FAILURE);
487 fNeedsSaving = true;
488 }
489 else
490 {
491 errorSyntax(ControlVM::tr("Audio adapter not enabled in VM configuration."));
492 hrc = E_FAIL;
493 break;
494 }
495 }
496#ifdef VBOX_WITH_SHARED_CLIPBOARD
497 else if (!strcmp(a->argv[1], "clipboard"))
498 {
499 if (a->argc <= 1 + 1)
500 {
501 errorArgument(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
502 hrc = E_FAIL;
503 break;
504 }
505
506 ClipboardMode_T mode = ClipboardMode_Disabled; /* Shut up MSC */
507 if (!strcmp(a->argv[2], "mode"))
508 {
509 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_CLIPBOARD_MODE);
510 if (a->argc <= 1 + 2)
511 {
512 errorSyntax(ControlVM::tr("Missing argument to '%s %s'."), a->argv[1], a->argv[2]);
513 hrc = E_FAIL;
514 break;
515 }
516
517 if (!strcmp(a->argv[3], "disabled"))
518 mode = ClipboardMode_Disabled;
519 else if (!strcmp(a->argv[3], "hosttoguest"))
520 mode = ClipboardMode_HostToGuest;
521 else if (!strcmp(a->argv[3], "guesttohost"))
522 mode = ClipboardMode_GuestToHost;
523 else if (!strcmp(a->argv[3], "bidirectional"))
524 mode = ClipboardMode_Bidirectional;
525 else
526 {
527 errorSyntax(ControlVM::tr("Invalid '%s %s' argument '%s'."), a->argv[1], a->argv[2], a->argv[3]);
528 hrc = E_FAIL;
529 break;
530 }
531
532 CHECK_ERROR_BREAK(sessionMachine, COMSETTER(ClipboardMode)(mode));
533 if (SUCCEEDED(hrc))
534 fNeedsSaving = true;
535 }
536# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
537 else if (!strcmp(a->argv[2], "filetransfers"))
538 {
539 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_CLIPBOARD_FILETRANSFERS);
540 if (a->argc <= 1 + 2)
541 {
542 errorSyntax(ControlVM::tr("Missing argument to '%s %s'."), a->argv[1], a->argv[2]);
543 hrc = E_FAIL;
544 break;
545 }
546
547 bool fEnabled;
548 if (RT_FAILURE(parseBool(a->argv[3], &fEnabled)))
549 {
550 errorSyntax(ControlVM::tr("Invalid '%s %s' argument '%s'."), a->argv[1], a->argv[2], a->argv[3]);
551 hrc = E_FAIL;
552 break;
553 }
554
555 CHECK_ERROR_BREAK(sessionMachine, COMSETTER(ClipboardFileTransfersEnabled)(fEnabled));
556 fNeedsSaving = true;
557 }
558# endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
559 else
560 {
561 errorArgument(ControlVM::tr("Invalid '%s' argument '%s'."), a->argv[1], a->argv[2]);
562 hrc = E_FAIL;
563 break;
564 }
565 }
566#endif /* VBOX_WITH_SHARED_CLIPBOARD */
567 else if (!strcmp(a->argv[1], "draganddrop"))
568 {
569 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_DRAGANDDROP);
570 if (a->argc <= 1 + 1)
571 {
572 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
573 hrc = E_FAIL;
574 break;
575 }
576
577 DnDMode_T mode = DnDMode_Disabled; /* Shup up MSC. */
578 if (!strcmp(a->argv[2], "disabled"))
579 mode = DnDMode_Disabled;
580 else if (!strcmp(a->argv[2], "hosttoguest"))
581 mode = DnDMode_HostToGuest;
582 else if (!strcmp(a->argv[2], "guesttohost"))
583 mode = DnDMode_GuestToHost;
584 else if (!strcmp(a->argv[2], "bidirectional"))
585 mode = DnDMode_Bidirectional;
586 else
587 {
588 errorSyntax(ControlVM::tr("Invalid '%s' argument '%s'."), a->argv[1], a->argv[2]);
589 hrc = E_FAIL;
590 }
591 if (SUCCEEDED(hrc))
592 {
593 CHECK_ERROR_BREAK(sessionMachine, COMSETTER(DnDMode)(mode));
594 if (SUCCEEDED(hrc))
595 fNeedsSaving = true;
596 }
597 }
598 else if (!strcmp(a->argv[1], "poweroff"))
599 {
600 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_POWEROFF);
601 ComPtr<IProgress> progress;
602 CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
603
604 hrc = showProgress(progress);
605 CHECK_PROGRESS_ERROR(progress, (ControlVM::tr("Failed to power off machine.")));
606 }
607 else if (!strcmp(a->argv[1], "savestate"))
608 {
609 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SAVESTATE);
610 /* first pause so we don't trigger a live save which needs more time/resources */
611 bool fPaused = false;
612 hrc = console->Pause();
613 if (FAILED(hrc))
614 {
615 bool fError = true;
616 if (hrc == VBOX_E_INVALID_VM_STATE)
617 {
618 /* check if we are already paused */
619 MachineState_T machineState;
620 CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
621 /* the error code was lost by the previous instruction */
622 hrc = VBOX_E_INVALID_VM_STATE;
623 if (machineState != MachineState_Paused)
624 {
625 RTMsgError(ControlVM::tr("Machine in invalid state %d -- %s."),
626 machineState, machineStateToName(machineState, false));
627 }
628 else
629 {
630 fError = false;
631 fPaused = true;
632 }
633 }
634 if (fError)
635 break;
636 }
637
638 ComPtr<IProgress> progress;
639 CHECK_ERROR(sessionMachine, SaveState(progress.asOutParam()));
640 if (FAILED(hrc))
641 {
642 if (!fPaused)
643 console->Resume();
644 break;
645 }
646
647 hrc = showProgress(progress);
648 CHECK_PROGRESS_ERROR(progress, (ControlVM::tr("Failed to save machine state.")));
649 if (FAILED(hrc))
650 {
651 if (!fPaused)
652 console->Resume();
653 }
654 }
655 else if (!strcmp(a->argv[1], "acpipowerbutton"))
656 {
657 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_ACPIPOWERBUTTON);
658 CHECK_ERROR_BREAK(console, PowerButton());
659 }
660 else if (!strcmp(a->argv[1], "acpisleepbutton"))
661 {
662 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_ACPISLEEPBUTTON);
663 CHECK_ERROR_BREAK(console, SleepButton());
664 }
665#ifdef VBOX_WITH_GUEST_CONTROL
666 else if ( !strcmp(a->argv[1], "reboot")
667 || !strcmp(a->argv[1], "shutdown")) /* With shutdown we mean gracefully powering off the VM by letting the guest OS do its thing. */
668 {
669 const bool fReboot = !strcmp(a->argv[1], "reboot");
670 if (fReboot)
671 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_REBOOT);
672 else
673 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SHUTDOWN);
674
675 ComPtr<IGuest> pGuest;
676 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pGuest.asOutParam()));
677 if (!pGuest)
678 {
679 RTMsgError(ControlVM::tr("Guest not running."));
680 hrc = E_FAIL;
681 break;
682 }
683
684 com::SafeArray<GuestShutdownFlag_T> aShutdownFlags;
685 if (fReboot)
686 aShutdownFlags.push_back(GuestShutdownFlag_Reboot);
687 else
688 aShutdownFlags.push_back(GuestShutdownFlag_PowerOff);
689
690 if ( a->argc >= 3
691 && !strcmp(a->argv[2], "--force"))
692 aShutdownFlags.push_back(GuestShutdownFlag_Force);
693
694 CHECK_ERROR(pGuest, Shutdown(ComSafeArrayAsInParam(aShutdownFlags)));
695 if (FAILED(hrc))
696 {
697 if (hrc == VBOX_E_NOT_SUPPORTED)
698 {
699 if (fReboot)
700 RTMsgError(ControlVM::tr("Current installed Guest Additions don't support rebooting the guest."));
701 else
702 RTMsgError(ControlVM::tr("Current installed Guest Additions don't support shutting down the guest."));
703 }
704 }
705 }
706#endif
707 else if (!strcmp(a->argv[1], "keyboardputscancode"))
708 {
709 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_KEYBOARDPUTSCANCODE);
710 ComPtr<IKeyboard> pKeyboard;
711 CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(pKeyboard.asOutParam()));
712 if (!pKeyboard)
713 {
714 RTMsgError(ControlVM::tr("Guest not running."));
715 hrc = E_FAIL;
716 break;
717 }
718
719 if (a->argc <= 1 + 1)
720 {
721 errorSyntax(ControlVM::tr("Missing argument to '%s'. Expected IBM PC AT set 2 keyboard scancode(s)."),
722 a->argv[1]);
723 hrc = E_FAIL;
724 break;
725 }
726
727 std::list<LONG> llScancodes;
728
729 /* Process the command line. */
730 int i;
731 for (i = 1 + 1; i < a->argc; i++)
732 {
733 if ( RT_C_IS_XDIGIT (a->argv[i][0])
734 && RT_C_IS_XDIGIT (a->argv[i][1])
735 && a->argv[i][2] == 0)
736 {
737 uint8_t u8Scancode;
738 int vrc = RTStrToUInt8Ex(a->argv[i], NULL, 16, &u8Scancode);
739 if (RT_FAILURE (vrc))
740 {
741 RTMsgError(ControlVM::tr("Converting '%s' returned %Rrc!"), a->argv[i], vrc);
742 hrc = E_FAIL;
743 break;
744 }
745
746 llScancodes.push_back(u8Scancode);
747 }
748 else
749 {
750 RTMsgError(ControlVM::tr("'%s' is not a hex byte!"), a->argv[i]);
751 hrc = E_FAIL;
752 break;
753 }
754 }
755
756 if (FAILED(hrc))
757 break;
758
759 hrc = keyboardPutScancodes(pKeyboard, llScancodes);
760 }
761 else if (!strcmp(a->argv[1], "keyboardputstring"))
762 {
763 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_KEYBOARDPUTSTRING);
764 ComPtr<IKeyboard> pKeyboard;
765 CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(pKeyboard.asOutParam()));
766 if (!pKeyboard)
767 {
768 RTMsgError(ControlVM::tr("Guest not running."));
769 hrc = E_FAIL;
770 break;
771 }
772
773 if (a->argc <= 1 + 1)
774 {
775 errorSyntax(ControlVM::tr("Missing argument to '%s'. Expected ASCII string(s)."), a->argv[1]);
776 hrc = E_FAIL;
777 break;
778 }
779
780 hrc = keyboardPutString(pKeyboard, a->argc, a->argv);
781 }
782 else if (!strcmp(a->argv[1], "keyboardputfile"))
783 {
784 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_KEYBOARDPUTFILE);
785 ComPtr<IKeyboard> pKeyboard;
786 CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(pKeyboard.asOutParam()));
787 if (!pKeyboard)
788 {
789 RTMsgError(ControlVM::tr("Guest not running."));
790 hrc = E_FAIL;
791 break;
792 }
793
794 if (a->argc <= 1 + 1)
795 {
796 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
797 hrc = E_FAIL;
798 break;
799 }
800
801 hrc = keyboardPutFile(pKeyboard, a->argv[2]);
802 }
803 else if (!strncmp(a->argv[1], "setlinkstate", 12))
804 {
805 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETLINKSTATE);
806 /* Get the number of network adapters */
807 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
808 unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
809 if (!n)
810 {
811 hrc = E_FAIL;
812 break;
813 }
814 if (a->argc <= 1 + 1)
815 {
816 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
817 hrc = E_FAIL;
818 break;
819 }
820 /* get the corresponding network adapter */
821 ComPtr<INetworkAdapter> adapter;
822 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
823 if (adapter)
824 {
825 bool fEnabled;
826 if (RT_FAILURE(parseBool(a->argv[2], &fEnabled)))
827 {
828 errorSyntax(ControlVM::tr("Invalid link state '%s'."), a->argv[2]);
829 hrc = E_FAIL;
830 break;
831 }
832 CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(fEnabled));
833 fNeedsSaving = true;
834 }
835 }
836 /* here the order in which strncmp is called is important
837 * cause nictracefile can be very well compared with
838 * nictrace and nic and thus everything will always fail
839 * if the order is changed
840 */
841 else if (!strncmp(a->argv[1], "nictracefile", 12))
842 {
843 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICTRACEFILE);
844 /* Get the number of network adapters */
845 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
846 unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
847 if (!n)
848 {
849 hrc = E_FAIL;
850 break;
851 }
852 if (a->argc <= 2)
853 {
854 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
855 hrc = E_FAIL;
856 break;
857 }
858
859 /* get the corresponding network adapter */
860 ComPtr<INetworkAdapter> adapter;
861 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
862 if (adapter)
863 {
864 BOOL fEnabled;
865 adapter->COMGETTER(Enabled)(&fEnabled);
866 if (fEnabled)
867 {
868 if (a->argv[2])
869 {
870 CHECK_ERROR_RET(adapter, COMSETTER(TraceFile)(Bstr(a->argv[2]).raw()), RTEXITCODE_FAILURE);
871 }
872 else
873 {
874 errorSyntax(ControlVM::tr("Filename not specified for NIC %lu."), n);
875 hrc = E_FAIL;
876 break;
877 }
878 if (SUCCEEDED(hrc))
879 fNeedsSaving = true;
880 }
881 else
882 RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its tracefile can't be changed."), n);
883 }
884 }
885 else if (!strncmp(a->argv[1], "nictrace", 8))
886 {
887 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICTRACE);
888 /* Get the number of network adapters */
889 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
890 unsigned n = parseNum(&a->argv[1][8], NetworkAdapterCount, "NIC");
891 if (!n)
892 {
893 hrc = E_FAIL;
894 break;
895 }
896 if (a->argc <= 2)
897 {
898 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
899 hrc = E_FAIL;
900 break;
901 }
902
903 /* get the corresponding network adapter */
904 ComPtr<INetworkAdapter> adapter;
905 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
906 if (adapter)
907 {
908 BOOL fEnabled;
909 adapter->COMGETTER(Enabled)(&fEnabled);
910 if (fEnabled)
911 {
912 bool fTraceEnabled;
913 if (RT_FAILURE(parseBool(a->argv[2], &fTraceEnabled)))
914 {
915 errorSyntax(ControlVM::tr("Invalid nictrace%lu argument '%s'."), n, a->argv[2]);
916 hrc = E_FAIL;
917 break;
918 }
919 CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(fTraceEnabled), RTEXITCODE_FAILURE);
920 fNeedsSaving = true;
921 }
922 else
923 RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its trace flag can't be changed."), n);
924 }
925 }
926 else if( a->argc > 2
927 && !strncmp(a->argv[1], "natpf", 5))
928 {
929 /* Get the number of network adapters */
930 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
931 unsigned n = parseNum(&a->argv[1][5], NetworkAdapterCount, "NIC");
932 if (!n)
933 {
934 hrc = E_FAIL;
935 break;
936 }
937 if (a->argc <= 2)
938 {
939 errorArgument(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
940 hrc = E_FAIL;
941 break;
942 }
943
944 /* get the corresponding network adapter */
945 ComPtr<INetworkAdapter> adapter;
946 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
947 if (!adapter)
948 {
949 hrc = E_FAIL;
950 break;
951 }
952 ComPtr<INATEngine> engine;
953 CHECK_ERROR(adapter, COMGETTER(NATEngine)(engine.asOutParam()));
954 if (!engine)
955 {
956 hrc = E_FAIL;
957 break;
958 }
959
960 if (!strcmp(a->argv[2], "delete"))
961 {
962 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NATPF_DELETE);
963 if (a->argc >= 3)
964 CHECK_ERROR(engine, RemoveRedirect(Bstr(a->argv[3]).raw()));
965 }
966 else
967 {
968 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NATPF);
969#define ITERATE_TO_NEXT_TERM(ch) \
970 do { \
971 while (*ch != ',') \
972 { \
973 if (*ch == 0) \
974 { \
975 return errorSyntax(ControlVM::tr("Missing or invalid argument to '%s'."), \
976 a->argv[1]); \
977 } \
978 ch++; \
979 } \
980 *ch = '\0'; \
981 ch++; \
982 } while(0)
983
984 char *strName;
985 char *strProto;
986 char *strHostIp;
987 char *strHostPort;
988 char *strGuestIp;
989 char *strGuestPort;
990 char *strRaw = RTStrDup(a->argv[2]);
991 char *ch = strRaw;
992 strName = RTStrStrip(ch);
993 ITERATE_TO_NEXT_TERM(ch);
994 strProto = RTStrStrip(ch);
995 ITERATE_TO_NEXT_TERM(ch);
996 strHostIp = RTStrStrip(ch);
997 ITERATE_TO_NEXT_TERM(ch);
998 strHostPort = RTStrStrip(ch);
999 ITERATE_TO_NEXT_TERM(ch);
1000 strGuestIp = RTStrStrip(ch);
1001 ITERATE_TO_NEXT_TERM(ch);
1002 strGuestPort = RTStrStrip(ch);
1003 NATProtocol_T proto;
1004 if (RTStrICmp(strProto, "udp") == 0)
1005 proto = NATProtocol_UDP;
1006 else if (RTStrICmp(strProto, "tcp") == 0)
1007 proto = NATProtocol_TCP;
1008 else
1009 {
1010 return errorSyntax(ControlVM::tr("Wrong rule proto '%s' specified -- only 'udp' and 'tcp' are allowed."),
1011 strProto);
1012 }
1013 CHECK_ERROR(engine, AddRedirect(Bstr(strName).raw(), proto, Bstr(strHostIp).raw(),
1014 RTStrToUInt16(strHostPort), Bstr(strGuestIp).raw(), RTStrToUInt16(strGuestPort)));
1015#undef ITERATE_TO_NEXT_TERM
1016 }
1017 if (SUCCEEDED(hrc))
1018 fNeedsSaving = true;
1019 }
1020 else if (!strncmp(a->argv[1], "nicproperty", 11))
1021 {
1022 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICPROPERTY);
1023 /* Get the number of network adapters */
1024 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
1025 unsigned n = parseNum(&a->argv[1][11], NetworkAdapterCount, "NIC");
1026 if (!n)
1027 {
1028 hrc = E_FAIL;
1029 break;
1030 }
1031 if (a->argc <= 2)
1032 {
1033 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1034 hrc = E_FAIL;
1035 break;
1036 }
1037
1038 /* get the corresponding network adapter */
1039 ComPtr<INetworkAdapter> adapter;
1040 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
1041 if (adapter)
1042 {
1043 BOOL fEnabled;
1044 adapter->COMGETTER(Enabled)(&fEnabled);
1045 if (fEnabled)
1046 {
1047 /* Parse 'name=value' */
1048 char *pszProperty = RTStrDup(a->argv[2]);
1049 if (pszProperty)
1050 {
1051 char *pDelimiter = strchr(pszProperty, '=');
1052 if (pDelimiter)
1053 {
1054 *pDelimiter = '\0';
1055
1056 Bstr bstrName = pszProperty;
1057 Bstr bstrValue = &pDelimiter[1];
1058 CHECK_ERROR(adapter, SetProperty(bstrName.raw(), bstrValue.raw()));
1059 if (SUCCEEDED(hrc))
1060 fNeedsSaving = true;
1061 }
1062 else
1063 {
1064 errorSyntax(ControlVM::tr("Invalid nicproperty%d argument '%s'."), n, a->argv[2]);
1065 hrc = E_FAIL;
1066 }
1067 RTStrFree(pszProperty);
1068 }
1069 else
1070 {
1071 RTMsgError(ControlVM::tr("Failed to allocate memory for nicproperty%d '%s'."),
1072 n, a->argv[2]);
1073 hrc = E_FAIL;
1074 }
1075 if (FAILED(hrc))
1076 break;
1077 }
1078 else
1079 RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its properties can't be changed."), n);
1080 }
1081 }
1082 else if (!strncmp(a->argv[1], "nicpromisc", 10))
1083 {
1084 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICPROMISC);
1085 /* Get the number of network adapters */
1086 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
1087 unsigned n = parseNum(&a->argv[1][10], NetworkAdapterCount, "NIC");
1088 if (!n)
1089 {
1090 hrc = E_FAIL;
1091 break;
1092 }
1093 if (a->argc <= 2)
1094 {
1095 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1096 hrc = E_FAIL;
1097 break;
1098 }
1099
1100 /* get the corresponding network adapter */
1101 ComPtr<INetworkAdapter> adapter;
1102 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
1103 if (adapter)
1104 {
1105 BOOL fEnabled;
1106 adapter->COMGETTER(Enabled)(&fEnabled);
1107 if (fEnabled)
1108 {
1109 NetworkAdapterPromiscModePolicy_T enmPromiscModePolicy;
1110 if (!strcmp(a->argv[2], "deny"))
1111 enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny;
1112 else if ( !strcmp(a->argv[2], "allow-vms")
1113 || !strcmp(a->argv[2], "allow-network"))
1114 enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork;
1115 else if (!strcmp(a->argv[2], "allow-all"))
1116 enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll;
1117 else
1118 {
1119 errorSyntax(ControlVM::tr("Unknown promiscuous mode policy '%s'."), a->argv[2]);
1120 hrc = E_INVALIDARG;
1121 break;
1122 }
1123
1124 CHECK_ERROR(adapter, COMSETTER(PromiscModePolicy)(enmPromiscModePolicy));
1125 if (SUCCEEDED(hrc))
1126 fNeedsSaving = true;
1127 }
1128 else
1129 RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its promiscuous mode can't be changed."), n);
1130 }
1131 }
1132 else if (!strncmp(a->argv[1], "nic", 3))
1133 {
1134 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NIC);
1135 /* Get the number of network adapters */
1136 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
1137 unsigned n = parseNum(&a->argv[1][3], NetworkAdapterCount, "NIC");
1138 if (!n)
1139 {
1140 hrc = E_FAIL;
1141 break;
1142 }
1143 if (a->argc <= 2)
1144 {
1145 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1146 hrc = E_FAIL;
1147 break;
1148 }
1149
1150 /* get the corresponding network adapter */
1151 ComPtr<INetworkAdapter> adapter;
1152 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
1153 if (adapter)
1154 {
1155 BOOL fEnabled;
1156 adapter->COMGETTER(Enabled)(&fEnabled);
1157 if (fEnabled)
1158 {
1159 if (!strcmp(a->argv[2], "null"))
1160 {
1161 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Null), RTEXITCODE_FAILURE);
1162 }
1163 else if (!strcmp(a->argv[2], "nat"))
1164 {
1165 if (a->argc == 4)
1166 CHECK_ERROR_RET(adapter, COMSETTER(NATNetwork)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1167 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_NAT), RTEXITCODE_FAILURE);
1168 }
1169 else if ( !strcmp(a->argv[2], "bridged")
1170 || !strcmp(a->argv[2], "hostif")) /* backward compatibility */
1171 {
1172 if (a->argc <= 3)
1173 {
1174 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]);
1175 hrc = E_FAIL;
1176 break;
1177 }
1178 CHECK_ERROR_RET(adapter, COMSETTER(BridgedInterface)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1179 verifyHostNetworkInterfaceName(a->virtualBox, a->argv[3], HostNetworkInterfaceType_Bridged);
1180 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged), RTEXITCODE_FAILURE);
1181 }
1182 else if (!strcmp(a->argv[2], "intnet"))
1183 {
1184 if (a->argc <= 3)
1185 {
1186 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]);
1187 hrc = E_FAIL;
1188 break;
1189 }
1190 CHECK_ERROR_RET(adapter, COMSETTER(InternalNetwork)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1191 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Internal), RTEXITCODE_FAILURE);
1192 }
1193#if defined(VBOX_WITH_NETFLT)
1194 else if (!strcmp(a->argv[2], "hostonly"))
1195 {
1196 if (a->argc <= 3)
1197 {
1198 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]);
1199 hrc = E_FAIL;
1200 break;
1201 }
1202 CHECK_ERROR_RET(adapter, COMSETTER(HostOnlyInterface)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1203 verifyHostNetworkInterfaceName(a->virtualBox, a->argv[3], HostNetworkInterfaceType_HostOnly);
1204 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly), RTEXITCODE_FAILURE);
1205 }
1206#endif
1207 else if (!strcmp(a->argv[2], "generic"))
1208 {
1209 if (a->argc <= 3)
1210 {
1211 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]);
1212 hrc = E_FAIL;
1213 break;
1214 }
1215 CHECK_ERROR_RET(adapter, COMSETTER(GenericDriver)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1216 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), RTEXITCODE_FAILURE);
1217 }
1218 else if (!strcmp(a->argv[2], "natnetwork"))
1219 {
1220 if (a->argc <= 3)
1221 {
1222 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]);
1223 hrc = E_FAIL;
1224 break;
1225 }
1226 CHECK_ERROR_RET(adapter, COMSETTER(NATNetwork)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1227 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_NATNetwork), RTEXITCODE_FAILURE);
1228 }
1229 else
1230 {
1231 errorSyntax(ControlVM::tr("Invalid type '%s' specfied for NIC %lu."), a->argv[2], n);
1232 hrc = E_FAIL;
1233 break;
1234 }
1235 if (SUCCEEDED(hrc))
1236 fNeedsSaving = true;
1237 }
1238 else
1239 RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its attachment type can't be changed."), n);
1240 }
1241 }
1242 else if ( !strcmp(a->argv[1], "vrde")
1243 || !strcmp(a->argv[1], "vrdp"))
1244 {
1245 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDE);
1246 if (!strcmp(a->argv[1], "vrdp"))
1247 RTMsgWarning(ControlVM::tr("'vrdp' is deprecated. Use 'vrde'."));
1248
1249 if (a->argc <= 1 + 1)
1250 {
1251 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1252 hrc = E_FAIL;
1253 break;
1254 }
1255 ComPtr<IVRDEServer> vrdeServer;
1256 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
1257 ASSERT(vrdeServer);
1258 if (vrdeServer)
1259 {
1260 bool fEnabled;
1261 if (RT_FAILURE(parseBool(a->argv[2], &fEnabled)))
1262 {
1263 errorSyntax(ControlVM::tr("Invalid remote desktop server state '%s'."), a->argv[2]);
1264 hrc = E_FAIL;
1265 break;
1266 }
1267 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(fEnabled));
1268 fNeedsSaving = true;
1269 }
1270 }
1271 else if ( !strcmp(a->argv[1], "vrdeport")
1272 || !strcmp(a->argv[1], "vrdpport"))
1273 {
1274 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDEPORT);
1275 if (!strcmp(a->argv[1], "vrdpport"))
1276 RTMsgWarning(ControlVM::tr("'vrdpport' is deprecated. Use 'vrdeport'."));
1277
1278 if (a->argc <= 1 + 1)
1279 {
1280 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1281 hrc = E_FAIL;
1282 break;
1283 }
1284
1285 ComPtr<IVRDEServer> vrdeServer;
1286 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
1287 ASSERT(vrdeServer);
1288 if (vrdeServer)
1289 {
1290 Bstr ports;
1291
1292 if (!strcmp(a->argv[2], "default"))
1293 ports = "0";
1294 else
1295 ports = a->argv[2];
1296
1297 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), ports.raw()));
1298 if (SUCCEEDED(hrc))
1299 fNeedsSaving = true;
1300 }
1301 }
1302 else if ( !strcmp(a->argv[1], "vrdevideochannelquality")
1303 || !strcmp(a->argv[1], "vrdpvideochannelquality"))
1304 {
1305 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDEVIDEOCHANNELQUALITY);
1306 if (!strcmp(a->argv[1], "vrdpvideochannelquality"))
1307 RTMsgWarning(ControlVM::tr("'vrdpvideochannelquality' is deprecated. Use 'vrdevideochannelquality'."));
1308
1309 if (a->argc <= 1 + 1)
1310 {
1311 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1312 hrc = E_FAIL;
1313 break;
1314 }
1315 ComPtr<IVRDEServer> vrdeServer;
1316 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
1317 ASSERT(vrdeServer);
1318 if (vrdeServer)
1319 {
1320 Bstr value = a->argv[2];
1321
1322 CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("VideoChannel/Quality").raw(), value.raw()));
1323 if (SUCCEEDED(hrc))
1324 fNeedsSaving = true;
1325 }
1326 }
1327 else if (!strcmp(a->argv[1], "vrdeproperty"))
1328 {
1329 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDEPROPERTY);
1330 if (a->argc <= 1 + 1)
1331 {
1332 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1333 hrc = E_FAIL;
1334 break;
1335 }
1336 ComPtr<IVRDEServer> vrdeServer;
1337 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
1338 ASSERT(vrdeServer);
1339 if (vrdeServer)
1340 {
1341 /* Parse 'name=value' */
1342 char *pszProperty = RTStrDup(a->argv[2]);
1343 if (pszProperty)
1344 {
1345 char *pDelimiter = strchr(pszProperty, '=');
1346 if (pDelimiter)
1347 {
1348 *pDelimiter = '\0';
1349
1350 Bstr bstrName = pszProperty;
1351 Bstr bstrValue = &pDelimiter[1];
1352 CHECK_ERROR(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
1353 if (SUCCEEDED(hrc))
1354 fNeedsSaving = true;
1355 }
1356 else
1357 {
1358 errorSyntax(ControlVM::tr("Invalid vrdeproperty argument '%s'."), a->argv[2]);
1359 hrc = E_FAIL;
1360 }
1361 RTStrFree(pszProperty);
1362 }
1363 else
1364 {
1365 RTMsgError(ControlVM::tr("Failed to allocate memory for VRDE property '%s'."),
1366 a->argv[2]);
1367 hrc = E_FAIL;
1368 }
1369 }
1370 if (FAILED(hrc))
1371 {
1372 break;
1373 }
1374 }
1375 else if ( !strcmp(a->argv[1], "usbattach")
1376 || !strcmp(a->argv[1], "usbdetach"))
1377 {
1378 bool attach = !strcmp(a->argv[1], "usbattach");
1379 if (attach)
1380 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_USBATTACH);
1381 else
1382 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_USBDETACH);
1383
1384 if (a->argc < 3)
1385 {
1386 errorSyntax(ControlVM::tr("Not enough parameters."));
1387 hrc = E_FAIL;
1388 break;
1389 }
1390 else if (a->argc == 4 || a->argc > 5)
1391 {
1392 errorSyntax(ControlVM::tr("Wrong number of arguments."));
1393 hrc = E_FAIL;
1394 break;
1395 }
1396
1397 Bstr usbId = a->argv[2];
1398 Bstr captureFilename;
1399
1400 if (a->argc == 5)
1401 {
1402 if (!strcmp(a->argv[3], "--capturefile"))
1403 captureFilename = a->argv[4];
1404 else
1405 {
1406 errorSyntax(ControlVM::tr("Invalid parameter '%s'."), a->argv[3]);
1407 hrc = E_FAIL;
1408 break;
1409 }
1410 }
1411
1412 Guid guid(usbId);
1413 if (!guid.isValid())
1414 {
1415 // assume address
1416 if (attach)
1417 {
1418 ComPtr<IHost> host;
1419 CHECK_ERROR_BREAK(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
1420 SafeIfaceArray <IHostUSBDevice> coll;
1421 CHECK_ERROR_BREAK(host, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
1422 ComPtr<IHostUSBDevice> dev;
1423 CHECK_ERROR_BREAK(host, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
1424 dev.asOutParam()));
1425 CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
1426 }
1427 else
1428 {
1429 SafeIfaceArray <IUSBDevice> coll;
1430 CHECK_ERROR_BREAK(console, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
1431 ComPtr<IUSBDevice> dev;
1432 CHECK_ERROR_BREAK(console, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
1433 dev.asOutParam()));
1434 CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
1435 }
1436 }
1437 else if (guid.isZero())
1438 {
1439 errorSyntax(ControlVM::tr("Zero UUID argument '%s'."), a->argv[2]);
1440 hrc = E_FAIL;
1441 break;
1442 }
1443
1444 if (attach)
1445 CHECK_ERROR_BREAK(console, AttachUSBDevice(usbId.raw(), captureFilename.raw()));
1446 else
1447 {
1448 ComPtr<IUSBDevice> dev;
1449 CHECK_ERROR_BREAK(console, DetachUSBDevice(usbId.raw(),
1450 dev.asOutParam()));
1451 }
1452 }
1453 else if (!strcmp(a->argv[1], "setvideomodehint"))
1454 {
1455 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETVIDEOMODEHINT);
1456 if (a->argc != 5 && a->argc != 6 && a->argc != 7 && a->argc != 9)
1457 {
1458 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1459 hrc = E_FAIL;
1460 break;
1461 }
1462 bool fEnabled = true;
1463 uint32_t uXRes = RTStrToUInt32(a->argv[2]);
1464 uint32_t uYRes = RTStrToUInt32(a->argv[3]);
1465 uint32_t uBpp = RTStrToUInt32(a->argv[4]);
1466 uint32_t uDisplayIdx = 0;
1467 bool fChangeOrigin = false;
1468 int32_t iOriginX = 0;
1469 int32_t iOriginY = 0;
1470 if (a->argc >= 6)
1471 uDisplayIdx = RTStrToUInt32(a->argv[5]);
1472 if (a->argc >= 7)
1473 {
1474 if (RT_FAILURE(parseBool(a->argv[6], &fEnabled)))
1475 {
1476 errorSyntax(ControlVM::tr("Either \"yes\" or \"no\" is expected."));
1477 hrc = E_FAIL;
1478 break;
1479 }
1480 }
1481 if (a->argc == 9)
1482 {
1483 iOriginX = RTStrToInt32(a->argv[7]);
1484 iOriginY = RTStrToInt32(a->argv[8]);
1485 fChangeOrigin = true;
1486 }
1487
1488 ComPtr<IDisplay> pDisplay;
1489 CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
1490 if (!pDisplay)
1491 {
1492 RTMsgError(ControlVM::tr("Guest not running."));
1493 hrc = E_FAIL;
1494 break;
1495 }
1496 CHECK_ERROR_BREAK(pDisplay, SetVideoModeHint(uDisplayIdx, fEnabled,
1497 fChangeOrigin, iOriginX, iOriginY,
1498 uXRes, uYRes, uBpp, true));
1499 }
1500 else if (!strcmp(a->argv[1], "setscreenlayout"))
1501 {
1502 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETSCREENLAYOUT);
1503 if (a->argc < 4)
1504 {
1505 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1506 hrc = E_FAIL;
1507 break;
1508 }
1509
1510 ComPtr<IDisplay> pDisplay;
1511 CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
1512 if (!pDisplay)
1513 {
1514 RTMsgError(ControlVM::tr("Guest not running."));
1515 hrc = E_FAIL;
1516 break;
1517 }
1518
1519 com::SafeIfaceArray<IGuestScreenInfo> aGuestScreenInfos;
1520
1521 /* Parse "<display> on|primary <xorigin> <yorigin> <xres> <yres> <bpp> | off" sequences. */
1522 int argc = a->argc - 2;
1523 char **argv = &a->argv[2];
1524 while (argc >= 2)
1525 {
1526 ULONG aDisplay = RTStrToUInt32(argv[0]);
1527 BOOL aPrimary = FALSE;
1528
1529 GuestMonitorStatus_T aStatus;
1530 if (RTStrICmp(argv[1], "primary") == 0)
1531 {
1532 aStatus = GuestMonitorStatus_Enabled;
1533 aPrimary = TRUE;
1534 }
1535 else if (RTStrICmp(argv[1], "on") == 0)
1536 aStatus = GuestMonitorStatus_Enabled;
1537 else if (RTStrICmp(argv[1], "off") == 0)
1538 aStatus = GuestMonitorStatus_Disabled;
1539 else
1540 {
1541 errorSyntax(ControlVM::tr("Display status must be <on> or <off>."));
1542 hrc = E_FAIL;
1543 break;
1544 }
1545
1546 BOOL aChangeOrigin = FALSE;
1547 LONG aOriginX = 0;
1548 LONG aOriginY = 0;
1549 ULONG aWidth = 0;
1550 ULONG aHeight = 0;
1551 ULONG aBitsPerPixel = 0;
1552 if (aStatus == GuestMonitorStatus_Enabled)
1553 {
1554 if (argc < 7)
1555 {
1556 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1557 hrc = E_FAIL;
1558 break;
1559 }
1560
1561 aChangeOrigin = TRUE;
1562 aOriginX = RTStrToUInt32(argv[2]);
1563 aOriginY = RTStrToUInt32(argv[3]);
1564 aWidth = RTStrToUInt32(argv[4]);
1565 aHeight = RTStrToUInt32(argv[5]);
1566 aBitsPerPixel = RTStrToUInt32(argv[6]);
1567
1568 argc -= 7;
1569 argv += 7;
1570 }
1571 else
1572 {
1573 argc -= 2;
1574 argv += 2;
1575 }
1576
1577 ComPtr<IGuestScreenInfo> pInfo;
1578 CHECK_ERROR_BREAK(pDisplay, CreateGuestScreenInfo(aDisplay, aStatus, aPrimary, aChangeOrigin,
1579 aOriginX, aOriginY, aWidth, aHeight, aBitsPerPixel,
1580 pInfo.asOutParam()));
1581 aGuestScreenInfos.push_back(pInfo);
1582 }
1583
1584 if (FAILED(hrc))
1585 break;
1586
1587 CHECK_ERROR_BREAK(pDisplay, SetScreenLayout(ScreenLayoutMode_Apply, ComSafeArrayAsInParam(aGuestScreenInfos)));
1588 }
1589 else if (!strcmp(a->argv[1], "setcredentials"))
1590 {
1591 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETCREDENTIALS);
1592 bool fAllowLocalLogon = true;
1593 if ( a->argc == 7
1594 || ( a->argc == 8
1595 && ( !strcmp(a->argv[3], "-p")
1596 || !strcmp(a->argv[3], "--passwordfile"))))
1597 {
1598 if ( strcmp(a->argv[5 + (a->argc - 7)], "--allowlocallogon")
1599 && strcmp(a->argv[5 + (a->argc - 7)], "-allowlocallogon"))
1600 {
1601 errorSyntax(ControlVM::tr("Invalid parameter '%s'."), a->argv[5]);
1602 hrc = E_FAIL;
1603 break;
1604 }
1605 if (!strcmp(a->argv[6 + (a->argc - 7)], "no"))
1606 fAllowLocalLogon = false;
1607 }
1608 else if ( a->argc != 5
1609 && ( a->argc != 6
1610 || ( strcmp(a->argv[3], "-p")
1611 && strcmp(a->argv[3], "--passwordfile"))))
1612 {
1613 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1614 hrc = E_FAIL;
1615 break;
1616 }
1617 Utf8Str passwd, domain;
1618 if (a->argc == 5 || a->argc == 7)
1619 {
1620 passwd = a->argv[3];
1621 domain = a->argv[4];
1622 }
1623 else
1624 {
1625 RTEXITCODE rcExit = readPasswordFile(a->argv[4], &passwd);
1626 if (rcExit != RTEXITCODE_SUCCESS)
1627 {
1628 hrc = E_FAIL;
1629 break;
1630 }
1631 domain = a->argv[5];
1632 }
1633
1634 ComPtr<IGuest> pGuest;
1635 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pGuest.asOutParam()));
1636 if (!pGuest)
1637 {
1638 RTMsgError(ControlVM::tr("Guest not running."));
1639 hrc = E_FAIL;
1640 break;
1641 }
1642 CHECK_ERROR_BREAK(pGuest, SetCredentials(Bstr(a->argv[2]).raw(),
1643 Bstr(passwd).raw(),
1644 Bstr(domain).raw(),
1645 fAllowLocalLogon));
1646 }
1647 else if (!strcmp(a->argv[1], "guestmemoryballoon"))
1648 {
1649 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_GUESTMEMORYBALLOON);
1650 if (a->argc != 3)
1651 {
1652 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1653 hrc = E_FAIL;
1654 break;
1655 }
1656 uint32_t uVal;
1657 int vrc;
1658 vrc = RTStrToUInt32Ex(a->argv[2], NULL, 0, &uVal);
1659 if (vrc != VINF_SUCCESS)
1660 {
1661 errorSyntax(ControlVM::tr("Error parsing guest memory balloon size '%s'."), a->argv[2]);
1662 hrc = E_FAIL;
1663 break;
1664 }
1665 /* guest is running; update IGuest */
1666 ComPtr<IGuest> pGuest;
1667 hrc = console->COMGETTER(Guest)(pGuest.asOutParam());
1668 if (SUCCEEDED(hrc))
1669 {
1670 if (!pGuest)
1671 {
1672 RTMsgError(ControlVM::tr("Guest not running."));
1673 hrc = E_FAIL;
1674 break;
1675 }
1676 CHECK_ERROR(pGuest, COMSETTER(MemoryBalloonSize)(uVal));
1677 }
1678 }
1679 else if (!strcmp(a->argv[1], "teleport"))
1680 {
1681 Bstr bstrHostname;
1682 uint32_t uMaxDowntime = 250 /*ms*/;
1683 uint32_t uPort = UINT32_MAX;
1684 uint32_t cMsTimeout = 0;
1685 Utf8Str strPassword;
1686 static const RTGETOPTDEF s_aTeleportOptions[] =
1687 {
1688 { "--host", 'h', RTGETOPT_REQ_STRING }, /** @todo RTGETOPT_FLAG_MANDATORY */
1689 { "--maxdowntime", 'd', RTGETOPT_REQ_UINT32 },
1690 { "--port", 'P', RTGETOPT_REQ_UINT32 }, /** @todo RTGETOPT_FLAG_MANDATORY */
1691 { "--passwordfile", 'p', RTGETOPT_REQ_STRING },
1692 { "--password", 'W', RTGETOPT_REQ_STRING },
1693 { "--timeout", 't', RTGETOPT_REQ_UINT32 },
1694 { "--detailed-progress", 'D', RTGETOPT_REQ_NOTHING }
1695 };
1696 RTGETOPTSTATE GetOptState;
1697 RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTeleportOptions, RT_ELEMENTS(s_aTeleportOptions), 2, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1698 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_TELEPORT);
1699 int ch;
1700 RTGETOPTUNION Value;
1701 while ( SUCCEEDED(hrc)
1702 && (ch = RTGetOpt(&GetOptState, &Value)))
1703 {
1704 switch (ch)
1705 {
1706 case 'h': bstrHostname = Value.psz; break;
1707 case 'd': uMaxDowntime = Value.u32; break;
1708 case 'D': g_fDetailedProgress = true; break;
1709 case 'P': uPort = Value.u32; break;
1710 case 'p':
1711 {
1712 RTEXITCODE rcExit = readPasswordFile(Value.psz, &strPassword);
1713 if (rcExit != RTEXITCODE_SUCCESS)
1714 hrc = E_FAIL;
1715 break;
1716 }
1717 case 'W': strPassword = Value.psz; break;
1718 case 't': cMsTimeout = Value.u32; break;
1719 default:
1720 errorGetOpt(ch, &Value);
1721 hrc = E_FAIL;
1722 break;
1723 }
1724 }
1725 if (FAILED(hrc))
1726 break;
1727
1728 ComPtr<IProgress> progress;
1729 CHECK_ERROR_BREAK(console, Teleport(bstrHostname.raw(), uPort,
1730 Bstr(strPassword).raw(),
1731 uMaxDowntime,
1732 progress.asOutParam()));
1733
1734 if (cMsTimeout)
1735 {
1736 hrc = progress->COMSETTER(Timeout)(cMsTimeout);
1737 if (FAILED(hrc) && hrc != VBOX_E_INVALID_OBJECT_STATE)
1738 CHECK_ERROR_BREAK(progress, COMSETTER(Timeout)(cMsTimeout)); /* lazyness */
1739 }
1740
1741 hrc = showProgress(progress);
1742 CHECK_PROGRESS_ERROR(progress, (ControlVM::tr("Teleportation failed")));
1743 }
1744 else if (!strcmp(a->argv[1], "screenshotpng"))
1745 {
1746 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SCREENSHOTPNG);
1747 if (a->argc <= 2 || a->argc > 4)
1748 {
1749 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1750 hrc = E_FAIL;
1751 break;
1752 }
1753 int vrc;
1754 uint32_t iScreen = 0;
1755 if (a->argc == 4)
1756 {
1757 vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &iScreen);
1758 if (vrc != VINF_SUCCESS)
1759 {
1760 errorSyntax(ControlVM::tr("Error parsing display number '%s'."), a->argv[3]);
1761 hrc = E_FAIL;
1762 break;
1763 }
1764 }
1765 ComPtr<IDisplay> pDisplay;
1766 CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
1767 if (!pDisplay)
1768 {
1769 RTMsgError(ControlVM::tr("Guest not running."));
1770 hrc = E_FAIL;
1771 break;
1772 }
1773 ULONG width, height, bpp;
1774 LONG xOrigin, yOrigin;
1775 GuestMonitorStatus_T monitorStatus;
1776 CHECK_ERROR_BREAK(pDisplay, GetScreenResolution(iScreen, &width, &height, &bpp, &xOrigin, &yOrigin, &monitorStatus));
1777 com::SafeArray<BYTE> saScreenshot;
1778 CHECK_ERROR_BREAK(pDisplay, TakeScreenShotToArray(iScreen, width, height, BitmapFormat_PNG, ComSafeArrayAsOutParam(saScreenshot)));
1779 RTFILE pngFile = NIL_RTFILE;
1780 vrc = RTFileOpen(&pngFile, a->argv[2], RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_ALL);
1781 if (RT_FAILURE(vrc))
1782 {
1783 RTMsgError(ControlVM::tr("Failed to create file '%s' (%Rrc)."), a->argv[2], vrc);
1784 hrc = E_FAIL;
1785 break;
1786 }
1787 vrc = RTFileWrite(pngFile, saScreenshot.raw(), saScreenshot.size(), NULL);
1788 if (RT_FAILURE(vrc))
1789 {
1790 RTMsgError(ControlVM::tr("Failed to write screenshot to file '%s' (%Rrc)."), a->argv[2], vrc);
1791 hrc = E_FAIL;
1792 }
1793 RTFileClose(pngFile);
1794 }
1795#ifdef VBOX_WITH_RECORDING
1796 else if ( !strcmp(a->argv[1], "recording")
1797 || !strcmp(a->argv[1], "videocap") /* legacy command */)
1798 {
1799 if (!strcmp(a->argv[1], "videocap"))
1800 RTMsgWarning(ControlVM::tr("Sub command 'videocap' is deprecated -- please use 'recording' instead ."));
1801
1802 if (a->argc < 3)
1803 {
1804 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1805 hrc = E_FAIL;
1806 break;
1807 }
1808
1809 ComPtr<IRecordingSettings> recordingSettings;
1810 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()));
1811
1812 SafeIfaceArray <IRecordingScreenSettings> saRecordingScreenScreens;
1813 CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saRecordingScreenScreens)));
1814
1815 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
1816 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam()));
1817
1818 /* Note: For now all screens have the same configuration. */
1819
1820 bool fEnabled;
1821 if (RT_SUCCESS(parseBool(a->argv[2], &fEnabled)))
1822 {
1823 //setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING);
1824 CHECK_ERROR_RET(recordingSettings, COMSETTER(Enabled)(fEnabled), RTEXITCODE_FAILURE);
1825
1826 if (fEnabled)
1827 RTPrintf(ControlVM::tr("Recording enabled. Use 'start' to start recording.\n"));
1828 }
1829 else if (!strcmp(a->argv[2], "start"))
1830 {
1831 //setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_START);
1832 bool fWait = false;
1833 if (a->argc >= 4 && !strcmp(a->argv[3], "--wait"))
1834 fWait = true;
1835
1836 ComPtr<IProgress> progress;
1837 CHECK_ERROR_BREAK(recordingSettings, Start(progress.asOutParam()));
1838
1839 if (fWait)
1840 {
1841 hrc = showProgress(progress, SHOW_PROGRESS_OPS);
1842 CHECK_PROGRESS_ERROR(progress, (ControlVM::tr("Recording failed.")));
1843 }
1844 else
1845 RTPrintf(ControlVM::tr("Recording started (detacted).\n"));
1846 }
1847 else if (!strcmp(a->argv[2], "stop"))
1848 {
1849 //setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_STOP);
1850 ComPtr<IProgress> progress;
1851 CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Progress)(progress.asOutParam()));
1852 CHECK_ERROR_BREAK(progress, Cancel());
1853 }
1854 else if (!strcmp(a->argv[2], "attach"))
1855 {
1856 //setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_ATTACH);
1857 ComPtr<IProgress> progress;
1858 CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Progress)(progress.asOutParam()));
1859 hrc = showProgress(progress, SHOW_PROGRESS_OPS);
1860 }
1861 else if (!strcmp(a->argv[2], "screens"))
1862 {
1863 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_SCREENS);
1864 ULONG cMonitors = 64;
1865 CHECK_ERROR_BREAK(pGraphicsAdapter, COMGETTER(MonitorCount)(&cMonitors));
1866 com::SafeArray<BOOL> saScreens(cMonitors);
1867 if (a->argc != 4)
1868 {
1869 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1870 hrc = E_FAIL;
1871 break;
1872 }
1873 if (RT_FAILURE(parseScreens(a->argv[3], &saScreens)))
1874 {
1875 errorSyntax(ControlVM::tr("Error parsing list of screen IDs '%s'."), a->argv[3]);
1876 hrc = E_FAIL;
1877 break;
1878 }
1879
1880 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1881 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Enabled)(saScreens[i]));
1882 }
1883 else if (!strcmp(a->argv[2], "filename"))
1884 {
1885 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_FILENAME);
1886 if (a->argc != 4)
1887 {
1888 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1889 hrc = E_FAIL;
1890 break;
1891 }
1892
1893 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1894 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Filename)(Bstr(a->argv[3]).raw()));
1895 }
1896 else if ( !strcmp(a->argv[2], "videores")
1897 || !strcmp(a->argv[2], "videoresolution"))
1898 {
1899 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_VIDEORES);
1900 if (a->argc != 5)
1901 {
1902 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1903 hrc = E_FAIL;
1904 break;
1905 }
1906
1907 uint32_t uWidth;
1908 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uWidth);
1909 if (RT_FAILURE(vrc))
1910 {
1911 errorSyntax(ControlVM::tr("Error parsing video width '%s'."), a->argv[3]);
1912 hrc = E_FAIL;
1913 break;
1914 }
1915
1916 uint32_t uHeight;
1917 vrc = RTStrToUInt32Ex(a->argv[4], NULL, 0, &uHeight);
1918 if (RT_FAILURE(vrc))
1919 {
1920 errorSyntax(ControlVM::tr("Error parsing video height '%s'."), a->argv[4]);
1921 hrc = E_FAIL;
1922 break;
1923 }
1924
1925 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1926 {
1927 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoWidth)(uWidth));
1928 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoHeight)(uHeight));
1929 }
1930 }
1931 else if (!strcmp(a->argv[2], "videorate"))
1932 {
1933 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_VIDEORATE);
1934 if (a->argc != 4)
1935 {
1936 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1937 hrc = E_FAIL;
1938 break;
1939 }
1940
1941 uint32_t uRate;
1942 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uRate);
1943 if (RT_FAILURE(vrc))
1944 {
1945 errorSyntax(ControlVM::tr("Error parsing video rate '%s'."), a->argv[3]);
1946 hrc = E_FAIL;
1947 break;
1948 }
1949
1950 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1951 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoRate)(uRate));
1952 }
1953 else if (!strcmp(a->argv[2], "videofps"))
1954 {
1955 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_VIDEOFPS);
1956 if (a->argc != 4)
1957 {
1958 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1959 hrc = E_FAIL;
1960 break;
1961 }
1962
1963 uint32_t uFPS;
1964 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uFPS);
1965 if (RT_FAILURE(vrc))
1966 {
1967 errorSyntax(ControlVM::tr("Error parsing video FPS '%s'."), a->argv[3]);
1968 hrc = E_FAIL;
1969 break;
1970 }
1971
1972 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1973 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoFPS)(uFPS));
1974 }
1975 else if (!strcmp(a->argv[2], "maxtime"))
1976 {
1977 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_MAXTIME);
1978 if (a->argc != 4)
1979 {
1980 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1981 hrc = E_FAIL;
1982 break;
1983 }
1984
1985 uint32_t uMaxTime;
1986 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxTime);
1987 if (RT_FAILURE(vrc))
1988 {
1989 errorSyntax(ControlVM::tr("Error parsing maximum time '%s'."), a->argv[3]);
1990 hrc = E_FAIL;
1991 break;
1992 }
1993
1994 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1995 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(MaxTime)(uMaxTime));
1996 }
1997 else if (!strcmp(a->argv[2], "maxfilesize"))
1998 {
1999 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_MAXFILESIZE);
2000 if (a->argc != 4)
2001 {
2002 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
2003 hrc = E_FAIL;
2004 break;
2005 }
2006
2007 uint32_t uMaxFileSize;
2008 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxFileSize);
2009 if (RT_FAILURE(vrc))
2010 {
2011 errorSyntax(ControlVM::tr("Error parsing maximum file size '%s'."), a->argv[3]);
2012 hrc = E_FAIL;
2013 break;
2014 }
2015
2016 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
2017 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(MaxFileSize)(uMaxFileSize));
2018 }
2019 else if (!strcmp(a->argv[2], "opts"))
2020 {
2021#if 0 /* Add when the corresponding documentation is enabled. */
2022 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_OPTS);
2023#endif
2024 if (a->argc != 4)
2025 {
2026 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
2027 hrc = E_FAIL;
2028 break;
2029 }
2030
2031 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
2032 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Options)(Bstr(a->argv[3]).raw()));
2033 }
2034 }
2035#endif /* VBOX_WITH_RECORDING */
2036 else if (!strcmp(a->argv[1], "webcam"))
2037 {
2038 if (a->argc < 3)
2039 {
2040 errorArgument(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
2041 hrc = E_FAIL;
2042 break;
2043 }
2044
2045 ComPtr<IEmulatedUSB> pEmulatedUSB;
2046 CHECK_ERROR_BREAK(console, COMGETTER(EmulatedUSB)(pEmulatedUSB.asOutParam()));
2047 if (!pEmulatedUSB)
2048 {
2049 RTMsgError(ControlVM::tr("Guest not running."));
2050 hrc = E_FAIL;
2051 break;
2052 }
2053
2054 if (!strcmp(a->argv[2], "attach"))
2055 {
2056 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_ATTACH);
2057 Bstr path("");
2058 if (a->argc >= 4)
2059 path = a->argv[3];
2060 Bstr settings("");
2061 if (a->argc >= 5)
2062 settings = a->argv[4];
2063 CHECK_ERROR_BREAK(pEmulatedUSB, WebcamAttach(path.raw(), settings.raw()));
2064 }
2065 else if (!strcmp(a->argv[2], "detach"))
2066 {
2067 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_DETACH);
2068 Bstr path("");
2069 if (a->argc >= 4)
2070 path = a->argv[3];
2071 CHECK_ERROR_BREAK(pEmulatedUSB, WebcamDetach(path.raw()));
2072 }
2073 else if (!strcmp(a->argv[2], "list"))
2074 {
2075 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_LIST);
2076 com::SafeArray <BSTR> webcams;
2077 CHECK_ERROR_BREAK(pEmulatedUSB, COMGETTER(Webcams)(ComSafeArrayAsOutParam(webcams)));
2078 for (size_t i = 0; i < webcams.size(); ++i)
2079 {
2080 RTPrintf("%ls\n", webcams[i][0]? webcams[i]: Bstr("default").raw());
2081 }
2082 }
2083 else
2084 {
2085 errorArgument(ControlVM::tr("Invalid argument to '%s'."), a->argv[1]);
2086 hrc = E_FAIL;
2087 break;
2088 }
2089 }
2090 else if (!strcmp(a->argv[1], "addencpassword"))
2091 {
2092 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_ADDENCPASSWORD);
2093 if ( a->argc != 4
2094 && a->argc != 6)
2095 {
2096 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
2097 break;
2098 }
2099
2100 BOOL fRemoveOnSuspend = FALSE;
2101 if (a->argc == 6)
2102 {
2103 if ( strcmp(a->argv[4], "--removeonsuspend")
2104 || ( strcmp(a->argv[5], "yes")
2105 && strcmp(a->argv[5], "no")))
2106 {
2107 errorSyntax(ControlVM::tr("Invalid parameters."));
2108 break;
2109 }
2110 if (!strcmp(a->argv[5], "yes"))
2111 fRemoveOnSuspend = TRUE;
2112 }
2113
2114 Bstr bstrPwId(a->argv[2]);
2115 Utf8Str strPassword;
2116
2117 if (!RTStrCmp(a->argv[3], "-"))
2118 {
2119 /* Get password from console. */
2120 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, ControlVM::tr("Enter password:"));
2121 if (rcExit == RTEXITCODE_FAILURE)
2122 break;
2123 }
2124 else
2125 {
2126 RTEXITCODE rcExit = readPasswordFile(a->argv[3], &strPassword);
2127 if (rcExit == RTEXITCODE_FAILURE)
2128 {
2129 RTMsgError(ControlVM::tr("Failed to read new password from file."));
2130 break;
2131 }
2132 }
2133
2134 CHECK_ERROR_BREAK(console, AddEncryptionPassword(bstrPwId.raw(), Bstr(strPassword).raw(), fRemoveOnSuspend));
2135 }
2136 else if (!strcmp(a->argv[1], "removeencpassword"))
2137 {
2138 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_REMOVEENCPASSWORD);
2139 if (a->argc != 3)
2140 {
2141 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
2142 break;
2143 }
2144 Bstr bstrPwId(a->argv[2]);
2145 CHECK_ERROR_BREAK(console, RemoveEncryptionPassword(bstrPwId.raw()));
2146 }
2147 /** @todo r=bird: 'removeallencpasswords' is very much unreadable carp. Use
2148 * dashes as word separators to make it less fishy. */
2149 else if (!strcmp(a->argv[1], "removeallencpasswords"))
2150 {
2151 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_REMOVEALLENCPASSWORDS);
2152 CHECK_ERROR_BREAK(console, ClearAllEncryptionPasswords());
2153 }
2154 else if (!strncmp(a->argv[1], "changeuartmode", 14))
2155 {
2156 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_CHANGEUARTMODE);
2157 unsigned n = parseNum(&a->argv[1][14], 4, "UART");
2158 if (!n)
2159 {
2160 hrc = E_FAIL;
2161 break;
2162 }
2163 if (a->argc < 3)
2164 {
2165 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
2166 hrc = E_FAIL;
2167 break;
2168 }
2169
2170 ComPtr<ISerialPort> uart;
2171
2172 CHECK_ERROR_BREAK(sessionMachine, GetSerialPort(n - 1, uart.asOutParam()));
2173 ASSERT(uart);
2174
2175 if (!RTStrICmp(a->argv[2], "disconnected"))
2176 {
2177 if (a->argc != 3)
2178 {
2179 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2180 hrc = E_FAIL;
2181 break;
2182 }
2183 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
2184 }
2185 else if ( !RTStrICmp(a->argv[2], "server")
2186 || !RTStrICmp(a->argv[2], "client")
2187 || !RTStrICmp(a->argv[2], "tcpserver")
2188 || !RTStrICmp(a->argv[2], "tcpclient")
2189 || !RTStrICmp(a->argv[2], "file"))
2190 {
2191 const char *pszMode = a->argv[2];
2192 if (a->argc != 4)
2193 {
2194 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2195 hrc = E_FAIL;
2196 break;
2197 }
2198
2199 CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[3]).raw()));
2200
2201 /*
2202 * Change to disconnected first to get changes in just a parameter causing
2203 * the correct changes later on.
2204 */
2205 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
2206 if (!RTStrICmp(pszMode, "server"))
2207 {
2208 CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
2209 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
2210 }
2211 else if (!RTStrICmp(pszMode, "client"))
2212 {
2213 CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
2214 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
2215 }
2216 else if (!RTStrICmp(pszMode, "tcpserver"))
2217 {
2218 CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
2219 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
2220 }
2221 else if (!RTStrICmp(pszMode, "tcpclient"))
2222 {
2223 CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
2224 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
2225 }
2226 else if (!RTStrICmp(pszMode, "file"))
2227 {
2228 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_RawFile));
2229 }
2230 }
2231 else
2232 {
2233 if (a->argc != 3)
2234 {
2235 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2236 hrc = E_FAIL;
2237 break;
2238 }
2239 CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[2]).raw()));
2240 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostDevice));
2241 }
2242 }
2243 else if (!strncmp(a->argv[1], "vm-process-priority", 14))
2244 {
2245 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VM_PROCESS_PRIORITY);
2246 if (a->argc != 3)
2247 {
2248 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2249 hrc = E_FAIL;
2250 break;
2251 }
2252 VMProcPriority_T enmPriority = nameToVMProcPriority(a->argv[2]);
2253 if (enmPriority == VMProcPriority_Invalid)
2254 {
2255 errorSyntax(ControlVM::tr("Invalid vm-process-priority '%s'."), a->argv[2]);
2256 hrc = E_FAIL;
2257 }
2258 else
2259 {
2260 CHECK_ERROR(sessionMachine, COMSETTER(VMProcessPriority)(enmPriority));
2261 }
2262 break;
2263 }
2264 else if (!strncmp(a->argv[1], "autostart-enabled", 17))
2265 {
2266 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUTOSTART_ENABLED);
2267 if (a->argc != 3)
2268 {
2269 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2270 hrc = E_FAIL;
2271 break;
2272 }
2273 bool fEnabled;
2274 if (RT_FAILURE(parseBool(a->argv[2], &fEnabled)))
2275 {
2276 errorSyntax(ControlVM::tr("Invalid value '%s'."), a->argv[2]);
2277 hrc = E_FAIL;
2278 break;
2279 }
2280 CHECK_ERROR(sessionMachine, COMSETTER(AutostartEnabled)(TRUE));
2281 fNeedsSaving = true;
2282 break;
2283 }
2284 else if (!strncmp(a->argv[1], "autostart-delay", 15))
2285 {
2286 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUTOSTART_DELAY);
2287 if (a->argc != 3)
2288 {
2289 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2290 hrc = E_FAIL;
2291 break;
2292 }
2293 uint32_t u32;
2294 char *pszNext;
2295 int vrc = RTStrToUInt32Ex(a->argv[2], &pszNext, 10, &u32);
2296 if (RT_FAILURE(vrc) || *pszNext != '\0')
2297 {
2298 errorSyntax(ControlVM::tr("Invalid autostart delay number '%s'."), a->argv[2]);
2299 hrc = E_FAIL;
2300 break;
2301 }
2302 CHECK_ERROR(sessionMachine, COMSETTER(AutostartDelay)(u32));
2303 if (SUCCEEDED(hrc))
2304 fNeedsSaving = true;
2305 break;
2306 }
2307 else
2308 {
2309 errorSyntax(ControlVM::tr("Invalid parameter '%s'."), a->argv[1]);
2310 hrc = E_FAIL;
2311 }
2312 } while (0);
2313
2314 /* The client has to trigger saving the state explicitely. */
2315 if (fNeedsSaving)
2316 CHECK_ERROR(sessionMachine, SaveSettings());
2317
2318 a->session->UnlockMachine();
2319
2320 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2321}
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