VirtualBox

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

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

Main; VBoxManage: initial machine clone support

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

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