diff options
Diffstat (limited to 'src/VBox/Frontends/VBoxManage')
25 files changed, 5392 insertions, 1655 deletions
diff --git a/src/VBox/Frontends/VBoxManage/Makefile.kmk b/src/VBox/Frontends/VBoxManage/Makefile.kmk index 02427bf6..ae335991 100644 --- a/src/VBox/Frontends/VBoxManage/Makefile.kmk +++ b/src/VBox/Frontends/VBoxManage/Makefile.kmk @@ -4,7 +4,7 @@ # # -# Copyright (C) 2006-2012 Oracle Corporation +# Copyright (C) 2006-2013 Oracle Corporation # # This file is part of VirtualBox Open Source Edition (OSE), as # available from http://www.virtualbox.org. This file is free software; @@ -22,8 +22,10 @@ ifdef VBOX_WITH_DOCS PROGRAMS += VBoxManageHelp endif VBoxManageHelp_TEMPLATE = VBoxAdvBldProg -VBoxManageHelp_DEFS += VBOX_ONLY_DOCS -VBoxManageHelp_SOURCES = \ +VBoxManageHelp_DEFS += \ + VBOX_ONLY_DOCS \ + $(if $(VBOX_WITH_GUEST_CONTROL),VBOX_WITH_GUEST_CONTROL) +VBoxManageHelp_SOURCES = \ VBoxManage.cpp \ VBoxManageHelp.cpp \ $(if $(VBOX_WITH_GUEST_PROPS),VBoxManageGuestProp.cpp) \ @@ -34,7 +36,7 @@ ifndef VBOX_ONLY_DOCS endif VBoxManage_TEMPLATE = VBOXMAINCLIENTEXE VBoxManage_DEFS.win = _WIN32_WINNT=0x0500 -VBoxManage_SOURCES = \ +VBoxManage_SOURCES = \ VBoxManage.cpp \ VBoxInternalManage.cpp \ VBoxManageAppliance.cpp \ @@ -44,6 +46,7 @@ VBoxManage_SOURCES = \ VBoxManageDHCPServer.cpp \ VBoxManageDisk.cpp \ $(if $(VBOX_WITH_GUEST_CONTROL),VBoxManageGuestCtrl.cpp) \ + $(if $(VBOX_WITH_GUEST_CONTROL),VBoxManageGuestCtrlListener.cpp) \ $(if $(VBOX_WITH_GUEST_PROPS),VBoxManageGuestProp.cpp) \ VBoxManageHelp.cpp \ VBoxManageHostonly.cpp \ @@ -54,7 +57,9 @@ VBoxManage_SOURCES = \ VBoxManageModifyVM.cpp \ VBoxManageSnapshot.cpp \ VBoxManageStorageController.cpp \ - VBoxManageUSB.cpp + VBoxManageUSB.cpp \ + $(if $(VBOX_WITH_NAT_SERVICE),VBoxManageNATNetwork.cpp,) \ + $(if $(VBOX_WITH_NAT_SERVICE),../../NetworkServices/NetLib/VBoxNetPortForwardString.cpp,) VBoxManage_LIBS += $(LIB_DDU) VBoxManage_DEFS += \ @@ -76,7 +81,12 @@ VBoxManage_DEFS += \ $(if $(VBOX_WITH_VIRTIO),VBOX_WITH_VIRTIO) \ $(if $(VBOX_WITH_USB_CARDREADER),VBOX_WITH_USB_CARDREADER) \ $(if $(VBOX_WITH_PCI_PASSTHROUGH),VBOX_WITH_PCI_PASSTHROUGH) \ - $(if $(VBOX_WITH_VPX),VBOX_WITH_VPX) + $(if $(VBOX_WITH_VPX),VBOX_WITH_VPX) \ + $(if $(VBOX_WITH_NAT_SERVICE),VBOX_WITH_NAT_SERVICE) \ + $(if $(VBOX_WITH_VMSVGA),VBOX_WITH_VMSVGA) + +# VBoxNetPortForwardString.h +VBoxManageNATNetwork.cpp_INCS += ../../NetworkServices/NetLib/ ifneq ($(KBUILD_TARGET),win) # Workaround for buggy gcc-4.3 compilers, see diff --git a/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp b/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp index 5d3161a6..f4033f7b 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp @@ -121,7 +121,7 @@ typedef struct HOSTPARTITION typedef struct HOSTPARTITIONS { /** partitioning type - MBR or GPT */ - PARTITIONING_TYPE uPartitioningType; + VBOXHDDPARTTYPE uPartitioningType; unsigned cPartitions; HOSTPARTITION aPartitions[HOSTPARTITION_MAX]; } HOSTPARTITIONS, *PHOSTPARTITIONS; @@ -145,13 +145,13 @@ void printUsageInternal(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " incompatible ways without warning.\n", (u64Cmd & USAGE_LOADMAP) - ? " loadmap <vmname>|<uuid> <symfile> <address> [module] [subtrahend] [segment]\n" + ? " loadmap <vmname|uuid> <symfile> <address> [module] [subtrahend] [segment]\n" " This will instruct DBGF to load the given map file\n" " during initialization. (See also loadmap in the debugger.)\n" "\n" : "", (u64Cmd & USAGE_LOADSYMS) - ? " loadsyms <vmname>|<uuid> <symfile> [delta] [module] [module address]\n" + ? " loadsyms <vmname|uuid> <symfile> [delta] [module] [module address]\n" " This will instruct DBGF to load the given symbol file\n" " during initialization.\n" "\n" @@ -243,7 +243,7 @@ void printUsageInternal(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) "", #endif (u64Cmd & USAGE_DEBUGLOG) - ? " debuglog <vmname>|<uuid> [--enable|--disable] [--flags todo]\n" + ? " debuglog <vmname|uuid> [--enable|--disable] [--flags todo]\n" " [--groups todo] [--destinations todo]\n" " Controls debug logging.\n" "\n" @@ -254,7 +254,7 @@ void printUsageInternal(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) "\n" : "", (u64Cmd & USAGE_GUESTSTATS) - ? " gueststats <vmname>|<uuid> [--interval <seconds>]\n" + ? " gueststats <vmname|uuid> [--interval <seconds>]\n" " Obtains and prints internal guest statistics.\n" " Sets the update interval if specified.\n" "\n" @@ -672,7 +672,7 @@ static int CmdSetHDUUID(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, } /* Open the image */ - rc = VDOpen(pDisk, pszFormat, argv[1], VD_OPEN_FLAGS_NORMAL, NULL); + rc = VDOpen(pDisk, pszFormat, argv[1], VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_INFO, NULL); if (RT_FAILURE(rc)) { RTMsgError("Cannot open the image: %Rrc", rc); @@ -754,7 +754,7 @@ static int partRead(RTFILE File, PHOSTPARTITIONS pPart) uint64_t lastUsableLBA = 0; int rc; - PARTITIONING_TYPE partitioningType; + VBOXHDDPARTTYPE partitioningType; pPart->cPartitions = 0; memset(pPart->aPartitions, '\0', sizeof(pPart->aPartitions)); @@ -2383,7 +2383,7 @@ int CmdDebugLog(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<I break; default: - return errorGetOpt(USAGE_DEBUGLOG , ch, &ValueUnion); + return errorGetOpt(USAGE_DEBUGLOG, ch, &ValueUnion); } } @@ -2453,7 +2453,7 @@ int CmdGuestStats(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr break; default: - return errorGetOpt(USAGE_GUESTSTATS , ch, &ValueUnion); + return errorGetOpt(USAGE_GUESTSTATS, ch, &ValueUnion); } } @@ -2522,7 +2522,7 @@ int handleInternalCommands(HandlerArg *a) if (!strcmp(pszCmd, "loadsyms")) return CmdLoadSyms(a->argc - 1, &a->argv[1], a->virtualBox, a->session); //if (!strcmp(pszCmd, "unloadsyms")) - // return CmdUnloadSyms(argc - 1 , &a->argv[1]); + // return CmdUnloadSyms(argc - 1, &a->argv[1]); if (!strcmp(pszCmd, "sethduuid") || !strcmp(pszCmd, "sethdparentuuid")) return CmdSetHDUUID(a->argc, &a->argv[0], a->virtualBox, a->session); if (!strcmp(pszCmd, "dumphdinfo")) diff --git a/src/VBox/Frontends/VBoxManage/VBoxManage.cpp b/src/VBox/Frontends/VBoxManage/VBoxManage.cpp index 1d881aac..533b30fc 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManage.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManage.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -26,7 +26,7 @@ # include <VBox/com/array.h> # include <VBox/com/ErrorInfo.h> # include <VBox/com/errorprint.h> -# include <VBox/com/EventQueue.h> +# include <VBox/com/NativeEventQueue.h> # include <VBox/com/VirtualBox.h> #endif /* !VBOX_ONLY_DOCS */ @@ -90,7 +90,7 @@ HRESULT showProgress(ComPtr<IProgress> progress) ULONG ulLastOperation = (ULONG)-1; Bstr bstrOperationDescription; - EventQueue::getMainEventQueue()->processEventQueue(0); + NativeEventQueue::getMainEventQueue()->processEventQueue(0); ULONG cOperations = 1; HRESULT hrc = progress->COMGETTER(OperationCount)(&cOperations); @@ -159,7 +159,8 @@ HRESULT showProgress(ComPtr<IProgress> progress) LONG lSecsRem = 0; progress->COMGETTER(TimeRemaining)(&lSecsRem); - RTStrmPrintf(g_pStdErr, "(%u/%u) %ls %02u%% => %02u%% (%d s remaining)\n", ulOperation + 1, cOperations, bstrOperationDescription.raw(), ulCurrentOperationPercent, ulCurrentPercent, lSecsRem); + RTStrmPrintf(g_pStdErr, "(%u/%u) %ls %02u%% => %02u%% (%d s remaining)\n", ulOperation + 1, cOperations, + bstrOperationDescription.raw(), ulCurrentOperationPercent, ulCurrentPercent, lSecsRem); ulLastPercent = ulCurrentPercent; ulLastOperationPercent = ulCurrentOperationPercent; } @@ -197,7 +198,7 @@ HRESULT showProgress(ComPtr<IProgress> progress) /* make sure the loop is not too tight */ progress->WaitForCompletion(100); - EventQueue::getMainEventQueue()->processEventQueue(0); + NativeEventQueue::getMainEventQueue()->processEventQueue(0); hrc = progress->COMGETTER(Completed(&fCompleted)); } @@ -331,7 +332,7 @@ int main(int argc, char *argv[]) if (i >= argc - 1) { showLogo(g_pStdOut); - printUsage(USAGE_ALL, g_pStdOut); + printUsage(USAGE_ALL, ~0U, g_pStdOut); return 0; } fShowLogo = true; @@ -355,7 +356,7 @@ int main(int argc, char *argv[]) { /* Special option to dump really all commands, * even the ones not understood on this platform. */ - printUsage(USAGE_DUMPOPTS, g_pStdOut); + printUsage(USAGE_DUMPOPTS, ~0U, g_pStdOut); return 0; } @@ -514,6 +515,9 @@ int main(int argc, char *argv[]) { "hostonlyif", USAGE_HOSTONLYIFS, handleHostonlyIf }, #endif { "dhcpserver", USAGE_DHCPSERVER, handleDHCPServer}, +#ifdef VBOX_WITH_NAT_SERVICE + { "natnetwork", USAGE_NATNETWORK, handleNATNetwork}, +#endif { "extpack", USAGE_EXTPACK, handleExtPack}, { "bandwidthctl", USAGE_BANDWIDTHCONTROL, handleBandwidthControl}, { "debugvm", USAGE_DEBUGVM, handleDebugVM}, @@ -550,7 +554,7 @@ int main(int argc, char *argv[]) || ( argc - iCmdArg == 0 && s_commandHandlers[commandIndex].help)) { - printUsage(s_commandHandlers[commandIndex].help, g_pStdOut); + printUsage(s_commandHandlers[commandIndex].help, ~0U, g_pStdOut); rcExit = RTEXITCODE_FAILURE; /* error */ } else @@ -580,7 +584,7 @@ int main(int argc, char *argv[]) * state file (if the machine was in the Saved state before). */ session->UnlockMachine(); - EventQueue::getMainEventQueue()->processEventQueue(0); + NativeEventQueue::getMainEventQueue()->processEventQueue(0); // end "all-stuff" scope /////////////////////////////////////////////////////////////////////////// diff --git a/src/VBox/Frontends/VBoxManage/VBoxManage.h b/src/VBox/Frontends/VBoxManage/VBoxManage.h index e8f4214b..f2c23ee4 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManage.h +++ b/src/VBox/Frontends/VBoxManage/VBoxManage.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -29,6 +29,7 @@ #include <iprt/types.h> #include <iprt/message.h> #include <iprt/stream.h> +#include <iprt/getopt.h> //////////////////////////////////////////////////////////////////////////////// // @@ -101,9 +102,28 @@ #define USAGE_BANDWIDTHCONTROL RT_BIT_64(56) #define USAGE_GUESTSTATS RT_BIT_64(57) #define USAGE_REPAIRHD RT_BIT_64(58) +#define USAGE_NATNETWORK RT_BIT_64(59) #define USAGE_ALL (~(uint64_t)0) /** @} */ +#ifdef VBOX_WITH_GUEST_CONTROL +# define USAGE_GSTCTRL_EXEC RT_BIT(0) +# define USAGE_GSTCTRL_COPYFROM RT_BIT(1) +# define USAGE_GSTCTRL_COPYTO RT_BIT(2) +# define USAGE_GSTCTRL_CREATEDIR RT_BIT(3) +# define USAGE_GSTCTRL_REMOVEDIR RT_BIT(4) +# define USAGE_GSTCTRL_REMOVEFILE RT_BIT(5) +# define USAGE_GSTCTRL_RENAME RT_BIT(6) +# define USAGE_GSTCTRL_CREATETEMP RT_BIT(7) +# define USAGE_GSTCTRL_LIST RT_BIT(8) +# define USAGE_GSTCTRL_PROCESS RT_BIT(9) +# define USAGE_GSTCTRL_KILL RT_BIT(10) +# define USAGE_GSTCTRL_SESSION RT_BIT(11) +# define USAGE_GSTCTRL_STAT RT_BIT(12) +# define USAGE_GSTCTRL_UPDATEADDS RT_BIT(13) +# define USAGE_GSTCTRL_WATCH RT_BIT(14) +#endif + typedef uint64_t USAGECATEGORY; /** command handler argument */ @@ -146,12 +166,14 @@ extern bool g_fDetailedProgress; // in VBoxManage.cpp //////////////////////////////////////////////////////////////////////////////// /* VBoxManageHelp.cpp */ -void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm); -RTEXITCODE errorSyntax(USAGECATEGORY u64Cmd, const char *pszFormat, ...); -RTEXITCODE errorGetOpt(USAGECATEGORY u64Cmd, int rc, union RTGETOPTUNION const *pValueUnion); +void printUsage(USAGECATEGORY fCategory, uint32_t fSubCategory, PRTSTREAM pStrm); +RTEXITCODE errorSyntax(USAGECATEGORY fCategory, const char *pszFormat, ...); +RTEXITCODE errorSyntaxEx(USAGECATEGORY fCategory, uint32_t fSubCategory, const char *pszFormat, ...); +RTEXITCODE errorGetOpt(USAGECATEGORY fCategory, int rc, union RTGETOPTUNION const *pValueUnion); +RTEXITCODE errorGetOptEx(USAGECATEGORY fCategory, uint32_t fSubCategory, int rc, union RTGETOPTUNION const *pValueUnion); RTEXITCODE errorArgument(const char *pszFormat, ...); -void printUsageInternal(USAGECATEGORY u64Cmd, PRTSTREAM pStrm); +void printUsageInternal(USAGECATEGORY fCategory, PRTSTREAM pStrm); #ifndef VBOX_ONLY_DOCS HRESULT showProgress(ComPtr<IProgress> progress); @@ -185,7 +207,7 @@ int handleDebugVM(HandlerArg *a); extern void usageGuestProperty(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2); /* VBoxManageGuestCtrl.cpp */ -extern void usageGuestControl(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2); +extern void usageGuestControl(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2, uint32_t fSubCategory); #ifndef VBOX_ONLY_DOCS /* VBoxManageGuestProp.cpp */ @@ -198,13 +220,13 @@ extern int handleGuestControl(HandlerArg *a); HRESULT showSnapshots(ComPtr<ISnapshot> &rootSnapshot, ComPtr<ISnapshot> ¤tSnapshot, VMINFO_DETAILS details, - const com::Bstr &prefix = "", + const com::Utf8Str &prefix = "", int level = 0); int handleShowVMInfo(HandlerArg *a); HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, ComPtr<IMachine> machine, VMINFO_DETAILS details = VMINFO_NONE, - ComPtr <IConsole> console = ComPtr<IConsole>()); + ComPtr<IConsole> console = ComPtr<IConsole>()); const char *machineStateToName(MachineState_T machineState, bool fShort); HRESULT showBandwidthGroups(ComPtr<IBandwidthControl> &bwCtrl, VMINFO_DETAILS details); @@ -230,17 +252,18 @@ int handleSharedFolder(HandlerArg *a); int handleExtPack(HandlerArg *a); /* VBoxManageDisk.cpp */ -HRESULT findMedium(HandlerArg *a, const char *pszFilenameOrUuid, - DeviceType_T enmDevType, bool fSilent, - ComPtr<IMedium> &pMedium); -HRESULT findOrOpenMedium(HandlerArg *a, const char *pszFilenameOrUuid, - DeviceType_T enmDevType, AccessMode_T enmAccessMode, - ComPtr<IMedium> &pMedium, bool fForceNewUuidOnOpen, - bool *pfWasUnknown); +HRESULT openMedium(HandlerArg *a, const char *pszFilenameOrUuid, + DeviceType_T enmDevType, AccessMode_T enmAccessMode, + ComPtr<IMedium> &pMedium, bool fForceNewUuidOnOpen, + bool fSilent); int handleCreateHardDisk(HandlerArg *a); int handleModifyHardDisk(HandlerArg *a); int handleCloneHardDisk(HandlerArg *a); RTEXITCODE handleConvertFromRaw(int argc, char *argv[]); +HRESULT showMediumInfo(const ComPtr<IVirtualBox> &pVirtualBox, + const ComPtr<IMedium> &pMedium, + const char *pszParentUUID, + bool fOptLong); int handleShowHardDiskInfo(HandlerArg *a); int handleCloseMedium(HandlerArg *a); int parseDiskType(const char *psz, MediumType_T *pDiskType); @@ -263,9 +286,13 @@ int handleUSBFilter(HandlerArg *a); /* VBoxManageHostonly.cpp */ int handleHostonlyIf(HandlerArg *a); -/* VBoxManageHostonly.cpp */ +/* VBoxManageDHCPServer.cpp */ int handleDHCPServer(HandlerArg *a); +/* VBoxManageNATNetwork.cpp */ +int handleNATNetwork(HandlerArg *a); + + /* VBoxManageBandwidthControl.cpp */ int handleBandwidthControl(HandlerArg *a); diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp index 76c8be19..279bd447 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2010 Oracle Corporation + * Copyright (C) 2009-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -27,8 +27,6 @@ #include <VBox/com/array.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> -#include <VBox/com/EventQueue.h> - #include <VBox/com/VirtualBox.h> #include <list> @@ -376,13 +374,13 @@ int handleImportAppliance(HandlerArg *arg) com::SafeArray<VirtualSystemDescriptionType_T> retTypes; com::SafeArray<BSTR> aRefs; com::SafeArray<BSTR> aOvfValues; - com::SafeArray<BSTR> aVboxValues; + com::SafeArray<BSTR> aVBoxValues; com::SafeArray<BSTR> aExtraConfigValues; CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i], GetDescription(ComSafeArrayAsOutParam(retTypes), ComSafeArrayAsOutParam(aRefs), ComSafeArrayAsOutParam(aOvfValues), - ComSafeArrayAsOutParam(aVboxValues), + ComSafeArrayAsOutParam(aVBoxValues), ComSafeArrayAsOutParam(aExtraConfigValues))); RTPrintf("Virtual system %u:\n", i); @@ -403,7 +401,7 @@ int handleImportAppliance(HandlerArg *arg) Utf8Str strOverride; - Bstr bstrFinalValue = aVboxValues[a]; + Bstr bstrFinalValue = aVBoxValues[a]; bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a]; @@ -439,27 +437,27 @@ int handleImportAppliance(HandlerArg *arg) case VirtualSystemDescriptionType_Product: RTPrintf("%2u: Product (ignored): %ls\n", - a, aVboxValues[a]); + a, aVBoxValues[a]); break; case VirtualSystemDescriptionType_ProductUrl: RTPrintf("%2u: ProductUrl (ignored): %ls\n", - a, aVboxValues[a]); + a, aVBoxValues[a]); break; case VirtualSystemDescriptionType_Vendor: RTPrintf("%2u: Vendor (ignored): %ls\n", - a, aVboxValues[a]); + a, aVBoxValues[a]); break; case VirtualSystemDescriptionType_VendorUrl: RTPrintf("%2u: VendorUrl (ignored): %ls\n", - a, aVboxValues[a]); + a, aVBoxValues[a]); break; case VirtualSystemDescriptionType_Version: RTPrintf("%2u: Version (ignored): %ls\n", - a, aVboxValues[a]); + a, aVBoxValues[a]); break; case VirtualSystemDescriptionType_Description: @@ -552,14 +550,14 @@ int handleImportAppliance(HandlerArg *arg) { RTPrintf("%2u: IDE controller, type %ls -- disabled\n", a, - aVboxValues[a]); + aVBoxValues[a]); aEnabled[a] = false; } else RTPrintf("%2u: IDE controller, type %ls" "\n (disable with \"--vsys %u --unit %u --ignore\")\n", a, - aVboxValues[a], + aVBoxValues[a], i, a); break; @@ -568,14 +566,14 @@ int handleImportAppliance(HandlerArg *arg) { RTPrintf("%2u: SATA controller, type %ls -- disabled\n", a, - aVboxValues[a]); + aVBoxValues[a]); aEnabled[a] = false; } else RTPrintf("%2u: SATA controller, type %ls" "\n (disable with \"--vsys %u --unit %u --ignore\")\n", a, - aVboxValues[a], + aVBoxValues[a], i, a); break; @@ -584,14 +582,14 @@ int handleImportAppliance(HandlerArg *arg) { RTPrintf("%2u: SAS controller, type %ls -- disabled\n", a, - aVboxValues[a]); + aVBoxValues[a]); aEnabled[a] = false; } else RTPrintf("%2u: SAS controller, type %ls" "\n (disable with \"--vsys %u --unit %u --ignore\")\n", a, - aVboxValues[a], + aVBoxValues[a], i, a); break; @@ -600,7 +598,7 @@ int handleImportAppliance(HandlerArg *arg) { RTPrintf("%2u: SCSI controller, type %ls -- disabled\n", a, - aVboxValues[a]); + aVBoxValues[a]); aEnabled[a] = false; } else @@ -619,7 +617,7 @@ int handleImportAppliance(HandlerArg *arg) "\n (change with \"--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}\";" "\n disable with \"--vsys %u --unit %u --ignore\")\n", a, - aVboxValues[a], + aVBoxValues[a], i, a, i, a); } break; @@ -673,7 +671,7 @@ int handleImportAppliance(HandlerArg *arg) RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n", a, aOvfValues[a], - aVboxValues[a], + aVBoxValues[a], aExtraConfigValues[a]); } #endif @@ -683,7 +681,7 @@ int handleImportAppliance(HandlerArg *arg) "\n disable with \"--vsys %u --unit %u --ignore\")\n", a, aOvfValues[a], - aVboxValues[a], + aVBoxValues[a], aExtraConfigValues[a], i, a, i, a); } @@ -719,7 +717,7 @@ int handleImportAppliance(HandlerArg *arg) RTPrintf("%2u: Network adapter: orig %ls, config %ls, extra %ls\n", // @todo implement once we have a plan for the back-end a, aOvfValues[a], - aVboxValues[a], + aVBoxValues[a], aExtraConfigValues[a]); break; @@ -789,23 +787,67 @@ int handleImportAppliance(HandlerArg *arg) return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; } -static const RTGETOPTDEF g_aExportOptions[] - = { - { "--output", 'o', RTGETOPT_REQ_STRING }, - { "--legacy09", 'l', RTGETOPT_REQ_NOTHING }, - { "--ovf09", 'l', RTGETOPT_REQ_NOTHING }, - { "--ovf10", '1', RTGETOPT_REQ_NOTHING }, - { "--ovf20", '2', RTGETOPT_REQ_NOTHING }, - { "--manifest", 'm', RTGETOPT_REQ_NOTHING }, - { "--vsys", 's', RTGETOPT_REQ_UINT32 }, - { "--product", 'p', RTGETOPT_REQ_STRING }, - { "--producturl", 'P', RTGETOPT_REQ_STRING }, - { "--vendor", 'd', RTGETOPT_REQ_STRING }, - { "--vendorurl", 'D', RTGETOPT_REQ_STRING }, - { "--version", 'v', RTGETOPT_REQ_STRING }, - { "--eula", 'e', RTGETOPT_REQ_STRING }, - { "--eulafile", 'E', RTGETOPT_REQ_STRING }, - }; +static int parseExportOptions(const char *psz, com::SafeArray<ExportOptions_T> *options) +{ + int rc = VINF_SUCCESS; + while (psz && *psz && RT_SUCCESS(rc)) + { + size_t len; + const char *pszComma = strchr(psz, ','); + if (pszComma) + len = pszComma - psz; + else + len = strlen(psz); + if (len > 0) + { + if (!RTStrNICmp(psz, "CreateManifest", len)) + options->push_back(ExportOptions_CreateManifest); + else if (!RTStrNICmp(psz, "manifest", len)) + options->push_back(ExportOptions_CreateManifest); + else if (!RTStrNICmp(psz, "ExportDVDImages", len)) + options->push_back(ExportOptions_ExportDVDImages); + else if (!RTStrNICmp(psz, "iso", len)) + options->push_back(ExportOptions_ExportDVDImages); + else if (!RTStrNICmp(psz, "StripAllMACs", len)) + options->push_back(ExportOptions_StripAllMACs); + else if (!RTStrNICmp(psz, "nomacs", len)) + options->push_back(ExportOptions_StripAllMACs); + else if (!RTStrNICmp(psz, "StripAllNonNATMACs", len)) + options->push_back(ExportOptions_StripAllNonNATMACs); + else if (!RTStrNICmp(psz, "nomacsbutnat", len)) + options->push_back(ExportOptions_StripAllNonNATMACs); + else + rc = VERR_PARSE_ERROR; + } + if (pszComma) + psz += len + 1; + else + psz += len; + } + + return rc; +} + +static const RTGETOPTDEF g_aExportOptions[] = +{ + { "--output", 'o', RTGETOPT_REQ_STRING }, + { "--legacy09", 'l', RTGETOPT_REQ_NOTHING }, + { "--ovf09", 'l', RTGETOPT_REQ_NOTHING }, + { "--ovf10", '1', RTGETOPT_REQ_NOTHING }, + { "--ovf20", '2', RTGETOPT_REQ_NOTHING }, + { "--manifest", 'm', RTGETOPT_REQ_NOTHING }, // obsoleted by --options + { "--iso", 'I', RTGETOPT_REQ_NOTHING }, // obsoleted by --options + { "--vsys", 's', RTGETOPT_REQ_UINT32 }, + { "--product", 'p', RTGETOPT_REQ_STRING }, + { "--producturl", 'P', RTGETOPT_REQ_STRING }, + { "--vendor", 'n', RTGETOPT_REQ_STRING }, + { "--vendorurl", 'N', RTGETOPT_REQ_STRING }, + { "--version", 'v', RTGETOPT_REQ_STRING }, + { "--description", 'd', RTGETOPT_REQ_STRING }, + { "--eula", 'e', RTGETOPT_REQ_STRING }, + { "--eulafile", 'E', RTGETOPT_REQ_STRING }, + { "--options", 'O', RTGETOPT_REQ_STRING }, +}; int handleExportAppliance(HandlerArg *a) { @@ -814,6 +856,8 @@ int handleExportAppliance(HandlerArg *a) Utf8Str strOutputFile; Utf8Str strOvfFormat("ovf-1.0"); // the default export version bool fManifest = false; // the default + bool fExportISOImages = false; // the default + com::SafeArray<ExportOptions_T> options; std::list< ComPtr<IMachine> > llMachines; uint32_t ulCurVsys = (uint32_t)-1; @@ -842,66 +886,81 @@ int handleExportAppliance(HandlerArg *a) break; case 'l': // --legacy09/--ovf09 - strOvfFormat = "ovf-0.9"; - break; + strOvfFormat = "ovf-0.9"; + break; case '1': // --ovf10 - strOvfFormat = "ovf-1.0"; - break; + strOvfFormat = "ovf-1.0"; + break; case '2': // --ovf20 - strOvfFormat = "ovf-2.0"; - break; + strOvfFormat = "ovf-2.0"; + break; + + case 'I': // --iso + fExportISOImages = true; + break; case 'm': // --manifest - fManifest = true; - break; + fManifest = true; + break; case 's': // --vsys - ulCurVsys = ValueUnion.u32; - break; + ulCurVsys = ValueUnion.u32; + break; case 'p': // --product - if (ulCurVsys == (uint32_t)-1) - return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); - mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz; - break; + if (ulCurVsys == (uint32_t)-1) + return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz; + break; case 'P': // --producturl - if (ulCurVsys == (uint32_t)-1) - return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); - mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz; - break; - - case 'd': // --vendor - if (ulCurVsys == (uint32_t)-1) - return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); - mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz; - break; - - case 'D': // --vendorurl - if (ulCurVsys == (uint32_t)-1) - return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); - mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz; - break; + if (ulCurVsys == (uint32_t)-1) + return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz; + break; + + case 'n': // --vendor + if (ulCurVsys == (uint32_t)-1) + return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz; + break; + + case 'N': // --vendorurl + if (ulCurVsys == (uint32_t)-1) + return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz; + break; case 'v': // --version - if (ulCurVsys == (uint32_t)-1) - return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); - mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz; - break; + if (ulCurVsys == (uint32_t)-1) + return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz; + break; + + case 'd': // --description + if (ulCurVsys == (uint32_t)-1) + return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz; + break; case 'e': // --eula - if (ulCurVsys == (uint32_t)-1) - return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); - mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz; - break; + if (ulCurVsys == (uint32_t)-1) + return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz; + break; case 'E': // --eulafile - if (ulCurVsys == (uint32_t)-1) - return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); - mapArgsMapsPerVsys[ulCurVsys]["eulafile"] = ValueUnion.psz; - break; + if (ulCurVsys == (uint32_t)-1) + return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong); + mapArgsMapsPerVsys[ulCurVsys]["eulafile"] = ValueUnion.psz; + break; + + case 'O': // --options + if (RT_FAILURE(parseExportOptions(ValueUnion.psz, &options))) + return errorArgument("Invalid export options '%s'\n", ValueUnion.psz); + break; case VINF_GETOPT_NOT_OPTION: { @@ -976,7 +1035,7 @@ int handleExportAppliance(HandlerArg *a) { ComPtr<IMachine> pMachine = *itM; ComPtr<IVirtualSystemDescription> pVSD; - CHECK_ERROR_BREAK(pMachine, Export(pAppliance, Bstr(pszAbsFilePath).raw(), pVSD.asOutParam())); + CHECK_ERROR_BREAK(pMachine, ExportTo(pAppliance, Bstr(pszAbsFilePath).raw(), pVSD.asOutParam())); // Add additional info to the virtual system description if the user wants so ArgsMap *pmapArgs = NULL; ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i); @@ -1009,6 +1068,10 @@ int handleExportAppliance(HandlerArg *a) pVSD->AddDescription(VirtualSystemDescriptionType_Version, Bstr(itD->second).raw(), Bstr(itD->second).raw()); + else if (itD->first == "description") + pVSD->AddDescription(VirtualSystemDescriptionType_Description, + Bstr(itD->second).raw(), + Bstr(itD->second).raw()); else if (itD->first == "eula") pVSD->AddDescription(VirtualSystemDescriptionType_License, Bstr(itD->second).raw(), @@ -1021,7 +1084,7 @@ int handleExportAppliance(HandlerArg *a) int irc = RTFileReadAll(itD->second.c_str(), &pvFile, &cbFile); if (RT_SUCCESS(irc)) { - Bstr bstrContent((char*)pvFile); + Bstr bstrContent((char*)pvFile, cbFile); pVSD->AddDescription(VirtualSystemDescriptionType_License, bstrContent.raw(), bstrContent.raw()); @@ -1041,9 +1104,15 @@ int handleExportAppliance(HandlerArg *a) if (FAILED(rc)) break; + if (fManifest) + options.push_back(ExportOptions_CreateManifest); + + if (fExportISOImages) + options.push_back(ExportOptions_ExportDVDImages); + ComPtr<IProgress> progress; CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOvfFormat).raw(), - fManifest, + ComSafeArrayAsInParam(options), Bstr(pszAbsFilePath).raw(), progress.asOutParam())); RTStrFree(pszAbsFilePath); diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp index 6e708f9c..2765372b 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -107,6 +107,12 @@ static RTEXITCODE handleBandwidthControlAdd(HandlerArg *a, ComPtr<IBandwidthCont Bstr name(a->argv[2]); + if (name.isEmpty()) + { + errorArgument("Bandwidth group name must not be empty!\n"); + return RTEXITCODE_FAILURE; + } + const char *pszType = NULL; int64_t cMaxBytesPerSec = INT64_MAX; @@ -166,7 +172,7 @@ static RTEXITCODE handleBandwidthControlAdd(HandlerArg *a, ComPtr<IBandwidthCont errorArgument("Invalid bandwidth group type\n"); return RTEXITCODE_FAILURE; } - + CHECK_ERROR2_RET(bwCtrl, CreateBandwidthGroup(name.raw(), enmType, (LONG64)cMaxBytesPerSec), RTEXITCODE_FAILURE); return RTEXITCODE_SUCCESS; @@ -226,7 +232,7 @@ static RTEXITCODE handleBandwidthControlSet(HandlerArg *a, ComPtr<IBandwidthCont } } - + if (cMaxBytesPerSec != INT64_MAX) { ComPtr<IBandwidthGroup> bwGroup; @@ -274,8 +280,11 @@ static RTEXITCODE handleBandwidthControlList(HandlerArg *pArgs, ComPtr<IBandwidt { switch (c) { - case 'M': enmDetails = VMINFO_MACHINEREADABLE; break; - default: return errorGetOpt(USAGE_BANDWIDTHCONTROL, c, &ValueUnion); + case 'M': + enmDetails = VMINFO_MACHINEREADABLE; + break; + default: + return errorGetOpt(USAGE_BANDWIDTHCONTROL, c, &ValueUnion); } } diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp index e75310f1..31982d06 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -25,8 +25,6 @@ #include <VBox/com/array.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> -#include <VBox/com/EventQueue.h> - #include <VBox/com/VirtualBox.h> #include <iprt/ctype.h> @@ -66,7 +64,7 @@ static unsigned parseNum(const char *psz, unsigned cMaxNum, const char *name) unsigned int getMaxNics(IVirtualBox* vbox, IMachine* mach) { - ComPtr <ISystemProperties> info; + ComPtr<ISystemProperties> info; ChipsetType_T aChipset; ULONG NetworkAdapterCount = 0; HRESULT rc; @@ -92,7 +90,7 @@ int handleControlVM(HandlerArg *a) return errorSyntax(USAGE_CONTROLVM, "Not enough parameters"); /* try to find the given machine */ - ComPtr <IMachine> machine; + ComPtr<IMachine> machine; CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(), machine.asOutParam())); if (FAILED(rc)) @@ -283,8 +281,14 @@ int handleControlVM(HandlerArg *a) } else if (!strcmp(a->argv[1], "keyboardputscancode")) { - ComPtr<IKeyboard> keyboard; - CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(keyboard.asOutParam())); + ComPtr<IKeyboard> pKeyboard; + CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(pKeyboard.asOutParam())); + if (!pKeyboard) + { + RTMsgError("Guest not running"); + rc = E_FAIL; + break; + } if (a->argc <= 1 + 1) { @@ -328,7 +332,7 @@ int handleControlVM(HandlerArg *a) /* Send scancodes to the VM. */ com::SafeArray<LONG> saScancodes(llScancodes); ULONG codesStored = 0; - CHECK_ERROR_BREAK(keyboard, PutScancodes(ComSafeArrayAsInParam(saScancodes), + CHECK_ERROR_BREAK(pKeyboard, PutScancodes(ComSafeArrayAsInParam(saScancodes), &codesStored)); if (codesStored < saScancodes.size()) { @@ -620,6 +624,53 @@ int handleControlVM(HandlerArg *a) RTMsgError("The NIC %d is currently disabled and thus its properties can't be changed", n); } } + else if (!strncmp(a->argv[1], "nicpromisc", 10)) + { + /* Get the number of network adapters */ + ULONG NetworkAdapterCount = getMaxNics(a->virtualBox,sessionMachine) ; + unsigned n = parseNum(&a->argv[1][10], NetworkAdapterCount, "NIC"); + if (!n) + { + rc = E_FAIL; + break; + } + if (a->argc <= 2) + { + errorArgument("Missing argument to '%s'", a->argv[1]); + rc = E_FAIL; + break; + } + + /* get the corresponding network adapter */ + ComPtr<INetworkAdapter> adapter; + CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam())); + if (adapter) + { + BOOL fEnabled; + adapter->COMGETTER(Enabled)(&fEnabled); + if (fEnabled) + { + NetworkAdapterPromiscModePolicy_T enmPromiscModePolicy; + if (!strcmp(a->argv[2], "deny")) + enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny; + else if ( !strcmp(a->argv[2], "allow-vms") + || !strcmp(a->argv[2], "allow-network")) + enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork; + else if (!strcmp(a->argv[2], "allow-all")) + enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll; + else + { + errorArgument("Unknown promiscuous mode policy '%s'", a->argv[2]); + rc = E_INVALIDARG; + break; + } + + CHECK_ERROR(adapter, COMSETTER(PromiscModePolicy)(enmPromiscModePolicy)); + } + else + RTMsgError("The NIC %d is currently disabled and thus its promiscuous mode can't be changed", n); + } + } else if (!strncmp(a->argv[1], "nic", 3)) { /* Get the number of network adapters */ @@ -709,6 +760,18 @@ int handleControlVM(HandlerArg *a) CHECK_ERROR_RET(adapter, COMSETTER(GenericDriver)(Bstr(a->argv[3]).raw()), 1); CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), 1); } + else if (!strcmp(a->argv[2], "natnetwork")) + { + if (a->argc <= 3) + { + errorArgument("Missing argument to '%s'", a->argv[2]); + rc = E_FAIL; + break; + } + CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1); + CHECK_ERROR_RET(adapter, COMSETTER(NATNetwork)(Bstr(a->argv[3]).raw()), 1); + CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_NATNetwork), 1); + } /** @todo obsolete, remove eventually */ else if (!strcmp(a->argv[2], "vde")) { @@ -873,16 +936,18 @@ int handleControlVM(HandlerArg *a) bool attach = !strcmp(a->argv[1], "usbattach"); Bstr usbId = a->argv[2]; - if (Guid(usbId).isEmpty()) + + Guid guid(usbId); + if (!guid.isValid()) { // assume address if (attach) { - ComPtr <IHost> host; + ComPtr<IHost> host; CHECK_ERROR_BREAK(a->virtualBox, COMGETTER(Host)(host.asOutParam())); SafeIfaceArray <IHostUSBDevice> coll; CHECK_ERROR_BREAK(host, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll))); - ComPtr <IHostUSBDevice> dev; + ComPtr<IHostUSBDevice> dev; CHECK_ERROR_BREAK(host, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(), dev.asOutParam())); CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam())); @@ -891,18 +956,24 @@ int handleControlVM(HandlerArg *a) { SafeIfaceArray <IUSBDevice> coll; CHECK_ERROR_BREAK(console, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll))); - ComPtr <IUSBDevice> dev; + ComPtr<IUSBDevice> dev; CHECK_ERROR_BREAK(console, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(), dev.asOutParam())); CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam())); } } + else if (guid.isZero()) + { + errorArgument("Zero UUID argument '%s'", a->argv[2]); + rc = E_FAIL; + break; + } if (attach) CHECK_ERROR_BREAK(console, AttachUSBDevice(usbId.raw())); else { - ComPtr <IUSBDevice> dev; + ComPtr<IUSBDevice> dev; CHECK_ERROR_BREAK(console, DetachUSBDevice(usbId.raw(), dev.asOutParam())); } @@ -943,11 +1014,17 @@ int handleControlVM(HandlerArg *a) fChangeOrigin = true; } - ComPtr<IDisplay> display; - CHECK_ERROR_BREAK(console, COMGETTER(Display)(display.asOutParam())); - CHECK_ERROR_BREAK(display, SetVideoModeHint(uDisplayIdx, fEnabled, - fChangeOrigin, iOriginX, iOriginY, - uXRes, uYRes, uBpp)); + ComPtr<IDisplay> pDisplay; + CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam())); + if (!pDisplay) + { + RTMsgError("Guest not running"); + rc = E_FAIL; + break; + } + CHECK_ERROR_BREAK(pDisplay, SetVideoModeHint(uDisplayIdx, fEnabled, + fChangeOrigin, iOriginX, iOriginY, + uXRes, uYRes, uBpp)); } else if (!strcmp(a->argv[1], "setcredentials")) { @@ -993,12 +1070,18 @@ int handleControlVM(HandlerArg *a) domain = a->argv[5]; } - ComPtr<IGuest> guest; - CHECK_ERROR_BREAK(console, COMGETTER(Guest)(guest.asOutParam())); - CHECK_ERROR_BREAK(guest, SetCredentials(Bstr(a->argv[2]).raw(), - Bstr(passwd).raw(), - Bstr(domain).raw(), - fAllowLocalLogon)); + ComPtr<IGuest> pGuest; + CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pGuest.asOutParam())); + if (!pGuest) + { + RTMsgError("Guest not running"); + rc = E_FAIL; + break; + } + CHECK_ERROR_BREAK(pGuest, SetCredentials(Bstr(a->argv[2]).raw(), + Bstr(passwd).raw(), + Bstr(domain).raw(), + fAllowLocalLogon)); } #if 0 /* TODO: review & remove */ else if (!strcmp(a->argv[1], "dvdattach")) @@ -1140,10 +1223,18 @@ int handleControlVM(HandlerArg *a) break; } /* guest is running; update IGuest */ - ComPtr <IGuest> guest; - rc = console->COMGETTER(Guest)(guest.asOutParam()); + ComPtr<IGuest> pGuest; + rc = console->COMGETTER(Guest)(pGuest.asOutParam()); if (SUCCEEDED(rc)) - CHECK_ERROR(guest, COMSETTER(MemoryBalloonSize)(uVal)); + { + if (!pGuest) + { + RTMsgError("Guest not running"); + rc = E_FAIL; + break; + } + CHECK_ERROR(pGuest, COMSETTER(MemoryBalloonSize)(uVal)); + } } else if (!strcmp(a->argv[1], "teleport")) { @@ -1219,10 +1310,10 @@ int handleControlVM(HandlerArg *a) break; } int vrc; - uint32_t displayIdx = 0; + uint32_t iScreen = 0; if (a->argc == 4) { - vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &displayIdx); + vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &iScreen); if (vrc != VINF_SUCCESS) { errorArgument("Error parsing display number '%s'", a->argv[3]); @@ -1232,10 +1323,17 @@ int handleControlVM(HandlerArg *a) } ComPtr<IDisplay> pDisplay; CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam())); + if (!pDisplay) + { + RTMsgError("Guest not running"); + rc = E_FAIL; + break; + } ULONG width, height, bpp; - CHECK_ERROR_BREAK(pDisplay, GetScreenResolution(displayIdx, &width, &height, &bpp)); + LONG xOrigin, yOrigin; + CHECK_ERROR_BREAK(pDisplay, GetScreenResolution(iScreen, &width, &height, &bpp, &xOrigin, &yOrigin)); com::SafeArray<BYTE> saScreenshot; - CHECK_ERROR_BREAK(pDisplay, TakeScreenShotPNGToArray(displayIdx, width, height, ComSafeArrayAsOutParam(saScreenshot))); + CHECK_ERROR_BREAK(pDisplay, TakeScreenShotPNGToArray(iScreen, width, height, ComSafeArrayAsOutParam(saScreenshot))); RTFILE pngFile = NIL_RTFILE; vrc = RTFileOpen(&pngFile, a->argv[2], RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_ALL); if (RT_FAILURE(vrc)) @@ -1252,6 +1350,127 @@ int handleControlVM(HandlerArg *a) } RTFileClose(pngFile); } + else if ( !strcmp(a->argv[1], "vcpenabled")) + { + if (a->argc != 3) + { + errorArgument("Missing argument to '%s'", a->argv[1]); + rc = E_FAIL; + break; + } + if (!strcmp(a->argv[2], "on")) + { + CHECK_ERROR_RET(sessionMachine, COMSETTER(VideoCaptureEnabled)(TRUE), 1); + } + else if (!strcmp(a->argv[2], "off")) + { + CHECK_ERROR_RET(sessionMachine, COMSETTER(VideoCaptureEnabled)(FALSE), 1); + } + else + { + errorArgument("Invalid state '%s'", Utf8Str(a->argv[2]).c_str()); + rc = E_FAIL; + break; + } + } + else if ( !strcmp(a->argv[1], "videocapturescreens")) + { + ULONG cMonitors = 64; + CHECK_ERROR_BREAK(machine, COMGETTER(MonitorCount)(&cMonitors)); + com::SafeArray<BOOL> saScreens(cMonitors); + if ( a->argc == 3 + && !strcmp(a->argv[2], "all")) + { + /* enable all screens */ + for (unsigned i = 0; i < cMonitors; i++) + saScreens[i] = true; + } + else if ( a->argc == 3 + && !strcmp(a->argv[2], "none")) + { + /* disable all screens */ + for (unsigned i = 0; i < cMonitors; i++) + saScreens[i] = false; + } + else + { + /* enable selected screens */ + for (unsigned i = 0; i < cMonitors; i++) + saScreens[i] = false; + for (int i = 2; SUCCEEDED(rc) && i < a->argc; i++) + { + uint32_t iScreen; + int vrc = RTStrToUInt32Ex(a->argv[i], NULL, 0, &iScreen); + if (vrc != VINF_SUCCESS) + { + errorArgument("Error parsing display number '%s'", a->argv[i]); + rc = E_FAIL; + break; + } + if (iScreen >= cMonitors) + { + errorArgument("Invalid screen ID specified '%u'", iScreen); + rc = E_FAIL; + break; + } + saScreens[iScreen] = true; + } + } + + CHECK_ERROR_BREAK(sessionMachine, COMSETTER(VideoCaptureScreens)(ComSafeArrayAsInParam(saScreens))); + } + else if (!strcmp(a->argv[1], "webcam")) + { + if (a->argc < 3) + { + errorArgument("Missing argument to '%s'", a->argv[1]); + rc = E_FAIL; + break; + } + + ComPtr<IEmulatedUSB> pEmulatedUSB; + CHECK_ERROR_BREAK(console, COMGETTER(EmulatedUSB)(pEmulatedUSB.asOutParam())); + if (!pEmulatedUSB) + { + RTMsgError("Guest not running"); + rc = E_FAIL; + break; + } + + if (!strcmp(a->argv[2], "attach")) + { + Bstr path(""); + if (a->argc >= 4) + path = a->argv[3]; + Bstr settings(""); + if (a->argc >= 5) + settings = a->argv[4]; + CHECK_ERROR_BREAK(pEmulatedUSB, WebcamAttach(path.raw(), settings.raw())); + } + else if (!strcmp(a->argv[2], "detach")) + { + Bstr path(""); + if (a->argc >= 4) + path = a->argv[3]; + CHECK_ERROR_BREAK(pEmulatedUSB, WebcamDetach(path.raw())); + } + else if (!strcmp(a->argv[2], "list")) + { + com::SafeArray <BSTR> webcams; + CHECK_ERROR_BREAK(pEmulatedUSB, COMGETTER(Webcams)(ComSafeArrayAsOutParam(webcams))); + for (size_t i = 0; i < webcams.size(); ++i) + { + RTPrintf("%ls\n", webcams[i][0]? webcams[i]: Bstr("default").raw()); + } + } + else + { + errorArgument("Invalid argument to '%s'", a->argv[1]); + rc = E_FAIL; + break; + } + + } else { errorSyntax(USAGE_CONTROLVM, "Invalid parameter '%s'", a->argv[1]); diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp index 68ef44fc..e680cfe0 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp @@ -23,8 +23,6 @@ #include <VBox/com/array.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> -#include <VBox/com/EventQueue.h> - #include <VBox/com/VirtualBox.h> #endif /* !VBOX_ONLY_DOCS */ @@ -41,6 +39,10 @@ #include "VBoxManage.h" +#include <string> +#include <vector> +#include <map> + #ifndef VBOX_ONLY_DOCS using namespace com; @@ -51,6 +53,37 @@ typedef enum enMainOpCodes OP_MODIFY } OPCODE; +typedef std::map<DhcpOpt_T, std::string> DhcpOptMap; +typedef DhcpOptMap::iterator DhcpOptIterator; +typedef DhcpOptMap::value_type DhcpOptValuePair; + +struct VmNameSlotKey; +typedef struct VmNameSlotKey VmNameSlotKey; + +struct VmNameSlotKey +{ + std::string VmName; + uint8_t u8Slot; + + VmNameSlotKey(std::string aVmName, uint8_t aSlot) : + VmName(aVmName), + u8Slot(aSlot) {} + + bool operator< (const VmNameSlotKey& that) const + { + if (VmName == that.VmName) + return u8Slot < that.u8Slot; + else + return VmName < that.VmName; + } +}; + +typedef std::map<VmNameSlotKey, DhcpOptMap> VmSlot2OptionsM; +typedef VmSlot2OptionsM::iterator VmSlot2OptionsIterator; +typedef VmSlot2OptionsM::value_type VmSlot2OptionsPair; + +typedef std::vector<VmNameSlotKey> VmConfigs; + static const RTGETOPTDEF g_aDHCPIPOptions[] = { { "--netname", 't', RTGETOPT_REQ_STRING }, /* we use 't' instead of 'n' to avoid @@ -73,7 +106,13 @@ static const RTGETOPTDEF g_aDHCPIPOptions[] { "--enable", 'e', RTGETOPT_REQ_NOTHING }, { "-enable", 'e', RTGETOPT_REQ_NOTHING }, // deprecated { "--disable", 'd', RTGETOPT_REQ_NOTHING }, - { "-disable", 'd', RTGETOPT_REQ_NOTHING } // deprecated + { "-disable", 'd', RTGETOPT_REQ_NOTHING }, // deprecated + { "--options", 'o', RTGETOPT_REQ_NOTHING }, + {"--vm", 'n', RTGETOPT_REQ_STRING}, /* only with -o */ + {"--slot", 's', RTGETOPT_REQ_UINT8}, /* only with -o and -n */ + {"--id", 'i', RTGETOPT_REQ_UINT8}, /* only with -o */ + {"--value", 'p', RTGETOPT_REQ_STRING} /* only with -i */ + }; static int handleOp(HandlerArg *a, OPCODE enmCode, int iStart, int *pcProcessed) @@ -83,15 +122,26 @@ static int handleOp(HandlerArg *a, OPCODE enmCode, int iStart, int *pcProcessed) int index = iStart; HRESULT rc; + bool fOptionsRead = false; + bool fVmOptionRead = false; + const char *pszVmName = NULL; const char *pNetName = NULL; const char *pIfName = NULL; const char * pIp = NULL; const char * pNetmask = NULL; const char * pLowerIp = NULL; const char * pUpperIp = NULL; + + uint8_t u8OptId = (uint8_t)~0; + uint8_t u8Slot = (uint8_t)~0; + int enable = -1; + DhcpOptMap GlobalDhcpOptions; + VmSlot2OptionsM VmSlot2Options; + VmConfigs VmConfigs2Delete; + int c; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; @@ -177,6 +227,78 @@ static int handleOp(HandlerArg *a, OPCODE enmCode, int iStart, int *pcProcessed) case VINF_GETOPT_NOT_OPTION: return errorSyntax(USAGE_DHCPSERVER, "unhandled parameter: %s", ValueUnion.psz); break; + + case 'o': // --options + { + // {"--vm", 'n', RTGETOPT_REQ_STRING}, /* only with -o */ + // {"--slot", 's', RTGETOPT_REQ_UINT8}, /* only with -o and -n*/ + // {"--id", 'i', RTGETOPT_REQ_UINT8}, /* only with -o */ + // {"--value", 'p', RTGETOPT_REQ_STRING} /* only with -i */ + if (fOptionsRead) + return errorSyntax(USAGE_DHCPSERVER, + "previos option edition wasn't finished"); + fOptionsRead = true; + fVmOptionRead = false; /* we want specify new global or vm option*/ + u8Slot = (uint8_t)~0; + u8OptId = (uint8_t)~0; + pszVmName = NULL; + } /* end of --options */ + break; + + case 'n': // --vm-name + { + if (fVmOptionRead) + return errorSyntax(USAGE_DHCPSERVER, + "previous vm option edition wasn't finished"); + else + fVmOptionRead = true; + u8Slot = (uint8_t)~0; /* clear slor */ + pszVmName = RTStrDup(ValueUnion.psz); + } + break; /* end of --vm-name */ + + case 's': // --slot + { + if (!fVmOptionRead) + return errorSyntax(USAGE_DHCPSERVER, + "vm name wasn't specified"); + + u8Slot = ValueUnion.u8; + } + break; /* end of --slot */ + + case 'i': // --id + { + if (!fOptionsRead) + return errorSyntax(USAGE_DHCPSERVER, + "-o wasn't found"); + + u8OptId = ValueUnion.u8; + } + break; /* end of --id */ + + case 'p': // --value + { + if (!fOptionsRead) + return errorSyntax(USAGE_DHCPSERVER, + "-o wasn't found"); + + if (u8OptId == (uint8_t)~0) + return errorSyntax(USAGE_DHCPSERVER, + "--id wasn't found"); + if ( fVmOptionRead + && u8Slot == (uint8_t)~0) + return errorSyntax(USAGE_DHCPSERVER, + "--slot wasn't found"); + + DhcpOptMap& map = fVmOptionRead ? VmSlot2Options[VmNameSlotKey(pszVmName, u8Slot)] + : GlobalDhcpOptions; + std::string strVal = ValueUnion.psz; + map.insert(DhcpOptValuePair((DhcpOpt_T)u8OptId, strVal)); + + } + break; // --end of value + default: if (c > 0) { @@ -197,7 +319,9 @@ static int handleOp(HandlerArg *a, OPCODE enmCode, int iStart, int *pcProcessed) if(! pNetName && !pIfName) return errorSyntax(USAGE_DHCPSERVER, "You need to specify either --netname or --ifname to identify the DHCP server"); - if(enmCode != OP_REMOVE) + if( enmCode != OP_REMOVE + && GlobalDhcpOptions.size() == 0 + && VmSlot2Options.size() == 0) { if(enable < 0 || pIp || pNetmask || pLowerIp || pUpperIp) { @@ -255,7 +379,11 @@ static int handleOp(HandlerArg *a, OPCODE enmCode, int iStart, int *pcProcessed) { if (pIp || pNetmask || pLowerIp || pUpperIp) { - CHECK_ERROR(svr, SetConfiguration (Bstr(pIp).mutableRaw(), Bstr(pNetmask).mutableRaw(), Bstr(pLowerIp).mutableRaw(), Bstr(pUpperIp).mutableRaw())); + CHECK_ERROR(svr, SetConfiguration ( + Bstr(pIp).mutableRaw(), + Bstr(pNetmask).mutableRaw(), + Bstr(pLowerIp).mutableRaw(), + Bstr(pUpperIp).mutableRaw())); if(FAILED(rc)) return errorArgument("Failed to set configuration"); } @@ -264,6 +392,39 @@ static int handleOp(HandlerArg *a, OPCODE enmCode, int iStart, int *pcProcessed) { CHECK_ERROR(svr, COMSETTER(Enabled) ((BOOL)enable)); } + + /* option processing */ + DhcpOptIterator itOpt; + VmSlot2OptionsIterator it; + + /* Global Options */ + for(itOpt = GlobalDhcpOptions.begin(); + itOpt != GlobalDhcpOptions.end(); + ++itOpt) + { + CHECK_ERROR(svr, + AddGlobalOption( + itOpt->first, + com::Bstr(itOpt->second.c_str()).raw())); + } + + /* heh, vm slot options. */ + + for (it = VmSlot2Options.begin(); + it != VmSlot2Options.end(); + ++it) + { + for(itOpt = it->second.begin(); + itOpt != it->second.end(); + ++itOpt) + { + CHECK_ERROR(svr, + AddVmSlotOption(Bstr(it->first.VmName.c_str()).raw(), + it->first.u8Slot, + itOpt->first, + com::Bstr(itOpt->second.c_str()).raw())); + } + } } else { diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp index 5901c534..86cffce3 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp @@ -25,8 +25,6 @@ #include <VBox/com/array.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> -#include <VBox/com/EventQueue.h> - #include <VBox/com/VirtualBox.h> #include <iprt/ctype.h> diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp index e960c26e..d6b1f201 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -47,7 +47,7 @@ using namespace com; static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va) { - RTMsgError(pszFormat, va); + RTMsgErrorV(pszFormat, va); RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS); } @@ -148,16 +148,17 @@ int parseBool(const char *psz, bool *pb) return rc; } -HRESULT findMedium(HandlerArg *a, const char *pszFilenameOrUuid, - DeviceType_T enmDevType, bool fSilent, - ComPtr<IMedium> &pMedium) +HRESULT openMedium(HandlerArg *a, const char *pszFilenameOrUuid, + DeviceType_T enmDevType, AccessMode_T enmAccessMode, + ComPtr<IMedium> &pMedium, bool fForceNewUuidOnOpen, + bool fSilent) { HRESULT rc; Guid id(pszFilenameOrUuid); char szFilenameAbs[RTPATH_MAX] = ""; /* If it is no UUID, convert the filename to an absolute one. */ - if (id.isEmpty()) + if (!id.isValid()) { int irc = RTPathAbs(pszFilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs)); if (RT_FAILURE(irc)) @@ -170,59 +171,18 @@ HRESULT findMedium(HandlerArg *a, const char *pszFilenameOrUuid, } if (!fSilent) - CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(pszFilenameOrUuid).raw(), - enmDevType, - AccessMode_ReadWrite, - /*fForceNewUidOnOpen */ false, - pMedium.asOutParam())); - else - rc = a->virtualBox->OpenMedium(Bstr(pszFilenameOrUuid).raw(), - enmDevType, - AccessMode_ReadWrite, - /*fForceNewUidOnOpen */ false, - pMedium.asOutParam()); - return rc; -} - -HRESULT findOrOpenMedium(HandlerArg *a, const char *pszFilenameOrUuid, - DeviceType_T enmDevType, AccessMode_T enmAccessMode, - ComPtr<IMedium> &pMedium, bool fForceNewUuidOnOpen, - bool *pfWasUnknown) -{ - HRESULT rc; - bool fWasUnknown = false; - Guid id(pszFilenameOrUuid); - char szFilenameAbs[RTPATH_MAX] = ""; - - /* If it is no UUID, convert the filename to an absolute one. */ - if (id.isEmpty()) - { - int irc = RTPathAbs(pszFilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs)); - if (RT_FAILURE(irc)) - { - RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilenameOrUuid); - return E_FAIL; - } - pszFilenameOrUuid = szFilenameAbs; - } - - rc = a->virtualBox->OpenMedium(Bstr(pszFilenameOrUuid).raw(), - enmDevType, - enmAccessMode, - /*fForceNewUidOnOpen */ false, - pMedium.asOutParam()); - /* If the medium is unknown try to open it. */ - if (!pMedium) - { CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(pszFilenameOrUuid).raw(), - enmDevType, enmAccessMode, + enmDevType, + enmAccessMode, fForceNewUuidOnOpen, pMedium.asOutParam())); - if (SUCCEEDED(rc)) - fWasUnknown = true; - } - if (RT_VALID_PTR(pfWasUnknown)) - *pfWasUnknown = fWasUnknown; + else + rc = a->virtualBox->OpenMedium(Bstr(pszFilenameOrUuid).raw(), + enmDevType, + enmAccessMode, + fForceNewUuidOnOpen, + pMedium.asOutParam()); + return rc; } @@ -343,7 +303,6 @@ int handleCreateHardDisk(HandlerArg *a) } /* check the outcome */ - bool fUnknownParent = false; ComPtr<IMedium> parentHardDisk; if (fBase) { @@ -372,9 +331,9 @@ int handleCreateHardDisk(HandlerArg *a) else format = pszExt; } - rc = findOrOpenMedium(a, diffparent, DeviceType_HardDisk, AccessMode_ReadWrite, - parentHardDisk, false /* fForceNewUuidOnOpen */, - &fUnknownParent); + rc = openMedium(a, diffparent, DeviceType_HardDisk, + AccessMode_ReadWrite, parentHardDisk, + false /* fForceNewUuidOnOpen */, false /* fSilent */); if (FAILED(rc)) return 1; if (parentHardDisk.isNull()) @@ -413,10 +372,19 @@ int handleCreateHardDisk(HandlerArg *a) if (SUCCEEDED(rc) && hardDisk) { ComPtr<IProgress> progress; + com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8); + + for (ULONG i = 0; i < l_variants.size(); ++i) + { + ULONG temp = DiskVariant; + temp &= 1<<i; + l_variants [i] = (MediumVariant_T)temp; + } + if (fBase) - CHECK_ERROR(hardDisk, CreateBaseStorage(size, DiskVariant, progress.asOutParam())); + CHECK_ERROR(hardDisk, CreateBaseStorage(size, ComSafeArrayAsInParam(l_variants), progress.asOutParam())); else - CHECK_ERROR(parentHardDisk, CreateDiffStorage(hardDisk, DiskVariant, progress.asOutParam())); + CHECK_ERROR(parentHardDisk, CreateDiffStorage(hardDisk, ComSafeArrayAsInParam(l_variants), progress.asOutParam())); if (SUCCEEDED(rc) && progress) { rc = showProgress(progress); @@ -430,8 +398,6 @@ int handleCreateHardDisk(HandlerArg *a) } CHECK_ERROR(hardDisk, Close()); - if (!fBase && fUnknownParent) - CHECK_ERROR(parentHardDisk, Close()); } return SUCCEEDED(rc) ? 0 : 1; } @@ -444,6 +410,7 @@ static const RTGETOPTDEF g_aModifyHardDiskOptions[] = { "--autoreset", 'z', RTGETOPT_REQ_STRING }, { "-autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated { "autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated + { "--property", 'p', RTGETOPT_REQ_STRING }, { "--compact", 'c', RTGETOPT_REQ_NOTHING }, { "-compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated { "compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated @@ -458,11 +425,15 @@ int handleModifyHardDisk(HandlerArg *a) ComPtr<IMedium> hardDisk; MediumType_T DiskType; bool AutoReset = false; - bool fModifyDiskType = false, fModifyAutoReset = false, fModifyCompact = false; + SafeArray<BSTR> mediumPropNames; + SafeArray<BSTR> mediumPropValues; + bool fModifyDiskType = false; + bool fModifyAutoReset = false; + bool fModifyProperties = false; + bool fModifyCompact = false; bool fModifyResize = false; uint64_t cbResize = 0; const char *FilenameOrUuid = NULL; - bool unknown = false; int c; RTGETOPTUNION ValueUnion; @@ -488,6 +459,38 @@ int handleModifyHardDisk(HandlerArg *a) fModifyAutoReset = true; break; + case 'p': // --property + { + /* Parse 'name=value' */ + char *pszProperty = RTStrDup(ValueUnion.psz); + if (pszProperty) + { + char *pDelimiter = strchr(pszProperty, '='); + if (pDelimiter) + { + *pDelimiter = '\0'; + + Bstr bstrName(pszProperty); + Bstr bstrValue(&pDelimiter[1]); + bstrName.detachTo(mediumPropNames.appendedRaw()); + bstrValue.detachTo(mediumPropValues.appendedRaw()); + fModifyProperties = true; + } + else + { + errorArgument("Invalid --property argument '%s'", ValueUnion.psz); + rc = E_FAIL; + } + RTStrFree(pszProperty); + } + else + { + RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for medium property '%s'\n", ValueUnion.psz); + rc = E_FAIL; + } + break; + } + case 'c': // --compact fModifyCompact = true; break; @@ -529,16 +532,13 @@ int handleModifyHardDisk(HandlerArg *a) if (!FilenameOrUuid) return errorSyntax(USAGE_MODIFYHD, "Disk name or UUID required"); - if (!fModifyDiskType && !fModifyAutoReset && !fModifyCompact && !fModifyResize) + if (!fModifyDiskType && !fModifyAutoReset && !fModifyProperties && !fModifyCompact && !fModifyResize) return errorSyntax(USAGE_MODIFYHD, "No operation specified"); - /* Depending on the operation the medium must be in the registry or - * may be opened on demand. */ - if (fModifyDiskType || fModifyAutoReset) - rc = findMedium(a, FilenameOrUuid, DeviceType_HardDisk, false /* fSilent */, hardDisk); - else - rc = findOrOpenMedium(a, FilenameOrUuid, DeviceType_HardDisk, AccessMode_ReadWrite, - hardDisk, false /* fForceNewUuidOnOpen */, &unknown); + /* Always open the medium if necessary, there is no other way. */ + rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk, + AccessMode_ReadWrite, hardDisk, + false /* fForceNewUuidOnOpen */, false /* fSilent */); if (FAILED(rc)) return 1; if (hardDisk.isNull()) @@ -561,6 +561,11 @@ int handleModifyHardDisk(HandlerArg *a) CHECK_ERROR(hardDisk, COMSETTER(AutoReset)(AutoReset)); } + if (fModifyProperties) + { + CHECK_ERROR(hardDisk, SetProperties(ComSafeArrayAsInParam(mediumPropNames), ComSafeArrayAsInParam(mediumPropValues))); + } + if (fModifyCompact) { ComPtr<IProgress> progress; @@ -597,9 +602,6 @@ int handleModifyHardDisk(HandlerArg *a) } } - if (unknown) - hardDisk->Close(); - return SUCCEEDED(rc) ? 0 : 1; } @@ -691,11 +693,10 @@ int handleCloneHardDisk(HandlerArg *a) ComPtr<IMedium> srcDisk; ComPtr<IMedium> dstDisk; - bool fSrcUnknown = false; - bool fDstUnknown = false; - rc = findOrOpenMedium(a, pszSrc, DeviceType_HardDisk, AccessMode_ReadOnly, - srcDisk, false /* fForceNewUuidOnOpen */, &fSrcUnknown); + rc = openMedium(a, pszSrc, DeviceType_HardDisk, AccessMode_ReadOnly, + srcDisk, false /* fForceNewUuidOnOpen */, + false /* fSilent */); if (FAILED(rc)) return 1; @@ -704,8 +705,10 @@ int handleCloneHardDisk(HandlerArg *a) /* open/create destination hard disk */ if (fExisting) { - rc = findOrOpenMedium(a, pszDst, DeviceType_HardDisk, AccessMode_ReadWrite, - dstDisk, false /* fForceNewUuidOnOpen */, &fDstUnknown); + rc = openMedium(a, pszDst, DeviceType_HardDisk, + AccessMode_ReadWrite, dstDisk, + false /* fForceNewUuidOnOpen */, + false /* fSilent */); if (FAILED(rc)) break; @@ -722,11 +725,19 @@ int handleCloneHardDisk(HandlerArg *a) rc = createHardDisk(a, Utf8Str(format).c_str(), pszDst, dstDisk); if (FAILED(rc)) break; - fDstUnknown = true; } ComPtr<IProgress> progress; - CHECK_ERROR_BREAK(srcDisk, CloneTo(dstDisk, DiskVariant, NULL, progress.asOutParam())); + com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8); + + for (ULONG i = 0; i < l_variants.size(); ++i) + { + ULONG temp = DiskVariant; + temp &= 1<<i; + l_variants [i] = (MediumVariant_T)temp; + } + + CHECK_ERROR_BREAK(srcDisk, CloneTo(dstDisk, ComSafeArrayAsInParam(l_variants), NULL, progress.asOutParam())); rc = showProgress(progress); CHECK_PROGRESS_ERROR_BREAK(progress, ("Failed to clone hard disk")); @@ -739,17 +750,6 @@ int handleCloneHardDisk(HandlerArg *a) } while (0); - if (fDstUnknown && !dstDisk.isNull()) - { - /* forget the created clone */ - dstDisk->Close(); - } - if (fSrcUnknown) - { - /* close the unknown hard disk to forget it again */ - srcDisk->Close(); - } - return SUCCEEDED(rc) ? 0 : 1; } @@ -797,14 +797,15 @@ RTEXITCODE handleConvertFromRaw(int argc, char *argv[]) break; case 'm': // --variant - MediumVariant_T DiskVariant; + { + MediumVariant_T DiskVariant = MediumVariant_Standard; rc = parseDiskVariant(ValueUnion.psz, &DiskVariant); if (RT_FAILURE(rc)) return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz); /// @todo cleaner solution than assuming 1:1 mapping? uImageFlags = (unsigned)DiskVariant; break; - + } case VINF_GETOPT_NOT_OPTION: if (!srcfilename) { @@ -935,106 +936,73 @@ out: return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; } -static const RTGETOPTDEF g_aShowHardDiskInfoOptions[] = -{ - { "--dummy", 256, RTGETOPT_REQ_NOTHING }, // placeholder for C++ -}; - -int handleShowHardDiskInfo(HandlerArg *a) +HRESULT showMediumInfo(const ComPtr<IVirtualBox> &pVirtualBox, + const ComPtr<IMedium> &pMedium, + const char *pszParentUUID, + bool fOptLong) { - HRESULT rc; - const char *FilenameOrUuid = NULL; - - int c; - RTGETOPTUNION ValueUnion; - RTGETOPTSTATE GetState; - // start at 0 because main() has hacked both the argc and argv given to us - RTGetOptInit(&GetState, a->argc, a->argv, g_aShowHardDiskInfoOptions, RT_ELEMENTS(g_aShowHardDiskInfoOptions), - 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); - while ((c = RTGetOpt(&GetState, &ValueUnion))) - { - switch (c) - { - case VINF_GETOPT_NOT_OPTION: - if (!FilenameOrUuid) - FilenameOrUuid = ValueUnion.psz; - else - return errorSyntax(USAGE_SHOWHDINFO, "Invalid parameter '%s'", ValueUnion.psz); - break; - - default: - if (c > 0) - { - if (RT_C_IS_PRINT(c)) - return errorSyntax(USAGE_SHOWHDINFO, "Invalid option -%c", c); - else - return errorSyntax(USAGE_SHOWHDINFO, "Invalid option case %i", c); - } - else if (c == VERR_GETOPT_UNKNOWN_OPTION) - return errorSyntax(USAGE_SHOWHDINFO, "unknown option: %s\n", ValueUnion.psz); - else if (ValueUnion.pDef) - return errorSyntax(USAGE_SHOWHDINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c); - else - return errorSyntax(USAGE_SHOWHDINFO, "error: %Rrs", c); - } - } - - /* check for required options */ - if (!FilenameOrUuid) - return errorSyntax(USAGE_SHOWHDINFO, "Disk name or UUID required"); - - ComPtr<IMedium> hardDisk; - bool unknown = false; - - rc = findOrOpenMedium(a, FilenameOrUuid, DeviceType_HardDisk, AccessMode_ReadOnly, - hardDisk, false /* fForceNewUuidOnOpen */, &unknown); - if (FAILED(rc)) - return 1; - + HRESULT rc = S_OK; do { Bstr uuid; - hardDisk->COMGETTER(Id)(uuid.asOutParam()); - RTPrintf("UUID: %s\n", Utf8Str(uuid).c_str()); + pMedium->COMGETTER(Id)(uuid.asOutParam()); + RTPrintf("UUID: %ls\n", uuid.raw()); + if (pszParentUUID) + RTPrintf("Parent UUID: %s\n", pszParentUUID); /* check for accessibility */ - /// @todo NEWMEDIA check accessibility of all parents - /// @todo NEWMEDIA print the full state value - MediumState_T state; - CHECK_ERROR_BREAK(hardDisk, RefreshState(&state)); - RTPrintf("Accessible: %s\n", state != MediumState_Inaccessible ? "yes" : "no"); + MediumState_T enmState; + CHECK_ERROR_BREAK(pMedium, RefreshState(&enmState)); + pMedium->RefreshState(&enmState); + const char *pszState = "unknown"; + switch (enmState) + { + case MediumState_NotCreated: + pszState = "not created"; + break; + case MediumState_Created: + pszState = "created"; + break; + case MediumState_LockedRead: + pszState = "locked read"; + break; + case MediumState_LockedWrite: + pszState = "locked write"; + break; + case MediumState_Inaccessible: + pszState = "inaccessible"; + break; + case MediumState_Creating: + pszState = "creating"; + break; + case MediumState_Deleting: + pszState = "deleting"; + break; + } + RTPrintf("State: %s\n", pszState); - if (state == MediumState_Inaccessible) + if (fOptLong && enmState == MediumState_Inaccessible) { Bstr err; - CHECK_ERROR_BREAK(hardDisk, COMGETTER(LastAccessError)(err.asOutParam())); - RTPrintf("Access Error: %ls\n", err.raw()); + CHECK_ERROR_BREAK(pMedium, COMGETTER(LastAccessError)(err.asOutParam())); + RTPrintf("Access Error: %ls\n", err.raw()); } - Bstr description; - hardDisk->COMGETTER(Description)(description.asOutParam()); - if (!description.isEmpty()) + if (fOptLong) { - RTPrintf("Description: %ls\n", description.raw()); + Bstr description; + pMedium->COMGETTER(Description)(description.asOutParam()); + if (!description.isEmpty()) + RTPrintf("Description: %ls\n", description.raw()); } - LONG64 logicalSize; - hardDisk->COMGETTER(LogicalSize)(&logicalSize); - RTPrintf("Logical size: %lld MBytes\n", logicalSize >> 20); - LONG64 actualSize; - hardDisk->COMGETTER(Size)(&actualSize); - RTPrintf("Current size on disk: %lld MBytes\n", actualSize >> 20); - - ComPtr <IMedium> parent; - hardDisk->COMGETTER(Parent)(parent.asOutParam()); - MediumType_T type; - hardDisk->COMGETTER(Type)(&type); + pMedium->COMGETTER(Type)(&type); const char *typeStr = "unknown"; switch (type) { case MediumType_Normal: - if (!parent.isNull()) + if (pszParentUUID && Guid(pszParentUUID).isValid()) typeStr = "normal (differencing)"; else typeStr = "normal (base)"; @@ -1055,78 +1023,215 @@ int handleShowHardDiskInfo(HandlerArg *a) typeStr = "multiattach"; break; } - RTPrintf("Type: %s\n", typeStr); + RTPrintf("Type: %s\n", typeStr); + + /* print out information specific for differencing hard disks */ + if (fOptLong && pszParentUUID && Guid(pszParentUUID).isValid()) + { + BOOL autoReset = FALSE; + pMedium->COMGETTER(AutoReset)(&autoReset); + RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off"); + } + + Bstr loc; + pMedium->COMGETTER(Location)(loc.asOutParam()); + RTPrintf("Location: %ls\n", loc.raw()); Bstr format; - hardDisk->COMGETTER(Format)(format.asOutParam()); - RTPrintf("Storage format: %ls\n", format.raw()); - ULONG variant; - hardDisk->COMGETTER(Variant)(&variant); - const char *variantStr = "unknown"; - switch (variant & ~(MediumVariant_Fixed | MediumVariant_Diff)) + pMedium->COMGETTER(Format)(format.asOutParam()); + RTPrintf("Storage format: %ls\n", format.raw()); + + if (fOptLong) { - case MediumVariant_VmdkSplit2G: - variantStr = "split2G"; - break; - case MediumVariant_VmdkStreamOptimized: - variantStr = "streamOptimized"; - break; - case MediumVariant_VmdkESX: - variantStr = "ESX"; - break; - case MediumVariant_Standard: - variantStr = "default"; - break; + com::SafeArray<MediumVariant_T> safeArray_variant; + + pMedium->COMGETTER(Variant)(ComSafeArrayAsOutParam(safeArray_variant)); + ULONG variant=0; + for (size_t i = 0; i < safeArray_variant.size(); i++) + variant |= safeArray_variant[i]; + + const char *variantStr = "unknown"; + switch (variant & ~(MediumVariant_Fixed | MediumVariant_Diff)) + { + case MediumVariant_VmdkSplit2G: + variantStr = "split2G"; + break; + case MediumVariant_VmdkStreamOptimized: + variantStr = "streamOptimized"; + break; + case MediumVariant_VmdkESX: + variantStr = "ESX"; + break; + case MediumVariant_Standard: + variantStr = "default"; + break; + } + const char *variantTypeStr = "dynamic"; + if (variant & MediumVariant_Fixed) + variantTypeStr = "fixed"; + else if (variant & MediumVariant_Diff) + variantTypeStr = "differencing"; + RTPrintf("Format variant: %s %s\n", variantTypeStr, variantStr); + } + + LONG64 logicalSize; + pMedium->COMGETTER(LogicalSize)(&logicalSize); + RTPrintf("Capacity: %lld MBytes\n", logicalSize >> 20); + if (fOptLong) + { + LONG64 actualSize; + pMedium->COMGETTER(Size)(&actualSize); + RTPrintf("Size on disk: %lld MBytes\n", actualSize >> 20); } - const char *variantTypeStr = "dynamic"; - if (variant & MediumVariant_Fixed) - variantTypeStr = "fixed"; - else if (variant & MediumVariant_Diff) - variantTypeStr = "differencing"; - RTPrintf("Format variant: %s %s\n", variantTypeStr, variantStr); - /// @todo also dump config parameters (iSCSI) + if (fOptLong) + { + com::SafeArray<BSTR> names; + com::SafeArray<BSTR> values; + pMedium->GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values)); + size_t cNames = names.size(); + size_t cValues = values.size(); + bool fFirst = true; + for (size_t i = 0; i < cNames; i++) + { + Bstr value; + if (i < cValues) + value = values[i]; + RTPrintf("%s%ls=%ls\n", + fFirst ? "Property: " : " ", + names[i], value.raw()); + } + } - if (!unknown) + if (fOptLong) { + bool fFirst = true; com::SafeArray<BSTR> machineIds; - hardDisk->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds)); - for (size_t j = 0; j < machineIds.size(); ++ j) + pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds)); + for (size_t i = 0; i < machineIds.size(); i++) { ComPtr<IMachine> machine; - CHECK_ERROR(a->virtualBox, FindMachine(machineIds[j], machine.asOutParam())); - ASSERT(machine); - Bstr name; - machine->COMGETTER(Name)(name.asOutParam()); - machine->COMGETTER(Id)(uuid.asOutParam()); - RTPrintf("%s%ls (UUID: %ls)\n", - j == 0 ? "In use by VMs: " : " ", - name.raw(), machineIds[j]); + CHECK_ERROR(pVirtualBox, FindMachine(machineIds[i], machine.asOutParam())); + if (machine) + { + Bstr name; + machine->COMGETTER(Name)(name.asOutParam()); + machine->COMGETTER(Id)(uuid.asOutParam()); + RTPrintf("%s%ls (UUID: %ls)", + fFirst ? "In use by VMs: " : " ", + name.raw(), machineIds[i]); + fFirst = false; + com::SafeArray<BSTR> snapshotIds; + pMedium->GetSnapshotIds(machineIds[i], + ComSafeArrayAsOutParam(snapshotIds)); + for (size_t j = 0; j < snapshotIds.size(); j++) + { + ComPtr<ISnapshot> snapshot; + machine->FindSnapshot(snapshotIds[j], snapshot.asOutParam()); + if (snapshot) + { + Bstr snapshotName; + snapshot->COMGETTER(Name)(snapshotName.asOutParam()); + RTPrintf(" [%ls (UUID: %ls)]", snapshotName.raw(), snapshotIds[j]); + } + } + RTPrintf("\n"); + } } - /// @todo NEWMEDIA check usage in snapshots too - /// @todo NEWMEDIA also list children } - Bstr loc; - hardDisk->COMGETTER(Location)(loc.asOutParam()); - RTPrintf("Location: %ls\n", loc.raw()); - - /* print out information specific for differencing hard disks */ - if (!parent.isNull()) + if (fOptLong) { - BOOL autoReset = FALSE; - hardDisk->COMGETTER(AutoReset)(&autoReset); - RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off"); + com::SafeIfaceArray<IMedium> children; + pMedium->COMGETTER(Children)(ComSafeArrayAsOutParam(children)); + bool fFirst = true; + for (size_t i = 0; i < children.size(); i++) + { + ComPtr<IMedium> pChild(children[i]); + if (pChild) + { + Bstr childUUID; + pChild->COMGETTER(Id)(childUUID.asOutParam()); + RTPrintf("%s%ls\n", + fFirst ? "Child UUIDs: " : " ", + childUUID.raw()); + fFirst = false; + } + } } } while (0); - if (unknown) + return rc; +} + +static const RTGETOPTDEF g_aShowHardDiskInfoOptions[] = +{ + { "--dummy", 256, RTGETOPT_REQ_NOTHING }, // placeholder for C++ +}; + +int handleShowHardDiskInfo(HandlerArg *a) +{ + HRESULT rc; + const char *FilenameOrUuid = NULL; + + int c; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + // start at 0 because main() has hacked both the argc and argv given to us + RTGetOptInit(&GetState, a->argc, a->argv, g_aShowHardDiskInfoOptions, RT_ELEMENTS(g_aShowHardDiskInfoOptions), + 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS); + while ((c = RTGetOpt(&GetState, &ValueUnion))) { - /* close the unknown hard disk to forget it again */ - hardDisk->Close(); + switch (c) + { + case VINF_GETOPT_NOT_OPTION: + if (!FilenameOrUuid) + FilenameOrUuid = ValueUnion.psz; + else + return errorSyntax(USAGE_SHOWHDINFO, "Invalid parameter '%s'", ValueUnion.psz); + break; + + default: + if (c > 0) + { + if (RT_C_IS_PRINT(c)) + return errorSyntax(USAGE_SHOWHDINFO, "Invalid option -%c", c); + else + return errorSyntax(USAGE_SHOWHDINFO, "Invalid option case %i", c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(USAGE_SHOWHDINFO, "unknown option: %s\n", ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax(USAGE_SHOWHDINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax(USAGE_SHOWHDINFO, "error: %Rrs", c); + } } + /* check for required options */ + if (!FilenameOrUuid) + return errorSyntax(USAGE_SHOWHDINFO, "Disk name or UUID required"); + + ComPtr<IMedium> hardDisk; + rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk, + AccessMode_ReadOnly, hardDisk, + false /* fForceNewUuidOnOpen */, false /* fSilent */); + if (FAILED(rc)) + return 1; + + Utf8Str strParentUUID("base"); + ComPtr<IMedium> parent; + hardDisk->COMGETTER(Parent)(parent.asOutParam()); + if (!parent.isNull()) + { + Bstr bstrParentUUID; + parent->COMGETTER(Id)(bstrParentUUID.asOutParam()); + strParentUUID = bstrParentUUID; + } + + rc = showMediumInfo(a->virtualBox, hardDisk, strParentUUID.c_str(), true); + return SUCCEEDED(rc) ? 0 : 1; } @@ -1215,11 +1320,17 @@ int handleCloseMedium(HandlerArg *a) ComPtr<IMedium> medium; if (cmd == CMD_DISK) - rc = findMedium(a, FilenameOrUuid, DeviceType_HardDisk, false /* fSilent */, medium); + rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk, + AccessMode_ReadWrite, medium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); else if (cmd == CMD_DVD) - rc = findMedium(a, FilenameOrUuid, DeviceType_DVD, false /* fSilent */, medium); + rc = openMedium(a, FilenameOrUuid, DeviceType_DVD, + AccessMode_ReadOnly, medium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); else if (cmd == CMD_FLOPPY) - rc = findMedium(a, FilenameOrUuid, DeviceType_Floppy, false /* fSilent */, medium); + rc = openMedium(a, FilenameOrUuid, DeviceType_Floppy, + AccessMode_ReadWrite, medium, + false /* fForceNewUuidOnOpen */, false /* fSilent */); if (SUCCEEDED(rc) && medium) { diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp index 53fe4336..ced18c1b 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010-2012 Oracle Corporation + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -20,16 +20,18 @@ * Header Files * *******************************************************************************/ #include "VBoxManage.h" +#include "VBoxManageGuestCtrl.h" #ifndef VBOX_ONLY_DOCS -#include <VBox/com/com.h> -#include <VBox/com/string.h> #include <VBox/com/array.h> +#include <VBox/com/com.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> +#include <VBox/com/listeners.h> +#include <VBox/com/NativeEventQueue.h> +#include <VBox/com/string.h> #include <VBox/com/VirtualBox.h> -#include <VBox/com/EventQueue.h> #include <VBox/err.h> #include <VBox/log.h> @@ -41,6 +43,7 @@ #include <iprt/getopt.h> #include <iprt/list.h> #include <iprt/path.h> +#include <iprt/process.h> /* For RTProcSelf(). */ #include <iprt/thread.h> #include <map> @@ -59,25 +62,85 @@ using namespace com; +/** Set by the signal handler when current guest control + * action shall be aborted. */ +static volatile bool g_fGuestCtrlCanceled = false; + /** - * IVirtualBoxCallback implementation for handling the GuestControlCallback in - * relation to the "guestcontrol * wait" command. + * Listener declarations. */ -/** @todo */ +VBOX_LISTENER_DECLARE(GuestFileEventListenerImpl) +VBOX_LISTENER_DECLARE(GuestProcessEventListenerImpl) +VBOX_LISTENER_DECLARE(GuestSessionEventListenerImpl) +VBOX_LISTENER_DECLARE(GuestEventListenerImpl) -/** Set by the signal handler. */ -static volatile bool g_fGuestCtrlCanceled = false; +/** + * Command context flags. + */ +/** No flags set. */ +#define CTLCMDCTX_FLAGS_NONE 0 +/** Don't install a signal handler (CTRL+C trap). */ +#define CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER RT_BIT(0) +/** No guest session needed. */ +#define CTLCMDCTX_FLAGS_SESSION_ANONYMOUS RT_BIT(1) +/** Detach the guest session. That is, don't close the + * guest session automatically on exit. */ +#define CTLCMDCTX_FLAGS_SESSION_DETACH RT_BIT(2) + +/** + * Context for handling a specific command. + */ +typedef struct GCTLCMDCTX +{ + HandlerArg handlerArg; + /** Command-specific argument count. */ + int iArgc; + /** Command-specific argument vector. */ + char **ppaArgv; + /** First argv to start parsing with. */ + int iFirstArgc; + /** Command context flags. */ + uint32_t uFlags; + /** Verbose flag. */ + bool fVerbose; + /** User name. */ + Utf8Str strUsername; + /** Password. */ + Utf8Str strPassword; + /** Domain. */ + Utf8Str strDomain; + /** Pointer to the IGuest interface. */ + ComPtr<IGuest> pGuest; + /** Pointer to the to be used guest session. */ + ComPtr<IGuestSession> pGuestSession; + /** The guest session ID. */ + ULONG uSessionID; + +} GCTLCMDCTX, *PGCTLCMDCTX; + +typedef struct GCTLCMD +{ + /** + * Actual command handler callback. + * + * @param pCtx Pointer to command context to use. + */ + DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (PGCTLCMDCTX pCtx)); + +} GCTLCMD, *PGCTLCMD; typedef struct COPYCONTEXT { - COPYCONTEXT() : fVerbose(false), fDryRun(false), fHostToGuest(false) + COPYCONTEXT() + : fDryRun(false), + fHostToGuest(false) { } - ComPtr<IGuestSession> pGuestSession; - bool fVerbose; + PGCTLCMDCTX pCmdCtx; bool fDryRun; bool fHostToGuest; + } COPYCONTEXT, *PCOPYCONTEXT; /** @@ -174,6 +237,15 @@ enum EXITCODEEXEC EXITCODEEXEC_CANCELED = 22 }; +/* + * Common getopt definitions, starting at 1000. + * Specific command definitions will start all at 2000. + */ +enum GETOPTDEF_COMMON +{ + GETOPTDEF_COMMON_PASSWORD = 1000 +}; + /** * RTGetOpt-IDs for the guest execution control command line. */ @@ -184,7 +256,6 @@ enum GETOPTDEF_EXEC GETOPTDEF_EXEC_OUTPUTFORMAT, GETOPTDEF_EXEC_DOS2UNIX, GETOPTDEF_EXEC_UNIX2DOS, - GETOPTDEF_EXEC_PASSWORD, GETOPTDEF_EXEC_WAITFOREXIT, GETOPTDEF_EXEC_WAITFORSTDOUT, GETOPTDEF_EXEC_WAITFORSTDERR @@ -194,18 +265,28 @@ enum GETOPTDEF_COPY { GETOPTDEF_COPY_DRYRUN = 1000, GETOPTDEF_COPY_FOLLOW, - GETOPTDEF_COPY_PASSWORD, GETOPTDEF_COPY_TARGETDIR }; enum GETOPTDEF_MKDIR { - GETOPTDEF_MKDIR_PASSWORD = 1000 +}; + +enum GETOPTDEF_RM +{ +}; + +enum GETOPTDEF_RMDIR +{ +}; + +enum GETOPTDEF_SESSIONCLOSE +{ + GETOPTDEF_SESSIONCLOSE_ALL = 2000 }; enum GETOPTDEF_STAT { - GETOPTDEF_STAT_PASSWORD = 1000 }; enum OUTPUTTYPE @@ -219,57 +300,152 @@ static int ctrlCopyDirExists(PCOPYCONTEXT pContext, bool bGuest, const char *psz #endif /* VBOX_ONLY_DOCS */ -void usageGuestControl(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2) +void usageGuestControl(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2, uint32_t uSubCmd) { RTStrmPrintf(pStrm, - "%s guestcontrol %s <vmname>|<uuid>\n" - " exec[ute]\n" - " --image <path to program> --username <name>\n" - " [--passwordfile <file> | --password <password>]\n" - " [--domain <domain>] [--verbose] [--timeout <msec>]\n" - " [--environment \"<NAME>=<VALUE> [<NAME>=<VALUE>]\"]\n" - " [--wait-exit] [--wait-stdout] [--wait-stderr]\n" - " [--dos2unix] [--unix2dos]\n" - " [-- [<argument1>] ... [<argumentN>]]\n" - /** @todo Add a "--" parameter (has to be last parameter) to directly execute - * stuff, e.g. "VBoxManage guestcontrol execute <VMName> --username <> ... -- /bin/rm -Rf /foo". */ - "\n" - " copyfrom\n" - " <guest source> <host dest> --username <name>\n" - " [--passwordfile <file> | --password <password>]\n" - " [--domain <domain>] [--verbose]\n" - " [--dryrun] [--follow] [--recursive]\n" - "\n" - " copyto|cp\n" - " <host source> <guest dest> --username <name>\n" - " [--passwordfile <file> | --password <password>]\n" - " [--domain <domain>] [--verbose]\n" - " [--dryrun] [--follow] [--recursive]\n" - "\n" - " createdir[ectory]|mkdir|md\n" - " <guest directory>... --username <name>\n" - " [--passwordfile <file> | --password <password>]\n" - " [--domain <domain>] [--verbose]\n" - " [--parents] [--mode <mode>]\n" - "\n" - " stat\n" - " <file>... --username <name>\n" - " [--passwordfile <file> | --password <password>]\n" - " [--domain <domain>] [--verbose]\n" - "\n" - " updateadditions\n" - " [--source <guest additions .ISO>] [--verbose]\n" - " [--wait-start]\n" - "\n", pcszSep1, pcszSep2); + "%s guestcontrol %s <uuid|vmname>\n%s", + pcszSep1, pcszSep2, + uSubCmd == ~0U ? "\n" : ""); + if (uSubCmd & USAGE_GSTCTRL_EXEC) + RTStrmPrintf(pStrm, + " exec[ute]\n" + " --image <path to program> --username <name>\n" + " [--passwordfile <file> | --password <password>]\n" + " [--domain <domain>] [--verbose] [--timeout <msec>]\n" + " [--environment \"<NAME>=<VALUE> [<NAME>=<VALUE>]\"]\n" + " [--wait-exit] [--wait-stdout] [--wait-stderr]\n" + " [--dos2unix] [--unix2dos]\n" + " [-- [<argument1>] ... [<argumentN>]]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_COPYFROM) + RTStrmPrintf(pStrm, + " copyfrom\n" + " <guest source> <host dest> --username <name>\n" + " [--passwordfile <file> | --password <password>]\n" + " [--domain <domain>] [--verbose]\n" + " [--dryrun] [--follow] [--recursive]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_COPYTO) + RTStrmPrintf(pStrm, + " copyto|cp\n" + " <host source> <guest dest> --username <name>\n" + " [--passwordfile <file> | --password <password>]\n" + " [--domain <domain>] [--verbose]\n" + " [--dryrun] [--follow] [--recursive]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_CREATEDIR) + RTStrmPrintf(pStrm, + " createdir[ectory]|mkdir|md\n" + " <guest directory>... --username <name>\n" + " [--passwordfile <file> | --password <password>]\n" + " [--domain <domain>] [--verbose]\n" + " [--parents] [--mode <mode>]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_REMOVEDIR) + RTStrmPrintf(pStrm, + " removedir[ectory]|rmdir\n" + " <guest directory>... --username <name>\n" + " [--passwordfile <file> | --password <password>]\n" + " [--domain <domain>] [--verbose]\n" + " [--recursive|-R|-r]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_REMOVEFILE) + RTStrmPrintf(pStrm, + " removefile|rm\n" + " <guest file>... --username <name>\n" + " [--passwordfile <file> | --password <password>]\n" + " [--domain <domain>] [--verbose]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_RENAME) + RTStrmPrintf(pStrm, + " ren[ame]|mv\n" + " <source>... <dest> --username <name>\n" + " [--passwordfile <file> | --password <password>]\n" + " [--domain <domain>] [--verbose]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_CREATETEMP) + RTStrmPrintf(pStrm, + " createtemp[orary]|mktemp\n" + " <template> --username <name>\n" + " [--passwordfile <file> | --password <password>]\n" + " [--directory] [--secure] [--tmpdir <directory>]\n" + " [--domain <domain>] [--mode <mode>] [--verbose]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_LIST) + RTStrmPrintf(pStrm, + " list <all|sessions|processes|files> [--verbose]\n" + "\n"); + /** @todo Add an own help group for "session" and "process" sub commands. */ + if (uSubCmd & USAGE_GSTCTRL_PROCESS) + RTStrmPrintf(pStrm, + " process kill --session-id <ID>\n" + " | --session-name <name or pattern>\n" + " [--verbose]\n" + " <PID> ... <PID n>\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_KILL) + RTStrmPrintf(pStrm, + " [p[s]]kill --session-id <ID>\n" + " | --session-name <name or pattern>\n" + " [--verbose]\n" + " <PID> ... <PID n>\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_SESSION) + RTStrmPrintf(pStrm, + " session close --session-id <ID>\n" + " | --session-name <name or pattern>\n" + " | --all\n" + " [--verbose]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_STAT) + RTStrmPrintf(pStrm, + " stat\n" + " <file>... --username <name>\n" + " [--passwordfile <file> | --password <password>]\n" + " [--domain <domain>] [--verbose]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_UPDATEADDS) + RTStrmPrintf(pStrm, + " updateadditions\n" + " [--source <guest additions .ISO>] [--verbose]\n" + " [--wait-start]\n" + " [-- [<argument1>] ... [<argumentN>]]\n" + "\n"); + if (uSubCmd & USAGE_GSTCTRL_WATCH) + RTStrmPrintf(pStrm, + " watch [--verbose]\n" + "\n"); } #ifndef VBOX_ONLY_DOCS +#ifdef RT_OS_WINDOWS +static BOOL WINAPI guestCtrlSignalHandler(DWORD dwCtrlType) +{ + bool fEventHandled = FALSE; + switch (dwCtrlType) + { + /* User pressed CTRL+C or CTRL+BREAK or an external event was sent + * via GenerateConsoleCtrlEvent(). */ + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_C_EVENT: + ASMAtomicWriteBool(&g_fGuestCtrlCanceled, true); + fEventHandled = TRUE; + break; + default: + break; + /** @todo Add other events here. */ + } + + return fEventHandled; +} +#else /* !RT_OS_WINDOWS */ /** * Signal handler that sets g_fGuestCtrlCanceled. * * This can be executed on any thread in the process, on Windows it may even be - * a thread dedicated to delivering this signal. Do not doing anything + * a thread dedicated to delivering this signal. Don't do anything * unnecessary here. */ static void guestCtrlSignalHandler(int iSignal) @@ -277,35 +453,58 @@ static void guestCtrlSignalHandler(int iSignal) NOREF(iSignal); ASMAtomicWriteBool(&g_fGuestCtrlCanceled, true); } +#endif /** * Installs a custom signal handler to get notified * whenever the user wants to intercept the program. + * + ** @todo Make this handler available for all VBoxManage modules? */ -static void ctrlSignalHandlerInstall() +static int ctrlSignalHandlerInstall(void) { + int rc = VINF_SUCCESS; +#ifdef RT_OS_WINDOWS + if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)guestCtrlSignalHandler, TRUE /* Add handler */)) + { + rc = RTErrConvertFromWin32(GetLastError()); + RTMsgError("Unable to install console control handler, rc=%Rrc\n", rc); + } +#else signal(SIGINT, guestCtrlSignalHandler); -#ifdef SIGBREAK +# ifdef SIGBREAK signal(SIGBREAK, guestCtrlSignalHandler); +# endif #endif + return rc; } /** * Uninstalls a previously installed signal handler. */ -static void ctrlSignalHandlerUninstall() +static int ctrlSignalHandlerUninstall(void) { + int rc = VINF_SUCCESS; +#ifdef RT_OS_WINDOWS + if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)NULL, FALSE /* Remove handler */)) + { + rc = RTErrConvertFromWin32(GetLastError()); + RTMsgError("Unable to uninstall console control handler, rc=%Rrc\n", rc); + } +#else signal(SIGINT, SIG_DFL); -#ifdef SIGBREAK +# ifdef SIGBREAK signal(SIGBREAK, SIG_DFL); +# endif #endif + return rc; } /** * Translates a process status to a human readable * string. */ -static const char *ctrlExecProcessStatusToText(ProcessStatus_T enmStatus) +const char *ctrlProcessStatusToText(ProcessStatus_T enmStatus) { switch (enmStatus) { @@ -384,6 +583,90 @@ static int ctrlExecProcessStatusToExitCode(ProcessStatus_T enmStatus, ULONG uExi return vrc; } +const char *ctrlProcessWaitResultToText(ProcessWaitResult_T enmWaitResult) +{ + switch (enmWaitResult) + { + case ProcessWaitResult_Start: + return "started"; + case ProcessWaitResult_Terminate: + return "terminated"; + case ProcessWaitResult_Status: + return "status changed"; + case ProcessWaitResult_Error: + return "error"; + case ProcessWaitResult_Timeout: + return "timed out"; + case ProcessWaitResult_StdIn: + return "stdin ready"; + case ProcessWaitResult_StdOut: + return "data on stdout"; + case ProcessWaitResult_StdErr: + return "data on stderr"; + case ProcessWaitResult_WaitFlagNotSupported: + return "waiting flag not supported"; + default: + break; + } + return "unknown"; +} + +/** + * Translates a guest session status to a human readable + * string. + */ +const char *ctrlSessionStatusToText(GuestSessionStatus_T enmStatus) +{ + switch (enmStatus) + { + case GuestSessionStatus_Starting: + return "starting"; + case GuestSessionStatus_Started: + return "started"; + case GuestSessionStatus_Terminating: + return "terminating"; + case GuestSessionStatus_Terminated: + return "terminated"; + case GuestSessionStatus_TimedOutKilled: + return "timed out"; + case GuestSessionStatus_TimedOutAbnormally: + return "timed out, hanging"; + case GuestSessionStatus_Down: + return "killed"; + case GuestSessionStatus_Error: + return "error"; + default: + break; + } + return "unknown"; +} + +/** + * Translates a guest file status to a human readable + * string. + */ +const char *ctrlFileStatusToText(FileStatus_T enmStatus) +{ + switch (enmStatus) + { + case FileStatus_Opening: + return "opening"; + case FileStatus_Open: + return "open"; + case FileStatus_Closing: + return "closing"; + case FileStatus_Closed: + return "closed"; + case FileStatus_Down: + return "killed"; + case FileStatus_Error: + return "error"; + default: + break; + } + return "unknown"; +} + static int ctrlPrintError(com::ErrorInfo &errorInfo) { if ( errorInfo.isFullAvailable() @@ -432,37 +715,86 @@ static int ctrlPrintProgressError(ComPtr<IProgress> pProgress) } while(0); - if (FAILED(rc)) - AssertMsgStmt(NULL, ("Could not lookup progress information\n"), vrc = VERR_COM_UNEXPECTED); + AssertMsgStmt(SUCCEEDED(rc), ("Could not lookup progress information\n"), vrc = VERR_COM_UNEXPECTED); return vrc; } /** * Un-initializes the VM after guest control usage. + * @param pCmdCtx Pointer to command context. + * @param uFlags Command context flags. */ -static void ctrlUninitVM(HandlerArg *pArg) +static void ctrlUninitVM(PGCTLCMDCTX pCtx, uint32_t uFlags) { - AssertPtrReturnVoid(pArg); - if (pArg->session) - pArg->session->UnlockMachine(); + AssertPtrReturnVoid(pCtx); + + if (!(pCtx->uFlags & CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER)) + ctrlSignalHandlerUninstall(); + + HRESULT rc; + + do + { + if (!pCtx->pGuestSession.isNull()) + { + if ( !(pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS) + && !(pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_DETACH)) + { + if (pCtx->fVerbose) + RTPrintf("Closing guest session ...\n"); + + CHECK_ERROR(pCtx->pGuestSession, Close()); + /* Keep going - don't break here. Try to unlock the + * machine down below. */ + } + else if ( (pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_DETACH) + && pCtx->fVerbose) + RTPrintf("Guest session detached\n"); + + pCtx->pGuestSession.setNull(); + } + + if (pCtx->handlerArg.session) + CHECK_ERROR(pCtx->handlerArg.session, UnlockMachine()); + + } while (0); + + for (int i = 0; i < pCtx->iArgc; i++) + RTStrFree(pCtx->ppaArgv[i]); + RTMemFree(pCtx->ppaArgv); + pCtx->iArgc = 0; } /** * Initializes the VM for IGuest operations. * * That is, checks whether it's up and running, if it can be locked (shared - * only) and returns a valid IGuest pointer on success. + * only) and returns a valid IGuest pointer on success. Also, it does some + * basic command line processing and opens a guest session, if required. * - * @return IPRT status code. - * @param pArg Our command line argument structure. - * @param pszNameOrId The VM's name or UUID. - * @param pGuest Where to return the IGuest interface pointer. + * @return RTEXITCODE status code. + * @param pArg Pointer to command line argument structure. + * @param pCmdCtx Pointer to command context. + * @param uFlags Command context flags. */ -static int ctrlInitVM(HandlerArg *pArg, const char *pszNameOrId, ComPtr<IGuest> *pGuest) +static RTEXITCODE ctrlInitVM(HandlerArg *pArg, + PGCTLCMDCTX pCtx, uint32_t uFlags, uint32_t uUsage) { - AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); - AssertPtrReturn(pszNameOrId, VERR_INVALID_PARAMETER); + AssertPtrReturn(pArg, RTEXITCODE_FAILURE); + AssertReturn(pArg->argc > 1, RTEXITCODE_FAILURE); + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + +#ifdef DEBUG_andy + RTPrintf("Original argv:\n"); + for (int i=0; i<pArg->argc;i++) + RTPrintf("\targv[%d]=%s\n", i, pArg->argv[i]); +#endif + + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + + const char *pszNameOrId = pArg->argv[0]; + const char *pszCmd = pArg->argv[1]; /* Lookup VM. */ ComPtr<IMachine> machine; @@ -470,36 +802,243 @@ static int ctrlInitVM(HandlerArg *pArg, const char *pszNameOrId, ComPtr<IGuest> HRESULT rc; CHECK_ERROR(pArg->virtualBox, FindMachine(Bstr(pszNameOrId).raw(), machine.asOutParam())); - if (FAILED(rc)) - return VERR_NOT_FOUND; + if (SUCCEEDED(rc)) + { + /* Machine is running? */ + MachineState_T machineState; + CHECK_ERROR(machine, COMGETTER(State)(&machineState)); + if ( SUCCEEDED(rc) + && (machineState != MachineState_Running)) + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine \"%s\" is not running (currently %s)!\n", + pszNameOrId, machineStateToName(machineState, false)); + } + else + rcExit = RTEXITCODE_FAILURE; - /* Machine is running? */ - MachineState_T machineState; - CHECK_ERROR_RET(machine, COMGETTER(State)(&machineState), 1); - if (machineState != MachineState_Running) + if (rcExit == RTEXITCODE_SUCCESS) { - RTMsgError("Machine \"%s\" is not running (currently %s)!\n", - pszNameOrId, machineStateToName(machineState, false)); - return VERR_VM_INVALID_VM_STATE; + /* + * Process standard options which are served by all commands. + */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--username", 'u', RTGETOPT_REQ_STRING }, + { "--passwordfile", 'p', RTGETOPT_REQ_STRING }, + { "--password", GETOPTDEF_COMMON_PASSWORD, RTGETOPT_REQ_STRING }, + { "--domain", 'd', RTGETOPT_REQ_STRING }, + { "--verbose", 'v', RTGETOPT_REQ_NOTHING } + }; + + /* + * Allocate per-command argv. This then only contains the specific arguments + * the command needs. + */ + pCtx->ppaArgv = (char**)RTMemAlloc(pArg->argc * sizeof(char*) + 1); + if (!pCtx->ppaArgv) + { + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Not enough memory for per-command argv\n"); + } + else + { + pCtx->iArgc = 0; + + int iArgIdx = 2; /* Skip VM name and guest control command */ + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, pArg->argc, pArg->argv, + s_aOptions, RT_ELEMENTS(s_aOptions), + iArgIdx, 0); + + while ( (ch = RTGetOpt(&GetState, &ValueUnion)) + && (rcExit == RTEXITCODE_SUCCESS)) + { + /* For options that require an argument, ValueUnion has received the value. */ + switch (ch) + { + case 'u': /* User name */ + if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) + pCtx->strUsername = ValueUnion.psz; + iArgIdx = GetState.iNext; + break; + + case GETOPTDEF_COMMON_PASSWORD: /* Password */ + if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) + { + if (pCtx->strPassword.isEmpty()) + pCtx->strPassword = ValueUnion.psz; + } + iArgIdx = GetState.iNext; + break; + + case 'p': /* Password file */ + { + if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) + rcExit = readPasswordFile(ValueUnion.psz, &pCtx->strPassword); + iArgIdx = GetState.iNext; + break; + } + + case 'd': /* domain */ + if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) + pCtx->strDomain = ValueUnion.psz; + iArgIdx = GetState.iNext; + break; + + case 'v': /* Verbose */ + pCtx->fVerbose = true; + iArgIdx = GetState.iNext; + break; + + case 'h': /* Help */ + errorGetOptEx(USAGE_GUESTCONTROL, uUsage, ch, &ValueUnion); + return RTEXITCODE_SYNTAX; + + default: + /* Simply skip; might be handled in a specific command + * handler later. */ + break; + + } /* switch */ + + int iArgDiff = GetState.iNext - iArgIdx; + if (iArgDiff) + { +#ifdef DEBUG_andy + RTPrintf("Not handled (iNext=%d, iArgsCur=%d):\n", GetState.iNext, iArgIdx); +#endif + for (int i = iArgIdx; i < GetState.iNext; i++) + { + char *pszArg = RTStrDup(pArg->argv[i]); + if (!pszArg) + { + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, + "Not enough memory for command line handling\n"); + break; + } + pCtx->ppaArgv[pCtx->iArgc] = pszArg; + pCtx->iArgc++; +#ifdef DEBUG_andy + RTPrintf("\targv[%d]=%s\n", i, pArg->argv[i]); +#endif + } + + iArgIdx = GetState.iNext; + } + + } /* while RTGetOpt */ + } } - do + /* + * Check for mandatory stuff. + */ + if (rcExit == RTEXITCODE_SUCCESS) { - /* Open a session for the VM. */ - CHECK_ERROR_BREAK(machine, LockMachine(pArg->session, LockType_Shared)); - /* Get the associated console. */ - ComPtr<IConsole> console; - CHECK_ERROR_BREAK(pArg->session, COMGETTER(Console)(console.asOutParam())); - /* ... and session machine. */ - ComPtr<IMachine> sessionMachine; - CHECK_ERROR_BREAK(pArg->session, COMGETTER(Machine)(sessionMachine.asOutParam())); - /* Get IGuest interface. */ - CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pGuest->asOutParam())); - } while (0); +#if 0 + RTPrintf("argc=%d\n", pCtx->iArgc); + for (int i = 0; i < pCtx->iArgc; i++) + RTPrintf("argv[%d]=%s\n", i, pCtx->ppaArgv[i]); +#endif + if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) + { + if (pCtx->strUsername.isEmpty()) + rcExit = errorSyntaxEx(USAGE_GUESTCONTROL, uUsage, "No user name specified!"); + } + } - if (FAILED(rc)) - ctrlUninitVM(pArg); - return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; + if (rcExit == RTEXITCODE_SUCCESS) + { + /* + * Build up a reasonable guest session name. Useful for identifying + * a specific session when listing / searching for them. + */ + char *pszSessionName; + if (0 >= RTStrAPrintf(&pszSessionName, + "[%RU32] VBoxManage Guest Control [%s] - %s", + RTProcSelf(), pszNameOrId, pszCmd)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "No enough memory for session name\n"); + + do + { + /* Open a session for the VM. */ + CHECK_ERROR_BREAK(machine, LockMachine(pArg->session, LockType_Shared)); + /* Get the associated console. */ + ComPtr<IConsole> console; + CHECK_ERROR_BREAK(pArg->session, COMGETTER(Console)(console.asOutParam())); + /* ... and session machine. */ + ComPtr<IMachine> sessionMachine; + CHECK_ERROR_BREAK(pArg->session, COMGETTER(Machine)(sessionMachine.asOutParam())); + /* Get IGuest interface. */ + CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pCtx->pGuest.asOutParam())); + if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) + { + if (pCtx->fVerbose) + RTPrintf("Opening guest session as user '%s' ...\n", pCtx->strUsername.c_str()); + + /* Open a guest session. */ + Assert(!pCtx->pGuest.isNull()); + CHECK_ERROR_BREAK(pCtx->pGuest, CreateSession(Bstr(pCtx->strUsername).raw(), + Bstr(pCtx->strPassword).raw(), + Bstr(pCtx->strDomain).raw(), + Bstr(pszSessionName).raw(), + pCtx->pGuestSession.asOutParam())); + + /* + * Wait for guest session to start. + */ + if (pCtx->fVerbose) + RTPrintf("Waiting for guest session to start ...\n"); + + com::SafeArray<GuestSessionWaitForFlag_T> aSessionWaitFlags; + aSessionWaitFlags.push_back(GuestSessionWaitForFlag_Start); + GuestSessionWaitResult_T sessionWaitResult; + CHECK_ERROR_BREAK(pCtx->pGuestSession, WaitForArray(ComSafeArrayAsInParam(aSessionWaitFlags), + /** @todo Make session handling timeouts configurable. */ + 30 * 1000, &sessionWaitResult)); + + if ( sessionWaitResult == GuestSessionWaitResult_Start + /* Note: This might happen when Guest Additions < 4.3 are installed which don't + * support dedicated guest sessions. */ + || sessionWaitResult == GuestSessionWaitResult_WaitFlagNotSupported) + { + CHECK_ERROR_BREAK(pCtx->pGuestSession, COMGETTER(Id)(&pCtx->uSessionID)); + if (pCtx->fVerbose) + RTPrintf("Guest session (ID %RU32) has been started\n", pCtx->uSessionID); + } + else + { + GuestSessionStatus_T sessionStatus; + CHECK_ERROR_BREAK(pCtx->pGuestSession, COMGETTER(Status)(&sessionStatus)); + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Error starting guest session (current status is: %s)\n", + ctrlSessionStatusToText(sessionStatus)); + break; + } + } + + if ( SUCCEEDED(rc) + && !(uFlags & CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER)) + { + ctrlSignalHandlerInstall(); + } + + } while (0); + + if (FAILED(rc)) + rcExit = RTEXITCODE_FAILURE; + + RTStrFree(pszSessionName); + } + + if (rcExit == RTEXITCODE_SUCCESS) + { + pCtx->handlerArg = *pArg; + pCtx->uFlags = uFlags; + } + else /* Clean up on failure. */ + ctrlUninitVM(pCtx, uFlags); + + return rcExit; } /** @@ -508,10 +1047,11 @@ static int ctrlInitVM(HandlerArg *pArg, const char *pszNameOrId, ComPtr<IGuest> * @return IPRT status code. * @param pProcess Pointer to appropriate process object. * @param pStrmOutput Where to write the data. - * @param hStream Where to read the data from. + * @param uHandle Handle where to read the data from. + * @param uTimeoutMS Timeout (in ms) to wait for the operation to complete. */ static int ctrlExecPrintOutput(IProcess *pProcess, PRTSTREAM pStrmOutput, - ULONG uHandle) + ULONG uHandle, ULONG uTimeoutMS) { AssertPtrReturn(pProcess, VERR_INVALID_POINTER); AssertPtrReturn(pStrmOutput, VERR_INVALID_POINTER); @@ -519,16 +1059,58 @@ static int ctrlExecPrintOutput(IProcess *pProcess, PRTSTREAM pStrmOutput, int vrc = VINF_SUCCESS; SafeArray<BYTE> aOutputData; - HRESULT rc = pProcess->Read(uHandle, _64K, 30 * 1000 /* 30s timeout. */, + HRESULT rc = pProcess->Read(uHandle, _64K, uTimeoutMS, ComSafeArrayAsOutParam(aOutputData)); if (FAILED(rc)) vrc = ctrlPrintError(pProcess, COM_IIDOF(IProcess)); else { - /** @todo implement the dos2unix/unix2dos conversions */ - vrc = RTStrmWrite(pStrmOutput, aOutputData.raw(), aOutputData.size()); - if (RT_FAILURE(vrc)) - RTMsgError("Unable to write output, rc=%Rrc\n", vrc); + size_t cbOutputData = aOutputData.size(); + if (cbOutputData > 0) + { + BYTE *pBuf = aOutputData.raw(); + AssertPtr(pBuf); + pBuf[cbOutputData - 1] = 0; /* Properly terminate buffer. */ + + /** @todo implement the dos2unix/unix2dos conversions */ + + /* + * If aOutputData is text data from the guest process' stdout or stderr, + * it has a platform dependent line ending. So standardize on + * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on + * Windows. Otherwise we end up with CR/CR/LF on Windows. + */ + + char *pszBufUTF8; + vrc = RTStrCurrentCPToUtf8(&pszBufUTF8, (const char*)aOutputData.raw()); + if (RT_SUCCESS(vrc)) + { + cbOutputData = strlen(pszBufUTF8); + + ULONG cbOutputDataPrint = cbOutputData; + for (char *s = pszBufUTF8, *d = s; + s - pszBufUTF8 < (ssize_t)cbOutputData; + s++, d++) + { + if (*s == '\r') + { + /* skip over CR, adjust destination */ + d--; + cbOutputDataPrint--; + } + else if (s != d) + *d = *s; + } + + vrc = RTStrmWrite(pStrmOutput, pszBufUTF8, cbOutputDataPrint); + if (RT_FAILURE(vrc)) + RTMsgError("Unable to write output, rc=%Rrc\n", vrc); + + RTStrFree(pszBufUTF8); + } + else + RTMsgError("Unable to convert output, rc=%Rrc\n", vrc); + } } return vrc; @@ -547,17 +1129,16 @@ inline RTMSINTERVAL ctrlExecGetRemainingTime(uint64_t u64StartMs, RTMSINTERVAL c if (!cMsTimeout || cMsTimeout == RT_INDEFINITE_WAIT) /* If no timeout specified, wait forever. */ return RT_INDEFINITE_WAIT; - uint32_t u64ElapsedMs = RTTimeMilliTS() - u64StartMs; + uint64_t u64ElapsedMs = RTTimeMilliTS() - u64StartMs; if (u64ElapsedMs >= cMsTimeout) return 0; - return cMsTimeout - u64ElapsedMs; + return cMsTimeout - (RTMSINTERVAL)u64ElapsedMs; } -/* <Missing documentation> */ -static int handleCtrlExecProgram(ComPtr<IGuest> pGuest, HandlerArg *pArg) +static DECLCALLBACK(RTEXITCODE) handleCtrlProcessExec(PGCTLCMDCTX pCtx) { - AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); /* * Parse arguments. @@ -570,352 +1151,374 @@ static int handleCtrlExecProgram(ComPtr<IGuest> pGuest, HandlerArg *pArg) { "--ignore-operhaned-processes", GETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES, RTGETOPT_REQ_NOTHING }, { "--image", 'i', RTGETOPT_REQ_STRING }, { "--no-profile", GETOPTDEF_EXEC_NO_PROFILE, RTGETOPT_REQ_NOTHING }, - { "--username", 'u', RTGETOPT_REQ_STRING }, - { "--passwordfile", 'p', RTGETOPT_REQ_STRING }, - { "--password", GETOPTDEF_EXEC_PASSWORD, RTGETOPT_REQ_STRING }, - { "--domain", 'd', RTGETOPT_REQ_STRING }, { "--timeout", 't', RTGETOPT_REQ_UINT32 }, { "--unix2dos", GETOPTDEF_EXEC_UNIX2DOS, RTGETOPT_REQ_NOTHING }, - { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, { "--wait-exit", GETOPTDEF_EXEC_WAITFOREXIT, RTGETOPT_REQ_NOTHING }, { "--wait-stdout", GETOPTDEF_EXEC_WAITFORSTDOUT, RTGETOPT_REQ_NOTHING }, { "--wait-stderr", GETOPTDEF_EXEC_WAITFORSTDERR, RTGETOPT_REQ_NOTHING } }; +#ifdef DEBUG_andy + RTPrintf("first=%d\n", pCtx->iFirstArgc); + for (int i=0; i<pCtx->iArgc;i++) + RTPrintf("\targv[%d]=%s\n", i, pCtx->ppaArgv[i]); +#endif + int ch; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; - RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, s_aOptions, RT_ELEMENTS(s_aOptions), + pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); - Utf8Str strCmd; + Utf8Str strCmd; com::SafeArray<ProcessCreateFlag_T> aCreateFlags; com::SafeArray<ProcessWaitForFlag_T> aWaitFlags; - com::SafeArray<IN_BSTR> args; - com::SafeArray<IN_BSTR> env; - Utf8Str strUsername; - Utf8Str strPassword; - Utf8Str strDomain; + com::SafeArray<IN_BSTR> aArgs; + com::SafeArray<IN_BSTR> aEnv; RTMSINTERVAL cMsTimeout = 0; OUTPUTTYPE eOutputType = OUTPUTTYPE_UNDEFINED; - bool fWaitForExit = false; - bool fVerbose = false; + bool fDetached = true; int vrc = VINF_SUCCESS; - /* Wait for process start in any case. This is useful for scripting VBoxManage - * when relying on its overall exit code. */ - aWaitFlags.push_back(ProcessWaitForFlag_Start); - - while ( (ch = RTGetOpt(&GetState, &ValueUnion)) - && RT_SUCCESS(vrc)) + try { - /* For options that require an argument, ValueUnion has received the value. */ - switch (ch) - { - case GETOPTDEF_EXEC_DOS2UNIX: - if (eOutputType != OUTPUTTYPE_UNDEFINED) - return errorSyntax(USAGE_GUESTCONTROL, "More than one output type (dos2unix/unix2dos) specified!"); - eOutputType = OUTPUTTYPE_DOS2UNIX; - break; + /* Wait for process start in any case. This is useful for scripting VBoxManage + * when relying on its overall exit code. */ + aWaitFlags.push_back(ProcessWaitForFlag_Start); - case 'e': /* Environment */ + while ( (ch = RTGetOpt(&GetState, &ValueUnion)) + && RT_SUCCESS(vrc)) + { + /* For options that require an argument, ValueUnion has received the value. */ + switch (ch) { - char **papszArg; - int cArgs; - - vrc = RTGetOptArgvFromString(&papszArg, &cArgs, ValueUnion.psz, NULL); - if (RT_FAILURE(vrc)) - return errorSyntax(USAGE_GUESTCONTROL, "Failed to parse environment value, rc=%Rrc", vrc); - for (int j = 0; j < cArgs; j++) - env.push_back(Bstr(papszArg[j]).raw()); - - RTGetOptArgvFree(papszArg); - break; - } - - case GETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES: - aCreateFlags.push_back(ProcessCreateFlag_IgnoreOrphanedProcesses); - break; + case GETOPTDEF_EXEC_DOS2UNIX: + if (eOutputType != OUTPUTTYPE_UNDEFINED) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC, + "More than one output type (dos2unix/unix2dos) specified!"); + eOutputType = OUTPUTTYPE_DOS2UNIX; + break; - case GETOPTDEF_EXEC_NO_PROFILE: - aCreateFlags.push_back(ProcessCreateFlag_NoProfile); - break; + case 'e': /* Environment */ + { + char **papszArg; + int cArgs; - case 'i': - strCmd = ValueUnion.psz; - break; + vrc = RTGetOptArgvFromString(&papszArg, &cArgs, ValueUnion.psz, NULL); + if (RT_FAILURE(vrc)) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC, + "Failed to parse environment value, rc=%Rrc", vrc); + for (int j = 0; j < cArgs; j++) + aEnv.push_back(Bstr(papszArg[j]).raw()); - /** @todo Add a hidden flag. */ + RTGetOptArgvFree(papszArg); + break; + } - case 'u': /* User name */ - strUsername = ValueUnion.psz; - break; + case GETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES: + aCreateFlags.push_back(ProcessCreateFlag_IgnoreOrphanedProcesses); + break; - case GETOPTDEF_EXEC_PASSWORD: /* Password */ - strPassword = ValueUnion.psz; - break; + case GETOPTDEF_EXEC_NO_PROFILE: + aCreateFlags.push_back(ProcessCreateFlag_NoProfile); + break; - case 'p': /* Password file */ - { - RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword); - if (rcExit != RTEXITCODE_SUCCESS) - return rcExit; - break; - } + case 'i': + strCmd = ValueUnion.psz; + break; - case 'd': /* domain */ - strDomain = ValueUnion.psz; - break; + /** @todo Add a hidden flag. */ - case 't': /* Timeout */ - cMsTimeout = ValueUnion.u32; - break; + case 't': /* Timeout */ + cMsTimeout = ValueUnion.u32; + break; - case GETOPTDEF_EXEC_UNIX2DOS: - if (eOutputType != OUTPUTTYPE_UNDEFINED) - return errorSyntax(USAGE_GUESTCONTROL, "More than one output type (dos2unix/unix2dos) specified!"); - eOutputType = OUTPUTTYPE_UNIX2DOS; - break; + case GETOPTDEF_EXEC_UNIX2DOS: + if (eOutputType != OUTPUTTYPE_UNDEFINED) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC, + "More than one output type (dos2unix/unix2dos) specified!"); + eOutputType = OUTPUTTYPE_UNIX2DOS; + break; - case 'v': /* Verbose */ - fVerbose = true; - break; + case GETOPTDEF_EXEC_WAITFOREXIT: + aWaitFlags.push_back(ProcessWaitForFlag_Terminate); + fDetached = false; + break; - case GETOPTDEF_EXEC_WAITFOREXIT: - aWaitFlags.push_back(ProcessWaitForFlag_Terminate); - fWaitForExit = true; - break; + case GETOPTDEF_EXEC_WAITFORSTDOUT: + aCreateFlags.push_back(ProcessCreateFlag_WaitForStdOut); + aWaitFlags.push_back(ProcessWaitForFlag_StdOut); + fDetached = false; + break; - case GETOPTDEF_EXEC_WAITFORSTDOUT: - aCreateFlags.push_back(ProcessCreateFlag_WaitForStdOut); - aWaitFlags.push_back(ProcessWaitForFlag_StdOut); - fWaitForExit = true; - break; + case GETOPTDEF_EXEC_WAITFORSTDERR: + aCreateFlags.push_back(ProcessCreateFlag_WaitForStdErr); + aWaitFlags.push_back(ProcessWaitForFlag_StdErr); + fDetached = false; + break; - case GETOPTDEF_EXEC_WAITFORSTDERR: - aCreateFlags.push_back(ProcessCreateFlag_WaitForStdErr); - aWaitFlags.push_back(ProcessWaitForFlag_StdErr); - fWaitForExit = true; - break; + case VINF_GETOPT_NOT_OPTION: + if (aArgs.size() == 0 && strCmd.isEmpty()) + strCmd = ValueUnion.psz; + else + aArgs.push_back(Bstr(ValueUnion.psz).raw()); + break; - case VINF_GETOPT_NOT_OPTION: - { - if (args.size() == 0 && strCmd.isEmpty()) - strCmd = ValueUnion.psz; - else - args.push_back(Bstr(ValueUnion.psz).raw()); - break; - } + default: + /* Note: Necessary for handling non-options (after --) which + * contain a single dash, e.g. "-- foo.exe -s". */ + if (GetState.argc == GetState.iNext) + aArgs.push_back(Bstr(ValueUnion.psz).raw()); + else + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC, ch, &ValueUnion); + break; - default: - return RTGetOptPrintError(ch, &ValueUnion); - } + } /* switch */ + } /* while RTGetOpt */ } - - if (strCmd.isEmpty()) - return errorSyntax(USAGE_GUESTCONTROL, "No command to execute specified!"); - - if (strUsername.isEmpty()) - return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!"); - - /* Any output conversion not supported yet! */ - if (eOutputType != OUTPUTTYPE_UNDEFINED) - return errorSyntax(USAGE_GUESTCONTROL, "Output conversion not implemented yet!"); - - /* - * Start with the real work. - */ - HRESULT rc = S_OK; - if (fVerbose) + catch (std::bad_alloc &) { - if (cMsTimeout == 0) - RTPrintf("Waiting for guest to start process ...\n"); - else - RTPrintf("Waiting for guest to start process (within %ums)\n", cMsTimeout); + vrc = VERR_NO_MEMORY; } - /** @todo This eventually needs a bit of revamping so that a valid session gets passed - * into this function already so that we don't need to mess around with closing - * the session all over the places below again. Later. */ - - ComPtr<IGuestSession> pGuestSession; - rc = pGuest->CreateSession(Bstr(strUsername).raw(), - Bstr(strPassword).raw(), - Bstr(strDomain).raw(), - Bstr("VBoxManage Guest Control Exec").raw(), - pGuestSession.asOutParam()); - if (FAILED(rc)) - { - ctrlPrintError(pGuest, COM_IIDOF(IGuest)); - return RTEXITCODE_FAILURE; - } + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize, rc=%Rrc\n", vrc); - /* Get current time stamp to later calculate rest of timeout left. */ - uint64_t u64StartMS = RTTimeMilliTS(); + if (strCmd.isEmpty()) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC, + "No command to execute specified!"); - /* - * Execute the process. - */ - ComPtr<IGuestProcess> pProcess; - rc = pGuestSession->ProcessCreate(Bstr(strCmd).raw(), - ComSafeArrayAsInParam(args), - ComSafeArrayAsInParam(env), - ComSafeArrayAsInParam(aCreateFlags), - cMsTimeout, - pProcess.asOutParam()); - if (FAILED(rc)) - { - ctrlPrintError(pGuestSession, COM_IIDOF(IGuestSession)); + /** @todo Any output conversion not supported yet! */ + if (eOutputType != OUTPUTTYPE_UNDEFINED) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC, + "Output conversion not implemented yet!"); - pGuestSession->Close(); - return RTEXITCODE_FAILURE; - } + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + HRESULT rc; - if (fWaitForExit) + try { - if (fVerbose) + do { - if (cMsTimeout) /* Wait with a certain timeout. */ + /* Adjust process creation flags if we don't want to wait for process termination. */ + if (fDetached) + aCreateFlags.push_back(ProcessCreateFlag_WaitForProcessStartOnly); + + /* Get current time stamp to later calculate rest of timeout left. */ + uint64_t u64StartMS = RTTimeMilliTS(); + + if (pCtx->fVerbose) { - /* Calculate timeout value left after process has been started. */ - uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS; - /* Is timeout still bigger than current difference? */ - if (cMsTimeout > u64Elapsed) - RTPrintf("Waiting for process to exit (%ums left) ...\n", cMsTimeout - u64Elapsed); + if (cMsTimeout == 0) + RTPrintf("Starting guest process ...\n"); else - RTPrintf("No time left to wait for process!\n"); /** @todo a bit misleading ... */ + RTPrintf("Starting guest process (within %ums)\n", cMsTimeout); } - else /* Wait forever. */ - RTPrintf("Waiting for process to exit ...\n"); - } - /** @todo does this need signal handling? there's no progress object etc etc */ - - vrc = RTStrmSetMode(g_pStdOut, 1 /* Binary mode */, -1 /* Code set, unchanged */); - if (RT_FAILURE(vrc)) - RTMsgError("Unable to set stdout's binary mode, rc=%Rrc\n", vrc); - vrc = RTStrmSetMode(g_pStdErr, 1 /* Binary mode */, -1 /* Code set, unchanged */); - if (RT_FAILURE(vrc)) - RTMsgError("Unable to set stderr's binary mode, rc=%Rrc\n", vrc); - - /* Wait for process to exit ... */ - RTMSINTERVAL cMsTimeLeft = 1; - bool fReadStdOut, fReadStdErr; - fReadStdOut = fReadStdErr = false; - bool fCompleted = false; - while (!fCompleted && cMsTimeLeft != 0) - { - cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout); + /* + * Execute the process. + */ + ComPtr<IGuestProcess> pProcess; + CHECK_ERROR_BREAK(pCtx->pGuestSession, ProcessCreate(Bstr(strCmd).raw(), + ComSafeArrayAsInParam(aArgs), + ComSafeArrayAsInParam(aEnv), + ComSafeArrayAsInParam(aCreateFlags), + ctrlExecGetRemainingTime(u64StartMS, cMsTimeout), + pProcess.asOutParam())); + + /* + * Explicitly wait for the guest process to be in a started + * state. + */ + com::SafeArray<ProcessWaitForFlag_T> aWaitStartFlags; + aWaitStartFlags.push_back(ProcessWaitForFlag_Start); ProcessWaitResult_T waitResult; - rc = pProcess->WaitForArray(ComSafeArrayAsInParam(aWaitFlags), cMsTimeLeft, &waitResult); - if (FAILED(rc)) - { - ctrlPrintError(pProcess, COM_IIDOF(IProcess)); + CHECK_ERROR_BREAK(pProcess, WaitForArray(ComSafeArrayAsInParam(aWaitStartFlags), + ctrlExecGetRemainingTime(u64StartMS, cMsTimeout), &waitResult)); + bool fCompleted = false; - pGuestSession->Close(); - return RTEXITCODE_FAILURE; + ULONG uPID = 0; + CHECK_ERROR_BREAK(pProcess, COMGETTER(PID)(&uPID)); + if (!fDetached && pCtx->fVerbose) + { + RTPrintf("Process '%s' (PID %RU32) started\n", + strCmd.c_str(), uPID); + } + else if (fDetached) /** @todo Introduce a --quiet option for not printing this. */ + { + /* Just print plain PID to make it easier for scripts + * invoking VBoxManage. */ + RTPrintf("[%RU32 - Session %RU32]\n", uPID, pCtx->uSessionID); } - switch (waitResult) + vrc = RTStrmSetMode(g_pStdOut, 1 /* Binary mode */, -1 /* Code set, unchanged */); + if (RT_FAILURE(vrc)) + RTMsgError("Unable to set stdout's binary mode, rc=%Rrc\n", vrc); + vrc = RTStrmSetMode(g_pStdErr, 1 /* Binary mode */, -1 /* Code set, unchanged */); + if (RT_FAILURE(vrc)) + RTMsgError("Unable to set stderr's binary mode, rc=%Rrc\n", vrc); + + /* Wait for process to exit ... */ + RTMSINTERVAL cMsTimeLeft = 1; /* Will be calculated. */ + bool fReadStdOut, fReadStdErr; + fReadStdOut = fReadStdErr = false; + + while ( !fCompleted + && !fDetached + && cMsTimeLeft != 0) { - case ProcessWaitResult_Start: + cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout); + CHECK_ERROR_BREAK(pProcess, WaitForArray(ComSafeArrayAsInParam(aWaitFlags), + 500 /* ms */, &waitResult)); + switch (waitResult) { - if (fVerbose) + case ProcessWaitResult_Start: { - ULONG uPID = 0; - rc = pProcess->COMGETTER(PID)(&uPID); - if (FAILED(rc)) - { - ctrlPrintError(pProcess, COM_IIDOF(IProcess)); + /* We're done here if we don't want to wait for termination. */ + if (fDetached) + fCompleted = true; - pGuestSession->Close(); - return RTEXITCODE_FAILURE; - } - RTPrintf("Process '%s' (PID: %u) started\n", strCmd.c_str(), uPID); + break; } - break; + case ProcessWaitResult_StdOut: + fReadStdOut = true; + break; + case ProcessWaitResult_StdErr: + fReadStdErr = true; + break; + case ProcessWaitResult_Terminate: + if (pCtx->fVerbose) + RTPrintf("Process terminated\n"); + /* Process terminated, we're done. */ + fCompleted = true; + break; + case ProcessWaitResult_WaitFlagNotSupported: + { + /* The guest does not support waiting for stdout/err, so + * yield to reduce the CPU load due to busy waiting. */ + RTThreadYield(); /* Optional, don't check rc. */ + + /* Try both, stdout + stderr. */ + fReadStdOut = fReadStdErr = true; + break; + } + case ProcessWaitResult_Timeout: + /* Fall through is intentional. */ + default: + /* Ignore all other results, let the timeout expire */ + break; } - case ProcessWaitResult_StdOut: - fReadStdOut = true; - break; - case ProcessWaitResult_StdErr: - fReadStdErr = true; - break; - case ProcessWaitResult_Terminate: - /* Process terminated, we're done */ - fCompleted = true; + + if (g_fGuestCtrlCanceled) break; - case ProcessWaitResult_WaitFlagNotSupported: + + if (fReadStdOut) /* Do we need to fetch stdout data? */ { - /* The guest does not support waiting for stdout/err, so - * yield to reduce the CPU load due to busy waiting. */ - RTThreadYield(); /* Optional, don't check rc. */ + cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout); + vrc = ctrlExecPrintOutput(pProcess, g_pStdOut, + 1 /* StdOut */, cMsTimeLeft); + fReadStdOut = false; + } - /* Try both, stdout + stderr. */ - fReadStdOut = fReadStdErr = true; - break; + if (fReadStdErr) /* Do we need to fetch stdout data? */ + { + cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout); + vrc = ctrlExecPrintOutput(pProcess, g_pStdErr, + 2 /* StdErr */, cMsTimeLeft); + fReadStdErr = false; } - default: - /* Ignore all other results, let the timeout expire */; - break; - } - if (fReadStdOut) /* Do we need to fetch stdout data? */ - { - vrc = ctrlExecPrintOutput(pProcess, g_pStdOut, 1 /* StdOut */); - fReadStdOut = false; - } + if ( RT_FAILURE(vrc) + || g_fGuestCtrlCanceled) + break; - if (fReadStdErr) /* Do we need to fetch stdout data? */ - { - vrc = ctrlExecPrintOutput(pProcess, g_pStdErr, 2 /* StdErr */); - fReadStdErr = false; - } + /* Did we run out of time? */ + if ( cMsTimeout + && RTTimeMilliTS() - u64StartMS > cMsTimeout) + break; - if (RT_FAILURE(vrc)) - break; + NativeEventQueue::getMainEventQueue()->processEventQueue(0); - } /* while */ + } /* while */ - /* Report status back to the user. */ - if (fCompleted) - { - ProcessStatus_T status; - rc = pProcess->COMGETTER(Status)(&status); - if (FAILED(rc)) + if (!fDetached) { - ctrlPrintError(pProcess, COM_IIDOF(IProcess)); + /* Report status back to the user. */ + if ( fCompleted + && !g_fGuestCtrlCanceled) + { - pGuestSession->Close(); - return RTEXITCODE_FAILURE; - } - LONG exitCode; - rc = pProcess->COMGETTER(ExitCode)(&exitCode); - if (FAILED(rc)) - { - ctrlPrintError(pProcess, COM_IIDOF(IProcess)); + { + ProcessStatus_T procStatus; + CHECK_ERROR_BREAK(pProcess, COMGETTER(Status)(&procStatus)); + if ( procStatus == ProcessStatus_TerminatedNormally + || procStatus == ProcessStatus_TerminatedAbnormally + || procStatus == ProcessStatus_TerminatedSignal) + { + LONG exitCode; + CHECK_ERROR_BREAK(pProcess, COMGETTER(ExitCode)(&exitCode)); + if (pCtx->fVerbose) + RTPrintf("Exit code=%u (Status=%u [%s])\n", + exitCode, procStatus, ctrlProcessStatusToText(procStatus)); - pGuestSession->Close(); - return RTEXITCODE_FAILURE; + rcExit = (RTEXITCODE)ctrlExecProcessStatusToExitCode(procStatus, exitCode); + } + else if (pCtx->fVerbose) + RTPrintf("Process now is in status [%s]\n", ctrlProcessStatusToText(procStatus)); + } + } + else + { + if (pCtx->fVerbose) + RTPrintf("Process execution aborted!\n"); + + rcExit = (RTEXITCODE)EXITCODEEXEC_TERM_ABEND; + } } - if (fVerbose) - RTPrintf("Exit code=%u (Status=%u [%s])\n", exitCode, status, ctrlExecProcessStatusToText(status)); - pGuestSession->Close(); - return ctrlExecProcessStatusToExitCode(status, exitCode); - } - else - { - if (fVerbose) - RTPrintf("Process execution aborted!\n"); + } while (0); + } + catch (std::bad_alloc) + { + rc = E_OUTOFMEMORY; + } - pGuestSession->Close(); - return EXITCODEEXEC_TERM_ABEND; - } + /* + * Decide what to do with the guest session. If we started a + * detached guest process (that is, without waiting for it to exit), + * don't close the guest session it is part of. + */ + bool fCloseSession = false; + if (SUCCEEDED(rc)) + { + /* + * Only close the guest session if we waited for the guest + * process to exit. Otherwise we wouldn't have any chance to + * access and/or kill detached guest process lateron. + */ + fCloseSession = !fDetached; + + /* + * If execution was aborted from the host side (signal handler), + * close the guest session in any case. + */ + if (g_fGuestCtrlCanceled) + fCloseSession = true; } + else /* Close session on error. */ + fCloseSession = true; + + if (!fCloseSession) + pCtx->uFlags |= CTLCMDCTX_FLAGS_SESSION_DETACH; - pGuestSession->Close(); + if ( rcExit == RTEXITCODE_SUCCESS + && FAILED(rc)) + { + /* Make sure an appropriate exit code is set on error. */ + rcExit = RTEXITCODE_FAILURE; + } - return RT_FAILURE(vrc) || FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS; + return rcExit; } /** @@ -923,44 +1526,36 @@ static int handleCtrlExecProgram(ComPtr<IGuest> pGuest, HandlerArg *pArg) * guest control copy functions. Needs to be free'd with ctrlCopyContextFree(). * * @return IPRT status code. - * @param pGuest Pointer to IGuest interface to use. - * @param fVerbose Flag indicating if we want to run in verbose mode. + * @param pCtx Pointer to command context. * @param fDryRun Flag indicating if we want to run a dry run only. * @param fHostToGuest Flag indicating if we want to copy from host to guest * or vice versa. - * @param strUsername Username of account to use on the guest side. - * @param strPassword Password of account to use. - * @param strDomain Domain of account to use. * @param strSessionName Session name (only for identification purposes). * @param ppContext Pointer which receives the allocated copy context. */ -static int ctrlCopyContextCreate(IGuest *pGuest, bool fVerbose, bool fDryRun, - bool fHostToGuest, const Utf8Str &strUsername, - const Utf8Str &strPassword, const Utf8Str &strDomain, +static int ctrlCopyContextCreate(PGCTLCMDCTX pCtx, bool fDryRun, bool fHostToGuest, const Utf8Str &strSessionName, PCOPYCONTEXT *ppContext) { - AssertPtrReturn(pGuest, VERR_INVALID_POINTER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); - PCOPYCONTEXT pContext = new COPYCONTEXT(); - AssertPtrReturn(pContext, VERR_NO_MEMORY); /**< @todo r=klaus cannot happen with new */ - ComPtr<IGuestSession> pGuestSession; - HRESULT rc = pGuest->CreateSession(Bstr(strUsername).raw(), - Bstr(strPassword).raw(), - Bstr(strDomain).raw(), - Bstr(strSessionName).raw(), - pGuestSession.asOutParam()); - if (FAILED(rc)) - return ctrlPrintError(pGuest, COM_IIDOF(IGuest)); + int vrc = VINF_SUCCESS; + try + { + PCOPYCONTEXT pContext = new COPYCONTEXT(); - pContext->fVerbose = fVerbose; - pContext->fDryRun = fDryRun; - pContext->fHostToGuest = fHostToGuest; - pContext->pGuestSession = pGuestSession; + pContext->pCmdCtx = pCtx; + pContext->fDryRun = fDryRun; + pContext->fHostToGuest = fHostToGuest; - *ppContext = pContext; + *ppContext = pContext; + } + catch (std::bad_alloc) + { + vrc = VERR_NO_MEMORY; + } - return VINF_SUCCESS; + return vrc; } /** @@ -971,11 +1566,7 @@ static int ctrlCopyContextCreate(IGuest *pGuest, bool fVerbose, bool fDryRun, static void ctrlCopyContextFree(PCOPYCONTEXT pContext) { if (pContext) - { - if (pContext->pGuestSession) - pContext->pGuestSession->Close(); delete pContext; - } } /** @@ -1129,7 +1720,7 @@ static int ctrlCopyDirCreate(PCOPYCONTEXT pContext, const char *pszDir) if ( RT_SUCCESS(vrc) && fDirExists) { - if (pContext->fVerbose) + if (pContext->pCmdCtx->fVerbose) RTPrintf("Directory \"%s\" already exists\n", pszDir); return VINF_SUCCESS; } @@ -1139,7 +1730,7 @@ static int ctrlCopyDirCreate(PCOPYCONTEXT pContext, const char *pszDir) if (RT_FAILURE(vrc)) return vrc; - if (pContext->fVerbose) + if (pContext->pCmdCtx->fVerbose) RTPrintf("Creating directory \"%s\" ...\n", pszDir); if (pContext->fDryRun) @@ -1149,10 +1740,10 @@ static int ctrlCopyDirCreate(PCOPYCONTEXT pContext, const char *pszDir) { SafeArray<DirectoryCreateFlag_T> dirCreateFlags; dirCreateFlags.push_back(DirectoryCreateFlag_Parents); - HRESULT rc = pContext->pGuestSession->DirectoryCreate(Bstr(pszDir).raw(), - 0700, ComSafeArrayAsInParam(dirCreateFlags)); + HRESULT rc = pContext->pCmdCtx->pGuestSession->DirectoryCreate(Bstr(pszDir).raw(), + 0700, ComSafeArrayAsInParam(dirCreateFlags)); if (FAILED(rc)) - vrc = ctrlPrintError(pContext->pGuestSession, COM_IIDOF(IGuestSession)); + vrc = ctrlPrintError(pContext->pCmdCtx->pGuestSession, COM_IIDOF(IGuestSession)); } else /* ... or on the host. */ { @@ -1168,13 +1759,13 @@ static int ctrlCopyDirCreate(PCOPYCONTEXT pContext, const char *pszDir) * * @return IPRT status code. * @param pContext Pointer to current copy control context. - * @param bGuest true if directory needs to be checked on the guest + * @param fOnGuest true if directory needs to be checked on the guest * or false if on the host. * @param pszDir Actual directory to check. * @param fExists Pointer which receives the result if the * given directory exists or not. */ -static int ctrlCopyDirExists(PCOPYCONTEXT pContext, bool bGuest, +static int ctrlCopyDirExists(PCOPYCONTEXT pContext, bool fOnGuest, const char *pszDir, bool *fExists) { AssertPtrReturn(pContext, false); @@ -1182,12 +1773,12 @@ static int ctrlCopyDirExists(PCOPYCONTEXT pContext, bool bGuest, AssertPtrReturn(fExists, false); int vrc = VINF_SUCCESS; - if (bGuest) + if (fOnGuest) { BOOL fDirExists = FALSE; - HRESULT rc = pContext->pGuestSession->DirectoryExists(Bstr(pszDir).raw(), &fDirExists); + HRESULT rc = pContext->pCmdCtx->pGuestSession->DirectoryExists(Bstr(pszDir).raw(), &fDirExists); if (FAILED(rc)) - vrc = ctrlPrintError(pContext->pGuestSession, COM_IIDOF(IGuestSession)); + vrc = ctrlPrintError(pContext->pCmdCtx->pGuestSession, COM_IIDOF(IGuestSession)); else *fExists = fDirExists ? true : false; } @@ -1252,9 +1843,9 @@ static int ctrlCopyFileExists(PCOPYCONTEXT pContext, bool bOnGuest, if (bOnGuest) { BOOL fFileExists = FALSE; - HRESULT rc = pContext->pGuestSession->FileExists(Bstr(pszFile).raw(), &fFileExists); + HRESULT rc = pContext->pCmdCtx->pGuestSession->FileExists(Bstr(pszFile).raw(), &fFileExists); if (FAILED(rc)) - vrc = ctrlPrintError(pContext->pGuestSession, COM_IIDOF(IGuestSession)); + vrc = ctrlPrintError(pContext->pCmdCtx->pGuestSession, COM_IIDOF(IGuestSession)); else *fExists = fFileExists ? true : false; } @@ -1315,7 +1906,7 @@ static int ctrlCopyFileToDest(PCOPYCONTEXT pContext, const char *pszFileSource, AssertPtrReturn(pszFileDest, VERR_INVALID_POINTER); AssertReturn(!fFlags, VERR_INVALID_POINTER); /* No flags supported yet. */ - if (pContext->fVerbose) + if (pContext->pCmdCtx->fVerbose) RTPrintf("Copying \"%s\" to \"%s\" ...\n", pszFileSource, pszFileDest); @@ -1328,26 +1919,25 @@ static int ctrlCopyFileToDest(PCOPYCONTEXT pContext, const char *pszFileSource, if (pContext->fHostToGuest) { SafeArray<CopyFileFlag_T> copyFlags; - rc = pContext->pGuestSession->CopyTo(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(), - ComSafeArrayAsInParam(copyFlags), - - pProgress.asOutParam()); + rc = pContext->pCmdCtx->pGuestSession->CopyTo(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(), + ComSafeArrayAsInParam(copyFlags), + pProgress.asOutParam()); } else { SafeArray<CopyFileFlag_T> copyFlags; - rc = pContext->pGuestSession->CopyFrom(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(), + rc = pContext->pCmdCtx->pGuestSession->CopyFrom(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(), ComSafeArrayAsInParam(copyFlags), pProgress.asOutParam()); } if (FAILED(rc)) { - vrc = ctrlPrintError(pContext->pGuestSession, COM_IIDOF(IGuestSession)); + vrc = ctrlPrintError(pContext->pCmdCtx->pGuestSession, COM_IIDOF(IGuestSession)); } else { - if (pContext->fVerbose) + if (pContext->pCmdCtx->fVerbose) rc = showProgress(pProgress); else rc = pProgress->WaitForCompletion(-1 /* No timeout */); @@ -1390,7 +1980,7 @@ static int ctrlCopyDirToGuest(PCOPYCONTEXT pContext, if (RT_SUCCESS(vrc) && pszSubDir) vrc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir); - if (pContext->fVerbose) + if (pContext->pCmdCtx->fVerbose) RTPrintf("Processing host directory: %s\n", szCurDir); /* Flag indicating whether the current directory was created on the @@ -1423,6 +2013,9 @@ static int ctrlCopyDirToGuest(PCOPYCONTEXT pContext, vrc = VINF_SUCCESS; break; } + /** @todo r=bird: This ain't gonna work on most UNIX file systems because + * enmType is RTDIRENTRYTYPE_UNKNOWN. This is clearly documented in + * RTDIRENTRY::enmType. For trunk, RTDirQueryUnknownType can be used. */ switch (DirEntry.enmType) { case RTDIRENTRYTYPE_DIRECTORY: @@ -1432,7 +2025,7 @@ static int ctrlCopyDirToGuest(PCOPYCONTEXT pContext, || !strcmp(DirEntry.szName, "..")) break; - if (pContext->fVerbose) + if (pContext->pCmdCtx->fVerbose) RTPrintf("Directory: %s\n", DirEntry.szName); if (fFlags & CopyFileFlag_Recursive) @@ -1476,7 +2069,7 @@ static int ctrlCopyDirToGuest(PCOPYCONTEXT pContext, break; /* Filter does not match. */ } - if (pContext->fVerbose) + if (pContext->pCmdCtx->fVerbose) RTPrintf("File: %s\n", DirEntry.szName); if (!fDirCreated) @@ -1559,7 +2152,7 @@ static int ctrlCopyDirToHost(PCOPYCONTEXT pContext, if (RT_FAILURE(vrc)) return vrc; - if (pContext->fVerbose) + if (pContext->pCmdCtx->fVerbose) RTPrintf("Processing guest directory: %s\n", szCurDir); /* Flag indicating whether the current directory was created on the @@ -1567,11 +2160,11 @@ static int ctrlCopyDirToHost(PCOPYCONTEXT pContext, bool fDirCreated = false; SafeArray<DirectoryOpenFlag_T> dirOpenFlags; /* No flags supported yet. */ ComPtr<IGuestDirectory> pDirectory; - HRESULT rc = pContext->pGuestSession->DirectoryOpen(Bstr(szCurDir).raw(), Bstr(pszFilter).raw(), + HRESULT rc = pContext->pCmdCtx->pGuestSession->DirectoryOpen(Bstr(szCurDir).raw(), Bstr(pszFilter).raw(), ComSafeArrayAsInParam(dirOpenFlags), pDirectory.asOutParam()); if (FAILED(rc)) - return ctrlPrintError(pContext->pGuestSession, COM_IIDOF(IGuestSession)); + return ctrlPrintError(pContext->pCmdCtx->pGuestSession, COM_IIDOF(IGuestSession)); ComPtr<IFsObjInfo> dirEntry; while (true) { @@ -1596,7 +2189,7 @@ static int ctrlCopyDirToHost(PCOPYCONTEXT pContext, || !strName.compare(Bstr(".."))) break; - if (pContext->fVerbose) + if (pContext->pCmdCtx->fVerbose) { Utf8Str strDir(strName); RTPrintf("Directory: %s\n", strDir.c_str()); @@ -1646,7 +2239,7 @@ static int ctrlCopyDirToHost(PCOPYCONTEXT pContext, break; /* Filter does not match. */ } - if (pContext->fVerbose) + if (pContext->pCmdCtx->fVerbose) RTPrintf("File: %s\n", strFile.c_str()); if (!fDirCreated) @@ -1803,10 +2396,9 @@ static void ctrlCopyFreeSourceRoot(char *pszSourceRoot) RTStrFree(pszSourceRoot); } -static int handleCtrlCopy(ComPtr<IGuest> guest, HandlerArg *pArg, - bool fHostToGuest) +static RTEXITCODE handleCtrlCopy(PGCTLCMDCTX pCtx, bool fHostToGuest) { - AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); /** @todo r=bird: This command isn't very unix friendly in general. mkdir * is much better (partly because it is much simpler of course). The main @@ -1826,30 +2418,22 @@ static int handleCtrlCopy(ComPtr<IGuest> guest, HandlerArg *pArg, { { "--dryrun", GETOPTDEF_COPY_DRYRUN, RTGETOPT_REQ_NOTHING }, { "--follow", GETOPTDEF_COPY_FOLLOW, RTGETOPT_REQ_NOTHING }, - { "--username", 'u', RTGETOPT_REQ_STRING }, - { "--passwordfile", 'p', RTGETOPT_REQ_STRING }, - { "--password", GETOPTDEF_COPY_PASSWORD, RTGETOPT_REQ_STRING }, - { "--domain", 'd', RTGETOPT_REQ_STRING }, { "--recursive", 'R', RTGETOPT_REQ_NOTHING }, - { "--target-directory", GETOPTDEF_COPY_TARGETDIR, RTGETOPT_REQ_STRING }, - { "--verbose", 'v', RTGETOPT_REQ_NOTHING } + { "--target-directory", GETOPTDEF_COPY_TARGETDIR, RTGETOPT_REQ_STRING } }; int ch; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; - RTGetOptInit(&GetState, pArg->argc, pArg->argv, - s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST); + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, + s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); Utf8Str strSource; Utf8Str strDest; - Utf8Str strUsername; - Utf8Str strPassword; - Utf8Str strDomain; uint32_t fFlags = CopyFileFlag_None; - bool fVerbose = false; bool fCopyRecursive = false; bool fDryRun = false; + uint32_t uUsage = fHostToGuest ? USAGE_GSTCTRL_COPYTO : USAGE_GSTCTRL_COPYFROM; SOURCEVEC vecSources; @@ -1867,26 +2451,6 @@ static int handleCtrlCopy(ComPtr<IGuest> guest, HandlerArg *pArg, fFlags |= CopyFileFlag_FollowLinks; break; - case 'u': /* User name */ - strUsername = ValueUnion.psz; - break; - - case GETOPTDEF_COPY_PASSWORD: /* Password */ - strPassword = ValueUnion.psz; - break; - - case 'p': /* Password file */ - { - RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword); - if (rcExit != RTEXITCODE_SUCCESS) - return rcExit; - break; - } - - case 'd': /* domain */ - strDomain = ValueUnion.psz; - break; - case 'R': /* Recursive processing */ fFlags |= CopyFileFlag_Recursive; break; @@ -1895,16 +2459,12 @@ static int handleCtrlCopy(ComPtr<IGuest> guest, HandlerArg *pArg, strDest = ValueUnion.psz; break; - case 'v': /* Verbose */ - fVerbose = true; - break; - case VINF_GETOPT_NOT_OPTION: { /* Last argument and no destination specified with * --target-directory yet? Then use the current * (= last) argument as destination. */ - if ( pArg->argc == GetState.iNext + if ( pCtx->iArgc == GetState.iNext && strDest.isEmpty()) { strDest = ValueUnion.psz; @@ -1918,26 +2478,22 @@ static int handleCtrlCopy(ComPtr<IGuest> guest, HandlerArg *pArg, } default: - return RTGetOptPrintError(ch, &ValueUnion); + return errorGetOptEx(USAGE_GUESTCONTROL, uUsage, ch, &ValueUnion); } } if (!vecSources.size()) - return errorSyntax(USAGE_GUESTCONTROL, - "No source(s) specified!"); + return errorSyntaxEx(USAGE_GUESTCONTROL, uUsage, + "No source(s) specified!"); if (strDest.isEmpty()) - return errorSyntax(USAGE_GUESTCONTROL, - "No destination specified!"); - - if (strUsername.isEmpty()) - return errorSyntax(USAGE_GUESTCONTROL, - "No user name specified!"); + return errorSyntaxEx(USAGE_GUESTCONTROL, uUsage, + "No destination specified!"); /* * Done parsing arguments, do some more preparations. */ - if (fVerbose) + if (pCtx->fVerbose) { if (fHostToGuest) RTPrintf("Copying from host to guest ...\n"); @@ -1950,9 +2506,10 @@ static int handleCtrlCopy(ComPtr<IGuest> guest, HandlerArg *pArg, /* Create the copy context -- it contains all information * the routines need to know when handling the actual copying. */ PCOPYCONTEXT pContext = NULL; - vrc = ctrlCopyContextCreate(guest, fVerbose, fDryRun, fHostToGuest, - strUsername, strPassword, strDomain, - "VBoxManage Guest Control Copy", &pContext); + vrc = ctrlCopyContextCreate(pCtx, fDryRun, fHostToGuest, + fHostToGuest + ? "VBoxManage Guest Control - Copy to guest" + : "VBoxManage Guest Control - Copy from guest", &pContext); if (RT_FAILURE(vrc)) { RTMsgError("Unable to create copy context, rc=%Rrc\n", vrc); @@ -2010,7 +2567,7 @@ static int handleCtrlCopy(ComPtr<IGuest> guest, HandlerArg *pArg, break; } - if (fVerbose) + if (pCtx->fVerbose) RTPrintf("Source: %s\n", pszSource); /** @todo Files with filter?? */ @@ -2097,40 +2654,34 @@ static int handleCtrlCopy(ComPtr<IGuest> guest, HandlerArg *pArg, return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; } -static int handleCtrlCreateDirectory(ComPtr<IGuest> pGuest, HandlerArg *pArg) +static DECLCALLBACK(RTEXITCODE) handleCtrlCopyFrom(PGCTLCMDCTX pCtx) { - AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); + return handleCtrlCopy(pCtx, false /* Guest to host */); +} + +static DECLCALLBACK(RTEXITCODE) handleCtrlCopyTo(PGCTLCMDCTX pCtx) +{ + return handleCtrlCopy(pCtx, true /* Host to guest */); +} + +static DECLCALLBACK(RTEXITCODE) handleCtrlCreateDirectory(PGCTLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); - /* - * Parse arguments. - * - * Note! No direct returns here, everyone must go thru the cleanup at the - * end of this function. - */ static const RTGETOPTDEF s_aOptions[] = { { "--mode", 'm', RTGETOPT_REQ_UINT32 }, - { "--parents", 'P', RTGETOPT_REQ_NOTHING }, - { "--username", 'u', RTGETOPT_REQ_STRING }, - { "--passwordfile", 'p', RTGETOPT_REQ_STRING }, - { "--password", GETOPTDEF_MKDIR_PASSWORD, RTGETOPT_REQ_STRING }, - { "--domain", 'd', RTGETOPT_REQ_STRING }, - { "--verbose", 'v', RTGETOPT_REQ_NOTHING } + { "--parents", 'P', RTGETOPT_REQ_NOTHING } }; int ch; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; - RTGetOptInit(&GetState, pArg->argc, pArg->argv, - s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST); + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, + s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); - Utf8Str strUsername; - Utf8Str strPassword; - Utf8Str strDomain; SafeArray<DirectoryCreateFlag_T> dirCreateFlags; uint32_t fDirMode = 0; /* Default mode. */ - bool fVerbose = false; - DESTDIRMAP mapDirs; while ((ch = RTGetOpt(&GetState, &ValueUnion))) @@ -2146,124 +2697,353 @@ static int handleCtrlCreateDirectory(ComPtr<IGuest> pGuest, HandlerArg *pArg) dirCreateFlags.push_back(DirectoryCreateFlag_Parents); break; - case 'u': /* User name */ - strUsername = ValueUnion.psz; + case VINF_GETOPT_NOT_OPTION: + mapDirs[ValueUnion.psz]; /* Add destination directory to map. */ break; - case GETOPTDEF_MKDIR_PASSWORD: /* Password */ - strPassword = ValueUnion.psz; - break; + default: + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CREATEDIR, ch, &ValueUnion); + } + } - case 'p': /* Password file */ - { - RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword); - if (rcExit != RTEXITCODE_SUCCESS) - return rcExit; - break; - } + uint32_t cDirs = mapDirs.size(); + if (!cDirs) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CREATEDIR, + "No directory to create specified!"); - case 'd': /* domain */ - strDomain = ValueUnion.psz; - break; + /* + * Create the directories. + */ + HRESULT rc = S_OK; + if (pCtx->fVerbose && cDirs) + RTPrintf("Creating %RU32 directories ...\n", cDirs); + + DESTDIRMAPITER it = mapDirs.begin(); + while ( (it != mapDirs.end()) + && !g_fGuestCtrlCanceled) + { + if (pCtx->fVerbose) + RTPrintf("Creating directory \"%s\" ...\n", it->first.c_str()); + + CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryCreate(Bstr(it->first).raw(), + fDirMode, ComSafeArrayAsInParam(dirCreateFlags))); + it++; + } + + return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS; +} + +static DECLCALLBACK(RTEXITCODE) handleCtrlRemoveDirectory(PGCTLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + static const RTGETOPTDEF s_aOptions[] = + { + { "--recursive", 'R', RTGETOPT_REQ_NOTHING } + }; + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, + s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); + + bool fRecursive = false; + DESTDIRMAP mapDirs; - case 'v': /* Verbose */ - fVerbose = true; + while ((ch = RTGetOpt(&GetState, &ValueUnion))) + { + /* For options that require an argument, ValueUnion has received the value. */ + switch (ch) + { + case 'R': + fRecursive = true; break; case VINF_GETOPT_NOT_OPTION: - { mapDirs[ValueUnion.psz]; /* Add destination directory to map. */ break; - } default: - return RTGetOptPrintError(ch, &ValueUnion); + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_REMOVEDIR, ch, &ValueUnion); } } uint32_t cDirs = mapDirs.size(); if (!cDirs) - return errorSyntax(USAGE_GUESTCONTROL, "No directory to create specified!"); - - if (strUsername.isEmpty()) - return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!"); + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_REMOVEDIR, + "No directory to remove specified!"); /* - * Create the directories. + * Remove the directories. */ - HRESULT hrc = S_OK; - if (fVerbose && cDirs) - RTPrintf("Creating %u directories ...\n", cDirs); - - ComPtr<IGuestSession> pGuestSession; - hrc = pGuest->CreateSession(Bstr(strUsername).raw(), - Bstr(strPassword).raw(), - Bstr(strDomain).raw(), - Bstr("VBoxManage Guest Control MkDir").raw(), - pGuestSession.asOutParam()); - if (FAILED(hrc)) - return ctrlPrintError(pGuest, COM_IIDOF(IGuest)); + HRESULT rc = S_OK; + if (pCtx->fVerbose && cDirs) + RTPrintf("Removing %RU32 directories ...\n", cDirs); DESTDIRMAPITER it = mapDirs.begin(); - while (it != mapDirs.end()) + while ( (it != mapDirs.end()) + && !g_fGuestCtrlCanceled) { - if (fVerbose) - RTPrintf("Creating directory \"%s\" ...\n", it->first.c_str()); + if (pCtx->fVerbose) + RTPrintf("%s directory \"%s\" ...\n", + fRecursive ? "Recursively removing" : "Removing", + it->first.c_str()); + try + { + if (fRecursive) + { + com::SafeArray<DirectoryRemoveRecFlag_T> aRemRecFlags; + /** @todo Make flags configurable. */ + aRemRecFlags.push_back(DirectoryRemoveRecFlag_ContentAndDir); + + ComPtr<IProgress> pProgress; + CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryRemoveRecursive(Bstr(it->first).raw(), + ComSafeArrayAsInParam(aRemRecFlags), + pProgress.asOutParam())); + if (pCtx->fVerbose) + rc = showProgress(pProgress); + else + rc = pProgress->WaitForCompletion(-1 /* No timeout */); + if (SUCCEEDED(rc)) + CHECK_PROGRESS_ERROR(pProgress, ("Directory deletion failed")); - hrc = pGuestSession->DirectoryCreate(Bstr(it->first).raw(), fDirMode, ComSafeArrayAsInParam(dirCreateFlags)); - if (FAILED(hrc)) + pProgress.setNull(); + } + else + CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryRemove(Bstr(it->first).raw())); + } + catch (std::bad_alloc) { - ctrlPrintError(pGuest, COM_IIDOF(IGuestSession)); /* Return code ignored, save original rc. */ + rc = E_OUTOFMEMORY; break; } it++; } - if (!pGuestSession.isNull()) - pGuestSession->Close(); + return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS; +} + +static DECLCALLBACK(RTEXITCODE) handleCtrlRemoveFile(PGCTLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, + NULL /* s_aOptions */, 0 /* RT_ELEMENTS(s_aOptions) */, + pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); + + DESTDIRMAP mapDirs; + + while ((ch = RTGetOpt(&GetState, &ValueUnion))) + { + /* For options that require an argument, ValueUnion has received the value. */ + switch (ch) + { + case VINF_GETOPT_NOT_OPTION: + mapDirs[ValueUnion.psz]; /* Add destination directory to map. */ + break; + + default: + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_REMOVEFILE, ch, &ValueUnion); + } + } + + uint32_t cFiles = mapDirs.size(); + if (!cFiles) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_REMOVEFILE, + "No file to remove specified!"); + + /* + * Create the directories. + */ + HRESULT rc = S_OK; + if (pCtx->fVerbose && cFiles) + RTPrintf("Removing %RU32 file(s) ...\n", cFiles); + + DESTDIRMAPITER it = mapDirs.begin(); + while ( (it != mapDirs.end()) + && !g_fGuestCtrlCanceled) + { + if (pCtx->fVerbose) + RTPrintf("Removing file \"%s\" ...\n", it->first.c_str()); - return FAILED(hrc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS; + CHECK_ERROR_BREAK(pCtx->pGuestSession, FileRemove(Bstr(it->first).raw())); + + it++; + } + + return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS; } -static int handleCtrlCreateTemp(ComPtr<IGuest> pGuest, HandlerArg *pArg) +static DECLCALLBACK(RTEXITCODE) handleCtrlRename(PGCTLCMDCTX pCtx) { - AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + static const RTGETOPTDEF s_aOptions[] = { 0 }; + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, + NULL /*s_aOptions*/, 0 /*RT_ELEMENTS(s_aOptions)*/, pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); + + int vrc = VINF_SUCCESS; + + bool fDryrun = false; + std::vector< Utf8Str > vecSources; + Utf8Str strDest; + com::SafeArray<PathRenameFlag_T> aRenameFlags; + + try + { + /** @todo Make flags configurable. */ + aRenameFlags.push_back(PathRenameFlag_NoReplace); + + while ( (ch = RTGetOpt(&GetState, &ValueUnion)) + && RT_SUCCESS(vrc)) + { + /* For options that require an argument, ValueUnion has received the value. */ + switch (ch) + { + /** @todo Implement a --dryrun command. */ + /** @todo Implement rename flags. */ + + case VINF_GETOPT_NOT_OPTION: + vecSources.push_back(Utf8Str(ValueUnion.psz)); + strDest = ValueUnion.psz; + break; + + default: + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RENAME, ch, &ValueUnion); + } + } + } + catch (std::bad_alloc) + { + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize, rc=%Rrc\n", vrc); + + uint32_t cSources = vecSources.size(); + if (!cSources) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RENAME, + "No source(s) to move specified!"); + if (cSources < 2) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RENAME, + "No destination specified!"); + + /* Delete last element, which now is the destination. */ + vecSources.pop_back(); + cSources = vecSources.size(); + + HRESULT rc = S_OK; + + if (cSources > 1) + { + ComPtr<IGuestFsObjInfo> pFsObjInfo; + rc = pCtx->pGuestSession->DirectoryQueryInfo(Bstr(strDest).raw(), pFsObjInfo.asOutParam()); + if (FAILED(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Destination must be a directory when specifying multiple sources\n"); + } /* - * Parse arguments. - * - * Note! No direct returns here, everyone must go thru the cleanup at the - * end of this function. + * Rename (move) the entries. */ + if (pCtx->fVerbose && cSources) + RTPrintf("Renaming %RU32 %s ...\n", cSources, + cSources > 1 + ? "entries" : "entry"); + + std::vector< Utf8Str >::iterator it = vecSources.begin(); + while ( (it != vecSources.end()) + && !g_fGuestCtrlCanceled) + { + bool fSourceIsDirectory = false; + Utf8Str strCurSource = (*it); + Utf8Str strCurDest = strDest; + + /** @todo Slooooow, but works for now. */ + ComPtr<IGuestFsObjInfo> pFsObjInfo; + rc = pCtx->pGuestSession->FileQueryInfo(Bstr(strCurSource).raw(), pFsObjInfo.asOutParam()); + if (FAILED(rc)) + { + rc = pCtx->pGuestSession->DirectoryQueryInfo(Bstr(strCurSource).raw(), pFsObjInfo.asOutParam()); + fSourceIsDirectory = SUCCEEDED(rc); + } + if (FAILED(rc)) + { + if (pCtx->fVerbose) + RTPrintf("Warning: Cannot stat for element \"%s\": No such element\n", + strCurSource.c_str()); + it++; + continue; /* Skip. */ + } + + if (pCtx->fVerbose) + RTPrintf("Renaming %s \"%s\" to \"%s\" ...\n", + fSourceIsDirectory ? "directory" : "file", + strCurSource.c_str(), strCurDest.c_str()); + + if (!fDryrun) + { + if (fSourceIsDirectory) + { + CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryRename(Bstr(strCurSource).raw(), + Bstr(strCurDest).raw(), + ComSafeArrayAsInParam(aRenameFlags))); + + /* Break here, since it makes no sense to rename mroe than one source to + * the same directory. */ + it = vecSources.end(); + break; + } + else + CHECK_ERROR_BREAK(pCtx->pGuestSession, FileRename(Bstr(strCurSource).raw(), + Bstr(strCurDest).raw(), + ComSafeArrayAsInParam(aRenameFlags))); + } + + it++; + } + + if ( (it != vecSources.end()) + && pCtx->fVerbose) + { + RTPrintf("Warning: Not all sources were renamed\n"); + } + + return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS; +} + +static DECLCALLBACK(RTEXITCODE) handleCtrlCreateTemp(PGCTLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + static const RTGETOPTDEF s_aOptions[] = { { "--mode", 'm', RTGETOPT_REQ_UINT32 }, { "--directory", 'D', RTGETOPT_REQ_NOTHING }, { "--secure", 's', RTGETOPT_REQ_NOTHING }, - { "--tmpdir", 't', RTGETOPT_REQ_STRING }, - { "--username", 'u', RTGETOPT_REQ_STRING }, - { "--passwordfile", 'p', RTGETOPT_REQ_STRING }, - { "--password", GETOPTDEF_MKDIR_PASSWORD, RTGETOPT_REQ_STRING }, - { "--domain", 'd', RTGETOPT_REQ_STRING }, - { "--verbose", 'v', RTGETOPT_REQ_NOTHING } + { "--tmpdir", 't', RTGETOPT_REQ_STRING } }; int ch; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; - RTGetOptInit(&GetState, pArg->argc, pArg->argv, - s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST); + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, + s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); - Utf8Str strUsername; - Utf8Str strPassword; - Utf8Str strDomain; Utf8Str strTemplate; uint32_t fMode = 0; /* Default mode. */ bool fDirectory = false; bool fSecure = false; Utf8Str strTempDir; - bool fVerbose = false; DESTDIRMAP mapDirs; @@ -2288,59 +3068,33 @@ static int handleCtrlCreateTemp(ComPtr<IGuest> pGuest, HandlerArg *pArg) strTempDir = ValueUnion.psz; break; - case 'u': /* User name */ - strUsername = ValueUnion.psz; - break; - - case GETOPTDEF_MKDIR_PASSWORD: /* Password */ - strPassword = ValueUnion.psz; - break; - - case 'p': /* Password file */ - { - RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword); - if (rcExit != RTEXITCODE_SUCCESS) - return rcExit; - break; - } - - case 'd': /* domain */ - strDomain = ValueUnion.psz; - break; - - case 'v': /* Verbose */ - fVerbose = true; - break; - case VINF_GETOPT_NOT_OPTION: { if (strTemplate.isEmpty()) strTemplate = ValueUnion.psz; else - return errorSyntax(USAGE_GUESTCONTROL, - "More than one template specified!\n"); + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CREATETEMP, + "More than one template specified!\n"); break; } default: - return RTGetOptPrintError(ch, &ValueUnion); + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CREATETEMP, ch, &ValueUnion); } } if (strTemplate.isEmpty()) - return errorSyntax(USAGE_GUESTCONTROL, "No template specified!"); - - if (strUsername.isEmpty()) - return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!"); + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CREATETEMP, + "No template specified!"); if (!fDirectory) - return errorSyntax(USAGE_GUESTCONTROL, "Creating temporary files is currently not supported!"); + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CREATETEMP, + "Creating temporary files is currently not supported!"); /* * Create the directories. */ - HRESULT hrc = S_OK; - if (fVerbose) + if (pCtx->fVerbose) { if (fDirectory && !strTempDir.isEmpty()) RTPrintf("Creating temporary directory from template '%s' in directory '%s' ...\n", @@ -2356,63 +3110,40 @@ static int handleCtrlCreateTemp(ComPtr<IGuest> pGuest, HandlerArg *pArg) strTemplate.c_str()); } - ComPtr<IGuestSession> pGuestSession; - hrc = pGuest->CreateSession(Bstr(strUsername).raw(), - Bstr(strPassword).raw(), - Bstr(strDomain).raw(), - Bstr("VBoxManage Guest Control MkTemp").raw(), - pGuestSession.asOutParam()); - if (FAILED(hrc)) - return ctrlPrintError(pGuest, COM_IIDOF(IGuest)); - + HRESULT rc = S_OK; if (fDirectory) { Bstr directory; - hrc = pGuestSession->DirectoryCreateTemp(Bstr(strTemplate).raw(), - fMode, Bstr(strTempDir).raw(), - fSecure, - directory.asOutParam()); - if (SUCCEEDED(hrc)) + CHECK_ERROR(pCtx->pGuestSession, DirectoryCreateTemp(Bstr(strTemplate).raw(), + fMode, Bstr(strTempDir).raw(), + fSecure, + directory.asOutParam())); + if (SUCCEEDED(rc)) RTPrintf("Directory name: %ls\n", directory.raw()); } // else - temporary file not yet implemented - if (FAILED(hrc)) - ctrlPrintError(pGuest, COM_IIDOF(IGuestSession)); /* Return code ignored, save original rc. */ - if (!pGuestSession.isNull()) - pGuestSession->Close(); - - return FAILED(hrc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS; + return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS; } -static int handleCtrlStat(ComPtr<IGuest> pGuest, HandlerArg *pArg) +static DECLCALLBACK(RTEXITCODE) handleCtrlStat(PGCTLCMDCTX pCtx) { - AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); static const RTGETOPTDEF s_aOptions[] = { { "--dereference", 'L', RTGETOPT_REQ_NOTHING }, { "--file-system", 'f', RTGETOPT_REQ_NOTHING }, { "--format", 'c', RTGETOPT_REQ_STRING }, - { "--username", 'u', RTGETOPT_REQ_STRING }, - { "--passwordfile", 'p', RTGETOPT_REQ_STRING }, - { "--password", GETOPTDEF_STAT_PASSWORD, RTGETOPT_REQ_STRING }, - { "--domain", 'd', RTGETOPT_REQ_STRING }, - { "--terse", 't', RTGETOPT_REQ_NOTHING }, - { "--verbose", 'v', RTGETOPT_REQ_NOTHING } + { "--terse", 't', RTGETOPT_REQ_NOTHING } }; int ch; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; - RTGetOptInit(&GetState, pArg->argc, pArg->argv, - s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST); - - Utf8Str strUsername; - Utf8Str strPassword; - Utf8Str strDomain; + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, + s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); - bool fVerbose = false; DESTDIRMAP mapObjs; while ((ch = RTGetOpt(&GetState, &ValueUnion))) @@ -2420,85 +3151,49 @@ static int handleCtrlStat(ComPtr<IGuest> pGuest, HandlerArg *pArg) /* For options that require an argument, ValueUnion has received the value. */ switch (ch) { - case 'u': /* User name */ - strUsername = ValueUnion.psz; - break; - - case GETOPTDEF_STAT_PASSWORD: /* Password */ - strPassword = ValueUnion.psz; - break; - - case 'p': /* Password file */ - { - RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword); - if (rcExit != RTEXITCODE_SUCCESS) - return rcExit; - break; - } - - case 'd': /* domain */ - strDomain = ValueUnion.psz; - break; - case 'L': /* Dereference */ case 'f': /* File-system */ case 'c': /* Format */ case 't': /* Terse */ - return errorSyntax(USAGE_GUESTCONTROL, "Command \"%s\" not implemented yet!", - ValueUnion.psz); - break; /* Never reached. */ - - case 'v': /* Verbose */ - fVerbose = true; - break; + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_STAT, + "Command \"%s\" not implemented yet!", ValueUnion.psz); case VINF_GETOPT_NOT_OPTION: - { mapObjs[ValueUnion.psz]; /* Add element to check to map. */ break; - } default: - return RTGetOptPrintError(ch, &ValueUnion); + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_STAT, ch, &ValueUnion); } } uint32_t cObjs = mapObjs.size(); if (!cObjs) - return errorSyntax(USAGE_GUESTCONTROL, "No element(s) to check specified!"); + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_STAT, + "No element(s) to check specified!"); - if (strUsername.isEmpty()) - return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!"); - - ComPtr<IGuestSession> pGuestSession; - HRESULT hrc = pGuest->CreateSession(Bstr(strUsername).raw(), - Bstr(strPassword).raw(), - Bstr(strDomain).raw(), - Bstr("VBoxManage Guest Control Stat").raw(), - pGuestSession.asOutParam()); - if (FAILED(hrc)) - return ctrlPrintError(pGuest, COM_IIDOF(IGuest)); + HRESULT rc; /* - * Create the directories. + * Doing the checks. */ RTEXITCODE rcExit = RTEXITCODE_SUCCESS; DESTDIRMAPITER it = mapObjs.begin(); while (it != mapObjs.end()) { - if (fVerbose) + if (pCtx->fVerbose) RTPrintf("Checking for element \"%s\" ...\n", it->first.c_str()); ComPtr<IGuestFsObjInfo> pFsObjInfo; - hrc = pGuestSession->FileQueryInfo(Bstr(it->first).raw(), pFsObjInfo.asOutParam()); - if (FAILED(hrc)) - hrc = pGuestSession->DirectoryQueryInfo(Bstr(it->first).raw(), pFsObjInfo.asOutParam()); + rc = pCtx->pGuestSession->FileQueryInfo(Bstr(it->first).raw(), pFsObjInfo.asOutParam()); + if (FAILED(rc)) + rc = pCtx->pGuestSession->DirectoryQueryInfo(Bstr(it->first).raw(), pFsObjInfo.asOutParam()); - if (FAILED(hrc)) + if (FAILED(rc)) { /* If there's at least one element which does not exist on the guest, * drop out with exitcode 1. */ - if (fVerbose) + if (pCtx->fVerbose) RTPrintf("Cannot stat for element \"%s\": No such element\n", it->first.c_str()); rcExit = RTEXITCODE_FAILURE; @@ -2506,9 +3201,7 @@ static int handleCtrlStat(ComPtr<IGuest> pGuest, HandlerArg *pArg) else { FsObjType_T objType; - hrc = pFsObjInfo->COMGETTER(Type)(&objType); - if (FAILED(hrc)) - return ctrlPrintError(pGuest, COM_IIDOF(IGuestFsObjInfo)); + pFsObjInfo->COMGETTER(Type)(&objType); switch (objType) { case FsObjType_File: @@ -2534,35 +3227,31 @@ static int handleCtrlStat(ComPtr<IGuest> pGuest, HandlerArg *pArg) it++; } - if (!pGuestSession.isNull()) - pGuestSession->Close(); - return rcExit; } -static int handleCtrlUpdateAdditions(ComPtr<IGuest> guest, HandlerArg *pArg) +static DECLCALLBACK(RTEXITCODE) handleCtrlUpdateAdditions(PGCTLCMDCTX pCtx) { - AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); /* * Check the syntax. We can deduce the correct syntax from the number of * arguments. */ Utf8Str strSource; - bool fVerbose = false; + com::SafeArray<IN_BSTR> aArgs; bool fWaitStartOnly = false; static const RTGETOPTDEF s_aOptions[] = { { "--source", 's', RTGETOPT_REQ_STRING }, - { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, { "--wait-start", 'w', RTGETOPT_REQ_NOTHING } }; int ch; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; - RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0); + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, 0); int vrc = VINF_SUCCESS; while ( (ch = RTGetOpt(&GetState, &ValueUnion)) @@ -2574,27 +3263,30 @@ static int handleCtrlUpdateAdditions(ComPtr<IGuest> guest, HandlerArg *pArg) strSource = ValueUnion.psz; break; - case 'v': - fVerbose = true; - break; - case 'w': fWaitStartOnly = true; break; + case VINF_GETOPT_NOT_OPTION: + if (aArgs.size() == 0 && strSource.isEmpty()) + strSource = ValueUnion.psz; + else + aArgs.push_back(Bstr(ValueUnion.psz).raw()); + break; + default: - return RTGetOptPrintError(ch, &ValueUnion); + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_UPDATEADDS, ch, &ValueUnion); } } - if (fVerbose) + if (pCtx->fVerbose) RTPrintf("Updating Guest Additions ...\n"); HRESULT rc = S_OK; while (strSource.isEmpty()) { ComPtr<ISystemProperties> pProperties; - CHECK_ERROR_BREAK(pArg->virtualBox, COMGETTER(SystemProperties)(pProperties.asOutParam())); + CHECK_ERROR_BREAK(pCtx->handlerArg.virtualBox, COMGETTER(SystemProperties)(pProperties.asOutParam())); Bstr strISO; CHECK_ERROR_BREAK(pProperties, COMGETTER(DefaultAdditionsISO)(strISO.asOutParam())); strSource = strISO; @@ -2615,27 +3307,28 @@ static int handleCtrlUpdateAdditions(ComPtr<IGuest> guest, HandlerArg *pArg) if (RT_SUCCESS(vrc)) { - if (fVerbose) + if (pCtx->fVerbose) RTPrintf("Using source: %s\n", strSource.c_str()); com::SafeArray<AdditionsUpdateFlag_T> aUpdateFlags; if (fWaitStartOnly) { aUpdateFlags.push_back(AdditionsUpdateFlag_WaitForUpdateStartOnly); - if (fVerbose) + if (pCtx->fVerbose) RTPrintf("Preparing and waiting for Guest Additions installer to start ...\n"); } ComPtr<IProgress> pProgress; - CHECK_ERROR(guest, UpdateGuestAdditions(Bstr(strSource).raw(), + CHECK_ERROR(pCtx->pGuest, UpdateGuestAdditions(Bstr(strSource).raw(), + ComSafeArrayAsInParam(aArgs), /* Wait for whole update process to complete. */ ComSafeArrayAsInParam(aUpdateFlags), pProgress.asOutParam())); if (FAILED(rc)) - vrc = ctrlPrintError(guest, COM_IIDOF(IGuest)); + vrc = ctrlPrintError(pCtx->pGuest, COM_IIDOF(IGuest)); else { - if (fVerbose) + if (pCtx->fVerbose) rc = showProgress(pProgress); else rc = pProgress->WaitForCompletion(-1 /* No timeout */); @@ -2644,7 +3337,7 @@ static int handleCtrlUpdateAdditions(ComPtr<IGuest> guest, HandlerArg *pArg) CHECK_PROGRESS_ERROR(pProgress, ("Guest additions update failed")); vrc = ctrlPrintProgressError(pProgress); if ( RT_SUCCESS(vrc) - && fVerbose) + && pCtx->fVerbose) { RTPrintf("Guest Additions update successful\n"); } @@ -2654,6 +3347,569 @@ static int handleCtrlUpdateAdditions(ComPtr<IGuest> guest, HandlerArg *pArg) return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; } +static DECLCALLBACK(RTEXITCODE) handleCtrlList(PGCTLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + if (pCtx->iArgc < 1) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_LIST, + "Must specify a listing category"); + + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + + /** Use RTGetOpt here when handling command line args gets more complex. */ + + bool fListAll = false; + bool fListSessions = false; + bool fListProcesses = false; + bool fListFiles = false; + if ( !RTStrICmp(pCtx->ppaArgv[0], "sessions") + || !RTStrICmp(pCtx->ppaArgv[0], "sess")) + fListSessions = true; + else if ( !RTStrICmp(pCtx->ppaArgv[0], "processes") + || !RTStrICmp(pCtx->ppaArgv[0], "procs")) + fListSessions = fListProcesses = true; /* Showing processes implies showing sessions. */ + else if ( !RTStrICmp(pCtx->ppaArgv[0], "files")) + fListSessions = fListFiles = true; /* Showing files implies showing sessions. */ + else if (!RTStrICmp(pCtx->ppaArgv[0], "all")) + fListAll = true; + + /** @todo Handle "--verbose" using RTGetOpt. */ + /** @todo Do we need a machine-readable output here as well? */ + + if ( fListAll + || fListSessions) + { + HRESULT rc; + do + { + size_t cTotalProcs = 0; + size_t cTotalFiles = 0; + + SafeIfaceArray <IGuestSession> collSessions; + CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions))); + size_t cSessions = collSessions.size(); + + if (cSessions) + { + RTPrintf("Active guest sessions:\n"); + + /** @todo Make this output a bit prettier. No time now. */ + + for (size_t i = 0; i < cSessions; i++) + { + ComPtr<IGuestSession> pCurSession = collSessions[i]; + if (!pCurSession.isNull()) + { + ULONG uID; + CHECK_ERROR_BREAK(pCurSession, COMGETTER(Id)(&uID)); + Bstr strName; + CHECK_ERROR_BREAK(pCurSession, COMGETTER(Name)(strName.asOutParam())); + Bstr strUser; + CHECK_ERROR_BREAK(pCurSession, COMGETTER(User)(strUser.asOutParam())); + GuestSessionStatus_T sessionStatus; + CHECK_ERROR_BREAK(pCurSession, COMGETTER(Status)(&sessionStatus)); + RTPrintf("\n\tSession #%-3zu ID=%-3RU32 User=%-16ls Status=[%s] Name=%ls", + i, uID, strUser.raw(), ctrlSessionStatusToText(sessionStatus), strName.raw()); + + if ( fListAll + || fListProcesses) + { + SafeIfaceArray <IGuestProcess> collProcesses; + CHECK_ERROR_BREAK(pCurSession, COMGETTER(Processes)(ComSafeArrayAsOutParam(collProcesses))); + for (size_t a = 0; a < collProcesses.size(); a++) + { + ComPtr<IGuestProcess> pCurProcess = collProcesses[a]; + if (!pCurProcess.isNull()) + { + ULONG uPID; + CHECK_ERROR_BREAK(pCurProcess, COMGETTER(PID)(&uPID)); + Bstr strExecPath; + CHECK_ERROR_BREAK(pCurProcess, COMGETTER(ExecutablePath)(strExecPath.asOutParam())); + ProcessStatus_T procStatus; + CHECK_ERROR_BREAK(pCurProcess, COMGETTER(Status)(&procStatus)); + + RTPrintf("\n\t\tProcess #%-03zu PID=%-6RU32 Status=[%s] Command=%ls", + a, uPID, ctrlProcessStatusToText(procStatus), strExecPath.raw()); + } + } + + cTotalProcs += collProcesses.size(); + } + + if ( fListAll + || fListFiles) + { + SafeIfaceArray <IGuestFile> collFiles; + CHECK_ERROR_BREAK(pCurSession, COMGETTER(Files)(ComSafeArrayAsOutParam(collFiles))); + for (size_t a = 0; a < collFiles.size(); a++) + { + ComPtr<IGuestFile> pCurFile = collFiles[a]; + if (!pCurFile.isNull()) + { + CHECK_ERROR_BREAK(pCurFile, COMGETTER(Id)(&uID)); + CHECK_ERROR_BREAK(pCurFile, COMGETTER(FileName)(strName.asOutParam())); + FileStatus_T fileStatus; + CHECK_ERROR_BREAK(pCurFile, COMGETTER(Status)(&fileStatus)); + + RTPrintf("\n\t\tFile #%-03zu ID=%-6RU32 Status=[%s] Name=%ls", + a, uID, ctrlFileStatusToText(fileStatus), strName.raw()); + } + } + + cTotalFiles += collFiles.size(); + } + } + } + + RTPrintf("\n\nTotal guest sessions: %zu\n", collSessions.size()); + if (fListAll || fListProcesses) + RTPrintf("Total guest processes: %zu\n", cTotalProcs); + if (fListAll || fListFiles) + RTPrintf("Total guest files: %zu\n", cTotalFiles); + } + else + RTPrintf("No active guest sessions found\n"); + + } while (0); + + if (FAILED(rc)) + rcExit = RTEXITCODE_FAILURE; + } + else + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_LIST, + "Invalid listing category '%s", pCtx->ppaArgv[0]); + + return rcExit; +} + +static DECLCALLBACK(RTEXITCODE) handleCtrlProcessClose(PGCTLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + if (pCtx->iArgc < 1) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_PROCESS, + "Must specify at least a PID to close"); + + static const RTGETOPTDEF s_aOptions[] = + { + { "--session-id", 'i', RTGETOPT_REQ_UINT32 }, + { "--session-name", 'n', RTGETOPT_REQ_STRING } + }; + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, + s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); + + std::vector < uint32_t > vecPID; + ULONG ulSessionID = UINT32_MAX; + Utf8Str strSessionName; + + int vrc = VINF_SUCCESS; + + while ( (ch = RTGetOpt(&GetState, &ValueUnion)) + && RT_SUCCESS(vrc)) + { + /* For options that require an argument, ValueUnion has received the value. */ + switch (ch) + { + case 'n': /* Session name (or pattern) */ + strSessionName = ValueUnion.psz; + break; + + case 'i': /* Session ID */ + ulSessionID = ValueUnion.u32; + break; + + case VINF_GETOPT_NOT_OPTION: + if (pCtx->iArgc == GetState.iNext) + { + /* Treat every else specified as a PID to kill. */ + try + { + uint32_t uPID = RTStrToUInt32(ValueUnion.psz); + if (uPID) /** @todo Is this what we want? If specifying PID 0 + this is not going to work on most systems anyway. */ + vecPID.push_back(uPID); + else + vrc = VERR_INVALID_PARAMETER; + } + catch(std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; + } + } + break; + + default: + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_PROCESS, ch, &ValueUnion); + } + } + + if (vecPID.empty()) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_PROCESS, + "At least one PID must be specified to kill!"); + + if ( strSessionName.isEmpty() + && ulSessionID == UINT32_MAX) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_PROCESS, + "No session ID specified!"); + + if ( !strSessionName.isEmpty() + && ulSessionID != UINT32_MAX) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_PROCESS, + "Either session ID or name (pattern) must be specified"); + + if (RT_FAILURE(vrc)) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_PROCESS, + "Invalid parameters specified"); + + HRESULT rc = S_OK; + + ComPtr<IGuestSession> pSession; + ComPtr<IGuestProcess> pProcess; + do + { + uint32_t uProcsTerminated = 0; + bool fSessionFound = false; + + SafeIfaceArray <IGuestSession> collSessions; + CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions))); + size_t cSessions = collSessions.size(); + + uint32_t uSessionsHandled = 0; + for (size_t i = 0; i < cSessions; i++) + { + pSession = collSessions[i]; + Assert(!pSession.isNull()); + + ULONG uID; /* Session ID */ + CHECK_ERROR_BREAK(pSession, COMGETTER(Id)(&uID)); + Bstr strName; + CHECK_ERROR_BREAK(pSession, COMGETTER(Name)(strName.asOutParam())); + Utf8Str strNameUtf8(strName); /* Session name */ + if (strSessionName.isEmpty()) /* Search by ID. Slow lookup. */ + { + fSessionFound = uID == ulSessionID; + } + else /* ... or by naming pattern. */ + { + if (RTStrSimplePatternMatch(strSessionName.c_str(), strNameUtf8.c_str())) + fSessionFound = true; + } + + if (fSessionFound) + { + AssertStmt(!pSession.isNull(), break); + uSessionsHandled++; + + SafeIfaceArray <IGuestProcess> collProcs; + CHECK_ERROR_BREAK(pSession, COMGETTER(Processes)(ComSafeArrayAsOutParam(collProcs))); + + size_t cProcs = collProcs.size(); + for (size_t p = 0; p < cProcs; p++) + { + pProcess = collProcs[p]; + Assert(!pProcess.isNull()); + + ULONG uPID; /* Process ID */ + CHECK_ERROR_BREAK(pProcess, COMGETTER(PID)(&uPID)); + + bool fProcFound = false; + for (size_t a = 0; a < vecPID.size(); a++) /* Slow, but works. */ + { + fProcFound = vecPID[a] == uPID; + if (fProcFound) + break; + } + + if (fProcFound) + { + if (pCtx->fVerbose) + RTPrintf("Terminating process (PID %RU32) (session ID %RU32) ...\n", + uPID, uID); + CHECK_ERROR_BREAK(pProcess, Terminate()); + uProcsTerminated++; + } + else + { + if (ulSessionID != UINT32_MAX) + RTPrintf("No matching process(es) for session ID %RU32 found\n", + ulSessionID); + } + + pProcess.setNull(); + } + + pSession.setNull(); + } + } + + if (!uSessionsHandled) + RTPrintf("No matching session(s) found\n"); + + if (uProcsTerminated) + RTPrintf("%RU32 %s terminated\n", + uProcsTerminated, uProcsTerminated == 1 ? "process" : "processes"); + + } while (0); + + pProcess.setNull(); + pSession.setNull(); + + return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static DECLCALLBACK(RTEXITCODE) handleCtrlProcess(PGCTLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + if (pCtx->iArgc < 1) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_PROCESS, + "Must specify an action"); + + /** Use RTGetOpt here when handling command line args gets more complex. */ + + if ( !RTStrICmp(pCtx->ppaArgv[0], "close") + || !RTStrICmp(pCtx->ppaArgv[0], "kill") + || !RTStrICmp(pCtx->ppaArgv[0], "terminate")) + { + pCtx->iFirstArgc++; /* Skip process action. */ + return handleCtrlProcessClose(pCtx); + } + + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_PROCESS, + "Invalid process action '%s'", pCtx->ppaArgv[0]); +} + +static DECLCALLBACK(RTEXITCODE) handleCtrlSessionClose(PGCTLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + if (pCtx->iArgc < 1) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_SESSION, + "Must specify at least a session to close"); + + static const RTGETOPTDEF s_aOptions[] = + { + { "--all", GETOPTDEF_SESSIONCLOSE_ALL, RTGETOPT_REQ_NOTHING }, + { "--session-id", 'i', RTGETOPT_REQ_UINT32 }, + { "--session-name", 'n', RTGETOPT_REQ_STRING } + }; + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, + s_aOptions, RT_ELEMENTS(s_aOptions), pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); + + ULONG ulSessionID = UINT32_MAX; + Utf8Str strSessionName; + + while ((ch = RTGetOpt(&GetState, &ValueUnion))) + { + /* For options that require an argument, ValueUnion has received the value. */ + switch (ch) + { + case 'n': /* Session name pattern */ + strSessionName = ValueUnion.psz; + break; + + case 'i': /* Session ID */ + ulSessionID = ValueUnion.u32; + break; + + case GETOPTDEF_SESSIONCLOSE_ALL: + strSessionName = "*"; + break; + + case VINF_GETOPT_NOT_OPTION: + /** @todo Supply a CSV list of IDs or patterns to close? */ + break; + + default: + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_SESSION, ch, &ValueUnion); + } + } + + if ( strSessionName.isEmpty() + && ulSessionID == UINT32_MAX) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_SESSION, + "No session ID specified!"); + + if ( !strSessionName.isEmpty() + && ulSessionID != UINT32_MAX) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_SESSION, + "Either session ID or name (pattern) must be specified"); + + HRESULT rc = S_OK; + + do + { + bool fSessionFound = false; + size_t cSessionsHandled = 0; + + SafeIfaceArray <IGuestSession> collSessions; + CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions))); + size_t cSessions = collSessions.size(); + + for (size_t i = 0; i < cSessions; i++) + { + ComPtr<IGuestSession> pSession = collSessions[i]; + Assert(!pSession.isNull()); + + ULONG uID; /* Session ID */ + CHECK_ERROR_BREAK(pSession, COMGETTER(Id)(&uID)); + Bstr strName; + CHECK_ERROR_BREAK(pSession, COMGETTER(Name)(strName.asOutParam())); + Utf8Str strNameUtf8(strName); /* Session name */ + + if (strSessionName.isEmpty()) /* Search by ID. Slow lookup. */ + { + fSessionFound = uID == ulSessionID; + } + else /* ... or by naming pattern. */ + { + if (RTStrSimplePatternMatch(strSessionName.c_str(), strNameUtf8.c_str())) + fSessionFound = true; + } + + if (fSessionFound) + { + cSessionsHandled++; + + Assert(!pSession.isNull()); + if (pCtx->fVerbose) + RTPrintf("Closing guest session ID=#%RU32 \"%s\" ...\n", + uID, strNameUtf8.c_str()); + CHECK_ERROR_BREAK(pSession, Close()); + if (pCtx->fVerbose) + RTPrintf("Guest session successfully closed\n"); + + pSession.setNull(); + } + } + + if (!cSessionsHandled) + { + RTPrintf("No guest session(s) found\n"); + rc = E_ABORT; /* To set exit code accordingly. */ + } + + } while (0); + + return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + +static DECLCALLBACK(RTEXITCODE) handleCtrlSession(PGCTLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + if (pCtx->iArgc < 1) + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_SESSION, + "Must specify an action"); + + /** Use RTGetOpt here when handling command line args gets more complex. */ + + if ( !RTStrICmp(pCtx->ppaArgv[0], "close") + || !RTStrICmp(pCtx->ppaArgv[0], "kill") + || !RTStrICmp(pCtx->ppaArgv[0], "terminate")) + { + pCtx->iFirstArgc++; /* Skip session action. */ + return handleCtrlSessionClose(pCtx); + } + + return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_SESSION, + "Invalid session action '%s'", pCtx->ppaArgv[0]); +} + +static DECLCALLBACK(RTEXITCODE) handleCtrlWatch(PGCTLCMDCTX pCtx) +{ + AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); + + /* + * Parse arguments. + */ + static const RTGETOPTDEF s_aOptions[] = { 0 }; + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, + NULL /*s_aOptions*/, 0 /*RT_ELEMENTS(s_aOptions)*/, pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); + + while ((ch = RTGetOpt(&GetState, &ValueUnion))) + { + /* For options that require an argument, ValueUnion has received the value. */ + switch (ch) + { + case VINF_GETOPT_NOT_OPTION: + break; + + default: + return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_WATCH, ch, &ValueUnion); + } + } + + /** @todo Specify categories to watch for. */ + /** @todo Specify a --timeout for waiting only for a certain amount of time? */ + + HRESULT rc; + + try + { + ComObjPtr<GuestEventListenerImpl> pGuestListener; + do + { + /* Listener creation. */ + pGuestListener.createObject(); + pGuestListener->init(new GuestEventListener()); + + /* Register for IGuest events. */ + ComPtr<IEventSource> es; + CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(EventSource)(es.asOutParam())); + com::SafeArray<VBoxEventType_T> eventTypes; + eventTypes.push_back(VBoxEventType_OnGuestSessionRegistered); + /** @todo Also register for VBoxEventType_OnGuestUserStateChanged on demand? */ + CHECK_ERROR_BREAK(es, RegisterListener(pGuestListener, ComSafeArrayAsInParam(eventTypes), + true /* Active listener */)); + /* Note: All other guest control events have to be registered + * as their corresponding objects appear. */ + + } while (0); + + if (pCtx->fVerbose) + RTPrintf("Waiting for events ...\n"); + + while (!g_fGuestCtrlCanceled) + { + /** @todo Timeout handling (see above)? */ + RTThreadSleep(10); + } + + if (pCtx->fVerbose) + RTPrintf("Signal caught, exiting ...\n"); + + if (!pGuestListener.isNull()) + { + /* Guest callback unregistration. */ + ComPtr<IEventSource> pES; + CHECK_ERROR(pCtx->pGuest, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR(pES, UnregisterListener(pGuestListener)); + pGuestListener.setNull(); + } + } + catch (std::bad_alloc &) + { + rc = E_OUTOFMEMORY; + } + + return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + /** * Access the guest control store. * @@ -2662,56 +3918,142 @@ static int handleCtrlUpdateAdditions(ComPtr<IGuest> guest, HandlerArg *pArg) */ int handleGuestControl(HandlerArg *pArg) { - AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); + AssertPtrReturn(pArg, VERR_INVALID_POINTER); #ifdef DEBUG_andy_disabled if (RT_FAILURE(tstTranslatePath())) return RTEXITCODE_FAILURE; #endif - HandlerArg arg = *pArg; - arg.argc = pArg->argc - 2; /* Skip VM name and sub command. */ - arg.argv = pArg->argv + 2; /* Same here. */ + /* pArg->argv[0] contains the VM name. */ + /* pArg->argv[1] contains the guest control command. */ + if (pArg->argc < 2) + return errorSyntax(USAGE_GUESTCONTROL, "No sub command specified!"); - ComPtr<IGuest> guest; - int vrc = ctrlInitVM(pArg, pArg->argv[0] /* VM Name */, &guest); - if (RT_SUCCESS(vrc)) + uint32_t uCmdCtxFlags = 0; + uint32_t uUsage; + GCTLCMD gctlCmd; + if ( !RTStrICmp(pArg->argv[1], "exec") + || !RTStrICmp(pArg->argv[1], "execute")) { - int rcExit; - if (pArg->argc < 2) - rcExit = errorSyntax(USAGE_GUESTCONTROL, "No sub command specified!"); - else if ( !strcmp(pArg->argv[1], "exec") - || !strcmp(pArg->argv[1], "execute")) - rcExit = handleCtrlExecProgram(guest, &arg); - else if (!strcmp(pArg->argv[1], "copyfrom")) - rcExit = handleCtrlCopy(guest, &arg, false /* Guest to host */); - else if ( !strcmp(pArg->argv[1], "copyto") - || !strcmp(pArg->argv[1], "cp")) - rcExit = handleCtrlCopy(guest, &arg, true /* Host to guest */); - else if ( !strcmp(pArg->argv[1], "createdirectory") - || !strcmp(pArg->argv[1], "createdir") - || !strcmp(pArg->argv[1], "mkdir") - || !strcmp(pArg->argv[1], "md")) - rcExit = handleCtrlCreateDirectory(guest, &arg); - else if ( !strcmp(pArg->argv[1], "createtemporary") - || !strcmp(pArg->argv[1], "createtemp") - || !strcmp(pArg->argv[1], "mktemp")) - rcExit = handleCtrlCreateTemp(guest, &arg); - else if ( !strcmp(pArg->argv[1], "stat")) - rcExit = handleCtrlStat(guest, &arg); - else if ( !strcmp(pArg->argv[1], "updateadditions") - || !strcmp(pArg->argv[1], "updateadds")) - rcExit = handleCtrlUpdateAdditions(guest, &arg); - /** @todo Implement a "sessions list" command to list all opened - * guest sessions along with their (friendly) names. */ - else - rcExit = errorSyntax(USAGE_GUESTCONTROL, "Unknown sub command '%s' specified!", pArg->argv[1]); + gctlCmd.pfnHandler = handleCtrlProcessExec; + uUsage = USAGE_GSTCTRL_EXEC; + } + else if (!RTStrICmp(pArg->argv[1], "copyfrom")) + { + gctlCmd.pfnHandler = handleCtrlCopyFrom; + uUsage = USAGE_GSTCTRL_COPYFROM; + } + else if ( !RTStrICmp(pArg->argv[1], "copyto") + || !RTStrICmp(pArg->argv[1], "cp")) + { + gctlCmd.pfnHandler = handleCtrlCopyTo; + uUsage = USAGE_GSTCTRL_COPYTO; + } + else if ( !RTStrICmp(pArg->argv[1], "createdirectory") + || !RTStrICmp(pArg->argv[1], "createdir") + || !RTStrICmp(pArg->argv[1], "mkdir") + || !RTStrICmp(pArg->argv[1], "md")) + { + gctlCmd.pfnHandler = handleCtrlCreateDirectory; + uUsage = USAGE_GSTCTRL_CREATEDIR; + } + else if ( !RTStrICmp(pArg->argv[1], "removedirectory") + || !RTStrICmp(pArg->argv[1], "removedir") + || !RTStrICmp(pArg->argv[1], "rmdir")) + { + gctlCmd.pfnHandler = handleCtrlRemoveDirectory; + uUsage = USAGE_GSTCTRL_REMOVEDIR; + } + else if ( !RTStrICmp(pArg->argv[1], "rm") + || !RTStrICmp(pArg->argv[1], "removefile")) + { + gctlCmd.pfnHandler = handleCtrlRemoveFile; + uUsage = USAGE_GSTCTRL_REMOVEFILE; + } + else if ( !RTStrICmp(pArg->argv[1], "ren") + || !RTStrICmp(pArg->argv[1], "rename") + || !RTStrICmp(pArg->argv[1], "mv")) + { + gctlCmd.pfnHandler = handleCtrlRename; + uUsage = USAGE_GSTCTRL_RENAME; + } + else if ( !RTStrICmp(pArg->argv[1], "createtemporary") + || !RTStrICmp(pArg->argv[1], "createtemp") + || !RTStrICmp(pArg->argv[1], "mktemp")) + { + gctlCmd.pfnHandler = handleCtrlCreateTemp; + uUsage = USAGE_GSTCTRL_CREATETEMP; + } + else if ( !RTStrICmp(pArg->argv[1], "kill") /* Linux. */ + || !RTStrICmp(pArg->argv[1], "pkill") /* Solaris / *BSD. */ + || !RTStrICmp(pArg->argv[1], "pskill")) /* SysInternals version. */ + { + /** @todo What about "taskkill" on Windows? */ + uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS + | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER; + gctlCmd.pfnHandler = handleCtrlProcessClose; + uUsage = USAGE_GSTCTRL_KILL; + } + /** @todo Implement "killall"? */ + else if ( !RTStrICmp(pArg->argv[1], "stat")) + { + gctlCmd.pfnHandler = handleCtrlStat; + uUsage = USAGE_GSTCTRL_STAT; + } + else if ( !RTStrICmp(pArg->argv[1], "updateadditions") + || !RTStrICmp(pArg->argv[1], "updateadds")) + { + uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS + | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER; + gctlCmd.pfnHandler = handleCtrlUpdateAdditions; + uUsage = USAGE_GSTCTRL_UPDATEADDS; + } + else if ( !RTStrICmp(pArg->argv[1], "list")) + { + uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS + | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER; + gctlCmd.pfnHandler = handleCtrlList; + uUsage = USAGE_GSTCTRL_LIST; + } + else if ( !RTStrICmp(pArg->argv[1], "session")) + { + uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS + | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER; + gctlCmd.pfnHandler = handleCtrlSession; + uUsage = USAGE_GSTCTRL_SESSION; + } + else if ( !RTStrICmp(pArg->argv[1], "process")) + { + uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS + | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER; + gctlCmd.pfnHandler = handleCtrlProcess; + uUsage = USAGE_GSTCTRL_PROCESS; + } + else if ( !RTStrICmp(pArg->argv[1], "watch")) + { + uCmdCtxFlags = CTLCMDCTX_FLAGS_SESSION_ANONYMOUS + | CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER; + gctlCmd.pfnHandler = handleCtrlWatch; + uUsage = USAGE_GSTCTRL_WATCH; + } + else + return errorSyntax(USAGE_GUESTCONTROL, "Unknown sub command '%s' specified!", pArg->argv[1]); + + GCTLCMDCTX cmdCtx; + RT_ZERO(cmdCtx); + + RTEXITCODE rcExit = ctrlInitVM(pArg, &cmdCtx, uCmdCtxFlags, uUsage); + if (rcExit == RTEXITCODE_SUCCESS) + { + /* Kick off the actual command handler. */ + rcExit = gctlCmd.pfnHandler(&cmdCtx); - ctrlUninitVM(pArg); + ctrlUninitVM(&cmdCtx, cmdCtx.uFlags); return rcExit; } - return RTEXITCODE_FAILURE; -} + return rcExit; +} #endif /* !VBOX_ONLY_DOCS */ diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.h b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.h new file mode 100644 index 00000000..e172f388 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.h @@ -0,0 +1,233 @@ +/* $Id: VBoxManageGuestCtrl.h $ */ +/** @file + * VBoxManageGuestCtrl.h - Definitions for guest control. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef ___H_VBOXMANAGE_GUESTCTRL +#define ___H_VBOXMANAGE_GUESTCTRL + +#ifndef VBOX_ONLY_DOCS + +#include <VBox/com/com.h> +#include <VBox/com/listeners.h> +#include <VBox/com/VirtualBox.h> + +#include <iprt/time.h> + +#include <map> + +const char *ctrlFileStatusToText(FileStatus_T enmStatus); +const char *ctrlProcessStatusToText(ProcessStatus_T enmStatus); +const char *ctrlSessionStatusToText(GuestSessionStatus_T enmStatus); + +using namespace com; + +class GuestFileEventListener; +typedef ListenerImpl<GuestFileEventListener> GuestFileEventListenerImpl; + +class GuestProcessEventListener; +typedef ListenerImpl<GuestProcessEventListener> GuestProcessEventListenerImpl; + +class GuestSessionEventListener; +typedef ListenerImpl<GuestSessionEventListener> GuestSessionEventListenerImpl; + +class GuestEventListener; +typedef ListenerImpl<GuestEventListener> GuestEventListenerImpl; + +/** Simple statistics class for binding locally + * held data to a specific guest object. */ +class GuestEventStats +{ + +public: + + GuestEventStats(void) + : uLastUpdatedMS(RTTimeMilliTS()) + { + } + + /** @todo Make this more a class than a structure. */ +public: + + uint64_t uLastUpdatedMS; +}; + +class GuestFileStats : public GuestEventStats +{ + +public: + + GuestFileStats(void) { } + + GuestFileStats(ComObjPtr<GuestFileEventListenerImpl> pListenerImpl) + : mListener(pListenerImpl) + { + } + +public: /** @todo */ + + ComObjPtr<GuestFileEventListenerImpl> mListener; +}; + +class GuestProcStats : public GuestEventStats +{ + +public: + + GuestProcStats(void) { } + + GuestProcStats(ComObjPtr<GuestProcessEventListenerImpl> pListenerImpl) + : mListener(pListenerImpl) + { + } + +public: /** @todo */ + + ComObjPtr<GuestProcessEventListenerImpl> mListener; +}; + +class GuestSessionStats : public GuestEventStats +{ + +public: + + GuestSessionStats(void) { } + + GuestSessionStats(ComObjPtr<GuestSessionEventListenerImpl> pListenerImpl) + : mListener(pListenerImpl) + { + } + +public: /** @todo */ + + ComObjPtr<GuestSessionEventListenerImpl> mListener; +}; + +/** Map containing all watched guest files. */ +typedef std::map< ComPtr<IGuestFile>, GuestFileStats > GuestEventFiles; +/** Map containing all watched guest processes. */ +typedef std::map< ComPtr<IGuestProcess>, GuestProcStats > GuestEventProcs; +/** Map containing all watched guest sessions. */ +typedef std::map< ComPtr<IGuestSession>, GuestSessionStats > GuestEventSessions; + +class GuestListenerBase +{ +public: + + GuestListenerBase(void); + + virtual ~GuestListenerBase(void); + +public: + + HRESULT init(bool fVerbose = false); + +protected: + + /** Verbose flag. */ + bool mfVerbose; +}; + +/** + * Handler for guest process events. + */ +class GuestFileEventListener : public GuestListenerBase +{ +public: + + GuestFileEventListener(void); + + virtual ~GuestFileEventListener(void); + +public: + + void uninit(void); + + STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent); + +protected: + +}; + +/** + * Handler for guest process events. + */ +class GuestProcessEventListener : public GuestListenerBase +{ +public: + + GuestProcessEventListener(void); + + virtual ~GuestProcessEventListener(void); + +public: + + void uninit(void); + + STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent); + +protected: + +}; + +/** + * Handler for guest session events. + */ +class GuestSessionEventListener : public GuestListenerBase +{ +public: + + GuestSessionEventListener(void); + + virtual ~GuestSessionEventListener(void); + +public: + + void uninit(void); + + STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent); + +protected: + + GuestEventFiles mFiles; + GuestEventProcs mProcs; +}; + +/** + * Handler for guest events. + */ +class GuestEventListener : public GuestListenerBase +{ + +public: + + GuestEventListener(void); + + virtual ~GuestEventListener(void); + +public: + + void uninit(void); + + STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent); + +protected: + + GuestEventSessions mSessions; +}; +#endif /* !VBOX_ONLY_DOCS */ + +#endif /* !___H_VBOXMANAGE_GUESTCTRL */ + diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrlListener.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrlListener.cpp new file mode 100644 index 00000000..04c5c575 --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrlListener.cpp @@ -0,0 +1,484 @@ +/* $Id: VBoxManageGuestCtrlListener.cpp $ */ +/** @file + * VBoxManage - Guest control listener implementations. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "VBoxManage.h" +#include "VBoxManageGuestCtrl.h" + +#ifndef VBOX_ONLY_DOCS + +#include <VBox/com/com.h> +#include <VBox/com/ErrorInfo.h> +#include <VBox/com/errorprint.h> + +#include <iprt/time.h> + +#include <map> +#include <vector> + +GuestListenerBase::GuestListenerBase(void) + : mfVerbose(false) +{ +} + +GuestListenerBase::~GuestListenerBase(void) +{ +} + +HRESULT GuestListenerBase::init(bool fVerbose) +{ + mfVerbose = fVerbose; + return S_OK; +} + + +GuestFileEventListener::GuestFileEventListener(void) +{ +} + +GuestFileEventListener::~GuestFileEventListener(void) +{ +} + +void GuestFileEventListener::uninit(void) +{ + +} + +STDMETHODIMP GuestFileEventListener::HandleEvent(VBoxEventType_T aType, IEvent *aEvent) +{ + switch (aType) + { + case VBoxEventType_OnGuestFileStateChanged: + { + HRESULT rc; + do + { + ComPtr<IGuestFileStateChangedEvent> pEvent = aEvent; + Assert(!pEvent.isNull()); + + ComPtr<IGuestFile> pProcess; + CHECK_ERROR_BREAK(pEvent, COMGETTER(File)(pProcess.asOutParam())); + AssertBreak(!pProcess.isNull()); + FileStatus_T fileSts; + CHECK_ERROR_BREAK(pEvent, COMGETTER(Status)(&fileSts)); + Bstr strPath; + CHECK_ERROR_BREAK(pProcess, COMGETTER(FileName)(strPath.asOutParam())); + ULONG uID; + CHECK_ERROR_BREAK(pProcess, COMGETTER(Id)(&uID)); + + RTPrintf("File ID=%RU32 \"%s\" changed status to [%s]\n", + uID, Utf8Str(strPath).c_str(), + ctrlFileStatusToText(fileSts)); + + } while (0); + break; + } + + default: + AssertFailed(); + } + + return S_OK; +} + +GuestProcessEventListener::GuestProcessEventListener(void) +{ +} + +GuestProcessEventListener::~GuestProcessEventListener(void) +{ +} + +void GuestProcessEventListener::uninit(void) +{ + +} + +STDMETHODIMP GuestProcessEventListener::HandleEvent(VBoxEventType_T aType, IEvent *aEvent) +{ + switch (aType) + { + case VBoxEventType_OnGuestProcessStateChanged: + { + HRESULT rc; + do + { + ComPtr<IGuestProcessStateChangedEvent> pEvent = aEvent; + Assert(!pEvent.isNull()); + + ComPtr<IGuestProcess> pProcess; + CHECK_ERROR_BREAK(pEvent, COMGETTER(Process)(pProcess.asOutParam())); + AssertBreak(!pProcess.isNull()); + ProcessStatus_T procSts; + CHECK_ERROR_BREAK(pEvent, COMGETTER(Status)(&procSts)); + Bstr strPath; + CHECK_ERROR_BREAK(pProcess, COMGETTER(ExecutablePath)(strPath.asOutParam())); + ULONG uPID; + CHECK_ERROR_BREAK(pProcess, COMGETTER(PID)(&uPID)); + + RTPrintf("Process PID=%RU32 \"%s\" changed status to [%s]\n", + uPID, Utf8Str(strPath).c_str(), + ctrlProcessStatusToText(procSts)); + + } while (0); + break; + } + + default: + AssertFailed(); + } + + return S_OK; +} + +GuestSessionEventListener::GuestSessionEventListener(void) +{ +} + +GuestSessionEventListener::~GuestSessionEventListener(void) +{ +} + +void GuestSessionEventListener::uninit(void) +{ + GuestEventProcs::iterator itProc = mProcs.begin(); + while (itProc != mProcs.end()) + { + if (!itProc->first.isNull()) + { + HRESULT rc; + do + { + /* Listener unregistration. */ + ComPtr<IEventSource> pES; + CHECK_ERROR_BREAK(itProc->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR_BREAK(pES, UnregisterListener(itProc->second.mListener)); + } while (0); + itProc->first->Release(); + } + + itProc++; + } + mProcs.clear(); + + GuestEventFiles::iterator itFile = mFiles.begin(); + while (itFile != mFiles.end()) + { + if (!itFile->first.isNull()) + { + HRESULT rc; + do + { + /* Listener unregistration. */ + ComPtr<IEventSource> pES; + CHECK_ERROR_BREAK(itFile->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR_BREAK(pES, UnregisterListener(itFile->second.mListener)); + } while (0); + itFile->first->Release(); + } + + itFile++; + } + mFiles.clear(); +} + +STDMETHODIMP GuestSessionEventListener::HandleEvent(VBoxEventType_T aType, IEvent *aEvent) +{ + switch (aType) + { + case VBoxEventType_OnGuestFileRegistered: + { + HRESULT rc; + do + { + ComPtr<IGuestFileRegisteredEvent> pEvent = aEvent; + Assert(!pEvent.isNull()); + + ComPtr<IGuestFile> pFile; + CHECK_ERROR_BREAK(pEvent, COMGETTER(File)(pFile.asOutParam())); + AssertBreak(!pFile.isNull()); + BOOL fRegistered; + CHECK_ERROR_BREAK(pEvent, COMGETTER(Registered)(&fRegistered)); + Bstr strPath; + CHECK_ERROR_BREAK(pFile, COMGETTER(FileName)(strPath.asOutParam())); + + RTPrintf("File \"%s\" %s\n", + Utf8Str(strPath).c_str(), + fRegistered ? "registered" : "unregistered"); + if (fRegistered) + { + if (mfVerbose) + RTPrintf("Registering ...\n"); + + /* Register for IGuestFile events. */ + ComObjPtr<GuestFileEventListenerImpl> pListener; + pListener.createObject(); + CHECK_ERROR_BREAK(pListener, init(new GuestFileEventListener())); + + ComPtr<IEventSource> es; + CHECK_ERROR_BREAK(pFile, COMGETTER(EventSource)(es.asOutParam())); + com::SafeArray<VBoxEventType_T> eventTypes; + eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged); + CHECK_ERROR_BREAK(es, RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes), + true /* Active listener */)); + + GuestFileStats fileStats(pListener); + mFiles[pFile] = fileStats; + } + else + { + GuestEventFiles::iterator itFile = mFiles.find(pFile); + if (itFile != mFiles.end()) + { + if (mfVerbose) + RTPrintf("Unregistering file ...\n"); + + if (!itFile->first.isNull()) + { + /* Listener unregistration. */ + ComPtr<IEventSource> pES; + CHECK_ERROR(itFile->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR(pES, UnregisterListener(itFile->second.mListener)); + itFile->first->Release(); + } + + mFiles.erase(itFile); + } + } + + } while (0); + break; + } + + case VBoxEventType_OnGuestProcessRegistered: + { + HRESULT rc; + do + { + ComPtr<IGuestProcessRegisteredEvent> pEvent = aEvent; + Assert(!pEvent.isNull()); + + ComPtr<IGuestProcess> pProcess; + CHECK_ERROR_BREAK(pEvent, COMGETTER(Process)(pProcess.asOutParam())); + AssertBreak(!pProcess.isNull()); + BOOL fRegistered; + CHECK_ERROR_BREAK(pEvent, COMGETTER(Registered)(&fRegistered)); + Bstr strPath; + CHECK_ERROR_BREAK(pProcess, COMGETTER(ExecutablePath)(strPath.asOutParam())); + + RTPrintf("Process \"%s\" %s\n", + Utf8Str(strPath).c_str(), + fRegistered ? "registered" : "unregistered"); + if (fRegistered) + { + if (mfVerbose) + RTPrintf("Registering ...\n"); + + /* Register for IGuestProcess events. */ + ComObjPtr<GuestProcessEventListenerImpl> pListener; + pListener.createObject(); + CHECK_ERROR_BREAK(pListener, init(new GuestProcessEventListener())); + + ComPtr<IEventSource> es; + CHECK_ERROR_BREAK(pProcess, COMGETTER(EventSource)(es.asOutParam())); + com::SafeArray<VBoxEventType_T> eventTypes; + eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged); + CHECK_ERROR_BREAK(es, RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes), + true /* Active listener */)); + + GuestProcStats procStats(pListener); + mProcs[pProcess] = procStats; + } + else + { + GuestEventProcs::iterator itProc = mProcs.find(pProcess); + if (itProc != mProcs.end()) + { + if (mfVerbose) + RTPrintf("Unregistering process ...\n"); + + if (!itProc->first.isNull()) + { + /* Listener unregistration. */ + ComPtr<IEventSource> pES; + CHECK_ERROR(itProc->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR(pES, UnregisterListener(itProc->second.mListener)); + itProc->first->Release(); + } + + mProcs.erase(itProc); + } + } + + } while (0); + break; + } + + case VBoxEventType_OnGuestSessionStateChanged: + { + HRESULT rc; + do + { + ComPtr<IGuestSessionStateChangedEvent> pEvent = aEvent; + Assert(!pEvent.isNull()); + ComPtr<IGuestSession> pSession; + CHECK_ERROR_BREAK(pEvent, COMGETTER(Session)(pSession.asOutParam())); + AssertBreak(!pSession.isNull()); + + GuestSessionStatus_T sessSts; + CHECK_ERROR_BREAK(pSession, COMGETTER(Status)(&sessSts)); + ULONG uID; + CHECK_ERROR_BREAK(pSession, COMGETTER(Id)(&uID)); + Bstr strName; + CHECK_ERROR_BREAK(pSession, COMGETTER(Name)(strName.asOutParam())); + + RTPrintf("Session ID=%RU32 \"%s\" changed status to [%s]\n", + uID, Utf8Str(strName).c_str(), + ctrlSessionStatusToText(sessSts)); + + } while (0); + break; + } + + default: + AssertFailed(); + } + + return S_OK; +} + +GuestEventListener::GuestEventListener(void) +{ +} + +GuestEventListener::~GuestEventListener(void) +{ +} + +void GuestEventListener::uninit(void) +{ + GuestEventSessions::iterator itSession = mSessions.begin(); + while (itSession != mSessions.end()) + { + if (!itSession->first.isNull()) + { + HRESULT rc; + do + { + /* Listener unregistration. */ + ComPtr<IEventSource> pES; + CHECK_ERROR_BREAK(itSession->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR_BREAK(pES, UnregisterListener(itSession->second.mListener)); + + } while (0); + itSession->first->Release(); + } + + itSession++; + } + mSessions.clear(); +} + +STDMETHODIMP GuestEventListener::HandleEvent(VBoxEventType_T aType, IEvent *aEvent) +{ + switch (aType) + { + case VBoxEventType_OnGuestSessionRegistered: + { + HRESULT rc; + do + { + ComPtr<IGuestSessionRegisteredEvent> pEvent = aEvent; + Assert(!pEvent.isNull()); + + ComPtr<IGuestSession> pSession; + CHECK_ERROR_BREAK(pEvent, COMGETTER(Session)(pSession.asOutParam())); + AssertBreak(!pSession.isNull()); + BOOL fRegistered; + CHECK_ERROR_BREAK(pEvent, COMGETTER(Registered)(&fRegistered)); + Bstr strName; + CHECK_ERROR_BREAK(pSession, COMGETTER(Name)(strName.asOutParam())); + ULONG uID; + CHECK_ERROR_BREAK(pSession, COMGETTER(Id)(&uID)); + + RTPrintf("Session ID=%RU32 \"%s\" %s\n", + uID, Utf8Str(strName).c_str(), + fRegistered ? "registered" : "unregistered"); + if (fRegistered) + { + if (mfVerbose) + RTPrintf("Registering ...\n"); + + /* Register for IGuestSession events. */ + ComObjPtr<GuestSessionEventListenerImpl> pListener; + pListener.createObject(); + CHECK_ERROR_BREAK(pListener, init(new GuestSessionEventListener())); + + ComPtr<IEventSource> es; + CHECK_ERROR_BREAK(pSession, COMGETTER(EventSource)(es.asOutParam())); + com::SafeArray<VBoxEventType_T> eventTypes; + eventTypes.push_back(VBoxEventType_OnGuestFileRegistered); + eventTypes.push_back(VBoxEventType_OnGuestProcessRegistered); + CHECK_ERROR_BREAK(es, RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes), + true /* Active listener */)); + + GuestSessionStats sessionStats(pListener); + mSessions[pSession] = sessionStats; + } + else + { + GuestEventSessions::iterator itSession = mSessions.find(pSession); + if (itSession != mSessions.end()) + { + if (mfVerbose) + RTPrintf("Unregistering ...\n"); + + if (!itSession->first.isNull()) + { + /* Listener unregistration. */ + ComPtr<IEventSource> pES; + CHECK_ERROR_BREAK(itSession->first, COMGETTER(EventSource)(pES.asOutParam())); + if (!pES.isNull()) + CHECK_ERROR_BREAK(pES, UnregisterListener(itSession->second.mListener)); + itSession->first->Release(); + } + + mSessions.erase(itSession); + } + } + + } while (0); + break; + } + + default: + AssertFailed(); + } + + return S_OK; +} +#endif /* !VBOX_ONLY_DOCS */ + diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp index d072a4ea..7d95bb57 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -28,9 +28,7 @@ #include <VBox/com/array.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> - #include <VBox/com/VirtualBox.h> -#include <VBox/com/EventQueue.h> #include <VBox/log.h> #include <iprt/asm.h> @@ -55,19 +53,23 @@ using namespace com; void usageGuestProperty(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2) { RTStrmPrintf(pStrm, - "%s guestproperty %s get <vmname>|<uuid>\n" + "%s guestproperty %s get <uuid|vmname>\n" " <property> [--verbose]\n" "\n", pcszSep1, pcszSep2); RTStrmPrintf(pStrm, - "%s guestproperty %s set <vmname>|<uuid>\n" + "%s guestproperty %s set <uuid|vmname>\n" " <property> [<value> [--flags <flags>]]\n" "\n", pcszSep1, pcszSep2); RTStrmPrintf(pStrm, - "%s guestproperty %s enumerate <vmname>|<uuid>\n" + "%s guestproperty %s delete|unset <uuid|vmname>\n" + " <property>\n" + "\n", pcszSep1, pcszSep2); + RTStrmPrintf(pStrm, + "%s guestproperty %s enumerate <uuid|vmname>\n" " [--patterns <patterns>]\n" "\n", pcszSep1, pcszSep2); RTStrmPrintf(pStrm, - "%s guestproperty %s wait <vmname>|<uuid> <patterns>\n" + "%s guestproperty %s wait <uuid|vmname> <patterns>\n" " [--timeout <msec>] [--fail-on-timeout]\n" "\n", pcszSep1, pcszSep2); } @@ -158,9 +160,7 @@ static int handleSetGuestProperty(HandlerArg *a) /* get the mutable session machine */ a->session->COMGETTER(Machine)(machine.asOutParam()); - if (!pszValue && !pszFlags) - CHECK_ERROR(machine, DeleteGuestProperty(Bstr(pszName).raw())); - else if (!pszFlags) + if (!pszFlags) CHECK_ERROR(machine, SetGuestPropertyValue(Bstr(pszName).raw(), Bstr(pszValue).raw())); else @@ -176,6 +176,44 @@ static int handleSetGuestProperty(HandlerArg *a) return SUCCEEDED(rc) ? 0 : 1; } +static int handleDeleteGuestProperty(HandlerArg *a) +{ + HRESULT rc = S_OK; + + /* + * Check the syntax. We can deduce the correct syntax from the number of + * arguments. + */ + bool usageOK = true; + const char *pszName = NULL; + if (a->argc != 2) + usageOK = false; + if (!usageOK) + return errorSyntax(USAGE_GUESTPROPERTY, "Incorrect parameters"); + /* This is always needed. */ + pszName = a->argv[1]; + + ComPtr<IMachine> machine; + CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(), + machine.asOutParam())); + if (machine) + { + /* open a session for the VM - new or existing */ + CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1); + + /* get the mutable session machine */ + a->session->COMGETTER(Machine)(machine.asOutParam()); + + CHECK_ERROR(machine, DeleteGuestProperty(Bstr(pszName).raw())); + + if (SUCCEEDED(rc)) + CHECK_ERROR(machine, SaveSettings()); + + a->session->UnlockMachine(); + } + return SUCCEEDED(rc) ? 0 : 1; +} + /** * Enumerates the properties in the guest property store. * @@ -384,6 +422,8 @@ int handleGuestProperty(HandlerArg *a) return handleGetGuestProperty(&arg); if (strcmp(a->argv[0], "set") == 0) return handleSetGuestProperty(&arg); + if (strcmp(a->argv[0], "delete") == 0 || strcmp(a->argv[0], "unset") == 0) + return handleDeleteGuestProperty(&arg); if (strcmp(a->argv[0], "enumerate") == 0) return handleEnumGuestProperty(&arg); if (strcmp(a->argv[0], "wait") == 0) diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp index 6c28c3b5..cc3ab446 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -46,7 +46,7 @@ void showLogo(PRTSTREAM pStrm) } } -void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) +void printUsage(USAGECATEGORY fCategory, uint32_t fSubCategory, PRTSTREAM pStrm) { bool fDumpOpts = false; #ifdef RT_OS_LINUX @@ -80,7 +80,7 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) bool fVBoxSDL = false; #endif - if (u64Cmd == USAGE_DUMPOPTS) + if (fCategory == USAGE_DUMPOPTS) { fDumpOpts = true; fLinux = true; @@ -89,14 +89,14 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) fFreeBSD = true; fDarwin = true; fVBoxSDL = true; - u64Cmd = USAGE_ALL; + fCategory = USAGE_ALL; } RTStrmPrintf(pStrm, "Usage:\n" "\n"); - if (u64Cmd == USAGE_ALL) + if (fCategory == USAGE_ALL) RTStrmPrintf(pStrm, " VBoxManage [<general option>] <command>\n" " \n \n" @@ -110,7 +110,7 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) const char *pcszSep1 = " "; const char *pcszSep2 = " "; - if (u64Cmd != USAGE_ALL) + if (fCategory != USAGE_ALL) { pcszSep1 = "VBoxManage"; pcszSep2 = ""; @@ -118,37 +118,37 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) #define SEP pcszSep1, pcszSep2 - if (u64Cmd & USAGE_LIST) + if (fCategory & USAGE_LIST) RTStrmPrintf(pStrm, "%s list [--long|-l]%s vms|runningvms|ostypes|hostdvds|hostfloppies|\n" #if defined(VBOX_WITH_NETFLT) - " bridgedifs|hostonlyifs|dhcpservers|hostinfo|\n" + " intnets|bridgedifs|hostonlyifs|natnets|dhcpservers|\n" #else - " bridgedifs|dhcpservers|hostinfo|\n" + " intnets|bridgedifs|natnets|dhcpservers|hostinfo|\n" #endif - " hostcpuids|hddbackends|hdds|dvds|floppies|\n" + " hostinfo|hostcpuids|hddbackends|hdds|dvds|floppies|\n" " usbhost|usbfilters|systemproperties|extpacks|\n" - " groups\n" + " groups|webcams\n" "\n", SEP); - if (u64Cmd & USAGE_SHOWVMINFO) + if (fCategory & USAGE_SHOWVMINFO) RTStrmPrintf(pStrm, - "%s showvminfo %s <uuid>|<name> [--details]\n" + "%s showvminfo %s <uuid|vmname> [--details]\n" " [--machinereadable]\n" - "%s showvminfo %s <uuid>|<name> --log <idx>\n" + "%s showvminfo %s <uuid|vmname> --log <idx>\n" "\n", SEP, SEP); - if (u64Cmd & USAGE_REGISTERVM) + if (fCategory & USAGE_REGISTERVM) RTStrmPrintf(pStrm, "%s registervm %s <filename>\n" "\n", SEP); - if (u64Cmd & USAGE_UNREGISTERVM) + if (fCategory & USAGE_UNREGISTERVM) RTStrmPrintf(pStrm, - "%s unregistervm %s <uuid>|<name> [--delete]\n" + "%s unregistervm %s <uuid|vmname> [--delete]\n" "\n", SEP); - if (u64Cmd & USAGE_CREATEVM) + if (fCategory & USAGE_CREATEVM) RTStrmPrintf(pStrm, "%s createvm %s --name <name>\n" " [--groups <group>, ...]\n" @@ -158,13 +158,14 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--uuid <uuid>]\n" "\n", SEP); - if (u64Cmd & USAGE_MODIFYVM) + if (fCategory & USAGE_MODIFYVM) { RTStrmPrintf(pStrm, - "%s modifyvm %s <uuid|name>\n" + "%s modifyvm %s <uuid|vmname>\n" " [--name <name>]\n" " [--groups <group>, ...]\n" " [--ostype <ostype>]\n" + " [--iconfile <filename>]\n" " [--memory <memorysize in MB>]\n" " [--pagefusion on|off]\n" " [--vram <vramsize in MB>]\n" @@ -175,13 +176,15 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--pcidetach 03:04.0]\n" #endif " [--ioapic on|off]\n" - " [--pae on|off]\n" " [--hpet on|off]\n" + " [--triplefaultreset on|off]\n" " [--hwvirtex on|off]\n" - " [--hwvirtexexcl on|off]\n" " [--nestedpaging on|off]\n" " [--largepages on|off]\n" " [--vtxvpid on|off]\n" + " [--vtxux on|off]\n" + " [--pae on|off]\n" + " [--longmode on|off]\n" " [--synthcpu on|off]\n" " [--cpuidset <leaf> <eax> <ebx> <ecx> <edx>]\n" " [--cpuidremove <leaf>]\n" @@ -193,6 +196,11 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--unplugcpu <id>]\n" " [--cpuexecutioncap <1-100>]\n" " [--rtcuseutc on|off]\n" +#ifdef VBOX_WITH_VMSVGA + " [--graphicscontroller none|vboxvga|vmsvga]\n" +#else + " [--graphicscontroller none|vboxvga]\n" +#endif " [--monitorcount <number>]\n" " [--accelerate3d on|off]\n" #ifdef VBOX_WITH_VIDEOHWACCEL @@ -213,7 +221,7 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) "|hostonly" #endif "|\n" - " generic" + " generic|natnetwork" "]\n" " [--nictype<1-N> Am79C970A|Am79C973" #ifdef VBOX_WITH_E1000 @@ -236,8 +244,9 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--hostonlyadapter<1-N> none|<devicename>]\n" #endif " [--intnet<1-N> <network name>]\n" - " [--natnet<1-N> <network>|default]\n" + " [--nat-network<1-N> <network name>]\n" " [--nicgenericdrv<1-N> <driver>\n" + " [--natnet<1-N> <network>|default]\n" " [--natsettings<1-N> [<mtu>],[<socksnd>],\n" " [<sockrcv>],[<tcpsnd>],\n" " [<tcprcv>]]\n" @@ -254,7 +263,7 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--nataliasmode<1-N> default|[log],[proxyonly],\n" " [sameports]]\n" " [--macaddress<1-N> auto|<mac>]\n" - " [--mouse ps2|usb|usbtablet\n" + " [--mouse ps2|usb|usbtablet|usbmultitouch]\n" " [--keyboard ps2|usb\n" " [--uart<1-N> off|<I/O base> <IRQ>]\n" " [--uartmode<1-N> disconnected|\n" @@ -267,7 +276,6 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--lptmode<1-N> <devicename>]\n" #endif " [--guestmemoryballoon <balloonsize in MB>]\n" - " [--gueststatisticsinterval <seconds>]\n" " [--audio none|null", SEP); if (fWin) { @@ -340,7 +348,7 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--teleporterport <port>]\n" " [--teleporteraddress <address|empty>\n" " [--teleporterpassword <password>]\n" - " [--teleporterpasswordfile <file>|stdin]\n" + " [--teleporterpasswordfile <file>|stdin]\n" " [--tracing-enabled on|off]\n" " [--tracing-config <config-string>]\n" " [--tracing-allow-vm-access on|off]\n" @@ -364,12 +372,22 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--autostop-type disabled|savestate|poweroff|\n" " acpishutdown]\n" #endif +#ifdef VBOX_WITH_VPX + " [--vcpenabled on|off]\n" + " [--vcpscreens [<display>],...\n" + " [--vcpfile <filename>]\n" + " [--vcpwidth <width>]\n" + " [--vcpheight <height>]\n" + " [--vcprate <rate>]\n" + " [--vcpfps <fps>]\n" +#endif + " [--defaultfrontend default|<name>]\n" "\n"); } - if (u64Cmd & USAGE_CLONEVM) + if (fCategory & USAGE_CLONEVM) RTStrmPrintf(pStrm, - "%s clonevm %s <uuid>|<name>\n" + "%s clonevm %s <uuid|vmname>\n" " [--snapshot <uuid>|<name>]\n" " [--mode machine|machineandchildren|all]\n" " [--options link|keepallmacs|keepnatmacs|\n" @@ -381,34 +399,37 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--register]\n" "\n", SEP); - if (u64Cmd & USAGE_IMPORTAPPLIANCE) + if (fCategory & USAGE_IMPORTAPPLIANCE) RTStrmPrintf(pStrm, - "%s import %s <ovf/ova>\n" + "%s import %s <ovfname/ovaname>\n" " [--dry-run|-n]\n" " [--options keepallmacs|keepnatmacs]\n" " [more options]\n" " (run with -n to have options displayed\n" " for a particular OVF)\n\n", SEP); - if (u64Cmd & USAGE_EXPORTAPPLIANCE) + if (fCategory & USAGE_EXPORTAPPLIANCE) RTStrmPrintf(pStrm, "%s export %s <machines> --output|-o <name>.<ovf/ova>\n" " [--legacy09|--ovf09|--ovf10|--ovf20]\n" " [--manifest]\n" + " [--iso]\n" + " [--options manifest|iso|nomacs|nomacsbutnat]\n" " [--vsys <number of virtual system>]\n" " [--product <product name>]\n" " [--producturl <product url>]\n" " [--vendor <vendor name>]\n" " [--vendorurl <vendor url>]\n" " [--version <version info>]\n" + " [--description <description info>]\n" " [--eula <license text>]\n" " [--eulafile <filename>]\n" "\n", SEP); - if (u64Cmd & USAGE_STARTVM) + if (fCategory & USAGE_STARTVM) { RTStrmPrintf(pStrm, - "%s startvm %s <uuid>|<name>...\n" + "%s startvm %s <uuid|vmname>...\n" " [--type gui", SEP); if (fVBoxSDL) RTStrmPrintf(pStrm, "|sdl"); @@ -417,43 +438,44 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) "\n"); } - if (u64Cmd & USAGE_CONTROLVM) + if (fCategory & USAGE_CONTROLVM) { RTStrmPrintf(pStrm, - "%s controlvm %s <uuid>|<name>\n" + "%s controlvm %s <uuid|vmname>\n" " pause|resume|reset|poweroff|savestate|\n" " acpipowerbutton|acpisleepbutton|\n" " keyboardputscancode <hex> [<hex> ...]|\n" " setlinkstate<1-N> on|off |\n" #if defined(VBOX_WITH_NETFLT) - " nic<1-N> null|nat|bridged|intnet|hostonly|generic" - "\n" - " [<devicename>] |\n" + " nic<1-N> null|nat|bridged|intnet|hostonly|generic|\n" + " natnetwork [<devicename>] |\n" #else /* !VBOX_WITH_NETFLT */ - " nic<1-N> null|nat|bridged|intnet|generic\n" + " nic<1-N> null|nat|bridged|intnet|generic|natnetwork\n" " [<devicename>] |\n" #endif /* !VBOX_WITH_NETFLT */ - " nictrace<1-N> on|off\n" - " nictracefile<1-N> <filename>\n" - " nicproperty<1-N> name=[value]\n" + " nictrace<1-N> on|off |\n" + " nictracefile<1-N> <filename> |\n" + " nicproperty<1-N> name=[value] |\n" + " nicpromisc<1-N> deny|allow-vms|allow-all |\n" " natpf<1-N> [<rulename>],tcp|udp,[<hostip>],\n" - " <hostport>,[<guestip>],<guestport>\n" - " natpf<1-N> delete <rulename>\n" - " guestmemoryballoon <balloonsize in MB>]\n" - " gueststatisticsinterval <seconds>]\n" + " <hostport>,[<guestip>],<guestport> |\n" + " natpf<1-N> delete <rulename> |\n" + " guestmemoryballoon <balloonsize in MB> |\n" " usbattach <uuid>|<address> |\n" " usbdetach <uuid>|<address> |\n" " clipboard disabled|hosttoguest|guesttohost|\n" - " bidirectional]\n" - " draganddrop disabled|hosttoguest]\n" + " bidirectional |\n" + " draganddrop disabled|hosttoguest |\n" " vrde on|off |\n" " vrdeport <port> |\n" " vrdeproperty <name=[value]> |\n" - " vrdevideochannelquality <percent>\n" + " vrdevideochannelquality <percent> |\n" " setvideomodehint <xres> <yres> <bpp>\n" - " [[<display>] [<enabled:yes|no>\n" + " [[<display>] [<enabled:yes|no> |\n" " [<xorigin> <yorigin>]]] |\n" " screenshotpng <file> [display] |\n" + " vcpenabled on|off |\n" + " vcpscreens all|none|<screen>,[<screen>...] |\n" " setcredentials <username>\n" " --passwordfile <file> | <password>\n" " <domain>\n" @@ -461,52 +483,53 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " teleport --host <name> --port <port>\n" " [--maxdowntime <msec>]\n" " [--passwordfile <file> |\n" - " --password <password>]\n" - " plugcpu <id>\n" - " unplugcpu <id>\n" + " --password <password>] |\n" + " plugcpu <id> |\n" + " unplugcpu <id> |\n" " cpuexecutioncap <1-100>\n" + " webcam <attach [path [settings]]> | <detach [path]> | <list>\n" "\n", SEP); } - if (u64Cmd & USAGE_DISCARDSTATE) + if (fCategory & USAGE_DISCARDSTATE) RTStrmPrintf(pStrm, - "%s discardstate %s <uuid>|<name>\n" + "%s discardstate %s <uuid|vmname>\n" "\n", SEP); - if (u64Cmd & USAGE_ADOPTSTATE) + if (fCategory & USAGE_ADOPTSTATE) RTStrmPrintf(pStrm, - "%s adoptstate %s <uuid>|<name> <state_file>\n" + "%s adoptstate %s <uuid|vmname> <state_file>\n" "\n", SEP); - if (u64Cmd & USAGE_SNAPSHOT) + if (fCategory & USAGE_SNAPSHOT) RTStrmPrintf(pStrm, - "%s snapshot %s <uuid>|<name>\n" - " take <name> [--description <desc>] [--pause] |\n" - " delete <uuid>|<name> |\n" - " restore <uuid>|<name> |\n" + "%s snapshot %s <uuid|vmname>\n" + " take <name> [--description <desc>] [--live] |\n" + " delete <uuid|snapname> |\n" + " restore <uuid|snapname> |\n" " restorecurrent |\n" - " edit <uuid>|<name>|--current\n" + " edit <uuid|snapname>|--current\n" " [--name <name>]\n" " [--description <desc>] |\n" " list [--details|--machinereadable]\n" - " showvminfo <uuid>|<name>\n" + " showvminfo <uuid|snapname>\n" "\n", SEP); - if (u64Cmd & USAGE_CLOSEMEDIUM) + if (fCategory & USAGE_CLOSEMEDIUM) RTStrmPrintf(pStrm, - "%s closemedium %s disk|dvd|floppy <uuid>|<filename>\n" + "%s closemedium %s disk|dvd|floppy <uuid|filename>\n" " [--delete]\n" "\n", SEP); - if (u64Cmd & USAGE_STORAGEATTACH) + if (fCategory & USAGE_STORAGEATTACH) RTStrmPrintf(pStrm, "%s storageattach %s <uuid|vmname>\n" " --storagectl <name>\n" " [--port <number>]\n" " [--device <number>]\n" " [--type dvddrive|hdd|fdd]\n" - " [--medium none|emptydrive|\n" - " <uuid>|<filename>|host:<drive>|iscsi]\n" + " [--medium none|emptydrive|additions|\n" + " <uuid|filename>|host:<drive>|iscsi]\n" " [--mtype normal|writethrough|immutable|shareable|\n" " readonly|multiattach]\n" " [--comment <text>]\n" @@ -529,20 +552,20 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--intnet]\n" "\n", SEP); - if (u64Cmd & USAGE_STORAGECONTROLLER) + if (fCategory & USAGE_STORAGECONTROLLER) RTStrmPrintf(pStrm, "%s storagectl %s <uuid|vmname>\n" " --name <name>\n" " [--add ide|sata|scsi|floppy|sas]\n" " [--controller LSILogic|LSILogicSAS|BusLogic|\n" " IntelAHCI|PIIX3|PIIX4|ICH6|I82078]\n" - " [--sataportcount <1-30>]\n" + " [--portcount <1-30>]\n" " [--hostiocache on|off]\n" " [--bootable on|off]\n" " [--remove]\n" "\n", SEP); - if (u64Cmd & USAGE_BANDWIDTHCONTROL) + if (fCategory & USAGE_BANDWIDTHCONTROL) RTStrmPrintf(pStrm, "%s bandwidthctl %s <uuid|vmname>\n" " add <name> --type disk|network\n" @@ -555,12 +578,12 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " K=kilobyte, M=megabyte, G=gigabyte)\n" "\n", SEP); - if (u64Cmd & USAGE_SHOWHDINFO) + if (fCategory & USAGE_SHOWHDINFO) RTStrmPrintf(pStrm, - "%s showhdinfo %s <uuid>|<filename>\n" + "%s showhdinfo %s <uuid|filename>\n" "\n", SEP); - if (u64Cmd & USAGE_CREATEHD) + if (fCategory & USAGE_CREATEHD) RTStrmPrintf(pStrm, "%s createhd %s --filename <filename>\n" " [--size <megabytes>|--sizebyte <bytes>]\n" @@ -569,25 +592,26 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--variant Standard,Fixed,Split2G,Stream,ESX]\n" "\n", SEP); - if (u64Cmd & USAGE_MODIFYHD) + if (fCategory & USAGE_MODIFYHD) RTStrmPrintf(pStrm, - "%s modifyhd %s <uuid>|<filename>\n" + "%s modifyhd %s <uuid|filename>\n" " [--type normal|writethrough|immutable|shareable|\n" " readonly|multiattach]\n" " [--autoreset on|off]\n" + " [--property <name=[value]>]\n" " [--compact]\n" " [--resize <megabytes>|--resizebyte <bytes>]\n" "\n", SEP); - if (u64Cmd & USAGE_CLONEHD) + if (fCategory & USAGE_CLONEHD) RTStrmPrintf(pStrm, - "%s clonehd %s <uuid>|<filename> <uuid>|<outputfile>\n" + "%s clonehd %s <uuid|inputfile> <uuid|outputfile>\n" " [--format VDI|VMDK|VHD|RAW|<other>]\n" " [--variant Standard,Fixed,Split2G,Stream,ESX]\n" " [--existing]\n" "\n", SEP); - if (u64Cmd & USAGE_CONVERTFROMRAW) + if (fCategory & USAGE_CONVERTFROMRAW) RTStrmPrintf(pStrm, "%s convertfromraw %s <filename> <outputfile>\n" " [--format VDI|VMDK|VHD]\n" @@ -599,33 +623,35 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--uuid <uuid>]\n" "\n", SEP, SEP); - if (u64Cmd & USAGE_GETEXTRADATA) + if (fCategory & USAGE_GETEXTRADATA) RTStrmPrintf(pStrm, - "%s getextradata %s global|<uuid>|<name>\n" + "%s getextradata %s global|<uuid|vmname>\n" " <key>|enumerate\n" "\n", SEP); - if (u64Cmd & USAGE_SETEXTRADATA) + if (fCategory & USAGE_SETEXTRADATA) RTStrmPrintf(pStrm, - "%s setextradata %s global|<uuid>|<name>\n" + "%s setextradata %s global|<uuid|vmname>\n" " <key>\n" " [<value>] (no value deletes key)\n" "\n", SEP); - if (u64Cmd & USAGE_SETPROPERTY) + if (fCategory & USAGE_SETPROPERTY) RTStrmPrintf(pStrm, "%s setproperty %s machinefolder default|<folder> |\n" + " hwvirtexclusive on|off |\n" " vrdeauthlibrary default|<library> |\n" " websrvauthlibrary default|null|<library> |\n" " vrdeextpack null|<library> |\n" " autostartdbpath null|<folder> |\n" " loghistorycount <value>\n" + " defaultfrontend default|<name>\n" "\n", SEP); - if (u64Cmd & USAGE_USBFILTER_ADD) + if (fCategory & USAGE_USBFILTER_ADD) RTStrmPrintf(pStrm, "%s usbfilter %s add <index,0-N>\n" - " --target <uuid>|<name>|global\n" + " --target <uuid|vmname>|global\n" " --name <string>\n" " --action ignore|hold (global filters only)\n" " [--active yes|no] (yes)\n" @@ -639,10 +665,10 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--maskedinterfaces <XXXXXXXX>]\n" "\n", SEP); - if (u64Cmd & USAGE_USBFILTER_MODIFY) + if (fCategory & USAGE_USBFILTER_MODIFY) RTStrmPrintf(pStrm, "%s usbfilter %s modify <index,0-N>\n" - " --target <uuid>|<name>|global\n" + " --target <uuid|vmname>|global\n" " [--name <string>]\n" " [--action ignore|hold] (global filters only)\n" " [--active yes|no]\n" @@ -656,39 +682,39 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--maskedinterfaces <XXXXXXXX>]\n" "\n", SEP); - if (u64Cmd & USAGE_USBFILTER_REMOVE) + if (fCategory & USAGE_USBFILTER_REMOVE) RTStrmPrintf(pStrm, "%s usbfilter %s remove <index,0-N>\n" - " --target <uuid>|<name>|global\n" + " --target <uuid|vmname>|global\n" "\n", SEP); - if (u64Cmd & USAGE_SHAREDFOLDER_ADD) + if (fCategory & USAGE_SHAREDFOLDER_ADD) RTStrmPrintf(pStrm, - "%s sharedfolder %s add <vmname>|<uuid>\n" + "%s sharedfolder %s add <uuid|vmname>\n" " --name <name> --hostpath <hostpath>\n" " [--transient] [--readonly] [--automount]\n" "\n", SEP); - if (u64Cmd & USAGE_SHAREDFOLDER_REMOVE) + if (fCategory & USAGE_SHAREDFOLDER_REMOVE) RTStrmPrintf(pStrm, - "%s sharedfolder %s remove <vmname>|<uuid>\n" + "%s sharedfolder %s remove <uuid|vmname>\n" " --name <name> [--transient]\n" "\n", SEP); #ifdef VBOX_WITH_GUEST_PROPS - if (u64Cmd & USAGE_GUESTPROPERTY) + if (fCategory & USAGE_GUESTPROPERTY) usageGuestProperty(pStrm, SEP); #endif /* VBOX_WITH_GUEST_PROPS defined */ #ifdef VBOX_WITH_GUEST_CONTROL - if (u64Cmd & USAGE_GUESTCONTROL) - usageGuestControl(pStrm, SEP); + if (fCategory & USAGE_GUESTCONTROL) + usageGuestControl(pStrm, SEP, fSubCategory); #endif /* VBOX_WITH_GUEST_CONTROL defined */ - if (u64Cmd & USAGE_DEBUGVM) + if (fCategory & USAGE_DEBUGVM) { RTStrmPrintf(pStrm, - "%s debugvm %s <uuid>|<name>\n" + "%s debugvm %s <uuid|vmname>\n" " dumpguestcore --filename <name> |\n" " info <item> [args] |\n" " injectnmi |\n" @@ -707,7 +733,7 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [--descriptions]\n" "\n", SEP); } - if (u64Cmd & USAGE_METRICS) + if (fCategory & USAGE_METRICS) RTStrmPrintf(pStrm, "%s metrics %s list [*|host|<vmname> [<metric_list>]]\n" " (comma-separated)\n\n" @@ -731,8 +757,39 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) " [*|host|<vmname> [<metric_list>]]\n" "\n", SEP, SEP, SEP, SEP, SEP, SEP); +#if defined(VBOX_WITH_NAT_SERVICE) + if (fCategory & USAGE_NATNETWORK) + { + RTStrmPrintf(pStrm, + "%s natnetwork %s add --netname <name>\n" + " --network <network>\n" + " [--enable|--disable]\n" + " [--dhcp on|off]\n" + " [--port-forward-4 <rule>]\n" + " [--loopback-4 <rule>]\n" + " [--ipv6 on|off]\n" + " [--port-forward-6 <rule>]\n" + " [--loopback-6 <rule>]\n\n" + "%s natnetwork %s remove --netname <name>\n\n" + "%s natnetwork %s modify --netname <name>\n" + " [--network <network>]\n" + " [--enable|--disable]\n" + " [--dhcp on|off]\n" + " [--port-forward-4 <rule>]\n" + " [--loopback-4 <rule>]\n" + " [--ipv6 on|off]\n" + " [--port-forward-6 <rule>]\n" + " [--loopback-6 <rule>]\n\n" + "%s natnetwork %s start --netname <name>\n\n" + "%s natnetwork %s stop --netname <name>\n" + "\n", SEP, SEP, SEP, SEP, SEP); + + + } +#endif + #if defined(VBOX_WITH_NETFLT) - if (u64Cmd & USAGE_HOSTONLYIFS) + if (fCategory & USAGE_HOSTONLYIFS) { RTStrmPrintf(pStrm, "%s hostonlyif %s ipconfig <name>\n" @@ -747,7 +804,7 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) } #endif - if (u64Cmd & USAGE_DHCPSERVER) + if (fCategory & USAGE_DHCPSERVER) { RTStrmPrintf(pStrm, "%s dhcpserver %s add|modify --netname <network_name> |\n" @@ -765,7 +822,7 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) #endif "\n", SEP, SEP); } - if (u64Cmd & USAGE_EXTPACK) + if (fCategory & USAGE_EXTPACK) { RTStrmPrintf(pStrm, "%s extpack %s install [--replace] <tarball> |\n" @@ -779,15 +836,35 @@ void printUsage(USAGECATEGORY u64Cmd, PRTSTREAM pStrm) * Print a usage synopsis and the syntax error message. * @returns RTEXITCODE_SYNTAX. */ -RTEXITCODE errorSyntax(USAGECATEGORY u64Cmd, const char *pszFormat, ...) +RTEXITCODE errorSyntax(USAGECATEGORY fCategory, const char *pszFormat, ...) +{ + va_list args; + showLogo(g_pStdErr); // show logo even if suppressed +#ifndef VBOX_ONLY_DOCS + if (g_fInternalMode) + printUsageInternal(fCategory, g_pStdErr); + else + printUsage(fCategory, ~0U, g_pStdErr); +#endif /* !VBOX_ONLY_DOCS */ + va_start(args, pszFormat); + RTStrmPrintf(g_pStdErr, "\nSyntax error: %N\n", pszFormat, &args); + va_end(args); + return RTEXITCODE_SYNTAX; +} + +/** + * Print a usage synopsis and the syntax error message. + * @returns RTEXITCODE_SYNTAX. + */ +RTEXITCODE errorSyntaxEx(USAGECATEGORY fCategory, uint32_t fSubCategory, const char *pszFormat, ...) { va_list args; showLogo(g_pStdErr); // show logo even if suppressed #ifndef VBOX_ONLY_DOCS if (g_fInternalMode) - printUsageInternal(u64Cmd, g_pStdErr); + printUsageInternal(fCategory, g_pStdErr); else - printUsage(u64Cmd, g_pStdErr); + printUsage(fCategory, fSubCategory, g_pStdErr); #endif /* !VBOX_ONLY_DOCS */ va_start(args, pszFormat); RTStrmPrintf(g_pStdErr, "\nSyntax error: %N\n", pszFormat, &args); @@ -800,11 +877,12 @@ RTEXITCODE errorSyntax(USAGECATEGORY u64Cmd, const char *pszFormat, ...) * * @returns RTEXITCODE_SYNTAX. * - * @param fUsageCategory The usage category of the command. + * @param fCategory The usage category of the command. + * @param fSubCategory The usage sub-category of the command. * @param rc The RTGetOpt return code. * @param pValueUnion The value union. */ -RTEXITCODE errorGetOpt(USAGECATEGORY fUsageCategory, int rc, union RTGETOPTUNION const *pValueUnion) +RTEXITCODE errorGetOptEx(USAGECATEGORY fCategory, uint32_t fSubCategory, int rc, union RTGETOPTUNION const *pValueUnion) { /* * Check if it is an unhandled standard option. @@ -820,9 +898,9 @@ RTEXITCODE errorGetOpt(USAGECATEGORY fUsageCategory, int rc, union RTGETOPTUNION showLogo(g_pStdErr); #ifndef VBOX_ONLY_DOCS if (g_fInternalMode) - printUsageInternal(fUsageCategory, g_pStdOut); + printUsageInternal(fCategory, g_pStdOut); else - printUsage(fUsageCategory, g_pStdOut); + printUsage(fCategory, fSubCategory, g_pStdOut); #endif return RTEXITCODE_SUCCESS; } @@ -833,9 +911,9 @@ RTEXITCODE errorGetOpt(USAGECATEGORY fUsageCategory, int rc, union RTGETOPTUNION showLogo(g_pStdErr); // show logo even if suppressed #ifndef VBOX_ONLY_DOCS if (g_fInternalMode) - printUsageInternal(fUsageCategory, g_pStdErr); + printUsageInternal(fCategory, g_pStdErr); else - printUsage(fUsageCategory, g_pStdErr); + printUsage(fCategory, fSubCategory, g_pStdErr); #endif /* !VBOX_ONLY_DOCS */ if (rc == VINF_GETOPT_NOT_OPTION) @@ -856,6 +934,20 @@ RTEXITCODE errorGetOpt(USAGECATEGORY fUsageCategory, int rc, union RTGETOPTUNION } /** + * errorSyntax for RTGetOpt users. + * + * @returns RTEXITCODE_SYNTAX. + * + * @param fUsageCategory The usage category of the command. + * @param rc The RTGetOpt return code. + * @param pValueUnion The value union. + */ +RTEXITCODE errorGetOpt(USAGECATEGORY fCategory, int rc, union RTGETOPTUNION const *pValueUnion) +{ + return errorGetOptEx(fCategory, ~0U, rc, pValueUnion); +} + +/** * Print an error message without the syntax stuff. * * @returns RTEXITCODE_SYNTAX. diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp index 3b3aed79..26b87a7e 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp @@ -23,8 +23,6 @@ #include <VBox/com/array.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> -#include <VBox/com/EventQueue.h> - #include <VBox/com/VirtualBox.h> #endif /* !VBOX_ONLY_DOCS */ diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp index dd49dd94..789bc792 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -50,29 +50,41 @@ using namespace com; HRESULT showSnapshots(ComPtr<ISnapshot> &rootSnapshot, ComPtr<ISnapshot> ¤tSnapshot, VMINFO_DETAILS details, - const Bstr &prefix /* = ""*/, + const Utf8Str &prefix /* = ""*/, int level /*= 0*/) { /* start with the root */ Bstr name; Bstr uuid; - CHECK_ERROR2_RET(rootSnapshot,COMGETTER(Name)(name.asOutParam()), hrcCheck); - CHECK_ERROR2_RET(rootSnapshot,COMGETTER(Id)(uuid.asOutParam()), hrcCheck); + Bstr description; + CHECK_ERROR2_RET(rootSnapshot, COMGETTER(Name)(name.asOutParam()), hrcCheck); + CHECK_ERROR2_RET(rootSnapshot, COMGETTER(Id)(uuid.asOutParam()), hrcCheck); + CHECK_ERROR2_RET(rootSnapshot, COMGETTER(Description)(description.asOutParam()), hrcCheck); + bool fCurrent = (rootSnapshot == currentSnapshot); if (details == VMINFO_MACHINEREADABLE) { /* print with hierarchical numbering */ - RTPrintf("SnapshotName%ls=\"%ls\"\n", prefix.raw(), name.raw()); - RTPrintf("SnapshotUUID%ls=\"%s\"\n", prefix.raw(), Utf8Str(uuid).c_str()); + RTPrintf("SnapshotName%s=\"%ls\"\n", prefix.c_str(), name.raw()); + RTPrintf("SnapshotUUID%s=\"%s\"\n", prefix.c_str(), Utf8Str(uuid).c_str()); + if (!description.isEmpty()) + RTPrintf("SnapshotDescription%s=\"%ls\"\n", prefix.c_str(), description.raw()); + if (fCurrent) + { + RTPrintf("CurrentSnapshotName=\"%ls\"\n", name.raw()); + RTPrintf("CurrentSnapshotUUID=\"%s\"\n", Utf8Str(uuid).c_str()); + RTPrintf("CurrentSnapshotNode=\"SnapshotName%s\"\n", prefix.c_str()); + } } else { /* print with indentation */ - bool fCurrent = (rootSnapshot == currentSnapshot); - RTPrintf(" %lsName: %ls (UUID: %s)%s\n", - prefix.raw(), + RTPrintf(" %sName: %ls (UUID: %s)%s\n", + prefix.c_str(), name.raw(), Utf8Str(uuid).c_str(), (fCurrent) ? " *" : ""); + if (!description.isEmpty()) + RTPrintf(" %sDescription:\n%ls\n", prefix.c_str(), description.raw()); } /* get the children */ @@ -86,12 +98,12 @@ HRESULT showSnapshots(ComPtr<ISnapshot> &rootSnapshot, ComPtr<ISnapshot> snapshot = coll[index]; if (snapshot) { - Bstr newPrefix; + Utf8Str newPrefix; if (details == VMINFO_MACHINEREADABLE) - newPrefix = Utf8StrFmt("%ls-%d", prefix.raw(), index + 1); + newPrefix = Utf8StrFmt("%s-%d", prefix.c_str(), index + 1); else { - newPrefix = Utf8StrFmt("%ls ", prefix.raw()); + newPrefix = Utf8StrFmt("%s ", prefix.c_str()); } /* recursive call */ @@ -341,7 +353,7 @@ HRESULT showBandwidthGroups(ComPtr<IBandwidthControl> &bwCtrl, HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, ComPtr<IMachine> machine, VMINFO_DETAILS details /*= VMINFO_NONE*/, - ComPtr<IConsole> console /*= ComPtr <IConsole> ()*/) + ComPtr<IConsole> console /*= ComPtr<IConsole> ()*/) { HRESULT rc; @@ -534,7 +546,9 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, RTPrintf("Firmware: %s\n", pszFirmwareType); SHOW_ULONG_PROP( machine, CPUCount, "cpus", "Number of CPUs", ""); - SHOW_BOOLEAN_METHOD( machine, GetCPUProperty(CPUPropertyType_Synthetic, &f), "synthcpu", "Synthetic Cpu"); + SHOW_BOOLEAN_METHOD( machine, GetCPUProperty(CPUPropertyType_PAE, &f), "pae", "PAE"); + SHOW_BOOLEAN_METHOD( machine, GetCPUProperty(CPUPropertyType_LongMode, &f), "longmode", "Long Mode"); + SHOW_BOOLEAN_METHOD( machine, GetCPUProperty(CPUPropertyType_Synthetic, &f), "synthcpu", "Synthetic CPU"); if (details != VMINFO_MACHINEREADABLE) RTPrintf("CPUID overrides: "); @@ -565,7 +579,7 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, if (!cFound && details != VMINFO_MACHINEREADABLE) RTPrintf("None\n"); - ComPtr <IBIOSSettings> biosSettings; + ComPtr<IBIOSSettings> biosSettings; CHECK_ERROR2_RET(machine, COMGETTER(BIOSSettings)(biosSettings.asOutParam()), hrcCheck); BIOSBootMenuMode_T bootMenuMode; @@ -654,14 +668,13 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, SHOW_BOOLEAN_PROP(biosSettings, ACPIEnabled, "acpi", "ACPI"); SHOW_BOOLEAN_PROP(biosSettings, IOAPICEnabled, "ioapic", "IOAPIC"); - SHOW_BOOLEAN_METHOD(machine, GetCPUProperty(CPUPropertyType_PAE, &f), "pae", "PAE"); SHOW_LONG64_PROP(biosSettings, TimeOffset, "biossystemtimeoffset", "Time offset", "ms"); SHOW_BOOLEAN_PROP_EX(machine, RTCUseUTC, "rtcuseutc", "RTC", "UTC", "local time"); SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_Enabled, &f), "hwvirtex", "Hardw. virt.ext"); - SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_Exclusive, &f), "hwvirtexexcl", "Hardw. virt.ext exclusive"); SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_NestedPaging, &f),"nestedpaging", "Nested Paging"); SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_LargePages, &f), "largepages", "Large Pages"); SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_VPID, &f), "vtxvpid", "VT-x VPID"); + SHOW_BOOLEAN_METHOD(machine, GetHWVirtExProperty(HWVirtExPropertyType_UnrestrictedExecution, &f), "vtxux", "VT-x unr. exec."); MachineState_T machineState; CHECK_ERROR2_RET(machine, COMGETTER(State)(&machineState), hrcCheck); @@ -700,6 +713,7 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, SHOW_STRING_PROP( machine, TracingConfig, "tracing-config", "Tracing Configuration"); SHOW_BOOLEAN_PROP( machine, AutostartEnabled, "autostart-enabled", "Autostart Enabled"); SHOW_ULONG_PROP( machine, AutostartDelay, "autostart-delay", "Autostart Delay", ""); + SHOW_STRING_PROP( machine, DefaultFrontend, "defaultfrontend", "Default Frontend"); /** @todo Convert the remainder of the function to SHOW_XXX macros and add error * checking where missing. */ @@ -1006,13 +1020,13 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, RTPrintf("natnet%d=\"%ls\"\n", currentNIC + 1, strNetwork.length() ? strNetwork.raw(): Bstr("nat").raw()); strAttachment = "nat"; strNatSettings = Utf8StrFmt("mtu=\"%d\"\nsockSnd=\"%d\"\nsockRcv=\"%d\"\ntcpWndSnd=\"%d\"\ntcpWndRcv=\"%d\"\n", - mtu, sockSnd ? sockSnd : 64, sockRcv ? sockRcv : 64 , tcpSnd ? tcpSnd : 64, tcpRcv ? tcpRcv : 64); + mtu, sockSnd ? sockSnd : 64, sockRcv ? sockRcv : 64, tcpSnd ? tcpSnd : 64, tcpRcv ? tcpRcv : 64); } else { strAttachment = "NAT"; strNatSettings = Utf8StrFmt("NIC %d Settings: MTU: %d, Socket (send: %d, receive: %d), TCP Window (send:%d, receive: %d)\n", - currentNIC + 1, mtu, sockSnd ? sockSnd : 64, sockRcv ? sockRcv : 64 , tcpSnd ? tcpSnd : 64, tcpRcv ? tcpRcv : 64); + currentNIC + 1, mtu, sockSnd ? sockSnd : 64, sockRcv ? sockRcv : 64, tcpSnd ? tcpSnd : 64, tcpRcv ? tcpRcv : 64); } break; } @@ -1058,6 +1072,7 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, strAttachment = Utf8StrFmt("Host-only Interface '%ls'", strHostonlyAdp.raw()); break; } + case NetworkAttachmentType_Generic: { Bstr strGenericDriver; @@ -1088,6 +1103,21 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, } break; } + + case NetworkAttachmentType_NATNetwork: + { + Bstr strNetwork; + nic->COMGETTER(NATNetwork)(strNetwork.asOutParam()); + if (details == VMINFO_MACHINEREADABLE) + { + RTPrintf("nat-network%d=\"%ls\"\n", currentNIC + 1, strNetwork.raw()); + strAttachment = "natnetwork"; + } + else + strAttachment = Utf8StrFmt("NAT Network '%s'", Utf8Str(strNetwork).c_str()); + break; + } + default: strAttachment = "unknown"; break; @@ -1106,7 +1136,7 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, case NetworkAdapterPromiscModePolicy_Deny: pszPromiscuousGuestPolicy = "deny"; break; case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPromiscuousGuestPolicy = "allow-vms"; break; case NetworkAdapterPromiscModePolicy_AllowAll: pszPromiscuousGuestPolicy = "allow-all"; break; - default: AssertFailedReturn(VERR_INTERNAL_ERROR_4); + default: AssertFailedReturn(E_INVALIDARG); } /* trace stuff */ @@ -1203,6 +1233,10 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, pszHID = "USB Tablet and PS/2 Mouse"; pszMrHID = "combomouse"; break; + case PointingHIDType_USBMultiTouch: + pszHID = "USB Multi-Touch"; + pszMrHID = "usbmultitouch"; + break; default: break; } @@ -1553,7 +1587,24 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, if (details == VMINFO_MACHINEREADABLE) RTPrintf("draganddrop=\"%s\"\n", psz); else - RTPrintf("Drag'n'drop Mode: %s\n", psz); + RTPrintf("Drag'n'drop Mode: %s\n", psz); + } + + { + SessionState_T sessState; + rc = machine->COMGETTER(SessionState)(&sessState); + if (SUCCEEDED(rc) && sessState != SessionState_Unlocked) + { + Bstr sessType; + rc = machine->COMGETTER(SessionType)(sessType.asOutParam()); + if (SUCCEEDED(rc) && !sessType.isEmpty()) + { + if (details == VMINFO_MACHINEREADABLE) + RTPrintf("SessionType=\"%ls\"\n", sessType.raw()); + else + RTPrintf("Session type: %ls\n", sessType.raw()); + } + } } if (console) @@ -1570,7 +1621,8 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, return rc; } ULONG xRes, yRes, bpp; - rc = display->GetScreenResolution(0, &xRes, &yRes, &bpp); + LONG xOrigin, yOrigin; + rc = display->GetScreenResolution(0, &xRes, &yRes, &bpp, &xOrigin, &yOrigin); if (rc == E_ACCESSDENIED) break; /* VM not powered up */ if (FAILED(rc)) @@ -1580,9 +1632,9 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, return rc; } if (details == VMINFO_MACHINEREADABLE) - RTPrintf("VideoMode=\"%d,%d,%d\"\n", xRes, yRes, bpp); + RTPrintf("VideoMode=\"%d,%d,%d\"@%d,%d\n", xRes, yRes, bpp, xOrigin, yOrigin); else - RTPrintf("Video mode: %dx%dx%d\n", xRes, yRes, bpp); + RTPrintf("Video mode: %dx%dx%d at %d,%d\n", xRes, yRes, bpp, xOrigin, yOrigin); } while (0); } @@ -1712,30 +1764,51 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, /* * USB. */ - ComPtr<IUSBController> USBCtl; - rc = machine->COMGETTER(USBController)(USBCtl.asOutParam()); + SafeIfaceArray<IUSBController> USBCtlColl; + rc = machine->COMGETTER(USBControllers)(ComSafeArrayAsOutParam(USBCtlColl)); if (SUCCEEDED(rc)) { - BOOL fEnabled; - BOOL fEHCIEnabled; - rc = USBCtl->COMGETTER(Enabled)(&fEnabled); - if (FAILED(rc)) - fEnabled = false; + bool fOhciEnabled = false; + bool fEhciEnabled = false; + + for (unsigned i = 0; i < USBCtlColl.size(); i++) + { + USBControllerType_T enmType; + + rc = USBCtlColl[i]->COMGETTER(Type)(&enmType); + if (SUCCEEDED(rc)) + { + switch (enmType) + { + case USBControllerType_OHCI: + fOhciEnabled = true; + break; + case USBControllerType_EHCI: + fEhciEnabled = true; + break; + default: + break; + } + } + } + if (details == VMINFO_MACHINEREADABLE) - RTPrintf("usb=\"%s\"\n", fEnabled ? "on" : "off"); + RTPrintf("usb=\"%s\"\n", fOhciEnabled ? "on" : "off"); else - RTPrintf("USB: %s\n", fEnabled ? "enabled" : "disabled"); + RTPrintf("USB: %s\n", fOhciEnabled ? "enabled" : "disabled"); - rc = USBCtl->COMGETTER(EnabledEHCI)(&fEHCIEnabled); - if (FAILED(rc)) - fEHCIEnabled = false; if (details == VMINFO_MACHINEREADABLE) - RTPrintf("ehci=\"%s\"\n", fEHCIEnabled ? "on" : "off"); + RTPrintf("ehci=\"%s\"\n", fEhciEnabled ? "on" : "off"); else - RTPrintf("EHCI: %s\n", fEHCIEnabled ? "enabled" : "disabled"); + RTPrintf("EHCI: %s\n", fEhciEnabled ? "enabled" : "disabled"); + } + ComPtr<IUSBDeviceFilters> USBFlts; + rc = machine->COMGETTER(USBDeviceFilters)(USBFlts.asOutParam()); + if (SUCCEEDED(rc)) + { SafeIfaceArray <IUSBDeviceFilter> Coll; - rc = USBCtl->COMGETTER(DeviceFilters)(ComSafeArrayAsOutParam(Coll)); + rc = USBFlts->COMGETTER(DeviceFilters)(ComSafeArrayAsOutParam(Coll)); if (SUCCEEDED(rc)) { if (details != VMINFO_MACHINEREADABLE) @@ -1836,7 +1909,7 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, { for (size_t index = 0; index < coll.size(); ++index) { - ComPtr <IHostUSBDevice> dev = coll[index]; + ComPtr<IHostUSBDevice> dev = coll[index]; /* Query info. */ Bstr id; @@ -1925,7 +1998,7 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, { for (size_t index = 0; index < coll.size(); ++index) { - ComPtr <IUSBDevice> dev = coll[index]; + ComPtr<IUSBDevice> dev = coll[index]; /* Query info. */ Bstr id; @@ -2082,7 +2155,7 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, for (size_t i = 0; i < folders.size(); ++i) { - ComPtr <ISharedFolder> sf = folders[i]; + ComPtr<ISharedFolder> sf = folders[i]; Bstr name, hostPath; BOOL writable; @@ -2113,7 +2186,7 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, for (size_t i = 0; i < folders.size(); ++i) { - ComPtr <ISharedFolder> sf = folders[i]; + ComPtr<ISharedFolder> sf = folders[i]; Bstr name, hostPath; sf->COMGETTER(Name)(name.asOutParam()); @@ -2269,6 +2342,60 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, RTPrintf("\n"); } + { + /* Video capture */ + BOOL bActive = FALSE; + CHECK_ERROR_RET(machine, COMGETTER(VideoCaptureEnabled)(&bActive), rc); + com::SafeArray<BOOL> screens; + CHECK_ERROR_RET(machine, COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens)), rc); + ULONG Width; + CHECK_ERROR_RET(machine, COMGETTER(VideoCaptureWidth)(&Width), rc); + ULONG Height; + CHECK_ERROR_RET(machine, COMGETTER(VideoCaptureHeight)(&Height), rc); + ULONG Rate; + CHECK_ERROR_RET(machine, COMGETTER(VideoCaptureRate)(&Rate), rc); + ULONG Fps; + CHECK_ERROR_RET(machine, COMGETTER(VideoCaptureFPS)(&Fps), rc); + Bstr File; + CHECK_ERROR_RET(machine, COMGETTER(VideoCaptureFile)(File.asOutParam()), rc); + if (details == VMINFO_MACHINEREADABLE) + { + RTPrintf("vcpenabled=\"%s\"\n", bActive ? "on" : "off"); + RTPrintf("vcpscreens="); + bool fComma = false; + for (unsigned i = 0; i < screens.size(); i++) + if (screens[i]) + { + RTPrintf("%s%u", fComma ? "," : "", i); + fComma = true; + } + RTPrintf("\n"); + RTPrintf("vcpfile=\"%ls\"\n", File.raw()); + RTPrintf("vcpwidth=%u\n", (unsigned)Width); + RTPrintf("vcpheight=%u\n", (unsigned)Height); + RTPrintf("vcprate=%u\n", (unsigned)Rate); + RTPrintf("vcpfps=%u\n", (unsigned)Fps); + } + else + { + RTPrintf("Video capturing: %s\n", bActive ? "active" : "not active"); + RTPrintf("Capture screens: "); + bool fComma = false; + for (unsigned i = 0; i < screens.size(); i++) + if (screens[i]) + { + RTPrintf("%s%u", fComma ? "," : "", i); + fComma = true; + } + RTPrintf("\n"); + RTPrintf("Capture file: %ls\n", File.raw()); + RTPrintf("Capture dimensions: %ux%u\n", Width, Height); + RTPrintf("Capture rate: %u kbps\n", Rate); + RTPrintf("Capture FPS: %u\n", Fps); + RTPrintf("\n"); + } + } + if ( details == VMINFO_STANDARD || details == VMINFO_FULL || details == VMINFO_MACHINEREADABLE) @@ -2284,7 +2411,6 @@ HRESULT showVMInfo(ComPtr<IVirtualBox> virtualBox, } } - if (details != VMINFO_MACHINEREADABLE) RTPrintf("Guest:\n\n"); @@ -2479,7 +2605,7 @@ int handleShowVMInfo(HandlerArg *a) return errorSyntax(USAGE_SHOWVMINFO, "VM name or UUID required"); /* try to find the given machine */ - ComPtr <IMachine> machine; + ComPtr<IMachine> machine; CHECK_ERROR(a->virtualBox, FindMachine(Bstr(VMNameOrUuid).raw(), machine.asOutParam())); if (FAILED(rc)) diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp index b98e2e04..ce7a7619 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -75,10 +75,31 @@ static const char*getDeviceTypeText(DeviceType_T enmType) /** + * List internal networks. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox smart pointer. + */ +static HRESULT listInternalNetworks(const ComPtr<IVirtualBox> pVirtualBox) +{ + HRESULT rc; + com::SafeArray<BSTR> internalNetworks; + CHECK_ERROR(pVirtualBox, COMGETTER(InternalNetworks)(ComSafeArrayAsOutParam(internalNetworks))); + for (size_t i = 0; i < internalNetworks.size(); ++i) + { + RTPrintf("Name: %ls\n", internalNetworks[i]); + } + return rc; +} + + +/** * List network interfaces information (bridged/host only). * * @returns See produceList. * @param pVirtualBox Reference to the IVirtualBox smart pointer. + * @param fIsBridged Selects between listing host interfaces (for + * use with bridging) or host only interfaces. */ static HRESULT listNetworkInterfaces(const ComPtr<IVirtualBox> pVirtualBox, bool fIsBridged) @@ -174,6 +195,12 @@ static HRESULT listHostInfo(const ComPtr<IVirtualBox> pVirtualBox) ULONG processorCount = 0; CHECK_ERROR(Host, COMGETTER(ProcessorCount)(&processorCount)); RTPrintf("Processor count: %lu\n", processorCount); + ULONG processorOnlineCoreCount = 0; + CHECK_ERROR(Host, COMGETTER(ProcessorOnlineCoreCount)(&processorOnlineCoreCount)); + RTPrintf("Processor online core count: %lu\n", processorOnlineCoreCount); + ULONG processorCoreCount = 0; + CHECK_ERROR(Host, COMGETTER(ProcessorCoreCount)(&processorCoreCount)); + RTPrintf("Processor core count: %lu\n", processorCoreCount); ULONG processorSpeed = 0; Bstr processorDescription; for (ULONG i = 0; i < processorCount; i++) @@ -182,7 +209,7 @@ static HRESULT listHostInfo(const ComPtr<IVirtualBox> pVirtualBox) if (processorSpeed) RTPrintf("Processor#%u speed: %lu MHz\n", i, processorSpeed); else - RTPrintf("Processor#%u speed: unknown\n", i, processorSpeed); + RTPrintf("Processor#%u speed: unknown\n", i); CHECK_ERROR(Host, GetProcessorDescription(i, processorDescription.asOutParam())); RTPrintf("Processor#%u description: %ls\n", i, processorDescription.raw()); } @@ -211,118 +238,33 @@ static HRESULT listHostInfo(const ComPtr<IVirtualBox> pVirtualBox) * * @returns See produceList. * @param pVirtualBox Reference to the IVirtualBox smart pointer. + * @param aMedia Medium objects to list information for. + * @param pszParentUUIDStr String with the parent UUID string (or "base"). + * @param fOptLong Long (@c true) or short list format. */ static HRESULT listMedia(const ComPtr<IVirtualBox> pVirtualBox, const com::SafeIfaceArray<IMedium> &aMedia, - const char *pszParentUUIDStr) + const char *pszParentUUIDStr, + bool fOptLong) { HRESULT rc = S_OK; for (size_t i = 0; i < aMedia.size(); ++i) { ComPtr<IMedium> pMedium = aMedia[i]; - Bstr uuid; - pMedium->COMGETTER(Id)(uuid.asOutParam()); - RTPrintf("UUID: %s\n", Utf8Str(uuid).c_str()); - if (pszParentUUIDStr) - RTPrintf("Parent UUID: %s\n", pszParentUUIDStr); - Bstr format; - pMedium->COMGETTER(Format)(format.asOutParam()); - RTPrintf("Format: %ls\n", format.raw()); - Bstr filepath; - pMedium->COMGETTER(Location)(filepath.asOutParam()); - RTPrintf("Location: %ls\n", filepath.raw()); - - MediumState_T enmState; - pMedium->RefreshState(&enmState); - const char *stateStr = "unknown"; - switch (enmState) - { - case MediumState_NotCreated: - stateStr = "not created"; - break; - case MediumState_Created: - stateStr = "created"; - break; - case MediumState_LockedRead: - stateStr = "locked read"; - break; - case MediumState_LockedWrite: - stateStr = "locked write"; - break; - case MediumState_Inaccessible: - stateStr = "inaccessible"; - break; - case MediumState_Creating: - stateStr = "creating"; - break; - case MediumState_Deleting: - stateStr = "deleting"; - break; - } - RTPrintf("State: %s\n", stateStr); - MediumType_T type; - pMedium->COMGETTER(Type)(&type); - const char *typeStr = "unknown"; - switch (type) - { - case MediumType_Normal: - typeStr = "normal"; - break; - case MediumType_Immutable: - typeStr = "immutable"; - break; - case MediumType_Writethrough: - typeStr = "writethrough"; - break; - case MediumType_Shareable: - typeStr = "shareable"; - break; - case MediumType_Readonly: - typeStr = "readonly"; - break; - case MediumType_MultiAttach: - typeStr = "multiattach"; - break; - } - RTPrintf("Type: %s\n", typeStr); + rc = showMediumInfo(pVirtualBox, pMedium, pszParentUUIDStr, fOptLong); - com::SafeArray<BSTR> machineIds; - pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds)); - for (size_t j = 0; j < machineIds.size(); ++j) - { - ComPtr<IMachine> machine; - CHECK_ERROR(pVirtualBox, FindMachine(machineIds[j], machine.asOutParam())); - ASSERT(machine); - Bstr name; - machine->COMGETTER(Name)(name.asOutParam()); - RTPrintf("%s%ls (UUID: %ls)", - j == 0 ? "Usage: " : " ", - name.raw(), machineIds[j]); - com::SafeArray<BSTR> snapshotIds; - pMedium->GetSnapshotIds(machineIds[j], - ComSafeArrayAsOutParam(snapshotIds)); - for (size_t k = 0; k < snapshotIds.size(); ++k) - { - ComPtr<ISnapshot> snapshot; - machine->FindSnapshot(snapshotIds[k], snapshot.asOutParam()); - if (snapshot) - { - Bstr snapshotName; - snapshot->COMGETTER(Name)(snapshotName.asOutParam()); - RTPrintf(" [%ls (UUID: %ls)]", snapshotName.raw(), snapshotIds[k]); - } - } - RTPrintf("\n"); - } RTPrintf("\n"); com::SafeIfaceArray<IMedium> children; CHECK_ERROR(pMedium, COMGETTER(Children)(ComSafeArrayAsOutParam(children))); if (children.size() > 0) { + Bstr uuid; + pMedium->COMGETTER(Id)(uuid.asOutParam()); + // depth first listing of child media - rc = listMedia(pVirtualBox, children, Utf8Str(uuid).c_str()); + rc = listMedia(pVirtualBox, children, Utf8Str(uuid).c_str(), fOptLong); } } @@ -353,18 +295,22 @@ static HRESULT listHddBackends(const ComPtr<IVirtualBox> pVirtualBox) Bstr description; CHECK_ERROR(mediumFormats[i], - COMGETTER(Id)(description.asOutParam())); + COMGETTER(Name)(description.asOutParam())); - ULONG caps; + ULONG caps = 0; + com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap; CHECK_ERROR(mediumFormats[i], - COMGETTER(Capabilities)(&caps)); + COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap))); + for (ULONG j = 0; j < mediumFormatCap.size(); j++) + caps |= mediumFormatCap[j]; + RTPrintf("Backend %u: id='%ls' description='%ls' capabilities=%#06x extensions='", i, id.raw(), description.raw(), caps); /* File extensions */ - com::SafeArray <BSTR> fileExtensions; - com::SafeArray <DeviceType_T> deviceTypes; + com::SafeArray<BSTR> fileExtensions; + com::SafeArray<DeviceType_T> deviceTypes; CHECK_ERROR(mediumFormats[i], DescribeFileExtensions(ComSafeArrayAsOutParam(fileExtensions), ComSafeArrayAsOutParam(deviceTypes))); for (size_t j = 0; j < fileExtensions.size(); ++j) @@ -376,11 +322,11 @@ static HRESULT listHddBackends(const ComPtr<IVirtualBox> pVirtualBox) RTPrintf("'"); /* Configuration keys */ - com::SafeArray <BSTR> propertyNames; - com::SafeArray <BSTR> propertyDescriptions; - com::SafeArray <DataType_T> propertyTypes; - com::SafeArray <ULONG> propertyFlags; - com::SafeArray <BSTR> propertyDefaults; + com::SafeArray<BSTR> propertyNames; + com::SafeArray<BSTR> propertyDescriptions; + com::SafeArray<DataType_T> propertyTypes; + com::SafeArray<ULONG> propertyFlags; + com::SafeArray<BSTR> propertyDefaults; CHECK_ERROR(mediumFormats[i], DescribeProperties(ComSafeArrayAsOutParam(propertyNames), ComSafeArrayAsOutParam(propertyDescriptions), @@ -438,7 +384,7 @@ static HRESULT listUsbHost(const ComPtr<IVirtualBox> &pVirtualBox) { for (size_t i = 0; i < CollPtr.size(); ++i) { - ComPtr <IHostUSBDevice> dev = CollPtr[i]; + ComPtr<IHostUSBDevice> dev = CollPtr[i]; /* Query info. */ Bstr id; @@ -605,6 +551,7 @@ static HRESULT listSystemProperties(const ComPtr<IVirtualBox> &pVirtualBox) Bstr str; ULONG ulValue; LONG64 i64Value; + BOOL fValue; pVirtualBox->COMGETTER(APIVersion)(str.asOutParam()); RTPrintf("API version: %ls\n", str.raw()); @@ -617,6 +564,8 @@ static HRESULT listSystemProperties(const ComPtr<IVirtualBox> &pVirtualBox) RTPrintf("Minimum video RAM size: %u Megabytes\n", ulValue); systemProperties->COMGETTER(MaxGuestVRAM)(&ulValue); RTPrintf("Maximum video RAM size: %u Megabytes\n", ulValue); + systemProperties->COMGETTER(MaxGuestMonitors)(&ulValue); + RTPrintf("Maximum guest monitor count: %u\n", ulValue); systemProperties->COMGETTER(MinGuestCPUCount)(&ulValue); RTPrintf("Minimum guest CPU count: %u\n", ulValue); systemProperties->COMGETTER(MaxGuestCPUCount)(&ulValue); @@ -673,8 +622,22 @@ static HRESULT listSystemProperties(const ComPtr<IVirtualBox> &pVirtualBox) RTPrintf("Maximum Floppy Port count: %u\n", ulValue); systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_Floppy, &ulValue); RTPrintf("Maximum Devices per Floppy Port: %u\n", ulValue); +#if 0 + systemProperties->GetFreeDiskSpaceWarning(&i64Value); + RTPrintf("Free disk space warning at: %u Bytes\n", i64Value); + systemProperties->GetFreeDiskSpacePercentWarning(&ulValue); + RTPrintf("Free disk space warning at: %u %%\n", ulValue); + systemProperties->GetFreeDiskSpaceError(&i64Value); + RTPrintf("Free disk space error at: %u Bytes\n", i64Value); + systemProperties->GetFreeDiskSpacePercentError(&ulValue); + RTPrintf("Free disk space error at: %u %%\n", ulValue); +#endif systemProperties->COMGETTER(DefaultMachineFolder)(str.asOutParam()); RTPrintf("Default machine folder: %ls\n", str.raw()); + systemProperties->COMGETTER(ExclusiveHwVirt)(&fValue); + RTPrintf("Exclusive HW virtualization use: %ls\n", fValue ? L"on" : L"off"); + systemProperties->COMGETTER(DefaultHardDiskFormat)(str.asOutParam()); + RTPrintf("Default hard disk format: %ls\n", str.raw()); systemProperties->COMGETTER(VRDEAuthLibrary)(str.asOutParam()); RTPrintf("VRDE auth library: %ls\n", str.raw()); systemProperties->COMGETTER(WebServiceAuthLibrary)(str.asOutParam()); @@ -683,6 +646,8 @@ static HRESULT listSystemProperties(const ComPtr<IVirtualBox> &pVirtualBox) RTPrintf("Remote desktop ExtPack: %ls\n", str.raw()); systemProperties->COMGETTER(LogHistoryCount)(&ulValue); RTPrintf("Log history count: %u\n", ulValue); + systemProperties->COMGETTER(DefaultFrontend)(str.asOutParam()); + RTPrintf("Default frontend: %ls\n", str.raw()); systemProperties->COMGETTER(AutostartDatabasePath)(str.asOutParam()); RTPrintf("Autostart database path: %ls\n", str.raw()); systemProperties->COMGETTER(DefaultAdditionsISO)(str.asOutParam()); @@ -773,6 +738,35 @@ static HRESULT listGroups(const ComPtr<IVirtualBox> &pVirtualBox) /** + * List video capture devices. + * + * @returns See produceList. + * @param pVirtualBox Reference to the IVirtualBox pointer. + */ +static HRESULT listVideoInputDevices(const ComPtr<IVirtualBox> pVirtualBox) +{ + HRESULT rc; + ComPtr<IHost> host; + CHECK_ERROR(pVirtualBox, COMGETTER(Host)(host.asOutParam())); + com::SafeIfaceArray<IHostVideoInputDevice> hostVideoInputDevices; + CHECK_ERROR(host, COMGETTER(VideoInputDevices)(ComSafeArrayAsOutParam(hostVideoInputDevices))); + RTPrintf("Video Input Devices: %u\n", hostVideoInputDevices.size()); + for (size_t i = 0; i < hostVideoInputDevices.size(); ++i) + { + ComPtr<IHostVideoInputDevice> p = hostVideoInputDevices[i]; + Bstr name; + p->COMGETTER(Name)(name.asOutParam()); + Bstr path; + p->COMGETTER(Path)(path.asOutParam()); + Bstr alias; + p->COMGETTER(Alias)(alias.asOutParam()); + RTPrintf("%ls \"%ls\"\n%ls\n", alias.raw(), name.raw(), path.raw()); + } + return rc; +} + + +/** * The type of lists we can produce. */ enum enmListType @@ -783,6 +777,7 @@ enum enmListType kListOsTypes, kListHostDvds, kListHostFloppies, + kListInternalNetworks, kListBridgedInterfaces, #if defined(VBOX_WITH_NETFLT) kListHostOnlyInterfaces, @@ -798,7 +793,9 @@ enum enmListType kListSystemProperties, kListDhcpServers, kListExtPacks, - kListGroups + kListGroups, + kListNatNetworks, + kListVideoInputDevices }; @@ -954,6 +951,10 @@ static HRESULT produceList(enum enmListType enmCommand, bool fOptLong, const Com break; } + case kListInternalNetworks: + rc = listInternalNetworks(pVirtualBox); + break; + case kListBridgedInterfaces: #if defined(VBOX_WITH_NETFLT) case kListHostOnlyInterfaces: @@ -1002,7 +1003,7 @@ static HRESULT produceList(enum enmListType enmCommand, bool fOptLong, const Com { com::SafeIfaceArray<IMedium> hdds; CHECK_ERROR(pVirtualBox, COMGETTER(HardDisks)(ComSafeArrayAsOutParam(hdds))); - rc = listMedia(pVirtualBox, hdds, "base"); + rc = listMedia(pVirtualBox, hdds, "base", fOptLong); break; } @@ -1010,7 +1011,7 @@ static HRESULT produceList(enum enmListType enmCommand, bool fOptLong, const Com { com::SafeIfaceArray<IMedium> dvds; CHECK_ERROR(pVirtualBox, COMGETTER(DVDImages)(ComSafeArrayAsOutParam(dvds))); - rc = listMedia(pVirtualBox, dvds, NULL); + rc = listMedia(pVirtualBox, dvds, NULL, fOptLong); break; } @@ -1018,7 +1019,7 @@ static HRESULT produceList(enum enmListType enmCommand, bool fOptLong, const Com { com::SafeIfaceArray<IMedium> floppies; CHECK_ERROR(pVirtualBox, COMGETTER(FloppyImages)(ComSafeArrayAsOutParam(floppies))); - rc = listMedia(pVirtualBox, floppies, NULL); + rc = listMedia(pVirtualBox, floppies, NULL, fOptLong); break; } @@ -1072,6 +1073,66 @@ static HRESULT produceList(enum enmListType enmCommand, bool fOptLong, const Com rc = listGroups(pVirtualBox); break; + case kListNatNetworks: + { + com::SafeIfaceArray<INATNetwork> nets; + CHECK_ERROR(pVirtualBox, COMGETTER(NATNetworks)(ComSafeArrayAsOutParam(nets))); + for (size_t i = 0; i < nets.size(); ++i) + { + ComPtr<INATNetwork> net = nets[i]; + Bstr netName; + net->COMGETTER(NetworkName)(netName.asOutParam()); + RTPrintf("NetworkName: %ls\n", netName.raw()); + Bstr gateway; + net->COMGETTER(Gateway)(gateway.asOutParam()); + RTPrintf("IP: %ls\n", gateway.raw()); + Bstr network; + net->COMGETTER(Network)(network.asOutParam()); + RTPrintf("Network: %ls\n", network.raw()); + BOOL fEnabled; + net->COMGETTER(IPv6Enabled)(&fEnabled); + RTPrintf("IPv6 Enabled: %s\n", fEnabled ? "Yes" : "No"); + Bstr ipv6prefix; + net->COMGETTER(Network)(network.asOutParam()); + RTPrintf("IPv6 Prefix: %ls\n", ipv6prefix.raw()); + net->COMGETTER(NeedDhcpServer)(&fEnabled); + RTPrintf("DHCP Enabled: %s\n", fEnabled ? "Yes" : "No"); + net->COMGETTER(Enabled)(&fEnabled); + RTPrintf("Enabled: %s\n", fEnabled ? "Yes" : "No"); + +#define PRINT_STRING_ARRAY(title) \ + if (strs.size() > 0) \ + { \ + RTPrintf(title); \ + size_t j = 0; \ + for (;j < strs.size(); ++j) \ + RTPrintf(" %s\n", Utf8Str(strs[j]).c_str()); \ + } + + com::SafeArray<BSTR> strs; + + CHECK_ERROR(nets[i], COMGETTER(PortForwardRules4)(ComSafeArrayAsOutParam(strs))); + PRINT_STRING_ARRAY("Port-forwarding (ipv4)\n"); + strs.setNull(); + + CHECK_ERROR(nets[i], COMGETTER(PortForwardRules6)(ComSafeArrayAsOutParam(strs))); + PRINT_STRING_ARRAY("Port-forwarding (ipv6)\n"); + strs.setNull(); + + CHECK_ERROR(nets[i], COMGETTER(LocalMappings)(ComSafeArrayAsOutParam(strs))); + PRINT_STRING_ARRAY("loopback mappings (ipv4)\n"); + strs.setNull(); + +#undef PRINT_STRING_ARRAY + RTPrintf("\n"); + } + break; + } + + case kListVideoInputDevices: + rc = listVideoInputDevices(pVirtualBox); + break; + /* No default here, want gcc warnings. */ } /* end switch */ @@ -1100,11 +1161,14 @@ int handleList(HandlerArg *a) { "ostypes", kListOsTypes, RTGETOPT_REQ_NOTHING }, { "hostdvds", kListHostDvds, RTGETOPT_REQ_NOTHING }, { "hostfloppies", kListHostFloppies, RTGETOPT_REQ_NOTHING }, + { "intnets", kListInternalNetworks, RTGETOPT_REQ_NOTHING }, { "hostifs", kListBridgedInterfaces, RTGETOPT_REQ_NOTHING }, /* backward compatibility */ { "bridgedifs", kListBridgedInterfaces, RTGETOPT_REQ_NOTHING }, #if defined(VBOX_WITH_NETFLT) { "hostonlyifs", kListHostOnlyInterfaces, RTGETOPT_REQ_NOTHING }, #endif + { "natnetworks", kListNatNetworks, RTGETOPT_REQ_NOTHING }, + { "natnets", kListNatNetworks, RTGETOPT_REQ_NOTHING }, { "hostinfo", kListHostInfo, RTGETOPT_REQ_NOTHING }, { "hostcpuids", kListHostCpuIDs, RTGETOPT_REQ_NOTHING }, { "hddbackends", kListHddBackends, RTGETOPT_REQ_NOTHING }, @@ -1117,6 +1181,7 @@ int handleList(HandlerArg *a) { "dhcpservers", kListDhcpServers, RTGETOPT_REQ_NOTHING }, { "extpacks", kListExtPacks, RTGETOPT_REQ_NOTHING }, { "groups", kListGroups, RTGETOPT_REQ_NOTHING }, + { "webcams", kListVideoInputDevices, RTGETOPT_REQ_NOTHING }, }; int ch; @@ -1144,6 +1209,7 @@ int handleList(HandlerArg *a) case kListOsTypes: case kListHostDvds: case kListHostFloppies: + case kListInternalNetworks: case kListBridgedInterfaces: #if defined(VBOX_WITH_NETFLT) case kListHostOnlyInterfaces: @@ -1160,6 +1226,8 @@ int handleList(HandlerArg *a) case kListDhcpServers: case kListExtPacks: case kListGroups: + case kListNatNetworks: + case kListVideoInputDevices: enmOptCommand = (enum enmListType)ch; if (fOptMultiple) { diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp index 86b23b65..fa6b4942 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -77,7 +77,7 @@ static int parseFilterParameters(int argc, char *argv[], } else { - ComPtr <IMachine> machine; + ComPtr<IMachine> machine; rc = aVirtualBox->FindMachine(Bstr(argv[0]).raw(), machine.asOutParam()); if (SUCCEEDED (rc)) @@ -103,11 +103,18 @@ static int parseFilterParameters(int argc, char *argv[], static Bstr toBaseName(Utf8Str& aFullName) { char *pszRaw = aFullName.mutableRaw(); - char *pszSlash = strrchr(pszRaw, '/'); - if (pszSlash) + /* + * Currently there are two metrics which base name is the same as the + * sub-metric name: CPU/MHz and Net/<iface>/LinkSpeed. + */ + if (strcmp(pszRaw, "CPU/MHz") && !RTStrSimplePatternMatch("Net/*/LinkSpeed", pszRaw)) { - *pszSlash = 0; - aFullName.jolt(); + char *pszSlash = strrchr(pszRaw, '/'); + if (pszSlash) + { + *pszSlash = 0; + aFullName.jolt(); + } } return Bstr(aFullName); } diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp index cc71cb04..a1158dd5 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -26,8 +26,6 @@ # include <VBox/com/array.h> # include <VBox/com/ErrorInfo.h> # include <VBox/com/errorprint.h> -# include <VBox/com/EventQueue.h> - # include <VBox/com/VirtualBox.h> #endif /* !VBOX_ONLY_DOCS */ @@ -155,18 +153,32 @@ int handleUnregisterVM(HandlerArg *a) machine.asOutParam()), RTEXITCODE_FAILURE); SafeIfaceArray<IMedium> aMedia; - CHECK_ERROR_RET(machine, Unregister(fDelete ? (CleanupMode_T)CleanupMode_DetachAllReturnHardDisksOnly : (CleanupMode_T)CleanupMode_DetachAllReturnNone, + CHECK_ERROR_RET(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(aMedia)), RTEXITCODE_FAILURE); if (fDelete) { ComPtr<IProgress> pProgress; - CHECK_ERROR_RET(machine, Delete(ComSafeArrayAsInParam(aMedia), pProgress.asOutParam()), + CHECK_ERROR_RET(machine, DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress.asOutParam()), RTEXITCODE_FAILURE); rc = showProgress(pProgress); CHECK_PROGRESS_ERROR_RET(pProgress, ("Machine delete failed"), RTEXITCODE_FAILURE); } + else + { + /* Note that the IMachine::Unregister method will return the medium + * reference in a sane order, which means that closing will normally + * succeed, unless there is still another machine which uses the + * medium. No harm done if we ignore the error. */ + for (size_t i = 0; i < aMedia.size(); i++) + { + IMedium *pMedium = aMedia[i]; + if (pMedium) + rc = pMedium->Close(); + } + rc = S_OK; + } return RTEXITCODE_SUCCESS; } @@ -485,7 +497,7 @@ int handleStartVM(HandlerArg *a) { HRESULT rc = S_OK; std::list<const char *> VMs; - Bstr sessionType = "gui"; + Bstr sessionType; static const RTGETOPTDEF s_aStartVMOptions[] = { @@ -825,6 +837,18 @@ int handleSetProperty(HandlerArg *a) else CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(Bstr(a->argv[1]).raw())); } + else if (!strcmp(a->argv[0], "hwvirtexclusive")) + { + bool fHwVirtExclusive; + + if (!strcmp(a->argv[1], "on")) + fHwVirtExclusive = true; + else if (!strcmp(a->argv[1], "off")) + fHwVirtExclusive = false; + else + return errorArgument("Invalid hwvirtexclusive argument '%s'", a->argv[1]); + CHECK_ERROR(systemProperties, COMSETTER(ExclusiveHwVirt)(fHwVirtExclusive)); + } else if ( !strcmp(a->argv[0], "vrdeauthlibrary") || !strcmp(a->argv[0], "vrdpauthlibrary")) { @@ -870,6 +894,13 @@ int handleSetProperty(HandlerArg *a) else CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(Bstr(a->argv[1]).raw())); } + else if (!strcmp(a->argv[0], "defaultfrontend")) + { + Bstr bstrDefaultFrontend(a->argv[1]); + if (!strcmp(a->argv[1], "default")) + bstrDefaultFrontend.setNull(); + CHECK_ERROR(systemProperties, COMSETTER(DefaultFrontend)(bstrDefaultFrontend.raw())); + } else return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]); @@ -950,7 +981,7 @@ int handleSharedFolder(HandlerArg *a) if (fTransient) { - ComPtr <IConsole> console; + ComPtr<IConsole> console; /* open an existing session for the VM */ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1); @@ -1016,7 +1047,7 @@ int handleSharedFolder(HandlerArg *a) if (fTransient) { - ComPtr <IConsole> console; + ComPtr<IConsole> console; /* open an existing session for the VM */ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1); diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp index 6d57035d..d7515fc7 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -23,8 +23,6 @@ #include <VBox/com/array.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> -#include <VBox/com/EventQueue.h> - #include <VBox/com/VirtualBox.h> #endif /* !VBOX_ONLY_DOCS */ @@ -50,7 +48,9 @@ enum { MODIFYVM_NAME = 1000, MODIFYVM_GROUPS, + MODIFYVM_DESCRIPTION, MODIFYVM_OSTYPE, + MODIFYVM_ICONFILE, MODIFYVM_MEMORY, MODIFYVM_PAGEFUSION, MODIFYVM_VRAM, @@ -58,12 +58,14 @@ enum MODIFYVM_ACPI, MODIFYVM_IOAPIC, MODIFYVM_PAE, + MODIFYVM_LONGMODE, MODIFYVM_SYNTHCPU, + MODIFYVM_TFRESET, MODIFYVM_HWVIRTEX, - MODIFYVM_HWVIRTEXEXCLUSIVE, MODIFYVM_NESTEDPAGING, MODIFYVM_LARGEPAGES, MODIFYVM_VTXVPID, + MODIFYVM_VTXUX, MODIFYVM_CPUS, MODIFYVM_CPUHOTPLUG, MODIFYVM_PLUGCPU, @@ -71,6 +73,7 @@ enum MODIFYVM_SETCPUID, MODIFYVM_DELCPUID, MODIFYVM_DELALLCPUID, + MODIFYVM_GRAPHICSCONTROLLER, MODIFYVM_MONITORCOUNT, MODIFYVM_ACCELERATE3D, #ifdef VBOX_WITH_VIDEOHWACCEL @@ -110,8 +113,9 @@ enum MODIFYVM_BRIDGEADAPTER, MODIFYVM_HOSTONLYADAPTER, MODIFYVM_INTNET, - MODIFYVM_NATNET, MODIFYVM_GENERICDRV, + MODIFYVM_NATNETWORKNAME, + MODIFYVM_NATNET, MODIFYVM_NATBINDIP, MODIFYVM_NATSETTINGS, MODIFYVM_NATPF, @@ -187,20 +191,26 @@ enum #ifdef VBOX_WITH_USB_CARDREADER MODIFYVM_USBCARDREADER, #endif - MODIFYVM_CHIPSET, #ifdef VBOX_WITH_VPX MODIFYVM_VCP, + MODIFYVM_VCP_SCREENS, MODIFYVM_VCP_FILENAME, MODIFYVM_VCP_WIDTH, - MODIFYVM_VCP_HEIGHT + MODIFYVM_VCP_HEIGHT, + MODIFYVM_VCP_RATE, + MODIFYVM_VCP_FPS, #endif + MODIFYVM_CHIPSET, + MODIFYVM_DEFAULTFRONTEND }; static const RTGETOPTDEF g_aModifyVMOptions[] = { { "--name", MODIFYVM_NAME, RTGETOPT_REQ_STRING }, { "--groups", MODIFYVM_GROUPS, RTGETOPT_REQ_STRING }, + { "--description", MODIFYVM_DESCRIPTION, RTGETOPT_REQ_STRING }, { "--ostype", MODIFYVM_OSTYPE, RTGETOPT_REQ_STRING }, + { "--iconfile", MODIFYVM_ICONFILE, RTGETOPT_REQ_STRING }, { "--memory", MODIFYVM_MEMORY, RTGETOPT_REQ_UINT32 }, { "--pagefusion", MODIFYVM_PAGEFUSION, RTGETOPT_REQ_BOOL_ONOFF }, { "--vram", MODIFYVM_VRAM, RTGETOPT_REQ_UINT32 }, @@ -208,12 +218,14 @@ static const RTGETOPTDEF g_aModifyVMOptions[] = { "--acpi", MODIFYVM_ACPI, RTGETOPT_REQ_BOOL_ONOFF }, { "--ioapic", MODIFYVM_IOAPIC, RTGETOPT_REQ_BOOL_ONOFF }, { "--pae", MODIFYVM_PAE, RTGETOPT_REQ_BOOL_ONOFF }, + { "--longmode", MODIFYVM_LONGMODE, RTGETOPT_REQ_BOOL_ONOFF }, { "--synthcpu", MODIFYVM_SYNTHCPU, RTGETOPT_REQ_BOOL_ONOFF }, + { "--triplefaultreset", MODIFYVM_TFRESET, RTGETOPT_REQ_BOOL_ONOFF }, { "--hwvirtex", MODIFYVM_HWVIRTEX, RTGETOPT_REQ_BOOL_ONOFF }, - { "--hwvirtexexcl", MODIFYVM_HWVIRTEXEXCLUSIVE, RTGETOPT_REQ_BOOL_ONOFF }, { "--nestedpaging", MODIFYVM_NESTEDPAGING, RTGETOPT_REQ_BOOL_ONOFF }, { "--largepages", MODIFYVM_LARGEPAGES, RTGETOPT_REQ_BOOL_ONOFF }, { "--vtxvpid", MODIFYVM_VTXVPID, RTGETOPT_REQ_BOOL_ONOFF }, + { "--vtxux", MODIFYVM_VTXUX, RTGETOPT_REQ_BOOL_ONOFF }, { "--cpuidset", MODIFYVM_SETCPUID, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX}, { "--cpuidremove", MODIFYVM_DELCPUID, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX}, { "--cpuidremoveall", MODIFYVM_DELALLCPUID, RTGETOPT_REQ_NOTHING}, @@ -223,6 +235,7 @@ static const RTGETOPTDEF g_aModifyVMOptions[] = { "--unplugcpu", MODIFYVM_UNPLUGCPU, RTGETOPT_REQ_UINT32 }, { "--cpuexecutioncap", MODIFYVM_CPU_EXECTUION_CAP, RTGETOPT_REQ_UINT32 }, { "--rtcuseutc", MODIFYVM_RTCUSEUTC, RTGETOPT_REQ_BOOL_ONOFF }, + { "--graphicscontroller", MODIFYVM_GRAPHICSCONTROLLER, RTGETOPT_REQ_STRING }, { "--monitorcount", MODIFYVM_MONITORCOUNT, RTGETOPT_REQ_UINT32 }, { "--accelerate3d", MODIFYVM_ACCELERATE3D, RTGETOPT_REQ_BOOL_ONOFF }, #ifdef VBOX_WITH_VIDEOHWACCEL @@ -262,8 +275,10 @@ static const RTGETOPTDEF g_aModifyVMOptions[] = { "--bridgeadapter", MODIFYVM_BRIDGEADAPTER, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, { "--hostonlyadapter", MODIFYVM_HOSTONLYADAPTER, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, { "--intnet", MODIFYVM_INTNET, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, - { "--natnet", MODIFYVM_NATNET, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, { "--nicgenericdrv", MODIFYVM_GENERICDRV, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, + { "--nat-network", MODIFYVM_NATNETWORKNAME, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, + { "--natnetwork", MODIFYVM_NATNETWORKNAME, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, + { "--natnet", MODIFYVM_NATNET, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, { "--natbindip", MODIFYVM_NATBINDIP, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, { "--natsettings", MODIFYVM_NATSETTINGS, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, { "--natpf", MODIFYVM_NATPF, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX }, @@ -331,9 +346,12 @@ static const RTGETOPTDEF g_aModifyVMOptions[] = { "--chipset", MODIFYVM_CHIPSET, RTGETOPT_REQ_STRING }, #ifdef VBOX_WITH_VPX { "--vcpenabled", MODIFYVM_VCP, RTGETOPT_REQ_BOOL_ONOFF }, + { "--vcpscreens", MODIFYVM_VCP_SCREENS, RTGETOPT_REQ_STRING }, { "--vcpfile", MODIFYVM_VCP_FILENAME, RTGETOPT_REQ_STRING }, { "--vcpwidth", MODIFYVM_VCP_WIDTH, RTGETOPT_REQ_UINT32 }, { "--vcpheight", MODIFYVM_VCP_HEIGHT, RTGETOPT_REQ_UINT32 }, + { "--vcprate", MODIFYVM_VCP_RATE, RTGETOPT_REQ_UINT32 }, + { "--vcpfps", MODIFYVM_VCP_FPS, RTGETOPT_REQ_UINT32 }, #endif { "--autostart-enabled", MODIFYVM_AUTOSTART_ENABLED, RTGETOPT_REQ_BOOL_ONOFF }, { "--autostart-delay", MODIFYVM_AUTOSTART_DELAY, RTGETOPT_REQ_UINT32 }, @@ -345,6 +363,7 @@ static const RTGETOPTDEF g_aModifyVMOptions[] = #ifdef VBOX_WITH_USB_CARDREADER { "--usbcardreader", MODIFYVM_USBCARDREADER, RTGETOPT_REQ_BOOL_ONOFF }, #endif + { "--defaultfrontend", MODIFYVM_DEFAULTFRONTEND, RTGETOPT_REQ_STRING }, }; static void vrdeWarningDeprecatedOption(const char *pszOption) @@ -392,6 +411,30 @@ void parseGroups(const char *pcszGroups, com::SafeArray<BSTR> *pGroups) } } +int parseScreens(const char *pcszScreens, com::SafeArray<BOOL> *pScreens) +{ + while (pcszScreens && *pcszScreens) + { + char *pszNext; + uint32_t iScreen; + int rc = RTStrToUInt32Ex(pcszScreens, &pszNext, 0, &iScreen); + if (RT_FAILURE(rc)) + return 1; + if (iScreen >= pScreens->size()) + return 1; + if (pszNext && *pszNext) + { + pszNext = RTStrStripL(pszNext); + if (*pszNext != ',') + return 1; + pszNext++; + } + (*pScreens)[iScreen] = true; + pcszScreens = pszNext; + } + return 0; +} + int handleModifyVM(HandlerArg *a) { int c; @@ -399,8 +442,8 @@ int handleModifyVM(HandlerArg *a) Bstr name; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetOptState; - ComPtr <IMachine> machine; - ComPtr <IBIOSSettings> biosSettings; + ComPtr<IMachine> machine; + ComPtr<IBIOSSettings> biosSettings; /* VM ID + at least one parameter. Parameter arguments are checked * individually. */ @@ -442,6 +485,11 @@ int handleModifyVM(HandlerArg *a) CHECK_ERROR(machine, COMSETTER(Groups)(ComSafeArrayAsInParam(groups))); break; } + case MODIFYVM_DESCRIPTION: + { + CHECK_ERROR(machine, COMSETTER(Description)(Bstr(ValueUnion.psz).raw())); + break; + } case MODIFYVM_OSTYPE: { ComPtr<IGuestOSType> guestOSType; @@ -453,9 +501,46 @@ int handleModifyVM(HandlerArg *a) } else { - errorArgument("Invalid guest OS type '%s'", Utf8Str(ValueUnion.psz).c_str()); + errorArgument("Invalid guest OS type '%s'", ValueUnion.psz); + rc = E_FAIL; + } + break; + } + + case MODIFYVM_ICONFILE: + { + RTFILE iconFile; + int vrc = RTFileOpen(&iconFile, ValueUnion.psz, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); + if (RT_FAILURE(vrc)) + { + RTMsgError("Cannot open file \"%s\": %Rrc", ValueUnion.psz, vrc); + rc = E_FAIL; + break; + } + uint64_t cbSize; + vrc = RTFileGetSize(iconFile, &cbSize); + if (RT_FAILURE(vrc)) + { + RTMsgError("Cannot get size of file \"%s\": %Rrc", ValueUnion.psz, vrc); + rc = E_FAIL; + break; + } + if (cbSize > _256K) + { + RTMsgError("File \"%s\" is bigger than 256KByte", ValueUnion.psz); rc = E_FAIL; + break; + } + SafeArray<BYTE> icon((size_t)cbSize); + rc = RTFileRead(iconFile, icon.raw(), (size_t)cbSize, NULL); + if (RT_FAILURE(vrc)) + { + RTMsgError("Cannot read contents of file \"%s\": %Rrc", ValueUnion.psz, vrc); + rc = E_FAIL; + break; } + RTFileClose(iconFile); + CHECK_ERROR(machine, COMSETTER(Icon)(ComSafeArrayAsInParam(icon))); break; } @@ -479,23 +564,23 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_FIRMWARE: { - if (!strcmp(ValueUnion.psz, "efi")) + if (!RTStrICmp(ValueUnion.psz, "efi")) { CHECK_ERROR(machine, COMSETTER(FirmwareType)(FirmwareType_EFI)); } - else if (!strcmp(ValueUnion.psz, "efi32")) + else if (!RTStrICmp(ValueUnion.psz, "efi32")) { CHECK_ERROR(machine, COMSETTER(FirmwareType)(FirmwareType_EFI32)); } - else if (!strcmp(ValueUnion.psz, "efi64")) + else if (!RTStrICmp(ValueUnion.psz, "efi64")) { CHECK_ERROR(machine, COMSETTER(FirmwareType)(FirmwareType_EFI64)); } - else if (!strcmp(ValueUnion.psz, "efidual")) + else if (!RTStrICmp(ValueUnion.psz, "efidual")) { CHECK_ERROR(machine, COMSETTER(FirmwareType)(FirmwareType_EFIDUAL)); } - else if (!strcmp(ValueUnion.psz, "bios")) + else if (!RTStrICmp(ValueUnion.psz, "bios")) { CHECK_ERROR(machine, COMSETTER(FirmwareType)(FirmwareType_BIOS)); } @@ -525,21 +610,27 @@ int handleModifyVM(HandlerArg *a) break; } + case MODIFYVM_LONGMODE: + { + CHECK_ERROR(machine, SetCPUProperty(CPUPropertyType_LongMode, ValueUnion.f)); + break; + } + case MODIFYVM_SYNTHCPU: { CHECK_ERROR(machine, SetCPUProperty(CPUPropertyType_Synthetic, ValueUnion.f)); break; } - case MODIFYVM_HWVIRTEX: + case MODIFYVM_TFRESET: { - CHECK_ERROR(machine, SetHWVirtExProperty(HWVirtExPropertyType_Enabled, ValueUnion.f)); + CHECK_ERROR(machine, SetCPUProperty(CPUPropertyType_TripleFaultReset, ValueUnion.f)); break; } - case MODIFYVM_HWVIRTEXEXCLUSIVE: + case MODIFYVM_HWVIRTEX: { - CHECK_ERROR(machine, SetHWVirtExProperty(HWVirtExPropertyType_Exclusive, ValueUnion.f)); + CHECK_ERROR(machine, SetHWVirtExProperty(HWVirtExPropertyType_Enabled, ValueUnion.f)); break; } @@ -591,6 +682,12 @@ int handleModifyVM(HandlerArg *a) break; } + case MODIFYVM_VTXUX: + { + CHECK_ERROR(machine, SetHWVirtExProperty(HWVirtExPropertyType_UnrestrictedExecution, ValueUnion.f)); + break; + } + case MODIFYVM_CPUS: { CHECK_ERROR(machine, COMSETTER(CPUCount)(ValueUnion.u32)); @@ -627,6 +724,29 @@ int handleModifyVM(HandlerArg *a) break; } + case MODIFYVM_GRAPHICSCONTROLLER: + { + if ( !RTStrICmp(ValueUnion.psz, "none") + || !RTStrICmp(ValueUnion.psz, "disabled")) + CHECK_ERROR(machine, COMSETTER(GraphicsControllerType)(GraphicsControllerType_Null)); + else if ( !RTStrICmp(ValueUnion.psz, "vboxvga") + || !RTStrICmp(ValueUnion.psz, "vbox") + || !RTStrICmp(ValueUnion.psz, "vga") + || !RTStrICmp(ValueUnion.psz, "vesa")) + CHECK_ERROR(machine, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VBoxVGA)); +#ifdef VBOX_WITH_VMSVGA + else if ( !RTStrICmp(ValueUnion.psz, "vmsvga") + || !RTStrICmp(ValueUnion.psz, "vmware")) + CHECK_ERROR(machine, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VMSVGA)); +#endif + else + { + errorArgument("Invalid --graphicscontroller argument '%s'", ValueUnion.psz); + rc = E_FAIL; + } + break; + } + case MODIFYVM_MONITORCOUNT: { CHECK_ERROR(machine, COMSETTER(MonitorCount)(ValueUnion.u32)); @@ -673,15 +793,15 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_BIOSBOOTMENU: { - if (!strcmp(ValueUnion.psz, "disabled")) + if (!RTStrICmp(ValueUnion.psz, "disabled")) { CHECK_ERROR(biosSettings, COMSETTER(BootMenuMode)(BIOSBootMenuMode_Disabled)); } - else if (!strcmp(ValueUnion.psz, "menuonly")) + else if (!RTStrICmp(ValueUnion.psz, "menuonly")) { CHECK_ERROR(biosSettings, COMSETTER(BootMenuMode)(BIOSBootMenuMode_MenuOnly)); } - else if (!strcmp(ValueUnion.psz, "messageandmenu")) + else if (!RTStrICmp(ValueUnion.psz, "messageandmenu")) { CHECK_ERROR(biosSettings, COMSETTER(BootMenuMode)(BIOSBootMenuMode_MessageAndMenu)); } @@ -707,23 +827,23 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_BOOT: { - if (!strcmp(ValueUnion.psz, "none")) + if (!RTStrICmp(ValueUnion.psz, "none")) { CHECK_ERROR(machine, SetBootOrder(GetOptState.uIndex, DeviceType_Null)); } - else if (!strcmp(ValueUnion.psz, "floppy")) + else if (!RTStrICmp(ValueUnion.psz, "floppy")) { CHECK_ERROR(machine, SetBootOrder(GetOptState.uIndex, DeviceType_Floppy)); } - else if (!strcmp(ValueUnion.psz, "dvd")) + else if (!RTStrICmp(ValueUnion.psz, "dvd")) { CHECK_ERROR(machine, SetBootOrder(GetOptState.uIndex, DeviceType_DVD)); } - else if (!strcmp(ValueUnion.psz, "disk")) + else if (!RTStrICmp(ValueUnion.psz, "disk")) { CHECK_ERROR(machine, SetBootOrder(GetOptState.uIndex, DeviceType_HardDisk)); } - else if (!strcmp(ValueUnion.psz, "net")) + else if (!RTStrICmp(ValueUnion.psz, "net")) { CHECK_ERROR(machine, SetBootOrder(GetOptState.uIndex, DeviceType_Network)); } @@ -762,16 +882,17 @@ int handleModifyVM(HandlerArg *a) break; } - if (!strcmp(ValueUnion.psz, "none")) + if (!RTStrICmp(ValueUnion.psz, "none")) { machine->DetachDevice(bstrController.raw(), u1, u2); } else { ComPtr<IMedium> hardDisk; - rc = findOrOpenMedium(a, ValueUnion.psz, DeviceType_HardDisk, - AccessMode_ReadWrite, hardDisk, - false /* fForceNewUuidOnOpen */, NULL); + rc = openMedium(a, ValueUnion.psz, DeviceType_HardDisk, + AccessMode_ReadWrite, hardDisk, + false /* fForceNewUuidOnOpen */, + false /* fSilent */); if (FAILED(rc)) break; if (hardDisk) @@ -826,7 +947,7 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_SATA: // deprecated { - if (!strcmp(ValueUnion.psz, "on") || !strcmp(ValueUnion.psz, "enable")) + if (!RTStrICmp(ValueUnion.psz, "on") || !RTStrICmp(ValueUnion.psz, "enable")) { ComPtr<IStorageController> ctl; CHECK_ERROR(machine, AddStorageController(Bstr("SATA").raw(), @@ -834,7 +955,7 @@ int handleModifyVM(HandlerArg *a) ctl.asOutParam())); CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_IntelAhci)); } - else if (!strcmp(ValueUnion.psz, "off") || !strcmp(ValueUnion.psz, "disable")) + else if (!RTStrICmp(ValueUnion.psz, "off") || !RTStrICmp(ValueUnion.psz, "disable")) CHECK_ERROR(machine, RemoveStorageController(Bstr("SATA").raw())); else return errorArgument("Invalid --usb argument '%s'", ValueUnion.psz); @@ -843,7 +964,7 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_SCSIPORT: // deprecated { - if (!strcmp(ValueUnion.psz, "none")) + if (!RTStrICmp(ValueUnion.psz, "none")) { rc = machine->DetachDevice(Bstr("LsiLogic").raw(), GetOptState.uIndex, 0); @@ -854,9 +975,10 @@ int handleModifyVM(HandlerArg *a) else { ComPtr<IMedium> hardDisk; - rc = findOrOpenMedium(a, ValueUnion.psz, DeviceType_HardDisk, - AccessMode_ReadWrite, hardDisk, - false /* fForceNewUuidOnOpen */, NULL); + rc = openMedium(a, ValueUnion.psz, DeviceType_HardDisk, + AccessMode_ReadWrite, hardDisk, + false /* fForceNewUuidOnOpen */, + false /* fSilent */); if (FAILED(rc)) break; if (hardDisk) @@ -917,7 +1039,7 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_SCSI: // deprecated { - if (!strcmp(ValueUnion.psz, "on") || !strcmp(ValueUnion.psz, "enable")) + if (!RTStrICmp(ValueUnion.psz, "on") || !RTStrICmp(ValueUnion.psz, "enable")) { ComPtr<IStorageController> ctl; @@ -927,7 +1049,7 @@ int handleModifyVM(HandlerArg *a) if (SUCCEEDED(rc)) CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_BusLogic)); } - else if (!strcmp(ValueUnion.psz, "off") || !strcmp(ValueUnion.psz, "disable")) + else if (!RTStrICmp(ValueUnion.psz, "off") || !RTStrICmp(ValueUnion.psz, "disable")) { rc = machine->RemoveStorageController(Bstr("BusLogic").raw()); if (FAILED(rc)) @@ -940,7 +1062,7 @@ int handleModifyVM(HandlerArg *a) { CHECK_ERROR(machine, PassthroughDevice(Bstr("IDE Controller").raw(), 1, 0, - !strcmp(ValueUnion.psz, "on"))); + !RTStrICmp(ValueUnion.psz, "on"))); break; } @@ -949,12 +1071,12 @@ int handleModifyVM(HandlerArg *a) ComPtr<IMedium> dvdMedium; /* unmount? */ - if (!strcmp(ValueUnion.psz, "none")) + if (!RTStrICmp(ValueUnion.psz, "none")) { /* nothing to do, NULL object will cause unmount */ } /* host drive? */ - else if (!strncmp(ValueUnion.psz, "host:", 5)) + else if (!RTStrNICmp(ValueUnion.psz, "host:", 5)) { ComPtr<IHost> host; CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam())); @@ -982,9 +1104,10 @@ int handleModifyVM(HandlerArg *a) } else { - rc = findOrOpenMedium(a, ValueUnion.psz, DeviceType_DVD, - AccessMode_ReadOnly, dvdMedium, - false /* fForceNewUuidOnOpen */, NULL); + rc = openMedium(a, ValueUnion.psz, DeviceType_DVD, + AccessMode_ReadOnly, dvdMedium, + false /* fForceNewUuidOnOpen */, + false /* fSilent */); if (FAILED(rc)) break; if (!dvdMedium) @@ -1009,7 +1132,7 @@ int handleModifyVM(HandlerArg *a) 0, 0, floppyAttachment.asOutParam()); /* disable? */ - if (!strcmp(ValueUnion.psz, "disabled")) + if (!RTStrICmp(ValueUnion.psz, "disabled")) { /* disable the controller */ if (floppyAttachment) @@ -1025,13 +1148,13 @@ int handleModifyVM(HandlerArg *a) DeviceType_Floppy)); /* unmount? */ - if ( !strcmp(ValueUnion.psz, "none") - || !strcmp(ValueUnion.psz, "empty")) // deprecated + if ( !RTStrICmp(ValueUnion.psz, "none") + || !RTStrICmp(ValueUnion.psz, "empty")) // deprecated { /* nothing to do, NULL object will cause unmount */ } /* host drive? */ - else if (!strncmp(ValueUnion.psz, "host:", 5)) + else if (!RTStrNICmp(ValueUnion.psz, "host:", 5)) { ComPtr<IHost> host; CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam())); @@ -1046,9 +1169,10 @@ int handleModifyVM(HandlerArg *a) } else { - rc = findOrOpenMedium(a, ValueUnion.psz, DeviceType_Floppy, - AccessMode_ReadWrite, floppyMedium, - false /* fForceNewUuidOnOpen */, NULL); + rc = openMedium(a, ValueUnion.psz, DeviceType_Floppy, + AccessMode_ReadWrite, floppyMedium, + false /* fForceNewUuidOnOpen */, + false /* fSilent */); if (FAILED(rc)) break; if (!floppyMedium) @@ -1131,30 +1255,30 @@ int handleModifyVM(HandlerArg *a) CHECK_ERROR_BREAK(machine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); ASSERT(nic); - if (!strcmp(ValueUnion.psz, "Am79C970A")) + if (!RTStrICmp(ValueUnion.psz, "Am79C970A")) { CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_Am79C970A)); } - else if (!strcmp(ValueUnion.psz, "Am79C973")) + else if (!RTStrICmp(ValueUnion.psz, "Am79C973")) { CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_Am79C973)); } #ifdef VBOX_WITH_E1000 - else if (!strcmp(ValueUnion.psz, "82540EM")) + else if (!RTStrICmp(ValueUnion.psz, "82540EM")) { CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_I82540EM)); } - else if (!strcmp(ValueUnion.psz, "82543GC")) + else if (!RTStrICmp(ValueUnion.psz, "82543GC")) { CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_I82543GC)); } - else if (!strcmp(ValueUnion.psz, "82545EM")) + else if (!RTStrICmp(ValueUnion.psz, "82545EM")) { CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_I82545EM)); } #endif #ifdef VBOX_WITH_VIRTIO - else if (!strcmp(ValueUnion.psz, "virtio")) + else if (!RTStrICmp(ValueUnion.psz, "virtio")) { CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_Virtio)); } @@ -1204,12 +1328,12 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_NICPROMISC: { NetworkAdapterPromiscModePolicy_T enmPromiscModePolicy; - if (!strcmp(ValueUnion.psz, "deny")) + if (!RTStrICmp(ValueUnion.psz, "deny")) enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny; - else if ( !strcmp(ValueUnion.psz, "allow-vms") - || !strcmp(ValueUnion.psz, "allow-network")) + else if ( !RTStrICmp(ValueUnion.psz, "allow-vms") + || !RTStrICmp(ValueUnion.psz, "allow-network")) enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork; - else if (!strcmp(ValueUnion.psz, "allow-all")) + else if (!RTStrICmp(ValueUnion.psz, "allow-all")) enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll; else { @@ -1263,43 +1387,49 @@ int handleModifyVM(HandlerArg *a) CHECK_ERROR_BREAK(machine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); ASSERT(nic); - if (!strcmp(ValueUnion.psz, "none")) + if (!RTStrICmp(ValueUnion.psz, "none")) { CHECK_ERROR(nic, COMSETTER(Enabled)(FALSE)); } - else if (!strcmp(ValueUnion.psz, "null")) + else if (!RTStrICmp(ValueUnion.psz, "null")) { CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Null)); } - else if (!strcmp(ValueUnion.psz, "nat")) + else if (!RTStrICmp(ValueUnion.psz, "nat")) { CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_NAT)); } - else if ( !strcmp(ValueUnion.psz, "bridged") - || !strcmp(ValueUnion.psz, "hostif")) /* backward compatibility */ + else if ( !RTStrICmp(ValueUnion.psz, "bridged") + || !RTStrICmp(ValueUnion.psz, "hostif")) /* backward compatibility */ { CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged)); } - else if (!strcmp(ValueUnion.psz, "intnet")) + else if (!RTStrICmp(ValueUnion.psz, "intnet")) { CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Internal)); } - else if (!strcmp(ValueUnion.psz, "hostonly")) + else if (!RTStrICmp(ValueUnion.psz, "hostonly")) { CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly)); } - else if (!strcmp(ValueUnion.psz, "generic")) + else if (!RTStrICmp(ValueUnion.psz, "generic")) { CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic)); } + else if (!RTStrICmp(ValueUnion.psz, "natnetwork")) + { + + CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE)); + CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_NATNetwork)); + } else { errorArgument("Invalid type '%s' specfied for NIC %u", ValueUnion.psz, GetOptState.uIndex); @@ -1327,7 +1457,7 @@ int handleModifyVM(HandlerArg *a) ASSERT(nic); /* remove it? */ - if (!strcmp(ValueUnion.psz, "none")) + if (!RTStrICmp(ValueUnion.psz, "none")) { CHECK_ERROR(nic, COMSETTER(BridgedInterface)(Bstr().raw())); } @@ -1346,7 +1476,7 @@ int handleModifyVM(HandlerArg *a) ASSERT(nic); /* remove it? */ - if (!strcmp(ValueUnion.psz, "none")) + if (!RTStrICmp(ValueUnion.psz, "none")) { CHECK_ERROR(nic, COMSETTER(HostOnlyInterface)(Bstr().raw())); } @@ -1365,7 +1495,7 @@ int handleModifyVM(HandlerArg *a) ASSERT(nic); /* remove it? */ - if (!strcmp(ValueUnion.psz, "none")) + if (!RTStrICmp(ValueUnion.psz, "none")) { CHECK_ERROR(nic, COMSETTER(InternalNetwork)(Bstr().raw())); } @@ -1387,6 +1517,17 @@ int handleModifyVM(HandlerArg *a) break; } + case MODIFYVM_NATNETWORKNAME: + { + ComPtr<INetworkAdapter> nic; + + CHECK_ERROR_BREAK(machine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam())); + ASSERT(nic); + + CHECK_ERROR(nic, COMSETTER(NATNetwork)(Bstr(ValueUnion.psz).raw())); + break; + } + case MODIFYVM_NATNET: { ComPtr<INetworkAdapter> nic; @@ -1398,7 +1539,7 @@ int handleModifyVM(HandlerArg *a) CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam())); const char *psz = ValueUnion.psz; - if (!strcmp("default", psz)) + if (!RTStrICmp("default", psz)) psz = ""; CHECK_ERROR(engine, COMSETTER(Network)(Bstr(psz).raw())); @@ -1535,7 +1676,7 @@ int handleModifyVM(HandlerArg *a) ASSERT(nic); CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam())); - if (RTStrCmp(ValueUnion.psz,"default") == 0) + if (RTStrCmp(ValueUnion.psz, "default") == 0) { aliasMode = 0; } @@ -1644,7 +1785,7 @@ int handleModifyVM(HandlerArg *a) ASSERT(nic); /* generate one? */ - if (!strcmp(ValueUnion.psz, "auto")) + if (!RTStrICmp(ValueUnion.psz, "auto")) { CHECK_ERROR(nic, COMSETTER(MACAddress)(Bstr().raw())); } @@ -1658,22 +1799,28 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_HIDPTR: { bool fEnableUsb = false; - if (!strcmp(ValueUnion.psz, "ps2")) + if (!RTStrICmp(ValueUnion.psz, "ps2")) { CHECK_ERROR(machine, COMSETTER(PointingHIDType)(PointingHIDType_PS2Mouse)); } - else if (!strcmp(ValueUnion.psz, "usb")) + else if (!RTStrICmp(ValueUnion.psz, "usb")) { CHECK_ERROR(machine, COMSETTER(PointingHIDType)(PointingHIDType_USBMouse)); if (SUCCEEDED(rc)) fEnableUsb = true; } - else if (!strcmp(ValueUnion.psz, "usbtablet")) + else if (!RTStrICmp(ValueUnion.psz, "usbtablet")) { CHECK_ERROR(machine, COMSETTER(PointingHIDType)(PointingHIDType_USBTablet)); if (SUCCEEDED(rc)) fEnableUsb = true; } + else if (!RTStrICmp(ValueUnion.psz, "usbmultitouch")) + { + CHECK_ERROR(machine, COMSETTER(PointingHIDType)(PointingHIDType_USBMultiTouch)); + if (SUCCEEDED(rc)) + fEnableUsb = true; + } else { errorArgument("Invalid type '%s' specfied for pointing device", ValueUnion.psz); @@ -1682,16 +1829,14 @@ int handleModifyVM(HandlerArg *a) if (fEnableUsb) { /* Make sure the OHCI controller is enabled. */ - ComPtr<IUSBController> UsbCtl; - rc = machine->COMGETTER(USBController)(UsbCtl.asOutParam()); - if (SUCCEEDED(rc)) + ULONG cOhciCtrls = 0; + rc = machine->GetUSBControllerCountByType(USBControllerType_OHCI, &cOhciCtrls); + if ( SUCCEEDED(rc) + && !cOhciCtrls) { - BOOL fEnabled; - rc = UsbCtl->COMGETTER(Enabled)(&fEnabled); - if (FAILED(rc)) - fEnabled = false; - if (!fEnabled) - CHECK_ERROR(UsbCtl, COMSETTER(Enabled)(true)); + ComPtr<IUSBController> UsbCtl; + CHECK_ERROR(machine, AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI, + UsbCtl.asOutParam())); } } break; @@ -1700,11 +1845,11 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_HIDKBD: { bool fEnableUsb = false; - if (!strcmp(ValueUnion.psz, "ps2")) + if (!RTStrICmp(ValueUnion.psz, "ps2")) { CHECK_ERROR(machine, COMSETTER(KeyboardHIDType)(KeyboardHIDType_PS2Keyboard)); } - else if (!strcmp(ValueUnion.psz, "usb")) + else if (!RTStrICmp(ValueUnion.psz, "usb")) { CHECK_ERROR(machine, COMSETTER(KeyboardHIDType)(KeyboardHIDType_USBKeyboard)); if (SUCCEEDED(rc)) @@ -1718,16 +1863,14 @@ int handleModifyVM(HandlerArg *a) if (fEnableUsb) { /* Make sure the OHCI controller is enabled. */ - ComPtr<IUSBController> UsbCtl; - rc = machine->COMGETTER(USBController)(UsbCtl.asOutParam()); - if (SUCCEEDED(rc)) + ULONG cOhciCtrls = 0; + rc = machine->GetUSBControllerCountByType(USBControllerType_OHCI, &cOhciCtrls); + if ( SUCCEEDED(rc) + && !cOhciCtrls) { - BOOL fEnabled; - rc = UsbCtl->COMGETTER(Enabled)(&fEnabled); - if (FAILED(rc)) - fEnabled = false; - if (!fEnabled) - CHECK_ERROR(UsbCtl, COMSETTER(Enabled)(true)); + ComPtr<IUSBController> UsbCtl; + CHECK_ERROR(machine, AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI, + UsbCtl.asOutParam())); } } break; @@ -1741,13 +1884,13 @@ int handleModifyVM(HandlerArg *a) CHECK_ERROR_BREAK(machine, GetSerialPort(GetOptState.uIndex - 1, uart.asOutParam())); ASSERT(uart); - if (!strcmp(ValueUnion.psz, "disconnected")) + if (!RTStrICmp(ValueUnion.psz, "disconnected")) { CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected)); } - else if ( !strcmp(ValueUnion.psz, "server") - || !strcmp(ValueUnion.psz, "client") - || !strcmp(ValueUnion.psz, "file")) + else if ( !RTStrICmp(ValueUnion.psz, "server") + || !RTStrICmp(ValueUnion.psz, "client") + || !RTStrICmp(ValueUnion.psz, "file")) { const char *pszMode = ValueUnion.psz; @@ -1759,17 +1902,17 @@ int handleModifyVM(HandlerArg *a) CHECK_ERROR(uart, COMSETTER(Path)(Bstr(ValueUnion.psz).raw())); - if (!strcmp(pszMode, "server")) + if (!RTStrICmp(pszMode, "server")) { CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe)); CHECK_ERROR(uart, COMSETTER(Server)(TRUE)); } - else if (!strcmp(pszMode, "client")) + else if (!RTStrICmp(pszMode, "client")) { CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe)); CHECK_ERROR(uart, COMSETTER(Server)(FALSE)); } - else if (!strcmp(pszMode, "file")) + else if (!RTStrICmp(pszMode, "file")) { CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_RawFile)); } @@ -1789,7 +1932,7 @@ int handleModifyVM(HandlerArg *a) CHECK_ERROR_BREAK(machine, GetSerialPort(GetOptState.uIndex - 1, uart.asOutParam())); ASSERT(uart); - if (!strcmp(ValueUnion.psz, "off") || !strcmp(ValueUnion.psz, "disable")) + if (!RTStrICmp(ValueUnion.psz, "off") || !RTStrICmp(ValueUnion.psz, "disable")) CHECK_ERROR(uart, COMSETTER(Enabled)(FALSE)); else { @@ -1834,7 +1977,7 @@ int handleModifyVM(HandlerArg *a) CHECK_ERROR_BREAK(machine, GetParallelPort(GetOptState.uIndex - 1, lpt.asOutParam())); ASSERT(lpt); - if (!strcmp(ValueUnion.psz, "off") || !strcmp(ValueUnion.psz, "disable")) + if (!RTStrICmp(ValueUnion.psz, "off") || !RTStrICmp(ValueUnion.psz, "disable")) CHECK_ERROR(lpt, COMSETTER(Enabled)(FALSE)); else { @@ -1872,11 +2015,11 @@ int handleModifyVM(HandlerArg *a) machine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam()); ASSERT(audioAdapter); - if (!strcmp(ValueUnion.psz, "sb16")) + if (!RTStrICmp(ValueUnion.psz, "sb16")) CHECK_ERROR(audioAdapter, COMSETTER(AudioController)(AudioControllerType_SB16)); - else if (!strcmp(ValueUnion.psz, "ac97")) + else if (!RTStrICmp(ValueUnion.psz, "ac97")) CHECK_ERROR(audioAdapter, COMSETTER(AudioController)(AudioControllerType_AC97)); - else if (!strcmp(ValueUnion.psz, "hda")) + else if (!RTStrICmp(ValueUnion.psz, "hda")) CHECK_ERROR(audioAdapter, COMSETTER(AudioController)(AudioControllerType_HDA)); else { @@ -1893,24 +2036,24 @@ int handleModifyVM(HandlerArg *a) ASSERT(audioAdapter); /* disable? */ - if (!strcmp(ValueUnion.psz, "none")) + if (!RTStrICmp(ValueUnion.psz, "none")) { CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(false)); } - else if (!strcmp(ValueUnion.psz, "null")) + else if (!RTStrICmp(ValueUnion.psz, "null")) { CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_Null)); CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true)); } #ifdef RT_OS_WINDOWS #ifdef VBOX_WITH_WINMM - else if (!strcmp(ValueUnion.psz, "winmm")) + else if (!RTStrICmp(ValueUnion.psz, "winmm")) { CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_WinMM)); CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true)); } #endif - else if (!strcmp(ValueUnion.psz, "dsound")) + else if (!RTStrICmp(ValueUnion.psz, "dsound")) { CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_DirectSound)); CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true)); @@ -1918,14 +2061,14 @@ int handleModifyVM(HandlerArg *a) #endif /* RT_OS_WINDOWS */ #ifdef RT_OS_LINUX # ifdef VBOX_WITH_ALSA - else if (!strcmp(ValueUnion.psz, "alsa")) + else if (!RTStrICmp(ValueUnion.psz, "alsa")) { CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_ALSA)); CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true)); } # endif # ifdef VBOX_WITH_PULSE - else if (!strcmp(ValueUnion.psz, "pulse")) + else if (!RTStrICmp(ValueUnion.psz, "pulse")) { CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_Pulse)); CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true)); @@ -1933,20 +2076,20 @@ int handleModifyVM(HandlerArg *a) # endif #endif /* !RT_OS_LINUX */ #ifdef RT_OS_SOLARIS - else if (!strcmp(ValueUnion.psz, "solaudio")) + else if (!RTStrICmp(ValueUnion.psz, "solaudio")) { CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_SolAudio)); CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true)); } #endif /* !RT_OS_SOLARIS */ #ifdef RT_OS_FREEBSD - else if (!strcmp(ValueUnion.psz, "oss")) + else if (!RTStrICmp(ValueUnion.psz, "oss")) { CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_OSS)); CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true)); } # ifdef VBOX_WITH_PULSE - else if (!strcmp(ValueUnion.psz, "pulse")) + else if (!RTStrICmp(ValueUnion.psz, "pulse")) { CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_Pulse)); CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true)); @@ -1954,7 +2097,7 @@ int handleModifyVM(HandlerArg *a) # endif #endif /* !RT_OS_FREEBSD */ #ifdef RT_OS_DARWIN - else if (!strcmp(ValueUnion.psz, "coreaudio")) + else if (!RTStrICmp(ValueUnion.psz, "coreaudio")) { CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_CoreAudio)); CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true)); @@ -1962,7 +2105,7 @@ int handleModifyVM(HandlerArg *a) #endif /* !RT_OS_DARWIN */ # if defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX) || defined(VBOX_WITH_SOLARIS_OSS) - else if (!strcmp(ValueUnion.psz, "oss")) + else if (!RTStrICmp(ValueUnion.psz, "oss")) { CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_OSS)); CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true)); @@ -1979,13 +2122,13 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_CLIPBOARD: { ClipboardMode_T mode; - if (!strcmp(ValueUnion.psz, "disabled")) + if (!RTStrICmp(ValueUnion.psz, "disabled")) mode = ClipboardMode_Disabled; - else if (!strcmp(ValueUnion.psz, "hosttoguest")) + else if (!RTStrICmp(ValueUnion.psz, "hosttoguest")) mode = ClipboardMode_HostToGuest; - else if (!strcmp(ValueUnion.psz, "guesttohost")) + else if (!RTStrICmp(ValueUnion.psz, "guesttohost")) mode = ClipboardMode_GuestToHost; - else if (!strcmp(ValueUnion.psz, "bidirectional")) + else if (!RTStrICmp(ValueUnion.psz, "bidirectional")) mode = ClipboardMode_Bidirectional; else { @@ -2002,13 +2145,13 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_DRAGANDDROP: { DragAndDropMode_T mode; - if (!strcmp(ValueUnion.psz, "disabled")) + if (!RTStrICmp(ValueUnion.psz, "disabled")) mode = DragAndDropMode_Disabled; - else if (!strcmp(ValueUnion.psz, "hosttoguest")) + else if (!RTStrICmp(ValueUnion.psz, "hosttoguest")) mode = DragAndDropMode_HostToGuest; - else if (!strcmp(ValueUnion.psz, "guesttohost")) + else if (!RTStrICmp(ValueUnion.psz, "guesttohost")) mode = DragAndDropMode_GuestToHost; - else if (!strcmp(ValueUnion.psz, "bidirectional")) + else if (!RTStrICmp(ValueUnion.psz, "bidirectional")) mode = DragAndDropMode_Bidirectional; else { @@ -2030,7 +2173,7 @@ int handleModifyVM(HandlerArg *a) if (vrdeServer) { - if (strcmp(ValueUnion.psz, "default") != 0) + if (RTStrICmp(ValueUnion.psz, "default") != 0) { Bstr bstr(ValueUnion.psz); CHECK_ERROR(vrdeServer, COMSETTER(VRDEExtPack)(bstr.raw())); @@ -2090,7 +2233,7 @@ int handleModifyVM(HandlerArg *a) machine->COMGETTER(VRDEServer)(vrdeServer.asOutParam()); ASSERT(vrdeServer); - if (!strcmp(ValueUnion.psz, "default")) + if (!RTStrICmp(ValueUnion.psz, "default")) CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), Bstr("0").raw())); else CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), Bstr(ValueUnion.psz).raw())); @@ -2118,15 +2261,15 @@ int handleModifyVM(HandlerArg *a) machine->COMGETTER(VRDEServer)(vrdeServer.asOutParam()); ASSERT(vrdeServer); - if (!strcmp(ValueUnion.psz, "null")) + if (!RTStrICmp(ValueUnion.psz, "null")) { CHECK_ERROR(vrdeServer, COMSETTER(AuthType)(AuthType_Null)); } - else if (!strcmp(ValueUnion.psz, "external")) + else if (!RTStrICmp(ValueUnion.psz, "external")) { CHECK_ERROR(vrdeServer, COMSETTER(AuthType)(AuthType_External)); } - else if (!strcmp(ValueUnion.psz, "guest")) + else if (!RTStrICmp(ValueUnion.psz, "guest")) { CHECK_ERROR(vrdeServer, COMSETTER(AuthType)(AuthType_Guest)); } @@ -2146,7 +2289,7 @@ int handleModifyVM(HandlerArg *a) if (vrdeServer) { - if (strcmp(ValueUnion.psz, "default") != 0) + if (RTStrICmp(ValueUnion.psz, "default") != 0) { Bstr bstr(ValueUnion.psz); CHECK_ERROR(vrdeServer, COMSETTER(AuthLibrary)(bstr.raw())); @@ -2221,25 +2364,43 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_USBEHCI: { - ComPtr<IUSBController> UsbCtl; - CHECK_ERROR(machine, COMGETTER(USBController)(UsbCtl.asOutParam())); + ULONG cEhciCtrls = 0; + rc = machine->GetUSBControllerCountByType(USBControllerType_EHCI, &cEhciCtrls); if (SUCCEEDED(rc)) - CHECK_ERROR(UsbCtl, COMSETTER(EnabledEHCI)(ValueUnion.f)); + { + if (!cEhciCtrls && ValueUnion.f) + { + ComPtr<IUSBController> UsbCtl; + CHECK_ERROR(machine, AddUSBController(Bstr("EHCI").raw(), USBControllerType_EHCI, + UsbCtl.asOutParam())); + } + else if (cEhciCtrls && !ValueUnion.f) + CHECK_ERROR(machine, RemoveUSBController(Bstr("EHCI").raw())); + } break; } case MODIFYVM_USB: { - ComPtr<IUSBController> UsbCtl; - CHECK_ERROR(machine, COMGETTER(USBController)(UsbCtl.asOutParam())); + ULONG cOhciCtrls = 0; + rc = machine->GetUSBControllerCountByType(USBControllerType_OHCI, &cOhciCtrls); if (SUCCEEDED(rc)) - CHECK_ERROR(UsbCtl, COMSETTER(Enabled)(ValueUnion.f)); + { + if (!cOhciCtrls && ValueUnion.f) + { + ComPtr<IUSBController> UsbCtl; + CHECK_ERROR(machine, AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI, + UsbCtl.asOutParam())); + } + else if (cOhciCtrls && !ValueUnion.f) + CHECK_ERROR(machine, RemoveUSBController(Bstr("OHCI").raw())); + } break; } case MODIFYVM_SNAPSHOTFOLDER: { - if (!strcmp(ValueUnion.psz, "default")) + if (!RTStrICmp(ValueUnion.psz, "default")) CHECK_ERROR(machine, COMSETTER(SnapshotFolder)(Bstr().raw())); else CHECK_ERROR(machine, COMSETTER(SnapshotFolder)(Bstr(ValueUnion.psz).raw())); @@ -2301,12 +2462,12 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_FAULT_TOLERANCE: { - if (!strcmp(ValueUnion.psz, "master")) + if (!RTStrICmp(ValueUnion.psz, "master")) { CHECK_ERROR(machine, COMSETTER(FaultToleranceState(FaultToleranceState_Master))); } else - if (!strcmp(ValueUnion.psz, "standby")) + if (!RTStrICmp(ValueUnion.psz, "standby")) { CHECK_ERROR(machine, COMSETTER(FaultToleranceState(FaultToleranceState_Standby))); } @@ -2368,11 +2529,11 @@ int handleModifyVM(HandlerArg *a) case MODIFYVM_CHIPSET: { - if (!strcmp(ValueUnion.psz, "piix3")) + if (!RTStrICmp(ValueUnion.psz, "piix3")) { CHECK_ERROR(machine, COMSETTER(ChipsetType)(ChipsetType_PIIX3)); } - else if (!strcmp(ValueUnion.psz, "ich9")) + else if (!RTStrICmp(ValueUnion.psz, "ich9")) { CHECK_ERROR(machine, COMSETTER(ChipsetType)(ChipsetType_ICH9)); BOOL fIoApic = FALSE; @@ -2396,9 +2557,36 @@ int handleModifyVM(HandlerArg *a) CHECK_ERROR(machine, COMSETTER(VideoCaptureEnabled)(ValueUnion.f)); break; } + case MODIFYVM_VCP_SCREENS: + { + ULONG cMonitors = 64; + CHECK_ERROR(machine, COMGETTER(MonitorCount)(&cMonitors)); + com::SafeArray<BOOL> screens(cMonitors); + if (parseScreens(ValueUnion.psz, &screens)) + { + errorArgument("Invalid list of screens specified\n"); + rc = E_FAIL; + break; + } + CHECK_ERROR(machine, COMSETTER(VideoCaptureScreens)(ComSafeArrayAsInParam(screens))); + break; + } case MODIFYVM_VCP_FILENAME: { - Bstr bstr(ValueUnion.psz); + Bstr bstr; + /* empty string will fall through, leaving bstr empty */ + if (*ValueUnion.psz) + { + char szVCFileAbs[RTPATH_MAX] = ""; + int vrc = RTPathAbs(ValueUnion.psz, szVCFileAbs, sizeof(szVCFileAbs)); + if (RT_FAILURE(vrc)) + { + errorArgument("Cannot convert filename \"%s\" to absolute path\n", ValueUnion.psz); + rc = E_FAIL; + break; + } + bstr = szVCFileAbs; + } CHECK_ERROR(machine, COMSETTER(VideoCaptureFile)(bstr.raw())); break; } @@ -2412,6 +2600,16 @@ int handleModifyVM(HandlerArg *a) CHECK_ERROR(machine, COMSETTER(VideoCaptureHeight)(ValueUnion.u32)); break; } + case MODIFYVM_VCP_RATE: + { + CHECK_ERROR(machine, COMSETTER(VideoCaptureRate)(ValueUnion.u32)); + break; + } + case MODIFYVM_VCP_FPS: + { + CHECK_ERROR(machine, COMSETTER(VideoCaptureFPS)(ValueUnion.u32)); + break; + } #endif case MODIFYVM_AUTOSTART_ENABLED: { @@ -2486,6 +2684,7 @@ int handleModifyVM(HandlerArg *a) break; } #endif + #ifdef VBOX_WITH_USB_CARDREADER case MODIFYVM_USBCARDREADER: { @@ -2494,6 +2693,15 @@ int handleModifyVM(HandlerArg *a) } #endif /* VBOX_WITH_USB_CARDREADER */ + case MODIFYVM_DEFAULTFRONTEND: + { + Bstr bstr(ValueUnion.psz); + if (bstr == "default") + bstr = Bstr::Empty; + CHECK_ERROR(machine, COMSETTER(DefaultFrontend)(bstr.raw())); + break; + } + default: { errorGetOpt(USAGE_MODIFYVM, c, &ValueUnion); diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp new file mode 100644 index 00000000..ef4f916e --- /dev/null +++ b/src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp @@ -0,0 +1,490 @@ +/* $Id: VBoxManageNATNetwork.cpp $ */ +/** @file + * VBoxManage - Implementation of NAT Network command command. + */ + +/* + * Copyright (C) 2006-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#ifndef VBOX_ONLY_DOCS + +#include <VBox/com/com.h> +#include <VBox/com/array.h> +#include <VBox/com/ErrorInfo.h> +#include <VBox/com/errorprint.h> +#include <VBox/com/VirtualBox.h> +#endif /* !VBOX_ONLY_DOCS */ + +#ifndef RT_OS_WINDOWS +# include <netinet/in.h> +#else +/* from <ws2ipdef.h> */ +# define INET6_ADDRSTRLEN 65 +#endif + +#define IPv6 + +#include <iprt/cdefs.h> +#include <iprt/cidr.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/net.h> +#include <iprt/getopt.h> +#include <iprt/ctype.h> + +#include <VBox/log.h> + +#include <vector> +#include <string> + +#include "VBoxManage.h" +#include "VBoxPortForwardString.h" + +#ifndef VBOX_ONLY_DOCS + +using namespace com; + +typedef enum enMainOpCodes +{ + OP_ADD = 1000, + OP_REMOVE, + OP_MODIFY, + OP_START, + OP_STOP +} OPCODE; + +static const RTGETOPTDEF g_aNATNetworkIPOptions[] + = { + { "--netname", 't', RTGETOPT_REQ_STRING }, + { "--network", 'n', RTGETOPT_REQ_STRING }, + { "--dhcp", 'h', RTGETOPT_REQ_BOOL }, + { "--ipv6", '6', RTGETOPT_REQ_BOOL}, + { "--enable", 'e', RTGETOPT_REQ_NOTHING }, + { "--disable", 'd', RTGETOPT_REQ_NOTHING }, + { "--port-forward-4", 'p', RTGETOPT_REQ_STRING }, + { "--port-forward-6", 'P', RTGETOPT_REQ_STRING }, + { "--loopback-4", 'l', RTGETOPT_REQ_STRING }, + { "--loopback-6", 'L', RTGETOPT_REQ_STRING }, + + }; + +typedef struct PFNAME2DELETE +{ + char szName[PF_NAMELEN]; + bool fIPv6; +} PFNAME2DELETE, *PPFNAME2DELETE; + +typedef std::vector<PFNAME2DELETE> VPF2DELETE; +typedef VPF2DELETE::const_iterator VPF2DELETEITERATOR; + +typedef std::vector<PORTFORWARDRULE> VPF2ADD; +typedef VPF2ADD::const_iterator VPF2ADDITERATOR; + +typedef std::vector<std::string> LOOPBACK2DELETEADD; +typedef LOOPBACK2DELETEADD::iterator LOOPBACK2DELETEADDITERATOR; + +static int handleOp(HandlerArg *a, OPCODE enmCode, int iStart, int *pcProcessed) +{ + if (a->argc - iStart < 2) + return errorSyntax(USAGE_NATNETWORK, "Not enough parameters"); + + int index = iStart; + HRESULT rc; + + const char *pNetName = NULL; + const char *pNetworkCidr = NULL; + int enable = -1; + int dhcp = -1; + int ipv6 = -1; + + VPF2DELETE vPfName2Delete; + VPF2ADD vPf2Add; + + LOOPBACK2DELETEADD vLoopback2Delete; + LOOPBACK2DELETEADD vLoopback2Add; + + LONG loopback6Offset = 0; /* ignore me */ + + int c; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + + RTGetOptInit(&GetState, + a->argc, + a->argv, + g_aNATNetworkIPOptions, + enmCode != OP_REMOVE ? RT_ELEMENTS(g_aNATNetworkIPOptions) : 4, /* we use only --netname and --ifname for remove*/ + index, + RTGETOPTINIT_FLAGS_NO_STD_OPTS); + while ((c = RTGetOpt(&GetState, &ValueUnion))) + { + switch (c) + { + case 't': // --netname + if (pNetName) + return errorSyntax(USAGE_NATNETWORK, "You can only specify --netname only once."); + else + pNetName = ValueUnion.psz; + break; + + case 'n': // --network + if (pNetworkCidr) + return errorSyntax(USAGE_NATNETWORK, "You can only specify --network only once."); + else + pNetworkCidr = ValueUnion.psz; + break; + + case 'e': // --enable + if (enable >= 0) + return errorSyntax(USAGE_NATNETWORK, "You can specify either --enable or --disable once."); + else + enable = 1; + break; + + case 'd': // --disable + if (enable >= 0) + return errorSyntax(USAGE_NATNETWORK, "You can specify either --enable or --disable once."); + else + enable = 0; + break; + + case 'h': + if (dhcp != -1) + return errorSyntax(USAGE_NATNETWORK, "You can specify --dhcp only once."); + dhcp = ValueUnion.f; + break; + + case '6': + if (ipv6 != -1) + return errorSyntax(USAGE_NATNETWORK, "You can specify --ipv6 only once."); + ipv6 = ValueUnion.f; + break; + + case 'L': /* ipv6 loopback */ + case 'l': /* ipv4 loopback */ + if (RTStrCmp(ValueUnion.psz, "delete") == 0) + { + /* deletion */ + if (enmCode != OP_MODIFY) + errorSyntax(USAGE_NATNETWORK, + "loopback couldn't be deleted on modified\n"); + if (c == 'L') + loopback6Offset = -1; + else + { + int vrc; + RTGETOPTUNION Addr2Delete; + vrc = RTGetOptFetchValue(&GetState, + &Addr2Delete, + RTGETOPT_REQ_STRING); + if (RT_FAILURE(vrc)) + return errorSyntax(USAGE_NATNETWORK, + "Not enough parmaters\n"); + + vLoopback2Delete.push_back(std::string(Addr2Delete.psz)); + } + } + else /* addition */ + { + if (c == 'L') + loopback6Offset = ValueUnion.u32; + else + vLoopback2Add.push_back(std::string(ValueUnion.psz)); + } + break; + + case 'P': /* ipv6 portforwarding*/ + case 'p': /* ipv4 portforwarding */ + { + if (RTStrCmp(ValueUnion.psz, "delete") != 0) + { + PORTFORWARDRULE Pfr; + + /* netPfStrToPf will clean up the Pfr */ + int irc = netPfStrToPf(ValueUnion.psz, (c == 'P'), &Pfr); + if (RT_FAILURE(irc)) + return errorSyntax(USAGE_NATNETWORK, + "Invalid port-forward rule %s\n", + ValueUnion.psz); + + vPf2Add.push_back(Pfr); + } + else + { + int vrc; + RTGETOPTUNION NamePf2DeleteUnion; + PFNAME2DELETE Name2Delete; + + if (enmCode != OP_MODIFY) + return errorSyntax(USAGE_NATNETWORK, + "Port-forward could be deleted on modify \n"); + + vrc = RTGetOptFetchValue(&GetState, + &NamePf2DeleteUnion, + RTGETOPT_REQ_STRING); + if (RT_FAILURE(vrc)) + return errorSyntax(USAGE_NATNETWORK, + "Not enough parmaters\n"); + + if (strlen(NamePf2DeleteUnion.psz) > PF_NAMELEN) + return errorSyntax(USAGE_NATNETWORK, + "Port-forward rule name is too long\n"); + + RT_ZERO(Name2Delete); + RTStrCopy(Name2Delete.szName, PF_NAMELEN, NamePf2DeleteUnion.psz); + Name2Delete.fIPv6 = (c == 'P'); + + vPfName2Delete.push_back(Name2Delete); + } + break; + } + + case VINF_GETOPT_NOT_OPTION: + return errorSyntax(USAGE_NATNETWORK, + "unhandled parameter: %s", + ValueUnion.psz); + + default: + if (c > 0) + { + if (RT_C_IS_GRAPH(c)) + return errorSyntax(USAGE_NATNETWORK, + "unhandled option: -%c", c); + else + return errorSyntax(USAGE_NATNETWORK, + "unhandled option: %i", c); + } + else if (c == VERR_GETOPT_UNKNOWN_OPTION) + return errorSyntax(USAGE_NATNETWORK, + "unknown option: %s", ValueUnion.psz); + else if (ValueUnion.pDef) + return errorSyntax(USAGE_NATNETWORK, + "%s: %Rrs", ValueUnion.pDef->pszLong, c); + else + return errorSyntax(USAGE_NATNETWORK, "%Rrs", c); + } + } + + if (!pNetName) + return errorSyntax(USAGE_NATNETWORK, + "You need to specify the --netname option"); + /* verification */ + switch (enmCode) + { + case OP_ADD: + if (!pNetworkCidr) + return errorSyntax(USAGE_NATNETWORK, + "You need to specify the --network option"); + break; + case OP_MODIFY: + case OP_REMOVE: + case OP_START: + case OP_STOP: + break; + default: + AssertMsgFailedReturn(("Unknown operation (:%d)", enmCode), VERR_NOT_IMPLEMENTED); + } + + Bstr NetName; + NetName = Bstr(pNetName); + + ComPtr<INATNetwork> net; + rc = a->virtualBox->FindNATNetworkByName(NetName.mutableRaw(), net.asOutParam()); + if (enmCode == OP_ADD) + { + if (SUCCEEDED(rc)) + return errorArgument("NATNetwork server already exists"); + + CHECK_ERROR(a->virtualBox, CreateNATNetwork(NetName.raw(), net.asOutParam())); + if (FAILED(rc)) + return errorArgument("Failed to create the NAT network service"); + } + else if (FAILED(rc)) + return errorArgument("NATNetwork server does not exist"); + + switch (enmCode) + { + case OP_ADD: + case OP_MODIFY: + { + if (pNetworkCidr) + { + CHECK_ERROR(net, COMSETTER(Network)(Bstr(pNetworkCidr).raw())); + if (FAILED(rc)) + return errorArgument("Failed to set configuration"); + } + if (dhcp >= 0) + { + CHECK_ERROR(net, COMSETTER(NeedDhcpServer) ((BOOL)dhcp)); + if (FAILED(rc)) + return errorArgument("Failed to set configuration"); + } + + if (ipv6 >= 0) + { + CHECK_ERROR(net, COMSETTER(IPv6Enabled) ((BOOL)ipv6)); + if (FAILED(rc)) + return errorArgument("Failed to set configuration"); + } + + if (!vPfName2Delete.empty()) + { + VPF2DELETEITERATOR it; + for (it = vPfName2Delete.begin(); it != vPfName2Delete.end(); ++it) + { + CHECK_ERROR(net, RemovePortForwardRule((BOOL)(*it).fIPv6, + Bstr((*it).szName).raw())); + if (FAILED(rc)) + return errorArgument("Failed to delete pf"); + } + } + + if (!vPf2Add.empty()) + { + VPF2ADDITERATOR it; + for (it = vPf2Add.begin(); it != vPf2Add.end(); ++it) + { + NATProtocol_T proto = NATProtocol_TCP; + if ((*it).iPfrProto == IPPROTO_TCP) + proto = NATProtocol_TCP; + else if ((*it).iPfrProto == IPPROTO_UDP) + proto = NATProtocol_UDP; + else + continue; /* XXX: warning here. */ + + CHECK_ERROR(net, AddPortForwardRule((BOOL)(*it).fPfrIPv6, + Bstr((*it).szPfrName).raw(), + proto, + Bstr((*it).szPfrHostAddr).raw(), + (*it).u16PfrHostPort, + Bstr((*it).szPfrGuestAddr).raw(), + (*it).u16PfrGuestPort)); + if (FAILED(rc)) + return errorArgument("Failed to add pf"); + } + } + + if (loopback6Offset) + { + if (loopback6Offset == -1) + loopback6Offset = 0; /* deletion */ + + CHECK_ERROR_RET(net, COMSETTER(LoopbackIp6)(loopback6Offset), rc); + } + + /* addLocalMapping (hostid, offset) */ + if (!vLoopback2Add.empty()) + { + /* we're expecting stings 127.0.0.1=5 */ + LOOPBACK2DELETEADDITERATOR it; + for (it = vLoopback2Add.begin(); + it != vLoopback2Add.end(); + ++it) + { + std::string address, strOffset; + int pos = it->find('='); + LONG lOffset = 0; + Bstr bstrAddress; + + AssertReturn(pos != -1, errorArgument("invalid loopback string")); + + address = it->substr(0, pos); + strOffset = it->substr(pos + 1); + + lOffset = RTStrToUInt32(strOffset.c_str()); + AssertReturn(lOffset > 0, errorArgument("invalid loopback string")); + + bstrAddress = Bstr(address.c_str()); + + CHECK_ERROR_RET(net, AddLocalMapping(bstrAddress.raw(), lOffset), rc); + } + } + + if (!vLoopback2Delete.empty()) + { + /* we're expecting stings 127.0.0.1 */ + LOOPBACK2DELETEADDITERATOR it; + for (it = vLoopback2Add.begin(); + it != vLoopback2Add.end(); + ++it) + { + Bstr bstrAddress; + bstrAddress = Bstr(it->c_str()); + + CHECK_ERROR_RET(net, AddLocalMapping(bstrAddress.raw(), 0), rc); + } + } + + if (enable >= 0) + { + CHECK_ERROR(net, COMSETTER(Enabled) ((BOOL)enable)); + if (FAILED(rc)) + return errorArgument("Failed to set configuration"); + } + break; + } + case OP_REMOVE: + { + CHECK_ERROR(a->virtualBox, RemoveNATNetwork(net)); + if (FAILED(rc)) + return errorArgument("Failed to remove nat network"); + break; + } + case OP_START: + { + CHECK_ERROR(net, Start(Bstr("whatever").raw())); + if (FAILED(rc)) + return errorArgument("Failed to start network"); + break; + } + case OP_STOP: + { + CHECK_ERROR(net, Stop()); + if (FAILED(rc)) + return errorArgument("Failed to start network"); + break; + } + default:; + } + return 0; +} + + +int handleNATNetwork(HandlerArg *a) +{ + if (a->argc < 1) + return errorSyntax(USAGE_NATNETWORK, "Not enough parameters"); + + int result; + int cProcessed; + if (strcmp(a->argv[0], "modify") == 0) + result = handleOp(a, OP_MODIFY, 1, &cProcessed); + else if (strcmp(a->argv[0], "add") == 0) + result = handleOp(a, OP_ADD, 1, &cProcessed); + else if (strcmp(a->argv[0], "remove") == 0) + result = handleOp(a, OP_REMOVE, 1, &cProcessed); + else if (strcmp(a->argv[0], "start") == 0) + result = handleOp(a, OP_START, 1, &cProcessed); + else if (strcmp(a->argv[0], "stop") == 0) + result = handleOp(a, OP_STOP, 1, &cProcessed); + else + result = errorSyntax(USAGE_NATNETWORK, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str()); + + return result; +} + +#endif /* !VBOX_ONLY_DOCS */ diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp index b76b94ba..1ac7dddd 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -306,13 +306,14 @@ int handleSnapshot(HandlerArg *a) /* parse the optional arguments */ Bstr desc; - bool fPause = false; + bool fPause = true; /* default is NO live snapshot */ static const RTGETOPTDEF s_aTakeOptions[] = { { "--description", 'd', RTGETOPT_REQ_STRING }, { "-description", 'd', RTGETOPT_REQ_STRING }, { "-desc", 'd', RTGETOPT_REQ_STRING }, - { "--pause", 'p', RTGETOPT_REQ_NOTHING } + { "--pause", 'p', RTGETOPT_REQ_NOTHING }, + { "--live", 'l', RTGETOPT_REQ_NOTHING } }; RTGETOPTSTATE GetOptState; RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTakeOptions, RT_ELEMENTS(s_aTakeOptions), @@ -328,6 +329,10 @@ int handleSnapshot(HandlerArg *a) fPause = true; break; + case 'l': + fPause = false; + break; + case 'd': desc = Value.psz; break; diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp index 8bafaae6..2de0befe 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp @@ -101,6 +101,8 @@ int handleStorageAttach(HandlerArg *a) Bstr bstrUsername; Bstr bstrPassword; Bstr bstrInitiator; + Bstr bstrIso; + Utf8Str strIso; bool fIntNet = false; RTGETOPTUNION ValueUnion; @@ -138,7 +140,7 @@ int handleStorageAttach(HandlerArg *a) break; } - case 'm': // medium <none|emptydrive|uuid|filename|host:<drive>|iSCSI> + case 'm': // medium <none|emptydrive|additions|uuid|filename|host:<drive>|iSCSI> { if (ValueUnion.psz) pszMedium = ValueUnion.psz; @@ -192,7 +194,7 @@ int handleStorageAttach(HandlerArg *a) break; } - case 'u': // nonrotational <on|off> + case 'u': // discard <on|off> { if (ValueUnion.psz) pszDiscard = ValueUnion.psz; @@ -482,9 +484,25 @@ int handleStorageAttach(HandlerArg *a) if (pszMedium) { + if (!RTStrICmp(pszMedium, "additions")) + { + ComPtr<ISystemProperties> pProperties; + CHECK_ERROR(a->virtualBox, + COMGETTER(SystemProperties)(pProperties.asOutParam())); + CHECK_ERROR(pProperties, COMGETTER(DefaultAdditionsISO)(bstrIso.asOutParam())); + strIso = Utf8Str(bstrIso); + if (strIso.isEmpty()) + throw Utf8Str("Cannot find the Guest Additions ISO image\n"); + pszMedium = strIso.c_str(); + if (devTypeRequested == DeviceType_Null) + devTypeRequested = DeviceType_DVD; + } ComPtr<IMedium> pExistingMedium; - rc = findMedium(a, pszMedium, deviceType, true /* fSilent */, - pExistingMedium); + rc = openMedium(a, pszMedium, deviceType, + AccessMode_ReadWrite, + pExistingMedium, + false /* fForceNewUuidOnOpen */, + true /* fSilent */); if (SUCCEEDED(rc) && pExistingMedium) { if ( (deviceType == DeviceType_DVD) @@ -601,7 +619,7 @@ int handleStorageAttach(HandlerArg *a) Bstr("InitiatorSecret").detachTo(names.appendedRaw()); bstrPassword.detachTo(values.appendedRaw()); } - if (!bstrPassword.isEmpty()) + if (!bstrInitiator.isEmpty()) { Bstr("InitiatorName").detachTo(names.appendedRaw()); bstrInitiator.detachTo(values.appendedRaw()); @@ -637,9 +655,9 @@ int handleStorageAttach(HandlerArg *a) else { Bstr bstrMedium(pszMedium); - rc = findOrOpenMedium(a, pszMedium, devTypeRequested, - AccessMode_ReadWrite, pMedium2Mount, - fSetNewUuid, NULL); + rc = openMedium(a, pszMedium, devTypeRequested, + AccessMode_ReadWrite, pMedium2Mount, + fSetNewUuid, false /* fSilent */); if (FAILED(rc) || !pMedium2Mount) throw Utf8StrFmt("Invalid UUID or filename \"%s\"", pszMedium); } @@ -888,7 +906,7 @@ static const RTGETOPTDEF g_aStorageControllerOptions[] = { "--name", 'n', RTGETOPT_REQ_STRING }, { "--add", 'a', RTGETOPT_REQ_STRING }, { "--controller", 'c', RTGETOPT_REQ_STRING }, - { "--sataportcount", 'p', RTGETOPT_REQ_UINT32 }, + { "--portcount", 'p', RTGETOPT_REQ_UINT32 }, { "--remove", 'r', RTGETOPT_REQ_NOTHING }, { "--hostiocache", 'i', RTGETOPT_REQ_STRING }, { "--bootable", 'b', RTGETOPT_REQ_STRING }, @@ -905,7 +923,7 @@ int handleStorageController(HandlerArg *a) const char *pszBootable = NULL; ULONG satabootdev = ~0U; ULONG sataidedev = ~0U; - ULONG sataportcount = ~0U; + ULONG portcount = ~0U; bool fRemoveCtl = false; ComPtr<IMachine> machine; RTGETOPTUNION ValueUnion; @@ -949,9 +967,9 @@ int handleStorageController(HandlerArg *a) break; } - case 'p': // sataportcount + case 'p': // portcount { - sataportcount = ValueUnion.u32; + portcount = ValueUnion.u32; break; } @@ -1105,7 +1123,7 @@ int handleStorageController(HandlerArg *a) } } - if ( (sataportcount != ~0U) + if ( (portcount != ~0U) && SUCCEEDED(rc)) { ComPtr<IStorageController> ctl; @@ -1115,7 +1133,7 @@ int handleStorageController(HandlerArg *a) if (SUCCEEDED(rc)) { - CHECK_ERROR(ctl, COMSETTER(PortCount)(sataportcount)); + CHECK_ERROR(ctl, COMSETTER(PortCount)(portcount)); } else { diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp index 91d98781..08f0d433 100644 --- a/src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp +++ b/src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -21,25 +21,12 @@ #include <VBox/com/array.h> #include <VBox/com/ErrorInfo.h> #include <VBox/com/errorprint.h> -#include <VBox/com/EventQueue.h> - #include <VBox/com/VirtualBox.h> #include "VBoxManage.h" #include <iprt/asm.h> -/* missing XPCOM <-> COM wrappers */ -#ifndef STDMETHOD_ -# define STDMETHOD_(ret, meth) NS_IMETHOD_(ret) meth -#endif -#ifndef NS_GET_IID -# define NS_GET_IID(I) IID_##I -#endif -#ifndef RT_OS_WINDOWS -#define IUnknown nsISupports -#endif - using namespace com; /** @@ -72,13 +59,13 @@ public: STDMETHOD(QueryInterface)(const IID &iid, void **ppvObject) { Guid guid(iid); - if (guid == Guid(NS_GET_IID(IUnknown))) + if (guid == Guid(COM_IIDOF(IUnknown))) *ppvObject = (IUnknown *)this; #ifdef RT_OS_WINDOWS - else if (guid == Guid(NS_GET_IID(IDispatch))) + else if (guid == Guid(COM_IIDOF(IDispatch))) *ppvObject = (IDispatch *)this; #endif - else if (guid == Guid(NS_GET_IID(IUSBDevice))) + else if (guid == Guid(COM_IIDOF(IUSBDevice))) *ppvObject = (IUSBDevice *)this; else return E_NOINTERFACE; @@ -400,18 +387,18 @@ int handleUSBFilter(HandlerArg *a) USBFilterCmd::USBFilter &f = cmd.mFilter; - ComPtr <IHost> host; - ComPtr <IUSBController> ctl; + ComPtr<IHost> host; + ComPtr<IUSBDeviceFilters> flts; if (cmd.mGlobal) CHECK_ERROR_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), 1); else { /* open a session for the VM */ - CHECK_ERROR_RET(cmd.mMachine, LockMachine(a->session, LockType_Write), 1); + CHECK_ERROR_RET(cmd.mMachine, LockMachine(a->session, LockType_Shared), 1); /* get the mutable session machine */ a->session->COMGETTER(Machine)(cmd.mMachine.asOutParam()); - /* and get the USB controller */ - CHECK_ERROR_RET(cmd.mMachine, COMGETTER(USBController)(ctl.asOutParam()), 1); + /* and get the USB device filters */ + CHECK_ERROR_RET(cmd.mMachine, COMGETTER(USBDeviceFilters)(flts.asOutParam()), 1); } switch (cmd.mAction) @@ -420,7 +407,7 @@ int handleUSBFilter(HandlerArg *a) { if (cmd.mGlobal) { - ComPtr <IHostUSBDeviceFilter> flt; + ComPtr<IHostUSBDeviceFilter> flt; CHECK_ERROR_BREAK(host, CreateUSBDeviceFilter(f.mName.raw(), flt.asOutParam())); @@ -446,8 +433,8 @@ int handleUSBFilter(HandlerArg *a) } else { - ComPtr <IUSBDeviceFilter> flt; - CHECK_ERROR_BREAK(ctl, CreateDeviceFilter(f.mName.raw(), + ComPtr<IUSBDeviceFilter> flt; + CHECK_ERROR_BREAK(flts, CreateDeviceFilter(f.mName.raw(), flt.asOutParam())); if (!f.mActive.isNull()) @@ -467,7 +454,7 @@ int handleUSBFilter(HandlerArg *a) if (!f.mMaskedInterfaces.isNull()) CHECK_ERROR_BREAK(flt, COMSETTER(MaskedInterfaces)(f.mMaskedInterfaces)); - CHECK_ERROR_BREAK(ctl, InsertDeviceFilter(cmd.mIndex, flt)); + CHECK_ERROR_BREAK(flts, InsertDeviceFilter(cmd.mIndex, flt)); } break; } @@ -478,7 +465,7 @@ int handleUSBFilter(HandlerArg *a) SafeIfaceArray <IHostUSBDeviceFilter> coll; CHECK_ERROR_BREAK(host, COMGETTER(USBDeviceFilters)(ComSafeArrayAsOutParam(coll))); - ComPtr <IHostUSBDeviceFilter> flt = coll[cmd.mIndex]; + ComPtr<IHostUSBDeviceFilter> flt = coll[cmd.mIndex]; if (!f.mName.isEmpty()) CHECK_ERROR_BREAK(flt, COMSETTER(Name)(f.mName.raw())); @@ -503,9 +490,9 @@ int handleUSBFilter(HandlerArg *a) else { SafeIfaceArray <IUSBDeviceFilter> coll; - CHECK_ERROR_BREAK(ctl, COMGETTER(DeviceFilters)(ComSafeArrayAsOutParam(coll))); + CHECK_ERROR_BREAK(flts, COMGETTER(DeviceFilters)(ComSafeArrayAsOutParam(coll))); - ComPtr <IUSBDeviceFilter> flt = coll[cmd.mIndex]; + ComPtr<IUSBDeviceFilter> flt = coll[cmd.mIndex]; if (!f.mName.isEmpty()) CHECK_ERROR_BREAK(flt, COMSETTER(Name)(f.mName.raw())); @@ -532,13 +519,13 @@ int handleUSBFilter(HandlerArg *a) { if (cmd.mGlobal) { - ComPtr <IHostUSBDeviceFilter> flt; + ComPtr<IHostUSBDeviceFilter> flt; CHECK_ERROR_BREAK(host, RemoveUSBDeviceFilter(cmd.mIndex)); } else { - ComPtr <IUSBDeviceFilter> flt; - CHECK_ERROR_BREAK(ctl, RemoveDeviceFilter(cmd.mIndex, flt.asOutParam())); + ComPtr<IUSBDeviceFilter> flt; + CHECK_ERROR_BREAK(flts, RemoveDeviceFilter(cmd.mIndex, flt.asOutParam())); } break; } |
