VirtualBox

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

Last change on this file since 107133 was 107100, checked in by vboxsync, 2 months ago

FE/Qt,VBoxManage,VBoxHeadless: Refuse to run in WoW64 to prevent mixing up amd64 and arm64 builds in the out directory. jiraref:VBP-1466

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette