VirtualBox

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

Last change on this file since 66297 was 66297, checked in by vboxsync, 8 years ago

VBoxManage extpack install: Return RTEXITCODE_FAILURE when not accepting the license. duh.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.1 KB
Line 
1/* $Id: VBoxManageMisc.cpp 66297 2017-03-28 11:54:30Z vboxsync $ */
2/** @file
3 * VBoxManage - VirtualBox's command-line interface.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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/stream.h>
45#include <iprt/string.h>
46#include <iprt/stdarg.h>
47#include <iprt/thread.h>
48#include <iprt/uuid.h>
49#include <iprt/getopt.h>
50#include <iprt/ctype.h>
51#include <VBox/version.h>
52#include <VBox/log.h>
53
54#include "VBoxManage.h"
55
56#include <list>
57
58using namespace com;
59
60
61
62RTEXITCODE handleRegisterVM(HandlerArg *a)
63{
64 HRESULT rc;
65
66 if (a->argc != 1)
67 return errorSyntax(USAGE_REGISTERVM, "Incorrect number of parameters");
68
69 ComPtr<IMachine> machine;
70 /** @todo Ugly hack to get both the API interpretation of relative paths
71 * and the client's interpretation of relative paths. Remove after the API
72 * has been redesigned. */
73 rc = a->virtualBox->OpenMachine(Bstr(a->argv[0]).raw(),
74 machine.asOutParam());
75 if (rc == VBOX_E_FILE_ERROR)
76 {
77 char szVMFileAbs[RTPATH_MAX] = "";
78 int vrc = RTPathAbs(a->argv[0], szVMFileAbs, sizeof(szVMFileAbs));
79 if (RT_FAILURE(vrc))
80 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
81 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(szVMFileAbs).raw(),
82 machine.asOutParam()));
83 }
84 else if (FAILED(rc))
85 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(a->argv[0]).raw(),
86 machine.asOutParam()));
87 if (SUCCEEDED(rc))
88 {
89 ASSERT(machine);
90 CHECK_ERROR(a->virtualBox, RegisterMachine(machine));
91 }
92 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
93}
94
95static const RTGETOPTDEF g_aUnregisterVMOptions[] =
96{
97 { "--delete", 'd', RTGETOPT_REQ_NOTHING },
98 { "-delete", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
99};
100
101RTEXITCODE handleUnregisterVM(HandlerArg *a)
102{
103 HRESULT rc;
104 const char *VMName = NULL;
105 bool fDelete = false;
106
107 int c;
108 RTGETOPTUNION ValueUnion;
109 RTGETOPTSTATE GetState;
110 // start at 0 because main() has hacked both the argc and argv given to us
111 RTGetOptInit(&GetState, a->argc, a->argv, g_aUnregisterVMOptions, RT_ELEMENTS(g_aUnregisterVMOptions),
112 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
113 while ((c = RTGetOpt(&GetState, &ValueUnion)))
114 {
115 switch (c)
116 {
117 case 'd': // --delete
118 fDelete = true;
119 break;
120
121 case VINF_GETOPT_NOT_OPTION:
122 if (!VMName)
123 VMName = ValueUnion.psz;
124 else
125 return errorSyntax(USAGE_UNREGISTERVM, "Invalid parameter '%s'", ValueUnion.psz);
126 break;
127
128 default:
129 if (c > 0)
130 {
131 if (RT_C_IS_PRINT(c))
132 return errorSyntax(USAGE_UNREGISTERVM, "Invalid option -%c", c);
133 else
134 return errorSyntax(USAGE_UNREGISTERVM, "Invalid option case %i", c);
135 }
136 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
137 return errorSyntax(USAGE_UNREGISTERVM, "unknown option: %s\n", ValueUnion.psz);
138 else if (ValueUnion.pDef)
139 return errorSyntax(USAGE_UNREGISTERVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
140 else
141 return errorSyntax(USAGE_UNREGISTERVM, "error: %Rrs", c);
142 }
143 }
144
145 /* check for required options */
146 if (!VMName)
147 return errorSyntax(USAGE_UNREGISTERVM, "VM name required");
148
149 ComPtr<IMachine> machine;
150 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(VMName).raw(),
151 machine.asOutParam()),
152 RTEXITCODE_FAILURE);
153 SafeIfaceArray<IMedium> aMedia;
154 CHECK_ERROR_RET(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly,
155 ComSafeArrayAsOutParam(aMedia)),
156 RTEXITCODE_FAILURE);
157 if (fDelete)
158 {
159 ComPtr<IProgress> pProgress;
160 CHECK_ERROR_RET(machine, DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress.asOutParam()),
161 RTEXITCODE_FAILURE);
162
163 rc = showProgress(pProgress);
164 CHECK_PROGRESS_ERROR_RET(pProgress, ("Machine delete failed"), RTEXITCODE_FAILURE);
165 }
166 else
167 {
168 /* Note that the IMachine::Unregister method will return the medium
169 * reference in a sane order, which means that closing will normally
170 * succeed, unless there is still another machine which uses the
171 * medium. No harm done if we ignore the error. */
172 for (size_t i = 0; i < aMedia.size(); i++)
173 {
174 IMedium *pMedium = aMedia[i];
175 if (pMedium)
176 rc = pMedium->Close();
177 }
178 rc = S_OK;
179 }
180 return RTEXITCODE_SUCCESS;
181}
182
183static const RTGETOPTDEF g_aCreateVMOptions[] =
184{
185 { "--name", 'n', RTGETOPT_REQ_STRING },
186 { "-name", 'n', RTGETOPT_REQ_STRING },
187 { "--groups", 'g', RTGETOPT_REQ_STRING },
188 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
189 { "-basefolder", 'p', RTGETOPT_REQ_STRING },
190 { "--ostype", 'o', RTGETOPT_REQ_STRING },
191 { "-ostype", 'o', RTGETOPT_REQ_STRING },
192 { "--uuid", 'u', RTGETOPT_REQ_UUID },
193 { "-uuid", 'u', RTGETOPT_REQ_UUID },
194 { "--register", 'r', RTGETOPT_REQ_NOTHING },
195 { "-register", 'r', RTGETOPT_REQ_NOTHING },
196};
197
198RTEXITCODE handleCreateVM(HandlerArg *a)
199{
200 HRESULT rc;
201 Bstr bstrBaseFolder;
202 Bstr bstrName;
203 Bstr bstrOsTypeId;
204 Bstr bstrUuid;
205 bool fRegister = false;
206 com::SafeArray<BSTR> groups;
207
208 int c;
209 RTGETOPTUNION ValueUnion;
210 RTGETOPTSTATE GetState;
211 // start at 0 because main() has hacked both the argc and argv given to us
212 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateVMOptions, RT_ELEMENTS(g_aCreateVMOptions),
213 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
214 while ((c = RTGetOpt(&GetState, &ValueUnion)))
215 {
216 switch (c)
217 {
218 case 'n': // --name
219 bstrName = ValueUnion.psz;
220 break;
221
222 case 'g': // --groups
223 parseGroups(ValueUnion.psz, &groups);
224 break;
225
226 case 'p': // --basefolder
227 bstrBaseFolder = ValueUnion.psz;
228 break;
229
230 case 'o': // --ostype
231 bstrOsTypeId = ValueUnion.psz;
232 break;
233
234 case 'u': // --uuid
235 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
236 break;
237
238 case 'r': // --register
239 fRegister = true;
240 break;
241
242 default:
243 return errorGetOpt(USAGE_CREATEVM, c, &ValueUnion);
244 }
245 }
246
247 /* check for required options */
248 if (bstrName.isEmpty())
249 return errorSyntax(USAGE_CREATEVM, "Parameter --name is required");
250
251 do
252 {
253 Bstr createFlags;
254 if (!bstrUuid.isEmpty())
255 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
256 Bstr bstrPrimaryGroup;
257 if (groups.size())
258 bstrPrimaryGroup = groups[0];
259 Bstr bstrSettingsFile;
260 CHECK_ERROR_BREAK(a->virtualBox,
261 ComposeMachineFilename(bstrName.raw(),
262 bstrPrimaryGroup.raw(),
263 createFlags.raw(),
264 bstrBaseFolder.raw(),
265 bstrSettingsFile.asOutParam()));
266 ComPtr<IMachine> machine;
267 CHECK_ERROR_BREAK(a->virtualBox,
268 CreateMachine(bstrSettingsFile.raw(),
269 bstrName.raw(),
270 ComSafeArrayAsInParam(groups),
271 bstrOsTypeId.raw(),
272 createFlags.raw(),
273 machine.asOutParam()));
274
275 CHECK_ERROR_BREAK(machine, SaveSettings());
276 if (fRegister)
277 {
278 CHECK_ERROR_BREAK(a->virtualBox, RegisterMachine(machine));
279 }
280 Bstr uuid;
281 CHECK_ERROR_BREAK(machine, COMGETTER(Id)(uuid.asOutParam()));
282 Bstr settingsFile;
283 CHECK_ERROR_BREAK(machine, COMGETTER(SettingsFilePath)(settingsFile.asOutParam()));
284 RTPrintf("Virtual machine '%ls' is created%s.\n"
285 "UUID: %s\n"
286 "Settings file: '%ls'\n",
287 bstrName.raw(), fRegister ? " and registered" : "",
288 Utf8Str(uuid).c_str(), settingsFile.raw());
289 }
290 while (0);
291
292 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
293}
294
295static const RTGETOPTDEF g_aCloneVMOptions[] =
296{
297 { "--snapshot", 's', RTGETOPT_REQ_STRING },
298 { "--name", 'n', RTGETOPT_REQ_STRING },
299 { "--groups", 'g', RTGETOPT_REQ_STRING },
300 { "--mode", 'm', RTGETOPT_REQ_STRING },
301 { "--options", 'o', RTGETOPT_REQ_STRING },
302 { "--register", 'r', RTGETOPT_REQ_NOTHING },
303 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
304 { "--uuid", 'u', RTGETOPT_REQ_UUID },
305};
306
307static int parseCloneMode(const char *psz, CloneMode_T *pMode)
308{
309 if (!RTStrICmp(psz, "machine"))
310 *pMode = CloneMode_MachineState;
311 else if (!RTStrICmp(psz, "machineandchildren"))
312 *pMode = CloneMode_MachineAndChildStates;
313 else if (!RTStrICmp(psz, "all"))
314 *pMode = CloneMode_AllStates;
315 else
316 return VERR_PARSE_ERROR;
317
318 return VINF_SUCCESS;
319}
320
321static int parseCloneOptions(const char *psz, com::SafeArray<CloneOptions_T> *options)
322{
323 int rc = VINF_SUCCESS;
324 while (psz && *psz && RT_SUCCESS(rc))
325 {
326 size_t len;
327 const char *pszComma = strchr(psz, ',');
328 if (pszComma)
329 len = pszComma - psz;
330 else
331 len = strlen(psz);
332 if (len > 0)
333 {
334 if (!RTStrNICmp(psz, "KeepAllMACs", len))
335 options->push_back(CloneOptions_KeepAllMACs);
336 else if (!RTStrNICmp(psz, "KeepNATMACs", len))
337 options->push_back(CloneOptions_KeepNATMACs);
338 else if (!RTStrNICmp(psz, "KeepDiskNames", len))
339 options->push_back(CloneOptions_KeepDiskNames);
340 else if ( !RTStrNICmp(psz, "Link", len)
341 || !RTStrNICmp(psz, "Linked", len))
342 options->push_back(CloneOptions_Link);
343 else
344 rc = VERR_PARSE_ERROR;
345 }
346 if (pszComma)
347 psz += len + 1;
348 else
349 psz += len;
350 }
351
352 return rc;
353}
354
355RTEXITCODE handleCloneVM(HandlerArg *a)
356{
357 HRESULT rc;
358 const char *pszSrcName = NULL;
359 const char *pszSnapshotName = NULL;
360 CloneMode_T mode = CloneMode_MachineState;
361 com::SafeArray<CloneOptions_T> options;
362 const char *pszTrgName = NULL;
363 const char *pszTrgBaseFolder = NULL;
364 bool fRegister = false;
365 Bstr bstrUuid;
366 com::SafeArray<BSTR> groups;
367
368 int c;
369 RTGETOPTUNION ValueUnion;
370 RTGETOPTSTATE GetState;
371 // start at 0 because main() has hacked both the argc and argv given to us
372 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneVMOptions, RT_ELEMENTS(g_aCloneVMOptions),
373 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
374 while ((c = RTGetOpt(&GetState, &ValueUnion)))
375 {
376 switch (c)
377 {
378 case 's': // --snapshot
379 pszSnapshotName = ValueUnion.psz;
380 break;
381
382 case 'n': // --name
383 pszTrgName = ValueUnion.psz;
384 break;
385
386 case 'g': // --groups
387 parseGroups(ValueUnion.psz, &groups);
388 break;
389
390 case 'p': // --basefolder
391 pszTrgBaseFolder = ValueUnion.psz;
392 break;
393
394 case 'm': // --mode
395 if (RT_FAILURE(parseCloneMode(ValueUnion.psz, &mode)))
396 return errorArgument("Invalid clone mode '%s'\n", ValueUnion.psz);
397 break;
398
399 case 'o': // --options
400 if (RT_FAILURE(parseCloneOptions(ValueUnion.psz, &options)))
401 return errorArgument("Invalid clone options '%s'\n", ValueUnion.psz);
402 break;
403
404 case 'u': // --uuid
405 bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
406 break;
407
408 case 'r': // --register
409 fRegister = true;
410 break;
411
412 case VINF_GETOPT_NOT_OPTION:
413 if (!pszSrcName)
414 pszSrcName = ValueUnion.psz;
415 else
416 return errorSyntax(USAGE_CLONEVM, "Invalid parameter '%s'", ValueUnion.psz);
417 break;
418
419 default:
420 return errorGetOpt(USAGE_CLONEVM, c, &ValueUnion);
421 }
422 }
423
424 /* Check for required options */
425 if (!pszSrcName)
426 return errorSyntax(USAGE_CLONEVM, "VM name required");
427
428 /* Get the machine object */
429 ComPtr<IMachine> srcMachine;
430 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
431 srcMachine.asOutParam()),
432 RTEXITCODE_FAILURE);
433
434 /* If a snapshot name/uuid was given, get the particular machine of this
435 * snapshot. */
436 if (pszSnapshotName)
437 {
438 ComPtr<ISnapshot> srcSnapshot;
439 CHECK_ERROR_RET(srcMachine, FindSnapshot(Bstr(pszSnapshotName).raw(),
440 srcSnapshot.asOutParam()),
441 RTEXITCODE_FAILURE);
442 CHECK_ERROR_RET(srcSnapshot, COMGETTER(Machine)(srcMachine.asOutParam()),
443 RTEXITCODE_FAILURE);
444 }
445
446 /* Default name necessary? */
447 if (!pszTrgName)
448 pszTrgName = RTStrAPrintf2("%s Clone", pszSrcName);
449
450 Bstr createFlags;
451 if (!bstrUuid.isEmpty())
452 createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
453 Bstr bstrPrimaryGroup;
454 if (groups.size())
455 bstrPrimaryGroup = groups[0];
456 Bstr bstrSettingsFile;
457 CHECK_ERROR_RET(a->virtualBox,
458 ComposeMachineFilename(Bstr(pszTrgName).raw(),
459 bstrPrimaryGroup.raw(),
460 createFlags.raw(),
461 Bstr(pszTrgBaseFolder).raw(),
462 bstrSettingsFile.asOutParam()),
463 RTEXITCODE_FAILURE);
464
465 ComPtr<IMachine> trgMachine;
466 CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(),
467 Bstr(pszTrgName).raw(),
468 ComSafeArrayAsInParam(groups),
469 NULL,
470 createFlags.raw(),
471 trgMachine.asOutParam()),
472 RTEXITCODE_FAILURE);
473
474 /* Start the cloning */
475 ComPtr<IProgress> progress;
476 CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine,
477 mode,
478 ComSafeArrayAsInParam(options),
479 progress.asOutParam()),
480 RTEXITCODE_FAILURE);
481 rc = showProgress(progress);
482 CHECK_PROGRESS_ERROR_RET(progress, ("Clone VM failed"), RTEXITCODE_FAILURE);
483
484 if (fRegister)
485 CHECK_ERROR_RET(a->virtualBox, RegisterMachine(trgMachine), RTEXITCODE_FAILURE);
486
487 Bstr bstrNewName;
488 CHECK_ERROR_RET(trgMachine, COMGETTER(Name)(bstrNewName.asOutParam()), RTEXITCODE_FAILURE);
489 RTPrintf("Machine has been successfully cloned as \"%ls\"\n", bstrNewName.raw());
490
491 return RTEXITCODE_SUCCESS;
492}
493
494RTEXITCODE handleStartVM(HandlerArg *a)
495{
496 HRESULT rc = S_OK;
497 std::list<const char *> VMs;
498 Bstr sessionType;
499 Utf8Str strEnv;
500
501#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
502 /* make sure the VM process will by default start on the same display as VBoxManage */
503 {
504 const char *pszDisplay = RTEnvGet("DISPLAY");
505 if (pszDisplay)
506 strEnv = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
507 const char *pszXAuth = RTEnvGet("XAUTHORITY");
508 if (pszXAuth)
509 strEnv.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
510 }
511#endif
512
513 static const RTGETOPTDEF s_aStartVMOptions[] =
514 {
515 { "--type", 't', RTGETOPT_REQ_STRING },
516 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
517 { "--putenv", 'E', RTGETOPT_REQ_STRING },
518 };
519 int c;
520 RTGETOPTUNION ValueUnion;
521 RTGETOPTSTATE GetState;
522 // start at 0 because main() has hacked both the argc and argv given to us
523 RTGetOptInit(&GetState, a->argc, a->argv, s_aStartVMOptions, RT_ELEMENTS(s_aStartVMOptions),
524 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
525 while ((c = RTGetOpt(&GetState, &ValueUnion)))
526 {
527 switch (c)
528 {
529 case 't': // --type
530 if (!RTStrICmp(ValueUnion.psz, "gui"))
531 {
532 sessionType = "gui";
533 }
534#ifdef VBOX_WITH_VBOXSDL
535 else if (!RTStrICmp(ValueUnion.psz, "sdl"))
536 {
537 sessionType = "sdl";
538 }
539#endif
540#ifdef VBOX_WITH_HEADLESS
541 else if (!RTStrICmp(ValueUnion.psz, "capture"))
542 {
543 sessionType = "capture";
544 }
545 else if (!RTStrICmp(ValueUnion.psz, "headless"))
546 {
547 sessionType = "headless";
548 }
549#endif
550 else
551 sessionType = ValueUnion.psz;
552 break;
553
554 case 'E': // --putenv
555 if (!RTStrStr(ValueUnion.psz, "\n"))
556 strEnv.append(Utf8StrFmt("%s\n", ValueUnion.psz));
557 else
558 return errorSyntax(USAGE_STARTVM, "Parameter to option --putenv must not contain any newline character");
559 break;
560
561 case VINF_GETOPT_NOT_OPTION:
562 VMs.push_back(ValueUnion.psz);
563 break;
564
565 default:
566 if (c > 0)
567 {
568 if (RT_C_IS_PRINT(c))
569 return errorSyntax(USAGE_STARTVM, "Invalid option -%c", c);
570 else
571 return errorSyntax(USAGE_STARTVM, "Invalid option case %i", c);
572 }
573 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
574 return errorSyntax(USAGE_STARTVM, "unknown option: %s\n", ValueUnion.psz);
575 else if (ValueUnion.pDef)
576 return errorSyntax(USAGE_STARTVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
577 else
578 return errorSyntax(USAGE_STARTVM, "error: %Rrs", c);
579 }
580 }
581
582 /* check for required options */
583 if (VMs.empty())
584 return errorSyntax(USAGE_STARTVM, "at least one VM name or uuid required");
585
586 for (std::list<const char *>::const_iterator it = VMs.begin();
587 it != VMs.end();
588 ++it)
589 {
590 HRESULT rc2 = rc;
591 const char *pszVM = *it;
592 ComPtr<IMachine> machine;
593 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(),
594 machine.asOutParam()));
595 if (machine)
596 {
597 ComPtr<IProgress> progress;
598 CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
599 Bstr(strEnv).raw(), progress.asOutParam()));
600 if (SUCCEEDED(rc) && !progress.isNull())
601 {
602 RTPrintf("Waiting for VM \"%s\" to power on...\n", pszVM);
603 CHECK_ERROR(progress, WaitForCompletion(-1));
604 if (SUCCEEDED(rc))
605 {
606 BOOL completed = true;
607 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
608 if (SUCCEEDED(rc))
609 {
610 ASSERT(completed);
611
612 LONG iRc;
613 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
614 if (SUCCEEDED(rc))
615 {
616 if (SUCCEEDED(iRc))
617 RTPrintf("VM \"%s\" has been successfully started.\n", pszVM);
618 else
619 {
620 ProgressErrorInfo info(progress);
621 com::GluePrintErrorInfo(info);
622 }
623 rc = iRc;
624 }
625 }
626 }
627 }
628 }
629
630 /* it's important to always close sessions */
631 a->session->UnlockMachine();
632
633 /* make sure that we remember the failed state */
634 if (FAILED(rc2))
635 rc = rc2;
636 }
637
638 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
639}
640
641RTEXITCODE handleDiscardState(HandlerArg *a)
642{
643 HRESULT rc;
644
645 if (a->argc != 1)
646 return errorSyntax(USAGE_DISCARDSTATE, "Incorrect number of parameters");
647
648 ComPtr<IMachine> machine;
649 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
650 machine.asOutParam()));
651 if (machine)
652 {
653 do
654 {
655 /* we have to open a session for this task */
656 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
657 do
658 {
659 ComPtr<IMachine> sessionMachine;
660 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
661 CHECK_ERROR_BREAK(sessionMachine, DiscardSavedState(true /* fDeleteFile */));
662 } while (0);
663 CHECK_ERROR_BREAK(a->session, UnlockMachine());
664 } while (0);
665 }
666
667 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
668}
669
670RTEXITCODE handleAdoptState(HandlerArg *a)
671{
672 HRESULT rc;
673
674 if (a->argc != 2)
675 return errorSyntax(USAGE_ADOPTSTATE, "Incorrect number of parameters");
676
677 ComPtr<IMachine> machine;
678 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
679 machine.asOutParam()));
680 if (machine)
681 {
682 char szStateFileAbs[RTPATH_MAX] = "";
683 int vrc = RTPathAbs(a->argv[1], szStateFileAbs, sizeof(szStateFileAbs));
684 if (RT_FAILURE(vrc))
685 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
686
687 do
688 {
689 /* we have to open a session for this task */
690 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
691 do
692 {
693 ComPtr<IMachine> sessionMachine;
694 CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
695 CHECK_ERROR_BREAK(sessionMachine, AdoptSavedState(Bstr(szStateFileAbs).raw()));
696 } while (0);
697 CHECK_ERROR_BREAK(a->session, UnlockMachine());
698 } while (0);
699 }
700
701 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
702}
703
704RTEXITCODE handleGetExtraData(HandlerArg *a)
705{
706 HRESULT rc = S_OK;
707
708 if (a->argc != 2)
709 return errorSyntax(USAGE_GETEXTRADATA, "Incorrect number of parameters");
710
711 /* global data? */
712 if (!strcmp(a->argv[0], "global"))
713 {
714 /* enumeration? */
715 if (!strcmp(a->argv[1], "enumerate"))
716 {
717 SafeArray<BSTR> aKeys;
718 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
719
720 for (size_t i = 0;
721 i < aKeys.size();
722 ++i)
723 {
724 Bstr bstrKey(aKeys[i]);
725 Bstr bstrValue;
726 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
727 bstrValue.asOutParam()));
728
729 RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
730 }
731 }
732 else
733 {
734 Bstr value;
735 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
736 value.asOutParam()));
737 if (!value.isEmpty())
738 RTPrintf("Value: %ls\n", value.raw());
739 else
740 RTPrintf("No value set!\n");
741 }
742 }
743 else
744 {
745 ComPtr<IMachine> machine;
746 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
747 machine.asOutParam()));
748 if (machine)
749 {
750 /* enumeration? */
751 if (!strcmp(a->argv[1], "enumerate"))
752 {
753 SafeArray<BSTR> aKeys;
754 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
755
756 for (size_t i = 0;
757 i < aKeys.size();
758 ++i)
759 {
760 Bstr bstrKey(aKeys[i]);
761 Bstr bstrValue;
762 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
763 bstrValue.asOutParam()));
764
765 RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
766 }
767 }
768 else
769 {
770 Bstr value;
771 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
772 value.asOutParam()));
773 if (!value.isEmpty())
774 RTPrintf("Value: %ls\n", value.raw());
775 else
776 RTPrintf("No value set!\n");
777 }
778 }
779 }
780 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
781}
782
783RTEXITCODE handleSetExtraData(HandlerArg *a)
784{
785 HRESULT rc = S_OK;
786
787 if (a->argc < 2)
788 return errorSyntax(USAGE_SETEXTRADATA, "Not enough parameters");
789
790 /* global data? */
791 if (!strcmp(a->argv[0], "global"))
792 {
793 /** @todo passing NULL is deprecated */
794 if (a->argc < 3)
795 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
796 NULL));
797 else if (a->argc == 3)
798 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
799 Bstr(a->argv[2]).raw()));
800 else
801 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
802 }
803 else
804 {
805 ComPtr<IMachine> machine;
806 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
807 machine.asOutParam()));
808 if (machine)
809 {
810 /* open an existing session for the VM */
811 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
812 /* get the session machine */
813 ComPtr<IMachine> sessionMachine;
814 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
815 /** @todo passing NULL is deprecated */
816 if (a->argc < 3)
817 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
818 NULL));
819 else if (a->argc == 3)
820 CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
821 Bstr(a->argv[2]).raw()));
822 else
823 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
824 }
825 }
826 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
827}
828
829RTEXITCODE handleSetProperty(HandlerArg *a)
830{
831 HRESULT rc;
832
833 /* there must be two arguments: property name and value */
834 if (a->argc != 2)
835 return errorSyntax(USAGE_SETPROPERTY, "Incorrect number of parameters");
836
837 ComPtr<ISystemProperties> systemProperties;
838 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
839
840 if (!strcmp(a->argv[0], "machinefolder"))
841 {
842 /* reset to default? */
843 if (!strcmp(a->argv[1], "default"))
844 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
845 else
846 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
847 }
848 else if (!strcmp(a->argv[0], "hwvirtexclusive"))
849 {
850 bool fHwVirtExclusive;
851
852 if (!strcmp(a->argv[1], "on"))
853 fHwVirtExclusive = true;
854 else if (!strcmp(a->argv[1], "off"))
855 fHwVirtExclusive = false;
856 else
857 return errorArgument("Invalid hwvirtexclusive argument '%s'", a->argv[1]);
858 CHECK_ERROR(systemProperties, COMSETTER(ExclusiveHwVirt)(fHwVirtExclusive));
859 }
860 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
861 || !strcmp(a->argv[0], "vrdpauthlibrary"))
862 {
863 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
864 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n");
865
866 /* reset to default? */
867 if (!strcmp(a->argv[1], "default"))
868 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
869 else
870 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
871 }
872 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
873 {
874 /* reset to default? */
875 if (!strcmp(a->argv[1], "default"))
876 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
877 else
878 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
879 }
880 else if (!strcmp(a->argv[0], "vrdeextpack"))
881 {
882 /* disable? */
883 if (!strcmp(a->argv[1], "null"))
884 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
885 else
886 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
887 }
888 else if (!strcmp(a->argv[0], "loghistorycount"))
889 {
890 uint32_t uVal;
891 int vrc;
892 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
893 if (vrc != VINF_SUCCESS)
894 return errorArgument("Error parsing Log history count '%s'", a->argv[1]);
895 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
896 }
897 else if (!strcmp(a->argv[0], "autostartdbpath"))
898 {
899 /* disable? */
900 if (!strcmp(a->argv[1], "null"))
901 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(NULL));
902 else
903 CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(Bstr(a->argv[1]).raw()));
904 }
905 else if (!strcmp(a->argv[0], "defaultfrontend"))
906 {
907 Bstr bstrDefaultFrontend(a->argv[1]);
908 if (!strcmp(a->argv[1], "default"))
909 bstrDefaultFrontend.setNull();
910 CHECK_ERROR(systemProperties, COMSETTER(DefaultFrontend)(bstrDefaultFrontend.raw()));
911 }
912 else if (!strcmp(a->argv[0], "logginglevel"))
913 {
914 Bstr bstrLoggingLevel(a->argv[1]);
915 if (!strcmp(a->argv[1], "default"))
916 bstrLoggingLevel.setNull();
917 CHECK_ERROR(systemProperties, COMSETTER(LoggingLevel)(bstrLoggingLevel.raw()));
918 }
919 else
920 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]);
921
922 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
923}
924
925RTEXITCODE handleSharedFolder(HandlerArg *a)
926{
927 HRESULT rc;
928
929 /* we need at least a command and target */
930 if (a->argc < 2)
931 return errorSyntax(USAGE_SHAREDFOLDER, "Not enough parameters");
932
933 const char *pszMachineName = a->argv[1];
934 ComPtr<IMachine> machine;
935 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), machine.asOutParam()));
936 if (!machine)
937 return RTEXITCODE_FAILURE;
938
939 if (!strcmp(a->argv[0], "add"))
940 {
941 /* we need at least four more parameters */
942 if (a->argc < 5)
943 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Not enough parameters");
944
945 char *name = NULL;
946 char *hostpath = NULL;
947 bool fTransient = false;
948 bool fWritable = true;
949 bool fAutoMount = false;
950
951 for (int i = 2; i < a->argc; i++)
952 {
953 if ( !strcmp(a->argv[i], "--name")
954 || !strcmp(a->argv[i], "-name"))
955 {
956 if (a->argc <= i + 1 || !*a->argv[i+1])
957 return errorArgument("Missing argument to '%s'", a->argv[i]);
958 i++;
959 name = a->argv[i];
960 }
961 else if ( !strcmp(a->argv[i], "--hostpath")
962 || !strcmp(a->argv[i], "-hostpath"))
963 {
964 if (a->argc <= i + 1 || !*a->argv[i+1])
965 return errorArgument("Missing argument to '%s'", a->argv[i]);
966 i++;
967 hostpath = a->argv[i];
968 }
969 else if ( !strcmp(a->argv[i], "--readonly")
970 || !strcmp(a->argv[i], "-readonly"))
971 {
972 fWritable = false;
973 }
974 else if ( !strcmp(a->argv[i], "--transient")
975 || !strcmp(a->argv[i], "-transient"))
976 {
977 fTransient = true;
978 }
979 else if ( !strcmp(a->argv[i], "--automount")
980 || !strcmp(a->argv[i], "-automount"))
981 {
982 fAutoMount = true;
983 }
984 else
985 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
986 }
987
988 if (NULL != strstr(name, " "))
989 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "No spaces allowed in parameter '-name'!");
990
991 /* required arguments */
992 if (!name || !hostpath)
993 {
994 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Parameters --name and --hostpath are required");
995 }
996
997 if (fTransient)
998 {
999 ComPtr<IConsole> console;
1000
1001 /* open an existing session for the VM */
1002 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1003
1004 /* get the session machine */
1005 ComPtr<IMachine> sessionMachine;
1006 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1007
1008 /* get the session console */
1009 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1010 if (console.isNull())
1011 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1012 "Machine '%s' is not currently running.\n", pszMachineName);
1013
1014 CHECK_ERROR(console, CreateSharedFolder(Bstr(name).raw(),
1015 Bstr(hostpath).raw(),
1016 fWritable, fAutoMount));
1017 a->session->UnlockMachine();
1018 }
1019 else
1020 {
1021 /* open a session for the VM */
1022 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1023
1024 /* get the mutable session machine */
1025 ComPtr<IMachine> sessionMachine;
1026 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1027
1028 CHECK_ERROR(sessionMachine, CreateSharedFolder(Bstr(name).raw(),
1029 Bstr(hostpath).raw(),
1030 fWritable, fAutoMount));
1031 if (SUCCEEDED(rc))
1032 CHECK_ERROR(sessionMachine, SaveSettings());
1033
1034 a->session->UnlockMachine();
1035 }
1036 }
1037 else if (!strcmp(a->argv[0], "remove"))
1038 {
1039 /* we need at least two more parameters */
1040 if (a->argc < 3)
1041 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Not enough parameters");
1042
1043 char *name = NULL;
1044 bool fTransient = false;
1045
1046 for (int i = 2; i < a->argc; i++)
1047 {
1048 if ( !strcmp(a->argv[i], "--name")
1049 || !strcmp(a->argv[i], "-name"))
1050 {
1051 if (a->argc <= i + 1 || !*a->argv[i+1])
1052 return errorArgument("Missing argument to '%s'", a->argv[i]);
1053 i++;
1054 name = a->argv[i];
1055 }
1056 else if ( !strcmp(a->argv[i], "--transient")
1057 || !strcmp(a->argv[i], "-transient"))
1058 {
1059 fTransient = true;
1060 }
1061 else
1062 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1063 }
1064
1065 /* required arguments */
1066 if (!name)
1067 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Parameter --name is required");
1068
1069 if (fTransient)
1070 {
1071 /* open an existing session for the VM */
1072 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1073 /* get the session machine */
1074 ComPtr<IMachine> sessionMachine;
1075 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1076 /* get the session console */
1077 ComPtr<IConsole> console;
1078 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1079 if (console.isNull())
1080 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1081 "Machine '%s' is not currently running.\n", pszMachineName);
1082
1083 CHECK_ERROR(console, RemoveSharedFolder(Bstr(name).raw()));
1084
1085 a->session->UnlockMachine();
1086 }
1087 else
1088 {
1089 /* open a session for the VM */
1090 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1091
1092 /* get the mutable session machine */
1093 ComPtr<IMachine> sessionMachine;
1094 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1095
1096 CHECK_ERROR(sessionMachine, RemoveSharedFolder(Bstr(name).raw()));
1097
1098 /* commit and close the session */
1099 CHECK_ERROR(sessionMachine, SaveSettings());
1100 a->session->UnlockMachine();
1101 }
1102 }
1103 else
1104 return errorSyntax(USAGE_SHAREDFOLDER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
1105
1106 return RTEXITCODE_SUCCESS;
1107}
1108
1109RTEXITCODE handleExtPack(HandlerArg *a)
1110{
1111 if (a->argc < 1)
1112 return errorNoSubcommand();
1113
1114 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1115 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1116
1117 RTGETOPTSTATE GetState;
1118 RTGETOPTUNION ValueUnion;
1119 int ch;
1120 HRESULT hrc = S_OK;
1121
1122 if (!strcmp(a->argv[0], "install"))
1123 {
1124 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
1125 const char *pszName = NULL;
1126 bool fReplace = false;
1127
1128 static const RTGETOPTDEF s_aInstallOptions[] =
1129 {
1130 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1131 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
1132 };
1133
1134 RTCList<RTCString> lstLicenseHashes;
1135 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1136 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1137 {
1138 switch (ch)
1139 {
1140 case 'r':
1141 fReplace = true;
1142 break;
1143
1144 case 'a':
1145 lstLicenseHashes.append(ValueUnion.psz);
1146 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
1147 break;
1148
1149 case VINF_GETOPT_NOT_OPTION:
1150 if (pszName)
1151 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1152 pszName = ValueUnion.psz;
1153 break;
1154
1155 default:
1156 return errorGetOpt(ch, &ValueUnion);
1157 }
1158 }
1159 if (!pszName)
1160 return errorSyntax("No extension pack name was given to \"extpack install\"");
1161
1162 char szPath[RTPATH_MAX];
1163 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
1164 if (RT_FAILURE(vrc))
1165 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszName, vrc);
1166
1167 Bstr bstrTarball(szPath);
1168 Bstr bstrName;
1169 ComPtr<IExtPackFile> ptrExtPackFile;
1170 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1171 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1172 BOOL fShowLicense = true;
1173 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
1174 if (fShowLicense)
1175 {
1176 Bstr bstrLicense;
1177 CHECK_ERROR2I_RET(ptrExtPackFile,
1178 QueryLicense(Bstr("").raw() /* PreferredLocale */,
1179 Bstr("").raw() /* PreferredLanguage */,
1180 Bstr("txt").raw() /* Format */,
1181 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
1182 Utf8Str strLicense(bstrLicense);
1183 uint8_t abHash[RTSHA256_HASH_SIZE];
1184 char szDigest[RTSHA256_DIGEST_LEN + 1];
1185 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
1186 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
1187 AssertRCStmt(vrc, szDigest[0] = '\0');
1188 if (lstLicenseHashes.contains(szDigest))
1189 RTPrintf("License accepted.\n");
1190 else
1191 {
1192 RTPrintf("%s\n", strLicense.c_str());
1193 RTPrintf("Do you agree to these license terms and conditions (y/n)? " );
1194 ch = RTStrmGetCh(g_pStdIn);
1195 RTPrintf("\n");
1196 if (ch != 'y' && ch != 'Y')
1197 {
1198 RTPrintf("Installation of \"%ls\" aborted.\n", bstrName.raw());
1199 return RTEXITCODE_FAILURE;
1200 }
1201 if (szDigest[0])
1202 RTPrintf("License accepted. For batch installaltion add\n"
1203 "--accept-license=%s\n"
1204 "to the VBoxManage command line.\n\n", szDigest);
1205 }
1206 }
1207 ComPtr<IProgress> ptrProgress;
1208 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1209 hrc = showProgress(ptrProgress);
1210 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to install \"%s\"", szPath), RTEXITCODE_FAILURE);
1211
1212 RTPrintf("Successfully installed \"%ls\".\n", bstrName.raw());
1213 }
1214 else if (!strcmp(a->argv[0], "uninstall"))
1215 {
1216 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
1217 const char *pszName = NULL;
1218 bool fForced = false;
1219
1220 static const RTGETOPTDEF s_aUninstallOptions[] =
1221 {
1222 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1223 };
1224
1225 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1226 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1227 {
1228 switch (ch)
1229 {
1230 case 'f':
1231 fForced = true;
1232 break;
1233
1234 case VINF_GETOPT_NOT_OPTION:
1235 if (pszName)
1236 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1237 pszName = ValueUnion.psz;
1238 break;
1239
1240 default:
1241 return errorGetOpt(ch, &ValueUnion);
1242 }
1243 }
1244 if (!pszName)
1245 return errorSyntax("No extension pack name was given to \"extpack uninstall\"");
1246
1247 Bstr bstrName(pszName);
1248 ComPtr<IProgress> ptrProgress;
1249 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1250 hrc = showProgress(ptrProgress);
1251 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to uninstall \"%s\"", pszName), RTEXITCODE_FAILURE);
1252
1253 RTPrintf("Successfully uninstalled \"%s\".\n", pszName);
1254 }
1255 else if (!strcmp(a->argv[0], "cleanup"))
1256 {
1257 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
1258 if (a->argc > 1)
1259 return errorTooManyParameters(&a->argv[1]);
1260 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1261 RTPrintf("Successfully performed extension pack cleanup\n");
1262 }
1263 else
1264 return errorUnknownSubcommand(a->argv[0]);
1265
1266 return RTEXITCODE_SUCCESS;
1267}
1268
1269static const RTGETOPTDEF g_aUnattendedInstallOptions[] =
1270{
1271 { "--user", 'u', RTGETOPT_REQ_STRING },
1272 { "-user", 'u', RTGETOPT_REQ_STRING },
1273 { "--password", 'p', RTGETOPT_REQ_STRING },
1274 { "-password", 'p', RTGETOPT_REQ_STRING },
1275 { "--key", 'k', RTGETOPT_REQ_STRING },
1276 { "-key", 'k', RTGETOPT_REQ_STRING },
1277 { "--isopath", 'i', RTGETOPT_REQ_STRING },
1278 { "-isopath", 'i', RTGETOPT_REQ_STRING },
1279 { "--addisopath", 'a', RTGETOPT_REQ_STRING },
1280 { "-addisopath", 'a', RTGETOPT_REQ_STRING },
1281 { "-imageindex", 'm', RTGETOPT_REQ_UINT16 },
1282 { "--imageindex", 'm', RTGETOPT_REQ_UINT16 },
1283 { "-settingfile", 's', RTGETOPT_REQ_STRING },
1284 { "--settingfile", 's', RTGETOPT_REQ_STRING },
1285};
1286
1287RTEXITCODE handleUnattendedInstall(HandlerArg *a)
1288{
1289 HRESULT rc;
1290 Bstr vboxAdditionsIsoPath;
1291 Bstr isoPath;
1292 Bstr user;
1293 Bstr password;
1294 Bstr productKey;
1295 Bstr group("group");
1296 Bstr uuid = Guid(a->argv[0]).toUtf16().raw();
1297 Bstr machineName;
1298 BOOL bInstallGuestAdditions;
1299 Bstr fileWithSettings;
1300 unsigned short imageIndex = 1;//applied only to Windows installation
1301
1302 int c;
1303 RTGETOPTUNION ValueUnion;
1304 RTGETOPTSTATE GetState;
1305
1306 // start at 2(third) argument because 0(first) is <uuid\vmname>, 1(second) is <usedata\usefile>
1307 RTGetOptInit(&GetState, a->argc, a->argv, g_aUnattendedInstallOptions, RT_ELEMENTS(g_aUnattendedInstallOptions),
1308 2, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1309
1310 if (!strcmp(a->argv[1], "usefile"))
1311 {
1312 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1313 {
1314 switch (c)
1315 {
1316 case 's': // --a file with data prepared by user
1317 fileWithSettings = ValueUnion.psz;
1318 break;
1319 default:
1320 return errorGetOpt(USAGE_UNATTENDEDINSTALL, c, &ValueUnion);
1321 }
1322 }
1323
1324 /* check for required options */
1325 if (fileWithSettings.isEmpty())
1326 return errorSyntax(USAGE_UNATTENDEDINSTALL, "One of needed parameters has been missed");
1327 }
1328 else if(!strcmp(a->argv[1], "usedata"))
1329 {
1330 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1331 {
1332 switch (c)
1333 {
1334 case 'u': // --user
1335 user = ValueUnion.psz;
1336 break;
1337
1338 case 'p': // --password
1339 password = ValueUnion.psz;
1340 break;
1341
1342 case 'k': // --key
1343 productKey = ValueUnion.psz;
1344 break;
1345
1346 case 'a': // --addisopath
1347 vboxAdditionsIsoPath = ValueUnion.psz;
1348 break;
1349
1350 case 'i': // --isopath
1351 isoPath = ValueUnion.psz;
1352 break;
1353 case 'm': // --image index
1354 imageIndex = ValueUnion.u8;
1355 break;
1356 default:
1357 return errorGetOpt(USAGE_UNATTENDEDINSTALL, c, &ValueUnion);
1358 }
1359 }
1360
1361 /* check for required options */
1362 if (user.isEmpty() || password.isEmpty() || isoPath.isEmpty())
1363 return errorSyntax(USAGE_UNATTENDEDINSTALL, "One of needed parameters has been missed");
1364 }
1365 else
1366 {
1367 return errorSyntax(USAGE_UNATTENDEDINSTALL, "Unknown subcommand. Must be \"usefile\" or \"usedata\" ");
1368 }
1369
1370 /* try to find the given machine */
1371 ComPtr<IMachine> machine;
1372 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(), machine.asOutParam()));
1373 if (FAILED(rc))
1374 return RTEXITCODE_FAILURE;
1375
1376 CHECK_ERROR_RET(machine, COMGETTER(Name)(machineName.asOutParam()), RTEXITCODE_FAILURE);
1377 /* open a session for the VM */
1378 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1379
1380 ComPtr<IConsole> console;
1381 ComPtr<IMachine> sessionMachine;
1382 ComPtr<IUnattended> unAttended;
1383
1384 /* get the associated console */
1385 CHECK_ERROR(a->session, COMGETTER(Console)(console.asOutParam()));
1386 if (console)
1387 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%s' is currently running", Utf8Str(uuid).c_str());
1388
1389 /* ... and session machine */
1390 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1391
1392 BSTR installedOSBSTR;
1393
1394 CHECK_ERROR_RET(sessionMachine, COMGETTER(OSTypeId)(&installedOSBSTR), RTEXITCODE_FAILURE);
1395
1396 Utf8Str installedOSStr(installedOSBSTR);
1397
1398 do {
1399 RTPrintf("Start unattended installation OS %s on virtual machine '%ls'.\n"
1400 "UUID: %s\n",
1401 installedOSStr.c_str(),
1402 machineName.raw(),
1403 Utf8Str(uuid).c_str());
1404
1405 CHECK_ERROR_BREAK(machine, COMGETTER(Unattended)(unAttended.asOutParam()));
1406
1407 if (fileWithSettings.isEmpty())//may use also this condition (!strcmp(a->argv[1], "usedata"))
1408 {
1409 CHECK_ERROR_BREAK(unAttended, COMSETTER(Group)(group.raw()));
1410
1411 if (installedOSStr.contains("Windows") && productKey.isEmpty())
1412 {
1413 return errorSyntax(USAGE_UNATTENDEDINSTALL, "Product key has been missed.");
1414 }
1415
1416 CHECK_ERROR_BREAK(unAttended, COMSETTER(VboxAdditionsIsoPath)(vboxAdditionsIsoPath.raw()));
1417
1418 CHECK_ERROR_BREAK(unAttended, COMSETTER(IsoPath)(isoPath.raw()));
1419
1420 CHECK_ERROR_BREAK(unAttended, COMSETTER(User)(user.raw()));
1421
1422 CHECK_ERROR_BREAK(unAttended, COMSETTER(Password)(password.raw()));
1423
1424 CHECK_ERROR_BREAK(unAttended, COMSETTER(ProductKey)(productKey.raw()));
1425
1426 bool fInstallGuestAdditions = (vboxAdditionsIsoPath.isEmpty()) ? false: true;
1427 CHECK_ERROR_BREAK(unAttended, COMSETTER(InstallGuestAdditions)(fInstallGuestAdditions));
1428
1429 CHECK_ERROR_BREAK(unAttended, COMSETTER(ImageIndex)(imageIndex));
1430 CHECK_ERROR_BREAK(unAttended,Preparation());
1431 CHECK_ERROR_BREAK(unAttended,ConstructScript());
1432 CHECK_ERROR_BREAK(unAttended,ConstructMedia());
1433 CHECK_ERROR_BREAK(unAttended,ReconstructVM());
1434 CHECK_ERROR_BREAK(unAttended,RunInstallation());
1435
1436 }
1437 else
1438 {
1439 CHECK_ERROR_BREAK(unAttended, COMSETTER(FileWithPreparedData)(fileWithSettings.raw()));
1440 CHECK_ERROR_BREAK(unAttended,Preparation());
1441 CHECK_ERROR_BREAK(unAttended,ReconstructVM());
1442 CHECK_ERROR_BREAK(unAttended,RunInstallation());
1443 }
1444
1445 a->session->UnlockMachine();
1446
1447 {
1448 Bstr env;
1449 Bstr sessionType = "headless";//"gui";
1450
1451#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1452 /* make sure the VM process will start on the same display as VBoxManage */
1453 Utf8Str str;
1454 const char *pszDisplay = RTEnvGet("DISPLAY");
1455 if (pszDisplay)
1456 str = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
1457 const char *pszXAuth = RTEnvGet("XAUTHORITY");
1458 if (pszXAuth)
1459 str.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
1460 env = str;
1461#endif
1462 ComPtr<IProgress> progress;
1463 CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
1464 env.raw(), progress.asOutParam()));
1465 if (SUCCEEDED(rc) && !progress.isNull())
1466 {
1467 RTPrintf("Waiting for VM \"%s\" to power on...\n", Utf8Str(uuid).c_str());
1468 CHECK_ERROR(progress, WaitForCompletion(-1));
1469 if (SUCCEEDED(rc))
1470 {
1471 BOOL completed = true;
1472 CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
1473 if (SUCCEEDED(rc))
1474 {
1475 ASSERT(completed);
1476
1477 LONG iRc;
1478 CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
1479 if (SUCCEEDED(rc))
1480 {
1481 if (SUCCEEDED(iRc))
1482 RTPrintf("VM \"%s\" has been successfully started.\n", Utf8Str(uuid).c_str());
1483 else
1484 {
1485 ProgressErrorInfo info(progress);
1486 com::GluePrintErrorInfo(info);
1487 }
1488 rc = iRc;
1489 }
1490 }
1491 }
1492 }
1493 }
1494
1495 vboxAdditionsIsoPath.setNull();
1496 isoPath.setNull();
1497 user.setNull();
1498 password.setNull();
1499 productKey.setNull();
1500 group.setNull();
1501 bInstallGuestAdditions = false;
1502 imageIndex = 0;
1503 fileWithSettings.setNull();
1504
1505 CHECK_ERROR_BREAK(unAttended, COMGETTER(FileWithPreparedData)(fileWithSettings.asOutParam()));
1506 CHECK_ERROR_BREAK(unAttended, COMGETTER(Group)(group.asOutParam()));
1507 CHECK_ERROR_BREAK(unAttended, COMGETTER(VboxAdditionsIsoPath)(vboxAdditionsIsoPath.asOutParam()));
1508 CHECK_ERROR_BREAK(unAttended, COMGETTER(IsoPath)(isoPath.asOutParam()));
1509 CHECK_ERROR_BREAK(unAttended, COMGETTER(User)(user.asOutParam()));
1510 CHECK_ERROR_BREAK(unAttended, COMGETTER(Password)(password.asOutParam()));
1511 CHECK_ERROR_BREAK(unAttended, COMGETTER(ProductKey)(productKey.asOutParam()));
1512 CHECK_ERROR_BREAK(unAttended, COMGETTER(ImageIndex)(&imageIndex));
1513 RTPrintf("Got values:\n fileWithSettings: %s\n user: %s\n password: %s\n productKey: %s\n image index: %d\n "
1514 "isoPath: %s\n vboxAdditionsIsoPath: %s\n",
1515 Utf8Str(fileWithSettings).c_str(),
1516 Utf8Str(user).c_str(),
1517 Utf8Str(password).c_str(),
1518 Utf8Str(productKey).c_str(),
1519 imageIndex,
1520 Utf8Str(isoPath).c_str(),
1521 Utf8Str(vboxAdditionsIsoPath).c_str()
1522 );
1523 }
1524 while (0);
1525
1526 RTPrintf("Unattended installation OS %s has been running on virtual machine '%ls'.\n"
1527 "UUID: %s\n",
1528 installedOSStr.c_str(),
1529 machineName.raw(),
1530 Utf8Str(uuid).c_str());
1531
1532 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1533}
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