VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp@ 67816

Last change on this file since 67816 was 67208, checked in by vboxsync, 8 years ago

Main,VBoxManage: More OPC hacking. Seems to be working now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.2 KB
Line 
1/* $Id: VBoxManageAppliance.cpp 67208 2017-06-01 13:32:35Z vboxsync $ */
2/** @file
3 * VBoxManage - The appliance-related commands.
4 */
5
6/*
7 * Copyright (C) 2009-2016 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#ifndef VBOX_ONLY_DOCS
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#ifndef VBOX_ONLY_DOCS
25#include <VBox/com/com.h>
26#include <VBox/com/string.h>
27#include <VBox/com/Guid.h>
28#include <VBox/com/array.h>
29#include <VBox/com/ErrorInfo.h>
30#include <VBox/com/errorprint.h>
31#include <VBox/com/VirtualBox.h>
32
33#include <list>
34#include <map>
35#endif /* !VBOX_ONLY_DOCS */
36
37#include <iprt/stream.h>
38#include <iprt/getopt.h>
39#include <iprt/ctype.h>
40#include <iprt/path.h>
41#include <iprt/file.h>
42
43#include <VBox/log.h>
44#include <VBox/param.h>
45
46#include "VBoxManage.h"
47using namespace com;
48
49
50// funcs
51///////////////////////////////////////////////////////////////////////////////
52
53typedef std::map<Utf8Str, Utf8Str> ArgsMap; // pairs of strings like "vmname" => "newvmname"
54typedef std::map<uint32_t, ArgsMap> ArgsMapsMap; // map of maps, one for each virtual system, sorted by index
55
56typedef std::map<uint32_t, bool> IgnoresMap; // pairs of numeric description entry indices
57typedef std::map<uint32_t, IgnoresMap> IgnoresMapsMap; // map of maps, one for each virtual system, sorted by index
58
59static bool findArgValue(Utf8Str &strOut,
60 ArgsMap *pmapArgs,
61 const Utf8Str &strKey)
62{
63 if (pmapArgs)
64 {
65 ArgsMap::iterator it;
66 it = pmapArgs->find(strKey);
67 if (it != pmapArgs->end())
68 {
69 strOut = it->second;
70 pmapArgs->erase(it);
71 return true;
72 }
73 }
74
75 return false;
76}
77
78static int parseImportOptions(const char *psz, com::SafeArray<ImportOptions_T> *options)
79{
80 int rc = VINF_SUCCESS;
81 while (psz && *psz && RT_SUCCESS(rc))
82 {
83 size_t len;
84 const char *pszComma = strchr(psz, ',');
85 if (pszComma)
86 len = pszComma - psz;
87 else
88 len = strlen(psz);
89 if (len > 0)
90 {
91 if (!RTStrNICmp(psz, "KeepAllMACs", len))
92 options->push_back(ImportOptions_KeepAllMACs);
93 else if (!RTStrNICmp(psz, "KeepNATMACs", len))
94 options->push_back(ImportOptions_KeepNATMACs);
95 else if (!RTStrNICmp(psz, "ImportToVDI", len))
96 options->push_back(ImportOptions_ImportToVDI);
97 else
98 rc = VERR_PARSE_ERROR;
99 }
100 if (pszComma)
101 psz += len + 1;
102 else
103 psz += len;
104 }
105
106 return rc;
107}
108
109static const RTGETOPTDEF g_aImportApplianceOptions[] =
110{
111 { "--dry-run", 'n', RTGETOPT_REQ_NOTHING },
112 { "-dry-run", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
113 { "--dryrun", 'n', RTGETOPT_REQ_NOTHING },
114 { "-dryrun", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
115 { "--detailed-progress", 'P', RTGETOPT_REQ_NOTHING },
116 { "-detailed-progress", 'P', RTGETOPT_REQ_NOTHING }, // deprecated
117 { "--vsys", 's', RTGETOPT_REQ_UINT32 },
118 { "-vsys", 's', RTGETOPT_REQ_UINT32 }, // deprecated
119 { "--ostype", 'o', RTGETOPT_REQ_STRING },
120 { "-ostype", 'o', RTGETOPT_REQ_STRING }, // deprecated
121 { "--vmname", 'V', RTGETOPT_REQ_STRING },
122 { "-vmname", 'V', RTGETOPT_REQ_STRING }, // deprecated
123 { "--memory", 'm', RTGETOPT_REQ_STRING },
124 { "-memory", 'm', RTGETOPT_REQ_STRING }, // deprecated
125 { "--cpus", 'c', RTGETOPT_REQ_STRING },
126 { "--description", 'd', RTGETOPT_REQ_STRING },
127 { "--eula", 'L', RTGETOPT_REQ_STRING },
128 { "-eula", 'L', RTGETOPT_REQ_STRING }, // deprecated
129 { "--unit", 'u', RTGETOPT_REQ_UINT32 },
130 { "-unit", 'u', RTGETOPT_REQ_UINT32 }, // deprecated
131 { "--ignore", 'x', RTGETOPT_REQ_NOTHING },
132 { "-ignore", 'x', RTGETOPT_REQ_NOTHING }, // deprecated
133 { "--scsitype", 'T', RTGETOPT_REQ_UINT32 },
134 { "-scsitype", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
135 { "--type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
136 { "-type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
137#if 0 /* Changing the controller is fully valid, but the current design on how
138 the params are evaluated here doesn't allow two parameter for one
139 unit. The target disk path is more important. I leave it for future
140 improvments. */
141 { "--controller", 'C', RTGETOPT_REQ_STRING },
142#endif
143 { "--disk", 'D', RTGETOPT_REQ_STRING },
144 { "--options", 'O', RTGETOPT_REQ_STRING },
145};
146
147RTEXITCODE handleImportAppliance(HandlerArg *arg)
148{
149 HRESULT rc = S_OK;
150
151 Utf8Str strOvfFilename;
152 bool fExecute = true; // if true, then we actually do the import
153 com::SafeArray<ImportOptions_T> options;
154 uint32_t ulCurVsys = (uint32_t)-1;
155 uint32_t ulCurUnit = (uint32_t)-1;
156 // for each --vsys X command, maintain a map of command line items
157 // (we'll parse them later after interpreting the OVF, when we can
158 // actually check whether they make sense semantically)
159 ArgsMapsMap mapArgsMapsPerVsys;
160 IgnoresMapsMap mapIgnoresMapsPerVsys;
161
162 int c;
163 RTGETOPTUNION ValueUnion;
164 RTGETOPTSTATE GetState;
165 // start at 0 because main() has hacked both the argc and argv given to us
166 RTGetOptInit(&GetState, arg->argc, arg->argv, g_aImportApplianceOptions, RT_ELEMENTS(g_aImportApplianceOptions),
167 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
168 while ((c = RTGetOpt(&GetState, &ValueUnion)))
169 {
170 switch (c)
171 {
172 case 'n': // --dry-run
173 fExecute = false;
174 break;
175
176 case 'P': // --detailed-progress
177 g_fDetailedProgress = true;
178 break;
179
180 case 's': // --vsys
181 ulCurVsys = ValueUnion.u32;
182 ulCurUnit = (uint32_t)-1;
183 break;
184
185 case 'o': // --ostype
186 if (ulCurVsys == (uint32_t)-1)
187 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
188 mapArgsMapsPerVsys[ulCurVsys]["ostype"] = ValueUnion.psz;
189 break;
190
191 case 'V': // --vmname
192 if (ulCurVsys == (uint32_t)-1)
193 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
194 mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz;
195 break;
196
197 case 'd': // --description
198 if (ulCurVsys == (uint32_t)-1)
199 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
200 mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
201 break;
202
203 case 'L': // --eula
204 if (ulCurVsys == (uint32_t)-1)
205 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
206 mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
207 break;
208
209 case 'm': // --memory
210 if (ulCurVsys == (uint32_t)-1)
211 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
212 mapArgsMapsPerVsys[ulCurVsys]["memory"] = ValueUnion.psz;
213 break;
214
215 case 'c': // --cpus
216 if (ulCurVsys == (uint32_t)-1)
217 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
218 mapArgsMapsPerVsys[ulCurVsys]["cpus"] = ValueUnion.psz;
219 break;
220
221 case 'u': // --unit
222 ulCurUnit = ValueUnion.u32;
223 break;
224
225 case 'x': // --ignore
226 if (ulCurVsys == (uint32_t)-1)
227 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
228 if (ulCurUnit == (uint32_t)-1)
229 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
230 mapIgnoresMapsPerVsys[ulCurVsys][ulCurUnit] = true;
231 break;
232
233 case 'T': // --scsitype
234 if (ulCurVsys == (uint32_t)-1)
235 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
236 if (ulCurUnit == (uint32_t)-1)
237 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
238 mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("scsitype%u", ulCurUnit)] = ValueUnion.psz;
239 break;
240
241 case 'C': // --controller
242 if (ulCurVsys == (uint32_t)-1)
243 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
244 if (ulCurUnit == (uint32_t)-1)
245 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
246 mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("controller%u", ulCurUnit)] = ValueUnion.psz;
247 break;
248
249 case 'D': // --disk
250 if (ulCurVsys == (uint32_t)-1)
251 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
252 if (ulCurUnit == (uint32_t)-1)
253 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
254 mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("disk%u", ulCurUnit)] = ValueUnion.psz;
255 break;
256
257 case 'O': // --options
258 if (RT_FAILURE(parseImportOptions(ValueUnion.psz, &options)))
259 return errorArgument("Invalid import options '%s'\n", ValueUnion.psz);
260 break;
261
262 case VINF_GETOPT_NOT_OPTION:
263 if (strOvfFilename.isEmpty())
264 strOvfFilename = ValueUnion.psz;
265 else
266 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid parameter '%s'", ValueUnion.psz);
267 break;
268
269 default:
270 if (c > 0)
271 {
272 if (RT_C_IS_PRINT(c))
273 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid option -%c", c);
274 else
275 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid option case %i", c);
276 }
277 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
278 return errorSyntax(USAGE_IMPORTAPPLIANCE, "unknown option: %s\n", ValueUnion.psz);
279 else if (ValueUnion.pDef)
280 return errorSyntax(USAGE_IMPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
281 else
282 return errorSyntax(USAGE_IMPORTAPPLIANCE, "error: %Rrs", c);
283 }
284 }
285
286 if (strOvfFilename.isEmpty())
287 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Not enough arguments for \"import\" command.");
288
289 do
290 {
291 ComPtr<IAppliance> pAppliance;
292 CHECK_ERROR_BREAK(arg->virtualBox, CreateAppliance(pAppliance.asOutParam()));
293
294 char *pszAbsFilePath;
295 if (strOvfFilename.startsWith("S3://", RTCString::CaseInsensitive) ||
296 strOvfFilename.startsWith("SunCloud://", RTCString::CaseInsensitive) ||
297 strOvfFilename.startsWith("webdav://", RTCString::CaseInsensitive))
298 pszAbsFilePath = RTStrDup(strOvfFilename.c_str());
299 else
300 pszAbsFilePath = RTPathAbsDup(strOvfFilename.c_str());
301 ComPtr<IProgress> progressRead;
302 CHECK_ERROR_BREAK(pAppliance, Read(Bstr(pszAbsFilePath).raw(),
303 progressRead.asOutParam()));
304 RTStrFree(pszAbsFilePath);
305
306 rc = showProgress(progressRead);
307 CHECK_PROGRESS_ERROR_RET(progressRead, ("Appliance read failed"), RTEXITCODE_FAILURE);
308
309 Bstr path; /* fetch the path, there is stuff like username/password removed if any */
310 CHECK_ERROR_BREAK(pAppliance, COMGETTER(Path)(path.asOutParam()));
311 // call interpret(); this can yield both warnings and errors, so we need
312 // to tinker with the error info a bit
313 RTStrmPrintf(g_pStdErr, "Interpreting %ls...\n", path.raw());
314 rc = pAppliance->Interpret();
315 com::ErrorInfo info0(pAppliance, COM_IIDOF(IAppliance));
316
317 com::SafeArray<BSTR> aWarnings;
318 if (SUCCEEDED(pAppliance->GetWarnings(ComSafeArrayAsOutParam(aWarnings))))
319 {
320 size_t cWarnings = aWarnings.size();
321 for (unsigned i = 0; i < cWarnings; ++i)
322 {
323 Bstr bstrWarning(aWarnings[i]);
324 RTMsgWarning("%ls.", bstrWarning.raw());
325 }
326 }
327
328 if (FAILED(rc)) // during interpret, after printing warnings
329 {
330 com::GluePrintErrorInfo(info0);
331 com::GluePrintErrorContext("Interpret", __FILE__, __LINE__);
332 break;
333 }
334
335 RTStrmPrintf(g_pStdErr, "OK.\n");
336
337 // fetch all disks
338 com::SafeArray<BSTR> retDisks;
339 CHECK_ERROR_BREAK(pAppliance,
340 COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
341 if (retDisks.size() > 0)
342 {
343 RTPrintf("Disks:\n");
344 for (unsigned i = 0; i < retDisks.size(); i++)
345 RTPrintf(" %ls\n", retDisks[i]);
346 RTPrintf("\n");
347 }
348
349 // fetch virtual system descriptions
350 com::SafeIfaceArray<IVirtualSystemDescription> aVirtualSystemDescriptions;
351 CHECK_ERROR_BREAK(pAppliance,
352 COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
353
354 size_t cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
355
356 // match command line arguments with virtual system descriptions;
357 // this is only to sort out invalid indices at this time
358 ArgsMapsMap::const_iterator it;
359 for (it = mapArgsMapsPerVsys.begin();
360 it != mapArgsMapsPerVsys.end();
361 ++it)
362 {
363 uint32_t ulVsys = it->first;
364 if (ulVsys >= cVirtualSystemDescriptions)
365 return errorSyntax(USAGE_IMPORTAPPLIANCE,
366 "Invalid index %RI32 with -vsys option; the OVF contains only %zu virtual system(s).",
367 ulVsys, cVirtualSystemDescriptions);
368 }
369
370 uint32_t cLicensesInTheWay = 0;
371
372 // dump virtual system descriptions and match command-line arguments
373 if (cVirtualSystemDescriptions > 0)
374 {
375 for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
376 {
377 com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
378 com::SafeArray<BSTR> aRefs;
379 com::SafeArray<BSTR> aOvfValues;
380 com::SafeArray<BSTR> aVBoxValues;
381 com::SafeArray<BSTR> aExtraConfigValues;
382 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
383 GetDescription(ComSafeArrayAsOutParam(retTypes),
384 ComSafeArrayAsOutParam(aRefs),
385 ComSafeArrayAsOutParam(aOvfValues),
386 ComSafeArrayAsOutParam(aVBoxValues),
387 ComSafeArrayAsOutParam(aExtraConfigValues)));
388
389 RTPrintf("Virtual system %u:\n", i);
390
391 // look up the corresponding command line options, if any
392 ArgsMap *pmapArgs = NULL;
393 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
394 if (itm != mapArgsMapsPerVsys.end())
395 pmapArgs = &itm->second;
396
397 // this collects the final values for setFinalValues()
398 com::SafeArray<BOOL> aEnabled(retTypes.size());
399 com::SafeArray<BSTR> aFinalValues(retTypes.size());
400
401 for (unsigned a = 0; a < retTypes.size(); ++a)
402 {
403 VirtualSystemDescriptionType_T t = retTypes[a];
404
405 Utf8Str strOverride;
406
407 Bstr bstrFinalValue = aVBoxValues[a];
408
409 bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a];
410
411 aEnabled[a] = true;
412
413 switch (t)
414 {
415 case VirtualSystemDescriptionType_OS:
416 if (findArgValue(strOverride, pmapArgs, "ostype"))
417 {
418 bstrFinalValue = strOverride;
419 RTPrintf("%2u: OS type specified with --ostype: \"%ls\"\n",
420 a, bstrFinalValue.raw());
421 }
422 else
423 RTPrintf("%2u: Suggested OS type: \"%ls\""
424 "\n (change with \"--vsys %u --ostype <type>\"; use \"list ostypes\" to list all possible values)\n",
425 a, bstrFinalValue.raw(), i);
426 break;
427
428 case VirtualSystemDescriptionType_Name:
429 if (findArgValue(strOverride, pmapArgs, "vmname"))
430 {
431 bstrFinalValue = strOverride;
432 RTPrintf("%2u: VM name specified with --vmname: \"%ls\"\n",
433 a, bstrFinalValue.raw());
434 }
435 else
436 RTPrintf("%2u: Suggested VM name \"%ls\""
437 "\n (change with \"--vsys %u --vmname <name>\")\n",
438 a, bstrFinalValue.raw(), i);
439 break;
440
441 case VirtualSystemDescriptionType_Product:
442 RTPrintf("%2u: Product (ignored): %ls\n",
443 a, aVBoxValues[a]);
444 break;
445
446 case VirtualSystemDescriptionType_ProductUrl:
447 RTPrintf("%2u: ProductUrl (ignored): %ls\n",
448 a, aVBoxValues[a]);
449 break;
450
451 case VirtualSystemDescriptionType_Vendor:
452 RTPrintf("%2u: Vendor (ignored): %ls\n",
453 a, aVBoxValues[a]);
454 break;
455
456 case VirtualSystemDescriptionType_VendorUrl:
457 RTPrintf("%2u: VendorUrl (ignored): %ls\n",
458 a, aVBoxValues[a]);
459 break;
460
461 case VirtualSystemDescriptionType_Version:
462 RTPrintf("%2u: Version (ignored): %ls\n",
463 a, aVBoxValues[a]);
464 break;
465
466 case VirtualSystemDescriptionType_Description:
467 if (findArgValue(strOverride, pmapArgs, "description"))
468 {
469 bstrFinalValue = strOverride;
470 RTPrintf("%2u: Description specified with --description: \"%ls\"\n",
471 a, bstrFinalValue.raw());
472 }
473 else
474 RTPrintf("%2u: Description \"%ls\""
475 "\n (change with \"--vsys %u --description <desc>\")\n",
476 a, bstrFinalValue.raw(), i);
477 break;
478
479 case VirtualSystemDescriptionType_License:
480 ++cLicensesInTheWay;
481 if (findArgValue(strOverride, pmapArgs, "eula"))
482 {
483 if (strOverride == "show")
484 {
485 RTPrintf("%2u: End-user license agreement"
486 "\n (accept with \"--vsys %u --eula accept\"):"
487 "\n\n%ls\n\n",
488 a, i, bstrFinalValue.raw());
489 }
490 else if (strOverride == "accept")
491 {
492 RTPrintf("%2u: End-user license agreement (accepted)\n",
493 a);
494 --cLicensesInTheWay;
495 }
496 else
497 return errorSyntax(USAGE_IMPORTAPPLIANCE,
498 "Argument to --eula must be either \"show\" or \"accept\".");
499 }
500 else
501 RTPrintf("%2u: End-user license agreement"
502 "\n (display with \"--vsys %u --eula show\";"
503 "\n accept with \"--vsys %u --eula accept\")\n",
504 a, i, i);
505 break;
506
507 case VirtualSystemDescriptionType_CPU:
508 if (findArgValue(strOverride, pmapArgs, "cpus"))
509 {
510 uint32_t cCPUs;
511 if ( strOverride.toInt(cCPUs) == VINF_SUCCESS
512 && cCPUs >= VMM_MIN_CPU_COUNT
513 && cCPUs <= VMM_MAX_CPU_COUNT
514 )
515 {
516 bstrFinalValue = strOverride;
517 RTPrintf("%2u: No. of CPUs specified with --cpus: %ls\n",
518 a, bstrFinalValue.raw());
519 }
520 else
521 return errorSyntax(USAGE_IMPORTAPPLIANCE,
522 "Argument to --cpus option must be a number greater than %d and less than %d.",
523 VMM_MIN_CPU_COUNT - 1, VMM_MAX_CPU_COUNT + 1);
524 }
525 else
526 RTPrintf("%2u: Number of CPUs: %ls\n (change with \"--vsys %u --cpus <n>\")\n",
527 a, bstrFinalValue.raw(), i);
528 break;
529
530 case VirtualSystemDescriptionType_Memory:
531 {
532 if (findArgValue(strOverride, pmapArgs, "memory"))
533 {
534 uint32_t ulMemMB;
535 if (VINF_SUCCESS == strOverride.toInt(ulMemMB))
536 {
537 bstrFinalValue = strOverride;
538 RTPrintf("%2u: Guest memory specified with --memory: %ls MB\n",
539 a, bstrFinalValue.raw());
540 }
541 else
542 return errorSyntax(USAGE_IMPORTAPPLIANCE,
543 "Argument to --memory option must be a non-negative number.");
544 }
545 else
546 RTPrintf("%2u: Guest memory: %ls MB\n (change with \"--vsys %u --memory <MB>\")\n",
547 a, bstrFinalValue.raw(), i);
548 break;
549 }
550
551 case VirtualSystemDescriptionType_HardDiskControllerIDE:
552 if (fIgnoreThis)
553 {
554 RTPrintf("%2u: IDE controller, type %ls -- disabled\n",
555 a,
556 aVBoxValues[a]);
557 aEnabled[a] = false;
558 }
559 else
560 RTPrintf("%2u: IDE controller, type %ls"
561 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
562 a,
563 aVBoxValues[a],
564 i, a);
565 break;
566
567 case VirtualSystemDescriptionType_HardDiskControllerSATA:
568 if (fIgnoreThis)
569 {
570 RTPrintf("%2u: SATA controller, type %ls -- disabled\n",
571 a,
572 aVBoxValues[a]);
573 aEnabled[a] = false;
574 }
575 else
576 RTPrintf("%2u: SATA controller, type %ls"
577 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
578 a,
579 aVBoxValues[a],
580 i, a);
581 break;
582
583 case VirtualSystemDescriptionType_HardDiskControllerSAS:
584 if (fIgnoreThis)
585 {
586 RTPrintf("%2u: SAS controller, type %ls -- disabled\n",
587 a,
588 aVBoxValues[a]);
589 aEnabled[a] = false;
590 }
591 else
592 RTPrintf("%2u: SAS controller, type %ls"
593 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
594 a,
595 aVBoxValues[a],
596 i, a);
597 break;
598
599 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
600 if (fIgnoreThis)
601 {
602 RTPrintf("%2u: SCSI controller, type %ls -- disabled\n",
603 a,
604 aVBoxValues[a]);
605 aEnabled[a] = false;
606 }
607 else
608 {
609 Utf8StrFmt strTypeArg("scsitype%u", a);
610 if (findArgValue(strOverride, pmapArgs, strTypeArg))
611 {
612 bstrFinalValue = strOverride;
613 RTPrintf("%2u: SCSI controller, type set with --unit %u --scsitype: \"%ls\"\n",
614 a,
615 a,
616 bstrFinalValue.raw());
617 }
618 else
619 RTPrintf("%2u: SCSI controller, type %ls"
620 "\n (change with \"--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}\";"
621 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
622 a,
623 aVBoxValues[a],
624 i, a, i, a);
625 }
626 break;
627
628 case VirtualSystemDescriptionType_HardDiskImage:
629 if (fIgnoreThis)
630 {
631 RTPrintf("%2u: Hard disk image: source image=%ls -- disabled\n",
632 a,
633 aOvfValues[a]);
634 aEnabled[a] = false;
635 }
636 else
637 {
638 Utf8StrFmt strTypeArg("disk%u", a);
639 RTCList<ImportOptions_T> optionsList = options.toList();
640
641 bstrFinalValue = aVBoxValues[a];
642
643 if (findArgValue(strOverride, pmapArgs, strTypeArg))
644 {
645 if (!optionsList.contains(ImportOptions_ImportToVDI))
646 {
647 RTUUID uuid;
648 /* Check if this is a uuid. If so, don't touch. */
649 int vrc = RTUuidFromStr(&uuid, strOverride.c_str());
650 if (vrc != VINF_SUCCESS)
651 {
652 /* Make the path absolute. */
653 if (!RTPathStartsWithRoot(strOverride.c_str()))
654 {
655 char pszPwd[RTPATH_MAX];
656 vrc = RTPathGetCurrent(pszPwd, RTPATH_MAX);
657 if (RT_SUCCESS(vrc))
658 strOverride = Utf8Str(pszPwd).append(RTPATH_SLASH).append(strOverride);
659 }
660 }
661 bstrFinalValue = strOverride;
662 }
663 else
664 {
665 //print some error about incompatible command-line arguments
666 return errorSyntax(USAGE_IMPORTAPPLIANCE,
667 "Option --ImportToVDI shall not be used together with "
668 "manually set target path.");
669
670 }
671
672 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n",
673 a,
674 aOvfValues[a],
675 bstrFinalValue.raw(),
676 aExtraConfigValues[a]);
677 }
678#if 0 /* Changing the controller is fully valid, but the current design on how
679 the params are evaluated here doesn't allow two parameter for one
680 unit. The target disk path is more important I leave it for future
681 improvments. */
682 Utf8StrFmt strTypeArg("controller%u", a);
683 if (findArgValue(strOverride, pmapArgs, strTypeArg))
684 {
685 // strOverride now has the controller index as a number, but we
686 // need a "controller=X" format string
687 strOverride = Utf8StrFmt("controller=%s", strOverride.c_str());
688 Bstr bstrExtraConfigValue = strOverride;
689 bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]);
690 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n",
691 a,
692 aOvfValues[a],
693 aVBoxValues[a],
694 aExtraConfigValues[a]);
695 }
696#endif
697 else
698 {
699 strOverride = aVBoxValues[a];
700
701 /*
702 * Current solution isn't optimal.
703 * Better way is to provide API call for function
704 * Appliance::i_findMediumFormatFromDiskImage()
705 * and creating one new function which returns
706 * struct ovf::DiskImage for currently processed disk.
707 */
708
709 /*
710 * if user wants to convert all imported disks to VDI format
711 * we need to replace files extensions to "vdi"
712 * except CD/DVD disks
713 */
714 if (optionsList.contains(ImportOptions_ImportToVDI))
715 {
716 ComPtr<IVirtualBox> pVirtualBox = arg->virtualBox;
717 ComPtr<ISystemProperties> systemProperties;
718 com::SafeIfaceArray<IMediumFormat> mediumFormats;
719 Bstr bstrFormatName;
720
721 CHECK_ERROR(pVirtualBox,
722 COMGETTER(SystemProperties)(systemProperties.asOutParam()));
723
724 CHECK_ERROR(systemProperties,
725 COMGETTER(MediumFormats)(ComSafeArrayAsOutParam(mediumFormats)));
726
727 /* go through all supported media formats and store files extensions only for RAW */
728 com::SafeArray<BSTR> extensions;
729
730 for (unsigned j = 0; j < mediumFormats.size(); ++j)
731 {
732 com::SafeArray<DeviceType_T> deviceType;
733 ComPtr<IMediumFormat> mediumFormat = mediumFormats[j];
734 CHECK_ERROR(mediumFormat, COMGETTER(Name)(bstrFormatName.asOutParam()));
735 Utf8Str strFormatName = Utf8Str(bstrFormatName);
736
737 if (strFormatName.compare("RAW", Utf8Str::CaseInsensitive) == 0)
738 {
739 /* getting files extensions for "RAW" format */
740 CHECK_ERROR(mediumFormat,
741 DescribeFileExtensions(ComSafeArrayAsOutParam(extensions),
742 ComSafeArrayAsOutParam(deviceType)));
743 break;
744 }
745 }
746
747 /* go through files extensions for RAW format and compare them with
748 * extension of current file
749 */
750 bool fReplace = true;
751
752 const char *pszExtension = RTPathSuffix(strOverride.c_str());
753 if (pszExtension)
754 pszExtension++;
755
756 for (unsigned j = 0; j < extensions.size(); ++j)
757 {
758 Bstr bstrExt(extensions[j]);
759 Utf8Str strExtension(bstrExt);
760 if(strExtension.compare(pszExtension, Utf8Str::CaseInsensitive) == 0)
761 {
762 fReplace = false;
763 break;
764 }
765 }
766
767 if (fReplace)
768 {
769 strOverride = strOverride.stripSuffix();
770 strOverride = strOverride.append(".").append("vdi");
771 }
772 }
773
774 bstrFinalValue = strOverride;
775
776 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls"
777 "\n (change target path with \"--vsys %u --unit %u --disk path\";"
778 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
779 a,
780 aOvfValues[a],
781 bstrFinalValue.raw(),
782 aExtraConfigValues[a],
783 i, a, i, a);
784 }
785 }
786 break;
787
788 case VirtualSystemDescriptionType_CDROM:
789 if (fIgnoreThis)
790 {
791 RTPrintf("%2u: CD-ROM -- disabled\n",
792 a);
793 aEnabled[a] = false;
794 }
795 else
796 RTPrintf("%2u: CD-ROM"
797 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
798 a, i, a);
799 break;
800
801 case VirtualSystemDescriptionType_Floppy:
802 if (fIgnoreThis)
803 {
804 RTPrintf("%2u: Floppy -- disabled\n",
805 a);
806 aEnabled[a] = false;
807 }
808 else
809 RTPrintf("%2u: Floppy"
810 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
811 a, i, a);
812 break;
813
814 case VirtualSystemDescriptionType_NetworkAdapter:
815 RTPrintf("%2u: Network adapter: orig %ls, config %ls, extra %ls\n", /// @todo implement once we have a plan for the back-end
816 a,
817 aOvfValues[a],
818 aVBoxValues[a],
819 aExtraConfigValues[a]);
820 break;
821
822 case VirtualSystemDescriptionType_USBController:
823 if (fIgnoreThis)
824 {
825 RTPrintf("%2u: USB controller -- disabled\n",
826 a);
827 aEnabled[a] = false;
828 }
829 else
830 RTPrintf("%2u: USB controller"
831 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
832 a, i, a);
833 break;
834
835 case VirtualSystemDescriptionType_SoundCard:
836 if (fIgnoreThis)
837 {
838 RTPrintf("%2u: Sound card \"%ls\" -- disabled\n",
839 a,
840 aOvfValues[a]);
841 aEnabled[a] = false;
842 }
843 else
844 RTPrintf("%2u: Sound card (appliance expects \"%ls\", can change on import)"
845 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
846 a,
847 aOvfValues[a],
848 i,
849 a);
850 break;
851
852 case VirtualSystemDescriptionType_SettingsFile:
853 /** @todo VirtualSystemDescriptionType_SettingsFile? */
854 break;
855 case VirtualSystemDescriptionType_Miscellaneous:
856 /** @todo VirtualSystemDescriptionType_Miscellaneous? */
857 break;
858 case VirtualSystemDescriptionType_Ignore:
859 break;
860 }
861
862 bstrFinalValue.detachTo(&aFinalValues[a]);
863 }
864
865 if (fExecute)
866 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
867 SetFinalValues(ComSafeArrayAsInParam(aEnabled),
868 ComSafeArrayAsInParam(aFinalValues),
869 ComSafeArrayAsInParam(aExtraConfigValues)));
870
871 } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
872
873 if (cLicensesInTheWay == 1)
874 RTMsgError("Cannot import until the license agreement listed above is accepted.");
875 else if (cLicensesInTheWay > 1)
876 RTMsgError("Cannot import until the %c license agreements listed above are accepted.", cLicensesInTheWay);
877
878 if (!cLicensesInTheWay && fExecute)
879 {
880 // go!
881 ComPtr<IProgress> progress;
882 CHECK_ERROR_BREAK(pAppliance,
883 ImportMachines(ComSafeArrayAsInParam(options), progress.asOutParam()));
884
885 rc = showProgress(progress);
886 CHECK_PROGRESS_ERROR_RET(progress, ("Appliance import failed"), RTEXITCODE_FAILURE);
887
888 if (SUCCEEDED(rc))
889 RTPrintf("Successfully imported the appliance.\n");
890 }
891 } // end if (aVirtualSystemDescriptions.size() > 0)
892 } while (0);
893
894 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
895}
896
897static int parseExportOptions(const char *psz, com::SafeArray<ExportOptions_T> *options)
898{
899 int rc = VINF_SUCCESS;
900 while (psz && *psz && RT_SUCCESS(rc))
901 {
902 size_t len;
903 const char *pszComma = strchr(psz, ',');
904 if (pszComma)
905 len = pszComma - psz;
906 else
907 len = strlen(psz);
908 if (len > 0)
909 {
910 if (!RTStrNICmp(psz, "CreateManifest", len))
911 options->push_back(ExportOptions_CreateManifest);
912 else if (!RTStrNICmp(psz, "manifest", len))
913 options->push_back(ExportOptions_CreateManifest);
914 else if (!RTStrNICmp(psz, "ExportDVDImages", len))
915 options->push_back(ExportOptions_ExportDVDImages);
916 else if (!RTStrNICmp(psz, "iso", len))
917 options->push_back(ExportOptions_ExportDVDImages);
918 else if (!RTStrNICmp(psz, "StripAllMACs", len))
919 options->push_back(ExportOptions_StripAllMACs);
920 else if (!RTStrNICmp(psz, "nomacs", len))
921 options->push_back(ExportOptions_StripAllMACs);
922 else if (!RTStrNICmp(psz, "StripAllNonNATMACs", len))
923 options->push_back(ExportOptions_StripAllNonNATMACs);
924 else if (!RTStrNICmp(psz, "nomacsbutnat", len))
925 options->push_back(ExportOptions_StripAllNonNATMACs);
926 else
927 rc = VERR_PARSE_ERROR;
928 }
929 if (pszComma)
930 psz += len + 1;
931 else
932 psz += len;
933 }
934
935 return rc;
936}
937
938static const RTGETOPTDEF g_aExportOptions[] =
939{
940 { "--output", 'o', RTGETOPT_REQ_STRING },
941 { "--legacy09", 'l', RTGETOPT_REQ_NOTHING },
942 { "--ovf09", 'l', RTGETOPT_REQ_NOTHING },
943 { "--ovf10", '1', RTGETOPT_REQ_NOTHING },
944 { "--ovf20", '2', RTGETOPT_REQ_NOTHING },
945 { "--opc10", 'c', RTGETOPT_REQ_NOTHING },
946 { "--manifest", 'm', RTGETOPT_REQ_NOTHING }, // obsoleted by --options
947 { "--iso", 'I', RTGETOPT_REQ_NOTHING }, // obsoleted by --options
948 { "--vsys", 's', RTGETOPT_REQ_UINT32 },
949 { "--product", 'p', RTGETOPT_REQ_STRING },
950 { "--producturl", 'P', RTGETOPT_REQ_STRING },
951 { "--vendor", 'n', RTGETOPT_REQ_STRING },
952 { "--vendorurl", 'N', RTGETOPT_REQ_STRING },
953 { "--version", 'v', RTGETOPT_REQ_STRING },
954 { "--description", 'd', RTGETOPT_REQ_STRING },
955 { "--eula", 'e', RTGETOPT_REQ_STRING },
956 { "--eulafile", 'E', RTGETOPT_REQ_STRING },
957 { "--options", 'O', RTGETOPT_REQ_STRING },
958};
959
960RTEXITCODE handleExportAppliance(HandlerArg *a)
961{
962 HRESULT rc = S_OK;
963
964 Utf8Str strOutputFile;
965 Utf8Str strOvfFormat("ovf-1.0"); // the default export version
966 bool fManifest = false; // the default
967 bool fExportISOImages = false; // the default
968 com::SafeArray<ExportOptions_T> options;
969 std::list< ComPtr<IMachine> > llMachines;
970
971 uint32_t ulCurVsys = (uint32_t)-1;
972 // for each --vsys X command, maintain a map of command line items
973 ArgsMapsMap mapArgsMapsPerVsys;
974 do
975 {
976 int c;
977
978 RTGETOPTUNION ValueUnion;
979 RTGETOPTSTATE GetState;
980 // start at 0 because main() has hacked both the argc and argv given to us
981 RTGetOptInit(&GetState, a->argc, a->argv, g_aExportOptions,
982 RT_ELEMENTS(g_aExportOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
983
984 Utf8Str strProductUrl;
985 while ((c = RTGetOpt(&GetState, &ValueUnion)))
986 {
987 switch (c)
988 {
989 case 'o': // --output
990 if (strOutputFile.length())
991 return errorSyntax(USAGE_EXPORTAPPLIANCE, "You can only specify --output once.");
992 else
993 strOutputFile = ValueUnion.psz;
994 break;
995
996 case 'l': // --legacy09/--ovf09
997 strOvfFormat = "ovf-0.9";
998 break;
999
1000 case '1': // --ovf10
1001 strOvfFormat = "ovf-1.0";
1002 break;
1003
1004 case '2': // --ovf20
1005 strOvfFormat = "ovf-2.0";
1006 break;
1007
1008 case 'c': // --opc
1009 strOvfFormat = "opc-1.0";
1010 break;
1011
1012 case 'I': // --iso
1013 fExportISOImages = true;
1014 break;
1015
1016 case 'm': // --manifest
1017 fManifest = true;
1018 break;
1019
1020 case 's': // --vsys
1021 ulCurVsys = ValueUnion.u32;
1022 break;
1023
1024 case 'p': // --product
1025 if (ulCurVsys == (uint32_t)-1)
1026 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1027 mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz;
1028 break;
1029
1030 case 'P': // --producturl
1031 if (ulCurVsys == (uint32_t)-1)
1032 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1033 mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz;
1034 break;
1035
1036 case 'n': // --vendor
1037 if (ulCurVsys == (uint32_t)-1)
1038 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1039 mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz;
1040 break;
1041
1042 case 'N': // --vendorurl
1043 if (ulCurVsys == (uint32_t)-1)
1044 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1045 mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz;
1046 break;
1047
1048 case 'v': // --version
1049 if (ulCurVsys == (uint32_t)-1)
1050 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1051 mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz;
1052 break;
1053
1054 case 'd': // --description
1055 if (ulCurVsys == (uint32_t)-1)
1056 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1057 mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
1058 break;
1059
1060 case 'e': // --eula
1061 if (ulCurVsys == (uint32_t)-1)
1062 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1063 mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
1064 break;
1065
1066 case 'E': // --eulafile
1067 if (ulCurVsys == (uint32_t)-1)
1068 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
1069 mapArgsMapsPerVsys[ulCurVsys]["eulafile"] = ValueUnion.psz;
1070 break;
1071
1072 case 'O': // --options
1073 if (RT_FAILURE(parseExportOptions(ValueUnion.psz, &options)))
1074 return errorArgument("Invalid export options '%s'\n", ValueUnion.psz);
1075 break;
1076
1077 case VINF_GETOPT_NOT_OPTION:
1078 {
1079 Utf8Str strMachine(ValueUnion.psz);
1080 // must be machine: try UUID or name
1081 ComPtr<IMachine> machine;
1082 CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine).raw(),
1083 machine.asOutParam()));
1084 if (machine)
1085 llMachines.push_back(machine);
1086 break;
1087 }
1088
1089 default:
1090 if (c > 0)
1091 {
1092 if (RT_C_IS_GRAPH(c))
1093 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: -%c", c);
1094 else
1095 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: %i", c);
1096 }
1097 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1098 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unknown option: %s", ValueUnion.psz);
1099 else if (ValueUnion.pDef)
1100 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1101 else
1102 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%Rrs", c);
1103 }
1104
1105 if (FAILED(rc))
1106 break;
1107 }
1108
1109 if (FAILED(rc))
1110 break;
1111
1112 if (llMachines.size() == 0)
1113 return errorSyntax(USAGE_EXPORTAPPLIANCE, "At least one machine must be specified with the export command.");
1114 if (!strOutputFile.length())
1115 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Missing --output argument with export command.");
1116
1117 // match command line arguments with the machines count
1118 // this is only to sort out invalid indices at this time
1119 ArgsMapsMap::const_iterator it;
1120 for (it = mapArgsMapsPerVsys.begin();
1121 it != mapArgsMapsPerVsys.end();
1122 ++it)
1123 {
1124 uint32_t ulVsys = it->first;
1125 if (ulVsys >= llMachines.size())
1126 return errorSyntax(USAGE_EXPORTAPPLIANCE,
1127 "Invalid index %RI32 with -vsys option; you specified only %zu virtual system(s).",
1128 ulVsys, llMachines.size());
1129 }
1130
1131 ComPtr<IAppliance> pAppliance;
1132 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
1133
1134 char *pszAbsFilePath = 0;
1135 if (strOutputFile.startsWith("S3://", RTCString::CaseInsensitive) ||
1136 strOutputFile.startsWith("SunCloud://", RTCString::CaseInsensitive) ||
1137 strOutputFile.startsWith("webdav://", RTCString::CaseInsensitive))
1138 pszAbsFilePath = RTStrDup(strOutputFile.c_str());
1139 else
1140 pszAbsFilePath = RTPathAbsDup(strOutputFile.c_str());
1141
1142 std::list< ComPtr<IMachine> >::iterator itM;
1143 uint32_t i=0;
1144 for (itM = llMachines.begin();
1145 itM != llMachines.end();
1146 ++itM, ++i)
1147 {
1148 ComPtr<IMachine> pMachine = *itM;
1149 ComPtr<IVirtualSystemDescription> pVSD;
1150 CHECK_ERROR_BREAK(pMachine, ExportTo(pAppliance, Bstr(pszAbsFilePath).raw(), pVSD.asOutParam()));
1151 // Add additional info to the virtual system description if the user wants so
1152 ArgsMap *pmapArgs = NULL;
1153 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
1154 if (itm != mapArgsMapsPerVsys.end())
1155 pmapArgs = &itm->second;
1156 if (pmapArgs)
1157 {
1158 ArgsMap::iterator itD;
1159 for (itD = pmapArgs->begin();
1160 itD != pmapArgs->end();
1161 ++itD)
1162 {
1163 if (itD->first == "product")
1164 pVSD->AddDescription(VirtualSystemDescriptionType_Product,
1165 Bstr(itD->second).raw(),
1166 Bstr(itD->second).raw());
1167 else if (itD->first == "producturl")
1168 pVSD->AddDescription(VirtualSystemDescriptionType_ProductUrl,
1169 Bstr(itD->second).raw(),
1170 Bstr(itD->second).raw());
1171 else if (itD->first == "vendor")
1172 pVSD->AddDescription(VirtualSystemDescriptionType_Vendor,
1173 Bstr(itD->second).raw(),
1174 Bstr(itD->second).raw());
1175 else if (itD->first == "vendorurl")
1176 pVSD->AddDescription(VirtualSystemDescriptionType_VendorUrl,
1177 Bstr(itD->second).raw(),
1178 Bstr(itD->second).raw());
1179 else if (itD->first == "version")
1180 pVSD->AddDescription(VirtualSystemDescriptionType_Version,
1181 Bstr(itD->second).raw(),
1182 Bstr(itD->second).raw());
1183 else if (itD->first == "description")
1184 pVSD->AddDescription(VirtualSystemDescriptionType_Description,
1185 Bstr(itD->second).raw(),
1186 Bstr(itD->second).raw());
1187 else if (itD->first == "eula")
1188 pVSD->AddDescription(VirtualSystemDescriptionType_License,
1189 Bstr(itD->second).raw(),
1190 Bstr(itD->second).raw());
1191 else if (itD->first == "eulafile")
1192 {
1193 Utf8Str strContent;
1194 void *pvFile;
1195 size_t cbFile;
1196 int irc = RTFileReadAll(itD->second.c_str(), &pvFile, &cbFile);
1197 if (RT_SUCCESS(irc))
1198 {
1199 Bstr bstrContent((char*)pvFile, cbFile);
1200 pVSD->AddDescription(VirtualSystemDescriptionType_License,
1201 bstrContent.raw(),
1202 bstrContent.raw());
1203 RTFileReadAllFree(pvFile, cbFile);
1204 }
1205 else
1206 {
1207 RTMsgError("Cannot read license file \"%s\" which should be included in the virtual system %u.",
1208 itD->second.c_str(), i);
1209 return RTEXITCODE_FAILURE;
1210 }
1211 }
1212 }
1213 }
1214 }
1215
1216 if (FAILED(rc))
1217 break;
1218
1219 /* Query required passwords and supply them to the appliance. */
1220 com::SafeArray<BSTR> aIdentifiers;
1221
1222 CHECK_ERROR_BREAK(pAppliance, GetPasswordIds(ComSafeArrayAsOutParam(aIdentifiers)));
1223
1224 if (aIdentifiers.size() > 0)
1225 {
1226 com::SafeArray<BSTR> aPasswords(aIdentifiers.size());
1227 RTPrintf("Enter the passwords for the following identifiers to export the apppliance:\n");
1228 for (unsigned idxId = 0; idxId < aIdentifiers.size(); idxId++)
1229 {
1230 com::Utf8Str strPassword;
1231 Bstr bstrPassword;
1232 Bstr bstrId = aIdentifiers[idxId];
1233
1234 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Password ID %s:", Utf8Str(bstrId).c_str());
1235 if (rcExit == RTEXITCODE_FAILURE)
1236 {
1237 RTStrFree(pszAbsFilePath);
1238 return rcExit;
1239 }
1240
1241 bstrPassword = strPassword;
1242 bstrPassword.detachTo(&aPasswords[idxId]);
1243 }
1244
1245 CHECK_ERROR_BREAK(pAppliance, AddPasswords(ComSafeArrayAsInParam(aIdentifiers),
1246 ComSafeArrayAsInParam(aPasswords)));
1247 }
1248
1249 if (fManifest)
1250 options.push_back(ExportOptions_CreateManifest);
1251
1252 if (fExportISOImages)
1253 options.push_back(ExportOptions_ExportDVDImages);
1254
1255 ComPtr<IProgress> progress;
1256 CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOvfFormat).raw(),
1257 ComSafeArrayAsInParam(options),
1258 Bstr(pszAbsFilePath).raw(),
1259 progress.asOutParam()));
1260 RTStrFree(pszAbsFilePath);
1261
1262 rc = showProgress(progress);
1263 CHECK_PROGRESS_ERROR_RET(progress, ("Appliance write failed"), RTEXITCODE_FAILURE);
1264
1265 if (SUCCEEDED(rc))
1266 RTPrintf("Successfully exported %d machine(s).\n", llMachines.size());
1267
1268 } while (0);
1269
1270 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1271}
1272
1273#endif /* !VBOX_ONLY_DOCS */
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