VirtualBox

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

Last change on this file since 37200 was 37200, checked in by vboxsync, 14 years ago

API+Frontends: Generic network attachment driver support which obsoletes the special case for VDE. Big API cleanup in the same area. Adapt all frontends to these changes (full implementation in VBoxManage, minimum implementation in GUI).

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