VirtualBox

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

Last change on this file since 107212 was 107161, checked in by vboxsync, 3 months ago

jiraref:VBP-1459. Added the function handleTrackedObjects into the registered command handlers list g_aCommands.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.9 KB
Line 
1/* $Id: VBoxManage.cpp 107161 2024-11-27 10:44:32Z 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 { "objtracker", HELP_CMD_OBJTRACKER, handleTrackedObjects, 0 },
275};
276
277/**
278 * Looks up a command by name.
279 *
280 * @returns Pointer to the command structure.
281 * @param pszCommand Name of the command.
282 */
283static PCVBMGCMD lookupCommand(const char *pszCommand)
284{
285 if (pszCommand)
286 for (uint32_t i = 0; i < RT_ELEMENTS(g_aCommands); i++)
287 if (!strcmp(g_aCommands[i].pszCommand, pszCommand))
288 return &g_aCommands[i];
289 return NULL;
290}
291
292
293/**
294 * Signal handler that sets g_fCanceled.
295 *
296 * This can be executed on any thread in the process, on Windows it may even be
297 * a thread dedicated to delivering this signal. Do not doing anything
298 * unnecessary here.
299 */
300static void showProgressSignalHandler(int iSignal) RT_NOTHROW_DEF
301{
302 NOREF(iSignal);
303 ASMAtomicWriteBool(&g_fCanceled, true);
304}
305
306/**
307 * Print out progress on the console.
308 *
309 * This runs the main event queue every now and then to prevent piling up
310 * unhandled things (which doesn't cause real problems, just makes things
311 * react a little slower than in the ideal case).
312 */
313HRESULT showProgress(ComPtr<IProgress> progress, uint32_t fFlags)
314{
315 using namespace com;
316 HRESULT hrc;
317
318 AssertReturn(progress.isNotNull(), E_FAIL);
319
320 /* grandfather the old callers */
321 if (g_fDetailedProgress)
322 fFlags = SHOW_PROGRESS_DETAILS;
323
324 const bool fDetailed = RT_BOOL(fFlags & SHOW_PROGRESS_DETAILS);
325 const bool fOps = RT_BOOL(fFlags & SHOW_PROGRESS_OPS);
326 const bool fQuiet = !RT_BOOL(fFlags & (SHOW_PROGRESS | SHOW_PROGRESS_DETAILS | SHOW_PROGRESS_OPS));
327
328 AssertReturn((!fDetailed && !fOps) || (fDetailed != fOps), E_INVALIDARG); /* Mutually exclusive. */
329
330 BOOL fCompleted = FALSE;
331 ULONG ulCurrentPercent = 0;
332 ULONG ulLastPercent = 0;
333
334 ULONG ulLastOperationPercent = (ULONG)-1;
335
336 ULONG ulLastOperation = (ULONG)-1;
337 Bstr bstrOperationDescription;
338
339 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
340
341 ULONG cOperations = 1;
342 hrc = progress->COMGETTER(OperationCount)(&cOperations);
343 if (FAILED(hrc))
344 {
345 RTStrmPrintf(g_pStdErr, VBoxManage::tr("Progress object failure: %Rhrc\n"), hrc);
346 RTStrmFlush(g_pStdErr);
347 return hrc;
348 }
349
350 /*
351 * Note: Outputting the progress info to stderr (g_pStdErr) is intentional
352 * to not get intermixed with other (raw) stdout data which might get
353 * written in the meanwhile.
354 */
355
356 if (fFlags & SHOW_PROGRESS_DESC)
357 {
358 com::Bstr bstrDescription;
359 hrc = progress->COMGETTER(Description(bstrDescription.asOutParam()));
360 if (FAILED(hrc))
361 {
362 RTStrmPrintf(g_pStdErr, VBoxManage::tr("Failed to get progress description: %Rhrc\n"), hrc);
363 return hrc;
364 }
365
366 const char *pcszDescSep;
367 if (fDetailed) /* multiline output */
368 pcszDescSep = "\n";
369 else /* continues on the same line */
370 pcszDescSep = ": ";
371
372 RTStrmPrintf(g_pStdErr, "%ls%s", bstrDescription.raw(), pcszDescSep);
373 RTStrmFlush(g_pStdErr);
374 }
375
376 if (!fQuiet && !fDetailed && !fOps)
377 {
378 RTStrmPrintf(g_pStdErr, "0%%...");
379 RTStrmFlush(g_pStdErr);
380 }
381
382 /* setup signal handling if cancelable */
383 bool fCanceledAlready = false;
384 BOOL fCancelable;
385 hrc = progress->COMGETTER(Cancelable)(&fCancelable);
386 if (FAILED(hrc))
387 fCancelable = FALSE;
388 if (fCancelable)
389 {
390 signal(SIGINT, showProgressSignalHandler);
391 signal(SIGTERM, showProgressSignalHandler);
392#ifdef SIGBREAK
393 signal(SIGBREAK, showProgressSignalHandler);
394#endif
395 }
396
397 hrc = progress->COMGETTER(Completed(&fCompleted));
398 while (SUCCEEDED(hrc))
399 {
400 progress->COMGETTER(Percent(&ulCurrentPercent));
401
402 if ( fDetailed
403 || fOps)
404 {
405 ULONG ulOperation = 1;
406 hrc = progress->COMGETTER(Operation)(&ulOperation);
407 if (FAILED(hrc))
408 break;
409 ULONG ulCurrentOperationPercent = 0;
410 hrc = progress->COMGETTER(OperationPercent(&ulCurrentOperationPercent));
411 if (FAILED(hrc))
412 break;
413
414 if (ulLastOperation != ulOperation)
415 {
416 hrc = progress->COMGETTER(OperationDescription(bstrOperationDescription.asOutParam()));
417 if (FAILED(hrc))
418 break;
419 ulLastPercent = (ULONG)-1; // force print
420 ulLastOperation = ulOperation;
421 }
422
423 if ( ulCurrentPercent != ulLastPercent
424 || ulCurrentOperationPercent != ulLastOperationPercent
425 )
426 {
427 LONG lSecsRem = 0;
428 progress->COMGETTER(TimeRemaining)(&lSecsRem);
429
430 if (fDetailed)
431 RTStrmPrintf(g_pStdErr, VBoxManage::tr("(%u/%u) %ls %02u%% => %02u%% (%d s remaining)\n"), ulOperation + 1, cOperations,
432 bstrOperationDescription.raw(), ulCurrentOperationPercent, ulCurrentPercent, lSecsRem);
433 else
434 RTStrmPrintf(g_pStdErr, VBoxManage::tr("%02u%%: %ls\n"), ulCurrentPercent, bstrOperationDescription.raw());
435
436 ulLastPercent = ulCurrentPercent;
437 ulLastOperationPercent = ulCurrentOperationPercent;
438 }
439 }
440 else if (!fQuiet)
441 {
442 /* did we cross a 10% mark? */
443 if (ulCurrentPercent / 10 > ulLastPercent / 10)
444 {
445 /* make sure to also print out missed steps */
446 for (ULONG curVal = (ulLastPercent / 10) * 10 + 10; curVal <= (ulCurrentPercent / 10) * 10; curVal += 10)
447 {
448 if (curVal < 100)
449 {
450 RTStrmPrintf(g_pStdErr, "%u%%...", curVal);
451 RTStrmFlush(g_pStdErr);
452 }
453 }
454 ulLastPercent = (ulCurrentPercent / 10) * 10;
455 }
456 }
457 if (fCompleted)
458 break;
459
460 /* process async cancelation */
461 if (g_fCanceled && !fCanceledAlready)
462 {
463 hrc = progress->Cancel();
464 if (SUCCEEDED(hrc))
465 fCanceledAlready = true;
466 else
467 g_fCanceled = false;
468 }
469
470 /* make sure the loop is not too tight */
471 progress->WaitForCompletion(100);
472
473 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
474 hrc = progress->COMGETTER(Completed(&fCompleted));
475 }
476
477 /* undo signal handling */
478 if (fCancelable)
479 {
480 signal(SIGINT, SIG_DFL);
481 signal(SIGTERM, SIG_DFL);
482# ifdef SIGBREAK
483 signal(SIGBREAK, SIG_DFL);
484# endif
485 }
486
487 /* complete the line. */
488 LONG iRc = E_FAIL;
489 hrc = progress->COMGETTER(ResultCode)(&iRc);
490 if (SUCCEEDED(hrc))
491 {
492 /* async operation completed successfully */
493 if (SUCCEEDED(iRc))
494 {
495 if (!fDetailed)
496 {
497 if (fFlags == SHOW_PROGRESS_DESC)
498 RTStrmPrintf(g_pStdErr, "ok\n");
499 else if (!fQuiet)
500 RTStrmPrintf(g_pStdErr, "100%%\n");
501 }
502 }
503 else if (g_fCanceled)
504 RTStrmPrintf(g_pStdErr, VBoxManage::tr("CANCELED\n"));
505 else
506 {
507 if (fDetailed)
508 RTStrmPrintf(g_pStdErr, VBoxManage::tr("Progress state: %Rhrc\n"), iRc);
509 else if (fFlags != SHOW_PROGRESS_NONE)
510 RTStrmPrintf(g_pStdErr, "%Rhrc\n", iRc);
511 }
512 hrc = iRc;
513 }
514 else
515 {
516 if (!fDetailed)
517 RTStrmPrintf(g_pStdErr, "\n");
518 RTStrmPrintf(g_pStdErr, VBoxManage::tr("Progress object failure: %Rhrc\n"), hrc);
519 }
520 RTStrmFlush(g_pStdErr);
521 return hrc;
522}
523
524
525void setBuiltInHelpLanguage(const char *pszLang)
526{
527#ifdef VBOX_WITH_VBOXMANAGE_NLS
528 if (pszLang == NULL || pszLang[0] == '\0' || (pszLang[0] == 'C' && pszLang[1] == '\0'))
529 pszLang = "en_US";
530
531 /* find language entry matching exactly pszLang */
532 PCHELP_LANG_ENTRY_T pHelpLangEntry = NULL;
533 for (uint32_t i = 0; i < g_cHelpLangEntries; i++)
534 {
535 if (strcmp(g_aHelpLangEntries[i].pszLang, pszLang) == 0)
536 {
537 pHelpLangEntry = &g_aHelpLangEntries[i];
538 break;
539 }
540 }
541
542 /* find first entry containing language specified if pszLang contains only language */
543 if (pHelpLangEntry == NULL)
544 {
545 size_t const cchLang = strlen(pszLang);
546 for (uint32_t i = 0; i < g_cHelpLangEntries; i++)
547 {
548 if ( cchLang < g_aHelpLangEntries[i].cchLang
549 && memcmp(g_aHelpLangEntries[i].pszLang, pszLang, cchLang) == 0)
550 {
551 pHelpLangEntry = &g_aHelpLangEntries[i];
552 break;
553 }
554 }
555 }
556
557 /* set to en_US (i.e. untranslated) if not found */
558 if (pHelpLangEntry == NULL)
559 pHelpLangEntry = &g_aHelpLangEntries[0];
560
561 ASMAtomicWritePtr(&g_pHelpLangEntry, pHelpLangEntry);
562#else
563 NOREF(pszLang);
564#endif
565}
566
567
568int main(int argc, char *argv[])
569{
570 /*
571 * Before we do anything, init the runtime without loading
572 * the support driver.
573 */
574 int vrc = RTR3InitExe(argc, &argv, 0);
575 if (RT_FAILURE(vrc))
576 return RTMsgInitFailure(vrc);
577#if defined(RT_OS_WINDOWS)
578 ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
579#endif
580
581#ifdef VBOX_WITH_VBOXMANAGE_NLS
582 /*
583 * Initialize the translator and associated fun.
584 */
585 util::InitAutoLockSystem();
586 ComObjPtr<VBoxEventListenerImpl> ptrEventListner;
587 PTRCOMPONENT pTrComponent = NULL;
588 VirtualBoxTranslator *pTranslator = VirtualBoxTranslator::instance();
589 if (pTranslator != NULL)
590 {
591 char szNlsPath[RTPATH_MAX];
592 vrc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
593 if (RT_SUCCESS(vrc))
594 vrc = RTPathAppend(szNlsPath, sizeof(szNlsPath), "nls" RTPATH_SLASH_STR "VBoxManageNls");
595 if (RT_SUCCESS(vrc))
596 {
597 vrc = pTranslator->registerTranslation(szNlsPath, true, &pTrComponent);
598 if (RT_SUCCESS(vrc))
599 {
600 vrc = pTranslator->i_loadLanguage(NULL);
601 if (RT_SUCCESS(vrc))
602 {
603 com::Utf8Str strLang = pTranslator->language();
604 setBuiltInHelpLanguage(strLang.c_str());
605 }
606 else
607 RTMsgWarning("Load language failed: %Rrc\n", vrc);
608 }
609 else
610 RTMsgWarning("Register translation failed: %Rrc\n", vrc);
611 }
612 else
613 RTMsgWarning("Path constructing failed: %Rrc\n", vrc);
614 }
615#endif
616
617 /*
618 * Parse the global options
619 */
620 bool fShowLogo = false;
621 bool fShowHelp = false;
622 int iCmd = 1;
623 int iCmdArg;
624 const char *pszSettingsPw = NULL;
625 const char *pszSettingsPwFile = NULL;
626 int cResponseFileArgs = 0;
627 char **papszResponseFileArgs = NULL;
628 char **papszNewArgv = NULL;
629
630 for (int i = 1; i < argc || argc <= iCmd; i++)
631 {
632 if ( argc <= iCmd
633 || !strcmp(argv[i], "help")
634 || !strcmp(argv[i], "--help")
635 || !strcmp(argv[i], "-?")
636 || !strcmp(argv[i], "-h")
637 || !strcmp(argv[i], "-help"))
638 {
639 if (i >= argc - 1)
640 {
641 showLogo(g_pStdOut);
642 printUsage(g_pStdOut);
643 return 0;
644 }
645 fShowLogo = true;
646 fShowHelp = true;
647 iCmd++;
648 continue;
649 }
650
651 if ( !strcmp(argv[i], "-V")
652 || !strcmp(argv[i], "--version")
653 || !strcmp(argv[i], "-v") /* deprecated */
654 || !strcmp(argv[i], "-version") /* deprecated */
655 || !strcmp(argv[i], "-Version") /* deprecated */)
656 {
657 /* Print version number, and do nothing else. */
658 RTPrintf("%sr%u\n", VBOX_VERSION_STRING, RTBldCfgRevision());
659 return 0;
660 }
661 if (!strcmp(argv[i], "--dump-build-type"))
662 {
663 /* Print the build type, and do nothing else. (Used by ValKit to detect build type.) */
664 RTPrintf("%s\n", RTBldCfgType());
665 return 0;
666 }
667
668 if ( !strcmp(argv[i], "--dumpopts")
669 || !strcmp(argv[i], "-dumpopts") /* deprecated */)
670 {
671 /* Special option to dump really all commands,
672 * even the ones not understood on this platform. */
673 printUsage(g_pStdOut);
674 return 0;
675 }
676
677 if ( !strcmp(argv[i], "--nologo")
678 || !strcmp(argv[i], "-q")
679 || !strcmp(argv[i], "-nologo") /* deprecated */)
680 {
681 /* suppress the logo */
682 fShowLogo = false;
683 iCmd++;
684 }
685 else if ( !strcmp(argv[i], "--detailed-progress")
686 || !strcmp(argv[i], "-d"))
687 {
688 /* detailed progress report */
689 g_fDetailedProgress = true;
690 iCmd++;
691 }
692 else if (!strcmp(argv[i], "--settingspw"))
693 {
694 if (i >= argc - 1)
695 return RTMsgErrorExit(RTEXITCODE_FAILURE, VBoxManage::tr("Password expected"));
696 /* password for certain settings */
697 pszSettingsPw = argv[i + 1];
698 iCmd += 2;
699 }
700 else if (!strcmp(argv[i], "--settingspwfile"))
701 {
702 if (i >= argc-1)
703 return RTMsgErrorExit(RTEXITCODE_FAILURE, VBoxManage::tr("No password file specified"));
704 pszSettingsPwFile = argv[i+1];
705 iCmd += 2;
706 }
707 else if (argv[i][0] == '@')
708 {
709 if (papszResponseFileArgs)
710 return RTMsgErrorExitFailure(VBoxManage::tr("Only one response file allowed"));
711
712 /* Load response file, making sure it's valid UTF-8. */
713 char *pszResponseFile;
714 size_t cbResponseFile;
715 vrc = RTFileReadAllEx(&argv[i][1], 0, RTFOFF_MAX, RTFILE_RDALL_O_DENY_NONE | RTFILE_RDALL_F_TRAILING_ZERO_BYTE,
716 (void **)&pszResponseFile, &cbResponseFile);
717 if (RT_FAILURE(vrc))
718 return RTMsgErrorExitFailure(VBoxManage::tr("Error reading response file '%s': %Rrc"), &argv[i][1], vrc);
719 vrc = RTStrValidateEncoding(pszResponseFile);
720 if (RT_FAILURE(vrc))
721 {
722 RTFileReadAllFree(pszResponseFile, cbResponseFile);
723 return RTMsgErrorExitFailure(VBoxManage::tr("Invalid response file ('%s') encoding: %Rrc"), &argv[i][1], vrc);
724 }
725
726 /* Parse it. */
727 vrc = RTGetOptArgvFromString(&papszResponseFileArgs, &cResponseFileArgs, pszResponseFile,
728 RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
729 RTFileReadAllFree(pszResponseFile, cbResponseFile);
730 if (RT_FAILURE(vrc))
731 return RTMsgErrorExitFailure(VBoxManage::tr("Failed to parse response file '%s' (bourne shell style): %Rrc"), &argv[i][1], vrc);
732
733 /* Construct new argv+argc with the response file arguments inserted. */
734 int cNewArgs = argc + cResponseFileArgs;
735 papszNewArgv = (char **)RTMemAllocZ((cNewArgs + 2) * sizeof(papszNewArgv[0]));
736 if (!papszNewArgv)
737 return RTMsgErrorExitFailure(VBoxManage::tr("out of memory"));
738 memcpy(&papszNewArgv[0], &argv[0], sizeof(argv[0]) * (i + 1));
739 memcpy(&papszNewArgv[i + 1], papszResponseFileArgs, sizeof(argv[0]) * cResponseFileArgs);
740 memcpy(&papszNewArgv[i + 1 + cResponseFileArgs], &argv[i + 1], sizeof(argv[0]) * (argc - i - 1 + 1));
741 argv = papszNewArgv;
742 argc = argc + cResponseFileArgs;
743
744 iCmd++;
745 }
746 else
747 break;
748 }
749
750 iCmdArg = iCmd + 1;
751
752 /*
753 * Show the logo and lookup the command and deal with fShowHelp = true.
754 */
755 if (fShowLogo)
756 showLogo(g_pStdOut);
757
758 PCVBMGCMD pCmd = lookupCommand(argv[iCmd]);
759 if (pCmd && pCmd->enmCmdHelp != VBMG_CMD_INTERNAL)
760 setCurrentCommand(pCmd->enmCmdHelp);
761
762 if ( pCmd
763 && ( fShowHelp
764 || argc - iCmdArg == 0))
765 {
766 if (pCmd->enmCmdHelp == VBMG_CMD_INTERNAL)
767 printUsageInternalCmds(g_pStdOut);
768 else if (fShowHelp)
769 printHelp(g_pStdOut);
770 else
771 printUsage(g_pStdOut);
772 return RTEXITCODE_FAILURE; /* error */
773 }
774 if (!pCmd)
775 {
776 if (!strcmp(argv[iCmd], "commands"))
777 {
778 RTPrintf(VBoxManage::tr("commands:\n"));
779 for (unsigned i = 0; i < RT_ELEMENTS(g_aCommands); i++)
780 if ( i == 0 /* skip backwards compatibility entries */
781 || (g_aCommands[i].enmCmdHelp != g_aCommands[i - 1].enmCmdHelp))
782 RTPrintf(" %s\n", g_aCommands[i].pszCommand);
783 return RTEXITCODE_SUCCESS;
784 }
785 return errorSyntax(VBoxManage::tr("Invalid command '%s'"), argv[iCmd]);
786 }
787
788 RTEXITCODE rcExit;
789 if (!(pCmd->fFlags & VBMG_CMD_F_NO_COM))
790 {
791 /*
792 * Before we try to initialize COM, make sure we're not running inside
793 * an emulator (these days, running amd64 code in an emulator on arm64).
794 */
795 uint32_t const uNativeArch = RTSystemGetNativeArch();
796 if (uNativeArch != RT_ARCH_VAL && uNativeArch != 0)
797 {
798 RTMsgError("Binary (%s) doesn't match the host CPU (%s)!",
799 RTArchValToString(RT_ARCH_VAL), RTArchValToString(uNativeArch));
800 return RTEXITCODE_FAILURE;
801 }
802
803 /*
804 * Initialize COM.
805 */
806 using namespace com;
807 HRESULT hrc = com::Initialize();
808 if (FAILED(hrc))
809 {
810# ifdef VBOX_WITH_XPCOM
811 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
812 {
813 char szHome[RTPATH_MAX] = "";
814 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
815 return RTMsgErrorExit(RTEXITCODE_FAILURE,
816 VBoxManage::tr("Failed to initialize COM because the global settings directory '%s' is not accessible!"), szHome);
817 }
818# endif
819 return RTMsgErrorExit(RTEXITCODE_FAILURE, VBoxManage::tr("Failed to initialize COM! (hrc=%Rhrc)"), hrc);
820 }
821
822
823 /*
824 * Get the remote VirtualBox object and create a local session object.
825 */
826 rcExit = RTEXITCODE_FAILURE;
827 ComPtr<IVirtualBoxClient> virtualBoxClient;
828 ComPtr<IVirtualBox> virtualBox;
829 hrc = virtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
830 if (SUCCEEDED(hrc))
831 hrc = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
832 if (SUCCEEDED(hrc))
833 {
834#ifdef VBOX_WITH_VBOXMANAGE_NLS
835 /* Load language settings from IVirtualBox. */
836 if (pTranslator != NULL)
837 {
838 HRESULT hrc1 = pTranslator->loadLanguage(virtualBox);
839 if (SUCCEEDED(hrc1))
840 {
841 com::Utf8Str strLang = pTranslator->language();
842 setBuiltInHelpLanguage(strLang.c_str());
843 }
844 else
845 RTMsgWarning("Failed to load API language: %Rhrc", hrc1);
846
847 /* VirtualBox language events registration. */
848 ComPtr<IEventSource> pES;
849 hrc1 = virtualBox->COMGETTER(EventSource)(pES.asOutParam());
850 if (SUCCEEDED(hrc1))
851 {
852 hrc1 = ptrEventListner.createObject();
853 if (SUCCEEDED(hrc1))
854 hrc1 = ptrEventListner->init(new VBoxEventListener());
855 if (SUCCEEDED(hrc1))
856 {
857 com::SafeArray<VBoxEventType_T> eventTypes;
858 eventTypes.push_back(VBoxEventType_OnLanguageChanged);
859 hrc1 = pES->RegisterListener(ptrEventListner, ComSafeArrayAsInParam(eventTypes), true);
860 }
861 if (FAILED(hrc1))
862 {
863 ptrEventListner.setNull();
864 RTMsgWarning("Failed to register event listener: %Rhrc", hrc1);
865 }
866 }
867 }
868#endif
869
870 ComPtr<ISession> session;
871 hrc = session.createInprocObject(CLSID_Session);
872 if (SUCCEEDED(hrc))
873 {
874 /* Session secret. */
875 if (pszSettingsPw)
876 CHECK_ERROR2I_STMT(virtualBox, SetSettingsSecret(Bstr(pszSettingsPw).raw()), rcExit = RTEXITCODE_FAILURE);
877 else if (pszSettingsPwFile)
878 rcExit = settingsPasswordFile(virtualBox, pszSettingsPwFile);
879 else
880 rcExit = RTEXITCODE_SUCCESS;
881 if (rcExit == RTEXITCODE_SUCCESS)
882 {
883 /*
884 * Call the handler.
885 */
886 HandlerArg handlerArg = { argc - iCmdArg, &argv[iCmdArg], virtualBox, session };
887 rcExit = pCmd->pfnHandler(&handlerArg);
888
889 /* Although all handlers should always close the session if they open it,
890 * we do it here just in case if some of the handlers contains a bug --
891 * leaving the direct session not closed will turn the machine state to
892 * Aborted which may have unwanted side effects like killing the saved
893 * state file (if the machine was in the Saved state before). */
894 session->UnlockMachine();
895 }
896
897 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
898 }
899 else
900 {
901 com::ErrorInfo info;
902 RTMsgError(VBoxManage::tr("Failed to create a session object!"));
903 if (!info.isFullAvailable() && !info.isBasicAvailable())
904 com::GluePrintRCMessage(hrc);
905 else
906 com::GluePrintErrorInfo(info);
907 }
908 }
909 else
910 {
911 com::ErrorInfo info;
912 RTMsgError(VBoxManage::tr("Failed to create the VirtualBox object!"));
913 if (!info.isFullAvailable() && !info.isBasicAvailable())
914 {
915 com::GluePrintRCMessage(hrc);
916 RTMsgError(VBoxManage::tr("Most likely, the VirtualBox COM server is not running or failed to start."));
917 }
918 else
919 com::GluePrintErrorInfo(info);
920 }
921
922#ifdef VBOX_WITH_VBOXMANAGE_NLS
923 /* VirtualBox event callback unregistration. */
924 if (ptrEventListner.isNotNull())
925 {
926 ComPtr<IEventSource> pES;
927 HRESULT hrc1 = virtualBox->COMGETTER(EventSource)(pES.asOutParam());
928 if (pES.isNotNull())
929 {
930 hrc1 = pES->UnregisterListener(ptrEventListner);
931 if (FAILED(hrc1))
932 LogRel(("Failed to unregister listener, %Rhrc", hrc1));
933 }
934 ptrEventListner.setNull();
935 }
936#endif
937 /*
938 * Terminate COM, make sure the virtualBox object has been released.
939 */
940 virtualBox.setNull();
941 virtualBoxClient.setNull();
942 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
943 com::Shutdown();
944 }
945 else
946 {
947 /*
948 * The command needs no COM.
949 */
950 HandlerArg handlerArg;
951 handlerArg.argc = argc - iCmdArg;
952 handlerArg.argv = &argv[iCmdArg];
953 rcExit = pCmd->pfnHandler(&handlerArg);
954 }
955
956#ifdef VBOX_WITH_VBOXMANAGE_NLS
957 if (pTranslator != NULL)
958 {
959 pTranslator->release();
960 pTranslator = NULL;
961 pTrComponent = NULL;
962 }
963#endif
964
965 if (papszResponseFileArgs)
966 {
967 RTGetOptArgvFree(papszResponseFileArgs);
968 RTMemFree(papszNewArgv);
969 }
970
971 return rcExit;
972}
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