VirtualBox

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

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

FE/CLI: fix default uuid of cloned VM's

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