| 1 | /* $Id: VBoxManage.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
|
|---|
| 2 | /** @file
|
|---|
| 3 | * VBoxManage - VirtualBox's command-line interface.
|
|---|
| 4 | */
|
|---|
| 5 |
|
|---|
| 6 | /*
|
|---|
| 7 | * Copyright (C) 2006-2024 Oracle and/or its affiliates.
|
|---|
| 8 | *
|
|---|
| 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
|
|---|
| 26 | */
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 | /*********************************************************************************************************************************
|
|---|
| 30 | * Header Files *
|
|---|
| 31 | *********************************************************************************************************************************/
|
|---|
| 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>
|
|---|
| 37 | #include <VBox/com/errorprint.h>
|
|---|
| 38 | #include <VBox/com/NativeEventQueue.h>
|
|---|
| 39 |
|
|---|
| 40 | #include <VBox/com/VirtualBox.h>
|
|---|
| 41 |
|
|---|
| 42 | #ifdef VBOX_WITH_VBOXMANAGE_NLS
|
|---|
| 43 | # include <VBox/com/AutoLock.h>
|
|---|
| 44 | # include <VBox/com/listeners.h>
|
|---|
| 45 | #endif
|
|---|
| 46 |
|
|---|
| 47 | #include <VBox/version.h>
|
|---|
| 48 |
|
|---|
| 49 | #include <iprt/asm.h>
|
|---|
| 50 | #include <iprt/buildconfig.h>
|
|---|
| 51 | #include <iprt/ctype.h>
|
|---|
| 52 | #include <iprt/file.h>
|
|---|
| 53 | #include <iprt/getopt.h>
|
|---|
| 54 | #include <iprt/initterm.h>
|
|---|
| 55 | #include <iprt/log.h>
|
|---|
| 56 | #include <iprt/path.h>
|
|---|
| 57 | #include <iprt/stream.h>
|
|---|
| 58 | #include <iprt/string.h>
|
|---|
| 59 |
|
|---|
| 60 | #include <signal.h>
|
|---|
| 61 |
|
|---|
| 62 | #include "VBoxManage.h"
|
|---|
| 63 |
|
|---|
| 64 |
|
|---|
| 65 | /*********************************************************************************************************************************
|
|---|
| 66 | * Defined Constants And Macros *
|
|---|
| 67 | *********************************************************************************************************************************/
|
|---|
| 68 |
|
|---|
| 69 | /** The command doesn't need the COM stuff. */
|
|---|
| 70 | #define VBMG_CMD_F_NO_COM RT_BIT_32(0)
|
|---|
| 71 |
|
|---|
| 72 | #define VBMG_CMD_INTERNAL HELP_CMD_VBOXMANAGE_INVALID
|
|---|
| 73 |
|
|---|
| 74 |
|
|---|
| 75 | /*********************************************************************************************************************************
|
|---|
| 76 | * Structures and Typedefs *
|
|---|
| 77 | *********************************************************************************************************************************/
|
|---|
| 78 |
|
|---|
| 79 | /**
|
|---|
| 80 | * VBoxManage command descriptor.
|
|---|
| 81 | */
|
|---|
| 82 | typedef struct VBMGCMD
|
|---|
| 83 | {
|
|---|
| 84 | /** The command. */
|
|---|
| 85 | const char *pszCommand;
|
|---|
| 86 | /** The new help command. */
|
|---|
| 87 | enum HELP_CMD_VBOXMANAGE enmCmdHelp;
|
|---|
| 88 | /** The handler. */
|
|---|
| 89 | RTEXITCODE (*pfnHandler)(HandlerArg *pArg);
|
|---|
| 90 | /** VBMG_CMD_F_XXX, */
|
|---|
| 91 | uint32_t fFlags;
|
|---|
| 92 | } VBMGCMD;
|
|---|
| 93 | /** Pointer to a const VBoxManage command descriptor. */
|
|---|
| 94 | typedef VBMGCMD const *PCVBMGCMD;
|
|---|
| 95 |
|
|---|
| 96 |
|
|---|
| 97 | DECLARE_TRANSLATION_CONTEXT(VBoxManage);
|
|---|
| 98 |
|
|---|
| 99 | void setBuiltInHelpLanguage(const char *pszLang);
|
|---|
| 100 |
|
|---|
| 101 | #ifdef VBOX_WITH_VBOXMANAGE_NLS
|
|---|
| 102 | /* listener class for language updates */
|
|---|
| 103 | class VBoxEventListener
|
|---|
| 104 | {
|
|---|
| 105 | public:
|
|---|
| 106 | VBoxEventListener()
|
|---|
| 107 | {}
|
|---|
| 108 |
|
|---|
| 109 |
|
|---|
| 110 | HRESULT init(void *)
|
|---|
| 111 | {
|
|---|
| 112 | return S_OK;
|
|---|
| 113 | }
|
|---|
| 114 |
|
|---|
| 115 | HRESULT init()
|
|---|
| 116 | {
|
|---|
| 117 | return S_OK;
|
|---|
| 118 | }
|
|---|
| 119 |
|
|---|
| 120 | void uninit()
|
|---|
| 121 | {
|
|---|
| 122 | }
|
|---|
| 123 |
|
|---|
| 124 | virtual ~VBoxEventListener()
|
|---|
| 125 | {
|
|---|
| 126 | }
|
|---|
| 127 |
|
|---|
| 128 | STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
|
|---|
| 129 | {
|
|---|
| 130 | switch(aType)
|
|---|
| 131 | {
|
|---|
| 132 | case VBoxEventType_OnLanguageChanged:
|
|---|
| 133 | {
|
|---|
| 134 | /*
|
|---|
| 135 | * Proceed with uttmost care as we might be racing com::Shutdown()
|
|---|
| 136 | * and have the ground open up beneath us.
|
|---|
| 137 | */
|
|---|
| 138 | LogFunc(("VBoxEventType_OnLanguageChanged\n"));
|
|---|
| 139 | VirtualBoxTranslator *pTranslator = VirtualBoxTranslator::tryInstance();
|
|---|
| 140 | if (pTranslator)
|
|---|
| 141 | {
|
|---|
| 142 | ComPtr<ILanguageChangedEvent> pEvent = aEvent;
|
|---|
| 143 | Assert(pEvent);
|
|---|
| 144 |
|
|---|
| 145 | /* This call may fail if we're racing COM shutdown. */
|
|---|
| 146 | com::Bstr bstrLanguageId;
|
|---|
| 147 | HRESULT hrc = pEvent->COMGETTER(LanguageId)(bstrLanguageId.asOutParam());
|
|---|
| 148 | if (SUCCEEDED(hrc))
|
|---|
| 149 | {
|
|---|
| 150 | try
|
|---|
| 151 | {
|
|---|
| 152 | com::Utf8Str strLanguageId(bstrLanguageId);
|
|---|
| 153 | LogFunc(("New language ID: %s\n", strLanguageId.c_str()));
|
|---|
| 154 | pTranslator->i_loadLanguage(strLanguageId.c_str());
|
|---|
| 155 | setBuiltInHelpLanguage(strLanguageId.c_str());
|
|---|
| 156 | }
|
|---|
| 157 | catch (std::bad_alloc &)
|
|---|
| 158 | {
|
|---|
| 159 | LogFunc(("Caught bad_alloc"));
|
|---|
| 160 | }
|
|---|
| 161 | }
|
|---|
| 162 | else
|
|---|
| 163 | LogFunc(("Failed to get new language ID: %Rhrc\n", hrc));
|
|---|
| 164 |
|
|---|
| 165 | pTranslator->release();
|
|---|
| 166 | }
|
|---|
| 167 | break;
|
|---|
| 168 | }
|
|---|
| 169 |
|
|---|
| 170 | default:
|
|---|
| 171 | AssertFailed();
|
|---|
| 172 | }
|
|---|
| 173 |
|
|---|
| 174 | return S_OK;
|
|---|
| 175 | }
|
|---|
| 176 | };
|
|---|
| 177 |
|
|---|
| 178 | typedef ListenerImpl<VBoxEventListener> VBoxEventListenerImpl;
|
|---|
| 179 |
|
|---|
| 180 | VBOX_LISTENER_DECLARE(VBoxEventListenerImpl)
|
|---|
| 181 | #endif /* !VBOX_WITH_VBOXMANAGE_NLS */
|
|---|
| 182 |
|
|---|
| 183 |
|
|---|
| 184 | /*********************************************************************************************************************************
|
|---|
| 185 | * Global Variables *
|
|---|
| 186 | *********************************************************************************************************************************/
|
|---|
| 187 | /*extern*/ bool g_fDetailedProgress = false;
|
|---|
| 188 | /** Set by the signal handler. */
|
|---|
| 189 | static volatile bool g_fCanceled = false;
|
|---|
| 190 |
|
|---|
| 191 |
|
|---|
| 192 | /**
|
|---|
| 193 | * All registered command handlers
|
|---|
| 194 | */
|
|---|
| 195 | static const VBMGCMD g_aCommands[] =
|
|---|
| 196 | {
|
|---|
| 197 | { "internalcommands", VBMG_CMD_INTERNAL, handleInternalCommands, 0 },
|
|---|
| 198 | { "list", HELP_CMD_LIST, handleList, 0 },
|
|---|
| 199 | { "showvminfo", HELP_CMD_SHOWVMINFO, handleShowVMInfo, 0 },
|
|---|
| 200 | { "registervm", HELP_CMD_REGISTERVM, handleRegisterVM, 0 },
|
|---|
| 201 | { "unregistervm", HELP_CMD_UNREGISTERVM, handleUnregisterVM, 0 },
|
|---|
| 202 | { "clonevm", HELP_CMD_CLONEVM, handleCloneVM, 0 },
|
|---|
| 203 | { "movevm", HELP_CMD_MOVEVM, handleMoveVM, 0 },
|
|---|
| 204 | #ifdef VBOX_WITH_FULL_VM_ENCRYPTION
|
|---|
| 205 | { "encryptvm", HELP_CMD_ENCRYPTVM, handleEncryptVM, 0 },
|
|---|
| 206 | #endif
|
|---|
| 207 | { "mediumproperty", HELP_CMD_MEDIUMPROPERTY, handleMediumProperty, 0 },
|
|---|
| 208 | { "hdproperty", HELP_CMD_MEDIUMPROPERTY, handleMediumProperty, 0 }, /* backward compatibility */
|
|---|
| 209 | { "createmedium", HELP_CMD_CREATEMEDIUM, handleCreateMedium, 0 },
|
|---|
| 210 | { "createhd", HELP_CMD_CREATEMEDIUM, handleCreateMedium, 0 }, /* backward compatibility */
|
|---|
| 211 | { "createvdi", HELP_CMD_CREATEMEDIUM, handleCreateMedium, 0 }, /* backward compatibility */
|
|---|
| 212 | { "modifymedium", HELP_CMD_MODIFYMEDIUM, handleModifyMedium, 0 },
|
|---|
| 213 | { "modifyhd", HELP_CMD_MODIFYMEDIUM, handleModifyMedium, 0 }, /* backward compatibility */
|
|---|
| 214 | { "modifyvdi", HELP_CMD_MODIFYMEDIUM, handleModifyMedium, 0 }, /* backward compatibility */
|
|---|
| 215 | { "clonemedium", HELP_CMD_CLONEMEDIUM, handleCloneMedium, 0 },
|
|---|
| 216 | { "clonehd", HELP_CMD_CLONEMEDIUM, handleCloneMedium, 0 }, /* backward compatibility */
|
|---|
| 217 | { "clonevdi", HELP_CMD_CLONEMEDIUM, handleCloneMedium, 0 }, /* backward compatibility */
|
|---|
| 218 | { "encryptmedium", HELP_CMD_ENCRYPTMEDIUM, handleEncryptMedium, 0 },
|
|---|
| 219 | { "checkmediumpwd", HELP_CMD_CHECKMEDIUMPWD, handleCheckMediumPassword, 0 },
|
|---|
| 220 | { "createvm", HELP_CMD_CREATEVM, handleCreateVM, 0 },
|
|---|
| 221 | { "modifyvm", HELP_CMD_MODIFYVM, handleModifyVM, 0 },
|
|---|
| 222 | { "startvm", HELP_CMD_STARTVM, handleStartVM, 0 },
|
|---|
| 223 | { "controlvm", HELP_CMD_CONTROLVM, handleControlVM, 0 },
|
|---|
| 224 | { "unattended", HELP_CMD_UNATTENDED, handleUnattended, 0 },
|
|---|
| 225 | { "discardstate", HELP_CMD_DISCARDSTATE, handleDiscardState, 0 },
|
|---|
| 226 | { "adoptstate", HELP_CMD_ADOPTSTATE, handleAdoptState, 0 },
|
|---|
| 227 | { "snapshot", HELP_CMD_SNAPSHOT, handleSnapshot, 0 },
|
|---|
| 228 | { "closemedium", HELP_CMD_CLOSEMEDIUM, handleCloseMedium, 0 },
|
|---|
| 229 | { "storageattach", HELP_CMD_STORAGEATTACH, handleStorageAttach, 0 },
|
|---|
| 230 | { "storagectl", HELP_CMD_STORAGECTL, handleStorageController, 0 },
|
|---|
| 231 | { "showmediuminfo", HELP_CMD_SHOWMEDIUMINFO, handleShowMediumInfo, 0 },
|
|---|
| 232 | { "showhdinfo", HELP_CMD_SHOWMEDIUMINFO, handleShowMediumInfo, 0 }, /* backward compatibility */
|
|---|
| 233 | { "showvdiinfo", HELP_CMD_SHOWMEDIUMINFO, handleShowMediumInfo, 0 }, /* backward compatibility */
|
|---|
| 234 | { "mediumio", HELP_CMD_MEDIUMIO, handleMediumIO, 0 },
|
|---|
| 235 | { "getextradata", HELP_CMD_GETEXTRADATA, handleGetExtraData, 0 },
|
|---|
| 236 | { "setextradata", HELP_CMD_SETEXTRADATA, handleSetExtraData, 0 },
|
|---|
| 237 | { "setproperty", HELP_CMD_SETPROPERTY, handleSetProperty, 0 },
|
|---|
| 238 | { "usbfilter", HELP_CMD_USBFILTER, handleUSBFilter, 0 },
|
|---|
| 239 | { "sharedfolder", HELP_CMD_SHAREDFOLDER, handleSharedFolder, 0 },
|
|---|
| 240 | #ifdef VBOX_WITH_GUEST_PROPS
|
|---|
| 241 | { "guestproperty", HELP_CMD_GUESTPROPERTY, handleGuestProperty, 0 },
|
|---|
| 242 | #endif
|
|---|
| 243 | #ifdef VBOX_WITH_GUEST_CONTROL
|
|---|
| 244 | { "guestcontrol", HELP_CMD_GUESTCONTROL, handleGuestControl, 0 },
|
|---|
| 245 | #endif
|
|---|
| 246 | { "metrics", HELP_CMD_METRICS, handleMetrics, 0 },
|
|---|
| 247 | { "import", HELP_CMD_IMPORT, handleImportAppliance, 0 },
|
|---|
| 248 | { "export", HELP_CMD_EXPORT, handleExportAppliance, 0 },
|
|---|
| 249 | { "signova", HELP_CMD_SIGNOVA, handleSignAppliance, VBMG_CMD_F_NO_COM },
|
|---|
| 250 | #ifdef VBOX_WITH_NETFLT
|
|---|
| 251 | { "hostonlyif", HELP_CMD_HOSTONLYIF, handleHostonlyIf, 0 },
|
|---|
| 252 | #endif
|
|---|
| 253 | #ifdef VBOX_WITH_VMNET
|
|---|
| 254 | { "hostonlynet", HELP_CMD_HOSTONLYNET, handleHostonlyNet, 0 },
|
|---|
| 255 | #endif
|
|---|
| 256 | { "dhcpserver", HELP_CMD_DHCPSERVER, handleDHCPServer, 0 },
|
|---|
| 257 | #ifdef VBOX_WITH_NAT_SERVICE
|
|---|
| 258 | { "natnetwork", HELP_CMD_NATNETWORK, handleNATNetwork, 0 },
|
|---|
| 259 | #endif
|
|---|
| 260 | { "extpack", HELP_CMD_EXTPACK, handleExtPack, 0 },
|
|---|
| 261 | { "bandwidthctl", HELP_CMD_BANDWIDTHCTL, handleBandwidthControl, 0 },
|
|---|
| 262 | { "debugvm", HELP_CMD_DEBUGVM, handleDebugVM, 0 },
|
|---|
| 263 | { "convertfromraw", HELP_CMD_CONVERTFROMRAW, handleConvertFromRaw, VBMG_CMD_F_NO_COM },
|
|---|
| 264 | { "convertdd", HELP_CMD_CONVERTFROMRAW, handleConvertFromRaw, VBMG_CMD_F_NO_COM },
|
|---|
| 265 | { "usbdevsource", HELP_CMD_USBDEVSOURCE, handleUSBDevSource, 0 },
|
|---|
| 266 | { "cloudprofile", HELP_CMD_CLOUDPROFILE, handleCloudProfile, 0 },
|
|---|
| 267 | { "cloud", HELP_CMD_CLOUD, handleCloud, 0 },
|
|---|
| 268 | #ifdef VBOX_WITH_UPDATE_AGENT
|
|---|
| 269 | { "updatecheck", HELP_CMD_UPDATECHECK, handleUpdateCheck, 0 },
|
|---|
| 270 | #endif
|
|---|
| 271 | { "modifynvram", HELP_CMD_MODIFYNVRAM, handleModifyNvram, 0 },
|
|---|
| 272 | };
|
|---|
| 273 |
|
|---|
| 274 | /**
|
|---|
| 275 | * Looks up a command by name.
|
|---|
| 276 | *
|
|---|
| 277 | * @returns Pointer to the command structure.
|
|---|
| 278 | * @param pszCommand Name of the command.
|
|---|
| 279 | */
|
|---|
| 280 | static PCVBMGCMD lookupCommand(const char *pszCommand)
|
|---|
| 281 | {
|
|---|
| 282 | if (pszCommand)
|
|---|
| 283 | for (uint32_t i = 0; i < RT_ELEMENTS(g_aCommands); i++)
|
|---|
| 284 | if (!strcmp(g_aCommands[i].pszCommand, pszCommand))
|
|---|
| 285 | return &g_aCommands[i];
|
|---|
| 286 | return NULL;
|
|---|
| 287 | }
|
|---|
| 288 |
|
|---|
| 289 |
|
|---|
| 290 | /**
|
|---|
| 291 | * Signal handler that sets g_fCanceled.
|
|---|
| 292 | *
|
|---|
| 293 | * This can be executed on any thread in the process, on Windows it may even be
|
|---|
| 294 | * a thread dedicated to delivering this signal. Do not doing anything
|
|---|
| 295 | * unnecessary here.
|
|---|
| 296 | */
|
|---|
| 297 | static void showProgressSignalHandler(int iSignal) RT_NOTHROW_DEF
|
|---|
| 298 | {
|
|---|
| 299 | NOREF(iSignal);
|
|---|
| 300 | ASMAtomicWriteBool(&g_fCanceled, true);
|
|---|
| 301 | }
|
|---|
| 302 |
|
|---|
| 303 | /**
|
|---|
| 304 | * Print out progress on the console.
|
|---|
| 305 | *
|
|---|
| 306 | * This runs the main event queue every now and then to prevent piling up
|
|---|
| 307 | * unhandled things (which doesn't cause real problems, just makes things
|
|---|
| 308 | * react a little slower than in the ideal case).
|
|---|
| 309 | */
|
|---|
| 310 | HRESULT showProgress(ComPtr<IProgress> progress, uint32_t fFlags)
|
|---|
| 311 | {
|
|---|
| 312 | using namespace com;
|
|---|
| 313 | HRESULT hrc;
|
|---|
| 314 |
|
|---|
| 315 | AssertReturn(progress.isNotNull(), E_FAIL);
|
|---|
| 316 |
|
|---|
| 317 | /* grandfather the old callers */
|
|---|
| 318 | if (g_fDetailedProgress)
|
|---|
| 319 | fFlags = SHOW_PROGRESS_DETAILS;
|
|---|
| 320 |
|
|---|
| 321 | const bool fDetailed = RT_BOOL(fFlags & SHOW_PROGRESS_DETAILS);
|
|---|
| 322 | const bool fOps = RT_BOOL(fFlags & SHOW_PROGRESS_OPS);
|
|---|
| 323 | const bool fQuiet = !RT_BOOL(fFlags & (SHOW_PROGRESS | SHOW_PROGRESS_DETAILS | SHOW_PROGRESS_OPS));
|
|---|
| 324 |
|
|---|
| 325 | AssertReturn((!fDetailed && !fOps) || (fDetailed != fOps), E_INVALIDARG); /* Mutually exclusive. */
|
|---|
| 326 |
|
|---|
| 327 | BOOL fCompleted = FALSE;
|
|---|
| 328 | ULONG ulCurrentPercent = 0;
|
|---|
| 329 | ULONG ulLastPercent = 0;
|
|---|
| 330 |
|
|---|
| 331 | ULONG ulLastOperationPercent = (ULONG)-1;
|
|---|
| 332 |
|
|---|
| 333 | ULONG ulLastOperation = (ULONG)-1;
|
|---|
| 334 | Bstr bstrOperationDescription;
|
|---|
| 335 |
|
|---|
| 336 | NativeEventQueue::getMainEventQueue()->processEventQueue(0);
|
|---|
| 337 |
|
|---|
| 338 | ULONG cOperations = 1;
|
|---|
| 339 | hrc = progress->COMGETTER(OperationCount)(&cOperations);
|
|---|
| 340 | if (FAILED(hrc))
|
|---|
| 341 | {
|
|---|
| 342 | RTStrmPrintf(g_pStdErr, VBoxManage::tr("Progress object failure: %Rhrc\n"), hrc);
|
|---|
| 343 | RTStrmFlush(g_pStdErr);
|
|---|
| 344 | return hrc;
|
|---|
| 345 | }
|
|---|
| 346 |
|
|---|
| 347 | /*
|
|---|
| 348 | * Note: Outputting the progress info to stderr (g_pStdErr) is intentional
|
|---|
| 349 | * to not get intermixed with other (raw) stdout data which might get
|
|---|
| 350 | * written in the meanwhile.
|
|---|
| 351 | */
|
|---|
| 352 |
|
|---|
| 353 | if (fFlags & SHOW_PROGRESS_DESC)
|
|---|
| 354 | {
|
|---|
| 355 | com::Bstr bstrDescription;
|
|---|
| 356 | hrc = progress->COMGETTER(Description(bstrDescription.asOutParam()));
|
|---|
| 357 | if (FAILED(hrc))
|
|---|
| 358 | {
|
|---|
| 359 | RTStrmPrintf(g_pStdErr, VBoxManage::tr("Failed to get progress description: %Rhrc\n"), hrc);
|
|---|
| 360 | return hrc;
|
|---|
| 361 | }
|
|---|
| 362 |
|
|---|
| 363 | const char *pcszDescSep;
|
|---|
| 364 | if (fDetailed) /* multiline output */
|
|---|
| 365 | pcszDescSep = "\n";
|
|---|
| 366 | else /* continues on the same line */
|
|---|
| 367 | pcszDescSep = ": ";
|
|---|
| 368 |
|
|---|
| 369 | RTStrmPrintf(g_pStdErr, "%ls%s", bstrDescription.raw(), pcszDescSep);
|
|---|
| 370 | RTStrmFlush(g_pStdErr);
|
|---|
| 371 | }
|
|---|
| 372 |
|
|---|
| 373 | if (!fQuiet && !fDetailed && !fOps)
|
|---|
| 374 | {
|
|---|
| 375 | RTStrmPrintf(g_pStdErr, "0%%...");
|
|---|
| 376 | RTStrmFlush(g_pStdErr);
|
|---|
| 377 | }
|
|---|
| 378 |
|
|---|
| 379 | /* setup signal handling if cancelable */
|
|---|
| 380 | bool fCanceledAlready = false;
|
|---|
| 381 | BOOL fCancelable;
|
|---|
| 382 | hrc = progress->COMGETTER(Cancelable)(&fCancelable);
|
|---|
| 383 | if (FAILED(hrc))
|
|---|
| 384 | fCancelable = FALSE;
|
|---|
| 385 | if (fCancelable)
|
|---|
| 386 | {
|
|---|
| 387 | signal(SIGINT, showProgressSignalHandler);
|
|---|
| 388 | signal(SIGTERM, showProgressSignalHandler);
|
|---|
| 389 | #ifdef SIGBREAK
|
|---|
| 390 | signal(SIGBREAK, showProgressSignalHandler);
|
|---|
| 391 | #endif
|
|---|
| 392 | }
|
|---|
| 393 |
|
|---|
| 394 | hrc = progress->COMGETTER(Completed(&fCompleted));
|
|---|
| 395 | while (SUCCEEDED(hrc))
|
|---|
| 396 | {
|
|---|
| 397 | progress->COMGETTER(Percent(&ulCurrentPercent));
|
|---|
| 398 |
|
|---|
| 399 | if ( fDetailed
|
|---|
| 400 | || fOps)
|
|---|
| 401 | {
|
|---|
| 402 | ULONG ulOperation = 1;
|
|---|
| 403 | hrc = progress->COMGETTER(Operation)(&ulOperation);
|
|---|
| 404 | if (FAILED(hrc))
|
|---|
| 405 | break;
|
|---|
| 406 | ULONG ulCurrentOperationPercent = 0;
|
|---|
| 407 | hrc = progress->COMGETTER(OperationPercent(&ulCurrentOperationPercent));
|
|---|
| 408 | if (FAILED(hrc))
|
|---|
| 409 | break;
|
|---|
| 410 |
|
|---|
| 411 | if (ulLastOperation != ulOperation)
|
|---|
| 412 | {
|
|---|
| 413 | hrc = progress->COMGETTER(OperationDescription(bstrOperationDescription.asOutParam()));
|
|---|
| 414 | if (FAILED(hrc))
|
|---|
| 415 | break;
|
|---|
| 416 | ulLastPercent = (ULONG)-1; // force print
|
|---|
| 417 | ulLastOperation = ulOperation;
|
|---|
| 418 | }
|
|---|
| 419 |
|
|---|
| 420 | if ( ulCurrentPercent != ulLastPercent
|
|---|
| 421 | || ulCurrentOperationPercent != ulLastOperationPercent
|
|---|
| 422 | )
|
|---|
| 423 | {
|
|---|
| 424 | LONG lSecsRem = 0;
|
|---|
| 425 | progress->COMGETTER(TimeRemaining)(&lSecsRem);
|
|---|
| 426 |
|
|---|
| 427 | if (fDetailed)
|
|---|
| 428 | RTStrmPrintf(g_pStdErr, VBoxManage::tr("(%u/%u) %ls %02u%% => %02u%% (%d s remaining)\n"), ulOperation + 1, cOperations,
|
|---|
| 429 | bstrOperationDescription.raw(), ulCurrentOperationPercent, ulCurrentPercent, lSecsRem);
|
|---|
| 430 | else
|
|---|
| 431 | RTStrmPrintf(g_pStdErr, VBoxManage::tr("%02u%%: %ls\n"), ulCurrentPercent, bstrOperationDescription.raw());
|
|---|
| 432 |
|
|---|
| 433 | ulLastPercent = ulCurrentPercent;
|
|---|
| 434 | ulLastOperationPercent = ulCurrentOperationPercent;
|
|---|
| 435 | }
|
|---|
| 436 | }
|
|---|
| 437 | else if (!fQuiet)
|
|---|
| 438 | {
|
|---|
| 439 | /* did we cross a 10% mark? */
|
|---|
| 440 | if (ulCurrentPercent / 10 > ulLastPercent / 10)
|
|---|
| 441 | {
|
|---|
| 442 | /* make sure to also print out missed steps */
|
|---|
| 443 | for (ULONG curVal = (ulLastPercent / 10) * 10 + 10; curVal <= (ulCurrentPercent / 10) * 10; curVal += 10)
|
|---|
| 444 | {
|
|---|
| 445 | if (curVal < 100)
|
|---|
| 446 | {
|
|---|
| 447 | RTStrmPrintf(g_pStdErr, "%u%%...", curVal);
|
|---|
| 448 | RTStrmFlush(g_pStdErr);
|
|---|
| 449 | }
|
|---|
| 450 | }
|
|---|
| 451 | ulLastPercent = (ulCurrentPercent / 10) * 10;
|
|---|
| 452 | }
|
|---|
| 453 | }
|
|---|
| 454 | if (fCompleted)
|
|---|
| 455 | break;
|
|---|
| 456 |
|
|---|
| 457 | /* process async cancelation */
|
|---|
| 458 | if (g_fCanceled && !fCanceledAlready)
|
|---|
| 459 | {
|
|---|
| 460 | hrc = progress->Cancel();
|
|---|
| 461 | if (SUCCEEDED(hrc))
|
|---|
| 462 | fCanceledAlready = true;
|
|---|
| 463 | else
|
|---|
| 464 | g_fCanceled = false;
|
|---|
| 465 | }
|
|---|
| 466 |
|
|---|
| 467 | /* make sure the loop is not too tight */
|
|---|
| 468 | progress->WaitForCompletion(100);
|
|---|
| 469 |
|
|---|
| 470 | NativeEventQueue::getMainEventQueue()->processEventQueue(0);
|
|---|
| 471 | hrc = progress->COMGETTER(Completed(&fCompleted));
|
|---|
| 472 | }
|
|---|
| 473 |
|
|---|
| 474 | /* undo signal handling */
|
|---|
| 475 | if (fCancelable)
|
|---|
| 476 | {
|
|---|
| 477 | signal(SIGINT, SIG_DFL);
|
|---|
| 478 | signal(SIGTERM, SIG_DFL);
|
|---|
| 479 | # ifdef SIGBREAK
|
|---|
| 480 | signal(SIGBREAK, SIG_DFL);
|
|---|
| 481 | # endif
|
|---|
| 482 | }
|
|---|
| 483 |
|
|---|
| 484 | /* complete the line. */
|
|---|
| 485 | LONG iRc = E_FAIL;
|
|---|
| 486 | hrc = progress->COMGETTER(ResultCode)(&iRc);
|
|---|
| 487 | if (SUCCEEDED(hrc))
|
|---|
| 488 | {
|
|---|
| 489 | /* async operation completed successfully */
|
|---|
| 490 | if (SUCCEEDED(iRc))
|
|---|
| 491 | {
|
|---|
| 492 | if (!fDetailed)
|
|---|
| 493 | {
|
|---|
| 494 | if (fFlags == SHOW_PROGRESS_DESC)
|
|---|
| 495 | RTStrmPrintf(g_pStdErr, "ok\n");
|
|---|
| 496 | else if (!fQuiet)
|
|---|
| 497 | RTStrmPrintf(g_pStdErr, "100%%\n");
|
|---|
| 498 | }
|
|---|
| 499 | }
|
|---|
| 500 | else if (g_fCanceled)
|
|---|
| 501 | RTStrmPrintf(g_pStdErr, VBoxManage::tr("CANCELED\n"));
|
|---|
| 502 | else
|
|---|
| 503 | {
|
|---|
| 504 | if (fDetailed)
|
|---|
| 505 | RTStrmPrintf(g_pStdErr, VBoxManage::tr("Progress state: %Rhrc\n"), iRc);
|
|---|
| 506 | else if (fFlags != SHOW_PROGRESS_NONE)
|
|---|
| 507 | RTStrmPrintf(g_pStdErr, "%Rhrc\n", iRc);
|
|---|
| 508 | }
|
|---|
| 509 | hrc = iRc;
|
|---|
| 510 | }
|
|---|
| 511 | else
|
|---|
| 512 | {
|
|---|
| 513 | if (!fDetailed)
|
|---|
| 514 | RTStrmPrintf(g_pStdErr, "\n");
|
|---|
| 515 | RTStrmPrintf(g_pStdErr, VBoxManage::tr("Progress object failure: %Rhrc\n"), hrc);
|
|---|
| 516 | }
|
|---|
| 517 | RTStrmFlush(g_pStdErr);
|
|---|
| 518 | return hrc;
|
|---|
| 519 | }
|
|---|
| 520 |
|
|---|
| 521 |
|
|---|
| 522 | void setBuiltInHelpLanguage(const char *pszLang)
|
|---|
| 523 | {
|
|---|
| 524 | #ifdef VBOX_WITH_VBOXMANAGE_NLS
|
|---|
| 525 | if (pszLang == NULL || pszLang[0] == '\0' || (pszLang[0] == 'C' && pszLang[1] == '\0'))
|
|---|
| 526 | pszLang = "en_US";
|
|---|
| 527 |
|
|---|
| 528 | /* find language entry matching exactly pszLang */
|
|---|
| 529 | PCHELP_LANG_ENTRY_T pHelpLangEntry = NULL;
|
|---|
| 530 | for (uint32_t i = 0; i < g_cHelpLangEntries; i++)
|
|---|
| 531 | {
|
|---|
| 532 | if (strcmp(g_aHelpLangEntries[i].pszLang, pszLang) == 0)
|
|---|
| 533 | {
|
|---|
| 534 | pHelpLangEntry = &g_aHelpLangEntries[i];
|
|---|
| 535 | break;
|
|---|
| 536 | }
|
|---|
| 537 | }
|
|---|
| 538 |
|
|---|
| 539 | /* find first entry containing language specified if pszLang contains only language */
|
|---|
| 540 | if (pHelpLangEntry == NULL)
|
|---|
| 541 | {
|
|---|
| 542 | size_t const cchLang = strlen(pszLang);
|
|---|
| 543 | for (uint32_t i = 0; i < g_cHelpLangEntries; i++)
|
|---|
| 544 | {
|
|---|
| 545 | if ( cchLang < g_aHelpLangEntries[i].cchLang
|
|---|
| 546 | && memcmp(g_aHelpLangEntries[i].pszLang, pszLang, cchLang) == 0)
|
|---|
| 547 | {
|
|---|
| 548 | pHelpLangEntry = &g_aHelpLangEntries[i];
|
|---|
| 549 | break;
|
|---|
| 550 | }
|
|---|
| 551 | }
|
|---|
| 552 | }
|
|---|
| 553 |
|
|---|
| 554 | /* set to en_US (i.e. untranslated) if not found */
|
|---|
| 555 | if (pHelpLangEntry == NULL)
|
|---|
| 556 | pHelpLangEntry = &g_aHelpLangEntries[0];
|
|---|
| 557 |
|
|---|
| 558 | ASMAtomicWritePtr(&g_pHelpLangEntry, pHelpLangEntry);
|
|---|
| 559 | #else
|
|---|
| 560 | NOREF(pszLang);
|
|---|
| 561 | #endif
|
|---|
| 562 | }
|
|---|
| 563 |
|
|---|
| 564 |
|
|---|
| 565 | int main(int argc, char *argv[])
|
|---|
| 566 | {
|
|---|
| 567 | /*
|
|---|
| 568 | * Before we do anything, init the runtime without loading
|
|---|
| 569 | * the support driver.
|
|---|
| 570 | */
|
|---|
| 571 | int vrc = RTR3InitExe(argc, &argv, 0);
|
|---|
| 572 | if (RT_FAILURE(vrc))
|
|---|
| 573 | return RTMsgInitFailure(vrc);
|
|---|
| 574 | #if defined(RT_OS_WINDOWS)
|
|---|
| 575 | ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
|
|---|
| 576 | #endif
|
|---|
| 577 |
|
|---|
| 578 | #ifdef VBOX_WITH_VBOXMANAGE_NLS
|
|---|
| 579 | /*
|
|---|
| 580 | * Initialize the translator and associated fun.
|
|---|
| 581 | */
|
|---|
| 582 | util::InitAutoLockSystem();
|
|---|
| 583 | ComObjPtr<VBoxEventListenerImpl> ptrEventListner;
|
|---|
| 584 | PTRCOMPONENT pTrComponent = NULL;
|
|---|
| 585 | VirtualBoxTranslator *pTranslator = VirtualBoxTranslator::instance();
|
|---|
| 586 | if (pTranslator != NULL)
|
|---|
| 587 | {
|
|---|
| 588 | char szNlsPath[RTPATH_MAX];
|
|---|
| 589 | vrc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
|
|---|
| 590 | if (RT_SUCCESS(vrc))
|
|---|
| 591 | vrc = RTPathAppend(szNlsPath, sizeof(szNlsPath), "nls" RTPATH_SLASH_STR "VBoxManageNls");
|
|---|
| 592 | if (RT_SUCCESS(vrc))
|
|---|
| 593 | {
|
|---|
| 594 | vrc = pTranslator->registerTranslation(szNlsPath, true, &pTrComponent);
|
|---|
| 595 | if (RT_SUCCESS(vrc))
|
|---|
| 596 | {
|
|---|
| 597 | vrc = pTranslator->i_loadLanguage(NULL);
|
|---|
| 598 | if (RT_SUCCESS(vrc))
|
|---|
| 599 | {
|
|---|
| 600 | com::Utf8Str strLang = pTranslator->language();
|
|---|
| 601 | setBuiltInHelpLanguage(strLang.c_str());
|
|---|
| 602 | }
|
|---|
| 603 | else
|
|---|
| 604 | RTMsgWarning("Load language failed: %Rrc\n", vrc);
|
|---|
| 605 | }
|
|---|
| 606 | else
|
|---|
| 607 | RTMsgWarning("Register translation failed: %Rrc\n", vrc);
|
|---|
| 608 | }
|
|---|
| 609 | else
|
|---|
| 610 | RTMsgWarning("Path constructing failed: %Rrc\n", vrc);
|
|---|
| 611 | }
|
|---|
| 612 | #endif
|
|---|
| 613 |
|
|---|
| 614 | /*
|
|---|
| 615 | * Parse the global options
|
|---|
| 616 | */
|
|---|
| 617 | bool fShowLogo = false;
|
|---|
| 618 | bool fShowHelp = false;
|
|---|
| 619 | int iCmd = 1;
|
|---|
| 620 | int iCmdArg;
|
|---|
| 621 | const char *pszSettingsPw = NULL;
|
|---|
| 622 | const char *pszSettingsPwFile = NULL;
|
|---|
| 623 | int cResponseFileArgs = 0;
|
|---|
| 624 | char **papszResponseFileArgs = NULL;
|
|---|
| 625 | char **papszNewArgv = NULL;
|
|---|
| 626 |
|
|---|
| 627 | for (int i = 1; i < argc || argc <= iCmd; i++)
|
|---|
| 628 | {
|
|---|
| 629 | if ( argc <= iCmd
|
|---|
| 630 | || !strcmp(argv[i], "help")
|
|---|
| 631 | || !strcmp(argv[i], "--help")
|
|---|
| 632 | || !strcmp(argv[i], "-?")
|
|---|
| 633 | || !strcmp(argv[i], "-h")
|
|---|
| 634 | || !strcmp(argv[i], "-help"))
|
|---|
| 635 | {
|
|---|
| 636 | if (i >= argc - 1)
|
|---|
| 637 | {
|
|---|
| 638 | showLogo(g_pStdOut);
|
|---|
| 639 | printUsage(g_pStdOut);
|
|---|
| 640 | return 0;
|
|---|
| 641 | }
|
|---|
| 642 | fShowLogo = true;
|
|---|
| 643 | fShowHelp = true;
|
|---|
| 644 | iCmd++;
|
|---|
| 645 | continue;
|
|---|
| 646 | }
|
|---|
| 647 |
|
|---|
| 648 | if ( !strcmp(argv[i], "-V")
|
|---|
| 649 | || !strcmp(argv[i], "--version")
|
|---|
| 650 | || !strcmp(argv[i], "-v") /* deprecated */
|
|---|
| 651 | || !strcmp(argv[i], "-version") /* deprecated */
|
|---|
| 652 | || !strcmp(argv[i], "-Version") /* deprecated */)
|
|---|
| 653 | {
|
|---|
| 654 | /* Print version number, and do nothing else. */
|
|---|
| 655 | RTPrintf("%sr%u\n", VBOX_VERSION_STRING, RTBldCfgRevision());
|
|---|
| 656 | return 0;
|
|---|
| 657 | }
|
|---|
| 658 | if (!strcmp(argv[i], "--dump-build-type"))
|
|---|
| 659 | {
|
|---|
| 660 | /* Print the build type, and do nothing else. (Used by ValKit to detect build type.) */
|
|---|
| 661 | RTPrintf("%s\n", RTBldCfgType());
|
|---|
| 662 | return 0;
|
|---|
| 663 | }
|
|---|
| 664 |
|
|---|
| 665 | if ( !strcmp(argv[i], "--dumpopts")
|
|---|
| 666 | || !strcmp(argv[i], "-dumpopts") /* deprecated */)
|
|---|
| 667 | {
|
|---|
| 668 | /* Special option to dump really all commands,
|
|---|
| 669 | * even the ones not understood on this platform. */
|
|---|
| 670 | printUsage(g_pStdOut);
|
|---|
| 671 | return 0;
|
|---|
| 672 | }
|
|---|
| 673 |
|
|---|
| 674 | if ( !strcmp(argv[i], "--nologo")
|
|---|
| 675 | || !strcmp(argv[i], "-q")
|
|---|
| 676 | || !strcmp(argv[i], "-nologo") /* deprecated */)
|
|---|
| 677 | {
|
|---|
| 678 | /* suppress the logo */
|
|---|
| 679 | fShowLogo = false;
|
|---|
| 680 | iCmd++;
|
|---|
| 681 | }
|
|---|
| 682 | else if ( !strcmp(argv[i], "--detailed-progress")
|
|---|
| 683 | || !strcmp(argv[i], "-d"))
|
|---|
| 684 | {
|
|---|
| 685 | /* detailed progress report */
|
|---|
| 686 | g_fDetailedProgress = true;
|
|---|
| 687 | iCmd++;
|
|---|
| 688 | }
|
|---|
| 689 | else if (!strcmp(argv[i], "--settingspw"))
|
|---|
| 690 | {
|
|---|
| 691 | if (i >= argc - 1)
|
|---|
| 692 | return RTMsgErrorExit(RTEXITCODE_FAILURE, VBoxManage::tr("Password expected"));
|
|---|
| 693 | /* password for certain settings */
|
|---|
| 694 | pszSettingsPw = argv[i + 1];
|
|---|
| 695 | iCmd += 2;
|
|---|
| 696 | }
|
|---|
| 697 | else if (!strcmp(argv[i], "--settingspwfile"))
|
|---|
| 698 | {
|
|---|
| 699 | if (i >= argc-1)
|
|---|
| 700 | return RTMsgErrorExit(RTEXITCODE_FAILURE, VBoxManage::tr("No password file specified"));
|
|---|
| 701 | pszSettingsPwFile = argv[i+1];
|
|---|
| 702 | iCmd += 2;
|
|---|
| 703 | }
|
|---|
| 704 | else if (argv[i][0] == '@')
|
|---|
| 705 | {
|
|---|
| 706 | if (papszResponseFileArgs)
|
|---|
| 707 | return RTMsgErrorExitFailure(VBoxManage::tr("Only one response file allowed"));
|
|---|
| 708 |
|
|---|
| 709 | /* Load response file, making sure it's valid UTF-8. */
|
|---|
| 710 | char *pszResponseFile;
|
|---|
| 711 | size_t cbResponseFile;
|
|---|
| 712 | vrc = RTFileReadAllEx(&argv[i][1], 0, RTFOFF_MAX, RTFILE_RDALL_O_DENY_NONE | RTFILE_RDALL_F_TRAILING_ZERO_BYTE,
|
|---|
| 713 | (void **)&pszResponseFile, &cbResponseFile);
|
|---|
| 714 | if (RT_FAILURE(vrc))
|
|---|
| 715 | return RTMsgErrorExitFailure(VBoxManage::tr("Error reading response file '%s': %Rrc"), &argv[i][1], vrc);
|
|---|
| 716 | vrc = RTStrValidateEncoding(pszResponseFile);
|
|---|
| 717 | if (RT_FAILURE(vrc))
|
|---|
| 718 | {
|
|---|
| 719 | RTFileReadAllFree(pszResponseFile, cbResponseFile);
|
|---|
| 720 | return RTMsgErrorExitFailure(VBoxManage::tr("Invalid response file ('%s') encoding: %Rrc"), &argv[i][1], vrc);
|
|---|
| 721 | }
|
|---|
| 722 |
|
|---|
| 723 | /* Parse it. */
|
|---|
| 724 | vrc = RTGetOptArgvFromString(&papszResponseFileArgs, &cResponseFileArgs, pszResponseFile,
|
|---|
| 725 | RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
|
|---|
| 726 | RTFileReadAllFree(pszResponseFile, cbResponseFile);
|
|---|
| 727 | if (RT_FAILURE(vrc))
|
|---|
| 728 | return RTMsgErrorExitFailure(VBoxManage::tr("Failed to parse response file '%s' (bourne shell style): %Rrc"), &argv[i][1], vrc);
|
|---|
| 729 |
|
|---|
| 730 | /* Construct new argv+argc with the response file arguments inserted. */
|
|---|
| 731 | int cNewArgs = argc + cResponseFileArgs;
|
|---|
| 732 | papszNewArgv = (char **)RTMemAllocZ((cNewArgs + 2) * sizeof(papszNewArgv[0]));
|
|---|
| 733 | if (!papszNewArgv)
|
|---|
| 734 | return RTMsgErrorExitFailure(VBoxManage::tr("out of memory"));
|
|---|
| 735 | memcpy(&papszNewArgv[0], &argv[0], sizeof(argv[0]) * (i + 1));
|
|---|
| 736 | memcpy(&papszNewArgv[i + 1], papszResponseFileArgs, sizeof(argv[0]) * cResponseFileArgs);
|
|---|
| 737 | memcpy(&papszNewArgv[i + 1 + cResponseFileArgs], &argv[i + 1], sizeof(argv[0]) * (argc - i - 1 + 1));
|
|---|
| 738 | argv = papszNewArgv;
|
|---|
| 739 | argc = argc + cResponseFileArgs;
|
|---|
| 740 |
|
|---|
| 741 | iCmd++;
|
|---|
| 742 | }
|
|---|
| 743 | else
|
|---|
| 744 | break;
|
|---|
| 745 | }
|
|---|
| 746 |
|
|---|
| 747 | iCmdArg = iCmd + 1;
|
|---|
| 748 |
|
|---|
| 749 | /*
|
|---|
| 750 | * Show the logo and lookup the command and deal with fShowHelp = true.
|
|---|
| 751 | */
|
|---|
| 752 | if (fShowLogo)
|
|---|
| 753 | showLogo(g_pStdOut);
|
|---|
| 754 |
|
|---|
| 755 | PCVBMGCMD pCmd = lookupCommand(argv[iCmd]);
|
|---|
| 756 | if (pCmd && pCmd->enmCmdHelp != VBMG_CMD_INTERNAL)
|
|---|
| 757 | setCurrentCommand(pCmd->enmCmdHelp);
|
|---|
| 758 |
|
|---|
| 759 | if ( pCmd
|
|---|
| 760 | && ( fShowHelp
|
|---|
| 761 | || argc - iCmdArg == 0))
|
|---|
| 762 | {
|
|---|
| 763 | if (pCmd->enmCmdHelp == VBMG_CMD_INTERNAL)
|
|---|
| 764 | printUsageInternalCmds(g_pStdOut);
|
|---|
| 765 | else if (fShowHelp)
|
|---|
| 766 | printHelp(g_pStdOut);
|
|---|
| 767 | else
|
|---|
| 768 | printUsage(g_pStdOut);
|
|---|
| 769 | return RTEXITCODE_FAILURE; /* error */
|
|---|
| 770 | }
|
|---|
| 771 | if (!pCmd)
|
|---|
| 772 | {
|
|---|
| 773 | if (!strcmp(argv[iCmd], "commands"))
|
|---|
| 774 | {
|
|---|
| 775 | RTPrintf(VBoxManage::tr("commands:\n"));
|
|---|
| 776 | for (unsigned i = 0; i < RT_ELEMENTS(g_aCommands); i++)
|
|---|
| 777 | if ( i == 0 /* skip backwards compatibility entries */
|
|---|
| 778 | || (g_aCommands[i].enmCmdHelp != g_aCommands[i - 1].enmCmdHelp))
|
|---|
| 779 | RTPrintf(" %s\n", g_aCommands[i].pszCommand);
|
|---|
| 780 | return RTEXITCODE_SUCCESS;
|
|---|
| 781 | }
|
|---|
| 782 | return errorSyntax(VBoxManage::tr("Invalid command '%s'"), argv[iCmd]);
|
|---|
| 783 | }
|
|---|
| 784 |
|
|---|
| 785 | RTEXITCODE rcExit;
|
|---|
| 786 | if (!(pCmd->fFlags & VBMG_CMD_F_NO_COM))
|
|---|
| 787 | {
|
|---|
| 788 | /*
|
|---|
| 789 | * Initialize COM.
|
|---|
| 790 | */
|
|---|
| 791 | using namespace com;
|
|---|
| 792 | HRESULT hrc = com::Initialize();
|
|---|
| 793 | if (FAILED(hrc))
|
|---|
| 794 | {
|
|---|
| 795 | # ifdef VBOX_WITH_XPCOM
|
|---|
| 796 | if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
|
|---|
| 797 | {
|
|---|
| 798 | char szHome[RTPATH_MAX] = "";
|
|---|
| 799 | com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
|
|---|
| 800 | return RTMsgErrorExit(RTEXITCODE_FAILURE,
|
|---|
| 801 | VBoxManage::tr("Failed to initialize COM because the global settings directory '%s' is not accessible!"), szHome);
|
|---|
| 802 | }
|
|---|
| 803 | # endif
|
|---|
| 804 | return RTMsgErrorExit(RTEXITCODE_FAILURE, VBoxManage::tr("Failed to initialize COM! (hrc=%Rhrc)"), hrc);
|
|---|
| 805 | }
|
|---|
| 806 |
|
|---|
| 807 |
|
|---|
| 808 | /*
|
|---|
| 809 | * Get the remote VirtualBox object and create a local session object.
|
|---|
| 810 | */
|
|---|
| 811 | rcExit = RTEXITCODE_FAILURE;
|
|---|
| 812 | ComPtr<IVirtualBoxClient> virtualBoxClient;
|
|---|
| 813 | ComPtr<IVirtualBox> virtualBox;
|
|---|
| 814 | hrc = virtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
|
|---|
| 815 | if (SUCCEEDED(hrc))
|
|---|
| 816 | hrc = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
|
|---|
| 817 | if (SUCCEEDED(hrc))
|
|---|
| 818 | {
|
|---|
| 819 | #ifdef VBOX_WITH_VBOXMANAGE_NLS
|
|---|
| 820 | /* Load language settings from IVirtualBox. */
|
|---|
| 821 | if (pTranslator != NULL)
|
|---|
| 822 | {
|
|---|
| 823 | HRESULT hrc1 = pTranslator->loadLanguage(virtualBox);
|
|---|
| 824 | if (SUCCEEDED(hrc1))
|
|---|
| 825 | {
|
|---|
| 826 | com::Utf8Str strLang = pTranslator->language();
|
|---|
| 827 | setBuiltInHelpLanguage(strLang.c_str());
|
|---|
| 828 | }
|
|---|
| 829 | else
|
|---|
| 830 | RTMsgWarning("Failed to load API language: %Rhrc", hrc1);
|
|---|
| 831 |
|
|---|
| 832 | /* VirtualBox language events registration. */
|
|---|
| 833 | ComPtr<IEventSource> pES;
|
|---|
| 834 | hrc1 = virtualBox->COMGETTER(EventSource)(pES.asOutParam());
|
|---|
| 835 | if (SUCCEEDED(hrc1))
|
|---|
| 836 | {
|
|---|
| 837 | hrc1 = ptrEventListner.createObject();
|
|---|
| 838 | if (SUCCEEDED(hrc1))
|
|---|
| 839 | hrc1 = ptrEventListner->init(new VBoxEventListener());
|
|---|
| 840 | if (SUCCEEDED(hrc1))
|
|---|
| 841 | {
|
|---|
| 842 | com::SafeArray<VBoxEventType_T> eventTypes;
|
|---|
| 843 | eventTypes.push_back(VBoxEventType_OnLanguageChanged);
|
|---|
| 844 | hrc1 = pES->RegisterListener(ptrEventListner, ComSafeArrayAsInParam(eventTypes), true);
|
|---|
| 845 | }
|
|---|
| 846 | if (FAILED(hrc1))
|
|---|
| 847 | {
|
|---|
| 848 | ptrEventListner.setNull();
|
|---|
| 849 | RTMsgWarning("Failed to register event listener: %Rhrc", hrc1);
|
|---|
| 850 | }
|
|---|
| 851 | }
|
|---|
| 852 | }
|
|---|
| 853 | #endif
|
|---|
| 854 |
|
|---|
| 855 | ComPtr<ISession> session;
|
|---|
| 856 | hrc = session.createInprocObject(CLSID_Session);
|
|---|
| 857 | if (SUCCEEDED(hrc))
|
|---|
| 858 | {
|
|---|
| 859 | /* Session secret. */
|
|---|
| 860 | if (pszSettingsPw)
|
|---|
| 861 | CHECK_ERROR2I_STMT(virtualBox, SetSettingsSecret(Bstr(pszSettingsPw).raw()), rcExit = RTEXITCODE_FAILURE);
|
|---|
| 862 | else if (pszSettingsPwFile)
|
|---|
| 863 | rcExit = settingsPasswordFile(virtualBox, pszSettingsPwFile);
|
|---|
| 864 | else
|
|---|
| 865 | rcExit = RTEXITCODE_SUCCESS;
|
|---|
| 866 | if (rcExit == RTEXITCODE_SUCCESS)
|
|---|
| 867 | {
|
|---|
| 868 | /*
|
|---|
| 869 | * Call the handler.
|
|---|
| 870 | */
|
|---|
| 871 | HandlerArg handlerArg = { argc - iCmdArg, &argv[iCmdArg], virtualBox, session };
|
|---|
| 872 | rcExit = pCmd->pfnHandler(&handlerArg);
|
|---|
| 873 |
|
|---|
| 874 | /* Although all handlers should always close the session if they open it,
|
|---|
| 875 | * we do it here just in case if some of the handlers contains a bug --
|
|---|
| 876 | * leaving the direct session not closed will turn the machine state to
|
|---|
| 877 | * Aborted which may have unwanted side effects like killing the saved
|
|---|
| 878 | * state file (if the machine was in the Saved state before). */
|
|---|
| 879 | session->UnlockMachine();
|
|---|
| 880 | }
|
|---|
| 881 |
|
|---|
| 882 | NativeEventQueue::getMainEventQueue()->processEventQueue(0);
|
|---|
| 883 | }
|
|---|
| 884 | else
|
|---|
| 885 | {
|
|---|
| 886 | com::ErrorInfo info;
|
|---|
| 887 | RTMsgError(VBoxManage::tr("Failed to create a session object!"));
|
|---|
| 888 | if (!info.isFullAvailable() && !info.isBasicAvailable())
|
|---|
| 889 | com::GluePrintRCMessage(hrc);
|
|---|
| 890 | else
|
|---|
| 891 | com::GluePrintErrorInfo(info);
|
|---|
| 892 | }
|
|---|
| 893 | }
|
|---|
| 894 | else
|
|---|
| 895 | {
|
|---|
| 896 | com::ErrorInfo info;
|
|---|
| 897 | RTMsgError(VBoxManage::tr("Failed to create the VirtualBox object!"));
|
|---|
| 898 | if (!info.isFullAvailable() && !info.isBasicAvailable())
|
|---|
| 899 | {
|
|---|
| 900 | com::GluePrintRCMessage(hrc);
|
|---|
| 901 | RTMsgError(VBoxManage::tr("Most likely, the VirtualBox COM server is not running or failed to start."));
|
|---|
| 902 | }
|
|---|
| 903 | else
|
|---|
| 904 | com::GluePrintErrorInfo(info);
|
|---|
| 905 | }
|
|---|
| 906 |
|
|---|
| 907 | #ifdef VBOX_WITH_VBOXMANAGE_NLS
|
|---|
| 908 | /* VirtualBox event callback unregistration. */
|
|---|
| 909 | if (ptrEventListner.isNotNull())
|
|---|
| 910 | {
|
|---|
| 911 | ComPtr<IEventSource> pES;
|
|---|
| 912 | HRESULT hrc1 = virtualBox->COMGETTER(EventSource)(pES.asOutParam());
|
|---|
| 913 | if (pES.isNotNull())
|
|---|
| 914 | {
|
|---|
| 915 | hrc1 = pES->UnregisterListener(ptrEventListner);
|
|---|
| 916 | if (FAILED(hrc1))
|
|---|
| 917 | LogRel(("Failed to unregister listener, %Rhrc", hrc1));
|
|---|
| 918 | }
|
|---|
| 919 | ptrEventListner.setNull();
|
|---|
| 920 | }
|
|---|
| 921 | #endif
|
|---|
| 922 | /*
|
|---|
| 923 | * Terminate COM, make sure the virtualBox object has been released.
|
|---|
| 924 | */
|
|---|
| 925 | virtualBox.setNull();
|
|---|
| 926 | virtualBoxClient.setNull();
|
|---|
| 927 | NativeEventQueue::getMainEventQueue()->processEventQueue(0);
|
|---|
| 928 | com::Shutdown();
|
|---|
| 929 | }
|
|---|
| 930 | else
|
|---|
| 931 | {
|
|---|
| 932 | /*
|
|---|
| 933 | * The command needs no COM.
|
|---|
| 934 | */
|
|---|
| 935 | HandlerArg handlerArg;
|
|---|
| 936 | handlerArg.argc = argc - iCmdArg;
|
|---|
| 937 | handlerArg.argv = &argv[iCmdArg];
|
|---|
| 938 | rcExit = pCmd->pfnHandler(&handlerArg);
|
|---|
| 939 | }
|
|---|
| 940 |
|
|---|
| 941 | #ifdef VBOX_WITH_VBOXMANAGE_NLS
|
|---|
| 942 | if (pTranslator != NULL)
|
|---|
| 943 | {
|
|---|
| 944 | pTranslator->release();
|
|---|
| 945 | pTranslator = NULL;
|
|---|
| 946 | pTrComponent = NULL;
|
|---|
| 947 | }
|
|---|
| 948 | #endif
|
|---|
| 949 |
|
|---|
| 950 | if (papszResponseFileArgs)
|
|---|
| 951 | {
|
|---|
| 952 | RTGetOptArgvFree(papszResponseFileArgs);
|
|---|
| 953 | RTMemFree(papszNewArgv);
|
|---|
| 954 | }
|
|---|
| 955 |
|
|---|
| 956 | return rcExit;
|
|---|
| 957 | }
|
|---|