VirtualBox

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

Last change on this file since 77887 was 77887, checked in by vboxsync, 5 years ago

manual/VBoxManage: Made the new clonevm build. Again, there must be '=' between an option name and its value. The help-scope in the example section is not needed for commands without sub-commands. Use 'vmname|uuid' rather than just 'vm' as it's easier to grasp when just seeing the command synopsis.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use