VirtualBox

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

Last change on this file since 94887 was 94660, checked in by vboxsync, 3 years ago

doc/manual,Main,Frontends: API changes in preparation for full VM encryption, guarded by VBOX_WITH_FULL_VM_ENCRYPTION (disabled by default) and returning a not supported error for now, bugref:9955

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