VirtualBox

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

Last change on this file since 47002 was 46649, checked in by vboxsync, 11 years ago

Forward ported r85941 and required build fixes (Main: Implemented new event queue to separate system's native event queue and our own. Also, XPCOM is not needed for handling our own events. On Windows this also fixes the system's queue quota limitation).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.2 KB
Line 
1/* $Id: VBoxManage.cpp 46649 2013-06-19 11:47:32Z vboxsync $ */
2/** @file
3 * VBoxManage - VirtualBox's command-line interface.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#ifndef VBOX_ONLY_DOCS
23# include <VBox/com/com.h>
24# include <VBox/com/string.h>
25# include <VBox/com/Guid.h>
26# include <VBox/com/array.h>
27# include <VBox/com/ErrorInfo.h>
28# include <VBox/com/errorprint.h>
29# include <VBox/com/NativeEventQueue.h>
30
31# include <VBox/com/VirtualBox.h>
32#endif /* !VBOX_ONLY_DOCS */
33
34#include <VBox/err.h>
35#include <VBox/version.h>
36
37#include <iprt/asm.h>
38#include <iprt/buildconfig.h>
39#include <iprt/ctype.h>
40#include <iprt/initterm.h>
41#include <iprt/path.h>
42#include <iprt/stream.h>
43#include <iprt/string.h>
44
45#include <signal.h>
46
47#include "VBoxManage.h"
48
49
50/*******************************************************************************
51* Global Variables *
52*******************************************************************************/
53/*extern*/ bool g_fDetailedProgress = false;
54
55#ifndef VBOX_ONLY_DOCS
56/** Set by the signal handler. */
57static volatile bool g_fCanceled = false;
58
59
60/**
61 * Signal handler that sets g_fCanceled.
62 *
63 * This can be executed on any thread in the process, on Windows it may even be
64 * a thread dedicated to delivering this signal. Do not doing anything
65 * unnecessary here.
66 */
67static void showProgressSignalHandler(int iSignal)
68{
69 NOREF(iSignal);
70 ASMAtomicWriteBool(&g_fCanceled, true);
71}
72
73/**
74 * Print out progress on the console.
75 *
76 * This runs the main event queue every now and then to prevent piling up
77 * unhandled things (which doesn't cause real problems, just makes things
78 * react a little slower than in the ideal case).
79 */
80HRESULT showProgress(ComPtr<IProgress> progress)
81{
82 using namespace com;
83
84 BOOL fCompleted = FALSE;
85 ULONG ulCurrentPercent = 0;
86 ULONG ulLastPercent = 0;
87
88 ULONG ulLastOperationPercent = (ULONG)-1;
89
90 ULONG ulLastOperation = (ULONG)-1;
91 Bstr bstrOperationDescription;
92
93 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
94
95 ULONG cOperations = 1;
96 HRESULT hrc = progress->COMGETTER(OperationCount)(&cOperations);
97 if (FAILED(hrc))
98 {
99 RTStrmPrintf(g_pStdErr, "Progress object failure: %Rhrc\n", hrc);
100 RTStrmFlush(g_pStdErr);
101 return hrc;
102 }
103
104 /*
105 * Note: Outputting the progress info to stderr (g_pStdErr) is intentional
106 * to not get intermixed with other (raw) stdout data which might get
107 * written in the meanwhile.
108 */
109
110 if (!g_fDetailedProgress)
111 {
112 RTStrmPrintf(g_pStdErr, "0%%...");
113 RTStrmFlush(g_pStdErr);
114 }
115
116 /* setup signal handling if cancelable */
117 bool fCanceledAlready = false;
118 BOOL fCancelable;
119 hrc = progress->COMGETTER(Cancelable)(&fCancelable);
120 if (FAILED(hrc))
121 fCancelable = FALSE;
122 if (fCancelable)
123 {
124 signal(SIGINT, showProgressSignalHandler);
125#ifdef SIGBREAK
126 signal(SIGBREAK, showProgressSignalHandler);
127#endif
128 }
129
130 hrc = progress->COMGETTER(Completed(&fCompleted));
131 while (SUCCEEDED(hrc))
132 {
133 progress->COMGETTER(Percent(&ulCurrentPercent));
134
135 if (g_fDetailedProgress)
136 {
137 ULONG ulOperation = 1;
138 hrc = progress->COMGETTER(Operation)(&ulOperation);
139 if (FAILED(hrc))
140 break;
141 ULONG ulCurrentOperationPercent = 0;
142 hrc = progress->COMGETTER(OperationPercent(&ulCurrentOperationPercent));
143 if (FAILED(hrc))
144 break;
145
146 if (ulLastOperation != ulOperation)
147 {
148 hrc = progress->COMGETTER(OperationDescription(bstrOperationDescription.asOutParam()));
149 if (FAILED(hrc))
150 break;
151 ulLastPercent = (ULONG)-1; // force print
152 ulLastOperation = ulOperation;
153 }
154
155 if ( ulCurrentPercent != ulLastPercent
156 || ulCurrentOperationPercent != ulLastOperationPercent
157 )
158 {
159 LONG lSecsRem = 0;
160 progress->COMGETTER(TimeRemaining)(&lSecsRem);
161
162 RTStrmPrintf(g_pStdErr, "(%u/%u) %ls %02u%% => %02u%% (%d s remaining)\n", ulOperation + 1, cOperations,
163 bstrOperationDescription.raw(), ulCurrentOperationPercent, ulCurrentPercent, lSecsRem);
164 ulLastPercent = ulCurrentPercent;
165 ulLastOperationPercent = ulCurrentOperationPercent;
166 }
167 }
168 else
169 {
170 /* did we cross a 10% mark? */
171 if (ulCurrentPercent / 10 > ulLastPercent / 10)
172 {
173 /* make sure to also print out missed steps */
174 for (ULONG curVal = (ulLastPercent / 10) * 10 + 10; curVal <= (ulCurrentPercent / 10) * 10; curVal += 10)
175 {
176 if (curVal < 100)
177 {
178 RTStrmPrintf(g_pStdErr, "%u%%...", curVal);
179 RTStrmFlush(g_pStdErr);
180 }
181 }
182 ulLastPercent = (ulCurrentPercent / 10) * 10;
183 }
184 }
185 if (fCompleted)
186 break;
187
188 /* process async cancelation */
189 if (g_fCanceled && !fCanceledAlready)
190 {
191 hrc = progress->Cancel();
192 if (SUCCEEDED(hrc))
193 fCanceledAlready = true;
194 else
195 g_fCanceled = false;
196 }
197
198 /* make sure the loop is not too tight */
199 progress->WaitForCompletion(100);
200
201 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
202 hrc = progress->COMGETTER(Completed(&fCompleted));
203 }
204
205 /* undo signal handling */
206 if (fCancelable)
207 {
208 signal(SIGINT, SIG_DFL);
209#ifdef SIGBREAK
210 signal(SIGBREAK, SIG_DFL);
211#endif
212 }
213
214 /* complete the line. */
215 LONG iRc = E_FAIL;
216 hrc = progress->COMGETTER(ResultCode)(&iRc);
217 if (SUCCEEDED(hrc))
218 {
219 if (SUCCEEDED(iRc))
220 RTStrmPrintf(g_pStdErr, "100%%\n");
221 else if (g_fCanceled)
222 RTStrmPrintf(g_pStdErr, "CANCELED\n");
223 else
224 {
225 if (!g_fDetailedProgress)
226 RTStrmPrintf(g_pStdErr, "\n");
227 RTStrmPrintf(g_pStdErr, "Progress state: %Rhrc\n", iRc);
228 }
229 hrc = iRc;
230 }
231 else
232 {
233 if (!g_fDetailedProgress)
234 RTStrmPrintf(g_pStdErr, "\n");
235 RTStrmPrintf(g_pStdErr, "Progress object failure: %Rhrc\n", hrc);
236 }
237 RTStrmFlush(g_pStdErr);
238 return hrc;
239}
240
241#ifdef RT_OS_WINDOWS
242// Required for ATL
243static CComModule _Module;
244#endif
245
246#endif /* !VBOX_ONLY_DOCS */
247
248
249#ifndef VBOX_ONLY_DOCS
250RTEXITCODE readPasswordFile(const char *pszFilename, com::Utf8Str *pPasswd)
251{
252 size_t cbFile;
253 char szPasswd[512];
254 int vrc = VINF_SUCCESS;
255 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
256 bool fStdIn = !strcmp(pszFilename, "stdin");
257 PRTSTREAM pStrm;
258 if (!fStdIn)
259 vrc = RTStrmOpen(pszFilename, "r", &pStrm);
260 else
261 pStrm = g_pStdIn;
262 if (RT_SUCCESS(vrc))
263 {
264 vrc = RTStrmReadEx(pStrm, szPasswd, sizeof(szPasswd)-1, &cbFile);
265 if (RT_SUCCESS(vrc))
266 {
267 if (cbFile >= sizeof(szPasswd)-1)
268 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Provided password in file '%s' is too long", pszFilename);
269 else
270 {
271 unsigned i;
272 for (i = 0; i < cbFile && !RT_C_IS_CNTRL(szPasswd[i]); i++)
273 ;
274 szPasswd[i] = '\0';
275 *pPasswd = szPasswd;
276 }
277 }
278 else
279 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot read password from file '%s': %Rrc", pszFilename, vrc);
280 if (!fStdIn)
281 RTStrmClose(pStrm);
282 }
283 else
284 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open password file '%s' (%Rrc)", pszFilename, vrc);
285
286 return rcExit;
287}
288
289static RTEXITCODE settingsPasswordFile(ComPtr<IVirtualBox> virtualBox, const char *pszFilename)
290{
291 com::Utf8Str passwd;
292 RTEXITCODE rcExit = readPasswordFile(pszFilename, &passwd);
293 if (rcExit == RTEXITCODE_SUCCESS)
294 {
295 int rc;
296 CHECK_ERROR(virtualBox, SetSettingsSecret(com::Bstr(passwd).raw()));
297 if (FAILED(rc))
298 rcExit = RTEXITCODE_FAILURE;
299 }
300
301 return rcExit;
302}
303#endif
304
305int main(int argc, char *argv[])
306{
307 /*
308 * Before we do anything, init the runtime without loading
309 * the support driver.
310 */
311 RTR3InitExe(argc, &argv, 0);
312
313 /*
314 * Parse the global options
315 */
316 bool fShowLogo = false;
317 bool fShowHelp = false;
318 int iCmd = 1;
319 int iCmdArg;
320 const char *g_pszSettingsPw = NULL;
321 const char *g_pszSettingsPwFile = NULL;
322
323 for (int i = 1; i < argc || argc <= iCmd; i++)
324 {
325 if ( argc <= iCmd
326 || !strcmp(argv[i], "help")
327 || !strcmp(argv[i], "-?")
328 || !strcmp(argv[i], "-h")
329 || !strcmp(argv[i], "-help")
330 || !strcmp(argv[i], "--help"))
331 {
332 if (i >= argc - 1)
333 {
334 showLogo(g_pStdOut);
335 printUsage(USAGE_ALL, g_pStdOut);
336 return 0;
337 }
338 fShowLogo = true;
339 fShowHelp = true;
340 iCmd++;
341 continue;
342 }
343
344 if ( !strcmp(argv[i], "-v")
345 || !strcmp(argv[i], "-version")
346 || !strcmp(argv[i], "-Version")
347 || !strcmp(argv[i], "--version"))
348 {
349 /* Print version number, and do nothing else. */
350 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
351 return 0;
352 }
353
354 if ( !strcmp(argv[i], "--dumpopts")
355 || !strcmp(argv[i], "-dumpopts"))
356 {
357 /* Special option to dump really all commands,
358 * even the ones not understood on this platform. */
359 printUsage(USAGE_DUMPOPTS, g_pStdOut);
360 return 0;
361 }
362
363 if ( !strcmp(argv[i], "--nologo")
364 || !strcmp(argv[i], "-nologo")
365 || !strcmp(argv[i], "-q"))
366 {
367 /* suppress the logo */
368 fShowLogo = false;
369 iCmd++;
370 }
371 else if ( !strcmp(argv[i], "--detailed-progress")
372 || !strcmp(argv[i], "-d"))
373 {
374 /* detailed progress report */
375 g_fDetailedProgress = true;
376 iCmd++;
377 }
378 else if (!strcmp(argv[i], "--settingspw"))
379 {
380 if (i >= argc-1)
381 return RTMsgErrorExit(RTEXITCODE_FAILURE,
382 "Password expected");
383 /* password for certain settings */
384 g_pszSettingsPw = argv[i+1];
385 iCmd += 2;
386 }
387 else if (!strcmp(argv[i], "--settingspwfile"))
388 {
389 if (i >= argc-1)
390 return RTMsgErrorExit(RTEXITCODE_FAILURE,
391 "No password file specified");
392 g_pszSettingsPwFile = argv[i+1];
393 iCmd += 2;
394 }
395 else
396 break;
397 }
398
399 iCmdArg = iCmd + 1;
400
401 if (fShowLogo)
402 showLogo(g_pStdOut);
403
404
405#ifndef VBOX_ONLY_DOCS
406 /*
407 * Initialize COM.
408 */
409 using namespace com;
410 HRESULT hrc = com::Initialize();
411# ifdef VBOX_WITH_XPCOM
412 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
413 {
414 char szHome[RTPATH_MAX] = "";
415 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
416 return RTMsgErrorExit(RTEXITCODE_FAILURE,
417 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
418 }
419# endif
420 if (FAILED(hrc))
421 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM!");
422
423 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
424 do
425 {
426 ///////////////////////////////////////////////////////////////////////////
427 // scopes all the stuff till shutdown
428 /*
429 * convertfromraw: does not need a VirtualBox instantiation.
430 */
431 if (argc >= iCmdArg && ( !strcmp(argv[iCmd], "convertfromraw")
432 || !strcmp(argv[iCmd], "convertdd")))
433 {
434 rcExit = handleConvertFromRaw(argc - iCmdArg, argv + iCmdArg);
435 break;
436 }
437
438 /*
439 * Get the remote VirtualBox object and create a local session object.
440 */
441 ComPtr<IVirtualBox> virtualBox;
442 ComPtr<ISession> session;
443
444 hrc = virtualBox.createLocalObject(CLSID_VirtualBox);
445 if (FAILED(hrc))
446 RTMsgError("Failed to create the VirtualBox object!");
447 else
448 {
449 hrc = session.createInprocObject(CLSID_Session);
450 if (FAILED(hrc))
451 RTMsgError("Failed to create a session object!");
452 }
453 if (FAILED(hrc))
454 {
455 com::ErrorInfo info;
456 if (!info.isFullAvailable() && !info.isBasicAvailable())
457 {
458 com::GluePrintRCMessage(hrc);
459 RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
460 }
461 else
462 com::GluePrintErrorInfo(info);
463 break;
464 }
465
466 /*
467 * All registered command handlers
468 */
469 static const struct
470 {
471 const char *command;
472 USAGECATEGORY help;
473 int (*handler)(HandlerArg *a);
474 } s_commandHandlers[] =
475 {
476 { "internalcommands", 0, handleInternalCommands },
477 { "list", USAGE_LIST, handleList },
478 { "showvminfo", USAGE_SHOWVMINFO, handleShowVMInfo },
479 { "registervm", USAGE_REGISTERVM, handleRegisterVM },
480 { "unregistervm", USAGE_UNREGISTERVM, handleUnregisterVM },
481 { "clonevm", USAGE_CLONEVM, handleCloneVM },
482 { "createhd", USAGE_CREATEHD, handleCreateHardDisk },
483 { "createvdi", USAGE_CREATEHD, handleCreateHardDisk }, /* backward compatibility */
484 { "modifyhd", USAGE_MODIFYHD, handleModifyHardDisk },
485 { "modifyvdi", USAGE_MODIFYHD, handleModifyHardDisk }, /* backward compatibility */
486 { "clonehd", USAGE_CLONEHD, handleCloneHardDisk },
487 { "clonevdi", USAGE_CLONEHD, handleCloneHardDisk }, /* backward compatibility */
488 { "createvm", USAGE_CREATEVM, handleCreateVM },
489 { "modifyvm", USAGE_MODIFYVM, handleModifyVM },
490 { "startvm", USAGE_STARTVM, handleStartVM },
491 { "controlvm", USAGE_CONTROLVM, handleControlVM },
492 { "discardstate", USAGE_DISCARDSTATE, handleDiscardState },
493 { "adoptstate", USAGE_ADOPTSTATE, handleAdoptState },
494 { "snapshot", USAGE_SNAPSHOT, handleSnapshot },
495 { "closemedium", USAGE_CLOSEMEDIUM, handleCloseMedium },
496 { "storageattach", USAGE_STORAGEATTACH, handleStorageAttach },
497 { "storagectl", USAGE_STORAGECONTROLLER, handleStorageController },
498 { "showhdinfo", USAGE_SHOWHDINFO, handleShowHardDiskInfo },
499 { "showvdiinfo", USAGE_SHOWHDINFO, handleShowHardDiskInfo }, /* backward compatibility */
500 { "getextradata", USAGE_GETEXTRADATA, handleGetExtraData },
501 { "setextradata", USAGE_SETEXTRADATA, handleSetExtraData },
502 { "setproperty", USAGE_SETPROPERTY, handleSetProperty },
503 { "usbfilter", USAGE_USBFILTER, handleUSBFilter },
504 { "sharedfolder", USAGE_SHAREDFOLDER, handleSharedFolder },
505#ifdef VBOX_WITH_GUEST_PROPS
506 { "guestproperty", USAGE_GUESTPROPERTY, handleGuestProperty },
507#endif
508#ifdef VBOX_WITH_GUEST_CONTROL
509 { "guestcontrol", USAGE_GUESTCONTROL, handleGuestControl },
510#endif
511 { "metrics", USAGE_METRICS, handleMetrics },
512 { "import", USAGE_IMPORTAPPLIANCE, handleImportAppliance },
513 { "export", USAGE_EXPORTAPPLIANCE, handleExportAppliance },
514#ifdef VBOX_WITH_NETFLT
515 { "hostonlyif", USAGE_HOSTONLYIFS, handleHostonlyIf },
516#endif
517 { "dhcpserver", USAGE_DHCPSERVER, handleDHCPServer},
518#ifdef VBOX_WITH_NAT_SERVICE
519 { "natnetwork", USAGE_NATNETWORK, handleNATNetwork},
520#endif
521 { "extpack", USAGE_EXTPACK, handleExtPack},
522 { "bandwidthctl", USAGE_BANDWIDTHCONTROL, handleBandwidthControl},
523 { "debugvm", USAGE_DEBUGVM, handleDebugVM},
524 { NULL, 0, NULL }
525 };
526
527 if (g_pszSettingsPw)
528 {
529 int rc;
530 CHECK_ERROR(virtualBox, SetSettingsSecret(Bstr(g_pszSettingsPw).raw()));
531 if (FAILED(rc))
532 {
533 rcExit = RTEXITCODE_FAILURE;
534 break;
535 }
536 }
537 else if (g_pszSettingsPwFile)
538 {
539 rcExit = settingsPasswordFile(virtualBox, g_pszSettingsPwFile);
540 if (rcExit != RTEXITCODE_SUCCESS)
541 break;
542 }
543
544 HandlerArg handlerArg = { 0, NULL, virtualBox, session };
545 int commandIndex;
546 for (commandIndex = 0; s_commandHandlers[commandIndex].command != NULL; commandIndex++)
547 {
548 if (!strcmp(s_commandHandlers[commandIndex].command, argv[iCmd]))
549 {
550 handlerArg.argc = argc - iCmdArg;
551 handlerArg.argv = &argv[iCmdArg];
552
553 if ( fShowHelp
554 || ( argc - iCmdArg == 0
555 && s_commandHandlers[commandIndex].help))
556 {
557 printUsage(s_commandHandlers[commandIndex].help, g_pStdOut);
558 rcExit = RTEXITCODE_FAILURE; /* error */
559 }
560 else
561 rcExit = (RTEXITCODE)s_commandHandlers[commandIndex].handler(&handlerArg); /** @todo Change to return RTEXITCODE. */
562 break;
563 }
564 }
565 if (!s_commandHandlers[commandIndex].command)
566 {
567 /* Help topics. */
568 if (fShowHelp && !strcmp(argv[iCmd], "commands"))
569 {
570 RTPrintf("commands:\n");
571 for (unsigned i = 0; i < RT_ELEMENTS(s_commandHandlers) - 1; i++)
572 if ( i == 0 /* skip backwards compatibility entries */
573 || s_commandHandlers[i].help != s_commandHandlers[i - 1].help)
574 RTPrintf(" %s\n", s_commandHandlers[i].command);
575 }
576 else
577 rcExit = errorSyntax(USAGE_ALL, "Invalid command '%s'", Utf8Str(argv[iCmd]).c_str());
578 }
579
580 /* Although all handlers should always close the session if they open it,
581 * we do it here just in case if some of the handlers contains a bug --
582 * leaving the direct session not closed will turn the machine state to
583 * Aborted which may have unwanted side effects like killing the saved
584 * state file (if the machine was in the Saved state before). */
585 session->UnlockMachine();
586
587 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
588
589 // end "all-stuff" scope
590 ///////////////////////////////////////////////////////////////////////////
591 } while (0);
592
593 com::Shutdown();
594
595 return rcExit;
596#else /* VBOX_ONLY_DOCS */
597 return RTEXITCODE_SUCCESS;
598#endif /* VBOX_ONLY_DOCS */
599}
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