VirtualBox

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

Last change on this file since 55263 was 55182, checked in by vboxsync, 10 years ago

Main,FE/VBoxManage: Support exporting machines as appliances which have encrypted disks. Because the OVF standard doesn't support encrypted disks so far we always decrypt exported images which requires the password before starting the export proess

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