VirtualBox

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

Last change on this file since 68485 was 68318, checked in by vboxsync, 7 years ago

Unattended: detectIsoOS updates.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.0 KB
Line 
1/* $Id: VBoxManageMisc.cpp 68318 2017-08-07 15:14:26Z vboxsync $ */
2/** @file
3 * VBoxManage - VirtualBox's command-line interface.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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 handleUnattendedDetect(HandlerArg *a)
1271{
1272 HRESULT hrc;
1273
1274 /*
1275 * Options. We work directly on an IUnattended instace while parsing
1276 * the options. This saves a lot of extra clutter.
1277 */
1278 bool fMachineReadable = false;
1279 char szIsoPath[RTPATH_MAX];
1280 szIsoPath[0] = '\0';
1281
1282 /*
1283 * Parse options.
1284 */
1285 static const RTGETOPTDEF s_aOptions[] =
1286 {
1287 { "--iso", 'i', RTGETOPT_REQ_STRING },
1288 { "--machine-readable", 'M', RTGETOPT_REQ_NOTHING },
1289 };
1290
1291 RTGETOPTSTATE GetState;
1292 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1293 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1294
1295 int c;
1296 RTGETOPTUNION ValueUnion;
1297 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1298 {
1299 switch (c)
1300 {
1301 case 'i': // --iso
1302 vrc = RTPathAbs(ValueUnion.psz, szIsoPath, sizeof(szIsoPath));
1303 if (RT_FAILURE(vrc))
1304 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1305 break;
1306
1307 case 'M': // --machine-readable.
1308 fMachineReadable = true;
1309 break;
1310
1311 default:
1312 return errorGetOpt(c, &ValueUnion);
1313 }
1314 }
1315
1316 /*
1317 * Check for required stuff.
1318 */
1319 if (szIsoPath[0] == '\0')
1320 return errorSyntax("No ISO specified");
1321
1322 /*
1323 * Do the job.
1324 */
1325 ComPtr<IUnattended> ptrUnattended;
1326 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1327 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szIsoPath).raw()), RTEXITCODE_FAILURE);
1328 CHECK_ERROR2(hrc, ptrUnattended, DetectIsoOS());
1329 RTEXITCODE rcExit = SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1330
1331 /*
1332 * Retrieve the results.
1333 */
1334 Bstr bstrDetectedOSTypeId;
1335 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSTypeId)(bstrDetectedOSTypeId.asOutParam()), RTEXITCODE_FAILURE);
1336 Bstr bstrDetectedVersion;
1337 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSVersion)(bstrDetectedVersion.asOutParam()), RTEXITCODE_FAILURE);
1338 Bstr bstrDetectedFlavor;
1339 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSFlavor)(bstrDetectedFlavor.asOutParam()), RTEXITCODE_FAILURE);
1340 Bstr bstrDetectedLanguages;
1341 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSLanguages)(bstrDetectedLanguages.asOutParam()), RTEXITCODE_FAILURE);
1342 Bstr bstrDetectedHints;
1343 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSHints)(bstrDetectedHints.asOutParam()), RTEXITCODE_FAILURE);
1344 if (fMachineReadable)
1345 RTPrintf("OSTypeId=\"%ls\"\n"
1346 "OSVersion=\"%ls\"\n"
1347 "OSFlavor=\"%ls\"\n"
1348 "OSLanguages=\"%ls\"\n"
1349 "OSHints=\"%ls\"\n",
1350 bstrDetectedOSTypeId.raw(),
1351 bstrDetectedVersion.raw(),
1352 bstrDetectedFlavor.raw(),
1353 bstrDetectedLanguages.raw(),
1354 bstrDetectedHints.raw());
1355 else
1356 {
1357 RTMsgInfo("Detected '%s' to be:\n", szIsoPath);
1358 RTPrintf(" OS TypeId = %ls\n"
1359 " OS Version = %ls\n"
1360 " OS Flavor = %ls\n"
1361 " OS Languages = %ls\n"
1362 " OS Hints = %ls\n",
1363 bstrDetectedOSTypeId.raw(),
1364 bstrDetectedVersion.raw(),
1365 bstrDetectedFlavor.raw(),
1366 bstrDetectedLanguages.raw(),
1367 bstrDetectedHints.raw());
1368 }
1369
1370 return rcExit;
1371}
1372
1373RTEXITCODE handleUnattendedInstall(HandlerArg *a)
1374{
1375 HRESULT hrc;
1376 char szAbsPath[RTPATH_MAX];
1377
1378 /*
1379 * Options. We work directly on an IUnattended instace while parsing
1380 * the options. This saves a lot of extra clutter.
1381 */
1382 ComPtr<IUnattended> ptrUnattended;
1383 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1384 RTCList<RTCString> arrPackageSelectionAdjustments;
1385 ComPtr<IMachine> ptrMachine;
1386 bool fDryRun = false;
1387 const char *pszSessionType = "none";
1388
1389 /*
1390 * Parse options.
1391 */
1392 static const RTGETOPTDEF s_aOptions[] =
1393 {
1394 { "--iso", 'i', RTGETOPT_REQ_STRING },
1395 { "--user", 'u', RTGETOPT_REQ_STRING },
1396 { "--password", 'p', RTGETOPT_REQ_STRING },
1397 { "--password-file", 'X', RTGETOPT_REQ_STRING },
1398 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
1399 { "--key", 'k', RTGETOPT_REQ_STRING },
1400 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
1401 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
1402 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
1403 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
1404 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
1405 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
1406 { "--locale", 'l', RTGETOPT_REQ_STRING },
1407 { "--country", 'Y', RTGETOPT_REQ_STRING },
1408 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
1409 { "--proxy", 'y', RTGETOPT_REQ_STRING },
1410 { "--hostname", 'H', RTGETOPT_REQ_STRING },
1411 { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
1412 { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
1413 // advance options:
1414 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
1415 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
1416 { "--script-template", 'c', RTGETOPT_REQ_STRING },
1417 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
1418 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
1419 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
1420 { "--language", 'L', RTGETOPT_REQ_STRING },
1421 // start vm related options:
1422 { "--start-vm", 'S', RTGETOPT_REQ_STRING },
1423 /** @todo Add a --wait option too for waiting for the VM to shut down or
1424 * something like that...? */
1425 };
1426
1427 RTGETOPTSTATE GetState;
1428 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1429 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1430
1431 int c;
1432 RTGETOPTUNION ValueUnion;
1433 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1434 {
1435 switch (c)
1436 {
1437 case VINF_GETOPT_NOT_OPTION:
1438 if (ptrMachine.isNotNull())
1439 return errorSyntax("VM name/UUID given more than once!");
1440 CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1441 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
1442 break;
1443
1444 case 'i': // --iso
1445 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1446 if (RT_FAILURE(vrc))
1447 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1448 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1449 break;
1450
1451 case 'u': // --user
1452 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1453 break;
1454
1455 case 'p': // --password
1456 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1457 break;
1458
1459 case 'X': // --password-file
1460 {
1461 Utf8Str strPassword;
1462 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
1463 if (rcExit != RTEXITCODE_SUCCESS)
1464 return rcExit;
1465 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
1466 break;
1467 }
1468
1469 case 'U': // --full-user-name
1470 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1471 break;
1472
1473 case 'k': // --key
1474 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1475 break;
1476
1477 case 'A': // --install-additions
1478 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
1479 break;
1480 case 'N': // --no-install-additions
1481 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
1482 break;
1483 case 'a': // --additions-iso
1484 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1485 if (RT_FAILURE(vrc))
1486 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1487 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1488 break;
1489
1490 case 't': // --install-txs
1491 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
1492 break;
1493 case 'T': // --no-install-txs
1494 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
1495 break;
1496 case 'K': // --valiation-kit-iso
1497 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1498 if (RT_FAILURE(vrc))
1499 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1500 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1501 break;
1502
1503 case 'l': // --locale
1504 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1505 break;
1506
1507 case 'Y': // --country
1508 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1509 break;
1510
1511 case 'z': // --time-zone;
1512 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1513 break;
1514
1515 case 'y': // --proxy
1516 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1517 break;
1518
1519 case 'H': // --hostname
1520 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1521 break;
1522
1523 case 's': // --package-selection-adjustment
1524 arrPackageSelectionAdjustments.append(ValueUnion.psz);
1525 break;
1526
1527 case 'D':
1528 fDryRun = true;
1529 break;
1530
1531 case 'x': // --auxiliary-base-path
1532 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1533 if (RT_FAILURE(vrc))
1534 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1535 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1536 break;
1537
1538 case 'm': // --image-index
1539 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
1540 break;
1541
1542 case 'c': // --script-template
1543 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1544 if (RT_FAILURE(vrc))
1545 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1546 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1547 break;
1548
1549 case 'C': // --post-install-script-template
1550 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1551 if (RT_FAILURE(vrc))
1552 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1553 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1554 break;
1555
1556 case 'P': // --post-install-command.
1557 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1558 break;
1559
1560 case 'I': // --extra-install-kernel-parameters
1561 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1562 break;
1563
1564 case 'L': // --language
1565 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1566 break;
1567
1568 case 'S': // --start-vm
1569 pszSessionType = ValueUnion.psz;
1570 break;
1571
1572 default:
1573 return errorGetOpt(c, &ValueUnion);
1574 }
1575 }
1576
1577 /*
1578 * Check for required stuff.
1579 */
1580 if (ptrMachine.isNull())
1581 return errorSyntax("Missing VM name/UUID");
1582
1583 /*
1584 * Set accumulative attributes.
1585 */
1586 if (arrPackageSelectionAdjustments.size() == 1)
1587 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
1588 RTEXITCODE_FAILURE);
1589 else if (arrPackageSelectionAdjustments.size() > 1)
1590 {
1591 RTCString strAdjustments;
1592 strAdjustments.join(arrPackageSelectionAdjustments, ";");
1593 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
1594 }
1595
1596 /*
1597 * Get details about the machine so we can display them below.
1598 */
1599 Bstr bstrMachineName;
1600 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
1601 Bstr bstrUuid;
1602 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
1603 BSTR bstrInstalledOS;
1604 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
1605 Utf8Str strInstalledOS(bstrInstalledOS);
1606
1607 /*
1608 * Temporarily lock the machine to check whether it's running or not.
1609 * We take this opportunity to disable the first run wizard.
1610 */
1611 CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1612 {
1613 ComPtr<IConsole> ptrConsole;
1614 CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
1615
1616 if ( ptrConsole.isNull()
1617 && SUCCEEDED(hrc)
1618 && ( RTStrICmp(pszSessionType, "gui") == 0
1619 || RTStrICmp(pszSessionType, "none") == 0))
1620 {
1621 ComPtr<IMachine> ptrSessonMachine;
1622 CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
1623 if (ptrSessonMachine.isNotNull())
1624 {
1625 CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
1626 }
1627 }
1628
1629 a->session->UnlockMachine();
1630 if (FAILED(hrc))
1631 return RTEXITCODE_FAILURE;
1632 if (ptrConsole.isNotNull())
1633 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%ls' is currently running", bstrMachineName.raw());
1634 }
1635
1636 /*
1637 * Do the work.
1638 */
1639 RTMsgInfo("%s unattended installation of %s in machine '%ls' (%ls).\n",
1640 RTStrICmp(pszSessionType, "none") == 0 ? "Preparing" : "Starting",
1641 strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
1642
1643 CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
1644 if (!fDryRun)
1645 {
1646 CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
1647 CHECK_ERROR2_RET(hrc, ptrUnattended,ReconfigureVM(), RTEXITCODE_FAILURE);
1648 }
1649
1650 /*
1651 * Retrieve and display the parameters actually used.
1652 */
1653 RTMsgInfo("Using values:\n");
1654#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
1655 a_Type Value; \
1656 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
1657 if (SUCCEEDED(hrc2)) \
1658 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
1659 else \
1660 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1661 } while (0)
1662#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
1663 Bstr bstrString; \
1664 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
1665 if (SUCCEEDED(hrc2)) \
1666 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
1667 else \
1668 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1669 } while (0)
1670
1671 SHOW_STR_ATTR(IsoPath, "isoPath");
1672 SHOW_STR_ATTR(User, "user");
1673 SHOW_STR_ATTR(Password, "password");
1674 SHOW_STR_ATTR(FullUserName, "fullUserName");
1675 SHOW_STR_ATTR(ProductKey, "productKey");
1676 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
1677 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
1678 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
1679 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
1680 SHOW_STR_ATTR(Locale, "locale");
1681 SHOW_STR_ATTR(Country, "country");
1682 SHOW_STR_ATTR(TimeZone, "timeZone");
1683 SHOW_STR_ATTR(Proxy, "proxy");
1684 SHOW_STR_ATTR(Hostname, "hostname");
1685 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
1686 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
1687 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
1688 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
1689 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
1690 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
1691 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
1692 SHOW_STR_ATTR(Language, "language");
1693 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
1694 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
1695 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
1696 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
1697 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
1698
1699#undef SHOW_STR_ATTR
1700#undef SHOW_ATTR
1701
1702 /* We can drop the IUnatteded object now. */
1703 ptrUnattended.setNull();
1704
1705 /*
1706 * Start the VM if requested.
1707 */
1708 if ( fDryRun
1709 || RTStrICmp(pszSessionType, "none") == 0)
1710 {
1711 if (!fDryRun)
1712 RTMsgInfo("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n", bstrMachineName.raw(), bstrUuid.raw());
1713 hrc = S_OK;
1714 }
1715 else
1716 {
1717 Bstr env;
1718#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1719 /* make sure the VM process will start on the same display as VBoxManage */
1720 Utf8Str str;
1721 const char *pszDisplay = RTEnvGet("DISPLAY");
1722 if (pszDisplay)
1723 str = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
1724 const char *pszXAuth = RTEnvGet("XAUTHORITY");
1725 if (pszXAuth)
1726 str.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
1727 env = str;
1728#endif
1729 ComPtr<IProgress> ptrProgress;
1730 CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), env.raw(), ptrProgress.asOutParam()));
1731 if (SUCCEEDED(hrc) && !ptrProgress.isNull())
1732 {
1733 RTMsgInfo("Waiting for VM '%ls' to power on...\n", bstrMachineName.raw());
1734 CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
1735 if (SUCCEEDED(hrc))
1736 {
1737 BOOL fCompleted = true;
1738 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
1739 if (SUCCEEDED(hrc))
1740 {
1741 ASSERT(fCompleted);
1742
1743 LONG iRc;
1744 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
1745 if (SUCCEEDED(hrc))
1746 {
1747 if (SUCCEEDED(iRc))
1748 RTMsgInfo("VM '%ls' (%ls) has been successfully started.\n", bstrMachineName.raw(), bstrUuid.raw());
1749 else
1750 {
1751 ProgressErrorInfo info(ptrProgress);
1752 com::GluePrintErrorInfo(info);
1753 }
1754 hrc = iRc;
1755 }
1756 }
1757 }
1758 }
1759
1760 /*
1761 * Do we wait for the VM to power down?
1762 */
1763 }
1764
1765 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1766}
1767
1768
1769RTEXITCODE handleUnattended(HandlerArg *a)
1770{
1771 /*
1772 * Sub-command switch.
1773 */
1774 if (a->argc < 1)
1775 return errorNoSubcommand();
1776
1777 if (!strcmp(a->argv[0], "detect"))
1778 {
1779 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
1780 return handleUnattendedDetect(a);
1781 }
1782
1783 if (!strcmp(a->argv[0], "install"))
1784 {
1785 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
1786 return handleUnattendedInstall(a);
1787 }
1788
1789 /* Consider some kind of create-vm-and-install-guest-os command. */
1790 return errorUnknownSubcommand(a->argv[0]);
1791}
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