VirtualBox

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

Last change on this file since 40711 was 40673, checked in by vboxsync, 13 years ago

VBoxManage: fixed error code if 'controlvm savestate' failed

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.3 KB
Line 
1/* $Id: VBoxManageControlVM.cpp 40673 2012-03-28 11:12:00Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of the controlvm command.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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], "poweroff"))
166 {
167 ComPtr<IProgress> progress;
168 CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
169
170 rc = showProgress(progress);
171 CHECK_PROGRESS_ERROR(progress, ("Failed to power off machine"));
172 }
173 else if (!strcmp(a->argv[1], "savestate"))
174 {
175 /* first pause so we don't trigger a live save which needs more time/resources */
176 rc = console->Pause();
177 if (FAILED(rc))
178 {
179 if (rc == VBOX_E_INVALID_VM_STATE)
180 {
181 /* check if we are already paused */
182 MachineState_T machineState;
183 CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
184 /* the error code was lost by the previous instruction */
185 rc = VBOX_E_INVALID_VM_STATE;
186 if (machineState != MachineState_Paused)
187 {
188 RTMsgError("Machine in invalid state %d -- %s\n",
189 machineState, machineStateToName(machineState, false));
190 }
191 }
192 break;
193 }
194
195 ComPtr<IProgress> progress;
196 CHECK_ERROR(console, SaveState(progress.asOutParam()));
197 if (FAILED(rc))
198 {
199 console->Resume();
200 break;
201 }
202
203 rc = showProgress(progress);
204 CHECK_PROGRESS_ERROR(progress, ("Failed to save machine state"));
205 if (FAILED(rc))
206 console->Resume();
207 }
208 else if (!strcmp(a->argv[1], "acpipowerbutton"))
209 {
210 CHECK_ERROR_BREAK(console, PowerButton());
211 }
212 else if (!strcmp(a->argv[1], "acpisleepbutton"))
213 {
214 CHECK_ERROR_BREAK(console, SleepButton());
215 }
216 else if (!strcmp(a->argv[1], "keyboardputscancode"))
217 {
218 ComPtr<IKeyboard> keyboard;
219 CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(keyboard.asOutParam()));
220
221 if (a->argc <= 1 + 1)
222 {
223 errorArgument("Missing argument to '%s'. Expected IBM PC AT set 2 keyboard scancode(s) as hex byte(s).", a->argv[1]);
224 rc = E_FAIL;
225 break;
226 }
227
228 std::list<LONG> llScancodes;
229
230 /* Process the command line. */
231 int i;
232 for (i = 1 + 1; i < a->argc; i++)
233 {
234 if ( RT_C_IS_XDIGIT (a->argv[i][0])
235 && RT_C_IS_XDIGIT (a->argv[i][1])
236 && a->argv[i][2] == 0)
237 {
238 uint8_t u8Scancode;
239 int irc = RTStrToUInt8Ex(a->argv[i], NULL, 16, &u8Scancode);
240 if (RT_FAILURE (irc))
241 {
242 RTMsgError("Converting '%s' returned %Rrc!", a->argv[i], rc);
243 rc = E_FAIL;
244 break;
245 }
246
247 llScancodes.push_back(u8Scancode);
248 }
249 else
250 {
251 RTMsgError("Error: '%s' is not a hex byte!", a->argv[i]);
252 rc = E_FAIL;
253 break;
254 }
255 }
256
257 if (FAILED(rc))
258 break;
259
260 /* Send scancodes to the VM. */
261 com::SafeArray<LONG> saScancodes(llScancodes);
262 ULONG codesStored = 0;
263 CHECK_ERROR_BREAK(keyboard, PutScancodes(ComSafeArrayAsInParam(saScancodes),
264 &codesStored));
265 if (codesStored < saScancodes.size())
266 {
267 RTMsgError("Only %d scancodes were stored", codesStored);
268 rc = E_FAIL;
269 break;
270 }
271 }
272 else if (!strncmp(a->argv[1], "setlinkstate", 12))
273 {
274 /* Get the number of network adapters */
275 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
276
277 unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
278 if (!n)
279 {
280 rc = E_FAIL;
281 break;
282 }
283 if (a->argc <= 1 + 1)
284 {
285 errorArgument("Missing argument to '%s'", a->argv[1]);
286 rc = E_FAIL;
287 break;
288 }
289 /* get the corresponding network adapter */
290 ComPtr<INetworkAdapter> adapter;
291 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
292 if (adapter)
293 {
294 if (!strcmp(a->argv[2], "on"))
295 {
296 CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(TRUE));
297 }
298 else if (!strcmp(a->argv[2], "off"))
299 {
300 CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(FALSE));
301 }
302 else
303 {
304 errorArgument("Invalid link state '%s'", Utf8Str(a->argv[2]).c_str());
305 rc = E_FAIL;
306 break;
307 }
308 }
309 }
310 /* here the order in which strncmp is called is important
311 * cause nictracefile can be very well compared with
312 * nictrace and nic and thus everything will always fail
313 * if the order is changed
314 */
315 else if (!strncmp(a->argv[1], "nictracefile", 12))
316 {
317 /* Get the number of network adapters */
318 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
319 unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
320 if (!n)
321 {
322 rc = E_FAIL;
323 break;
324 }
325 if (a->argc <= 2)
326 {
327 errorArgument("Missing argument to '%s'", a->argv[1]);
328 rc = E_FAIL;
329 break;
330 }
331
332 /* get the corresponding network adapter */
333 ComPtr<INetworkAdapter> adapter;
334 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
335 if (adapter)
336 {
337 BOOL fEnabled;
338 adapter->COMGETTER(Enabled)(&fEnabled);
339 if (fEnabled)
340 {
341 if (a->argv[2])
342 {
343 CHECK_ERROR_RET(adapter, COMSETTER(TraceFile)(Bstr(a->argv[2]).raw()), 1);
344 }
345 else
346 {
347 errorArgument("Invalid filename or filename not specified for NIC %lu", n);
348 rc = E_FAIL;
349 break;
350 }
351 }
352 else
353 RTMsgError("The NIC %d is currently disabled and thus its tracefile can't be changed", n);
354 }
355 }
356 else if (!strncmp(a->argv[1], "nictrace", 8))
357 {
358 /* Get the number of network adapters */
359 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
360
361 unsigned n = parseNum(&a->argv[1][8], NetworkAdapterCount, "NIC");
362 if (!n)
363 {
364 rc = E_FAIL;
365 break;
366 }
367 if (a->argc <= 2)
368 {
369 errorArgument("Missing argument to '%s'", a->argv[1]);
370 rc = E_FAIL;
371 break;
372 }
373
374 /* get the corresponding network adapter */
375 ComPtr<INetworkAdapter> adapter;
376 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
377 if (adapter)
378 {
379 BOOL fEnabled;
380 adapter->COMGETTER(Enabled)(&fEnabled);
381 if (fEnabled)
382 {
383 if (!strcmp(a->argv[2], "on"))
384 {
385 CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(TRUE), 1);
386 }
387 else if (!strcmp(a->argv[2], "off"))
388 {
389 CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(FALSE), 1);
390 }
391 else
392 {
393 errorArgument("Invalid nictrace%lu argument '%s'", n, Utf8Str(a->argv[2]).c_str());
394 rc = E_FAIL;
395 break;
396 }
397 }
398 else
399 RTMsgError("The NIC %d is currently disabled and thus its trace flag can't be changed", n);
400 }
401 }
402 else if( a->argc > 2
403 && !strncmp(a->argv[1], "natpf", 5))
404 {
405 /* Get the number of network adapters */
406 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
407 ComPtr<INATEngine> engine;
408 unsigned n = parseNum(&a->argv[1][5], NetworkAdapterCount, "NIC");
409 if (!n)
410 {
411 rc = E_FAIL;
412 break;
413 }
414 if (a->argc <= 2)
415 {
416 errorArgument("Missing argument to '%s'", a->argv[1]);
417 rc = E_FAIL;
418 break;
419 }
420
421 /* get the corresponding network adapter */
422 ComPtr<INetworkAdapter> adapter;
423 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
424 if (!adapter)
425 {
426 rc = E_FAIL;
427 break;
428 }
429 CHECK_ERROR(adapter, COMGETTER(NatDriver)(engine.asOutParam()));
430 if (!engine)
431 {
432 rc = E_FAIL;
433 break;
434 }
435
436 if (!strcmp(a->argv[2], "delete"))
437 {
438 if (a->argc >= 3)
439 CHECK_ERROR(engine, RemoveRedirect(Bstr(a->argv[3]).raw()));
440 }
441 else
442 {
443#define ITERATE_TO_NEXT_TERM(ch) \
444 do { \
445 while (*ch != ',') \
446 { \
447 if (*ch == 0) \
448 { \
449 return errorSyntax(USAGE_CONTROLVM, \
450 "Missing or invalid argument to '%s'", \
451 a->argv[1]); \
452 } \
453 ch++; \
454 } \
455 *ch = '\0'; \
456 ch++; \
457 } while(0)
458
459 char *strName;
460 char *strProto;
461 char *strHostIp;
462 char *strHostPort;
463 char *strGuestIp;
464 char *strGuestPort;
465 char *strRaw = RTStrDup(a->argv[2]);
466 char *ch = strRaw;
467 strName = RTStrStrip(ch);
468 ITERATE_TO_NEXT_TERM(ch);
469 strProto = RTStrStrip(ch);
470 ITERATE_TO_NEXT_TERM(ch);
471 strHostIp = RTStrStrip(ch);
472 ITERATE_TO_NEXT_TERM(ch);
473 strHostPort = RTStrStrip(ch);
474 ITERATE_TO_NEXT_TERM(ch);
475 strGuestIp = RTStrStrip(ch);
476 ITERATE_TO_NEXT_TERM(ch);
477 strGuestPort = RTStrStrip(ch);
478 NATProtocol_T proto;
479 if (RTStrICmp(strProto, "udp") == 0)
480 proto = NATProtocol_UDP;
481 else if (RTStrICmp(strProto, "tcp") == 0)
482 proto = NATProtocol_TCP;
483 else
484 {
485 return errorSyntax(USAGE_CONTROLVM,
486 "Wrong rule proto '%s' specified -- only 'udp' and 'tcp' are allowed.",
487 strProto);
488 }
489 CHECK_ERROR(engine, AddRedirect(Bstr(strName).raw(), proto, Bstr(strHostIp).raw(),
490 RTStrToUInt16(strHostPort), Bstr(strGuestIp).raw(), RTStrToUInt16(strGuestPort)));
491#undef ITERATE_TO_NEXT_TERM
492 }
493 /* commit changes */
494 if (SUCCEEDED(rc))
495 CHECK_ERROR(sessionMachine, SaveSettings());
496 }
497 else if (!strncmp(a->argv[1], "nicproperty", 11))
498 {
499 /* Get the number of network adapters */
500 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox,sessionMachine) ;
501 unsigned n = parseNum(&a->argv[1][11], NetworkAdapterCount, "NIC");
502 if (!n)
503 {
504 rc = E_FAIL;
505 break;
506 }
507 if (a->argc <= 2)
508 {
509 errorArgument("Missing argument to '%s'", a->argv[1]);
510 rc = E_FAIL;
511 break;
512 }
513
514 /* get the corresponding network adapter */
515 ComPtr<INetworkAdapter> adapter;
516 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
517 if (adapter)
518 {
519 BOOL fEnabled;
520 adapter->COMGETTER(Enabled)(&fEnabled);
521 if (fEnabled)
522 {
523 /* Parse 'name=value' */
524 char *pszProperty = RTStrDup(a->argv[2]);
525 if (pszProperty)
526 {
527 char *pDelimiter = strchr(pszProperty, '=');
528 if (pDelimiter)
529 {
530 *pDelimiter = '\0';
531
532 Bstr bstrName = pszProperty;
533 Bstr bstrValue = &pDelimiter[1];
534 CHECK_ERROR(adapter, SetProperty(bstrName.raw(), bstrValue.raw()));
535 }
536 else
537 {
538 errorArgument("Invalid nicproperty%d argument '%s'", n, a->argv[2]);
539 rc = E_FAIL;
540 }
541 RTStrFree(pszProperty);
542 }
543 else
544 {
545 RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for nicproperty%d '%s'\n", n, a->argv[2]);
546 rc = E_FAIL;
547 }
548 if (FAILED(rc))
549 break;
550 }
551 else
552 RTMsgError("The NIC %d is currently disabled and thus its properties can't be changed", n);
553 }
554 }
555 else if (!strncmp(a->argv[1], "nic", 3))
556 {
557 /* Get the number of network adapters */
558 ULONG NetworkAdapterCount = getMaxNics(a->virtualBox,sessionMachine) ;
559 unsigned n = parseNum(&a->argv[1][3], NetworkAdapterCount, "NIC");
560 if (!n)
561 {
562 rc = E_FAIL;
563 break;
564 }
565 if (a->argc <= 2)
566 {
567 errorArgument("Missing argument to '%s'", a->argv[1]);
568 rc = E_FAIL;
569 break;
570 }
571
572 /* get the corresponding network adapter */
573 ComPtr<INetworkAdapter> adapter;
574 CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
575 if (adapter)
576 {
577 BOOL fEnabled;
578 adapter->COMGETTER(Enabled)(&fEnabled);
579 if (fEnabled)
580 {
581 if (!strcmp(a->argv[2], "null"))
582 {
583 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
584 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Null), 1);
585 }
586 else if (!strcmp(a->argv[2], "nat"))
587 {
588 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
589 if (a->argc == 4)
590 CHECK_ERROR_RET(adapter, COMSETTER(NATNetwork)(Bstr(a->argv[3]).raw()), 1);
591 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_NAT), 1);
592 }
593 else if ( !strcmp(a->argv[2], "bridged")
594 || !strcmp(a->argv[2], "hostif")) /* backward compatibility */
595 {
596 if (a->argc <= 3)
597 {
598 errorArgument("Missing argument to '%s'", a->argv[2]);
599 rc = E_FAIL;
600 break;
601 }
602 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
603 CHECK_ERROR_RET(adapter, COMSETTER(BridgedInterface)(Bstr(a->argv[3]).raw()), 1);
604 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged), 1);
605 }
606 else if (!strcmp(a->argv[2], "intnet"))
607 {
608 if (a->argc <= 3)
609 {
610 errorArgument("Missing argument to '%s'", a->argv[2]);
611 rc = E_FAIL;
612 break;
613 }
614 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
615 CHECK_ERROR_RET(adapter, COMSETTER(InternalNetwork)(Bstr(a->argv[3]).raw()), 1);
616 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Internal), 1);
617 }
618#if defined(VBOX_WITH_NETFLT)
619 else if (!strcmp(a->argv[2], "hostonly"))
620 {
621 if (a->argc <= 3)
622 {
623 errorArgument("Missing argument to '%s'", a->argv[2]);
624 rc = E_FAIL;
625 break;
626 }
627 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
628 CHECK_ERROR_RET(adapter, COMSETTER(HostOnlyInterface)(Bstr(a->argv[3]).raw()), 1);
629 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly), 1);
630 }
631#endif
632 else if (!strcmp(a->argv[2], "generic"))
633 {
634 if (a->argc <= 3)
635 {
636 errorArgument("Missing argument to '%s'", a->argv[2]);
637 rc = E_FAIL;
638 break;
639 }
640 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
641 CHECK_ERROR_RET(adapter, COMSETTER(GenericDriver)(Bstr(a->argv[3]).raw()), 1);
642 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), 1);
643 }
644 /** @todo obsolete, remove eventually */
645 else if (!strcmp(a->argv[2], "vde"))
646 {
647 if (a->argc <= 3)
648 {
649 errorArgument("Missing argument to '%s'", a->argv[2]);
650 rc = E_FAIL;
651 break;
652 }
653 CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
654 CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), 1);
655 CHECK_ERROR_RET(adapter, SetProperty(Bstr("name").raw(), Bstr(a->argv[3]).raw()), 1);
656 }
657 else
658 {
659 errorArgument("Invalid type '%s' specfied for NIC %lu", Utf8Str(a->argv[2]).c_str(), n);
660 rc = E_FAIL;
661 break;
662 }
663 }
664 else
665 RTMsgError("The NIC %d is currently disabled and thus its attachment type can't be changed", n);
666 }
667 }
668 else if ( !strcmp(a->argv[1], "vrde")
669 || !strcmp(a->argv[1], "vrdp"))
670 {
671 if (!strcmp(a->argv[1], "vrdp"))
672 RTStrmPrintf(g_pStdErr, "Warning: 'vrdp' is deprecated. Use 'vrde'.\n");
673
674 if (a->argc <= 1 + 1)
675 {
676 errorArgument("Missing argument to '%s'", a->argv[1]);
677 rc = E_FAIL;
678 break;
679 }
680 ComPtr<IVRDEServer> vrdeServer;
681 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
682 ASSERT(vrdeServer);
683 if (vrdeServer)
684 {
685 if (!strcmp(a->argv[2], "on"))
686 {
687 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(TRUE));
688 }
689 else if (!strcmp(a->argv[2], "off"))
690 {
691 CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(FALSE));
692 }
693 else
694 {
695 errorArgument("Invalid remote desktop server state '%s'", Utf8Str(a->argv[2]).c_str());
696 rc = E_FAIL;
697 break;
698 }
699 }
700 }
701 else if ( !strcmp(a->argv[1], "vrdeport")
702 || !strcmp(a->argv[1], "vrdpport"))
703 {
704 if (!strcmp(a->argv[1], "vrdpport"))
705 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpport' is deprecated. Use 'vrdeport'.\n");
706
707 if (a->argc <= 1 + 1)
708 {
709 errorArgument("Missing argument to '%s'", a->argv[1]);
710 rc = E_FAIL;
711 break;
712 }
713
714 ComPtr<IVRDEServer> vrdeServer;
715 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
716 ASSERT(vrdeServer);
717 if (vrdeServer)
718 {
719 Bstr ports;
720
721 if (!strcmp(a->argv[2], "default"))
722 ports = "0";
723 else
724 ports = a->argv[2];
725
726 CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), ports.raw()));
727 }
728 }
729 else if ( !strcmp(a->argv[1], "vrdevideochannelquality")
730 || !strcmp(a->argv[1], "vrdpvideochannelquality"))
731 {
732 if (!strcmp(a->argv[1], "vrdpvideochannelquality"))
733 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpvideochannelquality' is deprecated. Use 'vrdevideochannelquality'.\n");
734
735 if (a->argc <= 1 + 1)
736 {
737 errorArgument("Missing argument to '%s'", a->argv[1]);
738 rc = E_FAIL;
739 break;
740 }
741 ComPtr<IVRDEServer> vrdeServer;
742 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
743 ASSERT(vrdeServer);
744 if (vrdeServer)
745 {
746 Bstr value = a->argv[2];
747
748 CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("VideoChannel/Quality").raw(), value.raw()));
749 }
750 }
751 else if (!strcmp(a->argv[1], "vrdeproperty"))
752 {
753 if (a->argc <= 1 + 1)
754 {
755 errorArgument("Missing argument to '%s'", a->argv[1]);
756 rc = E_FAIL;
757 break;
758 }
759 ComPtr<IVRDEServer> vrdeServer;
760 sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
761 ASSERT(vrdeServer);
762 if (vrdeServer)
763 {
764 /* Parse 'name=value' */
765 char *pszProperty = RTStrDup(a->argv[2]);
766 if (pszProperty)
767 {
768 char *pDelimiter = strchr(pszProperty, '=');
769 if (pDelimiter)
770 {
771 *pDelimiter = '\0';
772
773 Bstr bstrName = pszProperty;
774 Bstr bstrValue = &pDelimiter[1];
775 CHECK_ERROR(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
776 }
777 else
778 {
779 errorArgument("Invalid vrdeproperty argument '%s'", a->argv[2]);
780 rc = E_FAIL;
781 }
782 RTStrFree(pszProperty);
783 }
784 else
785 {
786 RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for VRDE property '%s'\n", a->argv[2]);
787 rc = E_FAIL;
788 }
789 }
790 if (FAILED(rc))
791 {
792 break;
793 }
794 }
795 else if ( !strcmp(a->argv[1], "usbattach")
796 || !strcmp(a->argv[1], "usbdetach"))
797 {
798 if (a->argc < 3)
799 {
800 errorSyntax(USAGE_CONTROLVM, "Not enough parameters");
801 rc = E_FAIL;
802 break;
803 }
804
805 bool attach = !strcmp(a->argv[1], "usbattach");
806
807 Bstr usbId = a->argv[2];
808 if (Guid(usbId).isEmpty())
809 {
810 // assume address
811 if (attach)
812 {
813 ComPtr <IHost> host;
814 CHECK_ERROR_BREAK(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
815 SafeIfaceArray <IHostUSBDevice> coll;
816 CHECK_ERROR_BREAK(host, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
817 ComPtr <IHostUSBDevice> dev;
818 CHECK_ERROR_BREAK(host, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
819 dev.asOutParam()));
820 CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
821 }
822 else
823 {
824 SafeIfaceArray <IUSBDevice> coll;
825 CHECK_ERROR_BREAK(console, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
826 ComPtr <IUSBDevice> dev;
827 CHECK_ERROR_BREAK(console, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
828 dev.asOutParam()));
829 CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
830 }
831 }
832
833 if (attach)
834 CHECK_ERROR_BREAK(console, AttachUSBDevice(usbId.raw()));
835 else
836 {
837 ComPtr <IUSBDevice> dev;
838 CHECK_ERROR_BREAK(console, DetachUSBDevice(usbId.raw(),
839 dev.asOutParam()));
840 }
841 }
842 else if (!strcmp(a->argv[1], "setvideomodehint"))
843 {
844 if (a->argc != 5 && a->argc != 6)
845 {
846 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
847 rc = E_FAIL;
848 break;
849 }
850 uint32_t xres = RTStrToUInt32(a->argv[2]);
851 uint32_t yres = RTStrToUInt32(a->argv[3]);
852 uint32_t bpp = RTStrToUInt32(a->argv[4]);
853 uint32_t displayIdx = 0;
854 if (a->argc == 6)
855 displayIdx = RTStrToUInt32(a->argv[5]);
856
857 ComPtr<IDisplay> display;
858 CHECK_ERROR_BREAK(console, COMGETTER(Display)(display.asOutParam()));
859 CHECK_ERROR_BREAK(display, SetVideoModeHint(xres, yres, bpp, displayIdx));
860 }
861 else if (!strcmp(a->argv[1], "setcredentials"))
862 {
863 bool fAllowLocalLogon = true;
864 if (a->argc == 7)
865 {
866 if ( strcmp(a->argv[5], "--allowlocallogon")
867 && strcmp(a->argv[5], "-allowlocallogon"))
868 {
869 errorArgument("Invalid parameter '%s'", a->argv[5]);
870 rc = E_FAIL;
871 break;
872 }
873 if (!strcmp(a->argv[6], "no"))
874 fAllowLocalLogon = false;
875 }
876 else if (a->argc != 5)
877 {
878 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
879 rc = E_FAIL;
880 break;
881 }
882
883 ComPtr<IGuest> guest;
884 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(guest.asOutParam()));
885 CHECK_ERROR_BREAK(guest, SetCredentials(Bstr(a->argv[2]).raw(),
886 Bstr(a->argv[3]).raw(),
887 Bstr(a->argv[4]).raw(),
888 fAllowLocalLogon));
889 }
890#if 0 /* TODO: review & remove */
891 else if (!strcmp(a->argv[1], "dvdattach"))
892 {
893 Bstr uuid;
894 if (a->argc != 3)
895 {
896 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
897 rc = E_FAIL;
898 break;
899 }
900
901 ComPtr<IMedium> dvdMedium;
902
903 /* unmount? */
904 if (!strcmp(a->argv[2], "none"))
905 {
906 /* nothing to do, NULL object will cause unmount */
907 }
908 /* host drive? */
909 else if (!strncmp(a->argv[2], "host:", 5))
910 {
911 ComPtr<IHost> host;
912 CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
913
914 rc = host->FindHostDVDDrive(Bstr(a->argv[2] + 5), dvdMedium.asOutParam());
915 if (!dvdMedium)
916 {
917 errorArgument("Invalid host DVD drive name \"%s\"",
918 a->argv[2] + 5);
919 rc = E_FAIL;
920 break;
921 }
922 }
923 else
924 {
925 /* first assume it's a UUID */
926 uuid = a->argv[2];
927 rc = a->virtualBox->GetDVDImage(uuid, dvdMedium.asOutParam());
928 if (FAILED(rc) || !dvdMedium)
929 {
930 /* must be a filename, check if it's in the collection */
931 rc = a->virtualBox->FindDVDImage(Bstr(a->argv[2]), dvdMedium.asOutParam());
932 /* not registered, do that on the fly */
933 if (!dvdMedium)
934 {
935 Bstr emptyUUID;
936 CHECK_ERROR(a->virtualBox, OpenDVDImage(Bstr(a->argv[2]), emptyUUID, dvdMedium.asOutParam()));
937 }
938 }
939 if (!dvdMedium)
940 {
941 rc = E_FAIL;
942 break;
943 }
944 }
945
946 /** @todo generalize this, allow arbitrary number of DVD drives
947 * and as a consequence multiple attachments and different
948 * storage controllers. */
949 if (dvdMedium)
950 dvdMedium->COMGETTER(Id)(uuid.asOutParam());
951 else
952 uuid = Guid().toString();
953 CHECK_ERROR(machine, MountMedium(Bstr("IDE Controller"), 1, 0, uuid, FALSE /* aForce */));
954 }
955 else if (!strcmp(a->argv[1], "floppyattach"))
956 {
957 Bstr uuid;
958 if (a->argc != 3)
959 {
960 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
961 rc = E_FAIL;
962 break;
963 }
964
965 ComPtr<IMedium> floppyMedium;
966
967 /* unmount? */
968 if (!strcmp(a->argv[2], "none"))
969 {
970 /* nothing to do, NULL object will cause unmount */
971 }
972 /* host drive? */
973 else if (!strncmp(a->argv[2], "host:", 5))
974 {
975 ComPtr<IHost> host;
976 CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
977 host->FindHostFloppyDrive(Bstr(a->argv[2] + 5), floppyMedium.asOutParam());
978 if (!floppyMedium)
979 {
980 errorArgument("Invalid host floppy drive name \"%s\"",
981 a->argv[2] + 5);
982 rc = E_FAIL;
983 break;
984 }
985 }
986 else
987 {
988 /* first assume it's a UUID */
989 uuid = a->argv[2];
990 rc = a->virtualBox->GetFloppyImage(uuid, floppyMedium.asOutParam());
991 if (FAILED(rc) || !floppyMedium)
992 {
993 /* must be a filename, check if it's in the collection */
994 rc = a->virtualBox->FindFloppyImage(Bstr(a->argv[2]), floppyMedium.asOutParam());
995 /* not registered, do that on the fly */
996 if (!floppyMedium)
997 {
998 Bstr emptyUUID;
999 CHECK_ERROR(a->virtualBox, OpenFloppyImage(Bstr(a->argv[2]), emptyUUID, floppyMedium.asOutParam()));
1000 }
1001 }
1002 if (!floppyMedium)
1003 {
1004 rc = E_FAIL;
1005 break;
1006 }
1007 }
1008 floppyMedium->COMGETTER(Id)(uuid.asOutParam());
1009 CHECK_ERROR(machine, MountMedium(Bstr("Floppy Controller"), 0, 0, uuid, FALSE /* aForce */));
1010 }
1011#endif /* obsolete dvdattach/floppyattach */
1012 else if (!strcmp(a->argv[1], "guestmemoryballoon"))
1013 {
1014 if (a->argc != 3)
1015 {
1016 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
1017 rc = E_FAIL;
1018 break;
1019 }
1020 uint32_t uVal;
1021 int vrc;
1022 vrc = RTStrToUInt32Ex(a->argv[2], NULL, 0, &uVal);
1023 if (vrc != VINF_SUCCESS)
1024 {
1025 errorArgument("Error parsing guest memory balloon size '%s'", a->argv[2]);
1026 rc = E_FAIL;
1027 break;
1028 }
1029 /* guest is running; update IGuest */
1030 ComPtr <IGuest> guest;
1031 rc = console->COMGETTER(Guest)(guest.asOutParam());
1032 if (SUCCEEDED(rc))
1033 CHECK_ERROR(guest, COMSETTER(MemoryBalloonSize)(uVal));
1034 }
1035 else if (!strcmp(a->argv[1], "teleport"))
1036 {
1037 Bstr bstrHostname;
1038 uint32_t uMaxDowntime = 250 /*ms*/;
1039 uint32_t uPort = UINT32_MAX;
1040 uint32_t cMsTimeout = 0;
1041 Bstr bstrPassword("");
1042 static const RTGETOPTDEF s_aTeleportOptions[] =
1043 {
1044 { "--host", 'h', RTGETOPT_REQ_STRING }, /** @todo RTGETOPT_FLAG_MANDATORY */
1045 { "--hostname", 'h', RTGETOPT_REQ_STRING }, /** @todo remove this */
1046 { "--maxdowntime", 'd', RTGETOPT_REQ_UINT32 },
1047 { "--port", 'p', RTGETOPT_REQ_UINT32 }, /** @todo RTGETOPT_FLAG_MANDATORY */
1048 { "--password", 'P', RTGETOPT_REQ_STRING },
1049 { "--timeout", 't', RTGETOPT_REQ_UINT32 },
1050 { "--detailed-progress", 'D', RTGETOPT_REQ_NOTHING }
1051 };
1052 RTGETOPTSTATE GetOptState;
1053 RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTeleportOptions, RT_ELEMENTS(s_aTeleportOptions), 2, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1054 int ch;
1055 RTGETOPTUNION Value;
1056 while ( SUCCEEDED(rc)
1057 && (ch = RTGetOpt(&GetOptState, &Value)))
1058 {
1059 switch (ch)
1060 {
1061 case 'h': bstrHostname = Value.psz; break;
1062 case 'd': uMaxDowntime = Value.u32; break;
1063 case 'D': g_fDetailedProgress = true; break;
1064 case 'p': uPort = Value.u32; break;
1065 case 'P': bstrPassword = Value.psz; break;
1066 case 't': cMsTimeout = Value.u32; break;
1067 default:
1068 errorGetOpt(USAGE_CONTROLVM, ch, &Value);
1069 rc = E_FAIL;
1070 break;
1071 }
1072 }
1073 if (FAILED(rc))
1074 break;
1075
1076 ComPtr<IProgress> progress;
1077 CHECK_ERROR_BREAK(console, Teleport(bstrHostname.raw(), uPort,
1078 bstrPassword.raw(),
1079 uMaxDowntime,
1080 progress.asOutParam()));
1081
1082 if (cMsTimeout)
1083 {
1084 rc = progress->COMSETTER(Timeout)(cMsTimeout);
1085 if (FAILED(rc) && rc != VBOX_E_INVALID_OBJECT_STATE)
1086 CHECK_ERROR_BREAK(progress, COMSETTER(Timeout)(cMsTimeout)); /* lazyness */
1087 }
1088
1089 rc = showProgress(progress);
1090 CHECK_PROGRESS_ERROR(progress, ("Teleportation failed"));
1091 }
1092 else if (!strcmp(a->argv[1], "screenshotpng"))
1093 {
1094 if (a->argc <= 2 || a->argc > 4)
1095 {
1096 errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
1097 rc = E_FAIL;
1098 break;
1099 }
1100 int vrc;
1101 uint32_t displayIdx = 0;
1102 if (a->argc == 4)
1103 {
1104 vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &displayIdx);
1105 if (vrc != VINF_SUCCESS)
1106 {
1107 errorArgument("Error parsing display number '%s'", a->argv[3]);
1108 rc = E_FAIL;
1109 break;
1110 }
1111 }
1112 ComPtr<IDisplay> pDisplay;
1113 CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
1114 ULONG width, height, bpp;
1115 CHECK_ERROR_BREAK(pDisplay, GetScreenResolution(displayIdx, &width, &height, &bpp));
1116 com::SafeArray<BYTE> saScreenshot;
1117 CHECK_ERROR_BREAK(pDisplay, TakeScreenShotPNGToArray(displayIdx, width, height, ComSafeArrayAsOutParam(saScreenshot)));
1118 RTFILE pngFile = NIL_RTFILE;
1119 vrc = RTFileOpen(&pngFile, a->argv[2], RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_TRUNCATE);
1120 if (RT_FAILURE(vrc))
1121 {
1122 RTMsgError("Failed to create file '%s'. rc=%Rrc", a->argv[2], vrc);
1123 rc = E_FAIL;
1124 break;
1125 }
1126 vrc = RTFileWrite(pngFile, saScreenshot.raw(), saScreenshot.size(), NULL);
1127 if (RT_FAILURE(vrc))
1128 {
1129 RTMsgError("Failed to write screenshot to file '%s'. rc=%Rrc", a->argv[2], vrc);
1130 rc = E_FAIL;
1131 }
1132 RTFileClose(pngFile);
1133 }
1134 else
1135 {
1136 errorSyntax(USAGE_CONTROLVM, "Invalid parameter '%s'", a->argv[1]);
1137 rc = E_FAIL;
1138 }
1139 } while (0);
1140
1141 a->session->UnlockMachine();
1142
1143 return SUCCEEDED(rc) ? 0 : 1;
1144}
Note: See TracBrowser for help on using the repository browser.

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