VirtualBox

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

Last change on this file since 98186 was 98103, checked in by vboxsync, 21 months ago

Copyright year updates by scm.

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