VirtualBox

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

Last change on this file since 37531 was 37531, checked in by vboxsync, 14 years ago

Main;FE;CloneVM: add clone options; regenerate MACs of all network adapters dependent of the user option; docs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.4 KB
Line 
1/* $Id: VBoxManageMisc.cpp 37531 2011-06-17 12:12:29Z vboxsync $ */
2/** @file
3 * VBoxManage - VirtualBox's command-line interface.
4 */
5
6/*
7 * Copyright (C) 2006-2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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/EventQueue.h>
30
31# include <VBox/com/VirtualBox.h>
32#endif /* !VBOX_ONLY_DOCS */
33
34#include <iprt/asm.h>
35#include <iprt/buildconfig.h>
36#include <iprt/cidr.h>
37#include <iprt/ctype.h>
38#include <iprt/dir.h>
39#include <iprt/env.h>
40#include <VBox/err.h>
41#include <iprt/file.h>
42#include <iprt/initterm.h>
43#include <iprt/param.h>
44#include <iprt/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
57using namespace com;
58
59
60
61int handleRegisterVM(HandlerArg *a)
62{
63 HRESULT rc;
64
65 if (a->argc != 1)
66 return errorSyntax(USAGE_REGISTERVM, "Incorrect number of parameters");
67
68 ComPtr<IMachine> machine;
69 /** @todo Ugly hack to get both the API interpretation of relative paths
70 * and the client's interpretation of relative paths. Remove after the API
71 * has been redesigned. */
72 rc = a->virtualBox->OpenMachine(Bstr(a->argv[0]).raw(),
73 machine.asOutParam());
74 if (rc == VBOX_E_FILE_ERROR)
75 {
76 char szVMFileAbs[RTPATH_MAX] = "";
77 int vrc = RTPathAbs(a->argv[0], szVMFileAbs, sizeof(szVMFileAbs));
78 if (RT_FAILURE(vrc))
79 {
80 RTMsgError("Cannot convert filename \"%s\" to absolute path", a->argv[0]);
81 return 1;
82 }
83 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(szVMFileAbs).raw(),
84 machine.asOutParam()));
85 }
86 else if (FAILED(rc))
87 CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(a->argv[0]).raw(),
88 machine.asOutParam()));
89 if (SUCCEEDED(rc))
90 {
91 ASSERT(machine);
92 CHECK_ERROR(a->virtualBox, RegisterMachine(machine));
93 }
94 return SUCCEEDED(rc) ? 0 : 1;
95}
96
97static const RTGETOPTDEF g_aUnregisterVMOptions[] =
98{
99 { "--delete", 'd', RTGETOPT_REQ_NOTHING },
100 { "-delete", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
101};
102
103int handleUnregisterVM(HandlerArg *a)
104{
105 HRESULT rc;
106 const char *VMName = NULL;
107 bool fDelete = false;
108
109 int c;
110 RTGETOPTUNION ValueUnion;
111 RTGETOPTSTATE GetState;
112 // start at 0 because main() has hacked both the argc and argv given to us
113 RTGetOptInit(&GetState, a->argc, a->argv, g_aUnregisterVMOptions, RT_ELEMENTS(g_aUnregisterVMOptions),
114 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
115 while ((c = RTGetOpt(&GetState, &ValueUnion)))
116 {
117 switch (c)
118 {
119 case 'd': // --delete
120 fDelete = true;
121 break;
122
123 case VINF_GETOPT_NOT_OPTION:
124 if (!VMName)
125 VMName = ValueUnion.psz;
126 else
127 return errorSyntax(USAGE_UNREGISTERVM, "Invalid parameter '%s'", ValueUnion.psz);
128 break;
129
130 default:
131 if (c > 0)
132 {
133 if (RT_C_IS_PRINT(c))
134 return errorSyntax(USAGE_UNREGISTERVM, "Invalid option -%c", c);
135 else
136 return errorSyntax(USAGE_UNREGISTERVM, "Invalid option case %i", c);
137 }
138 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
139 return errorSyntax(USAGE_UNREGISTERVM, "unknown option: %s\n", ValueUnion.psz);
140 else if (ValueUnion.pDef)
141 return errorSyntax(USAGE_UNREGISTERVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
142 else
143 return errorSyntax(USAGE_UNREGISTERVM, "error: %Rrs", c);
144 }
145 }
146
147 /* check for required options */
148 if (!VMName)
149 return errorSyntax(USAGE_UNREGISTERVM, "VM name required");
150
151 ComPtr<IMachine> machine;
152 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(VMName).raw(),
153 machine.asOutParam()));
154 if (machine)
155 {
156 SafeIfaceArray<IMedium> aMedia;
157 CleanupMode_T cleanupMode = CleanupMode_DetachAllReturnNone;
158 if (fDelete)
159 cleanupMode = CleanupMode_DetachAllReturnHardDisksOnly;
160 CHECK_ERROR(machine, Unregister(cleanupMode,
161 ComSafeArrayAsOutParam(aMedia)));
162 if (SUCCEEDED(rc))
163 {
164 if (fDelete)
165 {
166 ComPtr<IProgress> pProgress;
167 CHECK_ERROR(machine, Delete(ComSafeArrayAsInParam(aMedia), pProgress.asOutParam()));
168 if (SUCCEEDED(rc))
169 CHECK_ERROR(pProgress, WaitForCompletion(-1));
170 }
171 }
172 }
173 return SUCCEEDED(rc) ? 0 : 1;
174}
175
176int handleCreateVM(HandlerArg *a)
177{
178 HRESULT rc;
179 Bstr baseFolder;
180 Bstr name;
181 Bstr osTypeId;
182 RTUUID id;
183 bool fRegister = false;
184
185 RTUuidClear(&id);
186 for (int i = 0; i < a->argc; i++)
187 {
188 if ( !strcmp(a->argv[i], "--basefolder")
189 || !strcmp(a->argv[i], "-basefolder"))
190 {
191 if (a->argc <= i + 1)
192 return errorArgument("Missing argument to '%s'", a->argv[i]);
193 i++;
194 baseFolder = a->argv[i];
195 }
196 else if ( !strcmp(a->argv[i], "--name")
197 || !strcmp(a->argv[i], "-name"))
198 {
199 if (a->argc <= i + 1)
200 return errorArgument("Missing argument to '%s'", a->argv[i]);
201 i++;
202 name = a->argv[i];
203 }
204 else if ( !strcmp(a->argv[i], "--ostype")
205 || !strcmp(a->argv[i], "-ostype"))
206 {
207 if (a->argc <= i + 1)
208 return errorArgument("Missing argument to '%s'", a->argv[i]);
209 i++;
210 osTypeId = a->argv[i];
211 }
212 else if ( !strcmp(a->argv[i], "--uuid")
213 || !strcmp(a->argv[i], "-uuid"))
214 {
215 if (a->argc <= i + 1)
216 return errorArgument("Missing argument to '%s'", a->argv[i]);
217 i++;
218 if (RT_FAILURE(RTUuidFromStr(&id, a->argv[i])))
219 return errorArgument("Invalid UUID format %s\n", a->argv[i]);
220 }
221 else if ( !strcmp(a->argv[i], "--register")
222 || !strcmp(a->argv[i], "-register"))
223 {
224 fRegister = true;
225 }
226 else
227 return errorSyntax(USAGE_CREATEVM, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
228 }
229
230 /* check for required options */
231 if (name.isEmpty())
232 return errorSyntax(USAGE_CREATEVM, "Parameter --name is required");
233
234 do
235 {
236 Bstr bstrSettingsFile;
237 CHECK_ERROR_BREAK(a->virtualBox,
238 ComposeMachineFilename(name.raw(),
239 baseFolder.raw(),
240 bstrSettingsFile.asOutParam()));
241 ComPtr<IMachine> machine;
242 CHECK_ERROR_BREAK(a->virtualBox,
243 CreateMachine(bstrSettingsFile.raw(),
244 name.raw(),
245 osTypeId.raw(),
246 Guid(id).toUtf16().raw(),
247 FALSE /* forceOverwrite */,
248 machine.asOutParam()));
249
250 CHECK_ERROR_BREAK(machine, SaveSettings());
251 if (fRegister)
252 {
253 CHECK_ERROR_BREAK(a->virtualBox, RegisterMachine(machine));
254 }
255 Bstr uuid;
256 CHECK_ERROR_BREAK(machine, COMGETTER(Id)(uuid.asOutParam()));
257 Bstr settingsFile;
258 CHECK_ERROR_BREAK(machine, COMGETTER(SettingsFilePath)(settingsFile.asOutParam()));
259 RTPrintf("Virtual machine '%ls' is created%s.\n"
260 "UUID: %s\n"
261 "Settings file: '%ls'\n",
262 name.raw(), fRegister ? " and registered" : "",
263 Utf8Str(uuid).c_str(), settingsFile.raw());
264 }
265 while (0);
266
267 return SUCCEEDED(rc) ? 0 : 1;
268}
269
270static const RTGETOPTDEF g_aCloneVMOptions[] =
271{
272 { "--snapshot", 's', RTGETOPT_REQ_STRING },
273 { "--name", 'n', RTGETOPT_REQ_STRING },
274 { "--mode", 'm', RTGETOPT_REQ_STRING },
275 { "--register", 'r', RTGETOPT_REQ_NOTHING },
276 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
277 { "--uuid", 'u', RTGETOPT_REQ_STRING },
278};
279
280static int parseCloneMode(const char *psz, CloneMode_T *pMode)
281{
282 if (!RTStrICmp(psz, "machine"))
283 *pMode = CloneMode_MachineState;
284 else if (!RTStrICmp(psz, "machineandchilds"))
285 *pMode = CloneMode_MachineAndChildStates;
286 else if (!RTStrICmp(psz, "all"))
287 *pMode = CloneMode_AllStates;
288 else
289 return VERR_PARSE_ERROR;
290
291 return VINF_SUCCESS;
292}
293
294int handleCloneVM(HandlerArg *a)
295{
296 HRESULT rc;
297 const char *pszSrcName = NULL;
298 const char *pszSnapshotName = NULL;
299 CloneMode_T mode = CloneMode_MachineState;
300 const char *pszTrgName = NULL;
301 const char *pszTrgBaseFolder = NULL;
302 bool fRegister = false;
303 RTUUID trgUuid;
304
305 int c;
306 RTGETOPTUNION ValueUnion;
307 RTGETOPTSTATE GetState;
308 // start at 0 because main() has hacked both the argc and argv given to us
309 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneVMOptions, RT_ELEMENTS(g_aCloneVMOptions),
310 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
311 while ((c = RTGetOpt(&GetState, &ValueUnion)))
312 {
313 switch (c)
314 {
315 case 's': // --snapshot
316 pszSnapshotName = ValueUnion.psz;
317 break;
318
319 case 'm': // --mode
320 if (RT_FAILURE(parseCloneMode(ValueUnion.psz, &mode)))
321 return errorArgument("Invalid clone mode '%s'\n", ValueUnion.psz);
322 break;
323
324 case 'n': // --name
325 pszTrgName = ValueUnion.psz;
326 break;
327
328 case 'p': // --basefolder
329 pszTrgBaseFolder = ValueUnion.psz;
330 break;
331
332 case 'u': // --uuid
333 if (RT_FAILURE(RTUuidFromStr(&trgUuid, ValueUnion.psz)))
334 return errorArgument("Invalid UUID format %s\n", ValueUnion.psz);
335 break;
336
337 case 'r': // --register
338 fRegister = true;
339 break;
340
341 case VINF_GETOPT_NOT_OPTION:
342 if (!pszSrcName)
343 pszSrcName = ValueUnion.psz;
344 else
345 return errorSyntax(USAGE_CLONEVM, "Invalid parameter '%s'", ValueUnion.psz);
346 break;
347
348 default:
349 return errorGetOpt(USAGE_CLONEVM, c, &ValueUnion);
350 }
351 }
352
353 /* Check for required options */
354 if (!pszSrcName)
355 return errorSyntax(USAGE_CLONEVM, "VM name required");
356
357 /* Get the machine object */
358 ComPtr<IMachine> srcMachine;
359 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
360 srcMachine.asOutParam()),
361 RTEXITCODE_FAILURE);
362
363 /* If a snapshot name/uuid was given, get the particular machine of this
364 * snapshot. */
365 if (pszSnapshotName)
366 {
367 ComPtr<ISnapshot> srcSnapshot;
368 CHECK_ERROR_RET(srcMachine, FindSnapshot(Bstr(pszSnapshotName).raw(),
369 srcSnapshot.asOutParam()),
370 RTEXITCODE_FAILURE);
371 CHECK_ERROR_RET(srcSnapshot, COMGETTER(Machine)(srcMachine.asOutParam()),
372 RTEXITCODE_FAILURE);
373 }
374
375 /* Default name necessary? */
376 if (!pszTrgName)
377 pszTrgName = RTStrAPrintf2("%s Clone", pszSrcName);
378
379 Bstr bstrSettingsFile;
380 CHECK_ERROR_RET(a->virtualBox,
381 ComposeMachineFilename(Bstr(pszTrgName).raw(),
382 Bstr(pszTrgBaseFolder).raw(),
383 bstrSettingsFile.asOutParam()),
384 RTEXITCODE_FAILURE);
385
386 ComPtr<IMachine> trgMachine;
387 CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(),
388 Bstr(pszTrgName).raw(),
389 NULL,
390 Guid(trgUuid).toUtf16().raw(),
391 FALSE,
392 trgMachine.asOutParam()),
393 RTEXITCODE_FAILURE);
394
395 /* Clone options */
396 com::SafeArray<CloneOptions_T> options;
397 options.push_back(CloneOptions_KeepNATMACs);
398
399 /* Start the cloning */
400 ComPtr<IProgress> progress;
401 CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine,
402 mode,
403 ComSafeArrayAsInParam(options),
404 progress.asOutParam()),
405 RTEXITCODE_FAILURE);
406 rc = showProgress(progress);
407 if (FAILED(rc))
408 {
409 com::ProgressErrorInfo ErrInfo(progress);
410 com::GluePrintErrorInfo(ErrInfo);
411 return RTEXITCODE_FAILURE;
412 }
413
414 if (fRegister)
415 CHECK_ERROR_RET(a->virtualBox, RegisterMachine(trgMachine), RTEXITCODE_FAILURE);
416
417 Bstr bstrNewName;
418 CHECK_ERROR_RET(trgMachine, COMGETTER(Name)(bstrNewName.asOutParam()), RTEXITCODE_FAILURE);
419 RTPrintf("Machine has been successfully cloned as \"%lS\"\n", bstrNewName.raw());
420
421 return RTEXITCODE_SUCCESS;
422}
423
424int handleStartVM(HandlerArg *a)
425{
426 HRESULT rc;
427 const char *VMName = NULL;
428 Bstr sessionType = "gui";
429
430 static const RTGETOPTDEF s_aStartVMOptions[] =
431 {
432 { "--type", 't', RTGETOPT_REQ_STRING },
433 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
434 };
435 int c;
436 RTGETOPTUNION ValueUnion;
437 RTGETOPTSTATE GetState;
438 // start at 0 because main() has hacked both the argc and argv given to us
439 RTGetOptInit(&GetState, a->argc, a->argv, s_aStartVMOptions, RT_ELEMENTS(s_aStartVMOptions),
440 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
441 while ((c = RTGetOpt(&GetState, &ValueUnion)))
442 {
443 switch (c)
444 {
445 case 't': // --type
446 if (!RTStrICmp(ValueUnion.psz, "gui"))
447 {
448 sessionType = "gui";
449 }
450#ifdef VBOX_WITH_VBOXSDL
451 else if (!RTStrICmp(ValueUnion.psz, "sdl"))
452 {
453 sessionType = "sdl";
454 }
455#endif
456#ifdef VBOX_WITH_HEADLESS
457 else if (!RTStrICmp(ValueUnion.psz, "capture"))
458 {
459 sessionType = "capture";
460 }
461 else if (!RTStrICmp(ValueUnion.psz, "headless"))
462 {
463 sessionType = "headless";
464 }
465#endif
466 else
467 sessionType = ValueUnion.psz;
468 break;
469
470 case VINF_GETOPT_NOT_OPTION:
471 if (!VMName)
472 VMName = ValueUnion.psz;
473 else
474 return errorSyntax(USAGE_STARTVM, "Invalid parameter '%s'", ValueUnion.psz);
475 break;
476
477 default:
478 if (c > 0)
479 {
480 if (RT_C_IS_PRINT(c))
481 return errorSyntax(USAGE_STARTVM, "Invalid option -%c", c);
482 else
483 return errorSyntax(USAGE_STARTVM, "Invalid option case %i", c);
484 }
485 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
486 return errorSyntax(USAGE_STARTVM, "unknown option: %s\n", ValueUnion.psz);
487 else if (ValueUnion.pDef)
488 return errorSyntax(USAGE_STARTVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
489 else
490 return errorSyntax(USAGE_STARTVM, "error: %Rrs", c);
491 }
492 }
493
494 /* check for required options */
495 if (!VMName)
496 return errorSyntax(USAGE_STARTVM, "VM name required");
497
498 ComPtr<IMachine> machine;
499 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(VMName).raw(),
500 machine.asOutParam()));
501 if (machine)
502 {
503 Bstr env;
504#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
505 /* make sure the VM process will start on the same display as VBoxManage */
506 Utf8Str str;
507 const char *pszDisplay = RTEnvGet("DISPLAY");
508 if (pszDisplay)
509 str = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
510 const char *pszXAuth = RTEnvGet("XAUTHORITY");
511 if (pszXAuth)
512 str.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
513 env = str;
514#endif
515 ComPtr<IProgress> progress;
516 CHECK_ERROR_RET(machine, LaunchVMProcess(a->session, sessionType.raw(),
517 env.raw(), progress.asOutParam()), rc);
518 if (!progress.isNull())
519 {
520 RTPrintf("Waiting for the VM to power on...\n");
521 CHECK_ERROR_RET(progress, WaitForCompletion(-1), 1);
522
523 BOOL completed;
524 CHECK_ERROR_RET(progress, COMGETTER(Completed)(&completed), rc);
525 ASSERT(completed);
526
527 LONG iRc;
528 CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
529 if (FAILED(iRc))
530 {
531 ProgressErrorInfo info(progress);
532 com::GluePrintErrorInfo(info);
533 }
534 else
535 {
536 RTPrintf("VM has been successfully started.\n");
537 }
538 }
539 }
540
541 /* it's important to always close sessions */
542 a->session->UnlockMachine();
543
544 return SUCCEEDED(rc) ? 0 : 1;
545}
546
547int handleDiscardState(HandlerArg *a)
548{
549 HRESULT rc;
550
551 if (a->argc != 1)
552 return errorSyntax(USAGE_DISCARDSTATE, "Incorrect number of parameters");
553
554 ComPtr<IMachine> machine;
555 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
556 machine.asOutParam()));
557 if (machine)
558 {
559 do
560 {
561 /* we have to open a session for this task */
562 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
563 do
564 {
565 ComPtr<IConsole> console;
566 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
567 CHECK_ERROR_BREAK(console, DiscardSavedState(true /* fDeleteFile */));
568 } while (0);
569 CHECK_ERROR_BREAK(a->session, UnlockMachine());
570 } while (0);
571 }
572
573 return SUCCEEDED(rc) ? 0 : 1;
574}
575
576int handleAdoptState(HandlerArg *a)
577{
578 HRESULT rc;
579
580 if (a->argc != 2)
581 return errorSyntax(USAGE_ADOPTSTATE, "Incorrect number of parameters");
582
583 ComPtr<IMachine> machine;
584 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
585 machine.asOutParam()));
586 if (machine)
587 {
588 do
589 {
590 /* we have to open a session for this task */
591 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
592 do
593 {
594 ComPtr<IConsole> console;
595 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
596 CHECK_ERROR_BREAK(console, AdoptSavedState(Bstr(a->argv[1]).raw()));
597 } while (0);
598 CHECK_ERROR_BREAK(a->session, UnlockMachine());
599 } while (0);
600 }
601
602 return SUCCEEDED(rc) ? 0 : 1;
603}
604
605int handleGetExtraData(HandlerArg *a)
606{
607 HRESULT rc = S_OK;
608
609 if (a->argc != 2)
610 return errorSyntax(USAGE_GETEXTRADATA, "Incorrect number of parameters");
611
612 /* global data? */
613 if (!strcmp(a->argv[0], "global"))
614 {
615 /* enumeration? */
616 if (!strcmp(a->argv[1], "enumerate"))
617 {
618 SafeArray<BSTR> aKeys;
619 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
620
621 for (size_t i = 0;
622 i < aKeys.size();
623 ++i)
624 {
625 Bstr bstrKey(aKeys[i]);
626 Bstr bstrValue;
627 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
628 bstrValue.asOutParam()));
629
630 RTPrintf("Key: %lS, Value: %lS\n", bstrKey.raw(), bstrValue.raw());
631 }
632 }
633 else
634 {
635 Bstr value;
636 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
637 value.asOutParam()));
638 if (!value.isEmpty())
639 RTPrintf("Value: %lS\n", value.raw());
640 else
641 RTPrintf("No value set!\n");
642 }
643 }
644 else
645 {
646 ComPtr<IMachine> machine;
647 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
648 machine.asOutParam()));
649 if (machine)
650 {
651 /* enumeration? */
652 if (!strcmp(a->argv[1], "enumerate"))
653 {
654 SafeArray<BSTR> aKeys;
655 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
656
657 for (size_t i = 0;
658 i < aKeys.size();
659 ++i)
660 {
661 Bstr bstrKey(aKeys[i]);
662 Bstr bstrValue;
663 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
664 bstrValue.asOutParam()));
665
666 RTPrintf("Key: %lS, Value: %lS\n", bstrKey.raw(), bstrValue.raw());
667 }
668 }
669 else
670 {
671 Bstr value;
672 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
673 value.asOutParam()));
674 if (!value.isEmpty())
675 RTPrintf("Value: %lS\n", value.raw());
676 else
677 RTPrintf("No value set!\n");
678 }
679 }
680 }
681 return SUCCEEDED(rc) ? 0 : 1;
682}
683
684int handleSetExtraData(HandlerArg *a)
685{
686 HRESULT rc = S_OK;
687
688 if (a->argc < 2)
689 return errorSyntax(USAGE_SETEXTRADATA, "Not enough parameters");
690
691 /* global data? */
692 if (!strcmp(a->argv[0], "global"))
693 {
694 /** @todo passing NULL is deprecated */
695 if (a->argc < 3)
696 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
697 NULL));
698 else if (a->argc == 3)
699 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
700 Bstr(a->argv[2]).raw()));
701 else
702 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
703 }
704 else
705 {
706 ComPtr<IMachine> machine;
707 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
708 machine.asOutParam()));
709 if (machine)
710 {
711 /** @todo passing NULL is deprecated */
712 if (a->argc < 3)
713 CHECK_ERROR(machine, SetExtraData(Bstr(a->argv[1]).raw(),
714 NULL));
715 else if (a->argc == 3)
716 CHECK_ERROR(machine, SetExtraData(Bstr(a->argv[1]).raw(),
717 Bstr(a->argv[2]).raw()));
718 else
719 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
720 }
721 }
722 return SUCCEEDED(rc) ? 0 : 1;
723}
724
725int handleSetProperty(HandlerArg *a)
726{
727 HRESULT rc;
728
729 /* there must be two arguments: property name and value */
730 if (a->argc != 2)
731 return errorSyntax(USAGE_SETPROPERTY, "Incorrect number of parameters");
732
733 ComPtr<ISystemProperties> systemProperties;
734 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
735
736 if (!strcmp(a->argv[0], "machinefolder"))
737 {
738 /* reset to default? */
739 if (!strcmp(a->argv[1], "default"))
740 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
741 else
742 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
743 }
744 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
745 || !strcmp(a->argv[0], "vrdpauthlibrary"))
746 {
747 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
748 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n");
749
750 /* reset to default? */
751 if (!strcmp(a->argv[1], "default"))
752 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
753 else
754 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
755 }
756 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
757 {
758 /* reset to default? */
759 if (!strcmp(a->argv[1], "default"))
760 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
761 else
762 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
763 }
764 else if (!strcmp(a->argv[0], "vrdeextpack"))
765 {
766 /* disable? */
767 if (!strcmp(a->argv[1], "null"))
768 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
769 else
770 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
771 }
772 else if (!strcmp(a->argv[0], "loghistorycount"))
773 {
774 uint32_t uVal;
775 int vrc;
776 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
777 if (vrc != VINF_SUCCESS)
778 return errorArgument("Error parsing Log history count '%s'", a->argv[1]);
779 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
780 }
781 else
782 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]);
783
784 return SUCCEEDED(rc) ? 0 : 1;
785}
786
787int handleSharedFolder(HandlerArg *a)
788{
789 HRESULT rc;
790
791 /* we need at least a command and target */
792 if (a->argc < 2)
793 return errorSyntax(USAGE_SHAREDFOLDER, "Not enough parameters");
794
795 ComPtr<IMachine> machine;
796 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[1]).raw(),
797 machine.asOutParam()));
798 if (!machine)
799 return 1;
800
801 if (!strcmp(a->argv[0], "add"))
802 {
803 /* we need at least four more parameters */
804 if (a->argc < 5)
805 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Not enough parameters");
806
807 char *name = NULL;
808 char *hostpath = NULL;
809 bool fTransient = false;
810 bool fWritable = true;
811 bool fAutoMount = false;
812
813 for (int i = 2; i < a->argc; i++)
814 {
815 if ( !strcmp(a->argv[i], "--name")
816 || !strcmp(a->argv[i], "-name"))
817 {
818 if (a->argc <= i + 1 || !*a->argv[i+1])
819 return errorArgument("Missing argument to '%s'", a->argv[i]);
820 i++;
821 name = a->argv[i];
822 }
823 else if ( !strcmp(a->argv[i], "--hostpath")
824 || !strcmp(a->argv[i], "-hostpath"))
825 {
826 if (a->argc <= i + 1 || !*a->argv[i+1])
827 return errorArgument("Missing argument to '%s'", a->argv[i]);
828 i++;
829 hostpath = a->argv[i];
830 }
831 else if ( !strcmp(a->argv[i], "--readonly")
832 || !strcmp(a->argv[i], "-readonly"))
833 {
834 fWritable = false;
835 }
836 else if ( !strcmp(a->argv[i], "--transient")
837 || !strcmp(a->argv[i], "-transient"))
838 {
839 fTransient = true;
840 }
841 else if ( !strcmp(a->argv[i], "--automount")
842 || !strcmp(a->argv[i], "-automount"))
843 {
844 fAutoMount = true;
845 }
846 else
847 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
848 }
849
850 if (NULL != strstr(name, " "))
851 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "No spaces allowed in parameter '-name'!");
852
853 /* required arguments */
854 if (!name || !hostpath)
855 {
856 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Parameters --name and --hostpath are required");
857 }
858
859 if (fTransient)
860 {
861 ComPtr <IConsole> console;
862
863 /* open an existing session for the VM */
864 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);
865 /* get the session machine */
866 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(machine.asOutParam()), 1);
867 /* get the session console */
868 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), 1);
869
870 CHECK_ERROR(console, CreateSharedFolder(Bstr(name).raw(),
871 Bstr(hostpath).raw(),
872 fWritable, fAutoMount));
873 if (console)
874 a->session->UnlockMachine();
875 }
876 else
877 {
878 /* open a session for the VM */
879 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), 1);
880
881 /* get the mutable session machine */
882 a->session->COMGETTER(Machine)(machine.asOutParam());
883
884 CHECK_ERROR(machine, CreateSharedFolder(Bstr(name).raw(),
885 Bstr(hostpath).raw(),
886 fWritable, fAutoMount));
887 if (SUCCEEDED(rc))
888 CHECK_ERROR(machine, SaveSettings());
889
890 a->session->UnlockMachine();
891 }
892 }
893 else if (!strcmp(a->argv[0], "remove"))
894 {
895 /* we need at least two more parameters */
896 if (a->argc < 3)
897 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Not enough parameters");
898
899 char *name = NULL;
900 bool fTransient = false;
901
902 for (int i = 2; i < a->argc; i++)
903 {
904 if ( !strcmp(a->argv[i], "--name")
905 || !strcmp(a->argv[i], "-name"))
906 {
907 if (a->argc <= i + 1 || !*a->argv[i+1])
908 return errorArgument("Missing argument to '%s'", a->argv[i]);
909 i++;
910 name = a->argv[i];
911 }
912 else if ( !strcmp(a->argv[i], "--transient")
913 || !strcmp(a->argv[i], "-transient"))
914 {
915 fTransient = true;
916 }
917 else
918 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
919 }
920
921 /* required arguments */
922 if (!name)
923 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Parameter --name is required");
924
925 if (fTransient)
926 {
927 ComPtr <IConsole> console;
928
929 /* open an existing session for the VM */
930 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);
931 /* get the session machine */
932 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(machine.asOutParam()), 1);
933 /* get the session console */
934 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), 1);
935
936 CHECK_ERROR(console, RemoveSharedFolder(Bstr(name).raw()));
937
938 if (console)
939 a->session->UnlockMachine();
940 }
941 else
942 {
943 /* open a session for the VM */
944 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), 1);
945
946 /* get the mutable session machine */
947 a->session->COMGETTER(Machine)(machine.asOutParam());
948
949 CHECK_ERROR(machine, RemoveSharedFolder(Bstr(name).raw()));
950
951 /* commit and close the session */
952 CHECK_ERROR(machine, SaveSettings());
953 a->session->UnlockMachine();
954 }
955 }
956 else
957 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
958
959 return 0;
960}
961
962int handleExtPack(HandlerArg *a)
963{
964 if (a->argc < 1)
965 return errorSyntax(USAGE_EXTPACK, "Incorrect number of parameters");
966
967 ComObjPtr<IExtPackManager> ptrExtPackMgr;
968 CHECK_ERROR2_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
969
970 RTGETOPTSTATE GetState;
971 RTGETOPTUNION ValueUnion;
972 int ch;
973 HRESULT hrc = S_OK;
974
975 if (!strcmp(a->argv[0], "install"))
976 {
977 const char *pszName = NULL;
978 bool fReplace = false;
979
980 static const RTGETOPTDEF s_aInstallOptions[] =
981 {
982 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
983 };
984
985 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
986 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
987 {
988 switch (ch)
989 {
990 case 'f':
991 fReplace = true;
992 break;
993
994 case VINF_GETOPT_NOT_OPTION:
995 if (pszName)
996 return errorSyntax(USAGE_EXTPACK, "Too many extension pack names given to \"extpack uninstall\"");
997 pszName = ValueUnion.psz;
998 break;
999
1000 default:
1001 return errorGetOpt(USAGE_EXTPACK, ch, &ValueUnion);
1002 }
1003 }
1004 if (!pszName)
1005 return errorSyntax(USAGE_EXTPACK, "No extension pack name was given to \"extpack install\"");
1006
1007 char szPath[RTPATH_MAX];
1008 int vrc = RTPathAbs(a->argv[1], szPath, sizeof(szPath));
1009 if (RT_FAILURE(vrc))
1010 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", a->argv[1], vrc);
1011
1012 Bstr bstrTarball(szPath);
1013 Bstr bstrName;
1014 ComPtr<IExtPackFile> ptrExtPackFile;
1015 CHECK_ERROR2_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1016 CHECK_ERROR2_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1017 ComPtr<IProgress> ptrProgress;
1018 CHECK_ERROR2_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1019 hrc = showProgress(ptrProgress);
1020 if (FAILED(hrc))
1021 {
1022 com::ProgressErrorInfo ErrInfo(ptrProgress);
1023 if (ErrInfo.isBasicAvailable())
1024 RTMsgError("Failed to install \"%s\": %lS", szPath, ErrInfo.getText().raw());
1025 else
1026 RTMsgError("Failed to install \"%s\": No error message available!", szPath);
1027 return RTEXITCODE_FAILURE;
1028 }
1029 RTPrintf("Successfully installed \"%lS\".\n", bstrName.raw());
1030 }
1031 else if (!strcmp(a->argv[0], "uninstall"))
1032 {
1033 const char *pszName = NULL;
1034 bool fForced = false;
1035
1036 static const RTGETOPTDEF s_aUninstallOptions[] =
1037 {
1038 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1039 };
1040
1041 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1042 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1043 {
1044 switch (ch)
1045 {
1046 case 'f':
1047 fForced = true;
1048 break;
1049
1050 case VINF_GETOPT_NOT_OPTION:
1051 if (pszName)
1052 return errorSyntax(USAGE_EXTPACK, "Too many extension pack names given to \"extpack uninstall\"");
1053 pszName = ValueUnion.psz;
1054 break;
1055
1056 default:
1057 return errorGetOpt(USAGE_EXTPACK, ch, &ValueUnion);
1058 }
1059 }
1060 if (!pszName)
1061 return errorSyntax(USAGE_EXTPACK, "No extension pack name was given to \"extpack uninstall\"");
1062
1063 Bstr bstrName(pszName);
1064 ComPtr<IProgress> ptrProgress;
1065 CHECK_ERROR2_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1066 hrc = showProgress(ptrProgress);
1067 if (FAILED(hrc))
1068 {
1069 com::ProgressErrorInfo ErrInfo(ptrProgress);
1070 if (ErrInfo.isBasicAvailable())
1071 RTMsgError("Failed to uninstall \"%s\": %lS", pszName, ErrInfo.getText().raw());
1072 else
1073 RTMsgError("Failed to uninstall \"%s\": No error message available!", pszName);
1074 return RTEXITCODE_FAILURE;
1075 }
1076 RTPrintf("Successfully uninstalled \"%s\".\n", pszName);
1077 }
1078 else if (!strcmp(a->argv[0], "cleanup"))
1079 {
1080 if (a->argc > 1)
1081 return errorSyntax(USAGE_EXTPACK, "Too many parameters given to \"extpack cleanup\"");
1082
1083 CHECK_ERROR2_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1084 RTPrintf("Successfully performed extension pack cleanup\n");
1085 }
1086 else
1087 return errorSyntax(USAGE_EXTPACK, "Unknown command \"%s\"", a->argv[0]);
1088
1089 return RTEXITCODE_SUCCESS;
1090}
1091
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette