VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp@ 68095

Last change on this file since 68095 was 68095, checked in by vboxsync, 8 years ago

VBoxManage/unattended: Implemented new attributes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 63.7 KB
Line 
1/* $Id: VBoxManageMisc.cpp 68095 2017-07-24 12:45:11Z vboxsync $ */
2/** @file
3 * VBoxManage - VirtualBox's command-line interface.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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#ifndef VBOX_ONLY_DOCS
23# include <VBox/com/com.h>
24# include <VBox/com/string.h>
25# include <VBox/com/Guid.h>
26# include <VBox/com/array.h>
27# include <VBox/com/ErrorInfo.h>
28# include <VBox/com/errorprint.h>
29# include <VBox/com/VirtualBox.h>
30#endif /* !VBOX_ONLY_DOCS */
31
32#include <iprt/asm.h>
33#include <iprt/buildconfig.h>
34#include <iprt/cidr.h>
35#include <iprt/ctype.h>
36#include <iprt/dir.h>
37#include <iprt/env.h>
38#include <VBox/err.h>
39#include <iprt/file.h>
40#include <iprt/sha.h>
41#include <iprt/initterm.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/cpp/path.h>
45#include <iprt/stream.h>
46#include <iprt/string.h>
47#include <iprt/stdarg.h>
48#include <iprt/thread.h>
49#include <iprt/uuid.h>
50#include <iprt/getopt.h>
51#include <iprt/ctype.h>
52#include <VBox/version.h>
53#include <VBox/log.h>
54
55#include "VBoxManage.h"
56
57#include <list>
58
59using namespace com;
60
61
62
63RTEXITCODE handleRegisterVM(HandlerArg *a)
64{
65 HRESULT rc;
66
67 if (a->argc != 1)
68 return errorSyntax(USAGE_REGISTERVM, "Incorrect number of parameters");
69
70 ComPtr<IMachine> machine;
71 /** @todo Ugly hack to get both the API interpretation of relative paths
72 * and the client's interpretation of relative paths. Remove after the API
73 * has been redesigned. */
74 rc = a->virtualBox->OpenMachine(Bstr(a->argv[0]).raw(),
75 machine.asOutParam());
76 if (rc == VBOX_E_FILE_ERROR)
77 {
78 char szVMFileAbs[RTPATH_MAX] = "";
79 int vrc = RTPathAbs(a->argv[0], szVMFileAbs, sizeof(szVMFileAbs));
80 if (RT_FAILURE(vrc))
81 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
82 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(szVMFileAbs).raw(),
83 machine.asOutParam()));
84 }
85 else if (FAILED(rc))
86 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(a->argv[0]).raw(),
87 machine.asOutParam()));
88 if (SUCCEEDED(rc))
89 {
90 ASSERT(machine);
91 CHECK_ERROR(a->virtualBox, RegisterMachine(machine));
92 }
93 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
94}
95
96static const RTGETOPTDEF g_aUnregisterVMOptions[] =
97{
98 { "--delete", 'd', RTGETOPT_REQ_NOTHING },
99 { "-delete", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
100};
101
102RTEXITCODE handleUnregisterVM(HandlerArg *a)
103{
104 HRESULT rc;
105 const char *VMName = NULL;
106 bool fDelete = false;
107
108 int c;
109 RTGETOPTUNION ValueUnion;
110 RTGETOPTSTATE GetState;
111 // start at 0 because main() has hacked both the argc and argv given to us
112 RTGetOptInit(&GetState, a->argc, a->argv, g_aUnregisterVMOptions, RT_ELEMENTS(g_aUnregisterVMOptions),
113 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
114 while ((c = RTGetOpt(&GetState, &ValueUnion)))
115 {
116 switch (c)
117 {
118 case 'd': // --delete
119 fDelete = true;
120 break;
121
122 case VINF_GETOPT_NOT_OPTION:
123 if (!VMName)
124 VMName = ValueUnion.psz;
125 else
126 return errorSyntax(USAGE_UNREGISTERVM, "Invalid parameter '%s'", ValueUnion.psz);
127 break;
128
129 default:
130 if (c > 0)
131 {
132 if (RT_C_IS_PRINT(c))
133 return errorSyntax(USAGE_UNREGISTERVM, "Invalid option -%c", c);
134 else
135 return errorSyntax(USAGE_UNREGISTERVM, "Invalid option case %i", c);
136 }
137 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
138 return errorSyntax(USAGE_UNREGISTERVM, "unknown option: %s\n", ValueUnion.psz);
139 else if (ValueUnion.pDef)
140 return errorSyntax(USAGE_UNREGISTERVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
141 else
142 return errorSyntax(USAGE_UNREGISTERVM, "error: %Rrs", c);
143 }
144 }
145
146 /* check for required options */
147 if (!VMName)
148 return errorSyntax(USAGE_UNREGISTERVM, "VM name required");
149
150 ComPtr<IMachine> machine;
151 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(VMName).raw(),
152 machine.asOutParam()),
153 RTEXITCODE_FAILURE);
154 SafeIfaceArray<IMedium> aMedia;
155 CHECK_ERROR_RET(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly,
156 ComSafeArrayAsOutParam(aMedia)),
157 RTEXITCODE_FAILURE);
158 if (fDelete)
159 {
160 ComPtr<IProgress> pProgress;
161 CHECK_ERROR_RET(machine, DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress.asOutParam()),
162 RTEXITCODE_FAILURE);
163
164 rc = showProgress(pProgress);
165 CHECK_PROGRESS_ERROR_RET(pProgress, ("Machine delete failed"), RTEXITCODE_FAILURE);
166 }
167 else
168 {
169 /* Note that the IMachine::Unregister method will return the medium
170 * reference in a sane order, which means that closing will normally
171 * succeed, unless there is still another machine which uses the
172 * medium. No harm done if we ignore the error. */
173 for (size_t i = 0; i < aMedia.size(); i++)
174 {
175 IMedium *pMedium = aMedia[i];
176 if (pMedium)
177 rc = pMedium->Close();
178 }
179 rc = S_OK;
180 }
181 return RTEXITCODE_SUCCESS;
182}
183
184static const RTGETOPTDEF g_aCreateVMOptions[] =
185{
186 { "--name", 'n', RTGETOPT_REQ_STRING },
187 { "-name", 'n', RTGETOPT_REQ_STRING },
188 { "--groups", 'g', RTGETOPT_REQ_STRING },
189 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
190 { "-basefolder", 'p', RTGETOPT_REQ_STRING },
191 { "--ostype", 'o', RTGETOPT_REQ_STRING },
192 { "-ostype", 'o', RTGETOPT_REQ_STRING },
193 { "--uuid", 'u', RTGETOPT_REQ_UUID },
194 { "-uuid", 'u', RTGETOPT_REQ_UUID },
195 { "--register", 'r', RTGETOPT_REQ_NOTHING },
196 { "-register", 'r', RTGETOPT_REQ_NOTHING },
197};
198
199RTEXITCODE handleCreateVM(HandlerArg *a)
200{
201 HRESULT rc;
202 Bstr bstrBaseFolder;
203 Bstr bstrName;
204 Bstr bstrOsTypeId;
205 Bstr bstrUuid;
206 bool fRegister = false;
207 com::SafeArray<BSTR> groups;
208
209 int c;
210 RTGETOPTUNION ValueUnion;
211 RTGETOPTSTATE GetState;
212 // start at 0 because main() has hacked both the argc and argv given to us
213 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateVMOptions, RT_ELEMENTS(g_aCreateVMOptions),
214 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
215 while ((c = RTGetOpt(&GetState, &ValueUnion)))
216 {
217 switch (c)
218 {
219 case 'n': // --name
220 bstrName = ValueUnion.psz;
221 break;
222
223 case 'g': // --groups
224 parseGroups(ValueUnion.psz, &groups);
225 break;
226
227 case 'p': // --basefolder
228 bstrBaseFolder = ValueUnion.psz;
229 break;
230
231 case 'o': // --ostype
232 bstrOsTypeId = ValueUnion.psz;
233 break;
234
235 case 'u': // --uuid
236 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
237 break;
238
239 case 'r': // --register
240 fRegister = true;
241 break;
242
243 default:
244 return errorGetOpt(USAGE_CREATEVM, c, &ValueUnion);
245 }
246 }
247
248 /* check for required options */
249 if (bstrName.isEmpty())
250 return errorSyntax(USAGE_CREATEVM, "Parameter --name is required");
251
252 do
253 {
254 Bstr createFlags;
255 if (!bstrUuid.isEmpty())
256 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
257 Bstr bstrPrimaryGroup;
258 if (groups.size())
259 bstrPrimaryGroup = groups[0];
260 Bstr bstrSettingsFile;
261 CHECK_ERROR_BREAK(a->virtualBox,
262 ComposeMachineFilename(bstrName.raw(),
263 bstrPrimaryGroup.raw(),
264 createFlags.raw(),
265 bstrBaseFolder.raw(),
266 bstrSettingsFile.asOutParam()));
267 ComPtr<IMachine> machine;
268 CHECK_ERROR_BREAK(a->virtualBox,
269 CreateMachine(bstrSettingsFile.raw(),
270 bstrName.raw(),
271 ComSafeArrayAsInParam(groups),
272 bstrOsTypeId.raw(),
273 createFlags.raw(),
274 machine.asOutParam()));
275
276 CHECK_ERROR_BREAK(machine, SaveSettings());
277 if (fRegister)
278 {
279 CHECK_ERROR_BREAK(a->virtualBox, RegisterMachine(machine));
280 }
281 Bstr uuid;
282 CHECK_ERROR_BREAK(machine, COMGETTER(Id)(uuid.asOutParam()));
283 Bstr settingsFile;
284 CHECK_ERROR_BREAK(machine, COMGETTER(SettingsFilePath)(settingsFile.asOutParam()));
285 RTPrintf("Virtual machine '%ls' is created%s.\n"
286 "UUID: %s\n"
287 "Settings file: '%ls'\n",
288 bstrName.raw(), fRegister ? " and registered" : "",
289 Utf8Str(uuid).c_str(), settingsFile.raw());
290 }
291 while (0);
292
293 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
294}
295
296static const RTGETOPTDEF g_aCloneVMOptions[] =
297{
298 { "--snapshot", 's', RTGETOPT_REQ_STRING },
299 { "--name", 'n', RTGETOPT_REQ_STRING },
300 { "--groups", 'g', RTGETOPT_REQ_STRING },
301 { "--mode", 'm', RTGETOPT_REQ_STRING },
302 { "--options", 'o', RTGETOPT_REQ_STRING },
303 { "--register", 'r', RTGETOPT_REQ_NOTHING },
304 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
305 { "--uuid", 'u', RTGETOPT_REQ_UUID },
306};
307
308static int parseCloneMode(const char *psz, CloneMode_T *pMode)
309{
310 if (!RTStrICmp(psz, "machine"))
311 *pMode = CloneMode_MachineState;
312 else if (!RTStrICmp(psz, "machineandchildren"))
313 *pMode = CloneMode_MachineAndChildStates;
314 else if (!RTStrICmp(psz, "all"))
315 *pMode = CloneMode_AllStates;
316 else
317 return VERR_PARSE_ERROR;
318
319 return VINF_SUCCESS;
320}
321
322static int parseCloneOptions(const char *psz, com::SafeArray<CloneOptions_T> *options)
323{
324 int rc = VINF_SUCCESS;
325 while (psz && *psz && RT_SUCCESS(rc))
326 {
327 size_t len;
328 const char *pszComma = strchr(psz, ',');
329 if (pszComma)
330 len = pszComma - psz;
331 else
332 len = strlen(psz);
333 if (len > 0)
334 {
335 if (!RTStrNICmp(psz, "KeepAllMACs", len))
336 options->push_back(CloneOptions_KeepAllMACs);
337 else if (!RTStrNICmp(psz, "KeepNATMACs", len))
338 options->push_back(CloneOptions_KeepNATMACs);
339 else if (!RTStrNICmp(psz, "KeepDiskNames", len))
340 options->push_back(CloneOptions_KeepDiskNames);
341 else if ( !RTStrNICmp(psz, "Link", len)
342 || !RTStrNICmp(psz, "Linked", len))
343 options->push_back(CloneOptions_Link);
344 else
345 rc = VERR_PARSE_ERROR;
346 }
347 if (pszComma)
348 psz += len + 1;
349 else
350 psz += len;
351 }
352
353 return rc;
354}
355
356RTEXITCODE handleCloneVM(HandlerArg *a)
357{
358 HRESULT rc;
359 const char *pszSrcName = NULL;
360 const char *pszSnapshotName = NULL;
361 CloneMode_T mode = CloneMode_MachineState;
362 com::SafeArray<CloneOptions_T> options;
363 const char *pszTrgName = NULL;
364 const char *pszTrgBaseFolder = NULL;
365 bool fRegister = false;
366 Bstr bstrUuid;
367 com::SafeArray<BSTR> groups;
368
369 int c;
370 RTGETOPTUNION ValueUnion;
371 RTGETOPTSTATE GetState;
372 // start at 0 because main() has hacked both the argc and argv given to us
373 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneVMOptions, RT_ELEMENTS(g_aCloneVMOptions),
374 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
375 while ((c = RTGetOpt(&GetState, &ValueUnion)))
376 {
377 switch (c)
378 {
379 case 's': // --snapshot
380 pszSnapshotName = ValueUnion.psz;
381 break;
382
383 case 'n': // --name
384 pszTrgName = ValueUnion.psz;
385 break;
386
387 case 'g': // --groups
388 parseGroups(ValueUnion.psz, &groups);
389 break;
390
391 case 'p': // --basefolder
392 pszTrgBaseFolder = ValueUnion.psz;
393 break;
394
395 case 'm': // --mode
396 if (RT_FAILURE(parseCloneMode(ValueUnion.psz, &mode)))
397 return errorArgument("Invalid clone mode '%s'\n", ValueUnion.psz);
398 break;
399
400 case 'o': // --options
401 if (RT_FAILURE(parseCloneOptions(ValueUnion.psz, &options)))
402 return errorArgument("Invalid clone options '%s'\n", ValueUnion.psz);
403 break;
404
405 case 'u': // --uuid
406 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
407 break;
408
409 case 'r': // --register
410 fRegister = true;
411 break;
412
413 case VINF_GETOPT_NOT_OPTION:
414 if (!pszSrcName)
415 pszSrcName = ValueUnion.psz;
416 else
417 return errorSyntax(USAGE_CLONEVM, "Invalid parameter '%s'", ValueUnion.psz);
418 break;
419
420 default:
421 return errorGetOpt(USAGE_CLONEVM, c, &ValueUnion);
422 }
423 }
424
425 /* Check for required options */
426 if (!pszSrcName)
427 return errorSyntax(USAGE_CLONEVM, "VM name required");
428
429 /* Get the machine object */
430 ComPtr<IMachine> srcMachine;
431 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
432 srcMachine.asOutParam()),
433 RTEXITCODE_FAILURE);
434
435 /* If a snapshot name/uuid was given, get the particular machine of this
436 * snapshot. */
437 if (pszSnapshotName)
438 {
439 ComPtr<ISnapshot> srcSnapshot;
440 CHECK_ERROR_RET(srcMachine, FindSnapshot(Bstr(pszSnapshotName).raw(),
441 srcSnapshot.asOutParam()),
442 RTEXITCODE_FAILURE);
443 CHECK_ERROR_RET(srcSnapshot, COMGETTER(Machine)(srcMachine.asOutParam()),
444 RTEXITCODE_FAILURE);
445 }
446
447 /* Default name necessary? */
448 if (!pszTrgName)
449 pszTrgName = RTStrAPrintf2("%s Clone", pszSrcName);
450
451 Bstr createFlags;
452 if (!bstrUuid.isEmpty())
453 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
454 Bstr bstrPrimaryGroup;
455 if (groups.size())
456 bstrPrimaryGroup = groups[0];
457 Bstr bstrSettingsFile;
458 CHECK_ERROR_RET(a->virtualBox,
459 ComposeMachineFilename(Bstr(pszTrgName).raw(),
460 bstrPrimaryGroup.raw(),
461 createFlags.raw(),
462 Bstr(pszTrgBaseFolder).raw(),
463 bstrSettingsFile.asOutParam()),
464 RTEXITCODE_FAILURE);
465
466 ComPtr<IMachine> trgMachine;
467 CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(),
468 Bstr(pszTrgName).raw(),
469 ComSafeArrayAsInParam(groups),
470 NULL,
471 createFlags.raw(),
472 trgMachine.asOutParam()),
473 RTEXITCODE_FAILURE);
474
475 /* Start the cloning */
476 ComPtr<IProgress> progress;
477 CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine,
478 mode,
479 ComSafeArrayAsInParam(options),
480 progress.asOutParam()),
481 RTEXITCODE_FAILURE);
482 rc = showProgress(progress);
483 CHECK_PROGRESS_ERROR_RET(progress, ("Clone VM failed"), RTEXITCODE_FAILURE);
484
485 if (fRegister)
486 CHECK_ERROR_RET(a->virtualBox, RegisterMachine(trgMachine), RTEXITCODE_FAILURE);
487
488 Bstr bstrNewName;
489 CHECK_ERROR_RET(trgMachine, COMGETTER(Name)(bstrNewName.asOutParam()), RTEXITCODE_FAILURE);
490 RTPrintf("Machine has been successfully cloned as \"%ls\"\n", bstrNewName.raw());
491
492 return RTEXITCODE_SUCCESS;
493}
494
495RTEXITCODE handleStartVM(HandlerArg *a)
496{
497 HRESULT rc = S_OK;
498 std::list<const char *> VMs;
499 Bstr sessionType;
500 Utf8Str strEnv;
501
502#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
503 /* make sure the VM process will by default start on the same display as VBoxManage */
504 {
505 const char *pszDisplay = RTEnvGet("DISPLAY");
506 if (pszDisplay)
507 strEnv = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
508 const char *pszXAuth = RTEnvGet("XAUTHORITY");
509 if (pszXAuth)
510 strEnv.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
511 }
512#endif
513
514 static const RTGETOPTDEF s_aStartVMOptions[] =
515 {
516 { "--type", 't', RTGETOPT_REQ_STRING },
517 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
518 { "--putenv", 'E', RTGETOPT_REQ_STRING },
519 };
520 int c;
521 RTGETOPTUNION ValueUnion;
522 RTGETOPTSTATE GetState;
523 // start at 0 because main() has hacked both the argc and argv given to us
524 RTGetOptInit(&GetState, a->argc, a->argv, s_aStartVMOptions, RT_ELEMENTS(s_aStartVMOptions),
525 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
526 while ((c = RTGetOpt(&GetState, &ValueUnion)))
527 {
528 switch (c)
529 {
530 case 't': // --type
531 if (!RTStrICmp(ValueUnion.psz, "gui"))
532 {
533 sessionType = "gui";
534 }
535#ifdef VBOX_WITH_VBOXSDL
536 else if (!RTStrICmp(ValueUnion.psz, "sdl"))
537 {
538 sessionType = "sdl";
539 }
540#endif
541#ifdef VBOX_WITH_HEADLESS
542 else if (!RTStrICmp(ValueUnion.psz, "capture"))
543 {
544 sessionType = "capture";
545 }
546 else if (!RTStrICmp(ValueUnion.psz, "headless"))
547 {
548 sessionType = "headless";
549 }
550#endif
551 else
552 sessionType = ValueUnion.psz;
553 break;
554
555 case 'E': // --putenv
556 if (!RTStrStr(ValueUnion.psz, "\n"))
557 strEnv.append(Utf8StrFmt("%s\n", ValueUnion.psz));
558 else
559 return errorSyntax(USAGE_STARTVM, "Parameter to option --putenv must not contain any newline character");
560 break;
561
562 case VINF_GETOPT_NOT_OPTION:
563 VMs.push_back(ValueUnion.psz);
564 break;
565
566 default:
567 if (c > 0)
568 {
569 if (RT_C_IS_PRINT(c))
570 return errorSyntax(USAGE_STARTVM, "Invalid option -%c", c);
571 else
572 return errorSyntax(USAGE_STARTVM, "Invalid option case %i", c);
573 }
574 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
575 return errorSyntax(USAGE_STARTVM, "unknown option: %s\n", ValueUnion.psz);
576 else if (ValueUnion.pDef)
577 return errorSyntax(USAGE_STARTVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
578 else
579 return errorSyntax(USAGE_STARTVM, "error: %Rrs", c);
580 }
581 }
582
583 /* check for required options */
584 if (VMs.empty())
585 return errorSyntax(USAGE_STARTVM, "at least one VM name or uuid required");
586
587 for (std::list<const char *>::const_iterator it = VMs.begin();
588 it != VMs.end();
589 ++it)
590 {
591 HRESULT rc2 = rc;
592 const char *pszVM = *it;
593 ComPtr<IMachine> machine;
594 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(),
595 machine.asOutParam()));
596 if (machine)
597 {
598 ComPtr<IProgress> progress;
599 CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
600 Bstr(strEnv).raw(), progress.asOutParam()));
601 if (SUCCEEDED(rc) && !progress.isNull())
602 {
603 RTPrintf("Waiting for VM \"%s\" to power on...\n", pszVM);
604 CHECK_ERROR(progress, WaitForCompletion(-1));
605 if (SUCCEEDED(rc))
606 {
607 BOOL completed = true;
608 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
609 if (SUCCEEDED(rc))
610 {
611 ASSERT(completed);
612
613 LONG iRc;
614 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
615 if (SUCCEEDED(rc))
616 {
617 if (SUCCEEDED(iRc))
618 RTPrintf("VM \"%s\" has been successfully started.\n", pszVM);
619 else
620 {
621 ProgressErrorInfo info(progress);
622 com::GluePrintErrorInfo(info);
623 }
624 rc = iRc;
625 }
626 }
627 }
628 }
629 }
630
631 /* it's important to always close sessions */
632 a->session->UnlockMachine();
633
634 /* make sure that we remember the failed state */
635 if (FAILED(rc2))
636 rc = rc2;
637 }
638
639 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
640}
641
642RTEXITCODE handleDiscardState(HandlerArg *a)
643{
644 HRESULT rc;
645
646 if (a->argc != 1)
647 return errorSyntax(USAGE_DISCARDSTATE, "Incorrect number of parameters");
648
649 ComPtr<IMachine> machine;
650 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
651 machine.asOutParam()));
652 if (machine)
653 {
654 do
655 {
656 /* we have to open a session for this task */
657 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
658 do
659 {
660 ComPtr<IMachine> sessionMachine;
661 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
662 CHECK_ERROR_BREAK(sessionMachine, DiscardSavedState(true /* fDeleteFile */));
663 } while (0);
664 CHECK_ERROR_BREAK(a->session, UnlockMachine());
665 } while (0);
666 }
667
668 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
669}
670
671RTEXITCODE handleAdoptState(HandlerArg *a)
672{
673 HRESULT rc;
674
675 if (a->argc != 2)
676 return errorSyntax(USAGE_ADOPTSTATE, "Incorrect number of parameters");
677
678 ComPtr<IMachine> machine;
679 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
680 machine.asOutParam()));
681 if (machine)
682 {
683 char szStateFileAbs[RTPATH_MAX] = "";
684 int vrc = RTPathAbs(a->argv[1], szStateFileAbs, sizeof(szStateFileAbs));
685 if (RT_FAILURE(vrc))
686 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
687
688 do
689 {
690 /* we have to open a session for this task */
691 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
692 do
693 {
694 ComPtr<IMachine> sessionMachine;
695 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
696 CHECK_ERROR_BREAK(sessionMachine, AdoptSavedState(Bstr(szStateFileAbs).raw()));
697 } while (0);
698 CHECK_ERROR_BREAK(a->session, UnlockMachine());
699 } while (0);
700 }
701
702 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
703}
704
705RTEXITCODE handleGetExtraData(HandlerArg *a)
706{
707 HRESULT rc = S_OK;
708
709 if (a->argc != 2)
710 return errorSyntax(USAGE_GETEXTRADATA, "Incorrect number of parameters");
711
712 /* global data? */
713 if (!strcmp(a->argv[0], "global"))
714 {
715 /* enumeration? */
716 if (!strcmp(a->argv[1], "enumerate"))
717 {
718 SafeArray<BSTR> aKeys;
719 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
720
721 for (size_t i = 0;
722 i < aKeys.size();
723 ++i)
724 {
725 Bstr bstrKey(aKeys[i]);
726 Bstr bstrValue;
727 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
728 bstrValue.asOutParam()));
729
730 RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
731 }
732 }
733 else
734 {
735 Bstr value;
736 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
737 value.asOutParam()));
738 if (!value.isEmpty())
739 RTPrintf("Value: %ls\n", value.raw());
740 else
741 RTPrintf("No value set!\n");
742 }
743 }
744 else
745 {
746 ComPtr<IMachine> machine;
747 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
748 machine.asOutParam()));
749 if (machine)
750 {
751 /* enumeration? */
752 if (!strcmp(a->argv[1], "enumerate"))
753 {
754 SafeArray<BSTR> aKeys;
755 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
756
757 for (size_t i = 0;
758 i < aKeys.size();
759 ++i)
760 {
761 Bstr bstrKey(aKeys[i]);
762 Bstr bstrValue;
763 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
764 bstrValue.asOutParam()));
765
766 RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
767 }
768 }
769 else
770 {
771 Bstr value;
772 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
773 value.asOutParam()));
774 if (!value.isEmpty())
775 RTPrintf("Value: %ls\n", value.raw());
776 else
777 RTPrintf("No value set!\n");
778 }
779 }
780 }
781 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
782}
783
784RTEXITCODE handleSetExtraData(HandlerArg *a)
785{
786 HRESULT rc = S_OK;
787
788 if (a->argc < 2)
789 return errorSyntax(USAGE_SETEXTRADATA, "Not enough parameters");
790
791 /* global data? */
792 if (!strcmp(a->argv[0], "global"))
793 {
794 /** @todo passing NULL is deprecated */
795 if (a->argc < 3)
796 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
797 NULL));
798 else if (a->argc == 3)
799 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
800 Bstr(a->argv[2]).raw()));
801 else
802 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
803 }
804 else
805 {
806 ComPtr<IMachine> machine;
807 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
808 machine.asOutParam()));
809 if (machine)
810 {
811 /* open an existing session for the VM */
812 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
813 /* get the session machine */
814 ComPtr<IMachine> sessionMachine;
815 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
816 /** @todo passing NULL is deprecated */
817 if (a->argc < 3)
818 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
819 NULL));
820 else if (a->argc == 3)
821 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
822 Bstr(a->argv[2]).raw()));
823 else
824 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
825 }
826 }
827 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
828}
829
830RTEXITCODE handleSetProperty(HandlerArg *a)
831{
832 HRESULT rc;
833
834 /* there must be two arguments: property name and value */
835 if (a->argc != 2)
836 return errorSyntax(USAGE_SETPROPERTY, "Incorrect number of parameters");
837
838 ComPtr<ISystemProperties> systemProperties;
839 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
840
841 if (!strcmp(a->argv[0], "machinefolder"))
842 {
843 /* reset to default? */
844 if (!strcmp(a->argv[1], "default"))
845 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
846 else
847 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
848 }
849 else if (!strcmp(a->argv[0], "hwvirtexclusive"))
850 {
851 bool fHwVirtExclusive;
852
853 if (!strcmp(a->argv[1], "on"))
854 fHwVirtExclusive = true;
855 else if (!strcmp(a->argv[1], "off"))
856 fHwVirtExclusive = false;
857 else
858 return errorArgument("Invalid hwvirtexclusive argument '%s'", a->argv[1]);
859 CHECK_ERROR(systemProperties, COMSETTER(ExclusiveHwVirt)(fHwVirtExclusive));
860 }
861 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
862 || !strcmp(a->argv[0], "vrdpauthlibrary"))
863 {
864 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
865 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n");
866
867 /* reset to default? */
868 if (!strcmp(a->argv[1], "default"))
869 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
870 else
871 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
872 }
873 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
874 {
875 /* reset to default? */
876 if (!strcmp(a->argv[1], "default"))
877 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
878 else
879 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
880 }
881 else if (!strcmp(a->argv[0], "vrdeextpack"))
882 {
883 /* disable? */
884 if (!strcmp(a->argv[1], "null"))
885 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
886 else
887 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
888 }
889 else if (!strcmp(a->argv[0], "loghistorycount"))
890 {
891 uint32_t uVal;
892 int vrc;
893 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
894 if (vrc != VINF_SUCCESS)
895 return errorArgument("Error parsing Log history count '%s'", a->argv[1]);
896 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
897 }
898 else if (!strcmp(a->argv[0], "autostartdbpath"))
899 {
900 /* disable? */
901 if (!strcmp(a->argv[1], "null"))
902 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(NULL));
903 else
904 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(Bstr(a->argv[1]).raw()));
905 }
906 else if (!strcmp(a->argv[0], "defaultfrontend"))
907 {
908 Bstr bstrDefaultFrontend(a->argv[1]);
909 if (!strcmp(a->argv[1], "default"))
910 bstrDefaultFrontend.setNull();
911 CHECK_ERROR(systemProperties, COMSETTER(DefaultFrontend)(bstrDefaultFrontend.raw()));
912 }
913 else if (!strcmp(a->argv[0], "logginglevel"))
914 {
915 Bstr bstrLoggingLevel(a->argv[1]);
916 if (!strcmp(a->argv[1], "default"))
917 bstrLoggingLevel.setNull();
918 CHECK_ERROR(systemProperties, COMSETTER(LoggingLevel)(bstrLoggingLevel.raw()));
919 }
920 else
921 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]);
922
923 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
924}
925
926RTEXITCODE handleSharedFolder(HandlerArg *a)
927{
928 HRESULT rc;
929
930 /* we need at least a command and target */
931 if (a->argc < 2)
932 return errorSyntax(USAGE_SHAREDFOLDER, "Not enough parameters");
933
934 const char *pszMachineName = a->argv[1];
935 ComPtr<IMachine> machine;
936 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), machine.asOutParam()));
937 if (!machine)
938 return RTEXITCODE_FAILURE;
939
940 if (!strcmp(a->argv[0], "add"))
941 {
942 /* we need at least four more parameters */
943 if (a->argc < 5)
944 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Not enough parameters");
945
946 char *name = NULL;
947 char *hostpath = NULL;
948 bool fTransient = false;
949 bool fWritable = true;
950 bool fAutoMount = false;
951
952 for (int i = 2; i < a->argc; i++)
953 {
954 if ( !strcmp(a->argv[i], "--name")
955 || !strcmp(a->argv[i], "-name"))
956 {
957 if (a->argc <= i + 1 || !*a->argv[i+1])
958 return errorArgument("Missing argument to '%s'", a->argv[i]);
959 i++;
960 name = a->argv[i];
961 }
962 else if ( !strcmp(a->argv[i], "--hostpath")
963 || !strcmp(a->argv[i], "-hostpath"))
964 {
965 if (a->argc <= i + 1 || !*a->argv[i+1])
966 return errorArgument("Missing argument to '%s'", a->argv[i]);
967 i++;
968 hostpath = a->argv[i];
969 }
970 else if ( !strcmp(a->argv[i], "--readonly")
971 || !strcmp(a->argv[i], "-readonly"))
972 {
973 fWritable = false;
974 }
975 else if ( !strcmp(a->argv[i], "--transient")
976 || !strcmp(a->argv[i], "-transient"))
977 {
978 fTransient = true;
979 }
980 else if ( !strcmp(a->argv[i], "--automount")
981 || !strcmp(a->argv[i], "-automount"))
982 {
983 fAutoMount = true;
984 }
985 else
986 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
987 }
988
989 if (NULL != strstr(name, " "))
990 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "No spaces allowed in parameter '-name'!");
991
992 /* required arguments */
993 if (!name || !hostpath)
994 {
995 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Parameters --name and --hostpath are required");
996 }
997
998 if (fTransient)
999 {
1000 ComPtr<IConsole> console;
1001
1002 /* open an existing session for the VM */
1003 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1004
1005 /* get the session machine */
1006 ComPtr<IMachine> sessionMachine;
1007 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1008
1009 /* get the session console */
1010 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1011 if (console.isNull())
1012 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1013 "Machine '%s' is not currently running.\n", pszMachineName);
1014
1015 CHECK_ERROR(console, CreateSharedFolder(Bstr(name).raw(),
1016 Bstr(hostpath).raw(),
1017 fWritable, fAutoMount));
1018 a->session->UnlockMachine();
1019 }
1020 else
1021 {
1022 /* open a session for the VM */
1023 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1024
1025 /* get the mutable session machine */
1026 ComPtr<IMachine> sessionMachine;
1027 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1028
1029 CHECK_ERROR(sessionMachine, CreateSharedFolder(Bstr(name).raw(),
1030 Bstr(hostpath).raw(),
1031 fWritable, fAutoMount));
1032 if (SUCCEEDED(rc))
1033 CHECK_ERROR(sessionMachine, SaveSettings());
1034
1035 a->session->UnlockMachine();
1036 }
1037 }
1038 else if (!strcmp(a->argv[0], "remove"))
1039 {
1040 /* we need at least two more parameters */
1041 if (a->argc < 3)
1042 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Not enough parameters");
1043
1044 char *name = NULL;
1045 bool fTransient = false;
1046
1047 for (int i = 2; i < a->argc; i++)
1048 {
1049 if ( !strcmp(a->argv[i], "--name")
1050 || !strcmp(a->argv[i], "-name"))
1051 {
1052 if (a->argc <= i + 1 || !*a->argv[i+1])
1053 return errorArgument("Missing argument to '%s'", a->argv[i]);
1054 i++;
1055 name = a->argv[i];
1056 }
1057 else if ( !strcmp(a->argv[i], "--transient")
1058 || !strcmp(a->argv[i], "-transient"))
1059 {
1060 fTransient = true;
1061 }
1062 else
1063 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1064 }
1065
1066 /* required arguments */
1067 if (!name)
1068 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Parameter --name is required");
1069
1070 if (fTransient)
1071 {
1072 /* open an existing session for the VM */
1073 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1074 /* get the session machine */
1075 ComPtr<IMachine> sessionMachine;
1076 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1077 /* get the session console */
1078 ComPtr<IConsole> console;
1079 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1080 if (console.isNull())
1081 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1082 "Machine '%s' is not currently running.\n", pszMachineName);
1083
1084 CHECK_ERROR(console, RemoveSharedFolder(Bstr(name).raw()));
1085
1086 a->session->UnlockMachine();
1087 }
1088 else
1089 {
1090 /* open a session for the VM */
1091 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1092
1093 /* get the mutable session machine */
1094 ComPtr<IMachine> sessionMachine;
1095 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1096
1097 CHECK_ERROR(sessionMachine, RemoveSharedFolder(Bstr(name).raw()));
1098
1099 /* commit and close the session */
1100 CHECK_ERROR(sessionMachine, SaveSettings());
1101 a->session->UnlockMachine();
1102 }
1103 }
1104 else
1105 return errorSyntax(USAGE_SHAREDFOLDER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
1106
1107 return RTEXITCODE_SUCCESS;
1108}
1109
1110RTEXITCODE handleExtPack(HandlerArg *a)
1111{
1112 if (a->argc < 1)
1113 return errorNoSubcommand();
1114
1115 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1116 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1117
1118 RTGETOPTSTATE GetState;
1119 RTGETOPTUNION ValueUnion;
1120 int ch;
1121 HRESULT hrc = S_OK;
1122
1123 if (!strcmp(a->argv[0], "install"))
1124 {
1125 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
1126 const char *pszName = NULL;
1127 bool fReplace = false;
1128
1129 static const RTGETOPTDEF s_aInstallOptions[] =
1130 {
1131 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1132 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
1133 };
1134
1135 RTCList<RTCString> lstLicenseHashes;
1136 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1137 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1138 {
1139 switch (ch)
1140 {
1141 case 'r':
1142 fReplace = true;
1143 break;
1144
1145 case 'a':
1146 lstLicenseHashes.append(ValueUnion.psz);
1147 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
1148 break;
1149
1150 case VINF_GETOPT_NOT_OPTION:
1151 if (pszName)
1152 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1153 pszName = ValueUnion.psz;
1154 break;
1155
1156 default:
1157 return errorGetOpt(ch, &ValueUnion);
1158 }
1159 }
1160 if (!pszName)
1161 return errorSyntax("No extension pack name was given to \"extpack install\"");
1162
1163 char szPath[RTPATH_MAX];
1164 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
1165 if (RT_FAILURE(vrc))
1166 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszName, vrc);
1167
1168 Bstr bstrTarball(szPath);
1169 Bstr bstrName;
1170 ComPtr<IExtPackFile> ptrExtPackFile;
1171 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1172 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1173 BOOL fShowLicense = true;
1174 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
1175 if (fShowLicense)
1176 {
1177 Bstr bstrLicense;
1178 CHECK_ERROR2I_RET(ptrExtPackFile,
1179 QueryLicense(Bstr("").raw() /* PreferredLocale */,
1180 Bstr("").raw() /* PreferredLanguage */,
1181 Bstr("txt").raw() /* Format */,
1182 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
1183 Utf8Str strLicense(bstrLicense);
1184 uint8_t abHash[RTSHA256_HASH_SIZE];
1185 char szDigest[RTSHA256_DIGEST_LEN + 1];
1186 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
1187 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
1188 AssertRCStmt(vrc, szDigest[0] = '\0');
1189 if (lstLicenseHashes.contains(szDigest))
1190 RTPrintf("License accepted.\n");
1191 else
1192 {
1193 RTPrintf("%s\n", strLicense.c_str());
1194 RTPrintf("Do you agree to these license terms and conditions (y/n)? " );
1195 ch = RTStrmGetCh(g_pStdIn);
1196 RTPrintf("\n");
1197 if (ch != 'y' && ch != 'Y')
1198 {
1199 RTPrintf("Installation of \"%ls\" aborted.\n", bstrName.raw());
1200 return RTEXITCODE_FAILURE;
1201 }
1202 if (szDigest[0])
1203 RTPrintf("License accepted. For batch installaltion add\n"
1204 "--accept-license=%s\n"
1205 "to the VBoxManage command line.\n\n", szDigest);
1206 }
1207 }
1208 ComPtr<IProgress> ptrProgress;
1209 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1210 hrc = showProgress(ptrProgress);
1211 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to install \"%s\"", szPath), RTEXITCODE_FAILURE);
1212
1213 RTPrintf("Successfully installed \"%ls\".\n", bstrName.raw());
1214 }
1215 else if (!strcmp(a->argv[0], "uninstall"))
1216 {
1217 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
1218 const char *pszName = NULL;
1219 bool fForced = false;
1220
1221 static const RTGETOPTDEF s_aUninstallOptions[] =
1222 {
1223 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1224 };
1225
1226 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1227 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1228 {
1229 switch (ch)
1230 {
1231 case 'f':
1232 fForced = true;
1233 break;
1234
1235 case VINF_GETOPT_NOT_OPTION:
1236 if (pszName)
1237 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1238 pszName = ValueUnion.psz;
1239 break;
1240
1241 default:
1242 return errorGetOpt(ch, &ValueUnion);
1243 }
1244 }
1245 if (!pszName)
1246 return errorSyntax("No extension pack name was given to \"extpack uninstall\"");
1247
1248 Bstr bstrName(pszName);
1249 ComPtr<IProgress> ptrProgress;
1250 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1251 hrc = showProgress(ptrProgress);
1252 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to uninstall \"%s\"", pszName), RTEXITCODE_FAILURE);
1253
1254 RTPrintf("Successfully uninstalled \"%s\".\n", pszName);
1255 }
1256 else if (!strcmp(a->argv[0], "cleanup"))
1257 {
1258 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
1259 if (a->argc > 1)
1260 return errorTooManyParameters(&a->argv[1]);
1261 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1262 RTPrintf("Successfully performed extension pack cleanup\n");
1263 }
1264 else
1265 return errorUnknownSubcommand(a->argv[0]);
1266
1267 return RTEXITCODE_SUCCESS;
1268}
1269
1270RTEXITCODE handleUnattendedInstall(HandlerArg *a)
1271{
1272 /*
1273 * Options.
1274 */
1275 Utf8Str strAbsIsoPath;
1276 const char *pszIsoPath = NULL;
1277 const char *pszUser = NULL;
1278 const char *pszPassword = NULL;
1279 const char *pszFullUserName = NULL;
1280 const char *pszProductKey = NULL;
1281 Utf8Str strAbsAdditionsIsoPath;
1282 const char *pszAdditionsIsoPath = NULL;
1283 int fInstallAdditions = -1;
1284 Utf8Str strAbsValidationKitIsoPath;
1285 const char *pszValidationKitIsoPath = NULL;
1286 int fInstallTxs = -1;
1287 const char *pszMachineName = NULL;
1288 bool fSetImageIdx = false;
1289 uint32_t idxImage = 0;
1290 const char *pszLocale = NULL;
1291 const char *pszCountry = NULL;
1292 const char *pszTimeZone = NULL;
1293 const char *pszProxy = NULL;
1294 Utf8Str strAbsAuxiliaryBasePath;
1295 const char *pszAuxiliaryBasePath = NULL;
1296 Utf8Str strAbsScriptTemplatePath;
1297 const char *pszScriptTemplatePath = NULL;
1298 Utf8Str strAbsPostInstallScriptTemplatePath;
1299 const char *pszPostInstallScriptTemplatePath = NULL;
1300 const char *pszPostInstallCommand = NULL;
1301 const char *pszExtraInstallKernelParameters = NULL;
1302 const char *pszSessionType = "headless";
1303
1304 /*
1305 * Parse options.
1306 */
1307 if (a->argc <= 1)
1308 return errorSyntax(USAGE_UNATTENDEDINSTALL, "Missing VM name/UUID.");
1309
1310 static const RTGETOPTDEF s_aOptions[] =
1311 {
1312 { "--iso", 'i', RTGETOPT_REQ_STRING },
1313 { "--user", 'u', RTGETOPT_REQ_STRING },
1314 { "--password", 'p', RTGETOPT_REQ_STRING },
1315 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
1316 { "--key", 'k', RTGETOPT_REQ_STRING },
1317 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
1318 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
1319 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
1320 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
1321 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
1322 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
1323 { "--locale", 'l', RTGETOPT_REQ_STRING },
1324 { "--country", 'L', RTGETOPT_REQ_STRING },
1325 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
1326 { "--proxy", 'y', RTGETOPT_REQ_STRING },
1327 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
1328 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
1329 { "--script-template", 'c', RTGETOPT_REQ_STRING },
1330 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
1331 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
1332 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
1333 { "--session-type", 'S', RTGETOPT_REQ_STRING },
1334 };
1335
1336 RTGETOPTSTATE GetState;
1337 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1338 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1339
1340 int c;
1341 RTGETOPTUNION ValueUnion;
1342 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1343 {
1344 switch (c)
1345 {
1346 case VINF_GETOPT_NOT_OPTION:
1347 if (pszMachineName)
1348 return errorSyntax(USAGE_UNATTENDEDINSTALL, "VM name/UUID given more than once!");
1349 pszMachineName = ValueUnion.psz;
1350 if (*pszMachineName == '\0')
1351 return errorSyntax(USAGE_UNATTENDEDINSTALL, "VM name/UUID is empty!");
1352 break;
1353
1354 case 'i': // --iso
1355 vrc = RTPathAbsCxx(strAbsIsoPath, ValueUnion.psz);
1356 if (RT_FAILURE(vrc))
1357 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1358 pszIsoPath = strAbsIsoPath.c_str();
1359 break;
1360
1361 case 'u': // --user
1362 pszUser = ValueUnion.psz;
1363 break;
1364
1365 case 'p': // --password
1366 pszPassword = ValueUnion.psz;
1367 break;
1368
1369 case 'U': // --full-user-name
1370 pszFullUserName = ValueUnion.psz;
1371 break;
1372
1373 case 'k': // --key
1374 pszProductKey = ValueUnion.psz;
1375 break;
1376
1377 case 'A': // --install-additions
1378 fInstallAdditions = true;
1379 break;
1380 case 'N': // --no-install-additions
1381 fInstallAdditions = false;
1382 break;
1383 case 'a': // --additions-iso
1384 vrc = RTPathAbsCxx(strAbsAdditionsIsoPath, ValueUnion.psz);
1385 if (RT_FAILURE(vrc))
1386 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1387 pszAdditionsIsoPath = strAbsAdditionsIsoPath.c_str();
1388 break;
1389
1390 case 't': // --install-txs
1391 fInstallTxs = true;
1392 break;
1393 case 'T': // --no-install-txs
1394 fInstallTxs = false;
1395 break;
1396 case 'K': // --valiation-kit-iso
1397 vrc = RTPathAbsCxx(strAbsValidationKitIsoPath, ValueUnion.psz);
1398 if (RT_FAILURE(vrc))
1399 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1400 pszValidationKitIsoPath = strAbsValidationKitIsoPath.c_str();
1401 break;
1402
1403 case 'l': // --locale
1404 pszLocale = ValueUnion.psz;
1405 break;
1406
1407 case 'L': // --country
1408 pszCountry = ValueUnion.psz;
1409 break;
1410
1411 case 'z': // --time-zone;
1412 pszTimeZone = ValueUnion.psz;
1413 break;
1414
1415 case 'y': // --proxy
1416 pszProxy = ValueUnion.psz;
1417 break;
1418
1419 case 'x': // --auxiliary-base-path
1420 vrc = RTPathAbsCxx(strAbsAuxiliaryBasePath, ValueUnion.psz);
1421 if (RT_FAILURE(vrc))
1422 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1423 pszAuxiliaryBasePath = strAbsAuxiliaryBasePath.c_str();
1424 break;
1425
1426 case 'm': // --image-index
1427 idxImage = ValueUnion.u32;
1428 fSetImageIdx = true;
1429 break;
1430
1431 case 'c': // --script-template
1432 vrc = RTPathAbsCxx(strAbsScriptTemplatePath, ValueUnion.psz);
1433 if (RT_FAILURE(vrc))
1434 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1435 pszScriptTemplatePath = strAbsScriptTemplatePath.c_str();
1436 break;
1437
1438 case 'C': // --post-install-script-template
1439 vrc = RTPathAbsCxx(strAbsPostInstallScriptTemplatePath, ValueUnion.psz);
1440 if (RT_FAILURE(vrc))
1441 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1442 pszPostInstallScriptTemplatePath = strAbsPostInstallScriptTemplatePath.c_str();
1443 break;
1444
1445 case 'P': // --post-install-command.
1446 pszPostInstallCommand = ValueUnion.psz;
1447 break;
1448
1449 case 'I': // --extra-install-kernel-parameters
1450 pszExtraInstallKernelParameters = ValueUnion.psz;
1451 break;
1452
1453 case 'S': // --session-type
1454 pszSessionType = ValueUnion.psz;
1455 break;
1456
1457 default:
1458 return errorGetOpt(USAGE_UNATTENDEDINSTALL, c, &ValueUnion);
1459 }
1460 }
1461
1462 /*
1463 * Check for required stuff.
1464 */
1465 if (pszMachineName == NULL)
1466 return errorSyntax(USAGE_UNATTENDEDINSTALL, "Missing VM name/UUID");
1467
1468 if (!pszIsoPath)
1469 return errorSyntax(USAGE_UNATTENDEDINSTALL, "Missing required --iso option");
1470
1471 /*
1472 * Prepare.
1473 */
1474
1475 /* try to find the given machine */
1476 HRESULT rc;
1477 ComPtr<IMachine> machine;
1478 Bstr bstrMachineName = pszMachineName;
1479 CHECK_ERROR(a->virtualBox, FindMachine(bstrMachineName.raw(), machine.asOutParam()));
1480 if (FAILED(rc))
1481 return RTEXITCODE_FAILURE;
1482
1483 CHECK_ERROR_RET(machine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
1484 Bstr bstrUuid;
1485 CHECK_ERROR_RET(machine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
1486 /* open a session for the VM */
1487 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1488
1489 /* get the associated console */
1490 ComPtr<IConsole> console;
1491 CHECK_ERROR(a->session, COMGETTER(Console)(console.asOutParam()));
1492 if (console)
1493 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%ls' is currently running", bstrMachineName.raw());
1494
1495 /* ... and session machine */
1496 ComPtr<IMachine> sessionMachine;
1497 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1498
1499 /* Get the OS type name of the VM. */
1500 BSTR bstrInstalledOS;
1501 CHECK_ERROR_RET(sessionMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
1502 Utf8Str strInstalledOS(bstrInstalledOS);
1503
1504 do
1505 {
1506 RTPrintf("Start unattended installation OS %s on virtual machine '%ls'.\n"
1507 "UUID: %s\n",
1508 strInstalledOS.c_str(),
1509 bstrMachineName.raw(),
1510 Utf8Str(bstrUuid).c_str());
1511
1512 {
1513 /*
1514 * Instantiate and configure the unattended installer.
1515 */
1516 ComPtr<IUnattended> ptrUnattended;
1517 CHECK_ERROR_BREAK(machine, CreateUnattendedInstaller(ptrUnattended.asOutParam()));
1518
1519 if (pszIsoPath)
1520 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(IsoPath)(Bstr(pszIsoPath).raw()));
1521 if (pszUser)
1522 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(User)(Bstr(pszUser).raw()));
1523 if (pszPassword)
1524 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(Password)(Bstr(pszPassword).raw()));
1525 if (pszFullUserName)
1526 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(FullUserName)(Bstr(pszFullUserName).raw()));
1527 if (pszProductKey)
1528 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(ProductKey)(Bstr(pszProductKey).raw()));
1529 if (pszAdditionsIsoPath)
1530 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(pszAdditionsIsoPath).raw()));
1531 if (fInstallAdditions >= 0)
1532 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(InstallGuestAdditions)(fInstallAdditions != (int)false));
1533 if (pszValidationKitIsoPath)
1534 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(pszValidationKitIsoPath).raw()));
1535 if (fInstallTxs >= 0)
1536 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(InstallTestExecService)(fInstallTxs != (int)false));
1537 if (pszLocale)
1538 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(Locale)(Bstr(pszLocale).raw()));
1539 if (pszCountry)
1540 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(Country)(Bstr(pszCountry).raw()));
1541 if (pszTimeZone)
1542 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(TimeZone)(Bstr(pszTimeZone).raw()));
1543 if (pszProxy)
1544 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(Proxy)(Bstr(pszProxy).raw()));
1545 if (fSetImageIdx)
1546 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(ImageIndex)(idxImage));
1547 if (pszScriptTemplatePath)
1548 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(pszScriptTemplatePath).raw()));
1549 if (pszPostInstallScriptTemplatePath)
1550 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(pszPostInstallScriptTemplatePath).raw()));
1551 if (pszPostInstallCommand)
1552 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(pszPostInstallCommand).raw()));
1553 if (pszAuxiliaryBasePath)
1554 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(pszAuxiliaryBasePath).raw()));
1555 if (pszExtraInstallKernelParameters)
1556 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(pszExtraInstallKernelParameters).raw()));
1557
1558 CHECK_ERROR_BREAK(ptrUnattended,Prepare());
1559 CHECK_ERROR_BREAK(ptrUnattended,ConstructMedia());
1560 CHECK_ERROR_BREAK(ptrUnattended,ReconfigureVM());
1561
1562 /*
1563 * Retrieve and display the parameters actually used.
1564 */
1565 RTPrintf("Using values:\n");
1566#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
1567 a_Type Value; \
1568 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
1569 if (SUCCEEDED(hrc2)) \
1570 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
1571 else \
1572 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1573 } while (0)
1574#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
1575 Bstr bstrString; \
1576 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
1577 if (SUCCEEDED(hrc2)) \
1578 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
1579 else \
1580 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1581 } while (0)
1582
1583 SHOW_STR_ATTR(IsoPath, "isoPath");
1584 SHOW_STR_ATTR(User, "user");
1585 SHOW_STR_ATTR(Password, "password");
1586 SHOW_STR_ATTR(FullUserName, "fullUserName");
1587 SHOW_STR_ATTR(ProductKey, "productKey");
1588 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
1589 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
1590 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
1591 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
1592 SHOW_STR_ATTR(Locale, "locale");
1593 SHOW_STR_ATTR(Country, "country");
1594 SHOW_STR_ATTR(TimeZone, "timeZone");
1595 SHOW_STR_ATTR(Proxy, "proxy");
1596 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
1597 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
1598 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
1599 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
1600 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
1601 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
1602 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
1603 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
1604 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
1605 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
1606
1607#undef SHOW_STR_ATTR
1608#undef SHOW_ATTR
1609 }
1610
1611 a->session->UnlockMachine();
1612
1613 /*
1614 * Start the VM if requested.
1615 */
1616 if (RTStrICmp(pszSessionType, "none") != 0)
1617 {
1618 Bstr env;
1619#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1620 /* make sure the VM process will start on the same display as VBoxManage */
1621 Utf8Str str;
1622 const char *pszDisplay = RTEnvGet("DISPLAY");
1623 if (pszDisplay)
1624 str = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
1625 const char *pszXAuth = RTEnvGet("XAUTHORITY");
1626 if (pszXAuth)
1627 str.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
1628 env = str;
1629#endif
1630 ComPtr<IProgress> progress;
1631 CHECK_ERROR(machine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), env.raw(), progress.asOutParam()));
1632 if (SUCCEEDED(rc) && !progress.isNull())
1633 {
1634 RTPrintf("Waiting for VM \"%s\" to power on...\n", Utf8Str(bstrUuid).c_str());
1635 CHECK_ERROR(progress, WaitForCompletion(-1));
1636 if (SUCCEEDED(rc))
1637 {
1638 BOOL fCompleted = true;
1639 CHECK_ERROR(progress, COMGETTER(Completed)(&fCompleted));
1640 if (SUCCEEDED(rc))
1641 {
1642 ASSERT(fCompleted);
1643
1644 LONG iRc;
1645 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
1646 if (SUCCEEDED(rc))
1647 {
1648 if (SUCCEEDED(iRc))
1649 RTPrintf("VM \"%s\" has been successfully started.\n", Utf8Str(bstrUuid).c_str());
1650 else
1651 {
1652 ProgressErrorInfo info(progress);
1653 com::GluePrintErrorInfo(info);
1654 }
1655 rc = iRc;
1656 }
1657 }
1658 }
1659 }
1660
1661 /*
1662 * Do we wait for the VM to power down?
1663 */
1664 }
1665
1666 } while (0);
1667
1668 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1669}
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