VirtualBox

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

Last change on this file since 90923 was 90828, checked in by vboxsync, 4 years ago

Main: bugref:1909: Added API localization

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 89.8 KB
Line 
1/* $Id: VBoxManageMisc.cpp 90828 2021-08-24 09:44:46Z vboxsync $ */
2/** @file
3 * VBoxManage - VirtualBox's command-line interface.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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 com::SafeArray<IN_BSTR> aBstrEnv;
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 aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw());
635 const char *pszXAuth = RTEnvGet("XAUTHORITY");
636 if (pszXAuth)
637 aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw());
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 aBstrEnv.push_back(Bstr(ValueUnion.psz).raw());
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 ComSafeArrayAsInParam(aBstrEnv), 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#ifdef VBOX_WITH_MAIN_NLS
1066 else if (!strcmp(a->argv[0], "language"))
1067 {
1068 Bstr bstrLanguage(a->argv[1]);
1069 CHECK_ERROR(systemProperties, COMSETTER(LanguageId)(bstrLanguage.raw()));
1070 }
1071#endif
1072 else
1073 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]);
1074
1075 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1076}
1077
1078/**
1079 * sharedfolder add
1080 */
1081static RTEXITCODE handleSharedFolderAdd(HandlerArg *a)
1082{
1083 /*
1084 * Parse arguments (argv[0] == subcommand).
1085 */
1086 static const RTGETOPTDEF s_aAddOptions[] =
1087 {
1088 { "--name", 'n', RTGETOPT_REQ_STRING },
1089 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
1090 { "--hostpath", 'p', RTGETOPT_REQ_STRING },
1091 { "-hostpath", 'p', RTGETOPT_REQ_STRING }, // deprecated
1092 { "--readonly", 'r', RTGETOPT_REQ_NOTHING },
1093 { "-readonly", 'r', RTGETOPT_REQ_NOTHING }, // deprecated
1094 { "--transient", 't', RTGETOPT_REQ_NOTHING },
1095 { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated
1096 { "--automount", 'a', RTGETOPT_REQ_NOTHING },
1097 { "-automount", 'a', RTGETOPT_REQ_NOTHING }, // deprecated
1098 { "--auto-mount-point", 'm', RTGETOPT_REQ_STRING },
1099 };
1100 const char *pszMachineName = NULL;
1101 const char *pszName = NULL;
1102 const char *pszHostPath = NULL;
1103 bool fTransient = false;
1104 bool fWritable = true;
1105 bool fAutoMount = false;
1106 const char *pszAutoMountPoint = "";
1107
1108 RTGETOPTSTATE GetState;
1109 RTGetOptInit(&GetState, a->argc, a->argv, s_aAddOptions, RT_ELEMENTS(s_aAddOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1110 int c;
1111 RTGETOPTUNION ValueUnion;
1112 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1113 {
1114 switch (c)
1115 {
1116 case 'n':
1117 pszName = ValueUnion.psz;
1118 break;
1119 case 'p':
1120 pszHostPath = ValueUnion.psz;
1121 break;
1122 case 'r':
1123 fWritable = false;
1124 break;
1125 case 't':
1126 fTransient = true;
1127 break;
1128 case 'a':
1129 fAutoMount = true;
1130 break;
1131 case 'm':
1132 pszAutoMountPoint = ValueUnion.psz;
1133 break;
1134 case VINF_GETOPT_NOT_OPTION:
1135 if (pszMachineName)
1136 return errorArgument("Machine name is given more than once: first '%s', then '%s'",
1137 pszMachineName, ValueUnion.psz);
1138 pszMachineName = ValueUnion.psz;
1139 break;
1140 default:
1141 return errorGetOpt(c, &ValueUnion);
1142 }
1143 }
1144
1145 if (!pszMachineName)
1146 return errorSyntax("No machine was specified");
1147
1148 if (!pszName)
1149 return errorSyntax("No shared folder name (--name) was given");
1150 if (strchr(pszName, ' '))
1151 return errorSyntax("Invalid shared folder name '%s': contains space", pszName);
1152 if (strchr(pszName, '\t'))
1153 return errorSyntax("Invalid shared folder name '%s': contains tabs", pszName);
1154 if (strchr(pszName, '\n') || strchr(pszName, '\r'))
1155 return errorSyntax("Invalid shared folder name '%s': contains newline", pszName);
1156
1157 if (!pszHostPath)
1158 return errorSyntax("No host path (--hostpath) was given");
1159 char szAbsHostPath[RTPATH_MAX];
1160 int vrc = RTPathAbs(pszHostPath, szAbsHostPath, sizeof(szAbsHostPath));
1161 if (RT_FAILURE(vrc))
1162 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTAbsPath failed on '%s': %Rrc", pszHostPath, vrc);
1163
1164 /*
1165 * Done parsing, do some work.
1166 */
1167 ComPtr<IMachine> ptrMachine;
1168 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1169 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
1170
1171 HRESULT hrc;
1172 if (fTransient)
1173 {
1174 /* open an existing session for the VM */
1175 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1176
1177 /* get the session machine */
1178 ComPtr<IMachine> ptrSessionMachine;
1179 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1180
1181 /* get the session console */
1182 ComPtr<IConsole> ptrConsole;
1183 CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1184 if (ptrConsole.isNull())
1185 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%s' is not currently running.", pszMachineName);
1186
1187 CHECK_ERROR2(hrc, ptrConsole, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(),
1188 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1189 a->session->UnlockMachine();
1190 }
1191 else
1192 {
1193 /* open a session for the VM */
1194 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1195
1196 /* get the mutable session machine */
1197 ComPtr<IMachine> ptrSessionMachine;
1198 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1199
1200 CHECK_ERROR2(hrc, ptrSessionMachine, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(),
1201 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1202 if (SUCCEEDED(hrc))
1203 {
1204 CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings());
1205 }
1206
1207 a->session->UnlockMachine();
1208 }
1209
1210 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1211}
1212
1213/**
1214 * sharedfolder remove
1215 */
1216static RTEXITCODE handleSharedFolderRemove(HandlerArg *a)
1217{
1218 /*
1219 * Parse arguments (argv[0] == subcommand).
1220 */
1221 static const RTGETOPTDEF s_aRemoveOptions[] =
1222 {
1223 { "--name", 'n', RTGETOPT_REQ_STRING },
1224 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
1225 { "--transient", 't', RTGETOPT_REQ_NOTHING },
1226 { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated
1227 };
1228 const char *pszMachineName = NULL;
1229 const char *pszName = NULL;
1230 bool fTransient = false;
1231
1232 RTGETOPTSTATE GetState;
1233 RTGetOptInit(&GetState, a->argc, a->argv, s_aRemoveOptions, RT_ELEMENTS(s_aRemoveOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1234 int c;
1235 RTGETOPTUNION ValueUnion;
1236 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1237 {
1238 switch (c)
1239 {
1240 case 'n':
1241 pszName = ValueUnion.psz;
1242 break;
1243 case 't':
1244 fTransient = true;
1245 break;
1246 case VINF_GETOPT_NOT_OPTION:
1247 if (pszMachineName)
1248 return errorArgument("Machine name is given more than once: first '%s', then '%s'",
1249 pszMachineName, ValueUnion.psz);
1250 pszMachineName = ValueUnion.psz;
1251 break;
1252 default:
1253 return errorGetOpt(c, &ValueUnion);
1254 }
1255 }
1256
1257 if (!pszMachineName)
1258 return errorSyntax("No machine was specified");
1259 if (!pszName)
1260 return errorSyntax("No shared folder name (--name) was given");
1261
1262 /*
1263 * Done parsing, do some real work.
1264 */
1265 ComPtr<IMachine> ptrMachine;
1266 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1267 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
1268
1269 HRESULT hrc;
1270 if (fTransient)
1271 {
1272 /* open an existing session for the VM */
1273 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1274 /* get the session machine */
1275 ComPtr<IMachine> ptrSessionMachine;
1276 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1277 /* get the session console */
1278 ComPtr<IConsole> ptrConsole;
1279 CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1280 if (ptrConsole.isNull())
1281 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%s' is not currently running.\n", pszMachineName);
1282
1283 CHECK_ERROR2(hrc, ptrConsole, RemoveSharedFolder(Bstr(pszName).raw()));
1284
1285 a->session->UnlockMachine();
1286 }
1287 else
1288 {
1289 /* open a session for the VM */
1290 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1291
1292 /* get the mutable session machine */
1293 ComPtr<IMachine> ptrSessionMachine;
1294 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1295
1296 CHECK_ERROR2(hrc, ptrSessionMachine, RemoveSharedFolder(Bstr(pszName).raw()));
1297
1298 /* commit and close the session */
1299 if (SUCCEEDED(hrc))
1300 {
1301 CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings());
1302 }
1303 a->session->UnlockMachine();
1304 }
1305
1306 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1307}
1308
1309
1310RTEXITCODE handleSharedFolder(HandlerArg *a)
1311{
1312 if (a->argc < 1)
1313 return errorSyntax("Not enough parameters");
1314
1315 if (!strcmp(a->argv[0], "add"))
1316 {
1317 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_ADD);
1318 return handleSharedFolderAdd(a);
1319 }
1320
1321 if (!strcmp(a->argv[0], "remove"))
1322 {
1323 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_REMOVE);
1324 return handleSharedFolderRemove(a);
1325 }
1326
1327 return errorUnknownSubcommand(a->argv[0]);
1328}
1329
1330RTEXITCODE handleExtPack(HandlerArg *a)
1331{
1332 if (a->argc < 1)
1333 return errorNoSubcommand();
1334
1335 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1336 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1337
1338 RTGETOPTSTATE GetState;
1339 RTGETOPTUNION ValueUnion;
1340 int ch;
1341 HRESULT hrc = S_OK;
1342
1343 if (!strcmp(a->argv[0], "install"))
1344 {
1345 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
1346 const char *pszName = NULL;
1347 bool fReplace = false;
1348
1349 static const RTGETOPTDEF s_aInstallOptions[] =
1350 {
1351 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1352 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
1353 };
1354
1355 RTCList<RTCString> lstLicenseHashes;
1356 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1357 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1358 {
1359 switch (ch)
1360 {
1361 case 'r':
1362 fReplace = true;
1363 break;
1364
1365 case 'a':
1366 lstLicenseHashes.append(ValueUnion.psz);
1367 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
1368 break;
1369
1370 case VINF_GETOPT_NOT_OPTION:
1371 if (pszName)
1372 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1373 pszName = ValueUnion.psz;
1374 break;
1375
1376 default:
1377 return errorGetOpt(ch, &ValueUnion);
1378 }
1379 }
1380 if (!pszName)
1381 return errorSyntax("No extension pack name was given to \"extpack install\"");
1382
1383 char szPath[RTPATH_MAX];
1384 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
1385 if (RT_FAILURE(vrc))
1386 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszName, vrc);
1387
1388 Bstr bstrTarball(szPath);
1389 Bstr bstrName;
1390 ComPtr<IExtPackFile> ptrExtPackFile;
1391 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1392 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1393 BOOL fShowLicense = true;
1394 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
1395 if (fShowLicense)
1396 {
1397 Bstr bstrLicense;
1398 CHECK_ERROR2I_RET(ptrExtPackFile,
1399 QueryLicense(Bstr("").raw() /* PreferredLocale */,
1400 Bstr("").raw() /* PreferredLanguage */,
1401 Bstr("txt").raw() /* Format */,
1402 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
1403 Utf8Str strLicense(bstrLicense);
1404 uint8_t abHash[RTSHA256_HASH_SIZE];
1405 char szDigest[RTSHA256_DIGEST_LEN + 1];
1406 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
1407 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
1408 AssertRCStmt(vrc, szDigest[0] = '\0');
1409 if (lstLicenseHashes.contains(szDigest))
1410 RTPrintf("License accepted.\n");
1411 else
1412 {
1413 RTPrintf("%s\n", strLicense.c_str());
1414 RTPrintf("Do you agree to these license terms and conditions (y/n)? " );
1415 ch = RTStrmGetCh(g_pStdIn);
1416 RTPrintf("\n");
1417 if (ch != 'y' && ch != 'Y')
1418 {
1419 RTPrintf("Installation of \"%ls\" aborted.\n", bstrName.raw());
1420 return RTEXITCODE_FAILURE;
1421 }
1422 if (szDigest[0])
1423 RTPrintf("License accepted. For batch installation add\n"
1424 "--accept-license=%s\n"
1425 "to the VBoxManage command line.\n\n", szDigest);
1426 }
1427 }
1428 ComPtr<IProgress> ptrProgress;
1429 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1430 hrc = showProgress(ptrProgress);
1431 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to install \"%s\"", szPath), RTEXITCODE_FAILURE);
1432
1433 RTPrintf("Successfully installed \"%ls\".\n", bstrName.raw());
1434 }
1435 else if (!strcmp(a->argv[0], "uninstall"))
1436 {
1437 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
1438 const char *pszName = NULL;
1439 bool fForced = false;
1440
1441 static const RTGETOPTDEF s_aUninstallOptions[] =
1442 {
1443 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1444 };
1445
1446 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1447 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1448 {
1449 switch (ch)
1450 {
1451 case 'f':
1452 fForced = true;
1453 break;
1454
1455 case VINF_GETOPT_NOT_OPTION:
1456 if (pszName)
1457 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1458 pszName = ValueUnion.psz;
1459 break;
1460
1461 default:
1462 return errorGetOpt(ch, &ValueUnion);
1463 }
1464 }
1465 if (!pszName)
1466 return errorSyntax("No extension pack name was given to \"extpack uninstall\"");
1467
1468 Bstr bstrName(pszName);
1469 ComPtr<IProgress> ptrProgress;
1470 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1471 hrc = showProgress(ptrProgress);
1472 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to uninstall \"%s\"", pszName), RTEXITCODE_FAILURE);
1473
1474 RTPrintf("Successfully uninstalled \"%s\".\n", pszName);
1475 }
1476 else if (!strcmp(a->argv[0], "cleanup"))
1477 {
1478 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
1479 if (a->argc > 1)
1480 return errorTooManyParameters(&a->argv[1]);
1481 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1482 RTPrintf("Successfully performed extension pack cleanup\n");
1483 }
1484 else
1485 return errorUnknownSubcommand(a->argv[0]);
1486
1487 return RTEXITCODE_SUCCESS;
1488}
1489
1490RTEXITCODE handleUnattendedDetect(HandlerArg *a)
1491{
1492 HRESULT hrc;
1493
1494 /*
1495 * Options. We work directly on an IUnattended instace while parsing
1496 * the options. This saves a lot of extra clutter.
1497 */
1498 bool fMachineReadable = false;
1499 char szIsoPath[RTPATH_MAX];
1500 szIsoPath[0] = '\0';
1501
1502 /*
1503 * Parse options.
1504 */
1505 static const RTGETOPTDEF s_aOptions[] =
1506 {
1507 { "--iso", 'i', RTGETOPT_REQ_STRING },
1508 { "--machine-readable", 'M', RTGETOPT_REQ_NOTHING },
1509 };
1510
1511 RTGETOPTSTATE GetState;
1512 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1513 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1514
1515 int c;
1516 RTGETOPTUNION ValueUnion;
1517 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1518 {
1519 switch (c)
1520 {
1521 case 'i': // --iso
1522 vrc = RTPathAbs(ValueUnion.psz, szIsoPath, sizeof(szIsoPath));
1523 if (RT_FAILURE(vrc))
1524 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1525 break;
1526
1527 case 'M': // --machine-readable.
1528 fMachineReadable = true;
1529 break;
1530
1531 default:
1532 return errorGetOpt(c, &ValueUnion);
1533 }
1534 }
1535
1536 /*
1537 * Check for required stuff.
1538 */
1539 if (szIsoPath[0] == '\0')
1540 return errorSyntax("No ISO specified");
1541
1542 /*
1543 * Do the job.
1544 */
1545 ComPtr<IUnattended> ptrUnattended;
1546 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1547 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szIsoPath).raw()), RTEXITCODE_FAILURE);
1548 CHECK_ERROR2(hrc, ptrUnattended, DetectIsoOS());
1549 RTEXITCODE rcExit = SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1550
1551 /*
1552 * Retrieve the results.
1553 */
1554 Bstr bstrDetectedOSTypeId;
1555 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSTypeId)(bstrDetectedOSTypeId.asOutParam()), RTEXITCODE_FAILURE);
1556 Bstr bstrDetectedVersion;
1557 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSVersion)(bstrDetectedVersion.asOutParam()), RTEXITCODE_FAILURE);
1558 Bstr bstrDetectedFlavor;
1559 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSFlavor)(bstrDetectedFlavor.asOutParam()), RTEXITCODE_FAILURE);
1560 Bstr bstrDetectedLanguages;
1561 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSLanguages)(bstrDetectedLanguages.asOutParam()), RTEXITCODE_FAILURE);
1562 Bstr bstrDetectedHints;
1563 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSHints)(bstrDetectedHints.asOutParam()), RTEXITCODE_FAILURE);
1564 if (fMachineReadable)
1565 RTPrintf("OSTypeId=\"%ls\"\n"
1566 "OSVersion=\"%ls\"\n"
1567 "OSFlavor=\"%ls\"\n"
1568 "OSLanguages=\"%ls\"\n"
1569 "OSHints=\"%ls\"\n",
1570 bstrDetectedOSTypeId.raw(),
1571 bstrDetectedVersion.raw(),
1572 bstrDetectedFlavor.raw(),
1573 bstrDetectedLanguages.raw(),
1574 bstrDetectedHints.raw());
1575 else
1576 {
1577 RTMsgInfo("Detected '%s' to be:\n", szIsoPath);
1578 RTPrintf(" OS TypeId = %ls\n"
1579 " OS Version = %ls\n"
1580 " OS Flavor = %ls\n"
1581 " OS Languages = %ls\n"
1582 " OS Hints = %ls\n",
1583 bstrDetectedOSTypeId.raw(),
1584 bstrDetectedVersion.raw(),
1585 bstrDetectedFlavor.raw(),
1586 bstrDetectedLanguages.raw(),
1587 bstrDetectedHints.raw());
1588 }
1589
1590 return rcExit;
1591}
1592
1593RTEXITCODE handleUnattendedInstall(HandlerArg *a)
1594{
1595 HRESULT hrc;
1596 char szAbsPath[RTPATH_MAX];
1597
1598 /*
1599 * Options. We work directly on an IUnattended instace while parsing
1600 * the options. This saves a lot of extra clutter.
1601 */
1602 ComPtr<IUnattended> ptrUnattended;
1603 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1604 RTCList<RTCString> arrPackageSelectionAdjustments;
1605 ComPtr<IMachine> ptrMachine;
1606 bool fDryRun = false;
1607 const char *pszSessionType = "none";
1608
1609 /*
1610 * Parse options.
1611 */
1612 static const RTGETOPTDEF s_aOptions[] =
1613 {
1614 { "--iso", 'i', RTGETOPT_REQ_STRING },
1615 { "--user", 'u', RTGETOPT_REQ_STRING },
1616 { "--password", 'p', RTGETOPT_REQ_STRING },
1617 { "--password-file", 'X', RTGETOPT_REQ_STRING },
1618 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
1619 { "--key", 'k', RTGETOPT_REQ_STRING },
1620 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
1621 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
1622 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
1623 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
1624 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
1625 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
1626 { "--locale", 'l', RTGETOPT_REQ_STRING },
1627 { "--country", 'Y', RTGETOPT_REQ_STRING },
1628 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
1629 { "--proxy", 'y', RTGETOPT_REQ_STRING },
1630 { "--hostname", 'H', RTGETOPT_REQ_STRING },
1631 { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
1632 { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
1633 // advance options:
1634 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
1635 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
1636 { "--script-template", 'c', RTGETOPT_REQ_STRING },
1637 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
1638 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
1639 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
1640 { "--language", 'L', RTGETOPT_REQ_STRING },
1641 // start vm related options:
1642 { "--start-vm", 'S', RTGETOPT_REQ_STRING },
1643 /** @todo Add a --wait option too for waiting for the VM to shut down or
1644 * something like that...? */
1645 };
1646
1647 RTGETOPTSTATE GetState;
1648 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1649 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1650
1651 int c;
1652 RTGETOPTUNION ValueUnion;
1653 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1654 {
1655 switch (c)
1656 {
1657 case VINF_GETOPT_NOT_OPTION:
1658 if (ptrMachine.isNotNull())
1659 return errorSyntax("VM name/UUID given more than once!");
1660 CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1661 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
1662 break;
1663
1664 case 'i': // --iso
1665 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1666 if (RT_FAILURE(vrc))
1667 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1668 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1669 break;
1670
1671 case 'u': // --user
1672 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1673 break;
1674
1675 case 'p': // --password
1676 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1677 break;
1678
1679 case 'X': // --password-file
1680 {
1681 Utf8Str strPassword;
1682 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
1683 if (rcExit != RTEXITCODE_SUCCESS)
1684 return rcExit;
1685 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
1686 break;
1687 }
1688
1689 case 'U': // --full-user-name
1690 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1691 break;
1692
1693 case 'k': // --key
1694 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1695 break;
1696
1697 case 'A': // --install-additions
1698 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
1699 break;
1700 case 'N': // --no-install-additions
1701 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
1702 break;
1703 case 'a': // --additions-iso
1704 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1705 if (RT_FAILURE(vrc))
1706 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1707 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1708 break;
1709
1710 case 't': // --install-txs
1711 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
1712 break;
1713 case 'T': // --no-install-txs
1714 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
1715 break;
1716 case 'K': // --valiation-kit-iso
1717 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1718 if (RT_FAILURE(vrc))
1719 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1720 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1721 break;
1722
1723 case 'l': // --locale
1724 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1725 break;
1726
1727 case 'Y': // --country
1728 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1729 break;
1730
1731 case 'z': // --time-zone;
1732 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1733 break;
1734
1735 case 'y': // --proxy
1736 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1737 break;
1738
1739 case 'H': // --hostname
1740 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1741 break;
1742
1743 case 's': // --package-selection-adjustment
1744 arrPackageSelectionAdjustments.append(ValueUnion.psz);
1745 break;
1746
1747 case 'D':
1748 fDryRun = true;
1749 break;
1750
1751 case 'x': // --auxiliary-base-path
1752 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1753 if (RT_FAILURE(vrc))
1754 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1755 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1756 break;
1757
1758 case 'm': // --image-index
1759 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
1760 break;
1761
1762 case 'c': // --script-template
1763 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1764 if (RT_FAILURE(vrc))
1765 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1766 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1767 break;
1768
1769 case 'C': // --post-install-script-template
1770 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1771 if (RT_FAILURE(vrc))
1772 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1773 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1774 break;
1775
1776 case 'P': // --post-install-command.
1777 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1778 break;
1779
1780 case 'I': // --extra-install-kernel-parameters
1781 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1782 break;
1783
1784 case 'L': // --language
1785 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1786 break;
1787
1788 case 'S': // --start-vm
1789 pszSessionType = ValueUnion.psz;
1790 break;
1791
1792 default:
1793 return errorGetOpt(c, &ValueUnion);
1794 }
1795 }
1796
1797 /*
1798 * Check for required stuff.
1799 */
1800 if (ptrMachine.isNull())
1801 return errorSyntax("Missing VM name/UUID");
1802
1803 /*
1804 * Set accumulative attributes.
1805 */
1806 if (arrPackageSelectionAdjustments.size() == 1)
1807 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
1808 RTEXITCODE_FAILURE);
1809 else if (arrPackageSelectionAdjustments.size() > 1)
1810 {
1811 RTCString strAdjustments;
1812 strAdjustments.join(arrPackageSelectionAdjustments, ";");
1813 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
1814 }
1815
1816 /*
1817 * Get details about the machine so we can display them below.
1818 */
1819 Bstr bstrMachineName;
1820 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
1821 Bstr bstrUuid;
1822 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
1823 BSTR bstrInstalledOS;
1824 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
1825 Utf8Str strInstalledOS(bstrInstalledOS);
1826
1827 /*
1828 * Temporarily lock the machine to check whether it's running or not.
1829 * We take this opportunity to disable the first run wizard.
1830 */
1831 CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1832 {
1833 ComPtr<IConsole> ptrConsole;
1834 CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
1835
1836 if ( ptrConsole.isNull()
1837 && SUCCEEDED(hrc)
1838 && ( RTStrICmp(pszSessionType, "gui") == 0
1839 || RTStrICmp(pszSessionType, "none") == 0))
1840 {
1841 ComPtr<IMachine> ptrSessonMachine;
1842 CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
1843 if (ptrSessonMachine.isNotNull())
1844 {
1845 CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
1846 }
1847 }
1848
1849 a->session->UnlockMachine();
1850 if (FAILED(hrc))
1851 return RTEXITCODE_FAILURE;
1852 if (ptrConsole.isNotNull())
1853 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%ls' is currently running", bstrMachineName.raw());
1854 }
1855
1856 /*
1857 * Do the work.
1858 */
1859 RTMsgInfo("%s unattended installation of %s in machine '%ls' (%ls).\n",
1860 RTStrICmp(pszSessionType, "none") == 0 ? "Preparing" : "Starting",
1861 strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
1862
1863 CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
1864 if (!fDryRun)
1865 {
1866 CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
1867 CHECK_ERROR2_RET(hrc, ptrUnattended,ReconfigureVM(), RTEXITCODE_FAILURE);
1868 }
1869
1870 /*
1871 * Retrieve and display the parameters actually used.
1872 */
1873 RTMsgInfo("Using values:\n");
1874#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
1875 a_Type Value; \
1876 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
1877 if (SUCCEEDED(hrc2)) \
1878 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
1879 else \
1880 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1881 } while (0)
1882#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
1883 Bstr bstrString; \
1884 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
1885 if (SUCCEEDED(hrc2)) \
1886 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
1887 else \
1888 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1889 } while (0)
1890
1891 SHOW_STR_ATTR(IsoPath, "isoPath");
1892 SHOW_STR_ATTR(User, "user");
1893 SHOW_STR_ATTR(Password, "password");
1894 SHOW_STR_ATTR(FullUserName, "fullUserName");
1895 SHOW_STR_ATTR(ProductKey, "productKey");
1896 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
1897 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
1898 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
1899 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
1900 SHOW_STR_ATTR(Locale, "locale");
1901 SHOW_STR_ATTR(Country, "country");
1902 SHOW_STR_ATTR(TimeZone, "timeZone");
1903 SHOW_STR_ATTR(Proxy, "proxy");
1904 SHOW_STR_ATTR(Hostname, "hostname");
1905 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
1906 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
1907 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
1908 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
1909 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
1910 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
1911 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
1912 SHOW_STR_ATTR(Language, "language");
1913 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
1914 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
1915 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
1916 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
1917 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
1918
1919#undef SHOW_STR_ATTR
1920#undef SHOW_ATTR
1921
1922 /* We can drop the IUnatteded object now. */
1923 ptrUnattended.setNull();
1924
1925 /*
1926 * Start the VM if requested.
1927 */
1928 if ( fDryRun
1929 || RTStrICmp(pszSessionType, "none") == 0)
1930 {
1931 if (!fDryRun)
1932 RTMsgInfo("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n", bstrMachineName.raw(), bstrUuid.raw());
1933 hrc = S_OK;
1934 }
1935 else
1936 {
1937 com::SafeArray<IN_BSTR> aBstrEnv;
1938#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1939 /* make sure the VM process will start on the same display as VBoxManage */
1940 const char *pszDisplay = RTEnvGet("DISPLAY");
1941 if (pszDisplay)
1942 aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw());
1943 const char *pszXAuth = RTEnvGet("XAUTHORITY");
1944 if (pszXAuth)
1945 aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw());
1946#endif
1947 ComPtr<IProgress> ptrProgress;
1948 CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), ComSafeArrayAsInParam(aBstrEnv), ptrProgress.asOutParam()));
1949 if (SUCCEEDED(hrc) && !ptrProgress.isNull())
1950 {
1951 RTMsgInfo("Waiting for VM '%ls' to power on...\n", bstrMachineName.raw());
1952 CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
1953 if (SUCCEEDED(hrc))
1954 {
1955 BOOL fCompleted = true;
1956 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
1957 if (SUCCEEDED(hrc))
1958 {
1959 ASSERT(fCompleted);
1960
1961 LONG iRc;
1962 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
1963 if (SUCCEEDED(hrc))
1964 {
1965 if (SUCCEEDED(iRc))
1966 RTMsgInfo("VM '%ls' (%ls) has been successfully started.\n", bstrMachineName.raw(), bstrUuid.raw());
1967 else
1968 {
1969 ProgressErrorInfo info(ptrProgress);
1970 com::GluePrintErrorInfo(info);
1971 }
1972 hrc = iRc;
1973 }
1974 }
1975 }
1976 }
1977
1978 /*
1979 * Do we wait for the VM to power down?
1980 */
1981 }
1982
1983 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1984}
1985
1986
1987RTEXITCODE handleUnattended(HandlerArg *a)
1988{
1989 /*
1990 * Sub-command switch.
1991 */
1992 if (a->argc < 1)
1993 return errorNoSubcommand();
1994
1995 if (!strcmp(a->argv[0], "detect"))
1996 {
1997 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
1998 return handleUnattendedDetect(a);
1999 }
2000
2001 if (!strcmp(a->argv[0], "install"))
2002 {
2003 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
2004 return handleUnattendedInstall(a);
2005 }
2006
2007 /* Consider some kind of create-vm-and-install-guest-os command. */
2008 return errorUnknownSubcommand(a->argv[0]);
2009}
2010
2011/**
2012 * Common Cloud profile options.
2013 */
2014typedef struct
2015{
2016 const char *pszProviderName;
2017 const char *pszProfileName;
2018} CLOUDPROFILECOMMONOPT;
2019typedef CLOUDPROFILECOMMONOPT *PCLOUDPROFILECOMMONOPT;
2020
2021/**
2022 * Sets the properties of cloud profile
2023 *
2024 * @returns 0 on success, 1 on failure
2025 */
2026
2027static RTEXITCODE setCloudProfileProperties(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2028{
2029
2030 HRESULT hrc = S_OK;
2031
2032 Bstr bstrProvider(pCommonOpts->pszProviderName);
2033 Bstr bstrProfile(pCommonOpts->pszProfileName);
2034
2035 /*
2036 * Parse options.
2037 */
2038 static const RTGETOPTDEF s_aOptions[] =
2039 {
2040 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2041 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2042 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2043 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2044 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2045 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2046 { "--region", 'r', RTGETOPT_REQ_STRING }
2047 };
2048
2049 RTGETOPTSTATE GetState;
2050 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2051 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2052
2053 com::SafeArray<BSTR> names;
2054 com::SafeArray<BSTR> values;
2055
2056 int c;
2057 RTGETOPTUNION ValueUnion;
2058 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2059 {
2060 switch (c)
2061 {
2062 case 'u': // --clouduser
2063 Bstr("user").detachTo(names.appendedRaw());
2064 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2065 break;
2066 case 'p': // --fingerprint
2067 Bstr("fingerprint").detachTo(names.appendedRaw());
2068 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2069 break;
2070 case 'k': // --keyfile
2071 Bstr("key_file").detachTo(names.appendedRaw());
2072 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2073 break;
2074 case 'P': // --passphrase
2075 Bstr("pass_phrase").detachTo(names.appendedRaw());
2076 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2077 break;
2078 case 't': // --tenancy
2079 Bstr("tenancy").detachTo(names.appendedRaw());
2080 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2081 break;
2082 case 'c': // --compartment
2083 Bstr("compartment").detachTo(names.appendedRaw());
2084 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2085 break;
2086 case 'r': // --region
2087 Bstr("region").detachTo(names.appendedRaw());
2088 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2089 break;
2090 default:
2091 return errorGetOpt(USAGE_CLOUDPROFILE, c, &ValueUnion);
2092 }
2093 }
2094
2095 /* check for required options */
2096 if (bstrProvider.isEmpty())
2097 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --provider is required");
2098 if (bstrProfile.isEmpty())
2099 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --profile is required");
2100
2101 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2102
2103 ComPtr<ICloudProviderManager> pCloudProviderManager;
2104 CHECK_ERROR2_RET(hrc, pVirtualBox,
2105 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2106 RTEXITCODE_FAILURE);
2107
2108 ComPtr<ICloudProvider> pCloudProvider;
2109
2110 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2111 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2112 RTEXITCODE_FAILURE);
2113
2114 ComPtr<ICloudProfile> pCloudProfile;
2115
2116 if (pCloudProvider)
2117 {
2118 CHECK_ERROR2_RET(hrc, pCloudProvider,
2119 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2120 RTEXITCODE_FAILURE);
2121 CHECK_ERROR2_RET(hrc, pCloudProfile,
2122 SetProperties(ComSafeArrayAsInParam(names), ComSafeArrayAsInParam(values)),
2123 RTEXITCODE_FAILURE);
2124 }
2125
2126 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2127
2128 RTPrintf("Provider %ls: profile '%ls' was updated.\n",bstrProvider.raw(), bstrProfile.raw());
2129
2130 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2131}
2132
2133/**
2134 * Gets the properties of cloud profile
2135 *
2136 * @returns 0 on success, 1 on failure
2137 */
2138static RTEXITCODE showCloudProfileProperties(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2139{
2140 HRESULT hrc = S_OK;
2141
2142 Bstr bstrProvider(pCommonOpts->pszProviderName);
2143 Bstr bstrProfile(pCommonOpts->pszProfileName);
2144
2145 /* check for required options */
2146 if (bstrProvider.isEmpty())
2147 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --provider is required");
2148 if (bstrProfile.isEmpty())
2149 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --profile is required");
2150
2151 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2152 ComPtr<ICloudProviderManager> pCloudProviderManager;
2153 CHECK_ERROR2_RET(hrc, pVirtualBox,
2154 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2155 RTEXITCODE_FAILURE);
2156 ComPtr<ICloudProvider> pCloudProvider;
2157 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2158 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2159 RTEXITCODE_FAILURE);
2160
2161 ComPtr<ICloudProfile> pCloudProfile;
2162 if (pCloudProvider)
2163 {
2164 CHECK_ERROR2_RET(hrc, pCloudProvider,
2165 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2166 RTEXITCODE_FAILURE);
2167
2168 Bstr bstrProviderID;
2169 pCloudProfile->COMGETTER(ProviderId)(bstrProviderID.asOutParam());
2170 RTPrintf("Provider GUID: %ls\n", bstrProviderID.raw());
2171
2172 com::SafeArray<BSTR> names;
2173 com::SafeArray<BSTR> values;
2174 CHECK_ERROR2_RET(hrc, pCloudProfile,
2175 GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values)),
2176 RTEXITCODE_FAILURE);
2177 size_t cNames = names.size();
2178 size_t cValues = values.size();
2179 bool fFirst = true;
2180 for (size_t k = 0; k < cNames; k++)
2181 {
2182 Bstr value;
2183 if (k < cValues)
2184 value = values[k];
2185 RTPrintf("%s%ls=%ls\n",
2186 fFirst ? "Property: " : " ",
2187 names[k], value.raw());
2188 fFirst = false;
2189 }
2190
2191 RTPrintf("\n");
2192 }
2193
2194 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2195}
2196
2197static RTEXITCODE addCloudProfile(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2198{
2199 HRESULT hrc = S_OK;
2200
2201 Bstr bstrProvider(pCommonOpts->pszProviderName);
2202 Bstr bstrProfile(pCommonOpts->pszProfileName);
2203
2204
2205 /* check for required options */
2206 if (bstrProvider.isEmpty())
2207 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --provider is required");
2208 if (bstrProfile.isEmpty())
2209 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --profile is required");
2210
2211 /*
2212 * Parse options.
2213 */
2214 static const RTGETOPTDEF s_aOptions[] =
2215 {
2216 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2217 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2218 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2219 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2220 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2221 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2222 { "--region", 'r', RTGETOPT_REQ_STRING }
2223 };
2224
2225 RTGETOPTSTATE GetState;
2226 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2227 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2228
2229 com::SafeArray<BSTR> names;
2230 com::SafeArray<BSTR> values;
2231
2232 int c;
2233 RTGETOPTUNION ValueUnion;
2234 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2235 {
2236 switch (c)
2237 {
2238 case 'u': // --clouduser
2239 Bstr("user").detachTo(names.appendedRaw());
2240 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2241 break;
2242 case 'p': // --fingerprint
2243 Bstr("fingerprint").detachTo(names.appendedRaw());
2244 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2245 break;
2246 case 'k': // --keyfile
2247 Bstr("key_file").detachTo(names.appendedRaw());
2248 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2249 break;
2250 case 'P': // --passphrase
2251 Bstr("pass_phrase").detachTo(names.appendedRaw());
2252 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2253 break;
2254 case 't': // --tenancy
2255 Bstr("tenancy").detachTo(names.appendedRaw());
2256 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2257 break;
2258 case 'c': // --compartment
2259 Bstr("compartment").detachTo(names.appendedRaw());
2260 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2261 break;
2262 case 'r': // --region
2263 Bstr("region").detachTo(names.appendedRaw());
2264 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2265 break;
2266 default:
2267 return errorGetOpt(USAGE_CLOUDPROFILE, c, &ValueUnion);
2268 }
2269 }
2270
2271 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2272
2273 ComPtr<ICloudProviderManager> pCloudProviderManager;
2274 CHECK_ERROR2_RET(hrc, pVirtualBox,
2275 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2276 RTEXITCODE_FAILURE);
2277
2278 ComPtr<ICloudProvider> pCloudProvider;
2279 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2280 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2281 RTEXITCODE_FAILURE);
2282
2283 CHECK_ERROR2_RET(hrc, pCloudProvider,
2284 CreateProfile(bstrProfile.raw(),
2285 ComSafeArrayAsInParam(names),
2286 ComSafeArrayAsInParam(values)),
2287 RTEXITCODE_FAILURE);
2288
2289 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2290
2291 RTPrintf("Provider %ls: profile '%ls' was added.\n",bstrProvider.raw(), bstrProfile.raw());
2292
2293 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2294}
2295
2296static RTEXITCODE deleteCloudProfile(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2297{
2298 HRESULT hrc = S_OK;
2299
2300 Bstr bstrProvider(pCommonOpts->pszProviderName);
2301 Bstr bstrProfile(pCommonOpts->pszProfileName);
2302
2303 /* check for required options */
2304 if (bstrProvider.isEmpty())
2305 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --provider is required");
2306 if (bstrProfile.isEmpty())
2307 return errorSyntax(USAGE_CLOUDPROFILE, "Parameter --profile is required");
2308
2309 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2310 ComPtr<ICloudProviderManager> pCloudProviderManager;
2311 CHECK_ERROR2_RET(hrc, pVirtualBox,
2312 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2313 RTEXITCODE_FAILURE);
2314 ComPtr<ICloudProvider> pCloudProvider;
2315 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2316 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2317 RTEXITCODE_FAILURE);
2318
2319 ComPtr<ICloudProfile> pCloudProfile;
2320 if (pCloudProvider)
2321 {
2322 CHECK_ERROR2_RET(hrc, pCloudProvider,
2323 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2324 RTEXITCODE_FAILURE);
2325
2326 CHECK_ERROR2_RET(hrc, pCloudProfile,
2327 Remove(),
2328 RTEXITCODE_FAILURE);
2329
2330 CHECK_ERROR2_RET(hrc, pCloudProvider,
2331 SaveProfiles(),
2332 RTEXITCODE_FAILURE);
2333
2334 RTPrintf("Provider %ls: profile '%ls' was deleted.\n",bstrProvider.raw(), bstrProfile.raw());
2335 }
2336
2337 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2338}
2339
2340RTEXITCODE handleCloudProfile(HandlerArg *a)
2341{
2342 if (a->argc < 1)
2343 return errorNoSubcommand();
2344
2345 static const RTGETOPTDEF s_aOptions[] =
2346 {
2347 /* common options */
2348 { "--provider", 'v', RTGETOPT_REQ_STRING },
2349 { "--profile", 'f', RTGETOPT_REQ_STRING },
2350 /* subcommands */
2351 { "add", 1000, RTGETOPT_REQ_NOTHING },
2352 { "show", 1001, RTGETOPT_REQ_NOTHING },
2353 { "update", 1002, RTGETOPT_REQ_NOTHING },
2354 { "delete", 1003, RTGETOPT_REQ_NOTHING },
2355 };
2356
2357 RTGETOPTSTATE GetState;
2358 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2359 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2360
2361 CLOUDPROFILECOMMONOPT CommonOpts = { NULL, NULL };
2362 int c;
2363 RTGETOPTUNION ValueUnion;
2364 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2365 {
2366 switch (c)
2367 {
2368 case 'v': // --provider
2369 CommonOpts.pszProviderName = ValueUnion.psz;
2370 break;
2371 case 'f': // --profile
2372 CommonOpts.pszProfileName = ValueUnion.psz;
2373 break;
2374 /* Sub-commands: */
2375 case 1000:
2376 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_ADD);
2377 return addCloudProfile(a, GetState.iNext, &CommonOpts);
2378 case 1001:
2379 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_SHOW);
2380 return showCloudProfileProperties(a, &CommonOpts);
2381 case 1002:
2382 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_UPDATE);
2383 return setCloudProfileProperties(a, GetState.iNext, &CommonOpts);
2384 case 1003:
2385 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_DELETE);
2386 return deleteCloudProfile(a, &CommonOpts);
2387 case VINF_GETOPT_NOT_OPTION:
2388 return errorUnknownSubcommand(ValueUnion.psz);
2389
2390 default:
2391 return errorGetOpt(c, &ValueUnion);
2392 }
2393 }
2394
2395 return errorNoSubcommand();
2396}
Note: See TracBrowser for help on using the repository browser.

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