VirtualBox

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

Last change on this file since 97367 was 97367, checked in by vboxsync, 2 years ago

Backed out r154357,r154358,r154360,r154361,r154364 because it still doesn't build, old files weren't renamed or removed, and the changes for bugref:10180 and bugref:4787 got mixed up, making it difficult to selectively revert the latter one only.

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