VirtualBox

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

Last change on this file since 73673 was 73664, checked in by vboxsync, 6 years ago

Main: bugref:8598: added "Keep hardware UUIDs" option to "clone machine" operation.

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