VirtualBox

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

Last change on this file since 73927 was 73740, checked in by vboxsync, 6 years ago

Main: bugref:8612: add implementaion of Machine::applyDefault() in case of new vm creation.

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