VirtualBox

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

Last change on this file since 75390 was 75390, checked in by vboxsync, 6 years ago

Recording/FE/VBoxManage: Fixes for "recording filename" and "recording videores"; a bit of cleanup / renaming.

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