VirtualBox

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

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

FE/VBoxManage: spelling fix, thanks Timeless.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use