VirtualBox

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

Last change on this file since 67954 was 67898, checked in by vboxsync, 7 years ago

IUnattended: Merged constructScript() into prepare() as it only reads in the template file we ship.

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