VirtualBox

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

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

Main: bugref:8612: Fixed error happened during VM creation with default settings. Also changed storage controller name generation according to bus type

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

© 2023 Oracle
ContactPrivacy policyTerms of Use