VirtualBox

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

Last change on this file since 99775 was 99775, checked in by vboxsync, 12 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 88.9 KB
Line 
1/* $Id: VBoxManageControlVM.cpp 99775 2023-05-12 12:21:58Z 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(a->virtualBox, 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(a->virtualBox, 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(a->virtualBox, 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(a->virtualBox, 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(a->virtualBox, 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(a->virtualBox, 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(a->virtualBox, 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