VirtualBox

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

Last change on this file since 107606 was 107606, checked in by vboxsync, 10 days ago

FE/VBoxManage/VBoxManageControlVM.cpp: Remove useless if condition as it is implied by the other one already, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 89.4 KB
Line 
1/* $Id: VBoxManageControlVM.cpp 107606 2025-01-09 19:31:32Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of the controlvm command.
4 */
5
6/*
7 * Copyright (C) 2006-2024 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 (hrc == VBOX_E_NOT_SUPPORTED)
696 {
697 if (fReboot)
698 RTMsgError(ControlVM::tr("Current installed Guest Additions don't support rebooting the guest."));
699 else
700 RTMsgError(ControlVM::tr("Current installed Guest Additions don't support shutting down the guest."));
701 }
702 }
703#endif
704 else if (!strcmp(a->argv[1], "keyboardputscancode"))
705 {
706 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_KEYBOARDPUTSCANCODE);
707 ComPtr<IKeyboard> pKeyboard;
708 CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(pKeyboard.asOutParam()));
709 if (!pKeyboard)
710 {
711 RTMsgError(ControlVM::tr("Guest not running."));
712 hrc = E_FAIL;
713 break;
714 }
715
716 if (a->argc <= 1 + 1)
717 {
718 errorSyntax(ControlVM::tr("Missing argument to '%s'. Expected IBM PC AT set 2 keyboard scancode(s)."),
719 a->argv[1]);
720 hrc = E_FAIL;
721 break;
722 }
723
724 std::list<LONG> llScancodes;
725
726 /* Process the command line. */
727 int i;
728 for (i = 1 + 1; i < a->argc; i++)
729 {
730 if ( RT_C_IS_XDIGIT (a->argv[i][0])
731 && RT_C_IS_XDIGIT (a->argv[i][1])
732 && a->argv[i][2] == 0)
733 {
734 uint8_t u8Scancode;
735 int vrc = RTStrToUInt8Ex(a->argv[i], NULL, 16, &u8Scancode);
736 if (RT_FAILURE (vrc))
737 {
738 RTMsgError(ControlVM::tr("Converting '%s' returned %Rrc!"), a->argv[i], vrc);
739 hrc = E_FAIL;
740 break;
741 }
742
743 llScancodes.push_back(u8Scancode);
744 }
745 else
746 {
747 RTMsgError(ControlVM::tr("'%s' is not a hex byte!"), a->argv[i]);
748 hrc = E_FAIL;
749 break;
750 }
751 }
752
753 if (FAILED(hrc))
754 break;
755
756 hrc = keyboardPutScancodes(pKeyboard, llScancodes);
757 }
758 else if (!strcmp(a->argv[1], "keyboardputstring"))
759 {
760 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_KEYBOARDPUTSTRING);
761 ComPtr<IKeyboard> pKeyboard;
762 CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(pKeyboard.asOutParam()));
763 if (!pKeyboard)
764 {
765 RTMsgError(ControlVM::tr("Guest not running."));
766 hrc = E_FAIL;
767 break;
768 }
769
770 if (a->argc <= 1 + 1)
771 {
772 errorSyntax(ControlVM::tr("Missing argument to '%s'. Expected ASCII string(s)."), a->argv[1]);
773 hrc = E_FAIL;
774 break;
775 }
776
777 hrc = keyboardPutString(pKeyboard, a->argc, a->argv);
778 }
779 else if (!strcmp(a->argv[1], "keyboardputfile"))
780 {
781 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_KEYBOARDPUTFILE);
782 ComPtr<IKeyboard> pKeyboard;
783 CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(pKeyboard.asOutParam()));
784 if (!pKeyboard)
785 {
786 RTMsgError(ControlVM::tr("Guest not running."));
787 hrc = E_FAIL;
788 break;
789 }
790
791 if (a->argc <= 1 + 1)
792 {
793 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
794 hrc = E_FAIL;
795 break;
796 }
797
798 hrc = keyboardPutFile(pKeyboard, a->argv[2]);
799 }
800 else if (!strncmp(a->argv[1], "setlinkstate", 12))
801 {
802 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETLINKSTATE);
803 /* Get the number of network adapters */
804 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
805 unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
806 if (!n)
807 {
808 hrc = E_FAIL;
809 break;
810 }
811 if (a->argc <= 1 + 1)
812 {
813 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
814 hrc = E_FAIL;
815 break;
816 }
817 /* get the corresponding network adapter */
818 ComPtr<INetworkAdapter> adapter;
819 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
820 if (adapter)
821 {
822 bool fEnabled;
823 if (RT_FAILURE(parseBool(a->argv[2], &fEnabled)))
824 {
825 errorSyntax(ControlVM::tr("Invalid link state '%s'."), a->argv[2]);
826 hrc = E_FAIL;
827 break;
828 }
829 CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(fEnabled));
830 fNeedsSaving = true;
831 }
832 }
833 /* here the order in which strncmp is called is important
834 * cause nictracefile can be very well compared with
835 * nictrace and nic and thus everything will always fail
836 * if the order is changed
837 */
838 else if (!strncmp(a->argv[1], "nictracefile", 12))
839 {
840 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICTRACEFILE);
841 /* Get the number of network adapters */
842 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
843 unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
844 if (!n)
845 {
846 hrc = E_FAIL;
847 break;
848 }
849 if (a->argc <= 2)
850 {
851 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
852 hrc = E_FAIL;
853 break;
854 }
855
856 /* get the corresponding network adapter */
857 ComPtr<INetworkAdapter> adapter;
858 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
859 if (adapter)
860 {
861 BOOL fEnabled;
862 adapter->COMGETTER(Enabled)(&fEnabled);
863 if (fEnabled)
864 {
865 if (a->argv[2])
866 {
867 CHECK_ERROR_RET(adapter, COMSETTER(TraceFile)(Bstr(a->argv[2]).raw()), RTEXITCODE_FAILURE);
868 }
869 else
870 {
871 errorSyntax(ControlVM::tr("Filename not specified for NIC %lu."), n);
872 hrc = E_FAIL;
873 break;
874 }
875 if (SUCCEEDED(hrc))
876 fNeedsSaving = true;
877 }
878 else
879 RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its tracefile can't be changed."), n);
880 }
881 }
882 else if (!strncmp(a->argv[1], "nictrace", 8))
883 {
884 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICTRACE);
885 /* Get the number of network adapters */
886 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
887 unsigned n = parseNum(&a->argv[1][8], NetworkAdapterCount, "NIC");
888 if (!n)
889 {
890 hrc = E_FAIL;
891 break;
892 }
893 if (a->argc <= 2)
894 {
895 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
896 hrc = E_FAIL;
897 break;
898 }
899
900 /* get the corresponding network adapter */
901 ComPtr<INetworkAdapter> adapter;
902 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
903 if (adapter)
904 {
905 BOOL fEnabled;
906 adapter->COMGETTER(Enabled)(&fEnabled);
907 if (fEnabled)
908 {
909 bool fTraceEnabled;
910 if (RT_FAILURE(parseBool(a->argv[2], &fTraceEnabled)))
911 {
912 errorSyntax(ControlVM::tr("Invalid nictrace%lu argument '%s'."), n, a->argv[2]);
913 hrc = E_FAIL;
914 break;
915 }
916 CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(fTraceEnabled), RTEXITCODE_FAILURE);
917 fNeedsSaving = true;
918 }
919 else
920 RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its trace flag can't be changed."), n);
921 }
922 }
923 else if( a->argc > 2
924 && !strncmp(a->argv[1], "natpf", 5))
925 {
926 /* Get the number of network adapters */
927 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
928 unsigned n = parseNum(&a->argv[1][5], NetworkAdapterCount, "NIC");
929 if (!n)
930 {
931 hrc = E_FAIL;
932 break;
933 }
934 if (a->argc <= 2)
935 {
936 errorArgument(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
937 hrc = E_FAIL;
938 break;
939 }
940
941 /* get the corresponding network adapter */
942 ComPtr<INetworkAdapter> adapter;
943 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
944 if (!adapter)
945 {
946 hrc = E_FAIL;
947 break;
948 }
949 ComPtr<INATEngine> engine;
950 CHECK_ERROR(adapter, COMGETTER(NATEngine)(engine.asOutParam()));
951 if (!engine)
952 {
953 hrc = E_FAIL;
954 break;
955 }
956
957 if (!strcmp(a->argv[2], "delete"))
958 {
959 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NATPF_DELETE);
960 if (a->argc >= 3)
961 CHECK_ERROR(engine, RemoveRedirect(Bstr(a->argv[3]).raw()));
962 }
963 else
964 {
965 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NATPF);
966#define ITERATE_TO_NEXT_TERM(ch) \
967 do { \
968 while (*ch != ',') \
969 { \
970 if (*ch == 0) \
971 { \
972 return errorSyntax(ControlVM::tr("Missing or invalid argument to '%s'."), \
973 a->argv[1]); \
974 } \
975 ch++; \
976 } \
977 *ch = '\0'; \
978 ch++; \
979 } while(0)
980
981 char *strName;
982 char *strProto;
983 char *strHostIp;
984 char *strHostPort;
985 char *strGuestIp;
986 char *strGuestPort;
987 char *strRaw = RTStrDup(a->argv[2]);
988 char *ch = strRaw;
989 strName = RTStrStrip(ch);
990 ITERATE_TO_NEXT_TERM(ch);
991 strProto = RTStrStrip(ch);
992 ITERATE_TO_NEXT_TERM(ch);
993 strHostIp = RTStrStrip(ch);
994 ITERATE_TO_NEXT_TERM(ch);
995 strHostPort = RTStrStrip(ch);
996 ITERATE_TO_NEXT_TERM(ch);
997 strGuestIp = RTStrStrip(ch);
998 ITERATE_TO_NEXT_TERM(ch);
999 strGuestPort = RTStrStrip(ch);
1000 NATProtocol_T proto;
1001 if (RTStrICmp(strProto, "udp") == 0)
1002 proto = NATProtocol_UDP;
1003 else if (RTStrICmp(strProto, "tcp") == 0)
1004 proto = NATProtocol_TCP;
1005 else
1006 {
1007 return errorSyntax(ControlVM::tr("Wrong rule proto '%s' specified -- only 'udp' and 'tcp' are allowed."),
1008 strProto);
1009 }
1010 CHECK_ERROR(engine, AddRedirect(Bstr(strName).raw(), proto, Bstr(strHostIp).raw(),
1011 RTStrToUInt16(strHostPort), Bstr(strGuestIp).raw(), RTStrToUInt16(strGuestPort)));
1012#undef ITERATE_TO_NEXT_TERM
1013 }
1014 if (SUCCEEDED(hrc))
1015 fNeedsSaving = true;
1016 }
1017 else if (!strncmp(a->argv[1], "nicproperty", 11))
1018 {
1019 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICPROPERTY);
1020 /* Get the number of network adapters */
1021 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
1022 unsigned n = parseNum(&a->argv[1][11], NetworkAdapterCount, "NIC");
1023 if (!n)
1024 {
1025 hrc = E_FAIL;
1026 break;
1027 }
1028 if (a->argc <= 2)
1029 {
1030 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1031 hrc = E_FAIL;
1032 break;
1033 }
1034
1035 /* get the corresponding network adapter */
1036 ComPtr<INetworkAdapter> adapter;
1037 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
1038 if (adapter)
1039 {
1040 BOOL fEnabled;
1041 adapter->COMGETTER(Enabled)(&fEnabled);
1042 if (fEnabled)
1043 {
1044 /* Parse 'name=value' */
1045 char *pszProperty = RTStrDup(a->argv[2]);
1046 if (pszProperty)
1047 {
1048 char *pDelimiter = strchr(pszProperty, '=');
1049 if (pDelimiter)
1050 {
1051 *pDelimiter = '\0';
1052
1053 Bstr bstrName = pszProperty;
1054 Bstr bstrValue = &pDelimiter[1];
1055 CHECK_ERROR(adapter, SetProperty(bstrName.raw(), bstrValue.raw()));
1056 if (SUCCEEDED(hrc))
1057 fNeedsSaving = true;
1058 }
1059 else
1060 {
1061 errorSyntax(ControlVM::tr("Invalid nicproperty%d argument '%s'."), n, a->argv[2]);
1062 hrc = E_FAIL;
1063 }
1064 RTStrFree(pszProperty);
1065 }
1066 else
1067 {
1068 RTMsgError(ControlVM::tr("Failed to allocate memory for nicproperty%d '%s'."),
1069 n, a->argv[2]);
1070 hrc = E_FAIL;
1071 }
1072 if (FAILED(hrc))
1073 break;
1074 }
1075 else
1076 RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its properties can't be changed."), n);
1077 }
1078 }
1079 else if (!strncmp(a->argv[1], "nicpromisc", 10))
1080 {
1081 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NICPROMISC);
1082 /* Get the number of network adapters */
1083 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
1084 unsigned n = parseNum(&a->argv[1][10], NetworkAdapterCount, "NIC");
1085 if (!n)
1086 {
1087 hrc = E_FAIL;
1088 break;
1089 }
1090 if (a->argc <= 2)
1091 {
1092 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1093 hrc = E_FAIL;
1094 break;
1095 }
1096
1097 /* get the corresponding network adapter */
1098 ComPtr<INetworkAdapter> adapter;
1099 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
1100 if (adapter)
1101 {
1102 BOOL fEnabled;
1103 adapter->COMGETTER(Enabled)(&fEnabled);
1104 if (fEnabled)
1105 {
1106 NetworkAdapterPromiscModePolicy_T enmPromiscModePolicy;
1107 if (!strcmp(a->argv[2], "deny"))
1108 enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny;
1109 else if ( !strcmp(a->argv[2], "allow-vms")
1110 || !strcmp(a->argv[2], "allow-network"))
1111 enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork;
1112 else if (!strcmp(a->argv[2], "allow-all"))
1113 enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll;
1114 else
1115 {
1116 errorSyntax(ControlVM::tr("Unknown promiscuous mode policy '%s'."), a->argv[2]);
1117 hrc = E_INVALIDARG;
1118 break;
1119 }
1120
1121 CHECK_ERROR(adapter, COMSETTER(PromiscModePolicy)(enmPromiscModePolicy));
1122 if (SUCCEEDED(hrc))
1123 fNeedsSaving = true;
1124 }
1125 else
1126 RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its promiscuous mode can't be changed."), n);
1127 }
1128 }
1129 else if (!strncmp(a->argv[1], "nic", 3))
1130 {
1131 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_NIC);
1132 /* Get the number of network adapters */
1133 ULONG NetworkAdapterCount = getMaxNics(sessionMachine);
1134 unsigned n = parseNum(&a->argv[1][3], NetworkAdapterCount, "NIC");
1135 if (!n)
1136 {
1137 hrc = E_FAIL;
1138 break;
1139 }
1140 if (a->argc <= 2)
1141 {
1142 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1143 hrc = E_FAIL;
1144 break;
1145 }
1146
1147 /* get the corresponding network adapter */
1148 ComPtr<INetworkAdapter> adapter;
1149 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
1150 if (adapter)
1151 {
1152 BOOL fEnabled;
1153 adapter->COMGETTER(Enabled)(&fEnabled);
1154 if (fEnabled)
1155 {
1156 if (!strcmp(a->argv[2], "null"))
1157 {
1158 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Null), RTEXITCODE_FAILURE);
1159 }
1160 else if (!strcmp(a->argv[2], "nat"))
1161 {
1162 if (a->argc == 4)
1163 CHECK_ERROR_RET(adapter, COMSETTER(NATNetwork)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1164 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_NAT), RTEXITCODE_FAILURE);
1165 }
1166 else if ( !strcmp(a->argv[2], "bridged")
1167 || !strcmp(a->argv[2], "hostif")) /* backward compatibility */
1168 {
1169 if (a->argc <= 3)
1170 {
1171 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]);
1172 hrc = E_FAIL;
1173 break;
1174 }
1175 CHECK_ERROR_RET(adapter, COMSETTER(BridgedInterface)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1176 verifyHostNetworkInterfaceName(a->virtualBox, a->argv[3], HostNetworkInterfaceType_Bridged);
1177 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged), RTEXITCODE_FAILURE);
1178 }
1179 else if (!strcmp(a->argv[2], "intnet"))
1180 {
1181 if (a->argc <= 3)
1182 {
1183 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]);
1184 hrc = E_FAIL;
1185 break;
1186 }
1187 CHECK_ERROR_RET(adapter, COMSETTER(InternalNetwork)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1188 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Internal), RTEXITCODE_FAILURE);
1189 }
1190#if defined(VBOX_WITH_NETFLT)
1191 else if (!strcmp(a->argv[2], "hostonly"))
1192 {
1193 if (a->argc <= 3)
1194 {
1195 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]);
1196 hrc = E_FAIL;
1197 break;
1198 }
1199 CHECK_ERROR_RET(adapter, COMSETTER(HostOnlyInterface)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1200 verifyHostNetworkInterfaceName(a->virtualBox, a->argv[3], HostNetworkInterfaceType_HostOnly);
1201 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly), RTEXITCODE_FAILURE);
1202 }
1203#endif
1204 else if (!strcmp(a->argv[2], "generic"))
1205 {
1206 if (a->argc <= 3)
1207 {
1208 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]);
1209 hrc = E_FAIL;
1210 break;
1211 }
1212 CHECK_ERROR_RET(adapter, COMSETTER(GenericDriver)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1213 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), RTEXITCODE_FAILURE);
1214 }
1215 else if (!strcmp(a->argv[2], "natnetwork"))
1216 {
1217 if (a->argc <= 3)
1218 {
1219 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[2]);
1220 hrc = E_FAIL;
1221 break;
1222 }
1223 CHECK_ERROR_RET(adapter, COMSETTER(NATNetwork)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
1224 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_NATNetwork), RTEXITCODE_FAILURE);
1225 }
1226 else
1227 {
1228 errorSyntax(ControlVM::tr("Invalid type '%s' specfied for NIC %lu."), a->argv[2], n);
1229 hrc = E_FAIL;
1230 break;
1231 }
1232 if (SUCCEEDED(hrc))
1233 fNeedsSaving = true;
1234 }
1235 else
1236 RTMsgError(ControlVM::tr("The NIC %d is currently disabled and thus its attachment type can't be changed."), n);
1237 }
1238 }
1239 else if ( !strcmp(a->argv[1], "vrde")
1240 || !strcmp(a->argv[1], "vrdp"))
1241 {
1242 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDE);
1243 if (!strcmp(a->argv[1], "vrdp"))
1244 RTMsgWarning(ControlVM::tr("'vrdp' is deprecated. Use 'vrde'."));
1245
1246 if (a->argc <= 1 + 1)
1247 {
1248 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1249 hrc = E_FAIL;
1250 break;
1251 }
1252 ComPtr<IVRDEServer> vrdeServer;
1253 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
1254 ASSERT(vrdeServer);
1255 if (vrdeServer)
1256 {
1257 bool fEnabled;
1258 if (RT_FAILURE(parseBool(a->argv[2], &fEnabled)))
1259 {
1260 errorSyntax(ControlVM::tr("Invalid remote desktop server state '%s'."), a->argv[2]);
1261 hrc = E_FAIL;
1262 break;
1263 }
1264 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(fEnabled));
1265 fNeedsSaving = true;
1266 }
1267 }
1268 else if ( !strcmp(a->argv[1], "vrdeport")
1269 || !strcmp(a->argv[1], "vrdpport"))
1270 {
1271 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDEPORT);
1272 if (!strcmp(a->argv[1], "vrdpport"))
1273 RTMsgWarning(ControlVM::tr("'vrdpport' is deprecated. Use 'vrdeport'."));
1274
1275 if (a->argc <= 1 + 1)
1276 {
1277 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1278 hrc = E_FAIL;
1279 break;
1280 }
1281
1282 ComPtr<IVRDEServer> vrdeServer;
1283 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
1284 ASSERT(vrdeServer);
1285 if (vrdeServer)
1286 {
1287 Bstr ports;
1288
1289 if (!strcmp(a->argv[2], "default"))
1290 ports = "0";
1291 else
1292 ports = a->argv[2];
1293
1294 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), ports.raw()));
1295 if (SUCCEEDED(hrc))
1296 fNeedsSaving = true;
1297 }
1298 }
1299 else if ( !strcmp(a->argv[1], "vrdevideochannelquality")
1300 || !strcmp(a->argv[1], "vrdpvideochannelquality"))
1301 {
1302 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDEVIDEOCHANNELQUALITY);
1303 if (!strcmp(a->argv[1], "vrdpvideochannelquality"))
1304 RTMsgWarning(ControlVM::tr("'vrdpvideochannelquality' is deprecated. Use 'vrdevideochannelquality'."));
1305
1306 if (a->argc <= 1 + 1)
1307 {
1308 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1309 hrc = E_FAIL;
1310 break;
1311 }
1312 ComPtr<IVRDEServer> vrdeServer;
1313 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
1314 ASSERT(vrdeServer);
1315 if (vrdeServer)
1316 {
1317 Bstr value = a->argv[2];
1318
1319 CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("VideoChannel/Quality").raw(), value.raw()));
1320 if (SUCCEEDED(hrc))
1321 fNeedsSaving = true;
1322 }
1323 }
1324 else if (!strcmp(a->argv[1], "vrdeproperty"))
1325 {
1326 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VRDEPROPERTY);
1327 if (a->argc <= 1 + 1)
1328 {
1329 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
1330 hrc = E_FAIL;
1331 break;
1332 }
1333 ComPtr<IVRDEServer> vrdeServer;
1334 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
1335 ASSERT(vrdeServer);
1336 if (vrdeServer)
1337 {
1338 /* Parse 'name=value' */
1339 char *pszProperty = RTStrDup(a->argv[2]);
1340 if (pszProperty)
1341 {
1342 char *pDelimiter = strchr(pszProperty, '=');
1343 if (pDelimiter)
1344 {
1345 *pDelimiter = '\0';
1346
1347 Bstr bstrName = pszProperty;
1348 Bstr bstrValue = &pDelimiter[1];
1349 CHECK_ERROR(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
1350 if (SUCCEEDED(hrc))
1351 fNeedsSaving = true;
1352 }
1353 else
1354 {
1355 errorSyntax(ControlVM::tr("Invalid vrdeproperty argument '%s'."), a->argv[2]);
1356 hrc = E_FAIL;
1357 }
1358 RTStrFree(pszProperty);
1359 }
1360 else
1361 {
1362 RTMsgError(ControlVM::tr("Failed to allocate memory for VRDE property '%s'."),
1363 a->argv[2]);
1364 hrc = E_FAIL;
1365 }
1366 }
1367 if (FAILED(hrc))
1368 {
1369 break;
1370 }
1371 }
1372 else if ( !strcmp(a->argv[1], "usbattach")
1373 || !strcmp(a->argv[1], "usbdetach"))
1374 {
1375 bool attach = !strcmp(a->argv[1], "usbattach");
1376 if (attach)
1377 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_USBATTACH);
1378 else
1379 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_USBDETACH);
1380
1381 if (a->argc < 3)
1382 {
1383 errorSyntax(ControlVM::tr("Not enough parameters."));
1384 hrc = E_FAIL;
1385 break;
1386 }
1387 else if (a->argc == 4 || a->argc > 5)
1388 {
1389 errorSyntax(ControlVM::tr("Wrong number of arguments."));
1390 hrc = E_FAIL;
1391 break;
1392 }
1393
1394 Bstr usbId = a->argv[2];
1395 Bstr captureFilename;
1396
1397 if (a->argc == 5)
1398 {
1399 if (!strcmp(a->argv[3], "--capturefile"))
1400 captureFilename = a->argv[4];
1401 else
1402 {
1403 errorSyntax(ControlVM::tr("Invalid parameter '%s'."), a->argv[3]);
1404 hrc = E_FAIL;
1405 break;
1406 }
1407 }
1408
1409 Guid guid(usbId);
1410 if (!guid.isValid())
1411 {
1412 // assume address
1413 if (attach)
1414 {
1415 ComPtr<IHost> host;
1416 CHECK_ERROR_BREAK(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
1417 SafeIfaceArray <IHostUSBDevice> coll;
1418 CHECK_ERROR_BREAK(host, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
1419 ComPtr<IHostUSBDevice> dev;
1420 CHECK_ERROR_BREAK(host, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
1421 dev.asOutParam()));
1422 CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
1423 }
1424 else
1425 {
1426 SafeIfaceArray <IUSBDevice> coll;
1427 CHECK_ERROR_BREAK(console, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
1428 ComPtr<IUSBDevice> dev;
1429 CHECK_ERROR_BREAK(console, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
1430 dev.asOutParam()));
1431 CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
1432 }
1433 }
1434 else if (guid.isZero())
1435 {
1436 errorSyntax(ControlVM::tr("Zero UUID argument '%s'."), a->argv[2]);
1437 hrc = E_FAIL;
1438 break;
1439 }
1440
1441 if (attach)
1442 CHECK_ERROR_BREAK(console, AttachUSBDevice(usbId.raw(), captureFilename.raw()));
1443 else
1444 {
1445 ComPtr<IUSBDevice> dev;
1446 CHECK_ERROR_BREAK(console, DetachUSBDevice(usbId.raw(),
1447 dev.asOutParam()));
1448 }
1449 }
1450 else if (!strcmp(a->argv[1], "setvideomodehint"))
1451 {
1452 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETVIDEOMODEHINT);
1453 if (a->argc != 5 && a->argc != 6 && a->argc != 7 && a->argc != 9)
1454 {
1455 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1456 hrc = E_FAIL;
1457 break;
1458 }
1459 bool fEnabled = true;
1460 uint32_t uXRes = RTStrToUInt32(a->argv[2]);
1461 uint32_t uYRes = RTStrToUInt32(a->argv[3]);
1462 uint32_t uBpp = RTStrToUInt32(a->argv[4]);
1463 uint32_t uDisplayIdx = 0;
1464 bool fChangeOrigin = false;
1465 int32_t iOriginX = 0;
1466 int32_t iOriginY = 0;
1467 if (a->argc >= 6)
1468 uDisplayIdx = RTStrToUInt32(a->argv[5]);
1469 if (a->argc >= 7)
1470 {
1471 if (RT_FAILURE(parseBool(a->argv[6], &fEnabled)))
1472 {
1473 errorSyntax(ControlVM::tr("Either \"yes\" or \"no\" is expected."));
1474 hrc = E_FAIL;
1475 break;
1476 }
1477 }
1478 if (a->argc == 9)
1479 {
1480 iOriginX = RTStrToInt32(a->argv[7]);
1481 iOriginY = RTStrToInt32(a->argv[8]);
1482 fChangeOrigin = true;
1483 }
1484
1485 ComPtr<IDisplay> pDisplay;
1486 CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
1487 if (!pDisplay)
1488 {
1489 RTMsgError(ControlVM::tr("Guest not running."));
1490 hrc = E_FAIL;
1491 break;
1492 }
1493 CHECK_ERROR_BREAK(pDisplay, SetVideoModeHint(uDisplayIdx, fEnabled,
1494 fChangeOrigin, iOriginX, iOriginY,
1495 uXRes, uYRes, uBpp, true));
1496 }
1497 else if (!strcmp(a->argv[1], "setscreenlayout"))
1498 {
1499 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETSCREENLAYOUT);
1500 if (a->argc < 4)
1501 {
1502 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1503 hrc = E_FAIL;
1504 break;
1505 }
1506
1507 ComPtr<IDisplay> pDisplay;
1508 CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
1509 if (!pDisplay)
1510 {
1511 RTMsgError(ControlVM::tr("Guest not running."));
1512 hrc = E_FAIL;
1513 break;
1514 }
1515
1516 com::SafeIfaceArray<IGuestScreenInfo> aGuestScreenInfos;
1517
1518 /* Parse "<display> on|primary <xorigin> <yorigin> <xres> <yres> <bpp> | off" sequences. */
1519 int argc = a->argc - 2;
1520 char **argv = &a->argv[2];
1521 while (argc >= 2)
1522 {
1523 ULONG aDisplay = RTStrToUInt32(argv[0]);
1524 BOOL aPrimary = FALSE;
1525
1526 GuestMonitorStatus_T aStatus;
1527 if (RTStrICmp(argv[1], "primary") == 0)
1528 {
1529 aStatus = GuestMonitorStatus_Enabled;
1530 aPrimary = TRUE;
1531 }
1532 else if (RTStrICmp(argv[1], "on") == 0)
1533 aStatus = GuestMonitorStatus_Enabled;
1534 else if (RTStrICmp(argv[1], "off") == 0)
1535 aStatus = GuestMonitorStatus_Disabled;
1536 else
1537 {
1538 errorSyntax(ControlVM::tr("Display status must be <on> or <off>."));
1539 hrc = E_FAIL;
1540 break;
1541 }
1542
1543 BOOL aChangeOrigin = FALSE;
1544 LONG aOriginX = 0;
1545 LONG aOriginY = 0;
1546 ULONG aWidth = 0;
1547 ULONG aHeight = 0;
1548 ULONG aBitsPerPixel = 0;
1549 if (aStatus == GuestMonitorStatus_Enabled)
1550 {
1551 if (argc < 7)
1552 {
1553 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1554 hrc = E_FAIL;
1555 break;
1556 }
1557
1558 aChangeOrigin = TRUE;
1559 aOriginX = RTStrToUInt32(argv[2]);
1560 aOriginY = RTStrToUInt32(argv[3]);
1561 aWidth = RTStrToUInt32(argv[4]);
1562 aHeight = RTStrToUInt32(argv[5]);
1563 aBitsPerPixel = RTStrToUInt32(argv[6]);
1564
1565 argc -= 7;
1566 argv += 7;
1567 }
1568 else
1569 {
1570 argc -= 2;
1571 argv += 2;
1572 }
1573
1574 ComPtr<IGuestScreenInfo> pInfo;
1575 CHECK_ERROR_BREAK(pDisplay, CreateGuestScreenInfo(aDisplay, aStatus, aPrimary, aChangeOrigin,
1576 aOriginX, aOriginY, aWidth, aHeight, aBitsPerPixel,
1577 pInfo.asOutParam()));
1578 aGuestScreenInfos.push_back(pInfo);
1579 }
1580
1581 if (FAILED(hrc))
1582 break;
1583
1584 CHECK_ERROR_BREAK(pDisplay, SetScreenLayout(ScreenLayoutMode_Apply, ComSafeArrayAsInParam(aGuestScreenInfos)));
1585 }
1586 else if (!strcmp(a->argv[1], "setcredentials"))
1587 {
1588 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SETCREDENTIALS);
1589 bool fAllowLocalLogon = true;
1590 if ( a->argc == 7
1591 || ( a->argc == 8
1592 && ( !strcmp(a->argv[3], "-p")
1593 || !strcmp(a->argv[3], "--passwordfile"))))
1594 {
1595 if ( strcmp(a->argv[5 + (a->argc - 7)], "--allowlocallogon")
1596 && strcmp(a->argv[5 + (a->argc - 7)], "-allowlocallogon"))
1597 {
1598 errorSyntax(ControlVM::tr("Invalid parameter '%s'."), a->argv[5]);
1599 hrc = E_FAIL;
1600 break;
1601 }
1602 if (!strcmp(a->argv[6 + (a->argc - 7)], "no"))
1603 fAllowLocalLogon = false;
1604 }
1605 else if ( a->argc != 5
1606 && ( a->argc != 6
1607 || ( strcmp(a->argv[3], "-p")
1608 && strcmp(a->argv[3], "--passwordfile"))))
1609 {
1610 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1611 hrc = E_FAIL;
1612 break;
1613 }
1614 Utf8Str passwd, domain;
1615 if (a->argc == 5 || a->argc == 7)
1616 {
1617 passwd = a->argv[3];
1618 domain = a->argv[4];
1619 }
1620 else
1621 {
1622 RTEXITCODE rcExit = readPasswordFile(a->argv[4], &passwd);
1623 if (rcExit != RTEXITCODE_SUCCESS)
1624 {
1625 hrc = E_FAIL;
1626 break;
1627 }
1628 domain = a->argv[5];
1629 }
1630
1631 ComPtr<IGuest> pGuest;
1632 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pGuest.asOutParam()));
1633 if (!pGuest)
1634 {
1635 RTMsgError(ControlVM::tr("Guest not running."));
1636 hrc = E_FAIL;
1637 break;
1638 }
1639 CHECK_ERROR_BREAK(pGuest, SetCredentials(Bstr(a->argv[2]).raw(),
1640 Bstr(passwd).raw(),
1641 Bstr(domain).raw(),
1642 fAllowLocalLogon));
1643 }
1644 else if (!strcmp(a->argv[1], "guestmemoryballoon"))
1645 {
1646 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_GUESTMEMORYBALLOON);
1647 if (a->argc != 3)
1648 {
1649 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1650 hrc = E_FAIL;
1651 break;
1652 }
1653 uint32_t uVal;
1654 int vrc;
1655 vrc = RTStrToUInt32Ex(a->argv[2], NULL, 0, &uVal);
1656 if (vrc != VINF_SUCCESS)
1657 {
1658 errorSyntax(ControlVM::tr("Error parsing guest memory balloon size '%s'."), a->argv[2]);
1659 hrc = E_FAIL;
1660 break;
1661 }
1662 /* guest is running; update IGuest */
1663 ComPtr<IGuest> pGuest;
1664 hrc = console->COMGETTER(Guest)(pGuest.asOutParam());
1665 if (SUCCEEDED(hrc))
1666 {
1667 if (!pGuest)
1668 {
1669 RTMsgError(ControlVM::tr("Guest not running."));
1670 hrc = E_FAIL;
1671 break;
1672 }
1673 CHECK_ERROR(pGuest, COMSETTER(MemoryBalloonSize)(uVal));
1674 }
1675 }
1676 else if (!strcmp(a->argv[1], "teleport"))
1677 {
1678 Bstr bstrHostname;
1679 uint32_t uMaxDowntime = 250 /*ms*/;
1680 uint32_t uPort = UINT32_MAX;
1681 uint32_t cMsTimeout = 0;
1682 Utf8Str strPassword;
1683 static const RTGETOPTDEF s_aTeleportOptions[] =
1684 {
1685 { "--host", 'h', RTGETOPT_REQ_STRING }, /** @todo RTGETOPT_FLAG_MANDATORY */
1686 { "--maxdowntime", 'd', RTGETOPT_REQ_UINT32 },
1687 { "--port", 'P', RTGETOPT_REQ_UINT32 }, /** @todo RTGETOPT_FLAG_MANDATORY */
1688 { "--passwordfile", 'p', RTGETOPT_REQ_STRING },
1689 { "--password", 'W', RTGETOPT_REQ_STRING },
1690 { "--timeout", 't', RTGETOPT_REQ_UINT32 },
1691 { "--detailed-progress", 'D', RTGETOPT_REQ_NOTHING }
1692 };
1693 RTGETOPTSTATE GetOptState;
1694 RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTeleportOptions, RT_ELEMENTS(s_aTeleportOptions), 2, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1695 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_TELEPORT);
1696 int ch;
1697 RTGETOPTUNION Value;
1698 while ( SUCCEEDED(hrc)
1699 && (ch = RTGetOpt(&GetOptState, &Value)))
1700 {
1701 switch (ch)
1702 {
1703 case 'h': bstrHostname = Value.psz; break;
1704 case 'd': uMaxDowntime = Value.u32; break;
1705 case 'D': g_fDetailedProgress = true; break;
1706 case 'P': uPort = Value.u32; break;
1707 case 'p':
1708 {
1709 RTEXITCODE rcExit = readPasswordFile(Value.psz, &strPassword);
1710 if (rcExit != RTEXITCODE_SUCCESS)
1711 hrc = E_FAIL;
1712 break;
1713 }
1714 case 'W': strPassword = Value.psz; break;
1715 case 't': cMsTimeout = Value.u32; break;
1716 default:
1717 errorGetOpt(ch, &Value);
1718 hrc = E_FAIL;
1719 break;
1720 }
1721 }
1722 if (FAILED(hrc))
1723 break;
1724
1725 ComPtr<IProgress> progress;
1726 CHECK_ERROR_BREAK(console, Teleport(bstrHostname.raw(), uPort,
1727 Bstr(strPassword).raw(),
1728 uMaxDowntime,
1729 progress.asOutParam()));
1730
1731 if (cMsTimeout)
1732 {
1733 hrc = progress->COMSETTER(Timeout)(cMsTimeout);
1734 if (FAILED(hrc) && hrc != VBOX_E_INVALID_OBJECT_STATE)
1735 CHECK_ERROR_BREAK(progress, COMSETTER(Timeout)(cMsTimeout)); /* lazyness */
1736 }
1737
1738 hrc = showProgress(progress);
1739 CHECK_PROGRESS_ERROR(progress, (ControlVM::tr("Teleportation failed")));
1740 }
1741 else if (!strcmp(a->argv[1], "screenshotpng"))
1742 {
1743 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_SCREENSHOTPNG);
1744 if (a->argc <= 2 || a->argc > 4)
1745 {
1746 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1747 hrc = E_FAIL;
1748 break;
1749 }
1750 int vrc;
1751 uint32_t iScreen = 0;
1752 if (a->argc == 4)
1753 {
1754 vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &iScreen);
1755 if (vrc != VINF_SUCCESS)
1756 {
1757 errorSyntax(ControlVM::tr("Error parsing display number '%s'."), a->argv[3]);
1758 hrc = E_FAIL;
1759 break;
1760 }
1761 }
1762 ComPtr<IDisplay> pDisplay;
1763 CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
1764 if (!pDisplay)
1765 {
1766 RTMsgError(ControlVM::tr("Guest not running."));
1767 hrc = E_FAIL;
1768 break;
1769 }
1770 ULONG width, height, bpp;
1771 LONG xOrigin, yOrigin;
1772 GuestMonitorStatus_T monitorStatus;
1773 CHECK_ERROR_BREAK(pDisplay, GetScreenResolution(iScreen, &width, &height, &bpp, &xOrigin, &yOrigin, &monitorStatus));
1774 com::SafeArray<BYTE> saScreenshot;
1775 CHECK_ERROR_BREAK(pDisplay, TakeScreenShotToArray(iScreen, width, height, BitmapFormat_PNG, ComSafeArrayAsOutParam(saScreenshot)));
1776 RTFILE pngFile = NIL_RTFILE;
1777 vrc = RTFileOpen(&pngFile, a->argv[2], RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_ALL);
1778 if (RT_FAILURE(vrc))
1779 {
1780 RTMsgError(ControlVM::tr("Failed to create file '%s' (%Rrc)."), a->argv[2], vrc);
1781 hrc = E_FAIL;
1782 break;
1783 }
1784 vrc = RTFileWrite(pngFile, saScreenshot.raw(), saScreenshot.size(), NULL);
1785 if (RT_FAILURE(vrc))
1786 {
1787 RTMsgError(ControlVM::tr("Failed to write screenshot to file '%s' (%Rrc)."), a->argv[2], vrc);
1788 hrc = E_FAIL;
1789 }
1790 RTFileClose(pngFile);
1791 }
1792#ifdef VBOX_WITH_RECORDING
1793 else if ( !strcmp(a->argv[1], "recording")
1794 || !strcmp(a->argv[1], "videocap") /* legacy command */)
1795 {
1796 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_RECORDING);
1797
1798 if (!strcmp(a->argv[1], "videocap"))
1799 RTMsgWarning(ControlVM::tr("Sub command 'videocap' is deprecated -- please use 'recording' instead ."));
1800
1801 if (a->argc < 3)
1802 {
1803 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1804 hrc = E_FAIL;
1805 break;
1806 }
1807
1808 ComPtr<IRecordingSettings> recordingSettings;
1809 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()));
1810
1811 SafeIfaceArray <IRecordingScreenSettings> saRecordingScreenScreens;
1812 CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saRecordingScreenScreens)));
1813
1814 ComPtr<IGraphicsAdapter> pGraphicsAdapter;
1815 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam()));
1816
1817 /* Note: For now all screens have the same configuration. */
1818
1819 bool fEnabled;
1820 if (RT_SUCCESS(parseBool(a->argv[2], &fEnabled)))
1821 {
1822 CHECK_ERROR_RET(recordingSettings, COMSETTER(Enabled)(fEnabled), RTEXITCODE_FAILURE);
1823
1824 if (fEnabled)
1825 RTPrintf(ControlVM::tr("Recording enabled. Use 'start' to start recording.\n"));
1826 }
1827 else if (!strcmp(a->argv[2], "start"))
1828 {
1829 bool fWait = false;
1830 if (a->argc >= 4 && !strcmp(a->argv[3], "--wait"))
1831 fWait = true;
1832
1833 ComPtr<IProgress> progress;
1834 CHECK_ERROR_BREAK(recordingSettings, Start(progress.asOutParam()));
1835
1836 if (fWait)
1837 {
1838 hrc = showProgress(progress, SHOW_PROGRESS_OPS);
1839 CHECK_PROGRESS_ERROR(progress, (ControlVM::tr("Recording failed.")));
1840 }
1841 else
1842 RTPrintf(ControlVM::tr("Recording started (detacted).\n"));
1843 }
1844 else if (!strcmp(a->argv[2], "stop"))
1845 {
1846 ComPtr<IProgress> progress;
1847 CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Progress)(progress.asOutParam()));
1848 CHECK_ERROR_BREAK(progress, Cancel());
1849 }
1850 else if (!strcmp(a->argv[2], "attach"))
1851 {
1852 ComPtr<IProgress> progress;
1853 CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Progress)(progress.asOutParam()));
1854 hrc = showProgress(progress, SHOW_PROGRESS_OPS);
1855 }
1856 else if (!strcmp(a->argv[2], "screens"))
1857 {
1858 ULONG cMonitors = 64;
1859 CHECK_ERROR_BREAK(pGraphicsAdapter, COMGETTER(MonitorCount)(&cMonitors));
1860 com::SafeArray<BOOL> saScreens(cMonitors);
1861 if (a->argc != 4)
1862 {
1863 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1864 hrc = E_FAIL;
1865 break;
1866 }
1867 if (RT_FAILURE(parseScreens(a->argv[3], &saScreens)))
1868 {
1869 errorSyntax(ControlVM::tr("Error parsing list of screen IDs '%s'."), a->argv[3]);
1870 hrc = E_FAIL;
1871 break;
1872 }
1873
1874 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1875 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Enabled)(saScreens[i]));
1876 }
1877 else if (!strcmp(a->argv[2], "filename"))
1878 {
1879 if (a->argc != 4)
1880 {
1881 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1882 hrc = E_FAIL;
1883 break;
1884 }
1885
1886 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1887 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Filename)(Bstr(a->argv[3]).raw()));
1888 }
1889 else if ( !strcmp(a->argv[2], "videores")
1890 || !strcmp(a->argv[2], "videoresolution"))
1891 {
1892 if (a->argc != 5)
1893 {
1894 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1895 hrc = E_FAIL;
1896 break;
1897 }
1898
1899 uint32_t uWidth;
1900 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uWidth);
1901 if (RT_FAILURE(vrc))
1902 {
1903 errorSyntax(ControlVM::tr("Error parsing video width '%s'."), a->argv[3]);
1904 hrc = E_FAIL;
1905 break;
1906 }
1907
1908 uint32_t uHeight;
1909 vrc = RTStrToUInt32Ex(a->argv[4], NULL, 0, &uHeight);
1910 if (RT_FAILURE(vrc))
1911 {
1912 errorSyntax(ControlVM::tr("Error parsing video height '%s'."), a->argv[4]);
1913 hrc = E_FAIL;
1914 break;
1915 }
1916
1917 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1918 {
1919 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoWidth)(uWidth));
1920 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoHeight)(uHeight));
1921 }
1922 }
1923 else if (!strcmp(a->argv[2], "videorate"))
1924 {
1925 if (a->argc != 4)
1926 {
1927 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1928 hrc = E_FAIL;
1929 break;
1930 }
1931
1932 uint32_t uRate;
1933 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uRate);
1934 if (RT_FAILURE(vrc))
1935 {
1936 errorSyntax(ControlVM::tr("Error parsing video rate '%s'."), a->argv[3]);
1937 hrc = E_FAIL;
1938 break;
1939 }
1940
1941 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1942 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoRate)(uRate));
1943 }
1944 else if (!strcmp(a->argv[2], "videofps"))
1945 {
1946 if (a->argc != 4)
1947 {
1948 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1949 hrc = E_FAIL;
1950 break;
1951 }
1952
1953 uint32_t uFPS;
1954 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uFPS);
1955 if (RT_FAILURE(vrc))
1956 {
1957 errorSyntax(ControlVM::tr("Error parsing video FPS '%s'."), a->argv[3]);
1958 hrc = E_FAIL;
1959 break;
1960 }
1961
1962 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1963 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoFPS)(uFPS));
1964 }
1965 else if (!strcmp(a->argv[2], "maxtime"))
1966 {
1967 if (a->argc != 4)
1968 {
1969 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1970 hrc = E_FAIL;
1971 break;
1972 }
1973
1974 uint32_t uMaxTime;
1975 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxTime);
1976 if (RT_FAILURE(vrc))
1977 {
1978 errorSyntax(ControlVM::tr("Error parsing maximum time '%s'."), a->argv[3]);
1979 hrc = E_FAIL;
1980 break;
1981 }
1982
1983 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1984 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(MaxTime)(uMaxTime));
1985 }
1986 else if (!strcmp(a->argv[2], "maxfilesize"))
1987 {
1988 if (a->argc != 4)
1989 {
1990 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
1991 hrc = E_FAIL;
1992 break;
1993 }
1994
1995 uint32_t uMaxFileSize;
1996 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxFileSize);
1997 if (RT_FAILURE(vrc))
1998 {
1999 errorSyntax(ControlVM::tr("Error parsing maximum file size '%s'."), a->argv[3]);
2000 hrc = E_FAIL;
2001 break;
2002 }
2003
2004 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
2005 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(MaxFileSize)(uMaxFileSize));
2006 }
2007 else if (!strcmp(a->argv[2], "opts"))
2008 {
2009 if (a->argc != 4)
2010 {
2011 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
2012 hrc = E_FAIL;
2013 break;
2014 }
2015
2016 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
2017 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Options)(Bstr(a->argv[3]).raw()));
2018 }
2019 }
2020#endif /* VBOX_WITH_RECORDING */
2021 else if (!strcmp(a->argv[1], "webcam"))
2022 {
2023 if (a->argc < 3)
2024 {
2025 errorArgument(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
2026 hrc = E_FAIL;
2027 break;
2028 }
2029
2030 ComPtr<IEmulatedUSB> pEmulatedUSB;
2031 CHECK_ERROR_BREAK(console, COMGETTER(EmulatedUSB)(pEmulatedUSB.asOutParam()));
2032 if (!pEmulatedUSB)
2033 {
2034 RTMsgError(ControlVM::tr("Guest not running."));
2035 hrc = E_FAIL;
2036 break;
2037 }
2038
2039 if (!strcmp(a->argv[2], "attach"))
2040 {
2041 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_ATTACH);
2042 Bstr path("");
2043 if (a->argc >= 4)
2044 path = a->argv[3];
2045 Bstr settings("");
2046 if (a->argc >= 5)
2047 settings = a->argv[4];
2048 CHECK_ERROR_BREAK(pEmulatedUSB, WebcamAttach(path.raw(), settings.raw()));
2049 }
2050 else if (!strcmp(a->argv[2], "detach"))
2051 {
2052 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_DETACH);
2053 Bstr path("");
2054 if (a->argc >= 4)
2055 path = a->argv[3];
2056 CHECK_ERROR_BREAK(pEmulatedUSB, WebcamDetach(path.raw()));
2057 }
2058 else if (!strcmp(a->argv[2], "list"))
2059 {
2060 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_WEBCAM_LIST);
2061 com::SafeArray <BSTR> webcams;
2062 CHECK_ERROR_BREAK(pEmulatedUSB, COMGETTER(Webcams)(ComSafeArrayAsOutParam(webcams)));
2063 for (size_t i = 0; i < webcams.size(); ++i)
2064 {
2065 RTPrintf("%ls\n", webcams[i][0]? webcams[i]: Bstr("default").raw());
2066 }
2067 }
2068 else
2069 {
2070 errorArgument(ControlVM::tr("Invalid argument to '%s'."), a->argv[1]);
2071 hrc = E_FAIL;
2072 break;
2073 }
2074 }
2075 else if (!strcmp(a->argv[1], "addencpassword"))
2076 {
2077 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_ADDENCPASSWORD);
2078 if ( a->argc != 4
2079 && a->argc != 6)
2080 {
2081 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
2082 break;
2083 }
2084
2085 BOOL fRemoveOnSuspend = FALSE;
2086 if (a->argc == 6)
2087 {
2088 if ( strcmp(a->argv[4], "--removeonsuspend")
2089 || ( strcmp(a->argv[5], "yes")
2090 && strcmp(a->argv[5], "no")))
2091 {
2092 errorSyntax(ControlVM::tr("Invalid parameters."));
2093 break;
2094 }
2095 if (!strcmp(a->argv[5], "yes"))
2096 fRemoveOnSuspend = TRUE;
2097 }
2098
2099 Bstr bstrPwId(a->argv[2]);
2100 Utf8Str strPassword;
2101
2102 if (!RTStrCmp(a->argv[3], "-"))
2103 {
2104 /* Get password from console. */
2105 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, ControlVM::tr("Enter password:"));
2106 if (rcExit == RTEXITCODE_FAILURE)
2107 break;
2108 }
2109 else
2110 {
2111 RTEXITCODE rcExit = readPasswordFile(a->argv[3], &strPassword);
2112 if (rcExit == RTEXITCODE_FAILURE)
2113 {
2114 RTMsgError(ControlVM::tr("Failed to read new password from file."));
2115 break;
2116 }
2117 }
2118
2119 CHECK_ERROR_BREAK(console, AddEncryptionPassword(bstrPwId.raw(), Bstr(strPassword).raw(), fRemoveOnSuspend));
2120 }
2121 else if (!strcmp(a->argv[1], "removeencpassword"))
2122 {
2123 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_REMOVEENCPASSWORD);
2124 if (a->argc != 3)
2125 {
2126 errorSyntax(ControlVM::tr("Incorrect number of parameters."));
2127 break;
2128 }
2129 Bstr bstrPwId(a->argv[2]);
2130 CHECK_ERROR_BREAK(console, RemoveEncryptionPassword(bstrPwId.raw()));
2131 }
2132 /** @todo r=bird: 'removeallencpasswords' is very much unreadable carp. Use
2133 * dashes as word separators to make it less fishy. */
2134 else if (!strcmp(a->argv[1], "removeallencpasswords"))
2135 {
2136 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_REMOVEALLENCPASSWORDS);
2137 CHECK_ERROR_BREAK(console, ClearAllEncryptionPasswords());
2138 }
2139 else if (!strncmp(a->argv[1], "changeuartmode", 14))
2140 {
2141 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_CHANGEUARTMODE);
2142 unsigned n = parseNum(&a->argv[1][14], 4, "UART");
2143 if (!n)
2144 {
2145 hrc = E_FAIL;
2146 break;
2147 }
2148 if (a->argc < 3)
2149 {
2150 errorSyntax(ControlVM::tr("Missing argument to '%s'."), a->argv[1]);
2151 hrc = E_FAIL;
2152 break;
2153 }
2154
2155 ComPtr<ISerialPort> uart;
2156
2157 CHECK_ERROR_BREAK(sessionMachine, GetSerialPort(n - 1, uart.asOutParam()));
2158 ASSERT(uart);
2159
2160 if (!RTStrICmp(a->argv[2], "disconnected"))
2161 {
2162 if (a->argc != 3)
2163 {
2164 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2165 hrc = E_FAIL;
2166 break;
2167 }
2168 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
2169 }
2170 else if ( !RTStrICmp(a->argv[2], "server")
2171 || !RTStrICmp(a->argv[2], "client")
2172 || !RTStrICmp(a->argv[2], "tcpserver")
2173 || !RTStrICmp(a->argv[2], "tcpclient")
2174 || !RTStrICmp(a->argv[2], "file"))
2175 {
2176 const char *pszMode = a->argv[2];
2177 if (a->argc != 4)
2178 {
2179 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2180 hrc = E_FAIL;
2181 break;
2182 }
2183
2184 CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[3]).raw()));
2185
2186 /*
2187 * Change to disconnected first to get changes in just a parameter causing
2188 * the correct changes later on.
2189 */
2190 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
2191 if (!RTStrICmp(pszMode, "server"))
2192 {
2193 CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
2194 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
2195 }
2196 else if (!RTStrICmp(pszMode, "client"))
2197 {
2198 CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
2199 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
2200 }
2201 else if (!RTStrICmp(pszMode, "tcpserver"))
2202 {
2203 CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
2204 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
2205 }
2206 else if (!RTStrICmp(pszMode, "tcpclient"))
2207 {
2208 CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
2209 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
2210 }
2211 else if (!RTStrICmp(pszMode, "file"))
2212 {
2213 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_RawFile));
2214 }
2215 }
2216 else
2217 {
2218 if (a->argc != 3)
2219 {
2220 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2221 hrc = E_FAIL;
2222 break;
2223 }
2224 CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[2]).raw()));
2225 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostDevice));
2226 }
2227 }
2228 else if (!strncmp(a->argv[1], "vm-process-priority", 14))
2229 {
2230 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_VM_PROCESS_PRIORITY);
2231 if (a->argc != 3)
2232 {
2233 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2234 hrc = E_FAIL;
2235 break;
2236 }
2237 VMProcPriority_T enmPriority = nameToVMProcPriority(a->argv[2]);
2238 if (enmPriority == VMProcPriority_Invalid)
2239 {
2240 errorSyntax(ControlVM::tr("Invalid vm-process-priority '%s'."), a->argv[2]);
2241 hrc = E_FAIL;
2242 }
2243 else
2244 {
2245 CHECK_ERROR(sessionMachine, COMSETTER(VMProcessPriority)(enmPriority));
2246 }
2247 break;
2248 }
2249 else if (!strncmp(a->argv[1], "autostart-enabled", 17))
2250 {
2251 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUTOSTART_ENABLED);
2252 if (a->argc != 3)
2253 {
2254 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2255 hrc = E_FAIL;
2256 break;
2257 }
2258 bool fEnabled;
2259 if (RT_FAILURE(parseBool(a->argv[2], &fEnabled)))
2260 {
2261 errorSyntax(ControlVM::tr("Invalid value '%s'."), a->argv[2]);
2262 hrc = E_FAIL;
2263 break;
2264 }
2265 CHECK_ERROR(sessionMachine, COMSETTER(AutostartEnabled)(TRUE));
2266 fNeedsSaving = true;
2267 break;
2268 }
2269 else if (!strncmp(a->argv[1], "autostart-delay", 15))
2270 {
2271 setCurrentSubcommand(HELP_SCOPE_CONTROLVM_AUTOSTART_DELAY);
2272 if (a->argc != 3)
2273 {
2274 errorSyntax(ControlVM::tr("Incorrect arguments to '%s'."), a->argv[1]);
2275 hrc = E_FAIL;
2276 break;
2277 }
2278 uint32_t u32;
2279 char *pszNext;
2280 int vrc = RTStrToUInt32Ex(a->argv[2], &pszNext, 10, &u32);
2281 if (RT_FAILURE(vrc) || *pszNext != '\0')
2282 {
2283 errorSyntax(ControlVM::tr("Invalid autostart delay number '%s'."), a->argv[2]);
2284 hrc = E_FAIL;
2285 break;
2286 }
2287 CHECK_ERROR(sessionMachine, COMSETTER(AutostartDelay)(u32));
2288 if (SUCCEEDED(hrc))
2289 fNeedsSaving = true;
2290 break;
2291 }
2292 else
2293 {
2294 errorSyntax(ControlVM::tr("Invalid parameter '%s'."), a->argv[1]);
2295 hrc = E_FAIL;
2296 }
2297 } while (0);
2298
2299 /* The client has to trigger saving the state explicitely. */
2300 if (fNeedsSaving)
2301 CHECK_ERROR(sessionMachine, SaveSettings());
2302
2303 a->session->UnlockMachine();
2304
2305 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2306}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette