VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 119.4 KB
RevLine 
[12599]1/* $Id: VBoxManageMisc.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
[1]2/** @file
[12599]3 * VBoxManage - VirtualBox's command-line interface.
[1]4 */
5
6/*
[106061]7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
[1]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[1]26 */
27
28
[57358]29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[94234]32#include <VBox/com/com.h>
33#include <VBox/com/string.h>
34#include <VBox/com/Guid.h>
35#include <VBox/com/array.h>
36#include <VBox/com/ErrorInfo.h>
37#include <VBox/com/errorprint.h>
38#include <VBox/com/VirtualBox.h>
39#include <VBox/com/NativeEventQueue.h>
[1]40
[14619]41#include <iprt/asm.h>
[22562]42#include <iprt/buildconfig.h>
[14619]43#include <iprt/cidr.h>
44#include <iprt/ctype.h>
45#include <iprt/dir.h>
46#include <iprt/env.h>
47#include <iprt/file.h>
[66185]48#include <iprt/sha.h>
[14613]49#include <iprt/initterm.h>
[14619]50#include <iprt/param.h>
51#include <iprt/path.h>
[68055]52#include <iprt/cpp/path.h>
[1]53#include <iprt/stream.h>
54#include <iprt/string.h>
[14613]55#include <iprt/stdarg.h>
[14619]56#include <iprt/thread.h>
[1]57#include <iprt/uuid.h>
[18782]58#include <iprt/getopt.h>
59#include <iprt/ctype.h>
[1]60#include <VBox/version.h>
[14814]61#include <VBox/log.h>
[1]62
63#include "VBoxManage.h"
64
[38191]65#include <list>
66
[1]67using namespace com;
68
[92372]69DECLARE_TRANSLATION_CONTEXT(Misc);
[18396]70
[94660]71static const RTGETOPTDEF g_aRegisterVMOptions[] =
72{
73 { "--password", 'p', RTGETOPT_REQ_STRING },
74};
75
[56118]76RTEXITCODE handleRegisterVM(HandlerArg *a)
[18396]77{
[95140]78 HRESULT hrc;
[1]79
[103411]80 const char *pszVmFile = NULL;
81 const char *pszPasswordFile = NULL;
[1]82
[94660]83 int c;
84 RTGETOPTUNION ValueUnion;
85 RTGETOPTSTATE GetState;
86 // start at 0 because main() has hacked both the argc and argv given to us
87 RTGetOptInit(&GetState, a->argc, a->argv, g_aRegisterVMOptions, RT_ELEMENTS(g_aRegisterVMOptions),
88 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
89 while ((c = RTGetOpt(&GetState, &ValueUnion)))
90 {
91 switch (c)
92 {
93 case 'p': // --password
[103411]94 pszPasswordFile = ValueUnion.psz;
[94660]95 break;
96
97 case VINF_GETOPT_NOT_OPTION:
[103411]98 if (!pszVmFile)
99 pszVmFile = ValueUnion.psz;
[94660]100 else
101 return errorSyntax(Misc::tr("Invalid parameter '%s'"), ValueUnion.psz);
102 break;
103
104 default:
105 if (c > 0)
106 {
107 if (RT_C_IS_PRINT(c))
108 return errorSyntax(Misc::tr("Invalid option -%c"), c);
109 return errorSyntax(Misc::tr("Invalid option case %i"), c);
110 }
111 if (c == VERR_GETOPT_UNKNOWN_OPTION)
112 return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz);
113 if (ValueUnion.pDef)
114 return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c);
115 return errorSyntax(Misc::tr("error: %Rrs"), c);
116 }
117 }
118
119 Utf8Str strPassword;
120
[103411]121 if (pszPasswordFile)
[94660]122 {
[103411]123 if (pszPasswordFile[0] == '-' && pszPasswordFile[1] == '\0')
[94660]124 {
125 /* Get password from console. */
126 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, Misc::tr("Enter password:"));
127 if (rcExit == RTEXITCODE_FAILURE)
128 return rcExit;
129 }
130 else
131 {
[103411]132 RTEXITCODE rcExit = readPasswordFile(pszPasswordFile, &strPassword);
[94660]133 if (rcExit == RTEXITCODE_FAILURE)
[97602]134 return RTMsgErrorExitFailure(Misc::tr("Failed to read password from file"));
[94660]135 }
136 }
137
[1]138 ComPtr<IMachine> machine;
[18819]139 /** @todo Ugly hack to get both the API interpretation of relative paths
140 * and the client's interpretation of relative paths. Remove after the API
141 * has been redesigned. */
[103411]142 hrc = a->virtualBox->OpenMachine(Bstr(pszVmFile).raw(),
[95140]143 Bstr(strPassword).raw(),
144 machine.asOutParam());
[103411]145 if (FAILED(hrc) && !RTPathStartsWithRoot(pszVmFile))
[18819]146 {
147 char szVMFileAbs[RTPATH_MAX] = "";
[103411]148 int vrc = RTPathAbs(pszVmFile, szVMFileAbs, sizeof(szVMFileAbs));
[18819]149 if (RT_FAILURE(vrc))
[97602]150 return RTMsgErrorExitFailure(Misc::tr("Failed to convert \"%s\" to an absolute path: %Rrc"),
[103411]151 pszVmFile, vrc);
[32718]152 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(szVMFileAbs).raw(),
[94660]153 Bstr(strPassword).raw(),
[32718]154 machine.asOutParam()));
[18819]155 }
[95140]156 else if (FAILED(hrc))
[97602]157 com::GlueHandleComError(a->virtualBox,
158 "OpenMachine(Bstr(a->argv[0]).raw(), Bstr(strPassword).raw(), machine.asOutParam()))",
159 hrc, __FILE__, __LINE__);
[95140]160 if (SUCCEEDED(hrc))
[1]161 {
162 ASSERT(machine);
[16052]163 CHECK_ERROR(a->virtualBox, RegisterMachine(machine));
[1]164 }
[95140]165 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[1]166}
167
[18782]168static const RTGETOPTDEF g_aUnregisterVMOptions[] =
169{
[18783]170 { "--delete", 'd', RTGETOPT_REQ_NOTHING },
171 { "-delete", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
[98035]172 { "--delete-all", 'a', RTGETOPT_REQ_NOTHING },
173 { "-delete-all", 'a', RTGETOPT_REQ_NOTHING }, // deprecated
[18782]174};
175
[56118]176RTEXITCODE handleUnregisterVM(HandlerArg *a)
[1]177{
[95140]178 HRESULT hrc;
[18782]179 const char *VMName = NULL;
180 bool fDelete = false;
[98035]181 bool fDeleteAll = false;
[1]182
[18782]183 int c;
184 RTGETOPTUNION ValueUnion;
185 RTGETOPTSTATE GetState;
186 // start at 0 because main() has hacked both the argc and argv given to us
[26517]187 RTGetOptInit(&GetState, a->argc, a->argv, g_aUnregisterVMOptions, RT_ELEMENTS(g_aUnregisterVMOptions),
188 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
[18782]189 while ((c = RTGetOpt(&GetState, &ValueUnion)))
190 {
191 switch (c)
192 {
193 case 'd': // --delete
194 fDelete = true;
195 break;
[1]196
[98035]197 case 'a': // --delete-all
198 fDeleteAll = true;
199 break;
200
[18782]201 case VINF_GETOPT_NOT_OPTION:
202 if (!VMName)
203 VMName = ValueUnion.psz;
204 else
[93706]205 return errorSyntax(Misc::tr("Invalid parameter '%s'"), ValueUnion.psz);
[18782]206 break;
207
208 default:
209 if (c > 0)
210 {
211 if (RT_C_IS_PRINT(c))
[93706]212 return errorSyntax(Misc::tr("Invalid option -%c"), c);
213 return errorSyntax(Misc::tr("Invalid option case %i"), c);
[18782]214 }
[76433]215 if (c == VERR_GETOPT_UNKNOWN_OPTION)
[93706]216 return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz);
[76433]217 if (ValueUnion.pDef)
[93706]218 return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c);
219 return errorSyntax(Misc::tr("error: %Rrs"), c);
[18782]220 }
221 }
222
223 /* check for required options */
224 if (!VMName)
[93706]225 return errorSyntax(Misc::tr("VM name required"));
[18782]226
[1]227 ComPtr<IMachine> machine;
[37567]228 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(VMName).raw(),
229 machine.asOutParam()),
230 RTEXITCODE_FAILURE);
231 SafeIfaceArray<IMedium> aMedia;
[98035]232 CHECK_ERROR_RET(machine, Unregister(fDeleteAll ? CleanupMode_DetachAllReturnHardDisksAndVMRemovable
233 :CleanupMode_DetachAllReturnHardDisksOnly,
[37567]234 ComSafeArrayAsOutParam(aMedia)),
235 RTEXITCODE_FAILURE);
[98035]236 if (fDelete || fDeleteAll)
[1]237 {
[37567]238 ComPtr<IProgress> pProgress;
[45068]239 CHECK_ERROR_RET(machine, DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress.asOutParam()),
[37567]240 RTEXITCODE_FAILURE);
[38525]241
[95140]242 hrc = showProgress(pProgress);
[92372]243 CHECK_PROGRESS_ERROR_RET(pProgress, (Misc::tr("Machine delete failed")), RTEXITCODE_FAILURE);
[1]244 }
[48087]245 else
246 {
247 /* Note that the IMachine::Unregister method will return the medium
248 * reference in a sane order, which means that closing will normally
249 * succeed, unless there is still another machine which uses the
250 * medium. No harm done if we ignore the error. */
251 for (size_t i = 0; i < aMedia.size(); i++)
252 {
253 IMedium *pMedium = aMedia[i];
254 if (pMedium)
[95140]255 hrc = pMedium->Close();
[48087]256 }
[95140]257 hrc = S_OK; /** @todo r=andy Why overwriting the result from closing the medium above? */
[48087]258 }
[37567]259 return RTEXITCODE_SUCCESS;
[1]260}
261
[42129]262static const RTGETOPTDEF g_aCreateVMOptions[] =
263{
[101035]264 { "--name", 'n', RTGETOPT_REQ_STRING },
265 { "-name", 'n', RTGETOPT_REQ_STRING },
266 { "--groups", 'g', RTGETOPT_REQ_STRING },
267 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
268 { "-basefolder", 'p', RTGETOPT_REQ_STRING },
269 { "--ostype", 'o', RTGETOPT_REQ_STRING },
270 { "-ostype", 'o', RTGETOPT_REQ_STRING },
271 { "--uuid", 'u', RTGETOPT_REQ_UUID },
272 { "-uuid", 'u', RTGETOPT_REQ_UUID },
273 { "--register", 'r', RTGETOPT_REQ_NOTHING },
274 { "-register", 'r', RTGETOPT_REQ_NOTHING },
275 { "--default", 'd', RTGETOPT_REQ_NOTHING },
276 { "-default", 'd', RTGETOPT_REQ_NOTHING },
277 { "--cipher", 'c', RTGETOPT_REQ_STRING },
278 { "-cipher", 'c', RTGETOPT_REQ_STRING },
279 { "--password-id", 'i', RTGETOPT_REQ_STRING },
280 { "-password-id", 'i', RTGETOPT_REQ_STRING },
281 { "--password", 'w', RTGETOPT_REQ_STRING },
282 { "-password", 'w', RTGETOPT_REQ_STRING },
283 { "--platform-architecture", 'a', RTGETOPT_REQ_STRING },
284 { "--platform-arch", 'a', RTGETOPT_REQ_STRING }, /* Shorter. */
[42129]285};
286
[56118]287RTEXITCODE handleCreateVM(HandlerArg *a)
[1]288{
[95140]289 HRESULT hrc;
[101035]290 PlatformArchitecture_T platformArch = PlatformArchitecture_None;
[42129]291 Bstr bstrBaseFolder;
292 Bstr bstrName;
293 Bstr bstrOsTypeId;
294 Bstr bstrUuid;
[1]295 bool fRegister = false;
[73740]296 bool fDefault = false;
297 /* TBD. Now not used */
298 Bstr bstrDefaultFlags;
[42176]299 com::SafeArray<BSTR> groups;
[94660]300 Bstr bstrCipher;
301 Bstr bstrPasswordId;
302 const char *pszPassword = NULL;
[1]303
[42129]304 int c;
305 RTGETOPTUNION ValueUnion;
306 RTGETOPTSTATE GetState;
307 // start at 0 because main() has hacked both the argc and argv given to us
308 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateVMOptions, RT_ELEMENTS(g_aCreateVMOptions),
309 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
310 while ((c = RTGetOpt(&GetState, &ValueUnion)))
[1]311 {
[42129]312 switch (c)
[1]313 {
[42129]314 case 'n': // --name
315 bstrName = ValueUnion.psz;
316 break;
317
318 case 'g': // --groups
[42176]319 parseGroups(ValueUnion.psz, &groups);
[42129]320 break;
321
322 case 'p': // --basefolder
323 bstrBaseFolder = ValueUnion.psz;
324 break;
325
326 case 'o': // --ostype
327 bstrOsTypeId = ValueUnion.psz;
328 break;
329
330 case 'u': // --uuid
331 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
332 break;
333
334 case 'r': // --register
335 fRegister = true;
336 break;
337
[73740]338 case 'd': // --default
339 fDefault = true;
340 break;
341
[94660]342 case 'c': // --cipher
343 bstrCipher = ValueUnion.psz;
344 break;
345
346 case 'i': // --password-id
347 bstrPasswordId = ValueUnion.psz;
348 break;
349
350 case 'w': // --password
351 pszPassword = ValueUnion.psz;
352 break;
353
[101035]354 case 'a': // --platform-architecture
355 if (!RTStrICmp(ValueUnion.psz, "x86"))
356 platformArch = PlatformArchitecture_x86;
357 else if (!RTStrICmp(ValueUnion.psz, "arm"))
358 platformArch = PlatformArchitecture_ARM;
359 else
360 return errorArgument(Misc::tr("Invalid --platform-architecture argument '%s'"), ValueUnion.psz);
361 break;
362
[42129]363 default:
[93706]364 return errorGetOpt(c, &ValueUnion);
[1]365 }
366 }
[32701]367
368 /* check for required options */
[42129]369 if (bstrName.isEmpty())
[93706]370 return errorSyntax(Misc::tr("Parameter --name is required"));
[8373]371
[105847]372 /* If the platform architecture is not specified explicitly ... */
373 if (platformArch == PlatformArchitecture_None)
374 {
375 /* ... determine it from the guest OS type, if given. */
376 if (bstrOsTypeId.isNotEmpty())
377 {
378 ComPtr<IGuestOSType> ptrGuestOsType;
379 a->virtualBox->GetGuestOSType(bstrOsTypeId.raw(), ptrGuestOsType.asOutParam());
380 if (ptrGuestOsType.isNull())
381 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Unknown or invalid guest OS type given."));
382 CHECK_ERROR2I_RET(ptrGuestOsType, COMGETTER(PlatformArchitecture)(&platformArch), RTEXITCODE_FAILURE);
383 }
384 else /* use the host's platform type as the VM platform type. */
385 {
386 ComPtr<IHost> host;
387 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE);
388 CHECK_ERROR2I_RET(host, COMGETTER(Architecture)(&platformArch), RTEXITCODE_FAILURE);
389 }
390 }
391
[1]392 do
393 {
[43041]394 Bstr createFlags;
395 if (!bstrUuid.isEmpty())
396 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
[42176]397 Bstr bstrPrimaryGroup;
398 if (groups.size())
399 bstrPrimaryGroup = groups[0];
[33451]400 Bstr bstrSettingsFile;
401 CHECK_ERROR_BREAK(a->virtualBox,
[42129]402 ComposeMachineFilename(bstrName.raw(),
[42176]403 bstrPrimaryGroup.raw(),
[43041]404 createFlags.raw(),
[42129]405 bstrBaseFolder.raw(),
[33451]406 bstrSettingsFile.asOutParam()));
[94660]407 Utf8Str strPassword;
408 if (pszPassword)
409 {
410 if (!RTStrCmp(pszPassword, "-"))
411 {
412 /* Get password from console. */
413 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:");
414 if (rcExit == RTEXITCODE_FAILURE)
415 return rcExit;
416 }
417 else
418 {
419 RTEXITCODE rcExit = readPasswordFile(pszPassword, &strPassword);
420 if (rcExit == RTEXITCODE_FAILURE)
421 {
422 RTMsgError("Failed to read new password from file");
423 return rcExit;
424 }
425 }
426 }
[1]427 ComPtr<IMachine> machine;
[31595]428 CHECK_ERROR_BREAK(a->virtualBox,
[33451]429 CreateMachine(bstrSettingsFile.raw(),
[42129]430 bstrName.raw(),
[101035]431 platformArch,
[42131]432 ComSafeArrayAsInParam(groups),
[42129]433 bstrOsTypeId.raw(),
[43041]434 createFlags.raw(),
[94660]435 bstrCipher.raw(),
436 bstrPasswordId.raw(),
437 Bstr(strPassword).raw(),
[31595]438 machine.asOutParam()));
[1]439
440 CHECK_ERROR_BREAK(machine, SaveSettings());
[73740]441 if (fDefault)
442 {
443 /* ApplyDefaults assumes the machine is already registered */
444 CHECK_ERROR_BREAK(machine, ApplyDefaults(bstrDefaultFlags.raw()));
445 CHECK_ERROR_BREAK(machine, SaveSettings());
446 }
[78296]447 if (fRegister)
448 {
449 CHECK_ERROR_BREAK(a->virtualBox, RegisterMachine(machine));
450 }
451
[19239]452 Bstr uuid;
[1]453 CHECK_ERROR_BREAK(machine, COMGETTER(Id)(uuid.asOutParam()));
[31595]454 Bstr settingsFile;
[1]455 CHECK_ERROR_BREAK(machine, COMGETTER(SettingsFilePath)(settingsFile.asOutParam()));
[92372]456 RTPrintf(Misc::tr("Virtual machine '%ls' is created%s.\n"
457 "UUID: %s\n"
458 "Settings file: '%ls'\n"),
459 bstrName.raw(), fRegister ? Misc::tr(" and registered") : "",
[31539]460 Utf8Str(uuid).c_str(), settingsFile.raw());
[1]461 }
462 while (0);
463
[95140]464 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[1]465}
466
[70582]467static const RTGETOPTDEF g_aMoveVMOptions[] =
468{
469 { "--type", 't', RTGETOPT_REQ_STRING },
470 { "--folder", 'f', RTGETOPT_REQ_STRING },
471};
472
473RTEXITCODE handleMoveVM(HandlerArg *a)
474{
[95140]475 HRESULT hrc;
[70582]476 const char *pszSrcName = NULL;
477 const char *pszType = NULL;
[91131]478 char szTargetFolder[RTPATH_MAX];
[70582]479
480 int c;
481 int vrc = VINF_SUCCESS;
482 RTGETOPTUNION ValueUnion;
483 RTGETOPTSTATE GetState;
484
485 // start at 0 because main() has hacked both the argc and argv given to us
486 RTGetOptInit(&GetState, a->argc, a->argv, g_aMoveVMOptions, RT_ELEMENTS(g_aMoveVMOptions),
487 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
488 while ((c = RTGetOpt(&GetState, &ValueUnion)))
489 {
490 switch (c)
491 {
492 case 't': // --type
493 pszType = ValueUnion.psz;
494 break;
495
496 case 'f': // --target folder
[91131]497 if (ValueUnion.psz && ValueUnion.psz[0] != '\0')
498 {
499 vrc = RTPathAbs(ValueUnion.psz, szTargetFolder, sizeof(szTargetFolder));
500 if (RT_FAILURE(vrc))
[98298]501 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTPathAbs(%s,,) failed with vrc=%Rrc"),
[92372]502 ValueUnion.psz, vrc);
[91131]503 } else {
504 szTargetFolder[0] = '\0';
505 }
[70582]506 break;
507
508 case VINF_GETOPT_NOT_OPTION:
509 if (!pszSrcName)
510 pszSrcName = ValueUnion.psz;
511 else
[93706]512 return errorSyntax(Misc::tr("Invalid parameter '%s'"), ValueUnion.psz);
[70582]513 break;
514
515 default:
[93706]516 return errorGetOpt(c, &ValueUnion);
[70582]517 }
518 }
519
520
[71998]521 if (!pszType)
522 pszType = "basic";
523
[70582]524 /* Check for required options */
525 if (!pszSrcName)
[93706]526 return errorSyntax(Misc::tr("VM name required"));
[70582]527
528 /* Get the machine object */
529 ComPtr<IMachine> srcMachine;
530 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
531 srcMachine.asOutParam()),
532 RTEXITCODE_FAILURE);
533
534 if (srcMachine)
535 {
536 /* Start the moving */
537 ComPtr<IProgress> progress;
538
[71054]539 /* we have to open a session for this task */
540 CHECK_ERROR_RET(srcMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
541 ComPtr<IMachine> sessionMachine;
542
543 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
[91131]544 CHECK_ERROR_RET(sessionMachine,
545 MoveTo(Bstr(szTargetFolder).raw(),
[71054]546 Bstr(pszType).raw(),
[91131]547 progress.asOutParam()),
548 RTEXITCODE_FAILURE);
[95140]549 hrc = showProgress(progress);
[92372]550 CHECK_PROGRESS_ERROR_RET(progress, (Misc::tr("Move VM failed")), RTEXITCODE_FAILURE);
[71054]551
552 sessionMachine.setNull();
553 CHECK_ERROR_RET(a->session, UnlockMachine(), RTEXITCODE_FAILURE);
554
[92372]555 RTPrintf(Misc::tr("Machine has been successfully moved into %s\n"),
556 szTargetFolder[0] != '\0' ? szTargetFolder : Misc::tr("the same location"));
[70582]557 }
558
559 return RTEXITCODE_SUCCESS;
560}
561
[37074]562static const RTGETOPTDEF g_aCloneVMOptions[] =
563{
[37449]564 { "--snapshot", 's', RTGETOPT_REQ_STRING },
[37074]565 { "--name", 'n', RTGETOPT_REQ_STRING },
[42129]566 { "--groups", 'g', RTGETOPT_REQ_STRING },
[37449]567 { "--mode", 'm', RTGETOPT_REQ_STRING },
[37543]568 { "--options", 'o', RTGETOPT_REQ_STRING },
[37074]569 { "--register", 'r', RTGETOPT_REQ_NOTHING },
570 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
[42129]571 { "--uuid", 'u', RTGETOPT_REQ_UUID },
[37074]572};
573
[37449]574static int parseCloneMode(const char *psz, CloneMode_T *pMode)
575{
576 if (!RTStrICmp(psz, "machine"))
577 *pMode = CloneMode_MachineState;
[38099]578 else if (!RTStrICmp(psz, "machineandchildren"))
579 *pMode = CloneMode_MachineAndChildStates;
[37449]580 else if (!RTStrICmp(psz, "all"))
581 *pMode = CloneMode_AllStates;
582 else
583 return VERR_PARSE_ERROR;
584
585 return VINF_SUCCESS;
586}
587
[37543]588static int parseCloneOptions(const char *psz, com::SafeArray<CloneOptions_T> *options)
589{
[98298]590 int vrc = VINF_SUCCESS;
591 while (psz && *psz && RT_SUCCESS(vrc))
[37543]592 {
593 size_t len;
594 const char *pszComma = strchr(psz, ',');
595 if (pszComma)
596 len = pszComma - psz;
597 else
598 len = strlen(psz);
599 if (len > 0)
600 {
601 if (!RTStrNICmp(psz, "KeepAllMACs", len))
602 options->push_back(CloneOptions_KeepAllMACs);
603 else if (!RTStrNICmp(psz, "KeepNATMACs", len))
604 options->push_back(CloneOptions_KeepNATMACs);
[37900]605 else if (!RTStrNICmp(psz, "KeepDiskNames", len))
606 options->push_back(CloneOptions_KeepDiskNames);
[37971]607 else if ( !RTStrNICmp(psz, "Link", len)
608 || !RTStrNICmp(psz, "Linked", len))
609 options->push_back(CloneOptions_Link);
[73664]610 else if ( !RTStrNICmp(psz, "KeepHwUUIDs", len)
611 || !RTStrNICmp(psz, "KeepHwUUID", len))
612 options->push_back(CloneOptions_KeepHwUUIDs);
[37543]613 else
[98298]614 vrc = VERR_PARSE_ERROR;
[37543]615 }
616 if (pszComma)
617 psz += len + 1;
618 else
619 psz += len;
620 }
621
[98298]622 return vrc;
[37543]623}
624
[56118]625RTEXITCODE handleCloneVM(HandlerArg *a)
[37074]626{
[95140]627 HRESULT hrc;
[37543]628 const char *pszSrcName = NULL;
629 const char *pszSnapshotName = NULL;
630 CloneMode_T mode = CloneMode_MachineState;
631 com::SafeArray<CloneOptions_T> options;
632 const char *pszTrgName = NULL;
633 const char *pszTrgBaseFolder = NULL;
634 bool fRegister = false;
[37602]635 Bstr bstrUuid;
[42176]636 com::SafeArray<BSTR> groups;
[37074]637
638 int c;
639 RTGETOPTUNION ValueUnion;
640 RTGETOPTSTATE GetState;
641 // start at 0 because main() has hacked both the argc and argv given to us
642 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneVMOptions, RT_ELEMENTS(g_aCloneVMOptions),
643 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
644 while ((c = RTGetOpt(&GetState, &ValueUnion)))
645 {
646 switch (c)
647 {
[37449]648 case 's': // --snapshot
649 pszSnapshotName = ValueUnion.psz;
650 break;
651
[42129]652 case 'n': // --name
653 pszTrgName = ValueUnion.psz;
654 break;
655
656 case 'g': // --groups
[42176]657 parseGroups(ValueUnion.psz, &groups);
[42129]658 break;
659
660 case 'p': // --basefolder
661 pszTrgBaseFolder = ValueUnion.psz;
662 break;
663
[37449]664 case 'm': // --mode
665 if (RT_FAILURE(parseCloneMode(ValueUnion.psz, &mode)))
[92372]666 return errorArgument(Misc::tr("Invalid clone mode '%s'\n"), ValueUnion.psz);
[37449]667 break;
668
[37543]669 case 'o': // --options
670 if (RT_FAILURE(parseCloneOptions(ValueUnion.psz, &options)))
[92372]671 return errorArgument(Misc::tr("Invalid clone options '%s'\n"), ValueUnion.psz);
[37543]672 break;
673
[37074]674 case 'u': // --uuid
[42129]675 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
[37074]676 break;
677
678 case 'r': // --register
679 fRegister = true;
680 break;
681
682 case VINF_GETOPT_NOT_OPTION:
683 if (!pszSrcName)
684 pszSrcName = ValueUnion.psz;
685 else
[92372]686 return errorSyntax(Misc::tr("Invalid parameter '%s'"), ValueUnion.psz);
[37074]687 break;
688
689 default:
[77887]690 return errorGetOpt(c, &ValueUnion);
[37074]691 }
692 }
693
[37449]694 /* Check for required options */
[37074]695 if (!pszSrcName)
[92372]696 return errorSyntax(Misc::tr("VM name required"));
[37074]697
[37449]698 /* Get the machine object */
[37074]699 ComPtr<IMachine> srcMachine;
700 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
701 srcMachine.asOutParam()),
702 RTEXITCODE_FAILURE);
703
[101035]704 /* Get the platform architecture, to clone a VM which has the same architecture. */
705 ComPtr<IPlatform> platform;
706 CHECK_ERROR_RET(srcMachine, COMGETTER(Platform)(platform.asOutParam()), RTEXITCODE_FAILURE);
707 PlatformArchitecture_T platformArch;
708 CHECK_ERROR_RET(platform, COMGETTER(Architecture)(&platformArch), RTEXITCODE_FAILURE);
709
[37449]710 /* If a snapshot name/uuid was given, get the particular machine of this
711 * snapshot. */
712 if (pszSnapshotName)
713 {
714 ComPtr<ISnapshot> srcSnapshot;
715 CHECK_ERROR_RET(srcMachine, FindSnapshot(Bstr(pszSnapshotName).raw(),
716 srcSnapshot.asOutParam()),
717 RTEXITCODE_FAILURE);
718 CHECK_ERROR_RET(srcSnapshot, COMGETTER(Machine)(srcMachine.asOutParam()),
719 RTEXITCODE_FAILURE);
720 }
721
[37074]722 /* Default name necessary? */
723 if (!pszTrgName)
[92372]724 pszTrgName = RTStrAPrintf2(Misc::tr("%s Clone"), pszSrcName);
[37074]725
[43041]726 Bstr createFlags;
727 if (!bstrUuid.isEmpty())
728 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
[42176]729 Bstr bstrPrimaryGroup;
730 if (groups.size())
731 bstrPrimaryGroup = groups[0];
[37074]732 Bstr bstrSettingsFile;
733 CHECK_ERROR_RET(a->virtualBox,
734 ComposeMachineFilename(Bstr(pszTrgName).raw(),
[42176]735 bstrPrimaryGroup.raw(),
[43041]736 createFlags.raw(),
[37074]737 Bstr(pszTrgBaseFolder).raw(),
738 bstrSettingsFile.asOutParam()),
739 RTEXITCODE_FAILURE);
740
741 ComPtr<IMachine> trgMachine;
742 CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(),
743 Bstr(pszTrgName).raw(),
[101035]744 platformArch,
[42131]745 ComSafeArrayAsInParam(groups),
[37074]746 NULL,
[43041]747 createFlags.raw(),
[94660]748 NULL,
749 NULL,
750 NULL,
[37074]751 trgMachine.asOutParam()),
752 RTEXITCODE_FAILURE);
[37531]753
754 /* Start the cloning */
[37074]755 ComPtr<IProgress> progress;
756 CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine,
[37449]757 mode,
[37531]758 ComSafeArrayAsInParam(options),
[37074]759 progress.asOutParam()),
760 RTEXITCODE_FAILURE);
[95140]761 hrc = showProgress(progress);
[92372]762 CHECK_PROGRESS_ERROR_RET(progress, (Misc::tr("Clone VM failed")), RTEXITCODE_FAILURE);
[37074]763
764 if (fRegister)
765 CHECK_ERROR_RET(a->virtualBox, RegisterMachine(trgMachine), RTEXITCODE_FAILURE);
766
767 Bstr bstrNewName;
768 CHECK_ERROR_RET(trgMachine, COMGETTER(Name)(bstrNewName.asOutParam()), RTEXITCODE_FAILURE);
[92372]769 RTPrintf(Misc::tr("Machine has been successfully cloned as \"%ls\"\n"), bstrNewName.raw());
[37074]770
771 return RTEXITCODE_SUCCESS;
772}
773
[56118]774RTEXITCODE handleStartVM(HandlerArg *a)
[1]775{
[95140]776 HRESULT hrc = S_OK;
[38191]777 std::list<const char *> VMs;
[44948]778 Bstr sessionType;
[80824]779 com::SafeArray<IN_BSTR> aBstrEnv;
[94660]780 const char *pszPassword = NULL;
781 const char *pszPasswordId = NULL;
782 Utf8Str strPassword;
[1]783
[64434]784#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
785 /* make sure the VM process will by default start on the same display as VBoxManage */
786 {
787 const char *pszDisplay = RTEnvGet("DISPLAY");
788 if (pszDisplay)
[80824]789 aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw());
[64434]790 const char *pszXAuth = RTEnvGet("XAUTHORITY");
791 if (pszXAuth)
[80824]792 aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw());
[64434]793 }
794#endif
795
[24903]796 static const RTGETOPTDEF s_aStartVMOptions[] =
797 {
798 { "--type", 't', RTGETOPT_REQ_STRING },
799 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
[64434]800 { "--putenv", 'E', RTGETOPT_REQ_STRING },
[94660]801 { "--password", 'p', RTGETOPT_REQ_STRING },
802 { "--password-id", 'i', RTGETOPT_REQ_STRING }
[24903]803 };
[18782]804 int c;
805 RTGETOPTUNION ValueUnion;
806 RTGETOPTSTATE GetState;
807 // start at 0 because main() has hacked both the argc and argv given to us
[26517]808 RTGetOptInit(&GetState, a->argc, a->argv, s_aStartVMOptions, RT_ELEMENTS(s_aStartVMOptions),
809 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
[18782]810 while ((c = RTGetOpt(&GetState, &ValueUnion)))
811 {
812 switch (c)
813 {
814 case 't': // --type
815 if (!RTStrICmp(ValueUnion.psz, "gui"))
816 {
817 sessionType = "gui";
818 }
819#ifdef VBOX_WITH_VBOXSDL
820 else if (!RTStrICmp(ValueUnion.psz, "sdl"))
821 {
822 sessionType = "sdl";
823 }
824#endif
[20313]825#ifdef VBOX_WITH_HEADLESS
[18782]826 else if (!RTStrICmp(ValueUnion.psz, "capture"))
827 {
828 sessionType = "capture";
829 }
[20313]830 else if (!RTStrICmp(ValueUnion.psz, "headless"))
831 {
832 sessionType = "headless";
833 }
834#endif
[18782]835 else
[36673]836 sessionType = ValueUnion.psz;
[18782]837 break;
[1]838
[64434]839 case 'E': // --putenv
840 if (!RTStrStr(ValueUnion.psz, "\n"))
[80824]841 aBstrEnv.push_back(Bstr(ValueUnion.psz).raw());
[64434]842 else
[93708]843 return errorSyntax(Misc::tr("Parameter to option --putenv must not contain any newline character"));
[64434]844 break;
845
[94660]846 case 'p': // --password
847 pszPassword = ValueUnion.psz;
848 break;
849
850 case 'i': // --password-id
851 pszPasswordId = ValueUnion.psz;
852 break;
853
[18782]854 case VINF_GETOPT_NOT_OPTION:
[38191]855 VMs.push_back(ValueUnion.psz);
[18782]856 break;
857
858 default:
859 if (c > 0)
860 {
861 if (RT_C_IS_PRINT(c))
[93708]862 return errorSyntax(Misc::tr("Invalid option -%c"), c);
[18782]863 else
[93708]864 return errorSyntax(Misc::tr("Invalid option case %i"), c);
[18782]865 }
866 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
[93708]867 return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz);
[18782]868 else if (ValueUnion.pDef)
[93708]869 return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c);
[18782]870 else
[93708]871 return errorSyntax(Misc::tr("error: %Rrs"), c);
[18782]872 }
873 }
874
875 /* check for required options */
[38191]876 if (VMs.empty())
[93708]877 return errorSyntax(Misc::tr("at least one VM name or uuid required"));
[18782]878
[94992]879 if (pszPassword)
[94660]880 {
[94992]881 if (!RTStrCmp(pszPassword, "-"))
[94660]882 {
[94992]883 /* Get password from console. */
884 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:");
885 if (rcExit == RTEXITCODE_FAILURE)
886 return rcExit;
[94660]887 }
[94992]888 else
889 {
890 RTEXITCODE rcExit = readPasswordFile(pszPassword, &strPassword);
891 if (rcExit == RTEXITCODE_FAILURE)
892 {
893 RTMsgError("Failed to read new password from file");
894 return rcExit;
895 }
896 }
[94660]897 }
898
[38191]899 for (std::list<const char *>::const_iterator it = VMs.begin();
900 it != VMs.end();
901 ++it)
[1]902 {
[95140]903 HRESULT hrc2 = hrc;
[38191]904 const char *pszVM = *it;
905 ComPtr<IMachine> machine;
906 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(),
907 machine.asOutParam()));
908 if (machine)
909 {
[94660]910 if (pszPasswordId && strPassword.isNotEmpty())
[38191]911 {
[94660]912 CHECK_ERROR(machine, AddEncryptionPassword(Bstr(pszPasswordId).raw(), Bstr(strPassword).raw()));
[95140]913 if (hrc == VBOX_E_PASSWORD_INCORRECT)
[94660]914 RTMsgError("Password incorrect!");
915 }
[95140]916 if (SUCCEEDED(hrc))
[94660]917 {
918 ComPtr<IProgress> progress;
919 CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
920 ComSafeArrayAsInParam(aBstrEnv), progress.asOutParam()));
[95140]921 if (SUCCEEDED(hrc) && !progress.isNull())
[38191]922 {
[94660]923 RTPrintf("Waiting for VM \"%s\" to power on...\n", pszVM);
924 CHECK_ERROR(progress, WaitForCompletion(-1));
[95140]925 if (SUCCEEDED(hrc))
[38191]926 {
[94660]927 BOOL completed = true;
928 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
[95140]929 if (SUCCEEDED(hrc))
[38191]930 {
[94660]931 ASSERT(completed);
932
933 LONG iRc;
934 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
[95140]935 if (SUCCEEDED(hrc))
[38191]936 {
[94660]937 if (SUCCEEDED(iRc))
938 RTPrintf("VM \"%s\" has been successfully started.\n", pszVM);
939 else
940 {
941 ProgressErrorInfo info(progress);
942 com::GluePrintErrorInfo(info);
943 }
[95140]944 hrc = iRc;
[38191]945 }
946 }
947 }
948 }
[36673]949 }
[1]950 }
[38191]951
952 /* it's important to always close sessions */
953 a->session->UnlockMachine();
954
955 /* make sure that we remember the failed state */
[95140]956 if (FAILED(hrc2))
957 hrc = hrc2;
[1]958 }
959
[95140]960 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[1]961}
962
[94660]963#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
964static const RTGETOPTDEF g_aSetVMEncryptionOptions[] =
965{
966 { "--new-password", 'n', RTGETOPT_REQ_STRING },
967 { "--old-password", 'o', RTGETOPT_REQ_STRING },
968 { "--cipher", 'c', RTGETOPT_REQ_STRING },
969 { "--new-password-id", 'i', RTGETOPT_REQ_STRING },
970 { "--force", 'f', RTGETOPT_REQ_NOTHING},
971};
972
[99775]973static RTEXITCODE handleSetVMEncryption(HandlerArg *a, const char *pszFilenameOrUuid)
[94660]974{
[95140]975 HRESULT hrc;
[94660]976 ComPtr<IMachine> machine;
977 const char *pszPasswordNew = NULL;
978 const char *pszPasswordOld = NULL;
979 const char *pszCipher = NULL;
980 const char *pszNewPasswordId = NULL;
981 BOOL fForce = FALSE;
982 Utf8Str strPasswordNew;
983 Utf8Str strPasswordOld;
984
985 int c;
986 RTGETOPTUNION ValueUnion;
987 RTGETOPTSTATE GetState;
988 // start at 0 because main() has hacked both the argc and argv given to us
989 RTGetOptInit(&GetState, a->argc, a->argv, g_aSetVMEncryptionOptions, RT_ELEMENTS(g_aSetVMEncryptionOptions),
990 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
991 while ((c = RTGetOpt(&GetState, &ValueUnion)))
992 {
993 switch (c)
994 {
995 case 'n': // --new-password
996 pszPasswordNew = ValueUnion.psz;
997 break;
998
999 case 'o': // --old-password
1000 pszPasswordOld = ValueUnion.psz;
1001 break;
1002
1003 case 'c': // --cipher
1004 pszCipher = ValueUnion.psz;
1005 break;
1006
1007 case 'i': // --new-password-id
1008 pszNewPasswordId = ValueUnion.psz;
1009 break;
1010
1011 case 'f': // --force
1012 fForce = TRUE;
1013 break;
1014
1015 default:
1016 if (c > 0)
1017 {
1018 if (RT_C_IS_PRINT(c))
1019 return errorSyntax(Misc::tr("Invalid option -%c"), c);
1020 else
1021 return errorSyntax(Misc::tr("Invalid option case %i"), c);
1022 }
1023 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1024 return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz);
1025 else if (ValueUnion.pDef)
1026 return errorSyntax(Misc::tr("%s: %Rrs"), ValueUnion.pDef->pszLong, c);
1027 else
1028 return errorSyntax(Misc::tr("error: %Rrs"), c);
1029 }
1030 }
1031
1032 if (!pszFilenameOrUuid)
1033 return errorSyntax(Misc::tr("VM name or UUID required"));
1034
1035 if (!pszPasswordNew && !pszPasswordOld)
1036 return errorSyntax(Misc::tr("No password specified"));
1037
1038 if ( (pszPasswordNew && !pszNewPasswordId)
1039 || (!pszPasswordNew && pszNewPasswordId))
1040 return errorSyntax(Misc::tr("A new password must always have a valid identifier set at the same time"));
1041
1042 if (pszPasswordOld)
1043 {
1044 if (!RTStrCmp(pszPasswordOld, "-"))
1045 {
1046 /* Get password from console. */
1047 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordOld, "Enter old password:");
1048 if (rcExit == RTEXITCODE_FAILURE)
1049 return rcExit;
1050 }
1051 else
1052 {
1053 RTEXITCODE rcExit = readPasswordFile(pszPasswordOld, &strPasswordOld);
1054 if (rcExit == RTEXITCODE_FAILURE)
1055 {
1056 RTMsgError("Failed to read old password from file");
1057 return rcExit;
1058 }
1059 }
1060 }
1061 if (pszPasswordNew)
1062 {
1063 if (!RTStrCmp(pszPasswordNew, "-"))
1064 {
1065 /* Get password from console. */
1066 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordNew, "Enter new password:");
1067 if (rcExit == RTEXITCODE_FAILURE)
1068 return rcExit;
1069 }
1070 else
1071 {
1072 RTEXITCODE rcExit = readPasswordFile(pszPasswordNew, &strPasswordNew);
1073 if (rcExit == RTEXITCODE_FAILURE)
1074 {
1075 RTMsgError("Failed to read new password from file");
1076 return rcExit;
1077 }
1078 }
1079 }
1080
1081 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(),
1082 machine.asOutParam()));
1083 if (machine)
1084 {
1085 ComPtr<IProgress> progress;
1086 CHECK_ERROR(machine, ChangeEncryption(Bstr(strPasswordOld).raw(), Bstr(pszCipher).raw(),
1087 Bstr(strPasswordNew).raw(), Bstr(pszNewPasswordId).raw(),
1088 fForce, progress.asOutParam()));
[95140]1089 if (SUCCEEDED(hrc))
1090 hrc = showProgress(progress);
1091 if (FAILED(hrc))
[94660]1092 {
[95140]1093 if (hrc == E_NOTIMPL)
[94660]1094 RTMsgError("Encrypt VM operation is not implemented!");
[95140]1095 else if (hrc == VBOX_E_NOT_SUPPORTED)
[94660]1096 RTMsgError("Encrypt VM operation for this cipher is not implemented yet!");
1097 else if (!progress.isNull())
1098 CHECK_PROGRESS_ERROR(progress, ("Failed to encrypt the VM"));
1099 else
1100 RTMsgError("Failed to encrypt the VM!");
1101 }
1102 }
[95140]1103 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[94660]1104}
1105
[99775]1106static RTEXITCODE handleCheckVMPassword(HandlerArg *a, const char *pszFilenameOrUuid)
[94660]1107{
[95140]1108 HRESULT hrc;
[94660]1109 ComPtr<IMachine> machine;
1110 Utf8Str strPassword;
1111
1112 if (a->argc != 1)
1113 return errorSyntax(Misc::tr("Invalid number of arguments: %d"), a->argc);
1114
1115 if (!RTStrCmp(a->argv[0], "-"))
1116 {
1117 /* Get password from console. */
1118 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:");
1119 if (rcExit == RTEXITCODE_FAILURE)
1120 return rcExit;
1121 }
1122 else
1123 {
1124 RTEXITCODE rcExit = readPasswordFile(a->argv[0], &strPassword);
1125 if (rcExit == RTEXITCODE_FAILURE)
1126 {
1127 RTMsgError("Failed to read password from file");
1128 return rcExit;
1129 }
1130 }
1131
1132 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(),
1133 machine.asOutParam()));
1134 if (machine)
1135 {
1136 CHECK_ERROR(machine, CheckEncryptionPassword(Bstr(strPassword).raw()));
[95140]1137 if (SUCCEEDED(hrc))
[94660]1138 RTPrintf("The given password is correct\n");
1139 }
[95140]1140 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[94660]1141}
1142
1143static const RTGETOPTDEF g_aAddVMOptions[] =
1144{
1145 { "--password", 'p', RTGETOPT_REQ_STRING },
1146 { "--password-id", 'i', RTGETOPT_REQ_STRING }
1147};
1148
[99775]1149static RTEXITCODE handleAddVMPassword(HandlerArg *a, const char *pszFilenameOrUuid)
[94660]1150{
[95140]1151 HRESULT hrc;
[94660]1152 ComPtr<IMachine> machine;
1153 const char *pszPassword = NULL;
1154 const char *pszPasswordId = NULL;
1155 Utf8Str strPassword;
1156
1157 int c;
1158 RTGETOPTUNION ValueUnion;
1159 RTGETOPTSTATE GetState;
1160 // start at 0 because main() has hacked both the argc and argv given to us
1161 RTGetOptInit(&GetState, a->argc, a->argv, g_aAddVMOptions, RT_ELEMENTS(g_aAddVMOptions),
1162 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1163 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1164 {
1165 switch (c)
1166 {
1167 case 'p': // --password
1168 pszPassword = ValueUnion.psz;
1169 break;
1170
1171 case 'i': // --password-id
1172 pszPasswordId = ValueUnion.psz;
1173 break;
1174
1175 default:
1176 if (c > 0)
1177 {
1178 if (RT_C_IS_PRINT(c))
1179 return errorSyntax(Misc::tr("Invalid option -%c"), c);
1180 else
1181 return errorSyntax(Misc::tr("Invalid option case %i"), c);
1182 }
1183 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1184 return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz);
1185 else if (ValueUnion.pDef)
1186 return errorSyntax(Misc::tr("%s: %Rrs"), ValueUnion.pDef->pszLong, c);
1187 else
1188 return errorSyntax(Misc::tr("error: %Rrs"), c);
1189 }
1190 }
1191
1192 if (!pszFilenameOrUuid)
1193 return errorSyntax(Misc::tr("VM name or UUID required"));
1194
1195 if (!pszPassword)
1196 return errorSyntax(Misc::tr("No password specified"));
1197
1198 if (!pszPasswordId)
1199 return errorSyntax(Misc::tr("No password identifier specified"));
1200
1201 if (!RTStrCmp(pszPassword, "-"))
1202 {
1203 /* Get password from console. */
1204 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:");
1205 if (rcExit == RTEXITCODE_FAILURE)
1206 return rcExit;
1207 }
1208 else
1209 {
1210 RTEXITCODE rcExit = readPasswordFile(pszPassword, &strPassword);
1211 if (rcExit == RTEXITCODE_FAILURE)
1212 {
1213 RTMsgError("Failed to read new password from file");
1214 return rcExit;
1215 }
1216 }
1217
1218 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(),
1219 machine.asOutParam()));
1220 if (machine)
1221 {
1222 ComPtr<IProgress> progress;
1223 CHECK_ERROR(machine, AddEncryptionPassword(Bstr(pszPasswordId).raw(), Bstr(strPassword).raw()));
[95140]1224 if (hrc == VBOX_E_PASSWORD_INCORRECT)
[94660]1225 RTMsgError("Password incorrect!");
1226 }
[95140]1227 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[94660]1228}
1229
[99775]1230static RTEXITCODE handleRemoveVMPassword(HandlerArg *a, const char *pszFilenameOrUuid)
[94660]1231{
[95140]1232 HRESULT hrc;
[94660]1233 ComPtr<IMachine> machine;
1234
1235 if (a->argc != 1)
1236 return errorSyntax(Misc::tr("Invalid number of arguments: %d"), a->argc);
1237
1238 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(),
1239 machine.asOutParam()));
1240 if (machine)
1241 {
1242 CHECK_ERROR(machine, RemoveEncryptionPassword(Bstr(a->argv[0]).raw()));
[95140]1243 if (hrc == VBOX_E_INVALID_VM_STATE)
[94660]1244 RTMsgError("The machine is in online or transient state\n");
1245 }
[95140]1246 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[94660]1247}
1248
1249RTEXITCODE handleEncryptVM(HandlerArg *a)
1250{
1251 if (a->argc < 2)
1252 return errorSyntax(Misc::tr("subcommand required"));
1253
1254 HandlerArg handlerArg;
1255 handlerArg.argc = a->argc - 2;
1256 handlerArg.argv = &a->argv[2];
1257 handlerArg.virtualBox = a->virtualBox;
1258 handlerArg.session = a->session;
1259 if (!strcmp(a->argv[1], "setencryption"))
1260 return handleSetVMEncryption(&handlerArg, a->argv[0]);
1261 if (!strcmp(a->argv[1], "checkpassword"))
1262 return handleCheckVMPassword(&handlerArg, a->argv[0]);
1263 if (!strcmp(a->argv[1], "addpassword"))
1264 return handleAddVMPassword(&handlerArg, a->argv[0]);
1265 if (!strcmp(a->argv[1], "removepassword"))
1266 return handleRemoveVMPassword(&handlerArg, a->argv[0]);
1267 return errorSyntax(Misc::tr("unknown subcommand"));
1268}
1269#endif /* !VBOX_WITH_FULL_VM_ENCRYPTION */
1270
[56118]1271RTEXITCODE handleDiscardState(HandlerArg *a)
[1]1272{
[95140]1273 HRESULT hrc;
[1]1274
[16052]1275 if (a->argc != 1)
[94171]1276 return errorSyntax(Misc::tr("Incorrect number of parameters"));
[1]1277
1278 ComPtr<IMachine> machine;
[33294]1279 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1280 machine.asOutParam()));
[1]1281 if (machine)
1282 {
1283 do
1284 {
1285 /* we have to open a session for this task */
[31019]1286 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
[5391]1287 do
1288 {
[55234]1289 ComPtr<IMachine> sessionMachine;
1290 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
1291 CHECK_ERROR_BREAK(sessionMachine, DiscardSavedState(true /* fDeleteFile */));
[23934]1292 } while (0);
[31070]1293 CHECK_ERROR_BREAK(a->session, UnlockMachine());
[23934]1294 } while (0);
[1]1295 }
1296
[95140]1297 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[1]1298}
1299
[56118]1300RTEXITCODE handleAdoptState(HandlerArg *a)
[5391]1301{
[95140]1302 HRESULT hrc;
[5391]1303
[16052]1304 if (a->argc != 2)
[94172]1305 return errorSyntax(Misc::tr("Incorrect number of parameters"));
[5391]1306
1307 ComPtr<IMachine> machine;
[33294]1308 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1309 machine.asOutParam()));
[5391]1310 if (machine)
1311 {
[37896]1312 char szStateFileAbs[RTPATH_MAX] = "";
1313 int vrc = RTPathAbs(a->argv[1], szStateFileAbs, sizeof(szStateFileAbs));
1314 if (RT_FAILURE(vrc))
[92372]1315 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Cannot convert filename \"%s\" to absolute path: %Rrc"),
1316 a->argv[0], vrc);
[37896]1317
[5391]1318 do
1319 {
1320 /* we have to open a session for this task */
[31019]1321 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
[5391]1322 do
1323 {
[55234]1324 ComPtr<IMachine> sessionMachine;
1325 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
1326 CHECK_ERROR_BREAK(sessionMachine, AdoptSavedState(Bstr(szStateFileAbs).raw()));
[23934]1327 } while (0);
[31070]1328 CHECK_ERROR_BREAK(a->session, UnlockMachine());
[23934]1329 } while (0);
[5391]1330 }
1331
[95140]1332 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[5391]1333}
1334
[56118]1335RTEXITCODE handleGetExtraData(HandlerArg *a)
[1]1336{
[95140]1337 HRESULT hrc = S_OK;
[1]1338
[71433]1339 if (a->argc > 2 || a->argc < 1)
[94204]1340 return errorSyntax(Misc::tr("Incorrect number of parameters"));
[8373]1341
[1]1342 /* global data? */
[18782]1343 if (!strcmp(a->argv[0], "global"))
[1]1344 {
1345 /* enumeration? */
[71433]1346 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
[1]1347 {
[22173]1348 SafeArray<BSTR> aKeys;
1349 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
[1]1350
[22173]1351 for (size_t i = 0;
1352 i < aKeys.size();
1353 ++i)
[1]1354 {
[22173]1355 Bstr bstrKey(aKeys[i]);
1356 Bstr bstrValue;
[32718]1357 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
1358 bstrValue.asOutParam()));
[1]1359
[92372]1360 RTPrintf(Misc::tr("Key: %ls, Value: %ls\n"), bstrKey.raw(), bstrValue.raw());
[22173]1361 }
[1]1362 }
1363 else
1364 {
1365 Bstr value;
[32718]1366 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
1367 value.asOutParam()));
[20977]1368 if (!value.isEmpty())
[92372]1369 RTPrintf(Misc::tr("Value: %ls\n"), value.raw());
[1]1370 else
[92372]1371 RTPrintf(Misc::tr("No value set!\n"));
[1]1372 }
1373 }
1374 else
1375 {
1376 ComPtr<IMachine> machine;
[33294]1377 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1378 machine.asOutParam()));
[1]1379 if (machine)
1380 {
1381 /* enumeration? */
[71433]1382 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
[1]1383 {
[22173]1384 SafeArray<BSTR> aKeys;
1385 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
[1]1386
[22173]1387 for (size_t i = 0;
1388 i < aKeys.size();
1389 ++i)
[1]1390 {
[22173]1391 Bstr bstrKey(aKeys[i]);
1392 Bstr bstrValue;
[32718]1393 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
1394 bstrValue.asOutParam()));
[1]1395
[92372]1396 RTPrintf(Misc::tr("Key: %ls, Value: %ls\n"), bstrKey.raw(), bstrValue.raw());
[22173]1397 }
[1]1398 }
1399 else
1400 {
1401 Bstr value;
[32718]1402 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
1403 value.asOutParam()));
[20977]1404 if (!value.isEmpty())
[92372]1405 RTPrintf(Misc::tr("Value: %ls\n"), value.raw());
[1]1406 else
[92372]1407 RTPrintf(Misc::tr("No value set!\n"));
[1]1408 }
1409 }
1410 }
[95140]1411 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[1]1412}
1413
[56118]1414RTEXITCODE handleSetExtraData(HandlerArg *a)
[1]1415{
[95140]1416 HRESULT hrc = S_OK;
[1]1417
[16052]1418 if (a->argc < 2)
[94204]1419 return errorSyntax(Misc::tr("Not enough parameters"));
[8373]1420
[1]1421 /* global data? */
[18782]1422 if (!strcmp(a->argv[0], "global"))
[1]1423 {
[32718]1424 /** @todo passing NULL is deprecated */
[16052]1425 if (a->argc < 3)
[32718]1426 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
1427 NULL));
[16052]1428 else if (a->argc == 3)
[32718]1429 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
1430 Bstr(a->argv[2]).raw()));
[1]1431 else
[94204]1432 return errorSyntax(Misc::tr("Too many parameters"));
[1]1433 }
1434 else
1435 {
1436 ComPtr<IMachine> machine;
[33294]1437 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1438 machine.asOutParam()));
[1]1439 if (machine)
1440 {
[54791]1441 /* open an existing session for the VM */
[56118]1442 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
[54791]1443 /* get the session machine */
[55234]1444 ComPtr<IMachine> sessionMachine;
[56118]1445 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
[32718]1446 /** @todo passing NULL is deprecated */
[16052]1447 if (a->argc < 3)
[55234]1448 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
1449 NULL));
[16052]1450 else if (a->argc == 3)
[55234]1451 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
1452 Bstr(a->argv[2]).raw()));
[1]1453 else
[94204]1454 return errorSyntax(Misc::tr("Too many parameters"));
[1]1455 }
1456 }
[95140]1457 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[1]1458}
1459
[101035]1460RTEXITCODE handleSetProperty(HandlerArg *a) /** @todo r=andy Rename this to handle[Get|Set]SystemProperty? */
[1]1461{
[95140]1462 HRESULT hrc;
[1]1463
1464 /* there must be two arguments: property name and value */
[16052]1465 if (a->argc != 2)
[94206]1466 return errorSyntax(Misc::tr("Incorrect number of parameters"));
[8373]1467
[1]1468 ComPtr<ISystemProperties> systemProperties;
[16052]1469 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
[1]1470
[101035]1471 ComPtr<IPlatformProperties> platformProperties;
1472 systemProperties->COMGETTER(Platform)(platformProperties.asOutParam());
1473
[33238]1474 if (!strcmp(a->argv[0], "machinefolder"))
[1]1475 {
1476 /* reset to default? */
[18782]1477 if (!strcmp(a->argv[1], "default"))
[1]1478 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
1479 else
[32718]1480 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
[1]1481 }
[47991]1482 else if (!strcmp(a->argv[0], "hwvirtexclusive"))
1483 {
1484 bool fHwVirtExclusive;
1485
1486 if (!strcmp(a->argv[1], "on"))
1487 fHwVirtExclusive = true;
1488 else if (!strcmp(a->argv[1], "off"))
1489 fHwVirtExclusive = false;
1490 else
[92372]1491 return errorArgument(Misc::tr("Invalid hwvirtexclusive argument '%s'"), a->argv[1]);
[101035]1492 CHECK_ERROR(platformProperties, COMSETTER(ExclusiveHwVirt)(fHwVirtExclusive));
[47991]1493 }
[33386]1494 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
1495 || !strcmp(a->argv[0], "vrdpauthlibrary"))
[1]1496 {
[33386]1497 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
[92372]1498 RTStrmPrintf(g_pStdErr, Misc::tr("Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n"));
[33386]1499
[1]1500 /* reset to default? */
[18782]1501 if (!strcmp(a->argv[1], "default"))
[33386]1502 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
[1]1503 else
[33386]1504 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
[1]1505 }
[18782]1506 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
[5771]1507 {
1508 /* reset to default? */
[18782]1509 if (!strcmp(a->argv[1], "default"))
[5771]1510 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
1511 else
[32718]1512 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
[5771]1513 }
[34244]1514 else if (!strcmp(a->argv[0], "vrdeextpack"))
[33386]1515 {
1516 /* disable? */
1517 if (!strcmp(a->argv[1], "null"))
[34244]1518 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
[33386]1519 else
[34244]1520 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
[33386]1521 }
[18782]1522 else if (!strcmp(a->argv[0], "loghistorycount"))
[5150]1523 {
1524 uint32_t uVal;
1525 int vrc;
[16052]1526 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
[5150]1527 if (vrc != VINF_SUCCESS)
[92372]1528 return errorArgument(Misc::tr("Error parsing Log history count '%s'"), a->argv[1]);
[5150]1529 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
1530 }
[42178]1531 else if (!strcmp(a->argv[0], "autostartdbpath"))
1532 {
1533 /* disable? */
1534 if (!strcmp(a->argv[1], "null"))
1535 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(NULL));
1536 else
1537 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(Bstr(a->argv[1]).raw()));
1538 }
[44948]1539 else if (!strcmp(a->argv[0], "defaultfrontend"))
1540 {
1541 Bstr bstrDefaultFrontend(a->argv[1]);
1542 if (!strcmp(a->argv[1], "default"))
1543 bstrDefaultFrontend.setNull();
1544 CHECK_ERROR(systemProperties, COMSETTER(DefaultFrontend)(bstrDefaultFrontend.raw()));
1545 }
[55843]1546 else if (!strcmp(a->argv[0], "logginglevel"))
1547 {
1548 Bstr bstrLoggingLevel(a->argv[1]);
1549 if (!strcmp(a->argv[1], "default"))
1550 bstrLoggingLevel.setNull();
1551 CHECK_ERROR(systemProperties, COMSETTER(LoggingLevel)(bstrLoggingLevel.raw()));
1552 }
[74431]1553 else if (!strcmp(a->argv[0], "proxymode"))
1554 {
1555 ProxyMode_T enmProxyMode;
1556 if (!RTStrICmpAscii(a->argv[1], "system"))
1557 enmProxyMode = ProxyMode_System;
1558 else if (!RTStrICmpAscii(a->argv[1], "noproxy"))
1559 enmProxyMode = ProxyMode_NoProxy;
1560 else if (!RTStrICmpAscii(a->argv[1], "manual"))
1561 enmProxyMode = ProxyMode_Manual;
1562 else
[92372]1563 return errorArgument(Misc::tr("Unknown proxy mode: '%s'"), a->argv[1]);
[74431]1564 CHECK_ERROR(systemProperties, COMSETTER(ProxyMode)(enmProxyMode));
1565 }
1566 else if (!strcmp(a->argv[0], "proxyurl"))
1567 {
1568 Bstr bstrProxyUrl(a->argv[1]);
1569 CHECK_ERROR(systemProperties, COMSETTER(ProxyURL)(bstrProxyUrl.raw()));
1570 }
[90828]1571#ifdef VBOX_WITH_MAIN_NLS
1572 else if (!strcmp(a->argv[0], "language"))
1573 {
1574 Bstr bstrLanguage(a->argv[1]);
1575 CHECK_ERROR(systemProperties, COMSETTER(LanguageId)(bstrLanguage.raw()));
[92143]1576
1577 /* Kudge alert! Make sure the language change notification is processed,
1578 otherwise it may arrive as (XP)COM shuts down and cause
1579 trouble in debug builds. */
1580# ifdef DEBUG
1581 uint64_t const tsStart = RTTimeNanoTS();
1582# endif
1583 unsigned cMsgs = 0;
1584 int vrc;
1585 while ( RT_SUCCESS(vrc = NativeEventQueue::getMainEventQueue()->processEventQueue(32 /*ms*/))
1586 || vrc == VERR_INTERRUPTED)
1587 cMsgs++;
1588# ifdef DEBUG
1589 RTPrintf("vrc=%Rrc cMsgs=%u nsElapsed=%'RU64\n", vrc, cMsgs, RTTimeNanoTS() - tsStart);
1590# endif
[90828]1591 }
1592#endif
[1]1593 else
[94206]1594 return errorSyntax(Misc::tr("Invalid parameter '%s'"), a->argv[0]);
[1]1595
[95140]1596 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[1]1597}
1598
[85665]1599/**
1600 * sharedfolder add
1601 */
1602static RTEXITCODE handleSharedFolderAdd(HandlerArg *a)
[1]1603{
[85665]1604 /*
1605 * Parse arguments (argv[0] == subcommand).
1606 */
1607 static const RTGETOPTDEF s_aAddOptions[] =
1608 {
1609 { "--name", 'n', RTGETOPT_REQ_STRING },
1610 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
1611 { "--hostpath", 'p', RTGETOPT_REQ_STRING },
1612 { "-hostpath", 'p', RTGETOPT_REQ_STRING }, // deprecated
1613 { "--readonly", 'r', RTGETOPT_REQ_NOTHING },
1614 { "-readonly", 'r', RTGETOPT_REQ_NOTHING }, // deprecated
1615 { "--transient", 't', RTGETOPT_REQ_NOTHING },
1616 { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated
1617 { "--automount", 'a', RTGETOPT_REQ_NOTHING },
1618 { "-automount", 'a', RTGETOPT_REQ_NOTHING }, // deprecated
1619 { "--auto-mount-point", 'm', RTGETOPT_REQ_STRING },
1620 };
1621 const char *pszMachineName = NULL;
1622 const char *pszName = NULL;
1623 const char *pszHostPath = NULL;
1624 bool fTransient = false;
1625 bool fWritable = true;
1626 bool fAutoMount = false;
1627 const char *pszAutoMountPoint = "";
[1]1628
[85665]1629 RTGETOPTSTATE GetState;
1630 RTGetOptInit(&GetState, a->argc, a->argv, s_aAddOptions, RT_ELEMENTS(s_aAddOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1631 int c;
1632 RTGETOPTUNION ValueUnion;
1633 while ((c = RTGetOpt(&GetState, &ValueUnion)))
[1]1634 {
[85665]1635 switch (c)
[1]1636 {
[85665]1637 case 'n':
1638 pszName = ValueUnion.psz;
1639 break;
1640 case 'p':
1641 pszHostPath = ValueUnion.psz;
1642 break;
1643 case 'r':
[6384]1644 fWritable = false;
[85665]1645 break;
1646 case 't':
[1]1647 fTransient = true;
[85665]1648 break;
1649 case 'a':
[31002]1650 fAutoMount = true;
[85665]1651 break;
1652 case 'm':
1653 pszAutoMountPoint = ValueUnion.psz;
1654 break;
1655 case VINF_GETOPT_NOT_OPTION:
1656 if (pszMachineName)
[105087]1657 return errorArgument(Misc::tr("Machine name given more than once: first '%s', then '%s'"),
[85665]1658 pszMachineName, ValueUnion.psz);
1659 pszMachineName = ValueUnion.psz;
1660 break;
1661 default:
1662 return errorGetOpt(c, &ValueUnion);
[1]1663 }
[85665]1664 }
[1]1665
[85665]1666 if (!pszMachineName)
[92372]1667 return errorSyntax(Misc::tr("No machine was specified"));
[9551]1668
[85665]1669 if (!pszName)
[92372]1670 return errorSyntax(Misc::tr("No shared folder name (--name) was given"));
[85665]1671 if (strchr(pszName, ' '))
[92372]1672 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains space"), pszName);
[85665]1673 if (strchr(pszName, '\t'))
[92372]1674 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains tabs"), pszName);
[85665]1675 if (strchr(pszName, '\n') || strchr(pszName, '\r'))
[92372]1676 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains newline"), pszName);
[1]1677
[85665]1678 if (!pszHostPath)
[92372]1679 return errorSyntax(Misc::tr("No host path (--hostpath) was given"));
[85665]1680 char szAbsHostPath[RTPATH_MAX];
1681 int vrc = RTPathAbs(pszHostPath, szAbsHostPath, sizeof(szAbsHostPath));
1682 if (RT_FAILURE(vrc))
[92372]1683 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTAbsPath failed on '%s': %Rrc"), pszHostPath, vrc);
[1]1684
[85665]1685 /*
1686 * Done parsing, do some work.
1687 */
1688 ComPtr<IMachine> ptrMachine;
1689 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1690 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
[55234]1691
[85665]1692 HRESULT hrc;
1693 if (fTransient)
1694 {
1695 /* open an existing session for the VM */
1696 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
[55234]1697
[85665]1698 /* get the session machine */
1699 ComPtr<IMachine> ptrSessionMachine;
1700 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
[1]1701
[85665]1702 /* get the session console */
1703 ComPtr<IConsole> ptrConsole;
1704 CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1705 if (ptrConsole.isNull())
[92372]1706 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' is not currently running."), pszMachineName);
[1]1707
[85665]1708 CHECK_ERROR2(hrc, ptrConsole, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(),
1709 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1710 a->session->UnlockMachine();
1711 }
1712 else
1713 {
1714 /* open a session for the VM */
1715 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
[1]1716
[85665]1717 /* get the mutable session machine */
1718 ComPtr<IMachine> ptrSessionMachine;
1719 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
[1026]1720
[85665]1721 CHECK_ERROR2(hrc, ptrSessionMachine, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(),
1722 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1723 if (SUCCEEDED(hrc))
1724 {
1725 CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings());
[1]1726 }
[85665]1727
1728 a->session->UnlockMachine();
[1]1729 }
[85665]1730
1731 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1732}
1733
1734/**
1735 * sharedfolder remove
1736 */
1737static RTEXITCODE handleSharedFolderRemove(HandlerArg *a)
1738{
1739 /*
1740 * Parse arguments (argv[0] == subcommand).
1741 */
1742 static const RTGETOPTDEF s_aRemoveOptions[] =
[1]1743 {
[85665]1744 { "--name", 'n', RTGETOPT_REQ_STRING },
1745 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
1746 { "--transient", 't', RTGETOPT_REQ_NOTHING },
1747 { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated
1748 };
1749 const char *pszMachineName = NULL;
1750 const char *pszName = NULL;
1751 bool fTransient = false;
[1]1752
[85665]1753 RTGETOPTSTATE GetState;
1754 RTGetOptInit(&GetState, a->argc, a->argv, s_aRemoveOptions, RT_ELEMENTS(s_aRemoveOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1755 int c;
1756 RTGETOPTUNION ValueUnion;
1757 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1758 {
1759 switch (c)
[1]1760 {
[85665]1761 case 'n':
1762 pszName = ValueUnion.psz;
1763 break;
1764 case 't':
[1]1765 fTransient = true;
[85665]1766 break;
1767 case VINF_GETOPT_NOT_OPTION:
1768 if (pszMachineName)
[105087]1769 return errorArgument(Misc::tr("Machine name given more than once: first '%s', then '%s'"),
[85665]1770 pszMachineName, ValueUnion.psz);
1771 pszMachineName = ValueUnion.psz;
1772 break;
1773 default:
1774 return errorGetOpt(c, &ValueUnion);
[1]1775 }
[85665]1776 }
[1]1777
[85665]1778 if (!pszMachineName)
[92372]1779 return errorSyntax(Misc::tr("No machine was specified"));
[85665]1780 if (!pszName)
[92372]1781 return errorSyntax(Misc::tr("No shared folder name (--name) was given"));
[1]1782
[85665]1783 /*
1784 * Done parsing, do some real work.
1785 */
1786 ComPtr<IMachine> ptrMachine;
1787 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1788 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
[1]1789
[85665]1790 HRESULT hrc;
1791 if (fTransient)
1792 {
1793 /* open an existing session for the VM */
1794 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1795 /* get the session machine */
1796 ComPtr<IMachine> ptrSessionMachine;
1797 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1798 /* get the session console */
1799 ComPtr<IConsole> ptrConsole;
1800 CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1801 if (ptrConsole.isNull())
[92372]1802 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' is not currently running.\n"), pszMachineName);
[1]1803
[85665]1804 CHECK_ERROR2(hrc, ptrConsole, RemoveSharedFolder(Bstr(pszName).raw()));
[1]1805
[85665]1806 a->session->UnlockMachine();
1807 }
1808 else
1809 {
1810 /* open a session for the VM */
1811 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
[1]1812
[85665]1813 /* get the mutable session machine */
1814 ComPtr<IMachine> ptrSessionMachine;
1815 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
[1]1816
[85665]1817 CHECK_ERROR2(hrc, ptrSessionMachine, RemoveSharedFolder(Bstr(pszName).raw()));
1818
1819 /* commit and close the session */
1820 if (SUCCEEDED(hrc))
1821 {
1822 CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings());
[1]1823 }
[85665]1824 a->session->UnlockMachine();
[1]1825 }
[8373]1826
[85665]1827 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[1]1828}
1829
[105087]1830static SymlinkPolicy_T nameToSymlinkPolicy(const char *pszName)
1831{
1832 if (!RTStrICmp(pszName, "forbidden"))
1833 return SymlinkPolicy_Forbidden;
1834 if (!RTStrICmp(pszName, "subtree"))
1835 return SymlinkPolicy_AllowedInShareSubtree;
1836 if (!RTStrICmp(pszName, "relative"))
1837 return SymlinkPolicy_AllowedToRelativeTargets;
1838 if (!RTStrICmp(pszName, "any"))
1839 return SymlinkPolicy_AllowedToAnyTarget;
[85665]1840
[105087]1841 return SymlinkPolicy_None;
1842}
1843
1844/**
1845 * modify shared folder properties
1846 */
1847static RTEXITCODE handleSharedFolderModify(HandlerArg *a)
1848{
1849 /*
1850 * Parse arguments (argv[0] == subcommand).
1851 */
1852 static const RTGETOPTDEF s_aModifyOptions[] =
1853 {
1854 { "--name", 'n', RTGETOPT_REQ_STRING },
1855 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
[105333]1856 { "--automount", 'a', RTGETOPT_REQ_BOOL },
1857 { "-automount", 'a', RTGETOPT_REQ_BOOL }, // deprecated
1858 { "--readonly", 'r', RTGETOPT_REQ_BOOL },
1859 { "-readonly", 'r', RTGETOPT_REQ_BOOL }, // deprecated
[105087]1860 { "--symlink-policy", 's', RTGETOPT_REQ_STRING },
1861 { "-symlink-policy", 's', RTGETOPT_REQ_STRING }, // deprecated
[105333]1862 { "--auto-mount-point", 'm', RTGETOPT_REQ_STRING },
1863 { "-auto-mount-point", 'm', RTGETOPT_REQ_STRING }, // deprecated
[105087]1864 };
1865 const char *pszMachineName = NULL;
1866 const char *pszName = NULL;
[105333]1867 int fWritable = -1;
1868 int fAutoMount = -1;
1869 const char *pszAutoMountPoint = NULL;
[105087]1870 SymlinkPolicy_T enmSymlinkPolicy = SymlinkPolicy_None;
1871
1872 RTGETOPTSTATE GetState;
1873 RTGetOptInit(&GetState, a->argc, a->argv, s_aModifyOptions, RT_ELEMENTS(s_aModifyOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1874 int c;
1875 RTGETOPTUNION ValueUnion;
1876 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1877 {
1878 switch (c)
1879 {
1880 case 'n':
1881 pszName = ValueUnion.psz;
1882 break;
[105333]1883 case 'r':
1884 fWritable = !ValueUnion.f;
[105087]1885 break;
[105333]1886 case 'a':
1887 fAutoMount = ValueUnion.f;
1888 break;
1889 case 'm':
1890 pszAutoMountPoint = ValueUnion.psz;
1891 break;
[105087]1892 case 's':
1893 enmSymlinkPolicy = nameToSymlinkPolicy(ValueUnion.psz);
1894 if (enmSymlinkPolicy == SymlinkPolicy_None)
1895 return errorArgument(Misc::tr("Invalid --symlink-policy argument '%s'"), ValueUnion.psz);
1896 break;
1897 case VINF_GETOPT_NOT_OPTION:
1898 if (pszMachineName)
1899 return errorArgument(Misc::tr("Machine name given more than once: first '%s', then '%s'"),
1900 pszMachineName, ValueUnion.psz);
1901 pszMachineName = ValueUnion.psz;
1902 break;
1903 default:
1904 return errorGetOpt(c, &ValueUnion);
1905 }
1906 }
1907
1908 if (!pszMachineName)
1909 return errorSyntax(Misc::tr("No machine was specified"));
1910 if (!pszName)
1911 return errorSyntax(Misc::tr("No shared folder name (--name) was supplied."));
1912
[105333]1913 if (enmSymlinkPolicy == SymlinkPolicy_None && fWritable == -1 && fAutoMount == -1 && !pszAutoMountPoint)
1914 return errorSyntax(Misc::tr("No shared folder attributes specified."));
[105087]1915
1916 /*
1917 * Done parsing, do some real work.
1918 */
1919 ComPtr<IMachine> ptrMachine;
1920 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1921 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
1922
1923 HRESULT hrc;
[105333]1924 /* Open a session for the VM using an exclusive lock as this is for permanent shared folders.
1925 * Support for modifying transient shared folders hasn't been implemented yet. */
1926 CHECK_ERROR_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
[105087]1927
[105333]1928 /* get the mutable session machine */
1929 ComPtr<IMachine> ptrSessionMachine;
1930 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
[105087]1931
[105333]1932 /* find the desired shared folder to modify */
1933 com::SafeIfaceArray <ISharedFolder> sharedFolders;
1934 CHECK_ERROR_RET(ptrSessionMachine, COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(sharedFolders)), RTEXITCODE_FAILURE);
1935 if (sharedFolders.size() == 0)
1936 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' has no shared folders configured.\n"),
1937 pszMachineName);
[105087]1938
[105333]1939 bool fFound = false;
1940 for (size_t i = 0; i < sharedFolders.size(); ++i)
1941 {
1942 ComPtr<ISharedFolder> sharedFolder = sharedFolders[i];
1943 Bstr bstrSharedFolderName;
1944 CHECK_ERROR_RET(sharedFolder, COMGETTER(Name)(bstrSharedFolderName.asOutParam()), RTEXITCODE_FAILURE);
1945 Utf8Str strSharedFolderName(bstrSharedFolderName);
1946 if (!RTStrCmp(strSharedFolderName.c_str(), pszName))
[105087]1947 {
[105333]1948 fFound = true;
1949 if (enmSymlinkPolicy != SymlinkPolicy_None)
[105087]1950 CHECK_ERROR_RET(sharedFolder, COMSETTER(SymlinkPolicy)(enmSymlinkPolicy), RTEXITCODE_FAILURE);
[105333]1951 if (fWritable != -1)
1952 CHECK_ERROR_RET(sharedFolder, COMSETTER(Writable)((BOOL)fWritable), RTEXITCODE_FAILURE);
1953 if (fAutoMount != -1)
1954 CHECK_ERROR_RET(sharedFolder, COMSETTER(AutoMount)((BOOL)fAutoMount), RTEXITCODE_FAILURE);
1955 if (pszAutoMountPoint)
1956 CHECK_ERROR_RET(sharedFolder, COMSETTER(AutoMountPoint) (Bstr(pszAutoMountPoint).raw()), RTEXITCODE_FAILURE);
1957 break;
[105087]1958 }
1959 }
[105333]1960 if (!fFound)
1961 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Could not find a shared folder named '%s'.\n"), pszName);
[105087]1962
[105333]1963 /* commit and close the session */
1964 if (SUCCEEDED(hrc))
1965 CHECK_ERROR(ptrSessionMachine, SaveSettings());
[105087]1966
[105333]1967 CHECK_ERROR_RET(a->session, UnlockMachine(), RTEXITCODE_FAILURE);
[105087]1968
1969 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1970}
1971
[85665]1972RTEXITCODE handleSharedFolder(HandlerArg *a)
1973{
1974 if (a->argc < 1)
[92372]1975 return errorSyntax(Misc::tr("Not enough parameters"));
[85665]1976
1977 if (!strcmp(a->argv[0], "add"))
1978 {
1979 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_ADD);
1980 return handleSharedFolderAdd(a);
1981 }
1982
1983 if (!strcmp(a->argv[0], "remove"))
1984 {
1985 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_REMOVE);
1986 return handleSharedFolderRemove(a);
1987 }
1988
[105087]1989 if (!strcmp(a->argv[0], "modify"))
1990 {
1991 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_MODIFY);
1992 return handleSharedFolderModify(a);
1993 }
1994
[85665]1995 return errorUnknownSubcommand(a->argv[0]);
1996}
1997
[56118]1998RTEXITCODE handleExtPack(HandlerArg *a)
[33766]1999{
[34292]2000 if (a->argc < 1)
[56344]2001 return errorNoSubcommand();
[33766]2002
2003 ComObjPtr<IExtPackManager> ptrExtPackMgr;
[56118]2004 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
[33766]2005
2006 RTGETOPTSTATE GetState;
2007 RTGETOPTUNION ValueUnion;
2008 int ch;
2009 HRESULT hrc = S_OK;
2010
2011 if (!strcmp(a->argv[0], "install"))
2012 {
[56344]2013 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
[35100]2014 const char *pszName = NULL;
2015 bool fReplace = false;
[33766]2016
[35100]2017 static const RTGETOPTDEF s_aInstallOptions[] =
2018 {
[66185]2019 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
2020 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
[35100]2021 };
2022
[66185]2023 RTCList<RTCString> lstLicenseHashes;
[35100]2024 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
2025 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2026 {
2027 switch (ch)
2028 {
[38070]2029 case 'r':
[35100]2030 fReplace = true;
2031 break;
2032
[66185]2033 case 'a':
2034 lstLicenseHashes.append(ValueUnion.psz);
[66295]2035 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
[66185]2036 break;
2037
[35100]2038 case VINF_GETOPT_NOT_OPTION:
2039 if (pszName)
[92372]2040 return errorSyntax(Misc::tr("Too many extension pack names given to \"extpack uninstall\""));
[35100]2041 pszName = ValueUnion.psz;
2042 break;
2043
2044 default:
[56344]2045 return errorGetOpt(ch, &ValueUnion);
[35100]2046 }
2047 }
2048 if (!pszName)
[92372]2049 return errorSyntax(Misc::tr("No extension pack name was given to \"extpack install\""));
[35100]2050
[33775]2051 char szPath[RTPATH_MAX];
[38528]2052 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
[33775]2053 if (RT_FAILURE(vrc))
[98298]2054 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTPathAbs(%s,,) failed with vrc=%Rrc"), pszName, vrc);
[33775]2055
2056 Bstr bstrTarball(szPath);
[33766]2057 Bstr bstrName;
[34787]2058 ComPtr<IExtPackFile> ptrExtPackFile;
[56118]2059 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
2060 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
[66157]2061 BOOL fShowLicense = true;
2062 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
2063 if (fShowLicense)
2064 {
2065 Bstr bstrLicense;
2066 CHECK_ERROR2I_RET(ptrExtPackFile,
2067 QueryLicense(Bstr("").raw() /* PreferredLocale */,
2068 Bstr("").raw() /* PreferredLanguage */,
2069 Bstr("txt").raw() /* Format */,
2070 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
[66185]2071 Utf8Str strLicense(bstrLicense);
2072 uint8_t abHash[RTSHA256_HASH_SIZE];
[66295]2073 char szDigest[RTSHA256_DIGEST_LEN + 1];
[66185]2074 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
[66295]2075 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
2076 AssertRCStmt(vrc, szDigest[0] = '\0');
2077 if (lstLicenseHashes.contains(szDigest))
[92372]2078 RTPrintf(Misc::tr("License accepted.\n"));
[66185]2079 else
[66157]2080 {
[66296]2081 RTPrintf("%s\n", strLicense.c_str());
[92372]2082 RTPrintf(Misc::tr("Do you agree to these license terms and conditions (y/n)? "));
[66185]2083 ch = RTStrmGetCh(g_pStdIn);
2084 RTPrintf("\n");
2085 if (ch != 'y' && ch != 'Y')
2086 {
[92372]2087 RTPrintf(Misc::tr("Installation of \"%ls\" aborted.\n"), bstrName.raw());
[66297]2088 return RTEXITCODE_FAILURE;
[66185]2089 }
[66295]2090 if (szDigest[0])
[92372]2091 RTPrintf(Misc::tr("License accepted. For batch installation add\n"
2092 "--accept-license=%s\n"
2093 "to the VBoxManage command line.\n\n"), szDigest);
[66157]2094 }
2095 }
[35273]2096 ComPtr<IProgress> ptrProgress;
[56118]2097 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
[35523]2098 hrc = showProgress(ptrProgress);
[92372]2099 CHECK_PROGRESS_ERROR_RET(ptrProgress, (Misc::tr("Failed to install \"%s\""), szPath), RTEXITCODE_FAILURE);
[38525]2100
[92372]2101 RTPrintf(Misc::tr("Successfully installed \"%ls\".\n"), bstrName.raw());
[33766]2102 }
2103 else if (!strcmp(a->argv[0], "uninstall"))
2104 {
[56344]2105 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
[33766]2106 const char *pszName = NULL;
2107 bool fForced = false;
2108
2109 static const RTGETOPTDEF s_aUninstallOptions[] =
2110 {
[37118]2111 { "--force", 'f', RTGETOPT_REQ_NOTHING },
[33766]2112 };
2113
[35100]2114 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
[33766]2115 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2116 {
2117 switch (ch)
2118 {
2119 case 'f':
2120 fForced = true;
2121 break;
2122
2123 case VINF_GETOPT_NOT_OPTION:
2124 if (pszName)
[92372]2125 return errorSyntax(Misc::tr("Too many extension pack names given to \"extpack uninstall\""));
[33766]2126 pszName = ValueUnion.psz;
2127 break;
2128
2129 default:
[56344]2130 return errorGetOpt(ch, &ValueUnion);
[33766]2131 }
2132 }
2133 if (!pszName)
[92372]2134 return errorSyntax(Misc::tr("No extension pack name was given to \"extpack uninstall\""));
[33766]2135
2136 Bstr bstrName(pszName);
[35273]2137 ComPtr<IProgress> ptrProgress;
[56118]2138 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
[35523]2139 hrc = showProgress(ptrProgress);
[92372]2140 CHECK_PROGRESS_ERROR_RET(ptrProgress, (Misc::tr("Failed to uninstall \"%s\""), pszName), RTEXITCODE_FAILURE);
[38525]2141
[92372]2142 RTPrintf(Misc::tr("Successfully uninstalled \"%s\".\n"), pszName);
[33766]2143 }
[34244]2144 else if (!strcmp(a->argv[0], "cleanup"))
2145 {
[56344]2146 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
[34244]2147 if (a->argc > 1)
[56349]2148 return errorTooManyParameters(&a->argv[1]);
[56118]2149 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
[92372]2150 RTPrintf(Misc::tr("Successfully performed extension pack cleanup\n"));
[34244]2151 }
[33766]2152 else
[56344]2153 return errorUnknownSubcommand(a->argv[0]);
[33766]2154
2155 return RTEXITCODE_SUCCESS;
2156}
2157
[99775]2158static RTEXITCODE handleUnattendedDetect(HandlerArg *a)
[68140]2159{
2160 HRESULT hrc;
2161
2162 /*
2163 * Options. We work directly on an IUnattended instace while parsing
2164 * the options. This saves a lot of extra clutter.
2165 */
2166 bool fMachineReadable = false;
2167 char szIsoPath[RTPATH_MAX];
2168 szIsoPath[0] = '\0';
2169
2170 /*
2171 * Parse options.
2172 */
2173 static const RTGETOPTDEF s_aOptions[] =
2174 {
2175 { "--iso", 'i', RTGETOPT_REQ_STRING },
2176 { "--machine-readable", 'M', RTGETOPT_REQ_NOTHING },
2177 };
2178
2179 RTGETOPTSTATE GetState;
[68146]2180 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
[68140]2181 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2182
2183 int c;
2184 RTGETOPTUNION ValueUnion;
2185 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2186 {
2187 switch (c)
2188 {
2189 case 'i': // --iso
2190 vrc = RTPathAbs(ValueUnion.psz, szIsoPath, sizeof(szIsoPath));
2191 if (RT_FAILURE(vrc))
[92372]2192 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
[68140]2193 break;
2194
2195 case 'M': // --machine-readable.
2196 fMachineReadable = true;
2197 break;
2198
2199 default:
2200 return errorGetOpt(c, &ValueUnion);
2201 }
2202 }
2203
2204 /*
2205 * Check for required stuff.
2206 */
2207 if (szIsoPath[0] == '\0')
[92372]2208 return errorSyntax(Misc::tr("No ISO specified"));
[68140]2209
2210 /*
2211 * Do the job.
2212 */
2213 ComPtr<IUnattended> ptrUnattended;
2214 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
2215 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szIsoPath).raw()), RTEXITCODE_FAILURE);
[68318]2216 CHECK_ERROR2(hrc, ptrUnattended, DetectIsoOS());
2217 RTEXITCODE rcExit = SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[68140]2218
2219 /*
2220 * Retrieve the results.
2221 */
2222 Bstr bstrDetectedOSTypeId;
2223 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSTypeId)(bstrDetectedOSTypeId.asOutParam()), RTEXITCODE_FAILURE);
2224 Bstr bstrDetectedVersion;
2225 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSVersion)(bstrDetectedVersion.asOutParam()), RTEXITCODE_FAILURE);
2226 Bstr bstrDetectedFlavor;
2227 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSFlavor)(bstrDetectedFlavor.asOutParam()), RTEXITCODE_FAILURE);
[68239]2228 Bstr bstrDetectedLanguages;
2229 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSLanguages)(bstrDetectedLanguages.asOutParam()), RTEXITCODE_FAILURE);
[68140]2230 Bstr bstrDetectedHints;
2231 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSHints)(bstrDetectedHints.asOutParam()), RTEXITCODE_FAILURE);
[93558]2232 SafeArray<BSTR> aImageNames;
2233 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedImageNames)(ComSafeArrayAsOutParam(aImageNames)), RTEXITCODE_FAILURE);
2234 SafeArray<ULONG> aImageIndices;
2235 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedImageIndices)(ComSafeArrayAsOutParam(aImageIndices)), RTEXITCODE_FAILURE);
2236 Assert(aImageNames.size() == aImageIndices.size());
[96620]2237 BOOL fInstallSupported = FALSE;
2238 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(IsUnattendedInstallSupported)(&fInstallSupported), RTEXITCODE_FAILURE);
[93558]2239
[68140]2240 if (fMachineReadable)
[93558]2241 {
2242 outputMachineReadableString("OSTypeId", &bstrDetectedOSTypeId);
2243 outputMachineReadableString("OSVersion", &bstrDetectedVersion);
2244 outputMachineReadableString("OSFlavor", &bstrDetectedFlavor);
2245 outputMachineReadableString("OSLanguages", &bstrDetectedLanguages);
2246 outputMachineReadableString("OSHints", &bstrDetectedHints);
2247 for (size_t i = 0; i < aImageNames.size(); i++)
2248 {
2249 Bstr const bstrName = aImageNames[i];
2250 outputMachineReadableStringWithFmtName(&bstrName, false, "ImageIndex%u", aImageIndices[i]);
2251 }
[96620]2252 outputMachineReadableBool("IsInstallSupported", &fInstallSupported);
[93558]2253 }
[68140]2254 else
2255 {
[92372]2256 RTMsgInfo(Misc::tr("Detected '%s' to be:\n"), szIsoPath);
2257 RTPrintf(Misc::tr(" OS TypeId = %ls\n"
2258 " OS Version = %ls\n"
2259 " OS Flavor = %ls\n"
2260 " OS Languages = %ls\n"
2261 " OS Hints = %ls\n"),
[68140]2262 bstrDetectedOSTypeId.raw(),
2263 bstrDetectedVersion.raw(),
2264 bstrDetectedFlavor.raw(),
[68239]2265 bstrDetectedLanguages.raw(),
[68140]2266 bstrDetectedHints.raw());
[93558]2267 for (size_t i = 0; i < aImageNames.size(); i++)
2268 RTPrintf(" Image #%-3u = %ls\n", aImageIndices[i], aImageNames[i]);
[96620]2269 if (fInstallSupported)
2270 RTPrintf(Misc::tr(" Unattended installation supported = yes\n"));
2271 else
2272 RTPrintf(Misc::tr(" Unattended installation supported = no\n"));
[68140]2273 }
2274
[68318]2275 return rcExit;
[68140]2276}
2277
[99775]2278static RTEXITCODE handleUnattendedInstall(HandlerArg *a)
[64997]2279{
[68134]2280 HRESULT hrc;
2281 char szAbsPath[RTPATH_MAX];
2282
[67614]2283 /*
[91469]2284 * Options. We work directly on an IUnattended instance while parsing
[68134]2285 * the options. This saves a lot of extra clutter.
[67614]2286 */
[68134]2287 ComPtr<IUnattended> ptrUnattended;
2288 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
2289 RTCList<RTCString> arrPackageSelectionAdjustments;
2290 ComPtr<IMachine> ptrMachine;
[68140]2291 bool fDryRun = false;
[68213]2292 const char *pszSessionType = "none";
[64997]2293
[67614]2294 /*
2295 * Parse options.
2296 */
[102538]2297 enum kUnattendedInstallOpt
2298 {
[102540]2299 kUnattendedInstallOpt_AdminPassword = 1000,
2300 kUnattendedInstallOpt_AdminPasswordFile
[102538]2301 };
[67614]2302 static const RTGETOPTDEF s_aOptions[] =
[64997]2303 {
[68095]2304 { "--iso", 'i', RTGETOPT_REQ_STRING },
2305 { "--user", 'u', RTGETOPT_REQ_STRING },
[102538]2306 { "--password", 'p', RTGETOPT_REQ_STRING }, /* Keep for backwards compatibility! */
[102539]2307 { "--password-file", 'X', RTGETOPT_REQ_STRING }, /* Keep for backwards compatibility! */
[102538]2308 { "--user-password", 'p', RTGETOPT_REQ_STRING },
[102540]2309 { "--user-password-file", 'X', RTGETOPT_REQ_STRING },
[102538]2310 { "--admin-password", kUnattendedInstallOpt_AdminPassword, RTGETOPT_REQ_STRING },
[102540]2311 { "--admin-password-file", kUnattendedInstallOpt_AdminPasswordFile, RTGETOPT_REQ_STRING },
[68095]2312 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
2313 { "--key", 'k', RTGETOPT_REQ_STRING },
2314 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
2315 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
2316 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
2317 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
2318 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
2319 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
2320 { "--locale", 'l', RTGETOPT_REQ_STRING },
[68239]2321 { "--country", 'Y', RTGETOPT_REQ_STRING },
[68095]2322 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
2323 { "--proxy", 'y', RTGETOPT_REQ_STRING },
[68127]2324 { "--hostname", 'H', RTGETOPT_REQ_STRING },
[68129]2325 { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
[68140]2326 { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
[68129]2327 // advance options:
[68095]2328 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
2329 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
2330 { "--script-template", 'c', RTGETOPT_REQ_STRING },
2331 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
2332 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
2333 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
[68239]2334 { "--language", 'L', RTGETOPT_REQ_STRING },
[68129]2335 // start vm related options:
[68258]2336 { "--start-vm", 'S', RTGETOPT_REQ_STRING },
[68213]2337 /** @todo Add a --wait option too for waiting for the VM to shut down or
2338 * something like that...? */
[67614]2339 };
[64997]2340
[67614]2341 RTGETOPTSTATE GetState;
[68146]2342 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
[67614]2343 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2344
2345 int c;
2346 RTGETOPTUNION ValueUnion;
2347 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
[66068]2348 {
[67614]2349 switch (c)
[66068]2350 {
[67614]2351 case VINF_GETOPT_NOT_OPTION:
[68134]2352 if (ptrMachine.isNotNull())
[92372]2353 return errorSyntax(Misc::tr("VM name/UUID given more than once!"));
[68134]2354 CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
2355 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
[67614]2356 break;
[64997]2357
[68055]2358 case 'i': // --iso
[68134]2359 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
[68055]2360 if (RT_FAILURE(vrc))
[92372]2361 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
[68134]2362 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
[68055]2363 break;
2364
[67614]2365 case 'u': // --user
[68134]2366 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[67614]2367 break;
[64997]2368
[102538]2369 case 'p': // --[user-]password
[102535]2370 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(UserPassword)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[67614]2371 break;
[66068]2372
[102538]2373 case kUnattendedInstallOpt_AdminPassword: // --admin-password
2374 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdminPassword)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2375 break;
2376
[102540]2377 case 'X': // --[user-]password-file
[68187]2378 {
2379 Utf8Str strPassword;
2380 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
2381 if (rcExit != RTEXITCODE_SUCCESS)
2382 return rcExit;
[102535]2383 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(UserPassword)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
[68187]2384 break;
2385 }
2386
[102540]2387 case kUnattendedInstallOpt_AdminPasswordFile:
2388 {
2389 Utf8Str strPassword;
2390 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
2391 if (rcExit != RTEXITCODE_SUCCESS)
2392 return rcExit;
2393 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdminPassword)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
2394 break;
2395 }
2396
[67896]2397 case 'U': // --full-user-name
[68134]2398 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[67756]2399 break;
2400
[67614]2401 case 'k': // --key
[68134]2402 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[67614]2403 break;
2404
[67756]2405 case 'A': // --install-additions
[68134]2406 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
[67756]2407 break;
2408 case 'N': // --no-install-additions
[68134]2409 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
[67756]2410 break;
[68055]2411 case 'a': // --additions-iso
[68134]2412 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
[68055]2413 if (RT_FAILURE(vrc))
[92372]2414 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
[68134]2415 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
[67614]2416 break;
2417
[68055]2418 case 't': // --install-txs
[68134]2419 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
[67614]2420 break;
[68055]2421 case 'T': // --no-install-txs
[68134]2422 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
[68055]2423 break;
2424 case 'K': // --valiation-kit-iso
[68134]2425 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
[68055]2426 if (RT_FAILURE(vrc))
[92372]2427 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
[68134]2428 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
[68055]2429 break;
[67614]2430
[68095]2431 case 'l': // --locale
[68134]2432 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[68095]2433 break;
2434
[68239]2435 case 'Y': // --country
[68134]2436 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[68095]2437 break;
2438
2439 case 'z': // --time-zone;
[68134]2440 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[68095]2441 break;
2442
2443 case 'y': // --proxy
[68134]2444 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[68095]2445 break;
2446
[68127]2447 case 'H': // --hostname
[68134]2448 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[68127]2449 break;
2450
[68129]2451 case 's': // --package-selection-adjustment
2452 arrPackageSelectionAdjustments.append(ValueUnion.psz);
2453 break;
2454
[68140]2455 case 'D':
2456 fDryRun = true;
2457 break;
2458
[68129]2459 case 'x': // --auxiliary-base-path
[68134]2460 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
[68055]2461 if (RT_FAILURE(vrc))
[92372]2462 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
[68134]2463 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
[67619]2464 break;
2465
[67614]2466 case 'm': // --image-index
[68134]2467 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
[67614]2468 break;
2469
[68129]2470 case 'c': // --script-template
[68134]2471 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
[68055]2472 if (RT_FAILURE(vrc))
[92372]2473 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
[68134]2474 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
[68055]2475 break;
2476
[68129]2477 case 'C': // --post-install-script-template
[68134]2478 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
[68071]2479 if (RT_FAILURE(vrc))
[92372]2480 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
[68134]2481 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
[68071]2482 break;
2483
[68055]2484 case 'P': // --post-install-command.
[68134]2485 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[68055]2486 break;
2487
[68095]2488 case 'I': // --extra-install-kernel-parameters
[68134]2489 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
[68095]2490 break;
2491
[68239]2492 case 'L': // --language
2493 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2494 break;
2495
[68258]2496 case 'S': // --start-vm
[67756]2497 pszSessionType = ValueUnion.psz;
[67614]2498 break;
2499
2500 default:
[68140]2501 return errorGetOpt(c, &ValueUnion);
[64997]2502 }
[67614]2503 }
[66068]2504
[67614]2505 /*
2506 * Check for required stuff.
2507 */
[68134]2508 if (ptrMachine.isNull())
[92372]2509 return errorSyntax(Misc::tr("Missing VM name/UUID"));
[67614]2510
[68134]2511 /*
2512 * Set accumulative attributes.
2513 */
2514 if (arrPackageSelectionAdjustments.size() == 1)
2515 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
2516 RTEXITCODE_FAILURE);
2517 else if (arrPackageSelectionAdjustments.size() > 1)
2518 {
2519 RTCString strAdjustments;
2520 strAdjustments.join(arrPackageSelectionAdjustments, ";");
2521 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
2522 }
[64997]2523
[67614]2524 /*
[68135]2525 * Get details about the machine so we can display them below.
[67614]2526 */
[68134]2527 Bstr bstrMachineName;
2528 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
2529 Bstr bstrUuid;
2530 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
2531 BSTR bstrInstalledOS;
2532 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
2533 Utf8Str strInstalledOS(bstrInstalledOS);
[67614]2534
[68135]2535 /*
2536 * Temporarily lock the machine to check whether it's running or not.
[68192]2537 * We take this opportunity to disable the first run wizard.
[68135]2538 */
[68134]2539 CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
[68135]2540 {
2541 ComPtr<IConsole> ptrConsole;
2542 CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
[68192]2543
2544 if ( ptrConsole.isNull()
2545 && SUCCEEDED(hrc)
2546 && ( RTStrICmp(pszSessionType, "gui") == 0
2547 || RTStrICmp(pszSessionType, "none") == 0))
2548 {
2549 ComPtr<IMachine> ptrSessonMachine;
2550 CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
2551 if (ptrSessonMachine.isNotNull())
[68194]2552 {
[68192]2553 CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
[68194]2554 }
[68192]2555 }
2556
[68135]2557 a->session->UnlockMachine();
2558 if (FAILED(hrc))
2559 return RTEXITCODE_FAILURE;
2560 if (ptrConsole.isNotNull())
[92372]2561 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%ls' is currently running"), bstrMachineName.raw());
[68135]2562 }
[64997]2563
[68135]2564 /*
2565 * Do the work.
2566 */
[92372]2567 RTMsgInfo(Misc::tr("%s unattended installation of %s in machine '%ls' (%ls).\n"),
2568 RTStrICmp(pszSessionType, "none") == 0 ? Misc::tr("Preparing") : Misc::tr("Starting"),
[68135]2569 strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
[64997]2570
[68135]2571 CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
[68140]2572 if (!fDryRun)
2573 {
2574 CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
[91469]2575 CHECK_ERROR2_RET(hrc, ptrUnattended, ReconfigureVM(), RTEXITCODE_FAILURE);
[68140]2576 }
[64997]2577
[68135]2578 /*
2579 * Retrieve and display the parameters actually used.
2580 */
[92372]2581 RTMsgInfo(Misc::tr("Using values:\n"));
[68024]2582#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
[68135]2583 a_Type Value; \
2584 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
2585 if (SUCCEEDED(hrc2)) \
2586 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
2587 else \
[92372]2588 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
[68135]2589 } while (0)
[68024]2590#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
[68135]2591 Bstr bstrString; \
2592 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
2593 if (SUCCEEDED(hrc2)) \
2594 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
2595 else \
[92372]2596 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
[68135]2597 } while (0)
[66068]2598
[68135]2599 SHOW_STR_ATTR(IsoPath, "isoPath");
2600 SHOW_STR_ATTR(User, "user");
[102536]2601 SHOW_STR_ATTR(UserPassword, "password"); /* Keep for backwards compatibility! */
[102538]2602 SHOW_STR_ATTR(UserPassword, "user-password");
2603 SHOW_STR_ATTR(AdminPassword, "admin-password");
[68135]2604 SHOW_STR_ATTR(FullUserName, "fullUserName");
2605 SHOW_STR_ATTR(ProductKey, "productKey");
2606 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
2607 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
2608 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
2609 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
2610 SHOW_STR_ATTR(Locale, "locale");
2611 SHOW_STR_ATTR(Country, "country");
2612 SHOW_STR_ATTR(TimeZone, "timeZone");
2613 SHOW_STR_ATTR(Proxy, "proxy");
2614 SHOW_STR_ATTR(Hostname, "hostname");
2615 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
2616 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
2617 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
2618 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
2619 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
2620 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
2621 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
[68239]2622 SHOW_STR_ATTR(Language, "language");
[68135]2623 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
2624 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
2625 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
[68239]2626 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
[68135]2627 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
[93558]2628 {
2629 ULONG idxImage = 0;
2630 HRESULT hrc2 = ptrUnattended->COMGETTER(ImageIndex)(&idxImage);
2631 if (FAILED(hrc2))
2632 idxImage = 0;
2633 SafeArray<BSTR> aImageNames;
2634 hrc2 = ptrUnattended->COMGETTER(DetectedImageNames)(ComSafeArrayAsOutParam(aImageNames));
2635 if (SUCCEEDED(hrc2))
2636 {
2637 SafeArray<ULONG> aImageIndices;
2638 hrc2 = ptrUnattended->COMGETTER(DetectedImageIndices)(ComSafeArrayAsOutParam(aImageIndices));
2639 if (SUCCEEDED(hrc2))
2640 {
2641 Assert(aImageNames.size() == aImageIndices.size());
2642 for (size_t i = 0; i < aImageNames.size(); i++)
2643 {
2644 char szTmp[64];
2645 RTStrPrintf(szTmp, sizeof(szTmp), "detectedImage[%u]%s", i, idxImage != aImageIndices[i] ? "" : "*");
2646 RTPrintf(" %32s = #%u: %ls\n", szTmp, aImageIndices[i], aImageNames[i]);
2647 }
2648 }
2649 else
2650 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), "detectedImageIndices", hrc2);
2651 }
2652 else
2653 RTPrintf(Misc::tr(" %32 = failed: %Rhrc\n"), "detectedImageNames", hrc2);
2654 }
[68024]2655
2656#undef SHOW_STR_ATTR
2657#undef SHOW_ATTR
2658
[68135]2659 /* We can drop the IUnatteded object now. */
2660 ptrUnattended.setNull();
[68024]2661
[68135]2662 /*
2663 * Start the VM if requested.
2664 */
[68140]2665 if ( fDryRun
2666 || RTStrICmp(pszSessionType, "none") == 0)
[68213]2667 {
2668 if (!fDryRun)
[92372]2669 RTMsgInfo(Misc::tr("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n"), bstrMachineName.raw(), bstrUuid.raw());
[68135]2670 hrc = S_OK;
[68213]2671 }
[68135]2672 else
2673 {
[80824]2674 com::SafeArray<IN_BSTR> aBstrEnv;
[64997]2675#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
[68135]2676 /* make sure the VM process will start on the same display as VBoxManage */
2677 const char *pszDisplay = RTEnvGet("DISPLAY");
2678 if (pszDisplay)
[80824]2679 aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw());
[68135]2680 const char *pszXAuth = RTEnvGet("XAUTHORITY");
2681 if (pszXAuth)
[80825]2682 aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw());
[64997]2683#endif
[68135]2684 ComPtr<IProgress> ptrProgress;
[80824]2685 CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), ComSafeArrayAsInParam(aBstrEnv), ptrProgress.asOutParam()));
[68135]2686 if (SUCCEEDED(hrc) && !ptrProgress.isNull())
2687 {
[92372]2688 RTMsgInfo(Misc::tr("Waiting for VM '%ls' to power on...\n"), bstrMachineName.raw());
[68135]2689 CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
2690 if (SUCCEEDED(hrc))
[64997]2691 {
[68135]2692 BOOL fCompleted = true;
2693 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
2694 if (SUCCEEDED(hrc))
[64997]2695 {
[68135]2696 ASSERT(fCompleted);
2697
2698 LONG iRc;
2699 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
2700 if (SUCCEEDED(hrc))
[64997]2701 {
[68135]2702 if (SUCCEEDED(iRc))
[92372]2703 RTMsgInfo(Misc::tr("VM '%ls' (%ls) has been successfully started.\n"),
2704 bstrMachineName.raw(), bstrUuid.raw());
[68135]2705 else
[64997]2706 {
[68135]2707 ProgressErrorInfo info(ptrProgress);
2708 com::GluePrintErrorInfo(info);
[64997]2709 }
[68135]2710 hrc = iRc;
[64997]2711 }
2712 }
2713 }
2714 }
2715
[68135]2716 /*
2717 * Do we wait for the VM to power down?
2718 */
2719 }
[64997]2720
[68135]2721 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[64997]2722}
[68140]2723
2724
2725RTEXITCODE handleUnattended(HandlerArg *a)
2726{
2727 /*
2728 * Sub-command switch.
2729 */
2730 if (a->argc < 1)
2731 return errorNoSubcommand();
2732
2733 if (!strcmp(a->argv[0], "detect"))
2734 {
2735 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
2736 return handleUnattendedDetect(a);
2737 }
2738
2739 if (!strcmp(a->argv[0], "install"))
2740 {
2741 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
2742 return handleUnattendedInstall(a);
2743 }
2744
2745 /* Consider some kind of create-vm-and-install-guest-os command. */
2746 return errorUnknownSubcommand(a->argv[0]);
2747}
[77777]2748
2749/**
2750 * Common Cloud profile options.
2751 */
2752typedef struct
2753{
2754 const char *pszProviderName;
2755 const char *pszProfileName;
2756} CLOUDPROFILECOMMONOPT;
2757typedef CLOUDPROFILECOMMONOPT *PCLOUDPROFILECOMMONOPT;
2758
2759/**
2760 * Sets the properties of cloud profile
2761 *
2762 * @returns 0 on success, 1 on failure
2763 */
2764
2765static RTEXITCODE setCloudProfileProperties(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2766{
2767
2768 HRESULT hrc = S_OK;
2769
2770 Bstr bstrProvider(pCommonOpts->pszProviderName);
2771 Bstr bstrProfile(pCommonOpts->pszProfileName);
2772
2773 /*
2774 * Parse options.
2775 */
2776 static const RTGETOPTDEF s_aOptions[] =
2777 {
2778 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2779 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2780 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2781 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2782 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2783 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2784 { "--region", 'r', RTGETOPT_REQ_STRING }
2785 };
2786
2787 RTGETOPTSTATE GetState;
2788 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2789 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2790
2791 com::SafeArray<BSTR> names;
2792 com::SafeArray<BSTR> values;
2793
2794 int c;
2795 RTGETOPTUNION ValueUnion;
2796 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2797 {
2798 switch (c)
2799 {
2800 case 'u': // --clouduser
2801 Bstr("user").detachTo(names.appendedRaw());
2802 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2803 break;
2804 case 'p': // --fingerprint
2805 Bstr("fingerprint").detachTo(names.appendedRaw());
2806 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2807 break;
2808 case 'k': // --keyfile
2809 Bstr("key_file").detachTo(names.appendedRaw());
2810 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2811 break;
2812 case 'P': // --passphrase
2813 Bstr("pass_phrase").detachTo(names.appendedRaw());
2814 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2815 break;
2816 case 't': // --tenancy
2817 Bstr("tenancy").detachTo(names.appendedRaw());
2818 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2819 break;
2820 case 'c': // --compartment
2821 Bstr("compartment").detachTo(names.appendedRaw());
2822 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2823 break;
2824 case 'r': // --region
2825 Bstr("region").detachTo(names.appendedRaw());
2826 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2827 break;
2828 default:
[93706]2829 return errorGetOpt(c, &ValueUnion);
[77777]2830 }
2831 }
2832
2833 /* check for required options */
2834 if (bstrProvider.isEmpty())
[93706]2835 return errorSyntax(Misc::tr("Parameter --provider is required"));
[77777]2836 if (bstrProfile.isEmpty())
[93706]2837 return errorSyntax(Misc::tr("Parameter --profile is required"));
[77777]2838
2839 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2840
2841 ComPtr<ICloudProviderManager> pCloudProviderManager;
2842 CHECK_ERROR2_RET(hrc, pVirtualBox,
2843 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2844 RTEXITCODE_FAILURE);
2845
2846 ComPtr<ICloudProvider> pCloudProvider;
2847
2848 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2849 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2850 RTEXITCODE_FAILURE);
2851
2852 ComPtr<ICloudProfile> pCloudProfile;
2853
2854 if (pCloudProvider)
2855 {
2856 CHECK_ERROR2_RET(hrc, pCloudProvider,
2857 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2858 RTEXITCODE_FAILURE);
2859 CHECK_ERROR2_RET(hrc, pCloudProfile,
2860 SetProperties(ComSafeArrayAsInParam(names), ComSafeArrayAsInParam(values)),
2861 RTEXITCODE_FAILURE);
2862 }
2863
2864 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2865
[92372]2866 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was updated.\n"),bstrProvider.raw(), bstrProfile.raw());
[77777]2867
2868 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2869}
2870
2871/**
2872 * Gets the properties of cloud profile
2873 *
2874 * @returns 0 on success, 1 on failure
2875 */
2876static RTEXITCODE showCloudProfileProperties(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2877{
2878 HRESULT hrc = S_OK;
2879
2880 Bstr bstrProvider(pCommonOpts->pszProviderName);
2881 Bstr bstrProfile(pCommonOpts->pszProfileName);
2882
2883 /* check for required options */
2884 if (bstrProvider.isEmpty())
[93706]2885 return errorSyntax(Misc::tr("Parameter --provider is required"));
[77777]2886 if (bstrProfile.isEmpty())
[93706]2887 return errorSyntax(Misc::tr("Parameter --profile is required"));
[77777]2888
2889 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2890 ComPtr<ICloudProviderManager> pCloudProviderManager;
2891 CHECK_ERROR2_RET(hrc, pVirtualBox,
2892 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2893 RTEXITCODE_FAILURE);
2894 ComPtr<ICloudProvider> pCloudProvider;
2895 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2896 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2897 RTEXITCODE_FAILURE);
2898
2899 ComPtr<ICloudProfile> pCloudProfile;
2900 if (pCloudProvider)
2901 {
2902 CHECK_ERROR2_RET(hrc, pCloudProvider,
2903 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2904 RTEXITCODE_FAILURE);
2905
2906 Bstr bstrProviderID;
2907 pCloudProfile->COMGETTER(ProviderId)(bstrProviderID.asOutParam());
[92372]2908 RTPrintf(Misc::tr("Provider GUID: %ls\n"), bstrProviderID.raw());
[77777]2909
2910 com::SafeArray<BSTR> names;
2911 com::SafeArray<BSTR> values;
2912 CHECK_ERROR2_RET(hrc, pCloudProfile,
2913 GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values)),
2914 RTEXITCODE_FAILURE);
2915 size_t cNames = names.size();
2916 size_t cValues = values.size();
2917 bool fFirst = true;
2918 for (size_t k = 0; k < cNames; k++)
2919 {
2920 Bstr value;
2921 if (k < cValues)
2922 value = values[k];
2923 RTPrintf("%s%ls=%ls\n",
[92372]2924 fFirst ? Misc::tr("Property: ") : " ",
[77777]2925 names[k], value.raw());
2926 fFirst = false;
2927 }
2928
2929 RTPrintf("\n");
2930 }
2931
2932 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2933}
2934
2935static RTEXITCODE addCloudProfile(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2936{
2937 HRESULT hrc = S_OK;
2938
2939 Bstr bstrProvider(pCommonOpts->pszProviderName);
2940 Bstr bstrProfile(pCommonOpts->pszProfileName);
2941
2942
2943 /* check for required options */
2944 if (bstrProvider.isEmpty())
[93706]2945 return errorSyntax(Misc::tr("Parameter --provider is required"));
[77777]2946 if (bstrProfile.isEmpty())
[93706]2947 return errorSyntax(Misc::tr("Parameter --profile is required"));
[77777]2948
2949 /*
2950 * Parse options.
2951 */
2952 static const RTGETOPTDEF s_aOptions[] =
2953 {
2954 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2955 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2956 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2957 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2958 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2959 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2960 { "--region", 'r', RTGETOPT_REQ_STRING }
2961 };
2962
2963 RTGETOPTSTATE GetState;
2964 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2965 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2966
2967 com::SafeArray<BSTR> names;
2968 com::SafeArray<BSTR> values;
2969
2970 int c;
2971 RTGETOPTUNION ValueUnion;
2972 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2973 {
2974 switch (c)
2975 {
2976 case 'u': // --clouduser
2977 Bstr("user").detachTo(names.appendedRaw());
2978 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2979 break;
2980 case 'p': // --fingerprint
2981 Bstr("fingerprint").detachTo(names.appendedRaw());
2982 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2983 break;
2984 case 'k': // --keyfile
2985 Bstr("key_file").detachTo(names.appendedRaw());
2986 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2987 break;
2988 case 'P': // --passphrase
2989 Bstr("pass_phrase").detachTo(names.appendedRaw());
2990 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2991 break;
2992 case 't': // --tenancy
2993 Bstr("tenancy").detachTo(names.appendedRaw());
2994 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2995 break;
2996 case 'c': // --compartment
2997 Bstr("compartment").detachTo(names.appendedRaw());
2998 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2999 break;
3000 case 'r': // --region
3001 Bstr("region").detachTo(names.appendedRaw());
3002 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
3003 break;
3004 default:
[93706]3005 return errorGetOpt(c, &ValueUnion);
[77777]3006 }
3007 }
3008
3009 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
3010
3011 ComPtr<ICloudProviderManager> pCloudProviderManager;
3012 CHECK_ERROR2_RET(hrc, pVirtualBox,
3013 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
3014 RTEXITCODE_FAILURE);
3015
3016 ComPtr<ICloudProvider> pCloudProvider;
3017 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
3018 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
3019 RTEXITCODE_FAILURE);
3020
3021 CHECK_ERROR2_RET(hrc, pCloudProvider,
3022 CreateProfile(bstrProfile.raw(),
3023 ComSafeArrayAsInParam(names),
3024 ComSafeArrayAsInParam(values)),
3025 RTEXITCODE_FAILURE);
3026
3027 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
3028
[92372]3029 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was added.\n"),bstrProvider.raw(), bstrProfile.raw());
[77777]3030
3031 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
3032}
3033
3034static RTEXITCODE deleteCloudProfile(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
3035{
3036 HRESULT hrc = S_OK;
3037
3038 Bstr bstrProvider(pCommonOpts->pszProviderName);
3039 Bstr bstrProfile(pCommonOpts->pszProfileName);
3040
3041 /* check for required options */
3042 if (bstrProvider.isEmpty())
[93706]3043 return errorSyntax(Misc::tr("Parameter --provider is required"));
[77777]3044 if (bstrProfile.isEmpty())
[93706]3045 return errorSyntax(Misc::tr("Parameter --profile is required"));
[77777]3046
3047 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
3048 ComPtr<ICloudProviderManager> pCloudProviderManager;
3049 CHECK_ERROR2_RET(hrc, pVirtualBox,
3050 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
3051 RTEXITCODE_FAILURE);
3052 ComPtr<ICloudProvider> pCloudProvider;
3053 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
3054 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
3055 RTEXITCODE_FAILURE);
3056
3057 ComPtr<ICloudProfile> pCloudProfile;
3058 if (pCloudProvider)
3059 {
3060 CHECK_ERROR2_RET(hrc, pCloudProvider,
3061 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
3062 RTEXITCODE_FAILURE);
3063
3064 CHECK_ERROR2_RET(hrc, pCloudProfile,
3065 Remove(),
3066 RTEXITCODE_FAILURE);
3067
3068 CHECK_ERROR2_RET(hrc, pCloudProvider,
3069 SaveProfiles(),
3070 RTEXITCODE_FAILURE);
3071
[92372]3072 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was deleted.\n"), bstrProvider.raw(), bstrProfile.raw());
[77777]3073 }
3074
3075 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
3076}
3077
3078RTEXITCODE handleCloudProfile(HandlerArg *a)
3079{
3080 if (a->argc < 1)
3081 return errorNoSubcommand();
3082
3083 static const RTGETOPTDEF s_aOptions[] =
3084 {
3085 /* common options */
3086 { "--provider", 'v', RTGETOPT_REQ_STRING },
3087 { "--profile", 'f', RTGETOPT_REQ_STRING },
3088 /* subcommands */
3089 { "add", 1000, RTGETOPT_REQ_NOTHING },
3090 { "show", 1001, RTGETOPT_REQ_NOTHING },
3091 { "update", 1002, RTGETOPT_REQ_NOTHING },
3092 { "delete", 1003, RTGETOPT_REQ_NOTHING },
3093 };
3094
3095 RTGETOPTSTATE GetState;
3096 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
3097 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
3098
3099 CLOUDPROFILECOMMONOPT CommonOpts = { NULL, NULL };
3100 int c;
3101 RTGETOPTUNION ValueUnion;
3102 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
3103 {
3104 switch (c)
3105 {
3106 case 'v': // --provider
3107 CommonOpts.pszProviderName = ValueUnion.psz;
3108 break;
3109 case 'f': // --profile
3110 CommonOpts.pszProfileName = ValueUnion.psz;
3111 break;
3112 /* Sub-commands: */
3113 case 1000:
3114 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_ADD);
3115 return addCloudProfile(a, GetState.iNext, &CommonOpts);
3116 case 1001:
3117 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_SHOW);
3118 return showCloudProfileProperties(a, &CommonOpts);
3119 case 1002:
3120 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_UPDATE);
3121 return setCloudProfileProperties(a, GetState.iNext, &CommonOpts);
3122 case 1003:
3123 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_DELETE);
3124 return deleteCloudProfile(a, &CommonOpts);
3125 case VINF_GETOPT_NOT_OPTION:
3126 return errorUnknownSubcommand(ValueUnion.psz);
3127
3128 default:
3129 return errorGetOpt(c, &ValueUnion);
3130 }
3131 }
3132
3133 return errorNoSubcommand();
3134}
Note: See TracBrowser for help on using the repository browser.

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