VirtualBox

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

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

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.6 KB
Line 
1/* $Id: VBoxManageMisc.cpp 96407 2022-08-22 17:43:14Z 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
2043 if (fMachineReadable)
2044 {
2045 outputMachineReadableString("OSTypeId", &bstrDetectedOSTypeId);
2046 outputMachineReadableString("OSVersion", &bstrDetectedVersion);
2047 outputMachineReadableString("OSFlavor", &bstrDetectedFlavor);
2048 outputMachineReadableString("OSLanguages", &bstrDetectedLanguages);
2049 outputMachineReadableString("OSHints", &bstrDetectedHints);
2050 for (size_t i = 0; i < aImageNames.size(); i++)
2051 {
2052 Bstr const bstrName = aImageNames[i];
2053 outputMachineReadableStringWithFmtName(&bstrName, false, "ImageIndex%u", aImageIndices[i]);
2054 }
2055 }
2056 else
2057 {
2058 RTMsgInfo(Misc::tr("Detected '%s' to be:\n"), szIsoPath);
2059 RTPrintf(Misc::tr(" OS TypeId = %ls\n"
2060 " OS Version = %ls\n"
2061 " OS Flavor = %ls\n"
2062 " OS Languages = %ls\n"
2063 " OS Hints = %ls\n"),
2064 bstrDetectedOSTypeId.raw(),
2065 bstrDetectedVersion.raw(),
2066 bstrDetectedFlavor.raw(),
2067 bstrDetectedLanguages.raw(),
2068 bstrDetectedHints.raw());
2069 for (size_t i = 0; i < aImageNames.size(); i++)
2070 RTPrintf(" Image #%-3u = %ls\n", aImageIndices[i], aImageNames[i]);
2071 }
2072
2073 return rcExit;
2074}
2075
2076RTEXITCODE handleUnattendedInstall(HandlerArg *a)
2077{
2078 HRESULT hrc;
2079 char szAbsPath[RTPATH_MAX];
2080
2081 /*
2082 * Options. We work directly on an IUnattended instance while parsing
2083 * the options. This saves a lot of extra clutter.
2084 */
2085 ComPtr<IUnattended> ptrUnattended;
2086 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
2087 RTCList<RTCString> arrPackageSelectionAdjustments;
2088 ComPtr<IMachine> ptrMachine;
2089 bool fDryRun = false;
2090 const char *pszSessionType = "none";
2091
2092 /*
2093 * Parse options.
2094 */
2095 static const RTGETOPTDEF s_aOptions[] =
2096 {
2097 { "--iso", 'i', RTGETOPT_REQ_STRING },
2098 { "--user", 'u', RTGETOPT_REQ_STRING },
2099 { "--password", 'p', RTGETOPT_REQ_STRING },
2100 { "--password-file", 'X', RTGETOPT_REQ_STRING },
2101 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
2102 { "--key", 'k', RTGETOPT_REQ_STRING },
2103 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
2104 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
2105 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
2106 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
2107 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
2108 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
2109 { "--locale", 'l', RTGETOPT_REQ_STRING },
2110 { "--country", 'Y', RTGETOPT_REQ_STRING },
2111 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
2112 { "--proxy", 'y', RTGETOPT_REQ_STRING },
2113 { "--hostname", 'H', RTGETOPT_REQ_STRING },
2114 { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
2115 { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
2116 // advance options:
2117 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
2118 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
2119 { "--script-template", 'c', RTGETOPT_REQ_STRING },
2120 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
2121 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
2122 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
2123 { "--language", 'L', RTGETOPT_REQ_STRING },
2124 // start vm related options:
2125 { "--start-vm", 'S', RTGETOPT_REQ_STRING },
2126 /** @todo Add a --wait option too for waiting for the VM to shut down or
2127 * something like that...? */
2128 };
2129
2130 RTGETOPTSTATE GetState;
2131 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2132 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2133
2134 int c;
2135 RTGETOPTUNION ValueUnion;
2136 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2137 {
2138 switch (c)
2139 {
2140 case VINF_GETOPT_NOT_OPTION:
2141 if (ptrMachine.isNotNull())
2142 return errorSyntax(Misc::tr("VM name/UUID given more than once!"));
2143 CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
2144 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
2145 break;
2146
2147 case 'i': // --iso
2148 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2149 if (RT_FAILURE(vrc))
2150 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2151 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2152 break;
2153
2154 case 'u': // --user
2155 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2156 break;
2157
2158 case 'p': // --password
2159 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2160 break;
2161
2162 case 'X': // --password-file
2163 {
2164 Utf8Str strPassword;
2165 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
2166 if (rcExit != RTEXITCODE_SUCCESS)
2167 return rcExit;
2168 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
2169 break;
2170 }
2171
2172 case 'U': // --full-user-name
2173 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2174 break;
2175
2176 case 'k': // --key
2177 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2178 break;
2179
2180 case 'A': // --install-additions
2181 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
2182 break;
2183 case 'N': // --no-install-additions
2184 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
2185 break;
2186 case 'a': // --additions-iso
2187 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2188 if (RT_FAILURE(vrc))
2189 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2190 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2191 break;
2192
2193 case 't': // --install-txs
2194 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
2195 break;
2196 case 'T': // --no-install-txs
2197 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
2198 break;
2199 case 'K': // --valiation-kit-iso
2200 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2201 if (RT_FAILURE(vrc))
2202 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2203 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2204 break;
2205
2206 case 'l': // --locale
2207 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2208 break;
2209
2210 case 'Y': // --country
2211 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2212 break;
2213
2214 case 'z': // --time-zone;
2215 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2216 break;
2217
2218 case 'y': // --proxy
2219 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2220 break;
2221
2222 case 'H': // --hostname
2223 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2224 break;
2225
2226 case 's': // --package-selection-adjustment
2227 arrPackageSelectionAdjustments.append(ValueUnion.psz);
2228 break;
2229
2230 case 'D':
2231 fDryRun = true;
2232 break;
2233
2234 case 'x': // --auxiliary-base-path
2235 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2236 if (RT_FAILURE(vrc))
2237 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2238 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2239 break;
2240
2241 case 'm': // --image-index
2242 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
2243 break;
2244
2245 case 'c': // --script-template
2246 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
2247 if (RT_FAILURE(vrc))
2248 return errorSyntax(Misc::tr("RTPathAbs failed on '%s': %Rrc"), ValueUnion.psz, vrc);
2249 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2250 break;
2251
2252 case 'C': // --post-install-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(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
2257 break;
2258
2259 case 'P': // --post-install-command.
2260 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2261 break;
2262
2263 case 'I': // --extra-install-kernel-parameters
2264 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2265 break;
2266
2267 case 'L': // --language
2268 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
2269 break;
2270
2271 case 'S': // --start-vm
2272 pszSessionType = ValueUnion.psz;
2273 break;
2274
2275 default:
2276 return errorGetOpt(c, &ValueUnion);
2277 }
2278 }
2279
2280 /*
2281 * Check for required stuff.
2282 */
2283 if (ptrMachine.isNull())
2284 return errorSyntax(Misc::tr("Missing VM name/UUID"));
2285
2286 /*
2287 * Set accumulative attributes.
2288 */
2289 if (arrPackageSelectionAdjustments.size() == 1)
2290 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
2291 RTEXITCODE_FAILURE);
2292 else if (arrPackageSelectionAdjustments.size() > 1)
2293 {
2294 RTCString strAdjustments;
2295 strAdjustments.join(arrPackageSelectionAdjustments, ";");
2296 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
2297 }
2298
2299 /*
2300 * Get details about the machine so we can display them below.
2301 */
2302 Bstr bstrMachineName;
2303 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
2304 Bstr bstrUuid;
2305 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
2306 BSTR bstrInstalledOS;
2307 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
2308 Utf8Str strInstalledOS(bstrInstalledOS);
2309
2310 /*
2311 * Temporarily lock the machine to check whether it's running or not.
2312 * We take this opportunity to disable the first run wizard.
2313 */
2314 CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
2315 {
2316 ComPtr<IConsole> ptrConsole;
2317 CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
2318
2319 if ( ptrConsole.isNull()
2320 && SUCCEEDED(hrc)
2321 && ( RTStrICmp(pszSessionType, "gui") == 0
2322 || RTStrICmp(pszSessionType, "none") == 0))
2323 {
2324 ComPtr<IMachine> ptrSessonMachine;
2325 CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
2326 if (ptrSessonMachine.isNotNull())
2327 {
2328 CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
2329 }
2330 }
2331
2332 a->session->UnlockMachine();
2333 if (FAILED(hrc))
2334 return RTEXITCODE_FAILURE;
2335 if (ptrConsole.isNotNull())
2336 return RTMsgErrorExit(RTEXITCODE_FAILURE, Misc::tr("Machine '%ls' is currently running"), bstrMachineName.raw());
2337 }
2338
2339 /*
2340 * Do the work.
2341 */
2342 RTMsgInfo(Misc::tr("%s unattended installation of %s in machine '%ls' (%ls).\n"),
2343 RTStrICmp(pszSessionType, "none") == 0 ? Misc::tr("Preparing") : Misc::tr("Starting"),
2344 strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
2345
2346 CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
2347 if (!fDryRun)
2348 {
2349 CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
2350 CHECK_ERROR2_RET(hrc, ptrUnattended, ReconfigureVM(), RTEXITCODE_FAILURE);
2351 }
2352
2353 /*
2354 * Retrieve and display the parameters actually used.
2355 */
2356 RTMsgInfo(Misc::tr("Using values:\n"));
2357#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
2358 a_Type Value; \
2359 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
2360 if (SUCCEEDED(hrc2)) \
2361 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
2362 else \
2363 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
2364 } while (0)
2365#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
2366 Bstr bstrString; \
2367 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
2368 if (SUCCEEDED(hrc2)) \
2369 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
2370 else \
2371 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), a_szText, hrc2); \
2372 } while (0)
2373
2374 SHOW_STR_ATTR(IsoPath, "isoPath");
2375 SHOW_STR_ATTR(User, "user");
2376 SHOW_STR_ATTR(Password, "password");
2377 SHOW_STR_ATTR(FullUserName, "fullUserName");
2378 SHOW_STR_ATTR(ProductKey, "productKey");
2379 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
2380 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
2381 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
2382 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
2383 SHOW_STR_ATTR(Locale, "locale");
2384 SHOW_STR_ATTR(Country, "country");
2385 SHOW_STR_ATTR(TimeZone, "timeZone");
2386 SHOW_STR_ATTR(Proxy, "proxy");
2387 SHOW_STR_ATTR(Hostname, "hostname");
2388 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
2389 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
2390 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
2391 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
2392 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
2393 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
2394 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
2395 SHOW_STR_ATTR(Language, "language");
2396 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
2397 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
2398 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
2399 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
2400 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
2401 {
2402 ULONG idxImage = 0;
2403 HRESULT hrc2 = ptrUnattended->COMGETTER(ImageIndex)(&idxImage);
2404 if (FAILED(hrc2))
2405 idxImage = 0;
2406 SafeArray<BSTR> aImageNames;
2407 hrc2 = ptrUnattended->COMGETTER(DetectedImageNames)(ComSafeArrayAsOutParam(aImageNames));
2408 if (SUCCEEDED(hrc2))
2409 {
2410 SafeArray<ULONG> aImageIndices;
2411 hrc2 = ptrUnattended->COMGETTER(DetectedImageIndices)(ComSafeArrayAsOutParam(aImageIndices));
2412 if (SUCCEEDED(hrc2))
2413 {
2414 Assert(aImageNames.size() == aImageIndices.size());
2415 for (size_t i = 0; i < aImageNames.size(); i++)
2416 {
2417 char szTmp[64];
2418 RTStrPrintf(szTmp, sizeof(szTmp), "detectedImage[%u]%s", i, idxImage != aImageIndices[i] ? "" : "*");
2419 RTPrintf(" %32s = #%u: %ls\n", szTmp, aImageIndices[i], aImageNames[i]);
2420 }
2421 }
2422 else
2423 RTPrintf(Misc::tr(" %32s = failed: %Rhrc\n"), "detectedImageIndices", hrc2);
2424 }
2425 else
2426 RTPrintf(Misc::tr(" %32 = failed: %Rhrc\n"), "detectedImageNames", hrc2);
2427 }
2428
2429#undef SHOW_STR_ATTR
2430#undef SHOW_ATTR
2431
2432 /* We can drop the IUnatteded object now. */
2433 ptrUnattended.setNull();
2434
2435 /*
2436 * Start the VM if requested.
2437 */
2438 if ( fDryRun
2439 || RTStrICmp(pszSessionType, "none") == 0)
2440 {
2441 if (!fDryRun)
2442 RTMsgInfo(Misc::tr("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n"), bstrMachineName.raw(), bstrUuid.raw());
2443 hrc = S_OK;
2444 }
2445 else
2446 {
2447 com::SafeArray<IN_BSTR> aBstrEnv;
2448#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
2449 /* make sure the VM process will start on the same display as VBoxManage */
2450 const char *pszDisplay = RTEnvGet("DISPLAY");
2451 if (pszDisplay)
2452 aBstrEnv.push_back(BstrFmt("DISPLAY=%s", pszDisplay).raw());
2453 const char *pszXAuth = RTEnvGet("XAUTHORITY");
2454 if (pszXAuth)
2455 aBstrEnv.push_back(BstrFmt("XAUTHORITY=%s", pszXAuth).raw());
2456#endif
2457 ComPtr<IProgress> ptrProgress;
2458 CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), ComSafeArrayAsInParam(aBstrEnv), ptrProgress.asOutParam()));
2459 if (SUCCEEDED(hrc) && !ptrProgress.isNull())
2460 {
2461 RTMsgInfo(Misc::tr("Waiting for VM '%ls' to power on...\n"), bstrMachineName.raw());
2462 CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
2463 if (SUCCEEDED(hrc))
2464 {
2465 BOOL fCompleted = true;
2466 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
2467 if (SUCCEEDED(hrc))
2468 {
2469 ASSERT(fCompleted);
2470
2471 LONG iRc;
2472 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
2473 if (SUCCEEDED(hrc))
2474 {
2475 if (SUCCEEDED(iRc))
2476 RTMsgInfo(Misc::tr("VM '%ls' (%ls) has been successfully started.\n"),
2477 bstrMachineName.raw(), bstrUuid.raw());
2478 else
2479 {
2480 ProgressErrorInfo info(ptrProgress);
2481 com::GluePrintErrorInfo(info);
2482 }
2483 hrc = iRc;
2484 }
2485 }
2486 }
2487 }
2488
2489 /*
2490 * Do we wait for the VM to power down?
2491 */
2492 }
2493
2494 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2495}
2496
2497
2498RTEXITCODE handleUnattended(HandlerArg *a)
2499{
2500 /*
2501 * Sub-command switch.
2502 */
2503 if (a->argc < 1)
2504 return errorNoSubcommand();
2505
2506 if (!strcmp(a->argv[0], "detect"))
2507 {
2508 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
2509 return handleUnattendedDetect(a);
2510 }
2511
2512 if (!strcmp(a->argv[0], "install"))
2513 {
2514 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
2515 return handleUnattendedInstall(a);
2516 }
2517
2518 /* Consider some kind of create-vm-and-install-guest-os command. */
2519 return errorUnknownSubcommand(a->argv[0]);
2520}
2521
2522/**
2523 * Common Cloud profile options.
2524 */
2525typedef struct
2526{
2527 const char *pszProviderName;
2528 const char *pszProfileName;
2529} CLOUDPROFILECOMMONOPT;
2530typedef CLOUDPROFILECOMMONOPT *PCLOUDPROFILECOMMONOPT;
2531
2532/**
2533 * Sets the properties of cloud profile
2534 *
2535 * @returns 0 on success, 1 on failure
2536 */
2537
2538static RTEXITCODE setCloudProfileProperties(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2539{
2540
2541 HRESULT hrc = S_OK;
2542
2543 Bstr bstrProvider(pCommonOpts->pszProviderName);
2544 Bstr bstrProfile(pCommonOpts->pszProfileName);
2545
2546 /*
2547 * Parse options.
2548 */
2549 static const RTGETOPTDEF s_aOptions[] =
2550 {
2551 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2552 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2553 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2554 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2555 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2556 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2557 { "--region", 'r', RTGETOPT_REQ_STRING }
2558 };
2559
2560 RTGETOPTSTATE GetState;
2561 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2562 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2563
2564 com::SafeArray<BSTR> names;
2565 com::SafeArray<BSTR> values;
2566
2567 int c;
2568 RTGETOPTUNION ValueUnion;
2569 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2570 {
2571 switch (c)
2572 {
2573 case 'u': // --clouduser
2574 Bstr("user").detachTo(names.appendedRaw());
2575 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2576 break;
2577 case 'p': // --fingerprint
2578 Bstr("fingerprint").detachTo(names.appendedRaw());
2579 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2580 break;
2581 case 'k': // --keyfile
2582 Bstr("key_file").detachTo(names.appendedRaw());
2583 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2584 break;
2585 case 'P': // --passphrase
2586 Bstr("pass_phrase").detachTo(names.appendedRaw());
2587 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2588 break;
2589 case 't': // --tenancy
2590 Bstr("tenancy").detachTo(names.appendedRaw());
2591 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2592 break;
2593 case 'c': // --compartment
2594 Bstr("compartment").detachTo(names.appendedRaw());
2595 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2596 break;
2597 case 'r': // --region
2598 Bstr("region").detachTo(names.appendedRaw());
2599 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2600 break;
2601 default:
2602 return errorGetOpt(c, &ValueUnion);
2603 }
2604 }
2605
2606 /* check for required options */
2607 if (bstrProvider.isEmpty())
2608 return errorSyntax(Misc::tr("Parameter --provider is required"));
2609 if (bstrProfile.isEmpty())
2610 return errorSyntax(Misc::tr("Parameter --profile is required"));
2611
2612 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2613
2614 ComPtr<ICloudProviderManager> pCloudProviderManager;
2615 CHECK_ERROR2_RET(hrc, pVirtualBox,
2616 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2617 RTEXITCODE_FAILURE);
2618
2619 ComPtr<ICloudProvider> pCloudProvider;
2620
2621 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2622 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2623 RTEXITCODE_FAILURE);
2624
2625 ComPtr<ICloudProfile> pCloudProfile;
2626
2627 if (pCloudProvider)
2628 {
2629 CHECK_ERROR2_RET(hrc, pCloudProvider,
2630 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2631 RTEXITCODE_FAILURE);
2632 CHECK_ERROR2_RET(hrc, pCloudProfile,
2633 SetProperties(ComSafeArrayAsInParam(names), ComSafeArrayAsInParam(values)),
2634 RTEXITCODE_FAILURE);
2635 }
2636
2637 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2638
2639 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was updated.\n"),bstrProvider.raw(), bstrProfile.raw());
2640
2641 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2642}
2643
2644/**
2645 * Gets the properties of cloud profile
2646 *
2647 * @returns 0 on success, 1 on failure
2648 */
2649static RTEXITCODE showCloudProfileProperties(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2650{
2651 HRESULT hrc = S_OK;
2652
2653 Bstr bstrProvider(pCommonOpts->pszProviderName);
2654 Bstr bstrProfile(pCommonOpts->pszProfileName);
2655
2656 /* check for required options */
2657 if (bstrProvider.isEmpty())
2658 return errorSyntax(Misc::tr("Parameter --provider is required"));
2659 if (bstrProfile.isEmpty())
2660 return errorSyntax(Misc::tr("Parameter --profile is required"));
2661
2662 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2663 ComPtr<ICloudProviderManager> pCloudProviderManager;
2664 CHECK_ERROR2_RET(hrc, pVirtualBox,
2665 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2666 RTEXITCODE_FAILURE);
2667 ComPtr<ICloudProvider> pCloudProvider;
2668 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2669 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2670 RTEXITCODE_FAILURE);
2671
2672 ComPtr<ICloudProfile> pCloudProfile;
2673 if (pCloudProvider)
2674 {
2675 CHECK_ERROR2_RET(hrc, pCloudProvider,
2676 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2677 RTEXITCODE_FAILURE);
2678
2679 Bstr bstrProviderID;
2680 pCloudProfile->COMGETTER(ProviderId)(bstrProviderID.asOutParam());
2681 RTPrintf(Misc::tr("Provider GUID: %ls\n"), bstrProviderID.raw());
2682
2683 com::SafeArray<BSTR> names;
2684 com::SafeArray<BSTR> values;
2685 CHECK_ERROR2_RET(hrc, pCloudProfile,
2686 GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values)),
2687 RTEXITCODE_FAILURE);
2688 size_t cNames = names.size();
2689 size_t cValues = values.size();
2690 bool fFirst = true;
2691 for (size_t k = 0; k < cNames; k++)
2692 {
2693 Bstr value;
2694 if (k < cValues)
2695 value = values[k];
2696 RTPrintf("%s%ls=%ls\n",
2697 fFirst ? Misc::tr("Property: ") : " ",
2698 names[k], value.raw());
2699 fFirst = false;
2700 }
2701
2702 RTPrintf("\n");
2703 }
2704
2705 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2706}
2707
2708static RTEXITCODE addCloudProfile(HandlerArg *a, int iFirst, PCLOUDPROFILECOMMONOPT pCommonOpts)
2709{
2710 HRESULT hrc = S_OK;
2711
2712 Bstr bstrProvider(pCommonOpts->pszProviderName);
2713 Bstr bstrProfile(pCommonOpts->pszProfileName);
2714
2715
2716 /* check for required options */
2717 if (bstrProvider.isEmpty())
2718 return errorSyntax(Misc::tr("Parameter --provider is required"));
2719 if (bstrProfile.isEmpty())
2720 return errorSyntax(Misc::tr("Parameter --profile is required"));
2721
2722 /*
2723 * Parse options.
2724 */
2725 static const RTGETOPTDEF s_aOptions[] =
2726 {
2727 { "--clouduser", 'u', RTGETOPT_REQ_STRING },
2728 { "--fingerprint", 'p', RTGETOPT_REQ_STRING },
2729 { "--keyfile", 'k', RTGETOPT_REQ_STRING },
2730 { "--passphrase", 'P', RTGETOPT_REQ_STRING },
2731 { "--tenancy", 't', RTGETOPT_REQ_STRING },
2732 { "--compartment", 'c', RTGETOPT_REQ_STRING },
2733 { "--region", 'r', RTGETOPT_REQ_STRING }
2734 };
2735
2736 RTGETOPTSTATE GetState;
2737 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2738 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2739
2740 com::SafeArray<BSTR> names;
2741 com::SafeArray<BSTR> values;
2742
2743 int c;
2744 RTGETOPTUNION ValueUnion;
2745 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2746 {
2747 switch (c)
2748 {
2749 case 'u': // --clouduser
2750 Bstr("user").detachTo(names.appendedRaw());
2751 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2752 break;
2753 case 'p': // --fingerprint
2754 Bstr("fingerprint").detachTo(names.appendedRaw());
2755 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2756 break;
2757 case 'k': // --keyfile
2758 Bstr("key_file").detachTo(names.appendedRaw());
2759 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2760 break;
2761 case 'P': // --passphrase
2762 Bstr("pass_phrase").detachTo(names.appendedRaw());
2763 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2764 break;
2765 case 't': // --tenancy
2766 Bstr("tenancy").detachTo(names.appendedRaw());
2767 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2768 break;
2769 case 'c': // --compartment
2770 Bstr("compartment").detachTo(names.appendedRaw());
2771 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2772 break;
2773 case 'r': // --region
2774 Bstr("region").detachTo(names.appendedRaw());
2775 Bstr(ValueUnion.psz).detachTo(values.appendedRaw());
2776 break;
2777 default:
2778 return errorGetOpt(c, &ValueUnion);
2779 }
2780 }
2781
2782 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2783
2784 ComPtr<ICloudProviderManager> pCloudProviderManager;
2785 CHECK_ERROR2_RET(hrc, pVirtualBox,
2786 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2787 RTEXITCODE_FAILURE);
2788
2789 ComPtr<ICloudProvider> pCloudProvider;
2790 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2791 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2792 RTEXITCODE_FAILURE);
2793
2794 CHECK_ERROR2_RET(hrc, pCloudProvider,
2795 CreateProfile(bstrProfile.raw(),
2796 ComSafeArrayAsInParam(names),
2797 ComSafeArrayAsInParam(values)),
2798 RTEXITCODE_FAILURE);
2799
2800 CHECK_ERROR2(hrc, pCloudProvider, SaveProfiles());
2801
2802 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was added.\n"),bstrProvider.raw(), bstrProfile.raw());
2803
2804 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2805}
2806
2807static RTEXITCODE deleteCloudProfile(HandlerArg *a, PCLOUDPROFILECOMMONOPT pCommonOpts)
2808{
2809 HRESULT hrc = S_OK;
2810
2811 Bstr bstrProvider(pCommonOpts->pszProviderName);
2812 Bstr bstrProfile(pCommonOpts->pszProfileName);
2813
2814 /* check for required options */
2815 if (bstrProvider.isEmpty())
2816 return errorSyntax(Misc::tr("Parameter --provider is required"));
2817 if (bstrProfile.isEmpty())
2818 return errorSyntax(Misc::tr("Parameter --profile is required"));
2819
2820 ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
2821 ComPtr<ICloudProviderManager> pCloudProviderManager;
2822 CHECK_ERROR2_RET(hrc, pVirtualBox,
2823 COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()),
2824 RTEXITCODE_FAILURE);
2825 ComPtr<ICloudProvider> pCloudProvider;
2826 CHECK_ERROR2_RET(hrc, pCloudProviderManager,
2827 GetProviderByShortName(bstrProvider.raw(), pCloudProvider.asOutParam()),
2828 RTEXITCODE_FAILURE);
2829
2830 ComPtr<ICloudProfile> pCloudProfile;
2831 if (pCloudProvider)
2832 {
2833 CHECK_ERROR2_RET(hrc, pCloudProvider,
2834 GetProfileByName(bstrProfile.raw(), pCloudProfile.asOutParam()),
2835 RTEXITCODE_FAILURE);
2836
2837 CHECK_ERROR2_RET(hrc, pCloudProfile,
2838 Remove(),
2839 RTEXITCODE_FAILURE);
2840
2841 CHECK_ERROR2_RET(hrc, pCloudProvider,
2842 SaveProfiles(),
2843 RTEXITCODE_FAILURE);
2844
2845 RTPrintf(Misc::tr("Provider %ls: profile '%ls' was deleted.\n"), bstrProvider.raw(), bstrProfile.raw());
2846 }
2847
2848 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2849}
2850
2851RTEXITCODE handleCloudProfile(HandlerArg *a)
2852{
2853 if (a->argc < 1)
2854 return errorNoSubcommand();
2855
2856 static const RTGETOPTDEF s_aOptions[] =
2857 {
2858 /* common options */
2859 { "--provider", 'v', RTGETOPT_REQ_STRING },
2860 { "--profile", 'f', RTGETOPT_REQ_STRING },
2861 /* subcommands */
2862 { "add", 1000, RTGETOPT_REQ_NOTHING },
2863 { "show", 1001, RTGETOPT_REQ_NOTHING },
2864 { "update", 1002, RTGETOPT_REQ_NOTHING },
2865 { "delete", 1003, RTGETOPT_REQ_NOTHING },
2866 };
2867
2868 RTGETOPTSTATE GetState;
2869 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2870 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2871
2872 CLOUDPROFILECOMMONOPT CommonOpts = { NULL, NULL };
2873 int c;
2874 RTGETOPTUNION ValueUnion;
2875 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
2876 {
2877 switch (c)
2878 {
2879 case 'v': // --provider
2880 CommonOpts.pszProviderName = ValueUnion.psz;
2881 break;
2882 case 'f': // --profile
2883 CommonOpts.pszProfileName = ValueUnion.psz;
2884 break;
2885 /* Sub-commands: */
2886 case 1000:
2887 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_ADD);
2888 return addCloudProfile(a, GetState.iNext, &CommonOpts);
2889 case 1001:
2890 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_SHOW);
2891 return showCloudProfileProperties(a, &CommonOpts);
2892 case 1002:
2893 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_UPDATE);
2894 return setCloudProfileProperties(a, GetState.iNext, &CommonOpts);
2895 case 1003:
2896 setCurrentSubcommand(HELP_SCOPE_CLOUDPROFILE_DELETE);
2897 return deleteCloudProfile(a, &CommonOpts);
2898 case VINF_GETOPT_NOT_OPTION:
2899 return errorUnknownSubcommand(ValueUnion.psz);
2900
2901 default:
2902 return errorGetOpt(c, &ValueUnion);
2903 }
2904 }
2905
2906 return errorNoSubcommand();
2907}
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