VirtualBox

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

Last change on this file since 75500 was 75381, checked in by vboxsync, 6 years ago

Main,VBoxManage,FE/Qt: Extended the createSharedFolder and ISharedFolder methods with a mount poit parameter/attribute for use when auto-mounting. This is especially useful for Windows and OS/2 guests which operates with drive letters. The change has not yet trickled down to the guest interface and VBoxService. [build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 73.6 KB
Line 
1/* $Id: VBoxManageMisc.cpp 75381 2018-11-09 22:30:15Z 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 if (!strcmp(a->argv[0], "proxymode"))
1050 {
1051 ProxyMode_T enmProxyMode;
1052 if (!RTStrICmpAscii(a->argv[1], "system"))
1053 enmProxyMode = ProxyMode_System;
1054 else if (!RTStrICmpAscii(a->argv[1], "noproxy"))
1055 enmProxyMode = ProxyMode_NoProxy;
1056 else if (!RTStrICmpAscii(a->argv[1], "manual"))
1057 enmProxyMode = ProxyMode_Manual;
1058 else
1059 return errorArgument("Unknown proxy mode: '%s'", a->argv[1]);
1060 CHECK_ERROR(systemProperties, COMSETTER(ProxyMode)(enmProxyMode));
1061 }
1062 else if (!strcmp(a->argv[0], "proxyurl"))
1063 {
1064 Bstr bstrProxyUrl(a->argv[1]);
1065 CHECK_ERROR(systemProperties, COMSETTER(ProxyURL)(bstrProxyUrl.raw()));
1066 }
1067 else
1068 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]);
1069
1070 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1071}
1072
1073RTEXITCODE handleSharedFolder(HandlerArg *a)
1074{
1075 HRESULT rc;
1076
1077 /* we need at least a command and target */
1078 if (a->argc < 2)
1079 return errorSyntax(USAGE_SHAREDFOLDER, "Not enough parameters");
1080
1081 const char *pszMachineName = a->argv[1];
1082 ComPtr<IMachine> machine;
1083 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), machine.asOutParam()));
1084 if (!machine)
1085 return RTEXITCODE_FAILURE;
1086
1087 if (!strcmp(a->argv[0], "add"))
1088 {
1089 /* we need at least four more parameters */
1090 if (a->argc < 5)
1091 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Not enough parameters");
1092
1093 char *name = NULL;
1094 char *hostpath = NULL;
1095 bool fTransient = false;
1096 bool fWritable = true;
1097 bool fAutoMount = false;
1098 const char *pszAutoMountPoint = "";
1099
1100 for (int i = 2; i < a->argc; i++)
1101 {
1102 if ( !strcmp(a->argv[i], "--name")
1103 || !strcmp(a->argv[i], "-name"))
1104 {
1105 if (a->argc <= i + 1 || !*a->argv[i+1])
1106 return errorArgument("Missing argument to '%s'", a->argv[i]);
1107 i++;
1108 name = a->argv[i];
1109 }
1110 else if ( !strcmp(a->argv[i], "--hostpath")
1111 || !strcmp(a->argv[i], "-hostpath"))
1112 {
1113 if (a->argc <= i + 1 || !*a->argv[i+1])
1114 return errorArgument("Missing argument to '%s'", a->argv[i]);
1115 i++;
1116 hostpath = a->argv[i];
1117 }
1118 else if ( !strcmp(a->argv[i], "--readonly")
1119 || !strcmp(a->argv[i], "-readonly"))
1120 {
1121 fWritable = false;
1122 }
1123 else if ( !strcmp(a->argv[i], "--transient")
1124 || !strcmp(a->argv[i], "-transient"))
1125 {
1126 fTransient = true;
1127 }
1128 else if ( !strcmp(a->argv[i], "--automount")
1129 || !strcmp(a->argv[i], "-automount"))
1130 {
1131 fAutoMount = true;
1132 }
1133 else if (!strcmp(a->argv[i], "--auto-mount-point"))
1134 {
1135 if (a->argc <= i + 1 || !*a->argv[i+1])
1136 return errorArgument("Missing argument to '%s'", a->argv[i]);
1137 i++;
1138 pszAutoMountPoint = a->argv[i];
1139 }
1140 else
1141 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1142 }
1143
1144 if (NULL != strstr(name, " "))
1145 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "No spaces allowed in parameter '-name'!");
1146
1147 /* required arguments */
1148 if (!name || !hostpath)
1149 {
1150 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Parameters --name and --hostpath are required");
1151 }
1152
1153 if (fTransient)
1154 {
1155 ComPtr<IConsole> console;
1156
1157 /* open an existing session for the VM */
1158 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1159
1160 /* get the session machine */
1161 ComPtr<IMachine> sessionMachine;
1162 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1163
1164 /* get the session console */
1165 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1166 if (console.isNull())
1167 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1168 "Machine '%s' is not currently running.\n", pszMachineName);
1169
1170 CHECK_ERROR(console, CreateSharedFolder(Bstr(name).raw(), Bstr(hostpath).raw(),
1171 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1172 a->session->UnlockMachine();
1173 }
1174 else
1175 {
1176 /* open a session for the VM */
1177 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1178
1179 /* get the mutable session machine */
1180 ComPtr<IMachine> sessionMachine;
1181 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1182
1183 CHECK_ERROR(sessionMachine, CreateSharedFolder(Bstr(name).raw(), Bstr(hostpath).raw(),
1184 fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
1185 if (SUCCEEDED(rc))
1186 CHECK_ERROR(sessionMachine, SaveSettings());
1187
1188 a->session->UnlockMachine();
1189 }
1190 }
1191 else if (!strcmp(a->argv[0], "remove"))
1192 {
1193 /* we need at least two more parameters */
1194 if (a->argc < 3)
1195 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Not enough parameters");
1196
1197 char *name = NULL;
1198 bool fTransient = false;
1199
1200 for (int i = 2; i < a->argc; i++)
1201 {
1202 if ( !strcmp(a->argv[i], "--name")
1203 || !strcmp(a->argv[i], "-name"))
1204 {
1205 if (a->argc <= i + 1 || !*a->argv[i+1])
1206 return errorArgument("Missing argument to '%s'", a->argv[i]);
1207 i++;
1208 name = a->argv[i];
1209 }
1210 else if ( !strcmp(a->argv[i], "--transient")
1211 || !strcmp(a->argv[i], "-transient"))
1212 {
1213 fTransient = true;
1214 }
1215 else
1216 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
1217 }
1218
1219 /* required arguments */
1220 if (!name)
1221 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Parameter --name is required");
1222
1223 if (fTransient)
1224 {
1225 /* open an existing session for the VM */
1226 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1227 /* get the session machine */
1228 ComPtr<IMachine> sessionMachine;
1229 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
1230 /* get the session console */
1231 ComPtr<IConsole> console;
1232 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
1233 if (console.isNull())
1234 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1235 "Machine '%s' is not currently running.\n", pszMachineName);
1236
1237 CHECK_ERROR(console, RemoveSharedFolder(Bstr(name).raw()));
1238
1239 a->session->UnlockMachine();
1240 }
1241 else
1242 {
1243 /* open a session for the VM */
1244 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
1245
1246 /* get the mutable session machine */
1247 ComPtr<IMachine> sessionMachine;
1248 a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
1249
1250 CHECK_ERROR(sessionMachine, RemoveSharedFolder(Bstr(name).raw()));
1251
1252 /* commit and close the session */
1253 CHECK_ERROR(sessionMachine, SaveSettings());
1254 a->session->UnlockMachine();
1255 }
1256 }
1257 else
1258 return errorSyntax(USAGE_SHAREDFOLDER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
1259
1260 return RTEXITCODE_SUCCESS;
1261}
1262
1263RTEXITCODE handleExtPack(HandlerArg *a)
1264{
1265 if (a->argc < 1)
1266 return errorNoSubcommand();
1267
1268 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1269 CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1270
1271 RTGETOPTSTATE GetState;
1272 RTGETOPTUNION ValueUnion;
1273 int ch;
1274 HRESULT hrc = S_OK;
1275
1276 if (!strcmp(a->argv[0], "install"))
1277 {
1278 setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
1279 const char *pszName = NULL;
1280 bool fReplace = false;
1281
1282 static const RTGETOPTDEF s_aInstallOptions[] =
1283 {
1284 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1285 { "--accept-license", 'a', RTGETOPT_REQ_STRING },
1286 };
1287
1288 RTCList<RTCString> lstLicenseHashes;
1289 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1290 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1291 {
1292 switch (ch)
1293 {
1294 case 'r':
1295 fReplace = true;
1296 break;
1297
1298 case 'a':
1299 lstLicenseHashes.append(ValueUnion.psz);
1300 lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
1301 break;
1302
1303 case VINF_GETOPT_NOT_OPTION:
1304 if (pszName)
1305 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1306 pszName = ValueUnion.psz;
1307 break;
1308
1309 default:
1310 return errorGetOpt(ch, &ValueUnion);
1311 }
1312 }
1313 if (!pszName)
1314 return errorSyntax("No extension pack name was given to \"extpack install\"");
1315
1316 char szPath[RTPATH_MAX];
1317 int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
1318 if (RT_FAILURE(vrc))
1319 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszName, vrc);
1320
1321 Bstr bstrTarball(szPath);
1322 Bstr bstrName;
1323 ComPtr<IExtPackFile> ptrExtPackFile;
1324 CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1325 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1326 BOOL fShowLicense = true;
1327 CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
1328 if (fShowLicense)
1329 {
1330 Bstr bstrLicense;
1331 CHECK_ERROR2I_RET(ptrExtPackFile,
1332 QueryLicense(Bstr("").raw() /* PreferredLocale */,
1333 Bstr("").raw() /* PreferredLanguage */,
1334 Bstr("txt").raw() /* Format */,
1335 bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
1336 Utf8Str strLicense(bstrLicense);
1337 uint8_t abHash[RTSHA256_HASH_SIZE];
1338 char szDigest[RTSHA256_DIGEST_LEN + 1];
1339 RTSha256(strLicense.c_str(), strLicense.length(), abHash);
1340 vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
1341 AssertRCStmt(vrc, szDigest[0] = '\0');
1342 if (lstLicenseHashes.contains(szDigest))
1343 RTPrintf("License accepted.\n");
1344 else
1345 {
1346 RTPrintf("%s\n", strLicense.c_str());
1347 RTPrintf("Do you agree to these license terms and conditions (y/n)? " );
1348 ch = RTStrmGetCh(g_pStdIn);
1349 RTPrintf("\n");
1350 if (ch != 'y' && ch != 'Y')
1351 {
1352 RTPrintf("Installation of \"%ls\" aborted.\n", bstrName.raw());
1353 return RTEXITCODE_FAILURE;
1354 }
1355 if (szDigest[0])
1356 RTPrintf("License accepted. For batch installaltion add\n"
1357 "--accept-license=%s\n"
1358 "to the VBoxManage command line.\n\n", szDigest);
1359 }
1360 }
1361 ComPtr<IProgress> ptrProgress;
1362 CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1363 hrc = showProgress(ptrProgress);
1364 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to install \"%s\"", szPath), RTEXITCODE_FAILURE);
1365
1366 RTPrintf("Successfully installed \"%ls\".\n", bstrName.raw());
1367 }
1368 else if (!strcmp(a->argv[0], "uninstall"))
1369 {
1370 setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
1371 const char *pszName = NULL;
1372 bool fForced = false;
1373
1374 static const RTGETOPTDEF s_aUninstallOptions[] =
1375 {
1376 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1377 };
1378
1379 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1380 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1381 {
1382 switch (ch)
1383 {
1384 case 'f':
1385 fForced = true;
1386 break;
1387
1388 case VINF_GETOPT_NOT_OPTION:
1389 if (pszName)
1390 return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
1391 pszName = ValueUnion.psz;
1392 break;
1393
1394 default:
1395 return errorGetOpt(ch, &ValueUnion);
1396 }
1397 }
1398 if (!pszName)
1399 return errorSyntax("No extension pack name was given to \"extpack uninstall\"");
1400
1401 Bstr bstrName(pszName);
1402 ComPtr<IProgress> ptrProgress;
1403 CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1404 hrc = showProgress(ptrProgress);
1405 CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to uninstall \"%s\"", pszName), RTEXITCODE_FAILURE);
1406
1407 RTPrintf("Successfully uninstalled \"%s\".\n", pszName);
1408 }
1409 else if (!strcmp(a->argv[0], "cleanup"))
1410 {
1411 setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
1412 if (a->argc > 1)
1413 return errorTooManyParameters(&a->argv[1]);
1414 CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1415 RTPrintf("Successfully performed extension pack cleanup\n");
1416 }
1417 else
1418 return errorUnknownSubcommand(a->argv[0]);
1419
1420 return RTEXITCODE_SUCCESS;
1421}
1422
1423RTEXITCODE handleUnattendedDetect(HandlerArg *a)
1424{
1425 HRESULT hrc;
1426
1427 /*
1428 * Options. We work directly on an IUnattended instace while parsing
1429 * the options. This saves a lot of extra clutter.
1430 */
1431 bool fMachineReadable = false;
1432 char szIsoPath[RTPATH_MAX];
1433 szIsoPath[0] = '\0';
1434
1435 /*
1436 * Parse options.
1437 */
1438 static const RTGETOPTDEF s_aOptions[] =
1439 {
1440 { "--iso", 'i', RTGETOPT_REQ_STRING },
1441 { "--machine-readable", 'M', RTGETOPT_REQ_NOTHING },
1442 };
1443
1444 RTGETOPTSTATE GetState;
1445 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1446 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1447
1448 int c;
1449 RTGETOPTUNION ValueUnion;
1450 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1451 {
1452 switch (c)
1453 {
1454 case 'i': // --iso
1455 vrc = RTPathAbs(ValueUnion.psz, szIsoPath, sizeof(szIsoPath));
1456 if (RT_FAILURE(vrc))
1457 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1458 break;
1459
1460 case 'M': // --machine-readable.
1461 fMachineReadable = true;
1462 break;
1463
1464 default:
1465 return errorGetOpt(c, &ValueUnion);
1466 }
1467 }
1468
1469 /*
1470 * Check for required stuff.
1471 */
1472 if (szIsoPath[0] == '\0')
1473 return errorSyntax("No ISO specified");
1474
1475 /*
1476 * Do the job.
1477 */
1478 ComPtr<IUnattended> ptrUnattended;
1479 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1480 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szIsoPath).raw()), RTEXITCODE_FAILURE);
1481 CHECK_ERROR2(hrc, ptrUnattended, DetectIsoOS());
1482 RTEXITCODE rcExit = SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1483
1484 /*
1485 * Retrieve the results.
1486 */
1487 Bstr bstrDetectedOSTypeId;
1488 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSTypeId)(bstrDetectedOSTypeId.asOutParam()), RTEXITCODE_FAILURE);
1489 Bstr bstrDetectedVersion;
1490 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSVersion)(bstrDetectedVersion.asOutParam()), RTEXITCODE_FAILURE);
1491 Bstr bstrDetectedFlavor;
1492 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSFlavor)(bstrDetectedFlavor.asOutParam()), RTEXITCODE_FAILURE);
1493 Bstr bstrDetectedLanguages;
1494 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSLanguages)(bstrDetectedLanguages.asOutParam()), RTEXITCODE_FAILURE);
1495 Bstr bstrDetectedHints;
1496 CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSHints)(bstrDetectedHints.asOutParam()), RTEXITCODE_FAILURE);
1497 if (fMachineReadable)
1498 RTPrintf("OSTypeId=\"%ls\"\n"
1499 "OSVersion=\"%ls\"\n"
1500 "OSFlavor=\"%ls\"\n"
1501 "OSLanguages=\"%ls\"\n"
1502 "OSHints=\"%ls\"\n",
1503 bstrDetectedOSTypeId.raw(),
1504 bstrDetectedVersion.raw(),
1505 bstrDetectedFlavor.raw(),
1506 bstrDetectedLanguages.raw(),
1507 bstrDetectedHints.raw());
1508 else
1509 {
1510 RTMsgInfo("Detected '%s' to be:\n", szIsoPath);
1511 RTPrintf(" OS TypeId = %ls\n"
1512 " OS Version = %ls\n"
1513 " OS Flavor = %ls\n"
1514 " OS Languages = %ls\n"
1515 " OS Hints = %ls\n",
1516 bstrDetectedOSTypeId.raw(),
1517 bstrDetectedVersion.raw(),
1518 bstrDetectedFlavor.raw(),
1519 bstrDetectedLanguages.raw(),
1520 bstrDetectedHints.raw());
1521 }
1522
1523 return rcExit;
1524}
1525
1526RTEXITCODE handleUnattendedInstall(HandlerArg *a)
1527{
1528 HRESULT hrc;
1529 char szAbsPath[RTPATH_MAX];
1530
1531 /*
1532 * Options. We work directly on an IUnattended instace while parsing
1533 * the options. This saves a lot of extra clutter.
1534 */
1535 ComPtr<IUnattended> ptrUnattended;
1536 CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
1537 RTCList<RTCString> arrPackageSelectionAdjustments;
1538 ComPtr<IMachine> ptrMachine;
1539 bool fDryRun = false;
1540 const char *pszSessionType = "none";
1541
1542 /*
1543 * Parse options.
1544 */
1545 static const RTGETOPTDEF s_aOptions[] =
1546 {
1547 { "--iso", 'i', RTGETOPT_REQ_STRING },
1548 { "--user", 'u', RTGETOPT_REQ_STRING },
1549 { "--password", 'p', RTGETOPT_REQ_STRING },
1550 { "--password-file", 'X', RTGETOPT_REQ_STRING },
1551 { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
1552 { "--key", 'k', RTGETOPT_REQ_STRING },
1553 { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
1554 { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
1555 { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
1556 { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
1557 { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
1558 { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
1559 { "--locale", 'l', RTGETOPT_REQ_STRING },
1560 { "--country", 'Y', RTGETOPT_REQ_STRING },
1561 { "--time-zone", 'z', RTGETOPT_REQ_STRING },
1562 { "--proxy", 'y', RTGETOPT_REQ_STRING },
1563 { "--hostname", 'H', RTGETOPT_REQ_STRING },
1564 { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
1565 { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
1566 // advance options:
1567 { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
1568 { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
1569 { "--script-template", 'c', RTGETOPT_REQ_STRING },
1570 { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
1571 { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
1572 { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
1573 { "--language", 'L', RTGETOPT_REQ_STRING },
1574 // start vm related options:
1575 { "--start-vm", 'S', RTGETOPT_REQ_STRING },
1576 /** @todo Add a --wait option too for waiting for the VM to shut down or
1577 * something like that...? */
1578 };
1579
1580 RTGETOPTSTATE GetState;
1581 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1582 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
1583
1584 int c;
1585 RTGETOPTUNION ValueUnion;
1586 while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
1587 {
1588 switch (c)
1589 {
1590 case VINF_GETOPT_NOT_OPTION:
1591 if (ptrMachine.isNotNull())
1592 return errorSyntax("VM name/UUID given more than once!");
1593 CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1594 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
1595 break;
1596
1597 case 'i': // --iso
1598 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1599 if (RT_FAILURE(vrc))
1600 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1601 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1602 break;
1603
1604 case 'u': // --user
1605 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1606 break;
1607
1608 case 'p': // --password
1609 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1610 break;
1611
1612 case 'X': // --password-file
1613 {
1614 Utf8Str strPassword;
1615 RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
1616 if (rcExit != RTEXITCODE_SUCCESS)
1617 return rcExit;
1618 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
1619 break;
1620 }
1621
1622 case 'U': // --full-user-name
1623 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1624 break;
1625
1626 case 'k': // --key
1627 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1628 break;
1629
1630 case 'A': // --install-additions
1631 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
1632 break;
1633 case 'N': // --no-install-additions
1634 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
1635 break;
1636 case 'a': // --additions-iso
1637 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1638 if (RT_FAILURE(vrc))
1639 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1640 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1641 break;
1642
1643 case 't': // --install-txs
1644 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
1645 break;
1646 case 'T': // --no-install-txs
1647 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
1648 break;
1649 case 'K': // --valiation-kit-iso
1650 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1651 if (RT_FAILURE(vrc))
1652 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1653 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1654 break;
1655
1656 case 'l': // --locale
1657 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1658 break;
1659
1660 case 'Y': // --country
1661 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1662 break;
1663
1664 case 'z': // --time-zone;
1665 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1666 break;
1667
1668 case 'y': // --proxy
1669 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1670 break;
1671
1672 case 'H': // --hostname
1673 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1674 break;
1675
1676 case 's': // --package-selection-adjustment
1677 arrPackageSelectionAdjustments.append(ValueUnion.psz);
1678 break;
1679
1680 case 'D':
1681 fDryRun = true;
1682 break;
1683
1684 case 'x': // --auxiliary-base-path
1685 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1686 if (RT_FAILURE(vrc))
1687 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1688 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1689 break;
1690
1691 case 'm': // --image-index
1692 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
1693 break;
1694
1695 case 'c': // --script-template
1696 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1697 if (RT_FAILURE(vrc))
1698 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1699 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1700 break;
1701
1702 case 'C': // --post-install-script-template
1703 vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
1704 if (RT_FAILURE(vrc))
1705 return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
1706 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
1707 break;
1708
1709 case 'P': // --post-install-command.
1710 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1711 break;
1712
1713 case 'I': // --extra-install-kernel-parameters
1714 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1715 break;
1716
1717 case 'L': // --language
1718 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
1719 break;
1720
1721 case 'S': // --start-vm
1722 pszSessionType = ValueUnion.psz;
1723 break;
1724
1725 default:
1726 return errorGetOpt(c, &ValueUnion);
1727 }
1728 }
1729
1730 /*
1731 * Check for required stuff.
1732 */
1733 if (ptrMachine.isNull())
1734 return errorSyntax("Missing VM name/UUID");
1735
1736 /*
1737 * Set accumulative attributes.
1738 */
1739 if (arrPackageSelectionAdjustments.size() == 1)
1740 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
1741 RTEXITCODE_FAILURE);
1742 else if (arrPackageSelectionAdjustments.size() > 1)
1743 {
1744 RTCString strAdjustments;
1745 strAdjustments.join(arrPackageSelectionAdjustments, ";");
1746 CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
1747 }
1748
1749 /*
1750 * Get details about the machine so we can display them below.
1751 */
1752 Bstr bstrMachineName;
1753 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
1754 Bstr bstrUuid;
1755 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
1756 BSTR bstrInstalledOS;
1757 CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
1758 Utf8Str strInstalledOS(bstrInstalledOS);
1759
1760 /*
1761 * Temporarily lock the machine to check whether it's running or not.
1762 * We take this opportunity to disable the first run wizard.
1763 */
1764 CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
1765 {
1766 ComPtr<IConsole> ptrConsole;
1767 CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
1768
1769 if ( ptrConsole.isNull()
1770 && SUCCEEDED(hrc)
1771 && ( RTStrICmp(pszSessionType, "gui") == 0
1772 || RTStrICmp(pszSessionType, "none") == 0))
1773 {
1774 ComPtr<IMachine> ptrSessonMachine;
1775 CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
1776 if (ptrSessonMachine.isNotNull())
1777 {
1778 CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
1779 }
1780 }
1781
1782 a->session->UnlockMachine();
1783 if (FAILED(hrc))
1784 return RTEXITCODE_FAILURE;
1785 if (ptrConsole.isNotNull())
1786 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%ls' is currently running", bstrMachineName.raw());
1787 }
1788
1789 /*
1790 * Do the work.
1791 */
1792 RTMsgInfo("%s unattended installation of %s in machine '%ls' (%ls).\n",
1793 RTStrICmp(pszSessionType, "none") == 0 ? "Preparing" : "Starting",
1794 strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
1795
1796 CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
1797 if (!fDryRun)
1798 {
1799 CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
1800 CHECK_ERROR2_RET(hrc, ptrUnattended,ReconfigureVM(), RTEXITCODE_FAILURE);
1801 }
1802
1803 /*
1804 * Retrieve and display the parameters actually used.
1805 */
1806 RTMsgInfo("Using values:\n");
1807#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
1808 a_Type Value; \
1809 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
1810 if (SUCCEEDED(hrc2)) \
1811 RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
1812 else \
1813 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1814 } while (0)
1815#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
1816 Bstr bstrString; \
1817 HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
1818 if (SUCCEEDED(hrc2)) \
1819 RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
1820 else \
1821 RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
1822 } while (0)
1823
1824 SHOW_STR_ATTR(IsoPath, "isoPath");
1825 SHOW_STR_ATTR(User, "user");
1826 SHOW_STR_ATTR(Password, "password");
1827 SHOW_STR_ATTR(FullUserName, "fullUserName");
1828 SHOW_STR_ATTR(ProductKey, "productKey");
1829 SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
1830 SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
1831 SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
1832 SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
1833 SHOW_STR_ATTR(Locale, "locale");
1834 SHOW_STR_ATTR(Country, "country");
1835 SHOW_STR_ATTR(TimeZone, "timeZone");
1836 SHOW_STR_ATTR(Proxy, "proxy");
1837 SHOW_STR_ATTR(Hostname, "hostname");
1838 SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
1839 SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
1840 SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
1841 SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
1842 SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
1843 SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
1844 SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
1845 SHOW_STR_ATTR(Language, "language");
1846 SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
1847 SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
1848 SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
1849 SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
1850 SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
1851
1852#undef SHOW_STR_ATTR
1853#undef SHOW_ATTR
1854
1855 /* We can drop the IUnatteded object now. */
1856 ptrUnattended.setNull();
1857
1858 /*
1859 * Start the VM if requested.
1860 */
1861 if ( fDryRun
1862 || RTStrICmp(pszSessionType, "none") == 0)
1863 {
1864 if (!fDryRun)
1865 RTMsgInfo("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n", bstrMachineName.raw(), bstrUuid.raw());
1866 hrc = S_OK;
1867 }
1868 else
1869 {
1870 Bstr env;
1871#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
1872 /* make sure the VM process will start on the same display as VBoxManage */
1873 Utf8Str str;
1874 const char *pszDisplay = RTEnvGet("DISPLAY");
1875 if (pszDisplay)
1876 str = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
1877 const char *pszXAuth = RTEnvGet("XAUTHORITY");
1878 if (pszXAuth)
1879 str.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
1880 env = str;
1881#endif
1882 ComPtr<IProgress> ptrProgress;
1883 CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), env.raw(), ptrProgress.asOutParam()));
1884 if (SUCCEEDED(hrc) && !ptrProgress.isNull())
1885 {
1886 RTMsgInfo("Waiting for VM '%ls' to power on...\n", bstrMachineName.raw());
1887 CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
1888 if (SUCCEEDED(hrc))
1889 {
1890 BOOL fCompleted = true;
1891 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
1892 if (SUCCEEDED(hrc))
1893 {
1894 ASSERT(fCompleted);
1895
1896 LONG iRc;
1897 CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
1898 if (SUCCEEDED(hrc))
1899 {
1900 if (SUCCEEDED(iRc))
1901 RTMsgInfo("VM '%ls' (%ls) has been successfully started.\n", bstrMachineName.raw(), bstrUuid.raw());
1902 else
1903 {
1904 ProgressErrorInfo info(ptrProgress);
1905 com::GluePrintErrorInfo(info);
1906 }
1907 hrc = iRc;
1908 }
1909 }
1910 }
1911 }
1912
1913 /*
1914 * Do we wait for the VM to power down?
1915 */
1916 }
1917
1918 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1919}
1920
1921
1922RTEXITCODE handleUnattended(HandlerArg *a)
1923{
1924 /*
1925 * Sub-command switch.
1926 */
1927 if (a->argc < 1)
1928 return errorNoSubcommand();
1929
1930 if (!strcmp(a->argv[0], "detect"))
1931 {
1932 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
1933 return handleUnattendedDetect(a);
1934 }
1935
1936 if (!strcmp(a->argv[0], "install"))
1937 {
1938 setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
1939 return handleUnattendedInstall(a);
1940 }
1941
1942 /* Consider some kind of create-vm-and-install-guest-os command. */
1943 return errorUnknownSubcommand(a->argv[0]);
1944}
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