VirtualBox

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

Last change on this file since 95639 was 95140, checked in by vboxsync, 3 years ago

Frontends + Main: Adjust to the new rules wrt. to rc -> hrc,vrc usage. This also fixes quite a few bugs wrt shadow variables, wrong return values and output error translations / exit codes. Also see @todos added. ​​bugref:10223

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.4 KB
Line 
1/* $Id: VBoxManageMisc.cpp 95140 2022-05-31 09:11:39Z 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 hrc;
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 hrc = a->virtualBox->OpenMachine(Bstr(a->argv[0]).raw(),
134 Bstr(strPassword).raw(),
135 machine.asOutParam());
136 if (hrc == 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(hrc))
148 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(a->argv[0]).raw(),
149 Bstr(strPassword).raw(),
150 machine.asOutParam()));
151 if (SUCCEEDED(hrc))
152 {
153 ASSERT(machine);
154 CHECK_ERROR(a->virtualBox, RegisterMachine(machine));
155 }
156 return SUCCEEDED(hrc) ? 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 hrc;
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 hrc = 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 hrc = pMedium->Close();
239 }
240 hrc = S_OK; /** @todo r=andy Why overwriting the result from closing the medium above? */
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 hrc;
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(hrc) ? 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 hrc;
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 hrc = 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 hrc;
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 hrc = 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 hrc = 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 (pszPassword)
825 {
826 if (!RTStrCmp(pszPassword, "-"))
827 {
828 /* Get password from console. */
829 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:");
830 if (rcExit == RTEXITCODE_FAILURE)
831 return rcExit;
832 }
833 else
834 {
835 RTEXITCODE rcExit = readPasswordFile(pszPassword, &strPassword);
836 if (rcExit == RTEXITCODE_FAILURE)
837 {
838 RTMsgError("Failed to read new password from file");
839 return rcExit;
840 }
841 }
842 }
843
844 for (std::list<const char *>::const_iterator it = VMs.begin();
845 it != VMs.end();
846 ++it)
847 {
848 HRESULT hrc2 = hrc;
849 const char *pszVM = *it;
850 ComPtr<IMachine> machine;
851 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(),
852 machine.asOutParam()));
853 if (machine)
854 {
855 if (pszPasswordId && strPassword.isNotEmpty())
856 {
857 CHECK_ERROR(machine, AddEncryptionPassword(Bstr(pszPasswordId).raw(), Bstr(strPassword).raw()));
858 if (hrc == VBOX_E_PASSWORD_INCORRECT)
859 RTMsgError("Password incorrect!");
860 }
861 if (SUCCEEDED(hrc))
862 {
863 ComPtr<IProgress> progress;
864 CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
865 ComSafeArrayAsInParam(aBstrEnv), progress.asOutParam()));
866 if (SUCCEEDED(hrc) && !progress.isNull())
867 {
868 RTPrintf("Waiting for VM \"%s\" to power on...\n", pszVM);
869 CHECK_ERROR(progress, WaitForCompletion(-1));
870 if (SUCCEEDED(hrc))
871 {
872 BOOL completed = true;
873 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
874 if (SUCCEEDED(hrc))
875 {
876 ASSERT(completed);
877
878 LONG iRc;
879 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
880 if (SUCCEEDED(hrc))
881 {
882 if (SUCCEEDED(iRc))
883 RTPrintf("VM \"%s\" has been successfully started.\n", pszVM);
884 else
885 {
886 ProgressErrorInfo info(progress);
887 com::GluePrintErrorInfo(info);
888 }
889 hrc = iRc;
890 }
891 }
892 }
893 }
894 }
895 }
896
897 /* it's important to always close sessions */
898 a->session->UnlockMachine();
899
900 /* make sure that we remember the failed state */
901 if (FAILED(hrc2))
902 hrc = hrc2;
903 }
904
905 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
906}
907
908#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
909static const RTGETOPTDEF g_aSetVMEncryptionOptions[] =
910{
911 { "--new-password", 'n', RTGETOPT_REQ_STRING },
912 { "--old-password", 'o', RTGETOPT_REQ_STRING },
913 { "--cipher", 'c', RTGETOPT_REQ_STRING },
914 { "--new-password-id", 'i', RTGETOPT_REQ_STRING },
915 { "--force", 'f', RTGETOPT_REQ_NOTHING},
916};
917
918RTEXITCODE handleSetVMEncryption(HandlerArg *a, const char *pszFilenameOrUuid)
919{
920 HRESULT hrc;
921 ComPtr<IMachine> machine;
922 const char *pszPasswordNew = NULL;
923 const char *pszPasswordOld = NULL;
924 const char *pszCipher = NULL;
925 const char *pszNewPasswordId = NULL;
926 BOOL fForce = FALSE;
927 Utf8Str strPasswordNew;
928 Utf8Str strPasswordOld;
929
930 int c;
931 RTGETOPTUNION ValueUnion;
932 RTGETOPTSTATE GetState;
933 // start at 0 because main() has hacked both the argc and argv given to us
934 RTGetOptInit(&GetState, a->argc, a->argv, g_aSetVMEncryptionOptions, RT_ELEMENTS(g_aSetVMEncryptionOptions),
935 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
936 while ((c = RTGetOpt(&GetState, &ValueUnion)))
937 {
938 switch (c)
939 {
940 case 'n': // --new-password
941 pszPasswordNew = ValueUnion.psz;
942 break;
943
944 case 'o': // --old-password
945 pszPasswordOld = ValueUnion.psz;
946 break;
947
948 case 'c': // --cipher
949 pszCipher = ValueUnion.psz;
950 break;
951
952 case 'i': // --new-password-id
953 pszNewPasswordId = ValueUnion.psz;
954 break;
955
956 case 'f': // --force
957 fForce = TRUE;
958 break;
959
960 default:
961 if (c > 0)
962 {
963 if (RT_C_IS_PRINT(c))
964 return errorSyntax(Misc::tr("Invalid option -%c"), c);
965 else
966 return errorSyntax(Misc::tr("Invalid option case %i"), c);
967 }
968 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
969 return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz);
970 else if (ValueUnion.pDef)
971 return errorSyntax(Misc::tr("%s: %Rrs"), ValueUnion.pDef->pszLong, c);
972 else
973 return errorSyntax(Misc::tr("error: %Rrs"), c);
974 }
975 }
976
977 if (!pszFilenameOrUuid)
978 return errorSyntax(Misc::tr("VM name or UUID required"));
979
980 if (!pszPasswordNew && !pszPasswordOld)
981 return errorSyntax(Misc::tr("No password specified"));
982
983 if ( (pszPasswordNew && !pszNewPasswordId)
984 || (!pszPasswordNew && pszNewPasswordId))
985 return errorSyntax(Misc::tr("A new password must always have a valid identifier set at the same time"));
986
987 if (pszPasswordOld)
988 {
989 if (!RTStrCmp(pszPasswordOld, "-"))
990 {
991 /* Get password from console. */
992 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordOld, "Enter old password:");
993 if (rcExit == RTEXITCODE_FAILURE)
994 return rcExit;
995 }
996 else
997 {
998 RTEXITCODE rcExit = readPasswordFile(pszPasswordOld, &strPasswordOld);
999 if (rcExit == RTEXITCODE_FAILURE)
1000 {
1001 RTMsgError("Failed to read old password from file");
1002 return rcExit;
1003 }
1004 }
1005 }
1006 if (pszPasswordNew)
1007 {
1008 if (!RTStrCmp(pszPasswordNew, "-"))
1009 {
1010 /* Get password from console. */
1011 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordNew, "Enter new password:");
1012 if (rcExit == RTEXITCODE_FAILURE)
1013 return rcExit;
1014 }
1015 else
1016 {
1017 RTEXITCODE rcExit = readPasswordFile(pszPasswordNew, &strPasswordNew);
1018 if (rcExit == RTEXITCODE_FAILURE)
1019 {
1020 RTMsgError("Failed to read new password from file");
1021 return rcExit;
1022 }
1023 }
1024 }
1025
1026 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(),
1027 machine.asOutParam()));
1028 if (machine)
1029 {
1030 ComPtr<IProgress> progress;
1031 CHECK_ERROR(machine, ChangeEncryption(Bstr(strPasswordOld).raw(), Bstr(pszCipher).raw(),
1032 Bstr(strPasswordNew).raw(), Bstr(pszNewPasswordId).raw(),
1033 fForce, progress.asOutParam()));
1034 if (SUCCEEDED(hrc))
1035 hrc = showProgress(progress);
1036 if (FAILED(hrc))
1037 {
1038 if (hrc == E_NOTIMPL)
1039 RTMsgError("Encrypt VM operation is not implemented!");
1040 else if (hrc == VBOX_E_NOT_SUPPORTED)
1041 RTMsgError("Encrypt VM operation for this cipher is not implemented yet!");
1042 else if (!progress.isNull())
1043 CHECK_PROGRESS_ERROR(progress, ("Failed to encrypt the VM"));
1044 else
1045 RTMsgError("Failed to encrypt the VM!");
1046 }
1047 }
1048 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1049}
1050
1051RTEXITCODE handleCheckVMPassword(HandlerArg *a, const char *pszFilenameOrUuid)
1052{
1053 HRESULT hrc;
1054 ComPtr<IMachine> machine;
1055 Utf8Str strPassword;
1056
1057 if (a->argc != 1)
1058 return errorSyntax(Misc::tr("Invalid number of arguments: %d"), a->argc);
1059
1060 if (!RTStrCmp(a->argv[0], "-"))
1061 {
1062 /* Get password from console. */
1063 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:");
1064 if (rcExit == RTEXITCODE_FAILURE)
1065 return rcExit;
1066 }
1067 else
1068 {
1069 RTEXITCODE rcExit = readPasswordFile(a->argv[0], &strPassword);
1070 if (rcExit == RTEXITCODE_FAILURE)
1071 {
1072 RTMsgError("Failed to read password from file");
1073 return rcExit;
1074 }
1075 }
1076
1077 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(),
1078 machine.asOutParam()));
1079 if (machine)
1080 {
1081 CHECK_ERROR(machine, CheckEncryptionPassword(Bstr(strPassword).raw()));
1082 if (SUCCEEDED(hrc))
1083 RTPrintf("The given password is correct\n");
1084 }
1085 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1086}
1087
1088static const RTGETOPTDEF g_aAddVMOptions[] =
1089{
1090 { "--password", 'p', RTGETOPT_REQ_STRING },
1091 { "--password-id", 'i', RTGETOPT_REQ_STRING }
1092};
1093
1094RTEXITCODE handleAddVMPassword(HandlerArg *a, const char *pszFilenameOrUuid)
1095{
1096 HRESULT hrc;
1097 ComPtr<IMachine> machine;
1098 const char *pszPassword = NULL;
1099 const char *pszPasswordId = NULL;
1100 Utf8Str strPassword;
1101
1102 int c;
1103 RTGETOPTUNION ValueUnion;
1104 RTGETOPTSTATE GetState;
1105 // start at 0 because main() has hacked both the argc and argv given to us
1106 RTGetOptInit(&GetState, a->argc, a->argv, g_aAddVMOptions, RT_ELEMENTS(g_aAddVMOptions),
1107 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1108 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1109 {
1110 switch (c)
1111 {
1112 case 'p': // --password
1113 pszPassword = ValueUnion.psz;
1114 break;
1115
1116 case 'i': // --password-id
1117 pszPasswordId = ValueUnion.psz;
1118 break;
1119
1120 default:
1121 if (c > 0)
1122 {
1123 if (RT_C_IS_PRINT(c))
1124 return errorSyntax(Misc::tr("Invalid option -%c"), c);
1125 else
1126 return errorSyntax(Misc::tr("Invalid option case %i"), c);
1127 }
1128 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1129 return errorSyntax(Misc::tr("unknown option: %s\n"), ValueUnion.psz);
1130 else if (ValueUnion.pDef)
1131 return errorSyntax(Misc::tr("%s: %Rrs"), ValueUnion.pDef->pszLong, c);
1132 else
1133 return errorSyntax(Misc::tr("error: %Rrs"), c);
1134 }
1135 }
1136
1137 if (!pszFilenameOrUuid)
1138 return errorSyntax(Misc::tr("VM name or UUID required"));
1139
1140 if (!pszPassword)
1141 return errorSyntax(Misc::tr("No password specified"));
1142
1143 if (!pszPasswordId)
1144 return errorSyntax(Misc::tr("No password identifier specified"));
1145
1146 if (!RTStrCmp(pszPassword, "-"))
1147 {
1148 /* Get password from console. */
1149 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter the password:");
1150 if (rcExit == RTEXITCODE_FAILURE)
1151 return rcExit;
1152 }
1153 else
1154 {
1155 RTEXITCODE rcExit = readPasswordFile(pszPassword, &strPassword);
1156 if (rcExit == RTEXITCODE_FAILURE)
1157 {
1158 RTMsgError("Failed to read new password from file");
1159 return rcExit;
1160 }
1161 }
1162
1163 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(),
1164 machine.asOutParam()));
1165 if (machine)
1166 {
1167 ComPtr<IProgress> progress;
1168 CHECK_ERROR(machine, AddEncryptionPassword(Bstr(pszPasswordId).raw(), Bstr(strPassword).raw()));
1169 if (hrc == VBOX_E_PASSWORD_INCORRECT)
1170 RTMsgError("Password incorrect!");
1171 }
1172 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1173}
1174
1175RTEXITCODE handleRemoveVMPassword(HandlerArg *a, const char *pszFilenameOrUuid)
1176{
1177 HRESULT hrc;
1178 ComPtr<IMachine> machine;
1179
1180 if (a->argc != 1)
1181 return errorSyntax(Misc::tr("Invalid number of arguments: %d"), a->argc);
1182
1183 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszFilenameOrUuid).raw(),
1184 machine.asOutParam()));
1185 if (machine)
1186 {
1187 CHECK_ERROR(machine, RemoveEncryptionPassword(Bstr(a->argv[0]).raw()));
1188 if (hrc == VBOX_E_INVALID_VM_STATE)
1189 RTMsgError("The machine is in online or transient state\n");
1190 }
1191 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1192}
1193
1194RTEXITCODE handleEncryptVM(HandlerArg *a)
1195{
1196 if (a->argc < 2)
1197 return errorSyntax(Misc::tr("subcommand required"));
1198
1199 HandlerArg handlerArg;
1200 handlerArg.argc = a->argc - 2;
1201 handlerArg.argv = &a->argv[2];
1202 handlerArg.virtualBox = a->virtualBox;
1203 handlerArg.session = a->session;
1204 if (!strcmp(a->argv[1], "setencryption"))
1205 return handleSetVMEncryption(&handlerArg, a->argv[0]);
1206 if (!strcmp(a->argv[1], "checkpassword"))
1207 return handleCheckVMPassword(&handlerArg, a->argv[0]);
1208 if (!strcmp(a->argv[1], "addpassword"))
1209 return handleAddVMPassword(&handlerArg, a->argv[0]);
1210 if (!strcmp(a->argv[1], "removepassword"))
1211 return handleRemoveVMPassword(&handlerArg, a->argv[0]);
1212 return errorSyntax(Misc::tr("unknown subcommand"));
1213}
1214#endif /* !VBOX_WITH_FULL_VM_ENCRYPTION */
1215
1216RTEXITCODE handleDiscardState(HandlerArg *a)
1217{
1218 HRESULT hrc;
1219
1220 if (a->argc != 1)
1221 return errorSyntax(Misc::tr("Incorrect number of parameters"));
1222
1223 ComPtr<IMachine> machine;
1224 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1225 machine.asOutParam()));
1226 if (machine)
1227 {
1228 do
1229 {
1230 /* we have to open a session for this task */
1231 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
1232 do
1233 {
1234 ComPtr<IMachine> sessionMachine;
1235 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
1236 CHECK_ERROR_BREAK(sessionMachine, DiscardSavedState(true /* fDeleteFile */));
1237 } while (0);
1238 CHECK_ERROR_BREAK(a->session, UnlockMachine());
1239 } while (0);
1240 }
1241
1242 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1243}
1244
1245RTEXITCODE handleAdoptState(HandlerArg *a)
1246{
1247 HRESULT hrc;
1248
1249 if (a->argc != 2)
1250 return errorSyntax(Misc::tr("Incorrect number of parameters"));
1251
1252 ComPtr<IMachine> machine;
1253 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1254 machine.asOutParam()));
1255 if (machine)
1256 {
1257 char szStateFileAbs[RTPATH_MAX] = "";
1258 int vrc = RTPathAbs(a->argv[1], szStateFileAbs, sizeof(szStateFileAbs));
1259 if (RT_FAILURE(vrc))
1260 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Cannot convert filename \"%s\" to absolute path: %Rrc"),
1261 a->argv[0], vrc);
1262
1263 do
1264 {
1265 /* we have to open a session for this task */
1266 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
1267 do
1268 {
1269 ComPtr<IMachine> sessionMachine;
1270 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
1271 CHECK_ERROR_BREAK(sessionMachine, AdoptSavedState(Bstr(szStateFileAbs).raw()));
1272 } while (0);
1273 CHECK_ERROR_BREAK(a->session, UnlockMachine());
1274 } while (0);
1275 }
1276
1277 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1278}
1279
1280RTEXITCODE handleGetExtraData(HandlerArg *a)
1281{
1282 HRESULT hrc = S_OK;
1283
1284 if (a->argc > 2 || a->argc < 1)
1285 return errorSyntax(Misc::tr("Incorrect number of parameters"));
1286
1287 /* global data? */
1288 if (!strcmp(a->argv[0], "global"))
1289 {
1290 /* enumeration? */
1291 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
1292 {
1293 SafeArray<BSTR> aKeys;
1294 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
1295
1296 for (size_t i = 0;
1297 i < aKeys.size();
1298 ++i)
1299 {
1300 Bstr bstrKey(aKeys[i]);
1301 Bstr bstrValue;
1302 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
1303 bstrValue.asOutParam()));
1304
1305 RTPrintf(Misc::tr("Key: %ls, Value: %ls\n"), bstrKey.raw(), bstrValue.raw());
1306 }
1307 }
1308 else
1309 {
1310 Bstr value;
1311 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
1312 value.asOutParam()));
1313 if (!value.isEmpty())
1314 RTPrintf(Misc::tr("Value: %ls\n"), value.raw());
1315 else
1316 RTPrintf(Misc::tr("No value set!\n"));
1317 }
1318 }
1319 else
1320 {
1321 ComPtr<IMachine> machine;
1322 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1323 machine.asOutParam()));
1324 if (machine)
1325 {
1326 /* enumeration? */
1327 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
1328 {
1329 SafeArray<BSTR> aKeys;
1330 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
1331
1332 for (size_t i = 0;
1333 i < aKeys.size();
1334 ++i)
1335 {
1336 Bstr bstrKey(aKeys[i]);
1337 Bstr bstrValue;
1338 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
1339 bstrValue.asOutParam()));
1340
1341 RTPrintf(Misc::tr("Key: %ls, Value: %ls\n"), bstrKey.raw(), bstrValue.raw());
1342 }
1343 }
1344 else
1345 {
1346 Bstr value;
1347 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
1348 value.asOutParam()));
1349 if (!value.isEmpty())
1350 RTPrintf(Misc::tr("Value: %ls\n"), value.raw());
1351 else
1352 RTPrintf(Misc::tr("No value set!\n"));
1353 }
1354 }
1355 }
1356 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1357}
1358
1359RTEXITCODE handleSetExtraData(HandlerArg *a)
1360{
1361 HRESULT hrc = S_OK;
1362
1363 if (a->argc < 2)
1364 return errorSyntax(Misc::tr("Not enough parameters"));
1365
1366 /* global data? */
1367 if (!strcmp(a->argv[0], "global"))
1368 {
1369 /** @todo passing NULL is deprecated */
1370 if (a->argc < 3)
1371 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
1372 NULL));
1373 else if (a->argc == 3)
1374 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
1375 Bstr(a->argv[2]).raw()));
1376 else
1377 return errorSyntax(Misc::tr("Too many parameters"));
1378 }
1379 else
1380 {
1381 ComPtr<IMachine> machine;
1382 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
1383 machine.asOutParam()));
1384 if (machine)
1385 {
1386 /* open an existing session for the VM */
1387 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1388 /* get the session machine */
1389 ComPtr<IMachine> sessionMachine;
1390 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1391 /** @todo passing NULL is deprecated */
1392 if (a->argc < 3)
1393 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
1394 NULL));
1395 else if (a->argc == 3)
1396 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
1397 Bstr(a->argv[2]).raw()));
1398 else
1399 return errorSyntax(Misc::tr("Too many parameters"));
1400 }
1401 }
1402 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1403}
1404
1405RTEXITCODE handleSetProperty(HandlerArg *a)
1406{
1407 HRESULT hrc;
1408
1409 /* there must be two arguments: property name and value */
1410 if (a->argc != 2)
1411 return errorSyntax(Misc::tr("Incorrect number of parameters"));
1412
1413 ComPtr<ISystemProperties> systemProperties;
1414 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
1415
1416 if (!strcmp(a->argv[0], "machinefolder"))
1417 {
1418 /* reset to default? */
1419 if (!strcmp(a->argv[1], "default"))
1420 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
1421 else
1422 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
1423 }
1424 else if (!strcmp(a->argv[0], "hwvirtexclusive"))
1425 {
1426 bool fHwVirtExclusive;
1427
1428 if (!strcmp(a->argv[1], "on"))
1429 fHwVirtExclusive = true;
1430 else if (!strcmp(a->argv[1], "off"))
1431 fHwVirtExclusive = false;
1432 else
1433 return errorArgument(Misc::tr("Invalid hwvirtexclusive argument '%s'"), a->argv[1]);
1434 CHECK_ERROR(systemProperties, COMSETTER(ExclusiveHwVirt)(fHwVirtExclusive));
1435 }
1436 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
1437 || !strcmp(a->argv[0], "vrdpauthlibrary"))
1438 {
1439 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
1440 RTStrmPrintf(g_pStdErr, Misc::tr("Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n"));
1441
1442 /* reset to default? */
1443 if (!strcmp(a->argv[1], "default"))
1444 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
1445 else
1446 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
1447 }
1448 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
1449 {
1450 /* reset to default? */
1451 if (!strcmp(a->argv[1], "default"))
1452 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
1453 else
1454 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
1455 }
1456 else if (!strcmp(a->argv[0], "vrdeextpack"))
1457 {
1458 /* disable? */
1459 if (!strcmp(a->argv[1], "null"))
1460 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
1461 else
1462 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
1463 }
1464 else if (!strcmp(a->argv[0], "loghistorycount"))
1465 {
1466 uint32_t uVal;
1467 int vrc;
1468 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
1469 if (vrc != VINF_SUCCESS)
1470 return errorArgument(Misc::tr("Error parsing Log history count '%s'"), a->argv[1]);
1471 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
1472 }
1473 else if (!strcmp(a->argv[0], "autostartdbpath"))
1474 {
1475 /* disable? */
1476 if (!strcmp(a->argv[1], "null"))
1477 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(NULL));
1478 else
1479 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(Bstr(a->argv[1]).raw()));
1480 }
1481 else if (!strcmp(a->argv[0], "defaultfrontend"))
1482 {
1483 Bstr bstrDefaultFrontend(a->argv[1]);
1484 if (!strcmp(a->argv[1], "default"))
1485 bstrDefaultFrontend.setNull();
1486 CHECK_ERROR(systemProperties, COMSETTER(DefaultFrontend)(bstrDefaultFrontend.raw()));
1487 }
1488 else if (!strcmp(a->argv[0], "logginglevel"))
1489 {
1490 Bstr bstrLoggingLevel(a->argv[1]);
1491 if (!strcmp(a->argv[1], "default"))
1492 bstrLoggingLevel.setNull();
1493 CHECK_ERROR(systemProperties, COMSETTER(LoggingLevel)(bstrLoggingLevel.raw()));
1494 }
1495 else if (!strcmp(a->argv[0], "proxymode"))
1496 {
1497 ProxyMode_T enmProxyMode;
1498 if (!RTStrICmpAscii(a->argv[1], "system"))
1499 enmProxyMode = ProxyMode_System;
1500 else if (!RTStrICmpAscii(a->argv[1], "noproxy"))
1501 enmProxyMode = ProxyMode_NoProxy;
1502 else if (!RTStrICmpAscii(a->argv[1], "manual"))
1503 enmProxyMode = ProxyMode_Manual;
1504 else
1505 return errorArgument(Misc::tr("Unknown proxy mode: '%s'"), a->argv[1]);
1506 CHECK_ERROR(systemProperties, COMSETTER(ProxyMode)(enmProxyMode));
1507 }
1508 else if (!strcmp(a->argv[0], "proxyurl"))
1509 {
1510 Bstr bstrProxyUrl(a->argv[1]);
1511 CHECK_ERROR(systemProperties, COMSETTER(ProxyURL)(bstrProxyUrl.raw()));
1512 }
1513#ifdef VBOX_WITH_MAIN_NLS
1514 else if (!strcmp(a->argv[0], "language"))
1515 {
1516 Bstr bstrLanguage(a->argv[1]);
1517 CHECK_ERROR(systemProperties, COMSETTER(LanguageId)(bstrLanguage.raw()));
1518
1519 /* Kudge alert! Make sure the language change notification is processed,
1520 otherwise it may arrive as (XP)COM shuts down and cause
1521 trouble in debug builds. */
1522# ifdef DEBUG
1523 uint64_t const tsStart = RTTimeNanoTS();
1524# endif
1525 unsigned cMsgs = 0;
1526 int vrc;
1527 while ( RT_SUCCESS(vrc = NativeEventQueue::getMainEventQueue()->processEventQueue(32 /*ms*/))
1528 || vrc == VERR_INTERRUPTED)
1529 cMsgs++;
1530# ifdef DEBUG
1531 RTPrintf("vrc=%Rrc cMsgs=%u nsElapsed=%'RU64\n", vrc, cMsgs, RTTimeNanoTS() - tsStart);
1532# endif
1533 }
1534#endif
1535 else
1536 return errorSyntax(Misc::tr("Invalid parameter '%s'"), a->argv[0]);
1537
1538 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1539}
1540
1541/**
1542 * sharedfolder add
1543 */
1544static RTEXITCODE handleSharedFolderAdd(HandlerArg *a)
1545{
1546 /*
1547 * Parse arguments (argv[0] == subcommand).
1548 */
1549 static const RTGETOPTDEF s_aAddOptions[] =
1550 {
1551 { "--name", 'n', RTGETOPT_REQ_STRING },
1552 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
1553 { "--hostpath", 'p', RTGETOPT_REQ_STRING },
1554 { "-hostpath", 'p', RTGETOPT_REQ_STRING }, // deprecated
1555 { "--readonly", 'r', RTGETOPT_REQ_NOTHING },
1556 { "-readonly", 'r', RTGETOPT_REQ_NOTHING }, // deprecated
1557 { "--transient", 't', RTGETOPT_REQ_NOTHING },
1558 { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated
1559 { "--automount", 'a', RTGETOPT_REQ_NOTHING },
1560 { "-automount", 'a', RTGETOPT_REQ_NOTHING }, // deprecated
1561 { "--auto-mount-point", 'm', RTGETOPT_REQ_STRING },
1562 };
1563 const char *pszMachineName = NULL;
1564 const char *pszName = NULL;
1565 const char *pszHostPath = NULL;
1566 bool fTransient = false;
1567 bool fWritable = true;
1568 bool fAutoMount = false;
1569 const char *pszAutoMountPoint = "";
1570
1571 RTGETOPTSTATE GetState;
1572 RTGetOptInit(&GetState, a->argc, a->argv, s_aAddOptions, RT_ELEMENTS(s_aAddOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1573 int c;
1574 RTGETOPTUNION ValueUnion;
1575 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1576 {
1577 switch (c)
1578 {
1579 case 'n':
1580 pszName = ValueUnion.psz;
1581 break;
1582 case 'p':
1583 pszHostPath = ValueUnion.psz;
1584 break;
1585 case 'r':
1586 fWritable = false;
1587 break;
1588 case 't':
1589 fTransient = true;
1590 break;
1591 case 'a':
1592 fAutoMount = true;
1593 break;
1594 case 'm':
1595 pszAutoMountPoint = ValueUnion.psz;
1596 break;
1597 case VINF_GETOPT_NOT_OPTION:
1598 if (pszMachineName)
1599 return errorArgument(Misc::tr("Machine name is given more than once: first '%s', then '%s'"),
1600 pszMachineName, ValueUnion.psz);
1601 pszMachineName = ValueUnion.psz;
1602 break;
1603 default:
1604 return errorGetOpt(c, &ValueUnion);
1605 }
1606 }
1607
1608 if (!pszMachineName)
1609 return errorSyntax(Misc::tr("No machine was specified"));
1610
1611 if (!pszName)
1612 return errorSyntax(Misc::tr("No shared folder name (--name) was given"));
1613 if (strchr(pszName, ' '))
1614 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains space"), pszName);
1615 if (strchr(pszName, '\t'))
1616 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains tabs"), pszName);
1617 if (strchr(pszName, '\n') || strchr(pszName, '\r'))
1618 return errorSyntax(Misc::tr("Invalid shared folder name '%s': contains newline"), pszName);
1619
1620 if (!pszHostPath)
1621 return errorSyntax(Misc::tr("No host path (--hostpath) was given"));
1622 char szAbsHostPath[RTPATH_MAX];
1623 int vrc = RTPathAbs(pszHostPath, szAbsHostPath, sizeof(szAbsHostPath));
1624 if (RT_FAILURE(vrc))
1625 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTAbsPath failed on '%s': %Rrc"), pszHostPath, vrc);
1626
1627 /*
1628 * Done parsing, do some work.
1629 */
1630 ComPtr<IMachine> ptrMachine;
1631 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1632 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
1633
1634 HRESULT hrc;
1635 if (fTransient)
1636 {
1637 /* open an existing session for the VM */
1638 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1639
1640 /* get the session machine */
1641 ComPtr<IMachine> ptrSessionMachine;
1642 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1643
1644 /* get the session console */
1645 ComPtr<IConsole> ptrConsole;
1646 CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1647 if (ptrConsole.isNull())
1648 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' is not currently running."), pszMachineName);
1649
1650 CHECK_ERROR2(hrc, ptrConsole, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(),
1651 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1652 a->session->UnlockMachine();
1653 }
1654 else
1655 {
1656 /* open a session for the VM */
1657 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1658
1659 /* get the mutable session machine */
1660 ComPtr<IMachine> ptrSessionMachine;
1661 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1662
1663 CHECK_ERROR2(hrc, ptrSessionMachine, CreateSharedFolder(Bstr(pszName).raw(), Bstr(szAbsHostPath).raw(),
1664 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1665 if (SUCCEEDED(hrc))
1666 {
1667 CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings());
1668 }
1669
1670 a->session->UnlockMachine();
1671 }
1672
1673 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1674}
1675
1676/**
1677 * sharedfolder remove
1678 */
1679static RTEXITCODE handleSharedFolderRemove(HandlerArg *a)
1680{
1681 /*
1682 * Parse arguments (argv[0] == subcommand).
1683 */
1684 static const RTGETOPTDEF s_aRemoveOptions[] =
1685 {
1686 { "--name", 'n', RTGETOPT_REQ_STRING },
1687 { "-name", 'n', RTGETOPT_REQ_STRING }, // deprecated
1688 { "--transient", 't', RTGETOPT_REQ_NOTHING },
1689 { "-transient", 't', RTGETOPT_REQ_NOTHING }, // deprecated
1690 };
1691 const char *pszMachineName = NULL;
1692 const char *pszName = NULL;
1693 bool fTransient = false;
1694
1695 RTGETOPTSTATE GetState;
1696 RTGetOptInit(&GetState, a->argc, a->argv, s_aRemoveOptions, RT_ELEMENTS(s_aRemoveOptions), 1 /*iFirst*/, 0 /*fFlags*/);
1697 int c;
1698 RTGETOPTUNION ValueUnion;
1699 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1700 {
1701 switch (c)
1702 {
1703 case 'n':
1704 pszName = ValueUnion.psz;
1705 break;
1706 case 't':
1707 fTransient = true;
1708 break;
1709 case VINF_GETOPT_NOT_OPTION:
1710 if (pszMachineName)
1711 return errorArgument(Misc::tr("Machine name is given more than once: first '%s', then '%s'"),
1712 pszMachineName, ValueUnion.psz);
1713 pszMachineName = ValueUnion.psz;
1714 break;
1715 default:
1716 return errorGetOpt(c, &ValueUnion);
1717 }
1718 }
1719
1720 if (!pszMachineName)
1721 return errorSyntax(Misc::tr("No machine was specified"));
1722 if (!pszName)
1723 return errorSyntax(Misc::tr("No shared folder name (--name) was given"));
1724
1725 /*
1726 * Done parsing, do some real work.
1727 */
1728 ComPtr<IMachine> ptrMachine;
1729 CHECK_ERROR2I_RET(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1730 AssertReturn(ptrMachine.isNotNull(), RTEXITCODE_FAILURE);
1731
1732 HRESULT hrc;
1733 if (fTransient)
1734 {
1735 /* open an existing session for the VM */
1736 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1737 /* get the session machine */
1738 ComPtr<IMachine> ptrSessionMachine;
1739 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1740 /* get the session console */
1741 ComPtr<IConsole> ptrConsole;
1742 CHECK_ERROR2I_RET(a->session, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1743 if (ptrConsole.isNull())
1744 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%s' is not currently running.\n"), pszMachineName);
1745
1746 CHECK_ERROR2(hrc, ptrConsole, RemoveSharedFolder(Bstr(pszName).raw()));
1747
1748 a->session->UnlockMachine();
1749 }
1750 else
1751 {
1752 /* open a session for the VM */
1753 CHECK_ERROR2I_RET(ptrMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1754
1755 /* get the mutable session machine */
1756 ComPtr<IMachine> ptrSessionMachine;
1757 CHECK_ERROR2I_RET(a->session, COMGETTER(Machine)(ptrSessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1758
1759 CHECK_ERROR2(hrc, ptrSessionMachine, RemoveSharedFolder(Bstr(pszName).raw()));
1760
1761 /* commit and close the session */
1762 if (SUCCEEDED(hrc))
1763 {
1764 CHECK_ERROR2(hrc, ptrSessionMachine, SaveSettings());
1765 }
1766 a->session->UnlockMachine();
1767 }
1768
1769 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1770}
1771
1772
1773RTEXITCODE handleSharedFolder(HandlerArg *a)
1774{
1775 if (a->argc < 1)
1776 return errorSyntax(Misc::tr("Not enough parameters"));
1777
1778 if (!strcmp(a->argv[0], "add"))
1779 {
1780 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_ADD);
1781 return handleSharedFolderAdd(a);
1782 }
1783
1784 if (!strcmp(a->argv[0], "remove"))
1785 {
1786 setCurrentSubcommand(HELP_SCOPE_SHAREDFOLDER_REMOVE);
1787 return handleSharedFolderRemove(a);
1788 }
1789
1790 return errorUnknownSubcommand(a->argv[0]);
1791}
1792
1793RTEXITCODE handleExtPack(HandlerArg *a)
1794{
1795 if (a->argc < 1)
1796 return errorNoSubcommand();
1797
1798 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1799 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1800
1801 RTGETOPTSTATE GetState;
1802 RTGETOPTUNION ValueUnion;
1803 int ch;
1804 HRESULT hrc = S_OK;
1805
1806 if (!strcmp(a->argv[0], "install"))
1807 {
1808 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
1809 const char *pszName = NULL;
1810 bool fReplace = false;
1811
1812 static const RTGETOPTDEF s_aInstallOptions[] =
1813 {
1814 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1815 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
1816 };
1817
1818 RTCList<RTCString> lstLicenseHashes;
1819 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1820 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1821 {
1822 switch (ch)
1823 {
1824 case 'r':
1825 fReplace = true;
1826 break;
1827
1828 case 'a':
1829 lstLicenseHashes.append(ValueUnion.psz);
1830 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
1831 break;
1832
1833 case VINF_GETOPT_NOT_OPTION:
1834 if (pszName)
1835 return errorSyntax(Misc::tr("Too many extension pack names given to \"extpack uninstall\""));
1836 pszName = ValueUnion.psz;
1837 break;
1838
1839 default:
1840 return errorGetOpt(ch, &ValueUnion);
1841 }
1842 }
1843 if (!pszName)
1844 return errorSyntax(Misc::tr("No extension pack name was given to \"extpack install\""));
1845
1846 char szPath[RTPATH_MAX];
1847 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
1848 if (RT_FAILURE(vrc))
1849 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("RTPathAbs(%s,,) failed with rc=%Rrc"), pszName, vrc);
1850
1851 Bstr bstrTarball(szPath);
1852 Bstr bstrName;
1853 ComPtr<IExtPackFile> ptrExtPackFile;
1854 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1855 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1856 BOOL fShowLicense = true;
1857 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
1858 if (fShowLicense)
1859 {
1860 Bstr bstrLicense;
1861 CHECK_ERROR2I_RET(ptrExtPackFile,
1862 QueryLicense(Bstr("").raw() /* PreferredLocale */,
1863 Bstr("").raw() /* PreferredLanguage */,
1864 Bstr("txt").raw() /* Format */,
1865 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
1866 Utf8Str strLicense(bstrLicense);
1867 uint8_t abHash[RTSHA256_HASH_SIZE];
1868 char szDigest[RTSHA256_DIGEST_LEN + 1];
1869 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
1870 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
1871 AssertRCStmt(vrc, szDigest[0] = '\0');
1872 if (lstLicenseHashes.contains(szDigest))
1873 RTPrintf(Misc::tr("License accepted.\n"));
1874 else
1875 {
1876 RTPrintf("%s\n", strLicense.c_str());
1877 RTPrintf(Misc::tr("Do you agree to these license terms and conditions (y/n)? "));
1878 ch = RTStrmGetCh(g_pStdIn);
1879 RTPrintf("\n");
1880 if (ch != 'y' && ch != 'Y')
1881 {
1882 RTPrintf(Misc::tr("Installation of \"%ls\" aborted.\n"), bstrName.raw());
1883 return RTEXITCODE_FAILURE;
1884 }
1885 if (szDigest[0])
1886 RTPrintf(Misc::tr("License accepted. For batch installation add\n"
1887 "--accept-license=%s\n"
1888 "to the VBoxManage command line.\n\n"), szDigest);
1889 }
1890 }
1891 ComPtr<IProgress> ptrProgress;
1892 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1893 hrc = showProgress(ptrProgress);
1894 CHECK_PROGRESS_ERROR_RET(ptrProgress, (Misc::tr("Failed to install \"%s\""), szPath), RTEXITCODE_FAILURE);
1895
1896 RTPrintf(Misc::tr("Successfully installed \"%ls\".\n"), bstrName.raw());
1897 }
1898 else if (!strcmp(a->argv[0], "uninstall"))
1899 {
1900 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
1901 const char *pszName = NULL;
1902 bool fForced = false;
1903
1904 static const RTGETOPTDEF s_aUninstallOptions[] =
1905 {
1906 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1907 };
1908
1909 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1910 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1911 {
1912 switch (ch)
1913 {
1914 case 'f':
1915 fForced = true;
1916 break;
1917
1918 case VINF_GETOPT_NOT_OPTION:
1919 if (pszName)
1920 return errorSyntax(Misc::tr("Too many extension pack names given to \"extpack uninstall\""));
1921 pszName = ValueUnion.psz;
1922 break;
1923
1924 default:
1925 return errorGetOpt(ch, &ValueUnion);
1926 }
1927 }
1928 if (!pszName)
1929 return errorSyntax(Misc::tr("No extension pack name was given to \"extpack uninstall\""));
1930
1931 Bstr bstrName(pszName);
1932 ComPtr<IProgress> ptrProgress;
1933 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1934 hrc = showProgress(ptrProgress);
1935 CHECK_PROGRESS_ERROR_RET(ptrProgress, (Misc::tr("Failed to uninstall \"%s\""), pszName), RTEXITCODE_FAILURE);
1936
1937 RTPrintf(Misc::tr("Successfully uninstalled \"%s\".\n"), pszName);
1938 }
1939 else if (!strcmp(a->argv[0], "cleanup"))
1940 {
1941 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
1942 if (a->argc > 1)
1943 return errorTooManyParameters(&a->argv[1]);
1944 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1945 RTPrintf(Misc::tr("Successfully performed extension pack cleanup\n"));
1946 }
1947 else
1948 return errorUnknownSubcommand(a->argv[0]);
1949
1950 return RTEXITCODE_SUCCESS;
1951}
1952
1953RTEXITCODE handleUnattendedDetect(HandlerArg *a)
1954{
1955 HRESULT hrc;
1956
1957 /*
1958 * Options. We work directly on an IUnattended instace while parsing
1959 * the options. This saves a lot of extra clutter.
1960 */
1961 bool fMachineReadable = false;
1962 char szIsoPath[RTPATH_MAX];
1963 szIsoPath[0] = '\0';
1964
1965 /*
1966 * Parse options.
1967 */
1968 static const RTGETOPTDEF s_aOptions[] =
1969 {
1970 { "--iso", 'i', RTGETOPT_REQ_STRING },
1971 { "--machine-readable", 'M', RTGETOPT_REQ_NOTHING },
1972 };
1973
1974 RTGETOPTSTATE GetState;
1975 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1976 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1977
1978 int c;
1979 RTGETOPTUNION ValueUnion;
1980 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1981 {
1982 switch (c)
1983 {
1984 case 'i': // --iso
1985 vrc = RTPathAbs(ValueUnion.psz, szIsoPath, sizeof(szIsoPath));
1986 if (RT_FAILURE(vrc))
1987 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
1988 break;
1989
1990 case 'M': // --machine-readable.
1991 fMachineReadable = true;
1992 break;
1993
1994 default:
1995 return errorGetOpt(c, &ValueUnion);
1996 }
1997 }
1998
1999 /*
2000 * Check for required stuff.
2001 */
2002 if (szIsoPath[0] == '\0')
2003 return errorSyntax(Misc::tr("No ISO specified"));
2004
2005 /*
2006 * Do the job.
2007 */
2008 ComPtr<IUnattended> ptrUnattended;
2009 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
2010 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szIsoPath).raw()), RTEXITCODE_FAILURE);
2011 CHECK_ERROR2(hrc, ptrUnattended, DetectIsoOS());
2012 RTEXITCODE rcExit = SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2013
2014 /*
2015 * Retrieve the results.
2016 */
2017 Bstr bstrDetectedOSTypeId;
2018 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSTypeId)(bstrDetectedOSTypeId.asOutParam()), RTEXITCODE_FAILURE);
2019 Bstr bstrDetectedVersion;
2020 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSVersion)(bstrDetectedVersion.asOutParam()), RTEXITCODE_FAILURE);
2021 Bstr bstrDetectedFlavor;
2022 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSFlavor)(bstrDetectedFlavor.asOutParam()), RTEXITCODE_FAILURE);
2023 Bstr bstrDetectedLanguages;
2024 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSLanguages)(bstrDetectedLanguages.asOutParam()), RTEXITCODE_FAILURE);
2025 Bstr bstrDetectedHints;
2026 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSHints)(bstrDetectedHints.asOutParam()), RTEXITCODE_FAILURE);
2027 SafeArray<BSTR> aImageNames;
2028 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedImageNames)(ComSafeArrayAsOutParam(aImageNames)), RTEXITCODE_FAILURE);
2029 SafeArray<ULONG> aImageIndices;
2030 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedImageIndices)(ComSafeArrayAsOutParam(aImageIndices)), RTEXITCODE_FAILURE);
2031 Assert(aImageNames.size() == aImageIndices.size());
2032
2033 if (fMachineReadable)
2034 {
2035 outputMachineReadableString("OSTypeId", &bstrDetectedOSTypeId);
2036 outputMachineReadableString("OSVersion", &bstrDetectedVersion);
2037 outputMachineReadableString("OSFlavor", &bstrDetectedFlavor);
2038 outputMachineReadableString("OSLanguages", &bstrDetectedLanguages);
2039 outputMachineReadableString("OSHints", &bstrDetectedHints);
2040 for (size_t i = 0; i < aImageNames.size(); i++)
2041 {
2042 Bstr const bstrName = aImageNames[i];
2043 outputMachineReadableStringWithFmtName(&bstrName, false, "ImageIndex%u", aImageIndices[i]);
2044 }
2045 }
2046 else
2047 {
2048 RTMsgInfo(Misc::tr("Detected '%s' to be:\n"), szIsoPath);
2049 RTPrintf(Misc::tr(" OS TypeId = %ls\n"
2050 " OS Version = %ls\n"
2051 " OS Flavor = %ls\n"
2052 " OS Languages = %ls\n"
2053 " OS Hints = %ls\n"),
2054 bstrDetectedOSTypeId.raw(),
2055 bstrDetectedVersion.raw(),
2056 bstrDetectedFlavor.raw(),
2057 bstrDetectedLanguages.raw(),
2058 bstrDetectedHints.raw());
2059 for (size_t i = 0; i < aImageNames.size(); i++)
2060 RTPrintf(" Image #%-3u = %ls\n", aImageIndices[i], aImageNames[i]);
2061 }
2062
2063 return rcExit;
2064}
2065
2066RTEXITCODE handleUnattendedInstall(HandlerArg *a)
2067{
2068 HRESULT hrc;
2069 char szAbsPath[RTPATH_MAX];
2070
2071 /*
2072 * Options. We work directly on an IUnattended instance while parsing
2073 * the options. This saves a lot of extra clutter.
2074 */
2075 ComPtr<IUnattended> ptrUnattended;
2076 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
2077 RTCList<RTCString> arrPackageSelectionAdjustments;
2078 ComPtr<IMachine> ptrMachine;
2079 bool fDryRun = false;
2080 const char *pszSessionType = "none";
2081
2082 /*
2083 * Parse options.
2084 */
2085 static const RTGETOPTDEF s_aOptions[] =
2086 {
2087 { "--iso", 'i', RTGETOPT_REQ_STRING },
2088 { "--user", 'u', RTGETOPT_REQ_STRING },
2089 { "--password", 'p', RTGETOPT_REQ_STRING },
2090 { "--password-file", 'X', RTGETOPT_REQ_STRING },
2091 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
2092 { "--key", 'k', RTGETOPT_REQ_STRING },
2093 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
2094 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
2095 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
2096 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
2097 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
2098 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
2099 { "--locale", 'l', RTGETOPT_REQ_STRING },
2100 { "--country", 'Y', RTGETOPT_REQ_STRING },
2101 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
2102 { "--proxy", 'y', RTGETOPT_REQ_STRING },
2103 { "--hostname", 'H', RTGETOPT_REQ_STRING },
2104 { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
2105 { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
2106 // advance options:
2107 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
2108 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
2109 { "--script-template", 'c', RTGETOPT_REQ_STRING },
2110 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
2111 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
2112 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
2113 { "--language", 'L', RTGETOPT_REQ_STRING },
2114 // start vm related options:
2115 { "--start-vm", 'S', RTGETOPT_REQ_STRING },
2116 /** @todo Add a --wait option too for waiting for the VM to shut down or
2117 * something like that...? */
2118 };
2119
2120 RTGETOPTSTATE GetState;
2121 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2122 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2123
2124 int c;
2125 RTGETOPTUNION ValueUnion;
2126 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2127 {
2128 switch (c)
2129 {
2130 case VINF_GETOPT_NOT_OPTION:
2131 if (ptrMachine.isNotNull())
2132 return errorSyntax(Misc::tr("VM name/UUID given more than once!"));
2133 CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
2134 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
2135 break;
2136
2137 case 'i': // --iso
2138 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2139 if (RT_FAILURE(vrc))
2140 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2141 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2142 break;
2143
2144 case 'u': // --user
2145 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2146 break;
2147
2148 case 'p': // --password
2149 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2150 break;
2151
2152 case 'X': // --password-file
2153 {
2154 Utf8Str strPassword;
2155 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
2156 if (rcExit != RTEXITCODE_SUCCESS)
2157 return rcExit;
2158 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
2159 break;
2160 }
2161
2162 case 'U': // --full-user-name
2163 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2164 break;
2165
2166 case 'k': // --key
2167 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2168 break;
2169
2170 case 'A': // --install-additions
2171 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
2172 break;
2173 case 'N': // --no-install-additions
2174 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
2175 break;
2176 case 'a': // --additions-iso
2177 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2178 if (RT_FAILURE(vrc))
2179 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2180 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2181 break;
2182
2183 case 't': // --install-txs
2184 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
2185 break;
2186 case 'T': // --no-install-txs
2187 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
2188 break;
2189 case 'K': // --valiation-kit-iso
2190 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2191 if (RT_FAILURE(vrc))
2192 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2193 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2194 break;
2195
2196 case 'l': // --locale
2197 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2198 break;
2199
2200 case 'Y': // --country
2201 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2202 break;
2203
2204 case 'z': // --time-zone;
2205 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2206 break;
2207
2208 case 'y': // --proxy
2209 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2210 break;
2211
2212 case 'H': // --hostname
2213 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2214 break;
2215
2216 case 's': // --package-selection-adjustment
2217 arrPackageSelectionAdjustments.append(ValueUnion.psz);
2218 break;
2219
2220 case 'D':
2221 fDryRun = true;
2222 break;
2223
2224 case 'x': // --auxiliary-base-path
2225 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2226 if (RT_FAILURE(vrc))
2227 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2228 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2229 break;
2230
2231 case 'm': // --image-index
2232 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
2233 break;
2234
2235 case 'c': // --script-template
2236 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2237 if (RT_FAILURE(vrc))
2238 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2239 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2240 break;
2241
2242 case 'C': // --post-install-script-template
2243 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2244 if (RT_FAILURE(vrc))
2245 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2246 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2247 break;
2248
2249 case 'P': // --post-install-command.
2250 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2251 break;
2252
2253 case 'I': // --extra-install-kernel-parameters
2254 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2255 break;
2256
2257 case 'L': // --language
2258 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2259 break;
2260
2261 case 'S': // --start-vm
2262 pszSessionType = ValueUnion.psz;
2263 break;
2264
2265 default:
2266 return errorGetOpt(c, &ValueUnion);
2267 }
2268 }
2269
2270 /*
2271 * Check for required stuff.
2272 */
2273 if (ptrMachine.isNull())
2274 return errorSyntax(Misc::tr("Missing VM name/UUID"));
2275
2276 /*
2277 * Set accumulative attributes.
2278 */
2279 if (arrPackageSelectionAdjustments.size() == 1)
2280 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
2281 RTEXITCODE_FAILURE);
2282 else if (arrPackageSelectionAdjustments.size() > 1)
2283 {
2284 RTCString strAdjustments;
2285 strAdjustments.join(arrPackageSelectionAdjustments, ";");
2286 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
2287 }
2288
2289 /*
2290 * Get details about the machine so we can display them below.
2291 */
2292 Bstr bstrMachineName;
2293 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
2294 Bstr bstrUuid;
2295 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
2296 BSTR bstrInstalledOS;
2297 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
2298 Utf8Str strInstalledOS(bstrInstalledOS);
2299
2300 /*
2301 * Temporarily lock the machine to check whether it's running or not.
2302 * We take this opportunity to disable the first run wizard.
2303 */
2304 CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
2305 {
2306 ComPtr<IConsole> ptrConsole;
2307 CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
2308
2309 if ( ptrConsole.isNull()
2310 && SUCCEEDED(hrc)
2311 && ( RTStrICmp(pszSessionType, "gui") == 0
2312 || RTStrICmp(pszSessionType, "none") == 0))
2313 {
2314 ComPtr<IMachine> ptrSessonMachine;
2315 CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
2316 if (ptrSessonMachine.isNotNull())
2317 {
2318 CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
2319 }
2320 }
2321
2322 a->session->UnlockMachine();
2323 if (FAILED(hrc))
2324 return RTEXITCODE_FAILURE;
2325 if (ptrConsole.isNotNull())
2326 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%ls' is currently running"), bstrMachineName.raw());
2327 }
2328
2329 /*
2330 * Do the work.
2331 */
2332 RTMsgInfo(Misc::tr("%s unattended installation of %s in machine '%ls' (%ls).\n"),
2333 RTStrICmp(pszSessionType, "none") == 0 ? Misc::tr("Preparing") : Misc::tr("Starting"),
2334 strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
2335
2336 CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
2337 if (!fDryRun)
2338 {
2339 CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
2340 CHECK_ERROR2_RET(hrc, ptrUnattended, ReconfigureVM(), RTEXITCODE_FAILURE);
2341 }
2342
2343 /*
2344 * Retrieve and display the parameters actually used.
2345 */
2346 RTMsgInfo(Misc::tr("Using values:\n"));
2347#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
2348 a_Type Value; \
2349 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
2350 if (SUCCEEDED(hrc2)) \
2351 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
2352 else \
2353 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
2354 } while (0)
2355#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
2356 Bstr bstrString; \
2357 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
2358 if (SUCCEEDED(hrc2)) \
2359 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
2360 else \
2361 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
2362 } while (0)
2363
2364 SHOW_STR_ATTR(IsoPath, "isoPath");
2365 SHOW_STR_ATTR(User, "user");
2366 SHOW_STR_ATTR(Password, "password");
2367 SHOW_STR_ATTR(FullUserName, "fullUserName");
2368 SHOW_STR_ATTR(ProductKey, "productKey");
2369 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
2370 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
2371 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
2372 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
2373 SHOW_STR_ATTR(Locale, "locale");
2374 SHOW_STR_ATTR(Country, "country");
2375 SHOW_STR_ATTR(TimeZone, "timeZone");
2376 SHOW_STR_ATTR(Proxy, "proxy");
2377 SHOW_STR_ATTR(Hostname, "hostname");
2378 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
2379 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
2380 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
2381 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
2382 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
2383 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
2384 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
2385 SHOW_STR_ATTR(Language, "language");
2386 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
2387 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
2388 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
2389 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
2390 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
2391 {
2392 ULONG idxImage = 0;
2393 HRESULT hrc2 = ptrUnattended->COMGETTER(ImageIndex)(&idxImage);
2394 if (FAILED(hrc2))
2395 idxImage = 0;
2396 SafeArray<BSTR> aImageNames;
2397 hrc2 = ptrUnattended->COMGETTER(DetectedImageNames)(ComSafeArrayAsOutParam(aImageNames));
2398 if (SUCCEEDED(hrc2))
2399 {
2400 SafeArray<ULONG> aImageIndices;
2401 hrc2 = ptrUnattended->COMGETTER(DetectedImageIndices)(ComSafeArrayAsOutParam(aImageIndices));
2402 if (SUCCEEDED(hrc2))
2403 {
2404 Assert(aImageNames.size() == aImageIndices.size());
2405 for (size_t i = 0; i < aImageNames.size(); i++)
2406 {
2407 char szTmp[64];
2408 RTStrPrintf(szTmp, sizeof(szTmp), "detectedImage[%u]%s", i, idxImage != aImageIndices[i] ? "" : "*");
2409 RTPrintf(" %32s = #%u: %ls\n", szTmp, aImageIndices[i], aImageNames[i]);
2410 }
2411 }
2412 else
2413 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), "detectedImageIndices", hrc2);
2414 }
2415 else
2416 RTPrintf(Misc::tr(" %32 = failed: %Rhrc\n"), "detectedImageNames", hrc2);
2417 }
2418
2419#undef SHOW_STR_ATTR
2420#undef SHOW_ATTR
2421
2422 /* We can drop the IUnatteded object now. */
2423 ptrUnattended.setNull();
2424
2425 /*
2426 * Start the VM if requested.
2427 */
2428 if ( fDryRun
2429 || RTStrICmp(pszSessionType, "none") == 0)
2430 {
2431 if (!fDryRun)
2432 RTMsgInfo(Misc::tr("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n"), bstrMachineName.raw(), bstrUuid.raw());
2433 hrc = S_OK;
2434 }
2435 else
2436 {
2437 com::SafeArray<IN_BSTR> aBstrEnv;
2438#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
2439 /* make sure the VM process will start on the same display as VBoxManage */
2440 const char *pszDisplay = RTEnvGet("DISPLAY");
2441 if (pszDisplay)
2442 aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw());
2443 const char *pszXAuth = RTEnvGet("XAUTHORITY");
2444 if (pszXAuth)
2445 aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw());
2446#endif
2447 ComPtr<IProgress> ptrProgress;
2448 CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), ComSafeArrayAsInParam(aBstrEnv), ptrProgress.asOutParam()));
2449 if (SUCCEEDED(hrc) && !ptrProgress.isNull())
2450 {
2451 RTMsgInfo(Misc::tr("Waiting for VM '%ls' to power on...\n"), bstrMachineName.raw());
2452 CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
2453 if (SUCCEEDED(hrc))
2454 {
2455 BOOL fCompleted = true;
2456 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
2457 if (SUCCEEDED(hrc))
2458 {
2459 ASSERT(fCompleted);
2460
2461 LONG iRc;
2462 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
2463 if (SUCCEEDED(hrc))
2464 {
2465 if (SUCCEEDED(iRc))
2466 RTMsgInfo(Misc::tr("VM '%ls' (%ls) has been successfully started.\n"),
2467 bstrMachineName.raw(), bstrUuid.raw());
2468 else
2469 {
2470 ProgressErrorInfo info(ptrProgress);
2471 com::GluePrintErrorInfo(info);
2472 }
2473 hrc = iRc;
2474 }
2475 }
2476 }
2477 }
2478
2479 /*
2480 * Do we wait for the VM to power down?
2481 */
2482 }
2483
2484 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2485}
2486
2487
2488RTEXITCODE handleUnattended(HandlerArg *a)
2489{
2490 /*
2491 * Sub-command switch.
2492 */
2493 if (a->argc < 1)
2494 return errorNoSubcommand();
2495
2496 if (!strcmp(a->argv[0], "detect"))
2497 {
2498 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
2499 return handleUnattendedDetect(a);
2500 }
2501
2502 if (!strcmp(a->argv[0], "install"))
2503 {
2504 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
2505 return handleUnattendedInstall(a);
2506 }
2507
2508 /* Consider some kind of create-vm-and-install-guest-os command. */
2509 return errorUnknownSubcommand(a->argv[0]);
2510}
2511
2512/**
2513 * Common Cloud profile options.
2514 */
2515typedef struct
2516{
2517 const char *pszProviderName;
2518 const char *pszProfileName;
2519} CLOUDPROFILECOMMONOPT;
2520typedef CLOUDPROFILECOMMONOPT *PCLOUDPROFILECOMMONOPT;
2521
2522/**
2523 * Sets the properties of cloud profile
2524 *
2525 * @returns 0 on success, 1 on failure
2526 */
2527
2528static RTEXITCODE setCloudProfileProperties(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2529{
2530
2531 HRESULT hrc = S_OK;
2532
2533 Bstr bstrProvider(pCommonOpts->pszProviderName);
2534 Bstr bstrProfile(pCommonOpts->pszProfileName);
2535
2536 /*
2537 * Parse options.
2538 */
2539 static const RTGETOPTDEF s_aOptions[] =
2540 {
2541 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2542 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2543 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2544 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2545 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2546 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2547 { "--region", 'r', RTGETOPT_REQ_STRING }
2548 };
2549
2550 RTGETOPTSTATE GetState;
2551 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2552 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2553
2554 com::SafeArray<BSTR> names;
2555 com::SafeArray<BSTR> values;
2556
2557 int c;
2558 RTGETOPTUNION ValueUnion;
2559 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2560 {
2561 switch (c)
2562 {
2563 case 'u': // --clouduser
2564 Bstr("user").detachTo(names.appendedRaw());
2565 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2566 break;
2567 case 'p': // --fingerprint
2568 Bstr("fingerprint").detachTo(names.appendedRaw());
2569 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2570 break;
2571 case 'k': // --keyfile
2572 Bstr("key_file").detachTo(names.appendedRaw());
2573 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2574 break;
2575 case 'P': // --passphrase
2576 Bstr("pass_phrase").detachTo(names.appendedRaw());
2577 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2578 break;
2579 case 't': // --tenancy
2580 Bstr("tenancy").detachTo(names.appendedRaw());
2581 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2582 break;
2583 case 'c': // --compartment
2584 Bstr("compartment").detachTo(names.appendedRaw());
2585 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2586 break;
2587 case 'r': // --region
2588 Bstr("region").detachTo(names.appendedRaw());
2589 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2590 break;
2591 default:
2592 return errorGetOpt(c, &ValueUnion);
2593 }
2594 }
2595
2596 /* check for required options */
2597 if (bstrProvider.isEmpty())
2598 return errorSyntax(Misc::tr("Parameter --provider is required"));
2599 if (bstrProfile.isEmpty())
2600 return errorSyntax(Misc::tr("Parameter --profile is required"));
2601
2602 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2603
2604 ComPtr<ICloudProviderManager> pCloudProviderManager;
2605 CHECK_ERROR2_RET(hrc, pVirtualBox,
2606 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2607 RTEXITCODE_FAILURE);
2608
2609 ComPtr<ICloudProvider> pCloudProvider;
2610
2611 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2612 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2613 RTEXITCODE_FAILURE);
2614
2615 ComPtr<ICloudProfile> pCloudProfile;
2616
2617 if (pCloudProvider)
2618 {
2619 CHECK_ERROR2_RET(hrc, pCloudProvider,
2620 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2621 RTEXITCODE_FAILURE);
2622 CHECK_ERROR2_RET(hrc, pCloudProfile,
2623 SetProperties(ComSafeArrayAsInParam(names), ComSafeArrayAsInParam(values)),
2624 RTEXITCODE_FAILURE);
2625 }
2626
2627 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2628
2629 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was updated.\n"),bstrProvider.raw(), bstrProfile.raw());
2630
2631 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2632}
2633
2634/**
2635 * Gets the properties of cloud profile
2636 *
2637 * @returns 0 on success, 1 on failure
2638 */
2639static RTEXITCODE showCloudProfileProperties(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2640{
2641 HRESULT hrc = S_OK;
2642
2643 Bstr bstrProvider(pCommonOpts->pszProviderName);
2644 Bstr bstrProfile(pCommonOpts->pszProfileName);
2645
2646 /* check for required options */
2647 if (bstrProvider.isEmpty())
2648 return errorSyntax(Misc::tr("Parameter --provider is required"));
2649 if (bstrProfile.isEmpty())
2650 return errorSyntax(Misc::tr("Parameter --profile is required"));
2651
2652 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2653 ComPtr<ICloudProviderManager> pCloudProviderManager;
2654 CHECK_ERROR2_RET(hrc, pVirtualBox,
2655 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2656 RTEXITCODE_FAILURE);
2657 ComPtr<ICloudProvider> pCloudProvider;
2658 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2659 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2660 RTEXITCODE_FAILURE);
2661
2662 ComPtr<ICloudProfile> pCloudProfile;
2663 if (pCloudProvider)
2664 {
2665 CHECK_ERROR2_RET(hrc, pCloudProvider,
2666 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2667 RTEXITCODE_FAILURE);
2668
2669 Bstr bstrProviderID;
2670 pCloudProfile->COMGETTER(ProviderId)(bstrProviderID.asOutParam());
2671 RTPrintf(Misc::tr("Provider GUID: %ls\n"), bstrProviderID.raw());
2672
2673 com::SafeArray<BSTR> names;
2674 com::SafeArray<BSTR> values;
2675 CHECK_ERROR2_RET(hrc, pCloudProfile,
2676 GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values)),
2677 RTEXITCODE_FAILURE);
2678 size_t cNames = names.size();
2679 size_t cValues = values.size();
2680 bool fFirst = true;
2681 for (size_t k = 0; k < cNames; k++)
2682 {
2683 Bstr value;
2684 if (k < cValues)
2685 value = values[k];
2686 RTPrintf("%s%ls=%ls\n",
2687 fFirst ? Misc::tr("Property: ") : " ",
2688 names[k], value.raw());
2689 fFirst = false;
2690 }
2691
2692 RTPrintf("\n");
2693 }
2694
2695 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2696}
2697
2698static RTEXITCODE addCloudProfile(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2699{
2700 HRESULT hrc = S_OK;
2701
2702 Bstr bstrProvider(pCommonOpts->pszProviderName);
2703 Bstr bstrProfile(pCommonOpts->pszProfileName);
2704
2705
2706 /* check for required options */
2707 if (bstrProvider.isEmpty())
2708 return errorSyntax(Misc::tr("Parameter --provider is required"));
2709 if (bstrProfile.isEmpty())
2710 return errorSyntax(Misc::tr("Parameter --profile is required"));
2711
2712 /*
2713 * Parse options.
2714 */
2715 static const RTGETOPTDEF s_aOptions[] =
2716 {
2717 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2718 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2719 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2720 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2721 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2722 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2723 { "--region", 'r', RTGETOPT_REQ_STRING }
2724 };
2725
2726 RTGETOPTSTATE GetState;
2727 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2728 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2729
2730 com::SafeArray<BSTR> names;
2731 com::SafeArray<BSTR> values;
2732
2733 int c;
2734 RTGETOPTUNION ValueUnion;
2735 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2736 {
2737 switch (c)
2738 {
2739 case 'u': // --clouduser
2740 Bstr("user").detachTo(names.appendedRaw());
2741 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2742 break;
2743 case 'p': // --fingerprint
2744 Bstr("fingerprint").detachTo(names.appendedRaw());
2745 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2746 break;
2747 case 'k': // --keyfile
2748 Bstr("key_file").detachTo(names.appendedRaw());
2749 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2750 break;
2751 case 'P': // --passphrase
2752 Bstr("pass_phrase").detachTo(names.appendedRaw());
2753 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2754 break;
2755 case 't': // --tenancy
2756 Bstr("tenancy").detachTo(names.appendedRaw());
2757 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2758 break;
2759 case 'c': // --compartment
2760 Bstr("compartment").detachTo(names.appendedRaw());
2761 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2762 break;
2763 case 'r': // --region
2764 Bstr("region").detachTo(names.appendedRaw());
2765 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2766 break;
2767 default:
2768 return errorGetOpt(c, &ValueUnion);
2769 }
2770 }
2771
2772 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2773
2774 ComPtr<ICloudProviderManager> pCloudProviderManager;
2775 CHECK_ERROR2_RET(hrc, pVirtualBox,
2776 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2777 RTEXITCODE_FAILURE);
2778
2779 ComPtr<ICloudProvider> pCloudProvider;
2780 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2781 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2782 RTEXITCODE_FAILURE);
2783
2784 CHECK_ERROR2_RET(hrc, pCloudProvider,
2785 CreateProfile(bstrProfile.raw(),
2786 ComSafeArrayAsInParam(names),
2787 ComSafeArrayAsInParam(values)),
2788 RTEXITCODE_FAILURE);
2789
2790 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2791
2792 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was added.\n"),bstrProvider.raw(), bstrProfile.raw());
2793
2794 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2795}
2796
2797static RTEXITCODE deleteCloudProfile(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2798{
2799 HRESULT hrc = S_OK;
2800
2801 Bstr bstrProvider(pCommonOpts->pszProviderName);
2802 Bstr bstrProfile(pCommonOpts->pszProfileName);
2803
2804 /* check for required options */
2805 if (bstrProvider.isEmpty())
2806 return errorSyntax(Misc::tr("Parameter --provider is required"));
2807 if (bstrProfile.isEmpty())
2808 return errorSyntax(Misc::tr("Parameter --profile is required"));
2809
2810 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2811 ComPtr<ICloudProviderManager> pCloudProviderManager;
2812 CHECK_ERROR2_RET(hrc, pVirtualBox,
2813 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2814 RTEXITCODE_FAILURE);
2815 ComPtr<ICloudProvider> pCloudProvider;
2816 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2817 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2818 RTEXITCODE_FAILURE);
2819
2820 ComPtr<ICloudProfile> pCloudProfile;
2821 if (pCloudProvider)
2822 {
2823 CHECK_ERROR2_RET(hrc, pCloudProvider,
2824 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2825 RTEXITCODE_FAILURE);
2826
2827 CHECK_ERROR2_RET(hrc, pCloudProfile,
2828 Remove(),
2829 RTEXITCODE_FAILURE);
2830
2831 CHECK_ERROR2_RET(hrc, pCloudProvider,
2832 SaveProfiles(),
2833 RTEXITCODE_FAILURE);
2834
2835 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was deleted.\n"), bstrProvider.raw(), bstrProfile.raw());
2836 }
2837
2838 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2839}
2840
2841RTEXITCODE handleCloudProfile(HandlerArg *a)
2842{
2843 if (a->argc < 1)
2844 return errorNoSubcommand();
2845
2846 static const RTGETOPTDEF s_aOptions[] =
2847 {
2848 /* common options */
2849 { "--provider", 'v', RTGETOPT_REQ_STRING },
2850 { "--profile", 'f', RTGETOPT_REQ_STRING },
2851 /* subcommands */
2852 { "add", 1000, RTGETOPT_REQ_NOTHING },
2853 { "show", 1001, RTGETOPT_REQ_NOTHING },
2854 { "update", 1002, RTGETOPT_REQ_NOTHING },
2855 { "delete", 1003, RTGETOPT_REQ_NOTHING },
2856 };
2857
2858 RTGETOPTSTATE GetState;
2859 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2860 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2861
2862 CLOUDPROFILECOMMONOPT CommonOpts = { NULL, NULL };
2863 int c;
2864 RTGETOPTUNION ValueUnion;
2865 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2866 {
2867 switch (c)
2868 {
2869 case 'v': // --provider
2870 CommonOpts.pszProviderName = ValueUnion.psz;
2871 break;
2872 case 'f': // --profile
2873 CommonOpts.pszProfileName = ValueUnion.psz;
2874 break;
2875 /* Sub-commands: */
2876 case 1000:
2877 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_ADD);
2878 return addCloudProfile(a, GetState.iNext, &CommonOpts);
2879 case 1001:
2880 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_SHOW);
2881 return showCloudProfileProperties(a, &CommonOpts);
2882 case 1002:
2883 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_UPDATE);
2884 return setCloudProfileProperties(a, GetState.iNext, &CommonOpts);
2885 case 1003:
2886 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_DELETE);
2887 return deleteCloudProfile(a, &CommonOpts);
2888 case VINF_GETOPT_NOT_OPTION:
2889 return errorUnknownSubcommand(ValueUnion.psz);
2890
2891 default:
2892 return errorGetOpt(c, &ValueUnion);
2893 }
2894 }
2895
2896 return errorNoSubcommand();
2897}
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