VirtualBox

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

Last change on this file since 71586 was 71433, checked in by vboxsync, 7 years ago

VBoxManage: Made the enumerate argument for getextradata optional.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.9 KB
Line 
1/* $Id: VBoxManageMisc.cpp 71433 2018-03-21 13:44:20Z 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_aMoveVMOptions[] =
297{
298 { "--type", 't', RTGETOPT_REQ_STRING },
299 { "--folder", 'f', RTGETOPT_REQ_STRING },
300};
301
302RTEXITCODE handleMoveVM(HandlerArg *a)
303{
304 HRESULT rc;
305 const char *pszSrcName = NULL;
306 const char *pszTargetFolder = NULL;
307 const char *pszType = NULL;
308
309 int c;
310 int vrc = VINF_SUCCESS;
311 RTGETOPTUNION ValueUnion;
312 RTGETOPTSTATE GetState;
313
314 // start at 0 because main() has hacked both the argc and argv given to us
315 RTGetOptInit(&GetState, a->argc, a->argv, g_aMoveVMOptions, RT_ELEMENTS(g_aMoveVMOptions),
316 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
317 while ((c = RTGetOpt(&GetState, &ValueUnion)))
318 {
319 switch (c)
320 {
321 case 't': // --type
322 pszType = ValueUnion.psz;
323 break;
324
325 case 'f': // --target folder
326
327 char szPath[RTPATH_MAX];
328 pszTargetFolder = ValueUnion.psz;
329
330 vrc = RTPathAbs(pszTargetFolder, szPath, sizeof(szPath));
331 if (RT_FAILURE(vrc))
332 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszTargetFolder, vrc);
333 break;
334
335 case VINF_GETOPT_NOT_OPTION:
336 if (!pszSrcName)
337 pszSrcName = ValueUnion.psz;
338 else
339 return errorSyntax(USAGE_MOVEVM, "Invalid parameter '%s'", ValueUnion.psz);
340 break;
341
342 default:
343 return errorGetOpt(USAGE_MOVEVM, c, &ValueUnion);
344 }
345 }
346
347
348 /* Check for required options */
349 if (!pszSrcName)
350 return errorSyntax(USAGE_MOVEVM, "VM name required");
351
352 /* Get the machine object */
353 ComPtr<IMachine> srcMachine;
354 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
355 srcMachine.asOutParam()),
356 RTEXITCODE_FAILURE);
357
358 if (srcMachine)
359 {
360 /* Start the moving */
361 ComPtr<IProgress> progress;
362
363 /* we have to open a session for this task */
364 CHECK_ERROR_RET(srcMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
365 ComPtr<IMachine> sessionMachine;
366
367 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
368 CHECK_ERROR_RET(sessionMachine, MoveTo(Bstr(pszTargetFolder).raw(),
369 Bstr(pszType).raw(),
370 progress.asOutParam()), RTEXITCODE_FAILURE);
371 rc = showProgress(progress);
372 CHECK_PROGRESS_ERROR_RET(progress, ("Move VM failed"), RTEXITCODE_FAILURE);
373
374 sessionMachine.setNull();
375 CHECK_ERROR_RET(a->session, UnlockMachine(), RTEXITCODE_FAILURE);
376
377// do
378// {
379// /* we have to open a session for this task */
380// CHECK_ERROR_BREAK(srcMachine, LockMachine(a->session, LockType_Write));
381// ComPtr<IMachine> sessionMachine;
382// do
383// {
384// CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
385// CHECK_ERROR_BREAK(sessionMachine, MoveTo(Bstr(pszTargetFolder).raw(),
386// Bstr(pszType).raw(),
387// progress.asOutParam()));
388// rc = showProgress(progress);
389// CHECK_PROGRESS_ERROR_RET(progress, ("Move VM failed"), RTEXITCODE_FAILURE);
390//// CHECK_ERROR_BREAK(sessionMachine, SaveSettings());
391// } while (0);
392//
393// sessionMachine.setNull();
394// CHECK_ERROR_BREAK(a->session, UnlockMachine());
395// } while (0);
396 RTPrintf("Machine has been successfully moved into %s\n", pszTargetFolder);
397 }
398
399 return RTEXITCODE_SUCCESS;
400}
401
402static const RTGETOPTDEF g_aCloneVMOptions[] =
403{
404 { "--snapshot", 's', RTGETOPT_REQ_STRING },
405 { "--name", 'n', RTGETOPT_REQ_STRING },
406 { "--groups", 'g', RTGETOPT_REQ_STRING },
407 { "--mode", 'm', RTGETOPT_REQ_STRING },
408 { "--options", 'o', RTGETOPT_REQ_STRING },
409 { "--register", 'r', RTGETOPT_REQ_NOTHING },
410 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
411 { "--uuid", 'u', RTGETOPT_REQ_UUID },
412};
413
414static int parseCloneMode(const char *psz, CloneMode_T *pMode)
415{
416 if (!RTStrICmp(psz, "machine"))
417 *pMode = CloneMode_MachineState;
418 else if (!RTStrICmp(psz, "machineandchildren"))
419 *pMode = CloneMode_MachineAndChildStates;
420 else if (!RTStrICmp(psz, "all"))
421 *pMode = CloneMode_AllStates;
422 else
423 return VERR_PARSE_ERROR;
424
425 return VINF_SUCCESS;
426}
427
428static int parseCloneOptions(const char *psz, com::SafeArray<CloneOptions_T> *options)
429{
430 int rc = VINF_SUCCESS;
431 while (psz && *psz && RT_SUCCESS(rc))
432 {
433 size_t len;
434 const char *pszComma = strchr(psz, ',');
435 if (pszComma)
436 len = pszComma - psz;
437 else
438 len = strlen(psz);
439 if (len > 0)
440 {
441 if (!RTStrNICmp(psz, "KeepAllMACs", len))
442 options->push_back(CloneOptions_KeepAllMACs);
443 else if (!RTStrNICmp(psz, "KeepNATMACs", len))
444 options->push_back(CloneOptions_KeepNATMACs);
445 else if (!RTStrNICmp(psz, "KeepDiskNames", len))
446 options->push_back(CloneOptions_KeepDiskNames);
447 else if ( !RTStrNICmp(psz, "Link", len)
448 || !RTStrNICmp(psz, "Linked", len))
449 options->push_back(CloneOptions_Link);
450 else
451 rc = VERR_PARSE_ERROR;
452 }
453 if (pszComma)
454 psz += len + 1;
455 else
456 psz += len;
457 }
458
459 return rc;
460}
461
462RTEXITCODE handleCloneVM(HandlerArg *a)
463{
464 HRESULT rc;
465 const char *pszSrcName = NULL;
466 const char *pszSnapshotName = NULL;
467 CloneMode_T mode = CloneMode_MachineState;
468 com::SafeArray<CloneOptions_T> options;
469 const char *pszTrgName = NULL;
470 const char *pszTrgBaseFolder = NULL;
471 bool fRegister = false;
472 Bstr bstrUuid;
473 com::SafeArray<BSTR> groups;
474
475 int c;
476 RTGETOPTUNION ValueUnion;
477 RTGETOPTSTATE GetState;
478 // start at 0 because main() has hacked both the argc and argv given to us
479 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneVMOptions, RT_ELEMENTS(g_aCloneVMOptions),
480 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
481 while ((c = RTGetOpt(&GetState, &ValueUnion)))
482 {
483 switch (c)
484 {
485 case 's': // --snapshot
486 pszSnapshotName = ValueUnion.psz;
487 break;
488
489 case 'n': // --name
490 pszTrgName = ValueUnion.psz;
491 break;
492
493 case 'g': // --groups
494 parseGroups(ValueUnion.psz, &groups);
495 break;
496
497 case 'p': // --basefolder
498 pszTrgBaseFolder = ValueUnion.psz;
499 break;
500
501 case 'm': // --mode
502 if (RT_FAILURE(parseCloneMode(ValueUnion.psz, &mode)))
503 return errorArgument("Invalid clone mode '%s'\n", ValueUnion.psz);
504 break;
505
506 case 'o': // --options
507 if (RT_FAILURE(parseCloneOptions(ValueUnion.psz, &options)))
508 return errorArgument("Invalid clone options '%s'\n", ValueUnion.psz);
509 break;
510
511 case 'u': // --uuid
512 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
513 break;
514
515 case 'r': // --register
516 fRegister = true;
517 break;
518
519 case VINF_GETOPT_NOT_OPTION:
520 if (!pszSrcName)
521 pszSrcName = ValueUnion.psz;
522 else
523 return errorSyntax(USAGE_CLONEVM, "Invalid parameter '%s'", ValueUnion.psz);
524 break;
525
526 default:
527 return errorGetOpt(USAGE_CLONEVM, c, &ValueUnion);
528 }
529 }
530
531 /* Check for required options */
532 if (!pszSrcName)
533 return errorSyntax(USAGE_CLONEVM, "VM name required");
534
535 /* Get the machine object */
536 ComPtr<IMachine> srcMachine;
537 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
538 srcMachine.asOutParam()),
539 RTEXITCODE_FAILURE);
540
541 /* If a snapshot name/uuid was given, get the particular machine of this
542 * snapshot. */
543 if (pszSnapshotName)
544 {
545 ComPtr<ISnapshot> srcSnapshot;
546 CHECK_ERROR_RET(srcMachine, FindSnapshot(Bstr(pszSnapshotName).raw(),
547 srcSnapshot.asOutParam()),
548 RTEXITCODE_FAILURE);
549 CHECK_ERROR_RET(srcSnapshot, COMGETTER(Machine)(srcMachine.asOutParam()),
550 RTEXITCODE_FAILURE);
551 }
552
553 /* Default name necessary? */
554 if (!pszTrgName)
555 pszTrgName = RTStrAPrintf2("%s Clone", pszSrcName);
556
557 Bstr createFlags;
558 if (!bstrUuid.isEmpty())
559 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
560 Bstr bstrPrimaryGroup;
561 if (groups.size())
562 bstrPrimaryGroup = groups[0];
563 Bstr bstrSettingsFile;
564 CHECK_ERROR_RET(a->virtualBox,
565 ComposeMachineFilename(Bstr(pszTrgName).raw(),
566 bstrPrimaryGroup.raw(),
567 createFlags.raw(),
568 Bstr(pszTrgBaseFolder).raw(),
569 bstrSettingsFile.asOutParam()),
570 RTEXITCODE_FAILURE);
571
572 ComPtr<IMachine> trgMachine;
573 CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(),
574 Bstr(pszTrgName).raw(),
575 ComSafeArrayAsInParam(groups),
576 NULL,
577 createFlags.raw(),
578 trgMachine.asOutParam()),
579 RTEXITCODE_FAILURE);
580
581 /* Start the cloning */
582 ComPtr<IProgress> progress;
583 CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine,
584 mode,
585 ComSafeArrayAsInParam(options),
586 progress.asOutParam()),
587 RTEXITCODE_FAILURE);
588 rc = showProgress(progress);
589 CHECK_PROGRESS_ERROR_RET(progress, ("Clone VM failed"), RTEXITCODE_FAILURE);
590
591 if (fRegister)
592 CHECK_ERROR_RET(a->virtualBox, RegisterMachine(trgMachine), RTEXITCODE_FAILURE);
593
594 Bstr bstrNewName;
595 CHECK_ERROR_RET(trgMachine, COMGETTER(Name)(bstrNewName.asOutParam()), RTEXITCODE_FAILURE);
596 RTPrintf("Machine has been successfully cloned as \"%ls\"\n", bstrNewName.raw());
597
598 return RTEXITCODE_SUCCESS;
599}
600
601RTEXITCODE handleStartVM(HandlerArg *a)
602{
603 HRESULT rc = S_OK;
604 std::list<const char *> VMs;
605 Bstr sessionType;
606 Utf8Str strEnv;
607
608#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
609 /* make sure the VM process will by default start on the same display as VBoxManage */
610 {
611 const char *pszDisplay = RTEnvGet("DISPLAY");
612 if (pszDisplay)
613 strEnv = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
614 const char *pszXAuth = RTEnvGet("XAUTHORITY");
615 if (pszXAuth)
616 strEnv.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
617 }
618#endif
619
620 static const RTGETOPTDEF s_aStartVMOptions[] =
621 {
622 { "--type", 't', RTGETOPT_REQ_STRING },
623 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
624 { "--putenv", 'E', RTGETOPT_REQ_STRING },
625 };
626 int c;
627 RTGETOPTUNION ValueUnion;
628 RTGETOPTSTATE GetState;
629 // start at 0 because main() has hacked both the argc and argv given to us
630 RTGetOptInit(&GetState, a->argc, a->argv, s_aStartVMOptions, RT_ELEMENTS(s_aStartVMOptions),
631 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
632 while ((c = RTGetOpt(&GetState, &ValueUnion)))
633 {
634 switch (c)
635 {
636 case 't': // --type
637 if (!RTStrICmp(ValueUnion.psz, "gui"))
638 {
639 sessionType = "gui";
640 }
641#ifdef VBOX_WITH_VBOXSDL
642 else if (!RTStrICmp(ValueUnion.psz, "sdl"))
643 {
644 sessionType = "sdl";
645 }
646#endif
647#ifdef VBOX_WITH_HEADLESS
648 else if (!RTStrICmp(ValueUnion.psz, "capture"))
649 {
650 sessionType = "capture";
651 }
652 else if (!RTStrICmp(ValueUnion.psz, "headless"))
653 {
654 sessionType = "headless";
655 }
656#endif
657 else
658 sessionType = ValueUnion.psz;
659 break;
660
661 case 'E': // --putenv
662 if (!RTStrStr(ValueUnion.psz, "\n"))
663 strEnv.append(Utf8StrFmt("%s\n", ValueUnion.psz));
664 else
665 return errorSyntax(USAGE_STARTVM, "Parameter to option --putenv must not contain any newline character");
666 break;
667
668 case VINF_GETOPT_NOT_OPTION:
669 VMs.push_back(ValueUnion.psz);
670 break;
671
672 default:
673 if (c > 0)
674 {
675 if (RT_C_IS_PRINT(c))
676 return errorSyntax(USAGE_STARTVM, "Invalid option -%c", c);
677 else
678 return errorSyntax(USAGE_STARTVM, "Invalid option case %i", c);
679 }
680 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
681 return errorSyntax(USAGE_STARTVM, "unknown option: %s\n", ValueUnion.psz);
682 else if (ValueUnion.pDef)
683 return errorSyntax(USAGE_STARTVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
684 else
685 return errorSyntax(USAGE_STARTVM, "error: %Rrs", c);
686 }
687 }
688
689 /* check for required options */
690 if (VMs.empty())
691 return errorSyntax(USAGE_STARTVM, "at least one VM name or uuid required");
692
693 for (std::list<const char *>::const_iterator it = VMs.begin();
694 it != VMs.end();
695 ++it)
696 {
697 HRESULT rc2 = rc;
698 const char *pszVM = *it;
699 ComPtr<IMachine> machine;
700 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(),
701 machine.asOutParam()));
702 if (machine)
703 {
704 ComPtr<IProgress> progress;
705 CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
706 Bstr(strEnv).raw(), progress.asOutParam()));
707 if (SUCCEEDED(rc) && !progress.isNull())
708 {
709 RTPrintf("Waiting for VM \"%s\" to power on...\n", pszVM);
710 CHECK_ERROR(progress, WaitForCompletion(-1));
711 if (SUCCEEDED(rc))
712 {
713 BOOL completed = true;
714 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
715 if (SUCCEEDED(rc))
716 {
717 ASSERT(completed);
718
719 LONG iRc;
720 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
721 if (SUCCEEDED(rc))
722 {
723 if (SUCCEEDED(iRc))
724 RTPrintf("VM \"%s\" has been successfully started.\n", pszVM);
725 else
726 {
727 ProgressErrorInfo info(progress);
728 com::GluePrintErrorInfo(info);
729 }
730 rc = iRc;
731 }
732 }
733 }
734 }
735 }
736
737 /* it's important to always close sessions */
738 a->session->UnlockMachine();
739
740 /* make sure that we remember the failed state */
741 if (FAILED(rc2))
742 rc = rc2;
743 }
744
745 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
746}
747
748RTEXITCODE handleDiscardState(HandlerArg *a)
749{
750 HRESULT rc;
751
752 if (a->argc != 1)
753 return errorSyntax(USAGE_DISCARDSTATE, "Incorrect number of parameters");
754
755 ComPtr<IMachine> machine;
756 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
757 machine.asOutParam()));
758 if (machine)
759 {
760 do
761 {
762 /* we have to open a session for this task */
763 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
764 do
765 {
766 ComPtr<IMachine> sessionMachine;
767 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
768 CHECK_ERROR_BREAK(sessionMachine, DiscardSavedState(true /* fDeleteFile */));
769 } while (0);
770 CHECK_ERROR_BREAK(a->session, UnlockMachine());
771 } while (0);
772 }
773
774 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
775}
776
777RTEXITCODE handleAdoptState(HandlerArg *a)
778{
779 HRESULT rc;
780
781 if (a->argc != 2)
782 return errorSyntax(USAGE_ADOPTSTATE, "Incorrect number of parameters");
783
784 ComPtr<IMachine> machine;
785 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
786 machine.asOutParam()));
787 if (machine)
788 {
789 char szStateFileAbs[RTPATH_MAX] = "";
790 int vrc = RTPathAbs(a->argv[1], szStateFileAbs, sizeof(szStateFileAbs));
791 if (RT_FAILURE(vrc))
792 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
793
794 do
795 {
796 /* we have to open a session for this task */
797 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
798 do
799 {
800 ComPtr<IMachine> sessionMachine;
801 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
802 CHECK_ERROR_BREAK(sessionMachine, AdoptSavedState(Bstr(szStateFileAbs).raw()));
803 } while (0);
804 CHECK_ERROR_BREAK(a->session, UnlockMachine());
805 } while (0);
806 }
807
808 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
809}
810
811RTEXITCODE handleGetExtraData(HandlerArg *a)
812{
813 HRESULT rc = S_OK;
814
815 if (a->argc > 2 || a->argc < 1)
816 return errorSyntax(USAGE_GETEXTRADATA, "Incorrect number of parameters");
817
818 /* global data? */
819 if (!strcmp(a->argv[0], "global"))
820 {
821 /* enumeration? */
822 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
823 {
824 SafeArray<BSTR> aKeys;
825 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
826
827 for (size_t i = 0;
828 i < aKeys.size();
829 ++i)
830 {
831 Bstr bstrKey(aKeys[i]);
832 Bstr bstrValue;
833 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
834 bstrValue.asOutParam()));
835
836 RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
837 }
838 }
839 else
840 {
841 Bstr value;
842 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
843 value.asOutParam()));
844 if (!value.isEmpty())
845 RTPrintf("Value: %ls\n", value.raw());
846 else
847 RTPrintf("No value set!\n");
848 }
849 }
850 else
851 {
852 ComPtr<IMachine> machine;
853 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
854 machine.asOutParam()));
855 if (machine)
856 {
857 /* enumeration? */
858 if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
859 {
860 SafeArray<BSTR> aKeys;
861 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
862
863 for (size_t i = 0;
864 i < aKeys.size();
865 ++i)
866 {
867 Bstr bstrKey(aKeys[i]);
868 Bstr bstrValue;
869 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
870 bstrValue.asOutParam()));
871
872 RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
873 }
874 }
875 else
876 {
877 Bstr value;
878 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
879 value.asOutParam()));
880 if (!value.isEmpty())
881 RTPrintf("Value: %ls\n", value.raw());
882 else
883 RTPrintf("No value set!\n");
884 }
885 }
886 }
887 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
888}
889
890RTEXITCODE handleSetExtraData(HandlerArg *a)
891{
892 HRESULT rc = S_OK;
893
894 if (a->argc < 2)
895 return errorSyntax(USAGE_SETEXTRADATA, "Not enough parameters");
896
897 /* global data? */
898 if (!strcmp(a->argv[0], "global"))
899 {
900 /** @todo passing NULL is deprecated */
901 if (a->argc < 3)
902 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
903 NULL));
904 else if (a->argc == 3)
905 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
906 Bstr(a->argv[2]).raw()));
907 else
908 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
909 }
910 else
911 {
912 ComPtr<IMachine> machine;
913 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
914 machine.asOutParam()));
915 if (machine)
916 {
917 /* open an existing session for the VM */
918 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
919 /* get the session machine */
920 ComPtr<IMachine> sessionMachine;
921 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
922 /** @todo passing NULL is deprecated */
923 if (a->argc < 3)
924 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
925 NULL));
926 else if (a->argc == 3)
927 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
928 Bstr(a->argv[2]).raw()));
929 else
930 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
931 }
932 }
933 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
934}
935
936RTEXITCODE handleSetProperty(HandlerArg *a)
937{
938 HRESULT rc;
939
940 /* there must be two arguments: property name and value */
941 if (a->argc != 2)
942 return errorSyntax(USAGE_SETPROPERTY, "Incorrect number of parameters");
943
944 ComPtr<ISystemProperties> systemProperties;
945 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
946
947 if (!strcmp(a->argv[0], "machinefolder"))
948 {
949 /* reset to default? */
950 if (!strcmp(a->argv[1], "default"))
951 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
952 else
953 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
954 }
955 else if (!strcmp(a->argv[0], "hwvirtexclusive"))
956 {
957 bool fHwVirtExclusive;
958
959 if (!strcmp(a->argv[1], "on"))
960 fHwVirtExclusive = true;
961 else if (!strcmp(a->argv[1], "off"))
962 fHwVirtExclusive = false;
963 else
964 return errorArgument("Invalid hwvirtexclusive argument '%s'", a->argv[1]);
965 CHECK_ERROR(systemProperties, COMSETTER(ExclusiveHwVirt)(fHwVirtExclusive));
966 }
967 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
968 || !strcmp(a->argv[0], "vrdpauthlibrary"))
969 {
970 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
971 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n");
972
973 /* reset to default? */
974 if (!strcmp(a->argv[1], "default"))
975 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
976 else
977 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
978 }
979 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
980 {
981 /* reset to default? */
982 if (!strcmp(a->argv[1], "default"))
983 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
984 else
985 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
986 }
987 else if (!strcmp(a->argv[0], "vrdeextpack"))
988 {
989 /* disable? */
990 if (!strcmp(a->argv[1], "null"))
991 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
992 else
993 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
994 }
995 else if (!strcmp(a->argv[0], "loghistorycount"))
996 {
997 uint32_t uVal;
998 int vrc;
999 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
1000 if (vrc != VINF_SUCCESS)
1001 return errorArgument("Error parsing Log history count '%s'", a->argv[1]);
1002 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
1003 }
1004 else if (!strcmp(a->argv[0], "autostartdbpath"))
1005 {
1006 /* disable? */
1007 if (!strcmp(a->argv[1], "null"))
1008 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(NULL));
1009 else
1010 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(Bstr(a->argv[1]).raw()));
1011 }
1012 else if (!strcmp(a->argv[0], "defaultfrontend"))
1013 {
1014 Bstr bstrDefaultFrontend(a->argv[1]);
1015 if (!strcmp(a->argv[1], "default"))
1016 bstrDefaultFrontend.setNull();
1017 CHECK_ERROR(systemProperties, COMSETTER(DefaultFrontend)(bstrDefaultFrontend.raw()));
1018 }
1019 else if (!strcmp(a->argv[0], "logginglevel"))
1020 {
1021 Bstr bstrLoggingLevel(a->argv[1]);
1022 if (!strcmp(a->argv[1], "default"))
1023 bstrLoggingLevel.setNull();
1024 CHECK_ERROR(systemProperties, COMSETTER(LoggingLevel)(bstrLoggingLevel.raw()));
1025 }
1026 else
1027 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]);
1028
1029 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1030}
1031
1032RTEXITCODE handleSharedFolder(HandlerArg *a)
1033{
1034 HRESULT rc;
1035
1036 /* we need at least a command and target */
1037 if (a->argc < 2)
1038 return errorSyntax(USAGE_SHAREDFOLDER, "Not enough parameters");
1039
1040 const char *pszMachineName = a->argv[1];
1041 ComPtr<IMachine> machine;
1042 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), machine.asOutParam()));
1043 if (!machine)
1044 return RTEXITCODE_FAILURE;
1045
1046 if (!strcmp(a->argv[0], "add"))
1047 {
1048 /* we need at least four more parameters */
1049 if (a->argc < 5)
1050 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Not enough parameters");
1051
1052 char *name = NULL;
1053 char *hostpath = NULL;
1054 bool fTransient = false;
1055 bool fWritable = true;
1056 bool fAutoMount = false;
1057
1058 for (int i = 2; i < a->argc; i++)
1059 {
1060 if ( !strcmp(a->argv[i], "--name")
1061 || !strcmp(a->argv[i], "-name"))
1062 {
1063 if (a->argc <= i + 1 || !*a->argv[i+1])
1064 return errorArgument("Missing argument to '%s'", a->argv[i]);
1065 i++;
1066 name = a->argv[i];
1067 }
1068 else if ( !strcmp(a->argv[i], "--hostpath")
1069 || !strcmp(a->argv[i], "-hostpath"))
1070 {
1071 if (a->argc <= i + 1 || !*a->argv[i+1])
1072 return errorArgument("Missing argument to '%s'", a->argv[i]);
1073 i++;
1074 hostpath = a->argv[i];
1075 }
1076 else if ( !strcmp(a->argv[i], "--readonly")
1077 || !strcmp(a->argv[i], "-readonly"))
1078 {
1079 fWritable = false;
1080 }
1081 else if ( !strcmp(a->argv[i], "--transient")
1082 || !strcmp(a->argv[i], "-transient"))
1083 {
1084 fTransient = true;
1085 }
1086 else if ( !strcmp(a->argv[i], "--automount")
1087 || !strcmp(a->argv[i], "-automount"))
1088 {
1089 fAutoMount = true;
1090 }
1091 else
1092 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1093 }
1094
1095 if (NULL != strstr(name, " "))
1096 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "No spaces allowed in parameter '-name'!");
1097
1098 /* required arguments */
1099 if (!name || !hostpath)
1100 {
1101 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Parameters --name and --hostpath are required");
1102 }
1103
1104 if (fTransient)
1105 {
1106 ComPtr<IConsole> console;
1107
1108 /* open an existing session for the VM */
1109 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1110
1111 /* get the session machine */
1112 ComPtr<IMachine> sessionMachine;
1113 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1114
1115 /* get the session console */
1116 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1117 if (console.isNull())
1118 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1119 "Machine '%s' is not currently running.\n", pszMachineName);
1120
1121 CHECK_ERROR(console, CreateSharedFolder(Bstr(name).raw(),
1122 Bstr(hostpath).raw(),
1123 fWritable, fAutoMount));
1124 a->session->UnlockMachine();
1125 }
1126 else
1127 {
1128 /* open a session for the VM */
1129 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1130
1131 /* get the mutable session machine */
1132 ComPtr<IMachine> sessionMachine;
1133 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1134
1135 CHECK_ERROR(sessionMachine, CreateSharedFolder(Bstr(name).raw(),
1136 Bstr(hostpath).raw(),
1137 fWritable, fAutoMount));
1138 if (SUCCEEDED(rc))
1139 CHECK_ERROR(sessionMachine, SaveSettings());
1140
1141 a->session->UnlockMachine();
1142 }
1143 }
1144 else if (!strcmp(a->argv[0], "remove"))
1145 {
1146 /* we need at least two more parameters */
1147 if (a->argc < 3)
1148 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Not enough parameters");
1149
1150 char *name = NULL;
1151 bool fTransient = false;
1152
1153 for (int i = 2; i < a->argc; i++)
1154 {
1155 if ( !strcmp(a->argv[i], "--name")
1156 || !strcmp(a->argv[i], "-name"))
1157 {
1158 if (a->argc <= i + 1 || !*a->argv[i+1])
1159 return errorArgument("Missing argument to '%s'", a->argv[i]);
1160 i++;
1161 name = a->argv[i];
1162 }
1163 else if ( !strcmp(a->argv[i], "--transient")
1164 || !strcmp(a->argv[i], "-transient"))
1165 {
1166 fTransient = true;
1167 }
1168 else
1169 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1170 }
1171
1172 /* required arguments */
1173 if (!name)
1174 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Parameter --name is required");
1175
1176 if (fTransient)
1177 {
1178 /* open an existing session for the VM */
1179 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1180 /* get the session machine */
1181 ComPtr<IMachine> sessionMachine;
1182 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1183 /* get the session console */
1184 ComPtr<IConsole> console;
1185 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1186 if (console.isNull())
1187 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1188 "Machine '%s' is not currently running.\n", pszMachineName);
1189
1190 CHECK_ERROR(console, RemoveSharedFolder(Bstr(name).raw()));
1191
1192 a->session->UnlockMachine();
1193 }
1194 else
1195 {
1196 /* open a session for the VM */
1197 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1198
1199 /* get the mutable session machine */
1200 ComPtr<IMachine> sessionMachine;
1201 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1202
1203 CHECK_ERROR(sessionMachine, RemoveSharedFolder(Bstr(name).raw()));
1204
1205 /* commit and close the session */
1206 CHECK_ERROR(sessionMachine, SaveSettings());
1207 a->session->UnlockMachine();
1208 }
1209 }
1210 else
1211 return errorSyntax(USAGE_SHAREDFOLDER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
1212
1213 return RTEXITCODE_SUCCESS;
1214}
1215
1216RTEXITCODE handleExtPack(HandlerArg *a)
1217{
1218 if (a->argc < 1)
1219 return errorNoSubcommand();
1220
1221 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1222 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1223
1224 RTGETOPTSTATE GetState;
1225 RTGETOPTUNION ValueUnion;
1226 int ch;
1227 HRESULT hrc = S_OK;
1228
1229 if (!strcmp(a->argv[0], "install"))
1230 {
1231 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
1232 const char *pszName = NULL;
1233 bool fReplace = false;
1234
1235 static const RTGETOPTDEF s_aInstallOptions[] =
1236 {
1237 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1238 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
1239 };
1240
1241 RTCList<RTCString> lstLicenseHashes;
1242 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1243 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1244 {
1245 switch (ch)
1246 {
1247 case 'r':
1248 fReplace = true;
1249 break;
1250
1251 case 'a':
1252 lstLicenseHashes.append(ValueUnion.psz);
1253 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
1254 break;
1255
1256 case VINF_GETOPT_NOT_OPTION:
1257 if (pszName)
1258 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1259 pszName = ValueUnion.psz;
1260 break;
1261
1262 default:
1263 return errorGetOpt(ch, &ValueUnion);
1264 }
1265 }
1266 if (!pszName)
1267 return errorSyntax("No extension pack name was given to \"extpack install\"");
1268
1269 char szPath[RTPATH_MAX];
1270 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
1271 if (RT_FAILURE(vrc))
1272 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszName, vrc);
1273
1274 Bstr bstrTarball(szPath);
1275 Bstr bstrName;
1276 ComPtr<IExtPackFile> ptrExtPackFile;
1277 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1278 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1279 BOOL fShowLicense = true;
1280 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
1281 if (fShowLicense)
1282 {
1283 Bstr bstrLicense;
1284 CHECK_ERROR2I_RET(ptrExtPackFile,
1285 QueryLicense(Bstr("").raw() /* PreferredLocale */,
1286 Bstr("").raw() /* PreferredLanguage */,
1287 Bstr("txt").raw() /* Format */,
1288 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
1289 Utf8Str strLicense(bstrLicense);
1290 uint8_t abHash[RTSHA256_HASH_SIZE];
1291 char szDigest[RTSHA256_DIGEST_LEN + 1];
1292 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
1293 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
1294 AssertRCStmt(vrc, szDigest[0] = '\0');
1295 if (lstLicenseHashes.contains(szDigest))
1296 RTPrintf("License accepted.\n");
1297 else
1298 {
1299 RTPrintf("%s\n", strLicense.c_str());
1300 RTPrintf("Do you agree to these license terms and conditions (y/n)? " );
1301 ch = RTStrmGetCh(g_pStdIn);
1302 RTPrintf("\n");
1303 if (ch != 'y' && ch != 'Y')
1304 {
1305 RTPrintf("Installation of \"%ls\" aborted.\n", bstrName.raw());
1306 return RTEXITCODE_FAILURE;
1307 }
1308 if (szDigest[0])
1309 RTPrintf("License accepted. For batch installaltion add\n"
1310 "--accept-license=%s\n"
1311 "to the VBoxManage command line.\n\n", szDigest);
1312 }
1313 }
1314 ComPtr<IProgress> ptrProgress;
1315 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1316 hrc = showProgress(ptrProgress);
1317 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to install \"%s\"", szPath), RTEXITCODE_FAILURE);
1318
1319 RTPrintf("Successfully installed \"%ls\".\n", bstrName.raw());
1320 }
1321 else if (!strcmp(a->argv[0], "uninstall"))
1322 {
1323 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
1324 const char *pszName = NULL;
1325 bool fForced = false;
1326
1327 static const RTGETOPTDEF s_aUninstallOptions[] =
1328 {
1329 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1330 };
1331
1332 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1333 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1334 {
1335 switch (ch)
1336 {
1337 case 'f':
1338 fForced = true;
1339 break;
1340
1341 case VINF_GETOPT_NOT_OPTION:
1342 if (pszName)
1343 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1344 pszName = ValueUnion.psz;
1345 break;
1346
1347 default:
1348 return errorGetOpt(ch, &ValueUnion);
1349 }
1350 }
1351 if (!pszName)
1352 return errorSyntax("No extension pack name was given to \"extpack uninstall\"");
1353
1354 Bstr bstrName(pszName);
1355 ComPtr<IProgress> ptrProgress;
1356 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1357 hrc = showProgress(ptrProgress);
1358 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to uninstall \"%s\"", pszName), RTEXITCODE_FAILURE);
1359
1360 RTPrintf("Successfully uninstalled \"%s\".\n", pszName);
1361 }
1362 else if (!strcmp(a->argv[0], "cleanup"))
1363 {
1364 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
1365 if (a->argc > 1)
1366 return errorTooManyParameters(&a->argv[1]);
1367 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1368 RTPrintf("Successfully performed extension pack cleanup\n");
1369 }
1370 else
1371 return errorUnknownSubcommand(a->argv[0]);
1372
1373 return RTEXITCODE_SUCCESS;
1374}
1375
1376RTEXITCODE handleUnattendedDetect(HandlerArg *a)
1377{
1378 HRESULT hrc;
1379
1380 /*
1381 * Options. We work directly on an IUnattended instace while parsing
1382 * the options. This saves a lot of extra clutter.
1383 */
1384 bool fMachineReadable = false;
1385 char szIsoPath[RTPATH_MAX];
1386 szIsoPath[0] = '\0';
1387
1388 /*
1389 * Parse options.
1390 */
1391 static const RTGETOPTDEF s_aOptions[] =
1392 {
1393 { "--iso", 'i', RTGETOPT_REQ_STRING },
1394 { "--machine-readable", 'M', RTGETOPT_REQ_NOTHING },
1395 };
1396
1397 RTGETOPTSTATE GetState;
1398 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1399 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1400
1401 int c;
1402 RTGETOPTUNION ValueUnion;
1403 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1404 {
1405 switch (c)
1406 {
1407 case 'i': // --iso
1408 vrc = RTPathAbs(ValueUnion.psz, szIsoPath, sizeof(szIsoPath));
1409 if (RT_FAILURE(vrc))
1410 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1411 break;
1412
1413 case 'M': // --machine-readable.
1414 fMachineReadable = true;
1415 break;
1416
1417 default:
1418 return errorGetOpt(c, &ValueUnion);
1419 }
1420 }
1421
1422 /*
1423 * Check for required stuff.
1424 */
1425 if (szIsoPath[0] == '\0')
1426 return errorSyntax("No ISO specified");
1427
1428 /*
1429 * Do the job.
1430 */
1431 ComPtr<IUnattended> ptrUnattended;
1432 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1433 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szIsoPath).raw()), RTEXITCODE_FAILURE);
1434 CHECK_ERROR2(hrc, ptrUnattended, DetectIsoOS());
1435 RTEXITCODE rcExit = SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1436
1437 /*
1438 * Retrieve the results.
1439 */
1440 Bstr bstrDetectedOSTypeId;
1441 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSTypeId)(bstrDetectedOSTypeId.asOutParam()), RTEXITCODE_FAILURE);
1442 Bstr bstrDetectedVersion;
1443 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSVersion)(bstrDetectedVersion.asOutParam()), RTEXITCODE_FAILURE);
1444 Bstr bstrDetectedFlavor;
1445 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSFlavor)(bstrDetectedFlavor.asOutParam()), RTEXITCODE_FAILURE);
1446 Bstr bstrDetectedLanguages;
1447 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSLanguages)(bstrDetectedLanguages.asOutParam()), RTEXITCODE_FAILURE);
1448 Bstr bstrDetectedHints;
1449 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSHints)(bstrDetectedHints.asOutParam()), RTEXITCODE_FAILURE);
1450 if (fMachineReadable)
1451 RTPrintf("OSTypeId=\"%ls\"\n"
1452 "OSVersion=\"%ls\"\n"
1453 "OSFlavor=\"%ls\"\n"
1454 "OSLanguages=\"%ls\"\n"
1455 "OSHints=\"%ls\"\n",
1456 bstrDetectedOSTypeId.raw(),
1457 bstrDetectedVersion.raw(),
1458 bstrDetectedFlavor.raw(),
1459 bstrDetectedLanguages.raw(),
1460 bstrDetectedHints.raw());
1461 else
1462 {
1463 RTMsgInfo("Detected '%s' to be:\n", szIsoPath);
1464 RTPrintf(" OS TypeId = %ls\n"
1465 " OS Version = %ls\n"
1466 " OS Flavor = %ls\n"
1467 " OS Languages = %ls\n"
1468 " OS Hints = %ls\n",
1469 bstrDetectedOSTypeId.raw(),
1470 bstrDetectedVersion.raw(),
1471 bstrDetectedFlavor.raw(),
1472 bstrDetectedLanguages.raw(),
1473 bstrDetectedHints.raw());
1474 }
1475
1476 return rcExit;
1477}
1478
1479RTEXITCODE handleUnattendedInstall(HandlerArg *a)
1480{
1481 HRESULT hrc;
1482 char szAbsPath[RTPATH_MAX];
1483
1484 /*
1485 * Options. We work directly on an IUnattended instace while parsing
1486 * the options. This saves a lot of extra clutter.
1487 */
1488 ComPtr<IUnattended> ptrUnattended;
1489 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1490 RTCList<RTCString> arrPackageSelectionAdjustments;
1491 ComPtr<IMachine> ptrMachine;
1492 bool fDryRun = false;
1493 const char *pszSessionType = "none";
1494
1495 /*
1496 * Parse options.
1497 */
1498 static const RTGETOPTDEF s_aOptions[] =
1499 {
1500 { "--iso", 'i', RTGETOPT_REQ_STRING },
1501 { "--user", 'u', RTGETOPT_REQ_STRING },
1502 { "--password", 'p', RTGETOPT_REQ_STRING },
1503 { "--password-file", 'X', RTGETOPT_REQ_STRING },
1504 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
1505 { "--key", 'k', RTGETOPT_REQ_STRING },
1506 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
1507 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
1508 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
1509 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
1510 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
1511 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
1512 { "--locale", 'l', RTGETOPT_REQ_STRING },
1513 { "--country", 'Y', RTGETOPT_REQ_STRING },
1514 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
1515 { "--proxy", 'y', RTGETOPT_REQ_STRING },
1516 { "--hostname", 'H', RTGETOPT_REQ_STRING },
1517 { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
1518 { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
1519 // advance options:
1520 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
1521 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
1522 { "--script-template", 'c', RTGETOPT_REQ_STRING },
1523 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
1524 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
1525 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
1526 { "--language", 'L', RTGETOPT_REQ_STRING },
1527 // start vm related options:
1528 { "--start-vm", 'S', RTGETOPT_REQ_STRING },
1529 /** @todo Add a --wait option too for waiting for the VM to shut down or
1530 * something like that...? */
1531 };
1532
1533 RTGETOPTSTATE GetState;
1534 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1535 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1536
1537 int c;
1538 RTGETOPTUNION ValueUnion;
1539 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1540 {
1541 switch (c)
1542 {
1543 case VINF_GETOPT_NOT_OPTION:
1544 if (ptrMachine.isNotNull())
1545 return errorSyntax("VM name/UUID given more than once!");
1546 CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1547 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
1548 break;
1549
1550 case 'i': // --iso
1551 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1552 if (RT_FAILURE(vrc))
1553 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1554 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1555 break;
1556
1557 case 'u': // --user
1558 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1559 break;
1560
1561 case 'p': // --password
1562 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1563 break;
1564
1565 case 'X': // --password-file
1566 {
1567 Utf8Str strPassword;
1568 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
1569 if (rcExit != RTEXITCODE_SUCCESS)
1570 return rcExit;
1571 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
1572 break;
1573 }
1574
1575 case 'U': // --full-user-name
1576 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1577 break;
1578
1579 case 'k': // --key
1580 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1581 break;
1582
1583 case 'A': // --install-additions
1584 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
1585 break;
1586 case 'N': // --no-install-additions
1587 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
1588 break;
1589 case 'a': // --additions-iso
1590 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1591 if (RT_FAILURE(vrc))
1592 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1593 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1594 break;
1595
1596 case 't': // --install-txs
1597 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
1598 break;
1599 case 'T': // --no-install-txs
1600 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
1601 break;
1602 case 'K': // --valiation-kit-iso
1603 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1604 if (RT_FAILURE(vrc))
1605 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1606 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1607 break;
1608
1609 case 'l': // --locale
1610 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1611 break;
1612
1613 case 'Y': // --country
1614 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1615 break;
1616
1617 case 'z': // --time-zone;
1618 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1619 break;
1620
1621 case 'y': // --proxy
1622 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1623 break;
1624
1625 case 'H': // --hostname
1626 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1627 break;
1628
1629 case 's': // --package-selection-adjustment
1630 arrPackageSelectionAdjustments.append(ValueUnion.psz);
1631 break;
1632
1633 case 'D':
1634 fDryRun = true;
1635 break;
1636
1637 case 'x': // --auxiliary-base-path
1638 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1639 if (RT_FAILURE(vrc))
1640 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1641 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1642 break;
1643
1644 case 'm': // --image-index
1645 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
1646 break;
1647
1648 case 'c': // --script-template
1649 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1650 if (RT_FAILURE(vrc))
1651 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1652 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1653 break;
1654
1655 case 'C': // --post-install-script-template
1656 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1657 if (RT_FAILURE(vrc))
1658 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1659 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1660 break;
1661
1662 case 'P': // --post-install-command.
1663 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1664 break;
1665
1666 case 'I': // --extra-install-kernel-parameters
1667 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1668 break;
1669
1670 case 'L': // --language
1671 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1672 break;
1673
1674 case 'S': // --start-vm
1675 pszSessionType = ValueUnion.psz;
1676 break;
1677
1678 default:
1679 return errorGetOpt(c, &ValueUnion);
1680 }
1681 }
1682
1683 /*
1684 * Check for required stuff.
1685 */
1686 if (ptrMachine.isNull())
1687 return errorSyntax("Missing VM name/UUID");
1688
1689 /*
1690 * Set accumulative attributes.
1691 */
1692 if (arrPackageSelectionAdjustments.size() == 1)
1693 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
1694 RTEXITCODE_FAILURE);
1695 else if (arrPackageSelectionAdjustments.size() > 1)
1696 {
1697 RTCString strAdjustments;
1698 strAdjustments.join(arrPackageSelectionAdjustments, ";");
1699 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
1700 }
1701
1702 /*
1703 * Get details about the machine so we can display them below.
1704 */
1705 Bstr bstrMachineName;
1706 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
1707 Bstr bstrUuid;
1708 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
1709 BSTR bstrInstalledOS;
1710 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
1711 Utf8Str strInstalledOS(bstrInstalledOS);
1712
1713 /*
1714 * Temporarily lock the machine to check whether it's running or not.
1715 * We take this opportunity to disable the first run wizard.
1716 */
1717 CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1718 {
1719 ComPtr<IConsole> ptrConsole;
1720 CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
1721
1722 if ( ptrConsole.isNull()
1723 && SUCCEEDED(hrc)
1724 && ( RTStrICmp(pszSessionType, "gui") == 0
1725 || RTStrICmp(pszSessionType, "none") == 0))
1726 {
1727 ComPtr<IMachine> ptrSessonMachine;
1728 CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
1729 if (ptrSessonMachine.isNotNull())
1730 {
1731 CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
1732 }
1733 }
1734
1735 a->session->UnlockMachine();
1736 if (FAILED(hrc))
1737 return RTEXITCODE_FAILURE;
1738 if (ptrConsole.isNotNull())
1739 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%ls' is currently running", bstrMachineName.raw());
1740 }
1741
1742 /*
1743 * Do the work.
1744 */
1745 RTMsgInfo("%s unattended installation of %s in machine '%ls' (%ls).\n",
1746 RTStrICmp(pszSessionType, "none") == 0 ? "Preparing" : "Starting",
1747 strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
1748
1749 CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
1750 if (!fDryRun)
1751 {
1752 CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
1753 CHECK_ERROR2_RET(hrc, ptrUnattended,ReconfigureVM(), RTEXITCODE_FAILURE);
1754 }
1755
1756 /*
1757 * Retrieve and display the parameters actually used.
1758 */
1759 RTMsgInfo("Using values:\n");
1760#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
1761 a_Type Value; \
1762 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
1763 if (SUCCEEDED(hrc2)) \
1764 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
1765 else \
1766 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1767 } while (0)
1768#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
1769 Bstr bstrString; \
1770 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
1771 if (SUCCEEDED(hrc2)) \
1772 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
1773 else \
1774 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1775 } while (0)
1776
1777 SHOW_STR_ATTR(IsoPath, "isoPath");
1778 SHOW_STR_ATTR(User, "user");
1779 SHOW_STR_ATTR(Password, "password");
1780 SHOW_STR_ATTR(FullUserName, "fullUserName");
1781 SHOW_STR_ATTR(ProductKey, "productKey");
1782 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
1783 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
1784 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
1785 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
1786 SHOW_STR_ATTR(Locale, "locale");
1787 SHOW_STR_ATTR(Country, "country");
1788 SHOW_STR_ATTR(TimeZone, "timeZone");
1789 SHOW_STR_ATTR(Proxy, "proxy");
1790 SHOW_STR_ATTR(Hostname, "hostname");
1791 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
1792 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
1793 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
1794 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
1795 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
1796 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
1797 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
1798 SHOW_STR_ATTR(Language, "language");
1799 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
1800 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
1801 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
1802 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
1803 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
1804
1805#undef SHOW_STR_ATTR
1806#undef SHOW_ATTR
1807
1808 /* We can drop the IUnatteded object now. */
1809 ptrUnattended.setNull();
1810
1811 /*
1812 * Start the VM if requested.
1813 */
1814 if ( fDryRun
1815 || RTStrICmp(pszSessionType, "none") == 0)
1816 {
1817 if (!fDryRun)
1818 RTMsgInfo("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n", bstrMachineName.raw(), bstrUuid.raw());
1819 hrc = S_OK;
1820 }
1821 else
1822 {
1823 Bstr env;
1824#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1825 /* make sure the VM process will start on the same display as VBoxManage */
1826 Utf8Str str;
1827 const char *pszDisplay = RTEnvGet("DISPLAY");
1828 if (pszDisplay)
1829 str = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
1830 const char *pszXAuth = RTEnvGet("XAUTHORITY");
1831 if (pszXAuth)
1832 str.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
1833 env = str;
1834#endif
1835 ComPtr<IProgress> ptrProgress;
1836 CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), env.raw(), ptrProgress.asOutParam()));
1837 if (SUCCEEDED(hrc) && !ptrProgress.isNull())
1838 {
1839 RTMsgInfo("Waiting for VM '%ls' to power on...\n", bstrMachineName.raw());
1840 CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
1841 if (SUCCEEDED(hrc))
1842 {
1843 BOOL fCompleted = true;
1844 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
1845 if (SUCCEEDED(hrc))
1846 {
1847 ASSERT(fCompleted);
1848
1849 LONG iRc;
1850 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
1851 if (SUCCEEDED(hrc))
1852 {
1853 if (SUCCEEDED(iRc))
1854 RTMsgInfo("VM '%ls' (%ls) has been successfully started.\n", bstrMachineName.raw(), bstrUuid.raw());
1855 else
1856 {
1857 ProgressErrorInfo info(ptrProgress);
1858 com::GluePrintErrorInfo(info);
1859 }
1860 hrc = iRc;
1861 }
1862 }
1863 }
1864 }
1865
1866 /*
1867 * Do we wait for the VM to power down?
1868 */
1869 }
1870
1871 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1872}
1873
1874
1875RTEXITCODE handleUnattended(HandlerArg *a)
1876{
1877 /*
1878 * Sub-command switch.
1879 */
1880 if (a->argc < 1)
1881 return errorNoSubcommand();
1882
1883 if (!strcmp(a->argv[0], "detect"))
1884 {
1885 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
1886 return handleUnattendedDetect(a);
1887 }
1888
1889 if (!strcmp(a->argv[0], "install"))
1890 {
1891 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
1892 return handleUnattendedInstall(a);
1893 }
1894
1895 /* Consider some kind of create-vm-and-install-guest-os command. */
1896 return errorUnknownSubcommand(a->argv[0]);
1897}
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