VirtualBox

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

Last change on this file was 101035, checked in by vboxsync, 8 months ago

Initial commit (based draft v2 / on patch v5) for implementing platform architecture support for x86 and ARM. bugref:10384

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 88.8 KB
Line 
1/* $Id: VBoxManageControlVM.cpp 101035 2023-09-07 08:59:15Z 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 (a->argc < 3)
1800 {
1801 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1802 hrc = E_FAIL;
1803 break;
1804 }
1805
1806 ComPtr<IRecordingSettings> recordingSettings;
1807 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()));
1808
1809 SafeIfaceArray <IRecordingScreenSettings> saRecordingScreenScreens;
1810 CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saRecordingScreenScreens)));
1811
1812 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
1813 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam()));
1814
1815 /* Note: For now all screens have the same configuration. */
1816
1817 /*
1818 * Note: Commands starting with "vcp" are the deprecated versions and are
1819 * kept to ensure backwards compatibility.
1820 */
1821 bool fEnabled;
1822 if (RT_SUCCESS(parseBool(a->argv[2], &fEnabled)))
1823 {
1824 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING);
1825 CHECK_ERROR_RET(recordingSettings, COMSETTER(Enabled)(fEnabled), RTEXITCODE_FAILURE);
1826 }
1827 else if (!strcmp(a->argv[2], "screens"))
1828 {
1829 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_SCREENS);
1830 ULONG cMonitors = 64;
1831 CHECK_ERROR_BREAK(pGraphicsAdapter, COMGETTER(MonitorCount)(&cMonitors));
1832 com::SafeArray<BOOL> saScreens(cMonitors);
1833 if (a->argc != 4)
1834 {
1835 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1836 hrc = E_FAIL;
1837 break;
1838 }
1839 if (RT_FAILURE(parseScreens(a->argv[3], &saScreens)))
1840 {
1841 errorSyntax(ControlVM::tr("Error parsing list of screen IDs '%s'."), a->argv[3]);
1842 hrc = E_FAIL;
1843 break;
1844 }
1845
1846 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1847 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Enabled)(saScreens[i]));
1848 }
1849 else if (!strcmp(a->argv[2], "filename"))
1850 {
1851 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_FILENAME);
1852 if (a->argc != 4)
1853 {
1854 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1855 hrc = E_FAIL;
1856 break;
1857 }
1858
1859 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1860 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Filename)(Bstr(a->argv[3]).raw()));
1861 }
1862 else if ( !strcmp(a->argv[2], "videores")
1863 || !strcmp(a->argv[2], "videoresolution"))
1864 {
1865 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_VIDEORES);
1866 if (a->argc != 5)
1867 {
1868 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1869 hrc = E_FAIL;
1870 break;
1871 }
1872
1873 uint32_t uWidth;
1874 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uWidth);
1875 if (RT_FAILURE(vrc))
1876 {
1877 errorSyntax(ControlVM::tr("Error parsing video width '%s'."), a->argv[3]);
1878 hrc = E_FAIL;
1879 break;
1880 }
1881
1882 uint32_t uHeight;
1883 vrc = RTStrToUInt32Ex(a->argv[4], NULL, 0, &uHeight);
1884 if (RT_FAILURE(vrc))
1885 {
1886 errorSyntax(ControlVM::tr("Error parsing video height '%s'."), a->argv[4]);
1887 hrc = E_FAIL;
1888 break;
1889 }
1890
1891 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1892 {
1893 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoWidth)(uWidth));
1894 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoHeight)(uHeight));
1895 }
1896 }
1897 else if (!strcmp(a->argv[2], "videorate"))
1898 {
1899 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_VIDEORATE);
1900 if (a->argc != 4)
1901 {
1902 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1903 hrc = E_FAIL;
1904 break;
1905 }
1906
1907 uint32_t uRate;
1908 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uRate);
1909 if (RT_FAILURE(vrc))
1910 {
1911 errorSyntax(ControlVM::tr("Error parsing video rate '%s'."), a->argv[3]);
1912 hrc = E_FAIL;
1913 break;
1914 }
1915
1916 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1917 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoRate)(uRate));
1918 }
1919 else if (!strcmp(a->argv[2], "videofps"))
1920 {
1921 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_VIDEOFPS);
1922 if (a->argc != 4)
1923 {
1924 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1925 hrc = E_FAIL;
1926 break;
1927 }
1928
1929 uint32_t uFPS;
1930 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uFPS);
1931 if (RT_FAILURE(vrc))
1932 {
1933 errorSyntax(ControlVM::tr("Error parsing video FPS '%s'."), a->argv[3]);
1934 hrc = E_FAIL;
1935 break;
1936 }
1937
1938 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1939 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoFPS)(uFPS));
1940 }
1941 else if (!strcmp(a->argv[2], "maxtime"))
1942 {
1943 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_MAXTIME);
1944 if (a->argc != 4)
1945 {
1946 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1947 hrc = E_FAIL;
1948 break;
1949 }
1950
1951 uint32_t uMaxTime;
1952 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxTime);
1953 if (RT_FAILURE(vrc))
1954 {
1955 errorSyntax(ControlVM::tr("Error parsing maximum time '%s'."), a->argv[3]);
1956 hrc = E_FAIL;
1957 break;
1958 }
1959
1960 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1961 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(MaxTime)(uMaxTime));
1962 }
1963 else if (!strcmp(a->argv[2], "maxfilesize"))
1964 {
1965 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_MAXFILESIZE);
1966 if (a->argc != 4)
1967 {
1968 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1969 hrc = E_FAIL;
1970 break;
1971 }
1972
1973 uint32_t uMaxFileSize;
1974 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxFileSize);
1975 if (RT_FAILURE(vrc))
1976 {
1977 errorSyntax(ControlVM::tr("Error parsing maximum file size '%s'."), a->argv[3]);
1978 hrc = E_FAIL;
1979 break;
1980 }
1981
1982 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1983 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(MaxFileSize)(uMaxFileSize));
1984 }
1985 else if (!strcmp(a->argv[2], "opts"))
1986 {
1987#if 0 /* Add when the corresponding documentation is enabled. */
1988 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING_OPTS);
1989#endif
1990 if (a->argc != 4)
1991 {
1992 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1993 hrc = E_FAIL;
1994 break;
1995 }
1996
1997 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1998 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Options)(Bstr(a->argv[3]).raw()));
1999 }
2000 }
2001#endif /* VBOX_WITH_RECORDING */
2002 else if (!strcmp(a->argv[1], "webcam"))
2003 {
2004 if (a->argc < 3)
2005 {
2006 errorArgument(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
2007 hrc = E_FAIL;
2008 break;
2009 }
2010
2011 ComPtr<IEmulatedUSB> pEmulatedUSB;
2012 CHECK_ERROR_BREAK(console, COMGETTER(EmulatedUSB)(pEmulatedUSB.asOutParam()));
2013 if (!pEmulatedUSB)
2014 {
2015 RTMsgError(ControlVM::tr("Guest not running."));
2016 hrc = E_FAIL;
2017 break;
2018 }
2019
2020 if (!strcmp(a->argv[2], "attach"))
2021 {
2022 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_ATTACH);
2023 Bstr path("");
2024 if (a->argc >= 4)
2025 path = a->argv[3];
2026 Bstr settings("");
2027 if (a->argc >= 5)
2028 settings = a->argv[4];
2029 CHECK_ERROR_BREAK(pEmulatedUSB, WebcamAttach(path.raw(), settings.raw()));
2030 }
2031 else if (!strcmp(a->argv[2], "detach"))
2032 {
2033 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_DETACH);
2034 Bstr path("");
2035 if (a->argc >= 4)
2036 path = a->argv[3];
2037 CHECK_ERROR_BREAK(pEmulatedUSB, WebcamDetach(path.raw()));
2038 }
2039 else if (!strcmp(a->argv[2], "list"))
2040 {
2041 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_LIST);
2042 com::SafeArray <BSTR> webcams;
2043 CHECK_ERROR_BREAK(pEmulatedUSB, COMGETTER(Webcams)(ComSafeArrayAsOutParam(webcams)));
2044 for (size_t i = 0; i < webcams.size(); ++i)
2045 {
2046 RTPrintf("%ls\n", webcams[i][0]? webcams[i]: Bstr("default").raw());
2047 }
2048 }
2049 else
2050 {
2051 errorArgument(ControlVM::tr("Invalid argument to '%s'."), a->argv[1]);
2052 hrc = E_FAIL;
2053 break;
2054 }
2055 }
2056 else if (!strcmp(a->argv[1], "addencpassword"))
2057 {
2058 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_ADDENCPASSWORD);
2059 if ( a->argc != 4
2060 && a->argc != 6)
2061 {
2062 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
2063 break;
2064 }
2065
2066 BOOL fRemoveOnSuspend = FALSE;
2067 if (a->argc == 6)
2068 {
2069 if ( strcmp(a->argv[4], "--removeonsuspend")
2070 || ( strcmp(a->argv[5], "yes")
2071 && strcmp(a->argv[5], "no")))
2072 {
2073 errorSyntax(ControlVM::tr("Invalid parameters."));
2074 break;
2075 }
2076 if (!strcmp(a->argv[5], "yes"))
2077 fRemoveOnSuspend = TRUE;
2078 }
2079
2080 Bstr bstrPwId(a->argv[2]);
2081 Utf8Str strPassword;
2082
2083 if (!RTStrCmp(a->argv[3], "-"))
2084 {
2085 /* Get password from console. */
2086 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, ControlVM::tr("Enter password:"));
2087 if (rcExit == RTEXITCODE_FAILURE)
2088 break;
2089 }
2090 else
2091 {
2092 RTEXITCODE rcExit = readPasswordFile(a->argv[3], &strPassword);
2093 if (rcExit == RTEXITCODE_FAILURE)
2094 {
2095 RTMsgError(ControlVM::tr("Failed to read new password from file."));
2096 break;
2097 }
2098 }
2099
2100 CHECK_ERROR_BREAK(console, AddEncryptionPassword(bstrPwId.raw(), Bstr(strPassword).raw(), fRemoveOnSuspend));
2101 }
2102 else if (!strcmp(a->argv[1], "removeencpassword"))
2103 {
2104 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_REMOVEENCPASSWORD);
2105 if (a->argc != 3)
2106 {
2107 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
2108 break;
2109 }
2110 Bstr bstrPwId(a->argv[2]);
2111 CHECK_ERROR_BREAK(console, RemoveEncryptionPassword(bstrPwId.raw()));
2112 }
2113 /** @todo r=bird: 'removeallencpasswords' is very much unreadable carp. Use
2114 * dashes as word separators to make it less fishy. */
2115 else if (!strcmp(a->argv[1], "removeallencpasswords"))
2116 {
2117 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_REMOVEALLENCPASSWORDS);
2118 CHECK_ERROR_BREAK(console, ClearAllEncryptionPasswords());
2119 }
2120 else if (!strncmp(a->argv[1], "changeuartmode", 14))
2121 {
2122 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_CHANGEUARTMODE);
2123 unsigned n = parseNum(&a->argv[1][14], 4, "UART");
2124 if (!n)
2125 {
2126 hrc = E_FAIL;
2127 break;
2128 }
2129 if (a->argc < 3)
2130 {
2131 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
2132 hrc = E_FAIL;
2133 break;
2134 }
2135
2136 ComPtr<ISerialPort> uart;
2137
2138 CHECK_ERROR_BREAK(sessionMachine, GetSerialPort(n - 1, uart.asOutParam()));
2139 ASSERT(uart);
2140
2141 if (!RTStrICmp(a->argv[2], "disconnected"))
2142 {
2143 if (a->argc != 3)
2144 {
2145 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2146 hrc = E_FAIL;
2147 break;
2148 }
2149 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
2150 }
2151 else if ( !RTStrICmp(a->argv[2], "server")
2152 || !RTStrICmp(a->argv[2], "client")
2153 || !RTStrICmp(a->argv[2], "tcpserver")
2154 || !RTStrICmp(a->argv[2], "tcpclient")
2155 || !RTStrICmp(a->argv[2], "file"))
2156 {
2157 const char *pszMode = a->argv[2];
2158 if (a->argc != 4)
2159 {
2160 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2161 hrc = E_FAIL;
2162 break;
2163 }
2164
2165 CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[3]).raw()));
2166
2167 /*
2168 * Change to disconnected first to get changes in just a parameter causing
2169 * the correct changes later on.
2170 */
2171 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
2172 if (!RTStrICmp(pszMode, "server"))
2173 {
2174 CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
2175 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
2176 }
2177 else if (!RTStrICmp(pszMode, "client"))
2178 {
2179 CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
2180 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
2181 }
2182 else if (!RTStrICmp(pszMode, "tcpserver"))
2183 {
2184 CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
2185 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
2186 }
2187 else if (!RTStrICmp(pszMode, "tcpclient"))
2188 {
2189 CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
2190 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
2191 }
2192 else if (!RTStrICmp(pszMode, "file"))
2193 {
2194 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_RawFile));
2195 }
2196 }
2197 else
2198 {
2199 if (a->argc != 3)
2200 {
2201 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2202 hrc = E_FAIL;
2203 break;
2204 }
2205 CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[2]).raw()));
2206 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostDevice));
2207 }
2208 }
2209 else if (!strncmp(a->argv[1], "vm-process-priority", 14))
2210 {
2211 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VM_PROCESS_PRIORITY);
2212 if (a->argc != 3)
2213 {
2214 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2215 hrc = E_FAIL;
2216 break;
2217 }
2218 VMProcPriority_T enmPriority = nameToVMProcPriority(a->argv[2]);
2219 if (enmPriority == VMProcPriority_Invalid)
2220 {
2221 errorSyntax(ControlVM::tr("Invalid vm-process-priority '%s'."), a->argv[2]);
2222 hrc = E_FAIL;
2223 }
2224 else
2225 {
2226 CHECK_ERROR(sessionMachine, COMSETTER(VMProcessPriority)(enmPriority));
2227 }
2228 break;
2229 }
2230 else if (!strncmp(a->argv[1], "autostart-enabled", 17))
2231 {
2232 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUTOSTART_ENABLED);
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 bool fEnabled;
2240 if (RT_FAILURE(parseBool(a->argv[2], &fEnabled)))
2241 {
2242 errorSyntax(ControlVM::tr("Invalid value '%s'."), a->argv[2]);
2243 hrc = E_FAIL;
2244 break;
2245 }
2246 CHECK_ERROR(sessionMachine, COMSETTER(AutostartEnabled)(TRUE));
2247 fNeedsSaving = true;
2248 break;
2249 }
2250 else if (!strncmp(a->argv[1], "autostart-delay", 15))
2251 {
2252 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUTOSTART_DELAY);
2253 if (a->argc != 3)
2254 {
2255 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2256 hrc = E_FAIL;
2257 break;
2258 }
2259 uint32_t u32;
2260 char *pszNext;
2261 int vrc = RTStrToUInt32Ex(a->argv[2], &pszNext, 10, &u32);
2262 if (RT_FAILURE(vrc) || *pszNext != '\0')
2263 {
2264 errorSyntax(ControlVM::tr("Invalid autostart delay number '%s'."), a->argv[2]);
2265 hrc = E_FAIL;
2266 break;
2267 }
2268 CHECK_ERROR(sessionMachine, COMSETTER(AutostartDelay)(u32));
2269 if (SUCCEEDED(hrc))
2270 fNeedsSaving = true;
2271 break;
2272 }
2273 else
2274 {
2275 errorSyntax(ControlVM::tr("Invalid parameter '%s'."), a->argv[1]);
2276 hrc = E_FAIL;
2277 }
2278 } while (0);
2279
2280 /* The client has to trigger saving the state explicitely. */
2281 if (fNeedsSaving)
2282 CHECK_ERROR(sessionMachine, SaveSettings());
2283
2284 a->session->UnlockMachine();
2285
2286 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2287}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use