VirtualBox

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

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

Frontends/VBoxManage: Fix configuring the recording feature. It errored out due to using the wrong machine object. Restore the old command as an alias, it doesn't cost much and avoids annoying users who use the command to enable recording which worked in 5.2.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.6 KB
Line 
1/* $Id: VBoxManageControlVM.cpp 79363 2019-06-26 15:25:43Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of the controlvm command.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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
43VMProcPriority_T nameToVMProcPriority(const char *pszName);
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, true));
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(sessionMachine, 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(sessionMachine, 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 || !strcmp(a->argv[1], "videocap") /* legacy command */)
1859 {
1860 if (a->argc < 3)
1861 {
1862 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
1863 rc = E_FAIL;
1864 break;
1865 }
1866
1867 ComPtr<IRecordingSettings> recordingSettings;
1868 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()));
1869
1870 SafeIfaceArray <IRecordingScreenSettings> saRecordingScreenScreens;
1871 CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saRecordingScreenScreens)));
1872
1873 /* Note: For now all screens have the same configuration. */
1874
1875 /*
1876 * Note: Commands starting with "vcp" are the deprecated versions and are
1877 * kept to ensure backwards compatibility.
1878 */
1879 if (!strcmp(a->argv[2], "on"))
1880 {
1881 CHECK_ERROR_RET(recordingSettings, COMSETTER(Enabled)(TRUE), RTEXITCODE_FAILURE);
1882 }
1883 else if (!strcmp(a->argv[2], "off"))
1884 {
1885 CHECK_ERROR_RET(recordingSettings, COMSETTER(Enabled)(FALSE), RTEXITCODE_FAILURE);
1886 }
1887 else if (!strcmp(a->argv[2], "screens"))
1888 {
1889 ULONG cMonitors = 64;
1890 CHECK_ERROR_BREAK(sessionMachine, COMGETTER(MonitorCount)(&cMonitors));
1891 com::SafeArray<BOOL> saScreens(cMonitors);
1892 if ( a->argc == 4
1893 && !strcmp(a->argv[3], "all"))
1894 {
1895 /* enable all screens */
1896 for (unsigned i = 0; i < cMonitors; i++)
1897 saScreens[i] = true;
1898 }
1899 else if ( a->argc == 4
1900 && !strcmp(a->argv[3], "none"))
1901 {
1902 /* disable all screens */
1903 for (unsigned i = 0; i < cMonitors; i++)
1904 saScreens[i] = false;
1905
1906 /** @todo r=andy What if this is specified? */
1907 }
1908 else
1909 {
1910 /* enable selected screens */
1911 for (unsigned i = 0; i < cMonitors; i++)
1912 saScreens[i] = false;
1913 for (int i = 3; SUCCEEDED(rc) && i < a->argc; i++)
1914 {
1915 uint32_t iScreen;
1916 int vrc = RTStrToUInt32Ex(a->argv[i], NULL, 0, &iScreen);
1917 if (vrc != VINF_SUCCESS)
1918 {
1919 errorArgument("Error parsing display number '%s'", a->argv[i]);
1920 rc = E_FAIL;
1921 break;
1922 }
1923 if (iScreen >= cMonitors)
1924 {
1925 errorArgument("Invalid screen ID specified '%u'", iScreen);
1926 rc = E_FAIL;
1927 break;
1928 }
1929 saScreens[iScreen] = true;
1930 }
1931 }
1932
1933 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1934 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Enabled)(saScreens[i]));
1935 }
1936 else if (!strcmp(a->argv[2], "filename"))
1937 {
1938 if (a->argc != 4)
1939 {
1940 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
1941 rc = E_FAIL;
1942 break;
1943 }
1944
1945 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1946 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Filename)(Bstr(a->argv[3]).raw()));
1947 }
1948 else if ( !strcmp(a->argv[2], "videores")
1949 || !strcmp(a->argv[2], "videoresolution"))
1950 {
1951 if (a->argc != 5)
1952 {
1953 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
1954 rc = E_FAIL;
1955 break;
1956 }
1957
1958 uint32_t uWidth;
1959 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uWidth);
1960 if (RT_FAILURE(vrc))
1961 {
1962 errorArgument("Error parsing video width '%s'", a->argv[3]);
1963 rc = E_FAIL;
1964 break;
1965 }
1966
1967 uint32_t uHeight;
1968 vrc = RTStrToUInt32Ex(a->argv[4], NULL, 0, &uHeight);
1969 if (RT_FAILURE(vrc))
1970 {
1971 errorArgument("Error parsing video height '%s'", a->argv[4]);
1972 rc = E_FAIL;
1973 break;
1974 }
1975
1976 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
1977 {
1978 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoWidth)(uWidth));
1979 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoHeight)(uHeight));
1980 }
1981 }
1982 else if (!strcmp(a->argv[2], "videorate"))
1983 {
1984 if (a->argc != 4)
1985 {
1986 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
1987 rc = E_FAIL;
1988 break;
1989 }
1990
1991 uint32_t uRate;
1992 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uRate);
1993 if (RT_FAILURE(vrc))
1994 {
1995 errorArgument("Error parsing video rate '%s'", a->argv[3]);
1996 rc = E_FAIL;
1997 break;
1998 }
1999
2000 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
2001 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoRate)(uRate));
2002 }
2003 else if (!strcmp(a->argv[2], "videofps"))
2004 {
2005 if (a->argc != 4)
2006 {
2007 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
2008 rc = E_FAIL;
2009 break;
2010 }
2011
2012 uint32_t uFPS;
2013 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uFPS);
2014 if (RT_FAILURE(vrc))
2015 {
2016 errorArgument("Error parsing video FPS '%s'", a->argv[3]);
2017 rc = E_FAIL;
2018 break;
2019 }
2020
2021 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
2022 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoFPS)(uFPS));
2023 }
2024 else if (!strcmp(a->argv[2], "maxtime"))
2025 {
2026 if (a->argc != 4)
2027 {
2028 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
2029 rc = E_FAIL;
2030 break;
2031 }
2032
2033 uint32_t uMaxTime;
2034 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxTime);
2035 if (RT_FAILURE(vrc))
2036 {
2037 errorArgument("Error parsing maximum time '%s'", a->argv[3]);
2038 rc = E_FAIL;
2039 break;
2040 }
2041
2042 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
2043 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(MaxTime)(uMaxTime));
2044 }
2045 else if (!strcmp(a->argv[2], "maxfilesize"))
2046 {
2047 if (a->argc != 4)
2048 {
2049 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
2050 rc = E_FAIL;
2051 break;
2052 }
2053
2054 uint32_t uMaxFileSize;
2055 int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxFileSize);
2056 if (RT_FAILURE(vrc))
2057 {
2058 errorArgument("Error parsing maximum file size '%s'", a->argv[3]);
2059 rc = E_FAIL;
2060 break;
2061 }
2062
2063 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
2064 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(MaxFileSize)(uMaxFileSize));
2065 }
2066 else if (!strcmp(a->argv[2], "opts"))
2067 {
2068 if (a->argc != 4)
2069 {
2070 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
2071 rc = E_FAIL;
2072 break;
2073 }
2074
2075 for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
2076 CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Options)(Bstr(a->argv[3]).raw()));
2077 }
2078 }
2079#endif /* VBOX_WITH_RECORDING */
2080 else if (!strcmp(a->argv[1], "webcam"))
2081 {
2082 if (a->argc < 3)
2083 {
2084 errorArgument("Missing argument to '%s'", a->argv[1]);
2085 rc = E_FAIL;
2086 break;
2087 }
2088
2089 ComPtr<IEmulatedUSB> pEmulatedUSB;
2090 CHECK_ERROR_BREAK(console, COMGETTER(EmulatedUSB)(pEmulatedUSB.asOutParam()));
2091 if (!pEmulatedUSB)
2092 {
2093 RTMsgError("Guest not running");
2094 rc = E_FAIL;
2095 break;
2096 }
2097
2098 if (!strcmp(a->argv[2], "attach"))
2099 {
2100 Bstr path("");
2101 if (a->argc >= 4)
2102 path = a->argv[3];
2103 Bstr settings("");
2104 if (a->argc >= 5)
2105 settings = a->argv[4];
2106 CHECK_ERROR_BREAK(pEmulatedUSB, WebcamAttach(path.raw(), settings.raw()));
2107 }
2108 else if (!strcmp(a->argv[2], "detach"))
2109 {
2110 Bstr path("");
2111 if (a->argc >= 4)
2112 path = a->argv[3];
2113 CHECK_ERROR_BREAK(pEmulatedUSB, WebcamDetach(path.raw()));
2114 }
2115 else if (!strcmp(a->argv[2], "list"))
2116 {
2117 com::SafeArray <BSTR> webcams;
2118 CHECK_ERROR_BREAK(pEmulatedUSB, COMGETTER(Webcams)(ComSafeArrayAsOutParam(webcams)));
2119 for (size_t i = 0; i < webcams.size(); ++i)
2120 {
2121 RTPrintf("%ls\n", webcams[i][0]? webcams[i]: Bstr("default").raw());
2122 }
2123 }
2124 else
2125 {
2126 errorArgument("Invalid argument to '%s'", a->argv[1]);
2127 rc = E_FAIL;
2128 break;
2129 }
2130 }
2131 else if (!strcmp(a->argv[1], "addencpassword"))
2132 {
2133 if ( a->argc != 4
2134 && a->argc != 6)
2135 {
2136 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
2137 break;
2138 }
2139
2140 BOOL fRemoveOnSuspend = FALSE;
2141 if (a->argc == 6)
2142 {
2143 if ( strcmp(a->argv[4], "--removeonsuspend")
2144 || ( strcmp(a->argv[5], "yes")
2145 && strcmp(a->argv[5], "no")))
2146 {
2147 errorSyntax(USAGE_CONTROLVM, "Invalid parameters");
2148 break;
2149 }
2150 if (!strcmp(a->argv[5], "yes"))
2151 fRemoveOnSuspend = TRUE;
2152 }
2153
2154 Bstr bstrPwId(a->argv[2]);
2155 Utf8Str strPassword;
2156
2157 if (!RTStrCmp(a->argv[3], "-"))
2158 {
2159 /* Get password from console. */
2160 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter password:");
2161 if (rcExit == RTEXITCODE_FAILURE)
2162 break;
2163 }
2164 else
2165 {
2166 RTEXITCODE rcExit = readPasswordFile(a->argv[3], &strPassword);
2167 if (rcExit == RTEXITCODE_FAILURE)
2168 {
2169 RTMsgError("Failed to read new password from file");
2170 break;
2171 }
2172 }
2173
2174 CHECK_ERROR_BREAK(console, AddDiskEncryptionPassword(bstrPwId.raw(), Bstr(strPassword).raw(), fRemoveOnSuspend));
2175 }
2176 else if (!strcmp(a->argv[1], "removeencpassword"))
2177 {
2178 if (a->argc != 3)
2179 {
2180 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
2181 break;
2182 }
2183 Bstr bstrPwId(a->argv[2]);
2184 CHECK_ERROR_BREAK(console, RemoveDiskEncryptionPassword(bstrPwId.raw()));
2185 }
2186 else if (!strcmp(a->argv[1], "removeallencpasswords"))
2187 {
2188 CHECK_ERROR_BREAK(console, ClearAllDiskEncryptionPasswords());
2189 }
2190 else if (!strncmp(a->argv[1], "changeuartmode", 14))
2191 {
2192 unsigned n = parseNum(&a->argv[1][14], 4, "UART");
2193 if (!n)
2194 {
2195 rc = E_FAIL;
2196 break;
2197 }
2198 if (a->argc < 3)
2199 {
2200 errorArgument("Missing argument to '%s'", a->argv[1]);
2201 rc = E_FAIL;
2202 break;
2203 }
2204
2205 ComPtr<ISerialPort> uart;
2206
2207 CHECK_ERROR_BREAK(sessionMachine, GetSerialPort(n - 1, uart.asOutParam()));
2208 ASSERT(uart);
2209
2210 if (!RTStrICmp(a->argv[2], "disconnected"))
2211 {
2212 if (a->argc != 3)
2213 {
2214 errorArgument("Incorrect arguments to '%s'", a->argv[1]);
2215 rc = E_FAIL;
2216 break;
2217 }
2218 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
2219 }
2220 else if ( !RTStrICmp(a->argv[2], "server")
2221 || !RTStrICmp(a->argv[2], "client")
2222 || !RTStrICmp(a->argv[2], "tcpserver")
2223 || !RTStrICmp(a->argv[2], "tcpclient")
2224 || !RTStrICmp(a->argv[2], "file"))
2225 {
2226 const char *pszMode = a->argv[2];
2227 if (a->argc != 4)
2228 {
2229 errorArgument("Incorrect arguments to '%s'", a->argv[1]);
2230 rc = E_FAIL;
2231 break;
2232 }
2233
2234 CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[3]).raw()));
2235
2236 /*
2237 * Change to disconnected first to get changes in just a parameter causing
2238 * the correct changes later on.
2239 */
2240 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
2241 if (!RTStrICmp(pszMode, "server"))
2242 {
2243 CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
2244 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
2245 }
2246 else if (!RTStrICmp(pszMode, "client"))
2247 {
2248 CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
2249 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
2250 }
2251 else if (!RTStrICmp(pszMode, "tcpserver"))
2252 {
2253 CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
2254 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
2255 }
2256 else if (!RTStrICmp(pszMode, "tcpclient"))
2257 {
2258 CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
2259 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
2260 }
2261 else if (!RTStrICmp(pszMode, "file"))
2262 {
2263 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_RawFile));
2264 }
2265 }
2266 else
2267 {
2268 if (a->argc != 3)
2269 {
2270 errorArgument("Incorrect arguments to '%s'", a->argv[1]);
2271 rc = E_FAIL;
2272 break;
2273 }
2274 CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[2]).raw()));
2275 CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostDevice));
2276 }
2277 }
2278 else if (!strncmp(a->argv[1], "vm-process-priority", 14))
2279 {
2280 if (a->argc != 3)
2281 {
2282 errorArgument("Incorrect arguments to '%s'", a->argv[1]);
2283 rc = E_FAIL;
2284 break;
2285 }
2286 VMProcPriority_T enmPriority = nameToVMProcPriority(a->argv[2]);
2287 if (enmPriority == VMProcPriority_Invalid)
2288 {
2289 errorArgument("Invalid vm-process-priority '%s'", a->argv[2]);
2290 rc = E_FAIL;
2291 }
2292 else
2293 {
2294 CHECK_ERROR(sessionMachine, COMSETTER(VMProcessPriority)(enmPriority));
2295 }
2296 break;
2297 }
2298 else
2299 {
2300 errorSyntax(USAGE_CONTROLVM, "Invalid parameter '%s'", a->argv[1]);
2301 rc = E_FAIL;
2302 }
2303 } while (0);
2304
2305 /* The client has to trigger saving the state explicitely. */
2306 if (fNeedsSaving)
2307 CHECK_ERROR(sessionMachine, SaveSettings());
2308
2309 a->session->UnlockMachine();
2310
2311 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2312}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use