VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageImport.cpp@ 18270

Last change on this file since 18270 was 18223, checked in by vboxsync, 16 years ago

OVF: implement EULAs in VBoxManage.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.5 KB
Line 
1/* $Id: VBoxManageImport.cpp 18223 2009-03-24 19:39:47Z vboxsync $ */
2/** @file
3 * VBoxManage - The appliance-related commands.
4 */
5
6/*
7 * Copyright (C) 2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#ifndef VBOX_ONLY_DOCS
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#ifndef VBOX_ONLY_DOCS
28#include <VBox/com/com.h>
29#include <VBox/com/string.h>
30#include <VBox/com/Guid.h>
31#include <VBox/com/array.h>
32#include <VBox/com/ErrorInfo.h>
33#include <VBox/com/errorprint2.h>
34#include <VBox/com/EventQueue.h>
35
36#include <VBox/com/VirtualBox.h>
37
38#include <list>
39#include <map>
40#endif /* !VBOX_ONLY_DOCS */
41
42#include <iprt/stream.h>
43#include <iprt/getopt.h>
44#include <iprt/ctype.h>
45
46#include <VBox/log.h>
47
48#include "VBoxManage.h"
49using namespace com;
50
51
52// funcs
53///////////////////////////////////////////////////////////////////////////////
54
55typedef std::map<Utf8Str, Utf8Str> ArgsMap; // pairs of strings like "-vmname" => "newvmname"
56typedef std::map<uint32_t, ArgsMap> ArgsMapsMap; // map of maps, one for each virtual system, sorted by index
57
58typedef std::map<uint32_t, bool> IgnoresMap; // pairs of numeric description entry indices
59typedef std::map<uint32_t, IgnoresMap> IgnoresMapsMap; // map of maps, one for each virtual system, sorted by index
60
61static bool findArgValue(Utf8Str &strOut,
62 ArgsMap *pmapArgs,
63 const Utf8Str &strKey)
64{
65 if (pmapArgs)
66 {
67 ArgsMap::iterator it;
68 it = pmapArgs->find(strKey);
69 if (it != pmapArgs->end())
70 {
71 strOut = it->second;
72 pmapArgs->erase(it);
73 return true;
74 }
75 }
76
77 return false;
78}
79
80int handleImportAppliance(HandlerArg *a)
81{
82 HRESULT rc = S_OK;
83
84 Utf8Str strOvfFilename;
85 bool fExecute = true; // if true, then we actually do the import
86
87 uint32_t ulCurVsys = (uint32_t)-1;
88
89 // for each -vsys X command, maintain a map of command line items
90 // (we'll parse them later after interpreting the OVF, when we can
91 // actually check whether they make sense semantically)
92 ArgsMapsMap mapArgsMapsPerVsys;
93 IgnoresMapsMap mapIgnoresMapsPerVsys;
94
95 for (int i = 0;
96 i < a->argc;
97 ++i)
98 {
99 bool fIsIgnore = false;
100 Utf8Str strThisArg(a->argv[i]);
101 if ( (strThisArg == "--dry-run")
102 || (strThisArg == "-dry-run")
103 || (strThisArg == "-n")
104 )
105 fExecute = false;
106 else if (strThisArg == "-vsys")
107 {
108 if (++i < a->argc)
109 {
110 uint32_t ulVsys;
111 if (VINF_SUCCESS != (rc = Utf8Str(a->argv[i]).toInt(ulVsys))) // don't use SUCCESS() macro, fail even on warnings
112 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Argument to -vsys option must be a non-negative number.");
113
114 ulCurVsys = ulVsys;
115 }
116 else
117 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Missing argument to -vsys option.");
118 }
119 else if ( (strThisArg == "-ostype")
120 || (strThisArg == "-vmname")
121 || (strThisArg == "-memory")
122 || (strThisArg == "-eula")
123 || (fIsIgnore = (strThisArg == "-ignore"))
124 || (strThisArg.substr(0, 5) == "-type")
125 || (strThisArg.substr(0, 11) == "-controller")
126 )
127 {
128 if (ulCurVsys == (uint32_t)-1)
129 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding -vsys argument.", strThisArg.c_str());
130
131 if (++i < a->argc)
132 if (fIsIgnore)
133 {
134 uint32_t ulItem;
135 if (VINF_SUCCESS != Utf8Str(a->argv[i]).toInt(ulItem))
136 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Argument to -vsys option must be a non-negative number.");
137
138 mapIgnoresMapsPerVsys[ulCurVsys][ulItem] = true;
139 }
140 else
141 {
142 // store both this arg and the next one in the strings map for later parsing
143 mapArgsMapsPerVsys[ulCurVsys][strThisArg] = Utf8Str(a->argv[i]);
144 }
145 else
146 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Missing argument to \"%s\" option.", strThisArg.c_str());
147 }
148 else if (strThisArg[0] == '-')
149 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Unknown option \"%s\".", strThisArg.c_str());
150 else if (!strOvfFilename)
151 strOvfFilename = strThisArg;
152 else
153 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Too many arguments for \"import\" command.");
154 }
155
156 if (!strOvfFilename)
157 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Not enough arguments for \"import\" command.");
158
159 do
160 {
161 Bstr bstrOvfFilename(strOvfFilename);
162 ComPtr<IAppliance> pAppliance;
163 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
164
165 CHECK_ERROR_BREAK(pAppliance, Read(bstrOvfFilename));
166
167 // call interpret(); this can yield both warnings and errors, so we need
168 // to tinker with the error info a bit
169 RTPrintf("Interpreting %s...\n", strOvfFilename.c_str());
170 rc = pAppliance->Interpret();
171 com::ErrorInfo info0(pAppliance);
172
173 com::SafeArray<BSTR> aWarnings;
174 if (SUCCEEDED(pAppliance->GetWarnings(ComSafeArrayAsOutParam(aWarnings))))
175 {
176 unsigned cWarnings = aWarnings.size();
177 for (unsigned i = 0; i < cWarnings; ++i)
178 {
179 Bstr bstrWarning(aWarnings[i]);
180 RTPrintf("WARNING: %ls.\n", bstrWarning.raw());
181 }
182 }
183
184 if (FAILED(rc)) // during interpret, after printing warnings
185 {
186 com::GluePrintErrorInfo(info0);
187 com::GluePrintErrorContext("Interpret", __FILE__, __LINE__);
188 break;
189 }
190
191 RTPrintf("OK.\n");
192
193 // fetch all disks
194 com::SafeArray<BSTR> retDisks;
195 CHECK_ERROR_BREAK(pAppliance,
196 COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
197 if (retDisks.size() > 0)
198 {
199 RTPrintf("Disks:");
200 for (unsigned i = 0; i < retDisks.size(); i++)
201 RTPrintf(" %ls", retDisks[i]);
202 RTPrintf("\n");
203 }
204
205 // fetch virtual system descriptions
206 com::SafeIfaceArray<IVirtualSystemDescription> aVirtualSystemDescriptions;
207 CHECK_ERROR_BREAK(pAppliance,
208 COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
209
210 uint32_t cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
211
212 // match command line arguments with virtual system descriptions;
213 // this is only to sort out invalid indices at this time
214 ArgsMapsMap::const_iterator it;
215 for (it = mapArgsMapsPerVsys.begin();
216 it != mapArgsMapsPerVsys.end();
217 ++it)
218 {
219 uint32_t ulVsys = it->first;
220 if (ulVsys >= cVirtualSystemDescriptions)
221 return errorSyntax(USAGE_IMPORTAPPLIANCE,
222 "Invalid index %RI32 with -vsys option; the OVF contains only %RI32 virtual system(s).",
223 ulVsys, cVirtualSystemDescriptions);
224 }
225
226 uint32_t cLicensesInTheWay = 0;
227
228 // dump virtual system descriptions and match command-line arguments
229 if (cVirtualSystemDescriptions > 0)
230 {
231 for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
232 {
233 com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
234 com::SafeArray<BSTR> aRefs;
235 com::SafeArray<BSTR> aOvfValues;
236 com::SafeArray<BSTR> aVboxValues;
237 com::SafeArray<BSTR> aExtraConfigValues;
238 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
239 GetDescription(ComSafeArrayAsOutParam(retTypes),
240 ComSafeArrayAsOutParam(aRefs),
241 ComSafeArrayAsOutParam(aOvfValues),
242 ComSafeArrayAsOutParam(aVboxValues),
243 ComSafeArrayAsOutParam(aExtraConfigValues)));
244
245 RTPrintf("Virtual system %i:\n", i);
246
247 // look up the corresponding command line options, if any
248 ArgsMap *pmapArgs = NULL;
249 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
250 if (itm != mapArgsMapsPerVsys.end())
251 pmapArgs = &itm->second;
252
253 // this collects the final values for setFinalValues()
254 com::SafeArray<BOOL> aEnabled(retTypes.size());
255 com::SafeArray<BSTR> aFinalValues(retTypes.size());
256
257 for (unsigned a = 0; a < retTypes.size(); ++a)
258 {
259 VirtualSystemDescriptionType_T t = retTypes[a];
260
261 Utf8Str strOverride;
262
263 Bstr bstrFinalValue = aVboxValues[a];
264
265 bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a];
266
267 aEnabled[a] = true;
268
269 switch (t)
270 {
271 case VirtualSystemDescriptionType_Name:
272 if (findArgValue(strOverride, pmapArgs, "-vmname"))
273 {
274 bstrFinalValue = strOverride;
275 RTPrintf("%2d: VM name specified with -vmname: \"%ls\"\n",
276 a, bstrFinalValue.raw());
277 }
278 else
279 RTPrintf("%2d: Suggested VM name \"%ls\""
280 "\n (change with \"-vsys %d -vmname <name>\")\n",
281 a, bstrFinalValue.raw(), i);
282 break;
283
284 case VirtualSystemDescriptionType_OS:
285 if (findArgValue(strOverride, pmapArgs, "-ostype"))
286 {
287 bstrFinalValue = strOverride;
288 RTPrintf("%2d: OS type specified with -ostype: \"%ls\"\n",
289 a, bstrFinalValue.raw());
290 }
291 else
292 RTPrintf("%2d: Suggested OS type: \"%ls\""
293 "\n (change with \"-vsys %d -ostype <type>\"; use \"list ostypes\" to list all)\n",
294 a, bstrFinalValue.raw(), i);
295 break;
296
297 case VirtualSystemDescriptionType_License:
298 ++cLicensesInTheWay;
299 if (findArgValue(strOverride, pmapArgs, "-eula"))
300 {
301 if (strOverride == "show")
302 {
303 RTPrintf("%2d: End-user license agreement"
304 "\n (accept with \"-vsys %d -eula accept\"):"
305 "\n\n%ls\n\n",
306 a, i, bstrFinalValue.raw());
307 }
308 else if (strOverride == "accept")
309 {
310 RTPrintf("%2d: End-user license agreement (accepted)\n",
311 a);
312 --cLicensesInTheWay;
313 }
314 else
315 return errorSyntax(USAGE_IMPORTAPPLIANCE,
316 "Argument to -eula must be either \"show\" or \"accept\".");
317 }
318 else
319 RTPrintf("%2d: End-user license agreement"
320 "\n (display with \"-vsys %d -eula show\";"
321 "\n accept with \"-vsys %d -eula accept\")\n",
322 a, i, i);
323 break;
324
325 case VirtualSystemDescriptionType_CPU:
326 RTPrintf("%2d: Number of CPUs (ignored): %ls\n",
327 a, aVboxValues[a]);
328 break;
329
330 case VirtualSystemDescriptionType_Memory:
331 {
332 if (findArgValue(strOverride, pmapArgs, "-memory"))
333 {
334 uint32_t ulMemMB;
335 if (VINF_SUCCESS == strOverride.toInt(ulMemMB))
336 {
337 bstrFinalValue = strOverride;
338 RTPrintf("%2d: Guest memory specified with -memory: %ls MB\n",
339 a, bstrFinalValue.raw());
340 }
341 else
342 return errorSyntax(USAGE_IMPORTAPPLIANCE,
343 "Argument to -memory option must be a non-negative number.");
344 }
345 else
346 RTPrintf("%2d: Guest memory: %ls MB\n (change with \"-vsys %d -memory <MB>\")\n",
347 a, bstrFinalValue.raw(), i);
348 }
349 break;
350
351 case VirtualSystemDescriptionType_HardDiskControllerIDE:
352 if (fIgnoreThis)
353 {
354 RTPrintf("%2d: IDE controller, type %ls -- disabled\n",
355 a,
356 aVboxValues[a]);
357 aEnabled[a] = false;
358 }
359 else
360 RTPrintf("%2d: IDE controller, type %ls"
361 "\n (disable with \"-vsys %d -ignore %d\")\n",
362 a,
363 aVboxValues[a],
364 i, a);
365 break;
366
367 case VirtualSystemDescriptionType_HardDiskControllerSATA:
368 if (fIgnoreThis)
369 {
370 RTPrintf("%2d: SATA controller, type %ls -- disabled\n",
371 a,
372 aVboxValues[a]);
373 aEnabled[a] = false;
374 }
375 else
376 RTPrintf("%2d: SATA controller, type %ls"
377 "\n (disable with \"-vsys %d -ignore %d\")\n",
378 a,
379 aVboxValues[a],
380 i, a);
381 break;
382
383 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
384 if (fIgnoreThis)
385 {
386 RTPrintf("%2d: SCSI controller, type %ls -- disabled\n",
387 a,
388 aVboxValues[a]);
389 aEnabled[a] = false;
390 }
391 else
392 {
393 Utf8StrFmt strTypeArg("-type%RI16", a);
394 if (findArgValue(strOverride, pmapArgs, strTypeArg))
395 {
396 bstrFinalValue = strOverride;
397 RTPrintf("%2d: SCSI controller, type set with -type%d: %ls\n",
398 a,
399 a,
400 bstrFinalValue.raw());
401 }
402 else
403 RTPrintf("%2d: SCSI controller, type %ls"
404 "\n (change with \"-vsys %d -type%d {BusLogic|LsiLogic}\";"
405 "\n disable with \"-vsys %d -ignore %d\")\n",
406 a,
407 aVboxValues[a],
408 i, a, i, a);
409 }
410 break;
411
412 case VirtualSystemDescriptionType_HardDiskImage:
413 if (fIgnoreThis)
414 {
415 RTPrintf("%2d: Hard disk image: source image=%ls -- disabled\n",
416 a,
417 aOvfValues[a]);
418 aEnabled[a] = false;
419 }
420 else
421 {
422 Utf8StrFmt strTypeArg("-controller%RI16", a);
423 if (findArgValue(strOverride, pmapArgs, strTypeArg))
424 {
425 // strOverride now has the controller index as a number, but we
426 // need a "controller=X" format string
427 strOverride = Utf8StrFmt("controller=%s", strOverride.c_str());
428 Bstr bstrExtraConfigValue = strOverride;
429 bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]);
430 RTPrintf("%2d: Hard disk image: source image=%ls, target path=%ls, %ls\n",
431 a,
432 aOvfValues[a],
433 aVboxValues[a],
434 aExtraConfigValues[a]);
435 }
436 else
437 RTPrintf("%2d: Hard disk image: source image=%ls, target path=%ls, %ls"
438 "\n (change controller with \"-vsys %d -controller%d <id>\";"
439 "\n disable with \"-vsys %d -ignore %d\")\n",
440 a,
441 aOvfValues[a],
442 aVboxValues[a],
443 aExtraConfigValues[a],
444 i, a, i, a);
445 }
446 break;
447
448 case VirtualSystemDescriptionType_CDROM:
449 if (fIgnoreThis)
450 {
451 RTPrintf("%2d: CD-ROM -- disabled\n",
452 a);
453 aEnabled[a] = false;
454 }
455 else
456 RTPrintf("%2d: CD-ROM"
457 "\n (disable with \"-vsys %d -ignore %d\")\n",
458 a, i, a);
459 break;
460
461 case VirtualSystemDescriptionType_Floppy:
462 if (fIgnoreThis)
463 {
464 RTPrintf("%2d: Floppy -- disabled\n",
465 a);
466 aEnabled[a] = false;
467 }
468 else
469 RTPrintf("%2d: Floppy"
470 "\n (disable with \"-vsys %d -ignore %d\")\n",
471 a, i, a);
472 break;
473
474 case VirtualSystemDescriptionType_NetworkAdapter:
475 RTPrintf("%2d: Network adapter: orig %ls, config %ls, extra %ls\n", // @todo implement once we have a plan for the back-end
476 a,
477 aOvfValues[a],
478 aVboxValues[a],
479 aExtraConfigValues[a]);
480 break;
481
482 case VirtualSystemDescriptionType_USBController:
483 if (fIgnoreThis)
484 {
485 RTPrintf("%2d: USB controller -- disabled\n",
486 a);
487 aEnabled[a] = false;
488 }
489 else
490 RTPrintf("%2d: USB controller"
491 "\n (disable with \"-vsys %d -ignore %d\")\n",
492 a, i, a);
493 break;
494
495 case VirtualSystemDescriptionType_SoundCard:
496 if (fIgnoreThis)
497 {
498 RTPrintf("%2d: Sound card \"%ls\" -- disabled\n",
499 a,
500 aOvfValues[a]);
501 aEnabled[a] = false;
502 }
503 else
504 RTPrintf("%2d: Sound card (appliance expects \"%ls\", can change on import)"
505 "\n (disable with \"-vsys %d -ignore %d\")\n",
506 a,
507 aOvfValues[a],
508 i,
509 a);
510 break;
511 }
512
513 bstrFinalValue.detachTo(&aFinalValues[a]);
514 }
515
516 if (fExecute)
517 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
518 SetFinalValues(ComSafeArrayAsInParam(aEnabled),
519 ComSafeArrayAsInParam(aFinalValues),
520 ComSafeArrayAsInParam(aExtraConfigValues)));
521
522 } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
523
524 if (cLicensesInTheWay == 1)
525 RTPrintf("ERROR: Cannot import until the license agreement listed above is accepted.\n");
526 else if (cLicensesInTheWay > 1)
527 RTPrintf("ERROR: Cannot import until the %c license agreements listed above are accepted.\n", cLicensesInTheWay);
528
529 if (!cLicensesInTheWay && fExecute)
530 {
531 // go!
532 ComPtr<IProgress> progress;
533 CHECK_ERROR_BREAK(pAppliance,
534 ImportMachines(progress.asOutParam()));
535
536 showProgress(progress);
537
538 if (SUCCEEDED(rc))
539 progress->COMGETTER(ResultCode)(&rc);
540
541 if (FAILED(rc))
542 {
543 com::ProgressErrorInfo info(progress);
544 com::GluePrintErrorInfo(info);
545 com::GluePrintErrorContext("ImportAppliance", __FILE__, __LINE__);
546 }
547 else
548 RTPrintf("Successfully imported the appliance.\n");
549 }
550 } // end if (aVirtualSystemDescriptions.size() > 0)
551 } while (0);
552
553 return SUCCEEDED(rc) ? 0 : 1;
554}
555
556static const RTGETOPTDEF g_aExportOptions[]
557 = {
558 { "--output", 'o', RTGETOPT_REQ_STRING },
559 };
560
561int handleExportAppliance(HandlerArg *a)
562{
563 HRESULT rc = S_OK;
564
565 Utf8Str strOutputFile;
566 std::list< ComPtr<IMachine> > llMachines;
567
568 do
569 {
570 int c;
571
572 RTGETOPTUNION ValueUnion;
573 RTGETOPTSTATE GetState;
574 // start at 0 because main() has hacked both the argc and argv given to us
575 RTGetOptInit(&GetState, a->argc, a->argv, g_aExportOptions,
576 RT_ELEMENTS(g_aExportOptions), 0, 0 /* fFlags */);
577 while ((c = RTGetOpt(&GetState, &ValueUnion)))
578 {
579 switch (c)
580 {
581 case 'o': // --output
582 if (strOutputFile.length())
583 return errorSyntax(USAGE_EXPORTAPPLIANCE, "You can only specify --output once.");
584 else
585 strOutputFile = ValueUnion.psz;
586 break;
587
588 case VINF_GETOPT_NOT_OPTION:
589 {
590 Utf8Str strMachine(ValueUnion.psz);
591 // must be machine: try UUID or name
592 ComPtr<IMachine> machine;
593 /* assume it's a UUID */
594 rc = a->virtualBox->GetMachine(Guid(strMachine), machine.asOutParam());
595 if (FAILED(rc) || !machine)
596 {
597 /* must be a name */
598 CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine), machine.asOutParam()));
599 }
600
601 if (machine)
602 llMachines.push_back(machine);
603 }
604 break;
605
606 default:
607 if (c > 0)
608 {
609 if (RT_C_IS_GRAPH(c))
610 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: -%c", c);
611 else
612 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: %i", c);
613 }
614 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
615 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unknown option: %s", ValueUnion.psz);
616 else if (ValueUnion.pDef)
617 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
618 else
619 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%Rrs", c);
620 }
621
622 if (FAILED(rc))
623 break;
624 }
625
626 if (FAILED(rc))
627 break;
628
629 if (llMachines.size() == 0)
630 return errorSyntax(USAGE_EXPORTAPPLIANCE, "At least one machine must be specified with the export command.");
631 if (!strOutputFile.length())
632 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Missing --output argument with export command.");
633
634 ComPtr<IAppliance> pAppliance;
635 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
636
637 std::list< ComPtr<IMachine> >::iterator itM;
638 for (itM = llMachines.begin();
639 itM != llMachines.end();
640 ++itM)
641 {
642 ComPtr<IMachine> pMachine = *itM;
643 ComPtr<IVirtualSystemDescription> pVSD;
644 CHECK_ERROR_BREAK(pMachine, Export(pAppliance, pVSD.asOutParam()));
645 }
646
647 if (FAILED(rc))
648 break;
649
650 ComPtr<IProgress> progress;
651 CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOutputFile), progress.asOutParam()));
652
653 showProgress(progress);
654
655 if (SUCCEEDED(rc))
656 progress->COMGETTER(ResultCode)(&rc);
657
658 if (FAILED(rc))
659 {
660 com::ProgressErrorInfo info(progress);
661 com::GluePrintErrorInfo(info);
662 com::GluePrintErrorContext("Write", __FILE__, __LINE__);
663 }
664 else
665 RTPrintf("Successfully exported %d machine(s).\n", llMachines.size());
666
667 } while (0);
668
669 return SUCCEEDED(rc) ? 0 : 1;
670}
671
672#endif /* !VBOX_ONLY_DOCS */
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