VirtualBox

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

Last change on this file since 93632 was 93558, checked in by vboxsync, 3 years ago

VBoxManage/unattended: List detected windows images. bugref:9781

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 93.0 KB
Line 
1/* $Id: VBoxManageMisc.cpp 93558 2022-02-02 23:51:45Z 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(USAGE_REGISTERVM, 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(USAGE_UNREGISTERVM, 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(USAGE_UNREGISTERVM, Misc::tr("Invalid option -%c"), c);
135 return errorSyntax(USAGE_UNREGISTERVM, Misc::tr("Invalid option case %i"), c);
136 }
137 if (c == VERR_GETOPT_UNKNOWN_OPTION)
138 return errorSyntax(USAGE_UNREGISTERVM, Misc::tr("unknown option: %s\n"), ValueUnion.psz);
139 if (ValueUnion.pDef)
140 return errorSyntax(USAGE_UNREGISTERVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
141 return errorSyntax(USAGE_UNREGISTERVM, Misc::tr("error: %Rrs"), c);
142 }
143 }
144
145 /* check for required options */
146 if (!VMName)
147 return errorSyntax(USAGE_UNREGISTERVM, 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(USAGE_CREATEVM, c, &ValueUnion);
253 }
254 }
255
256 /* check for required options */
257 if (bstrName.isEmpty())
258 return errorSyntax(USAGE_CREATEVM, 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(USAGE_MOVEVM, Misc::tr("Invalid parameter '%s'"), ValueUnion.psz);
357 break;
358
359 default:
360 return errorGetOpt(USAGE_MOVEVM, 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(USAGE_MOVEVM, 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(USAGE_STARTVM,
675 Misc::tr("Parameter to option --putenv must not contain any newline character"));
676 break;
677
678 case VINF_GETOPT_NOT_OPTION:
679 VMs.push_back(ValueUnion.psz);
680 break;
681
682 default:
683 if (c > 0)
684 {
685 if (RT_C_IS_PRINT(c))
686 return errorSyntax(USAGE_STARTVM, Misc::tr("Invalid option -%c"), c);
687 else
688 return errorSyntax(USAGE_STARTVM, Misc::tr("Invalid option case %i"), c);
689 }
690 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
691 return errorSyntax(USAGE_STARTVM, Misc::tr("unknown option: %s\n"), ValueUnion.psz);
692 else if (ValueUnion.pDef)
693 return errorSyntax(USAGE_STARTVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
694 else
695 return errorSyntax(USAGE_STARTVM, Misc::tr("error: %Rrs"), c);
696 }
697 }
698
699 /* check for required options */
700 if (VMs.empty())
701 return errorSyntax(USAGE_STARTVM, Misc::tr("at least one VM name or uuid required"));
702
703 for (std::list<const char *>::const_iterator it = VMs.begin();
704 it != VMs.end();
705 ++it)
706 {
707 HRESULT rc2 = rc;
708 const char *pszVM = *it;
709 ComPtr<IMachine> machine;
710 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(),
711 machine.asOutParam()));
712 if (machine)
713 {
714 ComPtr<IProgress> progress;
715 CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
716 ComSafeArrayAsInParam(aBstrEnv), progress.asOutParam()));
717 if (SUCCEEDED(rc) && !progress.isNull())
718 {
719 RTPrintf(Misc::tr("Waiting for VM \"%s\" to power on...\n"), pszVM);
720 CHECK_ERROR(progress, WaitForCompletion(-1));
721 if (SUCCEEDED(rc))
722 {
723 BOOL completed = true;
724 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
725 if (SUCCEEDED(rc))
726 {
727 ASSERT(completed);
728
729 LONG iRc;
730 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
731 if (SUCCEEDED(rc))
732 {
733 if (SUCCEEDED(iRc))
734 RTPrintf(Misc::tr("VM \"%s\" has been successfully started.\n"), pszVM);
735 else
736 {
737 ProgressErrorInfo info(progress);
738 com::GluePrintErrorInfo(info);
739 }
740 rc = iRc;
741 }
742 }
743 }
744 }
745 }
746
747 /* it's important to always close sessions */
748 a->session->UnlockMachine();
749
750 /* make sure that we remember the failed state */
751 if (FAILED(rc2))
752 rc = rc2;
753 }
754
755 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
756}
757
758RTEXITCODE handleDiscardState(HandlerArg *a)
759{
760 HRESULT rc;
761
762 if (a->argc != 1)
763 return errorSyntax(USAGE_DISCARDSTATE, Misc::tr("Incorrect number of parameters"));
764
765 ComPtr<IMachine> machine;
766 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
767 machine.asOutParam()));
768 if (machine)
769 {
770 do
771 {
772 /* we have to open a session for this task */
773 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
774 do
775 {
776 ComPtr<IMachine> sessionMachine;
777 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
778 CHECK_ERROR_BREAK(sessionMachine, DiscardSavedState(true /* fDeleteFile */));
779 } while (0);
780 CHECK_ERROR_BREAK(a->session, UnlockMachine());
781 } while (0);
782 }
783
784 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
785}
786
787RTEXITCODE handleAdoptState(HandlerArg *a)
788{
789 HRESULT rc;
790
791 if (a->argc != 2)
792 return errorSyntax(USAGE_ADOPTSTATE, Misc::tr("Incorrect number of parameters"));
793
794 ComPtr<IMachine> machine;
795 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
796 machine.asOutParam()));
797 if (machine)
798 {
799 char szStateFileAbs[RTPATH_MAX] = "";
800 int vrc = RTPathAbs(a->argv[1], szStateFileAbs, sizeof(szStateFileAbs));
801 if (RT_FAILURE(vrc))
802 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Cannot convert filename \"%s\" to absolute path: %Rrc"),
803 a->argv[0], vrc);
804
805 do
806 {
807 /* we have to open a session for this task */
808 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
809 do
810 {
811 ComPtr<IMachine> sessionMachine;
812 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
813 CHECK_ERROR_BREAK(sessionMachine, AdoptSavedState(Bstr(szStateFileAbs).raw()));
814 } while (0);
815 CHECK_ERROR_BREAK(a->session, UnlockMachine());
816 } while (0);
817 }
818
819 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
820}
821
822RTEXITCODE handleGetExtraData(HandlerArg *a)
823{
824 HRESULT rc = S_OK;
825
826 if (a->argc > 2 || a->argc < 1)
827 return errorSyntax(USAGE_GETEXTRADATA, Misc::tr("Incorrect number of parameters"));
828
829 /* global data? */
830 if (!strcmp(a->argv[0], "global"))
831 {
832 /* enumeration? */
833 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
834 {
835 SafeArray<BSTR> aKeys;
836 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
837
838 for (size_t i = 0;
839 i < aKeys.size();
840 ++i)
841 {
842 Bstr bstrKey(aKeys[i]);
843 Bstr bstrValue;
844 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
845 bstrValue.asOutParam()));
846
847 RTPrintf(Misc::tr("Key: %ls, Value: %ls\n"), bstrKey.raw(), bstrValue.raw());
848 }
849 }
850 else
851 {
852 Bstr value;
853 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
854 value.asOutParam()));
855 if (!value.isEmpty())
856 RTPrintf(Misc::tr("Value: %ls\n"), value.raw());
857 else
858 RTPrintf(Misc::tr("No value set!\n"));
859 }
860 }
861 else
862 {
863 ComPtr<IMachine> machine;
864 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
865 machine.asOutParam()));
866 if (machine)
867 {
868 /* enumeration? */
869 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
870 {
871 SafeArray<BSTR> aKeys;
872 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
873
874 for (size_t i = 0;
875 i < aKeys.size();
876 ++i)
877 {
878 Bstr bstrKey(aKeys[i]);
879 Bstr bstrValue;
880 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
881 bstrValue.asOutParam()));
882
883 RTPrintf(Misc::tr("Key: %ls, Value: %ls\n"), bstrKey.raw(), bstrValue.raw());
884 }
885 }
886 else
887 {
888 Bstr value;
889 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
890 value.asOutParam()));
891 if (!value.isEmpty())
892 RTPrintf(Misc::tr("Value: %ls\n"), value.raw());
893 else
894 RTPrintf(Misc::tr("No value set!\n"));
895 }
896 }
897 }
898 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
899}
900
901RTEXITCODE handleSetExtraData(HandlerArg *a)
902{
903 HRESULT rc = S_OK;
904
905 if (a->argc < 2)
906 return errorSyntax(USAGE_SETEXTRADATA, Misc::tr("Not enough parameters"));
907
908 /* global data? */
909 if (!strcmp(a->argv[0], "global"))
910 {
911 /** @todo passing NULL is deprecated */
912 if (a->argc < 3)
913 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
914 NULL));
915 else if (a->argc == 3)
916 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
917 Bstr(a->argv[2]).raw()));
918 else
919 return errorSyntax(USAGE_SETEXTRADATA, Misc::tr("Too many parameters"));
920 }
921 else
922 {
923 ComPtr<IMachine> machine;
924 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
925 machine.asOutParam()));
926 if (machine)
927 {
928 /* open an existing session for the VM */
929 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
930 /* get the session machine */
931 ComPtr<IMachine> sessionMachine;
932 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
933 /** @todo passing NULL is deprecated */
934 if (a->argc < 3)
935 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
936 NULL));
937 else if (a->argc == 3)
938 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
939 Bstr(a->argv[2]).raw()));
940 else
941 return errorSyntax(USAGE_SETEXTRADATA, Misc::tr("Too many parameters"));
942 }
943 }
944 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
945}
946
947RTEXITCODE handleSetProperty(HandlerArg *a)
948{
949 HRESULT rc;
950
951 /* there must be two arguments: property name and value */
952 if (a->argc != 2)
953 return errorSyntax(USAGE_SETPROPERTY, Misc::tr("Incorrect number of parameters"));
954
955 ComPtr<ISystemProperties> systemProperties;
956 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
957
958 if (!strcmp(a->argv[0], "machinefolder"))
959 {
960 /* reset to default? */
961 if (!strcmp(a->argv[1], "default"))
962 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
963 else
964 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
965 }
966 else if (!strcmp(a->argv[0], "hwvirtexclusive"))
967 {
968 bool fHwVirtExclusive;
969
970 if (!strcmp(a->argv[1], "on"))
971 fHwVirtExclusive = true;
972 else if (!strcmp(a->argv[1], "off"))
973 fHwVirtExclusive = false;
974 else
975 return errorArgument(Misc::tr("Invalid hwvirtexclusive argument '%s'"), a->argv[1]);
976 CHECK_ERROR(systemProperties, COMSETTER(ExclusiveHwVirt)(fHwVirtExclusive));
977 }
978 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
979 || !strcmp(a->argv[0], "vrdpauthlibrary"))
980 {
981 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
982 RTStrmPrintf(g_pStdErr, Misc::tr("Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n"));
983
984 /* reset to default? */
985 if (!strcmp(a->argv[1], "default"))
986 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
987 else
988 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
989 }
990 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
991 {
992 /* reset to default? */
993 if (!strcmp(a->argv[1], "default"))
994 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
995 else
996 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
997 }
998 else if (!strcmp(a->argv[0], "vrdeextpack"))
999 {
1000 /* disable? */
1001 if (!strcmp(a->argv[1], "null"))
1002 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
1003 else
1004 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
1005 }
1006 else if (!strcmp(a->argv[0], "loghistorycount"))
1007 {
1008 uint32_t uVal;
1009 int vrc;
1010 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
1011 if (vrc != VINF_SUCCESS)
1012 return errorArgument(Misc::tr("Error parsing Log history count '%s'"), a->argv[1]);
1013 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
1014 }
1015 else if (!strcmp(a->argv[0], "autostartdbpath"))
1016 {
1017 /* disable? */
1018 if (!strcmp(a->argv[1], "null"))
1019 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(NULL));
1020 else
1021 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(Bstr(a->argv[1]).raw()));
1022 }
1023 else if (!strcmp(a->argv[0], "defaultfrontend"))
1024 {
1025 Bstr bstrDefaultFrontend(a->argv[1]);
1026 if (!strcmp(a->argv[1], "default"))
1027 bstrDefaultFrontend.setNull();
1028 CHECK_ERROR(systemProperties, COMSETTER(DefaultFrontend)(bstrDefaultFrontend.raw()));
1029 }
1030 else if (!strcmp(a->argv[0], "logginglevel"))
1031 {
1032 Bstr bstrLoggingLevel(a->argv[1]);
1033 if (!strcmp(a->argv[1], "default"))
1034 bstrLoggingLevel.setNull();
1035 CHECK_ERROR(systemProperties, COMSETTER(LoggingLevel)(bstrLoggingLevel.raw()));
1036 }
1037 else if (!strcmp(a->argv[0], "proxymode"))
1038 {
1039 ProxyMode_T enmProxyMode;
1040 if (!RTStrICmpAscii(a->argv[1], "system"))
1041 enmProxyMode = ProxyMode_System;
1042 else if (!RTStrICmpAscii(a->argv[1], "noproxy"))
1043 enmProxyMode = ProxyMode_NoProxy;
1044 else if (!RTStrICmpAscii(a->argv[1], "manual"))
1045 enmProxyMode = ProxyMode_Manual;
1046 else
1047 return errorArgument(Misc::tr("Unknown proxy mode: '%s'"), a->argv[1]);
1048 CHECK_ERROR(systemProperties, COMSETTER(ProxyMode)(enmProxyMode));
1049 }
1050 else if (!strcmp(a->argv[0], "proxyurl"))
1051 {
1052 Bstr bstrProxyUrl(a->argv[1]);
1053 CHECK_ERROR(systemProperties, COMSETTER(ProxyURL)(bstrProxyUrl.raw()));
1054 }
1055#ifdef VBOX_WITH_MAIN_NLS
1056 else if (!strcmp(a->argv[0], "language"))
1057 {
1058 Bstr bstrLanguage(a->argv[1]);
1059 CHECK_ERROR(systemProperties, COMSETTER(LanguageId)(bstrLanguage.raw()));
1060
1061 /* Kudge alert! Make sure the language change notification is processed,
1062 otherwise it may arrive as (XP)COM shuts down and cause
1063 trouble in debug builds. */
1064# ifdef DEBUG
1065 uint64_t const tsStart = RTTimeNanoTS();
1066# endif
1067 unsigned cMsgs = 0;
1068 int vrc;
1069 while ( RT_SUCCESS(vrc = NativeEventQueue::getMainEventQueue()->processEventQueue(32 /*ms*/))
1070 || vrc == VERR_INTERRUPTED)
1071 cMsgs++;
1072# ifdef DEBUG
1073 RTPrintf("vrc=%Rrc cMsgs=%u nsElapsed=%'RU64\n", vrc, cMsgs, RTTimeNanoTS() - tsStart);
1074# endif
1075 }
1076#endif
1077 else
1078 return errorSyntax(USAGE_SETPROPERTY, Misc::tr("Invalid parameter '%s'"), a->argv[0]);
1079
1080 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1081}
1082
1083/**
1084 * sharedfolder add
1085 */
1086static RTEXITCODE handleSharedFolderAdd(HandlerArg *a)
1087{
1088 /*
1089 * Parse arguments (argv[0] == subcommand).
1090 */
1091 static const RTGETOPTDEF s_aAddOptions[] =
1092 {
1093 { "--name", 'n', RTGETOPT_REQ_STRING },
1094 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
1095 { "--hostpath", 'p', RTGETOPT_REQ_STRING },
1096 { "-hostpath", 'p', RTGETOPT_REQ_STRING }, // deprecated
1097 { "--readonly", 'r', RTGETOPT_REQ_NOTHING },
1098 { "-readonly", 'r', RTGETOPT_REQ_NOTHING }, // deprecated
1099 { "--transient", 't', RTGETOPT_REQ_NOTHING },
1100 { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated
1101 { "--automount", 'a', RTGETOPT_REQ_NOTHING },
1102 { "-automount", 'a', RTGETOPT_REQ_NOTHING }, // deprecated
1103 { "--auto-mount-point", 'm', RTGETOPT_REQ_STRING },
1104 };
1105 const char *pszMachineName = NULL;
1106 const char *pszName = NULL;
1107 const char *pszHostPath = NULL;
1108 bool fTransient = false;
1109 bool fWritable = true;
1110 bool fAutoMount = false;
1111 const char *pszAutoMountPoint = "";
1112
1113 RTGETOPTSTATE GetState;
1114 RTGetOptInit(&GetState, a->argc, a->argv, s_aAddOptions, RT_ELEMENTS(s_aAddOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1115 int c;
1116 RTGETOPTUNION ValueUnion;
1117 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1118 {
1119 switch (c)
1120 {
1121 case 'n':
1122 pszName = ValueUnion.psz;
1123 break;
1124 case 'p':
1125 pszHostPath = ValueUnion.psz;
1126 break;
1127 case 'r':
1128 fWritable = false;
1129 break;
1130 case 't':
1131 fTransient = true;
1132 break;
1133 case 'a':
1134 fAutoMount = true;
1135 break;
1136 case 'm':
1137 pszAutoMountPoint = ValueUnion.psz;
1138 break;
1139 case VINF_GETOPT_NOT_OPTION:
1140 if (pszMachineName)
1141 return errorArgument(Misc::tr("Machine name is given more than once: first '%s', then '%s'"),
1142 pszMachineName, ValueUnion.psz);
1143 pszMachineName = ValueUnion.psz;
1144 break;
1145 default:
1146 return errorGetOpt(c, &ValueUnion);
1147 }
1148 }
1149
1150 if (!pszMachineName)
1151 return errorSyntax(Misc::tr("No machine was specified"));
1152
1153 if (!pszName)
1154 return errorSyntax(Misc::tr("No shared folder name (--name) was given"));
1155 if (strchr(pszName, ' '))
1156 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains space"), pszName);
1157 if (strchr(pszName, '\t'))
1158 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains tabs"), pszName);
1159 if (strchr(pszName, '\n') || strchr(pszName, '\r'))
1160 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains newline"), pszName);
1161
1162 if (!pszHostPath)
1163 return errorSyntax(Misc::tr("No host path (--hostpath) was given"));
1164 char szAbsHostPath[RTPATH_MAX];
1165 int vrc = RTPathAbs(pszHostPath, szAbsHostPath, sizeof(szAbsHostPath));
1166 if (RT_FAILURE(vrc))
1167 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTAbsPath failed on '%s': %Rrc"), pszHostPath, vrc);
1168
1169 /*
1170 * Done parsing, do some work.
1171 */
1172 ComPtr<IMachine> ptrMachine;
1173 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1174 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
1175
1176 HRESULT hrc;
1177 if (fTransient)
1178 {
1179 /* open an existing session for the VM */
1180 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1181
1182 /* get the session machine */
1183 ComPtr<IMachine> ptrSessionMachine;
1184 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1185
1186 /* get the session console */
1187 ComPtr<IConsole> ptrConsole;
1188 CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1189 if (ptrConsole.isNull())
1190 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' is not currently running."), pszMachineName);
1191
1192 CHECK_ERROR2(hrc, ptrConsole, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(),
1193 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1194 a->session->UnlockMachine();
1195 }
1196 else
1197 {
1198 /* open a session for the VM */
1199 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1200
1201 /* get the mutable session machine */
1202 ComPtr<IMachine> ptrSessionMachine;
1203 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1204
1205 CHECK_ERROR2(hrc, ptrSessionMachine, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(),
1206 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1207 if (SUCCEEDED(hrc))
1208 {
1209 CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings());
1210 }
1211
1212 a->session->UnlockMachine();
1213 }
1214
1215 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1216}
1217
1218/**
1219 * sharedfolder remove
1220 */
1221static RTEXITCODE handleSharedFolderRemove(HandlerArg *a)
1222{
1223 /*
1224 * Parse arguments (argv[0] == subcommand).
1225 */
1226 static const RTGETOPTDEF s_aRemoveOptions[] =
1227 {
1228 { "--name", 'n', RTGETOPT_REQ_STRING },
1229 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
1230 { "--transient", 't', RTGETOPT_REQ_NOTHING },
1231 { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated
1232 };
1233 const char *pszMachineName = NULL;
1234 const char *pszName = NULL;
1235 bool fTransient = false;
1236
1237 RTGETOPTSTATE GetState;
1238 RTGetOptInit(&GetState, a->argc, a->argv, s_aRemoveOptions, RT_ELEMENTS(s_aRemoveOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1239 int c;
1240 RTGETOPTUNION ValueUnion;
1241 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1242 {
1243 switch (c)
1244 {
1245 case 'n':
1246 pszName = ValueUnion.psz;
1247 break;
1248 case 't':
1249 fTransient = true;
1250 break;
1251 case VINF_GETOPT_NOT_OPTION:
1252 if (pszMachineName)
1253 return errorArgument(Misc::tr("Machine name is given more than once: first '%s', then '%s'"),
1254 pszMachineName, ValueUnion.psz);
1255 pszMachineName = ValueUnion.psz;
1256 break;
1257 default:
1258 return errorGetOpt(c, &ValueUnion);
1259 }
1260 }
1261
1262 if (!pszMachineName)
1263 return errorSyntax(Misc::tr("No machine was specified"));
1264 if (!pszName)
1265 return errorSyntax(Misc::tr("No shared folder name (--name) was given"));
1266
1267 /*
1268 * Done parsing, do some real work.
1269 */
1270 ComPtr<IMachine> ptrMachine;
1271 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1272 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
1273
1274 HRESULT hrc;
1275 if (fTransient)
1276 {
1277 /* open an existing session for the VM */
1278 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1279 /* get the session machine */
1280 ComPtr<IMachine> ptrSessionMachine;
1281 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1282 /* get the session console */
1283 ComPtr<IConsole> ptrConsole;
1284 CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1285 if (ptrConsole.isNull())
1286 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' is not currently running.\n"), pszMachineName);
1287
1288 CHECK_ERROR2(hrc, ptrConsole, RemoveSharedFolder(Bstr(pszName).raw()));
1289
1290 a->session->UnlockMachine();
1291 }
1292 else
1293 {
1294 /* open a session for the VM */
1295 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1296
1297 /* get the mutable session machine */
1298 ComPtr<IMachine> ptrSessionMachine;
1299 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1300
1301 CHECK_ERROR2(hrc, ptrSessionMachine, RemoveSharedFolder(Bstr(pszName).raw()));
1302
1303 /* commit and close the session */
1304 if (SUCCEEDED(hrc))
1305 {
1306 CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings());
1307 }
1308 a->session->UnlockMachine();
1309 }
1310
1311 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1312}
1313
1314
1315RTEXITCODE handleSharedFolder(HandlerArg *a)
1316{
1317 if (a->argc < 1)
1318 return errorSyntax(Misc::tr("Not enough parameters"));
1319
1320 if (!strcmp(a->argv[0], "add"))
1321 {
1322 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_ADD);
1323 return handleSharedFolderAdd(a);
1324 }
1325
1326 if (!strcmp(a->argv[0], "remove"))
1327 {
1328 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_REMOVE);
1329 return handleSharedFolderRemove(a);
1330 }
1331
1332 return errorUnknownSubcommand(a->argv[0]);
1333}
1334
1335RTEXITCODE handleExtPack(HandlerArg *a)
1336{
1337 if (a->argc < 1)
1338 return errorNoSubcommand();
1339
1340 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1341 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1342
1343 RTGETOPTSTATE GetState;
1344 RTGETOPTUNION ValueUnion;
1345 int ch;
1346 HRESULT hrc = S_OK;
1347
1348 if (!strcmp(a->argv[0], "install"))
1349 {
1350 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
1351 const char *pszName = NULL;
1352 bool fReplace = false;
1353
1354 static const RTGETOPTDEF s_aInstallOptions[] =
1355 {
1356 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1357 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
1358 };
1359
1360 RTCList<RTCString> lstLicenseHashes;
1361 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1362 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1363 {
1364 switch (ch)
1365 {
1366 case 'r':
1367 fReplace = true;
1368 break;
1369
1370 case 'a':
1371 lstLicenseHashes.append(ValueUnion.psz);
1372 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
1373 break;
1374
1375 case VINF_GETOPT_NOT_OPTION:
1376 if (pszName)
1377 return errorSyntax(Misc::tr("Too many extension pack names given to \"extpack uninstall\""));
1378 pszName = ValueUnion.psz;
1379 break;
1380
1381 default:
1382 return errorGetOpt(ch, &ValueUnion);
1383 }
1384 }
1385 if (!pszName)
1386 return errorSyntax(Misc::tr("No extension pack name was given to \"extpack install\""));
1387
1388 char szPath[RTPATH_MAX];
1389 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
1390 if (RT_FAILURE(vrc))
1391 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTPathAbs(%s,,) failed with rc=%Rrc"), pszName, vrc);
1392
1393 Bstr bstrTarball(szPath);
1394 Bstr bstrName;
1395 ComPtr<IExtPackFile> ptrExtPackFile;
1396 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1397 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1398 BOOL fShowLicense = true;
1399 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
1400 if (fShowLicense)
1401 {
1402 Bstr bstrLicense;
1403 CHECK_ERROR2I_RET(ptrExtPackFile,
1404 QueryLicense(Bstr("").raw() /* PreferredLocale */,
1405 Bstr("").raw() /* PreferredLanguage */,
1406 Bstr("txt").raw() /* Format */,
1407 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
1408 Utf8Str strLicense(bstrLicense);
1409 uint8_t abHash[RTSHA256_HASH_SIZE];
1410 char szDigest[RTSHA256_DIGEST_LEN + 1];
1411 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
1412 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
1413 AssertRCStmt(vrc, szDigest[0] = '\0');
1414 if (lstLicenseHashes.contains(szDigest))
1415 RTPrintf(Misc::tr("License accepted.\n"));
1416 else
1417 {
1418 RTPrintf("%s\n", strLicense.c_str());
1419 RTPrintf(Misc::tr("Do you agree to these license terms and conditions (y/n)? "));
1420 ch = RTStrmGetCh(g_pStdIn);
1421 RTPrintf("\n");
1422 if (ch != 'y' && ch != 'Y')
1423 {
1424 RTPrintf(Misc::tr("Installation of \"%ls\" aborted.\n"), bstrName.raw());
1425 return RTEXITCODE_FAILURE;
1426 }
1427 if (szDigest[0])
1428 RTPrintf(Misc::tr("License accepted. For batch installation add\n"
1429 "--accept-license=%s\n"
1430 "to the VBoxManage command line.\n\n"), szDigest);
1431 }
1432 }
1433 ComPtr<IProgress> ptrProgress;
1434 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1435 hrc = showProgress(ptrProgress);
1436 CHECK_PROGRESS_ERROR_RET(ptrProgress, (Misc::tr("Failed to install \"%s\""), szPath), RTEXITCODE_FAILURE);
1437
1438 RTPrintf(Misc::tr("Successfully installed \"%ls\".\n"), bstrName.raw());
1439 }
1440 else if (!strcmp(a->argv[0], "uninstall"))
1441 {
1442 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
1443 const char *pszName = NULL;
1444 bool fForced = false;
1445
1446 static const RTGETOPTDEF s_aUninstallOptions[] =
1447 {
1448 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1449 };
1450
1451 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1452 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1453 {
1454 switch (ch)
1455 {
1456 case 'f':
1457 fForced = true;
1458 break;
1459
1460 case VINF_GETOPT_NOT_OPTION:
1461 if (pszName)
1462 return errorSyntax(Misc::tr("Too many extension pack names given to \"extpack uninstall\""));
1463 pszName = ValueUnion.psz;
1464 break;
1465
1466 default:
1467 return errorGetOpt(ch, &ValueUnion);
1468 }
1469 }
1470 if (!pszName)
1471 return errorSyntax(Misc::tr("No extension pack name was given to \"extpack uninstall\""));
1472
1473 Bstr bstrName(pszName);
1474 ComPtr<IProgress> ptrProgress;
1475 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1476 hrc = showProgress(ptrProgress);
1477 CHECK_PROGRESS_ERROR_RET(ptrProgress, (Misc::tr("Failed to uninstall \"%s\""), pszName), RTEXITCODE_FAILURE);
1478
1479 RTPrintf(Misc::tr("Successfully uninstalled \"%s\".\n"), pszName);
1480 }
1481 else if (!strcmp(a->argv[0], "cleanup"))
1482 {
1483 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
1484 if (a->argc > 1)
1485 return errorTooManyParameters(&a->argv[1]);
1486 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1487 RTPrintf(Misc::tr("Successfully performed extension pack cleanup\n"));
1488 }
1489 else
1490 return errorUnknownSubcommand(a->argv[0]);
1491
1492 return RTEXITCODE_SUCCESS;
1493}
1494
1495RTEXITCODE handleUnattendedDetect(HandlerArg *a)
1496{
1497 HRESULT hrc;
1498
1499 /*
1500 * Options. We work directly on an IUnattended instace while parsing
1501 * the options. This saves a lot of extra clutter.
1502 */
1503 bool fMachineReadable = false;
1504 char szIsoPath[RTPATH_MAX];
1505 szIsoPath[0] = '\0';
1506
1507 /*
1508 * Parse options.
1509 */
1510 static const RTGETOPTDEF s_aOptions[] =
1511 {
1512 { "--iso", 'i', RTGETOPT_REQ_STRING },
1513 { "--machine-readable", 'M', RTGETOPT_REQ_NOTHING },
1514 };
1515
1516 RTGETOPTSTATE GetState;
1517 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1518 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1519
1520 int c;
1521 RTGETOPTUNION ValueUnion;
1522 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1523 {
1524 switch (c)
1525 {
1526 case 'i': // --iso
1527 vrc = RTPathAbs(ValueUnion.psz, szIsoPath, sizeof(szIsoPath));
1528 if (RT_FAILURE(vrc))
1529 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1530 break;
1531
1532 case 'M': // --machine-readable.
1533 fMachineReadable = true;
1534 break;
1535
1536 default:
1537 return errorGetOpt(c, &ValueUnion);
1538 }
1539 }
1540
1541 /*
1542 * Check for required stuff.
1543 */
1544 if (szIsoPath[0] == '\0')
1545 return errorSyntax(Misc::tr("No ISO specified"));
1546
1547 /*
1548 * Do the job.
1549 */
1550 ComPtr<IUnattended> ptrUnattended;
1551 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1552 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szIsoPath).raw()), RTEXITCODE_FAILURE);
1553 CHECK_ERROR2(hrc, ptrUnattended, DetectIsoOS());
1554 RTEXITCODE rcExit = SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1555
1556 /*
1557 * Retrieve the results.
1558 */
1559 Bstr bstrDetectedOSTypeId;
1560 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSTypeId)(bstrDetectedOSTypeId.asOutParam()), RTEXITCODE_FAILURE);
1561 Bstr bstrDetectedVersion;
1562 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSVersion)(bstrDetectedVersion.asOutParam()), RTEXITCODE_FAILURE);
1563 Bstr bstrDetectedFlavor;
1564 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSFlavor)(bstrDetectedFlavor.asOutParam()), RTEXITCODE_FAILURE);
1565 Bstr bstrDetectedLanguages;
1566 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSLanguages)(bstrDetectedLanguages.asOutParam()), RTEXITCODE_FAILURE);
1567 Bstr bstrDetectedHints;
1568 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSHints)(bstrDetectedHints.asOutParam()), RTEXITCODE_FAILURE);
1569 SafeArray<BSTR> aImageNames;
1570 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedImageNames)(ComSafeArrayAsOutParam(aImageNames)), RTEXITCODE_FAILURE);
1571 SafeArray<ULONG> aImageIndices;
1572 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedImageIndices)(ComSafeArrayAsOutParam(aImageIndices)), RTEXITCODE_FAILURE);
1573 Assert(aImageNames.size() == aImageIndices.size());
1574
1575 if (fMachineReadable)
1576 {
1577 outputMachineReadableString("OSTypeId", &bstrDetectedOSTypeId);
1578 outputMachineReadableString("OSVersion", &bstrDetectedVersion);
1579 outputMachineReadableString("OSFlavor", &bstrDetectedFlavor);
1580 outputMachineReadableString("OSLanguages", &bstrDetectedLanguages);
1581 outputMachineReadableString("OSHints", &bstrDetectedHints);
1582 for (size_t i = 0; i < aImageNames.size(); i++)
1583 {
1584 Bstr const bstrName = aImageNames[i];
1585 outputMachineReadableStringWithFmtName(&bstrName, false, "ImageIndex%u", aImageIndices[i]);
1586 }
1587 }
1588 else
1589 {
1590 RTMsgInfo(Misc::tr("Detected '%s' to be:\n"), szIsoPath);
1591 RTPrintf(Misc::tr(" OS TypeId = %ls\n"
1592 " OS Version = %ls\n"
1593 " OS Flavor = %ls\n"
1594 " OS Languages = %ls\n"
1595 " OS Hints = %ls\n"),
1596 bstrDetectedOSTypeId.raw(),
1597 bstrDetectedVersion.raw(),
1598 bstrDetectedFlavor.raw(),
1599 bstrDetectedLanguages.raw(),
1600 bstrDetectedHints.raw());
1601 for (size_t i = 0; i < aImageNames.size(); i++)
1602 RTPrintf(" Image #%-3u = %ls\n", aImageIndices[i], aImageNames[i]);
1603 }
1604
1605 return rcExit;
1606}
1607
1608RTEXITCODE handleUnattendedInstall(HandlerArg *a)
1609{
1610 HRESULT hrc;
1611 char szAbsPath[RTPATH_MAX];
1612
1613 /*
1614 * Options. We work directly on an IUnattended instance while parsing
1615 * the options. This saves a lot of extra clutter.
1616 */
1617 ComPtr<IUnattended> ptrUnattended;
1618 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1619 RTCList<RTCString> arrPackageSelectionAdjustments;
1620 ComPtr<IMachine> ptrMachine;
1621 bool fDryRun = false;
1622 const char *pszSessionType = "none";
1623
1624 /*
1625 * Parse options.
1626 */
1627 static const RTGETOPTDEF s_aOptions[] =
1628 {
1629 { "--iso", 'i', RTGETOPT_REQ_STRING },
1630 { "--user", 'u', RTGETOPT_REQ_STRING },
1631 { "--password", 'p', RTGETOPT_REQ_STRING },
1632 { "--password-file", 'X', RTGETOPT_REQ_STRING },
1633 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
1634 { "--key", 'k', RTGETOPT_REQ_STRING },
1635 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
1636 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
1637 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
1638 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
1639 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
1640 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
1641 { "--locale", 'l', RTGETOPT_REQ_STRING },
1642 { "--country", 'Y', RTGETOPT_REQ_STRING },
1643 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
1644 { "--proxy", 'y', RTGETOPT_REQ_STRING },
1645 { "--hostname", 'H', RTGETOPT_REQ_STRING },
1646 { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
1647 { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
1648 // advance options:
1649 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
1650 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
1651 { "--script-template", 'c', RTGETOPT_REQ_STRING },
1652 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
1653 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
1654 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
1655 { "--language", 'L', RTGETOPT_REQ_STRING },
1656 // start vm related options:
1657 { "--start-vm", 'S', RTGETOPT_REQ_STRING },
1658 /** @todo Add a --wait option too for waiting for the VM to shut down or
1659 * something like that...? */
1660 };
1661
1662 RTGETOPTSTATE GetState;
1663 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1664 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1665
1666 int c;
1667 RTGETOPTUNION ValueUnion;
1668 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1669 {
1670 switch (c)
1671 {
1672 case VINF_GETOPT_NOT_OPTION:
1673 if (ptrMachine.isNotNull())
1674 return errorSyntax(Misc::tr("VM name/UUID given more than once!"));
1675 CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1676 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
1677 break;
1678
1679 case 'i': // --iso
1680 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1681 if (RT_FAILURE(vrc))
1682 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1683 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1684 break;
1685
1686 case 'u': // --user
1687 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1688 break;
1689
1690 case 'p': // --password
1691 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1692 break;
1693
1694 case 'X': // --password-file
1695 {
1696 Utf8Str strPassword;
1697 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
1698 if (rcExit != RTEXITCODE_SUCCESS)
1699 return rcExit;
1700 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
1701 break;
1702 }
1703
1704 case 'U': // --full-user-name
1705 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1706 break;
1707
1708 case 'k': // --key
1709 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1710 break;
1711
1712 case 'A': // --install-additions
1713 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
1714 break;
1715 case 'N': // --no-install-additions
1716 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
1717 break;
1718 case 'a': // --additions-iso
1719 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1720 if (RT_FAILURE(vrc))
1721 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1722 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1723 break;
1724
1725 case 't': // --install-txs
1726 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
1727 break;
1728 case 'T': // --no-install-txs
1729 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
1730 break;
1731 case 'K': // --valiation-kit-iso
1732 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1733 if (RT_FAILURE(vrc))
1734 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1735 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1736 break;
1737
1738 case 'l': // --locale
1739 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1740 break;
1741
1742 case 'Y': // --country
1743 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1744 break;
1745
1746 case 'z': // --time-zone;
1747 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1748 break;
1749
1750 case 'y': // --proxy
1751 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1752 break;
1753
1754 case 'H': // --hostname
1755 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1756 break;
1757
1758 case 's': // --package-selection-adjustment
1759 arrPackageSelectionAdjustments.append(ValueUnion.psz);
1760 break;
1761
1762 case 'D':
1763 fDryRun = true;
1764 break;
1765
1766 case 'x': // --auxiliary-base-path
1767 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1768 if (RT_FAILURE(vrc))
1769 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1770 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1771 break;
1772
1773 case 'm': // --image-index
1774 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
1775 break;
1776
1777 case 'c': // --script-template
1778 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1779 if (RT_FAILURE(vrc))
1780 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1781 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1782 break;
1783
1784 case 'C': // --post-install-script-template
1785 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1786 if (RT_FAILURE(vrc))
1787 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1788 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1789 break;
1790
1791 case 'P': // --post-install-command.
1792 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1793 break;
1794
1795 case 'I': // --extra-install-kernel-parameters
1796 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1797 break;
1798
1799 case 'L': // --language
1800 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1801 break;
1802
1803 case 'S': // --start-vm
1804 pszSessionType = ValueUnion.psz;
1805 break;
1806
1807 default:
1808 return errorGetOpt(c, &ValueUnion);
1809 }
1810 }
1811
1812 /*
1813 * Check for required stuff.
1814 */
1815 if (ptrMachine.isNull())
1816 return errorSyntax(Misc::tr("Missing VM name/UUID"));
1817
1818 /*
1819 * Set accumulative attributes.
1820 */
1821 if (arrPackageSelectionAdjustments.size() == 1)
1822 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
1823 RTEXITCODE_FAILURE);
1824 else if (arrPackageSelectionAdjustments.size() > 1)
1825 {
1826 RTCString strAdjustments;
1827 strAdjustments.join(arrPackageSelectionAdjustments, ";");
1828 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
1829 }
1830
1831 /*
1832 * Get details about the machine so we can display them below.
1833 */
1834 Bstr bstrMachineName;
1835 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
1836 Bstr bstrUuid;
1837 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
1838 BSTR bstrInstalledOS;
1839 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
1840 Utf8Str strInstalledOS(bstrInstalledOS);
1841
1842 /*
1843 * Temporarily lock the machine to check whether it's running or not.
1844 * We take this opportunity to disable the first run wizard.
1845 */
1846 CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1847 {
1848 ComPtr<IConsole> ptrConsole;
1849 CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
1850
1851 if ( ptrConsole.isNull()
1852 && SUCCEEDED(hrc)
1853 && ( RTStrICmp(pszSessionType, "gui") == 0
1854 || RTStrICmp(pszSessionType, "none") == 0))
1855 {
1856 ComPtr<IMachine> ptrSessonMachine;
1857 CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
1858 if (ptrSessonMachine.isNotNull())
1859 {
1860 CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
1861 }
1862 }
1863
1864 a->session->UnlockMachine();
1865 if (FAILED(hrc))
1866 return RTEXITCODE_FAILURE;
1867 if (ptrConsole.isNotNull())
1868 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%ls' is currently running"), bstrMachineName.raw());
1869 }
1870
1871 /*
1872 * Do the work.
1873 */
1874 RTMsgInfo(Misc::tr("%s unattended installation of %s in machine '%ls' (%ls).\n"),
1875 RTStrICmp(pszSessionType, "none") == 0 ? Misc::tr("Preparing") : Misc::tr("Starting"),
1876 strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
1877
1878 CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
1879 if (!fDryRun)
1880 {
1881 CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
1882 CHECK_ERROR2_RET(hrc, ptrUnattended, ReconfigureVM(), RTEXITCODE_FAILURE);
1883 }
1884
1885 /*
1886 * Retrieve and display the parameters actually used.
1887 */
1888 RTMsgInfo(Misc::tr("Using values:\n"));
1889#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
1890 a_Type Value; \
1891 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
1892 if (SUCCEEDED(hrc2)) \
1893 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
1894 else \
1895 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
1896 } while (0)
1897#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
1898 Bstr bstrString; \
1899 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
1900 if (SUCCEEDED(hrc2)) \
1901 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
1902 else \
1903 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
1904 } while (0)
1905
1906 SHOW_STR_ATTR(IsoPath, "isoPath");
1907 SHOW_STR_ATTR(User, "user");
1908 SHOW_STR_ATTR(Password, "password");
1909 SHOW_STR_ATTR(FullUserName, "fullUserName");
1910 SHOW_STR_ATTR(ProductKey, "productKey");
1911 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
1912 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
1913 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
1914 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
1915 SHOW_STR_ATTR(Locale, "locale");
1916 SHOW_STR_ATTR(Country, "country");
1917 SHOW_STR_ATTR(TimeZone, "timeZone");
1918 SHOW_STR_ATTR(Proxy, "proxy");
1919 SHOW_STR_ATTR(Hostname, "hostname");
1920 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
1921 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
1922 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
1923 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
1924 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
1925 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
1926 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
1927 SHOW_STR_ATTR(Language, "language");
1928 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
1929 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
1930 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
1931 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
1932 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
1933 {
1934 ULONG idxImage = 0;
1935 HRESULT hrc2 = ptrUnattended->COMGETTER(ImageIndex)(&idxImage);
1936 if (FAILED(hrc2))
1937 idxImage = 0;
1938 SafeArray<BSTR> aImageNames;
1939 hrc2 = ptrUnattended->COMGETTER(DetectedImageNames)(ComSafeArrayAsOutParam(aImageNames));
1940 if (SUCCEEDED(hrc2))
1941 {
1942 SafeArray<ULONG> aImageIndices;
1943 hrc2 = ptrUnattended->COMGETTER(DetectedImageIndices)(ComSafeArrayAsOutParam(aImageIndices));
1944 if (SUCCEEDED(hrc2))
1945 {
1946 Assert(aImageNames.size() == aImageIndices.size());
1947 for (size_t i = 0; i < aImageNames.size(); i++)
1948 {
1949 char szTmp[64];
1950 RTStrPrintf(szTmp, sizeof(szTmp), "detectedImage[%u]%s", i, idxImage != aImageIndices[i] ? "" : "*");
1951 RTPrintf(" %32s = #%u: %ls\n", szTmp, aImageIndices[i], aImageNames[i]);
1952 }
1953 }
1954 else
1955 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), "detectedImageIndices", hrc2);
1956 }
1957 else
1958 RTPrintf(Misc::tr(" %32 = failed: %Rhrc\n"), "detectedImageNames", hrc2);
1959 }
1960
1961#undef SHOW_STR_ATTR
1962#undef SHOW_ATTR
1963
1964 /* We can drop the IUnatteded object now. */
1965 ptrUnattended.setNull();
1966
1967 /*
1968 * Start the VM if requested.
1969 */
1970 if ( fDryRun
1971 || RTStrICmp(pszSessionType, "none") == 0)
1972 {
1973 if (!fDryRun)
1974 RTMsgInfo(Misc::tr("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n"), bstrMachineName.raw(), bstrUuid.raw());
1975 hrc = S_OK;
1976 }
1977 else
1978 {
1979 com::SafeArray<IN_BSTR> aBstrEnv;
1980#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1981 /* make sure the VM process will start on the same display as VBoxManage */
1982 const char *pszDisplay = RTEnvGet("DISPLAY");
1983 if (pszDisplay)
1984 aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw());
1985 const char *pszXAuth = RTEnvGet("XAUTHORITY");
1986 if (pszXAuth)
1987 aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw());
1988#endif
1989 ComPtr<IProgress> ptrProgress;
1990 CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), ComSafeArrayAsInParam(aBstrEnv), ptrProgress.asOutParam()));
1991 if (SUCCEEDED(hrc) && !ptrProgress.isNull())
1992 {
1993 RTMsgInfo(Misc::tr("Waiting for VM '%ls' to power on...\n"), bstrMachineName.raw());
1994 CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
1995 if (SUCCEEDED(hrc))
1996 {
1997 BOOL fCompleted = true;
1998 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
1999 if (SUCCEEDED(hrc))
2000 {
2001 ASSERT(fCompleted);
2002
2003 LONG iRc;
2004 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
2005 if (SUCCEEDED(hrc))
2006 {
2007 if (SUCCEEDED(iRc))
2008 RTMsgInfo(Misc::tr("VM '%ls' (%ls) has been successfully started.\n"),
2009 bstrMachineName.raw(), bstrUuid.raw());
2010 else
2011 {
2012 ProgressErrorInfo info(ptrProgress);
2013 com::GluePrintErrorInfo(info);
2014 }
2015 hrc = iRc;
2016 }
2017 }
2018 }
2019 }
2020
2021 /*
2022 * Do we wait for the VM to power down?
2023 */
2024 }
2025
2026 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2027}
2028
2029
2030RTEXITCODE handleUnattended(HandlerArg *a)
2031{
2032 /*
2033 * Sub-command switch.
2034 */
2035 if (a->argc < 1)
2036 return errorNoSubcommand();
2037
2038 if (!strcmp(a->argv[0], "detect"))
2039 {
2040 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
2041 return handleUnattendedDetect(a);
2042 }
2043
2044 if (!strcmp(a->argv[0], "install"))
2045 {
2046 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
2047 return handleUnattendedInstall(a);
2048 }
2049
2050 /* Consider some kind of create-vm-and-install-guest-os command. */
2051 return errorUnknownSubcommand(a->argv[0]);
2052}
2053
2054/**
2055 * Common Cloud profile options.
2056 */
2057typedef struct
2058{
2059 const char *pszProviderName;
2060 const char *pszProfileName;
2061} CLOUDPROFILECOMMONOPT;
2062typedef CLOUDPROFILECOMMONOPT *PCLOUDPROFILECOMMONOPT;
2063
2064/**
2065 * Sets the properties of cloud profile
2066 *
2067 * @returns 0 on success, 1 on failure
2068 */
2069
2070static RTEXITCODE setCloudProfileProperties(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2071{
2072
2073 HRESULT hrc = S_OK;
2074
2075 Bstr bstrProvider(pCommonOpts->pszProviderName);
2076 Bstr bstrProfile(pCommonOpts->pszProfileName);
2077
2078 /*
2079 * Parse options.
2080 */
2081 static const RTGETOPTDEF s_aOptions[] =
2082 {
2083 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2084 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2085 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2086 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2087 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2088 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2089 { "--region", 'r', RTGETOPT_REQ_STRING }
2090 };
2091
2092 RTGETOPTSTATE GetState;
2093 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2094 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2095
2096 com::SafeArray<BSTR> names;
2097 com::SafeArray<BSTR> values;
2098
2099 int c;
2100 RTGETOPTUNION ValueUnion;
2101 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2102 {
2103 switch (c)
2104 {
2105 case 'u': // --clouduser
2106 Bstr("user").detachTo(names.appendedRaw());
2107 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2108 break;
2109 case 'p': // --fingerprint
2110 Bstr("fingerprint").detachTo(names.appendedRaw());
2111 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2112 break;
2113 case 'k': // --keyfile
2114 Bstr("key_file").detachTo(names.appendedRaw());
2115 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2116 break;
2117 case 'P': // --passphrase
2118 Bstr("pass_phrase").detachTo(names.appendedRaw());
2119 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2120 break;
2121 case 't': // --tenancy
2122 Bstr("tenancy").detachTo(names.appendedRaw());
2123 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2124 break;
2125 case 'c': // --compartment
2126 Bstr("compartment").detachTo(names.appendedRaw());
2127 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2128 break;
2129 case 'r': // --region
2130 Bstr("region").detachTo(names.appendedRaw());
2131 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2132 break;
2133 default:
2134 return errorGetOpt(USAGE_CLOUDPROFILE, c, &ValueUnion);
2135 }
2136 }
2137
2138 /* check for required options */
2139 if (bstrProvider.isEmpty())
2140 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --provider is required"));
2141 if (bstrProfile.isEmpty())
2142 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --profile is required"));
2143
2144 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2145
2146 ComPtr<ICloudProviderManager> pCloudProviderManager;
2147 CHECK_ERROR2_RET(hrc, pVirtualBox,
2148 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2149 RTEXITCODE_FAILURE);
2150
2151 ComPtr<ICloudProvider> pCloudProvider;
2152
2153 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2154 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2155 RTEXITCODE_FAILURE);
2156
2157 ComPtr<ICloudProfile> pCloudProfile;
2158
2159 if (pCloudProvider)
2160 {
2161 CHECK_ERROR2_RET(hrc, pCloudProvider,
2162 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2163 RTEXITCODE_FAILURE);
2164 CHECK_ERROR2_RET(hrc, pCloudProfile,
2165 SetProperties(ComSafeArrayAsInParam(names), ComSafeArrayAsInParam(values)),
2166 RTEXITCODE_FAILURE);
2167 }
2168
2169 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2170
2171 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was updated.\n"),bstrProvider.raw(), bstrProfile.raw());
2172
2173 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2174}
2175
2176/**
2177 * Gets the properties of cloud profile
2178 *
2179 * @returns 0 on success, 1 on failure
2180 */
2181static RTEXITCODE showCloudProfileProperties(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2182{
2183 HRESULT hrc = S_OK;
2184
2185 Bstr bstrProvider(pCommonOpts->pszProviderName);
2186 Bstr bstrProfile(pCommonOpts->pszProfileName);
2187
2188 /* check for required options */
2189 if (bstrProvider.isEmpty())
2190 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --provider is required"));
2191 if (bstrProfile.isEmpty())
2192 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --profile is required"));
2193
2194 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2195 ComPtr<ICloudProviderManager> pCloudProviderManager;
2196 CHECK_ERROR2_RET(hrc, pVirtualBox,
2197 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2198 RTEXITCODE_FAILURE);
2199 ComPtr<ICloudProvider> pCloudProvider;
2200 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2201 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2202 RTEXITCODE_FAILURE);
2203
2204 ComPtr<ICloudProfile> pCloudProfile;
2205 if (pCloudProvider)
2206 {
2207 CHECK_ERROR2_RET(hrc, pCloudProvider,
2208 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2209 RTEXITCODE_FAILURE);
2210
2211 Bstr bstrProviderID;
2212 pCloudProfile->COMGETTER(ProviderId)(bstrProviderID.asOutParam());
2213 RTPrintf(Misc::tr("Provider GUID: %ls\n"), bstrProviderID.raw());
2214
2215 com::SafeArray<BSTR> names;
2216 com::SafeArray<BSTR> values;
2217 CHECK_ERROR2_RET(hrc, pCloudProfile,
2218 GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values)),
2219 RTEXITCODE_FAILURE);
2220 size_t cNames = names.size();
2221 size_t cValues = values.size();
2222 bool fFirst = true;
2223 for (size_t k = 0; k < cNames; k++)
2224 {
2225 Bstr value;
2226 if (k < cValues)
2227 value = values[k];
2228 RTPrintf("%s%ls=%ls\n",
2229 fFirst ? Misc::tr("Property: ") : " ",
2230 names[k], value.raw());
2231 fFirst = false;
2232 }
2233
2234 RTPrintf("\n");
2235 }
2236
2237 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2238}
2239
2240static RTEXITCODE addCloudProfile(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2241{
2242 HRESULT hrc = S_OK;
2243
2244 Bstr bstrProvider(pCommonOpts->pszProviderName);
2245 Bstr bstrProfile(pCommonOpts->pszProfileName);
2246
2247
2248 /* check for required options */
2249 if (bstrProvider.isEmpty())
2250 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --provider is required"));
2251 if (bstrProfile.isEmpty())
2252 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --profile is required"));
2253
2254 /*
2255 * Parse options.
2256 */
2257 static const RTGETOPTDEF s_aOptions[] =
2258 {
2259 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2260 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2261 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2262 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2263 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2264 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2265 { "--region", 'r', RTGETOPT_REQ_STRING }
2266 };
2267
2268 RTGETOPTSTATE GetState;
2269 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2270 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2271
2272 com::SafeArray<BSTR> names;
2273 com::SafeArray<BSTR> values;
2274
2275 int c;
2276 RTGETOPTUNION ValueUnion;
2277 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2278 {
2279 switch (c)
2280 {
2281 case 'u': // --clouduser
2282 Bstr("user").detachTo(names.appendedRaw());
2283 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2284 break;
2285 case 'p': // --fingerprint
2286 Bstr("fingerprint").detachTo(names.appendedRaw());
2287 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2288 break;
2289 case 'k': // --keyfile
2290 Bstr("key_file").detachTo(names.appendedRaw());
2291 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2292 break;
2293 case 'P': // --passphrase
2294 Bstr("pass_phrase").detachTo(names.appendedRaw());
2295 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2296 break;
2297 case 't': // --tenancy
2298 Bstr("tenancy").detachTo(names.appendedRaw());
2299 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2300 break;
2301 case 'c': // --compartment
2302 Bstr("compartment").detachTo(names.appendedRaw());
2303 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2304 break;
2305 case 'r': // --region
2306 Bstr("region").detachTo(names.appendedRaw());
2307 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2308 break;
2309 default:
2310 return errorGetOpt(USAGE_CLOUDPROFILE, c, &ValueUnion);
2311 }
2312 }
2313
2314 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2315
2316 ComPtr<ICloudProviderManager> pCloudProviderManager;
2317 CHECK_ERROR2_RET(hrc, pVirtualBox,
2318 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2319 RTEXITCODE_FAILURE);
2320
2321 ComPtr<ICloudProvider> pCloudProvider;
2322 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2323 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2324 RTEXITCODE_FAILURE);
2325
2326 CHECK_ERROR2_RET(hrc, pCloudProvider,
2327 CreateProfile(bstrProfile.raw(),
2328 ComSafeArrayAsInParam(names),
2329 ComSafeArrayAsInParam(values)),
2330 RTEXITCODE_FAILURE);
2331
2332 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2333
2334 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was added.\n"),bstrProvider.raw(), bstrProfile.raw());
2335
2336 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2337}
2338
2339static RTEXITCODE deleteCloudProfile(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2340{
2341 HRESULT hrc = S_OK;
2342
2343 Bstr bstrProvider(pCommonOpts->pszProviderName);
2344 Bstr bstrProfile(pCommonOpts->pszProfileName);
2345
2346 /* check for required options */
2347 if (bstrProvider.isEmpty())
2348 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --provider is required"));
2349 if (bstrProfile.isEmpty())
2350 return errorSyntax(USAGE_CLOUDPROFILE, Misc::tr("Parameter --profile is required"));
2351
2352 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2353 ComPtr<ICloudProviderManager> pCloudProviderManager;
2354 CHECK_ERROR2_RET(hrc, pVirtualBox,
2355 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2356 RTEXITCODE_FAILURE);
2357 ComPtr<ICloudProvider> pCloudProvider;
2358 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2359 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2360 RTEXITCODE_FAILURE);
2361
2362 ComPtr<ICloudProfile> pCloudProfile;
2363 if (pCloudProvider)
2364 {
2365 CHECK_ERROR2_RET(hrc, pCloudProvider,
2366 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2367 RTEXITCODE_FAILURE);
2368
2369 CHECK_ERROR2_RET(hrc, pCloudProfile,
2370 Remove(),
2371 RTEXITCODE_FAILURE);
2372
2373 CHECK_ERROR2_RET(hrc, pCloudProvider,
2374 SaveProfiles(),
2375 RTEXITCODE_FAILURE);
2376
2377 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was deleted.\n"), bstrProvider.raw(), bstrProfile.raw());
2378 }
2379
2380 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2381}
2382
2383RTEXITCODE handleCloudProfile(HandlerArg *a)
2384{
2385 if (a->argc < 1)
2386 return errorNoSubcommand();
2387
2388 static const RTGETOPTDEF s_aOptions[] =
2389 {
2390 /* common options */
2391 { "--provider", 'v', RTGETOPT_REQ_STRING },
2392 { "--profile", 'f', RTGETOPT_REQ_STRING },
2393 /* subcommands */
2394 { "add", 1000, RTGETOPT_REQ_NOTHING },
2395 { "show", 1001, RTGETOPT_REQ_NOTHING },
2396 { "update", 1002, RTGETOPT_REQ_NOTHING },
2397 { "delete", 1003, RTGETOPT_REQ_NOTHING },
2398 };
2399
2400 RTGETOPTSTATE GetState;
2401 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2402 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2403
2404 CLOUDPROFILECOMMONOPT CommonOpts = { NULL, NULL };
2405 int c;
2406 RTGETOPTUNION ValueUnion;
2407 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2408 {
2409 switch (c)
2410 {
2411 case 'v': // --provider
2412 CommonOpts.pszProviderName = ValueUnion.psz;
2413 break;
2414 case 'f': // --profile
2415 CommonOpts.pszProfileName = ValueUnion.psz;
2416 break;
2417 /* Sub-commands: */
2418 case 1000:
2419 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_ADD);
2420 return addCloudProfile(a, GetState.iNext, &CommonOpts);
2421 case 1001:
2422 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_SHOW);
2423 return showCloudProfileProperties(a, &CommonOpts);
2424 case 1002:
2425 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_UPDATE);
2426 return setCloudProfileProperties(a, GetState.iNext, &CommonOpts);
2427 case 1003:
2428 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_DELETE);
2429 return deleteCloudProfile(a, &CommonOpts);
2430 case VINF_GETOPT_NOT_OPTION:
2431 return errorUnknownSubcommand(ValueUnion.psz);
2432
2433 default:
2434 return errorGetOpt(c, &ValueUnion);
2435 }
2436 }
2437
2438 return errorNoSubcommand();
2439}
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