VirtualBox

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

Last change on this file since 93775 was 93708, checked in by vboxsync, 3 years ago

include/iprt/message.h: Max out the available bits for the scope, we need a lot for the VBoxManage controlvm docs.

docs, VBoxManage: Integrate refman (and fix some content) for VBoxManage startvm and VBoxManage controlvm. Needs about 60 scopes (do RT_BIT_64 is a must have), and for protection against exceeding the limit there are now checks (as AssertCompile).

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