VirtualBox

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

Last change on this file since 42129 was 42129, checked in by vboxsync, 13 years ago

Main/VirtualBox+Machine: add support for VM groups (incomplete, saving of settings is not implemented), remaining code adjusted accordingly, use better parameter checking macros, make XSLT generators process setter attributes using safearrays correctly, various cleanups and warning fixes
Frontends/VBoxManage: first traces of groups support

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