VirtualBox

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

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

FE/CLI: add --options to clonevm

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.6 KB
Line 
1/* $Id: VBoxManageMisc.cpp 37543 2011-06-17 13:52: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 { "--options", 'o', RTGETOPT_REQ_STRING },
276 { "--register", 'r', RTGETOPT_REQ_NOTHING },
277 { "--basefolder", 'p', RTGETOPT_REQ_STRING },
278 { "--uuid", 'u', RTGETOPT_REQ_STRING },
279};
280
281static int parseCloneMode(const char *psz, CloneMode_T *pMode)
282{
283 if (!RTStrICmp(psz, "machine"))
284 *pMode = CloneMode_MachineState;
285 else if (!RTStrICmp(psz, "machineandchilds"))
286 *pMode = CloneMode_MachineAndChildStates;
287 else if (!RTStrICmp(psz, "all"))
288 *pMode = CloneMode_AllStates;
289 else
290 return VERR_PARSE_ERROR;
291
292 return VINF_SUCCESS;
293}
294
295static int parseCloneOptions(const char *psz, com::SafeArray<CloneOptions_T> *options)
296{
297 int rc = VINF_SUCCESS;
298 while (psz && *psz && RT_SUCCESS(rc))
299 {
300 size_t len;
301 const char *pszComma = strchr(psz, ',');
302 if (pszComma)
303 len = pszComma - psz;
304 else
305 len = strlen(psz);
306 if (len > 0)
307 {
308 if (!RTStrNICmp(psz, "KeepAllMACs", len))
309 options->push_back(CloneOptions_KeepAllMACs);
310 else if (!RTStrNICmp(psz, "KeepNATMACs", len))
311 options->push_back(CloneOptions_KeepNATMACs);
312// else if (!RTStrNICmp(psz, "Link", len))
313// *options.push_back(CloneOptions_Link)
314 else
315 rc = VERR_PARSE_ERROR;
316 }
317 if (pszComma)
318 psz += len + 1;
319 else
320 psz += len;
321 }
322
323 return rc;
324}
325
326int handleCloneVM(HandlerArg *a)
327{
328 HRESULT rc;
329 const char *pszSrcName = NULL;
330 const char *pszSnapshotName = NULL;
331 CloneMode_T mode = CloneMode_MachineState;
332 com::SafeArray<CloneOptions_T> options;
333 const char *pszTrgName = NULL;
334 const char *pszTrgBaseFolder = NULL;
335 bool fRegister = false;
336 RTUUID trgUuid;
337
338 int c;
339 RTGETOPTUNION ValueUnion;
340 RTGETOPTSTATE GetState;
341 // start at 0 because main() has hacked both the argc and argv given to us
342 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneVMOptions, RT_ELEMENTS(g_aCloneVMOptions),
343 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
344 while ((c = RTGetOpt(&GetState, &ValueUnion)))
345 {
346 switch (c)
347 {
348 case 's': // --snapshot
349 pszSnapshotName = ValueUnion.psz;
350 break;
351
352 case 'm': // --mode
353 if (RT_FAILURE(parseCloneMode(ValueUnion.psz, &mode)))
354 return errorArgument("Invalid clone mode '%s'\n", ValueUnion.psz);
355 break;
356
357 case 'o': // --options
358 if (RT_FAILURE(parseCloneOptions(ValueUnion.psz, &options)))
359 return errorArgument("Invalid clone options '%s'\n", ValueUnion.psz);
360 break;
361
362 case 'n': // --name
363 pszTrgName = ValueUnion.psz;
364 break;
365
366 case 'p': // --basefolder
367 pszTrgBaseFolder = ValueUnion.psz;
368 break;
369
370 case 'u': // --uuid
371 if (RT_FAILURE(RTUuidFromStr(&trgUuid, ValueUnion.psz)))
372 return errorArgument("Invalid UUID format %s\n", ValueUnion.psz);
373 break;
374
375 case 'r': // --register
376 fRegister = true;
377 break;
378
379 case VINF_GETOPT_NOT_OPTION:
380 if (!pszSrcName)
381 pszSrcName = ValueUnion.psz;
382 else
383 return errorSyntax(USAGE_CLONEVM, "Invalid parameter '%s'", ValueUnion.psz);
384 break;
385
386 default:
387 return errorGetOpt(USAGE_CLONEVM, c, &ValueUnion);
388 }
389 }
390
391 /* Check for required options */
392 if (!pszSrcName)
393 return errorSyntax(USAGE_CLONEVM, "VM name required");
394
395 /* Get the machine object */
396 ComPtr<IMachine> srcMachine;
397 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
398 srcMachine.asOutParam()),
399 RTEXITCODE_FAILURE);
400
401 /* If a snapshot name/uuid was given, get the particular machine of this
402 * snapshot. */
403 if (pszSnapshotName)
404 {
405 ComPtr<ISnapshot> srcSnapshot;
406 CHECK_ERROR_RET(srcMachine, FindSnapshot(Bstr(pszSnapshotName).raw(),
407 srcSnapshot.asOutParam()),
408 RTEXITCODE_FAILURE);
409 CHECK_ERROR_RET(srcSnapshot, COMGETTER(Machine)(srcMachine.asOutParam()),
410 RTEXITCODE_FAILURE);
411 }
412
413 /* Default name necessary? */
414 if (!pszTrgName)
415 pszTrgName = RTStrAPrintf2("%s Clone", pszSrcName);
416
417 Bstr bstrSettingsFile;
418 CHECK_ERROR_RET(a->virtualBox,
419 ComposeMachineFilename(Bstr(pszTrgName).raw(),
420 Bstr(pszTrgBaseFolder).raw(),
421 bstrSettingsFile.asOutParam()),
422 RTEXITCODE_FAILURE);
423
424 ComPtr<IMachine> trgMachine;
425 CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(),
426 Bstr(pszTrgName).raw(),
427 NULL,
428 Guid(trgUuid).toUtf16().raw(),
429 FALSE,
430 trgMachine.asOutParam()),
431 RTEXITCODE_FAILURE);
432
433 /* Start the cloning */
434 ComPtr<IProgress> progress;
435 CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine,
436 mode,
437 ComSafeArrayAsInParam(options),
438 progress.asOutParam()),
439 RTEXITCODE_FAILURE);
440 rc = showProgress(progress);
441 if (FAILED(rc))
442 {
443 com::ProgressErrorInfo ErrInfo(progress);
444 com::GluePrintErrorInfo(ErrInfo);
445 return RTEXITCODE_FAILURE;
446 }
447
448 if (fRegister)
449 CHECK_ERROR_RET(a->virtualBox, RegisterMachine(trgMachine), RTEXITCODE_FAILURE);
450
451 Bstr bstrNewName;
452 CHECK_ERROR_RET(trgMachine, COMGETTER(Name)(bstrNewName.asOutParam()), RTEXITCODE_FAILURE);
453 RTPrintf("Machine has been successfully cloned as \"%lS\"\n", bstrNewName.raw());
454
455 return RTEXITCODE_SUCCESS;
456}
457
458int handleStartVM(HandlerArg *a)
459{
460 HRESULT rc;
461 const char *VMName = NULL;
462 Bstr sessionType = "gui";
463
464 static const RTGETOPTDEF s_aStartVMOptions[] =
465 {
466 { "--type", 't', RTGETOPT_REQ_STRING },
467 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
468 };
469 int c;
470 RTGETOPTUNION ValueUnion;
471 RTGETOPTSTATE GetState;
472 // start at 0 because main() has hacked both the argc and argv given to us
473 RTGetOptInit(&GetState, a->argc, a->argv, s_aStartVMOptions, RT_ELEMENTS(s_aStartVMOptions),
474 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
475 while ((c = RTGetOpt(&GetState, &ValueUnion)))
476 {
477 switch (c)
478 {
479 case 't': // --type
480 if (!RTStrICmp(ValueUnion.psz, "gui"))
481 {
482 sessionType = "gui";
483 }
484#ifdef VBOX_WITH_VBOXSDL
485 else if (!RTStrICmp(ValueUnion.psz, "sdl"))
486 {
487 sessionType = "sdl";
488 }
489#endif
490#ifdef VBOX_WITH_HEADLESS
491 else if (!RTStrICmp(ValueUnion.psz, "capture"))
492 {
493 sessionType = "capture";
494 }
495 else if (!RTStrICmp(ValueUnion.psz, "headless"))
496 {
497 sessionType = "headless";
498 }
499#endif
500 else
501 sessionType = ValueUnion.psz;
502 break;
503
504 case VINF_GETOPT_NOT_OPTION:
505 if (!VMName)
506 VMName = ValueUnion.psz;
507 else
508 return errorSyntax(USAGE_STARTVM, "Invalid parameter '%s'", ValueUnion.psz);
509 break;
510
511 default:
512 if (c > 0)
513 {
514 if (RT_C_IS_PRINT(c))
515 return errorSyntax(USAGE_STARTVM, "Invalid option -%c", c);
516 else
517 return errorSyntax(USAGE_STARTVM, "Invalid option case %i", c);
518 }
519 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
520 return errorSyntax(USAGE_STARTVM, "unknown option: %s\n", ValueUnion.psz);
521 else if (ValueUnion.pDef)
522 return errorSyntax(USAGE_STARTVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
523 else
524 return errorSyntax(USAGE_STARTVM, "error: %Rrs", c);
525 }
526 }
527
528 /* check for required options */
529 if (!VMName)
530 return errorSyntax(USAGE_STARTVM, "VM name required");
531
532 ComPtr<IMachine> machine;
533 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(VMName).raw(),
534 machine.asOutParam()));
535 if (machine)
536 {
537 Bstr env;
538#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
539 /* make sure the VM process will start on the same display as VBoxManage */
540 Utf8Str str;
541 const char *pszDisplay = RTEnvGet("DISPLAY");
542 if (pszDisplay)
543 str = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
544 const char *pszXAuth = RTEnvGet("XAUTHORITY");
545 if (pszXAuth)
546 str.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
547 env = str;
548#endif
549 ComPtr<IProgress> progress;
550 CHECK_ERROR_RET(machine, LaunchVMProcess(a->session, sessionType.raw(),
551 env.raw(), progress.asOutParam()), rc);
552 if (!progress.isNull())
553 {
554 RTPrintf("Waiting for the VM to power on...\n");
555 CHECK_ERROR_RET(progress, WaitForCompletion(-1), 1);
556
557 BOOL completed;
558 CHECK_ERROR_RET(progress, COMGETTER(Completed)(&completed), rc);
559 ASSERT(completed);
560
561 LONG iRc;
562 CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
563 if (FAILED(iRc))
564 {
565 ProgressErrorInfo info(progress);
566 com::GluePrintErrorInfo(info);
567 }
568 else
569 {
570 RTPrintf("VM has been successfully started.\n");
571 }
572 }
573 }
574
575 /* it's important to always close sessions */
576 a->session->UnlockMachine();
577
578 return SUCCEEDED(rc) ? 0 : 1;
579}
580
581int handleDiscardState(HandlerArg *a)
582{
583 HRESULT rc;
584
585 if (a->argc != 1)
586 return errorSyntax(USAGE_DISCARDSTATE, "Incorrect number of parameters");
587
588 ComPtr<IMachine> machine;
589 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
590 machine.asOutParam()));
591 if (machine)
592 {
593 do
594 {
595 /* we have to open a session for this task */
596 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
597 do
598 {
599 ComPtr<IConsole> console;
600 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
601 CHECK_ERROR_BREAK(console, DiscardSavedState(true /* fDeleteFile */));
602 } while (0);
603 CHECK_ERROR_BREAK(a->session, UnlockMachine());
604 } while (0);
605 }
606
607 return SUCCEEDED(rc) ? 0 : 1;
608}
609
610int handleAdoptState(HandlerArg *a)
611{
612 HRESULT rc;
613
614 if (a->argc != 2)
615 return errorSyntax(USAGE_ADOPTSTATE, "Incorrect number of parameters");
616
617 ComPtr<IMachine> machine;
618 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
619 machine.asOutParam()));
620 if (machine)
621 {
622 do
623 {
624 /* we have to open a session for this task */
625 CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
626 do
627 {
628 ComPtr<IConsole> console;
629 CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
630 CHECK_ERROR_BREAK(console, AdoptSavedState(Bstr(a->argv[1]).raw()));
631 } while (0);
632 CHECK_ERROR_BREAK(a->session, UnlockMachine());
633 } while (0);
634 }
635
636 return SUCCEEDED(rc) ? 0 : 1;
637}
638
639int handleGetExtraData(HandlerArg *a)
640{
641 HRESULT rc = S_OK;
642
643 if (a->argc != 2)
644 return errorSyntax(USAGE_GETEXTRADATA, "Incorrect number of parameters");
645
646 /* global data? */
647 if (!strcmp(a->argv[0], "global"))
648 {
649 /* enumeration? */
650 if (!strcmp(a->argv[1], "enumerate"))
651 {
652 SafeArray<BSTR> aKeys;
653 CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
654
655 for (size_t i = 0;
656 i < aKeys.size();
657 ++i)
658 {
659 Bstr bstrKey(aKeys[i]);
660 Bstr bstrValue;
661 CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
662 bstrValue.asOutParam()));
663
664 RTPrintf("Key: %lS, Value: %lS\n", bstrKey.raw(), bstrValue.raw());
665 }
666 }
667 else
668 {
669 Bstr value;
670 CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
671 value.asOutParam()));
672 if (!value.isEmpty())
673 RTPrintf("Value: %lS\n", value.raw());
674 else
675 RTPrintf("No value set!\n");
676 }
677 }
678 else
679 {
680 ComPtr<IMachine> machine;
681 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
682 machine.asOutParam()));
683 if (machine)
684 {
685 /* enumeration? */
686 if (!strcmp(a->argv[1], "enumerate"))
687 {
688 SafeArray<BSTR> aKeys;
689 CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
690
691 for (size_t i = 0;
692 i < aKeys.size();
693 ++i)
694 {
695 Bstr bstrKey(aKeys[i]);
696 Bstr bstrValue;
697 CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
698 bstrValue.asOutParam()));
699
700 RTPrintf("Key: %lS, Value: %lS\n", bstrKey.raw(), bstrValue.raw());
701 }
702 }
703 else
704 {
705 Bstr value;
706 CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
707 value.asOutParam()));
708 if (!value.isEmpty())
709 RTPrintf("Value: %lS\n", value.raw());
710 else
711 RTPrintf("No value set!\n");
712 }
713 }
714 }
715 return SUCCEEDED(rc) ? 0 : 1;
716}
717
718int handleSetExtraData(HandlerArg *a)
719{
720 HRESULT rc = S_OK;
721
722 if (a->argc < 2)
723 return errorSyntax(USAGE_SETEXTRADATA, "Not enough parameters");
724
725 /* global data? */
726 if (!strcmp(a->argv[0], "global"))
727 {
728 /** @todo passing NULL is deprecated */
729 if (a->argc < 3)
730 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
731 NULL));
732 else if (a->argc == 3)
733 CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
734 Bstr(a->argv[2]).raw()));
735 else
736 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
737 }
738 else
739 {
740 ComPtr<IMachine> machine;
741 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
742 machine.asOutParam()));
743 if (machine)
744 {
745 /** @todo passing NULL is deprecated */
746 if (a->argc < 3)
747 CHECK_ERROR(machine, SetExtraData(Bstr(a->argv[1]).raw(),
748 NULL));
749 else if (a->argc == 3)
750 CHECK_ERROR(machine, SetExtraData(Bstr(a->argv[1]).raw(),
751 Bstr(a->argv[2]).raw()));
752 else
753 return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
754 }
755 }
756 return SUCCEEDED(rc) ? 0 : 1;
757}
758
759int handleSetProperty(HandlerArg *a)
760{
761 HRESULT rc;
762
763 /* there must be two arguments: property name and value */
764 if (a->argc != 2)
765 return errorSyntax(USAGE_SETPROPERTY, "Incorrect number of parameters");
766
767 ComPtr<ISystemProperties> systemProperties;
768 a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
769
770 if (!strcmp(a->argv[0], "machinefolder"))
771 {
772 /* reset to default? */
773 if (!strcmp(a->argv[1], "default"))
774 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
775 else
776 CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw()));
777 }
778 else if ( !strcmp(a->argv[0], "vrdeauthlibrary")
779 || !strcmp(a->argv[0], "vrdpauthlibrary"))
780 {
781 if (!strcmp(a->argv[0], "vrdpauthlibrary"))
782 RTStrmPrintf(g_pStdErr, "Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n");
783
784 /* reset to default? */
785 if (!strcmp(a->argv[1], "default"))
786 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
787 else
788 CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
789 }
790 else if (!strcmp(a->argv[0], "websrvauthlibrary"))
791 {
792 /* reset to default? */
793 if (!strcmp(a->argv[1], "default"))
794 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
795 else
796 CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
797 }
798 else if (!strcmp(a->argv[0], "vrdeextpack"))
799 {
800 /* disable? */
801 if (!strcmp(a->argv[1], "null"))
802 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
803 else
804 CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
805 }
806 else if (!strcmp(a->argv[0], "loghistorycount"))
807 {
808 uint32_t uVal;
809 int vrc;
810 vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
811 if (vrc != VINF_SUCCESS)
812 return errorArgument("Error parsing Log history count '%s'", a->argv[1]);
813 CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
814 }
815 else
816 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]);
817
818 return SUCCEEDED(rc) ? 0 : 1;
819}
820
821int handleSharedFolder(HandlerArg *a)
822{
823 HRESULT rc;
824
825 /* we need at least a command and target */
826 if (a->argc < 2)
827 return errorSyntax(USAGE_SHAREDFOLDER, "Not enough parameters");
828
829 ComPtr<IMachine> machine;
830 CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[1]).raw(),
831 machine.asOutParam()));
832 if (!machine)
833 return 1;
834
835 if (!strcmp(a->argv[0], "add"))
836 {
837 /* we need at least four more parameters */
838 if (a->argc < 5)
839 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Not enough parameters");
840
841 char *name = NULL;
842 char *hostpath = NULL;
843 bool fTransient = false;
844 bool fWritable = true;
845 bool fAutoMount = false;
846
847 for (int i = 2; i < a->argc; i++)
848 {
849 if ( !strcmp(a->argv[i], "--name")
850 || !strcmp(a->argv[i], "-name"))
851 {
852 if (a->argc <= i + 1 || !*a->argv[i+1])
853 return errorArgument("Missing argument to '%s'", a->argv[i]);
854 i++;
855 name = a->argv[i];
856 }
857 else if ( !strcmp(a->argv[i], "--hostpath")
858 || !strcmp(a->argv[i], "-hostpath"))
859 {
860 if (a->argc <= i + 1 || !*a->argv[i+1])
861 return errorArgument("Missing argument to '%s'", a->argv[i]);
862 i++;
863 hostpath = a->argv[i];
864 }
865 else if ( !strcmp(a->argv[i], "--readonly")
866 || !strcmp(a->argv[i], "-readonly"))
867 {
868 fWritable = false;
869 }
870 else if ( !strcmp(a->argv[i], "--transient")
871 || !strcmp(a->argv[i], "-transient"))
872 {
873 fTransient = true;
874 }
875 else if ( !strcmp(a->argv[i], "--automount")
876 || !strcmp(a->argv[i], "-automount"))
877 {
878 fAutoMount = true;
879 }
880 else
881 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
882 }
883
884 if (NULL != strstr(name, " "))
885 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "No spaces allowed in parameter '-name'!");
886
887 /* required arguments */
888 if (!name || !hostpath)
889 {
890 return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Parameters --name and --hostpath are required");
891 }
892
893 if (fTransient)
894 {
895 ComPtr <IConsole> console;
896
897 /* open an existing session for the VM */
898 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);
899 /* get the session machine */
900 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(machine.asOutParam()), 1);
901 /* get the session console */
902 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), 1);
903
904 CHECK_ERROR(console, CreateSharedFolder(Bstr(name).raw(),
905 Bstr(hostpath).raw(),
906 fWritable, fAutoMount));
907 if (console)
908 a->session->UnlockMachine();
909 }
910 else
911 {
912 /* open a session for the VM */
913 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), 1);
914
915 /* get the mutable session machine */
916 a->session->COMGETTER(Machine)(machine.asOutParam());
917
918 CHECK_ERROR(machine, CreateSharedFolder(Bstr(name).raw(),
919 Bstr(hostpath).raw(),
920 fWritable, fAutoMount));
921 if (SUCCEEDED(rc))
922 CHECK_ERROR(machine, SaveSettings());
923
924 a->session->UnlockMachine();
925 }
926 }
927 else if (!strcmp(a->argv[0], "remove"))
928 {
929 /* we need at least two more parameters */
930 if (a->argc < 3)
931 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Not enough parameters");
932
933 char *name = NULL;
934 bool fTransient = false;
935
936 for (int i = 2; i < a->argc; i++)
937 {
938 if ( !strcmp(a->argv[i], "--name")
939 || !strcmp(a->argv[i], "-name"))
940 {
941 if (a->argc <= i + 1 || !*a->argv[i+1])
942 return errorArgument("Missing argument to '%s'", a->argv[i]);
943 i++;
944 name = a->argv[i];
945 }
946 else if ( !strcmp(a->argv[i], "--transient")
947 || !strcmp(a->argv[i], "-transient"))
948 {
949 fTransient = true;
950 }
951 else
952 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
953 }
954
955 /* required arguments */
956 if (!name)
957 return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Parameter --name is required");
958
959 if (fTransient)
960 {
961 ComPtr <IConsole> console;
962
963 /* open an existing session for the VM */
964 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);
965 /* get the session machine */
966 CHECK_ERROR_RET(a->session, COMGETTER(Machine)(machine.asOutParam()), 1);
967 /* get the session console */
968 CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), 1);
969
970 CHECK_ERROR(console, RemoveSharedFolder(Bstr(name).raw()));
971
972 if (console)
973 a->session->UnlockMachine();
974 }
975 else
976 {
977 /* open a session for the VM */
978 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), 1);
979
980 /* get the mutable session machine */
981 a->session->COMGETTER(Machine)(machine.asOutParam());
982
983 CHECK_ERROR(machine, RemoveSharedFolder(Bstr(name).raw()));
984
985 /* commit and close the session */
986 CHECK_ERROR(machine, SaveSettings());
987 a->session->UnlockMachine();
988 }
989 }
990 else
991 return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
992
993 return 0;
994}
995
996int handleExtPack(HandlerArg *a)
997{
998 if (a->argc < 1)
999 return errorSyntax(USAGE_EXTPACK, "Incorrect number of parameters");
1000
1001 ComObjPtr<IExtPackManager> ptrExtPackMgr;
1002 CHECK_ERROR2_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
1003
1004 RTGETOPTSTATE GetState;
1005 RTGETOPTUNION ValueUnion;
1006 int ch;
1007 HRESULT hrc = S_OK;
1008
1009 if (!strcmp(a->argv[0], "install"))
1010 {
1011 const char *pszName = NULL;
1012 bool fReplace = false;
1013
1014 static const RTGETOPTDEF s_aInstallOptions[] =
1015 {
1016 { "--replace", 'r', RTGETOPT_REQ_NOTHING },
1017 };
1018
1019 RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
1020 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1021 {
1022 switch (ch)
1023 {
1024 case 'f':
1025 fReplace = true;
1026 break;
1027
1028 case VINF_GETOPT_NOT_OPTION:
1029 if (pszName)
1030 return errorSyntax(USAGE_EXTPACK, "Too many extension pack names given to \"extpack uninstall\"");
1031 pszName = ValueUnion.psz;
1032 break;
1033
1034 default:
1035 return errorGetOpt(USAGE_EXTPACK, ch, &ValueUnion);
1036 }
1037 }
1038 if (!pszName)
1039 return errorSyntax(USAGE_EXTPACK, "No extension pack name was given to \"extpack install\"");
1040
1041 char szPath[RTPATH_MAX];
1042 int vrc = RTPathAbs(a->argv[1], szPath, sizeof(szPath));
1043 if (RT_FAILURE(vrc))
1044 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", a->argv[1], vrc);
1045
1046 Bstr bstrTarball(szPath);
1047 Bstr bstrName;
1048 ComPtr<IExtPackFile> ptrExtPackFile;
1049 CHECK_ERROR2_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
1050 CHECK_ERROR2_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
1051 ComPtr<IProgress> ptrProgress;
1052 CHECK_ERROR2_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1053 hrc = showProgress(ptrProgress);
1054 if (FAILED(hrc))
1055 {
1056 com::ProgressErrorInfo ErrInfo(ptrProgress);
1057 if (ErrInfo.isBasicAvailable())
1058 RTMsgError("Failed to install \"%s\": %lS", szPath, ErrInfo.getText().raw());
1059 else
1060 RTMsgError("Failed to install \"%s\": No error message available!", szPath);
1061 return RTEXITCODE_FAILURE;
1062 }
1063 RTPrintf("Successfully installed \"%lS\".\n", bstrName.raw());
1064 }
1065 else if (!strcmp(a->argv[0], "uninstall"))
1066 {
1067 const char *pszName = NULL;
1068 bool fForced = false;
1069
1070 static const RTGETOPTDEF s_aUninstallOptions[] =
1071 {
1072 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1073 };
1074
1075 RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
1076 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1077 {
1078 switch (ch)
1079 {
1080 case 'f':
1081 fForced = true;
1082 break;
1083
1084 case VINF_GETOPT_NOT_OPTION:
1085 if (pszName)
1086 return errorSyntax(USAGE_EXTPACK, "Too many extension pack names given to \"extpack uninstall\"");
1087 pszName = ValueUnion.psz;
1088 break;
1089
1090 default:
1091 return errorGetOpt(USAGE_EXTPACK, ch, &ValueUnion);
1092 }
1093 }
1094 if (!pszName)
1095 return errorSyntax(USAGE_EXTPACK, "No extension pack name was given to \"extpack uninstall\"");
1096
1097 Bstr bstrName(pszName);
1098 ComPtr<IProgress> ptrProgress;
1099 CHECK_ERROR2_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
1100 hrc = showProgress(ptrProgress);
1101 if (FAILED(hrc))
1102 {
1103 com::ProgressErrorInfo ErrInfo(ptrProgress);
1104 if (ErrInfo.isBasicAvailable())
1105 RTMsgError("Failed to uninstall \"%s\": %lS", pszName, ErrInfo.getText().raw());
1106 else
1107 RTMsgError("Failed to uninstall \"%s\": No error message available!", pszName);
1108 return RTEXITCODE_FAILURE;
1109 }
1110 RTPrintf("Successfully uninstalled \"%s\".\n", pszName);
1111 }
1112 else if (!strcmp(a->argv[0], "cleanup"))
1113 {
1114 if (a->argc > 1)
1115 return errorSyntax(USAGE_EXTPACK, "Too many parameters given to \"extpack cleanup\"");
1116
1117 CHECK_ERROR2_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
1118 RTPrintf("Successfully performed extension pack cleanup\n");
1119 }
1120 else
1121 return errorSyntax(USAGE_EXTPACK, "Unknown command \"%s\"", a->argv[0]);
1122
1123 return RTEXITCODE_SUCCESS;
1124}
1125
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