VirtualBox

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

Last change on this file since 72524 was 71998, checked in by vboxsync, 7 years ago

bugref:8345. Fixed the case when user omits or forgets to point the type of move.

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