VirtualBox

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

Last change on this file since 74942 was 74431, checked in by vboxsync, 6 years ago

Main,GUI,VBoxManage: Provide API for proxy settings that picks up the current GUI settings and converts it to SystemProperties XML attributes. Adjusted GUI to use it. Updated VBoxMangage system properties handling accordingly. bugref:9249

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

© 2023 Oracle
ContactPrivacy policyTerms of Use