VirtualBox

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

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

VBoxManage: don't unpause a VM if 'controlvm savestate' failed and the VM was paused before

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