VirtualBox

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

Last change on this file since 78412 was 78296, checked in by vboxsync, 6 years ago

Main: bugref:8612: Fixed error happened during VM creation with default settings. Also changed storage controller name generation according to bus type

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 87.7 KB
Line 
1/* $Id: VBoxManageMisc.cpp 78296 2019-04-25 15:52:38Z vboxsync $ */
2/** @file
3 * VBoxManage - VirtualBox's command-line interface.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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#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 <iprt/file.h>
39#include <iprt/sha.h>
40#include <iprt/initterm.h>
41#include <iprt/param.h>
42#include <iprt/path.h>
43#include <iprt/cpp/path.h>
44#include <iprt/stream.h>
45#include <iprt/string.h>
46#include <iprt/stdarg.h>
47#include <iprt/thread.h>
48#include <iprt/uuid.h>
49#include <iprt/getopt.h>
50#include <iprt/ctype.h>
51#include <VBox/version.h>
52#include <VBox/log.h>
53
54#include "VBoxManage.h"
55
56#include <list>
57
58using namespace com;
59
60
61
62RTEXITCODE handleRegisterVM(HandlerArg *a)
63{
64 HRESULT rc;
65
66 if (a->argc != 1)
67 return errorSyntax(USAGE_REGISTERVM, "Incorrect number of parameters");
68
69 ComPtr<IMachine> machine;
70 /** @todo Ugly hack to get both the API interpretation of relative paths
71 * and the client's interpretation of relative paths. Remove after the API
72 * has been redesigned. */
73 rc = a->virtualBox->OpenMachine(Bstr(a->argv[0]).raw(),
74 machine.asOutParam());
75 if (rc == VBOX_E_FILE_ERROR)
76 {
77 char szVMFileAbs[RTPATH_MAX] = "";
78 int vrc = RTPathAbs(a->argv[0], szVMFileAbs, sizeof(szVMFileAbs));
79 if (RT_FAILURE(vrc))
80 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
81 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(szVMFileAbs).raw(),
82 machine.asOutParam()));
83 }
84 else if (FAILED(rc))
85 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(a->argv[0]).raw(),
86 machine.asOutParam()));
87 if (SUCCEEDED(rc))
88 {
89 ASSERT(machine);
90 CHECK_ERROR(a->virtualBox, RegisterMachine(machine));
91 }
92 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
93}
94
95static const RTGETOPTDEF g_aUnregisterVMOptions[] =
96{
97 { "--delete", 'd', RTGETOPT_REQ_NOTHING },
98 { "-delete", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
99};
100
101RTEXITCODE handleUnregisterVM(HandlerArg *a)
102{
103 HRESULT rc;
104 const char *VMName = NULL;
105 bool fDelete = false;
106
107 int c;
108 RTGETOPTUNION ValueUnion;
109 RTGETOPTSTATE GetState;
110 // start at 0 because main() has hacked both the argc and argv given to us
111 RTGetOptInit(&GetState, a->argc, a->argv, g_aUnregisterVMOptions, RT_ELEMENTS(g_aUnregisterVMOptions),
112 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
113 while ((c = RTGetOpt(&GetState, &ValueUnion)))
114 {
115 switch (c)
116 {
117 case 'd': // --delete
118 fDelete = true;
119 break;
120
121 case VINF_GETOPT_NOT_OPTION:
122 if (!VMName)
123 VMName = ValueUnion.psz;
124 else
125 return errorSyntax(USAGE_UNREGISTERVM, "Invalid parameter '%s'", ValueUnion.psz);
126 break;
127
128 default:
129 if (c > 0)
130 {
131 if (RT_C_IS_PRINT(c))
132 return errorSyntax(USAGE_UNREGISTERVM, "Invalid option -%c", c);
133 return errorSyntax(USAGE_UNREGISTERVM, "Invalid option case %i", c);
134 }
135 if (c == VERR_GETOPT_UNKNOWN_OPTION)
136 return errorSyntax(USAGE_UNREGISTERVM, "unknown option: %s\n", ValueUnion.psz);
137 if (ValueUnion.pDef)
138 return errorSyntax(USAGE_UNREGISTERVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
139 return errorSyntax(USAGE_UNREGISTERVM, "error: %Rrs", c);
140 }
141 }
142
143 /* check for required options */
144 if (!VMName)
145 return errorSyntax(USAGE_UNREGISTERVM, "VM name required");
146
147 ComPtr<IMachine> machine;
148 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(VMName).raw(),
149 machine.asOutParam()),
150 RTEXITCODE_FAILURE);
151 SafeIfaceArray<IMedium> aMedia;
152 CHECK_ERROR_RET(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly,
153 ComSafeArrayAsOutParam(aMedia)),
154 RTEXITCODE_FAILURE);
155 if (fDelete)
156 {
157 ComPtr<IProgress> pProgress;
158 CHECK_ERROR_RET(machine, DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress.asOutParam()),
159 RTEXITCODE_FAILURE);
160
161 rc = showProgress(pProgress);
162 CHECK_PROGRESS_ERROR_RET(pProgress, ("Machine delete failed"), RTEXITCODE_FAILURE);
163 }
164 else
165 {
166 /* Note that the IMachine::Unregister method will return the medium
167 * reference in a sane order, which means that closing will normally
168 * succeed, unless there is still another machine which uses the
169 * medium. No harm done if we ignore the error. */
170 for (size_t i = 0; i < aMedia.size(); i++)
171 {
172 IMedium *pMedium = aMedia[i];
173 if (pMedium)
174 rc = pMedium->Close();
175 }
176 rc = S_OK;
177 }
178 return RTEXITCODE_SUCCESS;
179}
180
181static const RTGETOPTDEF g_aCreateVMOptions[] =
182{
183 { "--name", 'n', RTGETOPT_REQ_STRING },
184 { "-name", 'n', RTGETOPT_REQ_STRING },
185 { "--groups", 'g', RTGETOPT_REQ_STRING },
186 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
187 { "-basefolder", 'p', RTGETOPT_REQ_STRING },
188 { "--ostype", 'o', RTGETOPT_REQ_STRING },
189 { "-ostype", 'o', RTGETOPT_REQ_STRING },
190 { "--uuid", 'u', RTGETOPT_REQ_UUID },
191 { "-uuid", 'u', RTGETOPT_REQ_UUID },
192 { "--register", 'r', RTGETOPT_REQ_NOTHING },
193 { "-register", 'r', RTGETOPT_REQ_NOTHING },
194 { "--default", 'd', RTGETOPT_REQ_NOTHING },
195 { "-default", 'd', RTGETOPT_REQ_NOTHING },
196};
197
198RTEXITCODE handleCreateVM(HandlerArg *a)
199{
200 HRESULT rc;
201 Bstr bstrBaseFolder;
202 Bstr bstrName;
203 Bstr bstrOsTypeId;
204 Bstr bstrUuid;
205 bool fRegister = false;
206 bool fDefault = false;
207 /* TBD. Now not used */
208 Bstr bstrDefaultFlags;
209 com::SafeArray<BSTR> groups;
210
211 int c;
212 RTGETOPTUNION ValueUnion;
213 RTGETOPTSTATE GetState;
214 // start at 0 because main() has hacked both the argc and argv given to us
215 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateVMOptions, RT_ELEMENTS(g_aCreateVMOptions),
216 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
217 while ((c = RTGetOpt(&GetState, &ValueUnion)))
218 {
219 switch (c)
220 {
221 case 'n': // --name
222 bstrName = ValueUnion.psz;
223 break;
224
225 case 'g': // --groups
226 parseGroups(ValueUnion.psz, &groups);
227 break;
228
229 case 'p': // --basefolder
230 bstrBaseFolder = ValueUnion.psz;
231 break;
232
233 case 'o': // --ostype
234 bstrOsTypeId = ValueUnion.psz;
235 break;
236
237 case 'u': // --uuid
238 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
239 break;
240
241 case 'r': // --register
242 fRegister = true;
243 break;
244
245 case 'd': // --default
246 fDefault = true;
247 break;
248
249 default:
250 return errorGetOpt(USAGE_CREATEVM, c, &ValueUnion);
251 }
252 }
253
254 /* check for required options */
255 if (bstrName.isEmpty())
256 return errorSyntax(USAGE_CREATEVM, "Parameter --name is required");
257
258 do
259 {
260 Bstr createFlags;
261 if (!bstrUuid.isEmpty())
262 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
263 Bstr bstrPrimaryGroup;
264 if (groups.size())
265 bstrPrimaryGroup = groups[0];
266 Bstr bstrSettingsFile;
267 CHECK_ERROR_BREAK(a->virtualBox,
268 ComposeMachineFilename(bstrName.raw(),
269 bstrPrimaryGroup.raw(),
270 createFlags.raw(),
271 bstrBaseFolder.raw(),
272 bstrSettingsFile.asOutParam()));
273 ComPtr<IMachine> machine;
274 CHECK_ERROR_BREAK(a->virtualBox,
275 CreateMachine(bstrSettingsFile.raw(),
276 bstrName.raw(),
277 ComSafeArrayAsInParam(groups),
278 bstrOsTypeId.raw(),
279 createFlags.raw(),
280 machine.asOutParam()));
281
282 CHECK_ERROR_BREAK(machine, SaveSettings());
283 if (fDefault)
284 {
285 /* ApplyDefaults assumes the machine is already registered */
286 CHECK_ERROR_BREAK(machine, ApplyDefaults(bstrDefaultFlags.raw()));
287 CHECK_ERROR_BREAK(machine, SaveSettings());
288 }
289 if (fRegister)
290 {
291 CHECK_ERROR_BREAK(a->virtualBox, RegisterMachine(machine));
292 }
293
294 Bstr uuid;
295 CHECK_ERROR_BREAK(machine, COMGETTER(Id)(uuid.asOutParam()));
296 Bstr settingsFile;
297 CHECK_ERROR_BREAK(machine, COMGETTER(SettingsFilePath)(settingsFile.asOutParam()));
298 RTPrintf("Virtual machine '%ls' is created%s.\n"
299 "UUID: %s\n"
300 "Settings file: '%ls'\n",
301 bstrName.raw(), fRegister ? " and registered" : "",
302 Utf8Str(uuid).c_str(), settingsFile.raw());
303 }
304 while (0);
305
306 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
307}
308
309static const RTGETOPTDEF g_aMoveVMOptions[] =
310{
311 { "--type", 't', RTGETOPT_REQ_STRING },
312 { "--folder", 'f', RTGETOPT_REQ_STRING },
313};
314
315RTEXITCODE handleMoveVM(HandlerArg *a)
316{
317 HRESULT rc;
318 const char *pszSrcName = NULL;
319 const char *pszTargetFolder = NULL;
320 const char *pszType = NULL;
321
322 int c;
323 int vrc = VINF_SUCCESS;
324 RTGETOPTUNION ValueUnion;
325 RTGETOPTSTATE GetState;
326
327 // start at 0 because main() has hacked both the argc and argv given to us
328 RTGetOptInit(&GetState, a->argc, a->argv, g_aMoveVMOptions, RT_ELEMENTS(g_aMoveVMOptions),
329 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
330 while ((c = RTGetOpt(&GetState, &ValueUnion)))
331 {
332 switch (c)
333 {
334 case 't': // --type
335 pszType = ValueUnion.psz;
336 break;
337
338 case 'f': // --target folder
339
340 char szPath[RTPATH_MAX];
341 pszTargetFolder = ValueUnion.psz;
342
343 vrc = RTPathAbs(pszTargetFolder, szPath, sizeof(szPath));
344 if (RT_FAILURE(vrc))
345 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszTargetFolder, vrc);
346 break;
347
348 case VINF_GETOPT_NOT_OPTION:
349 if (!pszSrcName)
350 pszSrcName = ValueUnion.psz;
351 else
352 return errorSyntax(USAGE_MOVEVM, "Invalid parameter '%s'", ValueUnion.psz);
353 break;
354
355 default:
356 return errorGetOpt(USAGE_MOVEVM, c, &ValueUnion);
357 }
358 }
359
360
361 if (!pszType)
362 {
363 pszType = "basic";
364 }
365
366 /* Check for required options */
367 if (!pszSrcName)
368 return errorSyntax(USAGE_MOVEVM, "VM name required");
369
370 /* Get the machine object */
371 ComPtr<IMachine> srcMachine;
372 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
373 srcMachine.asOutParam()),
374 RTEXITCODE_FAILURE);
375
376 if (srcMachine)
377 {
378 /* Start the moving */
379 ComPtr<IProgress> progress;
380
381 /* we have to open a session for this task */
382 CHECK_ERROR_RET(srcMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
383 ComPtr<IMachine> sessionMachine;
384
385 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
386 CHECK_ERROR_RET(sessionMachine, MoveTo(Bstr(pszTargetFolder).raw(),
387 Bstr(pszType).raw(),
388 progress.asOutParam()), RTEXITCODE_FAILURE);
389 rc = showProgress(progress);
390 CHECK_PROGRESS_ERROR_RET(progress, ("Move VM failed"), RTEXITCODE_FAILURE);
391
392 sessionMachine.setNull();
393 CHECK_ERROR_RET(a->session, UnlockMachine(), RTEXITCODE_FAILURE);
394
395// do
396// {
397// /* we have to open a session for this task */
398// CHECK_ERROR_BREAK(srcMachine, LockMachine(a->session, LockType_Write));
399// ComPtr<IMachine> sessionMachine;
400// do
401// {
402// CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
403// CHECK_ERROR_BREAK(sessionMachine, MoveTo(Bstr(pszTargetFolder).raw(),
404// Bstr(pszType).raw(),
405// progress.asOutParam()));
406// rc = showProgress(progress);
407// CHECK_PROGRESS_ERROR_RET(progress, ("Move VM failed"), RTEXITCODE_FAILURE);
408//// CHECK_ERROR_BREAK(sessionMachine, SaveSettings());
409// } while (0);
410//
411// sessionMachine.setNull();
412// CHECK_ERROR_BREAK(a->session, UnlockMachine());
413// } while (0);
414 RTPrintf("Machine has been successfully moved into %s\n", pszTargetFolder);
415 }
416
417 return RTEXITCODE_SUCCESS;
418}
419
420static const RTGETOPTDEF g_aCloneVMOptions[] =
421{
422 { "--snapshot", 's', RTGETOPT_REQ_STRING },
423 { "--name", 'n', RTGETOPT_REQ_STRING },
424 { "--groups", 'g', RTGETOPT_REQ_STRING },
425 { "--mode", 'm', RTGETOPT_REQ_STRING },
426 { "--options", 'o', RTGETOPT_REQ_STRING },
427 { "--register", 'r', RTGETOPT_REQ_NOTHING },
428 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
429 { "--uuid", 'u', RTGETOPT_REQ_UUID },
430};
431
432static int parseCloneMode(const char *psz, CloneMode_T *pMode)
433{
434 if (!RTStrICmp(psz, "machine"))
435 *pMode = CloneMode_MachineState;
436 else if (!RTStrICmp(psz, "machineandchildren"))
437 *pMode = CloneMode_MachineAndChildStates;
438 else if (!RTStrICmp(psz, "all"))
439 *pMode = CloneMode_AllStates;
440 else
441 return VERR_PARSE_ERROR;
442
443 return VINF_SUCCESS;
444}
445
446static int parseCloneOptions(const char *psz, com::SafeArray<CloneOptions_T> *options)
447{
448 int rc = VINF_SUCCESS;
449 while (psz && *psz && RT_SUCCESS(rc))
450 {
451 size_t len;
452 const char *pszComma = strchr(psz, ',');
453 if (pszComma)
454 len = pszComma - psz;
455 else
456 len = strlen(psz);
457 if (len > 0)
458 {
459 if (!RTStrNICmp(psz, "KeepAllMACs", len))
460 options->push_back(CloneOptions_KeepAllMACs);
461 else if (!RTStrNICmp(psz, "KeepNATMACs", len))
462 options->push_back(CloneOptions_KeepNATMACs);
463 else if (!RTStrNICmp(psz, "KeepDiskNames", len))
464 options->push_back(CloneOptions_KeepDiskNames);
465 else if ( !RTStrNICmp(psz, "Link", len)
466 || !RTStrNICmp(psz, "Linked", len))
467 options->push_back(CloneOptions_Link);
468 else if ( !RTStrNICmp(psz, "KeepHwUUIDs", len)
469 || !RTStrNICmp(psz, "KeepHwUUID", len))
470 options->push_back(CloneOptions_KeepHwUUIDs);
471 else
472 rc = VERR_PARSE_ERROR;
473 }
474 if (pszComma)
475 psz += len + 1;
476 else
477 psz += len;
478 }
479
480 return rc;
481}
482
483RTEXITCODE handleCloneVM(HandlerArg *a)
484{
485 HRESULT rc;
486 const char *pszSrcName = NULL;
487 const char *pszSnapshotName = NULL;
488 CloneMode_T mode = CloneMode_MachineState;
489 com::SafeArray<CloneOptions_T> options;
490 const char *pszTrgName = NULL;
491 const char *pszTrgBaseFolder = NULL;
492 bool fRegister = false;
493 Bstr bstrUuid;
494 com::SafeArray<BSTR> groups;
495
496 int c;
497 RTGETOPTUNION ValueUnion;
498 RTGETOPTSTATE GetState;
499 // start at 0 because main() has hacked both the argc and argv given to us
500 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneVMOptions, RT_ELEMENTS(g_aCloneVMOptions),
501 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
502 while ((c = RTGetOpt(&GetState, &ValueUnion)))
503 {
504 switch (c)
505 {
506 case 's': // --snapshot
507 pszSnapshotName = ValueUnion.psz;
508 break;
509
510 case 'n': // --name
511 pszTrgName = ValueUnion.psz;
512 break;
513
514 case 'g': // --groups
515 parseGroups(ValueUnion.psz, &groups);
516 break;
517
518 case 'p': // --basefolder
519 pszTrgBaseFolder = ValueUnion.psz;
520 break;
521
522 case 'm': // --mode
523 if (RT_FAILURE(parseCloneMode(ValueUnion.psz, &mode)))
524 return errorArgument("Invalid clone mode '%s'\n", ValueUnion.psz);
525 break;
526
527 case 'o': // --options
528 if (RT_FAILURE(parseCloneOptions(ValueUnion.psz, &options)))
529 return errorArgument("Invalid clone options '%s'\n", ValueUnion.psz);
530 break;
531
532 case 'u': // --uuid
533 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
534 break;
535
536 case 'r': // --register
537 fRegister = true;
538 break;
539
540 case VINF_GETOPT_NOT_OPTION:
541 if (!pszSrcName)
542 pszSrcName = ValueUnion.psz;
543 else
544 return errorSyntax("Invalid parameter '%s'", ValueUnion.psz);
545 break;
546
547 default:
548 return errorGetOpt(c, &ValueUnion);
549 }
550 }
551
552 /* Check for required options */
553 if (!pszSrcName)
554 return errorSyntax("VM name required");
555
556 /* Get the machine object */
557 ComPtr<IMachine> srcMachine;
558 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
559 srcMachine.asOutParam()),
560 RTEXITCODE_FAILURE);
561
562 /* If a snapshot name/uuid was given, get the particular machine of this
563 * snapshot. */
564 if (pszSnapshotName)
565 {
566 ComPtr<ISnapshot> srcSnapshot;
567 CHECK_ERROR_RET(srcMachine, FindSnapshot(Bstr(pszSnapshotName).raw(),
568 srcSnapshot.asOutParam()),
569 RTEXITCODE_FAILURE);
570 CHECK_ERROR_RET(srcSnapshot, COMGETTER(Machine)(srcMachine.asOutParam()),
571 RTEXITCODE_FAILURE);
572 }
573
574 /* Default name necessary? */
575 if (!pszTrgName)
576 pszTrgName = RTStrAPrintf2("%s Clone", pszSrcName);
577
578 Bstr createFlags;
579 if (!bstrUuid.isEmpty())
580 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
581 Bstr bstrPrimaryGroup;
582 if (groups.size())
583 bstrPrimaryGroup = groups[0];
584 Bstr bstrSettingsFile;
585 CHECK_ERROR_RET(a->virtualBox,
586 ComposeMachineFilename(Bstr(pszTrgName).raw(),
587 bstrPrimaryGroup.raw(),
588 createFlags.raw(),
589 Bstr(pszTrgBaseFolder).raw(),
590 bstrSettingsFile.asOutParam()),
591 RTEXITCODE_FAILURE);
592
593 ComPtr<IMachine> trgMachine;
594 CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(),
595 Bstr(pszTrgName).raw(),
596 ComSafeArrayAsInParam(groups),
597 NULL,
598 createFlags.raw(),
599 trgMachine.asOutParam()),
600 RTEXITCODE_FAILURE);
601
602 /* Start the cloning */
603 ComPtr<IProgress> progress;
604 CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine,
605 mode,
606 ComSafeArrayAsInParam(options),
607 progress.asOutParam()),
608 RTEXITCODE_FAILURE);
609 rc = showProgress(progress);
610 CHECK_PROGRESS_ERROR_RET(progress, ("Clone VM failed"), RTEXITCODE_FAILURE);
611
612 if (fRegister)
613 CHECK_ERROR_RET(a->virtualBox, RegisterMachine(trgMachine), RTEXITCODE_FAILURE);
614
615 Bstr bstrNewName;
616 CHECK_ERROR_RET(trgMachine, COMGETTER(Name)(bstrNewName.asOutParam()), RTEXITCODE_FAILURE);
617 RTPrintf("Machine has been successfully cloned as \"%ls\"\n", bstrNewName.raw());
618
619 return RTEXITCODE_SUCCESS;
620}
621
622RTEXITCODE handleStartVM(HandlerArg *a)
623{
624 HRESULT rc = S_OK;
625 std::list<const char *> VMs;
626 Bstr sessionType;
627 Utf8Str strEnv;
628
629#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
630 /* make sure the VM process will by default start on the same display as VBoxManage */
631 {
632 const char *pszDisplay = RTEnvGet("DISPLAY");
633 if (pszDisplay)
634 strEnv = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
635 const char *pszXAuth = RTEnvGet("XAUTHORITY");
636 if (pszXAuth)
637 strEnv.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
638 }
639#endif
640
641 static const RTGETOPTDEF s_aStartVMOptions[] =
642 {
643 { "--type", 't', RTGETOPT_REQ_STRING },
644 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
645 { "--putenv", 'E', RTGETOPT_REQ_STRING },
646 };
647 int c;
648 RTGETOPTUNION ValueUnion;
649 RTGETOPTSTATE GetState;
650 // start at 0 because main() has hacked both the argc and argv given to us
651 RTGetOptInit(&GetState, a->argc, a->argv, s_aStartVMOptions, RT_ELEMENTS(s_aStartVMOptions),
652 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
653 while ((c = RTGetOpt(&GetState, &ValueUnion)))
654 {
655 switch (c)
656 {
657 case 't': // --type
658 if (!RTStrICmp(ValueUnion.psz, "gui"))
659 {
660 sessionType = "gui";
661 }
662#ifdef VBOX_WITH_VBOXSDL
663 else if (!RTStrICmp(ValueUnion.psz, "sdl"))
664 {
665 sessionType = "sdl";
666 }
667#endif
668#ifdef VBOX_WITH_HEADLESS
669 else if (!RTStrICmp(ValueUnion.psz, "capture"))
670 {
671 sessionType = "capture";
672 }
673 else if (!RTStrICmp(ValueUnion.psz, "headless"))
674 {
675 sessionType = "headless";
676 }
677#endif
678 else
679 sessionType = ValueUnion.psz;
680 break;
681
682 case 'E': // --putenv
683 if (!RTStrStr(ValueUnion.psz, "\n"))
684 strEnv.append(Utf8StrFmt("%s\n", ValueUnion.psz));
685 else
686 return errorSyntax(USAGE_STARTVM, "Parameter to option --putenv must not contain any newline character");
687 break;
688
689 case VINF_GETOPT_NOT_OPTION:
690 VMs.push_back(ValueUnion.psz);
691 break;
692
693 default:
694 if (c > 0)
695 {
696 if (RT_C_IS_PRINT(c))
697 return errorSyntax(USAGE_STARTVM, "Invalid option -%c", c);
698 else
699 return errorSyntax(USAGE_STARTVM, "Invalid option case %i", c);
700 }
701 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
702 return errorSyntax(USAGE_STARTVM, "unknown option: %s\n", ValueUnion.psz);
703 else if (ValueUnion.pDef)
704 return errorSyntax(USAGE_STARTVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
705 else
706 return errorSyntax(USAGE_STARTVM, "error: %Rrs", c);
707 }
708 }
709
710 /* check for required options */
711 if (VMs.empty())
712 return errorSyntax(USAGE_STARTVM, "at least one VM name or uuid required");
713
714 for (std::list<const char *>::const_iterator it = VMs.begin();
715 it != VMs.end();
716 ++it)
717 {
718 HRESULT rc2 = rc;
719 const char *pszVM = *it;
720 ComPtr<IMachine> machine;
721 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(),
722 machine.asOutParam()));
723 if (machine)
724 {
725 ComPtr<IProgress> progress;
726 CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
727 Bstr(strEnv).raw(), progress.asOutParam()));
728 if (SUCCEEDED(rc) && !progress.isNull())
729 {
730 RTPrintf("Waiting for VM \"%s\" to power on...\n", pszVM);
731 CHECK_ERROR(progress, WaitForCompletion(-1));
732 if (SUCCEEDED(rc))
733 {
734 BOOL completed = true;
735 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
736 if (SUCCEEDED(rc))
737 {
738 ASSERT(completed);
739
740 LONG iRc;
741 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
742 if (SUCCEEDED(rc))
743 {
744 if (SUCCEEDED(iRc))
745 RTPrintf("VM \"%s\" has been successfully started.\n", pszVM);
746 else
747 {
748 ProgressErrorInfo info(progress);
749 com::GluePrintErrorInfo(info);
750 }
751 rc = iRc;
752 }
753 }
754 }
755 }
756 }
757
758 /* it's important to always close sessions */
759 a->session->UnlockMachine();
760
761 /* make sure that we remember the failed state */
762 if (FAILED(rc2))
763 rc = rc2;
764 }
765
766 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
767}
768
769RTEXITCODE handleDiscardState(HandlerArg *a)
770{
771 HRESULT rc;
772
773 if (a->argc != 1)
774 return errorSyntax(USAGE_DISCARDSTATE, "Incorrect number of parameters");
775
776 ComPtr<IMachine> machine;
777 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
778 machine.asOutParam()));
779 if (machine)
780 {
781 do
782 {
783 /* we have to open a session for this task */
784 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
785 do
786 {
787 ComPtr<IMachine> sessionMachine;
788 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
789 CHECK_ERROR_BREAK(sessionMachine, DiscardSavedState(true /* fDeleteFile */));
790 } while (0);
791 CHECK_ERROR_BREAK(a->session, UnlockMachine());
792 } while (0);
793 }
794
795 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
796}
797
798RTEXITCODE handleAdoptState(HandlerArg *a)
799{
800 HRESULT rc;
801
802 if (a->argc != 2)
803 return errorSyntax(USAGE_ADOPTSTATE, "Incorrect number of parameters");
804
805 ComPtr<IMachine> machine;
806 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
807 machine.asOutParam()));
808 if (machine)
809 {
810 char szStateFileAbs[RTPATH_MAX] = "";
811 int vrc = RTPathAbs(a->argv[1], szStateFileAbs, sizeof(szStateFileAbs));
812 if (RT_FAILURE(vrc))
813 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
814
815 do
816 {
817 /* we have to open a session for this task */
818 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
819 do
820 {
821 ComPtr<IMachine> sessionMachine;
822 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
823 CHECK_ERROR_BREAK(sessionMachine, AdoptSavedState(Bstr(szStateFileAbs).raw()));
824 } while (0);
825 CHECK_ERROR_BREAK(a->session, UnlockMachine());
826 } while (0);
827 }
828
829 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
830}
831
832RTEXITCODE handleGetExtraData(HandlerArg *a)
833{
834 HRESULT rc = S_OK;
835
836 if (a->argc > 2 || a->argc < 1)
837 return errorSyntax(USAGE_GETEXTRADATA, "Incorrect number of parameters");
838
839 /* global data? */
840 if (!strcmp(a->argv[0], "global"))
841 {
842 /* enumeration? */
843 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
844 {
845 SafeArray<BSTR> aKeys;
846 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
847
848 for (size_t i = 0;
849 i < aKeys.size();
850 ++i)
851 {
852 Bstr bstrKey(aKeys[i]);
853 Bstr bstrValue;
854 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
855 bstrValue.asOutParam()));
856
857 RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
858 }
859 }
860 else
861 {
862 Bstr value;
863 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
864 value.asOutParam()));
865 if (!value.isEmpty())
866 RTPrintf("Value: %ls\n", value.raw());
867 else
868 RTPrintf("No value set!\n");
869 }
870 }
871 else
872 {
873 ComPtr<IMachine> machine;
874 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
875 machine.asOutParam()));
876 if (machine)
877 {
878 /* enumeration? */
879 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
880 {
881 SafeArray<BSTR> aKeys;
882 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
883
884 for (size_t i = 0;
885 i < aKeys.size();
886 ++i)
887 {
888 Bstr bstrKey(aKeys[i]);
889 Bstr bstrValue;
890 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
891 bstrValue.asOutParam()));
892
893 RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
894 }
895 }
896 else
897 {
898 Bstr value;
899 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
900 value.asOutParam()));
901 if (!value.isEmpty())
902 RTPrintf("Value: %ls\n", value.raw());
903 else
904 RTPrintf("No value set!\n");
905 }
906 }
907 }
908 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
909}
910
911RTEXITCODE handleSetExtraData(HandlerArg *a)
912{
913 HRESULT rc = S_OK;
914
915 if (a->argc < 2)
916 return errorSyntax(USAGE_SETEXTRADATA, "Not enough parameters");
917
918 /* global data? */
919 if (!strcmp(a->argv[0], "global"))
920 {
921 /** @todo passing NULL is deprecated */
922 if (a->argc < 3)
923 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
924 NULL));
925 else if (a->argc == 3)
926 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
927 Bstr(a->argv[2]).raw()));
928 else
929 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
930 }
931 else
932 {
933 ComPtr<IMachine> machine;
934 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
935 machine.asOutParam()));
936 if (machine)
937 {
938 /* open an existing session for the VM */
939 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
940 /* get the session machine */
941 ComPtr<IMachine> sessionMachine;
942 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
943 /** @todo passing NULL is deprecated */
944 if (a->argc < 3)
945 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
946 NULL));
947 else if (a->argc == 3)
948 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
949 Bstr(a->argv[2]).raw()));
950 else
951 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
952 }
953 }
954 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
955}
956
957RTEXITCODE handleSetProperty(HandlerArg *a)
958{
959 HRESULT rc;
960
961 /* there must be two arguments: property name and value */
962 if (a->argc != 2)
963 return errorSyntax(USAGE_SETPROPERTY, "Incorrect number of parameters");
964
965 ComPtr<ISystemProperties> systemProperties;
966 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
967
968 if (!strcmp(a->argv[0], "machinefolder"))
969 {
970 /* reset to default? */
971 if (!strcmp(a->argv[1], "default"))
972 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
973 else
974 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
975 }
976 else if (!strcmp(a->argv[0], "hwvirtexclusive"))
977 {
978 bool fHwVirtExclusive;
979
980 if (!strcmp(a->argv[1], "on"))
981 fHwVirtExclusive = true;
982 else if (!strcmp(a->argv[1], "off"))
983 fHwVirtExclusive = false;
984 else
985 return errorArgument("Invalid hwvirtexclusive argument '%s'", a->argv[1]);
986 CHECK_ERROR(systemProperties, COMSETTER(ExclusiveHwVirt)(fHwVirtExclusive));
987 }
988 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
989 || !strcmp(a->argv[0], "vrdpauthlibrary"))
990 {
991 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
992 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n");
993
994 /* reset to default? */
995 if (!strcmp(a->argv[1], "default"))
996 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
997 else
998 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
999 }
1000 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
1001 {
1002 /* reset to default? */
1003 if (!strcmp(a->argv[1], "default"))
1004 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
1005 else
1006 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
1007 }
1008 else if (!strcmp(a->argv[0], "vrdeextpack"))
1009 {
1010 /* disable? */
1011 if (!strcmp(a->argv[1], "null"))
1012 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
1013 else
1014 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
1015 }
1016 else if (!strcmp(a->argv[0], "loghistorycount"))
1017 {
1018 uint32_t uVal;
1019 int vrc;
1020 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
1021 if (vrc != VINF_SUCCESS)
1022 return errorArgument("Error parsing Log history count '%s'", a->argv[1]);
1023 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
1024 }
1025 else if (!strcmp(a->argv[0], "autostartdbpath"))
1026 {
1027 /* disable? */
1028 if (!strcmp(a->argv[1], "null"))
1029 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(NULL));
1030 else
1031 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(Bstr(a->argv[1]).raw()));
1032 }
1033 else if (!strcmp(a->argv[0], "defaultfrontend"))
1034 {
1035 Bstr bstrDefaultFrontend(a->argv[1]);
1036 if (!strcmp(a->argv[1], "default"))
1037 bstrDefaultFrontend.setNull();
1038 CHECK_ERROR(systemProperties, COMSETTER(DefaultFrontend)(bstrDefaultFrontend.raw()));
1039 }
1040 else if (!strcmp(a->argv[0], "logginglevel"))
1041 {
1042 Bstr bstrLoggingLevel(a->argv[1]);
1043 if (!strcmp(a->argv[1], "default"))
1044 bstrLoggingLevel.setNull();
1045 CHECK_ERROR(systemProperties, COMSETTER(LoggingLevel)(bstrLoggingLevel.raw()));
1046 }
1047 else if (!strcmp(a->argv[0], "proxymode"))
1048 {
1049 ProxyMode_T enmProxyMode;
1050 if (!RTStrICmpAscii(a->argv[1], "system"))
1051 enmProxyMode = ProxyMode_System;
1052 else if (!RTStrICmpAscii(a->argv[1], "noproxy"))
1053 enmProxyMode = ProxyMode_NoProxy;
1054 else if (!RTStrICmpAscii(a->argv[1], "manual"))
1055 enmProxyMode = ProxyMode_Manual;
1056 else
1057 return errorArgument("Unknown proxy mode: '%s'", a->argv[1]);
1058 CHECK_ERROR(systemProperties, COMSETTER(ProxyMode)(enmProxyMode));
1059 }
1060 else if (!strcmp(a->argv[0], "proxyurl"))
1061 {
1062 Bstr bstrProxyUrl(a->argv[1]);
1063 CHECK_ERROR(systemProperties, COMSETTER(ProxyURL)(bstrProxyUrl.raw()));
1064 }
1065 else
1066 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]);
1067
1068 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1069}
1070
1071RTEXITCODE handleSharedFolder(HandlerArg *a)
1072{
1073 HRESULT rc;
1074
1075 /* we need at least a command and target */
1076 if (a->argc < 2)
1077 return errorSyntax(USAGE_SHAREDFOLDER, "Not enough parameters");
1078
1079 const char *pszMachineName = a->argv[1];
1080 ComPtr<IMachine> machine;
1081 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), machine.asOutParam()));
1082 if (!machine)
1083 return RTEXITCODE_FAILURE;
1084
1085 if (!strcmp(a->argv[0], "add"))
1086 {
1087 /* we need at least four more parameters */
1088 if (a->argc < 5)
1089 return errorSyntaxEx(USAGE_SHAREDFOLDER, HELP_SCOPE_SHAREDFOLDER_ADD, "Not enough parameters");
1090
1091 char *name = NULL;
1092 char *hostpath = NULL;
1093 bool fTransient = false;
1094 bool fWritable = true;
1095 bool fAutoMount = false;
1096 const char *pszAutoMountPoint = "";
1097
1098 for (int i = 2; i < a->argc; i++)
1099 {
1100 if ( !strcmp(a->argv[i], "--name")
1101 || !strcmp(a->argv[i], "-name"))
1102 {
1103 if (a->argc <= i + 1 || !*a->argv[i+1])
1104 return errorArgument("Missing argument to '%s'", a->argv[i]);
1105 i++;
1106 name = a->argv[i];
1107 }
1108 else if ( !strcmp(a->argv[i], "--hostpath")
1109 || !strcmp(a->argv[i], "-hostpath"))
1110 {
1111 if (a->argc <= i + 1 || !*a->argv[i+1])
1112 return errorArgument("Missing argument to '%s'", a->argv[i]);
1113 i++;
1114 hostpath = a->argv[i];
1115 }
1116 else if ( !strcmp(a->argv[i], "--readonly")
1117 || !strcmp(a->argv[i], "-readonly"))
1118 {
1119 fWritable = false;
1120 }
1121 else if ( !strcmp(a->argv[i], "--transient")
1122 || !strcmp(a->argv[i], "-transient"))
1123 {
1124 fTransient = true;
1125 }
1126 else if ( !strcmp(a->argv[i], "--automount")
1127 || !strcmp(a->argv[i], "-automount"))
1128 {
1129 fAutoMount = true;
1130 }
1131 else if (!strcmp(a->argv[i], "--auto-mount-point"))
1132 {
1133 if (a->argc <= i + 1 || !*a->argv[i+1])
1134 return errorArgument("Missing argument to '%s'", a->argv[i]);
1135 i++;
1136 pszAutoMountPoint = a->argv[i];
1137 }
1138 else
1139 return errorSyntaxEx(USAGE_SHAREDFOLDER, HELP_SCOPE_SHAREDFOLDER_ADD, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1140 }
1141
1142 if (NULL != strstr(name, " "))
1143 return errorSyntaxEx(USAGE_SHAREDFOLDER, HELP_SCOPE_SHAREDFOLDER_ADD, "No spaces allowed in parameter '-name'!");
1144
1145 /* required arguments */
1146 if (!name || !hostpath)
1147 {
1148 return errorSyntaxEx(USAGE_SHAREDFOLDER, HELP_SCOPE_SHAREDFOLDER_ADD, "Parameters --name and --hostpath are required");
1149 }
1150
1151 if (fTransient)
1152 {
1153 ComPtr<IConsole> console;
1154
1155 /* open an existing session for the VM */
1156 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1157
1158 /* get the session machine */
1159 ComPtr<IMachine> sessionMachine;
1160 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1161
1162 /* get the session console */
1163 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1164 if (console.isNull())
1165 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1166 "Machine '%s' is not currently running.\n", pszMachineName);
1167
1168 CHECK_ERROR(console, CreateSharedFolder(Bstr(name).raw(), Bstr(hostpath).raw(),
1169 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1170 a->session->UnlockMachine();
1171 }
1172 else
1173 {
1174 /* open a session for the VM */
1175 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1176
1177 /* get the mutable session machine */
1178 ComPtr<IMachine> sessionMachine;
1179 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1180
1181 CHECK_ERROR(sessionMachine, CreateSharedFolder(Bstr(name).raw(), Bstr(hostpath).raw(),
1182 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1183 if (SUCCEEDED(rc))
1184 CHECK_ERROR(sessionMachine, SaveSettings());
1185
1186 a->session->UnlockMachine();
1187 }
1188 }
1189 else if (!strcmp(a->argv[0], "remove"))
1190 {
1191 /* we need at least two more parameters */
1192 if (a->argc < 3)
1193 return errorSyntaxEx(USAGE_SHAREDFOLDER, HELP_SCOPE_SHAREDFOLDER_REMOVE, "Not enough parameters");
1194
1195 char *name = NULL;
1196 bool fTransient = false;
1197
1198 for (int i = 2; i < a->argc; i++)
1199 {
1200 if ( !strcmp(a->argv[i], "--name")
1201 || !strcmp(a->argv[i], "-name"))
1202 {
1203 if (a->argc <= i + 1 || !*a->argv[i+1])
1204 return errorArgument("Missing argument to '%s'", a->argv[i]);
1205 i++;
1206 name = a->argv[i];
1207 }
1208 else if ( !strcmp(a->argv[i], "--transient")
1209 || !strcmp(a->argv[i], "-transient"))
1210 {
1211 fTransient = true;
1212 }
1213 else
1214 return errorSyntaxEx(USAGE_SHAREDFOLDER, HELP_SCOPE_SHAREDFOLDER_REMOVE, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1215 }
1216
1217 /* required arguments */
1218 if (!name)
1219 return errorSyntaxEx(USAGE_SHAREDFOLDER, HELP_SCOPE_SHAREDFOLDER_REMOVE, "Parameter --name is required");
1220
1221 if (fTransient)
1222 {
1223 /* open an existing session for the VM */
1224 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1225 /* get the session machine */
1226 ComPtr<IMachine> sessionMachine;
1227 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1228 /* get the session console */
1229 ComPtr<IConsole> console;
1230 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1231 if (console.isNull())
1232 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1233 "Machine '%s' is not currently running.\n", pszMachineName);
1234
1235 CHECK_ERROR(console, RemoveSharedFolder(Bstr(name).raw()));
1236
1237 a->session->UnlockMachine();
1238 }
1239 else
1240 {
1241 /* open a session for the VM */
1242 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1243
1244 /* get the mutable session machine */
1245 ComPtr<IMachine> sessionMachine;
1246 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1247
1248 CHECK_ERROR(sessionMachine, RemoveSharedFolder(Bstr(name).raw()));
1249
1250 /* commit and close the session */
1251 CHECK_ERROR(sessionMachine, SaveSettings());
1252 a->session->UnlockMachine();
1253 }
1254 }
1255 else
1256 return errorSyntax(USAGE_SHAREDFOLDER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
1257
1258 return RTEXITCODE_SUCCESS;
1259}
1260
1261RTEXITCODE handleExtPack(HandlerArg *a)
1262{
1263 if (a->argc < 1)
1264 return errorNoSubcommand();
1265
1266 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1267 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1268
1269 RTGETOPTSTATE GetState;
1270 RTGETOPTUNION ValueUnion;
1271 int ch;
1272 HRESULT hrc = S_OK;
1273
1274 if (!strcmp(a->argv[0], "install"))
1275 {
1276 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
1277 const char *pszName = NULL;
1278 bool fReplace = false;
1279
1280 static const RTGETOPTDEF s_aInstallOptions[] =
1281 {
1282 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1283 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
1284 };
1285
1286 RTCList<RTCString> lstLicenseHashes;
1287 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1288 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1289 {
1290 switch (ch)
1291 {
1292 case 'r':
1293 fReplace = true;
1294 break;
1295
1296 case 'a':
1297 lstLicenseHashes.append(ValueUnion.psz);
1298 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
1299 break;
1300
1301 case VINF_GETOPT_NOT_OPTION:
1302 if (pszName)
1303 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1304 pszName = ValueUnion.psz;
1305 break;
1306
1307 default:
1308 return errorGetOpt(ch, &ValueUnion);
1309 }
1310 }
1311 if (!pszName)
1312 return errorSyntax("No extension pack name was given to \"extpack install\"");
1313
1314 char szPath[RTPATH_MAX];
1315 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
1316 if (RT_FAILURE(vrc))
1317 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszName, vrc);
1318
1319 Bstr bstrTarball(szPath);
1320 Bstr bstrName;
1321 ComPtr<IExtPackFile> ptrExtPackFile;
1322 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1323 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1324 BOOL fShowLicense = true;
1325 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
1326 if (fShowLicense)
1327 {
1328 Bstr bstrLicense;
1329 CHECK_ERROR2I_RET(ptrExtPackFile,
1330 QueryLicense(Bstr("").raw() /* PreferredLocale */,
1331 Bstr("").raw() /* PreferredLanguage */,
1332 Bstr("txt").raw() /* Format */,
1333 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
1334 Utf8Str strLicense(bstrLicense);
1335 uint8_t abHash[RTSHA256_HASH_SIZE];
1336 char szDigest[RTSHA256_DIGEST_LEN + 1];
1337 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
1338 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
1339 AssertRCStmt(vrc, szDigest[0] = '\0');
1340 if (lstLicenseHashes.contains(szDigest))
1341 RTPrintf("License accepted.\n");
1342 else
1343 {
1344 RTPrintf("%s\n", strLicense.c_str());
1345 RTPrintf("Do you agree to these license terms and conditions (y/n)? " );
1346 ch = RTStrmGetCh(g_pStdIn);
1347 RTPrintf("\n");
1348 if (ch != 'y' && ch != 'Y')
1349 {
1350 RTPrintf("Installation of \"%ls\" aborted.\n", bstrName.raw());
1351 return RTEXITCODE_FAILURE;
1352 }
1353 if (szDigest[0])
1354 RTPrintf("License accepted. For batch installation add\n"
1355 "--accept-license=%s\n"
1356 "to the VBoxManage command line.\n\n", szDigest);
1357 }
1358 }
1359 ComPtr<IProgress> ptrProgress;
1360 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1361 hrc = showProgress(ptrProgress);
1362 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to install \"%s\"", szPath), RTEXITCODE_FAILURE);
1363
1364 RTPrintf("Successfully installed \"%ls\".\n", bstrName.raw());
1365 }
1366 else if (!strcmp(a->argv[0], "uninstall"))
1367 {
1368 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
1369 const char *pszName = NULL;
1370 bool fForced = false;
1371
1372 static const RTGETOPTDEF s_aUninstallOptions[] =
1373 {
1374 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1375 };
1376
1377 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1378 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1379 {
1380 switch (ch)
1381 {
1382 case 'f':
1383 fForced = true;
1384 break;
1385
1386 case VINF_GETOPT_NOT_OPTION:
1387 if (pszName)
1388 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1389 pszName = ValueUnion.psz;
1390 break;
1391
1392 default:
1393 return errorGetOpt(ch, &ValueUnion);
1394 }
1395 }
1396 if (!pszName)
1397 return errorSyntax("No extension pack name was given to \"extpack uninstall\"");
1398
1399 Bstr bstrName(pszName);
1400 ComPtr<IProgress> ptrProgress;
1401 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1402 hrc = showProgress(ptrProgress);
1403 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to uninstall \"%s\"", pszName), RTEXITCODE_FAILURE);
1404
1405 RTPrintf("Successfully uninstalled \"%s\".\n", pszName);
1406 }
1407 else if (!strcmp(a->argv[0], "cleanup"))
1408 {
1409 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
1410 if (a->argc > 1)
1411 return errorTooManyParameters(&a->argv[1]);
1412 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1413 RTPrintf("Successfully performed extension pack cleanup\n");
1414 }
1415 else
1416 return errorUnknownSubcommand(a->argv[0]);
1417
1418 return RTEXITCODE_SUCCESS;
1419}
1420
1421RTEXITCODE handleUnattendedDetect(HandlerArg *a)
1422{
1423 HRESULT hrc;
1424
1425 /*
1426 * Options. We work directly on an IUnattended instace while parsing
1427 * the options. This saves a lot of extra clutter.
1428 */
1429 bool fMachineReadable = false;
1430 char szIsoPath[RTPATH_MAX];
1431 szIsoPath[0] = '\0';
1432
1433 /*
1434 * Parse options.
1435 */
1436 static const RTGETOPTDEF s_aOptions[] =
1437 {
1438 { "--iso", 'i', RTGETOPT_REQ_STRING },
1439 { "--machine-readable", 'M', RTGETOPT_REQ_NOTHING },
1440 };
1441
1442 RTGETOPTSTATE GetState;
1443 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1444 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1445
1446 int c;
1447 RTGETOPTUNION ValueUnion;
1448 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1449 {
1450 switch (c)
1451 {
1452 case 'i': // --iso
1453 vrc = RTPathAbs(ValueUnion.psz, szIsoPath, sizeof(szIsoPath));
1454 if (RT_FAILURE(vrc))
1455 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1456 break;
1457
1458 case 'M': // --machine-readable.
1459 fMachineReadable = true;
1460 break;
1461
1462 default:
1463 return errorGetOpt(c, &ValueUnion);
1464 }
1465 }
1466
1467 /*
1468 * Check for required stuff.
1469 */
1470 if (szIsoPath[0] == '\0')
1471 return errorSyntax("No ISO specified");
1472
1473 /*
1474 * Do the job.
1475 */
1476 ComPtr<IUnattended> ptrUnattended;
1477 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1478 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szIsoPath).raw()), RTEXITCODE_FAILURE);
1479 CHECK_ERROR2(hrc, ptrUnattended, DetectIsoOS());
1480 RTEXITCODE rcExit = SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1481
1482 /*
1483 * Retrieve the results.
1484 */
1485 Bstr bstrDetectedOSTypeId;
1486 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSTypeId)(bstrDetectedOSTypeId.asOutParam()), RTEXITCODE_FAILURE);
1487 Bstr bstrDetectedVersion;
1488 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSVersion)(bstrDetectedVersion.asOutParam()), RTEXITCODE_FAILURE);
1489 Bstr bstrDetectedFlavor;
1490 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSFlavor)(bstrDetectedFlavor.asOutParam()), RTEXITCODE_FAILURE);
1491 Bstr bstrDetectedLanguages;
1492 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSLanguages)(bstrDetectedLanguages.asOutParam()), RTEXITCODE_FAILURE);
1493 Bstr bstrDetectedHints;
1494 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSHints)(bstrDetectedHints.asOutParam()), RTEXITCODE_FAILURE);
1495 if (fMachineReadable)
1496 RTPrintf("OSTypeId=\"%ls\"\n"
1497 "OSVersion=\"%ls\"\n"
1498 "OSFlavor=\"%ls\"\n"
1499 "OSLanguages=\"%ls\"\n"
1500 "OSHints=\"%ls\"\n",
1501 bstrDetectedOSTypeId.raw(),
1502 bstrDetectedVersion.raw(),
1503 bstrDetectedFlavor.raw(),
1504 bstrDetectedLanguages.raw(),
1505 bstrDetectedHints.raw());
1506 else
1507 {
1508 RTMsgInfo("Detected '%s' to be:\n", szIsoPath);
1509 RTPrintf(" OS TypeId = %ls\n"
1510 " OS Version = %ls\n"
1511 " OS Flavor = %ls\n"
1512 " OS Languages = %ls\n"
1513 " OS Hints = %ls\n",
1514 bstrDetectedOSTypeId.raw(),
1515 bstrDetectedVersion.raw(),
1516 bstrDetectedFlavor.raw(),
1517 bstrDetectedLanguages.raw(),
1518 bstrDetectedHints.raw());
1519 }
1520
1521 return rcExit;
1522}
1523
1524RTEXITCODE handleUnattendedInstall(HandlerArg *a)
1525{
1526 HRESULT hrc;
1527 char szAbsPath[RTPATH_MAX];
1528
1529 /*
1530 * Options. We work directly on an IUnattended instace while parsing
1531 * the options. This saves a lot of extra clutter.
1532 */
1533 ComPtr<IUnattended> ptrUnattended;
1534 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1535 RTCList<RTCString> arrPackageSelectionAdjustments;
1536 ComPtr<IMachine> ptrMachine;
1537 bool fDryRun = false;
1538 const char *pszSessionType = "none";
1539
1540 /*
1541 * Parse options.
1542 */
1543 static const RTGETOPTDEF s_aOptions[] =
1544 {
1545 { "--iso", 'i', RTGETOPT_REQ_STRING },
1546 { "--user", 'u', RTGETOPT_REQ_STRING },
1547 { "--password", 'p', RTGETOPT_REQ_STRING },
1548 { "--password-file", 'X', RTGETOPT_REQ_STRING },
1549 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
1550 { "--key", 'k', RTGETOPT_REQ_STRING },
1551 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
1552 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
1553 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
1554 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
1555 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
1556 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
1557 { "--locale", 'l', RTGETOPT_REQ_STRING },
1558 { "--country", 'Y', RTGETOPT_REQ_STRING },
1559 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
1560 { "--proxy", 'y', RTGETOPT_REQ_STRING },
1561 { "--hostname", 'H', RTGETOPT_REQ_STRING },
1562 { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
1563 { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
1564 // advance options:
1565 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
1566 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
1567 { "--script-template", 'c', RTGETOPT_REQ_STRING },
1568 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
1569 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
1570 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
1571 { "--language", 'L', RTGETOPT_REQ_STRING },
1572 // start vm related options:
1573 { "--start-vm", 'S', RTGETOPT_REQ_STRING },
1574 /** @todo Add a --wait option too for waiting for the VM to shut down or
1575 * something like that...? */
1576 };
1577
1578 RTGETOPTSTATE GetState;
1579 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1580 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1581
1582 int c;
1583 RTGETOPTUNION ValueUnion;
1584 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1585 {
1586 switch (c)
1587 {
1588 case VINF_GETOPT_NOT_OPTION:
1589 if (ptrMachine.isNotNull())
1590 return errorSyntax("VM name/UUID given more than once!");
1591 CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1592 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
1593 break;
1594
1595 case 'i': // --iso
1596 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1597 if (RT_FAILURE(vrc))
1598 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1599 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1600 break;
1601
1602 case 'u': // --user
1603 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1604 break;
1605
1606 case 'p': // --password
1607 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1608 break;
1609
1610 case 'X': // --password-file
1611 {
1612 Utf8Str strPassword;
1613 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
1614 if (rcExit != RTEXITCODE_SUCCESS)
1615 return rcExit;
1616 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
1617 break;
1618 }
1619
1620 case 'U': // --full-user-name
1621 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1622 break;
1623
1624 case 'k': // --key
1625 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1626 break;
1627
1628 case 'A': // --install-additions
1629 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
1630 break;
1631 case 'N': // --no-install-additions
1632 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
1633 break;
1634 case 'a': // --additions-iso
1635 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1636 if (RT_FAILURE(vrc))
1637 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1638 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1639 break;
1640
1641 case 't': // --install-txs
1642 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
1643 break;
1644 case 'T': // --no-install-txs
1645 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
1646 break;
1647 case 'K': // --valiation-kit-iso
1648 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1649 if (RT_FAILURE(vrc))
1650 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1651 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1652 break;
1653
1654 case 'l': // --locale
1655 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1656 break;
1657
1658 case 'Y': // --country
1659 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1660 break;
1661
1662 case 'z': // --time-zone;
1663 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1664 break;
1665
1666 case 'y': // --proxy
1667 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1668 break;
1669
1670 case 'H': // --hostname
1671 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1672 break;
1673
1674 case 's': // --package-selection-adjustment
1675 arrPackageSelectionAdjustments.append(ValueUnion.psz);
1676 break;
1677
1678 case 'D':
1679 fDryRun = true;
1680 break;
1681
1682 case 'x': // --auxiliary-base-path
1683 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1684 if (RT_FAILURE(vrc))
1685 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1686 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1687 break;
1688
1689 case 'm': // --image-index
1690 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
1691 break;
1692
1693 case 'c': // --script-template
1694 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1695 if (RT_FAILURE(vrc))
1696 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1697 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1698 break;
1699
1700 case 'C': // --post-install-script-template
1701 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1702 if (RT_FAILURE(vrc))
1703 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1704 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1705 break;
1706
1707 case 'P': // --post-install-command.
1708 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1709 break;
1710
1711 case 'I': // --extra-install-kernel-parameters
1712 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1713 break;
1714
1715 case 'L': // --language
1716 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1717 break;
1718
1719 case 'S': // --start-vm
1720 pszSessionType = ValueUnion.psz;
1721 break;
1722
1723 default:
1724 return errorGetOpt(c, &ValueUnion);
1725 }
1726 }
1727
1728 /*
1729 * Check for required stuff.
1730 */
1731 if (ptrMachine.isNull())
1732 return errorSyntax("Missing VM name/UUID");
1733
1734 /*
1735 * Set accumulative attributes.
1736 */
1737 if (arrPackageSelectionAdjustments.size() == 1)
1738 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
1739 RTEXITCODE_FAILURE);
1740 else if (arrPackageSelectionAdjustments.size() > 1)
1741 {
1742 RTCString strAdjustments;
1743 strAdjustments.join(arrPackageSelectionAdjustments, ";");
1744 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
1745 }
1746
1747 /*
1748 * Get details about the machine so we can display them below.
1749 */
1750 Bstr bstrMachineName;
1751 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
1752 Bstr bstrUuid;
1753 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
1754 BSTR bstrInstalledOS;
1755 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
1756 Utf8Str strInstalledOS(bstrInstalledOS);
1757
1758 /*
1759 * Temporarily lock the machine to check whether it's running or not.
1760 * We take this opportunity to disable the first run wizard.
1761 */
1762 CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1763 {
1764 ComPtr<IConsole> ptrConsole;
1765 CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
1766
1767 if ( ptrConsole.isNull()
1768 && SUCCEEDED(hrc)
1769 && ( RTStrICmp(pszSessionType, "gui") == 0
1770 || RTStrICmp(pszSessionType, "none") == 0))
1771 {
1772 ComPtr<IMachine> ptrSessonMachine;
1773 CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
1774 if (ptrSessonMachine.isNotNull())
1775 {
1776 CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
1777 }
1778 }
1779
1780 a->session->UnlockMachine();
1781 if (FAILED(hrc))
1782 return RTEXITCODE_FAILURE;
1783 if (ptrConsole.isNotNull())
1784 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%ls' is currently running", bstrMachineName.raw());
1785 }
1786
1787 /*
1788 * Do the work.
1789 */
1790 RTMsgInfo("%s unattended installation of %s in machine '%ls' (%ls).\n",
1791 RTStrICmp(pszSessionType, "none") == 0 ? "Preparing" : "Starting",
1792 strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
1793
1794 CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
1795 if (!fDryRun)
1796 {
1797 CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
1798 CHECK_ERROR2_RET(hrc, ptrUnattended,ReconfigureVM(), RTEXITCODE_FAILURE);
1799 }
1800
1801 /*
1802 * Retrieve and display the parameters actually used.
1803 */
1804 RTMsgInfo("Using values:\n");
1805#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
1806 a_Type Value; \
1807 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
1808 if (SUCCEEDED(hrc2)) \
1809 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
1810 else \
1811 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1812 } while (0)
1813#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
1814 Bstr bstrString; \
1815 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
1816 if (SUCCEEDED(hrc2)) \
1817 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
1818 else \
1819 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1820 } while (0)
1821
1822 SHOW_STR_ATTR(IsoPath, "isoPath");
1823 SHOW_STR_ATTR(User, "user");
1824 SHOW_STR_ATTR(Password, "password");
1825 SHOW_STR_ATTR(FullUserName, "fullUserName");
1826 SHOW_STR_ATTR(ProductKey, "productKey");
1827 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
1828 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
1829 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
1830 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
1831 SHOW_STR_ATTR(Locale, "locale");
1832 SHOW_STR_ATTR(Country, "country");
1833 SHOW_STR_ATTR(TimeZone, "timeZone");
1834 SHOW_STR_ATTR(Proxy, "proxy");
1835 SHOW_STR_ATTR(Hostname, "hostname");
1836 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
1837 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
1838 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
1839 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
1840 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
1841 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
1842 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
1843 SHOW_STR_ATTR(Language, "language");
1844 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
1845 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
1846 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
1847 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
1848 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
1849
1850#undef SHOW_STR_ATTR
1851#undef SHOW_ATTR
1852
1853 /* We can drop the IUnatteded object now. */
1854 ptrUnattended.setNull();
1855
1856 /*
1857 * Start the VM if requested.
1858 */
1859 if ( fDryRun
1860 || RTStrICmp(pszSessionType, "none") == 0)
1861 {
1862 if (!fDryRun)
1863 RTMsgInfo("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n", bstrMachineName.raw(), bstrUuid.raw());
1864 hrc = S_OK;
1865 }
1866 else
1867 {
1868 Bstr env;
1869#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1870 /* make sure the VM process will start on the same display as VBoxManage */
1871 Utf8Str str;
1872 const char *pszDisplay = RTEnvGet("DISPLAY");
1873 if (pszDisplay)
1874 str = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
1875 const char *pszXAuth = RTEnvGet("XAUTHORITY");
1876 if (pszXAuth)
1877 str.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
1878 env = str;
1879#endif
1880 ComPtr<IProgress> ptrProgress;
1881 CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), env.raw(), ptrProgress.asOutParam()));
1882 if (SUCCEEDED(hrc) && !ptrProgress.isNull())
1883 {
1884 RTMsgInfo("Waiting for VM '%ls' to power on...\n", bstrMachineName.raw());
1885 CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
1886 if (SUCCEEDED(hrc))
1887 {
1888 BOOL fCompleted = true;
1889 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
1890 if (SUCCEEDED(hrc))
1891 {
1892 ASSERT(fCompleted);
1893
1894 LONG iRc;
1895 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
1896 if (SUCCEEDED(hrc))
1897 {
1898 if (SUCCEEDED(iRc))
1899 RTMsgInfo("VM '%ls' (%ls) has been successfully started.\n", bstrMachineName.raw(), bstrUuid.raw());
1900 else
1901 {
1902 ProgressErrorInfo info(ptrProgress);
1903 com::GluePrintErrorInfo(info);
1904 }
1905 hrc = iRc;
1906 }
1907 }
1908 }
1909 }
1910
1911 /*
1912 * Do we wait for the VM to power down?
1913 */
1914 }
1915
1916 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1917}
1918
1919
1920RTEXITCODE handleUnattended(HandlerArg *a)
1921{
1922 /*
1923 * Sub-command switch.
1924 */
1925 if (a->argc < 1)
1926 return errorNoSubcommand();
1927
1928 if (!strcmp(a->argv[0], "detect"))
1929 {
1930 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
1931 return handleUnattendedDetect(a);
1932 }
1933
1934 if (!strcmp(a->argv[0], "install"))
1935 {
1936 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
1937 return handleUnattendedInstall(a);
1938 }
1939
1940 /* Consider some kind of create-vm-and-install-guest-os command. */
1941 return errorUnknownSubcommand(a->argv[0]);
1942}
1943
1944/**
1945 * Common Cloud profile options.
1946 */
1947typedef struct
1948{
1949 const char *pszProviderName;
1950 const char *pszProfileName;
1951} CLOUDPROFILECOMMONOPT;
1952typedef CLOUDPROFILECOMMONOPT *PCLOUDPROFILECOMMONOPT;
1953
1954/**
1955 * Sets the properties of cloud profile
1956 *
1957 * @returns 0 on success, 1 on failure
1958 */
1959
1960static RTEXITCODE setCloudProfileProperties(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
1961{
1962
1963 HRESULT hrc = S_OK;
1964
1965 Bstr bstrProvider(pCommonOpts->pszProviderName);
1966 Bstr bstrProfile(pCommonOpts->pszProfileName);
1967
1968 /*
1969 * Parse options.
1970 */
1971 static const RTGETOPTDEF s_aOptions[] =
1972 {
1973 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
1974 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
1975 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
1976 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
1977 { "--tenancy", 't', RTGETOPT_REQ_STRING },
1978 { "--compartment", 'c', RTGETOPT_REQ_STRING },
1979 { "--region", 'r', RTGETOPT_REQ_STRING }
1980 };
1981
1982 RTGETOPTSTATE GetState;
1983 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
1984 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1985
1986 com::SafeArray<BSTR> names;
1987 com::SafeArray<BSTR> values;
1988
1989 int c;
1990 RTGETOPTUNION ValueUnion;
1991 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1992 {
1993 switch (c)
1994 {
1995 case 'u': // --clouduser
1996 Bstr("user").detachTo(names.appendedRaw());
1997 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
1998 break;
1999 case 'p': // --fingerprint
2000 Bstr("fingerprint").detachTo(names.appendedRaw());
2001 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2002 break;
2003 case 'k': // --keyfile
2004 Bstr("key_file").detachTo(names.appendedRaw());
2005 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2006 break;
2007 case 'P': // --passphrase
2008 Bstr("pass_phrase").detachTo(names.appendedRaw());
2009 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2010 break;
2011 case 't': // --tenancy
2012 Bstr("tenancy").detachTo(names.appendedRaw());
2013 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2014 break;
2015 case 'c': // --compartment
2016 Bstr("compartment").detachTo(names.appendedRaw());
2017 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2018 break;
2019 case 'r': // --region
2020 Bstr("region").detachTo(names.appendedRaw());
2021 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2022 break;
2023 default:
2024 return errorGetOpt(USAGE_CLOUDPROFILE, c, &ValueUnion);
2025 }
2026 }
2027
2028 /* check for required options */
2029 if (bstrProvider.isEmpty())
2030 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --provider is required");
2031 if (bstrProfile.isEmpty())
2032 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --profile is required");
2033
2034 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2035
2036 ComPtr<ICloudProviderManager> pCloudProviderManager;
2037 CHECK_ERROR2_RET(hrc, pVirtualBox,
2038 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2039 RTEXITCODE_FAILURE);
2040
2041 ComPtr<ICloudProvider> pCloudProvider;
2042
2043 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2044 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2045 RTEXITCODE_FAILURE);
2046
2047 ComPtr<ICloudProfile> pCloudProfile;
2048
2049 if (pCloudProvider)
2050 {
2051 CHECK_ERROR2_RET(hrc, pCloudProvider,
2052 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2053 RTEXITCODE_FAILURE);
2054 CHECK_ERROR2_RET(hrc, pCloudProfile,
2055 SetProperties(ComSafeArrayAsInParam(names), ComSafeArrayAsInParam(values)),
2056 RTEXITCODE_FAILURE);
2057 }
2058
2059 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2060
2061 RTPrintf("Provider %ls: profile '%ls' was updated.\n",bstrProvider.raw(), bstrProfile.raw());
2062
2063 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2064}
2065
2066/**
2067 * Gets the properties of cloud profile
2068 *
2069 * @returns 0 on success, 1 on failure
2070 */
2071static RTEXITCODE showCloudProfileProperties(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2072{
2073 HRESULT hrc = S_OK;
2074
2075 Bstr bstrProvider(pCommonOpts->pszProviderName);
2076 Bstr bstrProfile(pCommonOpts->pszProfileName);
2077
2078 /* check for required options */
2079 if (bstrProvider.isEmpty())
2080 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --provider is required");
2081 if (bstrProfile.isEmpty())
2082 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --profile is required");
2083
2084 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2085 ComPtr<ICloudProviderManager> pCloudProviderManager;
2086 CHECK_ERROR2_RET(hrc, pVirtualBox,
2087 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2088 RTEXITCODE_FAILURE);
2089 ComPtr<ICloudProvider> pCloudProvider;
2090 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2091 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2092 RTEXITCODE_FAILURE);
2093
2094 ComPtr<ICloudProfile> pCloudProfile;
2095 if (pCloudProvider)
2096 {
2097 CHECK_ERROR2_RET(hrc, pCloudProvider,
2098 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2099 RTEXITCODE_FAILURE);
2100
2101 Bstr bstrProviderID;
2102 pCloudProfile->COMGETTER(ProviderId)(bstrProviderID.asOutParam());
2103 RTPrintf("Provider GUID: %ls\n", bstrProviderID.raw());
2104
2105 com::SafeArray<BSTR> names;
2106 com::SafeArray<BSTR> values;
2107 CHECK_ERROR2_RET(hrc, pCloudProfile,
2108 GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values)),
2109 RTEXITCODE_FAILURE);
2110 size_t cNames = names.size();
2111 size_t cValues = values.size();
2112 bool fFirst = true;
2113 for (size_t k = 0; k < cNames; k++)
2114 {
2115 Bstr value;
2116 if (k < cValues)
2117 value = values[k];
2118 RTPrintf("%s%ls=%ls\n",
2119 fFirst ? "Property: " : " ",
2120 names[k], value.raw());
2121 fFirst = false;
2122 }
2123
2124 RTPrintf("\n");
2125 }
2126
2127 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2128}
2129
2130static RTEXITCODE addCloudProfile(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2131{
2132 HRESULT hrc = S_OK;
2133
2134 Bstr bstrProvider(pCommonOpts->pszProviderName);
2135 Bstr bstrProfile(pCommonOpts->pszProfileName);
2136
2137
2138 /* check for required options */
2139 if (bstrProvider.isEmpty())
2140 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --provider is required");
2141 if (bstrProfile.isEmpty())
2142 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --profile is required");
2143
2144 /*
2145 * Parse options.
2146 */
2147 static const RTGETOPTDEF s_aOptions[] =
2148 {
2149 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2150 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2151 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2152 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2153 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2154 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2155 { "--region", 'r', RTGETOPT_REQ_STRING }
2156 };
2157
2158 RTGETOPTSTATE GetState;
2159 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2160 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2161
2162 com::SafeArray<BSTR> names;
2163 com::SafeArray<BSTR> values;
2164
2165 int c;
2166 RTGETOPTUNION ValueUnion;
2167 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2168 {
2169 switch (c)
2170 {
2171 case 'u': // --clouduser
2172 Bstr("user").detachTo(names.appendedRaw());
2173 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2174 break;
2175 case 'p': // --fingerprint
2176 Bstr("fingerprint").detachTo(names.appendedRaw());
2177 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2178 break;
2179 case 'k': // --keyfile
2180 Bstr("key_file").detachTo(names.appendedRaw());
2181 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2182 break;
2183 case 'P': // --passphrase
2184 Bstr("pass_phrase").detachTo(names.appendedRaw());
2185 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2186 break;
2187 case 't': // --tenancy
2188 Bstr("tenancy").detachTo(names.appendedRaw());
2189 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2190 break;
2191 case 'c': // --compartment
2192 Bstr("compartment").detachTo(names.appendedRaw());
2193 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2194 break;
2195 case 'r': // --region
2196 Bstr("region").detachTo(names.appendedRaw());
2197 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2198 break;
2199 default:
2200 return errorGetOpt(USAGE_CLOUDPROFILE, c, &ValueUnion);
2201 }
2202 }
2203
2204 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2205
2206 ComPtr<ICloudProviderManager> pCloudProviderManager;
2207 CHECK_ERROR2_RET(hrc, pVirtualBox,
2208 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2209 RTEXITCODE_FAILURE);
2210
2211 ComPtr<ICloudProvider> pCloudProvider;
2212 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2213 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2214 RTEXITCODE_FAILURE);
2215
2216 CHECK_ERROR2_RET(hrc, pCloudProvider,
2217 CreateProfile(bstrProfile.raw(),
2218 ComSafeArrayAsInParam(names),
2219 ComSafeArrayAsInParam(values)),
2220 RTEXITCODE_FAILURE);
2221
2222 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2223
2224 RTPrintf("Provider %ls: profile '%ls' was added.\n",bstrProvider.raw(), bstrProfile.raw());
2225
2226 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2227}
2228
2229static RTEXITCODE deleteCloudProfile(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2230{
2231 HRESULT hrc = S_OK;
2232
2233 Bstr bstrProvider(pCommonOpts->pszProviderName);
2234 Bstr bstrProfile(pCommonOpts->pszProfileName);
2235
2236 /* check for required options */
2237 if (bstrProvider.isEmpty())
2238 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --provider is required");
2239 if (bstrProfile.isEmpty())
2240 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --profile is required");
2241
2242 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2243 ComPtr<ICloudProviderManager> pCloudProviderManager;
2244 CHECK_ERROR2_RET(hrc, pVirtualBox,
2245 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2246 RTEXITCODE_FAILURE);
2247 ComPtr<ICloudProvider> pCloudProvider;
2248 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2249 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2250 RTEXITCODE_FAILURE);
2251
2252 ComPtr<ICloudProfile> pCloudProfile;
2253 if (pCloudProvider)
2254 {
2255 CHECK_ERROR2_RET(hrc, pCloudProvider,
2256 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2257 RTEXITCODE_FAILURE);
2258
2259 CHECK_ERROR2_RET(hrc, pCloudProfile,
2260 Remove(),
2261 RTEXITCODE_FAILURE);
2262
2263 CHECK_ERROR2_RET(hrc, pCloudProvider,
2264 SaveProfiles(),
2265 RTEXITCODE_FAILURE);
2266
2267 RTPrintf("Provider %ls: profile '%ls' was deleted.\n",bstrProvider.raw(), bstrProfile.raw());
2268 }
2269
2270 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2271}
2272
2273RTEXITCODE handleCloudProfile(HandlerArg *a)
2274{
2275 if (a->argc < 1)
2276 return errorNoSubcommand();
2277
2278 static const RTGETOPTDEF s_aOptions[] =
2279 {
2280 /* common options */
2281 { "--provider", 'v', RTGETOPT_REQ_STRING },
2282 { "--profile", 'f', RTGETOPT_REQ_STRING },
2283 /* subcommands */
2284 { "add", 1000, RTGETOPT_REQ_NOTHING },
2285 { "show", 1001, RTGETOPT_REQ_NOTHING },
2286 { "update", 1002, RTGETOPT_REQ_NOTHING },
2287 { "delete", 1003, RTGETOPT_REQ_NOTHING },
2288 };
2289
2290 RTGETOPTSTATE GetState;
2291 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2292 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2293
2294 CLOUDPROFILECOMMONOPT CommonOpts = { NULL, NULL };
2295 int c;
2296 RTGETOPTUNION ValueUnion;
2297 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2298 {
2299 switch (c)
2300 {
2301 case 'v': // --provider
2302 CommonOpts.pszProviderName = ValueUnion.psz;
2303 break;
2304 case 'f': // --profile
2305 CommonOpts.pszProfileName = ValueUnion.psz;
2306 break;
2307 /* Sub-commands: */
2308 case 1000:
2309 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_ADD);
2310 return addCloudProfile(a, GetState.iNext, &CommonOpts);
2311 case 1001:
2312 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_SHOW);
2313 return showCloudProfileProperties(a, &CommonOpts);
2314 case 1002:
2315 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_UPDATE);
2316 return setCloudProfileProperties(a, GetState.iNext, &CommonOpts);
2317 case 1003:
2318 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_DELETE);
2319 return deleteCloudProfile(a, &CommonOpts);
2320 case VINF_GETOPT_NOT_OPTION:
2321 return errorUnknownSubcommand(ValueUnion.psz);
2322
2323 default:
2324 return errorGetOpt(c, &ValueUnion);
2325 }
2326 }
2327
2328 return errorNoSubcommand();
2329}
2330
Note: See TracBrowser for help on using the repository browser.

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