VirtualBox

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

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

com/string: Remove bool conversion operator and other convenience error operators. They are hiding programming errors (like incorrect empty string checks, and in one case a free of the wrong pointer).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.0 KB
Line 
1/* $Id: VBoxManageImport.cpp 32718 2010-09-23 12:57:52Z vboxsync $ */
2/** @file
3 * VBoxManage - The appliance-related commands.
4 */
5
6/*
7 * Copyright (C) 2009-2010 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/EventQueue.h>
31
32#include <VBox/com/VirtualBox.h>
33
34#include <list>
35#include <map>
36#endif /* !VBOX_ONLY_DOCS */
37
38#include <iprt/stream.h>
39#include <iprt/getopt.h>
40#include <iprt/ctype.h>
41#include <iprt/path.h>
42#include <iprt/file.h>
43
44#include <VBox/log.h>
45#include <VBox/param.h>
46
47#include "VBoxManage.h"
48using namespace com;
49
50
51// funcs
52///////////////////////////////////////////////////////////////////////////////
53
54typedef std::map<Utf8Str, Utf8Str> ArgsMap; // pairs of strings like "vmname" => "newvmname"
55typedef std::map<uint32_t, ArgsMap> ArgsMapsMap; // map of maps, one for each virtual system, sorted by index
56
57typedef std::map<uint32_t, bool> IgnoresMap; // pairs of numeric description entry indices
58typedef std::map<uint32_t, IgnoresMap> IgnoresMapsMap; // map of maps, one for each virtual system, sorted by index
59
60static bool findArgValue(Utf8Str &strOut,
61 ArgsMap *pmapArgs,
62 const Utf8Str &strKey)
63{
64 if (pmapArgs)
65 {
66 ArgsMap::iterator it;
67 it = pmapArgs->find(strKey);
68 if (it != pmapArgs->end())
69 {
70 strOut = it->second;
71 pmapArgs->erase(it);
72 return true;
73 }
74 }
75
76 return false;
77}
78
79static const RTGETOPTDEF g_aImportApplianceOptions[] =
80{
81 { "--dry-run", 'n', RTGETOPT_REQ_NOTHING },
82 { "-dry-run", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
83 { "--dryrun", 'n', RTGETOPT_REQ_NOTHING },
84 { "-dryrun", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
85 { "--detailed-progress", 'P', RTGETOPT_REQ_NOTHING },
86 { "-detailed-progress", 'P', RTGETOPT_REQ_NOTHING }, // deprecated
87 { "--vsys", 's', RTGETOPT_REQ_UINT32 },
88 { "-vsys", 's', RTGETOPT_REQ_UINT32 }, // deprecated
89 { "--ostype", 'o', RTGETOPT_REQ_STRING },
90 { "-ostype", 'o', RTGETOPT_REQ_STRING }, // deprecated
91 { "--vmname", 'V', RTGETOPT_REQ_STRING },
92 { "-vmname", 'V', RTGETOPT_REQ_STRING }, // deprecated
93 { "--memory", 'm', RTGETOPT_REQ_STRING },
94 { "-memory", 'm', RTGETOPT_REQ_STRING }, // deprecated
95 { "--cpus", 'c', RTGETOPT_REQ_STRING },
96 { "--description", 'd', RTGETOPT_REQ_STRING },
97 { "--eula", 'L', RTGETOPT_REQ_STRING },
98 { "-eula", 'L', RTGETOPT_REQ_STRING }, // deprecated
99 { "--unit", 'u', RTGETOPT_REQ_UINT32 },
100 { "-unit", 'u', RTGETOPT_REQ_UINT32 }, // deprecated
101 { "--ignore", 'x', RTGETOPT_REQ_NOTHING },
102 { "-ignore", 'x', RTGETOPT_REQ_NOTHING }, // deprecated
103 { "--scsitype", 'T', RTGETOPT_REQ_UINT32 },
104 { "-scsitype", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
105 { "--type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
106 { "-type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
107};
108
109int handleImportAppliance(HandlerArg *arg)
110{
111 HRESULT rc = S_OK;
112
113 Utf8Str strOvfFilename;
114 bool fExecute = true; // if true, then we actually do the import
115 uint32_t ulCurVsys = (uint32_t)-1;
116 uint32_t ulCurUnit = (uint32_t)-1;
117 // for each --vsys X command, maintain a map of command line items
118 // (we'll parse them later after interpreting the OVF, when we can
119 // actually check whether they make sense semantically)
120 ArgsMapsMap mapArgsMapsPerVsys;
121 IgnoresMapsMap mapIgnoresMapsPerVsys;
122
123 int c;
124 RTGETOPTUNION ValueUnion;
125 RTGETOPTSTATE GetState;
126 // start at 0 because main() has hacked both the argc and argv given to us
127 RTGetOptInit(&GetState, arg->argc, arg->argv, g_aImportApplianceOptions, RT_ELEMENTS(g_aImportApplianceOptions),
128 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
129 while ((c = RTGetOpt(&GetState, &ValueUnion)))
130 {
131 switch (c)
132 {
133 case 'n': // --dry-run
134 fExecute = false;
135 break;
136
137 case 'P': // --detailed-progress
138 g_fDetailedProgress = true;
139 break;
140
141 case 's': // --vsys
142 ulCurVsys = ValueUnion.u32;
143 ulCurUnit = (uint32_t)-1;
144 break;
145
146 case 'o': // --ostype
147 if (ulCurVsys == (uint32_t)-1)
148 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
149 mapArgsMapsPerVsys[ulCurVsys]["ostype"] = ValueUnion.psz;
150 break;
151
152 case 'V': // --vmname
153 if (ulCurVsys == (uint32_t)-1)
154 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
155 mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz;
156 break;
157
158 case 'd': // --description
159 if (ulCurVsys == (uint32_t)-1)
160 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
161 mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
162 break;
163
164 case 'L': // --eula
165 if (ulCurVsys == (uint32_t)-1)
166 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
167 mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
168 break;
169
170 case 'm': // --memory
171 if (ulCurVsys == (uint32_t)-1)
172 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
173 mapArgsMapsPerVsys[ulCurVsys]["memory"] = ValueUnion.psz;
174 break;
175
176 case 'c': // --cpus
177 if (ulCurVsys == (uint32_t)-1)
178 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
179 mapArgsMapsPerVsys[ulCurVsys]["cpus"] = ValueUnion.psz;
180 break;
181
182 case 'u': // --unit
183 ulCurUnit = ValueUnion.u32;
184 break;
185
186 case 'x': // --ignore
187 if (ulCurVsys == (uint32_t)-1)
188 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
189 if (ulCurUnit == (uint32_t)-1)
190 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
191 mapIgnoresMapsPerVsys[ulCurVsys][ulCurUnit] = true;
192 break;
193
194 case 'T': // --scsitype
195 if (ulCurVsys == (uint32_t)-1)
196 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
197 if (ulCurUnit == (uint32_t)-1)
198 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
199 mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("scsitype%u", ulCurUnit)] = ValueUnion.psz;
200 break;
201
202 case 'C': // --controller
203 if (ulCurVsys == (uint32_t)-1)
204 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
205 if (ulCurUnit == (uint32_t)-1)
206 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
207 mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("controller%u", ulCurUnit)] = ValueUnion.psz;
208 break;
209
210 case VINF_GETOPT_NOT_OPTION:
211 if (strOvfFilename.isEmpty())
212 strOvfFilename = ValueUnion.psz;
213 else
214 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid parameter '%s'", ValueUnion.psz);
215 break;
216
217 default:
218 if (c > 0)
219 {
220 if (RT_C_IS_PRINT(c))
221 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid option -%c", c);
222 else
223 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid option case %i", c);
224 }
225 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
226 return errorSyntax(USAGE_IMPORTAPPLIANCE, "unknown option: %s\n", ValueUnion.psz);
227 else if (ValueUnion.pDef)
228 return errorSyntax(USAGE_IMPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
229 else
230 return errorSyntax(USAGE_IMPORTAPPLIANCE, "error: %Rrs", c);
231 }
232 }
233
234 if (strOvfFilename.isEmpty())
235 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Not enough arguments for \"import\" command.");
236
237 do
238 {
239 ComPtr<IAppliance> pAppliance;
240 CHECK_ERROR_BREAK(arg->virtualBox, CreateAppliance(pAppliance.asOutParam()));
241
242 char *pszAbsFilePath;
243 if (strOvfFilename.startsWith("S3://", iprt::MiniString::CaseInsensitive) ||
244 strOvfFilename.startsWith("SunCloud://", iprt::MiniString::CaseInsensitive) ||
245 strOvfFilename.startsWith("webdav://", iprt::MiniString::CaseInsensitive))
246 pszAbsFilePath = RTStrDup(strOvfFilename.c_str());
247 else
248 pszAbsFilePath = RTPathAbsDup(strOvfFilename.c_str());
249 ComPtr<IProgress> progressRead;
250 CHECK_ERROR_BREAK(pAppliance, Read(Bstr(pszAbsFilePath).raw(),
251 progressRead.asOutParam()));
252 RTStrFree(pszAbsFilePath);
253
254 rc = showProgress(progressRead);
255
256 if (FAILED(rc))
257 {
258 com::ProgressErrorInfo info(progressRead);
259 com::GluePrintErrorInfo(info);
260 com::GluePrintErrorContext("ImportAppliance", __FILE__, __LINE__);
261 return 1;
262 }
263
264 Bstr path; /* fetch the path, there is stuff like username/password removed if any */
265 CHECK_ERROR_BREAK(pAppliance, COMGETTER(Path)(path.asOutParam()));
266 // call interpret(); this can yield both warnings and errors, so we need
267 // to tinker with the error info a bit
268 RTStrmPrintf(g_pStdErr, "Interpreting %ls...\n", path.raw());
269 rc = pAppliance->Interpret();
270 com::ErrorInfo info0(pAppliance, COM_IIDOF(IAppliance));
271
272 com::SafeArray<BSTR> aWarnings;
273 if (SUCCEEDED(pAppliance->GetWarnings(ComSafeArrayAsOutParam(aWarnings))))
274 {
275 size_t cWarnings = aWarnings.size();
276 for (unsigned i = 0; i < cWarnings; ++i)
277 {
278 Bstr bstrWarning(aWarnings[i]);
279 RTMsgWarning("%ls.", bstrWarning.raw());
280 }
281 }
282
283 if (FAILED(rc)) // during interpret, after printing warnings
284 {
285 com::GluePrintErrorInfo(info0);
286 com::GluePrintErrorContext("Interpret", __FILE__, __LINE__);
287 break;
288 }
289
290 RTStrmPrintf(g_pStdErr, "OK.\n");
291
292 // fetch all disks
293 com::SafeArray<BSTR> retDisks;
294 CHECK_ERROR_BREAK(pAppliance,
295 COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
296 if (retDisks.size() > 0)
297 {
298 RTPrintf("Disks:");
299 for (unsigned i = 0; i < retDisks.size(); i++)
300 RTPrintf(" %ls", retDisks[i]);
301 RTPrintf("\n");
302 }
303
304 // fetch virtual system descriptions
305 com::SafeIfaceArray<IVirtualSystemDescription> aVirtualSystemDescriptions;
306 CHECK_ERROR_BREAK(pAppliance,
307 COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
308
309 size_t cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
310
311 // match command line arguments with virtual system descriptions;
312 // this is only to sort out invalid indices at this time
313 ArgsMapsMap::const_iterator it;
314 for (it = mapArgsMapsPerVsys.begin();
315 it != mapArgsMapsPerVsys.end();
316 ++it)
317 {
318 uint32_t ulVsys = it->first;
319 if (ulVsys >= cVirtualSystemDescriptions)
320 return errorSyntax(USAGE_IMPORTAPPLIANCE,
321 "Invalid index %RI32 with -vsys option; the OVF contains only %zu virtual system(s).",
322 ulVsys, cVirtualSystemDescriptions);
323 }
324
325 uint32_t cLicensesInTheWay = 0;
326
327 // dump virtual system descriptions and match command-line arguments
328 if (cVirtualSystemDescriptions > 0)
329 {
330 for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
331 {
332 com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
333 com::SafeArray<BSTR> aRefs;
334 com::SafeArray<BSTR> aOvfValues;
335 com::SafeArray<BSTR> aVboxValues;
336 com::SafeArray<BSTR> aExtraConfigValues;
337 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
338 GetDescription(ComSafeArrayAsOutParam(retTypes),
339 ComSafeArrayAsOutParam(aRefs),
340 ComSafeArrayAsOutParam(aOvfValues),
341 ComSafeArrayAsOutParam(aVboxValues),
342 ComSafeArrayAsOutParam(aExtraConfigValues)));
343
344 RTPrintf("Virtual system %u:\n", i);
345
346 // look up the corresponding command line options, if any
347 ArgsMap *pmapArgs = NULL;
348 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
349 if (itm != mapArgsMapsPerVsys.end())
350 pmapArgs = &itm->second;
351
352 // this collects the final values for setFinalValues()
353 com::SafeArray<BOOL> aEnabled(retTypes.size());
354 com::SafeArray<BSTR> aFinalValues(retTypes.size());
355
356 for (unsigned a = 0; a < retTypes.size(); ++a)
357 {
358 VirtualSystemDescriptionType_T t = retTypes[a];
359
360 Utf8Str strOverride;
361
362 Bstr bstrFinalValue = aVboxValues[a];
363
364 bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a];
365
366 aEnabled[a] = true;
367
368 switch (t)
369 {
370 case VirtualSystemDescriptionType_OS:
371 if (findArgValue(strOverride, pmapArgs, "ostype"))
372 {
373 bstrFinalValue = strOverride;
374 RTPrintf("%2u: OS type specified with --ostype: \"%ls\"\n",
375 a, bstrFinalValue.raw());
376 }
377 else
378 RTPrintf("%2u: Suggested OS type: \"%ls\""
379 "\n (change with \"--vsys %u --ostype <type>\"; use \"list ostypes\" to list all possible values)\n",
380 a, bstrFinalValue.raw(), i);
381 break;
382
383 case VirtualSystemDescriptionType_Name:
384 if (findArgValue(strOverride, pmapArgs, "vmname"))
385 {
386 bstrFinalValue = strOverride;
387 RTPrintf("%2u: VM name specified with --vmname: \"%ls\"\n",
388 a, bstrFinalValue.raw());
389 }
390 else
391 RTPrintf("%2u: Suggested VM name \"%ls\""
392 "\n (change with \"--vsys %u --vmname <name>\")\n",
393 a, bstrFinalValue.raw(), i);
394 break;
395
396 case VirtualSystemDescriptionType_Product:
397 RTPrintf("%2u: Product (ignored): %ls\n",
398 a, aVboxValues[a]);
399 break;
400
401 case VirtualSystemDescriptionType_ProductUrl:
402 RTPrintf("%2u: ProductUrl (ignored): %ls\n",
403 a, aVboxValues[a]);
404 break;
405
406 case VirtualSystemDescriptionType_Vendor:
407 RTPrintf("%2u: Vendor (ignored): %ls\n",
408 a, aVboxValues[a]);
409 break;
410
411 case VirtualSystemDescriptionType_VendorUrl:
412 RTPrintf("%2u: VendorUrl (ignored): %ls\n",
413 a, aVboxValues[a]);
414 break;
415
416 case VirtualSystemDescriptionType_Version:
417 RTPrintf("%2u: Version (ignored): %ls\n",
418 a, aVboxValues[a]);
419 break;
420
421 case VirtualSystemDescriptionType_Description:
422 if (findArgValue(strOverride, pmapArgs, "description"))
423 {
424 bstrFinalValue = strOverride;
425 RTPrintf("%2u: Description specified with --description: \"%ls\"\n",
426 a, bstrFinalValue.raw());
427 }
428 else
429 RTPrintf("%2u: Description \"%ls\""
430 "\n (change with \"--vsys %u --description <desc>\")\n",
431 a, bstrFinalValue.raw(), i);
432 break;
433
434 case VirtualSystemDescriptionType_License:
435 ++cLicensesInTheWay;
436 if (findArgValue(strOverride, pmapArgs, "eula"))
437 {
438 if (strOverride == "show")
439 {
440 RTPrintf("%2u: End-user license agreement"
441 "\n (accept with \"--vsys %u --eula accept\"):"
442 "\n\n%ls\n\n",
443 a, i, bstrFinalValue.raw());
444 }
445 else if (strOverride == "accept")
446 {
447 RTPrintf("%2u: End-user license agreement (accepted)\n",
448 a);
449 --cLicensesInTheWay;
450 }
451 else
452 return errorSyntax(USAGE_IMPORTAPPLIANCE,
453 "Argument to --eula must be either \"show\" or \"accept\".");
454 }
455 else
456 RTPrintf("%2u: End-user license agreement"
457 "\n (display with \"--vsys %u --eula show\";"
458 "\n accept with \"--vsys %u --eula accept\")\n",
459 a, i, i);
460 break;
461
462 case VirtualSystemDescriptionType_CPU:
463 if (findArgValue(strOverride, pmapArgs, "cpus"))
464 {
465 uint32_t cCPUs;
466 if ( strOverride.toInt(cCPUs) == VINF_SUCCESS
467 && cCPUs >= VMM_MIN_CPU_COUNT
468 && cCPUs <= VMM_MAX_CPU_COUNT
469 )
470 {
471 bstrFinalValue = strOverride;
472 RTPrintf("%2u: No. of CPUs specified with --cpus: %ls\n",
473 a, bstrFinalValue.raw());
474 }
475 else
476 return errorSyntax(USAGE_IMPORTAPPLIANCE,
477 "Argument to --cpus option must be a number greater than %d and less than %d.",
478 VMM_MIN_CPU_COUNT - 1, VMM_MAX_CPU_COUNT + 1);
479 }
480 else
481 RTPrintf("%2u: Number of CPUs: %ls\n (change with \"--vsys %u --cpus <n>\")\n",
482 a, bstrFinalValue.raw(), i);
483 break;
484
485 case VirtualSystemDescriptionType_Memory:
486 {
487 if (findArgValue(strOverride, pmapArgs, "memory"))
488 {
489 uint32_t ulMemMB;
490 if (VINF_SUCCESS == strOverride.toInt(ulMemMB))
491 {
492 bstrFinalValue = strOverride;
493 RTPrintf("%2u: Guest memory specified with --memory: %ls MB\n",
494 a, bstrFinalValue.raw());
495 }
496 else
497 return errorSyntax(USAGE_IMPORTAPPLIANCE,
498 "Argument to --memory option must be a non-negative number.");
499 }
500 else
501 RTPrintf("%2u: Guest memory: %ls MB\n (change with \"--vsys %u --memory <MB>\")\n",
502 a, bstrFinalValue.raw(), i);
503 }
504 break;
505
506 case VirtualSystemDescriptionType_HardDiskControllerIDE:
507 if (fIgnoreThis)
508 {
509 RTPrintf("%2u: IDE controller, type %ls -- disabled\n",
510 a,
511 aVboxValues[a]);
512 aEnabled[a] = false;
513 }
514 else
515 RTPrintf("%2u: IDE controller, type %ls"
516 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
517 a,
518 aVboxValues[a],
519 i, a);
520 break;
521
522 case VirtualSystemDescriptionType_HardDiskControllerSATA:
523 if (fIgnoreThis)
524 {
525 RTPrintf("%2u: SATA controller, type %ls -- disabled\n",
526 a,
527 aVboxValues[a]);
528 aEnabled[a] = false;
529 }
530 else
531 RTPrintf("%2u: SATA controller, type %ls"
532 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
533 a,
534 aVboxValues[a],
535 i, a);
536 break;
537
538 case VirtualSystemDescriptionType_HardDiskControllerSAS:
539 if (fIgnoreThis)
540 {
541 RTPrintf("%2u: SAS controller, type %ls -- disabled\n",
542 a,
543 aVboxValues[a]);
544 aEnabled[a] = false;
545 }
546 else
547 RTPrintf("%2u: SAS controller, type %ls"
548 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
549 a,
550 aVboxValues[a],
551 i, a);
552 break;
553
554 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
555 if (fIgnoreThis)
556 {
557 RTPrintf("%2u: SCSI controller, type %ls -- disabled\n",
558 a,
559 aVboxValues[a]);
560 aEnabled[a] = false;
561 }
562 else
563 {
564 Utf8StrFmt strTypeArg("scsitype%u", a);
565 if (findArgValue(strOverride, pmapArgs, strTypeArg))
566 {
567 bstrFinalValue = strOverride;
568 RTPrintf("%2u: SCSI controller, type set with --unit %u --scsitype: \"%ls\"\n",
569 a,
570 a,
571 bstrFinalValue.raw());
572 }
573 else
574 RTPrintf("%2u: SCSI controller, type %ls"
575 "\n (change with \"--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}\";"
576 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
577 a,
578 aVboxValues[a],
579 i, a, i, a);
580 }
581 break;
582
583 case VirtualSystemDescriptionType_HardDiskImage:
584 if (fIgnoreThis)
585 {
586 RTPrintf("%2u: Hard disk image: source image=%ls -- disabled\n",
587 a,
588 aOvfValues[a]);
589 aEnabled[a] = false;
590 }
591 else
592 {
593 Utf8StrFmt strTypeArg("controller%u", a);
594 if (findArgValue(strOverride, pmapArgs, strTypeArg))
595 {
596 // strOverride now has the controller index as a number, but we
597 // need a "controller=X" format string
598 strOverride = Utf8StrFmt("controller=%s", strOverride.c_str());
599 Bstr bstrExtraConfigValue = strOverride;
600 bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]);
601 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n",
602 a,
603 aOvfValues[a],
604 aVboxValues[a],
605 aExtraConfigValues[a]);
606 }
607 else
608 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls"
609 "\n (change controller with \"--vsys %u --unit %u --controller <id>\";"
610 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
611 a,
612 aOvfValues[a],
613 aVboxValues[a],
614 aExtraConfigValues[a],
615 i, a, i, a);
616 }
617 break;
618
619 case VirtualSystemDescriptionType_CDROM:
620 if (fIgnoreThis)
621 {
622 RTPrintf("%2u: CD-ROM -- disabled\n",
623 a);
624 aEnabled[a] = false;
625 }
626 else
627 RTPrintf("%2u: CD-ROM"
628 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
629 a, i, a);
630 break;
631
632 case VirtualSystemDescriptionType_Floppy:
633 if (fIgnoreThis)
634 {
635 RTPrintf("%2u: Floppy -- disabled\n",
636 a);
637 aEnabled[a] = false;
638 }
639 else
640 RTPrintf("%2u: Floppy"
641 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
642 a, i, a);
643 break;
644
645 case VirtualSystemDescriptionType_NetworkAdapter:
646 RTPrintf("%2u: Network adapter: orig %ls, config %ls, extra %ls\n", // @todo implement once we have a plan for the back-end
647 a,
648 aOvfValues[a],
649 aVboxValues[a],
650 aExtraConfigValues[a]);
651 break;
652
653 case VirtualSystemDescriptionType_USBController:
654 if (fIgnoreThis)
655 {
656 RTPrintf("%2u: USB controller -- disabled\n",
657 a);
658 aEnabled[a] = false;
659 }
660 else
661 RTPrintf("%2u: USB controller"
662 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
663 a, i, a);
664 break;
665
666 case VirtualSystemDescriptionType_SoundCard:
667 if (fIgnoreThis)
668 {
669 RTPrintf("%2u: Sound card \"%ls\" -- disabled\n",
670 a,
671 aOvfValues[a]);
672 aEnabled[a] = false;
673 }
674 else
675 RTPrintf("%2u: Sound card (appliance expects \"%ls\", can change on import)"
676 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
677 a,
678 aOvfValues[a],
679 i,
680 a);
681 break;
682 }
683
684 bstrFinalValue.detachTo(&aFinalValues[a]);
685 }
686
687 if (fExecute)
688 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
689 SetFinalValues(ComSafeArrayAsInParam(aEnabled),
690 ComSafeArrayAsInParam(aFinalValues),
691 ComSafeArrayAsInParam(aExtraConfigValues)));
692
693 } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
694
695 if (cLicensesInTheWay == 1)
696 RTMsgError("Cannot import until the license agreement listed above is accepted.");
697 else if (cLicensesInTheWay > 1)
698 RTMsgError("Cannot import until the %c license agreements listed above are accepted.", cLicensesInTheWay);
699
700 if (!cLicensesInTheWay && fExecute)
701 {
702 // go!
703 ComPtr<IProgress> progress;
704 CHECK_ERROR_BREAK(pAppliance,
705 ImportMachines(progress.asOutParam()));
706
707 rc = showProgress(progress);
708
709 if (FAILED(rc))
710 {
711 com::ProgressErrorInfo info(progress);
712 com::GluePrintErrorInfo(info);
713 com::GluePrintErrorContext("ImportAppliance", __FILE__, __LINE__);
714 return 1;
715 }
716 else
717 RTPrintf("Successfully imported the appliance.\n");
718 }
719 } // end if (aVirtualSystemDescriptions.size() > 0)
720 } while (0);
721
722 return SUCCEEDED(rc) ? 0 : 1;
723}
724
725static const RTGETOPTDEF g_aExportOptions[]
726 = {
727 { "--output", 'o', RTGETOPT_REQ_STRING },
728 { "--legacy09", 'l', RTGETOPT_REQ_NOTHING },
729 { "--manifest", 'm', RTGETOPT_REQ_NOTHING },
730 { "--vsys", 's', RTGETOPT_REQ_UINT32 },
731 { "--product", 'p', RTGETOPT_REQ_STRING },
732 { "--producturl", 'P', RTGETOPT_REQ_STRING },
733 { "--vendor", 'd', RTGETOPT_REQ_STRING },
734 { "--vendorurl", 'D', RTGETOPT_REQ_STRING },
735 { "--version", 'v', RTGETOPT_REQ_STRING },
736 { "--eula", 'e', RTGETOPT_REQ_STRING },
737 { "--eulafile", 'E', RTGETOPT_REQ_STRING },
738 };
739
740int handleExportAppliance(HandlerArg *a)
741{
742 HRESULT rc = S_OK;
743
744 Utf8Str strOutputFile;
745 Utf8Str strOvfFormat("ovf-1.0"); // the default export version
746 bool fManifest = false; // the default
747 std::list< ComPtr<IMachine> > llMachines;
748
749 uint32_t ulCurVsys = (uint32_t)-1;
750 // for each --vsys X command, maintain a map of command line items
751 ArgsMapsMap mapArgsMapsPerVsys;
752 do
753 {
754 int c;
755
756 RTGETOPTUNION ValueUnion;
757 RTGETOPTSTATE GetState;
758 // start at 0 because main() has hacked both the argc and argv given to us
759 RTGetOptInit(&GetState, a->argc, a->argv, g_aExportOptions,
760 RT_ELEMENTS(g_aExportOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
761
762 Utf8Str strProductUrl;
763 while ((c = RTGetOpt(&GetState, &ValueUnion)))
764 {
765 switch (c)
766 {
767 case 'o': // --output
768 if (strOutputFile.length())
769 return errorSyntax(USAGE_EXPORTAPPLIANCE, "You can only specify --output once.");
770 else
771 strOutputFile = ValueUnion.psz;
772 break;
773
774 case 'l': // --legacy09
775 strOvfFormat = "ovf-0.9";
776 break;
777
778 case 'm': // --manifest
779 fManifest = true;
780 break;
781
782 case 's': // --vsys
783 ulCurVsys = ValueUnion.u32;
784 break;
785
786 case 'p': // --product
787 if (ulCurVsys == (uint32_t)-1)
788 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
789 mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz;
790 break;
791
792 case 'P': // --producturl
793 if (ulCurVsys == (uint32_t)-1)
794 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
795 mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz;
796 break;
797
798 case 'd': // --vendor
799 if (ulCurVsys == (uint32_t)-1)
800 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
801 mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz;
802 break;
803
804 case 'D': // --vendorurl
805 if (ulCurVsys == (uint32_t)-1)
806 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
807 mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz;
808 break;
809
810 case 'v': // --version
811 if (ulCurVsys == (uint32_t)-1)
812 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
813 mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz;
814 break;
815
816 case 'e': // --eula
817 if (ulCurVsys == (uint32_t)-1)
818 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
819 mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
820 break;
821
822 case 'E': // --eulafile
823 if (ulCurVsys == (uint32_t)-1)
824 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
825 mapArgsMapsPerVsys[ulCurVsys]["eulafile"] = ValueUnion.psz;
826 break;
827
828 case VINF_GETOPT_NOT_OPTION:
829 {
830 Utf8Str strMachine(ValueUnion.psz);
831 // must be machine: try UUID or name
832 ComPtr<IMachine> machine;
833 /* assume it's a UUID */
834 rc = a->virtualBox->GetMachine(Bstr(strMachine).raw(),
835 machine.asOutParam());
836 if (FAILED(rc) || !machine)
837 {
838 /* must be a name */
839 CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine).raw(),
840 machine.asOutParam()));
841 }
842
843 if (machine)
844 llMachines.push_back(machine);
845 }
846 break;
847
848 default:
849 if (c > 0)
850 {
851 if (RT_C_IS_GRAPH(c))
852 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: -%c", c);
853 else
854 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: %i", c);
855 }
856 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
857 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unknown option: %s", ValueUnion.psz);
858 else if (ValueUnion.pDef)
859 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
860 else
861 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%Rrs", c);
862 }
863
864 if (FAILED(rc))
865 break;
866 }
867
868 if (FAILED(rc))
869 break;
870
871 if (llMachines.size() == 0)
872 return errorSyntax(USAGE_EXPORTAPPLIANCE, "At least one machine must be specified with the export command.");
873 if (!strOutputFile.length())
874 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Missing --output argument with export command.");
875
876 // match command line arguments with the machines count
877 // this is only to sort out invalid indices at this time
878 ArgsMapsMap::const_iterator it;
879 for (it = mapArgsMapsPerVsys.begin();
880 it != mapArgsMapsPerVsys.end();
881 ++it)
882 {
883 uint32_t ulVsys = it->first;
884 if (ulVsys >= llMachines.size())
885 return errorSyntax(USAGE_EXPORTAPPLIANCE,
886 "Invalid index %RI32 with -vsys option; you specified only %zu virtual system(s).",
887 ulVsys, llMachines.size());
888 }
889
890 ComPtr<IAppliance> pAppliance;
891 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
892
893 std::list< ComPtr<IMachine> >::iterator itM;
894 uint32_t i=0;
895 for (itM = llMachines.begin();
896 itM != llMachines.end();
897 ++itM, ++i)
898 {
899 ComPtr<IMachine> pMachine = *itM;
900 ComPtr<IVirtualSystemDescription> pVSD;
901 CHECK_ERROR_BREAK(pMachine, Export(pAppliance, pVSD.asOutParam()));
902 // Add additional info to the virtal system description if the user wants so
903 ArgsMap *pmapArgs = NULL;
904 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
905 if (itm != mapArgsMapsPerVsys.end())
906 pmapArgs = &itm->second;
907 if (pmapArgs)
908 {
909 ArgsMap::iterator itD;
910 for (itD = pmapArgs->begin();
911 itD != pmapArgs->end();
912 ++itD)
913 {
914 if (itD->first == "product")
915 pVSD->AddDescription(VirtualSystemDescriptionType_Product,
916 Bstr(itD->second).raw(),
917 Bstr(itD->second).raw());
918 else if (itD->first == "producturl")
919 pVSD->AddDescription(VirtualSystemDescriptionType_ProductUrl,
920 Bstr(itD->second).raw(),
921 Bstr(itD->second).raw());
922 else if (itD->first == "vendor")
923 pVSD->AddDescription(VirtualSystemDescriptionType_Vendor,
924 Bstr(itD->second).raw(),
925 Bstr(itD->second).raw());
926 else if (itD->first == "vendorurl")
927 pVSD->AddDescription(VirtualSystemDescriptionType_VendorUrl,
928 Bstr(itD->second).raw(),
929 Bstr(itD->second).raw());
930 else if (itD->first == "version")
931 pVSD->AddDescription(VirtualSystemDescriptionType_Version,
932 Bstr(itD->second).raw(),
933 Bstr(itD->second).raw());
934 else if (itD->first == "eula")
935 pVSD->AddDescription(VirtualSystemDescriptionType_License,
936 Bstr(itD->second).raw(),
937 Bstr(itD->second).raw());
938 else if (itD->first == "eulafile")
939 {
940 Utf8Str strContent;
941 void *pvFile;
942 size_t cbFile;
943 int irc = RTFileReadAll(itD->second.c_str(), &pvFile, &cbFile);
944 if (RT_SUCCESS(irc))
945 {
946 Bstr bstrContent((char*)pvFile);
947 pVSD->AddDescription(VirtualSystemDescriptionType_License,
948 bstrContent.raw(),
949 bstrContent.raw());
950 RTFileReadAllFree(pvFile, cbFile);
951 }
952 else
953 {
954 RTMsgError("Cannot read license file \"%s\" which should be included in the virtual system %u.",
955 itD->second.c_str(), i);
956 return 1;
957 }
958 }
959 }
960 }
961 }
962
963 if (FAILED(rc))
964 break;
965
966 ComPtr<IProgress> progress;
967 char *pszAbsFilePath;
968 if (strOutputFile.startsWith("S3://", iprt::MiniString::CaseInsensitive) ||
969 strOutputFile.startsWith("SunCloud://", iprt::MiniString::CaseInsensitive) ||
970 strOutputFile.startsWith("webdav://", iprt::MiniString::CaseInsensitive))
971 pszAbsFilePath = RTStrDup(strOutputFile.c_str());
972 else
973 pszAbsFilePath = RTPathAbsDup(strOutputFile.c_str());
974 CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOvfFormat).raw(),
975 fManifest,
976 Bstr(pszAbsFilePath).raw(),
977 progress.asOutParam()));
978 RTStrFree(pszAbsFilePath);
979
980 rc = showProgress(progress);
981
982 if (FAILED(rc))
983 {
984 com::ProgressErrorInfo info(progress);
985 com::GluePrintErrorInfo(info);
986 com::GluePrintErrorContext("Write", __FILE__, __LINE__);
987 return 1;
988 }
989 else
990 RTPrintf("Successfully exported %d machine(s).\n", llMachines.size());
991
992 } while (0);
993
994 return SUCCEEDED(rc) ? 0 : 1;
995}
996
997#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