VirtualBox

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

Last change on this file since 76433 was 76433, checked in by vboxsync, 5 years ago

VBoxManage: getopt includes errcore.h now, mostly no need for err.h any more. bugref:9344

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

© 2023 Oracle
ContactPrivacy policyTerms of Use