[16491] | 1 | /* $Id: VBoxManageAppliance.cpp 101472 2023-10-17 11:45:00Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * VBoxManage - The appliance-related commands.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2009-2023 Oracle and/or its affiliates.
|
---|
[16491] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[16491] | 26 | */
|
---|
| 27 |
|
---|
[94236] | 28 |
|
---|
[57358] | 29 | /*********************************************************************************************************************************
|
---|
| 30 | * Header Files *
|
---|
| 31 | *********************************************************************************************************************************/
|
---|
[16495] | 32 | #include <VBox/com/com.h>
|
---|
| 33 | #include <VBox/com/string.h>
|
---|
| 34 | #include <VBox/com/Guid.h>
|
---|
| 35 | #include <VBox/com/array.h>
|
---|
| 36 | #include <VBox/com/ErrorInfo.h>
|
---|
[20928] | 37 | #include <VBox/com/errorprint.h>
|
---|
[16495] | 38 | #include <VBox/com/VirtualBox.h>
|
---|
[84032] | 39 | #include <VBox/log.h>
|
---|
| 40 | #include <VBox/param.h>
|
---|
[16495] | 41 |
|
---|
[84032] | 42 | #include <VBox/version.h>
|
---|
| 43 |
|
---|
[16495] | 44 | #include <list>
|
---|
[16601] | 45 | #include <map>
|
---|
[16495] | 46 |
|
---|
[17098] | 47 | #include <iprt/getopt.h>
|
---|
[18108] | 48 | #include <iprt/ctype.h>
|
---|
[18775] | 49 | #include <iprt/path.h>
|
---|
[18809] | 50 | #include <iprt/file.h>
|
---|
[84032] | 51 | #include <iprt/err.h>
|
---|
| 52 | #include <iprt/zip.h>
|
---|
| 53 | #include <iprt/stream.h>
|
---|
| 54 | #include <iprt/vfs.h>
|
---|
| 55 | #include <iprt/manifest.h>
|
---|
| 56 | #include <iprt/crypto/digest.h>
|
---|
| 57 | #include <iprt/crypto/x509.h>
|
---|
| 58 | #include <iprt/crypto/pkcs7.h>
|
---|
| 59 | #include <iprt/crypto/store.h>
|
---|
| 60 | #include <iprt/crypto/spc.h>
|
---|
| 61 | #include <iprt/crypto/key.h>
|
---|
| 62 | #include <iprt/crypto/pkix.h>
|
---|
[16495] | 63 |
|
---|
| 64 |
|
---|
[84032] | 65 |
|
---|
[16491] | 66 | #include "VBoxManage.h"
|
---|
| 67 | using namespace com;
|
---|
| 68 |
|
---|
[92372] | 69 | DECLARE_TRANSLATION_CONTEXT(Appliance);
|
---|
[81086] | 70 |
|
---|
[92372] | 71 |
|
---|
[16491] | 72 | // funcs
|
---|
| 73 | ///////////////////////////////////////////////////////////////////////////////
|
---|
| 74 |
|
---|
[18754] | 75 | typedef std::map<Utf8Str, Utf8Str> ArgsMap; // pairs of strings like "vmname" => "newvmname"
|
---|
[16606] | 76 | typedef std::map<uint32_t, ArgsMap> ArgsMapsMap; // map of maps, one for each virtual system, sorted by index
|
---|
[16601] | 77 |
|
---|
[16606] | 78 | typedef std::map<uint32_t, bool> IgnoresMap; // pairs of numeric description entry indices
|
---|
| 79 | typedef std::map<uint32_t, IgnoresMap> IgnoresMapsMap; // map of maps, one for each virtual system, sorted by index
|
---|
| 80 |
|
---|
[16601] | 81 | static bool findArgValue(Utf8Str &strOut,
|
---|
[16606] | 82 | ArgsMap *pmapArgs,
|
---|
[16601] | 83 | const Utf8Str &strKey)
|
---|
| 84 | {
|
---|
| 85 | if (pmapArgs)
|
---|
| 86 | {
|
---|
[16606] | 87 | ArgsMap::iterator it;
|
---|
[16601] | 88 | it = pmapArgs->find(strKey);
|
---|
| 89 | if (it != pmapArgs->end())
|
---|
| 90 | {
|
---|
| 91 | strOut = it->second;
|
---|
[16606] | 92 | pmapArgs->erase(it);
|
---|
[16601] | 93 | return true;
|
---|
| 94 | }
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | return false;
|
---|
| 98 | }
|
---|
| 99 |
|
---|
[37862] | 100 | static int parseImportOptions(const char *psz, com::SafeArray<ImportOptions_T> *options)
|
---|
| 101 | {
|
---|
[98298] | 102 | int vrc = VINF_SUCCESS;
|
---|
| 103 | while (psz && *psz && RT_SUCCESS(vrc))
|
---|
[37862] | 104 | {
|
---|
| 105 | size_t len;
|
---|
| 106 | const char *pszComma = strchr(psz, ',');
|
---|
| 107 | if (pszComma)
|
---|
| 108 | len = pszComma - psz;
|
---|
| 109 | else
|
---|
| 110 | len = strlen(psz);
|
---|
| 111 | if (len > 0)
|
---|
| 112 | {
|
---|
| 113 | if (!RTStrNICmp(psz, "KeepAllMACs", len))
|
---|
| 114 | options->push_back(ImportOptions_KeepAllMACs);
|
---|
| 115 | else if (!RTStrNICmp(psz, "KeepNATMACs", len))
|
---|
| 116 | options->push_back(ImportOptions_KeepNATMACs);
|
---|
[54979] | 117 | else if (!RTStrNICmp(psz, "ImportToVDI", len))
|
---|
| 118 | options->push_back(ImportOptions_ImportToVDI);
|
---|
[37862] | 119 | else
|
---|
[98298] | 120 | vrc = VERR_PARSE_ERROR;
|
---|
[37862] | 121 | }
|
---|
| 122 | if (pszComma)
|
---|
| 123 | psz += len + 1;
|
---|
| 124 | else
|
---|
| 125 | psz += len;
|
---|
| 126 | }
|
---|
| 127 |
|
---|
[98298] | 128 | return vrc;
|
---|
[37862] | 129 | }
|
---|
| 130 |
|
---|
[93480] | 131 | /**
|
---|
| 132 | * Helper routine to parse the ExtraData Utf8Str for a storage controller's
|
---|
| 133 | * value or channel value.
|
---|
| 134 | *
|
---|
| 135 | * @param aExtraData The ExtraData string which can have a format of
|
---|
| 136 | * either 'controller=13;channel=3' or '11'.
|
---|
| 137 | * @param pszKey The string being looked up, usually either 'controller'
|
---|
| 138 | * or 'channel' but can be NULL or empty.
|
---|
| 139 | * @param puVal The integer value of the 'controller=' or 'channel='
|
---|
| 140 | * key (or the controller number when there is no key) in
|
---|
| 141 | * the ExtraData string.
|
---|
| 142 | * @returns COM status code.
|
---|
| 143 | */
|
---|
| 144 | static int getStorageControllerDetailsFromStr(const com::Utf8Str &aExtraData, const char *pszKey, uint32_t *puVal)
|
---|
| 145 | {
|
---|
| 146 | int vrc;
|
---|
| 147 |
|
---|
| 148 | if (pszKey && *pszKey)
|
---|
| 149 | {
|
---|
| 150 | size_t posKey = aExtraData.find(pszKey);
|
---|
| 151 | if (posKey == Utf8Str::npos)
|
---|
| 152 | return VERR_INVALID_PARAMETER;
|
---|
| 153 | vrc = RTStrToUInt32Ex(aExtraData.c_str() + posKey + strlen(pszKey), NULL, 0, puVal);
|
---|
| 154 | }
|
---|
| 155 | else
|
---|
| 156 | {
|
---|
| 157 | vrc = RTStrToUInt32Ex(aExtraData.c_str(), NULL, 0, puVal);
|
---|
| 158 | }
|
---|
| 159 |
|
---|
| 160 | if (vrc == VWRN_NUMBER_TOO_BIG || vrc == VWRN_NEGATIVE_UNSIGNED)
|
---|
| 161 | return VERR_INVALID_PARAMETER;
|
---|
| 162 |
|
---|
| 163 | return vrc;
|
---|
| 164 | }
|
---|
| 165 |
|
---|
| 166 | static bool isStorageControllerType(VirtualSystemDescriptionType_T avsdType)
|
---|
| 167 | {
|
---|
| 168 | switch (avsdType)
|
---|
| 169 | {
|
---|
| 170 | case VirtualSystemDescriptionType_HardDiskControllerIDE:
|
---|
| 171 | case VirtualSystemDescriptionType_HardDiskControllerSATA:
|
---|
| 172 | case VirtualSystemDescriptionType_HardDiskControllerSCSI:
|
---|
| 173 | case VirtualSystemDescriptionType_HardDiskControllerSAS:
|
---|
| 174 | case VirtualSystemDescriptionType_HardDiskControllerVirtioSCSI:
|
---|
| 175 | return true;
|
---|
| 176 | default:
|
---|
| 177 | return false;
|
---|
| 178 | }
|
---|
| 179 | }
|
---|
| 180 |
|
---|
[18754] | 181 | static const RTGETOPTDEF g_aImportApplianceOptions[] =
|
---|
| 182 | {
|
---|
| 183 | { "--dry-run", 'n', RTGETOPT_REQ_NOTHING },
|
---|
| 184 | { "-dry-run", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
|
---|
| 185 | { "--dryrun", 'n', RTGETOPT_REQ_NOTHING },
|
---|
| 186 | { "-dryrun", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
|
---|
| 187 | { "--detailed-progress", 'P', RTGETOPT_REQ_NOTHING },
|
---|
| 188 | { "-detailed-progress", 'P', RTGETOPT_REQ_NOTHING }, // deprecated
|
---|
| 189 | { "--vsys", 's', RTGETOPT_REQ_UINT32 },
|
---|
| 190 | { "-vsys", 's', RTGETOPT_REQ_UINT32 }, // deprecated
|
---|
| 191 | { "--ostype", 'o', RTGETOPT_REQ_STRING },
|
---|
| 192 | { "-ostype", 'o', RTGETOPT_REQ_STRING }, // deprecated
|
---|
| 193 | { "--vmname", 'V', RTGETOPT_REQ_STRING },
|
---|
| 194 | { "-vmname", 'V', RTGETOPT_REQ_STRING }, // deprecated
|
---|
[72476] | 195 | { "--settingsfile", 'S', RTGETOPT_REQ_STRING },
|
---|
| 196 | { "--basefolder", 'p', RTGETOPT_REQ_STRING },
|
---|
| 197 | { "--group", 'g', RTGETOPT_REQ_STRING },
|
---|
[25142] | 198 | { "--memory", 'm', RTGETOPT_REQ_STRING },
|
---|
| 199 | { "-memory", 'm', RTGETOPT_REQ_STRING }, // deprecated
|
---|
| 200 | { "--cpus", 'c', RTGETOPT_REQ_STRING },
|
---|
[18809] | 201 | { "--description", 'd', RTGETOPT_REQ_STRING },
|
---|
[18754] | 202 | { "--eula", 'L', RTGETOPT_REQ_STRING },
|
---|
| 203 | { "-eula", 'L', RTGETOPT_REQ_STRING }, // deprecated
|
---|
| 204 | { "--unit", 'u', RTGETOPT_REQ_UINT32 },
|
---|
| 205 | { "-unit", 'u', RTGETOPT_REQ_UINT32 }, // deprecated
|
---|
| 206 | { "--ignore", 'x', RTGETOPT_REQ_NOTHING },
|
---|
| 207 | { "-ignore", 'x', RTGETOPT_REQ_NOTHING }, // deprecated
|
---|
| 208 | { "--scsitype", 'T', RTGETOPT_REQ_UINT32 },
|
---|
| 209 | { "-scsitype", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
|
---|
| 210 | { "--type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
|
---|
| 211 | { "-type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
|
---|
[33363] | 212 | { "--controller", 'C', RTGETOPT_REQ_STRING },
|
---|
[93480] | 213 | { "--port", 'E', RTGETOPT_REQ_STRING },
|
---|
[33361] | 214 | { "--disk", 'D', RTGETOPT_REQ_STRING },
|
---|
[37862] | 215 | { "--options", 'O', RTGETOPT_REQ_STRING },
|
---|
[78428] | 216 |
|
---|
| 217 | { "--cloud", 'j', RTGETOPT_REQ_NOTHING},
|
---|
| 218 | { "--cloudprofile", 'k', RTGETOPT_REQ_STRING },
|
---|
[78841] | 219 | { "--cloudinstanceid", 'l', RTGETOPT_REQ_STRING },
|
---|
| 220 | { "--cloudbucket", 'B', RTGETOPT_REQ_STRING }
|
---|
[18754] | 221 | };
|
---|
| 222 |
|
---|
[91204] | 223 | typedef enum APPLIANCETYPE
|
---|
[78428] | 224 | {
|
---|
| 225 | NOT_SET, LOCAL, CLOUD
|
---|
[91204] | 226 | } APPLIANCETYPE;
|
---|
[78428] | 227 |
|
---|
[56118] | 228 | RTEXITCODE handleImportAppliance(HandlerArg *arg)
|
---|
[16491] | 229 | {
|
---|
[95140] | 230 | HRESULT hrc = S_OK;
|
---|
[91204] | 231 | APPLIANCETYPE enmApplType = NOT_SET;
|
---|
[16495] | 232 | Utf8Str strOvfFilename;
|
---|
[16830] | 233 | bool fExecute = true; // if true, then we actually do the import
|
---|
[37862] | 234 | com::SafeArray<ImportOptions_T> options;
|
---|
[16601] | 235 | uint32_t ulCurVsys = (uint32_t)-1;
|
---|
[18754] | 236 | uint32_t ulCurUnit = (uint32_t)-1;
|
---|
| 237 | // for each --vsys X command, maintain a map of command line items
|
---|
[16601] | 238 | // (we'll parse them later after interpreting the OVF, when we can
|
---|
| 239 | // actually check whether they make sense semantically)
|
---|
| 240 | ArgsMapsMap mapArgsMapsPerVsys;
|
---|
[16606] | 241 | IgnoresMapsMap mapIgnoresMapsPerVsys;
|
---|
[16601] | 242 |
|
---|
[18754] | 243 | int c;
|
---|
| 244 | RTGETOPTUNION ValueUnion;
|
---|
| 245 | RTGETOPTSTATE GetState;
|
---|
| 246 | // start at 0 because main() has hacked both the argc and argv given to us
|
---|
[26517] | 247 | RTGetOptInit(&GetState, arg->argc, arg->argv, g_aImportApplianceOptions, RT_ELEMENTS(g_aImportApplianceOptions),
|
---|
| 248 | 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
|
---|
[18754] | 249 | while ((c = RTGetOpt(&GetState, &ValueUnion)))
|
---|
[16495] | 250 | {
|
---|
[18754] | 251 | switch (c)
|
---|
[16601] | 252 | {
|
---|
[18754] | 253 | case 'n': // --dry-run
|
---|
| 254 | fExecute = false;
|
---|
| 255 | break;
|
---|
[16606] | 256 |
|
---|
[18754] | 257 | case 'P': // --detailed-progress
|
---|
| 258 | g_fDetailedProgress = true;
|
---|
| 259 | break;
|
---|
[16601] | 260 |
|
---|
[18754] | 261 | case 's': // --vsys
|
---|
[91204] | 262 | if (enmApplType == NOT_SET)
|
---|
| 263 | enmApplType = LOCAL;
|
---|
[78428] | 264 |
|
---|
[91204] | 265 | if (enmApplType != LOCAL)
|
---|
[93703] | 266 | return errorSyntax(Appliance::tr("Option \"%s\" can't be used together with \"--cloud\" option."),
|
---|
[78428] | 267 | GetState.pDef->pszLong);
|
---|
[91204] | 268 | if (ValueUnion.u32 == (uint32_t)-1)
|
---|
[93703] | 269 | return errorSyntax(Appliance::tr("Value of option \"%s\" is out of range."),
|
---|
[91204] | 270 | GetState.pDef->pszLong);
|
---|
[78428] | 271 |
|
---|
[18754] | 272 | ulCurVsys = ValueUnion.u32;
|
---|
| 273 | ulCurUnit = (uint32_t)-1;
|
---|
| 274 | break;
|
---|
[16606] | 275 |
|
---|
[18754] | 276 | case 'o': // --ostype
|
---|
[91204] | 277 | if (enmApplType == NOT_SET)
|
---|
[93703] | 278 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."),
|
---|
[92372] | 279 | GetState.pDef->pszLong);
|
---|
[18754] | 280 | mapArgsMapsPerVsys[ulCurVsys]["ostype"] = ValueUnion.psz;
|
---|
| 281 | break;
|
---|
| 282 |
|
---|
| 283 | case 'V': // --vmname
|
---|
[91204] | 284 | if (enmApplType == NOT_SET)
|
---|
[93703] | 285 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."),
|
---|
[92372] | 286 | GetState.pDef->pszLong);
|
---|
[18754] | 287 | mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz;
|
---|
| 288 | break;
|
---|
| 289 |
|
---|
[72476] | 290 | case 'S': // --settingsfile
|
---|
[91204] | 291 | if (enmApplType != LOCAL)
|
---|
[93703] | 292 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 293 | GetState.pDef->pszLong);
|
---|
[72476] | 294 | mapArgsMapsPerVsys[ulCurVsys]["settingsfile"] = ValueUnion.psz;
|
---|
| 295 | break;
|
---|
| 296 |
|
---|
| 297 | case 'p': // --basefolder
|
---|
[91204] | 298 | if (enmApplType == NOT_SET)
|
---|
[93703] | 299 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."),
|
---|
[92372] | 300 | GetState.pDef->pszLong);
|
---|
[72476] | 301 | mapArgsMapsPerVsys[ulCurVsys]["basefolder"] = ValueUnion.psz;
|
---|
| 302 | break;
|
---|
| 303 |
|
---|
| 304 | case 'g': // --group
|
---|
[91204] | 305 | if (enmApplType != LOCAL)
|
---|
[93703] | 306 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 307 | GetState.pDef->pszLong);
|
---|
[72476] | 308 | mapArgsMapsPerVsys[ulCurVsys]["group"] = ValueUnion.psz;
|
---|
| 309 | break;
|
---|
| 310 |
|
---|
[18809] | 311 | case 'd': // --description
|
---|
[91204] | 312 | if (enmApplType == NOT_SET)
|
---|
[93703] | 313 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."),
|
---|
[92372] | 314 | GetState.pDef->pszLong);
|
---|
[18809] | 315 | mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
|
---|
| 316 | break;
|
---|
| 317 |
|
---|
| 318 | case 'L': // --eula
|
---|
[91204] | 319 | if (enmApplType != LOCAL)
|
---|
[93703] | 320 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 321 | GetState.pDef->pszLong);
|
---|
[18809] | 322 | mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
|
---|
| 323 | break;
|
---|
| 324 |
|
---|
[18754] | 325 | case 'm': // --memory
|
---|
[91204] | 326 | if (enmApplType == NOT_SET)
|
---|
[93703] | 327 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."),
|
---|
[92372] | 328 | GetState.pDef->pszLong);
|
---|
[18754] | 329 | mapArgsMapsPerVsys[ulCurVsys]["memory"] = ValueUnion.psz;
|
---|
| 330 | break;
|
---|
| 331 |
|
---|
[25142] | 332 | case 'c': // --cpus
|
---|
[91204] | 333 | if (enmApplType == NOT_SET)
|
---|
[93703] | 334 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."),
|
---|
[92372] | 335 | GetState.pDef->pszLong);
|
---|
[25142] | 336 | mapArgsMapsPerVsys[ulCurVsys]["cpus"] = ValueUnion.psz;
|
---|
| 337 | break;
|
---|
| 338 |
|
---|
[18754] | 339 | case 'u': // --unit
|
---|
[91204] | 340 | if (enmApplType != LOCAL)
|
---|
[93703] | 341 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 342 | GetState.pDef->pszLong);
|
---|
[91204] | 343 | if (ValueUnion.u32 == (uint32_t)-1)
|
---|
[93703] | 344 | return errorSyntax(Appliance::tr("Value of option \"%s\" is out of range."),
|
---|
[91204] | 345 | GetState.pDef->pszLong);
|
---|
| 346 |
|
---|
[18754] | 347 | ulCurUnit = ValueUnion.u32;
|
---|
| 348 | break;
|
---|
| 349 |
|
---|
| 350 | case 'x': // --ignore
|
---|
[91204] | 351 | if (enmApplType != LOCAL)
|
---|
[93703] | 352 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 353 | GetState.pDef->pszLong);
|
---|
[18754] | 354 | if (ulCurUnit == (uint32_t)-1)
|
---|
[93703] | 355 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --unit option."),
|
---|
[92372] | 356 | GetState.pDef->pszLong);
|
---|
[18754] | 357 | mapIgnoresMapsPerVsys[ulCurVsys][ulCurUnit] = true;
|
---|
| 358 | break;
|
---|
| 359 |
|
---|
| 360 | case 'T': // --scsitype
|
---|
[91204] | 361 | if (enmApplType != LOCAL)
|
---|
[93703] | 362 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 363 | GetState.pDef->pszLong);
|
---|
[18754] | 364 | if (ulCurUnit == (uint32_t)-1)
|
---|
[93703] | 365 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --unit option."),
|
---|
[92372] | 366 | GetState.pDef->pszLong);
|
---|
[18754] | 367 | mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("scsitype%u", ulCurUnit)] = ValueUnion.psz;
|
---|
| 368 | break;
|
---|
| 369 |
|
---|
| 370 | case 'C': // --controller
|
---|
[91204] | 371 | if (enmApplType != LOCAL)
|
---|
[93703] | 372 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 373 | GetState.pDef->pszLong);
|
---|
[18754] | 374 | if (ulCurUnit == (uint32_t)-1)
|
---|
[93703] | 375 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --unit option."),
|
---|
[92372] | 376 | GetState.pDef->pszLong);
|
---|
[18754] | 377 | mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("controller%u", ulCurUnit)] = ValueUnion.psz;
|
---|
| 378 | break;
|
---|
| 379 |
|
---|
[93480] | 380 | case 'E': // --port
|
---|
| 381 | if (enmApplType != LOCAL)
|
---|
[93703] | 382 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[93480] | 383 | GetState.pDef->pszLong);
|
---|
| 384 | if (ulCurUnit == (uint32_t)-1)
|
---|
[93703] | 385 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --unit option."),
|
---|
[93480] | 386 | GetState.pDef->pszLong);
|
---|
| 387 | mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("port%u", ulCurUnit)] = ValueUnion.psz;
|
---|
| 388 | break;
|
---|
| 389 |
|
---|
[33361] | 390 | case 'D': // --disk
|
---|
[91204] | 391 | if (enmApplType != LOCAL)
|
---|
[93703] | 392 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 393 | GetState.pDef->pszLong);
|
---|
[33361] | 394 | if (ulCurUnit == (uint32_t)-1)
|
---|
[93703] | 395 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --unit option."),
|
---|
[92372] | 396 | GetState.pDef->pszLong);
|
---|
[33361] | 397 | mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("disk%u", ulCurUnit)] = ValueUnion.psz;
|
---|
| 398 | break;
|
---|
| 399 |
|
---|
[37862] | 400 | case 'O': // --options
|
---|
| 401 | if (RT_FAILURE(parseImportOptions(ValueUnion.psz, &options)))
|
---|
[92372] | 402 | return errorArgument(Appliance::tr("Invalid import options '%s'\n"), ValueUnion.psz);
|
---|
[37862] | 403 | break;
|
---|
| 404 |
|
---|
[81086] | 405 | /*--cloud and --vsys are orthogonal, only one must be presented*/
|
---|
[78428] | 406 | case 'j': // --cloud
|
---|
[91204] | 407 | if (enmApplType == NOT_SET)
|
---|
| 408 | enmApplType = CLOUD;
|
---|
[78428] | 409 |
|
---|
[91204] | 410 | if (enmApplType != CLOUD)
|
---|
[93703] | 411 | return errorSyntax(Appliance::tr("Option \"%s\" can't be used together with \"--vsys\" option."),
|
---|
[78428] | 412 | GetState.pDef->pszLong);
|
---|
| 413 |
|
---|
| 414 | ulCurVsys = 0;
|
---|
| 415 | break;
|
---|
| 416 |
|
---|
| 417 | /* Cloud export settings */
|
---|
| 418 | case 'k': // --cloudprofile
|
---|
[91204] | 419 | if (enmApplType != CLOUD)
|
---|
[93703] | 420 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[78428] | 421 | GetState.pDef->pszLong);
|
---|
| 422 | mapArgsMapsPerVsys[ulCurVsys]["cloudprofile"] = ValueUnion.psz;
|
---|
| 423 | break;
|
---|
| 424 |
|
---|
| 425 | case 'l': // --cloudinstanceid
|
---|
[91204] | 426 | if (enmApplType != CLOUD)
|
---|
[93703] | 427 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[78428] | 428 | GetState.pDef->pszLong);
|
---|
| 429 | mapArgsMapsPerVsys[ulCurVsys]["cloudinstanceid"] = ValueUnion.psz;
|
---|
| 430 | break;
|
---|
| 431 |
|
---|
[78841] | 432 | case 'B': // --cloudbucket
|
---|
[91204] | 433 | if (enmApplType != CLOUD)
|
---|
[93703] | 434 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[78841] | 435 | GetState.pDef->pszLong);
|
---|
| 436 | mapArgsMapsPerVsys[ulCurVsys]["cloudbucket"] = ValueUnion.psz;
|
---|
| 437 | break;
|
---|
| 438 |
|
---|
[18754] | 439 | case VINF_GETOPT_NOT_OPTION:
|
---|
[21394] | 440 | if (strOvfFilename.isEmpty())
|
---|
[18754] | 441 | strOvfFilename = ValueUnion.psz;
|
---|
[16606] | 442 | else
|
---|
[93703] | 443 | return errorSyntax(Appliance::tr("Invalid parameter '%s'"), ValueUnion.psz);
|
---|
[18754] | 444 | break;
|
---|
| 445 |
|
---|
| 446 | default:
|
---|
| 447 | if (c > 0)
|
---|
[16606] | 448 | {
|
---|
[18754] | 449 | if (RT_C_IS_PRINT(c))
|
---|
[93703] | 450 | return errorSyntax(Appliance::tr("Invalid option -%c"), c);
|
---|
[18754] | 451 | else
|
---|
[93703] | 452 | return errorSyntax(Appliance::tr("Invalid option case %i"), c);
|
---|
[16606] | 453 | }
|
---|
[18754] | 454 | else if (c == VERR_GETOPT_UNKNOWN_OPTION)
|
---|
[93703] | 455 | return errorSyntax(Appliance::tr("unknown option: %s\n"), ValueUnion.psz);
|
---|
[18754] | 456 | else if (ValueUnion.pDef)
|
---|
[93703] | 457 | return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c);
|
---|
[18754] | 458 | else
|
---|
[93703] | 459 | return errorSyntax(Appliance::tr("error: %Rrs"), c);
|
---|
[16601] | 460 | }
|
---|
[16495] | 461 | }
|
---|
| 462 |
|
---|
[81515] | 463 | /* Last check after parsing all arguments */
|
---|
[91204] | 464 | if (strOvfFilename.isEmpty())
|
---|
[93703] | 465 | return errorSyntax(Appliance::tr("Not enough arguments for \"import\" command."));
|
---|
[16495] | 466 |
|
---|
[91204] | 467 | if (enmApplType == NOT_SET)
|
---|
| 468 | enmApplType = LOCAL;
|
---|
| 469 |
|
---|
[16495] | 470 | do
|
---|
| 471 | {
|
---|
[17033] | 472 | ComPtr<IAppliance> pAppliance;
|
---|
[24998] | 473 | CHECK_ERROR_BREAK(arg->virtualBox, CreateAppliance(pAppliance.asOutParam()));
|
---|
[78428] | 474 | //in the case of Cloud, append the instance id here because later it's harder to do
|
---|
[91204] | 475 | if (enmApplType == CLOUD)
|
---|
[78428] | 476 | {
|
---|
[78858] | 477 | try
|
---|
| 478 | {
|
---|
| 479 | /* Check presence of cloudprofile and cloudinstanceid in the map.
|
---|
| 480 | * If there isn't the exception is triggered. It's standard std:map logic.*/
|
---|
| 481 | ArgsMap a = mapArgsMapsPerVsys[ulCurVsys];
|
---|
[84349] | 482 | (void)a.at("cloudprofile");
|
---|
| 483 | (void)a.at("cloudinstanceid");
|
---|
| 484 | }
|
---|
| 485 | catch (...)
|
---|
[78858] | 486 | {
|
---|
[93703] | 487 | return errorSyntax(Appliance::tr("Not enough arguments for import from the Cloud."));
|
---|
[78858] | 488 | }
|
---|
| 489 |
|
---|
[78428] | 490 | strOvfFilename.append(mapArgsMapsPerVsys[ulCurVsys]["cloudprofile"]);
|
---|
| 491 | strOvfFilename.append("/");
|
---|
| 492 | strOvfFilename.append(mapArgsMapsPerVsys[ulCurVsys]["cloudinstanceid"]);
|
---|
| 493 | }
|
---|
| 494 |
|
---|
[21612] | 495 | char *pszAbsFilePath;
|
---|
[36527] | 496 | if (strOvfFilename.startsWith("S3://", RTCString::CaseInsensitive) ||
|
---|
| 497 | strOvfFilename.startsWith("SunCloud://", RTCString::CaseInsensitive) ||
|
---|
[78428] | 498 | strOvfFilename.startsWith("webdav://", RTCString::CaseInsensitive) ||
|
---|
| 499 | strOvfFilename.startsWith("OCI://", RTCString::CaseInsensitive))
|
---|
[21612] | 500 | pszAbsFilePath = RTStrDup(strOvfFilename.c_str());
|
---|
| 501 | else
|
---|
| 502 | pszAbsFilePath = RTPathAbsDup(strOvfFilename.c_str());
|
---|
[78428] | 503 |
|
---|
[21612] | 504 | ComPtr<IProgress> progressRead;
|
---|
[32718] | 505 | CHECK_ERROR_BREAK(pAppliance, Read(Bstr(pszAbsFilePath).raw(),
|
---|
| 506 | progressRead.asOutParam()));
|
---|
[18775] | 507 | RTStrFree(pszAbsFilePath);
|
---|
[16662] | 508 |
|
---|
[95140] | 509 | hrc = showProgress(progressRead);
|
---|
[92372] | 510 | CHECK_PROGRESS_ERROR_RET(progressRead, (Appliance::tr("Appliance read failed")), RTEXITCODE_FAILURE);
|
---|
[21612] | 511 |
|
---|
| 512 | Bstr path; /* fetch the path, there is stuff like username/password removed if any */
|
---|
| 513 | CHECK_ERROR_BREAK(pAppliance, COMGETTER(Path)(path.asOutParam()));
|
---|
[17827] | 514 |
|
---|
[78858] | 515 | size_t cVirtualSystemDescriptions = 0;
|
---|
[16495] | 516 | com::SafeIfaceArray<IVirtualSystemDescription> aVirtualSystemDescriptions;
|
---|
[16601] | 517 |
|
---|
[91204] | 518 | if (enmApplType == LOCAL)
|
---|
[16495] | 519 | {
|
---|
[78428] | 520 | // call interpret(); this can yield both warnings and errors, so we need
|
---|
| 521 | // to tinker with the error info a bit
|
---|
[92372] | 522 | RTStrmPrintf(g_pStdErr, Appliance::tr("Interpreting %ls...\n"), path.raw());
|
---|
[95140] | 523 | hrc = pAppliance->Interpret();
|
---|
[89719] | 524 | com::ErrorInfoKeeper eik;
|
---|
[78428] | 525 |
|
---|
[89719] | 526 | /** @todo r=klaus Eliminate this special way of signalling
|
---|
| 527 | * warnings which should be part of the ErrorInfo. */
|
---|
[78428] | 528 | com::SafeArray<BSTR> aWarnings;
|
---|
| 529 | if (SUCCEEDED(pAppliance->GetWarnings(ComSafeArrayAsOutParam(aWarnings))))
|
---|
| 530 | {
|
---|
| 531 | size_t cWarnings = aWarnings.size();
|
---|
| 532 | for (unsigned i = 0; i < cWarnings; ++i)
|
---|
| 533 | {
|
---|
| 534 | Bstr bstrWarning(aWarnings[i]);
|
---|
[97533] | 535 | RTMsgWarning("%ls", bstrWarning.raw());
|
---|
[78428] | 536 | }
|
---|
| 537 | }
|
---|
| 538 |
|
---|
[89719] | 539 | eik.restore();
|
---|
[95140] | 540 | if (FAILED(hrc)) // during interpret, after printing warnings
|
---|
[78428] | 541 | {
|
---|
[95140] | 542 | com::GlueHandleComError(pAppliance, "Interpret()", hrc, __FILE__, __LINE__);
|
---|
[78428] | 543 | break;
|
---|
| 544 | }
|
---|
| 545 |
|
---|
| 546 | RTStrmPrintf(g_pStdErr, "OK.\n");
|
---|
| 547 |
|
---|
| 548 | // fetch all disks
|
---|
| 549 | com::SafeArray<BSTR> retDisks;
|
---|
| 550 | CHECK_ERROR_BREAK(pAppliance,
|
---|
| 551 | COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
|
---|
| 552 | if (retDisks.size() > 0)
|
---|
| 553 | {
|
---|
[92372] | 554 | RTPrintf(Appliance::tr("Disks:\n"));
|
---|
[78428] | 555 | for (unsigned i = 0; i < retDisks.size(); i++)
|
---|
| 556 | RTPrintf(" %ls\n", retDisks[i]);
|
---|
| 557 | RTPrintf("\n");
|
---|
| 558 | }
|
---|
| 559 |
|
---|
[78858] | 560 | // fetch virtual system descriptions
|
---|
| 561 | CHECK_ERROR_BREAK(pAppliance,
|
---|
| 562 | COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
|
---|
| 563 |
|
---|
| 564 | cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
|
---|
| 565 |
|
---|
[78428] | 566 | // match command line arguments with virtual system descriptions;
|
---|
| 567 | // this is only to sort out invalid indices at this time
|
---|
| 568 | ArgsMapsMap::const_iterator it;
|
---|
| 569 | for (it = mapArgsMapsPerVsys.begin();
|
---|
| 570 | it != mapArgsMapsPerVsys.end();
|
---|
| 571 | ++it)
|
---|
| 572 | {
|
---|
| 573 | uint32_t ulVsys = it->first;
|
---|
| 574 | if (ulVsys >= cVirtualSystemDescriptions)
|
---|
[93703] | 575 | return errorSyntax(Appliance::tr("Invalid index %RI32 with -vsys option; the OVF contains only %zu virtual system(s).",
|
---|
[92594] | 576 | "", cVirtualSystemDescriptions),
|
---|
[78428] | 577 | ulVsys, cVirtualSystemDescriptions);
|
---|
| 578 | }
|
---|
[16601] | 579 | }
|
---|
[91204] | 580 | else if (enmApplType == CLOUD)
|
---|
[78858] | 581 | {
|
---|
| 582 | /* In the Cloud case the call of interpret() isn't needed because there isn't any OVF XML file.
|
---|
| 583 | * All info is got from the Cloud and VSD is filled inside IAppliance::read(). */
|
---|
| 584 | // fetch virtual system descriptions
|
---|
| 585 | CHECK_ERROR_BREAK(pAppliance,
|
---|
| 586 | COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
|
---|
[16601] | 587 |
|
---|
[78858] | 588 | cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
|
---|
| 589 | }
|
---|
| 590 |
|
---|
[18223] | 591 | uint32_t cLicensesInTheWay = 0;
|
---|
| 592 |
|
---|
[16601] | 593 | // dump virtual system descriptions and match command-line arguments
|
---|
| 594 | if (cVirtualSystemDescriptions > 0)
|
---|
| 595 | {
|
---|
| 596 | for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
|
---|
[16495] | 597 | {
|
---|
| 598 | com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
|
---|
[16515] | 599 | com::SafeArray<BSTR> aRefs;
|
---|
[17291] | 600 | com::SafeArray<BSTR> aOvfValues;
|
---|
[50323] | 601 | com::SafeArray<BSTR> aVBoxValues;
|
---|
[16495] | 602 | com::SafeArray<BSTR> aExtraConfigValues;
|
---|
| 603 | CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
|
---|
| 604 | GetDescription(ComSafeArrayAsOutParam(retTypes),
|
---|
| 605 | ComSafeArrayAsOutParam(aRefs),
|
---|
[17291] | 606 | ComSafeArrayAsOutParam(aOvfValues),
|
---|
[50323] | 607 | ComSafeArrayAsOutParam(aVBoxValues),
|
---|
[16495] | 608 | ComSafeArrayAsOutParam(aExtraConfigValues)));
|
---|
| 609 |
|
---|
[92372] | 610 | RTPrintf(Appliance::tr("Virtual system %u:\n"), i);
|
---|
[16601] | 611 |
|
---|
| 612 | // look up the corresponding command line options, if any
|
---|
[16606] | 613 | ArgsMap *pmapArgs = NULL;
|
---|
| 614 | ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
|
---|
[16601] | 615 | if (itm != mapArgsMapsPerVsys.end())
|
---|
| 616 | pmapArgs = &itm->second;
|
---|
| 617 |
|
---|
| 618 | // this collects the final values for setFinalValues()
|
---|
| 619 | com::SafeArray<BOOL> aEnabled(retTypes.size());
|
---|
| 620 | com::SafeArray<BSTR> aFinalValues(retTypes.size());
|
---|
| 621 |
|
---|
[16495] | 622 | for (unsigned a = 0; a < retTypes.size(); ++a)
|
---|
| 623 | {
|
---|
| 624 | VirtualSystemDescriptionType_T t = retTypes[a];
|
---|
| 625 |
|
---|
[16601] | 626 | Utf8Str strOverride;
|
---|
[16495] | 627 |
|
---|
[50323] | 628 | Bstr bstrFinalValue = aVBoxValues[a];
|
---|
[16601] | 629 |
|
---|
[16606] | 630 | bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a];
|
---|
| 631 |
|
---|
[16830] | 632 | aEnabled[a] = true;
|
---|
| 633 |
|
---|
[16495] | 634 | switch (t)
|
---|
| 635 | {
|
---|
[18809] | 636 | case VirtualSystemDescriptionType_OS:
|
---|
| 637 | if (findArgValue(strOverride, pmapArgs, "ostype"))
|
---|
| 638 | {
|
---|
| 639 | bstrFinalValue = strOverride;
|
---|
[92372] | 640 | RTPrintf(Appliance::tr("%2u: OS type specified with --ostype: \"%ls\"\n"),
|
---|
[18809] | 641 | a, bstrFinalValue.raw());
|
---|
| 642 | }
|
---|
| 643 | else
|
---|
[93703] | 644 | RTPrintf(Appliance::tr("%2u: Suggested OS type: \"%ls\"\n"
|
---|
| 645 | " (change with \"--vsys %u --ostype <type>\"; use \"list ostypes\" to list all possible values)\n"),
|
---|
[18809] | 646 | a, bstrFinalValue.raw(), i);
|
---|
[63300] | 647 | break;
|
---|
[18809] | 648 |
|
---|
[16495] | 649 | case VirtualSystemDescriptionType_Name:
|
---|
[18754] | 650 | if (findArgValue(strOverride, pmapArgs, "vmname"))
|
---|
[16601] | 651 | {
|
---|
| 652 | bstrFinalValue = strOverride;
|
---|
[92372] | 653 | RTPrintf(Appliance::tr("%2u: VM name specified with --vmname: \"%ls\"\n"),
|
---|
[16601] | 654 | a, bstrFinalValue.raw());
|
---|
| 655 | }
|
---|
| 656 | else
|
---|
[93703] | 657 | RTPrintf(Appliance::tr("%2u: Suggested VM name \"%ls\"\n"
|
---|
| 658 | " (change with \"--vsys %u --vmname <name>\")\n"),
|
---|
[16601] | 659 | a, bstrFinalValue.raw(), i);
|
---|
[63300] | 660 | break;
|
---|
[16495] | 661 |
|
---|
[18809] | 662 | case VirtualSystemDescriptionType_Product:
|
---|
[92372] | 663 | RTPrintf(Appliance::tr("%2u: Product (ignored): %ls\n"),
|
---|
[50323] | 664 | a, aVBoxValues[a]);
|
---|
[63300] | 665 | break;
|
---|
[18809] | 666 |
|
---|
| 667 | case VirtualSystemDescriptionType_ProductUrl:
|
---|
[92372] | 668 | RTPrintf(Appliance::tr("%2u: ProductUrl (ignored): %ls\n"),
|
---|
[50323] | 669 | a, aVBoxValues[a]);
|
---|
[63300] | 670 | break;
|
---|
[18809] | 671 |
|
---|
| 672 | case VirtualSystemDescriptionType_Vendor:
|
---|
[92372] | 673 | RTPrintf(Appliance::tr("%2u: Vendor (ignored): %ls\n"),
|
---|
[50323] | 674 | a, aVBoxValues[a]);
|
---|
[63300] | 675 | break;
|
---|
[18809] | 676 |
|
---|
| 677 | case VirtualSystemDescriptionType_VendorUrl:
|
---|
[92372] | 678 | RTPrintf(Appliance::tr("%2u: VendorUrl (ignored): %ls\n"),
|
---|
[50323] | 679 | a, aVBoxValues[a]);
|
---|
[63300] | 680 | break;
|
---|
[18809] | 681 |
|
---|
| 682 | case VirtualSystemDescriptionType_Version:
|
---|
[92372] | 683 | RTPrintf(Appliance::tr("%2u: Version (ignored): %ls\n"),
|
---|
[50323] | 684 | a, aVBoxValues[a]);
|
---|
[63300] | 685 | break;
|
---|
[18809] | 686 |
|
---|
| 687 | case VirtualSystemDescriptionType_Description:
|
---|
| 688 | if (findArgValue(strOverride, pmapArgs, "description"))
|
---|
[16601] | 689 | {
|
---|
| 690 | bstrFinalValue = strOverride;
|
---|
[92372] | 691 | RTPrintf(Appliance::tr("%2u: Description specified with --description: \"%ls\"\n"),
|
---|
[16601] | 692 | a, bstrFinalValue.raw());
|
---|
| 693 | }
|
---|
| 694 | else
|
---|
[93703] | 695 | RTPrintf(Appliance::tr("%2u: Description \"%ls\"\n"
|
---|
| 696 | " (change with \"--vsys %u --description <desc>\")\n"),
|
---|
[16601] | 697 | a, bstrFinalValue.raw(), i);
|
---|
[63300] | 698 | break;
|
---|
[16495] | 699 |
|
---|
[18223] | 700 | case VirtualSystemDescriptionType_License:
|
---|
| 701 | ++cLicensesInTheWay;
|
---|
[18754] | 702 | if (findArgValue(strOverride, pmapArgs, "eula"))
|
---|
[18223] | 703 | {
|
---|
| 704 | if (strOverride == "show")
|
---|
| 705 | {
|
---|
[93703] | 706 | RTPrintf(Appliance::tr("%2u: End-user license agreement\n"
|
---|
| 707 | " (accept with \"--vsys %u --eula accept\"):\n"
|
---|
| 708 | "\n%ls\n\n"),
|
---|
[18223] | 709 | a, i, bstrFinalValue.raw());
|
---|
| 710 | }
|
---|
| 711 | else if (strOverride == "accept")
|
---|
| 712 | {
|
---|
[92372] | 713 | RTPrintf(Appliance::tr("%2u: End-user license agreement (accepted)\n"),
|
---|
[18223] | 714 | a);
|
---|
| 715 | --cLicensesInTheWay;
|
---|
| 716 | }
|
---|
| 717 | else
|
---|
[93703] | 718 | return errorSyntax(Appliance::tr("Argument to --eula must be either \"show\" or \"accept\"."));
|
---|
[18223] | 719 | }
|
---|
| 720 | else
|
---|
[93703] | 721 | RTPrintf(Appliance::tr("%2u: End-user license agreement\n"
|
---|
| 722 | " (display with \"--vsys %u --eula show\";\n"
|
---|
| 723 | " accept with \"--vsys %u --eula accept\")\n"),
|
---|
[18223] | 724 | a, i, i);
|
---|
[63300] | 725 | break;
|
---|
[18223] | 726 |
|
---|
[16495] | 727 | case VirtualSystemDescriptionType_CPU:
|
---|
[25142] | 728 | if (findArgValue(strOverride, pmapArgs, "cpus"))
|
---|
| 729 | {
|
---|
| 730 | uint32_t cCPUs;
|
---|
[25165] | 731 | if ( strOverride.toInt(cCPUs) == VINF_SUCCESS
|
---|
| 732 | && cCPUs >= VMM_MIN_CPU_COUNT
|
---|
| 733 | && cCPUs <= VMM_MAX_CPU_COUNT
|
---|
[25142] | 734 | )
|
---|
| 735 | {
|
---|
| 736 | bstrFinalValue = strOverride;
|
---|
[92372] | 737 | RTPrintf(Appliance::tr("%2u: No. of CPUs specified with --cpus: %ls\n"),
|
---|
[25142] | 738 | a, bstrFinalValue.raw());
|
---|
| 739 | }
|
---|
| 740 | else
|
---|
[93703] | 741 | return errorSyntax(Appliance::tr("Argument to --cpus option must be a number greater than %d and less than %d."),
|
---|
[25165] | 742 | VMM_MIN_CPU_COUNT - 1, VMM_MAX_CPU_COUNT + 1);
|
---|
[25142] | 743 | }
|
---|
| 744 | else
|
---|
[92372] | 745 | RTPrintf(Appliance::tr("%2u: Number of CPUs: %ls\n (change with \"--vsys %u --cpus <n>\")\n"),
|
---|
[25142] | 746 | a, bstrFinalValue.raw(), i);
|
---|
[63300] | 747 | break;
|
---|
[16495] | 748 |
|
---|
| 749 | case VirtualSystemDescriptionType_Memory:
|
---|
[16601] | 750 | {
|
---|
[18754] | 751 | if (findArgValue(strOverride, pmapArgs, "memory"))
|
---|
[16601] | 752 | {
|
---|
| 753 | uint32_t ulMemMB;
|
---|
| 754 | if (VINF_SUCCESS == strOverride.toInt(ulMemMB))
|
---|
| 755 | {
|
---|
[97536] | 756 | /* 'VBoxManage import --memory' size is in megabytes */
|
---|
| 757 | RTPrintf(Appliance::tr("%2u: Guest memory specified with --memory: %RU32 MB\n"),
|
---|
| 758 | a, ulMemMB);
|
---|
[99604] | 759 |
|
---|
| 760 | /* IVirtualSystemDescription guest memory size is in bytes.
|
---|
| 761 | It's alway stored in bytes in VSD according to the old internal agreement within the team */
|
---|
| 762 | uint64_t ullMemBytes = (uint64_t)ulMemMB * _1M;
|
---|
| 763 | strOverride = Utf8StrFmt("%RU64", ullMemBytes);
|
---|
[16601] | 764 | bstrFinalValue = strOverride;
|
---|
| 765 | }
|
---|
| 766 | else
|
---|
[93703] | 767 | return errorSyntax(Appliance::tr("Argument to --memory option must be a non-negative number."));
|
---|
[16601] | 768 | }
|
---|
| 769 | else
|
---|
[97533] | 770 | {
|
---|
| 771 | strOverride = aVBoxValues[a];
|
---|
[99604] | 772 | uint64_t ullMemMB = strOverride.toUInt64() / _1M;
|
---|
[97533] | 773 | RTPrintf(Appliance::tr("%2u: Guest memory: %RU64 MB\n (change with \"--vsys %u --memory <MB>\")\n"),
|
---|
| 774 | a, ullMemMB, i);
|
---|
| 775 | }
|
---|
[63300] | 776 | break;
|
---|
[16601] | 777 | }
|
---|
[16495] | 778 |
|
---|
| 779 | case VirtualSystemDescriptionType_HardDiskControllerIDE:
|
---|
[16606] | 780 | if (fIgnoreThis)
|
---|
| 781 | {
|
---|
[92372] | 782 | RTPrintf(Appliance::tr("%2u: IDE controller, type %ls -- disabled\n"),
|
---|
[16606] | 783 | a,
|
---|
[50323] | 784 | aVBoxValues[a]);
|
---|
[16606] | 785 | aEnabled[a] = false;
|
---|
| 786 | }
|
---|
| 787 | else
|
---|
[93703] | 788 | RTPrintf(Appliance::tr("%2u: IDE controller, type %ls\n"
|
---|
| 789 | " (disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
[16606] | 790 | a,
|
---|
[50323] | 791 | aVBoxValues[a],
|
---|
[16606] | 792 | i, a);
|
---|
[63300] | 793 | break;
|
---|
[16495] | 794 |
|
---|
| 795 | case VirtualSystemDescriptionType_HardDiskControllerSATA:
|
---|
[16606] | 796 | if (fIgnoreThis)
|
---|
| 797 | {
|
---|
[92372] | 798 | RTPrintf(Appliance::tr("%2u: SATA controller, type %ls -- disabled\n"),
|
---|
[16606] | 799 | a,
|
---|
[50323] | 800 | aVBoxValues[a]);
|
---|
[16606] | 801 | aEnabled[a] = false;
|
---|
| 802 | }
|
---|
| 803 | else
|
---|
[93703] | 804 | RTPrintf(Appliance::tr("%2u: SATA controller, type %ls\n"
|
---|
| 805 | " (disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
[16606] | 806 | a,
|
---|
[50323] | 807 | aVBoxValues[a],
|
---|
[16606] | 808 | i, a);
|
---|
[63300] | 809 | break;
|
---|
[16495] | 810 |
|
---|
[29994] | 811 | case VirtualSystemDescriptionType_HardDiskControllerSAS:
|
---|
| 812 | if (fIgnoreThis)
|
---|
| 813 | {
|
---|
[92372] | 814 | RTPrintf(Appliance::tr("%2u: SAS controller, type %ls -- disabled\n"),
|
---|
[29994] | 815 | a,
|
---|
[50323] | 816 | aVBoxValues[a]);
|
---|
[29994] | 817 | aEnabled[a] = false;
|
---|
| 818 | }
|
---|
| 819 | else
|
---|
[93703] | 820 | RTPrintf(Appliance::tr("%2u: SAS controller, type %ls\n"
|
---|
| 821 | " (disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
[29994] | 822 | a,
|
---|
[50323] | 823 | aVBoxValues[a],
|
---|
[29994] | 824 | i, a);
|
---|
[63300] | 825 | break;
|
---|
[29994] | 826 |
|
---|
[16495] | 827 | case VirtualSystemDescriptionType_HardDiskControllerSCSI:
|
---|
[16606] | 828 | if (fIgnoreThis)
|
---|
| 829 | {
|
---|
[92372] | 830 | RTPrintf(Appliance::tr("%2u: SCSI controller, type %ls -- disabled\n"),
|
---|
[16606] | 831 | a,
|
---|
[50323] | 832 | aVBoxValues[a]);
|
---|
[16606] | 833 | aEnabled[a] = false;
|
---|
| 834 | }
|
---|
| 835 | else
|
---|
[16834] | 836 | {
|
---|
[18754] | 837 | Utf8StrFmt strTypeArg("scsitype%u", a);
|
---|
[16834] | 838 | if (findArgValue(strOverride, pmapArgs, strTypeArg))
|
---|
| 839 | {
|
---|
| 840 | bstrFinalValue = strOverride;
|
---|
[92372] | 841 | RTPrintf(Appliance::tr("%2u: SCSI controller, type set with --unit %u --scsitype: \"%ls\"\n"),
|
---|
[16834] | 842 | a,
|
---|
| 843 | a,
|
---|
| 844 | bstrFinalValue.raw());
|
---|
| 845 | }
|
---|
| 846 | else
|
---|
[93703] | 847 | RTPrintf(Appliance::tr("%2u: SCSI controller, type %ls\n"
|
---|
| 848 | " (change with \"--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}\";\n"
|
---|
| 849 | " disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
[16834] | 850 | a,
|
---|
[50323] | 851 | aVBoxValues[a],
|
---|
[16834] | 852 | i, a, i, a);
|
---|
| 853 | }
|
---|
[63300] | 854 | break;
|
---|
[16495] | 855 |
|
---|
[84534] | 856 | case VirtualSystemDescriptionType_HardDiskControllerVirtioSCSI:
|
---|
| 857 | if (fIgnoreThis)
|
---|
| 858 | {
|
---|
[92372] | 859 | RTPrintf(Appliance::tr("%2u: VirtioSCSI controller, type %ls -- disabled\n"),
|
---|
[84534] | 860 | a,
|
---|
| 861 | aVBoxValues[a]);
|
---|
| 862 | aEnabled[a] = false;
|
---|
| 863 | }
|
---|
| 864 | else
|
---|
[93703] | 865 | RTPrintf(Appliance::tr("%2u: VirtioSCSI controller, type %ls\n"
|
---|
| 866 | " (disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
[84534] | 867 | a,
|
---|
| 868 | aVBoxValues[a],
|
---|
| 869 | i, a);
|
---|
| 870 | break;
|
---|
| 871 |
|
---|
[101472] | 872 | case VirtualSystemDescriptionType_HardDiskControllerNVMe:
|
---|
| 873 | if (fIgnoreThis)
|
---|
| 874 | {
|
---|
| 875 | RTPrintf(Appliance::tr("%2u: NVMe controller, type %ls -- disabled\n"),
|
---|
| 876 | a,
|
---|
| 877 | aVBoxValues[a]);
|
---|
| 878 | aEnabled[a] = false;
|
---|
| 879 | }
|
---|
| 880 | else
|
---|
| 881 | RTPrintf(Appliance::tr("%2u: NVMe controller, type %ls\n"
|
---|
| 882 | " (disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
| 883 | a,
|
---|
| 884 | aVBoxValues[a],
|
---|
| 885 | i, a);
|
---|
| 886 | break;
|
---|
| 887 |
|
---|
[16495] | 888 | case VirtualSystemDescriptionType_HardDiskImage:
|
---|
[16606] | 889 | if (fIgnoreThis)
|
---|
| 890 | {
|
---|
[92372] | 891 | RTPrintf(Appliance::tr("%2u: Hard disk image: source image=%ls -- disabled\n"),
|
---|
[16606] | 892 | a,
|
---|
[17291] | 893 | aOvfValues[a]);
|
---|
[16606] | 894 | aEnabled[a] = false;
|
---|
| 895 | }
|
---|
| 896 | else
|
---|
[16834] | 897 | {
|
---|
[33361] | 898 | Utf8StrFmt strTypeArg("disk%u", a);
|
---|
[93480] | 899 | bool fDiskChanged = false;
|
---|
| 900 | int vrc;
|
---|
[54979] | 901 | RTCList<ImportOptions_T> optionsList = options.toList();
|
---|
| 902 |
|
---|
[16834] | 903 | if (findArgValue(strOverride, pmapArgs, strTypeArg))
|
---|
| 904 | {
|
---|
[93480] | 905 | if (optionsList.contains(ImportOptions_ImportToVDI))
|
---|
[93703] | 906 | return errorSyntax(Appliance::tr("Option --ImportToVDI can not be used together with a manually set target path."));
|
---|
[93480] | 907 | RTUUID uuid;
|
---|
| 908 | /* Check if this is a uuid. If so, don't touch. */
|
---|
| 909 | vrc = RTUuidFromStr(&uuid, strOverride.c_str());
|
---|
| 910 | if (vrc != VINF_SUCCESS)
|
---|
[33361] | 911 | {
|
---|
[93480] | 912 | /* Make the path absolute. */
|
---|
| 913 | if (!RTPathStartsWithRoot(strOverride.c_str()))
|
---|
[33361] | 914 | {
|
---|
[93480] | 915 | char pszPwd[RTPATH_MAX];
|
---|
| 916 | vrc = RTPathGetCurrent(pszPwd, RTPATH_MAX);
|
---|
| 917 | if (RT_SUCCESS(vrc))
|
---|
| 918 | strOverride = Utf8Str(pszPwd).append(RTPATH_SLASH).append(strOverride);
|
---|
[33361] | 919 | }
|
---|
| 920 | }
|
---|
[93480] | 921 | bstrFinalValue = strOverride;
|
---|
| 922 | fDiskChanged = true;
|
---|
| 923 | }
|
---|
| 924 |
|
---|
| 925 | strTypeArg.printf("controller%u", a);
|
---|
| 926 | bool fControllerChanged = false;
|
---|
| 927 | uint32_t uTargetController = (uint32_t)-1;
|
---|
| 928 | VirtualSystemDescriptionType_T vsdControllerType = VirtualSystemDescriptionType_Ignore;
|
---|
| 929 | Utf8Str strExtraConfigValue;
|
---|
| 930 | if (findArgValue(strOverride, pmapArgs, strTypeArg))
|
---|
| 931 | {
|
---|
| 932 | vrc = getStorageControllerDetailsFromStr(strOverride, NULL, &uTargetController);
|
---|
| 933 | if (RT_FAILURE(vrc))
|
---|
[93703] | 934 | return errorSyntax(Appliance::tr("Invalid controller value: '%s'"),
|
---|
[93480] | 935 | strOverride.c_str());
|
---|
[54979] | 936 |
|
---|
[93480] | 937 | vsdControllerType = retTypes[uTargetController];
|
---|
| 938 | if (!isStorageControllerType(vsdControllerType))
|
---|
[93703] | 939 | return errorSyntax(Appliance::tr("Invalid storage controller specified: %u"),
|
---|
[93480] | 940 | uTargetController);
|
---|
[54979] | 941 |
|
---|
[93480] | 942 | fControllerChanged = true;
|
---|
[16834] | 943 | }
|
---|
[93480] | 944 |
|
---|
| 945 | strTypeArg.printf("port%u", a);
|
---|
| 946 | bool fControllerPortChanged = false;
|
---|
| 947 | uint32_t uTargetControllerPort = (uint32_t)-1;;
|
---|
[33363] | 948 | if (findArgValue(strOverride, pmapArgs, strTypeArg))
|
---|
| 949 | {
|
---|
[93480] | 950 | vrc = getStorageControllerDetailsFromStr(strOverride, NULL, &uTargetControllerPort);
|
---|
| 951 | if (RT_FAILURE(vrc))
|
---|
[93703] | 952 | return errorSyntax(Appliance::tr("Invalid port value: '%s'"),
|
---|
[93480] | 953 | strOverride.c_str());
|
---|
| 954 |
|
---|
| 955 | fControllerPortChanged = true;
|
---|
| 956 | }
|
---|
| 957 |
|
---|
| 958 | /*
|
---|
| 959 | * aExtraConfigValues[a] has a format of 'controller=12;channel=0' and is set by
|
---|
| 960 | * Appliance::interpret() so any parsing errors here aren't due to user-supplied
|
---|
| 961 | * values so different error messages here.
|
---|
| 962 | */
|
---|
| 963 | uint32_t uOrigController;
|
---|
| 964 | Utf8Str strOrigController(Bstr(aExtraConfigValues[a]).raw());
|
---|
| 965 | vrc = getStorageControllerDetailsFromStr(strOrigController, "controller=", &uOrigController);
|
---|
| 966 | if (RT_FAILURE(vrc))
|
---|
| 967 | return RTMsgErrorExitFailure(Appliance::tr("Failed to extract controller value from ExtraConfig: '%s'"),
|
---|
| 968 | strOrigController.c_str());
|
---|
| 969 |
|
---|
| 970 | uint32_t uOrigControllerPort;
|
---|
| 971 | vrc = getStorageControllerDetailsFromStr(strOrigController, "channel=", &uOrigControllerPort);
|
---|
| 972 | if (RT_FAILURE(vrc))
|
---|
| 973 | return RTMsgErrorExitFailure(Appliance::tr("Failed to extract channel value from ExtraConfig: '%s'"),
|
---|
| 974 | strOrigController.c_str());
|
---|
| 975 |
|
---|
| 976 | /*
|
---|
| 977 | * The 'strExtraConfigValue' string is used to display the storage controller and
|
---|
| 978 | * port details for each virtual hard disk using the more accurate 'controller=' and
|
---|
| 979 | * 'port=' labels. The aExtraConfigValues[a] string has a format of
|
---|
| 980 | * 'controller=%u;channel=%u' from Appliance::interpret() which is required as per
|
---|
| 981 | * the API but for consistency and clarity with the CLI options --controller and
|
---|
| 982 | * --port we instead use strExtraConfigValue in the output below.
|
---|
| 983 | */
|
---|
| 984 | strExtraConfigValue = Utf8StrFmt("controller=%u;port=%u", uOrigController, uOrigControllerPort);
|
---|
| 985 |
|
---|
| 986 | if (fControllerChanged || fControllerPortChanged)
|
---|
| 987 | {
|
---|
| 988 | /*
|
---|
| 989 | * Verify that the new combination of controller and controller port is valid.
|
---|
| 990 | * cf. StorageController::i_checkPortAndDeviceValid()
|
---|
| 991 | */
|
---|
| 992 | if (uTargetControllerPort == (uint32_t)-1)
|
---|
| 993 | uTargetControllerPort = uOrigControllerPort;
|
---|
| 994 | if (uTargetController == (uint32_t)-1)
|
---|
| 995 | uTargetController = uOrigController;
|
---|
| 996 |
|
---|
| 997 | if ( uOrigController == uTargetController
|
---|
| 998 | && uOrigControllerPort == uTargetControllerPort)
|
---|
[93703] | 999 | return errorSyntax(Appliance::tr("Device already attached to controller %u at this port (%u) location."),
|
---|
[93480] | 1000 | uTargetController,
|
---|
| 1001 | uTargetControllerPort);
|
---|
| 1002 |
|
---|
| 1003 | if (vsdControllerType == VirtualSystemDescriptionType_Ignore)
|
---|
| 1004 | vsdControllerType = retTypes[uOrigController];
|
---|
| 1005 | if (!isStorageControllerType(vsdControllerType))
|
---|
[93703] | 1006 | return errorSyntax(Appliance::tr("Invalid storage controller specified: %u"),
|
---|
[93480] | 1007 | uOrigController);
|
---|
| 1008 |
|
---|
| 1009 | ComPtr<IVirtualBox> pVirtualBox = arg->virtualBox;
|
---|
| 1010 | ComPtr<ISystemProperties> systemProperties;
|
---|
| 1011 | CHECK_ERROR(pVirtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam()));
|
---|
| 1012 | ULONG maxPorts = 0;
|
---|
| 1013 | StorageBus_T enmStorageBus = StorageBus_Null;;
|
---|
| 1014 | switch (vsdControllerType)
|
---|
| 1015 | {
|
---|
| 1016 | case VirtualSystemDescriptionType_HardDiskControllerIDE:
|
---|
| 1017 | enmStorageBus = StorageBus_IDE;
|
---|
| 1018 | break;
|
---|
| 1019 | case VirtualSystemDescriptionType_HardDiskControllerSATA:
|
---|
| 1020 | enmStorageBus = StorageBus_SATA;
|
---|
| 1021 | break;
|
---|
| 1022 | case VirtualSystemDescriptionType_HardDiskControllerSCSI:
|
---|
| 1023 | enmStorageBus = StorageBus_SCSI;
|
---|
| 1024 | break;
|
---|
| 1025 | case VirtualSystemDescriptionType_HardDiskControllerSAS:
|
---|
| 1026 | enmStorageBus = StorageBus_SAS;
|
---|
| 1027 | break;
|
---|
| 1028 | case VirtualSystemDescriptionType_HardDiskControllerVirtioSCSI:
|
---|
| 1029 | enmStorageBus = StorageBus_VirtioSCSI;
|
---|
| 1030 | break;
|
---|
| 1031 | default: // Not reached since vsdControllerType validated above but silence gcc.
|
---|
| 1032 | break;
|
---|
| 1033 | }
|
---|
[101035] | 1034 |
|
---|
| 1035 | PlatformArchitecture_T platformArch = PlatformArchitecture_x86; /** @todo BUGBUG Appliances only handle x86 so far! */
|
---|
| 1036 |
|
---|
| 1037 | ComPtr<IPlatformProperties> pPlatformProperties;
|
---|
| 1038 | CHECK_ERROR_RET(pVirtualBox, GetPlatformProperties(platformArch, pPlatformProperties.asOutParam()),
|
---|
| 1039 | RTEXITCODE_FAILURE);
|
---|
| 1040 |
|
---|
| 1041 | CHECK_ERROR_RET(pPlatformProperties, GetMaxPortCountForStorageBus(enmStorageBus, &maxPorts),
|
---|
| 1042 | RTEXITCODE_FAILURE);
|
---|
[93480] | 1043 | if (uTargetControllerPort >= maxPorts)
|
---|
[93703] | 1044 | return errorSyntax(Appliance::tr("Illegal port value: %u. For %ls controllers the only valid values are 0 to %lu (inclusive)"),
|
---|
[93480] | 1045 | uTargetControllerPort,
|
---|
| 1046 | aVBoxValues[uTargetController],
|
---|
| 1047 | maxPorts);
|
---|
| 1048 |
|
---|
| 1049 | /*
|
---|
| 1050 | * The 'strOverride' string will be mapped to the strExtraConfigCurrent value in
|
---|
| 1051 | * VirtualSystemDescription::setFinalValues() which is then used in the appliance
|
---|
| 1052 | * import routines i_importVBoxMachine()/i_importMachineGeneric() later. This
|
---|
| 1053 | * aExtraConfigValues[] array entry must have a format of
|
---|
| 1054 | * 'controller=<index>;channel=<c>' as per the API documentation.
|
---|
| 1055 | */
|
---|
| 1056 | strExtraConfigValue = Utf8StrFmt("controller=%u;port=%u", uTargetController,
|
---|
| 1057 | uTargetControllerPort);
|
---|
| 1058 | strOverride = Utf8StrFmt("controller=%u;channel=%u", uTargetController,
|
---|
| 1059 | uTargetControllerPort);
|
---|
[33363] | 1060 | Bstr bstrExtraConfigValue = strOverride;
|
---|
| 1061 | bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]);
|
---|
| 1062 | }
|
---|
[93480] | 1063 |
|
---|
| 1064 | if (fDiskChanged && !fControllerChanged && !fControllerPortChanged)
|
---|
| 1065 | {
|
---|
[93703] | 1066 | RTPrintf(Appliance::tr("%2u: Hard disk image specified with --disk: source image=%ls, target path=%ls, %s\n"
|
---|
| 1067 | " (change controller with \"--vsys %u --unit %u --controller <index>\";\n"
|
---|
| 1068 | " change controller port with \"--vsys %u --unit %u --port <n>\")\n"),
|
---|
[93480] | 1069 | a,
|
---|
| 1070 | aOvfValues[a],
|
---|
| 1071 | bstrFinalValue.raw(),
|
---|
| 1072 | strExtraConfigValue.c_str(),
|
---|
| 1073 | i, a,
|
---|
| 1074 | i, a);
|
---|
| 1075 | }
|
---|
| 1076 | else if (fDiskChanged && fControllerChanged && !fControllerPortChanged)
|
---|
| 1077 | {
|
---|
[93703] | 1078 | RTPrintf(Appliance::tr("%2u: Hard disk image specified with --disk and --controller: source image=%ls, target path=%ls, %s\n"
|
---|
| 1079 | " (change controller port with \"--vsys %u --unit %u --port <n>\")\n"),
|
---|
[93480] | 1080 | a,
|
---|
| 1081 | aOvfValues[a],
|
---|
| 1082 | bstrFinalValue.raw(),
|
---|
| 1083 | strExtraConfigValue.c_str(),
|
---|
| 1084 | i, a);
|
---|
| 1085 | }
|
---|
| 1086 | else if (fDiskChanged && !fControllerChanged && fControllerPortChanged)
|
---|
| 1087 | {
|
---|
[93703] | 1088 | RTPrintf(Appliance::tr("%2u: Hard disk image specified with --disk and --port: source image=%ls, target path=%ls, %s\n"
|
---|
| 1089 | " (change controller with \"--vsys %u --unit %u --controller <index>\")\n"),
|
---|
[93480] | 1090 | a,
|
---|
| 1091 | aOvfValues[a],
|
---|
| 1092 | bstrFinalValue.raw(),
|
---|
| 1093 | strExtraConfigValue.c_str(),
|
---|
| 1094 | i, a);
|
---|
| 1095 | }
|
---|
| 1096 | else if (!fDiskChanged && fControllerChanged && fControllerPortChanged)
|
---|
| 1097 | {
|
---|
[93703] | 1098 | RTPrintf(Appliance::tr("%2u: Hard disk image specified with --controller and --port: source image=%ls, target path=%ls, %s\n"
|
---|
| 1099 | " (change target path with \"--vsys %u --unit %u --disk path\")\n"),
|
---|
[93480] | 1100 | a,
|
---|
| 1101 | aOvfValues[a],
|
---|
| 1102 | bstrFinalValue.raw(),
|
---|
| 1103 | strExtraConfigValue.c_str(),
|
---|
| 1104 | i, a);
|
---|
| 1105 | }
|
---|
| 1106 | else if (!fDiskChanged && !fControllerChanged && fControllerPortChanged)
|
---|
| 1107 | {
|
---|
[93703] | 1108 | RTPrintf(Appliance::tr("%2u: Hard disk image specified with --port: source image=%ls, target path=%ls, %s\n"
|
---|
| 1109 | " (change target path with \"--vsys %u --unit %u --disk path\";\n"
|
---|
| 1110 | " change controller with \"--vsys %u --unit %u --controller <index>\")\n"),
|
---|
[93480] | 1111 | a,
|
---|
| 1112 | aOvfValues[a],
|
---|
| 1113 | bstrFinalValue.raw(),
|
---|
| 1114 | strExtraConfigValue.c_str(),
|
---|
| 1115 | i, a,
|
---|
| 1116 | i, a);
|
---|
| 1117 | }
|
---|
| 1118 | else if (!fDiskChanged && fControllerChanged && !fControllerPortChanged)
|
---|
| 1119 | {
|
---|
[93703] | 1120 | RTPrintf(Appliance::tr("%2u: Hard disk image specified with --controller: source image=%ls, target path=%ls, %s\n"
|
---|
| 1121 | " (change target path with \"--vsys %u --unit %u --disk path\";\n"
|
---|
| 1122 | " change controller port with \"--vsys %u --unit %u --port <n>\")\n"),
|
---|
[93480] | 1123 | a,
|
---|
| 1124 | aOvfValues[a],
|
---|
| 1125 | bstrFinalValue.raw(),
|
---|
| 1126 | strExtraConfigValue.c_str(),
|
---|
| 1127 | i, a,
|
---|
| 1128 | i, a);
|
---|
| 1129 | }
|
---|
| 1130 | else if (fDiskChanged && fControllerChanged && fControllerPortChanged)
|
---|
| 1131 | {
|
---|
[93703] | 1132 | RTPrintf(Appliance::tr("%2u: Hard disk image specified with --disk and --controller and --port: source image=%ls, target path=%ls, %s\n"),
|
---|
[93480] | 1133 | a,
|
---|
| 1134 | aOvfValues[a],
|
---|
| 1135 | bstrFinalValue.raw(),
|
---|
| 1136 | strExtraConfigValue.c_str());
|
---|
| 1137 | }
|
---|
[16834] | 1138 | else
|
---|
[54979] | 1139 | {
|
---|
| 1140 | strOverride = aVBoxValues[a];
|
---|
| 1141 |
|
---|
| 1142 | /*
|
---|
[54991] | 1143 | * Current solution isn't optimal.
|
---|
[54979] | 1144 | * Better way is to provide API call for function
|
---|
| 1145 | * Appliance::i_findMediumFormatFromDiskImage()
|
---|
| 1146 | * and creating one new function which returns
|
---|
| 1147 | * struct ovf::DiskImage for currently processed disk.
|
---|
[93480] | 1148 | */
|
---|
[54979] | 1149 |
|
---|
| 1150 | /*
|
---|
| 1151 | * if user wants to convert all imported disks to VDI format
|
---|
| 1152 | * we need to replace files extensions to "vdi"
|
---|
| 1153 | * except CD/DVD disks
|
---|
| 1154 | */
|
---|
[54985] | 1155 | if (optionsList.contains(ImportOptions_ImportToVDI))
|
---|
[54979] | 1156 | {
|
---|
| 1157 | ComPtr<IVirtualBox> pVirtualBox = arg->virtualBox;
|
---|
| 1158 | ComPtr<ISystemProperties> systemProperties;
|
---|
| 1159 | com::SafeIfaceArray<IMediumFormat> mediumFormats;
|
---|
| 1160 | Bstr bstrFormatName;
|
---|
| 1161 |
|
---|
| 1162 | CHECK_ERROR(pVirtualBox,
|
---|
| 1163 | COMGETTER(SystemProperties)(systemProperties.asOutParam()));
|
---|
| 1164 |
|
---|
| 1165 | CHECK_ERROR(systemProperties,
|
---|
| 1166 | COMGETTER(MediumFormats)(ComSafeArrayAsOutParam(mediumFormats)));
|
---|
| 1167 |
|
---|
| 1168 | /* go through all supported media formats and store files extensions only for RAW */
|
---|
| 1169 | com::SafeArray<BSTR> extensions;
|
---|
| 1170 |
|
---|
[54991] | 1171 | for (unsigned j = 0; j < mediumFormats.size(); ++j)
|
---|
[54979] | 1172 | {
|
---|
| 1173 | com::SafeArray<DeviceType_T> deviceType;
|
---|
[54991] | 1174 | ComPtr<IMediumFormat> mediumFormat = mediumFormats[j];
|
---|
[54979] | 1175 | CHECK_ERROR(mediumFormat, COMGETTER(Name)(bstrFormatName.asOutParam()));
|
---|
| 1176 | Utf8Str strFormatName = Utf8Str(bstrFormatName);
|
---|
[54991] | 1177 |
|
---|
[54979] | 1178 | if (strFormatName.compare("RAW", Utf8Str::CaseInsensitive) == 0)
|
---|
| 1179 | {
|
---|
| 1180 | /* getting files extensions for "RAW" format */
|
---|
| 1181 | CHECK_ERROR(mediumFormat,
|
---|
| 1182 | DescribeFileExtensions(ComSafeArrayAsOutParam(extensions),
|
---|
| 1183 | ComSafeArrayAsOutParam(deviceType)));
|
---|
| 1184 | break;
|
---|
| 1185 | }
|
---|
| 1186 | }
|
---|
| 1187 |
|
---|
| 1188 | /* go through files extensions for RAW format and compare them with
|
---|
| 1189 | * extension of current file
|
---|
| 1190 | */
|
---|
[54991] | 1191 | bool fReplace = true;
|
---|
[54979] | 1192 |
|
---|
| 1193 | const char *pszExtension = RTPathSuffix(strOverride.c_str());
|
---|
| 1194 | if (pszExtension)
|
---|
| 1195 | pszExtension++;
|
---|
| 1196 |
|
---|
[54991] | 1197 | for (unsigned j = 0; j < extensions.size(); ++j)
|
---|
[54979] | 1198 | {
|
---|
[54991] | 1199 | Bstr bstrExt(extensions[j]);
|
---|
[54983] | 1200 | Utf8Str strExtension(bstrExt);
|
---|
[54979] | 1201 | if(strExtension.compare(pszExtension, Utf8Str::CaseInsensitive) == 0)
|
---|
| 1202 | {
|
---|
[54991] | 1203 | fReplace = false;
|
---|
[54979] | 1204 | break;
|
---|
| 1205 | }
|
---|
| 1206 | }
|
---|
| 1207 |
|
---|
[54991] | 1208 | if (fReplace)
|
---|
[54979] | 1209 | {
|
---|
| 1210 | strOverride = strOverride.stripSuffix();
|
---|
| 1211 | strOverride = strOverride.append(".").append("vdi");
|
---|
| 1212 | }
|
---|
| 1213 | }
|
---|
| 1214 |
|
---|
| 1215 | bstrFinalValue = strOverride;
|
---|
| 1216 |
|
---|
[93703] | 1217 | RTPrintf(Appliance::tr("%2u: Hard disk image: source image=%ls, target path=%ls, %s\n"
|
---|
| 1218 | " (change target path with \"--vsys %u --unit %u --disk path\";\n"
|
---|
| 1219 | " change controller with \"--vsys %u --unit %u --controller <index>\";\n"
|
---|
| 1220 | " change controller port with \"--vsys %u --unit %u --port <n>\";\n"
|
---|
| 1221 | " disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
[93480] | 1222 | a, aOvfValues[a], bstrFinalValue.raw(), strExtraConfigValue.c_str(),
|
---|
| 1223 | i, a,
|
---|
| 1224 | i, a,
|
---|
| 1225 | i, a,
|
---|
| 1226 | i, a);
|
---|
[54979] | 1227 | }
|
---|
[16834] | 1228 | }
|
---|
[63300] | 1229 | break;
|
---|
[16495] | 1230 |
|
---|
| 1231 | case VirtualSystemDescriptionType_CDROM:
|
---|
[16606] | 1232 | if (fIgnoreThis)
|
---|
| 1233 | {
|
---|
[92372] | 1234 | RTPrintf(Appliance::tr("%2u: CD-ROM -- disabled\n"),
|
---|
[16606] | 1235 | a);
|
---|
| 1236 | aEnabled[a] = false;
|
---|
| 1237 | }
|
---|
| 1238 | else
|
---|
[93703] | 1239 | RTPrintf(Appliance::tr("%2u: CD-ROM\n"
|
---|
| 1240 | " (disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
[16606] | 1241 | a, i, a);
|
---|
[63300] | 1242 | break;
|
---|
[16495] | 1243 |
|
---|
| 1244 | case VirtualSystemDescriptionType_Floppy:
|
---|
[16606] | 1245 | if (fIgnoreThis)
|
---|
| 1246 | {
|
---|
[92372] | 1247 | RTPrintf(Appliance::tr("%2u: Floppy -- disabled\n"),
|
---|
[16606] | 1248 | a);
|
---|
| 1249 | aEnabled[a] = false;
|
---|
| 1250 | }
|
---|
| 1251 | else
|
---|
[93703] | 1252 | RTPrintf(Appliance::tr("%2u: Floppy\n"
|
---|
| 1253 | " (disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
[16606] | 1254 | a, i, a);
|
---|
[63300] | 1255 | break;
|
---|
[16495] | 1256 |
|
---|
| 1257 | case VirtualSystemDescriptionType_NetworkAdapter:
|
---|
[92372] | 1258 | RTPrintf(Appliance::tr("%2u: Network adapter: orig %ls, config %ls, extra %ls\n"), /// @todo implement once we have a plan for the back-end
|
---|
[16495] | 1259 | a,
|
---|
[17291] | 1260 | aOvfValues[a],
|
---|
[50323] | 1261 | aVBoxValues[a],
|
---|
[16495] | 1262 | aExtraConfigValues[a]);
|
---|
[63300] | 1263 | break;
|
---|
[16495] | 1264 |
|
---|
| 1265 | case VirtualSystemDescriptionType_USBController:
|
---|
[16606] | 1266 | if (fIgnoreThis)
|
---|
| 1267 | {
|
---|
[92372] | 1268 | RTPrintf(Appliance::tr("%2u: USB controller -- disabled\n"),
|
---|
[16606] | 1269 | a);
|
---|
| 1270 | aEnabled[a] = false;
|
---|
| 1271 | }
|
---|
| 1272 | else
|
---|
[93703] | 1273 | RTPrintf(Appliance::tr("%2u: USB controller\n"
|
---|
| 1274 | " (disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
[16606] | 1275 | a, i, a);
|
---|
[63300] | 1276 | break;
|
---|
[16495] | 1277 |
|
---|
| 1278 | case VirtualSystemDescriptionType_SoundCard:
|
---|
[16606] | 1279 | if (fIgnoreThis)
|
---|
| 1280 | {
|
---|
[92372] | 1281 | RTPrintf(Appliance::tr("%2u: Sound card \"%ls\" -- disabled\n"),
|
---|
[16606] | 1282 | a,
|
---|
[17291] | 1283 | aOvfValues[a]);
|
---|
[16606] | 1284 | aEnabled[a] = false;
|
---|
| 1285 | }
|
---|
| 1286 | else
|
---|
[93703] | 1287 | RTPrintf(Appliance::tr("%2u: Sound card (appliance expects \"%ls\", can change on import)\n"
|
---|
| 1288 | " (disable with \"--vsys %u --unit %u --ignore\")\n"),
|
---|
[16606] | 1289 | a,
|
---|
[17291] | 1290 | aOvfValues[a],
|
---|
[16606] | 1291 | i,
|
---|
| 1292 | a);
|
---|
[63300] | 1293 | break;
|
---|
| 1294 |
|
---|
| 1295 | case VirtualSystemDescriptionType_SettingsFile:
|
---|
[72476] | 1296 | if (findArgValue(strOverride, pmapArgs, "settingsfile"))
|
---|
| 1297 | {
|
---|
| 1298 | bstrFinalValue = strOverride;
|
---|
[92372] | 1299 | RTPrintf(Appliance::tr("%2u: VM settings file name specified with --settingsfile: \"%ls\"\n"),
|
---|
[72476] | 1300 | a, bstrFinalValue.raw());
|
---|
| 1301 | }
|
---|
| 1302 | else
|
---|
[93703] | 1303 | RTPrintf(Appliance::tr("%2u: Suggested VM settings file name \"%ls\"\n"
|
---|
| 1304 | " (change with \"--vsys %u --settingsfile <filename>\")\n"),
|
---|
[72476] | 1305 | a, bstrFinalValue.raw(), i);
|
---|
[63300] | 1306 | break;
|
---|
[72476] | 1307 |
|
---|
| 1308 | case VirtualSystemDescriptionType_BaseFolder:
|
---|
| 1309 | if (findArgValue(strOverride, pmapArgs, "basefolder"))
|
---|
| 1310 | {
|
---|
| 1311 | bstrFinalValue = strOverride;
|
---|
[92372] | 1312 | RTPrintf(Appliance::tr("%2u: VM base folder specified with --basefolder: \"%ls\"\n"),
|
---|
[72476] | 1313 | a, bstrFinalValue.raw());
|
---|
| 1314 | }
|
---|
| 1315 | else
|
---|
[93703] | 1316 | RTPrintf(Appliance::tr("%2u: Suggested VM base folder \"%ls\"\n"
|
---|
| 1317 | " (change with \"--vsys %u --basefolder <path>\")\n"),
|
---|
[72476] | 1318 | a, bstrFinalValue.raw(), i);
|
---|
| 1319 | break;
|
---|
| 1320 |
|
---|
| 1321 | case VirtualSystemDescriptionType_PrimaryGroup:
|
---|
| 1322 | if (findArgValue(strOverride, pmapArgs, "group"))
|
---|
| 1323 | {
|
---|
| 1324 | bstrFinalValue = strOverride;
|
---|
[92372] | 1325 | RTPrintf(Appliance::tr("%2u: VM group specified with --group: \"%ls\"\n"),
|
---|
[72476] | 1326 | a, bstrFinalValue.raw());
|
---|
| 1327 | }
|
---|
| 1328 | else
|
---|
[93703] | 1329 | RTPrintf(Appliance::tr("%2u: Suggested VM group \"%ls\"\n"
|
---|
| 1330 | " (change with \"--vsys %u --group <group>\")\n"),
|
---|
[72476] | 1331 | a, bstrFinalValue.raw(), i);
|
---|
| 1332 | break;
|
---|
| 1333 |
|
---|
[76192] | 1334 | case VirtualSystemDescriptionType_CloudInstanceShape:
|
---|
[92372] | 1335 | RTPrintf(Appliance::tr("%2u: Suggested cloud shape \"%ls\"\n"),
|
---|
[78842] | 1336 | a, bstrFinalValue.raw());
|
---|
[78841] | 1337 | break;
|
---|
| 1338 |
|
---|
| 1339 | case VirtualSystemDescriptionType_CloudBucket:
|
---|
| 1340 | if (findArgValue(strOverride, pmapArgs, "cloudbucket"))
|
---|
| 1341 | {
|
---|
| 1342 | bstrFinalValue = strOverride;
|
---|
[92372] | 1343 | RTPrintf(Appliance::tr("%2u: Cloud bucket id specified with --cloudbucket: \"%ls\"\n"),
|
---|
[78841] | 1344 | a, bstrFinalValue.raw());
|
---|
| 1345 | }
|
---|
| 1346 | else
|
---|
[93703] | 1347 | RTPrintf(Appliance::tr("%2u: Suggested cloud bucket id \"%ls\"\n"
|
---|
| 1348 | " (change with \"--cloud %u --cloudbucket <id>\")\n"),
|
---|
[78841] | 1349 | a, bstrFinalValue.raw(), i);
|
---|
| 1350 | break;
|
---|
| 1351 |
|
---|
| 1352 | case VirtualSystemDescriptionType_CloudProfileName:
|
---|
| 1353 | if (findArgValue(strOverride, pmapArgs, "cloudprofile"))
|
---|
| 1354 | {
|
---|
| 1355 | bstrFinalValue = strOverride;
|
---|
[92372] | 1356 | RTPrintf(Appliance::tr("%2u: Cloud profile name specified with --cloudprofile: \"%ls\"\n"),
|
---|
[78841] | 1357 | a, bstrFinalValue.raw());
|
---|
| 1358 | }
|
---|
| 1359 | else
|
---|
[93703] | 1360 | RTPrintf(Appliance::tr("%2u: Suggested cloud profile name \"%ls\"\n"
|
---|
| 1361 | " (change with \"--cloud %u --cloudprofile <id>\")\n"),
|
---|
[78841] | 1362 | a, bstrFinalValue.raw(), i);
|
---|
| 1363 | break;
|
---|
| 1364 |
|
---|
| 1365 | case VirtualSystemDescriptionType_CloudInstanceId:
|
---|
| 1366 | if (findArgValue(strOverride, pmapArgs, "cloudinstanceid"))
|
---|
| 1367 | {
|
---|
| 1368 | bstrFinalValue = strOverride;
|
---|
[92372] | 1369 | RTPrintf(Appliance::tr("%2u: Cloud instance id specified with --cloudinstanceid: \"%ls\"\n"),
|
---|
[78841] | 1370 | a, bstrFinalValue.raw());
|
---|
| 1371 | }
|
---|
| 1372 | else
|
---|
[93703] | 1373 | RTPrintf(Appliance::tr("%2u: Suggested cloud instance id \"%ls\"\n"
|
---|
| 1374 | " (change with \"--cloud %u --cloudinstanceid <id>\")\n"),
|
---|
[78841] | 1375 | a, bstrFinalValue.raw(), i);
|
---|
| 1376 | break;
|
---|
| 1377 |
|
---|
| 1378 | case VirtualSystemDescriptionType_CloudImageId:
|
---|
[92372] | 1379 | RTPrintf(Appliance::tr("%2u: Suggested cloud base image id \"%ls\"\n"),
|
---|
[78842] | 1380 | a, bstrFinalValue.raw());
|
---|
[78841] | 1381 | break;
|
---|
[76192] | 1382 | case VirtualSystemDescriptionType_CloudDomain:
|
---|
| 1383 | case VirtualSystemDescriptionType_CloudBootDiskSize:
|
---|
[73217] | 1384 | case VirtualSystemDescriptionType_CloudOCIVCN:
|
---|
[76192] | 1385 | case VirtualSystemDescriptionType_CloudPublicIP:
|
---|
[73929] | 1386 | case VirtualSystemDescriptionType_CloudOCISubnet:
|
---|
[76192] | 1387 | case VirtualSystemDescriptionType_CloudKeepObject:
|
---|
| 1388 | case VirtualSystemDescriptionType_CloudLaunchInstance:
|
---|
[78735] | 1389 | case VirtualSystemDescriptionType_CloudInstanceState:
|
---|
| 1390 | case VirtualSystemDescriptionType_CloudImageState:
|
---|
[63300] | 1391 | case VirtualSystemDescriptionType_Miscellaneous:
|
---|
[79646] | 1392 | case VirtualSystemDescriptionType_CloudInstanceDisplayName:
|
---|
| 1393 | case VirtualSystemDescriptionType_CloudImageDisplayName:
|
---|
[80676] | 1394 | case VirtualSystemDescriptionType_CloudOCILaunchMode:
|
---|
[80426] | 1395 | case VirtualSystemDescriptionType_CloudPrivateIP:
|
---|
[81085] | 1396 | case VirtualSystemDescriptionType_CloudBootVolumeId:
|
---|
[81191] | 1397 | case VirtualSystemDescriptionType_CloudOCIVCNCompartment:
|
---|
| 1398 | case VirtualSystemDescriptionType_CloudOCISubnetCompartment:
|
---|
[81400] | 1399 | case VirtualSystemDescriptionType_CloudPublicSSHKey:
|
---|
[82366] | 1400 | case VirtualSystemDescriptionType_BootingFirmware:
|
---|
[87278] | 1401 | case VirtualSystemDescriptionType_CloudInitScriptPath:
|
---|
[89777] | 1402 | case VirtualSystemDescriptionType_CloudCompartmentId:
|
---|
[93835] | 1403 | case VirtualSystemDescriptionType_CloudShapeCpus:
|
---|
| 1404 | case VirtualSystemDescriptionType_CloudShapeMemory:
|
---|
[98778] | 1405 | case VirtualSystemDescriptionType_CloudInstanceMetadata:
|
---|
| 1406 | case VirtualSystemDescriptionType_CloudInstanceFreeFormTags:
|
---|
| 1407 | case VirtualSystemDescriptionType_CloudImageFreeFormTags:
|
---|
[63300] | 1408 | /** @todo VirtualSystemDescriptionType_Miscellaneous? */
|
---|
| 1409 | break;
|
---|
[76192] | 1410 |
|
---|
[63300] | 1411 | case VirtualSystemDescriptionType_Ignore:
|
---|
[72976] | 1412 | #ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
|
---|
| 1413 | case VirtualSystemDescriptionType_32BitHack:
|
---|
| 1414 | #endif
|
---|
[63300] | 1415 | break;
|
---|
[16495] | 1416 | }
|
---|
[16601] | 1417 |
|
---|
| 1418 | bstrFinalValue.detachTo(&aFinalValues[a]);
|
---|
[16495] | 1419 | }
|
---|
[16517] | 1420 |
|
---|
[16601] | 1421 | if (fExecute)
|
---|
| 1422 | CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
|
---|
| 1423 | SetFinalValues(ComSafeArrayAsInParam(aEnabled),
|
---|
[16668] | 1424 | ComSafeArrayAsInParam(aFinalValues),
|
---|
| 1425 | ComSafeArrayAsInParam(aExtraConfigValues)));
|
---|
[16601] | 1426 |
|
---|
| 1427 | } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
|
---|
| 1428 |
|
---|
[18223] | 1429 | if (cLicensesInTheWay == 1)
|
---|
[92372] | 1430 | RTMsgError(Appliance::tr("Cannot import until the license agreement listed above is accepted."));
|
---|
[18223] | 1431 | else if (cLicensesInTheWay > 1)
|
---|
[92372] | 1432 | RTMsgError(Appliance::tr("Cannot import until the %c license agreements listed above are accepted."),
|
---|
| 1433 | cLicensesInTheWay);
|
---|
[18223] | 1434 |
|
---|
| 1435 | if (!cLicensesInTheWay && fExecute)
|
---|
[16517] | 1436 | {
|
---|
[17827] | 1437 | // go!
|
---|
[16517] | 1438 | ComPtr<IProgress> progress;
|
---|
[17033] | 1439 | CHECK_ERROR_BREAK(pAppliance,
|
---|
[37862] | 1440 | ImportMachines(ComSafeArrayAsInParam(options), progress.asOutParam()));
|
---|
[16517] | 1441 |
|
---|
[95140] | 1442 | hrc = showProgress(progress);
|
---|
[92372] | 1443 | CHECK_PROGRESS_ERROR_RET(progress, (Appliance::tr("Appliance import failed")), RTEXITCODE_FAILURE);
|
---|
[16517] | 1444 |
|
---|
[95140] | 1445 | if (SUCCEEDED(hrc))
|
---|
[92372] | 1446 | RTPrintf(Appliance::tr("Successfully imported the appliance.\n"));
|
---|
[16517] | 1447 | }
|
---|
| 1448 | } // end if (aVirtualSystemDescriptions.size() > 0)
|
---|
[16495] | 1449 | } while (0);
|
---|
| 1450 |
|
---|
[95140] | 1451 | return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
|
---|
[16491] | 1452 | }
|
---|
| 1453 |
|
---|
[50447] | 1454 | static int parseExportOptions(const char *psz, com::SafeArray<ExportOptions_T> *options)
|
---|
| 1455 | {
|
---|
[98298] | 1456 | int vrc = VINF_SUCCESS;
|
---|
| 1457 | while (psz && *psz && RT_SUCCESS(vrc))
|
---|
[50447] | 1458 | {
|
---|
| 1459 | size_t len;
|
---|
| 1460 | const char *pszComma = strchr(psz, ',');
|
---|
| 1461 | if (pszComma)
|
---|
| 1462 | len = pszComma - psz;
|
---|
| 1463 | else
|
---|
| 1464 | len = strlen(psz);
|
---|
| 1465 | if (len > 0)
|
---|
| 1466 | {
|
---|
| 1467 | if (!RTStrNICmp(psz, "CreateManifest", len))
|
---|
| 1468 | options->push_back(ExportOptions_CreateManifest);
|
---|
| 1469 | else if (!RTStrNICmp(psz, "manifest", len))
|
---|
| 1470 | options->push_back(ExportOptions_CreateManifest);
|
---|
| 1471 | else if (!RTStrNICmp(psz, "ExportDVDImages", len))
|
---|
| 1472 | options->push_back(ExportOptions_ExportDVDImages);
|
---|
| 1473 | else if (!RTStrNICmp(psz, "iso", len))
|
---|
| 1474 | options->push_back(ExportOptions_ExportDVDImages);
|
---|
| 1475 | else if (!RTStrNICmp(psz, "StripAllMACs", len))
|
---|
| 1476 | options->push_back(ExportOptions_StripAllMACs);
|
---|
| 1477 | else if (!RTStrNICmp(psz, "nomacs", len))
|
---|
| 1478 | options->push_back(ExportOptions_StripAllMACs);
|
---|
| 1479 | else if (!RTStrNICmp(psz, "StripAllNonNATMACs", len))
|
---|
| 1480 | options->push_back(ExportOptions_StripAllNonNATMACs);
|
---|
| 1481 | else if (!RTStrNICmp(psz, "nomacsbutnat", len))
|
---|
| 1482 | options->push_back(ExportOptions_StripAllNonNATMACs);
|
---|
| 1483 | else
|
---|
[98298] | 1484 | vrc = VERR_PARSE_ERROR;
|
---|
[50447] | 1485 | }
|
---|
| 1486 | if (pszComma)
|
---|
| 1487 | psz += len + 1;
|
---|
| 1488 | else
|
---|
| 1489 | psz += len;
|
---|
| 1490 | }
|
---|
[17098] | 1491 |
|
---|
[98298] | 1492 | return vrc;
|
---|
[50447] | 1493 | }
|
---|
| 1494 |
|
---|
| 1495 | static const RTGETOPTDEF g_aExportOptions[] =
|
---|
| 1496 | {
|
---|
| 1497 | { "--output", 'o', RTGETOPT_REQ_STRING },
|
---|
| 1498 | { "--legacy09", 'l', RTGETOPT_REQ_NOTHING },
|
---|
| 1499 | { "--ovf09", 'l', RTGETOPT_REQ_NOTHING },
|
---|
| 1500 | { "--ovf10", '1', RTGETOPT_REQ_NOTHING },
|
---|
| 1501 | { "--ovf20", '2', RTGETOPT_REQ_NOTHING },
|
---|
[67208] | 1502 | { "--opc10", 'c', RTGETOPT_REQ_NOTHING },
|
---|
[50447] | 1503 | { "--manifest", 'm', RTGETOPT_REQ_NOTHING }, // obsoleted by --options
|
---|
| 1504 | { "--vsys", 's', RTGETOPT_REQ_UINT32 },
|
---|
[72476] | 1505 | { "--vmname", 'V', RTGETOPT_REQ_STRING },
|
---|
[50447] | 1506 | { "--product", 'p', RTGETOPT_REQ_STRING },
|
---|
| 1507 | { "--producturl", 'P', RTGETOPT_REQ_STRING },
|
---|
| 1508 | { "--vendor", 'n', RTGETOPT_REQ_STRING },
|
---|
| 1509 | { "--vendorurl", 'N', RTGETOPT_REQ_STRING },
|
---|
| 1510 | { "--version", 'v', RTGETOPT_REQ_STRING },
|
---|
| 1511 | { "--description", 'd', RTGETOPT_REQ_STRING },
|
---|
| 1512 | { "--eula", 'e', RTGETOPT_REQ_STRING },
|
---|
| 1513 | { "--eulafile", 'E', RTGETOPT_REQ_STRING },
|
---|
| 1514 | { "--options", 'O', RTGETOPT_REQ_STRING },
|
---|
[75920] | 1515 | { "--cloud", 'C', RTGETOPT_REQ_UINT32 },
|
---|
| 1516 | { "--cloudshape", 'S', RTGETOPT_REQ_STRING },
|
---|
| 1517 | { "--clouddomain", 'D', RTGETOPT_REQ_STRING },
|
---|
| 1518 | { "--clouddisksize", 'R', RTGETOPT_REQ_STRING },
|
---|
| 1519 | { "--cloudbucket", 'B', RTGETOPT_REQ_STRING },
|
---|
| 1520 | { "--cloudocivcn", 'Q', RTGETOPT_REQ_STRING },
|
---|
| 1521 | { "--cloudpublicip", 'A', RTGETOPT_REQ_STRING },
|
---|
| 1522 | { "--cloudprofile", 'F', RTGETOPT_REQ_STRING },
|
---|
| 1523 | { "--cloudocisubnet", 'T', RTGETOPT_REQ_STRING },
|
---|
| 1524 | { "--cloudkeepobject", 'K', RTGETOPT_REQ_STRING },
|
---|
| 1525 | { "--cloudlaunchinstance", 'L', RTGETOPT_REQ_STRING },
|
---|
[80707] | 1526 | { "--cloudlaunchmode", 'M', RTGETOPT_REQ_STRING },
|
---|
| 1527 | { "--cloudprivateip", 'i', RTGETOPT_REQ_STRING },
|
---|
[87278] | 1528 | { "--cloudinitscriptpath", 'I', RTGETOPT_REQ_STRING },
|
---|
[50447] | 1529 | };
|
---|
| 1530 |
|
---|
[56118] | 1531 | RTEXITCODE handleExportAppliance(HandlerArg *a)
|
---|
[17033] | 1532 | {
|
---|
[95140] | 1533 | HRESULT hrc = S_OK;
|
---|
[17033] | 1534 |
|
---|
| 1535 | Utf8Str strOutputFile;
|
---|
[18809] | 1536 | Utf8Str strOvfFormat("ovf-1.0"); // the default export version
|
---|
[32448] | 1537 | bool fManifest = false; // the default
|
---|
[91204] | 1538 | APPLIANCETYPE enmApplType = NOT_SET;
|
---|
[49103] | 1539 | bool fExportISOImages = false; // the default
|
---|
[50447] | 1540 | com::SafeArray<ExportOptions_T> options;
|
---|
[17033] | 1541 | std::list< ComPtr<IMachine> > llMachines;
|
---|
| 1542 |
|
---|
[18809] | 1543 | uint32_t ulCurVsys = (uint32_t)-1;
|
---|
| 1544 | // for each --vsys X command, maintain a map of command line items
|
---|
| 1545 | ArgsMapsMap mapArgsMapsPerVsys;
|
---|
[17033] | 1546 | do
|
---|
| 1547 | {
|
---|
[17098] | 1548 | int c;
|
---|
| 1549 |
|
---|
| 1550 | RTGETOPTUNION ValueUnion;
|
---|
| 1551 | RTGETOPTSTATE GetState;
|
---|
[18108] | 1552 | // start at 0 because main() has hacked both the argc and argv given to us
|
---|
| 1553 | RTGetOptInit(&GetState, a->argc, a->argv, g_aExportOptions,
|
---|
[26517] | 1554 | RT_ELEMENTS(g_aExportOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
|
---|
[18809] | 1555 |
|
---|
| 1556 | Utf8Str strProductUrl;
|
---|
[17098] | 1557 | while ((c = RTGetOpt(&GetState, &ValueUnion)))
|
---|
[17033] | 1558 | {
|
---|
[17098] | 1559 | switch (c)
|
---|
[17033] | 1560 | {
|
---|
[17098] | 1561 | case 'o': // --output
|
---|
[17033] | 1562 | if (strOutputFile.length())
|
---|
[93703] | 1563 | return errorSyntax(Appliance::tr("You can only specify --output once."));
|
---|
[17033] | 1564 | else
|
---|
[17098] | 1565 | strOutputFile = ValueUnion.psz;
|
---|
[40329] | 1566 | break;
|
---|
[17098] | 1567 |
|
---|
[40329] | 1568 | case 'l': // --legacy09/--ovf09
|
---|
[50447] | 1569 | strOvfFormat = "ovf-0.9";
|
---|
| 1570 | break;
|
---|
[18809] | 1571 |
|
---|
[40329] | 1572 | case '1': // --ovf10
|
---|
[50447] | 1573 | strOvfFormat = "ovf-1.0";
|
---|
| 1574 | break;
|
---|
[40329] | 1575 |
|
---|
| 1576 | case '2': // --ovf20
|
---|
[50447] | 1577 | strOvfFormat = "ovf-2.0";
|
---|
| 1578 | break;
|
---|
[40329] | 1579 |
|
---|
[67194] | 1580 | case 'c': // --opc
|
---|
[67208] | 1581 | strOvfFormat = "opc-1.0";
|
---|
[67194] | 1582 | break;
|
---|
| 1583 |
|
---|
[87278] | 1584 | // case 'I': // --iso
|
---|
| 1585 | // fExportISOImages = true;
|
---|
| 1586 | // break;
|
---|
[49103] | 1587 |
|
---|
[32448] | 1588 | case 'm': // --manifest
|
---|
[50447] | 1589 | fManifest = true;
|
---|
| 1590 | break;
|
---|
[32448] | 1591 |
|
---|
[18809] | 1592 | case 's': // --vsys
|
---|
[91204] | 1593 | if (enmApplType == NOT_SET)
|
---|
| 1594 | enmApplType = LOCAL;
|
---|
[75920] | 1595 |
|
---|
[91204] | 1596 | if (enmApplType != LOCAL)
|
---|
[93703] | 1597 | return errorSyntax(Appliance::tr("Option \"%s\" can't be used together with \"--cloud\" option."),
|
---|
[75920] | 1598 | GetState.pDef->pszLong);
|
---|
[91204] | 1599 | if (ValueUnion.u32 == (uint32_t)-1)
|
---|
[93703] | 1600 | return errorSyntax(Appliance::tr("Value of option \"%s\" is out of range."),
|
---|
[91204] | 1601 | GetState.pDef->pszLong);
|
---|
[75920] | 1602 |
|
---|
[50447] | 1603 | ulCurVsys = ValueUnion.u32;
|
---|
| 1604 | break;
|
---|
[18809] | 1605 |
|
---|
[72476] | 1606 | case 'V': // --vmname
|
---|
[91204] | 1607 | if (enmApplType == NOT_SET)
|
---|
[93703] | 1608 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys or --cloud option."),
|
---|
[76192] | 1609 | GetState.pDef->pszLong);
|
---|
[72476] | 1610 | mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz;
|
---|
| 1611 | break;
|
---|
| 1612 |
|
---|
[18809] | 1613 | case 'p': // --product
|
---|
[91204] | 1614 | if (enmApplType != LOCAL)
|
---|
[93703] | 1615 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 1616 | GetState.pDef->pszLong);
|
---|
[50447] | 1617 | mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz;
|
---|
| 1618 | break;
|
---|
[18809] | 1619 |
|
---|
| 1620 | case 'P': // --producturl
|
---|
[91204] | 1621 | if (enmApplType != LOCAL)
|
---|
[93703] | 1622 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 1623 | GetState.pDef->pszLong);
|
---|
[50447] | 1624 | mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz;
|
---|
| 1625 | break;
|
---|
[18809] | 1626 |
|
---|
[46290] | 1627 | case 'n': // --vendor
|
---|
[91204] | 1628 | if (enmApplType != LOCAL)
|
---|
[93703] | 1629 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 1630 | GetState.pDef->pszLong);
|
---|
[50447] | 1631 | mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz;
|
---|
| 1632 | break;
|
---|
[18809] | 1633 |
|
---|
[46290] | 1634 | case 'N': // --vendorurl
|
---|
[91204] | 1635 | if (enmApplType != LOCAL)
|
---|
[93703] | 1636 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 1637 | GetState.pDef->pszLong);
|
---|
[50447] | 1638 | mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz;
|
---|
| 1639 | break;
|
---|
[18809] | 1640 |
|
---|
| 1641 | case 'v': // --version
|
---|
[91204] | 1642 | if (enmApplType != LOCAL)
|
---|
[93703] | 1643 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 1644 | GetState.pDef->pszLong);
|
---|
[50447] | 1645 | mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz;
|
---|
| 1646 | break;
|
---|
[18809] | 1647 |
|
---|
[46290] | 1648 | case 'd': // --description
|
---|
[91204] | 1649 | if (enmApplType != LOCAL)
|
---|
[93703] | 1650 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 1651 | GetState.pDef->pszLong);
|
---|
[50447] | 1652 | mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
|
---|
| 1653 | break;
|
---|
[46290] | 1654 |
|
---|
[18809] | 1655 | case 'e': // --eula
|
---|
[91204] | 1656 | if (enmApplType != LOCAL)
|
---|
[93703] | 1657 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 1658 | GetState.pDef->pszLong);
|
---|
[50447] | 1659 | mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
|
---|
| 1660 | break;
|
---|
[18809] | 1661 |
|
---|
| 1662 | case 'E': // --eulafile
|
---|
[91204] | 1663 | if (enmApplType != LOCAL)
|
---|
[93703] | 1664 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --vsys option."),
|
---|
[92372] | 1665 | GetState.pDef->pszLong);
|
---|
[50447] | 1666 | mapArgsMapsPerVsys[ulCurVsys]["eulafile"] = ValueUnion.psz;
|
---|
| 1667 | break;
|
---|
[18809] | 1668 |
|
---|
[50447] | 1669 | case 'O': // --options
|
---|
| 1670 | if (RT_FAILURE(parseExportOptions(ValueUnion.psz, &options)))
|
---|
[92372] | 1671 | return errorArgument(Appliance::tr("Invalid export options '%s'\n"), ValueUnion.psz);
|
---|
[50447] | 1672 | break;
|
---|
| 1673 |
|
---|
[75920] | 1674 | /*--cloud and --vsys are orthogonal, only one must be presented*/
|
---|
| 1675 | case 'C': // --cloud
|
---|
[91204] | 1676 | if (enmApplType == NOT_SET)
|
---|
| 1677 | enmApplType = CLOUD;
|
---|
[75920] | 1678 |
|
---|
[91204] | 1679 | if (enmApplType != CLOUD)
|
---|
[93703] | 1680 | return errorSyntax(Appliance::tr("Option \"%s\" can't be used together with \"--vsys\" option."),
|
---|
[75920] | 1681 | GetState.pDef->pszLong);
|
---|
[91204] | 1682 | if (ValueUnion.u32 == (uint32_t)-1)
|
---|
[93703] | 1683 | return errorSyntax(Appliance::tr("Value of option \"%s\" is out of range."),
|
---|
[91204] | 1684 | GetState.pDef->pszLong);
|
---|
[75920] | 1685 |
|
---|
| 1686 | ulCurVsys = ValueUnion.u32;
|
---|
| 1687 | break;
|
---|
| 1688 |
|
---|
| 1689 | /* Cloud export settings */
|
---|
| 1690 | case 'S': // --cloudshape
|
---|
[91204] | 1691 | if (enmApplType != CLOUD)
|
---|
[93703] | 1692 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[75920] | 1693 | GetState.pDef->pszLong);
|
---|
| 1694 | mapArgsMapsPerVsys[ulCurVsys]["cloudshape"] = ValueUnion.psz;
|
---|
| 1695 | break;
|
---|
| 1696 |
|
---|
| 1697 | case 'D': // --clouddomain
|
---|
[91204] | 1698 | if (enmApplType != CLOUD)
|
---|
[93703] | 1699 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[75920] | 1700 | GetState.pDef->pszLong);
|
---|
| 1701 | mapArgsMapsPerVsys[ulCurVsys]["clouddomain"] = ValueUnion.psz;
|
---|
| 1702 | break;
|
---|
| 1703 |
|
---|
| 1704 | case 'R': // --clouddisksize
|
---|
[91204] | 1705 | if (enmApplType != CLOUD)
|
---|
[93703] | 1706 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[75920] | 1707 | GetState.pDef->pszLong);
|
---|
| 1708 | mapArgsMapsPerVsys[ulCurVsys]["clouddisksize"] = ValueUnion.psz;
|
---|
| 1709 | break;
|
---|
| 1710 |
|
---|
| 1711 | case 'B': // --cloudbucket
|
---|
[91204] | 1712 | if (enmApplType != CLOUD)
|
---|
[93703] | 1713 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[75920] | 1714 | GetState.pDef->pszLong);
|
---|
| 1715 | mapArgsMapsPerVsys[ulCurVsys]["cloudbucket"] = ValueUnion.psz;
|
---|
| 1716 | break;
|
---|
| 1717 |
|
---|
| 1718 | case 'Q': // --cloudocivcn
|
---|
[91204] | 1719 | if (enmApplType != CLOUD)
|
---|
[93703] | 1720 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[75920] | 1721 | GetState.pDef->pszLong);
|
---|
| 1722 | mapArgsMapsPerVsys[ulCurVsys]["cloudocivcn"] = ValueUnion.psz;
|
---|
| 1723 | break;
|
---|
| 1724 |
|
---|
| 1725 | case 'A': // --cloudpublicip
|
---|
[91204] | 1726 | if (enmApplType != CLOUD)
|
---|
[93703] | 1727 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[75920] | 1728 | GetState.pDef->pszLong);
|
---|
| 1729 | mapArgsMapsPerVsys[ulCurVsys]["cloudpublicip"] = ValueUnion.psz;
|
---|
| 1730 | break;
|
---|
| 1731 |
|
---|
[80707] | 1732 | case 'i': /* --cloudprivateip */
|
---|
[91204] | 1733 | if (enmApplType != CLOUD)
|
---|
[93703] | 1734 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[80707] | 1735 | GetState.pDef->pszLong);
|
---|
| 1736 | mapArgsMapsPerVsys[ulCurVsys]["cloudprivateip"] = ValueUnion.psz;
|
---|
| 1737 | break;
|
---|
| 1738 |
|
---|
[75920] | 1739 | case 'F': // --cloudprofile
|
---|
[91204] | 1740 | if (enmApplType != CLOUD)
|
---|
[93703] | 1741 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[75920] | 1742 | GetState.pDef->pszLong);
|
---|
| 1743 | mapArgsMapsPerVsys[ulCurVsys]["cloudprofile"] = ValueUnion.psz;
|
---|
| 1744 | break;
|
---|
| 1745 |
|
---|
| 1746 | case 'T': // --cloudocisubnet
|
---|
[91204] | 1747 | if (enmApplType != CLOUD)
|
---|
[93703] | 1748 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[75920] | 1749 | GetState.pDef->pszLong);
|
---|
| 1750 | mapArgsMapsPerVsys[ulCurVsys]["cloudocisubnet"] = ValueUnion.psz;
|
---|
| 1751 | break;
|
---|
| 1752 |
|
---|
| 1753 | case 'K': // --cloudkeepobject
|
---|
[91204] | 1754 | if (enmApplType != CLOUD)
|
---|
[93703] | 1755 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[75920] | 1756 | GetState.pDef->pszLong);
|
---|
| 1757 | mapArgsMapsPerVsys[ulCurVsys]["cloudkeepobject"] = ValueUnion.psz;
|
---|
| 1758 | break;
|
---|
| 1759 |
|
---|
| 1760 | case 'L': // --cloudlaunchinstance
|
---|
[91204] | 1761 | if (enmApplType != CLOUD)
|
---|
[93703] | 1762 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[75920] | 1763 | GetState.pDef->pszLong);
|
---|
| 1764 | mapArgsMapsPerVsys[ulCurVsys]["cloudlaunchinstance"] = ValueUnion.psz;
|
---|
| 1765 | break;
|
---|
| 1766 |
|
---|
[80707] | 1767 | case 'M': /* --cloudlaunchmode */
|
---|
[91204] | 1768 | if (enmApplType != CLOUD)
|
---|
[93703] | 1769 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[80707] | 1770 | GetState.pDef->pszLong);
|
---|
| 1771 | mapArgsMapsPerVsys[ulCurVsys]["cloudlaunchmode"] = ValueUnion.psz;
|
---|
| 1772 | break;
|
---|
| 1773 |
|
---|
[87278] | 1774 | case 'I': // --cloudinitscriptpath
|
---|
[91204] | 1775 | if (enmApplType != CLOUD)
|
---|
[93703] | 1776 | return errorSyntax(Appliance::tr("Option \"%s\" requires preceding --cloud option."),
|
---|
[87278] | 1777 | GetState.pDef->pszLong);
|
---|
| 1778 | mapArgsMapsPerVsys[ulCurVsys]["cloudinitscriptpath"] = ValueUnion.psz;
|
---|
| 1779 | break;
|
---|
| 1780 |
|
---|
[17098] | 1781 | case VINF_GETOPT_NOT_OPTION:
|
---|
[17033] | 1782 | {
|
---|
[17098] | 1783 | Utf8Str strMachine(ValueUnion.psz);
|
---|
| 1784 | // must be machine: try UUID or name
|
---|
| 1785 | ComPtr<IMachine> machine;
|
---|
[33294] | 1786 | CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine).raw(),
|
---|
| 1787 | machine.asOutParam()));
|
---|
[17098] | 1788 | if (machine)
|
---|
| 1789 | llMachines.push_back(machine);
|
---|
[40329] | 1790 | break;
|
---|
[17033] | 1791 | }
|
---|
| 1792 |
|
---|
[17098] | 1793 | default:
|
---|
| 1794 | if (c > 0)
|
---|
[18108] | 1795 | {
|
---|
| 1796 | if (RT_C_IS_GRAPH(c))
|
---|
[93703] | 1797 | return errorSyntax(Appliance::tr("unhandled option: -%c"), c);
|
---|
[18108] | 1798 | else
|
---|
[93703] | 1799 | return errorSyntax(Appliance::tr("unhandled option: %i"), c);
|
---|
[18108] | 1800 | }
|
---|
| 1801 | else if (c == VERR_GETOPT_UNKNOWN_OPTION)
|
---|
[93703] | 1802 | return errorSyntax(Appliance::tr("unknown option: %s"), ValueUnion.psz);
|
---|
[17098] | 1803 | else if (ValueUnion.pDef)
|
---|
[93703] | 1804 | return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c);
|
---|
[17098] | 1805 | else
|
---|
[93703] | 1806 | return errorSyntax("%Rrs", c);
|
---|
[17033] | 1807 | }
|
---|
[17361] | 1808 |
|
---|
[95140] | 1809 | if (FAILED(hrc))
|
---|
[17361] | 1810 | break;
|
---|
[17033] | 1811 | }
|
---|
| 1812 |
|
---|
[95140] | 1813 | if (FAILED(hrc))
|
---|
[17074] | 1814 | break;
|
---|
| 1815 |
|
---|
[72823] | 1816 | if (llMachines.empty())
|
---|
[93703] | 1817 | return errorSyntax(Appliance::tr("At least one machine must be specified with the export command."));
|
---|
[81517] | 1818 |
|
---|
| 1819 | /* Last check after parsing all arguments */
|
---|
[91204] | 1820 | if (strOutputFile.isEmpty())
|
---|
[93703] | 1821 | return errorSyntax(Appliance::tr("Missing --output argument with export command."));
|
---|
[17033] | 1822 |
|
---|
[91204] | 1823 | if (enmApplType == NOT_SET)
|
---|
| 1824 | enmApplType = LOCAL;
|
---|
| 1825 |
|
---|
[18809] | 1826 | // match command line arguments with the machines count
|
---|
| 1827 | // this is only to sort out invalid indices at this time
|
---|
| 1828 | ArgsMapsMap::const_iterator it;
|
---|
| 1829 | for (it = mapArgsMapsPerVsys.begin();
|
---|
| 1830 | it != mapArgsMapsPerVsys.end();
|
---|
| 1831 | ++it)
|
---|
| 1832 | {
|
---|
| 1833 | uint32_t ulVsys = it->first;
|
---|
| 1834 | if (ulVsys >= llMachines.size())
|
---|
[93703] | 1835 | return errorSyntax(Appliance::tr("Invalid index %RI32 with -vsys option; you specified only %zu virtual system(s).",
|
---|
[92594] | 1836 | "", llMachines.size()),
|
---|
[18809] | 1837 | ulVsys, llMachines.size());
|
---|
| 1838 | }
|
---|
| 1839 |
|
---|
[17033] | 1840 | ComPtr<IAppliance> pAppliance;
|
---|
| 1841 | CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
|
---|
| 1842 |
|
---|
[33417] | 1843 | char *pszAbsFilePath = 0;
|
---|
[36527] | 1844 | if (strOutputFile.startsWith("S3://", RTCString::CaseInsensitive) ||
|
---|
| 1845 | strOutputFile.startsWith("SunCloud://", RTCString::CaseInsensitive) ||
|
---|
[75920] | 1846 | strOutputFile.startsWith("webdav://", RTCString::CaseInsensitive) ||
|
---|
| 1847 | strOutputFile.startsWith("OCI://", RTCString::CaseInsensitive))
|
---|
[33417] | 1848 | pszAbsFilePath = RTStrDup(strOutputFile.c_str());
|
---|
| 1849 | else
|
---|
| 1850 | pszAbsFilePath = RTPathAbsDup(strOutputFile.c_str());
|
---|
| 1851 |
|
---|
[80599] | 1852 | /*
|
---|
| 1853 | * The first stage - export machine/s to the Cloud or into the
|
---|
| 1854 | * OVA/OVF format on the local host.
|
---|
| 1855 | */
|
---|
| 1856 |
|
---|
| 1857 | /* VSDList is needed for the second stage where we launch the cloud instances if it was requested by user */
|
---|
| 1858 | std::list< ComPtr<IVirtualSystemDescription> > VSDList;
|
---|
[17033] | 1859 | std::list< ComPtr<IMachine> >::iterator itM;
|
---|
[18809] | 1860 | uint32_t i=0;
|
---|
[17033] | 1861 | for (itM = llMachines.begin();
|
---|
| 1862 | itM != llMachines.end();
|
---|
[18809] | 1863 | ++itM, ++i)
|
---|
[17033] | 1864 | {
|
---|
| 1865 | ComPtr<IMachine> pMachine = *itM;
|
---|
[18214] | 1866 | ComPtr<IVirtualSystemDescription> pVSD;
|
---|
[45068] | 1867 | CHECK_ERROR_BREAK(pMachine, ExportTo(pAppliance, Bstr(pszAbsFilePath).raw(), pVSD.asOutParam()));
|
---|
[75920] | 1868 |
|
---|
[33540] | 1869 | // Add additional info to the virtual system description if the user wants so
|
---|
[18809] | 1870 | ArgsMap *pmapArgs = NULL;
|
---|
| 1871 | ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
|
---|
| 1872 | if (itm != mapArgsMapsPerVsys.end())
|
---|
| 1873 | pmapArgs = &itm->second;
|
---|
| 1874 | if (pmapArgs)
|
---|
| 1875 | {
|
---|
| 1876 | ArgsMap::iterator itD;
|
---|
| 1877 | for (itD = pmapArgs->begin();
|
---|
| 1878 | itD != pmapArgs->end();
|
---|
| 1879 | ++itD)
|
---|
| 1880 | {
|
---|
[72476] | 1881 | if (itD->first == "vmname")
|
---|
[75920] | 1882 | {
|
---|
| 1883 | //remove default value if user has specified new name (default value is set in the ExportTo())
|
---|
[80738] | 1884 | // pVSD->RemoveDescriptionByType(VirtualSystemDescriptionType_Name);
|
---|
[72476] | 1885 | pVSD->AddDescription(VirtualSystemDescriptionType_Name,
|
---|
[82369] | 1886 | Bstr(itD->second).raw(), NULL);
|
---|
[75920] | 1887 | }
|
---|
[72476] | 1888 | else if (itD->first == "product")
|
---|
[32718] | 1889 | pVSD->AddDescription(VirtualSystemDescriptionType_Product,
|
---|
[82369] | 1890 | Bstr(itD->second).raw(), NULL);
|
---|
[18809] | 1891 | else if (itD->first == "producturl")
|
---|
[32718] | 1892 | pVSD->AddDescription(VirtualSystemDescriptionType_ProductUrl,
|
---|
[82369] | 1893 | Bstr(itD->second).raw(), NULL);
|
---|
[18809] | 1894 | else if (itD->first == "vendor")
|
---|
[32718] | 1895 | pVSD->AddDescription(VirtualSystemDescriptionType_Vendor,
|
---|
[82369] | 1896 | Bstr(itD->second).raw(), NULL);
|
---|
[18809] | 1897 | else if (itD->first == "vendorurl")
|
---|
[32718] | 1898 | pVSD->AddDescription(VirtualSystemDescriptionType_VendorUrl,
|
---|
[82369] | 1899 | Bstr(itD->second).raw(), NULL);
|
---|
[18809] | 1900 | else if (itD->first == "version")
|
---|
[32718] | 1901 | pVSD->AddDescription(VirtualSystemDescriptionType_Version,
|
---|
[82369] | 1902 | Bstr(itD->second).raw(), NULL);
|
---|
[46290] | 1903 | else if (itD->first == "description")
|
---|
| 1904 | pVSD->AddDescription(VirtualSystemDescriptionType_Description,
|
---|
[82369] | 1905 | Bstr(itD->second).raw(), NULL);
|
---|
[18809] | 1906 | else if (itD->first == "eula")
|
---|
[32718] | 1907 | pVSD->AddDescription(VirtualSystemDescriptionType_License,
|
---|
[82369] | 1908 | Bstr(itD->second).raw(), NULL);
|
---|
[18809] | 1909 | else if (itD->first == "eulafile")
|
---|
| 1910 | {
|
---|
| 1911 | Utf8Str strContent;
|
---|
| 1912 | void *pvFile;
|
---|
| 1913 | size_t cbFile;
|
---|
[24998] | 1914 | int irc = RTFileReadAll(itD->second.c_str(), &pvFile, &cbFile);
|
---|
| 1915 | if (RT_SUCCESS(irc))
|
---|
[18809] | 1916 | {
|
---|
[44868] | 1917 | Bstr bstrContent((char*)pvFile, cbFile);
|
---|
[32718] | 1918 | pVSD->AddDescription(VirtualSystemDescriptionType_License,
|
---|
[82369] | 1919 | bstrContent.raw(), NULL);
|
---|
[18809] | 1920 | RTFileReadAllFree(pvFile, cbFile);
|
---|
| 1921 | }
|
---|
| 1922 | else
|
---|
| 1923 | {
|
---|
[92372] | 1924 | RTMsgError(Appliance::tr("Cannot read license file \"%s\" which should be included in the virtual system %u."),
|
---|
[32701] | 1925 | itD->second.c_str(), i);
|
---|
[56118] | 1926 | return RTEXITCODE_FAILURE;
|
---|
[18809] | 1927 | }
|
---|
| 1928 | }
|
---|
[75920] | 1929 | /* add cloud export settings */
|
---|
| 1930 | else if (itD->first == "cloudshape")
|
---|
[76192] | 1931 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudInstanceShape,
|
---|
[82369] | 1932 | Bstr(itD->second).raw(), NULL);
|
---|
[75920] | 1933 | else if (itD->first == "clouddomain")
|
---|
[76192] | 1934 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudDomain,
|
---|
[82369] | 1935 | Bstr(itD->second).raw(), NULL);
|
---|
[75920] | 1936 | else if (itD->first == "clouddisksize")
|
---|
[76192] | 1937 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudBootDiskSize,
|
---|
[82369] | 1938 | Bstr(itD->second).raw(), NULL);
|
---|
[75920] | 1939 | else if (itD->first == "cloudbucket")
|
---|
[76192] | 1940 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudBucket,
|
---|
[82369] | 1941 | Bstr(itD->second).raw(), NULL);
|
---|
[75920] | 1942 | else if (itD->first == "cloudocivcn")
|
---|
| 1943 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCIVCN,
|
---|
[82369] | 1944 | Bstr(itD->second).raw(), NULL);
|
---|
[75920] | 1945 | else if (itD->first == "cloudpublicip")
|
---|
[76192] | 1946 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudPublicIP,
|
---|
[82369] | 1947 | Bstr(itD->second).raw(), NULL);
|
---|
[80707] | 1948 | else if (itD->first == "cloudprivateip")
|
---|
| 1949 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudPrivateIP,
|
---|
| 1950 | Bstr(itD->second).raw(), NULL);
|
---|
[75920] | 1951 | else if (itD->first == "cloudprofile")
|
---|
[76192] | 1952 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudProfileName,
|
---|
[82369] | 1953 | Bstr(itD->second).raw(), NULL);
|
---|
[75920] | 1954 | else if (itD->first == "cloudocisubnet")
|
---|
| 1955 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCISubnet,
|
---|
[82369] | 1956 | Bstr(itD->second).raw(), NULL);
|
---|
[75920] | 1957 | else if (itD->first == "cloudkeepobject")
|
---|
[76192] | 1958 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudKeepObject,
|
---|
[82369] | 1959 | Bstr(itD->second).raw(), NULL);
|
---|
[80707] | 1960 | else if (itD->first == "cloudlaunchmode")
|
---|
| 1961 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCILaunchMode,
|
---|
| 1962 | Bstr(itD->second).raw(), NULL);
|
---|
[75920] | 1963 | else if (itD->first == "cloudlaunchinstance")
|
---|
[76192] | 1964 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudLaunchInstance,
|
---|
[82369] | 1965 | Bstr(itD->second).raw(), NULL);
|
---|
[87278] | 1966 | else if (itD->first == "cloudinitscriptpath")
|
---|
| 1967 | pVSD->AddDescription(VirtualSystemDescriptionType_CloudInitScriptPath,
|
---|
| 1968 | Bstr(itD->second).raw(), NULL);
|
---|
| 1969 |
|
---|
[18809] | 1970 | }
|
---|
| 1971 | }
|
---|
[80599] | 1972 |
|
---|
| 1973 | VSDList.push_back(pVSD);//store vsd for the possible second stage
|
---|
[17033] | 1974 | }
|
---|
| 1975 |
|
---|
[95140] | 1976 | if (FAILED(hrc))
|
---|
[17033] | 1977 | break;
|
---|
| 1978 |
|
---|
[55182] | 1979 | /* Query required passwords and supply them to the appliance. */
|
---|
| 1980 | com::SafeArray<BSTR> aIdentifiers;
|
---|
| 1981 |
|
---|
| 1982 | CHECK_ERROR_BREAK(pAppliance, GetPasswordIds(ComSafeArrayAsOutParam(aIdentifiers)));
|
---|
| 1983 |
|
---|
| 1984 | if (aIdentifiers.size() > 0)
|
---|
| 1985 | {
|
---|
| 1986 | com::SafeArray<BSTR> aPasswords(aIdentifiers.size());
|
---|
[92372] | 1987 | RTPrintf(Appliance::tr("Enter the passwords for the following identifiers to export the apppliance:\n"));
|
---|
[55182] | 1988 | for (unsigned idxId = 0; idxId < aIdentifiers.size(); idxId++)
|
---|
| 1989 | {
|
---|
| 1990 | com::Utf8Str strPassword;
|
---|
| 1991 | Bstr bstrPassword;
|
---|
| 1992 | Bstr bstrId = aIdentifiers[idxId];
|
---|
| 1993 |
|
---|
[92372] | 1994 | RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, Appliance::tr("Password ID %s:"),
|
---|
| 1995 | Utf8Str(bstrId).c_str());
|
---|
[55182] | 1996 | if (rcExit == RTEXITCODE_FAILURE)
|
---|
[55268] | 1997 | {
|
---|
| 1998 | RTStrFree(pszAbsFilePath);
|
---|
[55182] | 1999 | return rcExit;
|
---|
[55268] | 2000 | }
|
---|
[55182] | 2001 |
|
---|
| 2002 | bstrPassword = strPassword;
|
---|
| 2003 | bstrPassword.detachTo(&aPasswords[idxId]);
|
---|
| 2004 | }
|
---|
| 2005 |
|
---|
| 2006 | CHECK_ERROR_BREAK(pAppliance, AddPasswords(ComSafeArrayAsInParam(aIdentifiers),
|
---|
| 2007 | ComSafeArrayAsInParam(aPasswords)));
|
---|
| 2008 | }
|
---|
| 2009 |
|
---|
[49038] | 2010 | if (fManifest)
|
---|
| 2011 | options.push_back(ExportOptions_CreateManifest);
|
---|
| 2012 |
|
---|
[49103] | 2013 | if (fExportISOImages)
|
---|
| 2014 | options.push_back(ExportOptions_ExportDVDImages);
|
---|
| 2015 |
|
---|
[17287] | 2016 | ComPtr<IProgress> progress;
|
---|
[32718] | 2017 | CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOvfFormat).raw(),
|
---|
[49038] | 2018 | ComSafeArrayAsInParam(options),
|
---|
[32718] | 2019 | Bstr(pszAbsFilePath).raw(),
|
---|
| 2020 | progress.asOutParam()));
|
---|
[18775] | 2021 | RTStrFree(pszAbsFilePath);
|
---|
[17033] | 2022 |
|
---|
[95140] | 2023 | hrc = showProgress(progress);
|
---|
[92372] | 2024 | CHECK_PROGRESS_ERROR_RET(progress, (Appliance::tr("Appliance write failed")), RTEXITCODE_FAILURE);
|
---|
[17287] | 2025 |
|
---|
[95140] | 2026 | if (SUCCEEDED(hrc))
|
---|
[92594] | 2027 | RTPrintf(Appliance::tr("Successfully exported %d machine(s).\n", "", llMachines.size()), llMachines.size());
|
---|
[17287] | 2028 |
|
---|
[80599] | 2029 | /*
|
---|
| 2030 | * The second stage for the cloud case
|
---|
| 2031 | */
|
---|
[91204] | 2032 | if (enmApplType == CLOUD)
|
---|
[80599] | 2033 | {
|
---|
| 2034 | /* Launch the exported VM if the appropriate flag had been set on the first stage */
|
---|
| 2035 | for (std::list< ComPtr<IVirtualSystemDescription> >::iterator itVSD = VSDList.begin();
|
---|
| 2036 | itVSD != VSDList.end();
|
---|
| 2037 | ++itVSD)
|
---|
| 2038 | {
|
---|
| 2039 | ComPtr<IVirtualSystemDescription> pVSD = *itVSD;
|
---|
[81086] | 2040 |
|
---|
[80599] | 2041 | com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
|
---|
| 2042 | com::SafeArray<BSTR> aRefs;
|
---|
| 2043 | com::SafeArray<BSTR> aOvfValues;
|
---|
| 2044 | com::SafeArray<BSTR> aVBoxValues;
|
---|
| 2045 | com::SafeArray<BSTR> aExtraConfigValues;
|
---|
| 2046 |
|
---|
| 2047 | CHECK_ERROR_BREAK(pVSD, GetDescriptionByType(VirtualSystemDescriptionType_CloudLaunchInstance,
|
---|
| 2048 | ComSafeArrayAsOutParam(retTypes),
|
---|
| 2049 | ComSafeArrayAsOutParam(aRefs),
|
---|
| 2050 | ComSafeArrayAsOutParam(aOvfValues),
|
---|
| 2051 | ComSafeArrayAsOutParam(aVBoxValues),
|
---|
| 2052 | ComSafeArrayAsOutParam(aExtraConfigValues)));
|
---|
| 2053 |
|
---|
| 2054 | Utf8Str flagCloudLaunchInstance(Bstr(aVBoxValues[0]).raw());
|
---|
| 2055 | retTypes.setNull(); aRefs.setNull(); aOvfValues.setNull(); aVBoxValues.setNull(); aExtraConfigValues.setNull();
|
---|
| 2056 |
|
---|
| 2057 | if (flagCloudLaunchInstance.equals("true"))
|
---|
| 2058 | {
|
---|
| 2059 | /* Getting the short provider name */
|
---|
| 2060 | Bstr bstrCloudProviderShortName(strOutputFile.c_str(), strOutputFile.find("://"));
|
---|
| 2061 |
|
---|
| 2062 | ComPtr<IVirtualBox> pVirtualBox = a->virtualBox;
|
---|
| 2063 | ComPtr<ICloudProviderManager> pCloudProviderManager;
|
---|
| 2064 | CHECK_ERROR_BREAK(pVirtualBox, COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()));
|
---|
| 2065 |
|
---|
| 2066 | ComPtr<ICloudProvider> pCloudProvider;
|
---|
| 2067 | CHECK_ERROR_BREAK(pCloudProviderManager,
|
---|
| 2068 | GetProviderByShortName(bstrCloudProviderShortName.raw(), pCloudProvider.asOutParam()));
|
---|
| 2069 |
|
---|
| 2070 | CHECK_ERROR_BREAK(pVSD, GetDescriptionByType(VirtualSystemDescriptionType_CloudProfileName,
|
---|
| 2071 | ComSafeArrayAsOutParam(retTypes),
|
---|
| 2072 | ComSafeArrayAsOutParam(aRefs),
|
---|
| 2073 | ComSafeArrayAsOutParam(aOvfValues),
|
---|
| 2074 | ComSafeArrayAsOutParam(aVBoxValues),
|
---|
| 2075 | ComSafeArrayAsOutParam(aExtraConfigValues)));
|
---|
[81086] | 2076 |
|
---|
[80599] | 2077 | ComPtr<ICloudProfile> pCloudProfile;
|
---|
| 2078 | CHECK_ERROR_BREAK(pCloudProvider, GetProfileByName(Bstr(aVBoxValues[0]).raw(), pCloudProfile.asOutParam()));
|
---|
| 2079 | retTypes.setNull(); aRefs.setNull(); aOvfValues.setNull(); aVBoxValues.setNull(); aExtraConfigValues.setNull();
|
---|
| 2080 |
|
---|
| 2081 | ComObjPtr<ICloudClient> oCloudClient;
|
---|
| 2082 | CHECK_ERROR_BREAK(pCloudProfile, CreateCloudClient(oCloudClient.asOutParam()));
|
---|
[92372] | 2083 | RTPrintf(Appliance::tr("Creating a cloud instance...\n"));
|
---|
[80599] | 2084 |
|
---|
| 2085 | ComPtr<IProgress> progress1;
|
---|
| 2086 | CHECK_ERROR_BREAK(oCloudClient, LaunchVM(pVSD, progress1.asOutParam()));
|
---|
[95140] | 2087 | hrc = showProgress(progress1);
|
---|
[92372] | 2088 | CHECK_PROGRESS_ERROR_RET(progress1, (Appliance::tr("Creating the cloud instance failed")),
|
---|
| 2089 | RTEXITCODE_FAILURE);
|
---|
[80599] | 2090 |
|
---|
[95140] | 2091 | if (SUCCEEDED(hrc))
|
---|
[80599] | 2092 | {
|
---|
| 2093 | CHECK_ERROR_BREAK(pVSD, GetDescriptionByType(VirtualSystemDescriptionType_CloudInstanceId,
|
---|
| 2094 | ComSafeArrayAsOutParam(retTypes),
|
---|
| 2095 | ComSafeArrayAsOutParam(aRefs),
|
---|
| 2096 | ComSafeArrayAsOutParam(aOvfValues),
|
---|
| 2097 | ComSafeArrayAsOutParam(aVBoxValues),
|
---|
| 2098 | ComSafeArrayAsOutParam(aExtraConfigValues)));
|
---|
| 2099 |
|
---|
[92372] | 2100 | RTPrintf(Appliance::tr("A cloud instance with id '%s' (provider '%s') was created\n"),
|
---|
[80599] | 2101 | Utf8Str(Bstr(aVBoxValues[0]).raw()).c_str(),
|
---|
| 2102 | Utf8Str(bstrCloudProviderShortName.raw()).c_str());
|
---|
| 2103 | retTypes.setNull(); aRefs.setNull(); aOvfValues.setNull(); aVBoxValues.setNull(); aExtraConfigValues.setNull();
|
---|
| 2104 | }
|
---|
| 2105 | }
|
---|
| 2106 | }
|
---|
| 2107 | }
|
---|
[17033] | 2108 | } while (0);
|
---|
| 2109 |
|
---|
[95140] | 2110 | return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
|
---|
[17033] | 2111 | }
|
---|
| 2112 |
|
---|
[84032] | 2113 |
|
---|
[84151] | 2114 | /*********************************************************************************************************************************
|
---|
| 2115 | * signova *
|
---|
| 2116 | *********************************************************************************************************************************/
|
---|
| 2117 |
|
---|
| 2118 | /**
|
---|
| 2119 | * Reads the OVA and saves the manifest and signed status.
|
---|
| 2120 | *
|
---|
| 2121 | * @returns VBox status code (fully messaged).
|
---|
| 2122 | * @param pszOva The name of the OVA.
|
---|
| 2123 | * @param iVerbosity The noise level.
|
---|
[84208] | 2124 | * @param fReSign Whether it is acceptable to have an existing signature
|
---|
| 2125 | * in the OVA or not.
|
---|
| 2126 | * @param phVfsFssOva Where to return the OVA file system stream handle.
|
---|
| 2127 | * This has been opened for updating and we're positioned
|
---|
| 2128 | * at the end of the stream.
|
---|
| 2129 | * @param pStrManifestName Where to return the manifest name.
|
---|
| 2130 | * @param phVfsManifest Where to return the manifest file handle (copy in mem).
|
---|
| 2131 | * @param phVfsOldSignature Where to return the handle to the old signature object.
|
---|
| 2132 | *
|
---|
| 2133 | * @note Caller must clean up return values on failure too!
|
---|
[84151] | 2134 | */
|
---|
[84208] | 2135 | static int openOvaAndGetManifestAndOldSignature(const char *pszOva, unsigned iVerbosity, bool fReSign,
|
---|
| 2136 | PRTVFSFSSTREAM phVfsFssOva, Utf8Str *pStrManifestName,
|
---|
| 2137 | PRTVFSFILE phVfsManifest, PRTVFSOBJ phVfsOldSignature)
|
---|
[84151] | 2138 | {
|
---|
| 2139 | /*
|
---|
| 2140 | * Clear return values.
|
---|
| 2141 | */
|
---|
[84208] | 2142 | *phVfsFssOva = NIL_RTVFSFSSTREAM;
|
---|
| 2143 | pStrManifestName->setNull();
|
---|
| 2144 | *phVfsManifest = NIL_RTVFSFILE;
|
---|
| 2145 | *phVfsOldSignature = NIL_RTVFSOBJ;
|
---|
[84151] | 2146 |
|
---|
| 2147 | /*
|
---|
| 2148 | * Open the file as a tar file system stream.
|
---|
| 2149 | */
|
---|
[84208] | 2150 | RTVFSFILE hVfsFileOva;
|
---|
[98298] | 2151 | int vrc = RTVfsFileOpenNormal(pszOva, RTFILE_O_OPEN | RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE, &hVfsFileOva);
|
---|
| 2152 | if (RT_FAILURE(vrc))
|
---|
| 2153 | return RTMsgErrorExitFailure(Appliance::tr("Failed to open OVA '%s' for updating: %Rrc"), pszOva, vrc);
|
---|
[84151] | 2154 |
|
---|
[84208] | 2155 | RTVFSFSSTREAM hVfsFssOva;
|
---|
[98298] | 2156 | vrc = RTZipTarFsStreamForFile(hVfsFileOva, RTZIPTARFORMAT_DEFAULT, RTZIPTAR_C_UPDATE, &hVfsFssOva);
|
---|
[84208] | 2157 | RTVfsFileRelease(hVfsFileOva);
|
---|
[98298] | 2158 | if (RT_FAILURE(vrc))
|
---|
| 2159 | return RTMsgErrorExitFailure(Appliance::tr("Failed to open OVA '%s' as a TAR file: %Rrc"), pszOva, vrc);
|
---|
[84208] | 2160 | *phVfsFssOva = hVfsFssOva;
|
---|
[84151] | 2161 |
|
---|
| 2162 | /*
|
---|
| 2163 | * Scan the objects in the stream and locate the manifest and any existing cert file.
|
---|
| 2164 | */
|
---|
| 2165 | if (iVerbosity >= 2)
|
---|
[92372] | 2166 | RTMsgInfo(Appliance::tr("Scanning OVA '%s' for a manifest and signature..."), pszOva);
|
---|
[84264] | 2167 | char *pszSignatureName = NULL;
|
---|
[84151] | 2168 | for (;;)
|
---|
| 2169 | {
|
---|
| 2170 | /*
|
---|
| 2171 | * Retrive the next object.
|
---|
| 2172 | */
|
---|
| 2173 | char *pszName;
|
---|
| 2174 | RTVFSOBJTYPE enmType;
|
---|
| 2175 | RTVFSOBJ hVfsObj;
|
---|
[98298] | 2176 | vrc = RTVfsFsStrmNext(hVfsFssOva, &pszName, &enmType, &hVfsObj);
|
---|
| 2177 | if (RT_FAILURE(vrc))
|
---|
[84151] | 2178 | {
|
---|
[98298] | 2179 | if (vrc == VERR_EOF)
|
---|
| 2180 | vrc = VINF_SUCCESS;
|
---|
[84151] | 2181 | else
|
---|
[98298] | 2182 | RTMsgError(Appliance::tr("RTVfsFsStrmNext returned %Rrc"), vrc);
|
---|
[84151] | 2183 | break;
|
---|
| 2184 | }
|
---|
| 2185 |
|
---|
| 2186 | if (iVerbosity > 2)
|
---|
[84208] | 2187 | RTMsgInfo(" %s %s\n", RTVfsTypeName(enmType), pszName);
|
---|
[84151] | 2188 |
|
---|
| 2189 | /*
|
---|
| 2190 | * Should we process this entry?
|
---|
| 2191 | */
|
---|
[84208] | 2192 | const char *pszSuffix = RTPathSuffix(pszName);
|
---|
| 2193 | if ( pszSuffix
|
---|
| 2194 | && RTStrICmpAscii(pszSuffix, ".mf") == 0
|
---|
| 2195 | && (enmType == RTVFSOBJTYPE_IO_STREAM || enmType == RTVFSOBJTYPE_FILE))
|
---|
[84151] | 2196 | {
|
---|
[84264] | 2197 | if (*phVfsManifest != NIL_RTVFSFILE)
|
---|
[98298] | 2198 | vrc = RTMsgErrorRc(VERR_DUPLICATE, Appliance::tr("OVA contains multiple manifests! first: %s second: %s"),
|
---|
| 2199 | pStrManifestName->c_str(), pszName);
|
---|
[84264] | 2200 | else if (pszSignatureName)
|
---|
[98298] | 2201 | vrc = RTMsgErrorRc(VERR_WRONG_ORDER,
|
---|
| 2202 | Appliance::tr("Unsupported OVA file ordering! Signature file ('%s') as succeeded by '%s'."),
|
---|
| 2203 | pszSignatureName, pszName);
|
---|
[84208] | 2204 | else
|
---|
[84151] | 2205 | {
|
---|
[84208] | 2206 | if (iVerbosity >= 2)
|
---|
[92372] | 2207 | RTMsgInfo(Appliance::tr("Found manifest file: %s"), pszName);
|
---|
[98298] | 2208 | vrc = pStrManifestName->assignNoThrow(pszName);
|
---|
| 2209 | if (RT_SUCCESS(vrc))
|
---|
[84151] | 2210 | {
|
---|
[84208] | 2211 | RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
|
---|
| 2212 | Assert(hVfsIos != NIL_RTVFSIOSTREAM);
|
---|
[98298] | 2213 | vrc = RTVfsMemorizeIoStreamAsFile(hVfsIos, RTFILE_O_READ, phVfsManifest);
|
---|
[84208] | 2214 | RTVfsIoStrmRelease(hVfsIos); /* consumes stream handle. */
|
---|
[98298] | 2215 | if (RT_FAILURE(vrc))
|
---|
| 2216 | vrc = RTMsgErrorRc(VERR_DUPLICATE, Appliance::tr("Failed to memorize the manifest: %Rrc"), vrc);
|
---|
[84151] | 2217 | }
|
---|
[84208] | 2218 | else
|
---|
[92372] | 2219 | RTMsgError(Appliance::tr("Out of memory!"));
|
---|
[84151] | 2220 | }
|
---|
[84208] | 2221 | }
|
---|
| 2222 | else if ( pszSuffix
|
---|
| 2223 | && RTStrICmpAscii(pszSuffix, ".cert") == 0
|
---|
| 2224 | && (enmType == RTVFSOBJTYPE_IO_STREAM || enmType == RTVFSOBJTYPE_FILE))
|
---|
| 2225 | {
|
---|
[84264] | 2226 | if (*phVfsOldSignature != NIL_RTVFSOBJ)
|
---|
[98298] | 2227 | vrc = RTMsgErrorRc(VERR_WRONG_ORDER, Appliance::tr("Multiple signature files! (%s)"), pszName);
|
---|
[84208] | 2228 | else
|
---|
[84151] | 2229 | {
|
---|
| 2230 | if (iVerbosity >= 2)
|
---|
[92372] | 2231 | RTMsgInfo(Appliance::tr("Found existing signature file: %s"), pszName);
|
---|
[84264] | 2232 | pszSignatureName = pszName;
|
---|
[84208] | 2233 | *phVfsOldSignature = hVfsObj;
|
---|
[84264] | 2234 | pszName = NULL;
|
---|
[84208] | 2235 | hVfsObj = NIL_RTVFSOBJ;
|
---|
[84151] | 2236 | }
|
---|
| 2237 | }
|
---|
[84264] | 2238 | else if (pszSignatureName)
|
---|
[98298] | 2239 | vrc = RTMsgErrorRc(VERR_WRONG_ORDER,
|
---|
| 2240 | Appliance::tr("Unsupported OVA file ordering! Signature file ('%s') as succeeded by '%s'."),
|
---|
| 2241 | pszSignatureName, pszName);
|
---|
[84151] | 2242 |
|
---|
| 2243 | /*
|
---|
| 2244 | * Release the current object and string.
|
---|
| 2245 | */
|
---|
| 2246 | RTVfsObjRelease(hVfsObj);
|
---|
| 2247 | RTStrFree(pszName);
|
---|
[98298] | 2248 | if (RT_FAILURE(vrc))
|
---|
[84151] | 2249 | break;
|
---|
| 2250 | }
|
---|
| 2251 |
|
---|
| 2252 | /*
|
---|
| 2253 | * Complain if no manifest.
|
---|
| 2254 | */
|
---|
[98298] | 2255 | if (RT_SUCCESS(vrc) && *phVfsManifest == NIL_RTVFSFILE)
|
---|
| 2256 | vrc = RTMsgErrorRc(VERR_NOT_FOUND, Appliance::tr("The OVA contains no manifest and cannot be signed!"));
|
---|
| 2257 | else if (RT_SUCCESS(vrc) && *phVfsOldSignature != NIL_RTVFSOBJ && !fReSign)
|
---|
| 2258 | vrc = RTMsgErrorRc(VERR_ALREADY_EXISTS,
|
---|
| 2259 | Appliance::tr("The OVA is already signed ('%s')! (Use the --force option to force re-signing it.)"),
|
---|
| 2260 | pszSignatureName);
|
---|
[84151] | 2261 |
|
---|
[84264] | 2262 | RTStrFree(pszSignatureName);
|
---|
[98298] | 2263 | return vrc;
|
---|
[84151] | 2264 | }
|
---|
| 2265 |
|
---|
| 2266 |
|
---|
| 2267 | /**
|
---|
[84208] | 2268 | * Continues where openOvaAndGetManifestAndOldSignature() left off and writes
|
---|
| 2269 | * the signature file to the OVA.
|
---|
| 2270 | *
|
---|
| 2271 | * When @a hVfsOldSignature isn't NIL, the old signature it represent will be
|
---|
| 2272 | * replaced. The open function has already made sure there isn't anything
|
---|
| 2273 | * following the .cert file in that case.
|
---|
| 2274 | */
|
---|
[84264] | 2275 | static int updateTheOvaSignature(RTVFSFSSTREAM hVfsFssOva, const char *pszOva, const char *pszSignatureName,
|
---|
| 2276 | RTVFSFILE hVfsFileSignature, RTVFSOBJ hVfsOldSignature, unsigned iVerbosity)
|
---|
[84208] | 2277 | {
|
---|
[84264] | 2278 | if (iVerbosity > 1)
|
---|
[92372] | 2279 | RTMsgInfo(Appliance::tr("Writing '%s' to the OVA..."), pszSignatureName);
|
---|
[84264] | 2280 |
|
---|
[84208] | 2281 | /*
|
---|
| 2282 | * Truncate the file at the old signature, if present.
|
---|
| 2283 | */
|
---|
[98298] | 2284 | int vrc;
|
---|
[84208] | 2285 | if (hVfsOldSignature != NIL_RTVFSOBJ)
|
---|
| 2286 | {
|
---|
[98298] | 2287 | vrc = RTZipTarFsStreamTruncate(hVfsFssOva, hVfsOldSignature, false /*fAfter*/);
|
---|
| 2288 | if (RT_FAILURE(vrc))
|
---|
| 2289 | return RTMsgErrorRc(vrc, Appliance::tr("RTZipTarFsStreamTruncate failed on '%s': %Rrc"), pszOva, vrc);
|
---|
[84208] | 2290 | }
|
---|
| 2291 |
|
---|
| 2292 | /*
|
---|
| 2293 | * Append the signature file. We have to rewind it first or
|
---|
| 2294 | * we'll end up with VERR_EOF, probably not a great idea...
|
---|
| 2295 | */
|
---|
[98298] | 2296 | vrc = RTVfsFileSeek(hVfsFileSignature, 0, RTFILE_SEEK_BEGIN, NULL);
|
---|
| 2297 | if (RT_FAILURE(vrc))
|
---|
| 2298 | return RTMsgErrorRc(vrc, Appliance::tr("RTVfsFileSeek(hVfsFileSignature) failed: %Rrc"), vrc);
|
---|
[84208] | 2299 |
|
---|
| 2300 | RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFileSignature);
|
---|
[98298] | 2301 | vrc = RTVfsFsStrmAdd(hVfsFssOva, pszSignatureName, hVfsObj, 0 /*fFlags*/);
|
---|
[84208] | 2302 | RTVfsObjRelease(hVfsObj);
|
---|
[98298] | 2303 | if (RT_FAILURE(vrc))
|
---|
| 2304 | return RTMsgErrorRc(vrc, Appliance::tr("RTVfsFsStrmAdd('%s') failed on '%s': %Rrc"), pszSignatureName, pszOva, vrc);
|
---|
[84208] | 2305 |
|
---|
| 2306 | /*
|
---|
| 2307 | * Terminate the file system stream.
|
---|
| 2308 | */
|
---|
[98298] | 2309 | vrc = RTVfsFsStrmEnd(hVfsFssOva);
|
---|
| 2310 | if (RT_FAILURE(vrc))
|
---|
| 2311 | return RTMsgErrorRc(vrc, Appliance::tr("RTVfsFsStrmEnd failed on '%s': %Rrc"), pszOva, vrc);
|
---|
[84208] | 2312 |
|
---|
| 2313 | return VINF_SUCCESS;
|
---|
| 2314 | }
|
---|
| 2315 |
|
---|
| 2316 |
|
---|
| 2317 | /**
|
---|
[84311] | 2318 | * Worker for doCheckPkcs7Signature.
|
---|
| 2319 | */
|
---|
| 2320 | static int doCheckPkcs7SignatureWorker(PRTCRPKCS7CONTENTINFO pContentInfo, void const *pvManifest, size_t cbManifest,
|
---|
| 2321 | unsigned iVerbosity, const char *pszTag, PRTERRINFOSTATIC pErrInfo)
|
---|
| 2322 | {
|
---|
[98298] | 2323 | int vrc;
|
---|
[84311] | 2324 |
|
---|
| 2325 | /*
|
---|
| 2326 | * It must be signedData.
|
---|
| 2327 | */
|
---|
| 2328 | if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
|
---|
| 2329 | {
|
---|
| 2330 | PRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData;
|
---|
| 2331 |
|
---|
| 2332 | /*
|
---|
| 2333 | * Inside the signedData there must be just 'data'.
|
---|
| 2334 | */
|
---|
| 2335 | if (!strcmp(pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID))
|
---|
| 2336 | {
|
---|
| 2337 | /*
|
---|
| 2338 | * Check that things add up.
|
---|
| 2339 | */
|
---|
[98298] | 2340 | vrc = RTCrPkcs7SignedData_CheckSanity(pSignedData,
|
---|
| 2341 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
|
---|
| 2342 | | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
|
---|
| 2343 | RTErrInfoInitStatic(pErrInfo), "SD");
|
---|
| 2344 | if (RT_SUCCESS(vrc))
|
---|
[84311] | 2345 | {
|
---|
| 2346 | if (iVerbosity > 2 && pszTag == NULL)
|
---|
[92372] | 2347 | RTMsgInfo(Appliance::tr(" Successfully decoded the PKCS#7/CMS signature..."));
|
---|
[84311] | 2348 |
|
---|
| 2349 | /*
|
---|
| 2350 | * Check that we can verify the signed data, but skip certificate validate as
|
---|
| 2351 | * we probably don't necessarily have the correct root certs handy here.
|
---|
| 2352 | */
|
---|
| 2353 | RTTIMESPEC Now;
|
---|
[98298] | 2354 | vrc = RTCrPkcs7VerifySignedDataWithExternalData(pContentInfo, RTCRPKCS7VERIFY_SD_F_TRUST_ALL_CERTS,
|
---|
| 2355 | NIL_RTCRSTORE /*hAdditionalCerts*/,
|
---|
| 2356 | NIL_RTCRSTORE /*hTrustedCerts*/,
|
---|
| 2357 | RTTimeNow(&Now),
|
---|
| 2358 | NULL /*pfnVerifyCert*/, NULL /*pvUser*/,
|
---|
| 2359 | pvManifest, cbManifest, RTErrInfoInitStatic(pErrInfo));
|
---|
| 2360 | if (RT_SUCCESS(vrc))
|
---|
[84311] | 2361 | {
|
---|
| 2362 | if (iVerbosity > 1 && pszTag != NULL)
|
---|
[92372] | 2363 | RTMsgInfo(Appliance::tr(" Successfully verified the PKCS#7/CMS signature"));
|
---|
[84311] | 2364 | }
|
---|
| 2365 | else
|
---|
[98298] | 2366 | vrc = RTMsgErrorRc(vrc, Appliance::tr("Failed to verify the PKCS#7/CMS signature: %Rrc%RTeim"),
|
---|
| 2367 | vrc, &pErrInfo->Core);
|
---|
[84311] | 2368 | }
|
---|
| 2369 | else
|
---|
[92372] | 2370 | RTMsgError(Appliance::tr("RTCrPkcs7SignedData_CheckSanity failed on PKCS#7/CMS signature: %Rrc%RTeim"),
|
---|
[98298] | 2371 | vrc, &pErrInfo->Core);
|
---|
[84311] | 2372 |
|
---|
| 2373 | }
|
---|
| 2374 | else
|
---|
[98298] | 2375 | vrc = RTMsgErrorRc(VERR_WRONG_TYPE, Appliance::tr("PKCS#7/CMS signature inner ContentType isn't 'data' but: %s"),
|
---|
| 2376 | pSignedData->ContentInfo.ContentType.szObjId);
|
---|
[84311] | 2377 | }
|
---|
| 2378 | else
|
---|
[98298] | 2379 | vrc = RTMsgErrorRc(VERR_WRONG_TYPE, Appliance::tr("PKCS#7/CMD signature is not 'signedData': %s"),
|
---|
| 2380 | pContentInfo->ContentType.szObjId);
|
---|
| 2381 | return vrc;
|
---|
[84311] | 2382 | }
|
---|
| 2383 |
|
---|
| 2384 | /**
|
---|
[84231] | 2385 | * For testing the decoding side.
|
---|
| 2386 | */
|
---|
| 2387 | static int doCheckPkcs7Signature(void const *pvSignature, size_t cbSignature, PCRTCRX509CERTIFICATE pCertificate,
|
---|
[84311] | 2388 | RTCRSTORE hIntermediateCerts, void const *pvManifest, size_t cbManifest,
|
---|
| 2389 | unsigned iVerbosity, PRTERRINFOSTATIC pErrInfo)
|
---|
[84231] | 2390 | {
|
---|
[84311] | 2391 | RT_NOREF(pCertificate, hIntermediateCerts);
|
---|
| 2392 |
|
---|
[84231] | 2393 | RTASN1CURSORPRIMARY PrimaryCursor;
|
---|
| 2394 | RTAsn1CursorInitPrimary(&PrimaryCursor, pvSignature, (uint32_t)cbSignature, RTErrInfoInitStatic(pErrInfo),
|
---|
| 2395 | &g_RTAsn1DefaultAllocator, 0, "Signature");
|
---|
| 2396 |
|
---|
| 2397 | RTCRPKCS7CONTENTINFO ContentInfo;
|
---|
| 2398 | RT_ZERO(ContentInfo);
|
---|
[98298] | 2399 | int vrc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &ContentInfo, "CI");
|
---|
| 2400 | if (RT_SUCCESS(vrc))
|
---|
[84231] | 2401 | {
|
---|
| 2402 | if (iVerbosity > 5)
|
---|
| 2403 | RTAsn1Dump(&ContentInfo.SeqCore.Asn1Core, 0 /*fFlags*/, 0 /*uLevel*/, RTStrmDumpPrintfV, g_pStdOut);
|
---|
| 2404 |
|
---|
[98298] | 2405 | vrc = doCheckPkcs7SignatureWorker(&ContentInfo, pvManifest, cbManifest, iVerbosity, NULL, pErrInfo);
|
---|
| 2406 | if (RT_SUCCESS(vrc))
|
---|
[84231] | 2407 | {
|
---|
| 2408 | /*
|
---|
[84311] | 2409 | * Clone it and repeat. This is to catch IPRT paths assuming
|
---|
| 2410 | * that encoded data is always on hand.
|
---|
[84231] | 2411 | */
|
---|
[84311] | 2412 | RTCRPKCS7CONTENTINFO ContentInfo2;
|
---|
[98298] | 2413 | vrc = RTCrPkcs7ContentInfo_Clone(&ContentInfo2, &ContentInfo, &g_RTAsn1DefaultAllocator);
|
---|
| 2414 | if (RT_SUCCESS(vrc))
|
---|
[84231] | 2415 | {
|
---|
[98298] | 2416 | vrc = doCheckPkcs7SignatureWorker(&ContentInfo2, pvManifest, cbManifest, iVerbosity, "cloned", pErrInfo);
|
---|
[84311] | 2417 | RTCrPkcs7ContentInfo_Delete(&ContentInfo2);
|
---|
[84231] | 2418 | }
|
---|
| 2419 | else
|
---|
[98298] | 2420 | vrc = RTMsgErrorRc(vrc, Appliance::tr("RTCrPkcs7ContentInfo_Clone failed: %Rrc"), vrc);
|
---|
[84231] | 2421 | }
|
---|
| 2422 | }
|
---|
| 2423 | else
|
---|
[92372] | 2424 | RTMsgError(Appliance::tr("RTCrPkcs7ContentInfo_DecodeAsn1 failed to decode PKCS#7/CMS signature: %Rrc%RTemi"),
|
---|
[98298] | 2425 | vrc, &pErrInfo->Core);
|
---|
[84231] | 2426 |
|
---|
| 2427 | RTCrPkcs7ContentInfo_Delete(&ContentInfo);
|
---|
[98298] | 2428 | return vrc;
|
---|
[84231] | 2429 | }
|
---|
| 2430 |
|
---|
| 2431 |
|
---|
| 2432 | /**
|
---|
[84151] | 2433 | * Creates a PKCS\#7 signature and appends it to the signature file in PEM
|
---|
| 2434 | * format.
|
---|
| 2435 | */
|
---|
[84250] | 2436 | static int doAddPkcs7Signature(PCRTCRX509CERTIFICATE pCertificate, RTCRKEY hPrivateKey, RTDIGESTTYPE enmDigestType,
|
---|
[84231] | 2437 | unsigned cIntermediateCerts, const char **papszIntermediateCerts, RTVFSFILE hVfsFileManifest,
|
---|
| 2438 | unsigned iVerbosity, PRTERRINFOSTATIC pErrInfo, RTVFSFILE hVfsFileSignature)
|
---|
[84151] | 2439 | {
|
---|
[84231] | 2440 | /*
|
---|
| 2441 | * Add a blank line, just for good measure.
|
---|
| 2442 | */
|
---|
[98298] | 2443 | int vrc = RTVfsFileWrite(hVfsFileSignature, "\n", 1, NULL);
|
---|
| 2444 | if (RT_FAILURE(vrc))
|
---|
| 2445 | return RTMsgErrorRc(vrc, "RTVfsFileWrite/signature: %Rrc", vrc);
|
---|
[84231] | 2446 |
|
---|
| 2447 | /*
|
---|
| 2448 | * Read the manifest into a single memory block.
|
---|
| 2449 | */
|
---|
| 2450 | uint64_t cbManifest;
|
---|
[98298] | 2451 | vrc = RTVfsFileQuerySize(hVfsFileManifest, &cbManifest);
|
---|
| 2452 | if (RT_FAILURE(vrc))
|
---|
| 2453 | return RTMsgErrorRc(vrc, "RTVfsFileQuerySize/manifest: %Rrc", vrc);
|
---|
[84231] | 2454 | if (cbManifest > _4M)
|
---|
[92372] | 2455 | return RTMsgErrorRc(VERR_OUT_OF_RANGE, Appliance::tr("Manifest is too big: %#RX64 bytes, max 4MiB", "", cbManifest),
|
---|
| 2456 | cbManifest);
|
---|
[84231] | 2457 |
|
---|
| 2458 | void *pvManifest = RTMemAllocZ(cbManifest + 1);
|
---|
| 2459 | if (!pvManifest)
|
---|
[92372] | 2460 | return RTMsgErrorRc(VERR_NO_MEMORY, Appliance::tr("Out of memory!"));
|
---|
[84231] | 2461 |
|
---|
[98298] | 2462 | vrc = RTVfsFileReadAt(hVfsFileManifest, 0, pvManifest, (size_t)cbManifest, NULL);
|
---|
| 2463 | if (RT_SUCCESS(vrc))
|
---|
[84231] | 2464 | {
|
---|
| 2465 | /*
|
---|
| 2466 | * Load intermediate certificates.
|
---|
| 2467 | */
|
---|
| 2468 | RTCRSTORE hIntermediateCerts = NIL_RTCRSTORE;
|
---|
| 2469 | if (cIntermediateCerts)
|
---|
| 2470 | {
|
---|
[98298] | 2471 | vrc = RTCrStoreCreateInMem(&hIntermediateCerts, cIntermediateCerts);
|
---|
| 2472 | if (RT_SUCCESS(vrc))
|
---|
[84231] | 2473 | {
|
---|
| 2474 | for (unsigned i = 0; i < cIntermediateCerts; i++)
|
---|
| 2475 | {
|
---|
| 2476 | const char *pszFile = papszIntermediateCerts[i];
|
---|
[98298] | 2477 | vrc = RTCrStoreCertAddFromFile(hIntermediateCerts, 0 /*fFlags*/, pszFile, &pErrInfo->Core);
|
---|
| 2478 | if (RT_FAILURE(vrc))
|
---|
[84231] | 2479 | {
|
---|
[98298] | 2480 | RTMsgError(Appliance::tr("RTCrStoreCertAddFromFile failed on '%s': %Rrc%#RTeim"),
|
---|
| 2481 | pszFile, vrc, &pErrInfo->Core);
|
---|
[84231] | 2482 | break;
|
---|
| 2483 | }
|
---|
| 2484 | }
|
---|
| 2485 | }
|
---|
| 2486 | else
|
---|
[98298] | 2487 | RTMsgError(Appliance::tr("RTCrStoreCreateInMem failed: %Rrc"), vrc);
|
---|
[84231] | 2488 | }
|
---|
[98298] | 2489 | if (RT_SUCCESS(vrc))
|
---|
[84231] | 2490 | {
|
---|
| 2491 | /*
|
---|
| 2492 | * Do a dry run to determin the size of the signed data.
|
---|
| 2493 | */
|
---|
| 2494 | size_t cbResult = 0;
|
---|
[98298] | 2495 | vrc = RTCrPkcs7SimpleSignSignedData(RTCRPKCS7SIGN_SD_F_DEATCHED | RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP,
|
---|
| 2496 | pCertificate, hPrivateKey, pvManifest, (size_t)cbManifest, enmDigestType,
|
---|
| 2497 | hIntermediateCerts, NULL /*pAdditionalAuthenticatedAttribs*/,
|
---|
| 2498 | NULL /*pvResult*/, &cbResult, RTErrInfoInitStatic(pErrInfo));
|
---|
| 2499 | if (vrc == VERR_BUFFER_OVERFLOW)
|
---|
[84231] | 2500 | {
|
---|
| 2501 | /*
|
---|
| 2502 | * Allocate a buffer of the right size and do the real run.
|
---|
| 2503 | */
|
---|
| 2504 | void *pvResult = RTMemAllocZ(cbResult);
|
---|
| 2505 | if (pvResult)
|
---|
| 2506 | {
|
---|
[98298] | 2507 | vrc = RTCrPkcs7SimpleSignSignedData(RTCRPKCS7SIGN_SD_F_DEATCHED | RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP,
|
---|
| 2508 | pCertificate, hPrivateKey, pvManifest, (size_t)cbManifest, enmDigestType,
|
---|
| 2509 | hIntermediateCerts, NULL /*pAdditionalAuthenticatedAttribs*/,
|
---|
| 2510 | pvResult, &cbResult, RTErrInfoInitStatic(pErrInfo));
|
---|
| 2511 | if (RT_SUCCESS(vrc))
|
---|
[84231] | 2512 | {
|
---|
| 2513 | /*
|
---|
| 2514 | * Add it to the signature file in PEM format.
|
---|
| 2515 | */
|
---|
[98298] | 2516 | vrc = (int)RTCrPemWriteBlobToVfsFile(hVfsFileSignature, pvResult, cbResult, "CMS");
|
---|
| 2517 | if (RT_SUCCESS(vrc))
|
---|
[84231] | 2518 | {
|
---|
| 2519 | if (iVerbosity > 1)
|
---|
[92594] | 2520 | RTMsgInfo(Appliance::tr("Created PKCS#7/CMS signature: %zu bytes, %s.", "", cbResult),
|
---|
[84264] | 2521 | cbResult, RTCrDigestTypeToName(enmDigestType));
|
---|
[85631] | 2522 | if (enmDigestType == RTDIGESTTYPE_SHA1)
|
---|
[92372] | 2523 | RTMsgWarning(Appliance::tr("Using SHA-1 instead of SHA-3 for the PKCS#7/CMS signature."));
|
---|
[84231] | 2524 |
|
---|
| 2525 | /*
|
---|
| 2526 | * Try decode and verify the signature.
|
---|
| 2527 | */
|
---|
[98298] | 2528 | vrc = doCheckPkcs7Signature(pvResult, cbResult, pCertificate, hIntermediateCerts,
|
---|
| 2529 | pvManifest, (size_t)cbManifest, iVerbosity, pErrInfo);
|
---|
[84231] | 2530 | }
|
---|
| 2531 | else
|
---|
[98298] | 2532 | RTMsgError(Appliance::tr("RTCrPemWriteBlobToVfsFile failed: %Rrc"), vrc);
|
---|
[84231] | 2533 | }
|
---|
| 2534 | RTMemFree(pvResult);
|
---|
| 2535 | }
|
---|
| 2536 | else
|
---|
[98298] | 2537 | vrc = RTMsgErrorRc(VERR_NO_MEMORY, Appliance::tr("Out of memory!"));
|
---|
[84231] | 2538 | }
|
---|
| 2539 | else
|
---|
[98298] | 2540 | RTMsgError(Appliance::tr("RTCrPkcs7SimpleSignSignedData failed: %Rrc%#RTeim"), vrc, &pErrInfo->Core);
|
---|
[84231] | 2541 | }
|
---|
| 2542 | }
|
---|
| 2543 | else
|
---|
[98298] | 2544 | RTMsgError(Appliance::tr("RTVfsFileReadAt failed: %Rrc"), vrc);
|
---|
[84231] | 2545 | RTMemFree(pvManifest);
|
---|
[98298] | 2546 | return vrc;
|
---|
[84151] | 2547 | }
|
---|
| 2548 |
|
---|
| 2549 |
|
---|
| 2550 | /**
|
---|
| 2551 | * Performs the OVA signing, producing an in-memory cert-file.
|
---|
| 2552 | */
|
---|
[84212] | 2553 | static int doTheOvaSigning(PRTCRX509CERTIFICATE pCertificate, RTCRKEY hPrivateKey, RTDIGESTTYPE enmDigestType,
|
---|
[84151] | 2554 | const char *pszManifestName, RTVFSFILE hVfsFileManifest,
|
---|
[84231] | 2555 | bool fPkcs7, unsigned cIntermediateCerts, const char **papszIntermediateCerts, unsigned iVerbosity,
|
---|
[84151] | 2556 | PRTERRINFOSTATIC pErrInfo, PRTVFSFILE phVfsFileSignature)
|
---|
| 2557 | {
|
---|
| 2558 | /*
|
---|
[84250] | 2559 | * Determine the digest types, preferring SHA-256 for the OVA signature
|
---|
| 2560 | * and SHA-512 for the PKCS#7/CMS one. Try use different hashes for the two.
|
---|
[84151] | 2561 | */
|
---|
[84250] | 2562 | if (enmDigestType == RTDIGESTTYPE_UNKNOWN)
|
---|
| 2563 | {
|
---|
| 2564 | if (RTCrPkixCanCertHandleDigestType(pCertificate, RTDIGESTTYPE_SHA256, NULL))
|
---|
| 2565 | enmDigestType = RTDIGESTTYPE_SHA256;
|
---|
| 2566 | else
|
---|
| 2567 | enmDigestType = RTDIGESTTYPE_SHA1;
|
---|
| 2568 | }
|
---|
[84151] | 2569 |
|
---|
[85631] | 2570 | /* Try SHA-3 for better diversity, only fall back on SHA1 if the private
|
---|
| 2571 | key doesn't have enough bits (we skip SHA2 as it has the same variants
|
---|
| 2572 | and key size requirements as SHA-3). */
|
---|
[84250] | 2573 | RTDIGESTTYPE enmPkcs7DigestType;
|
---|
[85631] | 2574 | if (RTCrPkixCanCertHandleDigestType(pCertificate, RTDIGESTTYPE_SHA3_512, NULL))
|
---|
| 2575 | enmPkcs7DigestType = RTDIGESTTYPE_SHA3_512;
|
---|
| 2576 | else if (RTCrPkixCanCertHandleDigestType(pCertificate, RTDIGESTTYPE_SHA3_384, NULL))
|
---|
| 2577 | enmPkcs7DigestType = RTDIGESTTYPE_SHA3_384;
|
---|
| 2578 | else if (RTCrPkixCanCertHandleDigestType(pCertificate, RTDIGESTTYPE_SHA3_256, NULL))
|
---|
| 2579 | enmPkcs7DigestType = RTDIGESTTYPE_SHA3_256;
|
---|
| 2580 | else if (RTCrPkixCanCertHandleDigestType(pCertificate, RTDIGESTTYPE_SHA3_224, NULL))
|
---|
| 2581 | enmPkcs7DigestType = RTDIGESTTYPE_SHA3_224;
|
---|
| 2582 | else
|
---|
| 2583 | enmPkcs7DigestType = RTDIGESTTYPE_SHA1;
|
---|
[84250] | 2584 |
|
---|
| 2585 | /*
|
---|
| 2586 | * Figure the string name for the .cert file.
|
---|
| 2587 | */
|
---|
[84212] | 2588 | const char *pszDigestType;
|
---|
[84151] | 2589 | switch (enmDigestType)
|
---|
| 2590 | {
|
---|
| 2591 | case RTDIGESTTYPE_SHA1: pszDigestType = "SHA1"; break;
|
---|
| 2592 | case RTDIGESTTYPE_SHA256: pszDigestType = "SHA256"; break;
|
---|
[84250] | 2593 | case RTDIGESTTYPE_SHA224: pszDigestType = "SHA224"; break;
|
---|
[84151] | 2594 | case RTDIGESTTYPE_SHA512: pszDigestType = "SHA512"; break;
|
---|
| 2595 | default:
|
---|
| 2596 | return RTMsgErrorRc(VERR_INVALID_PARAMETER,
|
---|
[92372] | 2597 | Appliance::tr("Unsupported digest type: %s"), RTCrDigestTypeToName(enmDigestType));
|
---|
[84151] | 2598 | }
|
---|
| 2599 |
|
---|
| 2600 | /*
|
---|
| 2601 | * Digest the manifest file.
|
---|
| 2602 | */
|
---|
[84250] | 2603 | RTCRDIGEST hDigest = NIL_RTCRDIGEST;
|
---|
[98298] | 2604 | int vrc = RTCrDigestCreateByType(&hDigest, enmDigestType);
|
---|
| 2605 | if (RT_FAILURE(vrc))
|
---|
| 2606 | return RTMsgErrorRc(vrc, Appliance::tr("Failed to create digest for %s: %Rrc"), RTCrDigestTypeToName(enmDigestType), vrc);
|
---|
[84250] | 2607 |
|
---|
[98298] | 2608 | vrc = RTCrDigestUpdateFromVfsFile(hDigest, hVfsFileManifest, true /*fRewindFile*/);
|
---|
| 2609 | if (RT_SUCCESS(vrc))
|
---|
| 2610 | vrc = RTCrDigestFinal(hDigest, NULL, 0);
|
---|
| 2611 | if (RT_SUCCESS(vrc))
|
---|
[84151] | 2612 | {
|
---|
| 2613 | /*
|
---|
| 2614 | * Sign the digest. Two passes, first to figure the signature size, the
|
---|
| 2615 | * second to do the actual signing.
|
---|
| 2616 | */
|
---|
| 2617 | PCRTASN1OBJID const pAlgorithm = &pCertificate->TbsCertificate.SubjectPublicKeyInfo.Algorithm.Algorithm;
|
---|
| 2618 | PCRTASN1DYNTYPE const pAlgoParams = &pCertificate->TbsCertificate.SubjectPublicKeyInfo.Algorithm.Parameters;
|
---|
| 2619 | size_t cbSignature = 0;
|
---|
[98298] | 2620 | vrc = RTCrPkixPubKeySignDigest(pAlgorithm, hPrivateKey, pAlgoParams, hDigest, 0 /*fFlags*/,
|
---|
| 2621 | NULL /*pvSignature*/, &cbSignature, RTErrInfoInitStatic(pErrInfo));
|
---|
| 2622 | if (vrc == VERR_BUFFER_OVERFLOW)
|
---|
[84151] | 2623 | {
|
---|
| 2624 | void *pvSignature = RTMemAllocZ(cbSignature);
|
---|
| 2625 | if (pvSignature)
|
---|
| 2626 | {
|
---|
[98298] | 2627 | vrc = RTCrPkixPubKeySignDigest(pAlgorithm, hPrivateKey, pAlgoParams, hDigest, 0,
|
---|
| 2628 | pvSignature, &cbSignature, RTErrInfoInitStatic(pErrInfo));
|
---|
| 2629 | if (RT_SUCCESS(vrc))
|
---|
[84151] | 2630 | {
|
---|
[84264] | 2631 | if (iVerbosity > 1)
|
---|
[92594] | 2632 | RTMsgInfo(Appliance::tr("Created OVA signature: %zu bytes, %s", "", cbSignature), cbSignature,
|
---|
[92372] | 2633 | RTCrDigestTypeToName(enmDigestType));
|
---|
[84264] | 2634 |
|
---|
[84151] | 2635 | /*
|
---|
| 2636 | * Verify the signature using the certificate to make sure we've
|
---|
| 2637 | * been given the right private key.
|
---|
| 2638 | */
|
---|
[98298] | 2639 | vrc = RTCrPkixPubKeyVerifySignedDigestByCertPubKeyInfo(&pCertificate->TbsCertificate.SubjectPublicKeyInfo,
|
---|
| 2640 | pvSignature, cbSignature, hDigest,
|
---|
| 2641 | RTErrInfoInitStatic(pErrInfo));
|
---|
| 2642 | if (RT_SUCCESS(vrc))
|
---|
[84151] | 2643 | {
|
---|
[84264] | 2644 | if (iVerbosity > 2)
|
---|
[92372] | 2645 | RTMsgInfo(Appliance::tr(" Successfully decoded and verified the OVA signature.\n"));
|
---|
[84231] | 2646 |
|
---|
[84151] | 2647 | /*
|
---|
| 2648 | * Create the output file.
|
---|
| 2649 | */
|
---|
| 2650 | RTVFSFILE hVfsFileSignature;
|
---|
[98298] | 2651 | vrc = RTVfsMemFileCreate(NIL_RTVFSIOSTREAM, _8K, &hVfsFileSignature);
|
---|
| 2652 | if (RT_SUCCESS(vrc))
|
---|
[84151] | 2653 | {
|
---|
[98298] | 2654 | vrc = (int)RTVfsFilePrintf(hVfsFileSignature, "%s(%s) = %#.*Rhxs\n\n",
|
---|
| 2655 | pszDigestType, pszManifestName, cbSignature, pvSignature);
|
---|
| 2656 | if (RT_SUCCESS(vrc))
|
---|
[84151] | 2657 | {
|
---|
[98298] | 2658 | vrc = (int)RTCrX509Certificate_WriteToVfsFile(hVfsFileSignature, pCertificate,
|
---|
| 2659 | RTErrInfoInitStatic(pErrInfo));
|
---|
| 2660 | if (RT_SUCCESS(vrc))
|
---|
[84164] | 2661 | {
|
---|
| 2662 | if (fPkcs7)
|
---|
[98298] | 2663 | vrc = doAddPkcs7Signature(pCertificate, hPrivateKey, enmPkcs7DigestType,
|
---|
| 2664 | cIntermediateCerts, papszIntermediateCerts, hVfsFileManifest,
|
---|
| 2665 | iVerbosity, pErrInfo, hVfsFileSignature);
|
---|
| 2666 | if (RT_SUCCESS(vrc))
|
---|
[84164] | 2667 | {
|
---|
| 2668 | /*
|
---|
| 2669 | * Success.
|
---|
| 2670 | */
|
---|
| 2671 | *phVfsFileSignature = hVfsFileSignature;
|
---|
| 2672 | hVfsFileSignature = NIL_RTVFSFILE;
|
---|
| 2673 | }
|
---|
| 2674 | }
|
---|
| 2675 | else
|
---|
[92372] | 2676 | RTMsgError(Appliance::tr("Failed to write certificate to signature file: %Rrc%#RTeim"),
|
---|
[98298] | 2677 | vrc, &pErrInfo->Core);
|
---|
[84151] | 2678 | }
|
---|
| 2679 | else
|
---|
[98298] | 2680 | RTMsgError(Appliance::tr("Failed to produce signature file: %Rrc"), vrc);
|
---|
[84164] | 2681 | RTVfsFileRelease(hVfsFileSignature);
|
---|
[84151] | 2682 | }
|
---|
| 2683 | else
|
---|
[98298] | 2684 | RTMsgError(Appliance::tr("RTVfsMemFileCreate failed: %Rrc"), vrc);
|
---|
[84151] | 2685 | }
|
---|
| 2686 | else
|
---|
[93703] | 2687 | RTMsgError(Appliance::tr("Encountered a problem when validating the signature we just created: %Rrc%#RTeim\n"
|
---|
| 2688 | "Please make sure the certificate and private key matches."),
|
---|
[98298] | 2689 | vrc, &pErrInfo->Core);
|
---|
[84151] | 2690 | }
|
---|
| 2691 | else
|
---|
[98298] | 2692 | RTMsgError(Appliance::tr("2nd RTCrPkixPubKeySignDigest call failed: %Rrc%#RTeim"), vrc, pErrInfo->Core);
|
---|
[84151] | 2693 | RTMemFree(pvSignature);
|
---|
| 2694 | }
|
---|
| 2695 | else
|
---|
[98298] | 2696 | vrc = RTMsgErrorRc(VERR_NO_MEMORY, Appliance::tr("Out of memory!"));
|
---|
[84151] | 2697 | }
|
---|
| 2698 | else
|
---|
[98298] | 2699 | RTMsgError(Appliance::tr("RTCrPkixPubKeySignDigest failed: %Rrc%#RTeim"), vrc, pErrInfo->Core);
|
---|
[84151] | 2700 | }
|
---|
| 2701 | else
|
---|
[98298] | 2702 | RTMsgError(Appliance::tr("Failed to create digest %s: %Rrc"), RTCrDigestTypeToName(enmDigestType), vrc);
|
---|
[84151] | 2703 | RTCrDigestRelease(hDigest);
|
---|
[98298] | 2704 | return vrc;
|
---|
[84151] | 2705 | }
|
---|
| 2706 |
|
---|
| 2707 |
|
---|
| 2708 | /**
|
---|
| 2709 | * Handles the 'ovasign' command.
|
---|
| 2710 | */
|
---|
[84032] | 2711 | RTEXITCODE handleSignAppliance(HandlerArg *arg)
|
---|
| 2712 | {
|
---|
[84145] | 2713 | /*
|
---|
| 2714 | * Parse arguments.
|
---|
| 2715 | */
|
---|
[84032] | 2716 | static const RTGETOPTDEF s_aOptions[] =
|
---|
| 2717 | {
|
---|
[84145] | 2718 | { "--certificate", 'c', RTGETOPT_REQ_STRING },
|
---|
| 2719 | { "--private-key", 'k', RTGETOPT_REQ_STRING },
|
---|
[84032] | 2720 | { "--private-key-password", 'p', RTGETOPT_REQ_STRING },
|
---|
[84145] | 2721 | { "--private-key-password-file",'P', RTGETOPT_REQ_STRING },
|
---|
[84212] | 2722 | { "--digest-type", 'd', RTGETOPT_REQ_STRING },
|
---|
[84145] | 2723 | { "--pkcs7", '7', RTGETOPT_REQ_NOTHING },
|
---|
[84264] | 2724 | { "--cms", '7', RTGETOPT_REQ_NOTHING },
|
---|
[84145] | 2725 | { "--no-pkcs7", 'n', RTGETOPT_REQ_NOTHING },
|
---|
[84264] | 2726 | { "--no-cms", 'n', RTGETOPT_REQ_NOTHING },
|
---|
[84032] | 2727 | { "--intermediate-cert-file", 'i', RTGETOPT_REQ_STRING },
|
---|
[84145] | 2728 | { "--force", 'f', RTGETOPT_REQ_NOTHING },
|
---|
[84151] | 2729 | { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
|
---|
| 2730 | { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
|
---|
[84145] | 2731 | { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
|
---|
[84032] | 2732 | };
|
---|
| 2733 |
|
---|
| 2734 | RTGETOPTSTATE GetState;
|
---|
[98298] | 2735 | int vrc = RTGetOptInit(&GetState, arg->argc, arg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
|
---|
| 2736 | AssertRCReturn(vrc, RTEXITCODE_FAILURE);
|
---|
[84032] | 2737 |
|
---|
[84212] | 2738 | const char *pszOva = NULL;
|
---|
| 2739 | const char *pszCertificate = NULL;
|
---|
| 2740 | const char *pszPrivateKey = NULL;
|
---|
| 2741 | Utf8Str strPrivateKeyPassword;
|
---|
[84250] | 2742 | RTDIGESTTYPE enmDigestType = RTDIGESTTYPE_UNKNOWN;
|
---|
[84264] | 2743 | bool fPkcs7 = true;
|
---|
[84212] | 2744 | unsigned cIntermediateCerts = 0;
|
---|
| 2745 | const char *apszIntermediateCerts[32];
|
---|
| 2746 | bool fReSign = false;
|
---|
| 2747 | unsigned iVerbosity = 1;
|
---|
| 2748 | bool fDryRun = false;
|
---|
[84032] | 2749 |
|
---|
| 2750 | int c;
|
---|
[84145] | 2751 | RTGETOPTUNION ValueUnion;
|
---|
[84032] | 2752 | while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
|
---|
| 2753 | {
|
---|
| 2754 | switch (c)
|
---|
| 2755 | {
|
---|
[84145] | 2756 | case 'c':
|
---|
| 2757 | pszCertificate = ValueUnion.psz;
|
---|
| 2758 | break;
|
---|
| 2759 |
|
---|
[84032] | 2760 | case 'k':
|
---|
[84145] | 2761 | pszPrivateKey = ValueUnion.psz;
|
---|
[84032] | 2762 | break;
|
---|
| 2763 |
|
---|
| 2764 | case 'p':
|
---|
[84145] | 2765 | if (strPrivateKeyPassword.isNotEmpty())
|
---|
[92372] | 2766 | RTMsgWarning(Appliance::tr("Password is given more than once."));
|
---|
[84145] | 2767 | strPrivateKeyPassword = ValueUnion.psz;
|
---|
[84032] | 2768 | break;
|
---|
| 2769 |
|
---|
[84145] | 2770 | case 'P':
|
---|
| 2771 | {
|
---|
| 2772 | if (strPrivateKeyPassword.isNotEmpty())
|
---|
[92372] | 2773 | RTMsgWarning(Appliance::tr("Password is given more than once."));
|
---|
[84145] | 2774 | RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPrivateKeyPassword);
|
---|
| 2775 | if (rcExit == RTEXITCODE_SUCCESS)
|
---|
| 2776 | break;
|
---|
| 2777 | return rcExit;
|
---|
| 2778 | }
|
---|
| 2779 |
|
---|
[84212] | 2780 | case 'd':
|
---|
| 2781 | if ( RTStrICmp(ValueUnion.psz, "sha1") == 0
|
---|
| 2782 | || RTStrICmp(ValueUnion.psz, "sha-1") == 0)
|
---|
| 2783 | enmDigestType = RTDIGESTTYPE_SHA1;
|
---|
| 2784 | else if ( RTStrICmp(ValueUnion.psz, "sha256") == 0
|
---|
| 2785 | || RTStrICmp(ValueUnion.psz, "sha-256") == 0)
|
---|
| 2786 | enmDigestType = RTDIGESTTYPE_SHA256;
|
---|
| 2787 | else if ( RTStrICmp(ValueUnion.psz, "sha512") == 0
|
---|
| 2788 | || RTStrICmp(ValueUnion.psz, "sha-512") == 0)
|
---|
| 2789 | enmDigestType = RTDIGESTTYPE_SHA512;
|
---|
| 2790 | else
|
---|
[92372] | 2791 | return RTMsgErrorExitFailure(Appliance::tr("Unknown digest type: %s"), ValueUnion.psz);
|
---|
[84212] | 2792 | break;
|
---|
| 2793 |
|
---|
[84145] | 2794 | case '7':
|
---|
| 2795 | fPkcs7 = true;
|
---|
[84032] | 2796 | break;
|
---|
| 2797 |
|
---|
[84145] | 2798 | case 'n':
|
---|
| 2799 | fPkcs7 = false;
|
---|
[84032] | 2800 | break;
|
---|
| 2801 |
|
---|
| 2802 | case 'i':
|
---|
[84145] | 2803 | if (cIntermediateCerts >= RT_ELEMENTS(apszIntermediateCerts))
|
---|
[92372] | 2804 | return RTMsgErrorExitFailure(Appliance::tr("Too many intermediate certificates: max %zu"),
|
---|
[84145] | 2805 | RT_ELEMENTS(apszIntermediateCerts));
|
---|
| 2806 | apszIntermediateCerts[cIntermediateCerts++] = ValueUnion.psz;
|
---|
[84264] | 2807 | fPkcs7 = true;
|
---|
[84032] | 2808 | break;
|
---|
| 2809 |
|
---|
[84145] | 2810 | case 'f':
|
---|
| 2811 | fReSign = true;
|
---|
[84032] | 2812 | break;
|
---|
| 2813 |
|
---|
[84151] | 2814 | case 'v':
|
---|
| 2815 | iVerbosity++;
|
---|
| 2816 | break;
|
---|
[84032] | 2817 |
|
---|
[84151] | 2818 | case 'q':
|
---|
| 2819 | iVerbosity = 0;
|
---|
[84032] | 2820 | break;
|
---|
| 2821 |
|
---|
[84145] | 2822 | case 'D':
|
---|
| 2823 | fDryRun = true;
|
---|
[84032] | 2824 | break;
|
---|
| 2825 |
|
---|
| 2826 | case VINF_GETOPT_NOT_OPTION:
|
---|
[84145] | 2827 | if (!pszOva)
|
---|
| 2828 | {
|
---|
| 2829 | pszOva = ValueUnion.psz;
|
---|
| 2830 | break;
|
---|
| 2831 | }
|
---|
| 2832 | RT_FALL_THRU();
|
---|
[84032] | 2833 | default:
|
---|
| 2834 | return errorGetOpt(c, &ValueUnion);
|
---|
| 2835 | }
|
---|
| 2836 | }
|
---|
| 2837 |
|
---|
[84145] | 2838 | /* Required paramaters: */
|
---|
| 2839 | if (!pszOva || !*pszOva)
|
---|
[92372] | 2840 | return RTMsgErrorExit(RTEXITCODE_SYNTAX, Appliance::tr("No OVA file was specified!"));
|
---|
[84145] | 2841 | if (!pszCertificate || !*pszCertificate)
|
---|
[92372] | 2842 | return RTMsgErrorExit(RTEXITCODE_SYNTAX, Appliance::tr("No signing certificate (--certificate=<file>) was specified!"));
|
---|
[84145] | 2843 | if (!pszPrivateKey || !*pszPrivateKey)
|
---|
[92372] | 2844 | return RTMsgErrorExit(RTEXITCODE_SYNTAX, Appliance::tr("No signing private key (--private-key=<file>) was specified!"));
|
---|
[84145] | 2845 |
|
---|
| 2846 | /* Check that input files exists before we commence: */
|
---|
| 2847 | if (!RTFileExists(pszOva))
|
---|
[92372] | 2848 | return RTMsgErrorExitFailure(Appliance::tr("The specified OVA file was not found: %s"), pszOva);
|
---|
[84145] | 2849 | if (!RTFileExists(pszCertificate))
|
---|
[92372] | 2850 | return RTMsgErrorExitFailure(Appliance::tr("The specified certificate file was not found: %s"), pszCertificate);
|
---|
[84145] | 2851 | if (!RTFileExists(pszPrivateKey))
|
---|
[92372] | 2852 | return RTMsgErrorExitFailure(Appliance::tr("The specified private key file was not found: %s"), pszPrivateKey);
|
---|
[84145] | 2853 |
|
---|
| 2854 | /*
|
---|
[84208] | 2855 | * Open the OVA, read the manifest and look for any existing signature.
|
---|
[84145] | 2856 | */
|
---|
[84208] | 2857 | RTVFSFSSTREAM hVfsFssOva = NIL_RTVFSFSSTREAM;
|
---|
| 2858 | RTVFSOBJ hVfsOldSignature = NIL_RTVFSOBJ;
|
---|
| 2859 | RTVFSFILE hVfsFileManifest = NIL_RTVFSFILE;
|
---|
| 2860 | Utf8Str strManifestName;
|
---|
[98298] | 2861 | vrc = openOvaAndGetManifestAndOldSignature(pszOva, iVerbosity, fReSign,
|
---|
| 2862 | &hVfsFssOva, &strManifestName, &hVfsFileManifest, &hVfsOldSignature);
|
---|
| 2863 | if (RT_SUCCESS(vrc))
|
---|
[84032] | 2864 | {
|
---|
| 2865 | /*
|
---|
[84208] | 2866 | * Read the certificate and private key.
|
---|
[84032] | 2867 | */
|
---|
[84208] | 2868 | RTERRINFOSTATIC ErrInfo;
|
---|
| 2869 | RTCRX509CERTIFICATE Certificate;
|
---|
[98298] | 2870 | vrc = RTCrX509Certificate_ReadFromFile(&Certificate, pszCertificate, 0, &g_RTAsn1DefaultAllocator,
|
---|
| 2871 | RTErrInfoInitStatic(&ErrInfo));
|
---|
| 2872 | if (RT_FAILURE(vrc))
|
---|
[92372] | 2873 | return RTMsgErrorExitFailure(Appliance::tr("Error reading certificate from '%s': %Rrc%#RTeim"),
|
---|
[98298] | 2874 | pszCertificate, vrc, &ErrInfo.Core);
|
---|
[84208] | 2875 |
|
---|
| 2876 | RTCRKEY hPrivateKey = NIL_RTCRKEY;
|
---|
[98298] | 2877 | vrc = RTCrKeyCreateFromFile(&hPrivateKey, 0 /*fFlags*/, pszPrivateKey, strPrivateKeyPassword.c_str(),
|
---|
| 2878 | RTErrInfoInitStatic(&ErrInfo));
|
---|
| 2879 | if (RT_SUCCESS(vrc))
|
---|
[84032] | 2880 | {
|
---|
[84208] | 2881 | if (iVerbosity > 1)
|
---|
[92372] | 2882 | RTMsgInfo(Appliance::tr("Successfully read the certificate and private key."));
|
---|
[84208] | 2883 |
|
---|
| 2884 | /*
|
---|
| 2885 | * Do the signing and create the signature file.
|
---|
| 2886 | */
|
---|
| 2887 | RTVFSFILE hVfsFileSignature = NIL_RTVFSFILE;
|
---|
[98298] | 2888 | vrc = doTheOvaSigning(&Certificate, hPrivateKey, enmDigestType, strManifestName.c_str(), hVfsFileManifest,
|
---|
| 2889 | fPkcs7, cIntermediateCerts, apszIntermediateCerts, iVerbosity, &ErrInfo, &hVfsFileSignature);
|
---|
[84208] | 2890 |
|
---|
| 2891 | /*
|
---|
| 2892 | * Construct the signature filename:
|
---|
| 2893 | */
|
---|
[98298] | 2894 | if (RT_SUCCESS(vrc))
|
---|
[84032] | 2895 | {
|
---|
[84208] | 2896 | Utf8Str strSignatureName;
|
---|
[98298] | 2897 | vrc = strSignatureName.assignNoThrow(strManifestName);
|
---|
| 2898 | if (RT_SUCCESS(vrc))
|
---|
| 2899 | vrc = strSignatureName.stripSuffix().appendNoThrow(".cert");
|
---|
| 2900 | if (RT_SUCCESS(vrc) && !fDryRun)
|
---|
[84151] | 2901 | {
|
---|
[84032] | 2902 | /*
|
---|
[84151] | 2903 | * Update the OVA.
|
---|
[84032] | 2904 | */
|
---|
[98298] | 2905 | vrc = updateTheOvaSignature(hVfsFssOva, pszOva, strSignatureName.c_str(),
|
---|
| 2906 | hVfsFileSignature, hVfsOldSignature, iVerbosity);
|
---|
| 2907 | if (RT_SUCCESS(vrc) && iVerbosity > 0)
|
---|
[92372] | 2908 | RTMsgInfo(Appliance::tr("Successfully signed '%s'."), pszOva);
|
---|
[84032] | 2909 | }
|
---|
| 2910 | }
|
---|
[84208] | 2911 | RTCrKeyRelease(hPrivateKey);
|
---|
[84032] | 2912 | }
|
---|
[84208] | 2913 | else
|
---|
[98298] | 2914 | RTPrintf(Appliance::tr("Error reading the private key from %s: %Rrc%#RTeim"), pszPrivateKey, vrc, &ErrInfo.Core);
|
---|
[84208] | 2915 | RTCrX509Certificate_Delete(&Certificate);
|
---|
[84032] | 2916 | }
|
---|
| 2917 |
|
---|
[84208] | 2918 | RTVfsObjRelease(hVfsOldSignature);
|
---|
| 2919 | RTVfsFileRelease(hVfsFileManifest);
|
---|
| 2920 | RTVfsFsStrmRelease(hVfsFssOva);
|
---|
| 2921 |
|
---|
[98298] | 2922 | return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
|
---|
[84032] | 2923 | }
|
---|