VirtualBox

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

Last change on this file since 43421 was 42551, checked in by vboxsync, 12 years ago

Main: big API naming cleanup, use all caps acronyms everywhere, including SDK docs
Frontends/VBoxManage: implement guestcontrol execute for new API, disabled by default

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

© 2023 Oracle
ContactPrivacy policyTerms of Use