VirtualBox

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

Last change on this file since 86671 was 85665, checked in by vboxsync, 4 years ago

VBoxManage: Converted the sharedfolder command to RTGetOpt and refentry help. ticketref:19800

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

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