VirtualBox

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

Last change on this file since 99806 was 99775, checked in by vboxsync, 19 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

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