VirtualBox

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

Last change on this file since 68071 was 68071, checked in by vboxsync, 8 years ago

Unattended: Split out the post installation script into a separate file. Dropped the IUnattended::loadSettings method, don't see much point. Simplified the script editor classes, moving the default filenames and templates to the installer constructor, allowing us to avoid having to 'new' the script editors. The editors are now direct members of UnattendedInstaller.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.1 KB
Line 
1/* $Id: VBoxManageMisc.cpp 68071 2017-07-20 16:24:08Z vboxsync $ */
2/** @file
3 * VBoxManage - VirtualBox's command-line interface.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#ifndef VBOX_ONLY_DOCS
23# include <VBox/com/com.h>
24# include <VBox/com/string.h>
25# include <VBox/com/Guid.h>
26# include <VBox/com/array.h>
27# include <VBox/com/ErrorInfo.h>
28# include <VBox/com/errorprint.h>
29# include <VBox/com/VirtualBox.h>
30#endif /* !VBOX_ONLY_DOCS */
31
32#include <iprt/asm.h>
33#include <iprt/buildconfig.h>
34#include <iprt/cidr.h>
35#include <iprt/ctype.h>
36#include <iprt/dir.h>
37#include <iprt/env.h>
38#include <VBox/err.h>
39#include <iprt/file.h>
40#include <iprt/sha.h>
41#include <iprt/initterm.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/cpp/path.h>
45#include <iprt/stream.h>
46#include <iprt/string.h>
47#include <iprt/stdarg.h>
48#include <iprt/thread.h>
49#include <iprt/uuid.h>
50#include <iprt/getopt.h>
51#include <iprt/ctype.h>
52#include <VBox/version.h>
53#include <VBox/log.h>
54
55#include "VBoxManage.h"
56
57#include <list>
58
59using namespace com;
60
61
62
63RTEXITCODE handleRegisterVM(HandlerArg *a)
64{
65 HRESULT rc;
66
67 if (a->argc != 1)
68 return errorSyntax(USAGE_REGISTERVM, "Incorrect number of parameters");
69
70 ComPtr<IMachine> machine;
71 /** @todo Ugly hack to get both the API interpretation of relative paths
72 * and the client's interpretation of relative paths. Remove after the API
73 * has been redesigned. */
74 rc = a->virtualBox->OpenMachine(Bstr(a->argv[0]).raw(),
75 machine.asOutParam());
76 if (rc == VBOX_E_FILE_ERROR)
77 {
78 char szVMFileAbs[RTPATH_MAX] = "";
79 int vrc = RTPathAbs(a->argv[0], szVMFileAbs, sizeof(szVMFileAbs));
80 if (RT_FAILURE(vrc))
81 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
82 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(szVMFileAbs).raw(),
83 machine.asOutParam()));
84 }
85 else if (FAILED(rc))
86 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(a->argv[0]).raw(),
87 machine.asOutParam()));
88 if (SUCCEEDED(rc))
89 {
90 ASSERT(machine);
91 CHECK_ERROR(a->virtualBox, RegisterMachine(machine));
92 }
93 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
94}
95
96static const RTGETOPTDEF g_aUnregisterVMOptions[] =
97{
98 { "--delete", 'd', RTGETOPT_REQ_NOTHING },
99 { "-delete", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
100};
101
102RTEXITCODE handleUnregisterVM(HandlerArg *a)
103{
104 HRESULT rc;
105 const char *VMName = NULL;
106 bool fDelete = false;
107
108 int c;
109 RTGETOPTUNION ValueUnion;
110 RTGETOPTSTATE GetState;
111 // start at 0 because main() has hacked both the argc and argv given to us
112 RTGetOptInit(&GetState, a->argc, a->argv, g_aUnregisterVMOptions, RT_ELEMENTS(g_aUnregisterVMOptions),
113 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
114 while ((c = RTGetOpt(&GetState, &ValueUnion)))
115 {
116 switch (c)
117 {
118 case 'd': // --delete
119 fDelete = true;
120 break;
121
122 case VINF_GETOPT_NOT_OPTION:
123 if (!VMName)
124 VMName = ValueUnion.psz;
125 else
126 return errorSyntax(USAGE_UNREGISTERVM, "Invalid parameter '%s'", ValueUnion.psz);
127 break;
128
129 default:
130 if (c > 0)
131 {
132 if (RT_C_IS_PRINT(c))
133 return errorSyntax(USAGE_UNREGISTERVM, "Invalid option -%c", c);
134 else
135 return errorSyntax(USAGE_UNREGISTERVM, "Invalid option case %i", c);
136 }
137 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
138 return errorSyntax(USAGE_UNREGISTERVM, "unknown option: %s\n", ValueUnion.psz);
139 else if (ValueUnion.pDef)
140 return errorSyntax(USAGE_UNREGISTERVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
141 else
142 return errorSyntax(USAGE_UNREGISTERVM, "error: %Rrs", c);
143 }
144 }
145
146 /* check for required options */
147 if (!VMName)
148 return errorSyntax(USAGE_UNREGISTERVM, "VM name required");
149
150 ComPtr<IMachine> machine;
151 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(VMName).raw(),
152 machine.asOutParam()),
153 RTEXITCODE_FAILURE);
154 SafeIfaceArray<IMedium> aMedia;
155 CHECK_ERROR_RET(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly,
156 ComSafeArrayAsOutParam(aMedia)),
157 RTEXITCODE_FAILURE);
158 if (fDelete)
159 {
160 ComPtr<IProgress> pProgress;
161 CHECK_ERROR_RET(machine, DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress.asOutParam()),
162 RTEXITCODE_FAILURE);
163
164 rc = showProgress(pProgress);
165 CHECK_PROGRESS_ERROR_RET(pProgress, ("Machine delete failed"), RTEXITCODE_FAILURE);
166 }
167 else
168 {
169 /* Note that the IMachine::Unregister method will return the medium
170 * reference in a sane order, which means that closing will normally
171 * succeed, unless there is still another machine which uses the
172 * medium. No harm done if we ignore the error. */
173 for (size_t i = 0; i < aMedia.size(); i++)
174 {
175 IMedium *pMedium = aMedia[i];
176 if (pMedium)
177 rc = pMedium->Close();
178 }
179 rc = S_OK;
180 }
181 return RTEXITCODE_SUCCESS;
182}
183
184static const RTGETOPTDEF g_aCreateVMOptions[] =
185{
186 { "--name", 'n', RTGETOPT_REQ_STRING },
187 { "-name", 'n', RTGETOPT_REQ_STRING },
188 { "--groups", 'g', RTGETOPT_REQ_STRING },
189 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
190 { "-basefolder", 'p', RTGETOPT_REQ_STRING },
191 { "--ostype", 'o', RTGETOPT_REQ_STRING },
192 { "-ostype", 'o', RTGETOPT_REQ_STRING },
193 { "--uuid", 'u', RTGETOPT_REQ_UUID },
194 { "-uuid", 'u', RTGETOPT_REQ_UUID },
195 { "--register", 'r', RTGETOPT_REQ_NOTHING },
196 { "-register", 'r', RTGETOPT_REQ_NOTHING },
197};
198
199RTEXITCODE handleCreateVM(HandlerArg *a)
200{
201 HRESULT rc;
202 Bstr bstrBaseFolder;
203 Bstr bstrName;
204 Bstr bstrOsTypeId;
205 Bstr bstrUuid;
206 bool fRegister = false;
207 com::SafeArray<BSTR> groups;
208
209 int c;
210 RTGETOPTUNION ValueUnion;
211 RTGETOPTSTATE GetState;
212 // start at 0 because main() has hacked both the argc and argv given to us
213 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateVMOptions, RT_ELEMENTS(g_aCreateVMOptions),
214 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
215 while ((c = RTGetOpt(&GetState, &ValueUnion)))
216 {
217 switch (c)
218 {
219 case 'n': // --name
220 bstrName = ValueUnion.psz;
221 break;
222
223 case 'g': // --groups
224 parseGroups(ValueUnion.psz, &groups);
225 break;
226
227 case 'p': // --basefolder
228 bstrBaseFolder = ValueUnion.psz;
229 break;
230
231 case 'o': // --ostype
232 bstrOsTypeId = ValueUnion.psz;
233 break;
234
235 case 'u': // --uuid
236 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
237 break;
238
239 case 'r': // --register
240 fRegister = true;
241 break;
242
243 default:
244 return errorGetOpt(USAGE_CREATEVM, c, &ValueUnion);
245 }
246 }
247
248 /* check for required options */
249 if (bstrName.isEmpty())
250 return errorSyntax(USAGE_CREATEVM, "Parameter --name is required");
251
252 do
253 {
254 Bstr createFlags;
255 if (!bstrUuid.isEmpty())
256 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
257 Bstr bstrPrimaryGroup;
258 if (groups.size())
259 bstrPrimaryGroup = groups[0];
260 Bstr bstrSettingsFile;
261 CHECK_ERROR_BREAK(a->virtualBox,
262 ComposeMachineFilename(bstrName.raw(),
263 bstrPrimaryGroup.raw(),
264 createFlags.raw(),
265 bstrBaseFolder.raw(),
266 bstrSettingsFile.asOutParam()));
267 ComPtr<IMachine> machine;
268 CHECK_ERROR_BREAK(a->virtualBox,
269 CreateMachine(bstrSettingsFile.raw(),
270 bstrName.raw(),
271 ComSafeArrayAsInParam(groups),
272 bstrOsTypeId.raw(),
273 createFlags.raw(),
274 machine.asOutParam()));
275
276 CHECK_ERROR_BREAK(machine, SaveSettings());
277 if (fRegister)
278 {
279 CHECK_ERROR_BREAK(a->virtualBox, RegisterMachine(machine));
280 }
281 Bstr uuid;
282 CHECK_ERROR_BREAK(machine, COMGETTER(Id)(uuid.asOutParam()));
283 Bstr settingsFile;
284 CHECK_ERROR_BREAK(machine, COMGETTER(SettingsFilePath)(settingsFile.asOutParam()));
285 RTPrintf("Virtual machine '%ls' is created%s.\n"
286 "UUID: %s\n"
287 "Settings file: '%ls'\n",
288 bstrName.raw(), fRegister ? " and registered" : "",
289 Utf8Str(uuid).c_str(), settingsFile.raw());
290 }
291 while (0);
292
293 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
294}
295
296static const RTGETOPTDEF g_aCloneVMOptions[] =
297{
298 { "--snapshot", 's', RTGETOPT_REQ_STRING },
299 { "--name", 'n', RTGETOPT_REQ_STRING },
300 { "--groups", 'g', RTGETOPT_REQ_STRING },
301 { "--mode", 'm', RTGETOPT_REQ_STRING },
302 { "--options", 'o', RTGETOPT_REQ_STRING },
303 { "--register", 'r', RTGETOPT_REQ_NOTHING },
304 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
305 { "--uuid", 'u', RTGETOPT_REQ_UUID },
306};
307
308static int parseCloneMode(const char *psz, CloneMode_T *pMode)
309{
310 if (!RTStrICmp(psz, "machine"))
311 *pMode = CloneMode_MachineState;
312 else if (!RTStrICmp(psz, "machineandchildren"))
313 *pMode = CloneMode_MachineAndChildStates;
314 else if (!RTStrICmp(psz, "all"))
315 *pMode = CloneMode_AllStates;
316 else
317 return VERR_PARSE_ERROR;
318
319 return VINF_SUCCESS;
320}
321
322static int parseCloneOptions(const char *psz, com::SafeArray<CloneOptions_T> *options)
323{
324 int rc = VINF_SUCCESS;
325 while (psz && *psz && RT_SUCCESS(rc))
326 {
327 size_t len;
328 const char *pszComma = strchr(psz, ',');
329 if (pszComma)
330 len = pszComma - psz;
331 else
332 len = strlen(psz);
333 if (len > 0)
334 {
335 if (!RTStrNICmp(psz, "KeepAllMACs", len))
336 options->push_back(CloneOptions_KeepAllMACs);
337 else if (!RTStrNICmp(psz, "KeepNATMACs", len))
338 options->push_back(CloneOptions_KeepNATMACs);
339 else if (!RTStrNICmp(psz, "KeepDiskNames", len))
340 options->push_back(CloneOptions_KeepDiskNames);
341 else if ( !RTStrNICmp(psz, "Link", len)
342 || !RTStrNICmp(psz, "Linked", len))
343 options->push_back(CloneOptions_Link);
344 else
345 rc = VERR_PARSE_ERROR;
346 }
347 if (pszComma)
348 psz += len + 1;
349 else
350 psz += len;
351 }
352
353 return rc;
354}
355
356RTEXITCODE handleCloneVM(HandlerArg *a)
357{
358 HRESULT rc;
359 const char *pszSrcName = NULL;
360 const char *pszSnapshotName = NULL;
361 CloneMode_T mode = CloneMode_MachineState;
362 com::SafeArray<CloneOptions_T> options;
363 const char *pszTrgName = NULL;
364 const char *pszTrgBaseFolder = NULL;
365 bool fRegister = false;
366 Bstr bstrUuid;
367 com::SafeArray<BSTR> groups;
368
369 int c;
370 RTGETOPTUNION ValueUnion;
371 RTGETOPTSTATE GetState;
372 // start at 0 because main() has hacked both the argc and argv given to us
373 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneVMOptions, RT_ELEMENTS(g_aCloneVMOptions),
374 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
375 while ((c = RTGetOpt(&GetState, &ValueUnion)))
376 {
377 switch (c)
378 {
379 case 's': // --snapshot
380 pszSnapshotName = ValueUnion.psz;
381 break;
382
383 case 'n': // --name
384 pszTrgName = ValueUnion.psz;
385 break;
386
387 case 'g': // --groups
388 parseGroups(ValueUnion.psz, &groups);
389 break;
390
391 case 'p': // --basefolder
392 pszTrgBaseFolder = ValueUnion.psz;
393 break;
394
395 case 'm': // --mode
396 if (RT_FAILURE(parseCloneMode(ValueUnion.psz, &mode)))
397 return errorArgument("Invalid clone mode '%s'\n", ValueUnion.psz);
398 break;
399
400 case 'o': // --options
401 if (RT_FAILURE(parseCloneOptions(ValueUnion.psz, &options)))
402 return errorArgument("Invalid clone options '%s'\n", ValueUnion.psz);
403 break;
404
405 case 'u': // --uuid
406 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
407 break;
408
409 case 'r': // --register
410 fRegister = true;
411 break;
412
413 case VINF_GETOPT_NOT_OPTION:
414 if (!pszSrcName)
415 pszSrcName = ValueUnion.psz;
416 else
417 return errorSyntax(USAGE_CLONEVM, "Invalid parameter '%s'", ValueUnion.psz);
418 break;
419
420 default:
421 return errorGetOpt(USAGE_CLONEVM, c, &ValueUnion);
422 }
423 }
424
425 /* Check for required options */
426 if (!pszSrcName)
427 return errorSyntax(USAGE_CLONEVM, "VM name required");
428
429 /* Get the machine object */
430 ComPtr<IMachine> srcMachine;
431 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
432 srcMachine.asOutParam()),
433 RTEXITCODE_FAILURE);
434
435 /* If a snapshot name/uuid was given, get the particular machine of this
436 * snapshot. */
437 if (pszSnapshotName)
438 {
439 ComPtr<ISnapshot> srcSnapshot;
440 CHECK_ERROR_RET(srcMachine, FindSnapshot(Bstr(pszSnapshotName).raw(),
441 srcSnapshot.asOutParam()),
442 RTEXITCODE_FAILURE);
443 CHECK_ERROR_RET(srcSnapshot, COMGETTER(Machine)(srcMachine.asOutParam()),
444 RTEXITCODE_FAILURE);
445 }
446
447 /* Default name necessary? */
448 if (!pszTrgName)
449 pszTrgName = RTStrAPrintf2("%s Clone", pszSrcName);
450
451 Bstr createFlags;
452 if (!bstrUuid.isEmpty())
453 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
454 Bstr bstrPrimaryGroup;
455 if (groups.size())
456 bstrPrimaryGroup = groups[0];
457 Bstr bstrSettingsFile;
458 CHECK_ERROR_RET(a->virtualBox,
459 ComposeMachineFilename(Bstr(pszTrgName).raw(),
460 bstrPrimaryGroup.raw(),
461 createFlags.raw(),
462 Bstr(pszTrgBaseFolder).raw(),
463 bstrSettingsFile.asOutParam()),
464 RTEXITCODE_FAILURE);
465
466 ComPtr<IMachine> trgMachine;
467 CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(),
468 Bstr(pszTrgName).raw(),
469 ComSafeArrayAsInParam(groups),
470 NULL,
471 createFlags.raw(),
472 trgMachine.asOutParam()),
473 RTEXITCODE_FAILURE);
474
475 /* Start the cloning */
476 ComPtr<IProgress> progress;
477 CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine,
478 mode,
479 ComSafeArrayAsInParam(options),
480 progress.asOutParam()),
481 RTEXITCODE_FAILURE);
482 rc = showProgress(progress);
483 CHECK_PROGRESS_ERROR_RET(progress, ("Clone VM failed"), RTEXITCODE_FAILURE);
484
485 if (fRegister)
486 CHECK_ERROR_RET(a->virtualBox, RegisterMachine(trgMachine), RTEXITCODE_FAILURE);
487
488 Bstr bstrNewName;
489 CHECK_ERROR_RET(trgMachine, COMGETTER(Name)(bstrNewName.asOutParam()), RTEXITCODE_FAILURE);
490 RTPrintf("Machine has been successfully cloned as \"%ls\"\n", bstrNewName.raw());
491
492 return RTEXITCODE_SUCCESS;
493}
494
495RTEXITCODE handleStartVM(HandlerArg *a)
496{
497 HRESULT rc = S_OK;
498 std::list<const char *> VMs;
499 Bstr sessionType;
500 Utf8Str strEnv;
501
502#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
503 /* make sure the VM process will by default start on the same display as VBoxManage */
504 {
505 const char *pszDisplay = RTEnvGet("DISPLAY");
506 if (pszDisplay)
507 strEnv = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
508 const char *pszXAuth = RTEnvGet("XAUTHORITY");
509 if (pszXAuth)
510 strEnv.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
511 }
512#endif
513
514 static const RTGETOPTDEF s_aStartVMOptions[] =
515 {
516 { "--type", 't', RTGETOPT_REQ_STRING },
517 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
518 { "--putenv", 'E', RTGETOPT_REQ_STRING },
519 };
520 int c;
521 RTGETOPTUNION ValueUnion;
522 RTGETOPTSTATE GetState;
523 // start at 0 because main() has hacked both the argc and argv given to us
524 RTGetOptInit(&GetState, a->argc, a->argv, s_aStartVMOptions, RT_ELEMENTS(s_aStartVMOptions),
525 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
526 while ((c = RTGetOpt(&GetState, &ValueUnion)))
527 {
528 switch (c)
529 {
530 case 't': // --type
531 if (!RTStrICmp(ValueUnion.psz, "gui"))
532 {
533 sessionType = "gui";
534 }
535#ifdef VBOX_WITH_VBOXSDL
536 else if (!RTStrICmp(ValueUnion.psz, "sdl"))
537 {
538 sessionType = "sdl";
539 }
540#endif
541#ifdef VBOX_WITH_HEADLESS
542 else if (!RTStrICmp(ValueUnion.psz, "capture"))
543 {
544 sessionType = "capture";
545 }
546 else if (!RTStrICmp(ValueUnion.psz, "headless"))
547 {
548 sessionType = "headless";
549 }
550#endif
551 else
552 sessionType = ValueUnion.psz;
553 break;
554
555 case 'E': // --putenv
556 if (!RTStrStr(ValueUnion.psz, "\n"))
557 strEnv.append(Utf8StrFmt("%s\n", ValueUnion.psz));
558 else
559 return errorSyntax(USAGE_STARTVM, "Parameter to option --putenv must not contain any newline character");
560 break;
561
562 case VINF_GETOPT_NOT_OPTION:
563 VMs.push_back(ValueUnion.psz);
564 break;
565
566 default:
567 if (c > 0)
568 {
569 if (RT_C_IS_PRINT(c))
570 return errorSyntax(USAGE_STARTVM, "Invalid option -%c", c);
571 else
572 return errorSyntax(USAGE_STARTVM, "Invalid option case %i", c);
573 }
574 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
575 return errorSyntax(USAGE_STARTVM, "unknown option: %s\n", ValueUnion.psz);
576 else if (ValueUnion.pDef)
577 return errorSyntax(USAGE_STARTVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
578 else
579 return errorSyntax(USAGE_STARTVM, "error: %Rrs", c);
580 }
581 }
582
583 /* check for required options */
584 if (VMs.empty())
585 return errorSyntax(USAGE_STARTVM, "at least one VM name or uuid required");
586
587 for (std::list<const char *>::const_iterator it = VMs.begin();
588 it != VMs.end();
589 ++it)
590 {
591 HRESULT rc2 = rc;
592 const char *pszVM = *it;
593 ComPtr<IMachine> machine;
594 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(),
595 machine.asOutParam()));
596 if (machine)
597 {
598 ComPtr<IProgress> progress;
599 CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
600 Bstr(strEnv).raw(), progress.asOutParam()));
601 if (SUCCEEDED(rc) && !progress.isNull())
602 {
603 RTPrintf("Waiting for VM \"%s\" to power on...\n", pszVM);
604 CHECK_ERROR(progress, WaitForCompletion(-1));
605 if (SUCCEEDED(rc))
606 {
607 BOOL completed = true;
608 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
609 if (SUCCEEDED(rc))
610 {
611 ASSERT(completed);
612
613 LONG iRc;
614 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
615 if (SUCCEEDED(rc))
616 {
617 if (SUCCEEDED(iRc))
618 RTPrintf("VM \"%s\" has been successfully started.\n", pszVM);
619 else
620 {
621 ProgressErrorInfo info(progress);
622 com::GluePrintErrorInfo(info);
623 }
624 rc = iRc;
625 }
626 }
627 }
628 }
629 }
630
631 /* it's important to always close sessions */
632 a->session->UnlockMachine();
633
634 /* make sure that we remember the failed state */
635 if (FAILED(rc2))
636 rc = rc2;
637 }
638
639 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
640}
641
642RTEXITCODE handleDiscardState(HandlerArg *a)
643{
644 HRESULT rc;
645
646 if (a->argc != 1)
647 return errorSyntax(USAGE_DISCARDSTATE, "Incorrect number of parameters");
648
649 ComPtr<IMachine> machine;
650 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
651 machine.asOutParam()));
652 if (machine)
653 {
654 do
655 {
656 /* we have to open a session for this task */
657 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
658 do
659 {
660 ComPtr<IMachine> sessionMachine;
661 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
662 CHECK_ERROR_BREAK(sessionMachine, DiscardSavedState(true /* fDeleteFile */));
663 } while (0);
664 CHECK_ERROR_BREAK(a->session, UnlockMachine());
665 } while (0);
666 }
667
668 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
669}
670
671RTEXITCODE handleAdoptState(HandlerArg *a)
672{
673 HRESULT rc;
674
675 if (a->argc != 2)
676 return errorSyntax(USAGE_ADOPTSTATE, "Incorrect number of parameters");
677
678 ComPtr<IMachine> machine;
679 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
680 machine.asOutParam()));
681 if (machine)
682 {
683 char szStateFileAbs[RTPATH_MAX] = "";
684 int vrc = RTPathAbs(a->argv[1], szStateFileAbs, sizeof(szStateFileAbs));
685 if (RT_FAILURE(vrc))
686 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
687
688 do
689 {
690 /* we have to open a session for this task */
691 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
692 do
693 {
694 ComPtr<IMachine> sessionMachine;
695 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
696 CHECK_ERROR_BREAK(sessionMachine, AdoptSavedState(Bstr(szStateFileAbs).raw()));
697 } while (0);
698 CHECK_ERROR_BREAK(a->session, UnlockMachine());
699 } while (0);
700 }
701
702 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
703}
704
705RTEXITCODE handleGetExtraData(HandlerArg *a)
706{
707 HRESULT rc = S_OK;
708
709 if (a->argc != 2)
710 return errorSyntax(USAGE_GETEXTRADATA, "Incorrect number of parameters");
711
712 /* global data? */
713 if (!strcmp(a->argv[0], "global"))
714 {
715 /* enumeration? */
716 if (!strcmp(a->argv[1], "enumerate"))
717 {
718 SafeArray<BSTR> aKeys;
719 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
720
721 for (size_t i = 0;
722 i < aKeys.size();
723 ++i)
724 {
725 Bstr bstrKey(aKeys[i]);
726 Bstr bstrValue;
727 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
728 bstrValue.asOutParam()));
729
730 RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
731 }
732 }
733 else
734 {
735 Bstr value;
736 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
737 value.asOutParam()));
738 if (!value.isEmpty())
739 RTPrintf("Value: %ls\n", value.raw());
740 else
741 RTPrintf("No value set!\n");
742 }
743 }
744 else
745 {
746 ComPtr<IMachine> machine;
747 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
748 machine.asOutParam()));
749 if (machine)
750 {
751 /* enumeration? */
752 if (!strcmp(a->argv[1], "enumerate"))
753 {
754 SafeArray<BSTR> aKeys;
755 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
756
757 for (size_t i = 0;
758 i < aKeys.size();
759 ++i)
760 {
761 Bstr bstrKey(aKeys[i]);
762 Bstr bstrValue;
763 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
764 bstrValue.asOutParam()));
765
766 RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
767 }
768 }
769 else
770 {
771 Bstr value;
772 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
773 value.asOutParam()));
774 if (!value.isEmpty())
775 RTPrintf("Value: %ls\n", value.raw());
776 else
777 RTPrintf("No value set!\n");
778 }
779 }
780 }
781 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
782}
783
784RTEXITCODE handleSetExtraData(HandlerArg *a)
785{
786 HRESULT rc = S_OK;
787
788 if (a->argc < 2)
789 return errorSyntax(USAGE_SETEXTRADATA, "Not enough parameters");
790
791 /* global data? */
792 if (!strcmp(a->argv[0], "global"))
793 {
794 /** @todo passing NULL is deprecated */
795 if (a->argc < 3)
796 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
797 NULL));
798 else if (a->argc == 3)
799 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
800 Bstr(a->argv[2]).raw()));
801 else
802 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
803 }
804 else
805 {
806 ComPtr<IMachine> machine;
807 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
808 machine.asOutParam()));
809 if (machine)
810 {
811 /* open an existing session for the VM */
812 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
813 /* get the session machine */
814 ComPtr<IMachine> sessionMachine;
815 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
816 /** @todo passing NULL is deprecated */
817 if (a->argc < 3)
818 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
819 NULL));
820 else if (a->argc == 3)
821 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
822 Bstr(a->argv[2]).raw()));
823 else
824 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
825 }
826 }
827 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
828}
829
830RTEXITCODE handleSetProperty(HandlerArg *a)
831{
832 HRESULT rc;
833
834 /* there must be two arguments: property name and value */
835 if (a->argc != 2)
836 return errorSyntax(USAGE_SETPROPERTY, "Incorrect number of parameters");
837
838 ComPtr<ISystemProperties> systemProperties;
839 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
840
841 if (!strcmp(a->argv[0], "machinefolder"))
842 {
843 /* reset to default? */
844 if (!strcmp(a->argv[1], "default"))
845 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
846 else
847 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
848 }
849 else if (!strcmp(a->argv[0], "hwvirtexclusive"))
850 {
851 bool fHwVirtExclusive;
852
853 if (!strcmp(a->argv[1], "on"))
854 fHwVirtExclusive = true;
855 else if (!strcmp(a->argv[1], "off"))
856 fHwVirtExclusive = false;
857 else
858 return errorArgument("Invalid hwvirtexclusive argument '%s'", a->argv[1]);
859 CHECK_ERROR(systemProperties, COMSETTER(ExclusiveHwVirt)(fHwVirtExclusive));
860 }
861 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
862 || !strcmp(a->argv[0], "vrdpauthlibrary"))
863 {
864 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
865 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n");
866
867 /* reset to default? */
868 if (!strcmp(a->argv[1], "default"))
869 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
870 else
871 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
872 }
873 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
874 {
875 /* reset to default? */
876 if (!strcmp(a->argv[1], "default"))
877 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
878 else
879 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
880 }
881 else if (!strcmp(a->argv[0], "vrdeextpack"))
882 {
883 /* disable? */
884 if (!strcmp(a->argv[1], "null"))
885 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
886 else
887 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
888 }
889 else if (!strcmp(a->argv[0], "loghistorycount"))
890 {
891 uint32_t uVal;
892 int vrc;
893 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
894 if (vrc != VINF_SUCCESS)
895 return errorArgument("Error parsing Log history count '%s'", a->argv[1]);
896 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
897 }
898 else if (!strcmp(a->argv[0], "autostartdbpath"))
899 {
900 /* disable? */
901 if (!strcmp(a->argv[1], "null"))
902 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(NULL));
903 else
904 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(Bstr(a->argv[1]).raw()));
905 }
906 else if (!strcmp(a->argv[0], "defaultfrontend"))
907 {
908 Bstr bstrDefaultFrontend(a->argv[1]);
909 if (!strcmp(a->argv[1], "default"))
910 bstrDefaultFrontend.setNull();
911 CHECK_ERROR(systemProperties, COMSETTER(DefaultFrontend)(bstrDefaultFrontend.raw()));
912 }
913 else if (!strcmp(a->argv[0], "logginglevel"))
914 {
915 Bstr bstrLoggingLevel(a->argv[1]);
916 if (!strcmp(a->argv[1], "default"))
917 bstrLoggingLevel.setNull();
918 CHECK_ERROR(systemProperties, COMSETTER(LoggingLevel)(bstrLoggingLevel.raw()));
919 }
920 else
921 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]);
922
923 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
924}
925
926RTEXITCODE handleSharedFolder(HandlerArg *a)
927{
928 HRESULT rc;
929
930 /* we need at least a command and target */
931 if (a->argc < 2)
932 return errorSyntax(USAGE_SHAREDFOLDER, "Not enough parameters");
933
934 const char *pszMachineName = a->argv[1];
935 ComPtr<IMachine> machine;
936 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), machine.asOutParam()));
937 if (!machine)
938 return RTEXITCODE_FAILURE;
939
940 if (!strcmp(a->argv[0], "add"))
941 {
942 /* we need at least four more parameters */
943 if (a->argc < 5)
944 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Not enough parameters");
945
946 char *name = NULL;
947 char *hostpath = NULL;
948 bool fTransient = false;
949 bool fWritable = true;
950 bool fAutoMount = false;
951
952 for (int i = 2; i < a->argc; i++)
953 {
954 if ( !strcmp(a->argv[i], "--name")
955 || !strcmp(a->argv[i], "-name"))
956 {
957 if (a->argc <= i + 1 || !*a->argv[i+1])
958 return errorArgument("Missing argument to '%s'", a->argv[i]);
959 i++;
960 name = a->argv[i];
961 }
962 else if ( !strcmp(a->argv[i], "--hostpath")
963 || !strcmp(a->argv[i], "-hostpath"))
964 {
965 if (a->argc <= i + 1 || !*a->argv[i+1])
966 return errorArgument("Missing argument to '%s'", a->argv[i]);
967 i++;
968 hostpath = a->argv[i];
969 }
970 else if ( !strcmp(a->argv[i], "--readonly")
971 || !strcmp(a->argv[i], "-readonly"))
972 {
973 fWritable = false;
974 }
975 else if ( !strcmp(a->argv[i], "--transient")
976 || !strcmp(a->argv[i], "-transient"))
977 {
978 fTransient = true;
979 }
980 else if ( !strcmp(a->argv[i], "--automount")
981 || !strcmp(a->argv[i], "-automount"))
982 {
983 fAutoMount = true;
984 }
985 else
986 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
987 }
988
989 if (NULL != strstr(name, " "))
990 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "No spaces allowed in parameter '-name'!");
991
992 /* required arguments */
993 if (!name || !hostpath)
994 {
995 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Parameters --name and --hostpath are required");
996 }
997
998 if (fTransient)
999 {
1000 ComPtr<IConsole> console;
1001
1002 /* open an existing session for the VM */
1003 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1004
1005 /* get the session machine */
1006 ComPtr<IMachine> sessionMachine;
1007 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1008
1009 /* get the session console */
1010 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1011 if (console.isNull())
1012 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1013 "Machine '%s' is not currently running.\n", pszMachineName);
1014
1015 CHECK_ERROR(console, CreateSharedFolder(Bstr(name).raw(),
1016 Bstr(hostpath).raw(),
1017 fWritable, fAutoMount));
1018 a->session->UnlockMachine();
1019 }
1020 else
1021 {
1022 /* open a session for the VM */
1023 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1024
1025 /* get the mutable session machine */
1026 ComPtr<IMachine> sessionMachine;
1027 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1028
1029 CHECK_ERROR(sessionMachine, CreateSharedFolder(Bstr(name).raw(),
1030 Bstr(hostpath).raw(),
1031 fWritable, fAutoMount));
1032 if (SUCCEEDED(rc))
1033 CHECK_ERROR(sessionMachine, SaveSettings());
1034
1035 a->session->UnlockMachine();
1036 }
1037 }
1038 else if (!strcmp(a->argv[0], "remove"))
1039 {
1040 /* we need at least two more parameters */
1041 if (a->argc < 3)
1042 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Not enough parameters");
1043
1044 char *name = NULL;
1045 bool fTransient = false;
1046
1047 for (int i = 2; i < a->argc; i++)
1048 {
1049 if ( !strcmp(a->argv[i], "--name")
1050 || !strcmp(a->argv[i], "-name"))
1051 {
1052 if (a->argc <= i + 1 || !*a->argv[i+1])
1053 return errorArgument("Missing argument to '%s'", a->argv[i]);
1054 i++;
1055 name = a->argv[i];
1056 }
1057 else if ( !strcmp(a->argv[i], "--transient")
1058 || !strcmp(a->argv[i], "-transient"))
1059 {
1060 fTransient = true;
1061 }
1062 else
1063 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1064 }
1065
1066 /* required arguments */
1067 if (!name)
1068 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Parameter --name is required");
1069
1070 if (fTransient)
1071 {
1072 /* open an existing session for the VM */
1073 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1074 /* get the session machine */
1075 ComPtr<IMachine> sessionMachine;
1076 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1077 /* get the session console */
1078 ComPtr<IConsole> console;
1079 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1080 if (console.isNull())
1081 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1082 "Machine '%s' is not currently running.\n", pszMachineName);
1083
1084 CHECK_ERROR(console, RemoveSharedFolder(Bstr(name).raw()));
1085
1086 a->session->UnlockMachine();
1087 }
1088 else
1089 {
1090 /* open a session for the VM */
1091 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1092
1093 /* get the mutable session machine */
1094 ComPtr<IMachine> sessionMachine;
1095 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1096
1097 CHECK_ERROR(sessionMachine, RemoveSharedFolder(Bstr(name).raw()));
1098
1099 /* commit and close the session */
1100 CHECK_ERROR(sessionMachine, SaveSettings());
1101 a->session->UnlockMachine();
1102 }
1103 }
1104 else
1105 return errorSyntax(USAGE_SHAREDFOLDER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
1106
1107 return RTEXITCODE_SUCCESS;
1108}
1109
1110RTEXITCODE handleExtPack(HandlerArg *a)
1111{
1112 if (a->argc < 1)
1113 return errorNoSubcommand();
1114
1115 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1116 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1117
1118 RTGETOPTSTATE GetState;
1119 RTGETOPTUNION ValueUnion;
1120 int ch;
1121 HRESULT hrc = S_OK;
1122
1123 if (!strcmp(a->argv[0], "install"))
1124 {
1125 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
1126 const char *pszName = NULL;
1127 bool fReplace = false;
1128
1129 static const RTGETOPTDEF s_aInstallOptions[] =
1130 {
1131 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1132 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
1133 };
1134
1135 RTCList<RTCString> lstLicenseHashes;
1136 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1137 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1138 {
1139 switch (ch)
1140 {
1141 case 'r':
1142 fReplace = true;
1143 break;
1144
1145 case 'a':
1146 lstLicenseHashes.append(ValueUnion.psz);
1147 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
1148 break;
1149
1150 case VINF_GETOPT_NOT_OPTION:
1151 if (pszName)
1152 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1153 pszName = ValueUnion.psz;
1154 break;
1155
1156 default:
1157 return errorGetOpt(ch, &ValueUnion);
1158 }
1159 }
1160 if (!pszName)
1161 return errorSyntax("No extension pack name was given to \"extpack install\"");
1162
1163 char szPath[RTPATH_MAX];
1164 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
1165 if (RT_FAILURE(vrc))
1166 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszName, vrc);
1167
1168 Bstr bstrTarball(szPath);
1169 Bstr bstrName;
1170 ComPtr<IExtPackFile> ptrExtPackFile;
1171 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1172 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1173 BOOL fShowLicense = true;
1174 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
1175 if (fShowLicense)
1176 {
1177 Bstr bstrLicense;
1178 CHECK_ERROR2I_RET(ptrExtPackFile,
1179 QueryLicense(Bstr("").raw() /* PreferredLocale */,
1180 Bstr("").raw() /* PreferredLanguage */,
1181 Bstr("txt").raw() /* Format */,
1182 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
1183 Utf8Str strLicense(bstrLicense);
1184 uint8_t abHash[RTSHA256_HASH_SIZE];
1185 char szDigest[RTSHA256_DIGEST_LEN + 1];
1186 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
1187 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
1188 AssertRCStmt(vrc, szDigest[0] = '\0');
1189 if (lstLicenseHashes.contains(szDigest))
1190 RTPrintf("License accepted.\n");
1191 else
1192 {
1193 RTPrintf("%s\n", strLicense.c_str());
1194 RTPrintf("Do you agree to these license terms and conditions (y/n)? " );
1195 ch = RTStrmGetCh(g_pStdIn);
1196 RTPrintf("\n");
1197 if (ch != 'y' && ch != 'Y')
1198 {
1199 RTPrintf("Installation of \"%ls\" aborted.\n", bstrName.raw());
1200 return RTEXITCODE_FAILURE;
1201 }
1202 if (szDigest[0])
1203 RTPrintf("License accepted. For batch installaltion add\n"
1204 "--accept-license=%s\n"
1205 "to the VBoxManage command line.\n\n", szDigest);
1206 }
1207 }
1208 ComPtr<IProgress> ptrProgress;
1209 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1210 hrc = showProgress(ptrProgress);
1211 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to install \"%s\"", szPath), RTEXITCODE_FAILURE);
1212
1213 RTPrintf("Successfully installed \"%ls\".\n", bstrName.raw());
1214 }
1215 else if (!strcmp(a->argv[0], "uninstall"))
1216 {
1217 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
1218 const char *pszName = NULL;
1219 bool fForced = false;
1220
1221 static const RTGETOPTDEF s_aUninstallOptions[] =
1222 {
1223 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1224 };
1225
1226 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1227 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1228 {
1229 switch (ch)
1230 {
1231 case 'f':
1232 fForced = true;
1233 break;
1234
1235 case VINF_GETOPT_NOT_OPTION:
1236 if (pszName)
1237 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1238 pszName = ValueUnion.psz;
1239 break;
1240
1241 default:
1242 return errorGetOpt(ch, &ValueUnion);
1243 }
1244 }
1245 if (!pszName)
1246 return errorSyntax("No extension pack name was given to \"extpack uninstall\"");
1247
1248 Bstr bstrName(pszName);
1249 ComPtr<IProgress> ptrProgress;
1250 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1251 hrc = showProgress(ptrProgress);
1252 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to uninstall \"%s\"", pszName), RTEXITCODE_FAILURE);
1253
1254 RTPrintf("Successfully uninstalled \"%s\".\n", pszName);
1255 }
1256 else if (!strcmp(a->argv[0], "cleanup"))
1257 {
1258 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
1259 if (a->argc > 1)
1260 return errorTooManyParameters(&a->argv[1]);
1261 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1262 RTPrintf("Successfully performed extension pack cleanup\n");
1263 }
1264 else
1265 return errorUnknownSubcommand(a->argv[0]);
1266
1267 return RTEXITCODE_SUCCESS;
1268}
1269
1270RTEXITCODE handleUnattendedInstall(HandlerArg *a)
1271{
1272 /*
1273 * Options.
1274 */
1275 Utf8Str strAbsIsoPath;
1276 const char *pszIsoPath = NULL;
1277 const char *pszUser = NULL;
1278 const char *pszPassword = NULL;
1279 const char *pszFullUserName = NULL;
1280 const char *pszProductKey = NULL;
1281 Utf8Str strAbsAdditionsIsoPath;
1282 const char *pszAdditionsIsoPath = NULL;
1283 int fInstallAdditions = -1;
1284 Utf8Str strAbsValidationKitIsoPath;
1285 const char *pszValidationKitIsoPath = NULL;
1286 int fInstallTxs = -1;
1287 const char *pszMachineName = NULL;
1288 bool fSetImageIdx = false;
1289 uint32_t idxImage = 0;
1290 const char *pszPostInstallCommand = NULL;
1291 Utf8Str strAbsAuxiliaryBasePath;
1292 const char *pszAuxiliaryBasePath = NULL;
1293 Utf8Str strAbsScriptTemplatePath;
1294 const char *pszScriptTemplatePath = NULL;
1295 Utf8Str strAbsPostInstallScriptTemplatePath;
1296 const char *pszPostInstallScriptTemplatePath = NULL;
1297 const char *pszSessionType = "headless";
1298
1299 /*
1300 * Parse options.
1301 */
1302 if (a->argc <= 1)
1303 return errorSyntax(USAGE_UNATTENDEDINSTALL, "Missing VM name/UUID.");
1304
1305 static const RTGETOPTDEF s_aOptions[] =
1306 {
1307 { "--iso", 'i', RTGETOPT_REQ_STRING },
1308 { "--user", 'u', RTGETOPT_REQ_STRING },
1309 { "--password", 'p', RTGETOPT_REQ_STRING },
1310 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
1311 { "--key", 'k', RTGETOPT_REQ_STRING },
1312 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
1313 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
1314 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
1315 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
1316 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
1317 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
1318 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
1319 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
1320 { "--script-template", 'c', RTGETOPT_REQ_STRING },
1321 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
1322 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
1323 { "--session-type", 'S', RTGETOPT_REQ_STRING },
1324 };
1325
1326 RTGETOPTSTATE GetState;
1327 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1328 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1329
1330 int c;
1331 RTGETOPTUNION ValueUnion;
1332 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1333 {
1334 switch (c)
1335 {
1336 case VINF_GETOPT_NOT_OPTION:
1337 if (pszMachineName)
1338 return errorSyntax(USAGE_UNATTENDEDINSTALL, "VM name/UUID given more than once!");
1339 pszMachineName = ValueUnion.psz;
1340 if (*pszMachineName == '\0')
1341 return errorSyntax(USAGE_UNATTENDEDINSTALL, "VM name/UUID is empty!");
1342 break;
1343
1344 case 'i': // --iso
1345 vrc = RTPathAbsCxx(strAbsIsoPath, ValueUnion.psz);
1346 if (RT_FAILURE(vrc))
1347 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1348 pszIsoPath = strAbsIsoPath.c_str();
1349 break;
1350
1351 case 'u': // --user
1352 pszUser = ValueUnion.psz;
1353 break;
1354
1355 case 'p': // --password
1356 pszPassword = ValueUnion.psz;
1357 break;
1358
1359 case 'U': // --full-user-name
1360 pszFullUserName = ValueUnion.psz;
1361 break;
1362
1363 case 'k': // --key
1364 pszProductKey = ValueUnion.psz;
1365 break;
1366
1367 case 'A': // --install-additions
1368 fInstallAdditions = true;
1369 break;
1370 case 'N': // --no-install-additions
1371 fInstallAdditions = false;
1372 break;
1373 case 'a': // --additions-iso
1374 vrc = RTPathAbsCxx(strAbsAdditionsIsoPath, ValueUnion.psz);
1375 if (RT_FAILURE(vrc))
1376 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1377 pszAdditionsIsoPath = strAbsAdditionsIsoPath.c_str();
1378 break;
1379
1380 case 't': // --install-txs
1381 fInstallTxs = true;
1382 break;
1383 case 'T': // --no-install-txs
1384 fInstallTxs = false;
1385 break;
1386 case 'K': // --valiation-kit-iso
1387 vrc = RTPathAbsCxx(strAbsValidationKitIsoPath, ValueUnion.psz);
1388 if (RT_FAILURE(vrc))
1389 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1390 pszValidationKitIsoPath = strAbsValidationKitIsoPath.c_str();
1391 break;
1392
1393 case 'x': // --auxiliary-base-path
1394 vrc = RTPathAbsCxx(strAbsAuxiliaryBasePath, ValueUnion.psz);
1395 if (RT_FAILURE(vrc))
1396 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1397 pszAuxiliaryBasePath = strAbsAuxiliaryBasePath.c_str();
1398 break;
1399
1400 case 'm': // --image-index
1401 idxImage = ValueUnion.u32;
1402 fSetImageIdx = true;
1403 break;
1404
1405 case 'c': // --script-template
1406 vrc = RTPathAbsCxx(strAbsScriptTemplatePath, ValueUnion.psz);
1407 if (RT_FAILURE(vrc))
1408 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1409 pszScriptTemplatePath = strAbsScriptTemplatePath.c_str();
1410 break;
1411
1412 case 'C': // --post-install-script-template
1413 vrc = RTPathAbsCxx(strAbsPostInstallScriptTemplatePath, ValueUnion.psz);
1414 if (RT_FAILURE(vrc))
1415 return errorSyntax(USAGE_UNATTENDEDINSTALL, "RTPathAbsCxx failed on '%s': %Rrc", ValueUnion.psz, vrc);
1416 pszPostInstallScriptTemplatePath = strAbsPostInstallScriptTemplatePath.c_str();
1417 break;
1418
1419 case 'P': // --post-install-command.
1420 pszPostInstallCommand = ValueUnion.psz;
1421 break;
1422
1423 case 'S': // --session-type
1424 pszSessionType = ValueUnion.psz;
1425 break;
1426
1427 default:
1428 return errorGetOpt(USAGE_UNATTENDEDINSTALL, c, &ValueUnion);
1429 }
1430 }
1431
1432 /*
1433 * Check for required stuff.
1434 */
1435 if (pszMachineName == NULL)
1436 return errorSyntax(USAGE_UNATTENDEDINSTALL, "Missing VM name/UUID");
1437
1438 if (!pszIsoPath)
1439 return errorSyntax(USAGE_UNATTENDEDINSTALL, "Missing required --iso option");
1440
1441 /*
1442 * Prepare.
1443 */
1444
1445 /* try to find the given machine */
1446 HRESULT rc;
1447 ComPtr<IMachine> machine;
1448 Bstr bstrMachineName = pszMachineName;
1449 CHECK_ERROR(a->virtualBox, FindMachine(bstrMachineName.raw(), machine.asOutParam()));
1450 if (FAILED(rc))
1451 return RTEXITCODE_FAILURE;
1452
1453 CHECK_ERROR_RET(machine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
1454 Bstr bstrUuid;
1455 CHECK_ERROR_RET(machine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
1456 /* open a session for the VM */
1457 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1458
1459 /* get the associated console */
1460 ComPtr<IConsole> console;
1461 CHECK_ERROR(a->session, COMGETTER(Console)(console.asOutParam()));
1462 if (console)
1463 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%ls' is currently running", bstrMachineName.raw());
1464
1465 /* ... and session machine */
1466 ComPtr<IMachine> sessionMachine;
1467 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1468
1469 /* Get the OS type name of the VM. */
1470 BSTR bstrInstalledOS;
1471 CHECK_ERROR_RET(sessionMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
1472 Utf8Str strInstalledOS(bstrInstalledOS);
1473
1474 do
1475 {
1476 RTPrintf("Start unattended installation OS %s on virtual machine '%ls'.\n"
1477 "UUID: %s\n",
1478 strInstalledOS.c_str(),
1479 bstrMachineName.raw(),
1480 Utf8Str(bstrUuid).c_str());
1481
1482 {
1483 /*
1484 * Instantiate and configure the unattended installer.
1485 */
1486 ComPtr<IUnattended> ptrUnattended;
1487 CHECK_ERROR_BREAK(machine, CreateUnattendedInstaller(ptrUnattended.asOutParam()));
1488
1489 if (pszIsoPath)
1490 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(IsoPath)(Bstr(pszIsoPath).raw()));
1491 if (pszUser)
1492 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(User)(Bstr(pszUser).raw()));
1493 if (pszPassword)
1494 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(Password)(Bstr(pszPassword).raw()));
1495 if (pszFullUserName)
1496 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(FullUserName)(Bstr(pszFullUserName).raw()));
1497 if (pszProductKey)
1498 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(ProductKey)(Bstr(pszProductKey).raw()));
1499 if (pszAdditionsIsoPath)
1500 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(pszAdditionsIsoPath).raw()));
1501 if (fInstallAdditions >= 0)
1502 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(InstallGuestAdditions)(fInstallAdditions != (int)false));
1503 if (pszValidationKitIsoPath)
1504 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(pszValidationKitIsoPath).raw()));
1505 if (fInstallTxs >= 0)
1506 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(InstallTestExecService)(fInstallTxs != (int)false));
1507 if (fSetImageIdx)
1508 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(ImageIndex)(idxImage));
1509 if (pszScriptTemplatePath)
1510 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(pszScriptTemplatePath).raw()));
1511 if (pszPostInstallScriptTemplatePath)
1512 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(pszPostInstallScriptTemplatePath).raw()));
1513 if (pszPostInstallCommand)
1514 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(pszPostInstallCommand).raw()));
1515 if (pszAuxiliaryBasePath)
1516 CHECK_ERROR_BREAK(ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(pszAuxiliaryBasePath).raw()));
1517
1518 CHECK_ERROR_BREAK(ptrUnattended,Prepare());
1519 CHECK_ERROR_BREAK(ptrUnattended,ConstructMedia());
1520 CHECK_ERROR_BREAK(ptrUnattended,ReconfigureVM());
1521
1522 /*
1523 * Retrieve and display the parameters actually used.
1524 */
1525 RTPrintf("Using values:\n");
1526#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
1527 a_Type Value; \
1528 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
1529 if (SUCCEEDED(hrc2)) \
1530 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
1531 else \
1532 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1533 } while (0)
1534#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
1535 Bstr bstrString; \
1536 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
1537 if (SUCCEEDED(hrc2)) \
1538 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
1539 else \
1540 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1541 } while (0)
1542
1543 SHOW_STR_ATTR(IsoPath, "isoPath");
1544 SHOW_STR_ATTR(User, "user");
1545 SHOW_STR_ATTR(Password, "password");
1546 SHOW_STR_ATTR(FullUserName, "fullUserName");
1547 SHOW_STR_ATTR(ProductKey, "productKey");
1548 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
1549 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
1550 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
1551 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
1552 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
1553 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
1554 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
1555 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
1556 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
1557
1558#undef SHOW_STR_ATTR
1559#undef SHOW_ATTR
1560 }
1561
1562 a->session->UnlockMachine();
1563
1564 /*
1565 * Start the VM if requested.
1566 */
1567 if (RTStrICmp(pszSessionType, "none") != 0)
1568 {
1569 Bstr env;
1570#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1571 /* make sure the VM process will start on the same display as VBoxManage */
1572 Utf8Str str;
1573 const char *pszDisplay = RTEnvGet("DISPLAY");
1574 if (pszDisplay)
1575 str = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
1576 const char *pszXAuth = RTEnvGet("XAUTHORITY");
1577 if (pszXAuth)
1578 str.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
1579 env = str;
1580#endif
1581 ComPtr<IProgress> progress;
1582 CHECK_ERROR(machine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), env.raw(), progress.asOutParam()));
1583 if (SUCCEEDED(rc) && !progress.isNull())
1584 {
1585 RTPrintf("Waiting for VM \"%s\" to power on...\n", Utf8Str(bstrUuid).c_str());
1586 CHECK_ERROR(progress, WaitForCompletion(-1));
1587 if (SUCCEEDED(rc))
1588 {
1589 BOOL fCompleted = true;
1590 CHECK_ERROR(progress, COMGETTER(Completed)(&fCompleted));
1591 if (SUCCEEDED(rc))
1592 {
1593 ASSERT(fCompleted);
1594
1595 LONG iRc;
1596 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
1597 if (SUCCEEDED(rc))
1598 {
1599 if (SUCCEEDED(iRc))
1600 RTPrintf("VM \"%s\" has been successfully started.\n", Utf8Str(bstrUuid).c_str());
1601 else
1602 {
1603 ProgressErrorInfo info(progress);
1604 com::GluePrintErrorInfo(info);
1605 }
1606 rc = iRc;
1607 }
1608 }
1609 }
1610 }
1611
1612 /*
1613 * Do we wait for the VM to power down?
1614 */
1615 }
1616
1617 } while (0);
1618
1619 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1620}
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