VirtualBox

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

Last change on this file since 107886 was 107886, checked in by vboxsync, 2 weeks ago

Frontends/VBoxManage/VBoxManageMisc.cpp: Fixed warnings found by Parfait (unused assignment + unread variable). jiraref:VBP-1424

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