VirtualBox

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

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

Frontends/VBoxManage: allow starting several VMs with startvm

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