VirtualBox

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

Last change on this file since 44824 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.5 KB
Line 
1/* $Id: VBoxManageControlVM.cpp 44528 2013-02-04 14:27:54Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of the controlvm command.
4 */
5
6/*
7 * Copyright (C) 2006-2013 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/EventQueue.h>
29
30#include <VBox/com/VirtualBox.h>
31
32#include <iprt/ctype.h>
33#include <VBox/err.h>
34#include <iprt/getopt.h>
35#include <iprt/stream.h>
36#include <iprt/string.h>
37#include <iprt/uuid.h>
38#include <iprt/file.h>
39#include <VBox/log.h>
40
41#include "VBoxManage.h"
42
43#include <list>
44
45
46/**
47 * Parses a number.
48 *
49 * @returns Valid number on success.
50 * @returns 0 if invalid number. All necessary bitching has been done.
51 * @param psz Pointer to the nic number.
52 */
53static unsigned parseNum(const char *psz, unsigned cMaxNum, const char *name)
54{
55 uint32_t u32;
56 char *pszNext;
57 int rc = RTStrToUInt32Ex(psz, &pszNext, 10, &u32);
58 if ( RT_SUCCESS(rc)
59 && *pszNext == '\0'
60 && u32 >= 1
61 && u32 <= cMaxNum)
62 return (unsigned)u32;
63 errorArgument("Invalid %s number '%s'", name, psz);
64 return 0;
65}
66
67unsigned int getMaxNics(IVirtualBox* vbox, IMachine* mach)
68{
69 ComPtr <ISystemProperties> info;
70 ChipsetType_T aChipset;
71 ULONG NetworkAdapterCount = 0;
72 HRESULT rc;
73
74 do {
75 CHECK_ERROR_BREAK(vbox, COMGETTER(SystemProperties)(info.asOutParam()));
76 CHECK_ERROR_BREAK(mach, COMGETTER(ChipsetType)(&aChipset));
77 CHECK_ERROR_BREAK(info, GetMaxNetworkAdapters(aChipset, &NetworkAdapterCount));
78
79 return (unsigned int)NetworkAdapterCount;
80 } while (0);
81
82 return 0;
83}
84
85
86int handleControlVM(HandlerArg *a)
87{
88 using namespace com;
89 HRESULT rc;
90
91 if (a->argc < 2)
92 return errorSyntax(USAGE_CONTROLVM, "Not enough parameters");
93
94 /* try to find the given machine */
95 ComPtr <IMachine> machine;
96 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
97 machine.asOutParam()));
98 if (FAILED(rc))
99 return 1;
100
101 /* open a session for the VM */
102 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);
103
104 do
105 {
106 /* get the associated console */
107 ComPtr<IConsole> console;
108 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
109 /* ... and session machine */
110 ComPtr<IMachine> sessionMachine;
111 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
112
113 /* which command? */
114 if (!strcmp(a->argv[1], "pause"))
115 {
116 CHECK_ERROR_BREAK(console, Pause());
117 }
118 else if (!strcmp(a->argv[1], "resume"))
119 {
120 CHECK_ERROR_BREAK(console, Resume());
121 }
122 else if (!strcmp(a->argv[1], "reset"))
123 {
124 CHECK_ERROR_BREAK(console, Reset());
125 }
126 else if (!strcmp(a->argv[1], "unplugcpu"))
127 {
128 if (a->argc <= 1 + 1)
129 {
130 errorArgument("Missing argument to '%s'. Expected CPU number.", a->argv[1]);
131 rc = E_FAIL;
132 break;
133 }
134
135 unsigned n = parseNum(a->argv[2], 32, "CPU");
136
137 CHECK_ERROR_BREAK(sessionMachine, HotUnplugCPU(n));
138 }
139 else if (!strcmp(a->argv[1], "plugcpu"))
140 {
141 if (a->argc <= 1 + 1)
142 {
143 errorArgument("Missing argument to '%s'. Expected CPU number.", a->argv[1]);
144 rc = E_FAIL;
145 break;
146 }
147
148 unsigned n = parseNum(a->argv[2], 32, "CPU");
149
150 CHECK_ERROR_BREAK(sessionMachine, HotPlugCPU(n));
151 }
152 else if (!strcmp(a->argv[1], "cpuexecutioncap"))
153 {
154 if (a->argc <= 1 + 1)
155 {
156 errorArgument("Missing argument to '%s'. Expected execution cap number.", a->argv[1]);
157 rc = E_FAIL;
158 break;
159 }
160
161 unsigned n = parseNum(a->argv[2], 100, "ExecutionCap");
162
163 CHECK_ERROR_BREAK(sessionMachine, COMSETTER(CPUExecutionCap)(n));
164 }
165 else if (!strcmp(a->argv[1], "clipboard"))
166 {
167 if (a->argc <= 1 + 1)
168 {
169 errorArgument("Missing argument to '%s'. Expected clipboard mode.", a->argv[1]);
170 rc = E_FAIL;
171 break;
172 }
173
174 ClipboardMode_T mode;
175 if (!strcmp(a->argv[2], "disabled"))
176 mode = ClipboardMode_Disabled;
177 else if (!strcmp(a->argv[2], "hosttoguest"))
178 mode = ClipboardMode_HostToGuest;
179 else if (!strcmp(a->argv[2], "guesttohost"))
180 mode = ClipboardMode_GuestToHost;
181 else if (!strcmp(a->argv[2], "bidirectional"))
182 mode = ClipboardMode_Bidirectional;
183 else
184 {
185 errorArgument("Invalid '%s' argument '%s'.", a->argv[1], a->argv[2]);
186 rc = E_FAIL;
187 }
188 if (SUCCEEDED(rc))
189 {
190 CHECK_ERROR_BREAK(sessionMachine, COMSETTER(ClipboardMode)(mode));
191 }
192 }
193 else if (!strcmp(a->argv[1], "draganddrop"))
194 {
195 if (a->argc <= 1 + 1)
196 {
197 errorArgument("Missing argument to '%s'. Expected drag'n'drop mode.", a->argv[1]);
198 rc = E_FAIL;
199 break;
200 }
201
202 DragAndDropMode_T mode;
203 if (!strcmp(a->argv[2], "disabled"))
204 mode = DragAndDropMode_Disabled;
205 else if (!strcmp(a->argv[2], "hosttoguest"))
206 mode = DragAndDropMode_HostToGuest;
207 else if (!strcmp(a->argv[2], "guesttohost"))
208 mode = DragAndDropMode_GuestToHost;
209 else if (!strcmp(a->argv[2], "bidirectional"))
210 mode = DragAndDropMode_Bidirectional;
211 else
212 {
213 errorArgument("Invalid '%s' argument '%s'.", a->argv[1], a->argv[2]);
214 rc = E_FAIL;
215 }
216 if (SUCCEEDED(rc))
217 {
218 CHECK_ERROR_BREAK(sessionMachine, COMSETTER(DragAndDropMode)(mode));
219 }
220 }
221 else if (!strcmp(a->argv[1], "poweroff"))
222 {
223 ComPtr<IProgress> progress;
224 CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
225
226 rc = showProgress(progress);
227 CHECK_PROGRESS_ERROR(progress, ("Failed to power off machine"));
228 }
229 else if (!strcmp(a->argv[1], "savestate"))
230 {
231 /* first pause so we don't trigger a live save which needs more time/resources */
232 bool fPaused = false;
233 rc = console->Pause();
234 if (FAILED(rc))
235 {
236 bool fError = true;
237 if (rc == VBOX_E_INVALID_VM_STATE)
238 {
239 /* check if we are already paused */
240 MachineState_T machineState;
241 CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
242 /* the error code was lost by the previous instruction */
243 rc = VBOX_E_INVALID_VM_STATE;
244 if (machineState != MachineState_Paused)
245 {
246 RTMsgError("Machine in invalid state %d -- %s\n",
247 machineState, machineStateToName(machineState, false));
248 }
249 else
250 {
251 fError = false;
252 fPaused = true;
253 }
254 }
255 if (fError)
256 break;
257 }
258
259 ComPtr<IProgress> progress;
260 CHECK_ERROR(console, SaveState(progress.asOutParam()));
261 if (FAILED(rc))
262 {
263 if (!fPaused)
264 console->Resume();
265 break;
266 }
267
268 rc = showProgress(progress);
269 CHECK_PROGRESS_ERROR(progress, ("Failed to save machine state"));
270 if (FAILED(rc))
271 {
272 if (!fPaused)
273 console->Resume();
274 }
275 }
276 else if (!strcmp(a->argv[1], "acpipowerbutton"))
277 {
278 CHECK_ERROR_BREAK(console, PowerButton());
279 }
280 else if (!strcmp(a->argv[1], "acpisleepbutton"))
281 {
282 CHECK_ERROR_BREAK(console, SleepButton());
283 }
284 else if (!strcmp(a->argv[1], "keyboardputscancode"))
285 {
286 ComPtr<IKeyboard> keyboard;
287 CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(keyboard.asOutParam()));
288
289 if (a->argc <= 1 + 1)
290 {
291 errorArgument("Missing argument to '%s'. Expected IBM PC AT set 2 keyboard scancode(s) as hex byte(s).", a->argv[1]);
292 rc = E_FAIL;
293 break;
294 }
295
296 std::list<LONG> llScancodes;
297
298 /* Process the command line. */
299 int i;
300 for (i = 1 + 1; i < a->argc; i++)
301 {
302 if ( RT_C_IS_XDIGIT (a->argv[i][0])
303 && RT_C_IS_XDIGIT (a->argv[i][1])
304 && a->argv[i][2] == 0)
305 {
306 uint8_t u8Scancode;
307 int irc = RTStrToUInt8Ex(a->argv[i], NULL, 16, &u8Scancode);
308 if (RT_FAILURE (irc))
309 {
310 RTMsgError("Converting '%s' returned %Rrc!", a->argv[i], rc);
311 rc = E_FAIL;
312 break;
313 }
314
315 llScancodes.push_back(u8Scancode);
316 }
317 else
318 {
319 RTMsgError("Error: '%s' is not a hex byte!", a->argv[i]);
320 rc = E_FAIL;
321 break;
322 }
323 }
324
325 if (FAILED(rc))
326 break;
327
328 /* Send scancodes to the VM. */
329 com::SafeArray<LONG> saScancodes(llScancodes);
330 ULONG codesStored = 0;
331 CHECK_ERROR_BREAK(keyboard, PutScancodes(ComSafeArrayAsInParam(saScancodes),
332 &codesStored));
333 if (codesStored < saScancodes.size())
334 {
335 RTMsgError("Only %d scancodes were stored", codesStored);
336 rc = E_FAIL;
337 break;
338 }
339 }
340 else if (!strncmp(a->argv[1], "setlinkstate", 12))
341 {
342 /* Get the number of network adapters */
343 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
344
345 unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
346 if (!n)
347 {
348 rc = E_FAIL;
349 break;
350 }
351 if (a->argc <= 1 + 1)
352 {
353 errorArgument("Missing argument to '%s'", a->argv[1]);
354 rc = E_FAIL;
355 break;
356 }
357 /* get the corresponding network adapter */
358 ComPtr<INetworkAdapter> adapter;
359 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
360 if (adapter)
361 {
362 if (!strcmp(a->argv[2], "on"))
363 {
364 CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(TRUE));
365 }
366 else if (!strcmp(a->argv[2], "off"))
367 {
368 CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(FALSE));
369 }
370 else
371 {
372 errorArgument("Invalid link state '%s'", Utf8Str(a->argv[2]).c_str());
373 rc = E_FAIL;
374 break;
375 }
376 }
377 }
378 /* here the order in which strncmp is called is important
379 * cause nictracefile can be very well compared with
380 * nictrace and nic and thus everything will always fail
381 * if the order is changed
382 */
383 else if (!strncmp(a->argv[1], "nictracefile", 12))
384 {
385 /* Get the number of network adapters */
386 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
387 unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
388 if (!n)
389 {
390 rc = E_FAIL;
391 break;
392 }
393 if (a->argc <= 2)
394 {
395 errorArgument("Missing argument to '%s'", a->argv[1]);
396 rc = E_FAIL;
397 break;
398 }
399
400 /* get the corresponding network adapter */
401 ComPtr<INetworkAdapter> adapter;
402 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
403 if (adapter)
404 {
405 BOOL fEnabled;
406 adapter->COMGETTER(Enabled)(&fEnabled);
407 if (fEnabled)
408 {
409 if (a->argv[2])
410 {
411 CHECK_ERROR_RET(adapter, COMSETTER(TraceFile)(Bstr(a->argv[2]).raw()), 1);
412 }
413 else
414 {
415 errorArgument("Invalid filename or filename not specified for NIC %lu", n);
416 rc = E_FAIL;
417 break;
418 }
419 }
420 else
421 RTMsgError("The NIC %d is currently disabled and thus its tracefile can't be changed", n);
422 }
423 }
424 else if (!strncmp(a->argv[1], "nictrace", 8))
425 {
426 /* Get the number of network adapters */
427 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
428
429 unsigned n = parseNum(&a->argv[1][8], NetworkAdapterCount, "NIC");
430 if (!n)
431 {
432 rc = E_FAIL;
433 break;
434 }
435 if (a->argc <= 2)
436 {
437 errorArgument("Missing argument to '%s'", a->argv[1]);
438 rc = E_FAIL;
439 break;
440 }
441
442 /* get the corresponding network adapter */
443 ComPtr<INetworkAdapter> adapter;
444 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
445 if (adapter)
446 {
447 BOOL fEnabled;
448 adapter->COMGETTER(Enabled)(&fEnabled);
449 if (fEnabled)
450 {
451 if (!strcmp(a->argv[2], "on"))
452 {
453 CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(TRUE), 1);
454 }
455 else if (!strcmp(a->argv[2], "off"))
456 {
457 CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(FALSE), 1);
458 }
459 else
460 {
461 errorArgument("Invalid nictrace%lu argument '%s'", n, Utf8Str(a->argv[2]).c_str());
462 rc = E_FAIL;
463 break;
464 }
465 }
466 else
467 RTMsgError("The NIC %d is currently disabled and thus its trace flag can't be changed", n);
468 }
469 }
470 else if( a->argc > 2
471 && !strncmp(a->argv[1], "natpf", 5))
472 {
473 /* Get the number of network adapters */
474 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
475 ComPtr<INATEngine> engine;
476 unsigned n = parseNum(&a->argv[1][5], NetworkAdapterCount, "NIC");
477 if (!n)
478 {
479 rc = E_FAIL;
480 break;
481 }
482 if (a->argc <= 2)
483 {
484 errorArgument("Missing argument to '%s'", a->argv[1]);
485 rc = E_FAIL;
486 break;
487 }
488
489 /* get the corresponding network adapter */
490 ComPtr<INetworkAdapter> adapter;
491 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
492 if (!adapter)
493 {
494 rc = E_FAIL;
495 break;
496 }
497 CHECK_ERROR(adapter, COMGETTER(NATEngine)(engine.asOutParam()));
498 if (!engine)
499 {
500 rc = E_FAIL;
501 break;
502 }
503
504 if (!strcmp(a->argv[2], "delete"))
505 {
506 if (a->argc >= 3)
507 CHECK_ERROR(engine, RemoveRedirect(Bstr(a->argv[3]).raw()));
508 }
509 else
510 {
511#define ITERATE_TO_NEXT_TERM(ch) \
512 do { \
513 while (*ch != ',') \
514 { \
515 if (*ch == 0) \
516 { \
517 return errorSyntax(USAGE_CONTROLVM, \
518 "Missing or invalid argument to '%s'", \
519 a->argv[1]); \
520 } \
521 ch++; \
522 } \
523 *ch = '\0'; \
524 ch++; \
525 } while(0)
526
527 char *strName;
528 char *strProto;
529 char *strHostIp;
530 char *strHostPort;
531 char *strGuestIp;
532 char *strGuestPort;
533 char *strRaw = RTStrDup(a->argv[2]);
534 char *ch = strRaw;
535 strName = RTStrStrip(ch);
536 ITERATE_TO_NEXT_TERM(ch);
537 strProto = RTStrStrip(ch);
538 ITERATE_TO_NEXT_TERM(ch);
539 strHostIp = RTStrStrip(ch);
540 ITERATE_TO_NEXT_TERM(ch);
541 strHostPort = RTStrStrip(ch);
542 ITERATE_TO_NEXT_TERM(ch);
543 strGuestIp = RTStrStrip(ch);
544 ITERATE_TO_NEXT_TERM(ch);
545 strGuestPort = RTStrStrip(ch);
546 NATProtocol_T proto;
547 if (RTStrICmp(strProto, "udp") == 0)
548 proto = NATProtocol_UDP;
549 else if (RTStrICmp(strProto, "tcp") == 0)
550 proto = NATProtocol_TCP;
551 else
552 {
553 return errorSyntax(USAGE_CONTROLVM,
554 "Wrong rule proto '%s' specified -- only 'udp' and 'tcp' are allowed.",
555 strProto);
556 }
557 CHECK_ERROR(engine, AddRedirect(Bstr(strName).raw(), proto, Bstr(strHostIp).raw(),
558 RTStrToUInt16(strHostPort), Bstr(strGuestIp).raw(), RTStrToUInt16(strGuestPort)));
559#undef ITERATE_TO_NEXT_TERM
560 }
561 /* commit changes */
562 if (SUCCEEDED(rc))
563 CHECK_ERROR(sessionMachine, SaveSettings());
564 }
565 else if (!strncmp(a->argv[1], "nicproperty", 11))
566 {
567 /* Get the number of network adapters */
568 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox,sessionMachine) ;
569 unsigned n = parseNum(&a->argv[1][11], NetworkAdapterCount, "NIC");
570 if (!n)
571 {
572 rc = E_FAIL;
573 break;
574 }
575 if (a->argc <= 2)
576 {
577 errorArgument("Missing argument to '%s'", a->argv[1]);
578 rc = E_FAIL;
579 break;
580 }
581
582 /* get the corresponding network adapter */
583 ComPtr<INetworkAdapter> adapter;
584 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
585 if (adapter)
586 {
587 BOOL fEnabled;
588 adapter->COMGETTER(Enabled)(&fEnabled);
589 if (fEnabled)
590 {
591 /* Parse 'name=value' */
592 char *pszProperty = RTStrDup(a->argv[2]);
593 if (pszProperty)
594 {
595 char *pDelimiter = strchr(pszProperty, '=');
596 if (pDelimiter)
597 {
598 *pDelimiter = '\0';
599
600 Bstr bstrName = pszProperty;
601 Bstr bstrValue = &pDelimiter[1];
602 CHECK_ERROR(adapter, SetProperty(bstrName.raw(), bstrValue.raw()));
603 }
604 else
605 {
606 errorArgument("Invalid nicproperty%d argument '%s'", n, a->argv[2]);
607 rc = E_FAIL;
608 }
609 RTStrFree(pszProperty);
610 }
611 else
612 {
613 RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for nicproperty%d '%s'\n", n, a->argv[2]);
614 rc = E_FAIL;
615 }
616 if (FAILED(rc))
617 break;
618 }
619 else
620 RTMsgError("The NIC %d is currently disabled and thus its properties can't be changed", n);
621 }
622 }
623 else if (!strncmp(a->argv[1], "nic", 3))
624 {
625 /* Get the number of network adapters */
626 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox,sessionMachine) ;
627 unsigned n = parseNum(&a->argv[1][3], NetworkAdapterCount, "NIC");
628 if (!n)
629 {
630 rc = E_FAIL;
631 break;
632 }
633 if (a->argc <= 2)
634 {
635 errorArgument("Missing argument to '%s'", a->argv[1]);
636 rc = E_FAIL;
637 break;
638 }
639
640 /* get the corresponding network adapter */
641 ComPtr<INetworkAdapter> adapter;
642 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
643 if (adapter)
644 {
645 BOOL fEnabled;
646 adapter->COMGETTER(Enabled)(&fEnabled);
647 if (fEnabled)
648 {
649 if (!strcmp(a->argv[2], "null"))
650 {
651 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
652 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Null), 1);
653 }
654 else if (!strcmp(a->argv[2], "nat"))
655 {
656 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
657 if (a->argc == 4)
658 CHECK_ERROR_RET(adapter, COMSETTER(NATNetwork)(Bstr(a->argv[3]).raw()), 1);
659 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_NAT), 1);
660 }
661 else if ( !strcmp(a->argv[2], "bridged")
662 || !strcmp(a->argv[2], "hostif")) /* backward compatibility */
663 {
664 if (a->argc <= 3)
665 {
666 errorArgument("Missing argument to '%s'", a->argv[2]);
667 rc = E_FAIL;
668 break;
669 }
670 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
671 CHECK_ERROR_RET(adapter, COMSETTER(BridgedInterface)(Bstr(a->argv[3]).raw()), 1);
672 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged), 1);
673 }
674 else if (!strcmp(a->argv[2], "intnet"))
675 {
676 if (a->argc <= 3)
677 {
678 errorArgument("Missing argument to '%s'", a->argv[2]);
679 rc = E_FAIL;
680 break;
681 }
682 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
683 CHECK_ERROR_RET(adapter, COMSETTER(InternalNetwork)(Bstr(a->argv[3]).raw()), 1);
684 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Internal), 1);
685 }
686#if defined(VBOX_WITH_NETFLT)
687 else if (!strcmp(a->argv[2], "hostonly"))
688 {
689 if (a->argc <= 3)
690 {
691 errorArgument("Missing argument to '%s'", a->argv[2]);
692 rc = E_FAIL;
693 break;
694 }
695 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
696 CHECK_ERROR_RET(adapter, COMSETTER(HostOnlyInterface)(Bstr(a->argv[3]).raw()), 1);
697 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly), 1);
698 }
699#endif
700 else if (!strcmp(a->argv[2], "generic"))
701 {
702 if (a->argc <= 3)
703 {
704 errorArgument("Missing argument to '%s'", a->argv[2]);
705 rc = E_FAIL;
706 break;
707 }
708 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
709 CHECK_ERROR_RET(adapter, COMSETTER(GenericDriver)(Bstr(a->argv[3]).raw()), 1);
710 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), 1);
711 }
712 /** @todo obsolete, remove eventually */
713 else if (!strcmp(a->argv[2], "vde"))
714 {
715 if (a->argc <= 3)
716 {
717 errorArgument("Missing argument to '%s'", a->argv[2]);
718 rc = E_FAIL;
719 break;
720 }
721 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
722 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), 1);
723 CHECK_ERROR_RET(adapter, SetProperty(Bstr("name").raw(), Bstr(a->argv[3]).raw()), 1);
724 }
725 else
726 {
727 errorArgument("Invalid type '%s' specfied for NIC %lu", Utf8Str(a->argv[2]).c_str(), n);
728 rc = E_FAIL;
729 break;
730 }
731 }
732 else
733 RTMsgError("The NIC %d is currently disabled and thus its attachment type can't be changed", n);
734 }
735 }
736 else if ( !strcmp(a->argv[1], "vrde")
737 || !strcmp(a->argv[1], "vrdp"))
738 {
739 if (!strcmp(a->argv[1], "vrdp"))
740 RTStrmPrintf(g_pStdErr, "Warning: 'vrdp' is deprecated. Use 'vrde'.\n");
741
742 if (a->argc <= 1 + 1)
743 {
744 errorArgument("Missing argument to '%s'", a->argv[1]);
745 rc = E_FAIL;
746 break;
747 }
748 ComPtr<IVRDEServer> vrdeServer;
749 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
750 ASSERT(vrdeServer);
751 if (vrdeServer)
752 {
753 if (!strcmp(a->argv[2], "on"))
754 {
755 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(TRUE));
756 }
757 else if (!strcmp(a->argv[2], "off"))
758 {
759 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(FALSE));
760 }
761 else
762 {
763 errorArgument("Invalid remote desktop server state '%s'", Utf8Str(a->argv[2]).c_str());
764 rc = E_FAIL;
765 break;
766 }
767 }
768 }
769 else if ( !strcmp(a->argv[1], "vrdeport")
770 || !strcmp(a->argv[1], "vrdpport"))
771 {
772 if (!strcmp(a->argv[1], "vrdpport"))
773 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpport' is deprecated. Use 'vrdeport'.\n");
774
775 if (a->argc <= 1 + 1)
776 {
777 errorArgument("Missing argument to '%s'", a->argv[1]);
778 rc = E_FAIL;
779 break;
780 }
781
782 ComPtr<IVRDEServer> vrdeServer;
783 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
784 ASSERT(vrdeServer);
785 if (vrdeServer)
786 {
787 Bstr ports;
788
789 if (!strcmp(a->argv[2], "default"))
790 ports = "0";
791 else
792 ports = a->argv[2];
793
794 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), ports.raw()));
795 }
796 }
797 else if ( !strcmp(a->argv[1], "vrdevideochannelquality")
798 || !strcmp(a->argv[1], "vrdpvideochannelquality"))
799 {
800 if (!strcmp(a->argv[1], "vrdpvideochannelquality"))
801 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpvideochannelquality' is deprecated. Use 'vrdevideochannelquality'.\n");
802
803 if (a->argc <= 1 + 1)
804 {
805 errorArgument("Missing argument to '%s'", a->argv[1]);
806 rc = E_FAIL;
807 break;
808 }
809 ComPtr<IVRDEServer> vrdeServer;
810 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
811 ASSERT(vrdeServer);
812 if (vrdeServer)
813 {
814 Bstr value = a->argv[2];
815
816 CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("VideoChannel/Quality").raw(), value.raw()));
817 }
818 }
819 else if (!strcmp(a->argv[1], "vrdeproperty"))
820 {
821 if (a->argc <= 1 + 1)
822 {
823 errorArgument("Missing argument to '%s'", a->argv[1]);
824 rc = E_FAIL;
825 break;
826 }
827 ComPtr<IVRDEServer> vrdeServer;
828 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
829 ASSERT(vrdeServer);
830 if (vrdeServer)
831 {
832 /* Parse 'name=value' */
833 char *pszProperty = RTStrDup(a->argv[2]);
834 if (pszProperty)
835 {
836 char *pDelimiter = strchr(pszProperty, '=');
837 if (pDelimiter)
838 {
839 *pDelimiter = '\0';
840
841 Bstr bstrName = pszProperty;
842 Bstr bstrValue = &pDelimiter[1];
843 CHECK_ERROR(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
844 }
845 else
846 {
847 errorArgument("Invalid vrdeproperty argument '%s'", a->argv[2]);
848 rc = E_FAIL;
849 }
850 RTStrFree(pszProperty);
851 }
852 else
853 {
854 RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for VRDE property '%s'\n", a->argv[2]);
855 rc = E_FAIL;
856 }
857 }
858 if (FAILED(rc))
859 {
860 break;
861 }
862 }
863 else if ( !strcmp(a->argv[1], "usbattach")
864 || !strcmp(a->argv[1], "usbdetach"))
865 {
866 if (a->argc < 3)
867 {
868 errorSyntax(USAGE_CONTROLVM, "Not enough parameters");
869 rc = E_FAIL;
870 break;
871 }
872
873 bool attach = !strcmp(a->argv[1], "usbattach");
874
875 Bstr usbId = a->argv[2];
876
877 Guid guid(usbId);
878 if (!guid.isValid())
879 {
880 // assume address
881 if (attach)
882 {
883 ComPtr <IHost> host;
884 CHECK_ERROR_BREAK(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
885 SafeIfaceArray <IHostUSBDevice> coll;
886 CHECK_ERROR_BREAK(host, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
887 ComPtr <IHostUSBDevice> dev;
888 CHECK_ERROR_BREAK(host, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
889 dev.asOutParam()));
890 CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
891 }
892 else
893 {
894 SafeIfaceArray <IUSBDevice> coll;
895 CHECK_ERROR_BREAK(console, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
896 ComPtr <IUSBDevice> dev;
897 CHECK_ERROR_BREAK(console, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
898 dev.asOutParam()));
899 CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
900 }
901 }
902 else if (guid.isZero())
903 {
904 errorArgument("Zero UUID argument '%s'", a->argv[2]);
905 rc = E_FAIL;
906 break;
907 }
908
909 if (attach)
910 CHECK_ERROR_BREAK(console, AttachUSBDevice(usbId.raw()));
911 else
912 {
913 ComPtr <IUSBDevice> dev;
914 CHECK_ERROR_BREAK(console, DetachUSBDevice(usbId.raw(),
915 dev.asOutParam()));
916 }
917 }
918 else if (!strcmp(a->argv[1], "setvideomodehint"))
919 {
920 if (a->argc != 5 && a->argc != 6 && a->argc != 7 && a->argc != 9)
921 {
922 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
923 rc = E_FAIL;
924 break;
925 }
926 bool fEnabled = true;
927 uint32_t uXRes = RTStrToUInt32(a->argv[2]);
928 uint32_t uYRes = RTStrToUInt32(a->argv[3]);
929 uint32_t uBpp = RTStrToUInt32(a->argv[4]);
930 uint32_t uDisplayIdx = 0;
931 bool fChangeOrigin = false;
932 int32_t iOriginX = 0;
933 int32_t iOriginY = 0;
934 if (a->argc >= 6)
935 uDisplayIdx = RTStrToUInt32(a->argv[5]);
936 if (a->argc >= 7)
937 {
938 int vrc = parseBool(a->argv[6], &fEnabled);
939 if (RT_FAILURE(vrc))
940 {
941 errorSyntax(USAGE_CONTROLVM, "Either \"yes\" or \"no\" is expected");
942 rc = E_FAIL;
943 break;
944 }
945 fEnabled = !RTStrICmp(a->argv[6], "yes");
946 }
947 if (a->argc == 9)
948 {
949 iOriginX = RTStrToInt32(a->argv[7]);
950 iOriginY = RTStrToInt32(a->argv[8]);
951 fChangeOrigin = true;
952 }
953
954 ComPtr<IDisplay> pDisplay;
955 CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
956 if (!pDisplay)
957 {
958 RTMsgError("Cannot send a video mode hint without a display");
959 rc = E_FAIL;
960 break;
961 }
962 CHECK_ERROR_BREAK(pDisplay, SetVideoModeHint(uDisplayIdx, fEnabled,
963 fChangeOrigin, iOriginX, iOriginY,
964 uXRes, uYRes, uBpp));
965 }
966 else if (!strcmp(a->argv[1], "setcredentials"))
967 {
968 bool fAllowLocalLogon = true;
969 if ( a->argc == 7
970 || ( a->argc == 8
971 && ( !strcmp(a->argv[3], "-p")
972 || !strcmp(a->argv[3], "--passwordfile"))))
973 {
974 if ( strcmp(a->argv[5 + (a->argc - 7)], "--allowlocallogon")
975 && strcmp(a->argv[5 + (a->argc - 7)], "-allowlocallogon"))
976 {
977 errorArgument("Invalid parameter '%s'", a->argv[5]);
978 rc = E_FAIL;
979 break;
980 }
981 if (!strcmp(a->argv[6 + (a->argc - 7)], "no"))
982 fAllowLocalLogon = false;
983 }
984 else if ( a->argc != 5
985 && ( a->argc != 6
986 || ( strcmp(a->argv[3], "-p")
987 && strcmp(a->argv[3], "--passwordfile"))))
988 {
989 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
990 rc = E_FAIL;
991 break;
992 }
993 Utf8Str passwd, domain;
994 if (a->argc == 5 || a->argc == 7)
995 {
996 passwd = a->argv[3];
997 domain = a->argv[4];
998 }
999 else
1000 {
1001 RTEXITCODE rcExit = readPasswordFile(a->argv[4], &passwd);
1002 if (rcExit != RTEXITCODE_SUCCESS)
1003 {
1004 rc = E_FAIL;
1005 break;
1006 }
1007 domain = a->argv[5];
1008 }
1009
1010 ComPtr<IGuest> pGuest;
1011 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pGuest.asOutParam()));
1012 if (!pGuest)
1013 {
1014 RTMsgError("Guest not running");
1015 rc = E_FAIL;
1016 break;
1017 }
1018 CHECK_ERROR_BREAK(pGuest, SetCredentials(Bstr(a->argv[2]).raw(),
1019 Bstr(passwd).raw(),
1020 Bstr(domain).raw(),
1021 fAllowLocalLogon));
1022 }
1023#if 0 /* TODO: review & remove */
1024 else if (!strcmp(a->argv[1], "dvdattach"))
1025 {
1026 Bstr uuid;
1027 if (a->argc != 3)
1028 {
1029 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
1030 rc = E_FAIL;
1031 break;
1032 }
1033
1034 ComPtr<IMedium> dvdMedium;
1035
1036 /* unmount? */
1037 if (!strcmp(a->argv[2], "none"))
1038 {
1039 /* nothing to do, NULL object will cause unmount */
1040 }
1041 /* host drive? */
1042 else if (!strncmp(a->argv[2], "host:", 5))
1043 {
1044 ComPtr<IHost> host;
1045 CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
1046
1047 rc = host->FindHostDVDDrive(Bstr(a->argv[2] + 5), dvdMedium.asOutParam());
1048 if (!dvdMedium)
1049 {
1050 errorArgument("Invalid host DVD drive name \"%s\"",
1051 a->argv[2] + 5);
1052 rc = E_FAIL;
1053 break;
1054 }
1055 }
1056 else
1057 {
1058 /* first assume it's a UUID */
1059 uuid = a->argv[2];
1060 rc = a->virtualBox->GetDVDImage(uuid, dvdMedium.asOutParam());
1061 if (FAILED(rc) || !dvdMedium)
1062 {
1063 /* must be a filename, check if it's in the collection */
1064 rc = a->virtualBox->FindDVDImage(Bstr(a->argv[2]), dvdMedium.asOutParam());
1065 /* not registered, do that on the fly */
1066 if (!dvdMedium)
1067 {
1068 Bstr emptyUUID;
1069 CHECK_ERROR(a->virtualBox, OpenDVDImage(Bstr(a->argv[2]), emptyUUID, dvdMedium.asOutParam()));
1070 }
1071 }
1072 if (!dvdMedium)
1073 {
1074 rc = E_FAIL;
1075 break;
1076 }
1077 }
1078
1079 /** @todo generalize this, allow arbitrary number of DVD drives
1080 * and as a consequence multiple attachments and different
1081 * storage controllers. */
1082 if (dvdMedium)
1083 dvdMedium->COMGETTER(Id)(uuid.asOutParam());
1084 else
1085 uuid = Guid().toString();
1086 CHECK_ERROR(machine, MountMedium(Bstr("IDE Controller"), 1, 0, uuid, FALSE /* aForce */));
1087 }
1088 else if (!strcmp(a->argv[1], "floppyattach"))
1089 {
1090 Bstr uuid;
1091 if (a->argc != 3)
1092 {
1093 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
1094 rc = E_FAIL;
1095 break;
1096 }
1097
1098 ComPtr<IMedium> floppyMedium;
1099
1100 /* unmount? */
1101 if (!strcmp(a->argv[2], "none"))
1102 {
1103 /* nothing to do, NULL object will cause unmount */
1104 }
1105 /* host drive? */
1106 else if (!strncmp(a->argv[2], "host:", 5))
1107 {
1108 ComPtr<IHost> host;
1109 CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
1110 host->FindHostFloppyDrive(Bstr(a->argv[2] + 5), floppyMedium.asOutParam());
1111 if (!floppyMedium)
1112 {
1113 errorArgument("Invalid host floppy drive name \"%s\"",
1114 a->argv[2] + 5);
1115 rc = E_FAIL;
1116 break;
1117 }
1118 }
1119 else
1120 {
1121 /* first assume it's a UUID */
1122 uuid = a->argv[2];
1123 rc = a->virtualBox->GetFloppyImage(uuid, floppyMedium.asOutParam());
1124 if (FAILED(rc) || !floppyMedium)
1125 {
1126 /* must be a filename, check if it's in the collection */
1127 rc = a->virtualBox->FindFloppyImage(Bstr(a->argv[2]), floppyMedium.asOutParam());
1128 /* not registered, do that on the fly */
1129 if (!floppyMedium)
1130 {
1131 Bstr emptyUUID;
1132 CHECK_ERROR(a->virtualBox, OpenFloppyImage(Bstr(a->argv[2]), emptyUUID, floppyMedium.asOutParam()));
1133 }
1134 }
1135 if (!floppyMedium)
1136 {
1137 rc = E_FAIL;
1138 break;
1139 }
1140 }
1141 floppyMedium->COMGETTER(Id)(uuid.asOutParam());
1142 CHECK_ERROR(machine, MountMedium(Bstr("Floppy Controller"), 0, 0, uuid, FALSE /* aForce */));
1143 }
1144#endif /* obsolete dvdattach/floppyattach */
1145 else if (!strcmp(a->argv[1], "guestmemoryballoon"))
1146 {
1147 if (a->argc != 3)
1148 {
1149 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
1150 rc = E_FAIL;
1151 break;
1152 }
1153 uint32_t uVal;
1154 int vrc;
1155 vrc = RTStrToUInt32Ex(a->argv[2], NULL, 0, &uVal);
1156 if (vrc != VINF_SUCCESS)
1157 {
1158 errorArgument("Error parsing guest memory balloon size '%s'", a->argv[2]);
1159 rc = E_FAIL;
1160 break;
1161 }
1162 /* guest is running; update IGuest */
1163 ComPtr <IGuest> guest;
1164 rc = console->COMGETTER(Guest)(guest.asOutParam());
1165 if (SUCCEEDED(rc))
1166 CHECK_ERROR(guest, COMSETTER(MemoryBalloonSize)(uVal));
1167 }
1168 else if (!strcmp(a->argv[1], "teleport"))
1169 {
1170 Bstr bstrHostname;
1171 uint32_t uMaxDowntime = 250 /*ms*/;
1172 uint32_t uPort = UINT32_MAX;
1173 uint32_t cMsTimeout = 0;
1174 Utf8Str strPassword;
1175 static const RTGETOPTDEF s_aTeleportOptions[] =
1176 {
1177 { "--host", 'h', RTGETOPT_REQ_STRING }, /** @todo RTGETOPT_FLAG_MANDATORY */
1178 { "--hostname", 'h', RTGETOPT_REQ_STRING }, /** @todo remove this */
1179 { "--maxdowntime", 'd', RTGETOPT_REQ_UINT32 },
1180 { "--port", 'P', RTGETOPT_REQ_UINT32 }, /** @todo RTGETOPT_FLAG_MANDATORY */
1181 { "--passwordfile", 'p', RTGETOPT_REQ_STRING },
1182 { "--password", 'W', RTGETOPT_REQ_STRING },
1183 { "--timeout", 't', RTGETOPT_REQ_UINT32 },
1184 { "--detailed-progress", 'D', RTGETOPT_REQ_NOTHING }
1185 };
1186 RTGETOPTSTATE GetOptState;
1187 RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTeleportOptions, RT_ELEMENTS(s_aTeleportOptions), 2, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1188 int ch;
1189 RTGETOPTUNION Value;
1190 while ( SUCCEEDED(rc)
1191 && (ch = RTGetOpt(&GetOptState, &Value)))
1192 {
1193 switch (ch)
1194 {
1195 case 'h': bstrHostname = Value.psz; break;
1196 case 'd': uMaxDowntime = Value.u32; break;
1197 case 'D': g_fDetailedProgress = true; break;
1198 case 'P': uPort = Value.u32; break;
1199 case 'p':
1200 {
1201 RTEXITCODE rcExit = readPasswordFile(Value.psz, &strPassword);
1202 if (rcExit != RTEXITCODE_SUCCESS)
1203 rc = E_FAIL;
1204 break;
1205 }
1206 case 'W': strPassword = Value.psz; break;
1207 case 't': cMsTimeout = Value.u32; break;
1208 default:
1209 errorGetOpt(USAGE_CONTROLVM, ch, &Value);
1210 rc = E_FAIL;
1211 break;
1212 }
1213 }
1214 if (FAILED(rc))
1215 break;
1216
1217 ComPtr<IProgress> progress;
1218 CHECK_ERROR_BREAK(console, Teleport(bstrHostname.raw(), uPort,
1219 Bstr(strPassword).raw(),
1220 uMaxDowntime,
1221 progress.asOutParam()));
1222
1223 if (cMsTimeout)
1224 {
1225 rc = progress->COMSETTER(Timeout)(cMsTimeout);
1226 if (FAILED(rc) && rc != VBOX_E_INVALID_OBJECT_STATE)
1227 CHECK_ERROR_BREAK(progress, COMSETTER(Timeout)(cMsTimeout)); /* lazyness */
1228 }
1229
1230 rc = showProgress(progress);
1231 CHECK_PROGRESS_ERROR(progress, ("Teleportation failed"));
1232 }
1233 else if (!strcmp(a->argv[1], "screenshotpng"))
1234 {
1235 if (a->argc <= 2 || a->argc > 4)
1236 {
1237 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
1238 rc = E_FAIL;
1239 break;
1240 }
1241 int vrc;
1242 uint32_t displayIdx = 0;
1243 if (a->argc == 4)
1244 {
1245 vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &displayIdx);
1246 if (vrc != VINF_SUCCESS)
1247 {
1248 errorArgument("Error parsing display number '%s'", a->argv[3]);
1249 rc = E_FAIL;
1250 break;
1251 }
1252 }
1253 ComPtr<IDisplay> pDisplay;
1254 CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
1255 if (!pDisplay)
1256 {
1257 RTMsgError("Cannot take a screenshot without a display");
1258 rc = E_FAIL;
1259 break;
1260 }
1261 ULONG width, height, bpp;
1262 CHECK_ERROR_BREAK(pDisplay, GetScreenResolution(displayIdx, &width, &height, &bpp));
1263 com::SafeArray<BYTE> saScreenshot;
1264 CHECK_ERROR_BREAK(pDisplay, TakeScreenShotPNGToArray(displayIdx, width, height, ComSafeArrayAsOutParam(saScreenshot)));
1265 RTFILE pngFile = NIL_RTFILE;
1266 vrc = RTFileOpen(&pngFile, a->argv[2], RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_ALL);
1267 if (RT_FAILURE(vrc))
1268 {
1269 RTMsgError("Failed to create file '%s'. rc=%Rrc", a->argv[2], vrc);
1270 rc = E_FAIL;
1271 break;
1272 }
1273 vrc = RTFileWrite(pngFile, saScreenshot.raw(), saScreenshot.size(), NULL);
1274 if (RT_FAILURE(vrc))
1275 {
1276 RTMsgError("Failed to write screenshot to file '%s'. rc=%Rrc", a->argv[2], vrc);
1277 rc = E_FAIL;
1278 }
1279 RTFileClose(pngFile);
1280 }
1281 else
1282 {
1283 errorSyntax(USAGE_CONTROLVM, "Invalid parameter '%s'", a->argv[1]);
1284 rc = E_FAIL;
1285 }
1286 } while (0);
1287
1288 a->session->UnlockMachine();
1289
1290 return SUCCEEDED(rc) ? 0 : 1;
1291}
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