summaryrefslogtreecommitdiffstats
path: root/src/VBox/Frontends/VBoxManage
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Frontends/VBoxManage')
-rw-r--r--src/VBox/Frontends/VBoxManage/Makefile.kmk208
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp2597
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManage.cpp760
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManage.h338
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManage.rc51
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp1516
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp373
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp2290
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp525
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp899
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp2560
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp3435
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.h236
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrlListener.cpp518
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp437
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp1404
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp283
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp2691
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageList.cpp1608
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp651
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp1941
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp3202
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp523
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp641
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp1290
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp600
26 files changed, 31577 insertions, 0 deletions
diff --git a/src/VBox/Frontends/VBoxManage/Makefile.kmk b/src/VBox/Frontends/VBoxManage/Makefile.kmk
new file mode 100644
index 00000000..02b93dc4
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/Makefile.kmk
@@ -0,0 +1,208 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for VBoxManage (the cli frontend).
+#
+
+#
+# Copyright (C) 2006-2019 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.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+include $(PATH_ROOT)/doc/manual/Config.kmk
+
+
+VBOX_COMMON_VBOXMANAGE_DEFS = \
+ $(if $(VBOX_WITH_AHCI), VBOX_WITH_AHCI) \
+ $(if $(VBOX_WITH_COPYTOGUEST),VBOX_WITH_COPYTOGUEST) \
+ $(if $(VBOX_WITH_E1000),VBOX_WITH_E1000) \
+ $(if $(VBOX_WITH_GUEST_CONTROL),VBOX_WITH_GUEST_CONTROL) \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS) \
+ $(if $(VBOX_WITH_HEADLESS), VBOX_WITH_HEADLESS) \
+ $(if $(VBOX_WITH_HGCM), VBOX_WITH_HGCM) \
+ $(if $(VBOX_WITH_HOSTNETIF_API), VBOX_WITH_HOSTNETIF_API) \
+ $(if $(VBOX_WITH_NETFLT), VBOX_WITH_NETFLT) \
+ $(if $(VBOX_WITH_AUDIO_OSS), VBOX_WITH_AUDIO_OSS) \
+ $(if $(VBOX_WITH_AUDIO_ALSA), VBOX_WITH_AUDIO_ALSA) \
+ $(if $(VBOX_WITH_AUDIO_PULSE),VBOX_WITH_AUDIO_PULSE) \
+ $(if $(VBOX_WITH_SCSI), VBOX_WITH_SCSI) \
+ $(if $(VBOX_WITH_VBOXSDL), VBOX_WITH_VBOXSDL) \
+ $(if $(VBOX_WITH_VIDEOHWACCEL), VBOX_WITH_VIDEOHWACCEL) \
+ $(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_RECORDING),VBOX_WITH_RECORDING) \
+ $(if $(VBOX_WITH_AUDIO_RECORDING),VBOX_WITH_AUDIO_RECORDING) \
+ $(if $(VBOX_WITH_NAT_SERVICE),VBOX_WITH_NAT_SERVICE) \
+ $(if $(VBOX_WITH_VMSVGA),VBOX_WITH_VMSVGA)
+
+
+ifdef VBOX_WITH_DOCS
+ PROGRAMS += VBoxManageHelp
+endif
+VBoxManageHelp_TEMPLATE = VBoxAdvBldProg
+VBoxManageHelp_DEFS += \
+ VBOX_ONLY_DOCS \
+ $(VBOX_COMMON_VBOXMANAGE_DEFS)
+VBoxManageHelp_SOURCES = \
+ VBoxManage.cpp \
+ VBoxManageHelp.cpp \
+ $(if $(VBOX_WITH_GUEST_PROPS),VBoxManageGuestProp.cpp) \
+ $(if $(VBOX_WITH_GUEST_CONTROL),VBoxManageGuestCtrl.cpp)
+
+ifndef VBOX_ONLY_DOCS
+ PROGRAMS += VBoxManage
+ VBoxManage_TEMPLATE = VBOXMAINCLIENTEXE
+ VBoxManage_DEFS += $(VBOX_COMMON_VBOXMANAGE_DEFS)
+ VBoxManage_DEFS.win = _WIN32_WINNT=0x0500
+ VBoxManage_INCS = \
+ $(VBoxManage_0_OUTDIR)
+ VBoxManage_INTERMEDIATES = \
+ $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.h
+ VBoxManage_SOURCES = \
+ VBoxManage.cpp \
+ VBoxInternalManage.cpp \
+ VBoxManageAppliance.cpp \
+ VBoxManageBandwidthControl.cpp \
+ VBoxManageControlVM.cpp \
+ VBoxManageDebugVM.cpp \
+ 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 \
+ $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.cpp \
+ VBoxManageHostonly.cpp \
+ VBoxManageInfo.cpp \
+ VBoxManageList.cpp \
+ VBoxManageMetrics.cpp \
+ VBoxManageMisc.cpp \
+ VBoxManageModifyVM.cpp \
+ VBoxManageSnapshot.cpp \
+ VBoxManageStorageController.cpp \
+ VBoxManageUSB.cpp \
+ $(if $(VBOX_WITH_NAT_SERVICE),VBoxManageNATNetwork.cpp,) \
+ $(if $(VBOX_WITH_NAT_SERVICE),../../NetworkServices/NetLib/VBoxNetPortForwardString.cpp,)
+ VBoxManage_SOURCES.win = \
+ VBoxManage.rc
+ VBoxManage_LIBS += $(LIB_DDU)
+
+ # VBoxNetPortForwardString.h
+ VBoxManageNATNetwork.cpp_INCS += ../../NetworkServices/NetLib/
+
+endif # VBOX_ONLY_DOCS
+
+ifneq ($(KBUILD_TARGET),win)
+ # Workaround for buggy gcc-4.3 compilers, see
+ #
+ # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36474
+ #
+ # Some later revisions of version 4.3.1 are known to work so we assume
+ # that version 4.3.2 or later has this bug definitely fixed.
+ VBoxManage_CXXFLAGS.release += \
+ $(if $(VBOX_GCC_VERSION_CXX),$(if-expr $(VBOX_GCC_VERSION_CXX) < 40300 || $(VBOX_GCC_VERSION_CXX) > 40301,,--param max-fields-for-field-sensitive=0),)
+ VBoxManageHelp_CXXFLAGS.release = $(VBoxManage_CXXFLAGS.release)
+endif
+
+
+#
+# VBoxManage built-in help from XML refentry in doc/manual/en_US/.
+#
+$(call KB_FN_DO_PASS0_ON_TARGET,VBoxManage)
+
+VBoxManage_CLEAN += \
+ $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.cpp \
+ $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.cpp.ts \
+ $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.h \
+ $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.h.ts \
+ $(addprefix $(VBoxManage_0_OUTDIR)/,$(filter man_VBoxManage-%,$(VBOX_MANUAL_XML_REFENTRY_FILES)))
+
+
+
+# Preprocess the xml files, applying remarks.
+$(foreach file,$(filter man_VBoxManage-%,$(VBOX_MANUAL_XML_REFENTRY_FILES)) \
+, $(evalcall2 def_vbox_refentry_preprocess_for_manpage,$(VBoxManage_0_OUTDIR),$(file),$(VBOX_PATH_MANUAL_SRC)/en_US/$(file)))
+
+
+# Generate the .cpp file.
+$(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.cpp.ts \
++| $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.cpp: \
+ $(VBOX_DOCBOOK_REFENTRY_TO_C_HELP) \
+ $(addprefix $(VBoxManage_0_OUTDIR)/,$(filter man_VBoxManage-%,$(VBOX_MANUAL_XML_REFENTRY_FILES))) \
+ $(VBOX_XML_CATALOG) $(VBOX_XML_CATALOG_DOCBOOK) $(MAKEFILE) | $$(dir $$@)
+ $(call MSG_TOOL,xsltproc $(notdir $(firstword $(filter %.xsl,$^))),,$(filter %.xml,$^),$(patsubst %.ts,%,$@))
+ $(QUIET)$(APPEND) -tn "$@" \
+ '/* Autogenerated by $<, do not edit! */' \
+ '' \
+ '#include "VBoxManageBuiltInHelp.h"' \
+ ''
+ $(foreach refentry,$(filter %.xml,$^) \
+ ,$(NLTAB)$(QUIET)$(call VBOX_XSLTPROC_WITH_CAT, -a+to "$@") $< $(refentry))
+ $(QUIET)$(APPEND) -n "$@" \
+ '' \
+ 'PCRTMSGREFENTRY g_apHelpEntries[] = ' \
+ '{'
+ $(foreach refentry,$(filter %.xml,$^) \
+ ,$(NLTAB)$(QUIET)$(APPEND) -n "$@" \
+ ' &g_$(subst -,_,$(tolower $(patsubst man_%,%,$(notdir $(basename $(refentry)))))), ')
+ $(QUIET)$(APPEND) -n "$@" \
+ '};' \
+ 'const uint32_t g_cHelpEntries = RT_ELEMENTS(g_apHelpEntries);' \
+ ''
+ $(QUIET)$(CP) --changed -- "$@" "$(patsubst %.ts,%,$@)"
+# The above APPEND stuff trigger some kind of problem on some boxes when not split up...
+# update: Fixed in SVN (strcpy -> memmove in new_job(), job.c - r2591). Just need to rebuild all platforms.
+
+
+$(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.h.ts \
++| $(VBoxManage_0_OUTDIR)/VBoxManageBuiltInHelp.h: \
+ $(VBOX_DOCBOOK_REFENTRY_TO_H_HELP) \
+ $(addprefix $(VBoxManage_0_OUTDIR)/,$(filter man_VBoxManage-%,$(VBOX_MANUAL_XML_REFENTRY_FILES))) \
+ $(VBOX_XML_CATALOG) $(VBOX_XML_CATALOG_DOCBOOK) $(MAKEFILE) | $$(dir $$@)
+ $(call MSG_TOOL,xsltproc $(notdir $(firstword $(filter %.xsl,$^))),,$(filter %.xml,$^),$(patsubst %.ts,%,$@))
+ $(QUIET)$(APPEND) -tn "$@" \
+ '/* Autogenerated by $<, do not edit! */' \
+ '' \
+ '#ifndef ___VBoxManageBuiltInHelp_h___' \
+ '#define ___VBoxManageBuiltInHelp_h___' \
+ '' \
+ '#include <iprt/message.h>' \
+ '' \
+ 'RT_C_DECLS_BEGIN' \
+ '' \
+ 'typedef enum HELP_CMD_VBOXMANAGE' \
+ '{' \
+ ' HELP_CMD_VBOXMANAGE_INVALID = 0,'
+ $(foreach refentry,$(filter %.xml,$^) \
+ ,$(NLTAB)$(QUIET)$(call VBOX_XSLTPROC_WITH_CAT, -a+to "$@") \
+ --stringparam 'g_sMode' 'cmd' $< $(refentry))
+ $(QUIET)$(APPEND) -n "$@" \
+ ' HELP_CMD_VBOXMANAGE_END' \
+ '} HELP_CMD_VBOXMANAGE;'
+ $(foreach refentry,$(filter %.xml,$^) \
+ ,$(NLTAB)$(QUIET)$(call VBOX_XSLTPROC_WITH_CAT, -a+to "$@") \
+ --stringparam 'g_sMode' 'subcmd' $< $(refentry))
+ $(QUIET)$(APPEND) -n "$@" \
+ '' \
+ 'extern PCRTMSGREFENTRY g_apHelpEntries[];' \
+ 'extern const uint32_t g_cHelpEntries;' \
+ '' \
+ 'RT_C_DECLS_END' \
+ '' \
+ '#endif' \
+ ''
+ $(QUIET)$(CP) --changed -- "$@" "$(patsubst %.ts,%,$@)"
+
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp b/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp
new file mode 100644
index 00000000..9beaf9bc
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp
@@ -0,0 +1,2597 @@
+/* $Id: VBoxInternalManage.cpp $ */
+/** @file
+ * VBoxManage - The 'internalcommands' command.
+ *
+ * VBoxInternalManage used to be a second CLI for doing special tricks,
+ * not intended for general usage, only for assisting VBox developers.
+ * It is now integrated into VBoxManage.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+
+#include <VBox/com/VirtualBox.h>
+
+#include <VBox/vd.h>
+#include <VBox/sup.h>
+#include <VBox/log.h>
+
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <iprt/sha.h>
+
+#include "VBoxManage.h"
+
+/* Includes for the raw disk stuff. */
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+# include <winioctl.h>
+#elif defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) \
+ || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
+# include <errno.h>
+# include <sys/ioctl.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+# include <unistd.h>
+#endif
+#ifdef RT_OS_LINUX
+# include <sys/utsname.h>
+# include <linux/hdreg.h>
+# include <linux/fs.h>
+# include <stdlib.h> /* atoi() */
+#endif /* RT_OS_LINUX */
+#ifdef RT_OS_DARWIN
+# include <sys/disk.h>
+#endif /* RT_OS_DARWIN */
+#ifdef RT_OS_SOLARIS
+# include <stropts.h>
+# include <sys/dkio.h>
+# include <sys/vtoc.h>
+#endif /* RT_OS_SOLARIS */
+#ifdef RT_OS_FREEBSD
+# include <sys/disk.h>
+#endif /* RT_OS_FREEBSD */
+
+using namespace com;
+
+
+/** Macro for checking whether a partition is of extended type or not. */
+#define PARTTYPE_IS_EXTENDED(x) ((x) == 0x05 || (x) == 0x0f || (x) == 0x85)
+
+/** Maximum number of partitions we can deal with.
+ * Ridiculously large number, but the memory consumption is rather low so who
+ * cares about never using most entries. */
+#define HOSTPARTITION_MAX 100
+
+
+typedef struct HOSTPARTITION
+{
+ /** partition number */
+ unsigned uIndex;
+ /** partition number (internal only, windows specific numbering) */
+ unsigned uIndexWin;
+ /** partition type */
+ unsigned uType;
+ /** CHS/cylinder of the first sector */
+ unsigned uStartCylinder;
+ /** CHS/head of the first sector */
+ unsigned uStartHead;
+ /** CHS/head of the first sector */
+ unsigned uStartSector;
+ /** CHS/cylinder of the last sector */
+ unsigned uEndCylinder;
+ /** CHS/head of the last sector */
+ unsigned uEndHead;
+ /** CHS/sector of the last sector */
+ unsigned uEndSector;
+ /** start sector of this partition relative to the beginning of the hard
+ * disk or relative to the beginning of the extended partition table */
+ uint64_t uStart;
+ /** numer of sectors of the partition */
+ uint64_t uSize;
+ /** start sector of this partition _table_ */
+ uint64_t uPartDataStart;
+ /** numer of sectors of this partition _table_ */
+ uint64_t cPartDataSectors;
+} HOSTPARTITION, *PHOSTPARTITION;
+
+typedef struct HOSTPARTITIONS
+{
+ /** partitioning type - MBR or GPT */
+ VDISKPARTTYPE uPartitioningType;
+ unsigned cPartitions;
+ HOSTPARTITION aPartitions[HOSTPARTITION_MAX];
+} HOSTPARTITIONS, *PHOSTPARTITIONS;
+
+/** flag whether we're in internal mode */
+bool g_fInternalMode;
+
+/**
+ * Print the usage info.
+ */
+void printUsageInternal(USAGECATEGORY u64Cmd, PRTSTREAM pStrm)
+{
+ RTStrmPrintf(pStrm,
+ "Usage: VBoxManage internalcommands <command> [command arguments]\n"
+ "\n"
+ "Commands:\n"
+ "\n"
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
+ "WARNING: This is a development tool and shall only be used to analyse\n"
+ " problems. It is completely unsupported and will change in\n"
+ " incompatible ways without warning.\n",
+
+ (u64Cmd & USAGE_LOADMAP)
+ ? " 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"
+ " This will instruct DBGF to load the given symbol file\n"
+ " during initialization.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_SETHDUUID)
+ ? " sethduuid <filepath> [<uuid>]\n"
+ " Assigns a new UUID to the given image file. This way, multiple copies\n"
+ " of a container can be registered.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_SETHDPARENTUUID)
+ ? " sethdparentuuid <filepath> <uuid>\n"
+ " Assigns a new parent UUID to the given image file.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_DUMPHDINFO)
+ ? " dumphdinfo <filepath>\n"
+ " Prints information about the image at the given location.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_LISTPARTITIONS)
+ ? " listpartitions -rawdisk <diskname>\n"
+ " Lists all partitions on <diskname>.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_CREATERAWVMDK)
+ ? " createrawvmdk -filename <filename> -rawdisk <diskname>\n"
+ " [-partitions <list of partition numbers> [-mbr <filename>] ]\n"
+ " [-relative]\n"
+ " Creates a new VMDK image which gives access to an entire host disk (if\n"
+ " the parameter -partitions is not specified) or some partitions of a\n"
+ " host disk. If access to individual partitions is granted, then the\n"
+ " parameter -mbr can be used to specify an alternative MBR to be used\n"
+ " (the partitioning information in the MBR file is ignored).\n"
+ " The diskname is on Linux e.g. /dev/sda, and on Windows e.g.\n"
+ " \\\\.\\PhysicalDrive0).\n"
+ " On Linux or FreeBSD host the parameter -relative causes a VMDK file to\n"
+ " be created which refers to individual partitions instead to the entire\n"
+ " disk.\n"
+ " The necessary partition numbers can be queried with\n"
+ " VBoxManage internalcommands listpartitions\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_RENAMEVMDK)
+ ? " renamevmdk -from <filename> -to <filename>\n"
+ " Renames an existing VMDK image, including the base file and all its extents.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_CONVERTTORAW)
+ ? " converttoraw [-format <fileformat>] <filename> <outputfile>"
+#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
+ "|stdout"
+#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
+ "\n"
+ " Convert image to raw, writing to file"
+#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
+ " or stdout"
+#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
+ ".\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_CONVERTHD)
+ ? " converthd [-srcformat VDI|VMDK|VHD|RAW]\n"
+ " [-dstformat VDI|VMDK|VHD|RAW]\n"
+ " <inputfile> <outputfile>\n"
+ " converts hard disk images between formats\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_REPAIRHD)
+ ? " repairhd [-dry-run]\n"
+ " [-format VDI|VMDK|VHD|...]\n"
+ " <filename>\n"
+ " Tries to repair corrupted disk images\n"
+ "\n"
+ : "",
+#ifdef RT_OS_WINDOWS
+ (u64Cmd & USAGE_MODINSTALL)
+ ? " modinstall\n"
+ " Installs the necessary driver for the host OS\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_MODUNINSTALL)
+ ? " moduninstall\n"
+ " Deinstalls the driver\n"
+ "\n"
+ : "",
+#else
+ "",
+ "",
+#endif
+ (u64Cmd & USAGE_DEBUGLOG)
+ ? " debuglog <vmname|uuid> [--enable|--disable] [--flags todo]\n"
+ " [--groups todo] [--destinations todo]\n"
+ " Controls debug logging.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_PASSWORDHASH)
+ ? " passwordhash <passsword>\n"
+ " Generates a password hash.\n"
+ "\n"
+ : "",
+ (u64Cmd & USAGE_GUESTSTATS)
+ ? " gueststats <vmname|uuid> [--interval <seconds>]\n"
+ " Obtains and prints internal guest statistics.\n"
+ " Sets the update interval if specified.\n"
+ "\n"
+ : ""
+ );
+}
+
+/** @todo this is no longer necessary, we can enumerate extra data */
+/**
+ * Finds a new unique key name.
+ *
+ * I don't think this is 100% race condition proof, but we assumes
+ * the user is not trying to push this point.
+ *
+ * @returns Result from the insert.
+ * @param pMachine The Machine object.
+ * @param pszKeyBase The base key.
+ * @param rKey Reference to the string object in which we will return the key.
+ */
+static HRESULT NewUniqueKey(ComPtr<IMachine> pMachine, const char *pszKeyBase, Utf8Str &rKey)
+{
+ Bstr KeyBase(pszKeyBase);
+ Bstr Keys;
+ HRESULT hrc = pMachine->GetExtraData(KeyBase.raw(), Keys.asOutParam());
+ if (FAILED(hrc))
+ return hrc;
+
+ /* if there are no keys, it's simple. */
+ if (Keys.isEmpty())
+ {
+ rKey = "1";
+ return pMachine->SetExtraData(KeyBase.raw(), Bstr(rKey).raw());
+ }
+
+ /* find a unique number - brute force rulez. */
+ Utf8Str KeysUtf8(Keys);
+ const char *pszKeys = RTStrStripL(KeysUtf8.c_str());
+ for (unsigned i = 1; i < 1000000; i++)
+ {
+ char szKey[32];
+ size_t cchKey = RTStrPrintf(szKey, sizeof(szKey), "%#x", i);
+ const char *psz = strstr(pszKeys, szKey);
+ while (psz)
+ {
+ if ( ( psz == pszKeys
+ || psz[-1] == ' ')
+ && ( psz[cchKey] == ' '
+ || !psz[cchKey])
+ )
+ break;
+ psz = strstr(psz + cchKey, szKey);
+ }
+ if (!psz)
+ {
+ rKey = szKey;
+ Utf8StrFmt NewKeysUtf8("%s %s", pszKeys, szKey);
+ return pMachine->SetExtraData(KeyBase.raw(),
+ Bstr(NewKeysUtf8).raw());
+ }
+ }
+ RTMsgError("Cannot find unique key for '%s'!", pszKeyBase);
+ return E_FAIL;
+}
+
+
+#if 0
+/**
+ * Remove a key.
+ *
+ * I don't think this isn't 100% race condition proof, but we assumes
+ * the user is not trying to push this point.
+ *
+ * @returns Result from the insert.
+ * @param pMachine The machine object.
+ * @param pszKeyBase The base key.
+ * @param pszKey The key to remove.
+ */
+static HRESULT RemoveKey(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey)
+{
+ Bstr Keys;
+ HRESULT hrc = pMachine->GetExtraData(Bstr(pszKeyBase), Keys.asOutParam());
+ if (FAILED(hrc))
+ return hrc;
+
+ /* if there are no keys, it's simple. */
+ if (Keys.isEmpty())
+ return S_OK;
+
+ char *pszKeys;
+ int rc = RTUtf16ToUtf8(Keys.raw(), &pszKeys);
+ if (RT_SUCCESS(rc))
+ {
+ /* locate it */
+ size_t cchKey = strlen(pszKey);
+ char *psz = strstr(pszKeys, pszKey);
+ while (psz)
+ {
+ if ( ( psz == pszKeys
+ || psz[-1] == ' ')
+ && ( psz[cchKey] == ' '
+ || !psz[cchKey])
+ )
+ break;
+ psz = strstr(psz + cchKey, pszKey);
+ }
+ if (psz)
+ {
+ /* remove it */
+ char *pszNext = RTStrStripL(psz + cchKey);
+ if (*pszNext)
+ memmove(psz, pszNext, strlen(pszNext) + 1);
+ else
+ *psz = '\0';
+ psz = RTStrStrip(pszKeys);
+
+ /* update */
+ hrc = pMachine->SetExtraData(Bstr(pszKeyBase), Bstr(psz));
+ }
+
+ RTStrFree(pszKeys);
+ return hrc;
+ }
+ else
+ RTMsgError("Failed to delete key '%s' from '%s', string conversion error %Rrc!",
+ pszKey, pszKeyBase, rc);
+
+ return E_FAIL;
+}
+#endif
+
+
+/**
+ * Sets a key value, does necessary error bitching.
+ *
+ * @returns COM status code.
+ * @param pMachine The Machine object.
+ * @param pszKeyBase The key base.
+ * @param pszKey The key.
+ * @param pszAttribute The attribute name.
+ * @param pszValue The string value.
+ */
+static HRESULT SetString(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, const char *pszValue)
+{
+ HRESULT hrc = pMachine->SetExtraData(BstrFmt("%s/%s/%s", pszKeyBase,
+ pszKey, pszAttribute).raw(),
+ Bstr(pszValue).raw());
+ if (FAILED(hrc))
+ RTMsgError("Failed to set '%s/%s/%s' to '%s'! hrc=%#x",
+ pszKeyBase, pszKey, pszAttribute, pszValue, hrc);
+ return hrc;
+}
+
+
+/**
+ * Sets a key value, does necessary error bitching.
+ *
+ * @returns COM status code.
+ * @param pMachine The Machine object.
+ * @param pszKeyBase The key base.
+ * @param pszKey The key.
+ * @param pszAttribute The attribute name.
+ * @param u64Value The value.
+ */
+static HRESULT SetUInt64(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, uint64_t u64Value)
+{
+ char szValue[64];
+ RTStrPrintf(szValue, sizeof(szValue), "%#RX64", u64Value);
+ return SetString(pMachine, pszKeyBase, pszKey, pszAttribute, szValue);
+}
+
+
+/**
+ * Sets a key value, does necessary error bitching.
+ *
+ * @returns COM status code.
+ * @param pMachine The Machine object.
+ * @param pszKeyBase The key base.
+ * @param pszKey The key.
+ * @param pszAttribute The attribute name.
+ * @param i64Value The value.
+ */
+static HRESULT SetInt64(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, int64_t i64Value)
+{
+ char szValue[64];
+ RTStrPrintf(szValue, sizeof(szValue), "%RI64", i64Value);
+ return SetString(pMachine, pszKeyBase, pszKey, pszAttribute, szValue);
+}
+
+
+/**
+ * Identical to the 'loadsyms' command.
+ */
+static RTEXITCODE CmdLoadSyms(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aSession);
+ HRESULT rc;
+
+ /*
+ * Get the VM
+ */
+ ComPtr<IMachine> machine;
+ CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
+ machine.asOutParam()), RTEXITCODE_FAILURE);
+
+ /*
+ * Parse the command.
+ */
+ const char *pszFilename;
+ int64_t offDelta = 0;
+ const char *pszModule = NULL;
+ uint64_t ModuleAddress = UINT64_MAX;
+ uint64_t ModuleSize = 0;
+
+ /* filename */
+ if (argc < 2)
+ return errorArgument("Missing the filename argument!\n");
+ pszFilename = argv[1];
+
+ /* offDelta */
+ if (argc >= 3)
+ {
+ int irc = RTStrToInt64Ex(argv[2], NULL, 0, &offDelta);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read delta '%s', rc=%Rrc\n", argv[2], rc);
+ }
+
+ /* pszModule */
+ if (argc >= 4)
+ pszModule = argv[3];
+
+ /* ModuleAddress */
+ if (argc >= 5)
+ {
+ int irc = RTStrToUInt64Ex(argv[4], NULL, 0, &ModuleAddress);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read module address '%s', rc=%Rrc\n", argv[4], rc);
+ }
+
+ /* ModuleSize */
+ if (argc >= 6)
+ {
+ int irc = RTStrToUInt64Ex(argv[5], NULL, 0, &ModuleSize);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read module size '%s', rc=%Rrc\n", argv[5], rc);
+ }
+
+ /*
+ * Add extra data.
+ */
+ Utf8Str KeyStr;
+ HRESULT hrc = NewUniqueKey(machine, "VBoxInternal/DBGF/loadsyms", KeyStr);
+ if (SUCCEEDED(hrc))
+ hrc = SetString(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Filename", pszFilename);
+ if (SUCCEEDED(hrc) && argc >= 3)
+ hrc = SetInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Delta", offDelta);
+ if (SUCCEEDED(hrc) && argc >= 4)
+ hrc = SetString(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Module", pszModule);
+ if (SUCCEEDED(hrc) && argc >= 5)
+ hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "ModuleAddress", ModuleAddress);
+ if (SUCCEEDED(hrc) && argc >= 6)
+ hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "ModuleSize", ModuleSize);
+
+ return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Identical to the 'loadmap' command.
+ */
+static RTEXITCODE CmdLoadMap(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aSession);
+ HRESULT rc;
+
+ /*
+ * Get the VM
+ */
+ ComPtr<IMachine> machine;
+ CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
+ machine.asOutParam()), RTEXITCODE_FAILURE);
+
+ /*
+ * Parse the command.
+ */
+ const char *pszFilename;
+ uint64_t ModuleAddress = UINT64_MAX;
+ const char *pszModule = NULL;
+ uint64_t offSubtrahend = 0;
+ uint32_t iSeg = UINT32_MAX;
+
+ /* filename */
+ if (argc < 2)
+ return errorArgument("Missing the filename argument!\n");
+ pszFilename = argv[1];
+
+ /* address */
+ if (argc < 3)
+ return errorArgument("Missing the module address argument!\n");
+ int irc = RTStrToUInt64Ex(argv[2], NULL, 0, &ModuleAddress);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read module address '%s', rc=%Rrc\n", argv[2], rc);
+
+ /* name (optional) */
+ if (argc > 3)
+ pszModule = argv[3];
+
+ /* subtrahend (optional) */
+ if (argc > 4)
+ {
+ irc = RTStrToUInt64Ex(argv[4], NULL, 0, &offSubtrahend);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read subtrahend '%s', rc=%Rrc\n", argv[4], rc);
+ }
+
+ /* segment (optional) */
+ if (argc > 5)
+ {
+ irc = RTStrToUInt32Ex(argv[5], NULL, 0, &iSeg);
+ if (RT_FAILURE(irc))
+ return errorArgument(argv[0], "Failed to read segment number '%s', rc=%Rrc\n", argv[5], rc);
+ }
+
+ /*
+ * Add extra data.
+ */
+ Utf8Str KeyStr;
+ HRESULT hrc = NewUniqueKey(machine, "VBoxInternal/DBGF/loadmap", KeyStr);
+ if (SUCCEEDED(hrc))
+ hrc = SetString(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Filename", pszFilename);
+ if (SUCCEEDED(hrc))
+ hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Address", ModuleAddress);
+ if (SUCCEEDED(hrc) && pszModule != NULL)
+ hrc = SetString(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Name", pszModule);
+ if (SUCCEEDED(hrc) && offSubtrahend != 0)
+ hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Subtrahend", offSubtrahend);
+ if (SUCCEEDED(hrc) && iSeg != UINT32_MAX)
+ hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Segment", iSeg);
+
+ return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
+{
+ RT_NOREF(pvUser);
+ RTMsgErrorV(pszFormat, va);
+ RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS);
+}
+
+static DECLCALLBACK(int) handleVDMessage(void *pvUser, const char *pszFormat, va_list va)
+{
+ NOREF(pvUser);
+ return RTPrintfV(pszFormat, va);
+}
+
+static RTEXITCODE CmdSetHDUUID(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Guid uuid;
+ RTUUID rtuuid;
+ enum eUuidType {
+ HDUUID,
+ HDPARENTUUID
+ } uuidType;
+
+ if (!strcmp(argv[0], "sethduuid"))
+ {
+ uuidType = HDUUID;
+ if (argc != 3 && argc != 2)
+ return errorSyntax(USAGE_SETHDUUID, "Not enough parameters");
+ /* if specified, take UUID, otherwise generate a new one */
+ if (argc == 3)
+ {
+ if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
+ return errorSyntax(USAGE_SETHDUUID, "Invalid UUID parameter");
+ uuid = argv[2];
+ } else
+ uuid.create();
+ }
+ else if (!strcmp(argv[0], "sethdparentuuid"))
+ {
+ uuidType = HDPARENTUUID;
+ if (argc != 3)
+ return errorSyntax(USAGE_SETHDPARENTUUID, "Not enough parameters");
+ if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
+ return errorSyntax(USAGE_SETHDPARENTUUID, "Invalid UUID parameter");
+ uuid = argv[2];
+ }
+ else
+ return errorSyntax(USAGE_SETHDUUID, "Invalid invocation");
+
+ /* just try it */
+ char *pszFormat = NULL;
+ VDTYPE enmType = VDTYPE_INVALID;
+ int rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+ argv[1], &pszFormat, &enmType);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Format autodetect failed: %Rrc", rc);
+
+ PVDISK pDisk = NULL;
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(rc);
+
+ rc = VDCreate(pVDIfs, enmType, &pDisk);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create the virtual disk container: %Rrc", rc);
+
+ /* Open the image */
+ rc = VDOpen(pDisk, pszFormat, argv[1], VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_INFO, NULL);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open the image: %Rrc", rc);
+
+ if (uuidType == HDUUID)
+ rc = VDSetUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
+ else
+ rc = VDSetParentUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
+ if (RT_FAILURE(rc))
+ RTMsgError("Cannot set a new UUID: %Rrc", rc);
+ else
+ RTPrintf("UUID changed to: %s\n", uuid.toString().c_str());
+
+ VDCloseAll(pDisk);
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+static RTEXITCODE CmdDumpHDInfo(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+
+ /* we need exactly one parameter: the image file */
+ if (argc != 1)
+ {
+ return errorSyntax(USAGE_DUMPHDINFO, "Not enough parameters");
+ }
+
+ /* just try it */
+ char *pszFormat = NULL;
+ VDTYPE enmType = VDTYPE_INVALID;
+ int rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+ argv[0], &pszFormat, &enmType);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Format autodetect failed: %Rrc", rc);
+
+ PVDISK pDisk = NULL;
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(rc);
+
+ rc = VDCreate(pVDIfs, enmType, &pDisk);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create the virtual disk container: %Rrc", rc);
+
+ /* Open the image */
+ rc = VDOpen(pDisk, pszFormat, argv[0], VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO, NULL);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open the image: %Rrc", rc);
+
+ VDDumpImages(pDisk);
+
+ VDCloseAll(pDisk);
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static int partRead(RTFILE File, PHOSTPARTITIONS pPart)
+{
+ uint8_t aBuffer[512];
+ uint8_t partitionTableHeader[512];
+ uint32_t sector_size = 512;
+ uint64_t lastUsableLBA = 0;
+ int rc;
+
+ VDISKPARTTYPE partitioningType;
+
+ pPart->cPartitions = 0;
+ memset(pPart->aPartitions, '\0', sizeof(pPart->aPartitions));
+
+ rc = RTFileReadAt(File, 0, &aBuffer, sizeof(aBuffer), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (aBuffer[450] == 0xEE)/* check the sign of the GPT disk*/
+ {
+ partitioningType = GPT;
+ pPart->uPartitioningType = GPT;//partitioningType;
+
+ if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
+ return VERR_INVALID_PARAMETER;
+
+ rc = RTFileReadAt(File, sector_size, &partitionTableHeader, sector_size, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo r=bird: This is a 64-bit magic value, right... */
+ const char *l_ppth = (char *)partitionTableHeader;
+ if (strncmp(l_ppth, "EFI PART", 8))
+ return VERR_INVALID_PARAMETER;
+
+ /** @todo check GPT Version */
+
+ /** @todo r=bird: C have this handy concept called structures which
+ * greatly simplify data access... (Someone is really lazy here!) */
+#if 0 /* unused */
+ uint64_t firstUsableLBA = RT_MAKE_U64_FROM_U8(partitionTableHeader[40],
+ partitionTableHeader[41],
+ partitionTableHeader[42],
+ partitionTableHeader[43],
+ partitionTableHeader[44],
+ partitionTableHeader[45],
+ partitionTableHeader[46],
+ partitionTableHeader[47]
+ );
+#endif
+ lastUsableLBA = RT_MAKE_U64_FROM_U8(partitionTableHeader[48],
+ partitionTableHeader[49],
+ partitionTableHeader[50],
+ partitionTableHeader[51],
+ partitionTableHeader[52],
+ partitionTableHeader[53],
+ partitionTableHeader[54],
+ partitionTableHeader[55]
+ );
+ uint32_t partitionsNumber = RT_MAKE_U32_FROM_U8(partitionTableHeader[80],
+ partitionTableHeader[81],
+ partitionTableHeader[82],
+ partitionTableHeader[83]
+ );
+ uint32_t partitionEntrySize = RT_MAKE_U32_FROM_U8(partitionTableHeader[84],
+ partitionTableHeader[85],
+ partitionTableHeader[86],
+ partitionTableHeader[87]
+ );
+
+ uint32_t currentEntry = 0;
+
+ if (partitionEntrySize * partitionsNumber > 4 * _1M)
+ {
+ RTMsgError("The GPT header seems corrupt because it contains too many entries");
+ return VERR_INVALID_PARAMETER;
+ }
+
+ uint8_t *pbPartTable = (uint8_t *)RTMemAllocZ(RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512));
+ if (!pbPartTable)
+ {
+ RTMsgError("Allocating memory for the GPT partitions entries failed");
+ return VERR_NO_MEMORY;
+ }
+
+ /* partition entries begin from LBA2 */
+ /** @todo r=aeichner: Reading from LBA 2 is not always correct, the header will contain the starting LBA. */
+ rc = RTFileReadAt(File, 1024, pbPartTable, RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512), NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Reading the partition table failed");
+ RTMemFree(pbPartTable);
+ return rc;
+ }
+
+ while (currentEntry < partitionsNumber)
+ {
+ uint8_t *partitionEntry = pbPartTable + currentEntry * partitionEntrySize;
+
+ uint64_t start = RT_MAKE_U64_FROM_U8(partitionEntry[32], partitionEntry[33], partitionEntry[34], partitionEntry[35],
+ partitionEntry[36], partitionEntry[37], partitionEntry[38], partitionEntry[39]);
+ uint64_t end = RT_MAKE_U64_FROM_U8(partitionEntry[40], partitionEntry[41], partitionEntry[42], partitionEntry[43],
+ partitionEntry[44], partitionEntry[45], partitionEntry[46], partitionEntry[47]);
+
+ PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
+ pCP->uIndex = currentEntry + 1;
+ pCP->uIndexWin = currentEntry + 1;
+ pCP->uType = 0;
+ pCP->uStartCylinder = 0;
+ pCP->uStartHead = 0;
+ pCP->uStartSector = 0;
+ pCP->uEndCylinder = 0;
+ pCP->uEndHead = 0;
+ pCP->uEndSector = 0;
+ pCP->uPartDataStart = 0; /* will be filled out later properly. */
+ pCP->cPartDataSectors = 0;
+ if (start==0 || end==0)
+ {
+ pCP->uIndex = 0;
+ pCP->uIndexWin = 0;
+ --pPart->cPartitions;
+ break;
+ }
+ else
+ {
+ pCP->uStart = start;
+ pCP->uSize = (end +1) - start;/*+1 LBA because the last address is included*/
+ }
+
+ ++currentEntry;
+ }
+
+ RTMemFree(pbPartTable);
+ }
+ }
+ else
+ {
+ partitioningType = MBR;
+ pPart->uPartitioningType = MBR;//partitioningType;
+
+ if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
+ return VERR_INVALID_PARAMETER;
+
+ unsigned uExtended = (unsigned)-1;
+ unsigned uIndexWin = 1;
+
+ for (unsigned i = 0; i < 4; i++)
+ {
+ uint8_t *p = &aBuffer[0x1be + i * 16];
+ if (p[4] == 0)
+ continue;
+ PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
+ pCP->uIndex = i + 1;
+ pCP->uType = p[4];
+ pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
+ pCP->uStartHead = p[1];
+ pCP->uStartSector = p[2] & 0x3f;
+ pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
+ pCP->uEndHead = p[5];
+ pCP->uEndSector = p[6] & 0x3f;
+ pCP->uStart = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
+ pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
+ pCP->uPartDataStart = 0; /* will be filled out later properly. */
+ pCP->cPartDataSectors = 0;
+
+ if (PARTTYPE_IS_EXTENDED(p[4]))
+ {
+ if (uExtended == (unsigned)-1)
+ {
+ uExtended = (unsigned)(pCP - pPart->aPartitions);
+ pCP->uIndexWin = 0;
+ }
+ else
+ {
+ RTMsgError("More than one extended partition");
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ pCP->uIndexWin = uIndexWin;
+ uIndexWin++;
+ }
+ }
+
+ if (uExtended != (unsigned)-1)
+ {
+ unsigned uIndex = 5;
+ uint64_t uStart = pPart->aPartitions[uExtended].uStart;
+ uint64_t uOffset = 0;
+ if (!uStart)
+ {
+ RTMsgError("Inconsistency for logical partition start");
+ return VERR_INVALID_PARAMETER;
+ }
+
+ do
+ {
+ rc = RTFileReadAt(File, (uStart + uOffset) * 512, &aBuffer, sizeof(aBuffer), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
+ {
+ RTMsgError("Logical partition without magic");
+ return VERR_INVALID_PARAMETER;
+ }
+ uint8_t *p = &aBuffer[0x1be];
+
+ if (p[4] == 0)
+ {
+ RTMsgError("Logical partition with type 0 encountered");
+ return VERR_INVALID_PARAMETER;
+ }
+
+ PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
+ pCP->uIndex = uIndex;
+ pCP->uIndexWin = uIndexWin;
+ pCP->uType = p[4];
+ pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
+ pCP->uStartHead = p[1];
+ pCP->uStartSector = p[2] & 0x3f;
+ pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
+ pCP->uEndHead = p[5];
+ pCP->uEndSector = p[6] & 0x3f;
+ uint32_t uStartOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
+ if (!uStartOffset)
+ {
+ RTMsgError("Invalid partition start offset");
+ return VERR_INVALID_PARAMETER;
+ }
+ pCP->uStart = uStart + uOffset + uStartOffset;
+ pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
+ /* Fill out partitioning location info for EBR. */
+ pCP->uPartDataStart = uStart + uOffset;
+ pCP->cPartDataSectors = uStartOffset;
+ p += 16;
+ if (p[4] == 0)
+ uExtended = (unsigned)-1;
+ else if (PARTTYPE_IS_EXTENDED(p[4]))
+ {
+ uExtended = uIndex;
+ uIndex++;
+ uIndexWin++;
+ uOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
+ }
+ else
+ {
+ RTMsgError("Logical partition chain broken");
+ return VERR_INVALID_PARAMETER;
+ }
+ } while (uExtended != (unsigned)-1);
+ }
+ }
+
+
+ /* Sort partitions in ascending order of start sector, plus a trivial
+ * bit of consistency checking. */
+ for (unsigned i = 0; i < pPart->cPartitions-1; i++)
+ {
+ unsigned uMinIdx = i;
+ uint64_t uMinVal = pPart->aPartitions[i].uStart;
+ for (unsigned j = i + 1; j < pPart->cPartitions; j++)
+ {
+ if (pPart->aPartitions[j].uStart < uMinVal)
+ {
+ uMinIdx = j;
+ uMinVal = pPart->aPartitions[j].uStart;
+ }
+ else if (pPart->aPartitions[j].uStart == uMinVal)
+ {
+ RTMsgError("Two partitions start at the same place");
+ return VERR_INVALID_PARAMETER;
+ }
+ else if (pPart->aPartitions[j].uStart == 0)
+ {
+ RTMsgError("Partition starts at sector 0");
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+ if (uMinIdx != i)
+ {
+ /* Swap entries at index i and uMinIdx. */
+ memcpy(&pPart->aPartitions[pPart->cPartitions],
+ &pPart->aPartitions[i], sizeof(HOSTPARTITION));
+ memcpy(&pPart->aPartitions[i],
+ &pPart->aPartitions[uMinIdx], sizeof(HOSTPARTITION));
+ memcpy(&pPart->aPartitions[uMinIdx],
+ &pPart->aPartitions[pPart->cPartitions], sizeof(HOSTPARTITION));
+ }
+ }
+
+ /* Fill out partitioning location info for MBR or GPT. */
+ pPart->aPartitions[0].uPartDataStart = 0;
+ pPart->aPartitions[0].cPartDataSectors = pPart->aPartitions[0].uStart;
+
+ /* Fill out partitioning location info for backup GPT. */
+ if (partitioningType == GPT)
+ {
+ pPart->aPartitions[pPart->cPartitions-1].uPartDataStart = lastUsableLBA+1;
+ pPart->aPartitions[pPart->cPartitions-1].cPartDataSectors = 33;
+
+ /* Now do a some partition table consistency checking, to reject the most
+ * obvious garbage which can lead to trouble later. */
+ uint64_t uPrevEnd = 0;
+ for (unsigned i = 0; i < pPart->cPartitions; i++)
+ {
+ if (pPart->aPartitions[i].cPartDataSectors)
+ uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
+ if (pPart->aPartitions[i].uStart < uPrevEnd &&
+ pPart->cPartitions-1 != i)
+ {
+ RTMsgError("Overlapping GPT partitions");
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+ }
+ else
+ {
+ /* Now do a some partition table consistency checking, to reject the most
+ * obvious garbage which can lead to trouble later. */
+ uint64_t uPrevEnd = 0;
+ for (unsigned i = 0; i < pPart->cPartitions; i++)
+ {
+ if (pPart->aPartitions[i].cPartDataSectors)
+ uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
+ if (pPart->aPartitions[i].uStart < uPrevEnd)
+ {
+ RTMsgError("Overlapping MBR partitions");
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!PARTTYPE_IS_EXTENDED(pPart->aPartitions[i].uType))
+ uPrevEnd = pPart->aPartitions[i].uStart + pPart->aPartitions[i].uSize;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+static RTEXITCODE CmdListPartitions(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Utf8Str rawdisk;
+
+ /* let's have a closer look at the arguments */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-rawdisk") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ rawdisk = argv[i];
+ }
+ else
+ {
+ return errorSyntax(USAGE_LISTPARTITIONS, "Invalid parameter '%s'", argv[i]);
+ }
+ }
+
+ if (rawdisk.isEmpty())
+ return errorSyntax(USAGE_LISTPARTITIONS, "Mandatory parameter -rawdisk missing");
+
+ RTFILE hRawFile;
+ int vrc = RTFileOpen(&hRawFile, rawdisk.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open the raw disk: %Rrc", vrc);
+
+ HOSTPARTITIONS partitions;
+ vrc = partRead(hRawFile, &partitions);
+ /* Don't bail out on errors, print the table and return the result code. */
+
+ RTPrintf("Number Type StartCHS EndCHS Size (MiB) Start (Sect)\n");
+ for (unsigned i = 0; i < partitions.cPartitions; i++)
+ {
+ /* Don't show the extended partition, otherwise users might think they
+ * can add it to the list of partitions for raw partition access. */
+ if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
+ continue;
+
+ RTPrintf("%-7u %#04x %-4u/%-3u/%-2u %-4u/%-3u/%-2u %10llu %10llu\n",
+ partitions.aPartitions[i].uIndex,
+ partitions.aPartitions[i].uType,
+ partitions.aPartitions[i].uStartCylinder,
+ partitions.aPartitions[i].uStartHead,
+ partitions.aPartitions[i].uStartSector,
+ partitions.aPartitions[i].uEndCylinder,
+ partitions.aPartitions[i].uEndHead,
+ partitions.aPartitions[i].uEndSector,
+ partitions.aPartitions[i].uSize / 2048,
+ partitions.aPartitions[i].uStart);
+ }
+
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static PVDISKRAWPARTDESC appendPartDesc(uint32_t *pcPartDescs, PVDISKRAWPARTDESC *ppPartDescs)
+{
+ (*pcPartDescs)++;
+ PVDISKRAWPARTDESC p;
+ p = (PVDISKRAWPARTDESC)RTMemRealloc(*ppPartDescs,
+ *pcPartDescs * sizeof(VDISKRAWPARTDESC));
+ *ppPartDescs = p;
+ if (p)
+ {
+ p = p + *pcPartDescs - 1;
+ memset(p, '\0', sizeof(VDISKRAWPARTDESC));
+ }
+
+ return p;
+}
+
+static RTEXITCODE CmdCreateRawVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ HRESULT rc = S_OK;
+ Utf8Str filename;
+ const char *pszMBRFilename = NULL;
+ Utf8Str rawdisk;
+ const char *pszPartitions = NULL;
+ bool fRelative = false;
+
+ uint64_t cbSize = 0;
+ PVDISK pDisk = NULL;
+ VDISKRAW RawDescriptor;
+ PVDINTERFACE pVDIfs = NULL;
+
+ /* let's have a closer look at the arguments */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-filename") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ filename = argv[i];
+ }
+ else if (strcmp(argv[i], "-mbr") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ pszMBRFilename = argv[i];
+ }
+ else if (strcmp(argv[i], "-rawdisk") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ rawdisk = argv[i];
+ }
+ else if (strcmp(argv[i], "-partitions") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ pszPartitions = argv[i];
+ }
+#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_WINDOWS)
+ else if (strcmp(argv[i], "-relative") == 0)
+ {
+ fRelative = true;
+ }
+#endif /* RT_OS_LINUX || RT_OS_FREEBSD */
+ else
+ return errorSyntax(USAGE_CREATERAWVMDK, "Invalid parameter '%s'", argv[i]);
+ }
+
+ if (filename.isEmpty())
+ return errorSyntax(USAGE_CREATERAWVMDK, "Mandatory parameter -filename missing");
+ if (rawdisk.isEmpty())
+ return errorSyntax(USAGE_CREATERAWVMDK, "Mandatory parameter -rawdisk missing");
+ if (!pszPartitions && pszMBRFilename)
+ return errorSyntax(USAGE_CREATERAWVMDK, "The parameter -mbr is only valid when the parameter -partitions is also present");
+
+#ifdef RT_OS_DARWIN
+ fRelative = true;
+#endif /* RT_OS_DARWIN */
+ RTFILE hRawFile;
+ int vrc = RTFileOpen(&hRawFile, rawdisk.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot open the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+
+#ifdef RT_OS_WINDOWS
+ /* Windows NT has no IOCTL_DISK_GET_LENGTH_INFORMATION ioctl. This was
+ * added to Windows XP, so we have to use the available info from DriveGeo.
+ * Note that we cannot simply use IOCTL_DISK_GET_DRIVE_GEOMETRY as it
+ * yields a slightly different result than IOCTL_DISK_GET_LENGTH_INFO.
+ * We call IOCTL_DISK_GET_DRIVE_GEOMETRY first as we need to check the media
+ * type anyway, and if IOCTL_DISK_GET_LENGTH_INFORMATION is supported
+ * we will later override cbSize.
+ */
+ DISK_GEOMETRY DriveGeo;
+ DWORD cbDriveGeo;
+ if (DeviceIoControl((HANDLE)RTFileToNative(hRawFile),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
+ &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
+ {
+ if ( DriveGeo.MediaType == FixedMedia
+ || DriveGeo.MediaType == RemovableMedia)
+ {
+ cbSize = DriveGeo.Cylinders.QuadPart
+ * DriveGeo.TracksPerCylinder
+ * DriveGeo.SectorsPerTrack
+ * DriveGeo.BytesPerSector;
+ }
+ else
+ {
+ RTMsgError("File '%s' is no fixed/removable medium device", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+
+ GET_LENGTH_INFORMATION DiskLenInfo;
+ DWORD junk;
+ if (DeviceIoControl((HANDLE)RTFileToNative(hRawFile),
+ IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
+ &DiskLenInfo, sizeof(DiskLenInfo), &junk, (LPOVERLAPPED)NULL))
+ {
+ /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */
+ cbSize = DiskLenInfo.Length.QuadPart;
+ }
+ if ( fRelative
+ && !rawdisk.startsWith("\\\\.\\PhysicalDrive", Utf8Str::CaseInsensitive))
+ {
+ RTMsgError("The -relative parameter is invalid for raw disk %s", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ /*
+ * Could be raw image, remember error code and try to get the size first
+ * before failing.
+ */
+ vrc = RTErrConvertFromWin32(GetLastError());
+ if (RT_FAILURE(RTFileGetSize(hRawFile, &cbSize)))
+ {
+ RTMsgError("Cannot get the geometry of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ else
+ {
+ if (fRelative)
+ {
+ RTMsgError("The -relative parameter is invalid for raw images");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ vrc = VINF_SUCCESS;
+ }
+ }
+#elif defined(RT_OS_LINUX)
+ struct stat DevStat;
+ if (!fstat(RTFileToNative(hRawFile), &DevStat))
+ {
+ if (S_ISBLK(DevStat.st_mode))
+ {
+#ifdef BLKGETSIZE64
+ /* BLKGETSIZE64 is broken up to 2.4.17 and in many 2.5.x. In 2.6.0
+ * it works without problems. */
+ struct utsname utsname;
+ if ( uname(&utsname) == 0
+ && ( (strncmp(utsname.release, "2.5.", 4) == 0 && atoi(&utsname.release[4]) >= 18)
+ || (strncmp(utsname.release, "2.", 2) == 0 && atoi(&utsname.release[2]) >= 6)))
+ {
+ uint64_t cbBlk;
+ if (!ioctl(RTFileToNative(hRawFile), BLKGETSIZE64, &cbBlk))
+ cbSize = cbBlk;
+ }
+#endif /* BLKGETSIZE64 */
+ if (!cbSize)
+ {
+ long cBlocks;
+ if (!ioctl(RTFileToNative(hRawFile), BLKGETSIZE, &cBlocks))
+ cbSize = (uint64_t)cBlocks << 9;
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ }
+ else if (S_ISREG(DevStat.st_mode))
+ {
+ vrc = RTFileGetSize(hRawFile, &cbSize);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Failed to get size of file '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ else if (fRelative)
+ {
+ RTMsgError("The -relative parameter is invalid for raw images");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ RTMsgError("File '%s' is no block device", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
+ rawdisk.c_str(), vrc);
+ }
+#elif defined(RT_OS_DARWIN)
+ struct stat DevStat;
+ if (!fstat(RTFileToNative(hRawFile), &DevStat))
+ {
+ if (S_ISBLK(DevStat.st_mode))
+ {
+ uint64_t cBlocks;
+ uint32_t cbBlock;
+ if (!ioctl(RTFileToNative(hRawFile), DKIOCGETBLOCKCOUNT, &cBlocks))
+ {
+ if (!ioctl(RTFileToNative(hRawFile), DKIOCGETBLOCKSIZE, &cbBlock))
+ cbSize = cBlocks * cbBlock;
+ else
+ {
+ RTMsgError("Cannot get the block size for file '%s': %Rrc", rawdisk.c_str(), vrc);
+ vrc = RTErrConvertFromErrno(errno);
+ goto out;
+ }
+ }
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Cannot get the block count for file '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ else if (S_ISREG(DevStat.st_mode))
+ {
+ fRelative = false; /* Must be false for raw image files. */
+ vrc = RTFileGetSize(hRawFile, &cbSize);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Failed to get size of file '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ else
+ {
+ RTMsgError("File '%s' is neither block device nor regular file", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
+ rawdisk.c_str(), vrc);
+ }
+#elif defined(RT_OS_SOLARIS)
+ struct stat DevStat;
+ if (!fstat(RTFileToNative(hRawFile), &DevStat))
+ {
+ if (S_ISBLK(DevStat.st_mode) || S_ISCHR(DevStat.st_mode))
+ {
+ struct dk_minfo mediainfo;
+ if (!ioctl(RTFileToNative(hRawFile), DKIOCGMEDIAINFO, &mediainfo))
+ cbSize = mediainfo.dki_capacity * mediainfo.dki_lbsize;
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ else if (S_ISREG(DevStat.st_mode))
+ {
+ vrc = RTFileGetSize(hRawFile, &cbSize);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Failed to get size of file '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ else
+ {
+ RTMsgError("File '%s' is no block or char device", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
+ rawdisk.c_str(), vrc);
+ }
+#elif defined(RT_OS_FREEBSD)
+ struct stat DevStat;
+ if (!fstat(RTFileToNative(hRawFile), &DevStat))
+ {
+ if (S_ISCHR(DevStat.st_mode))
+ {
+ off_t cbMedia = 0;
+ if (!ioctl(RTFileToNative(hRawFile), DIOCGMEDIASIZE, &cbMedia))
+ cbSize = cbMedia;
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Cannot get the block count for file '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ }
+ else if (S_ISREG(DevStat.st_mode))
+ {
+ if (fRelative)
+ {
+ RTMsgError("The -relative parameter is invalid for raw images");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ cbSize = DevStat.st_size;
+ }
+ else
+ {
+ RTMsgError("File '%s' is neither character device nor regular file", rawdisk.c_str());
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ else
+ {
+ vrc = RTErrConvertFromErrno(errno);
+ RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
+ rawdisk.c_str(), vrc);
+ }
+#else /* all unrecognized OSes */
+ /* Hopefully this works on all other hosts. If it doesn't, it'll just fail
+ * creating the VMDK, so no real harm done. */
+ vrc = RTFileGetSize(hRawFile, &cbSize);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+#endif
+
+ /* Check whether cbSize is actually sensible. */
+ if (!cbSize || cbSize % 512)
+ {
+ RTMsgError("Detected size of raw disk '%s' is %RU64, an invalid value", rawdisk.c_str(), cbSize);
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+
+ RawDescriptor.szSignature[0] = 'R';
+ RawDescriptor.szSignature[1] = 'A';
+ RawDescriptor.szSignature[2] = 'W';
+ RawDescriptor.szSignature[3] = '\0';
+ if (!pszPartitions)
+ {
+ RawDescriptor.uFlags = VDISKRAW_DISK;
+ RawDescriptor.pszRawDisk = rawdisk.c_str();
+ }
+ else
+ {
+ RawDescriptor.uFlags = VDISKRAW_NORMAL;
+ RawDescriptor.pszRawDisk = NULL;
+ RawDescriptor.cPartDescs = 0;
+ RawDescriptor.pPartDescs = NULL;
+
+ uint32_t uPartitions = 0;
+ uint32_t uPartitionsRO = 0;
+
+ const char *p = pszPartitions;
+ char *pszNext;
+ uint32_t u32;
+ while (*p != '\0')
+ {
+ vrc = RTStrToUInt32Ex(p, &pszNext, 0, &u32);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Incorrect value in partitions parameter");
+ goto out;
+ }
+ uPartitions |= RT_BIT(u32);
+ p = pszNext;
+ if (*p == 'r')
+ {
+ uPartitionsRO |= RT_BIT(u32);
+ p++;
+ }
+ if (*p == ',')
+ p++;
+ else if (*p != '\0')
+ {
+ RTMsgError("Incorrect separator in partitions parameter");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+
+ HOSTPARTITIONS partitions;
+ vrc = partRead(hRawFile, &partitions);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot read the partition information from '%s'", rawdisk.c_str());
+ goto out;
+ }
+
+ RawDescriptor.uPartitioningType = partitions.uPartitioningType;
+
+ for (unsigned i = 0; i < partitions.cPartitions; i++)
+ {
+ if ( uPartitions & RT_BIT(partitions.aPartitions[i].uIndex)
+ && PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
+ {
+ /* Some ignorant user specified an extended partition.
+ * Bad idea, as this would trigger an overlapping
+ * partitions error later during VMDK creation. So warn
+ * here and ignore what the user requested. */
+ RTMsgWarning("It is not possible (and necessary) to explicitly give access to the "
+ "extended partition %u. If required, enable access to all logical "
+ "partitions inside this extended partition.",
+ partitions.aPartitions[i].uIndex);
+ uPartitions &= ~RT_BIT(partitions.aPartitions[i].uIndex);
+ }
+ }
+
+ for (unsigned i = 0; i < partitions.cPartitions; i++)
+ {
+ PVDISKRAWPARTDESC pPartDesc = NULL;
+
+ /* first dump the MBR/EPT data area */
+ if (partitions.aPartitions[i].cPartDataSectors)
+ {
+ pPartDesc = appendPartDesc(&RawDescriptor.cPartDescs,
+ &RawDescriptor.pPartDescs);
+ if (!pPartDesc)
+ {
+ RTMsgError("Out of memory allocating the partition list for '%s'", rawdisk.c_str());
+ vrc = VERR_NO_MEMORY;
+ goto out;
+ }
+
+ /** @todo the clipping below isn't 100% accurate, as it should
+ * actually clip to the track size. However, that's easier said
+ * than done as figuring out the track size is heuristics. In
+ * any case the clipping is adjusted later after sorting, to
+ * prevent overlapping data areas on the resulting image. */
+ pPartDesc->cbData = RT_MIN(partitions.aPartitions[i].cPartDataSectors, 63) * 512;
+ pPartDesc->uStart = partitions.aPartitions[i].uPartDataStart * 512;
+ Assert(pPartDesc->cbData - (size_t)pPartDesc->cbData == 0);
+ void *pPartData = RTMemAlloc((size_t)pPartDesc->cbData);
+ if (!pPartData)
+ {
+ RTMsgError("Out of memory allocating the partition descriptor for '%s'", rawdisk.c_str());
+ vrc = VERR_NO_MEMORY;
+ goto out;
+ }
+ vrc = RTFileReadAt(hRawFile, partitions.aPartitions[i].uPartDataStart * 512,
+ pPartData, (size_t)pPartDesc->cbData, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot read partition data from raw device '%s': %Rrc", rawdisk.c_str(), vrc);
+ goto out;
+ }
+ /* Splice in the replacement MBR code if specified. */
+ if ( partitions.aPartitions[i].uPartDataStart == 0
+ && pszMBRFilename)
+ {
+ RTFILE MBRFile;
+ vrc = RTFileOpen(&MBRFile, pszMBRFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot open replacement MBR file '%s' specified with -mbr: %Rrc", pszMBRFilename, vrc);
+ goto out;
+ }
+ vrc = RTFileReadAt(MBRFile, 0, pPartData, 0x1be, NULL);
+ RTFileClose(MBRFile);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot read replacement MBR file '%s': %Rrc", pszMBRFilename, vrc);
+ goto out;
+ }
+ }
+ pPartDesc->pvPartitionData = pPartData;
+ }
+
+ if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
+ {
+ /* Suppress exporting the actual extended partition. Only
+ * logical partitions should be processed. However completely
+ * ignoring it leads to leaving out the EBR data. */
+ continue;
+ }
+
+ /* set up values for non-relative device names */
+ const char *pszRawName = rawdisk.c_str();
+ uint64_t uStartOffset = partitions.aPartitions[i].uStart * 512;
+
+ pPartDesc = appendPartDesc(&RawDescriptor.cPartDescs,
+ &RawDescriptor.pPartDescs);
+ if (!pPartDesc)
+ {
+ RTMsgError("Out of memory allocating the partition list for '%s'", rawdisk.c_str());
+ vrc = VERR_NO_MEMORY;
+ goto out;
+ }
+
+ if (uPartitions & RT_BIT(partitions.aPartitions[i].uIndex))
+ {
+ if (uPartitionsRO & RT_BIT(partitions.aPartitions[i].uIndex))
+ pPartDesc->uFlags |= VDISKRAW_READONLY;
+
+ if (fRelative)
+ {
+#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+ /* Refer to the correct partition and use offset 0. */
+ char *psz;
+#if defined(RT_OS_LINUX)
+ /*
+ * Check whether raw disk ends with a digit. In that case
+ * insert a p before adding the partition number.
+ * This is used for nvme devices only currently which look like
+ * /dev/nvme0n1p1 but might be extended to other devices in the
+ * future.
+ */
+ size_t cchRawDisk = rawdisk.length();
+ if (RT_C_IS_DIGIT(pszRawName[cchRawDisk - 1]))
+ RTStrAPrintf(&psz,
+ "%sp%u",
+ rawdisk.c_str(),
+ partitions.aPartitions[i].uIndex);
+ else
+ RTStrAPrintf(&psz,
+ "%s%u",
+ rawdisk.c_str(),
+ partitions.aPartitions[i].uIndex);
+#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+ RTStrAPrintf(&psz,
+ "%ss%u",
+ rawdisk.c_str(),
+ partitions.aPartitions[i].uIndex);
+#endif
+ if (!psz)
+ {
+ vrc = VERR_NO_STR_MEMORY;
+ RTMsgError("Cannot create reference to individual partition %u, rc=%Rrc",
+ partitions.aPartitions[i].uIndex, vrc);
+ goto out;
+ }
+ pszRawName = psz;
+ uStartOffset = 0;
+#elif defined(RT_OS_WINDOWS)
+ /* Refer to the correct partition and use offset 0. */
+ char *psz;
+ RTStrAPrintf(&psz, "\\\\.\\Harddisk%sPartition%u",
+ rawdisk.c_str() + 17,
+ partitions.aPartitions[i].uIndexWin);
+ if (!psz)
+ {
+ vrc = VERR_NO_STR_MEMORY;
+ RTMsgError("Cannot create reference to individual partition %u (numbered %u), rc=%Rrc",
+ partitions.aPartitions[i].uIndex, partitions.aPartitions[i].uIndexWin, vrc);
+ goto out;
+ }
+ pszRawName = psz;
+ uStartOffset = 0;
+#else
+ /** @todo not implemented for other hosts. Treat just like
+ * not specified (this code is actually never reached). */
+#endif
+ }
+
+ pPartDesc->pszRawDevice = pszRawName;
+ pPartDesc->uStartOffset = uStartOffset;
+ }
+ else
+ {
+ pPartDesc->pszRawDevice = NULL;
+ pPartDesc->uStartOffset = 0;
+ }
+
+ pPartDesc->uStart = partitions.aPartitions[i].uStart * 512;
+ pPartDesc->cbData = partitions.aPartitions[i].uSize * 512;
+ }
+
+ /* Sort data areas in ascending order of start. */
+ for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
+ {
+ unsigned uMinIdx = i;
+ uint64_t uMinVal = RawDescriptor.pPartDescs[i].uStart;
+ for (unsigned j = i + 1; j < RawDescriptor.cPartDescs; j++)
+ {
+ if (RawDescriptor.pPartDescs[j].uStart < uMinVal)
+ {
+ uMinIdx = j;
+ uMinVal = RawDescriptor.pPartDescs[j].uStart;
+ }
+ }
+ if (uMinIdx != i)
+ {
+ /* Swap entries at index i and uMinIdx. */
+ VDISKRAWPARTDESC tmp;
+ memcpy(&tmp, &RawDescriptor.pPartDescs[i], sizeof(tmp));
+ memcpy(&RawDescriptor.pPartDescs[i], &RawDescriptor.pPartDescs[uMinIdx], sizeof(tmp));
+ memcpy(&RawDescriptor.pPartDescs[uMinIdx], &tmp, sizeof(tmp));
+ }
+ }
+
+ /* Have a second go at MBR/EPT, GPT area clipping. Now that the data areas
+ * are sorted this is much easier to get 100% right. */
+ //for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
+ for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
+ {
+ if (RawDescriptor.pPartDescs[i].pvPartitionData)
+ {
+ RawDescriptor.pPartDescs[i].cbData = RT_MIN(RawDescriptor.pPartDescs[i+1].uStart - RawDescriptor.pPartDescs[i].uStart, RawDescriptor.pPartDescs[i].cbData);
+ if (!RawDescriptor.pPartDescs[i].cbData)
+ {
+ if (RawDescriptor.uPartitioningType == MBR)
+ {
+ RTMsgError("MBR/EPT overlaps with data area");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ else
+ {
+ if (RawDescriptor.cPartDescs != i+1)
+ {
+ RTMsgError("GPT overlaps with data area");
+ vrc = VERR_INVALID_PARAMETER;
+ goto out;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ RTFileClose(hRawFile);
+
+#ifdef DEBUG_klaus
+ if (!(RawDescriptor.uFlags & VDISKRAW_DISK))
+ {
+ RTPrintf("# start length startoffset partdataptr device\n");
+ for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
+ {
+ RTPrintf("%2u %14RU64 %14RU64 %14RU64 %#18p %s\n", i,
+ RawDescriptor.pPartDescs[i].uStart,
+ RawDescriptor.pPartDescs[i].cbData,
+ RawDescriptor.pPartDescs[i].uStartOffset,
+ RawDescriptor.pPartDescs[i].pvPartitionData,
+ RawDescriptor.pPartDescs[i].pszRawDevice);
+ }
+ }
+#endif
+
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(vrc);
+
+ vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk); /* Raw VMDK's are harddisk only. */
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot create the virtual disk container: %Rrc", vrc);
+ goto out;
+ }
+
+ Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) -
+ (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383) == 0);
+ VDGEOMETRY PCHS, LCHS;
+ PCHS.cCylinders = (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383);
+ PCHS.cHeads = 16;
+ PCHS.cSectors = 63;
+ LCHS.cCylinders = 0;
+ LCHS.cHeads = 0;
+ LCHS.cSectors = 0;
+ vrc = VDCreateBase(pDisk, "VMDK", filename.c_str(), cbSize,
+ VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_RAWDISK,
+ (char *)&RawDescriptor, &PCHS, &LCHS, NULL,
+ VD_OPEN_FLAGS_NORMAL, NULL, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot create the raw disk VMDK: %Rrc", vrc);
+ goto out;
+ }
+ RTPrintf("RAW host disk access VMDK file %s created successfully.\n", filename.c_str());
+
+ VDCloseAll(pDisk);
+
+ /* Clean up allocated memory etc. */
+ if (pszPartitions)
+ {
+ for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
+ {
+ /* Free memory allocated for relative device name. */
+ if (fRelative && RawDescriptor.pPartDescs[i].pszRawDevice)
+ RTStrFree((char *)(void *)RawDescriptor.pPartDescs[i].pszRawDevice);
+ if (RawDescriptor.pPartDescs[i].pvPartitionData)
+ RTMemFree((void *)RawDescriptor.pPartDescs[i].pvPartitionData);
+ }
+ if (RawDescriptor.pPartDescs)
+ RTMemFree(RawDescriptor.pPartDescs);
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+
+out:
+ RTMsgError("The raw disk vmdk file was not created");
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static RTEXITCODE CmdRenameVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Utf8Str src;
+ Utf8Str dst;
+ /* Parse the arguments. */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-from") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ src = argv[i];
+ }
+ else if (strcmp(argv[i], "-to") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ dst = argv[i];
+ }
+ else
+ {
+ return errorSyntax(USAGE_RENAMEVMDK, "Invalid parameter '%s'", argv[i]);
+ }
+ }
+
+ if (src.isEmpty())
+ return errorSyntax(USAGE_RENAMEVMDK, "Mandatory parameter -from missing");
+ if (dst.isEmpty())
+ return errorSyntax(USAGE_RENAMEVMDK, "Mandatory parameter -to missing");
+
+ PVDISK pDisk = NULL;
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(vrc);
+
+ vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create the virtual disk container: %Rrc", vrc);
+
+ vrc = VDOpen(pDisk, "VMDK", src.c_str(), VD_OPEN_FLAGS_NORMAL, NULL);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = VDCopy(pDisk, 0, pDisk, "VMDK", dst.c_str(), true, 0,
+ VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_NORMAL,
+ NULL, NULL, NULL);
+ if (RT_FAILURE(vrc))
+ RTMsgError("Cannot rename the image: %Rrc", vrc);
+ }
+ else
+ RTMsgError("Cannot create the source image: %Rrc", vrc);
+ VDCloseAll(pDisk);
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static RTEXITCODE CmdConvertToRaw(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Utf8Str srcformat;
+ Utf8Str src;
+ Utf8Str dst;
+ bool fWriteToStdOut = false;
+
+ /* Parse the arguments. */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-format") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ srcformat = argv[i];
+ }
+ else if (src.isEmpty())
+ {
+ src = argv[i];
+ }
+ else if (dst.isEmpty())
+ {
+ dst = argv[i];
+#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
+ if (!strcmp(argv[i], "stdout"))
+ fWriteToStdOut = true;
+#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
+ }
+ else
+ {
+ return errorSyntax(USAGE_CONVERTTORAW, "Invalid parameter '%s'", argv[i]);
+ }
+ }
+
+ if (src.isEmpty())
+ return errorSyntax(USAGE_CONVERTTORAW, "Mandatory filename parameter missing");
+ if (dst.isEmpty())
+ return errorSyntax(USAGE_CONVERTTORAW, "Mandatory outputfile parameter missing");
+
+ PVDISK pDisk = NULL;
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(vrc);
+
+ /** @todo Support convert to raw for floppy and DVD images too. */
+ vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create the virtual disk container: %Rrc", vrc);
+
+ /* Open raw output file. */
+ RTFILE outFile;
+ vrc = VINF_SUCCESS;
+ if (fWriteToStdOut)
+ vrc = RTFileFromNative(&outFile, 1);
+ else
+ vrc = RTFileOpen(&outFile, dst.c_str(), RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
+ if (RT_FAILURE(vrc))
+ {
+ VDCloseAll(pDisk);
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create destination file \"%s\": %Rrc", dst.c_str(), vrc);
+ }
+
+ if (srcformat.isEmpty())
+ {
+ char *pszFormat = NULL;
+ VDTYPE enmType = VDTYPE_INVALID;
+ vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+ src.c_str(), &pszFormat, &enmType);
+ if (RT_FAILURE(vrc) || enmType != VDTYPE_HDD)
+ {
+ VDCloseAll(pDisk);
+ if (!fWriteToStdOut)
+ {
+ RTFileClose(outFile);
+ RTFileDelete(dst.c_str());
+ }
+ if (RT_FAILURE(vrc))
+ RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
+ else
+ RTMsgError("Only converting harddisk images is supported");
+ return RTEXITCODE_FAILURE;
+ }
+ srcformat = pszFormat;
+ RTStrFree(pszFormat);
+ }
+ vrc = VDOpen(pDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ VDCloseAll(pDisk);
+ if (!fWriteToStdOut)
+ {
+ RTFileClose(outFile);
+ RTFileDelete(dst.c_str());
+ }
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open the source image: %Rrc", vrc);
+ }
+
+ uint64_t cbSize = VDGetSize(pDisk, VD_LAST_IMAGE);
+ uint64_t offFile = 0;
+#define RAW_BUFFER_SIZE _128K
+ size_t cbBuf = RAW_BUFFER_SIZE;
+ void *pvBuf = RTMemAlloc(cbBuf);
+ if (pvBuf)
+ {
+ RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB) to raw...\n", src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
+ while (offFile < cbSize)
+ {
+ size_t cb = (size_t)RT_MIN(cbSize - offFile, cbBuf);
+ vrc = VDRead(pDisk, offFile, pvBuf, cb);
+ if (RT_FAILURE(vrc))
+ break;
+ vrc = RTFileWrite(outFile, pvBuf, cb, NULL);
+ if (RT_FAILURE(vrc))
+ break;
+ offFile += cb;
+ }
+ RTMemFree(pvBuf);
+ if (RT_FAILURE(vrc))
+ {
+ VDCloseAll(pDisk);
+ if (!fWriteToStdOut)
+ {
+ RTFileClose(outFile);
+ RTFileDelete(dst.c_str());
+ }
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot copy image data: %Rrc", vrc);
+ }
+ }
+ else
+ {
+ vrc = VERR_NO_MEMORY;
+ VDCloseAll(pDisk);
+ if (!fWriteToStdOut)
+ {
+ RTFileClose(outFile);
+ RTFileDelete(dst.c_str());
+ }
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory allocating read buffer");
+ }
+
+ if (!fWriteToStdOut)
+ RTFileClose(outFile);
+ VDCloseAll(pDisk);
+ return RTEXITCODE_SUCCESS;
+}
+
+static RTEXITCODE CmdConvertHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Utf8Str srcformat;
+ Utf8Str dstformat;
+ Utf8Str src;
+ Utf8Str dst;
+ int vrc;
+ PVDISK pSrcDisk = NULL;
+ PVDISK pDstDisk = NULL;
+ VDTYPE enmSrcType = VDTYPE_INVALID;
+
+ /* Parse the arguments. */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-srcformat") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ srcformat = argv[i];
+ }
+ else if (strcmp(argv[i], "-dstformat") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ dstformat = argv[i];
+ }
+ else if (src.isEmpty())
+ {
+ src = argv[i];
+ }
+ else if (dst.isEmpty())
+ {
+ dst = argv[i];
+ }
+ else
+ {
+ return errorSyntax(USAGE_CONVERTHD, "Invalid parameter '%s'", argv[i]);
+ }
+ }
+
+ if (src.isEmpty())
+ return errorSyntax(USAGE_CONVERTHD, "Mandatory input image parameter missing");
+ if (dst.isEmpty())
+ return errorSyntax(USAGE_CONVERTHD, "Mandatory output image parameter missing");
+
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(vrc);
+
+ do
+ {
+ /* Try to determine input image format */
+ if (srcformat.isEmpty())
+ {
+ char *pszFormat = NULL;
+ vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+ src.c_str(), &pszFormat, &enmSrcType);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
+ break;
+ }
+ srcformat = pszFormat;
+ RTStrFree(pszFormat);
+ }
+
+ vrc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot create the source virtual disk container: %Rrc", vrc);
+ break;
+ }
+
+ /* Open the input image */
+ vrc = VDOpen(pSrcDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot open the source image: %Rrc", vrc);
+ break;
+ }
+
+ /* Output format defaults to VDI */
+ if (dstformat.isEmpty())
+ dstformat = "VDI";
+
+ vrc = VDCreate(pVDIfs, enmSrcType, &pDstDisk);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot create the destination virtual disk container: %Rrc", vrc);
+ break;
+ }
+
+ uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
+ RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
+
+ /* Create the output image */
+ vrc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, dstformat.c_str(),
+ dst.c_str(), false, 0, VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED,
+ NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL, NULL);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Cannot copy the image: %Rrc", vrc);
+ break;
+ }
+ }
+ while (0);
+ if (pDstDisk)
+ VDCloseAll(pDstDisk);
+ if (pSrcDisk)
+ VDCloseAll(pSrcDisk);
+
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+/**
+ * Tries to repair a corrupted hard disk image.
+ *
+ * @returns VBox status code
+ */
+static RTEXITCODE CmdRepairHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+ Utf8Str image;
+ Utf8Str format;
+ int vrc;
+ bool fDryRun = false;
+
+ /* Parse the arguments. */
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-dry-run") == 0)
+ {
+ fDryRun = true;
+ }
+ else if (strcmp(argv[i], "-format") == 0)
+ {
+ if (argc <= i + 1)
+ {
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ }
+ i++;
+ format = argv[i];
+ }
+ else if (image.isEmpty())
+ {
+ image = argv[i];
+ }
+ else
+ {
+ return errorSyntax(USAGE_REPAIRHD, "Invalid parameter '%s'", argv[i]);
+ }
+ }
+
+ if (image.isEmpty())
+ return errorSyntax(USAGE_REPAIRHD, "Mandatory input image parameter missing");
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = handleVDMessage;
+
+ vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(vrc);
+
+ do
+ {
+ /* Try to determine input image format */
+ if (format.isEmpty())
+ {
+ char *pszFormat = NULL;
+ VDTYPE enmSrcType = VDTYPE_INVALID;
+
+ vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+ image.c_str(), &pszFormat, &enmSrcType);
+ if (RT_FAILURE(vrc) && (vrc != VERR_VD_IMAGE_CORRUPTED))
+ {
+ RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
+ break;
+ }
+ format = pszFormat;
+ RTStrFree(pszFormat);
+ }
+
+ uint32_t fFlags = 0;
+ if (fDryRun)
+ fFlags |= VD_REPAIR_DRY_RUN;
+
+ vrc = VDRepair(pVDIfs, NULL, image.c_str(), format.c_str(), fFlags);
+ }
+ while (0);
+
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+/**
+ * Unloads the necessary driver.
+ *
+ * @returns VBox status code
+ */
+static RTEXITCODE CmdModUninstall(void)
+{
+ int rc = SUPR3Uninstall();
+ if (RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED)
+ return RTEXITCODE_SUCCESS;
+ return RTEXITCODE_FAILURE;
+}
+
+/**
+ * Loads the necessary driver.
+ *
+ * @returns VBox status code
+ */
+static RTEXITCODE CmdModInstall(void)
+{
+ int rc = SUPR3Install();
+ if (RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED)
+ return RTEXITCODE_SUCCESS;
+ return RTEXITCODE_FAILURE;
+}
+
+static RTEXITCODE CmdDebugLog(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ /*
+ * The first parameter is the name or UUID of a VM with a direct session
+ * that we wish to open.
+ */
+ if (argc < 1)
+ return errorSyntax(USAGE_DEBUGLOG, "Missing VM name/UUID");
+
+ ComPtr<IMachine> ptrMachine;
+ HRESULT rc;
+ CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
+ ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
+
+ CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
+
+ /*
+ * Get the debugger interface.
+ */
+ ComPtr<IConsole> ptrConsole;
+ CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
+
+ ComPtr<IMachineDebugger> ptrDebugger;
+ CHECK_ERROR_RET(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()), RTEXITCODE_FAILURE);
+
+ /*
+ * Parse the command.
+ */
+ bool fEnablePresent = false;
+ bool fEnable = false;
+ bool fFlagsPresent = false;
+ RTCString strFlags;
+ bool fGroupsPresent = false;
+ RTCString strGroups;
+ bool fDestsPresent = false;
+ RTCString strDests;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--disable", 'E', RTGETOPT_REQ_NOTHING },
+ { "--enable", 'e', RTGETOPT_REQ_NOTHING },
+ { "--flags", 'f', RTGETOPT_REQ_STRING },
+ { "--groups", 'g', RTGETOPT_REQ_STRING },
+ { "--destinations", 'd', RTGETOPT_REQ_STRING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'e':
+ fEnablePresent = true;
+ fEnable = true;
+ break;
+
+ case 'E':
+ fEnablePresent = true;
+ fEnable = false;
+ break;
+
+ case 'f':
+ fFlagsPresent = true;
+ if (*ValueUnion.psz)
+ {
+ if (strFlags.isNotEmpty())
+ strFlags.append(' ');
+ strFlags.append(ValueUnion.psz);
+ }
+ break;
+
+ case 'g':
+ fGroupsPresent = true;
+ if (*ValueUnion.psz)
+ {
+ if (strGroups.isNotEmpty())
+ strGroups.append(' ');
+ strGroups.append(ValueUnion.psz);
+ }
+ break;
+
+ case 'd':
+ fDestsPresent = true;
+ if (*ValueUnion.psz)
+ {
+ if (strDests.isNotEmpty())
+ strDests.append(' ');
+ strDests.append(ValueUnion.psz);
+ }
+ break;
+
+ default:
+ return errorGetOpt(USAGE_DEBUGLOG, ch, &ValueUnion);
+ }
+ }
+
+ /*
+ * Do the job.
+ */
+ if (fEnablePresent && !fEnable)
+ CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(FALSE), RTEXITCODE_FAILURE);
+
+ /** @todo flags, groups destination. */
+ if (fFlagsPresent || fGroupsPresent || fDestsPresent)
+ RTMsgWarning("One or more of the requested features are not implemented! Feel free to do this.");
+
+ if (fEnablePresent && fEnable)
+ CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(TRUE), RTEXITCODE_FAILURE);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Generate a SHA-256 password hash
+ */
+static RTEXITCODE CmdGeneratePasswordHash(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ RT_NOREF(aVirtualBox, aSession);
+
+ /* one parameter, the password to hash */
+ if (argc != 1)
+ return errorSyntax(USAGE_PASSWORDHASH, "password to hash required");
+
+ uint8_t abDigest[RTSHA256_HASH_SIZE];
+ RTSha256(argv[0], strlen(argv[0]), abDigest);
+ char pszDigest[RTSHA256_DIGEST_LEN + 1];
+ RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest));
+ RTPrintf("Password hash: %s\n", pszDigest);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Print internal guest statistics or
+ * set internal guest statistics update interval if specified
+ */
+static RTEXITCODE CmdGuestStats(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
+{
+ /* one parameter, guest name */
+ if (argc < 1)
+ return errorSyntax(USAGE_GUESTSTATS, "Missing VM name/UUID");
+
+ /*
+ * Parse the command.
+ */
+ ULONG aUpdateInterval = 0;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--interval", 'i', RTGETOPT_REQ_UINT32 }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'i':
+ aUpdateInterval = ValueUnion.u32;
+ break;
+
+ default:
+ return errorGetOpt(USAGE_GUESTSTATS, ch, &ValueUnion);
+ }
+ }
+
+ if (argc > 1 && aUpdateInterval == 0)
+ return errorSyntax(USAGE_GUESTSTATS, "Invalid update interval specified");
+
+ RTPrintf("argc=%d interval=%u\n", argc, aUpdateInterval);
+
+ ComPtr<IMachine> ptrMachine;
+ HRESULT rc;
+ CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
+ ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
+
+ CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
+
+ /*
+ * Get the guest interface.
+ */
+ ComPtr<IConsole> ptrConsole;
+ CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
+
+ ComPtr<IGuest> ptrGuest;
+ CHECK_ERROR_RET(ptrConsole, COMGETTER(Guest)(ptrGuest.asOutParam()), RTEXITCODE_FAILURE);
+
+ if (aUpdateInterval)
+ CHECK_ERROR_RET(ptrGuest, COMSETTER(StatisticsUpdateInterval)(aUpdateInterval), RTEXITCODE_FAILURE);
+ else
+ {
+ ULONG mCpuUser, mCpuKernel, mCpuIdle;
+ ULONG mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache, mPageTotal;
+ ULONG ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal;
+
+ CHECK_ERROR_RET(ptrGuest, InternalGetStatistics(&mCpuUser, &mCpuKernel, &mCpuIdle,
+ &mMemTotal, &mMemFree, &mMemBalloon, &mMemShared, &mMemCache,
+ &mPageTotal, &ulMemAllocTotal, &ulMemFreeTotal,
+ &ulMemBalloonTotal, &ulMemSharedTotal),
+ RTEXITCODE_FAILURE);
+ RTPrintf("mCpuUser=%u mCpuKernel=%u mCpuIdle=%u\n"
+ "mMemTotal=%u mMemFree=%u mMemBalloon=%u mMemShared=%u mMemCache=%u\n"
+ "mPageTotal=%u ulMemAllocTotal=%u ulMemFreeTotal=%u ulMemBalloonTotal=%u ulMemSharedTotal=%u\n",
+ mCpuUser, mCpuKernel, mCpuIdle,
+ mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache,
+ mPageTotal, ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal);
+
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Wrapper for handling internal commands
+ */
+RTEXITCODE handleInternalCommands(HandlerArg *a)
+{
+ g_fInternalMode = true;
+
+ /* at least a command is required */
+ if (a->argc < 1)
+ return errorSyntax(USAGE_ALL, "Command missing");
+
+ /*
+ * The 'string switch' on command name.
+ */
+ const char *pszCmd = a->argv[0];
+ if (!strcmp(pszCmd, "loadmap"))
+ return CmdLoadMap(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ 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]);
+ if (!strcmp(pszCmd, "sethduuid") || !strcmp(pszCmd, "sethdparentuuid"))
+ return CmdSetHDUUID(a->argc, &a->argv[0], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "dumphdinfo"))
+ return CmdDumpHDInfo(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "listpartitions"))
+ return CmdListPartitions(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "createrawvmdk"))
+ return CmdCreateRawVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "renamevmdk"))
+ return CmdRenameVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "converttoraw"))
+ return CmdConvertToRaw(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "converthd"))
+ return CmdConvertHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "modinstall"))
+ return CmdModInstall();
+ if (!strcmp(pszCmd, "moduninstall"))
+ return CmdModUninstall();
+ if (!strcmp(pszCmd, "debuglog"))
+ return CmdDebugLog(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "passwordhash"))
+ return CmdGeneratePasswordHash(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "gueststats"))
+ return CmdGuestStats(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+ if (!strcmp(pszCmd, "repairhd"))
+ return CmdRepairHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
+
+ /* default: */
+ return errorSyntax(USAGE_ALL, "Invalid command '%s'", a->argv[0]);
+}
+
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManage.cpp b/src/VBox/Frontends/VBoxManage/VBoxManage.cpp
new file mode 100644
index 00000000..fe3fcffe
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManage.cpp
@@ -0,0 +1,760 @@
+/* $Id: VBoxManage.cpp $ */
+/** @file
+ * VBoxManage - VirtualBox's command-line interface.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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/string.h>
+# include <VBox/com/Guid.h>
+# include <VBox/com/array.h>
+# include <VBox/com/ErrorInfo.h>
+# include <VBox/com/errorprint.h>
+# include <VBox/com/NativeEventQueue.h>
+
+# include <VBox/com/VirtualBox.h>
+#endif /* !VBOX_ONLY_DOCS */
+
+#include <VBox/version.h>
+
+#include <iprt/asm.h>
+#include <iprt/buildconfig.h>
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+
+#include <signal.h>
+
+#include "VBoxManage.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The command doesn't need the COM stuff. */
+#define VBMG_CMD_F_NO_COM RT_BIT_32(0)
+
+#define VBMG_CMD_TODO HELP_CMD_VBOXMANAGE_INVALID
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#ifndef VBOX_ONLY_DOCS
+/**
+ * VBoxManage command descriptor.
+ */
+typedef struct VBMGCMD
+{
+ /** The command. */
+ const char *pszCommand;
+ /** The help category. */
+ USAGECATEGORY enmHelpCat;
+ /** The new help command. */
+ enum HELP_CMD_VBOXMANAGE enmCmdHelp;
+ /** The handler. */
+ RTEXITCODE (*pfnHandler)(HandlerArg *pArg);
+ /** VBMG_CMD_F_XXX, */
+ uint32_t fFlags;
+} VBMGCMD;
+/** Pointer to a const VBoxManage command descriptor. */
+typedef VBMGCMD const *PCVBMGCMD;
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/*extern*/ bool g_fDetailedProgress = false;
+
+#ifndef VBOX_ONLY_DOCS
+/** Set by the signal handler. */
+static volatile bool g_fCanceled = false;
+
+
+/**
+ * All registered command handlers
+ */
+static const VBMGCMD g_aCommands[] =
+{
+ { "internalcommands", 0, VBMG_CMD_TODO, handleInternalCommands, 0 },
+ { "list", USAGE_LIST, VBMG_CMD_TODO, handleList, 0 },
+ { "showvminfo", USAGE_SHOWVMINFO, VBMG_CMD_TODO, handleShowVMInfo, 0 },
+ { "registervm", USAGE_REGISTERVM, VBMG_CMD_TODO, handleRegisterVM, 0 },
+ { "unregistervm", USAGE_UNREGISTERVM, VBMG_CMD_TODO, handleUnregisterVM, 0 },
+ { "clonevm", USAGE_CLONEVM, VBMG_CMD_TODO, handleCloneVM, 0 },
+ { "movevm", USAGE_MOVEVM, VBMG_CMD_TODO, handleMoveVM, 0 },
+ { "mediumproperty", USAGE_MEDIUMPROPERTY, VBMG_CMD_TODO, handleMediumProperty, 0 },
+ { "hdproperty", USAGE_MEDIUMPROPERTY, VBMG_CMD_TODO, handleMediumProperty, 0 }, /* backward compatibility */
+ { "createmedium", USAGE_CREATEMEDIUM, VBMG_CMD_TODO, handleCreateMedium, 0 },
+ { "createhd", USAGE_CREATEMEDIUM, VBMG_CMD_TODO, handleCreateMedium, 0 }, /* backward compatibility */
+ { "createvdi", USAGE_CREATEMEDIUM, VBMG_CMD_TODO, handleCreateMedium, 0 }, /* backward compatibility */
+ { "modifymedium", USAGE_MODIFYMEDIUM, VBMG_CMD_TODO, handleModifyMedium, 0 },
+ { "modifyhd", USAGE_MODIFYMEDIUM, VBMG_CMD_TODO, handleModifyMedium, 0 }, /* backward compatibility */
+ { "modifyvdi", USAGE_MODIFYMEDIUM, VBMG_CMD_TODO, handleModifyMedium, 0 }, /* backward compatibility */
+ { "clonemedium", USAGE_CLONEMEDIUM, VBMG_CMD_TODO, handleCloneMedium, 0 },
+ { "clonehd", USAGE_CLONEMEDIUM, VBMG_CMD_TODO, handleCloneMedium, 0 }, /* backward compatibility */
+ { "clonevdi", USAGE_CLONEMEDIUM, VBMG_CMD_TODO, handleCloneMedium, 0 }, /* backward compatibility */
+ { "encryptmedium", USAGE_ENCRYPTMEDIUM, VBMG_CMD_TODO, handleEncryptMedium, 0 },
+ { "checkmediumpwd", USAGE_MEDIUMENCCHKPWD, VBMG_CMD_TODO, handleCheckMediumPassword, 0 },
+ { "createvm", USAGE_CREATEVM, VBMG_CMD_TODO, handleCreateVM, 0 },
+ { "modifyvm", USAGE_MODIFYVM, VBMG_CMD_TODO, handleModifyVM, 0 },
+ { "startvm", USAGE_STARTVM, VBMG_CMD_TODO, handleStartVM, 0 },
+ { "controlvm", USAGE_CONTROLVM, VBMG_CMD_TODO, handleControlVM, 0 },
+ { "unattended", USAGE_UNATTENDED, HELP_CMD_UNATTENDED, handleUnattended, 0 },
+ { "discardstate", USAGE_DISCARDSTATE, VBMG_CMD_TODO, handleDiscardState, 0 },
+ { "adoptstate", USAGE_ADOPTSTATE, VBMG_CMD_TODO, handleAdoptState, 0 },
+ { "snapshot", USAGE_SNAPSHOT, VBMG_CMD_TODO, handleSnapshot, 0 },
+ { "closemedium", USAGE_CLOSEMEDIUM, VBMG_CMD_TODO, handleCloseMedium, 0 },
+ { "storageattach", USAGE_STORAGEATTACH, VBMG_CMD_TODO, handleStorageAttach, 0 },
+ { "storagectl", USAGE_STORAGECONTROLLER,VBMG_CMD_TODO, handleStorageController, 0 },
+ { "showmediuminfo", USAGE_SHOWMEDIUMINFO, VBMG_CMD_TODO, handleShowMediumInfo, 0 },
+ { "showhdinfo", USAGE_SHOWMEDIUMINFO, VBMG_CMD_TODO, handleShowMediumInfo, 0 }, /* backward compatibility */
+ { "showvdiinfo", USAGE_SHOWMEDIUMINFO, VBMG_CMD_TODO, handleShowMediumInfo, 0 }, /* backward compatibility */
+ { "mediumio", USAGE_MEDIUMIO, HELP_CMD_MEDIUMIO, handleMediumIO, 0 },
+ { "getextradata", USAGE_GETEXTRADATA, VBMG_CMD_TODO, handleGetExtraData, 0 },
+ { "setextradata", USAGE_SETEXTRADATA, VBMG_CMD_TODO, handleSetExtraData, 0 },
+ { "setproperty", USAGE_SETPROPERTY, VBMG_CMD_TODO, handleSetProperty, 0 },
+ { "usbfilter", USAGE_USBFILTER, VBMG_CMD_TODO, handleUSBFilter, 0 },
+ { "sharedfolder", USAGE_SHAREDFOLDER, VBMG_CMD_TODO, handleSharedFolder, 0 },
+#ifdef VBOX_WITH_GUEST_PROPS
+ { "guestproperty", USAGE_GUESTPROPERTY, VBMG_CMD_TODO, handleGuestProperty, 0 },
+#endif
+#ifdef VBOX_WITH_GUEST_CONTROL
+ { "guestcontrol", USAGE_GUESTCONTROL, VBMG_CMD_TODO, handleGuestControl, 0 },
+#endif
+ { "metrics", USAGE_METRICS, VBMG_CMD_TODO, handleMetrics, 0 },
+ { "import", USAGE_IMPORTAPPLIANCE, VBMG_CMD_TODO, handleImportAppliance, 0 },
+ { "export", USAGE_EXPORTAPPLIANCE, VBMG_CMD_TODO, handleExportAppliance, 0 },
+#ifdef VBOX_WITH_NETFLT
+ { "hostonlyif", USAGE_HOSTONLYIFS, VBMG_CMD_TODO, handleHostonlyIf, 0 },
+#endif
+ { "dhcpserver", USAGE_DHCPSERVER, VBMG_CMD_TODO, handleDHCPServer, 0 },
+#ifdef VBOX_WITH_NAT_SERVICE
+ { "natnetwork", USAGE_NATNETWORK, VBMG_CMD_TODO, handleNATNetwork, 0 },
+#endif
+ { "extpack", USAGE_EXTPACK, HELP_CMD_EXTPACK, handleExtPack, 0 },
+ { "bandwidthctl", USAGE_BANDWIDTHCONTROL, VBMG_CMD_TODO, handleBandwidthControl, 0 },
+ { "debugvm", USAGE_DEBUGVM, HELP_CMD_DEBUGVM, handleDebugVM, 0 },
+ { "convertfromraw", USAGE_CONVERTFROMRAW, VBMG_CMD_TODO, handleConvertFromRaw, VBMG_CMD_F_NO_COM },
+ { "convertdd", USAGE_CONVERTFROMRAW, VBMG_CMD_TODO, handleConvertFromRaw, VBMG_CMD_F_NO_COM },
+ { "usbdevsource", USAGE_USBDEVSOURCE, VBMG_CMD_TODO, handleUSBDevSource, 0 }
+};
+
+
+/**
+ * Looks up a command by name.
+ *
+ * @returns Pointer to the command structure.
+ * @param pszCommand Name of the command.
+ */
+static PCVBMGCMD lookupCommand(const char *pszCommand)
+{
+ if (pszCommand)
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aCommands); i++)
+ if (!strcmp(g_aCommands[i].pszCommand, pszCommand))
+ return &g_aCommands[i];
+ return NULL;
+}
+
+
+/**
+ * Signal handler that sets g_fCanceled.
+ *
+ * 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
+ * unnecessary here.
+ */
+static void showProgressSignalHandler(int iSignal)
+{
+ NOREF(iSignal);
+ ASMAtomicWriteBool(&g_fCanceled, true);
+}
+
+/**
+ * Print out progress on the console.
+ *
+ * This runs the main event queue every now and then to prevent piling up
+ * unhandled things (which doesn't cause real problems, just makes things
+ * react a little slower than in the ideal case).
+ */
+HRESULT showProgress(ComPtr<IProgress> progress)
+{
+ using namespace com;
+
+ BOOL fCompleted = FALSE;
+ ULONG ulCurrentPercent = 0;
+ ULONG ulLastPercent = 0;
+
+ ULONG ulLastOperationPercent = (ULONG)-1;
+
+ ULONG ulLastOperation = (ULONG)-1;
+ Bstr bstrOperationDescription;
+
+ NativeEventQueue::getMainEventQueue()->processEventQueue(0);
+
+ ULONG cOperations = 1;
+ HRESULT hrc = progress->COMGETTER(OperationCount)(&cOperations);
+ if (FAILED(hrc))
+ {
+ RTStrmPrintf(g_pStdErr, "Progress object failure: %Rhrc\n", hrc);
+ RTStrmFlush(g_pStdErr);
+ return hrc;
+ }
+
+ /*
+ * Note: Outputting the progress info to stderr (g_pStdErr) is intentional
+ * to not get intermixed with other (raw) stdout data which might get
+ * written in the meanwhile.
+ */
+
+ if (!g_fDetailedProgress)
+ {
+ RTStrmPrintf(g_pStdErr, "0%%...");
+ RTStrmFlush(g_pStdErr);
+ }
+
+ /* setup signal handling if cancelable */
+ bool fCanceledAlready = false;
+ BOOL fCancelable;
+ hrc = progress->COMGETTER(Cancelable)(&fCancelable);
+ if (FAILED(hrc))
+ fCancelable = FALSE;
+ if (fCancelable)
+ {
+ signal(SIGINT, showProgressSignalHandler);
+ signal(SIGTERM, showProgressSignalHandler);
+#ifdef SIGBREAK
+ signal(SIGBREAK, showProgressSignalHandler);
+#endif
+ }
+
+ hrc = progress->COMGETTER(Completed(&fCompleted));
+ while (SUCCEEDED(hrc))
+ {
+ progress->COMGETTER(Percent(&ulCurrentPercent));
+
+ if (g_fDetailedProgress)
+ {
+ ULONG ulOperation = 1;
+ hrc = progress->COMGETTER(Operation)(&ulOperation);
+ if (FAILED(hrc))
+ break;
+ ULONG ulCurrentOperationPercent = 0;
+ hrc = progress->COMGETTER(OperationPercent(&ulCurrentOperationPercent));
+ if (FAILED(hrc))
+ break;
+
+ if (ulLastOperation != ulOperation)
+ {
+ hrc = progress->COMGETTER(OperationDescription(bstrOperationDescription.asOutParam()));
+ if (FAILED(hrc))
+ break;
+ ulLastPercent = (ULONG)-1; // force print
+ ulLastOperation = ulOperation;
+ }
+
+ if ( ulCurrentPercent != ulLastPercent
+ || ulCurrentOperationPercent != ulLastOperationPercent
+ )
+ {
+ 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);
+ ulLastPercent = ulCurrentPercent;
+ ulLastOperationPercent = ulCurrentOperationPercent;
+ }
+ }
+ else
+ {
+ /* did we cross a 10% mark? */
+ if (ulCurrentPercent / 10 > ulLastPercent / 10)
+ {
+ /* make sure to also print out missed steps */
+ for (ULONG curVal = (ulLastPercent / 10) * 10 + 10; curVal <= (ulCurrentPercent / 10) * 10; curVal += 10)
+ {
+ if (curVal < 100)
+ {
+ RTStrmPrintf(g_pStdErr, "%u%%...", curVal);
+ RTStrmFlush(g_pStdErr);
+ }
+ }
+ ulLastPercent = (ulCurrentPercent / 10) * 10;
+ }
+ }
+ if (fCompleted)
+ break;
+
+ /* process async cancelation */
+ if (g_fCanceled && !fCanceledAlready)
+ {
+ hrc = progress->Cancel();
+ if (SUCCEEDED(hrc))
+ fCanceledAlready = true;
+ else
+ g_fCanceled = false;
+ }
+
+ /* make sure the loop is not too tight */
+ progress->WaitForCompletion(100);
+
+ NativeEventQueue::getMainEventQueue()->processEventQueue(0);
+ hrc = progress->COMGETTER(Completed(&fCompleted));
+ }
+
+ /* undo signal handling */
+ if (fCancelable)
+ {
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+# ifdef SIGBREAK
+ signal(SIGBREAK, SIG_DFL);
+# endif
+ }
+
+ /* complete the line. */
+ LONG iRc = E_FAIL;
+ hrc = progress->COMGETTER(ResultCode)(&iRc);
+ if (SUCCEEDED(hrc))
+ {
+ if (SUCCEEDED(iRc))
+ RTStrmPrintf(g_pStdErr, "100%%\n");
+ else if (g_fCanceled)
+ RTStrmPrintf(g_pStdErr, "CANCELED\n");
+ else
+ {
+ if (!g_fDetailedProgress)
+ RTStrmPrintf(g_pStdErr, "\n");
+ RTStrmPrintf(g_pStdErr, "Progress state: %Rhrc\n", iRc);
+ }
+ hrc = iRc;
+ }
+ else
+ {
+ if (!g_fDetailedProgress)
+ RTStrmPrintf(g_pStdErr, "\n");
+ RTStrmPrintf(g_pStdErr, "Progress object failure: %Rhrc\n", hrc);
+ }
+ RTStrmFlush(g_pStdErr);
+ return hrc;
+}
+
+RTEXITCODE readPasswordFile(const char *pszFilename, com::Utf8Str *pPasswd)
+{
+ size_t cbFile;
+ char szPasswd[512];
+ int vrc = VINF_SUCCESS;
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ bool fStdIn = !strcmp(pszFilename, "stdin");
+ PRTSTREAM pStrm;
+ if (!fStdIn)
+ vrc = RTStrmOpen(pszFilename, "r", &pStrm);
+ else
+ pStrm = g_pStdIn;
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTStrmReadEx(pStrm, szPasswd, sizeof(szPasswd)-1, &cbFile);
+ if (RT_SUCCESS(vrc))
+ {
+ if (cbFile >= sizeof(szPasswd)-1)
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Provided password in file '%s' is too long", pszFilename);
+ else
+ {
+ unsigned i;
+ for (i = 0; i < cbFile && !RT_C_IS_CNTRL(szPasswd[i]); i++)
+ ;
+ szPasswd[i] = '\0';
+ *pPasswd = szPasswd;
+ }
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot read password from file '%s': %Rrc", pszFilename, vrc);
+ if (!fStdIn)
+ RTStrmClose(pStrm);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open password file '%s' (%Rrc)", pszFilename, vrc);
+
+ return rcExit;
+}
+
+static RTEXITCODE settingsPasswordFile(ComPtr<IVirtualBox> virtualBox, const char *pszFilename)
+{
+ com::Utf8Str passwd;
+ RTEXITCODE rcExit = readPasswordFile(pszFilename, &passwd);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ CHECK_ERROR2I_STMT(virtualBox, SetSettingsSecret(com::Bstr(passwd).raw()), rcExit = RTEXITCODE_FAILURE);
+ }
+
+ return rcExit;
+}
+
+RTEXITCODE readPasswordFromConsole(com::Utf8Str *pPassword, const char *pszPrompt, ...)
+{
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ char aszPwdInput[_1K] = { 0 };
+ va_list vaArgs;
+
+ va_start(vaArgs, pszPrompt);
+ int vrc = RTStrmPrintfV(g_pStdOut, pszPrompt, vaArgs);
+ if (RT_SUCCESS(vrc))
+ {
+ bool fEchoOld = false;
+ vrc = RTStrmInputGetEchoChars(g_pStdIn, &fEchoOld);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTStrmInputSetEchoChars(g_pStdIn, false);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTStrmGetLine(g_pStdIn, &aszPwdInput[0], sizeof(aszPwdInput));
+ if (RT_SUCCESS(vrc))
+ *pPassword = aszPwdInput;
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed read password from command line (%Rrc)", vrc);
+
+ int vrc2 = RTStrmInputSetEchoChars(g_pStdIn, fEchoOld);
+ AssertRC(vrc2);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to disable echoing typed characters (%Rrc)", vrc);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to retrieve echo setting (%Rrc)", vrc);
+
+ RTStrmPutStr(g_pStdOut, "\n");
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to print prompt (%Rrc)", vrc);
+ va_end(vaArgs);
+
+ return rcExit;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
+
+
+int main(int argc, char *argv[])
+{
+ /*
+ * Before we do anything, init the runtime without loading
+ * the support driver.
+ */
+ int vrc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(vrc))
+ return RTMsgInitFailure(vrc);
+#if defined(RT_OS_WINDOWS) && !defined(VBOX_ONLY_DOCS)
+ ATL::CComModule _Module; /* Required internally by ATL (constructor records instance in global variable). */
+#endif
+
+ /*
+ * Parse the global options
+ */
+ bool fShowLogo = false;
+ bool fShowHelp = false;
+ int iCmd = 1;
+ int iCmdArg;
+ const char *pszSettingsPw = NULL;
+ const char *pszSettingsPwFile = NULL;
+#ifndef VBOX_ONLY_DOCS
+ int cResponseFileArgs = 0;
+ char **papszResponseFileArgs = NULL;
+ char **papszNewArgv = NULL;
+#endif
+
+ for (int i = 1; i < argc || argc <= iCmd; i++)
+ {
+ if ( argc <= iCmd
+ || !strcmp(argv[i], "help")
+ || !strcmp(argv[i], "--help")
+ || !strcmp(argv[i], "-?")
+ || !strcmp(argv[i], "-h")
+ || !strcmp(argv[i], "-help"))
+ {
+ if (i >= argc - 1)
+ {
+ showLogo(g_pStdOut);
+ printUsage(USAGE_ALL, ~0U, g_pStdOut);
+ return 0;
+ }
+ fShowLogo = true;
+ fShowHelp = true;
+ iCmd++;
+ continue;
+ }
+
+#ifndef VBOX_ONLY_DOCS
+ if ( !strcmp(argv[i], "-V")
+ || !strcmp(argv[i], "--version")
+ || !strcmp(argv[i], "-v") /* deprecated */
+ || !strcmp(argv[i], "-version") /* deprecated */
+ || !strcmp(argv[i], "-Version") /* deprecated */)
+ {
+ /* Print version number, and do nothing else. */
+ RTPrintf("%sr%u\n", VBOX_VERSION_STRING, RTBldCfgRevision());
+ return 0;
+ }
+#endif
+
+ if ( !strcmp(argv[i], "--dumpopts")
+ || !strcmp(argv[i], "-dumpopts") /* deprecated */)
+ {
+ /* Special option to dump really all commands,
+ * even the ones not understood on this platform. */
+ printUsage(USAGE_DUMPOPTS, ~0U, g_pStdOut);
+ return 0;
+ }
+
+ if ( !strcmp(argv[i], "--nologo")
+ || !strcmp(argv[i], "-q")
+ || !strcmp(argv[i], "-nologo") /* deprecated */)
+ {
+ /* suppress the logo */
+ fShowLogo = false;
+ iCmd++;
+ }
+ else if ( !strcmp(argv[i], "--detailed-progress")
+ || !strcmp(argv[i], "-d"))
+ {
+ /* detailed progress report */
+ g_fDetailedProgress = true;
+ iCmd++;
+ }
+ else if (!strcmp(argv[i], "--settingspw"))
+ {
+ if (i >= argc - 1)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Password expected");
+ /* password for certain settings */
+ pszSettingsPw = argv[i + 1];
+ iCmd += 2;
+ }
+ else if (!strcmp(argv[i], "--settingspwfile"))
+ {
+ if (i >= argc-1)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No password file specified");
+ pszSettingsPwFile = argv[i+1];
+ iCmd += 2;
+ }
+#ifndef VBOX_ONLY_DOCS
+ else if (argv[i][0] == '@')
+ {
+ if (papszResponseFileArgs)
+ return RTMsgErrorExitFailure("Only one response file allowed");
+
+ /* Load response file, making sure it's valid UTF-8. */
+ char *pszResponseFile;
+ size_t cbResponseFile;
+ vrc = RTFileReadAllEx(&argv[i][1], 0, RTFOFF_MAX, RTFILE_RDALL_O_DENY_NONE | RTFILE_RDALL_F_TRAILING_ZERO_BYTE,
+ (void **)&pszResponseFile, &cbResponseFile);
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExitFailure("Error reading response file '%s': %Rrc", &argv[i][1], vrc);
+ vrc = RTStrValidateEncoding(pszResponseFile);
+ if (RT_FAILURE(vrc))
+ {
+ RTFileReadAllFree(pszResponseFile, cbResponseFile);
+ return RTMsgErrorExitFailure("Invalid response file ('%s') encoding: %Rrc", &argv[i][1], vrc);
+ }
+
+ /* Parse it. */
+ vrc = RTGetOptArgvFromString(&papszResponseFileArgs, &cResponseFileArgs, pszResponseFile,
+ RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
+ RTFileReadAllFree(pszResponseFile, cbResponseFile);
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExitFailure("Failed to parse response file '%s' (bourne shell style): %Rrc", &argv[i][1], vrc);
+
+ /* Construct new argv+argc with the response file arguments inserted. */
+ int cNewArgs = argc + cResponseFileArgs;
+ papszNewArgv = (char **)RTMemAllocZ((cNewArgs + 2) * sizeof(papszNewArgv[0]));
+ if (!papszNewArgv)
+ return RTMsgErrorExitFailure("out of memory");
+ memcpy(&papszNewArgv[0], &argv[0], sizeof(argv[0]) * (i + 1));
+ memcpy(&papszNewArgv[i + 1], papszResponseFileArgs, sizeof(argv[0]) * cResponseFileArgs);
+ memcpy(&papszNewArgv[i + 1 + cResponseFileArgs], &argv[i + 1], sizeof(argv[0]) * (argc - i - 1 + 1));
+ argv = papszNewArgv;
+ argc = argc + cResponseFileArgs;
+
+ iCmd++;
+ }
+#endif
+ else
+ break;
+ }
+
+ iCmdArg = iCmd + 1;
+
+ /*
+ * Show the logo and lookup the command and deal with fShowHelp = true.
+ */
+ if (fShowLogo)
+ showLogo(g_pStdOut);
+
+#ifndef VBOX_ONLY_DOCS
+ PCVBMGCMD pCmd = lookupCommand(argv[iCmd]);
+ if (pCmd && pCmd->enmCmdHelp != VBMG_CMD_TODO)
+ setCurrentCommand(pCmd->enmCmdHelp);
+
+ if ( pCmd
+ && ( fShowHelp
+ || ( argc - iCmdArg == 0
+ && pCmd->enmHelpCat != 0)))
+ {
+ if (pCmd->enmCmdHelp == VBMG_CMD_TODO)
+ printUsage(pCmd->enmHelpCat, ~0U, g_pStdOut);
+ else if (fShowHelp)
+ printHelp(g_pStdOut);
+ else
+ printUsage(g_pStdOut);
+ return RTEXITCODE_FAILURE; /* error */
+ }
+ if (!pCmd)
+ {
+ if (!strcmp(argv[iCmd], "commands"))
+ {
+ RTPrintf("commands:\n");
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aCommands); i++)
+ if ( i == 0 /* skip backwards compatibility entries */
+ || g_aCommands[i].enmHelpCat != g_aCommands[i - 1].enmHelpCat)
+ RTPrintf(" %s\n", g_aCommands[i].pszCommand);
+ return RTEXITCODE_SUCCESS;
+ }
+ return errorSyntax(USAGE_ALL, "Invalid command '%s'", argv[iCmd]);
+ }
+
+ RTEXITCODE rcExit;
+ if (!(pCmd->fFlags & VBMG_CMD_F_NO_COM))
+ {
+ /*
+ * Initialize COM.
+ */
+ using namespace com;
+ HRESULT hrc = com::Initialize();
+ if (FAILED(hrc))
+ {
+# ifdef VBOX_WITH_XPCOM
+ if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
+ {
+ char szHome[RTPATH_MAX] = "";
+ com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
+ return RTMsgErrorExit(RTEXITCODE_FAILURE,
+ "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
+ }
+# endif
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM! (hrc=%Rhrc)", hrc);
+ }
+
+
+ /*
+ * Get the remote VirtualBox object and create a local session object.
+ */
+ rcExit = RTEXITCODE_FAILURE;
+ ComPtr<IVirtualBoxClient> virtualBoxClient;
+ ComPtr<IVirtualBox> virtualBox;
+ hrc = virtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
+ if (SUCCEEDED(hrc))
+ hrc = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ ComPtr<ISession> session;
+ hrc = session.createInprocObject(CLSID_Session);
+ if (SUCCEEDED(hrc))
+ {
+ /* Session secret. */
+ if (pszSettingsPw)
+ CHECK_ERROR2I_STMT(virtualBox, SetSettingsSecret(Bstr(pszSettingsPw).raw()), rcExit = RTEXITCODE_FAILURE);
+ else if (pszSettingsPwFile)
+ rcExit = settingsPasswordFile(virtualBox, pszSettingsPwFile);
+ else
+ rcExit = RTEXITCODE_SUCCESS;
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /*
+ * Call the handler.
+ */
+ HandlerArg handlerArg = { argc - iCmdArg, &argv[iCmdArg], virtualBox, session };
+ rcExit = pCmd->pfnHandler(&handlerArg);
+
+ /* Although all handlers should always close the session if they open it,
+ * we do it here just in case if some of the handlers contains a bug --
+ * leaving the direct session not closed will turn the machine state to
+ * Aborted which may have unwanted side effects like killing the saved
+ * state file (if the machine was in the Saved state before). */
+ session->UnlockMachine();
+ }
+
+ NativeEventQueue::getMainEventQueue()->processEventQueue(0);
+ }
+ else
+ {
+ com::ErrorInfo info;
+ RTMsgError("Failed to create a session object!");
+ if (!info.isFullAvailable() && !info.isBasicAvailable())
+ com::GluePrintRCMessage(hrc);
+ else
+ com::GluePrintErrorInfo(info);
+ }
+ }
+ else
+ {
+ com::ErrorInfo info;
+ RTMsgError("Failed to create the VirtualBox object!");
+ if (!info.isFullAvailable() && !info.isBasicAvailable())
+ {
+ com::GluePrintRCMessage(hrc);
+ RTMsgError("Most likely, the VirtualBox COM server is not running or failed to start.");
+ }
+ else
+ com::GluePrintErrorInfo(info);
+ }
+
+ /*
+ * Terminate COM, make sure the virtualBox object has been released.
+ */
+ virtualBox.setNull();
+ virtualBoxClient.setNull();
+ NativeEventQueue::getMainEventQueue()->processEventQueue(0);
+ com::Shutdown();
+ }
+ else
+ {
+ /*
+ * The command needs no COM.
+ */
+ HandlerArg handlerArg;
+ handlerArg.argc = argc - iCmdArg;
+ handlerArg.argv = &argv[iCmdArg];
+ rcExit = pCmd->pfnHandler(&handlerArg);
+ }
+
+ if (papszResponseFileArgs)
+ {
+ RTGetOptArgvFree(papszResponseFileArgs);
+ RTMemFree(papszNewArgv);
+ }
+
+ return rcExit;
+#else /* VBOX_ONLY_DOCS */
+ return RTEXITCODE_SUCCESS;
+#endif /* VBOX_ONLY_DOCS */
+}
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManage.h b/src/VBox/Frontends/VBoxManage/VBoxManage.h
new file mode 100644
index 00000000..e1d619ae
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManage.h
@@ -0,0 +1,338 @@
+/* $Id: VBoxManage.h $ */
+/** @file
+ * VBoxManage - VirtualBox command-line interface, internal header file.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 VBOX_INCLUDED_SRC_VBoxManage_VBoxManage_h
+#define VBOX_INCLUDED_SRC_VBoxManage_VBoxManage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifndef VBOX_ONLY_DOCS
+#include <VBox/com/com.h>
+#include <VBox/com/ptr.h>
+#include <VBox/com/VirtualBox.h>
+#include <VBox/com/string.h>
+#include <VBox/com/array.h>
+#endif /* !VBOX_ONLY_DOCS */
+
+#include <iprt/types.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+#include <iprt/getopt.h>
+
+#ifndef VBOX_ONLY_DOCS
+# include "VBoxManageBuiltInHelp.h"
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// definitions
+//
+////////////////////////////////////////////////////////////////////////////////
+
+/** @name Syntax diagram category.
+ * @{ */
+#define USAGE_DUMPOPTS 0
+#define USAGE_LIST RT_BIT_64(0)
+#define USAGE_SHOWVMINFO RT_BIT_64(1)
+#define USAGE_REGISTERVM RT_BIT_64(2)
+#define USAGE_UNREGISTERVM RT_BIT_64(3)
+#define USAGE_CREATEVM RT_BIT_64(4)
+#define USAGE_MODIFYVM RT_BIT_64(5)
+#define USAGE_CLONEVM RT_BIT_64(6)
+#define USAGE_STARTVM RT_BIT_64(7)
+#define USAGE_CONTROLVM RT_BIT_64(8)
+#define USAGE_DISCARDSTATE RT_BIT_64(9)
+#define USAGE_SNAPSHOT RT_BIT_64(10)
+#define USAGE_CLOSEMEDIUM RT_BIT_64(11)
+#define USAGE_SHOWMEDIUMINFO RT_BIT_64(12)
+#define USAGE_CREATEMEDIUM RT_BIT_64(13)
+#define USAGE_MODIFYMEDIUM RT_BIT_64(14)
+#define USAGE_CLONEMEDIUM RT_BIT_64(15)
+#define USAGE_MOVEVM RT_BIT_64(16)
+#define USAGE_CREATEHOSTIF RT_BIT_64(17)
+#define USAGE_REMOVEHOSTIF RT_BIT_64(18)
+#define USAGE_GETEXTRADATA RT_BIT_64(19)
+#define USAGE_SETEXTRADATA RT_BIT_64(20)
+#define USAGE_SETPROPERTY RT_BIT_64(21)
+#define USAGE_USBFILTER (RT_BIT_64(22) | RT_BIT_64(23) | RT_BIT_64(24))
+#define USAGE_USBFILTER_ADD RT_BIT_64(22)
+#define USAGE_USBFILTER_MODIFY RT_BIT_64(23)
+#define USAGE_USBFILTER_REMOVE RT_BIT_64(24)
+#define USAGE_SHAREDFOLDER (RT_BIT_64(25) | RT_BIT_64(26))
+#define USAGE_SHAREDFOLDER_ADD RT_BIT_64(25)
+#define USAGE_SHAREDFOLDER_REMOVE RT_BIT_64(26)
+#define USAGE_UNATTENDED RT_BIT_64(27)
+#define USAGE_MEDIUMIO RT_BIT_64(28)
+#define USAGE_LOADSYMS RT_BIT_64(29)
+#define USAGE_LOADMAP RT_BIT_64(30)
+#define USAGE_SETHDUUID RT_BIT_64(31)
+#define USAGE_CONVERTFROMRAW RT_BIT_64(32)
+#define USAGE_LISTPARTITIONS RT_BIT_64(33)
+#define USAGE_CREATERAWVMDK RT_BIT_64(34)
+#define USAGE_DEBUGVM RT_BIT_64(35)
+#define USAGE_ADOPTSTATE RT_BIT_64(36)
+#define USAGE_MODINSTALL RT_BIT_64(37)
+#define USAGE_MODUNINSTALL RT_BIT_64(38)
+#define USAGE_RENAMEVMDK RT_BIT_64(39)
+#ifdef VBOX_WITH_GUEST_PROPS
+# define USAGE_GUESTPROPERTY RT_BIT_64(40)
+#endif /* VBOX_WITH_GUEST_PROPS defined */
+#define USAGE_CONVERTTORAW RT_BIT_64(41)
+#define USAGE_METRICS RT_BIT_64(42)
+#define USAGE_CONVERTHD RT_BIT_64(43)
+#define USAGE_IMPORTAPPLIANCE RT_BIT_64(44)
+#define USAGE_EXPORTAPPLIANCE RT_BIT_64(45)
+#define USAGE_HOSTONLYIFS RT_BIT_64(46)
+#define USAGE_DHCPSERVER RT_BIT_64(47)
+#define USAGE_DUMPHDINFO RT_BIT_64(48)
+#define USAGE_STORAGEATTACH RT_BIT_64(49)
+#define USAGE_STORAGECONTROLLER RT_BIT_64(50)
+#ifdef VBOX_WITH_GUEST_CONTROL
+# define USAGE_GUESTCONTROL RT_BIT_64(51)
+#endif /* VBOX_WITH_GUEST_CONTROL defined */
+#define USAGE_DEBUGLOG RT_BIT_64(52)
+#define USAGE_SETHDPARENTUUID RT_BIT_64(53)
+#define USAGE_PASSWORDHASH RT_BIT_64(54)
+#define USAGE_EXTPACK RT_BIT_64(55)
+#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_MEDIUMPROPERTY RT_BIT_64(60)
+#define USAGE_ENCRYPTMEDIUM RT_BIT_64(61)
+#define USAGE_MEDIUMENCCHKPWD RT_BIT_64(62)
+#define USAGE_USBDEVSOURCE RT_BIT_64(63)
+#define USAGE_ALL (~(uint64_t)0)
+/** @} */
+
+#ifdef VBOX_WITH_GUEST_CONTROL
+# define USAGE_GSTCTRL_RUN RT_BIT(0)
+# define USAGE_GSTCTRL_START RT_BIT(1)
+# define USAGE_GSTCTRL_COPYFROM RT_BIT(2)
+# define USAGE_GSTCTRL_COPYTO RT_BIT(3)
+# define USAGE_GSTCTRL_MKDIR RT_BIT(4)
+# define USAGE_GSTCTRL_RMDIR RT_BIT(5)
+# define USAGE_GSTCTRL_RM RT_BIT(6)
+# define USAGE_GSTCTRL_MV RT_BIT(7)
+# define USAGE_GSTCTRL_MKTEMP RT_BIT(8)
+# define USAGE_GSTCTRL_LIST RT_BIT(9)
+# define USAGE_GSTCTRL_CLOSEPROCESS RT_BIT(10)
+# define USAGE_GSTCTRL_CLOSESESSION RT_BIT(11)
+# define USAGE_GSTCTRL_STAT RT_BIT(12)
+# define USAGE_GSTCTRL_UPDATEGA RT_BIT(13)
+# define USAGE_GSTCTRL_WATCH RT_BIT(14)
+#endif
+
+
+typedef uint64_t USAGECATEGORY;
+
+/** command handler argument */
+struct HandlerArg
+{
+ int argc;
+ char **argv;
+
+#ifndef VBOX_ONLY_DOCS
+ ComPtr<IVirtualBox> virtualBox;
+ ComPtr<ISession> session;
+#endif
+};
+
+/** flag whether we're in internal mode */
+extern bool g_fInternalMode;
+
+/** showVMInfo details */
+typedef enum
+{
+ VMINFO_NONE = 0,
+ VMINFO_STANDARD = 1, /**< standard details */
+ VMINFO_FULL = 2, /**< both */
+ VMINFO_MACHINEREADABLE = 3, /**< both, and make it machine readable */
+ VMINFO_COMPACT = 4
+} VMINFO_DETAILS;
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// global variables
+//
+////////////////////////////////////////////////////////////////////////////////
+
+extern bool g_fDetailedProgress; // in VBoxManage.cpp
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// prototypes
+//
+////////////////////////////////////////////////////////////////////////////////
+
+/* VBoxManageHelp.cpp */
+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 fCategory, PRTSTREAM pStrm);
+
+#ifndef VBOX_ONLY_DOCS
+void setCurrentCommand(enum HELP_CMD_VBOXMANAGE enmCommand);
+void setCurrentSubcommand(uint64_t fCurSubcommandScope);
+
+void printUsage(PRTSTREAM pStrm);
+void printHelp(PRTSTREAM pStrm);
+RTEXITCODE errorNoSubcommand(void);
+RTEXITCODE errorUnknownSubcommand(const char *pszSubCmd);
+RTEXITCODE errorTooManyParameters(char **papszArgs);
+RTEXITCODE errorGetOpt(int rcGetOpt, union RTGETOPTUNION const *pValueUnion);
+RTEXITCODE errorSyntax(const char *pszFormat, ...);
+
+HRESULT showProgress(ComPtr<IProgress> progress);
+#endif
+
+/* VBoxManage.cpp */
+void showLogo(PRTSTREAM pStrm);
+
+#ifndef VBOX_ONLY_DOCS
+RTEXITCODE readPasswordFile(const char *pszFilename, com::Utf8Str *pPasswd);
+RTEXITCODE readPasswordFromConsole(com::Utf8Str *pPassword, const char *pszPrompt, ...);
+
+RTEXITCODE handleInternalCommands(HandlerArg *a);
+#endif /* !VBOX_ONLY_DOCS */
+
+/* VBoxManageControlVM.cpp */
+RTEXITCODE handleControlVM(HandlerArg *a);
+#ifndef VBOX_ONLY_DOCS
+unsigned int getMaxNics(IVirtualBox* vbox, IMachine* mach);
+#endif
+
+/* VBoxManageModifyVM.cpp */
+#ifndef VBOX_ONLY_DOCS
+void parseGroups(const char *pcszGroups, com::SafeArray<BSTR> *pGroups);
+#endif
+RTEXITCODE handleModifyVM(HandlerArg *a);
+
+/* VBoxManageDebugVM.cpp */
+RTEXITCODE handleDebugVM(HandlerArg *a);
+
+/* VBoxManageGuestProp.cpp */
+extern void usageGuestProperty(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2);
+
+/* VBoxManageGuestCtrl.cpp */
+extern void usageGuestControl(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2, uint32_t fSubCategory);
+
+#ifndef VBOX_ONLY_DOCS
+/* VBoxManageGuestProp.cpp */
+RTEXITCODE handleGuestProperty(HandlerArg *a);
+
+/* VBoxManageGuestCtrl.cpp */
+RTEXITCODE handleGuestControl(HandlerArg *a);
+
+/* VBoxManageVMInfo.cpp */
+HRESULT showSnapshots(ComPtr<ISnapshot> &rootSnapshot,
+ ComPtr<ISnapshot> &currentSnapshot,
+ VMINFO_DETAILS details,
+ const com::Utf8Str &prefix = "",
+ int level = 0);
+RTEXITCODE handleShowVMInfo(HandlerArg *a);
+HRESULT showVMInfo(ComPtr<IVirtualBox> pVirtualBox,
+ ComPtr<IMachine> pMachine,
+ ComPtr<ISession> pSession,
+ VMINFO_DETAILS details = VMINFO_NONE);
+const char *machineStateToName(MachineState_T machineState, bool fShort);
+HRESULT showBandwidthGroups(ComPtr<IBandwidthControl> &bwCtrl,
+ VMINFO_DETAILS details);
+
+/* VBoxManageList.cpp */
+RTEXITCODE handleList(HandlerArg *a);
+
+/* VBoxManageMetrics.cpp */
+RTEXITCODE handleMetrics(HandlerArg *a);
+
+/* VBoxManageMisc.cpp */
+RTEXITCODE handleRegisterVM(HandlerArg *a);
+RTEXITCODE handleUnregisterVM(HandlerArg *a);
+RTEXITCODE handleCreateVM(HandlerArg *a);
+RTEXITCODE handleCloneVM(HandlerArg *a);
+RTEXITCODE handleStartVM(HandlerArg *a);
+RTEXITCODE handleDiscardState(HandlerArg *a);
+RTEXITCODE handleAdoptState(HandlerArg *a);
+RTEXITCODE handleGetExtraData(HandlerArg *a);
+RTEXITCODE handleSetExtraData(HandlerArg *a);
+RTEXITCODE handleSetProperty(HandlerArg *a);
+RTEXITCODE handleSharedFolder(HandlerArg *a);
+RTEXITCODE handleExtPack(HandlerArg *a);
+RTEXITCODE handleUnattended(HandlerArg *a);
+RTEXITCODE handleMoveVM(HandlerArg *a);
+
+/* VBoxManageDisk.cpp */
+HRESULT openMedium(HandlerArg *a, const char *pszFilenameOrUuid,
+ DeviceType_T enmDevType, AccessMode_T enmAccessMode,
+ ComPtr<IMedium> &pMedium, bool fForceNewUuidOnOpen,
+ bool fSilent);
+RTEXITCODE handleCreateMedium(HandlerArg *a);
+RTEXITCODE handleModifyMedium(HandlerArg *a);
+RTEXITCODE handleCloneMedium(HandlerArg *a);
+RTEXITCODE handleMediumProperty(HandlerArg *a);
+RTEXITCODE handleEncryptMedium(HandlerArg *a);
+RTEXITCODE handleCheckMediumPassword(HandlerArg *a);
+RTEXITCODE handleConvertFromRaw(HandlerArg *a);
+HRESULT showMediumInfo(const ComPtr<IVirtualBox> &pVirtualBox,
+ const ComPtr<IMedium> &pMedium,
+ const char *pszParentUUID,
+ bool fOptLong);
+RTEXITCODE handleShowMediumInfo(HandlerArg *a);
+RTEXITCODE handleCloseMedium(HandlerArg *a);
+RTEXITCODE handleMediumIO(HandlerArg *a);
+int parseMediumType(const char *psz, MediumType_T *penmMediumType);
+int parseBool(const char *psz, bool *pb);
+
+/* VBoxManageStorageController.cpp */
+RTEXITCODE handleStorageAttach(HandlerArg *a);
+RTEXITCODE handleStorageController(HandlerArg *a);
+
+// VBoxManageImport.cpp
+RTEXITCODE handleImportAppliance(HandlerArg *a);
+RTEXITCODE handleExportAppliance(HandlerArg *a);
+
+// VBoxManageSnapshot.cpp
+RTEXITCODE handleSnapshot(HandlerArg *a);
+
+/* VBoxManageUSB.cpp */
+RTEXITCODE handleUSBFilter(HandlerArg *a);
+RTEXITCODE handleUSBDevSource(HandlerArg *a);
+
+/* VBoxManageHostonly.cpp */
+RTEXITCODE handleHostonlyIf(HandlerArg *a);
+
+/* VBoxManageDHCPServer.cpp */
+RTEXITCODE handleDHCPServer(HandlerArg *a);
+
+/* VBoxManageNATNetwork.cpp */
+RTEXITCODE handleNATNetwork(HandlerArg *a);
+
+
+/* VBoxManageBandwidthControl.cpp */
+RTEXITCODE handleBandwidthControl(HandlerArg *a);
+
+#endif /* !VBOX_ONLY_DOCS */
+
+#endif /* !VBOX_INCLUDED_SRC_VBoxManage_VBoxManage_h */
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManage.rc b/src/VBox/Frontends/VBoxManage/VBoxManage.rc
new file mode 100644
index 00000000..eaa3b632
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManage.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxManage.rc $ */
+/** @file
+ * VBoxManage - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-2019 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.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_APP
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Command Line Tool\0"
+ VALUE "InternalName", "VBoxManage\0"
+ VALUE "OriginalFilename", "VBoxManage.exe\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp
new file mode 100644
index 00000000..af80898c
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp
@@ -0,0 +1,1516 @@
+/* $Id: VBoxManageAppliance.cpp $ */
+/** @file
+ * VBoxManage - The appliance-related commands.
+ */
+
+/*
+ * Copyright (C) 2009-2019 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 VBOX_ONLY_DOCS
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifndef VBOX_ONLY_DOCS
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/com/VirtualBox.h>
+
+#include <list>
+#include <map>
+#endif /* !VBOX_ONLY_DOCS */
+
+#include <iprt/stream.h>
+#include <iprt/getopt.h>
+#include <iprt/ctype.h>
+#include <iprt/path.h>
+#include <iprt/file.h>
+
+#include <VBox/log.h>
+#include <VBox/param.h>
+
+#include "VBoxManage.h"
+using namespace com;
+
+
+// funcs
+///////////////////////////////////////////////////////////////////////////////
+
+typedef std::map<Utf8Str, Utf8Str> ArgsMap; // pairs of strings like "vmname" => "newvmname"
+typedef std::map<uint32_t, ArgsMap> ArgsMapsMap; // map of maps, one for each virtual system, sorted by index
+
+typedef std::map<uint32_t, bool> IgnoresMap; // pairs of numeric description entry indices
+typedef std::map<uint32_t, IgnoresMap> IgnoresMapsMap; // map of maps, one for each virtual system, sorted by index
+
+static bool findArgValue(Utf8Str &strOut,
+ ArgsMap *pmapArgs,
+ const Utf8Str &strKey)
+{
+ if (pmapArgs)
+ {
+ ArgsMap::iterator it;
+ it = pmapArgs->find(strKey);
+ if (it != pmapArgs->end())
+ {
+ strOut = it->second;
+ pmapArgs->erase(it);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int parseImportOptions(const char *psz, com::SafeArray<ImportOptions_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, "KeepAllMACs", len))
+ options->push_back(ImportOptions_KeepAllMACs);
+ else if (!RTStrNICmp(psz, "KeepNATMACs", len))
+ options->push_back(ImportOptions_KeepNATMACs);
+ else if (!RTStrNICmp(psz, "ImportToVDI", len))
+ options->push_back(ImportOptions_ImportToVDI);
+ else
+ rc = VERR_PARSE_ERROR;
+ }
+ if (pszComma)
+ psz += len + 1;
+ else
+ psz += len;
+ }
+
+ return rc;
+}
+
+static const RTGETOPTDEF g_aImportApplianceOptions[] =
+{
+ { "--dry-run", 'n', RTGETOPT_REQ_NOTHING },
+ { "-dry-run", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
+ { "--dryrun", 'n', RTGETOPT_REQ_NOTHING },
+ { "-dryrun", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
+ { "--detailed-progress", 'P', RTGETOPT_REQ_NOTHING },
+ { "-detailed-progress", 'P', RTGETOPT_REQ_NOTHING }, // deprecated
+ { "--vsys", 's', RTGETOPT_REQ_UINT32 },
+ { "-vsys", 's', RTGETOPT_REQ_UINT32 }, // deprecated
+ { "--ostype", 'o', RTGETOPT_REQ_STRING },
+ { "-ostype", 'o', RTGETOPT_REQ_STRING }, // deprecated
+ { "--vmname", 'V', RTGETOPT_REQ_STRING },
+ { "-vmname", 'V', RTGETOPT_REQ_STRING }, // deprecated
+ { "--settingsfile", 'S', RTGETOPT_REQ_STRING },
+ { "--basefolder", 'p', RTGETOPT_REQ_STRING },
+ { "--group", 'g', RTGETOPT_REQ_STRING },
+ { "--memory", 'm', RTGETOPT_REQ_STRING },
+ { "-memory", 'm', RTGETOPT_REQ_STRING }, // deprecated
+ { "--cpus", 'c', RTGETOPT_REQ_STRING },
+ { "--description", 'd', RTGETOPT_REQ_STRING },
+ { "--eula", 'L', RTGETOPT_REQ_STRING },
+ { "-eula", 'L', RTGETOPT_REQ_STRING }, // deprecated
+ { "--unit", 'u', RTGETOPT_REQ_UINT32 },
+ { "-unit", 'u', RTGETOPT_REQ_UINT32 }, // deprecated
+ { "--ignore", 'x', RTGETOPT_REQ_NOTHING },
+ { "-ignore", 'x', RTGETOPT_REQ_NOTHING }, // deprecated
+ { "--scsitype", 'T', RTGETOPT_REQ_UINT32 },
+ { "-scsitype", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
+ { "--type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
+ { "-type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
+#if 0 /* Changing the controller is fully valid, but the current design on how
+ the params are evaluated here doesn't allow two parameter for one
+ unit. The target disk path is more important. I leave it for future
+ improvments. */
+ { "--controller", 'C', RTGETOPT_REQ_STRING },
+#endif
+ { "--disk", 'D', RTGETOPT_REQ_STRING },
+ { "--options", 'O', RTGETOPT_REQ_STRING },
+};
+
+RTEXITCODE handleImportAppliance(HandlerArg *arg)
+{
+ HRESULT rc = S_OK;
+
+ Utf8Str strOvfFilename;
+ bool fExecute = true; // if true, then we actually do the import
+ com::SafeArray<ImportOptions_T> options;
+ uint32_t ulCurVsys = (uint32_t)-1;
+ uint32_t ulCurUnit = (uint32_t)-1;
+ // for each --vsys X command, maintain a map of command line items
+ // (we'll parse them later after interpreting the OVF, when we can
+ // actually check whether they make sense semantically)
+ ArgsMapsMap mapArgsMapsPerVsys;
+ IgnoresMapsMap mapIgnoresMapsPerVsys;
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ // start at 0 because main() has hacked both the argc and argv given to us
+ RTGetOptInit(&GetState, arg->argc, arg->argv, g_aImportApplianceOptions, RT_ELEMENTS(g_aImportApplianceOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'n': // --dry-run
+ fExecute = false;
+ break;
+
+ case 'P': // --detailed-progress
+ g_fDetailedProgress = true;
+ break;
+
+ case 's': // --vsys
+ ulCurVsys = ValueUnion.u32;
+ ulCurUnit = (uint32_t)-1;
+ break;
+
+ case 'o': // --ostype
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["ostype"] = ValueUnion.psz;
+ break;
+
+ case 'V': // --vmname
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz;
+ break;
+
+ case 'S': // --settingsfile
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["settingsfile"] = ValueUnion.psz;
+ break;
+
+ case 'p': // --basefolder
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["basefolder"] = ValueUnion.psz;
+ break;
+
+ case 'g': // --group
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["group"] = ValueUnion.psz;
+ break;
+
+ case 'd': // --description
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
+ break;
+
+ case 'L': // --eula
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
+ break;
+
+ case 'm': // --memory
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["memory"] = ValueUnion.psz;
+ break;
+
+ case 'c': // --cpus
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["cpus"] = ValueUnion.psz;
+ break;
+
+ case 'u': // --unit
+ ulCurUnit = ValueUnion.u32;
+ break;
+
+ case 'x': // --ignore
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ if (ulCurUnit == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
+ mapIgnoresMapsPerVsys[ulCurVsys][ulCurUnit] = true;
+ break;
+
+ case 'T': // --scsitype
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ if (ulCurUnit == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("scsitype%u", ulCurUnit)] = ValueUnion.psz;
+ break;
+
+ case 'C': // --controller
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ if (ulCurUnit == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("controller%u", ulCurUnit)] = ValueUnion.psz;
+ break;
+
+ case 'D': // --disk
+ if (ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ if (ulCurUnit == (uint32_t)-1)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("disk%u", ulCurUnit)] = ValueUnion.psz;
+ break;
+
+ case 'O': // --options
+ if (RT_FAILURE(parseImportOptions(ValueUnion.psz, &options)))
+ return errorArgument("Invalid import options '%s'\n", ValueUnion.psz);
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (strOvfFilename.isEmpty())
+ strOvfFilename = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_PRINT(c))
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid option -%c", c);
+ else
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid option case %i", c);
+ }
+ else if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "unknown option: %s\n", ValueUnion.psz);
+ else if (ValueUnion.pDef)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ else
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "error: %Rrs", c);
+ }
+ }
+
+ if (strOvfFilename.isEmpty())
+ return errorSyntax(USAGE_IMPORTAPPLIANCE, "Not enough arguments for \"import\" command.");
+
+ do
+ {
+ ComPtr<IAppliance> pAppliance;
+ CHECK_ERROR_BREAK(arg->virtualBox, CreateAppliance(pAppliance.asOutParam()));
+
+ char *pszAbsFilePath;
+ if (strOvfFilename.startsWith("S3://", RTCString::CaseInsensitive) ||
+ strOvfFilename.startsWith("SunCloud://", RTCString::CaseInsensitive) ||
+ strOvfFilename.startsWith("webdav://", RTCString::CaseInsensitive))
+ pszAbsFilePath = RTStrDup(strOvfFilename.c_str());
+ else
+ pszAbsFilePath = RTPathAbsDup(strOvfFilename.c_str());
+ ComPtr<IProgress> progressRead;
+ CHECK_ERROR_BREAK(pAppliance, Read(Bstr(pszAbsFilePath).raw(),
+ progressRead.asOutParam()));
+ RTStrFree(pszAbsFilePath);
+
+ rc = showProgress(progressRead);
+ CHECK_PROGRESS_ERROR_RET(progressRead, ("Appliance read failed"), RTEXITCODE_FAILURE);
+
+ Bstr path; /* fetch the path, there is stuff like username/password removed if any */
+ CHECK_ERROR_BREAK(pAppliance, COMGETTER(Path)(path.asOutParam()));
+ // call interpret(); this can yield both warnings and errors, so we need
+ // to tinker with the error info a bit
+ RTStrmPrintf(g_pStdErr, "Interpreting %ls...\n", path.raw());
+ rc = pAppliance->Interpret();
+ com::ErrorInfo info0(pAppliance, COM_IIDOF(IAppliance));
+
+ com::SafeArray<BSTR> aWarnings;
+ if (SUCCEEDED(pAppliance->GetWarnings(ComSafeArrayAsOutParam(aWarnings))))
+ {
+ size_t cWarnings = aWarnings.size();
+ for (unsigned i = 0; i < cWarnings; ++i)
+ {
+ Bstr bstrWarning(aWarnings[i]);
+ RTMsgWarning("%ls.", bstrWarning.raw());
+ }
+ }
+
+ if (FAILED(rc)) // during interpret, after printing warnings
+ {
+ com::GluePrintErrorInfo(info0);
+ com::GluePrintErrorContext("Interpret", __FILE__, __LINE__);
+ break;
+ }
+
+ RTStrmPrintf(g_pStdErr, "OK.\n");
+
+ // fetch all disks
+ com::SafeArray<BSTR> retDisks;
+ CHECK_ERROR_BREAK(pAppliance,
+ COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
+ if (retDisks.size() > 0)
+ {
+ RTPrintf("Disks:\n");
+ for (unsigned i = 0; i < retDisks.size(); i++)
+ RTPrintf(" %ls\n", retDisks[i]);
+ RTPrintf("\n");
+ }
+
+ // fetch virtual system descriptions
+ com::SafeIfaceArray<IVirtualSystemDescription> aVirtualSystemDescriptions;
+ CHECK_ERROR_BREAK(pAppliance,
+ COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
+
+ size_t cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
+
+ // match command line arguments with virtual system descriptions;
+ // this is only to sort out invalid indices at this time
+ ArgsMapsMap::const_iterator it;
+ for (it = mapArgsMapsPerVsys.begin();
+ it != mapArgsMapsPerVsys.end();
+ ++it)
+ {
+ uint32_t ulVsys = it->first;
+ if (ulVsys >= cVirtualSystemDescriptions)
+ return errorSyntax(USAGE_IMPORTAPPLIANCE,
+ "Invalid index %RI32 with -vsys option; the OVF contains only %zu virtual system(s).",
+ ulVsys, cVirtualSystemDescriptions);
+ }
+
+ uint32_t cLicensesInTheWay = 0;
+
+ // dump virtual system descriptions and match command-line arguments
+ if (cVirtualSystemDescriptions > 0)
+ {
+ for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
+ {
+ com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
+ com::SafeArray<BSTR> aRefs;
+ com::SafeArray<BSTR> aOvfValues;
+ com::SafeArray<BSTR> aVBoxValues;
+ com::SafeArray<BSTR> aExtraConfigValues;
+ CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
+ GetDescription(ComSafeArrayAsOutParam(retTypes),
+ ComSafeArrayAsOutParam(aRefs),
+ ComSafeArrayAsOutParam(aOvfValues),
+ ComSafeArrayAsOutParam(aVBoxValues),
+ ComSafeArrayAsOutParam(aExtraConfigValues)));
+
+ RTPrintf("Virtual system %u:\n", i);
+
+ // look up the corresponding command line options, if any
+ ArgsMap *pmapArgs = NULL;
+ ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
+ if (itm != mapArgsMapsPerVsys.end())
+ pmapArgs = &itm->second;
+
+ // this collects the final values for setFinalValues()
+ com::SafeArray<BOOL> aEnabled(retTypes.size());
+ com::SafeArray<BSTR> aFinalValues(retTypes.size());
+
+ for (unsigned a = 0; a < retTypes.size(); ++a)
+ {
+ VirtualSystemDescriptionType_T t = retTypes[a];
+
+ Utf8Str strOverride;
+
+ Bstr bstrFinalValue = aVBoxValues[a];
+
+ bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a];
+
+ aEnabled[a] = true;
+
+ switch (t)
+ {
+ case VirtualSystemDescriptionType_OS:
+ if (findArgValue(strOverride, pmapArgs, "ostype"))
+ {
+ bstrFinalValue = strOverride;
+ RTPrintf("%2u: OS type specified with --ostype: \"%ls\"\n",
+ a, bstrFinalValue.raw());
+ }
+ else
+ RTPrintf("%2u: Suggested OS type: \"%ls\""
+ "\n (change with \"--vsys %u --ostype <type>\"; use \"list ostypes\" to list all possible values)\n",
+ a, bstrFinalValue.raw(), i);
+ break;
+
+ case VirtualSystemDescriptionType_Name:
+ if (findArgValue(strOverride, pmapArgs, "vmname"))
+ {
+ bstrFinalValue = strOverride;
+ RTPrintf("%2u: VM name specified with --vmname: \"%ls\"\n",
+ a, bstrFinalValue.raw());
+ }
+ else
+ RTPrintf("%2u: Suggested VM name \"%ls\""
+ "\n (change with \"--vsys %u --vmname <name>\")\n",
+ a, bstrFinalValue.raw(), i);
+ break;
+
+ case VirtualSystemDescriptionType_Product:
+ RTPrintf("%2u: Product (ignored): %ls\n",
+ a, aVBoxValues[a]);
+ break;
+
+ case VirtualSystemDescriptionType_ProductUrl:
+ RTPrintf("%2u: ProductUrl (ignored): %ls\n",
+ a, aVBoxValues[a]);
+ break;
+
+ case VirtualSystemDescriptionType_Vendor:
+ RTPrintf("%2u: Vendor (ignored): %ls\n",
+ a, aVBoxValues[a]);
+ break;
+
+ case VirtualSystemDescriptionType_VendorUrl:
+ RTPrintf("%2u: VendorUrl (ignored): %ls\n",
+ a, aVBoxValues[a]);
+ break;
+
+ case VirtualSystemDescriptionType_Version:
+ RTPrintf("%2u: Version (ignored): %ls\n",
+ a, aVBoxValues[a]);
+ break;
+
+ case VirtualSystemDescriptionType_Description:
+ if (findArgValue(strOverride, pmapArgs, "description"))
+ {
+ bstrFinalValue = strOverride;
+ RTPrintf("%2u: Description specified with --description: \"%ls\"\n",
+ a, bstrFinalValue.raw());
+ }
+ else
+ RTPrintf("%2u: Description \"%ls\""
+ "\n (change with \"--vsys %u --description <desc>\")\n",
+ a, bstrFinalValue.raw(), i);
+ break;
+
+ case VirtualSystemDescriptionType_License:
+ ++cLicensesInTheWay;
+ if (findArgValue(strOverride, pmapArgs, "eula"))
+ {
+ if (strOverride == "show")
+ {
+ RTPrintf("%2u: End-user license agreement"
+ "\n (accept with \"--vsys %u --eula accept\"):"
+ "\n\n%ls\n\n",
+ a, i, bstrFinalValue.raw());
+ }
+ else if (strOverride == "accept")
+ {
+ RTPrintf("%2u: End-user license agreement (accepted)\n",
+ a);
+ --cLicensesInTheWay;
+ }
+ else
+ return errorSyntax(USAGE_IMPORTAPPLIANCE,
+ "Argument to --eula must be either \"show\" or \"accept\".");
+ }
+ else
+ RTPrintf("%2u: End-user license agreement"
+ "\n (display with \"--vsys %u --eula show\";"
+ "\n accept with \"--vsys %u --eula accept\")\n",
+ a, i, i);
+ break;
+
+ case VirtualSystemDescriptionType_CPU:
+ if (findArgValue(strOverride, pmapArgs, "cpus"))
+ {
+ uint32_t cCPUs;
+ if ( strOverride.toInt(cCPUs) == VINF_SUCCESS
+ && cCPUs >= VMM_MIN_CPU_COUNT
+ && cCPUs <= VMM_MAX_CPU_COUNT
+ )
+ {
+ bstrFinalValue = strOverride;
+ RTPrintf("%2u: No. of CPUs specified with --cpus: %ls\n",
+ a, bstrFinalValue.raw());
+ }
+ else
+ return errorSyntax(USAGE_IMPORTAPPLIANCE,
+ "Argument to --cpus option must be a number greater than %d and less than %d.",
+ VMM_MIN_CPU_COUNT - 1, VMM_MAX_CPU_COUNT + 1);
+ }
+ else
+ RTPrintf("%2u: Number of CPUs: %ls\n (change with \"--vsys %u --cpus <n>\")\n",
+ a, bstrFinalValue.raw(), i);
+ break;
+
+ case VirtualSystemDescriptionType_Memory:
+ {
+ if (findArgValue(strOverride, pmapArgs, "memory"))
+ {
+ uint32_t ulMemMB;
+ if (VINF_SUCCESS == strOverride.toInt(ulMemMB))
+ {
+ bstrFinalValue = strOverride;
+ RTPrintf("%2u: Guest memory specified with --memory: %ls MB\n",
+ a, bstrFinalValue.raw());
+ }
+ else
+ return errorSyntax(USAGE_IMPORTAPPLIANCE,
+ "Argument to --memory option must be a non-negative number.");
+ }
+ else
+ RTPrintf("%2u: Guest memory: %ls MB\n (change with \"--vsys %u --memory <MB>\")\n",
+ a, bstrFinalValue.raw(), i);
+ break;
+ }
+
+ case VirtualSystemDescriptionType_HardDiskControllerIDE:
+ if (fIgnoreThis)
+ {
+ RTPrintf("%2u: IDE controller, type %ls -- disabled\n",
+ 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],
+ i, a);
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskControllerSATA:
+ if (fIgnoreThis)
+ {
+ RTPrintf("%2u: SATA controller, type %ls -- disabled\n",
+ 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],
+ i, a);
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskControllerSAS:
+ if (fIgnoreThis)
+ {
+ RTPrintf("%2u: SAS controller, type %ls -- disabled\n",
+ 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],
+ i, a);
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskControllerSCSI:
+ if (fIgnoreThis)
+ {
+ RTPrintf("%2u: SCSI controller, type %ls -- disabled\n",
+ a,
+ aVBoxValues[a]);
+ aEnabled[a] = false;
+ }
+ else
+ {
+ Utf8StrFmt strTypeArg("scsitype%u", a);
+ if (findArgValue(strOverride, pmapArgs, strTypeArg))
+ {
+ bstrFinalValue = strOverride;
+ RTPrintf("%2u: SCSI controller, type set with --unit %u --scsitype: \"%ls\"\n",
+ a,
+ a,
+ bstrFinalValue.raw());
+ }
+ else
+ RTPrintf("%2u: SCSI controller, type %ls"
+ "\n (change with \"--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}\";"
+ "\n disable with \"--vsys %u --unit %u --ignore\")\n",
+ a,
+ aVBoxValues[a],
+ i, a, i, a);
+ }
+ break;
+
+ case VirtualSystemDescriptionType_HardDiskImage:
+ if (fIgnoreThis)
+ {
+ RTPrintf("%2u: Hard disk image: source image=%ls -- disabled\n",
+ a,
+ aOvfValues[a]);
+ aEnabled[a] = false;
+ }
+ else
+ {
+ Utf8StrFmt strTypeArg("disk%u", a);
+ RTCList<ImportOptions_T> optionsList = options.toList();
+
+ bstrFinalValue = aVBoxValues[a];
+
+ if (findArgValue(strOverride, pmapArgs, strTypeArg))
+ {
+ if (!optionsList.contains(ImportOptions_ImportToVDI))
+ {
+ RTUUID uuid;
+ /* Check if this is a uuid. If so, don't touch. */
+ int vrc = RTUuidFromStr(&uuid, strOverride.c_str());
+ if (vrc != VINF_SUCCESS)
+ {
+ /* Make the path absolute. */
+ if (!RTPathStartsWithRoot(strOverride.c_str()))
+ {
+ char pszPwd[RTPATH_MAX];
+ vrc = RTPathGetCurrent(pszPwd, RTPATH_MAX);
+ if (RT_SUCCESS(vrc))
+ strOverride = Utf8Str(pszPwd).append(RTPATH_SLASH).append(strOverride);
+ }
+ }
+ bstrFinalValue = strOverride;
+ }
+ else
+ {
+ //print some error about incompatible command-line arguments
+ return errorSyntax(USAGE_IMPORTAPPLIANCE,
+ "Option --ImportToVDI shall not be used together with "
+ "manually set target path.");
+
+ }
+
+ RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n",
+ a,
+ aOvfValues[a],
+ bstrFinalValue.raw(),
+ aExtraConfigValues[a]);
+ }
+#if 0 /* Changing the controller is fully valid, but the current design on how
+ the params are evaluated here doesn't allow two parameter for one
+ unit. The target disk path is more important I leave it for future
+ improvments. */
+ Utf8StrFmt strTypeArg("controller%u", a);
+ if (findArgValue(strOverride, pmapArgs, strTypeArg))
+ {
+ // strOverride now has the controller index as a number, but we
+ // need a "controller=X" format string
+ strOverride = Utf8StrFmt("controller=%s", strOverride.c_str());
+ Bstr bstrExtraConfigValue = strOverride;
+ bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]);
+ RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n",
+ a,
+ aOvfValues[a],
+ aVBoxValues[a],
+ aExtraConfigValues[a]);
+ }
+#endif
+ else
+ {
+ strOverride = aVBoxValues[a];
+
+ /*
+ * Current solution isn't optimal.
+ * Better way is to provide API call for function
+ * Appliance::i_findMediumFormatFromDiskImage()
+ * and creating one new function which returns
+ * struct ovf::DiskImage for currently processed disk.
+ */
+
+ /*
+ * if user wants to convert all imported disks to VDI format
+ * we need to replace files extensions to "vdi"
+ * except CD/DVD disks
+ */
+ if (optionsList.contains(ImportOptions_ImportToVDI))
+ {
+ ComPtr<IVirtualBox> pVirtualBox = arg->virtualBox;
+ ComPtr<ISystemProperties> systemProperties;
+ com::SafeIfaceArray<IMediumFormat> mediumFormats;
+ Bstr bstrFormatName;
+
+ CHECK_ERROR(pVirtualBox,
+ COMGETTER(SystemProperties)(systemProperties.asOutParam()));
+
+ CHECK_ERROR(systemProperties,
+ COMGETTER(MediumFormats)(ComSafeArrayAsOutParam(mediumFormats)));
+
+ /* go through all supported media formats and store files extensions only for RAW */
+ com::SafeArray<BSTR> extensions;
+
+ for (unsigned j = 0; j < mediumFormats.size(); ++j)
+ {
+ com::SafeArray<DeviceType_T> deviceType;
+ ComPtr<IMediumFormat> mediumFormat = mediumFormats[j];
+ CHECK_ERROR(mediumFormat, COMGETTER(Name)(bstrFormatName.asOutParam()));
+ Utf8Str strFormatName = Utf8Str(bstrFormatName);
+
+ if (strFormatName.compare("RAW", Utf8Str::CaseInsensitive) == 0)
+ {
+ /* getting files extensions for "RAW" format */
+ CHECK_ERROR(mediumFormat,
+ DescribeFileExtensions(ComSafeArrayAsOutParam(extensions),
+ ComSafeArrayAsOutParam(deviceType)));
+ break;
+ }
+ }
+
+ /* go through files extensions for RAW format and compare them with
+ * extension of current file
+ */
+ bool fReplace = true;
+
+ const char *pszExtension = RTPathSuffix(strOverride.c_str());
+ if (pszExtension)
+ pszExtension++;
+
+ for (unsigned j = 0; j < extensions.size(); ++j)
+ {
+ Bstr bstrExt(extensions[j]);
+ Utf8Str strExtension(bstrExt);
+ if(strExtension.compare(pszExtension, Utf8Str::CaseInsensitive) == 0)
+ {
+ fReplace = false;
+ break;
+ }
+ }
+
+ if (fReplace)
+ {
+ strOverride = strOverride.stripSuffix();
+ strOverride = strOverride.append(".").append("vdi");
+ }
+ }
+
+ bstrFinalValue = strOverride;
+
+ RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls"
+ "\n (change target path with \"--vsys %u --unit %u --disk path\";"
+ "\n disable with \"--vsys %u --unit %u --ignore\")\n",
+ a,
+ aOvfValues[a],
+ bstrFinalValue.raw(),
+ aExtraConfigValues[a],
+ i, a, i, a);
+ }
+ }
+ break;
+
+ case VirtualSystemDescriptionType_CDROM:
+ if (fIgnoreThis)
+ {
+ RTPrintf("%2u: CD-ROM -- disabled\n",
+ a);
+ aEnabled[a] = false;
+ }
+ else
+ RTPrintf("%2u: CD-ROM"
+ "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
+ a, i, a);
+ break;
+
+ case VirtualSystemDescriptionType_Floppy:
+ if (fIgnoreThis)
+ {
+ RTPrintf("%2u: Floppy -- disabled\n",
+ a);
+ aEnabled[a] = false;
+ }
+ else
+ RTPrintf("%2u: Floppy"
+ "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
+ a, i, a);
+ break;
+
+ case VirtualSystemDescriptionType_NetworkAdapter:
+ 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],
+ aExtraConfigValues[a]);
+ break;
+
+ case VirtualSystemDescriptionType_USBController:
+ if (fIgnoreThis)
+ {
+ RTPrintf("%2u: USB controller -- disabled\n",
+ a);
+ aEnabled[a] = false;
+ }
+ else
+ RTPrintf("%2u: USB controller"
+ "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
+ a, i, a);
+ break;
+
+ case VirtualSystemDescriptionType_SoundCard:
+ if (fIgnoreThis)
+ {
+ RTPrintf("%2u: Sound card \"%ls\" -- disabled\n",
+ a,
+ aOvfValues[a]);
+ aEnabled[a] = false;
+ }
+ else
+ RTPrintf("%2u: Sound card (appliance expects \"%ls\", can change on import)"
+ "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
+ a,
+ aOvfValues[a],
+ i,
+ a);
+ break;
+
+ case VirtualSystemDescriptionType_SettingsFile:
+ if (findArgValue(strOverride, pmapArgs, "settingsfile"))
+ {
+ bstrFinalValue = strOverride;
+ RTPrintf("%2u: VM settings file name specified with --settingsfile: \"%ls\"\n",
+ a, bstrFinalValue.raw());
+ }
+ else
+ RTPrintf("%2u: Suggested VM settings file name \"%ls\""
+ "\n (change with \"--vsys %u --settingsfile <filename>\")\n",
+ a, bstrFinalValue.raw(), i);
+ break;
+
+ case VirtualSystemDescriptionType_BaseFolder:
+ if (findArgValue(strOverride, pmapArgs, "basefolder"))
+ {
+ bstrFinalValue = strOverride;
+ RTPrintf("%2u: VM base folder specified with --basefolder: \"%ls\"\n",
+ a, bstrFinalValue.raw());
+ }
+ else
+ RTPrintf("%2u: Suggested VM base folder \"%ls\""
+ "\n (change with \"--vsys %u --basefolder <path>\")\n",
+ a, bstrFinalValue.raw(), i);
+ break;
+
+ case VirtualSystemDescriptionType_PrimaryGroup:
+ if (findArgValue(strOverride, pmapArgs, "group"))
+ {
+ bstrFinalValue = strOverride;
+ RTPrintf("%2u: VM group specified with --group: \"%ls\"\n",
+ a, bstrFinalValue.raw());
+ }
+ else
+ RTPrintf("%2u: Suggested VM group \"%ls\""
+ "\n (change with \"--vsys %u --group <group>\")\n",
+ a, bstrFinalValue.raw(), i);
+ break;
+
+ case VirtualSystemDescriptionType_CloudInstanceShape:
+ case VirtualSystemDescriptionType_CloudDomain:
+ case VirtualSystemDescriptionType_CloudBootDiskSize:
+ case VirtualSystemDescriptionType_CloudBucket:
+ case VirtualSystemDescriptionType_CloudOCIVCN:
+ case VirtualSystemDescriptionType_CloudPublicIP:
+ case VirtualSystemDescriptionType_CloudProfileName:
+ case VirtualSystemDescriptionType_CloudOCISubnet:
+ case VirtualSystemDescriptionType_CloudKeepObject:
+ case VirtualSystemDescriptionType_CloudLaunchInstance:
+ case VirtualSystemDescriptionType_Miscellaneous:
+ /** @todo VirtualSystemDescriptionType_Miscellaneous? */
+ break;
+
+ case VirtualSystemDescriptionType_Ignore:
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+ case VirtualSystemDescriptionType_32BitHack:
+#endif
+ break;
+ }
+
+ bstrFinalValue.detachTo(&aFinalValues[a]);
+ }
+
+ if (fExecute)
+ CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
+ SetFinalValues(ComSafeArrayAsInParam(aEnabled),
+ ComSafeArrayAsInParam(aFinalValues),
+ ComSafeArrayAsInParam(aExtraConfigValues)));
+
+ } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
+
+ if (cLicensesInTheWay == 1)
+ RTMsgError("Cannot import until the license agreement listed above is accepted.");
+ else if (cLicensesInTheWay > 1)
+ RTMsgError("Cannot import until the %c license agreements listed above are accepted.", cLicensesInTheWay);
+
+ if (!cLicensesInTheWay && fExecute)
+ {
+ // go!
+ ComPtr<IProgress> progress;
+ CHECK_ERROR_BREAK(pAppliance,
+ ImportMachines(ComSafeArrayAsInParam(options), progress.asOutParam()));
+
+ rc = showProgress(progress);
+ CHECK_PROGRESS_ERROR_RET(progress, ("Appliance import failed"), RTEXITCODE_FAILURE);
+
+ if (SUCCEEDED(rc))
+ RTPrintf("Successfully imported the appliance.\n");
+ }
+ } // end if (aVirtualSystemDescriptions.size() > 0)
+ } while (0);
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+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 },
+ { "--opc10", 'c', RTGETOPT_REQ_NOTHING },
+ { "--manifest", 'm', RTGETOPT_REQ_NOTHING }, // obsoleted by --options
+ { "--iso", 'I', RTGETOPT_REQ_NOTHING }, // obsoleted by --options
+ { "--vsys", 's', RTGETOPT_REQ_UINT32 },
+ { "--vmname", 'V', RTGETOPT_REQ_STRING },
+ { "--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 },
+ { "--cloud", 'C', RTGETOPT_REQ_UINT32 },
+ { "--cloudshape", 'S', RTGETOPT_REQ_STRING },
+ { "--clouddomain", 'D', RTGETOPT_REQ_STRING },
+ { "--clouddisksize", 'R', RTGETOPT_REQ_STRING },
+ { "--cloudbucket", 'B', RTGETOPT_REQ_STRING },
+ { "--cloudocivcn", 'Q', RTGETOPT_REQ_STRING },
+ { "--cloudpublicip", 'A', RTGETOPT_REQ_STRING },
+ { "--cloudprofile", 'F', RTGETOPT_REQ_STRING },
+ { "--cloudocisubnet", 'T', RTGETOPT_REQ_STRING },
+ { "--cloudkeepobject", 'K', RTGETOPT_REQ_STRING },
+ { "--cloudlaunchinstance", 'L', RTGETOPT_REQ_STRING },
+};
+
+enum
+{
+ NOT_SET, LOCAL, CLOUD
+} exportType;
+
+RTEXITCODE handleExportAppliance(HandlerArg *a)
+{
+ HRESULT rc = S_OK;
+
+ Utf8Str strOutputFile;
+ Utf8Str strOvfFormat("ovf-1.0"); // the default export version
+ bool fManifest = false; // the default
+ bool fCloud = false; // the default
+ exportType = NOT_SET;
+ bool fExportISOImages = false; // the default
+ com::SafeArray<ExportOptions_T> options;
+ std::list< ComPtr<IMachine> > llMachines;
+
+ uint32_t ulCurVsys = (uint32_t)-1;
+ // for each --vsys X command, maintain a map of command line items
+ ArgsMapsMap mapArgsMapsPerVsys;
+ do
+ {
+ 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_aExportOptions,
+ RT_ELEMENTS(g_aExportOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+
+ Utf8Str strProductUrl;
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'o': // --output
+ if (strOutputFile.length())
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "You can only specify --output once.");
+ else
+ strOutputFile = ValueUnion.psz;
+ break;
+
+ case 'l': // --legacy09/--ovf09
+ strOvfFormat = "ovf-0.9";
+ break;
+
+ case '1': // --ovf10
+ strOvfFormat = "ovf-1.0";
+ break;
+
+ case '2': // --ovf20
+ strOvfFormat = "ovf-2.0";
+ break;
+
+ case 'c': // --opc
+ strOvfFormat = "opc-1.0";
+ break;
+
+ case 'I': // --iso
+ fExportISOImages = true;
+ break;
+
+ case 'm': // --manifest
+ fManifest = true;
+ break;
+
+ case 's': // --vsys
+ if (fCloud == false && exportType == NOT_SET)
+ exportType = LOCAL;
+
+ if (exportType != LOCAL)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE,
+ "Option \"%s\" can't be used together with \"--cloud\" argument.",
+ GetState.pDef->pszLong);
+
+ ulCurVsys = ValueUnion.u32;
+ break;
+
+ case 'V': // --vmname
+ if (exportType == NOT_SET || ulCurVsys == (uint32_t)-1)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys or --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz;
+ break;
+
+ case 'p': // --product
+ if (exportType != LOCAL)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz;
+ break;
+
+ case 'P': // --producturl
+ if (exportType != LOCAL)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz;
+ break;
+
+ case 'n': // --vendor
+ if (exportType != LOCAL)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz;
+ break;
+
+ case 'N': // --vendorurl
+ if (exportType != LOCAL)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz;
+ break;
+
+ case 'v': // --version
+ if (exportType != LOCAL)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz;
+ break;
+
+ case 'd': // --description
+ if (exportType != LOCAL)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
+ break;
+
+ case 'e': // --eula
+ if (exportType != LOCAL)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
+ break;
+
+ case 'E': // --eulafile
+ if (exportType != LOCAL)
+ 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;
+
+ /*--cloud and --vsys are orthogonal, only one must be presented*/
+ case 'C': // --cloud
+ if (fCloud == false && exportType == NOT_SET)
+ {
+ fCloud = true;
+ exportType = CLOUD;
+ }
+
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE,
+ "Option \"%s\" can't be used together with \"--vsys\" argument.",
+ GetState.pDef->pszLong);
+
+ ulCurVsys = ValueUnion.u32;
+ break;
+
+ /* Cloud export settings */
+ case 'S': // --cloudshape
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["cloudshape"] = ValueUnion.psz;
+ break;
+
+ case 'D': // --clouddomain
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["clouddomain"] = ValueUnion.psz;
+ break;
+
+ case 'R': // --clouddisksize
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["clouddisksize"] = ValueUnion.psz;
+ break;
+
+ case 'B': // --cloudbucket
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["cloudbucket"] = ValueUnion.psz;
+ break;
+
+ case 'Q': // --cloudocivcn
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["cloudocivcn"] = ValueUnion.psz;
+ break;
+
+ case 'A': // --cloudpublicip
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["cloudpublicip"] = ValueUnion.psz;
+ break;
+
+ case 'F': // --cloudprofile
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["cloudprofile"] = ValueUnion.psz;
+ break;
+
+ case 'T': // --cloudocisubnet
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["cloudocisubnet"] = ValueUnion.psz;
+ break;
+
+ case 'K': // --cloudkeepobject
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["cloudkeepobject"] = ValueUnion.psz;
+ break;
+
+ case 'L': // --cloudlaunchinstance
+ if (exportType != CLOUD)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --cloud argument.",
+ GetState.pDef->pszLong);
+ mapArgsMapsPerVsys[ulCurVsys]["cloudlaunchinstance"] = ValueUnion.psz;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ Utf8Str strMachine(ValueUnion.psz);
+ // must be machine: try UUID or name
+ ComPtr<IMachine> machine;
+ CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine).raw(),
+ machine.asOutParam()));
+ if (machine)
+ llMachines.push_back(machine);
+ break;
+ }
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_GRAPH(c))
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: -%c", c);
+ else
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: %i", c);
+ }
+ else if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "unknown option: %s", ValueUnion.psz);
+ else if (ValueUnion.pDef)
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ else
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "%Rrs", c);
+ }
+
+ if (FAILED(rc))
+ break;
+ }
+
+ if (FAILED(rc))
+ break;
+
+ if (llMachines.empty())
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "At least one machine must be specified with the export command.");
+ if (!strOutputFile.length())
+ return errorSyntax(USAGE_EXPORTAPPLIANCE, "Missing --output argument with export command.");
+
+ // match command line arguments with the machines count
+ // this is only to sort out invalid indices at this time
+ ArgsMapsMap::const_iterator it;
+ for (it = mapArgsMapsPerVsys.begin();
+ it != mapArgsMapsPerVsys.end();
+ ++it)
+ {
+ uint32_t ulVsys = it->first;
+ if (ulVsys >= llMachines.size())
+ return errorSyntax(USAGE_EXPORTAPPLIANCE,
+ "Invalid index %RI32 with -vsys option; you specified only %zu virtual system(s).",
+ ulVsys, llMachines.size());
+ }
+
+ ComPtr<IAppliance> pAppliance;
+ CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
+
+ char *pszAbsFilePath = 0;
+ if (strOutputFile.startsWith("S3://", RTCString::CaseInsensitive) ||
+ strOutputFile.startsWith("SunCloud://", RTCString::CaseInsensitive) ||
+ strOutputFile.startsWith("webdav://", RTCString::CaseInsensitive) ||
+ strOutputFile.startsWith("OCI://", RTCString::CaseInsensitive))
+ pszAbsFilePath = RTStrDup(strOutputFile.c_str());
+ else
+ pszAbsFilePath = RTPathAbsDup(strOutputFile.c_str());
+
+ std::list< ComPtr<IMachine> >::iterator itM;
+ uint32_t i=0;
+ for (itM = llMachines.begin();
+ itM != llMachines.end();
+ ++itM, ++i)
+ {
+ ComPtr<IMachine> pMachine = *itM;
+ ComPtr<IVirtualSystemDescription> pVSD;
+ 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);
+ if (itm != mapArgsMapsPerVsys.end())
+ pmapArgs = &itm->second;
+ if (pmapArgs)
+ {
+ ArgsMap::iterator itD;
+ for (itD = pmapArgs->begin();
+ itD != pmapArgs->end();
+ ++itD)
+ {
+ if (itD->first == "vmname")
+ {
+ //remove default value if user has specified new name (default value is set in the ExportTo())
+ pVSD->RemoveDescriptionByType(VirtualSystemDescriptionType_Name);
+ pVSD->AddDescription(VirtualSystemDescriptionType_Name,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ }
+ else if (itD->first == "product")
+ pVSD->AddDescription(VirtualSystemDescriptionType_Product,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "producturl")
+ pVSD->AddDescription(VirtualSystemDescriptionType_ProductUrl,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "vendor")
+ pVSD->AddDescription(VirtualSystemDescriptionType_Vendor,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "vendorurl")
+ pVSD->AddDescription(VirtualSystemDescriptionType_VendorUrl,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "version")
+ 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(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "eulafile")
+ {
+ Utf8Str strContent;
+ void *pvFile;
+ size_t cbFile;
+ int irc = RTFileReadAll(itD->second.c_str(), &pvFile, &cbFile);
+ if (RT_SUCCESS(irc))
+ {
+ Bstr bstrContent((char*)pvFile, cbFile);
+ pVSD->AddDescription(VirtualSystemDescriptionType_License,
+ bstrContent.raw(),
+ bstrContent.raw());
+ RTFileReadAllFree(pvFile, cbFile);
+ }
+ else
+ {
+ RTMsgError("Cannot read license file \"%s\" which should be included in the virtual system %u.",
+ itD->second.c_str(), i);
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ /* add cloud export settings */
+ else if (itD->first == "cloudshape")
+ pVSD->AddDescription(VirtualSystemDescriptionType_CloudInstanceShape,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "clouddomain")
+ pVSD->AddDescription(VirtualSystemDescriptionType_CloudDomain,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "clouddisksize")
+ pVSD->AddDescription(VirtualSystemDescriptionType_CloudBootDiskSize,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "cloudbucket")
+ pVSD->AddDescription(VirtualSystemDescriptionType_CloudBucket,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "cloudocivcn")
+ pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCIVCN,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "cloudpublicip")
+ pVSD->AddDescription(VirtualSystemDescriptionType_CloudPublicIP,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "cloudprofile")
+ pVSD->AddDescription(VirtualSystemDescriptionType_CloudProfileName,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "cloudocisubnet")
+ pVSD->AddDescription(VirtualSystemDescriptionType_CloudOCISubnet,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "cloudkeepobject")
+ pVSD->AddDescription(VirtualSystemDescriptionType_CloudKeepObject,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ else if (itD->first == "cloudlaunchinstance")
+ pVSD->AddDescription(VirtualSystemDescriptionType_CloudLaunchInstance,
+ Bstr(itD->second).raw(),
+ Bstr(itD->second).raw());
+ }
+ }
+ }
+
+ if (FAILED(rc))
+ break;
+
+ /* Query required passwords and supply them to the appliance. */
+ com::SafeArray<BSTR> aIdentifiers;
+
+ CHECK_ERROR_BREAK(pAppliance, GetPasswordIds(ComSafeArrayAsOutParam(aIdentifiers)));
+
+ if (aIdentifiers.size() > 0)
+ {
+ com::SafeArray<BSTR> aPasswords(aIdentifiers.size());
+ RTPrintf("Enter the passwords for the following identifiers to export the apppliance:\n");
+ for (unsigned idxId = 0; idxId < aIdentifiers.size(); idxId++)
+ {
+ com::Utf8Str strPassword;
+ Bstr bstrPassword;
+ Bstr bstrId = aIdentifiers[idxId];
+
+ RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Password ID %s:", Utf8Str(bstrId).c_str());
+ if (rcExit == RTEXITCODE_FAILURE)
+ {
+ RTStrFree(pszAbsFilePath);
+ return rcExit;
+ }
+
+ bstrPassword = strPassword;
+ bstrPassword.detachTo(&aPasswords[idxId]);
+ }
+
+ CHECK_ERROR_BREAK(pAppliance, AddPasswords(ComSafeArrayAsInParam(aIdentifiers),
+ ComSafeArrayAsInParam(aPasswords)));
+ }
+
+ 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(),
+ ComSafeArrayAsInParam(options),
+ Bstr(pszAbsFilePath).raw(),
+ progress.asOutParam()));
+ RTStrFree(pszAbsFilePath);
+
+ rc = showProgress(progress);
+ CHECK_PROGRESS_ERROR_RET(progress, ("Appliance write failed"), RTEXITCODE_FAILURE);
+
+ if (SUCCEEDED(rc))
+ RTPrintf("Successfully exported %d machine(s).\n", llMachines.size());
+
+ } while (0);
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp
new file mode 100644
index 00000000..8b736c58
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageBandwidthControl.cpp
@@ -0,0 +1,373 @@
+/* $Id: VBoxManageBandwidthControl.cpp $ */
+/** @file
+ * VBoxManage - The bandwidth control related commands.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 VBOX_ONLY_DOCS
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#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>
+
+#include <iprt/err.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/ctype.h>
+#include <iprt/stream.h>
+#include <iprt/getopt.h>
+#include <VBox/log.h>
+
+#include "VBoxManage.h"
+using namespace com;
+
+
+// funcs
+///////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Parses a string in the following format "n[k|m|g|K|M|G]". Stores the value
+ * of n expressed in bytes to *pLimit. k meas kilobit, while K means kilobyte.
+ *
+ * @returns Error message or NULL if successful.
+ * @param pcszLimit The string to parse.
+ * @param pLimit Where to store the result.
+ */
+static const char *parseLimit(const char *pcszLimit, int64_t *pLimit)
+{
+ int iMultiplier = _1M;
+ char *pszNext = NULL;
+ int rc = RTStrToInt64Ex(pcszLimit, &pszNext, 10, pLimit);
+
+ switch (rc)
+ {
+ case VINF_SUCCESS:
+ break;
+ case VWRN_NUMBER_TOO_BIG:
+ return "Limit is too big\n";
+ case VWRN_TRAILING_CHARS:
+ switch (*pszNext)
+ {
+ case 'G': iMultiplier = _1G; break;
+ case 'M': iMultiplier = _1M; break;
+ case 'K': iMultiplier = _1K; break;
+ case 'g': iMultiplier = 125000000; break;
+ case 'm': iMultiplier = 125000; break;
+ case 'k': iMultiplier = 125; break;
+ default: return "Invalid unit suffix. Valid suffixes are: k, m, g, K, M, G\n";
+ }
+ break;
+ case VWRN_TRAILING_SPACES:
+ return "Trailing spaces in limit!\n";
+ case VERR_NO_DIGITS:
+ return "No digits in limit specifier\n";
+ default:
+ return "Invalid limit specifier\n";
+ }
+ if (*pLimit < 0)
+ return "Limit cannot be negative\n";
+ if (*pLimit > INT64_MAX / iMultiplier)
+ return "Limit is too big\n";
+ *pLimit *= iMultiplier;
+
+ return NULL;
+}
+
+/**
+ * Handles the 'bandwidthctl myvm add' sub-command.
+ * @returns Exit code.
+ * @param a The handler argument package.
+ * @param bwCtrl Reference to the bandwidth control interface.
+ */
+static RTEXITCODE handleBandwidthControlAdd(HandlerArg *a, ComPtr<IBandwidthControl> &bwCtrl)
+{
+ HRESULT rc = S_OK;
+ static const RTGETOPTDEF g_aBWCtlAddOptions[] =
+ {
+ { "--type", 't', RTGETOPT_REQ_STRING },
+ { "--limit", 'l', RTGETOPT_REQ_STRING }
+ };
+
+
+ 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;
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, a->argc, a->argv, g_aBWCtlAddOptions,
+ RT_ELEMENTS(g_aBWCtlAddOptions), 3, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+
+ while ( SUCCEEDED(rc)
+ && (c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 't': // bandwidth group type
+ {
+ if (ValueUnion.psz)
+ pszType = ValueUnion.psz;
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ case 'l': // limit
+ {
+ if (ValueUnion.psz)
+ {
+ const char *pcszError = parseLimit(ValueUnion.psz, &cMaxBytesPerSec);
+ if (pcszError)
+ {
+ errorArgument(pcszError);
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ default:
+ {
+ errorGetOpt(USAGE_BANDWIDTHCONTROL, c, &ValueUnion);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ }
+
+ BandwidthGroupType_T enmType;
+
+ if (!RTStrICmp(pszType, "disk"))
+ enmType = BandwidthGroupType_Disk;
+ else if (!RTStrICmp(pszType, "network"))
+ enmType = BandwidthGroupType_Network;
+ else
+ {
+ errorArgument("Invalid bandwidth group type\n");
+ return RTEXITCODE_FAILURE;
+ }
+
+ CHECK_ERROR2I_RET(bwCtrl, CreateBandwidthGroup(name.raw(), enmType, (LONG64)cMaxBytesPerSec), RTEXITCODE_FAILURE);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the 'bandwidthctl myvm set' sub-command.
+ * @returns Exit code.
+ * @param a The handler argument package.
+ * @param bwCtrl Reference to the bandwidth control interface.
+ */
+static RTEXITCODE handleBandwidthControlSet(HandlerArg *a, ComPtr<IBandwidthControl> &bwCtrl)
+{
+ HRESULT rc = S_OK;
+ static const RTGETOPTDEF g_aBWCtlAddOptions[] =
+ {
+ { "--limit", 'l', RTGETOPT_REQ_STRING }
+ };
+
+
+ Bstr name(a->argv[2]);
+ int64_t cMaxBytesPerSec = INT64_MAX;
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, a->argc, a->argv, g_aBWCtlAddOptions,
+ RT_ELEMENTS(g_aBWCtlAddOptions), 3, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+
+ while ( SUCCEEDED(rc)
+ && (c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'l': // limit
+ {
+ if (ValueUnion.psz)
+ {
+ const char *pcszError = parseLimit(ValueUnion.psz, &cMaxBytesPerSec);
+ if (pcszError)
+ {
+ errorArgument(pcszError);
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ default:
+ {
+ errorGetOpt(USAGE_BANDWIDTHCONTROL, c, &ValueUnion);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ }
+
+
+ if (cMaxBytesPerSec != INT64_MAX)
+ {
+ ComPtr<IBandwidthGroup> bwGroup;
+ CHECK_ERROR2I_RET(bwCtrl, GetBandwidthGroup(name.raw(), bwGroup.asOutParam()), RTEXITCODE_FAILURE);
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR2I_RET(bwGroup, COMSETTER(MaxBytesPerSec)((LONG64)cMaxBytesPerSec), RTEXITCODE_FAILURE);
+ }
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the 'bandwidthctl myvm remove' sub-command.
+ * @returns Exit code.
+ * @param a The handler argument package.
+ * @param bwCtrl Reference to the bandwidth control interface.
+ */
+static RTEXITCODE handleBandwidthControlRemove(HandlerArg *a, ComPtr<IBandwidthControl> &bwCtrl)
+{
+ Bstr name(a->argv[2]);
+ CHECK_ERROR2I_RET(bwCtrl, DeleteBandwidthGroup(name.raw()), RTEXITCODE_FAILURE);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the 'bandwidthctl myvm list' sub-command.
+ * @returns Exit code.
+ * @param a The handler argument package.
+ * @param bwCtrl Reference to the bandwidth control interface.
+ */
+static RTEXITCODE handleBandwidthControlList(HandlerArg *pArgs, ComPtr<IBandwidthControl> &rptrBWControl)
+{
+ static const RTGETOPTDEF g_aOptions[] =
+ {
+ { "--machinereadable", 'M', RTGETOPT_REQ_NOTHING },
+ };
+
+ VMINFO_DETAILS enmDetails = VMINFO_STANDARD;
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, g_aOptions, RT_ELEMENTS(g_aOptions), 2 /*iArg*/, 0 /*fFlags*/);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'M':
+ enmDetails = VMINFO_MACHINEREADABLE;
+ break;
+ default:
+ return errorGetOpt(USAGE_BANDWIDTHCONTROL, c, &ValueUnion);
+ }
+ }
+
+ if (FAILED(showBandwidthGroups(rptrBWControl, enmDetails)))
+ return RTEXITCODE_FAILURE;
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Handles the 'bandwidthctl' command.
+ * @returns Exit code.
+ * @param a The handler argument package.
+ */
+RTEXITCODE handleBandwidthControl(HandlerArg *a)
+{
+ HRESULT rc = S_OK;
+ ComPtr<IMachine> machine;
+ ComPtr<IBandwidthControl> bwCtrl;
+
+ if (a->argc < 2)
+ return errorSyntax(USAGE_BANDWIDTHCONTROL, "Too few parameters");
+ else if (a->argc > 7)
+ return errorSyntax(USAGE_BANDWIDTHCONTROL, "Too many parameters");
+
+ /* try to find the given machine */
+ CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()), RTEXITCODE_FAILURE);
+
+ /* open a session for the VM (new or shared) */
+ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
+ SessionType_T st;
+ CHECK_ERROR_RET(a->session, COMGETTER(Type)(&st), RTEXITCODE_FAILURE);
+ bool fRunTime = (st == SessionType_Shared);
+
+ /* get the mutable session machine */
+ a->session->COMGETTER(Machine)(machine.asOutParam());
+ rc = machine->COMGETTER(BandwidthControl)(bwCtrl.asOutParam());
+ if (FAILED(rc)) goto leave;
+
+ if (!strcmp(a->argv[1], "add"))
+ {
+ if (fRunTime)
+ {
+ errorArgument("Bandwidth groups cannot be created while the VM is running\n");
+ goto leave;
+ }
+ rc = handleBandwidthControlAdd(a, bwCtrl) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL;
+ }
+ else if (!strcmp(a->argv[1], "remove"))
+ {
+ if (fRunTime)
+ {
+ errorArgument("Bandwidth groups cannot be deleted while the VM is running\n");
+ goto leave;
+ }
+ rc = handleBandwidthControlRemove(a, bwCtrl) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL;
+ }
+ else if (!strcmp(a->argv[1], "set"))
+ rc = handleBandwidthControlSet(a, bwCtrl) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL;
+ else if (!strcmp(a->argv[1], "list"))
+ rc = handleBandwidthControlList(a, bwCtrl) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL;
+ else
+ {
+ errorSyntax(USAGE_BANDWIDTHCONTROL, "Invalid parameter '%s'", Utf8Str(a->argv[1]).c_str());
+ rc = E_FAIL;
+ }
+
+ /* commit changes */
+ if (SUCCEEDED(rc))
+ CHECK_ERROR(machine, SaveSettings());
+
+leave:
+ /* it's important to always close sessions */
+ a->session->UnlockMachine();
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
+
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp
new file mode 100644
index 00000000..8cb0abe5
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp
@@ -0,0 +1,2290 @@
+/* $Id: VBoxManageControlVM.cpp $ */
+/** @file
+ * VBoxManage - Implementation of the controlvm command.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/com/VirtualBox.h>
+
+#include <iprt/ctype.h>
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/uuid.h>
+#include <iprt/file.h>
+#include <VBox/log.h>
+
+#include "VBoxManage.h"
+
+#include <list>
+
+
+/**
+ * Parses a number.
+ *
+ * @returns Valid number on success.
+ * @returns 0 if invalid number. All necessary bitching has been done.
+ * @param psz Pointer to the nic number.
+ */
+static unsigned parseNum(const char *psz, unsigned cMaxNum, const char *name)
+{
+ uint32_t u32;
+ char *pszNext;
+ int rc = RTStrToUInt32Ex(psz, &pszNext, 10, &u32);
+ if ( RT_SUCCESS(rc)
+ && *pszNext == '\0'
+ && u32 >= 1
+ && u32 <= cMaxNum)
+ return (unsigned)u32;
+ errorArgument("Invalid %s number '%s'", name, psz);
+ return 0;
+}
+
+unsigned int getMaxNics(IVirtualBox* vbox, IMachine* mach)
+{
+ ComPtr<ISystemProperties> info;
+ ChipsetType_T aChipset;
+ ULONG NetworkAdapterCount = 0;
+ HRESULT rc;
+
+ do {
+ CHECK_ERROR_BREAK(vbox, COMGETTER(SystemProperties)(info.asOutParam()));
+ CHECK_ERROR_BREAK(mach, COMGETTER(ChipsetType)(&aChipset));
+ CHECK_ERROR_BREAK(info, GetMaxNetworkAdapters(aChipset, &NetworkAdapterCount));
+
+ return (unsigned int)NetworkAdapterCount;
+ } while (0);
+
+ return 0;
+}
+
+#define KBDCHARDEF_MOD_NONE 0x00
+#define KBDCHARDEF_MOD_SHIFT 0x01
+
+typedef struct KBDCHARDEF
+{
+ uint8_t u8Scancode;
+ uint8_t u8Modifiers;
+} KBDCHARDEF;
+
+static const KBDCHARDEF g_aASCIIChars[0x80] =
+{
+ /* 0x00 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x01 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x02 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x03 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x04 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x05 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x06 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x07 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x08 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x09 ' ' */ {0x0f, KBDCHARDEF_MOD_NONE},
+ /* 0x0A ' ' */ {0x1c, KBDCHARDEF_MOD_NONE},
+ /* 0x0B ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x0C ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x0D ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x0E ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x0F ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x10 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x11 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x12 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x13 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x14 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x15 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x16 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x17 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x18 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x19 ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x1A ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x1B ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x1C ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x1D ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x1E ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x1F ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+ /* 0x20 ' ' */ {0x39, KBDCHARDEF_MOD_NONE},
+ /* 0x21 '!' */ {0x02, KBDCHARDEF_MOD_SHIFT},
+ /* 0x22 '"' */ {0x28, KBDCHARDEF_MOD_SHIFT},
+ /* 0x23 '#' */ {0x04, KBDCHARDEF_MOD_SHIFT},
+ /* 0x24 '$' */ {0x05, KBDCHARDEF_MOD_SHIFT},
+ /* 0x25 '%' */ {0x06, KBDCHARDEF_MOD_SHIFT},
+ /* 0x26 '&' */ {0x08, KBDCHARDEF_MOD_SHIFT},
+ /* 0x27 ''' */ {0x28, KBDCHARDEF_MOD_NONE},
+ /* 0x28 '(' */ {0x0a, KBDCHARDEF_MOD_SHIFT},
+ /* 0x29 ')' */ {0x0b, KBDCHARDEF_MOD_SHIFT},
+ /* 0x2A '*' */ {0x09, KBDCHARDEF_MOD_SHIFT},
+ /* 0x2B '+' */ {0x0d, KBDCHARDEF_MOD_SHIFT},
+ /* 0x2C ',' */ {0x33, KBDCHARDEF_MOD_NONE},
+ /* 0x2D '-' */ {0x0c, KBDCHARDEF_MOD_NONE},
+ /* 0x2E '.' */ {0x34, KBDCHARDEF_MOD_NONE},
+ /* 0x2F '/' */ {0x35, KBDCHARDEF_MOD_NONE},
+ /* 0x30 '0' */ {0x0b, KBDCHARDEF_MOD_NONE},
+ /* 0x31 '1' */ {0x02, KBDCHARDEF_MOD_NONE},
+ /* 0x32 '2' */ {0x03, KBDCHARDEF_MOD_NONE},
+ /* 0x33 '3' */ {0x04, KBDCHARDEF_MOD_NONE},
+ /* 0x34 '4' */ {0x05, KBDCHARDEF_MOD_NONE},
+ /* 0x35 '5' */ {0x06, KBDCHARDEF_MOD_NONE},
+ /* 0x36 '6' */ {0x07, KBDCHARDEF_MOD_NONE},
+ /* 0x37 '7' */ {0x08, KBDCHARDEF_MOD_NONE},
+ /* 0x38 '8' */ {0x09, KBDCHARDEF_MOD_NONE},
+ /* 0x39 '9' */ {0x0a, KBDCHARDEF_MOD_NONE},
+ /* 0x3A ':' */ {0x27, KBDCHARDEF_MOD_SHIFT},
+ /* 0x3B ';' */ {0x27, KBDCHARDEF_MOD_NONE},
+ /* 0x3C '<' */ {0x33, KBDCHARDEF_MOD_SHIFT},
+ /* 0x3D '=' */ {0x0d, KBDCHARDEF_MOD_NONE},
+ /* 0x3E '>' */ {0x34, KBDCHARDEF_MOD_SHIFT},
+ /* 0x3F '?' */ {0x35, KBDCHARDEF_MOD_SHIFT},
+ /* 0x40 '@' */ {0x03, KBDCHARDEF_MOD_SHIFT},
+ /* 0x41 'A' */ {0x1e, KBDCHARDEF_MOD_SHIFT},
+ /* 0x42 'B' */ {0x30, KBDCHARDEF_MOD_SHIFT},
+ /* 0x43 'C' */ {0x2e, KBDCHARDEF_MOD_SHIFT},
+ /* 0x44 'D' */ {0x20, KBDCHARDEF_MOD_SHIFT},
+ /* 0x45 'E' */ {0x12, KBDCHARDEF_MOD_SHIFT},
+ /* 0x46 'F' */ {0x21, KBDCHARDEF_MOD_SHIFT},
+ /* 0x47 'G' */ {0x22, KBDCHARDEF_MOD_SHIFT},
+ /* 0x48 'H' */ {0x23, KBDCHARDEF_MOD_SHIFT},
+ /* 0x49 'I' */ {0x17, KBDCHARDEF_MOD_SHIFT},
+ /* 0x4A 'J' */ {0x24, KBDCHARDEF_MOD_SHIFT},
+ /* 0x4B 'K' */ {0x25, KBDCHARDEF_MOD_SHIFT},
+ /* 0x4C 'L' */ {0x26, KBDCHARDEF_MOD_SHIFT},
+ /* 0x4D 'M' */ {0x32, KBDCHARDEF_MOD_SHIFT},
+ /* 0x4E 'N' */ {0x31, KBDCHARDEF_MOD_SHIFT},
+ /* 0x4F 'O' */ {0x18, KBDCHARDEF_MOD_SHIFT},
+ /* 0x50 'P' */ {0x19, KBDCHARDEF_MOD_SHIFT},
+ /* 0x51 'Q' */ {0x10, KBDCHARDEF_MOD_SHIFT},
+ /* 0x52 'R' */ {0x13, KBDCHARDEF_MOD_SHIFT},
+ /* 0x53 'S' */ {0x1f, KBDCHARDEF_MOD_SHIFT},
+ /* 0x54 'T' */ {0x14, KBDCHARDEF_MOD_SHIFT},
+ /* 0x55 'U' */ {0x16, KBDCHARDEF_MOD_SHIFT},
+ /* 0x56 'V' */ {0x2f, KBDCHARDEF_MOD_SHIFT},
+ /* 0x57 'W' */ {0x11, KBDCHARDEF_MOD_SHIFT},
+ /* 0x58 'X' */ {0x2d, KBDCHARDEF_MOD_SHIFT},
+ /* 0x59 'Y' */ {0x15, KBDCHARDEF_MOD_SHIFT},
+ /* 0x5A 'Z' */ {0x2c, KBDCHARDEF_MOD_SHIFT},
+ /* 0x5B '[' */ {0x1a, KBDCHARDEF_MOD_NONE},
+ /* 0x5C '\' */ {0x2b, KBDCHARDEF_MOD_NONE},
+ /* 0x5D ']' */ {0x1b, KBDCHARDEF_MOD_NONE},
+ /* 0x5E '^' */ {0x07, KBDCHARDEF_MOD_SHIFT},
+ /* 0x5F '_' */ {0x0c, KBDCHARDEF_MOD_SHIFT},
+ /* 0x60 '`' */ {0x28, KBDCHARDEF_MOD_NONE},
+ /* 0x61 'a' */ {0x1e, KBDCHARDEF_MOD_NONE},
+ /* 0x62 'b' */ {0x30, KBDCHARDEF_MOD_NONE},
+ /* 0x63 'c' */ {0x2e, KBDCHARDEF_MOD_NONE},
+ /* 0x64 'd' */ {0x20, KBDCHARDEF_MOD_NONE},
+ /* 0x65 'e' */ {0x12, KBDCHARDEF_MOD_NONE},
+ /* 0x66 'f' */ {0x21, KBDCHARDEF_MOD_NONE},
+ /* 0x67 'g' */ {0x22, KBDCHARDEF_MOD_NONE},
+ /* 0x68 'h' */ {0x23, KBDCHARDEF_MOD_NONE},
+ /* 0x69 'i' */ {0x17, KBDCHARDEF_MOD_NONE},
+ /* 0x6A 'j' */ {0x24, KBDCHARDEF_MOD_NONE},
+ /* 0x6B 'k' */ {0x25, KBDCHARDEF_MOD_NONE},
+ /* 0x6C 'l' */ {0x26, KBDCHARDEF_MOD_NONE},
+ /* 0x6D 'm' */ {0x32, KBDCHARDEF_MOD_NONE},
+ /* 0x6E 'n' */ {0x31, KBDCHARDEF_MOD_NONE},
+ /* 0x6F 'o' */ {0x18, KBDCHARDEF_MOD_NONE},
+ /* 0x70 'p' */ {0x19, KBDCHARDEF_MOD_NONE},
+ /* 0x71 'q' */ {0x10, KBDCHARDEF_MOD_NONE},
+ /* 0x72 'r' */ {0x13, KBDCHARDEF_MOD_NONE},
+ /* 0x73 's' */ {0x1f, KBDCHARDEF_MOD_NONE},
+ /* 0x74 't' */ {0x14, KBDCHARDEF_MOD_NONE},
+ /* 0x75 'u' */ {0x16, KBDCHARDEF_MOD_NONE},
+ /* 0x76 'v' */ {0x2f, KBDCHARDEF_MOD_NONE},
+ /* 0x77 'w' */ {0x11, KBDCHARDEF_MOD_NONE},
+ /* 0x78 'x' */ {0x2d, KBDCHARDEF_MOD_NONE},
+ /* 0x79 'y' */ {0x15, KBDCHARDEF_MOD_NONE},
+ /* 0x7A 'z' */ {0x2c, KBDCHARDEF_MOD_NONE},
+ /* 0x7B '{' */ {0x1a, KBDCHARDEF_MOD_SHIFT},
+ /* 0x7C '|' */ {0x2b, KBDCHARDEF_MOD_SHIFT},
+ /* 0x7D '}' */ {0x1b, KBDCHARDEF_MOD_SHIFT},
+ /* 0x7E '~' */ {0x29, KBDCHARDEF_MOD_SHIFT},
+ /* 0x7F ' ' */ {0x00, KBDCHARDEF_MOD_NONE},
+};
+
+static HRESULT keyboardPutScancodes(IKeyboard *pKeyboard, const std::list<LONG> &llScancodes)
+{
+ /* Send scancodes to the VM. */
+ com::SafeArray<LONG> saScancodes(llScancodes);
+
+#if 1
+ HRESULT rc = S_OK;
+ size_t i;
+ for (i = 0; i < saScancodes.size(); ++i)
+ {
+ rc = pKeyboard->PutScancode(saScancodes[i]);
+ if (FAILED(rc))
+ {
+ RTMsgError("Failed to send a scancode");
+ break;
+ }
+
+ RTThreadSleep(10); /* "Typing" too fast causes lost characters. */
+ }
+#else
+ /** @todo PutScancodes does not deliver more than 20 scancodes. */
+ ULONG codesStored = 0;
+ HRESULT rc = pKeyboard->PutScancodes(ComSafeArrayAsInParam(saScancodes),
+ &codesStored);
+ if (SUCCEEDED(rc) && codesStored < saScancodes.size())
+ {
+ RTMsgError("Only %d scancodes were stored", codesStored);
+ rc = E_FAIL;
+ }
+#endif
+
+ return rc;
+}
+
+static void keyboardCharsToScancodes(const char *pch, size_t cchMax, std::list<LONG> &llScancodes, bool *pfShift)
+{
+ size_t cchProcessed = 0;
+ const char *p = pch;
+ while (cchProcessed < cchMax)
+ {
+ ++cchProcessed;
+ const uint8_t c = (uint8_t)*p++;
+ if (c < RT_ELEMENTS(g_aASCIIChars))
+ {
+ const KBDCHARDEF *d = &g_aASCIIChars[c];
+ if (d->u8Scancode)
+ {
+ const bool fNeedShift = RT_BOOL(d->u8Modifiers & KBDCHARDEF_MOD_SHIFT);
+ if (*pfShift != fNeedShift)
+ {
+ *pfShift = fNeedShift;
+ /* Press or release the SHIFT key. */
+ llScancodes.push_back(0x2a | (fNeedShift? 0x00: 0x80));
+ }
+
+ llScancodes.push_back(d->u8Scancode);
+ llScancodes.push_back(d->u8Scancode | 0x80);
+ }
+ }
+ }
+}
+
+static HRESULT keyboardPutString(IKeyboard *pKeyboard, int argc, char **argv)
+{
+ std::list<LONG> llScancodes;
+ bool fShift = false;
+
+ /* Convert command line string(s) to the en-us keyboard scancodes. */
+ int i;
+ for (i = 1 + 1; i < argc; ++i)
+ {
+ if (!llScancodes.empty())
+ {
+ /* Insert a SPACE before the next string. */
+ llScancodes.push_back(0x39);
+ llScancodes.push_back(0x39 | 0x80);
+ }
+
+ keyboardCharsToScancodes(argv[i], strlen(argv[i]), llScancodes, &fShift);
+ }
+
+ /* Release SHIFT if pressed. */
+ if (fShift)
+ llScancodes.push_back(0x2a | 0x80);
+
+ return keyboardPutScancodes(pKeyboard, llScancodes);
+}
+
+static HRESULT keyboardPutFile(IKeyboard *pKeyboard, const char *pszFilename)
+{
+ std::list<LONG> llScancodes;
+ bool fShift = false;
+
+ RTFILE File = NIL_RTFILE;
+ int vrc = RTFileOpen(&File, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(vrc))
+ {
+ uint64_t cbFile = 0;
+ vrc = RTFileGetSize(File, &cbFile);
+ if (RT_SUCCESS(vrc))
+ {
+ const uint64_t cbFileMax = _64K;
+ if (cbFile <= cbFileMax)
+ {
+ const size_t cbBuffer = _4K;
+ char *pchBuf = (char *)RTMemAlloc(cbBuffer);
+ if (pchBuf)
+ {
+ size_t cbRemaining = (size_t)cbFile;
+ while (cbRemaining > 0)
+ {
+ const size_t cbToRead = cbRemaining > cbBuffer ? cbBuffer : cbRemaining;
+
+ size_t cbRead = 0;
+ vrc = RTFileRead(File, pchBuf, cbToRead, &cbRead);
+ if (RT_FAILURE(vrc) || cbRead == 0)
+ break;
+
+ keyboardCharsToScancodes(pchBuf, cbRead, llScancodes, &fShift);
+ cbRemaining -= cbRead;
+ }
+
+ RTMemFree(pchBuf);
+ }
+ else
+ RTMsgError("Out of memory allocating %d bytes", cbBuffer);
+ }
+ else
+ RTMsgError("File size %RI64 is greater than %RI64: '%s'", cbFile, cbFileMax, pszFilename);
+ }
+ else
+ RTMsgError("Cannot get size of file '%s': %Rrc", pszFilename, vrc);
+
+ RTFileClose(File);
+ }
+ else
+ RTMsgError("Cannot open file '%s': %Rrc", pszFilename, vrc);
+
+ /* Release SHIFT if pressed. */
+ if (fShift)
+ llScancodes.push_back(0x2a | 0x80);
+
+ return keyboardPutScancodes(pKeyboard, llScancodes);
+}
+
+
+RTEXITCODE handleControlVM(HandlerArg *a)
+{
+ using namespace com;
+ bool fNeedsSaving = false;
+ HRESULT rc;
+
+ if (a->argc < 2)
+ return errorSyntax(USAGE_CONTROLVM, "Not enough parameters");
+
+ /* try to find the given machine */
+ ComPtr<IMachine> machine;
+ CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()));
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ /* open a session for the VM */
+ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
+
+ ComPtr<IConsole> console;
+ ComPtr<IMachine> sessionMachine;
+
+ do
+ {
+ /* get the associated console */
+ CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
+ if (!console)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%s' is not currently running", a->argv[0]);
+
+ /* ... and session machine */
+ CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
+
+ /* which command? */
+ if (!strcmp(a->argv[1], "pause"))
+ {
+ CHECK_ERROR_BREAK(console, Pause());
+ }
+ else if (!strcmp(a->argv[1], "resume"))
+ {
+ CHECK_ERROR_BREAK(console, Resume());
+ }
+ else if (!strcmp(a->argv[1], "reset"))
+ {
+ CHECK_ERROR_BREAK(console, Reset());
+ }
+ else if (!strcmp(a->argv[1], "unplugcpu"))
+ {
+ if (a->argc <= 1 + 1)
+ {
+ errorArgument("Missing argument to '%s'. Expected CPU number.", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ unsigned n = parseNum(a->argv[2], 32, "CPU");
+
+ CHECK_ERROR_BREAK(sessionMachine, HotUnplugCPU(n));
+ }
+ else if (!strcmp(a->argv[1], "plugcpu"))
+ {
+ if (a->argc <= 1 + 1)
+ {
+ errorArgument("Missing argument to '%s'. Expected CPU number.", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ unsigned n = parseNum(a->argv[2], 32, "CPU");
+
+ CHECK_ERROR_BREAK(sessionMachine, HotPlugCPU(n));
+ }
+ else if (!strcmp(a->argv[1], "cpuexecutioncap"))
+ {
+ if (a->argc <= 1 + 1)
+ {
+ errorArgument("Missing argument to '%s'. Expected execution cap number.", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ unsigned n = parseNum(a->argv[2], 100, "ExecutionCap");
+
+ CHECK_ERROR_BREAK(sessionMachine, COMSETTER(CPUExecutionCap)(n));
+ }
+ else if (!strcmp(a->argv[1], "audioin"))
+ {
+ ComPtr<IAudioAdapter> adapter;
+ CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioAdapter)(adapter.asOutParam()));
+ if (adapter)
+ {
+ if (!strcmp(a->argv[2], "on"))
+ {
+ CHECK_ERROR_RET(adapter, COMSETTER(EnabledIn)(TRUE), RTEXITCODE_FAILURE);
+ }
+ else if (!strcmp(a->argv[2], "off"))
+ {
+ CHECK_ERROR_RET(adapter, COMSETTER(EnabledIn)(FALSE), RTEXITCODE_FAILURE);
+ }
+ else
+ {
+ errorArgument("Invalid value '%s'", Utf8Str(a->argv[2]).c_str());
+ rc = E_FAIL;
+ break;
+ }
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ else
+ {
+ errorArgument("audio adapter not enabled in VM configuration");
+ rc = E_FAIL;
+ break;
+ }
+ }
+ else if (!strcmp(a->argv[1], "audioout"))
+ {
+ ComPtr<IAudioAdapter> adapter;
+ CHECK_ERROR_BREAK(sessionMachine, COMGETTER(AudioAdapter)(adapter.asOutParam()));
+ if (adapter)
+ {
+ if (!strcmp(a->argv[2], "on"))
+ {
+ CHECK_ERROR_RET(adapter, COMSETTER(EnabledOut)(TRUE), RTEXITCODE_FAILURE);
+ }
+ else if (!strcmp(a->argv[2], "off"))
+ {
+ CHECK_ERROR_RET(adapter, COMSETTER(EnabledOut)(FALSE), RTEXITCODE_FAILURE);
+ }
+ else
+ {
+ errorArgument("Invalid value '%s'", Utf8Str(a->argv[2]).c_str());
+ rc = E_FAIL;
+ break;
+ }
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ else
+ {
+ errorArgument("audio adapter not enabled in VM configuration");
+ rc = E_FAIL;
+ break;
+ }
+ }
+ else if (!strcmp(a->argv[1], "clipboard"))
+ {
+ if (a->argc <= 1 + 1)
+ {
+ errorArgument("Missing argument to '%s'. Expected clipboard mode.", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ ClipboardMode_T mode = ClipboardMode_Disabled; /* Shut up MSC */
+ if (!strcmp(a->argv[2], "disabled"))
+ mode = ClipboardMode_Disabled;
+ else if (!strcmp(a->argv[2], "hosttoguest"))
+ mode = ClipboardMode_HostToGuest;
+ else if (!strcmp(a->argv[2], "guesttohost"))
+ mode = ClipboardMode_GuestToHost;
+ else if (!strcmp(a->argv[2], "bidirectional"))
+ mode = ClipboardMode_Bidirectional;
+ else
+ {
+ errorArgument("Invalid '%s' argument '%s'.", a->argv[1], a->argv[2]);
+ rc = E_FAIL;
+ }
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR_BREAK(sessionMachine, COMSETTER(ClipboardMode)(mode));
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ }
+ else if (!strcmp(a->argv[1], "draganddrop"))
+ {
+ if (a->argc <= 1 + 1)
+ {
+ errorArgument("Missing argument to '%s'. Expected drag and drop mode.", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ DnDMode_T mode = DnDMode_Disabled; /* Shup up MSC. */
+ if (!strcmp(a->argv[2], "disabled"))
+ mode = DnDMode_Disabled;
+ else if (!strcmp(a->argv[2], "hosttoguest"))
+ mode = DnDMode_HostToGuest;
+ else if (!strcmp(a->argv[2], "guesttohost"))
+ mode = DnDMode_GuestToHost;
+ else if (!strcmp(a->argv[2], "bidirectional"))
+ mode = DnDMode_Bidirectional;
+ else
+ {
+ errorArgument("Invalid '%s' argument '%s'.", a->argv[1], a->argv[2]);
+ rc = E_FAIL;
+ }
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR_BREAK(sessionMachine, COMSETTER(DnDMode)(mode));
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ }
+ else if (!strcmp(a->argv[1], "poweroff"))
+ {
+ ComPtr<IProgress> progress;
+ CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
+
+ rc = showProgress(progress);
+ CHECK_PROGRESS_ERROR(progress, ("Failed to power off machine"));
+ }
+ else if (!strcmp(a->argv[1], "savestate"))
+ {
+ /* first pause so we don't trigger a live save which needs more time/resources */
+ bool fPaused = false;
+ rc = console->Pause();
+ if (FAILED(rc))
+ {
+ bool fError = true;
+ if (rc == VBOX_E_INVALID_VM_STATE)
+ {
+ /* check if we are already paused */
+ MachineState_T machineState;
+ CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
+ /* the error code was lost by the previous instruction */
+ rc = VBOX_E_INVALID_VM_STATE;
+ if (machineState != MachineState_Paused)
+ {
+ RTMsgError("Machine in invalid state %d -- %s\n",
+ machineState, machineStateToName(machineState, false));
+ }
+ else
+ {
+ fError = false;
+ fPaused = true;
+ }
+ }
+ if (fError)
+ break;
+ }
+
+ ComPtr<IProgress> progress;
+ CHECK_ERROR(sessionMachine, SaveState(progress.asOutParam()));
+ if (FAILED(rc))
+ {
+ if (!fPaused)
+ console->Resume();
+ break;
+ }
+
+ rc = showProgress(progress);
+ CHECK_PROGRESS_ERROR(progress, ("Failed to save machine state"));
+ if (FAILED(rc))
+ {
+ if (!fPaused)
+ console->Resume();
+ }
+ }
+ else if (!strcmp(a->argv[1], "acpipowerbutton"))
+ {
+ CHECK_ERROR_BREAK(console, PowerButton());
+ }
+ else if (!strcmp(a->argv[1], "acpisleepbutton"))
+ {
+ CHECK_ERROR_BREAK(console, SleepButton());
+ }
+ else if (!strcmp(a->argv[1], "keyboardputscancode"))
+ {
+ 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)
+ {
+ errorArgument("Missing argument to '%s'. Expected IBM PC AT set 2 keyboard scancode(s) as hex byte(s).", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ std::list<LONG> llScancodes;
+
+ /* Process the command line. */
+ int i;
+ for (i = 1 + 1; i < a->argc; i++)
+ {
+ if ( RT_C_IS_XDIGIT (a->argv[i][0])
+ && RT_C_IS_XDIGIT (a->argv[i][1])
+ && a->argv[i][2] == 0)
+ {
+ uint8_t u8Scancode;
+ int irc = RTStrToUInt8Ex(a->argv[i], NULL, 16, &u8Scancode);
+ if (RT_FAILURE (irc))
+ {
+ RTMsgError("Converting '%s' returned %Rrc!", a->argv[i], rc);
+ rc = E_FAIL;
+ break;
+ }
+
+ llScancodes.push_back(u8Scancode);
+ }
+ else
+ {
+ RTMsgError("Error: '%s' is not a hex byte!", a->argv[i]);
+ rc = E_FAIL;
+ break;
+ }
+ }
+
+ if (FAILED(rc))
+ break;
+
+ rc = keyboardPutScancodes(pKeyboard, llScancodes);
+ }
+ else if (!strcmp(a->argv[1], "keyboardputstring"))
+ {
+ 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)
+ {
+ errorArgument("Missing argument to '%s'. Expected ASCII string(s).", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ rc = keyboardPutString(pKeyboard, a->argc, a->argv);
+ }
+ else if (!strcmp(a->argv[1], "keyboardputfile"))
+ {
+ 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)
+ {
+ errorArgument("Missing argument to '%s'. Expected file name.", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ rc = keyboardPutFile(pKeyboard, a->argv[2]);
+ }
+ else if (!strncmp(a->argv[1], "setlinkstate", 12))
+ {
+ /* Get the number of network adapters */
+ ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
+ unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
+ if (!n)
+ {
+ rc = E_FAIL;
+ break;
+ }
+ if (a->argc <= 1 + 1)
+ {
+ 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)
+ {
+ if (!strcmp(a->argv[2], "on"))
+ {
+ CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(TRUE));
+ }
+ else if (!strcmp(a->argv[2], "off"))
+ {
+ CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(FALSE));
+ }
+ else
+ {
+ errorArgument("Invalid link state '%s'", Utf8Str(a->argv[2]).c_str());
+ rc = E_FAIL;
+ break;
+ }
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ }
+ /* here the order in which strncmp is called is important
+ * cause nictracefile can be very well compared with
+ * nictrace and nic and thus everything will always fail
+ * if the order is changed
+ */
+ else if (!strncmp(a->argv[1], "nictracefile", 12))
+ {
+ /* Get the number of network adapters */
+ ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
+ unsigned n = parseNum(&a->argv[1][12], 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)
+ {
+ if (a->argv[2])
+ {
+ CHECK_ERROR_RET(adapter, COMSETTER(TraceFile)(Bstr(a->argv[2]).raw()), RTEXITCODE_FAILURE);
+ }
+ else
+ {
+ errorArgument("Invalid filename or filename not specified for NIC %lu", n);
+ rc = E_FAIL;
+ break;
+ }
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ else
+ RTMsgError("The NIC %d is currently disabled and thus its tracefile can't be changed", n);
+ }
+ }
+ else if (!strncmp(a->argv[1], "nictrace", 8))
+ {
+ /* Get the number of network adapters */
+ ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
+ unsigned n = parseNum(&a->argv[1][8], 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)
+ {
+ if (!strcmp(a->argv[2], "on"))
+ {
+ CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(TRUE), RTEXITCODE_FAILURE);
+ }
+ else if (!strcmp(a->argv[2], "off"))
+ {
+ CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(FALSE), RTEXITCODE_FAILURE);
+ }
+ else
+ {
+ errorArgument("Invalid nictrace%lu argument '%s'", n, Utf8Str(a->argv[2]).c_str());
+ rc = E_FAIL;
+ break;
+ }
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ else
+ RTMsgError("The NIC %d is currently disabled and thus its trace flag can't be changed", n);
+ }
+ }
+ else if( a->argc > 2
+ && !strncmp(a->argv[1], "natpf", 5))
+ {
+ /* Get the number of network adapters */
+ ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
+ unsigned n = parseNum(&a->argv[1][5], 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)
+ {
+ rc = E_FAIL;
+ break;
+ }
+ ComPtr<INATEngine> engine;
+ CHECK_ERROR(adapter, COMGETTER(NATEngine)(engine.asOutParam()));
+ if (!engine)
+ {
+ rc = E_FAIL;
+ break;
+ }
+
+ if (!strcmp(a->argv[2], "delete"))
+ {
+ if (a->argc >= 3)
+ CHECK_ERROR(engine, RemoveRedirect(Bstr(a->argv[3]).raw()));
+ }
+ else
+ {
+#define ITERATE_TO_NEXT_TERM(ch) \
+ do { \
+ while (*ch != ',') \
+ { \
+ if (*ch == 0) \
+ { \
+ return errorSyntax(USAGE_CONTROLVM, \
+ "Missing or invalid argument to '%s'", \
+ a->argv[1]); \
+ } \
+ ch++; \
+ } \
+ *ch = '\0'; \
+ ch++; \
+ } while(0)
+
+ char *strName;
+ char *strProto;
+ char *strHostIp;
+ char *strHostPort;
+ char *strGuestIp;
+ char *strGuestPort;
+ char *strRaw = RTStrDup(a->argv[2]);
+ char *ch = strRaw;
+ strName = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strProto = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strHostIp = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strHostPort = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strGuestIp = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strGuestPort = RTStrStrip(ch);
+ NATProtocol_T proto;
+ if (RTStrICmp(strProto, "udp") == 0)
+ proto = NATProtocol_UDP;
+ else if (RTStrICmp(strProto, "tcp") == 0)
+ proto = NATProtocol_TCP;
+ else
+ {
+ return errorSyntax(USAGE_CONTROLVM,
+ "Wrong rule proto '%s' specified -- only 'udp' and 'tcp' are allowed.",
+ strProto);
+ }
+ CHECK_ERROR(engine, AddRedirect(Bstr(strName).raw(), proto, Bstr(strHostIp).raw(),
+ RTStrToUInt16(strHostPort), Bstr(strGuestIp).raw(), RTStrToUInt16(strGuestPort)));
+#undef ITERATE_TO_NEXT_TERM
+ }
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ else if (!strncmp(a->argv[1], "nicproperty", 11))
+ {
+ /* Get the number of network adapters */
+ ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
+ unsigned n = parseNum(&a->argv[1][11], 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)
+ {
+ /* Parse 'name=value' */
+ char *pszProperty = RTStrDup(a->argv[2]);
+ if (pszProperty)
+ {
+ char *pDelimiter = strchr(pszProperty, '=');
+ if (pDelimiter)
+ {
+ *pDelimiter = '\0';
+
+ Bstr bstrName = pszProperty;
+ Bstr bstrValue = &pDelimiter[1];
+ CHECK_ERROR(adapter, SetProperty(bstrName.raw(), bstrValue.raw()));
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ else
+ {
+ errorArgument("Invalid nicproperty%d argument '%s'", n, a->argv[2]);
+ rc = E_FAIL;
+ }
+ RTStrFree(pszProperty);
+ }
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for nicproperty%d '%s'\n", n, a->argv[2]);
+ rc = E_FAIL;
+ }
+ if (FAILED(rc))
+ break;
+ }
+ else
+ 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));
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ 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 */
+ ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
+ unsigned n = parseNum(&a->argv[1][3], 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)
+ {
+ if (!strcmp(a->argv[2], "null"))
+ {
+ CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Null), RTEXITCODE_FAILURE);
+ }
+ else if (!strcmp(a->argv[2], "nat"))
+ {
+ if (a->argc == 4)
+ CHECK_ERROR_RET(adapter, COMSETTER(NATNetwork)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_NAT), RTEXITCODE_FAILURE);
+ }
+ else if ( !strcmp(a->argv[2], "bridged")
+ || !strcmp(a->argv[2], "hostif")) /* backward compatibility */
+ {
+ if (a->argc <= 3)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[2]);
+ rc = E_FAIL;
+ break;
+ }
+ CHECK_ERROR_RET(adapter, COMSETTER(BridgedInterface)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged), RTEXITCODE_FAILURE);
+ }
+ else if (!strcmp(a->argv[2], "intnet"))
+ {
+ if (a->argc <= 3)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[2]);
+ rc = E_FAIL;
+ break;
+ }
+ CHECK_ERROR_RET(adapter, COMSETTER(InternalNetwork)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Internal), RTEXITCODE_FAILURE);
+ }
+#if defined(VBOX_WITH_NETFLT)
+ else if (!strcmp(a->argv[2], "hostonly"))
+ {
+ if (a->argc <= 3)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[2]);
+ rc = E_FAIL;
+ break;
+ }
+ CHECK_ERROR_RET(adapter, COMSETTER(HostOnlyInterface)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly), RTEXITCODE_FAILURE);
+ }
+#endif
+ else if (!strcmp(a->argv[2], "generic"))
+ {
+ if (a->argc <= 3)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[2]);
+ rc = E_FAIL;
+ break;
+ }
+ CHECK_ERROR_RET(adapter, COMSETTER(GenericDriver)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), RTEXITCODE_FAILURE);
+ }
+ 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(NATNetwork)(Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_NATNetwork), RTEXITCODE_FAILURE);
+ }
+ /** @todo obsolete, remove eventually */
+ else if (!strcmp(a->argv[2], "vde"))
+ {
+ if (a->argc <= 3)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[2]);
+ rc = E_FAIL;
+ break;
+ }
+ CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(adapter, SetProperty(Bstr("name").raw(), Bstr(a->argv[3]).raw()), RTEXITCODE_FAILURE);
+ }
+ else
+ {
+ errorArgument("Invalid type '%s' specfied for NIC %lu", Utf8Str(a->argv[2]).c_str(), n);
+ rc = E_FAIL;
+ break;
+ }
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ else
+ RTMsgError("The NIC %d is currently disabled and thus its attachment type can't be changed", n);
+ }
+ }
+ else if ( !strcmp(a->argv[1], "vrde")
+ || !strcmp(a->argv[1], "vrdp"))
+ {
+ if (!strcmp(a->argv[1], "vrdp"))
+ RTStrmPrintf(g_pStdErr, "Warning: 'vrdp' is deprecated. Use 'vrde'.\n");
+
+ if (a->argc <= 1 + 1)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+ if (vrdeServer)
+ {
+ if (!strcmp(a->argv[2], "on"))
+ {
+ CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(TRUE));
+ }
+ else if (!strcmp(a->argv[2], "off"))
+ {
+ CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(FALSE));
+ }
+ else
+ {
+ errorArgument("Invalid remote desktop server state '%s'", Utf8Str(a->argv[2]).c_str());
+ rc = E_FAIL;
+ break;
+ }
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ }
+ else if ( !strcmp(a->argv[1], "vrdeport")
+ || !strcmp(a->argv[1], "vrdpport"))
+ {
+ if (!strcmp(a->argv[1], "vrdpport"))
+ RTStrmPrintf(g_pStdErr, "Warning: 'vrdpport' is deprecated. Use 'vrdeport'.\n");
+
+ if (a->argc <= 1 + 1)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+ if (vrdeServer)
+ {
+ Bstr ports;
+
+ if (!strcmp(a->argv[2], "default"))
+ ports = "0";
+ else
+ ports = a->argv[2];
+
+ CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), ports.raw()));
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ }
+ else if ( !strcmp(a->argv[1], "vrdevideochannelquality")
+ || !strcmp(a->argv[1], "vrdpvideochannelquality"))
+ {
+ if (!strcmp(a->argv[1], "vrdpvideochannelquality"))
+ RTStrmPrintf(g_pStdErr, "Warning: 'vrdpvideochannelquality' is deprecated. Use 'vrdevideochannelquality'.\n");
+
+ if (a->argc <= 1 + 1)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+ if (vrdeServer)
+ {
+ Bstr value = a->argv[2];
+
+ CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("VideoChannel/Quality").raw(), value.raw()));
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ }
+ else if (!strcmp(a->argv[1], "vrdeproperty"))
+ {
+ if (a->argc <= 1 + 1)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+ if (vrdeServer)
+ {
+ /* Parse 'name=value' */
+ char *pszProperty = RTStrDup(a->argv[2]);
+ if (pszProperty)
+ {
+ char *pDelimiter = strchr(pszProperty, '=');
+ if (pDelimiter)
+ {
+ *pDelimiter = '\0';
+
+ Bstr bstrName = pszProperty;
+ Bstr bstrValue = &pDelimiter[1];
+ CHECK_ERROR(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
+ if (SUCCEEDED(rc))
+ fNeedsSaving = true;
+ }
+ else
+ {
+ errorArgument("Invalid vrdeproperty argument '%s'", a->argv[2]);
+ rc = E_FAIL;
+ }
+ RTStrFree(pszProperty);
+ }
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for VRDE property '%s'\n", a->argv[2]);
+ rc = E_FAIL;
+ }
+ }
+ if (FAILED(rc))
+ {
+ break;
+ }
+ }
+ else if ( !strcmp(a->argv[1], "usbattach")
+ || !strcmp(a->argv[1], "usbdetach"))
+ {
+ if (a->argc < 3)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Not enough parameters");
+ rc = E_FAIL;
+ break;
+ }
+ else if (a->argc == 4 || a->argc > 5)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Wrong number of arguments");
+ rc = E_FAIL;
+ break;
+ }
+
+ bool attach = !strcmp(a->argv[1], "usbattach");
+
+ Bstr usbId = a->argv[2];
+ Bstr captureFilename;
+
+ if (a->argc == 5)
+ {
+ if (!strcmp(a->argv[3], "--capturefile"))
+ captureFilename = a->argv[4];
+ else
+ {
+ errorArgument("Invalid parameter '%s'", a->argv[3]);
+ rc = E_FAIL;
+ break;
+ }
+ }
+
+ Guid guid(usbId);
+ if (!guid.isValid())
+ {
+ // assume address
+ if (attach)
+ {
+ 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;
+ CHECK_ERROR_BREAK(host, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
+ dev.asOutParam()));
+ CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
+ }
+ else
+ {
+ SafeIfaceArray <IUSBDevice> coll;
+ CHECK_ERROR_BREAK(console, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
+ 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(), captureFilename.raw()));
+ else
+ {
+ ComPtr<IUSBDevice> dev;
+ CHECK_ERROR_BREAK(console, DetachUSBDevice(usbId.raw(),
+ dev.asOutParam()));
+ }
+ }
+ else if (!strcmp(a->argv[1], "setvideomodehint"))
+ {
+ if (a->argc != 5 && a->argc != 6 && a->argc != 7 && a->argc != 9)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+ bool fEnabled = true;
+ uint32_t uXRes = RTStrToUInt32(a->argv[2]);
+ uint32_t uYRes = RTStrToUInt32(a->argv[3]);
+ uint32_t uBpp = RTStrToUInt32(a->argv[4]);
+ uint32_t uDisplayIdx = 0;
+ bool fChangeOrigin = false;
+ int32_t iOriginX = 0;
+ int32_t iOriginY = 0;
+ if (a->argc >= 6)
+ uDisplayIdx = RTStrToUInt32(a->argv[5]);
+ if (a->argc >= 7)
+ {
+ int vrc = parseBool(a->argv[6], &fEnabled);
+ if (RT_FAILURE(vrc))
+ {
+ errorSyntax(USAGE_CONTROLVM, "Either \"yes\" or \"no\" is expected");
+ rc = E_FAIL;
+ break;
+ }
+ fEnabled = !RTStrICmp(a->argv[6], "yes");
+ }
+ if (a->argc == 9)
+ {
+ iOriginX = RTStrToInt32(a->argv[7]);
+ iOriginY = RTStrToInt32(a->argv[8]);
+ fChangeOrigin = true;
+ }
+
+ 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], "setscreenlayout"))
+ {
+ if (a->argc < 4)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ ComPtr<IDisplay> pDisplay;
+ CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
+ if (!pDisplay)
+ {
+ RTMsgError("Guest not running");
+ rc = E_FAIL;
+ break;
+ }
+
+ com::SafeIfaceArray<IGuestScreenInfo> aGuestScreenInfos;
+
+ /* Parse "<display> on|primary <xorigin> <yorigin> <xres> <yres> <bpp> | off" sequences. */
+ int argc = a->argc - 2;
+ char **argv = &a->argv[2];
+ while (argc >= 2)
+ {
+ ULONG aDisplay = RTStrToUInt32(argv[0]);
+ BOOL aPrimary = FALSE;
+
+ GuestMonitorStatus_T aStatus;
+ if (RTStrICmp(argv[1], "primary") == 0)
+ {
+ aStatus = GuestMonitorStatus_Enabled;
+ aPrimary = TRUE;
+ }
+ else if (RTStrICmp(argv[1], "on") == 0)
+ aStatus = GuestMonitorStatus_Enabled;
+ else if (RTStrICmp(argv[1], "off") == 0)
+ aStatus = GuestMonitorStatus_Disabled;
+ else
+ {
+ errorSyntax(USAGE_CONTROLVM, "Display status must be <on> or <off>");
+ rc = E_FAIL;
+ break;
+ }
+
+ BOOL aChangeOrigin = FALSE;
+ LONG aOriginX = 0;
+ LONG aOriginY = 0;
+ ULONG aWidth = 0;
+ ULONG aHeight = 0;
+ ULONG aBitsPerPixel = 0;
+ if (aStatus == GuestMonitorStatus_Enabled)
+ {
+ if (argc < 7)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ aChangeOrigin = TRUE;
+ aOriginX = RTStrToUInt32(argv[2]);
+ aOriginY = RTStrToUInt32(argv[3]);
+ aWidth = RTStrToUInt32(argv[4]);
+ aHeight = RTStrToUInt32(argv[5]);
+ aBitsPerPixel = RTStrToUInt32(argv[6]);
+
+ argc -= 7;
+ argv += 7;
+ }
+ else
+ {
+ argc -= 2;
+ argv += 2;
+ }
+
+ ComPtr<IGuestScreenInfo> pInfo;
+ CHECK_ERROR_BREAK(pDisplay, CreateGuestScreenInfo(aDisplay, aStatus, aPrimary, aChangeOrigin,
+ aOriginX, aOriginY, aWidth, aHeight, aBitsPerPixel,
+ pInfo.asOutParam()));
+ aGuestScreenInfos.push_back(pInfo);
+ }
+
+ if (FAILED(rc))
+ break;
+
+ CHECK_ERROR_BREAK(pDisplay, SetScreenLayout(ScreenLayoutMode_Apply, ComSafeArrayAsInParam(aGuestScreenInfos)));
+ }
+ else if (!strcmp(a->argv[1], "setcredentials"))
+ {
+ bool fAllowLocalLogon = true;
+ if ( a->argc == 7
+ || ( a->argc == 8
+ && ( !strcmp(a->argv[3], "-p")
+ || !strcmp(a->argv[3], "--passwordfile"))))
+ {
+ if ( strcmp(a->argv[5 + (a->argc - 7)], "--allowlocallogon")
+ && strcmp(a->argv[5 + (a->argc - 7)], "-allowlocallogon"))
+ {
+ errorArgument("Invalid parameter '%s'", a->argv[5]);
+ rc = E_FAIL;
+ break;
+ }
+ if (!strcmp(a->argv[6 + (a->argc - 7)], "no"))
+ fAllowLocalLogon = false;
+ }
+ else if ( a->argc != 5
+ && ( a->argc != 6
+ || ( strcmp(a->argv[3], "-p")
+ && strcmp(a->argv[3], "--passwordfile"))))
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+ Utf8Str passwd, domain;
+ if (a->argc == 5 || a->argc == 7)
+ {
+ passwd = a->argv[3];
+ domain = a->argv[4];
+ }
+ else
+ {
+ RTEXITCODE rcExit = readPasswordFile(a->argv[4], &passwd);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ {
+ rc = E_FAIL;
+ break;
+ }
+ domain = a->argv[5];
+ }
+
+ 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"))
+ {
+ Bstr uuid;
+ if (a->argc != 3)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ ComPtr<IMedium> dvdMedium;
+
+ /* unmount? */
+ if (!strcmp(a->argv[2], "none"))
+ {
+ /* nothing to do, NULL object will cause unmount */
+ }
+ /* host drive? */
+ else if (!strncmp(a->argv[2], "host:", 5))
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
+
+ rc = host->FindHostDVDDrive(Bstr(a->argv[2] + 5), dvdMedium.asOutParam());
+ if (!dvdMedium)
+ {
+ errorArgument("Invalid host DVD drive name \"%s\"",
+ a->argv[2] + 5);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ else
+ {
+ /* first assume it's a UUID */
+ uuid = a->argv[2];
+ rc = a->virtualBox->GetDVDImage(uuid, dvdMedium.asOutParam());
+ if (FAILED(rc) || !dvdMedium)
+ {
+ /* must be a filename, check if it's in the collection */
+ rc = a->virtualBox->FindDVDImage(Bstr(a->argv[2]), dvdMedium.asOutParam());
+ /* not registered, do that on the fly */
+ if (!dvdMedium)
+ {
+ Bstr emptyUUID;
+ CHECK_ERROR(a->virtualBox, OpenDVDImage(Bstr(a->argv[2]), emptyUUID, dvdMedium.asOutParam()));
+ }
+ }
+ if (!dvdMedium)
+ {
+ rc = E_FAIL;
+ break;
+ }
+ }
+
+ /** @todo generalize this, allow arbitrary number of DVD drives
+ * and as a consequence multiple attachments and different
+ * storage controllers. */
+ if (dvdMedium)
+ dvdMedium->COMGETTER(Id)(uuid.asOutParam());
+ else
+ uuid = Guid().toString();
+ CHECK_ERROR(machine, MountMedium(Bstr("IDE Controller"), 1, 0, uuid, FALSE /* aForce */));
+ }
+ else if (!strcmp(a->argv[1], "floppyattach"))
+ {
+ Bstr uuid;
+ if (a->argc != 3)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ ComPtr<IMedium> floppyMedium;
+
+ /* unmount? */
+ if (!strcmp(a->argv[2], "none"))
+ {
+ /* nothing to do, NULL object will cause unmount */
+ }
+ /* host drive? */
+ else if (!strncmp(a->argv[2], "host:", 5))
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
+ host->FindHostFloppyDrive(Bstr(a->argv[2] + 5), floppyMedium.asOutParam());
+ if (!floppyMedium)
+ {
+ errorArgument("Invalid host floppy drive name \"%s\"",
+ a->argv[2] + 5);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ else
+ {
+ /* first assume it's a UUID */
+ uuid = a->argv[2];
+ rc = a->virtualBox->GetFloppyImage(uuid, floppyMedium.asOutParam());
+ if (FAILED(rc) || !floppyMedium)
+ {
+ /* must be a filename, check if it's in the collection */
+ rc = a->virtualBox->FindFloppyImage(Bstr(a->argv[2]), floppyMedium.asOutParam());
+ /* not registered, do that on the fly */
+ if (!floppyMedium)
+ {
+ Bstr emptyUUID;
+ CHECK_ERROR(a->virtualBox, OpenFloppyImage(Bstr(a->argv[2]), emptyUUID, floppyMedium.asOutParam()));
+ }
+ }
+ if (!floppyMedium)
+ {
+ rc = E_FAIL;
+ break;
+ }
+ }
+ floppyMedium->COMGETTER(Id)(uuid.asOutParam());
+ CHECK_ERROR(machine, MountMedium(Bstr("Floppy Controller"), 0, 0, uuid, FALSE /* aForce */));
+ }
+#endif /* obsolete dvdattach/floppyattach */
+ else if (!strcmp(a->argv[1], "guestmemoryballoon"))
+ {
+ if (a->argc != 3)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+ uint32_t uVal;
+ int vrc;
+ vrc = RTStrToUInt32Ex(a->argv[2], NULL, 0, &uVal);
+ if (vrc != VINF_SUCCESS)
+ {
+ errorArgument("Error parsing guest memory balloon size '%s'", a->argv[2]);
+ rc = E_FAIL;
+ break;
+ }
+ /* guest is running; update IGuest */
+ ComPtr<IGuest> pGuest;
+ rc = console->COMGETTER(Guest)(pGuest.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ if (!pGuest)
+ {
+ RTMsgError("Guest not running");
+ rc = E_FAIL;
+ break;
+ }
+ CHECK_ERROR(pGuest, COMSETTER(MemoryBalloonSize)(uVal));
+ }
+ }
+ else if (!strcmp(a->argv[1], "teleport"))
+ {
+ Bstr bstrHostname;
+ uint32_t uMaxDowntime = 250 /*ms*/;
+ uint32_t uPort = UINT32_MAX;
+ uint32_t cMsTimeout = 0;
+ Utf8Str strPassword;
+ static const RTGETOPTDEF s_aTeleportOptions[] =
+ {
+ { "--host", 'h', RTGETOPT_REQ_STRING }, /** @todo RTGETOPT_FLAG_MANDATORY */
+ { "--hostname", 'h', RTGETOPT_REQ_STRING }, /** @todo remove this */
+ { "--maxdowntime", 'd', RTGETOPT_REQ_UINT32 },
+ { "--port", 'P', RTGETOPT_REQ_UINT32 }, /** @todo RTGETOPT_FLAG_MANDATORY */
+ { "--passwordfile", 'p', RTGETOPT_REQ_STRING },
+ { "--password", 'W', RTGETOPT_REQ_STRING },
+ { "--timeout", 't', RTGETOPT_REQ_UINT32 },
+ { "--detailed-progress", 'D', RTGETOPT_REQ_NOTHING }
+ };
+ RTGETOPTSTATE GetOptState;
+ RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTeleportOptions, RT_ELEMENTS(s_aTeleportOptions), 2, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ int ch;
+ RTGETOPTUNION Value;
+ while ( SUCCEEDED(rc)
+ && (ch = RTGetOpt(&GetOptState, &Value)))
+ {
+ switch (ch)
+ {
+ case 'h': bstrHostname = Value.psz; break;
+ case 'd': uMaxDowntime = Value.u32; break;
+ case 'D': g_fDetailedProgress = true; break;
+ case 'P': uPort = Value.u32; break;
+ case 'p':
+ {
+ RTEXITCODE rcExit = readPasswordFile(Value.psz, &strPassword);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ rc = E_FAIL;
+ break;
+ }
+ case 'W': strPassword = Value.psz; break;
+ case 't': cMsTimeout = Value.u32; break;
+ default:
+ errorGetOpt(USAGE_CONTROLVM, ch, &Value);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ if (FAILED(rc))
+ break;
+
+ ComPtr<IProgress> progress;
+ CHECK_ERROR_BREAK(console, Teleport(bstrHostname.raw(), uPort,
+ Bstr(strPassword).raw(),
+ uMaxDowntime,
+ progress.asOutParam()));
+
+ if (cMsTimeout)
+ {
+ rc = progress->COMSETTER(Timeout)(cMsTimeout);
+ if (FAILED(rc) && rc != VBOX_E_INVALID_OBJECT_STATE)
+ CHECK_ERROR_BREAK(progress, COMSETTER(Timeout)(cMsTimeout)); /* lazyness */
+ }
+
+ rc = showProgress(progress);
+ CHECK_PROGRESS_ERROR(progress, ("Teleportation failed"));
+ }
+ else if (!strcmp(a->argv[1], "screenshotpng"))
+ {
+ if (a->argc <= 2 || a->argc > 4)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+ int vrc;
+ uint32_t iScreen = 0;
+ if (a->argc == 4)
+ {
+ vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &iScreen);
+ if (vrc != VINF_SUCCESS)
+ {
+ errorArgument("Error parsing display number '%s'", a->argv[3]);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ 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;
+ LONG xOrigin, yOrigin;
+ GuestMonitorStatus_T monitorStatus;
+ CHECK_ERROR_BREAK(pDisplay, GetScreenResolution(iScreen, &width, &height, &bpp, &xOrigin, &yOrigin, &monitorStatus));
+ com::SafeArray<BYTE> saScreenshot;
+ CHECK_ERROR_BREAK(pDisplay, TakeScreenShotToArray(iScreen, width, height, BitmapFormat_PNG, 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))
+ {
+ RTMsgError("Failed to create file '%s' (%Rrc)", a->argv[2], vrc);
+ rc = E_FAIL;
+ break;
+ }
+ vrc = RTFileWrite(pngFile, saScreenshot.raw(), saScreenshot.size(), NULL);
+ if (RT_FAILURE(vrc))
+ {
+ RTMsgError("Failed to write screenshot to file '%s' (%Rrc)", a->argv[2], vrc);
+ rc = E_FAIL;
+ }
+ RTFileClose(pngFile);
+ }
+#ifdef VBOX_WITH_RECORDING
+ else if (!strcmp(a->argv[1], "recording"))
+ {
+ if (a->argc < 3)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ ComPtr<IRecordingSettings> recordingSettings;
+ CHECK_ERROR_BREAK(machine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()));
+
+ SafeIfaceArray <IRecordingScreenSettings> saRecordingScreenScreens;
+ CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saRecordingScreenScreens)));
+
+ /* Note: For now all screens have the same configuration. */
+
+ /*
+ * Note: Commands starting with "vcp" are the deprecated versions and are
+ * kept to ensure backwards compatibility.
+ */
+ if (!strcmp(a->argv[2], "on"))
+ {
+ CHECK_ERROR_RET(recordingSettings, COMSETTER(Enabled)(TRUE), RTEXITCODE_FAILURE);
+ }
+ else if (!strcmp(a->argv[2], "off"))
+ {
+ CHECK_ERROR_RET(recordingSettings, COMSETTER(Enabled)(FALSE), RTEXITCODE_FAILURE);
+ }
+ else if (!strcmp(a->argv[2], "screens"))
+ {
+ ULONG cMonitors = 64;
+ CHECK_ERROR_BREAK(machine, COMGETTER(MonitorCount)(&cMonitors));
+ com::SafeArray<BOOL> saScreens(cMonitors);
+ if ( a->argc == 4
+ && !strcmp(a->argv[3], "all"))
+ {
+ /* enable all screens */
+ for (unsigned i = 0; i < cMonitors; i++)
+ saScreens[i] = true;
+ }
+ else if ( a->argc == 4
+ && !strcmp(a->argv[3], "none"))
+ {
+ /* disable all screens */
+ for (unsigned i = 0; i < cMonitors; i++)
+ saScreens[i] = false;
+
+ /** @todo r=andy What if this is specified? */
+ }
+ else
+ {
+ /* enable selected screens */
+ for (unsigned i = 0; i < cMonitors; i++)
+ saScreens[i] = false;
+ for (int i = 3; 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;
+ }
+ }
+
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Enabled)(saScreens[i]));
+ }
+ else if (!strcmp(a->argv[2], "filename"))
+ {
+ if (a->argc != 4)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Filename)(Bstr(a->argv[3]).raw()));
+ }
+ else if ( !strcmp(a->argv[2], "videores")
+ || !strcmp(a->argv[2], "videoresolution"))
+ {
+ if (a->argc != 5)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ uint32_t uWidth;
+ int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uWidth);
+ if (RT_FAILURE(vrc))
+ {
+ errorArgument("Error parsing video width '%s'", a->argv[3]);
+ rc = E_FAIL;
+ break;
+ }
+
+ uint32_t uHeight;
+ vrc = RTStrToUInt32Ex(a->argv[4], NULL, 0, &uHeight);
+ if (RT_FAILURE(vrc))
+ {
+ errorArgument("Error parsing video height '%s'", a->argv[4]);
+ rc = E_FAIL;
+ break;
+ }
+
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ {
+ CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoWidth)(uWidth));
+ CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoHeight)(uHeight));
+ }
+ }
+ else if (!strcmp(a->argv[2], "videorate"))
+ {
+ if (a->argc != 4)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ uint32_t uRate;
+ int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uRate);
+ if (RT_FAILURE(vrc))
+ {
+ errorArgument("Error parsing video rate '%s'", a->argv[3]);
+ rc = E_FAIL;
+ break;
+ }
+
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoRate)(uRate));
+ }
+ else if (!strcmp(a->argv[2], "videofps"))
+ {
+ if (a->argc != 4)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ uint32_t uFPS;
+ int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uFPS);
+ if (RT_FAILURE(vrc))
+ {
+ errorArgument("Error parsing video FPS '%s'", a->argv[3]);
+ rc = E_FAIL;
+ break;
+ }
+
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(VideoFPS)(uFPS));
+ }
+ else if (!strcmp(a->argv[2], "maxtime"))
+ {
+ if (a->argc != 4)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ uint32_t uMaxTime;
+ int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxTime);
+ if (RT_FAILURE(vrc))
+ {
+ errorArgument("Error parsing maximum time '%s'", a->argv[3]);
+ rc = E_FAIL;
+ break;
+ }
+
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(MaxTime)(uMaxTime));
+ }
+ else if (!strcmp(a->argv[2], "maxfilesize"))
+ {
+ if (a->argc != 4)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ uint32_t uMaxFileSize;
+ int vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &uMaxFileSize);
+ if (RT_FAILURE(vrc))
+ {
+ errorArgument("Error parsing maximum file size '%s'", a->argv[3]);
+ rc = E_FAIL;
+ break;
+ }
+
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(MaxFileSize)(uMaxFileSize));
+ }
+ else if (!strcmp(a->argv[2], "opts"))
+ {
+ if (a->argc != 4)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ rc = E_FAIL;
+ break;
+ }
+
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Options)(Bstr(a->argv[3]).raw()));
+ }
+ }
+#endif /* VBOX_WITH_RECORDING */
+ 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 if (!strcmp(a->argv[1], "addencpassword"))
+ {
+ if ( a->argc != 4
+ && a->argc != 6)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ break;
+ }
+
+ BOOL fRemoveOnSuspend = FALSE;
+ if (a->argc == 6)
+ {
+ if ( strcmp(a->argv[4], "--removeonsuspend")
+ || ( strcmp(a->argv[5], "yes")
+ && strcmp(a->argv[5], "no")))
+ {
+ errorSyntax(USAGE_CONTROLVM, "Invalid parameters");
+ break;
+ }
+ if (!strcmp(a->argv[5], "yes"))
+ fRemoveOnSuspend = TRUE;
+ }
+
+ Bstr bstrPwId(a->argv[2]);
+ Utf8Str strPassword;
+
+ if (!RTStrCmp(a->argv[3], "-"))
+ {
+ /* Get password from console. */
+ RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter password:");
+ if (rcExit == RTEXITCODE_FAILURE)
+ break;
+ }
+ else
+ {
+ RTEXITCODE rcExit = readPasswordFile(a->argv[3], &strPassword);
+ if (rcExit == RTEXITCODE_FAILURE)
+ {
+ RTMsgError("Failed to read new password from file");
+ break;
+ }
+ }
+
+ CHECK_ERROR_BREAK(console, AddDiskEncryptionPassword(bstrPwId.raw(), Bstr(strPassword).raw(), fRemoveOnSuspend));
+ }
+ else if (!strcmp(a->argv[1], "removeencpassword"))
+ {
+ if (a->argc != 3)
+ {
+ errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
+ break;
+ }
+ Bstr bstrPwId(a->argv[2]);
+ CHECK_ERROR_BREAK(console, RemoveDiskEncryptionPassword(bstrPwId.raw()));
+ }
+ else if (!strcmp(a->argv[1], "removeallencpasswords"))
+ {
+ CHECK_ERROR_BREAK(console, ClearAllDiskEncryptionPasswords());
+ }
+ else if (!strncmp(a->argv[1], "changeuartmode", 14))
+ {
+ unsigned n = parseNum(&a->argv[1][14], 4, "UART");
+ if (!n)
+ {
+ rc = E_FAIL;
+ break;
+ }
+ if (a->argc < 3)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ ComPtr<ISerialPort> uart;
+
+ CHECK_ERROR_BREAK(sessionMachine, GetSerialPort(n - 1, uart.asOutParam()));
+ ASSERT(uart);
+
+ if (!RTStrICmp(a->argv[2], "disconnected"))
+ {
+ if (a->argc != 3)
+ {
+ errorArgument("Incorrect arguments to '%s'", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
+ }
+ else if ( !RTStrICmp(a->argv[2], "server")
+ || !RTStrICmp(a->argv[2], "client")
+ || !RTStrICmp(a->argv[2], "tcpserver")
+ || !RTStrICmp(a->argv[2], "tcpclient")
+ || !RTStrICmp(a->argv[2], "file"))
+ {
+ const char *pszMode = a->argv[2];
+ if (a->argc != 4)
+ {
+ errorArgument("Incorrect arguments to '%s'", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+
+ CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[3]).raw()));
+
+ /*
+ * Change to disconnected first to get changes in just a parameter causing
+ * the correct changes later on.
+ */
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
+ if (!RTStrICmp(pszMode, "server"))
+ {
+ CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
+ }
+ else if (!RTStrICmp(pszMode, "client"))
+ {
+ CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
+ }
+ else if (!RTStrICmp(pszMode, "tcpserver"))
+ {
+ CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
+ }
+ else if (!RTStrICmp(pszMode, "tcpclient"))
+ {
+ CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
+ }
+ else if (!RTStrICmp(pszMode, "file"))
+ {
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_RawFile));
+ }
+ }
+ else
+ {
+ if (a->argc != 3)
+ {
+ errorArgument("Incorrect arguments to '%s'", a->argv[1]);
+ rc = E_FAIL;
+ break;
+ }
+ CHECK_ERROR(uart, COMSETTER(Path)(Bstr(a->argv[2]).raw()));
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostDevice));
+ }
+ }
+ else
+ {
+ errorSyntax(USAGE_CONTROLVM, "Invalid parameter '%s'", a->argv[1]);
+ rc = E_FAIL;
+ }
+ } while (0);
+
+ /* The client has to trigger saving the state explicitely. */
+ if (fNeedsSaving)
+ CHECK_ERROR(sessionMachine, SaveSettings());
+
+ a->session->UnlockMachine();
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp
new file mode 100644
index 00000000..499c60a3
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp
@@ -0,0 +1,525 @@
+/* $Id: VBoxManageDHCPServer.cpp $ */
+/** @file
+ * VBoxManage - Implementation of dhcpserver command.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 */
+
+#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 "VBoxManage.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+#ifndef VBOX_ONLY_DOCS
+using namespace com;
+
+typedef enum enMainOpCodes
+{
+ OP_ADD = 1000,
+ OP_REMOVE,
+ OP_MODIFY,
+ OP_RESTART
+} OPCODE;
+
+typedef std::pair<DhcpOpt_T, std::string> DhcpOptSpec;
+typedef std::vector<DhcpOptSpec> DhcpOpts;
+typedef DhcpOpts::iterator DhcpOptIterator;
+
+typedef std::vector<DhcpOpt_T> DhcpOptIds;
+typedef DhcpOptIds::iterator DhcpOptIdIterator;
+
+struct VmNameSlotKey
+{
+ const std::string VmName;
+ uint8_t u8Slot;
+
+ VmNameSlotKey(const 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, DhcpOpts> VmSlot2OptionsM;
+typedef VmSlot2OptionsM::iterator VmSlot2OptionsIterator;
+typedef VmSlot2OptionsM::value_type VmSlot2OptionsPair;
+
+typedef std::map<VmNameSlotKey, DhcpOptIds> VmSlot2OptionIdsM;
+typedef VmSlot2OptionIdsM::iterator VmSlot2OptionIdsIterator;
+
+typedef std::vector<VmNameSlotKey> VmConfigs;
+
+static const RTGETOPTDEF g_aDHCPIPOptions[] =
+{
+ { "--netname", 't', RTGETOPT_REQ_STRING }, /* we use 't' instead of 'n' to avoid
+ * 1. the misspelled "-enable" long option to be treated as 'e' (for -enable) + 'n' (for -netname) + "<the_rest_opt>" (for net name)
+ * 2. the misspelled "-netmask" to be treated as 'n' (for -netname) + "<the_rest_opt>" (for net name)
+ */
+ { "-netname", 't', RTGETOPT_REQ_STRING }, // deprecated (if removed check below)
+ { "--ifname", 'f', RTGETOPT_REQ_STRING }, /* we use 'f' instead of 'i' to avoid
+ * 1. the misspelled "-disable" long option to be treated as 'd' (for -disable) + 'i' (for -ifname) + "<the_rest_opt>" (for if name)
+ */
+ { "-ifname", 'f', RTGETOPT_REQ_STRING }, // deprecated
+ { "--ip", 'a', RTGETOPT_REQ_STRING },
+ { "-ip", 'a', RTGETOPT_REQ_STRING }, // deprecated
+ { "--netmask", 'm', RTGETOPT_REQ_STRING },
+ { "-netmask", 'm', RTGETOPT_REQ_STRING }, // deprecated
+ { "--lowerip", 'l', RTGETOPT_REQ_STRING },
+ { "-lowerip", 'l', RTGETOPT_REQ_STRING }, // deprecated
+ { "--upperip", 'u', RTGETOPT_REQ_STRING },
+ { "-upperip", 'u', RTGETOPT_REQ_STRING }, // deprecated
+ { "--enable", 'e', RTGETOPT_REQ_NOTHING },
+ { "-enable", 'e', RTGETOPT_REQ_NOTHING }, // deprecated
+ { "--disable", 'd', RTGETOPT_REQ_NOTHING },
+ { "-disable", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
+ { "--options", 'o', RTGETOPT_REQ_NOTHING },
+ { "--vm", 'M', RTGETOPT_REQ_STRING}, /* only with -o */
+ { "--nic", 'n', RTGETOPT_REQ_UINT8}, /* only with -o and -M */
+ { "--id", 'i', RTGETOPT_REQ_UINT8}, /* only with -o */
+ { "--value", 'p', RTGETOPT_REQ_STRING}, /* only with -i */
+ { "--remove", 'r', RTGETOPT_REQ_NOTHING} /* only with -i */
+};
+
+static RTEXITCODE handleOp(HandlerArg *a, OPCODE enmCode, int iStart)
+{
+ if (a->argc - iStart < 2)
+ return errorSyntax(USAGE_DHCPSERVER, "Not enough parameters");
+
+ 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;
+
+ DhcpOpts GlobalDhcpOptions;
+ DhcpOptIds GlobalDhcpOptions2Delete;
+ VmSlot2OptionsM VmSlot2Options;
+ VmSlot2OptionIdsM VmSlot2Options2Delete;
+ VmConfigs VmConfigs2Delete;
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState,
+ a->argc,
+ a->argv,
+ g_aDHCPIPOptions,
+ enmCode != OP_REMOVE ? RT_ELEMENTS(g_aDHCPIPOptions) : 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_DHCPSERVER, "You can only specify --netname once.");
+ else if (pIfName)
+ return errorSyntax(USAGE_DHCPSERVER, "You can either use a --netname or --ifname for identifying the DHCP server.");
+ else
+ {
+ pNetName = ValueUnion.psz;
+ }
+ break;
+ case 'f': // --ifname
+ if(pIfName)
+ return errorSyntax(USAGE_DHCPSERVER, "You can only specify --ifname once.");
+ else if (pNetName)
+ return errorSyntax(USAGE_DHCPSERVER, "You can either use a --netname or --ipname for identifying the DHCP server.");
+ else
+ {
+ pIfName = ValueUnion.psz;
+ }
+ break;
+ case 'a': // -ip
+ if(pIp)
+ return errorSyntax(USAGE_DHCPSERVER, "You can only specify --ip once.");
+ else
+ {
+ pIp = ValueUnion.psz;
+ }
+ break;
+ case 'm': // --netmask
+ if(pNetmask)
+ return errorSyntax(USAGE_DHCPSERVER, "You can only specify --netmask once.");
+ else
+ {
+ pNetmask = ValueUnion.psz;
+ }
+ break;
+ case 'l': // --lowerip
+ if(pLowerIp)
+ return errorSyntax(USAGE_DHCPSERVER, "You can only specify --lowerip once.");
+ else
+ {
+ pLowerIp = ValueUnion.psz;
+ }
+ break;
+ case 'u': // --upperip
+ if(pUpperIp)
+ return errorSyntax(USAGE_DHCPSERVER, "You can only specify --upperip once.");
+ else
+ {
+ pUpperIp = ValueUnion.psz;
+ }
+ break;
+ case 'e': // --enable
+ if(enable >= 0)
+ return errorSyntax(USAGE_DHCPSERVER, "You can specify either --enable or --disable once.");
+ else
+ {
+ enable = 1;
+ }
+ break;
+ case 'd': // --disable
+ if(enable >= 0)
+ return errorSyntax(USAGE_DHCPSERVER, "You can specify either --enable or --disable once.");
+ else
+ {
+ enable = 0;
+ }
+ break;
+ 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 */
+ // {"--nic", 'c', 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 'M': // --vm
+ {
+ 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 */
+
+ case 'n': // --nic
+ {
+ if (!fVmOptionRead)
+ return errorSyntax(USAGE_DHCPSERVER,
+ "vm name wasn't specified");
+
+ u8Slot = ValueUnion.u8;
+
+ if (u8Slot < 1)
+ return errorSyntax(USAGE_DHCPSERVER,
+ "invalid NIC number: %u", u8Slot);
+ --u8Slot;
+ }
+ break; /* end of --nic */
+
+ 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,
+ "--nic wasn't found");
+
+ DhcpOpts &opts = fVmOptionRead ? VmSlot2Options[VmNameSlotKey(pszVmName, u8Slot)]
+ : GlobalDhcpOptions;
+ std::string strVal = ValueUnion.psz;
+ opts.push_back(DhcpOptSpec((DhcpOpt_T)u8OptId, strVal));
+
+ }
+ break; // --end of value
+
+ case 'r': /* --remove */
+ {
+ 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,
+ "--nic wasn't found");
+
+ DhcpOptIds &optIds = fVmOptionRead ? VmSlot2Options2Delete[VmNameSlotKey(pszVmName, u8Slot)]
+ : GlobalDhcpOptions2Delete;
+ optIds.push_back((DhcpOpt_T)u8OptId);
+ }
+ break; /* --end of remove */
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_GRAPH(c))
+ return errorSyntax(USAGE_DHCPSERVER, "unhandled option: -%c", c);
+ return errorSyntax(USAGE_DHCPSERVER, "unhandled option: %i", c);
+ }
+ if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_DHCPSERVER, "unknown option: %s", ValueUnion.psz);
+ if (ValueUnion.pDef)
+ return errorSyntax(USAGE_DHCPSERVER, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ return errorSyntax(USAGE_DHCPSERVER, "%Rrs", c);
+ }
+ }
+
+ if(! pNetName && !pIfName)
+ return errorSyntax(USAGE_DHCPSERVER, "You need to specify either --netname or --ifname to identify the DHCP server");
+
+ if( enmCode != OP_REMOVE
+ && enmCode != OP_RESTART
+ && GlobalDhcpOptions.empty()
+ && VmSlot2Options.empty()
+ && GlobalDhcpOptions2Delete.empty()
+ && VmSlot2Options2Delete.empty())
+ {
+ if(enable < 0 || pIp || pNetmask || pLowerIp || pUpperIp)
+ {
+ if(!pIp)
+ return errorSyntax(USAGE_DHCPSERVER, "You need to specify --ip option");
+
+ if(!pNetmask)
+ return errorSyntax(USAGE_DHCPSERVER, "You need to specify --netmask option");
+
+ if(!pLowerIp)
+ return errorSyntax(USAGE_DHCPSERVER, "You need to specify --lowerip option");
+
+ if(!pUpperIp)
+ return errorSyntax(USAGE_DHCPSERVER, "You need to specify --upperip option");
+ }
+ }
+
+ Bstr NetName;
+ if(!pNetName)
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
+
+ ComPtr<IHostNetworkInterface> hif;
+ CHECK_ERROR(host, FindHostNetworkInterfaceByName(Bstr(pIfName).mutableRaw(), hif.asOutParam()));
+ if (FAILED(rc))
+ return errorArgument("Could not find interface '%s'", pIfName);
+
+ CHECK_ERROR(hif, COMGETTER(NetworkName) (NetName.asOutParam()));
+ if (FAILED(rc))
+ return errorArgument("Could not get network name for the interface '%s'", pIfName);
+ }
+ else
+ {
+ NetName = Bstr(pNetName);
+ }
+
+ ComPtr<IDHCPServer> svr;
+ rc = a->virtualBox->FindDHCPServerByNetworkName(NetName.mutableRaw(), svr.asOutParam());
+ if(enmCode == OP_ADD)
+ {
+ if (SUCCEEDED(rc))
+ return errorArgument("DHCP server already exists");
+
+ CHECK_ERROR(a->virtualBox, CreateDHCPServer(NetName.mutableRaw(), svr.asOutParam()));
+ if (FAILED(rc))
+ return errorArgument("Failed to create the DHCP server");
+ }
+ else if (FAILED(rc))
+ {
+ return errorArgument("DHCP server does not exist");
+ }
+
+ if (enmCode == OP_RESTART)
+ {
+ CHECK_ERROR(svr, Restart());
+ if(FAILED(rc))
+ return errorArgument("Failed to restart server");
+ }
+ else if (enmCode == OP_REMOVE)
+ {
+ CHECK_ERROR(a->virtualBox, RemoveDHCPServer(svr));
+ if(FAILED(rc))
+ return errorArgument("Failed to remove server");
+ }
+ else
+ {
+ if (pIp || pNetmask || pLowerIp || pUpperIp)
+ {
+ 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");
+ }
+
+ if(enable >= 0)
+ {
+ CHECK_ERROR(svr, COMSETTER(Enabled) ((BOOL)enable));
+ }
+
+ /* remove specified options */
+ DhcpOptIdIterator itOptId;
+ for (itOptId = GlobalDhcpOptions2Delete.begin();
+ itOptId != GlobalDhcpOptions2Delete.end();
+ ++itOptId)
+ {
+ CHECK_ERROR(svr, RemoveGlobalOption(*itOptId));
+ }
+ VmSlot2OptionIdsIterator itIdVector;
+ for (itIdVector = VmSlot2Options2Delete.begin();
+ itIdVector != VmSlot2Options2Delete.end();
+ ++itIdVector)
+ {
+ for(itOptId = itIdVector->second.begin();
+ itOptId != itIdVector->second.end();
+ ++itOptId)
+ {
+ CHECK_ERROR(svr,
+ RemoveVmSlotOption(Bstr(itIdVector->first.VmName.c_str()).raw(),
+ itIdVector->first.u8Slot,
+ *itOptId));
+ }
+ }
+
+ /* 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()));
+ }
+ }
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+RTEXITCODE handleDHCPServer(HandlerArg *a)
+{
+ if (a->argc < 1)
+ return errorSyntax(USAGE_DHCPSERVER, "Not enough parameters");
+
+ RTEXITCODE rcExit;
+ if (strcmp(a->argv[0], "modify") == 0)
+ rcExit = handleOp(a, OP_MODIFY, 1);
+ else if (strcmp(a->argv[0], "add") == 0)
+ rcExit = handleOp(a, OP_ADD, 1);
+ else if (strcmp(a->argv[0], "remove") == 0)
+ rcExit = handleOp(a, OP_REMOVE, 1);
+ else if (strcmp(a->argv[0], "restart") == 0)
+ rcExit = handleOp(a, OP_RESTART, 1);
+ else
+ rcExit = errorSyntax(USAGE_DHCPSERVER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
+
+ return rcExit;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
+
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp
new file mode 100644
index 00000000..5efaa0ad
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageDebugVM.cpp
@@ -0,0 +1,899 @@
+/* $Id: VBoxManageDebugVM.cpp $ */
+/** @file
+ * VBoxManage - Implementation of the debugvm command.
+ */
+
+/*
+ * Copyright (C) 2012-2019 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 <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/com/VirtualBox.h>
+
+#include <VBox/types.h>
+#include <iprt/ctype.h>
+#include <iprt/getopt.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <VBox/log.h>
+
+#include "VBoxManage.h"
+
+
+/**
+ * Handles the getregisters sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param pArgs The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_GetRegisters(HandlerArg *pArgs, IMachineDebugger *pDebugger)
+{
+ /*
+ * We take a list of register names (case insensitive). If 'all' is
+ * encountered we'll dump all registers.
+ */
+ ULONG idCpu = 0;
+ unsigned cRegisters = 0;
+
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--cpu", 'c', RTGETOPT_REQ_UINT32 },
+ };
+ int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'c':
+ idCpu = ValueUnion.u32;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!RTStrICmp(ValueUnion.psz, "all"))
+ {
+ com::SafeArray<BSTR> aBstrNames;
+ com::SafeArray<BSTR> aBstrValues;
+ CHECK_ERROR2I_RET(pDebugger, GetRegisters(idCpu, ComSafeArrayAsOutParam(aBstrNames),
+ ComSafeArrayAsOutParam(aBstrValues)),
+ RTEXITCODE_FAILURE);
+ Assert(aBstrNames.size() == aBstrValues.size());
+
+ size_t cchMaxName = 8;
+ for (size_t i = 0; i < aBstrNames.size(); i++)
+ {
+ size_t cchName = RTUtf16Len(aBstrNames[i]);
+ if (cchName > cchMaxName)
+ cchMaxName = cchName;
+ }
+
+ for (size_t i = 0; i < aBstrNames.size(); i++)
+ RTPrintf("%-*ls = %ls\n", cchMaxName, aBstrNames[i], aBstrValues[i]);
+ }
+ else
+ {
+ com::Bstr bstrName = ValueUnion.psz;
+ com::Bstr bstrValue;
+ CHECK_ERROR2I_RET(pDebugger, GetRegister(idCpu, bstrName.raw(), bstrValue.asOutParam()), RTEXITCODE_FAILURE);
+ RTPrintf("%s = %ls\n", ValueUnion.psz, bstrValue.raw());
+ }
+ cRegisters++;
+ break;
+
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+
+ if (!cRegisters)
+ return errorSyntax("The getregisters sub-command takes at least one register name");
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the info sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param pArgs The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_Info(HandlerArg *pArgs, IMachineDebugger *pDebugger)
+{
+ /*
+ * Parse arguments.
+ */
+ const char *pszInfo = NULL;
+ const char *pszArgs = NULL;
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+ int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, NULL, 0, 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszInfo)
+ pszInfo = ValueUnion.psz;
+ else if (!pszArgs)
+ pszArgs = ValueUnion.psz;
+ else
+ return errorTooManyParameters(&pArgs->argv[GetState.iNext - 1]);
+ break;
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+
+ if (!pszInfo)
+ return errorSyntax("Must specify info item to display");
+
+ /*
+ * Do the work.
+ */
+ com::Bstr bstrName(pszInfo);
+ com::Bstr bstrArgs(pszArgs);
+ com::Bstr bstrInfo;
+ CHECK_ERROR2I_RET(pDebugger, Info(bstrName.raw(), bstrArgs.raw(), bstrInfo.asOutParam()), RTEXITCODE_FAILURE);
+ RTPrintf("%ls", bstrInfo.raw());
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the inject sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param a The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_InjectNMI(HandlerArg *a, IMachineDebugger *pDebugger)
+{
+ if (a->argc != 2)
+ return errorTooManyParameters(&a->argv[1]);
+ CHECK_ERROR2I_RET(pDebugger, InjectNMI(), RTEXITCODE_FAILURE);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the log sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param pArgs The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ * @param pszSubCmd The sub command.
+ */
+static RTEXITCODE handleDebugVM_LogXXXX(HandlerArg *pArgs, IMachineDebugger *pDebugger, const char *pszSubCmd)
+{
+ /*
+ * Parse arguments.
+ */
+ bool fRelease = false;
+ com::Utf8Str strSettings;
+
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+
+ /*
+ * NB: don't use short options to prevent log specifications like
+ * "-drv_foo" from being interpreted as options.
+ */
+# define DEBUGVM_LOG_DEBUG (VINF_GETOPT_NOT_OPTION + 'd')
+# define DEBUGVM_LOG_RELEASE (VINF_GETOPT_NOT_OPTION + 'r')
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--debug", DEBUGVM_LOG_DEBUG, RTGETOPT_REQ_NOTHING },
+ { "--release", DEBUGVM_LOG_RELEASE, RTGETOPT_REQ_NOTHING }
+ };
+ int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2,
+ /*
+ * Note: RTGETOPTINIT_FLAGS_NO_STD_OPTS is needed to not get into an infinite hang in the following
+ * while-loop when processing log groups starting with "h",
+ * e.g. "VBoxManage debugvm <VM Name> log --debug -hex".
+ */
+ RTGETOPTINIT_FLAGS_OPTS_FIRST | RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case DEBUGVM_LOG_RELEASE:
+ fRelease = true;
+ break;
+
+ case DEBUGVM_LOG_DEBUG:
+ fRelease = false;
+ break;
+
+ /* Because log strings can start with "-" (like "-all+dev_foo")
+ * we have to take everything we got as a setting and apply it.
+ * IPRT will take care of the validation afterwards. */
+ default:
+ if (strSettings.length() == 0)
+ strSettings = ValueUnion.psz;
+ else
+ {
+ strSettings.append(' ');
+ strSettings.append(ValueUnion.psz);
+ }
+ break;
+ }
+ }
+
+ if (fRelease)
+ {
+ com::Utf8Str strTmp(strSettings);
+ strSettings = "release:";
+ strSettings.append(strTmp);
+ }
+
+ com::Bstr bstrSettings(strSettings);
+ if (!strcmp(pszSubCmd, "log"))
+ CHECK_ERROR2I_RET(pDebugger, ModifyLogGroups(bstrSettings.raw()), RTEXITCODE_FAILURE);
+ else if (!strcmp(pszSubCmd, "logdest"))
+ CHECK_ERROR2I_RET(pDebugger, ModifyLogDestinations(bstrSettings.raw()), RTEXITCODE_FAILURE);
+ else if (!strcmp(pszSubCmd, "logflags"))
+ CHECK_ERROR2I_RET(pDebugger, ModifyLogFlags(bstrSettings.raw()), RTEXITCODE_FAILURE);
+ else
+ AssertFailedReturn(RTEXITCODE_FAILURE);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Handles the inject sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param pArgs The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_DumpVMCore(HandlerArg *pArgs, IMachineDebugger *pDebugger)
+{
+ /*
+ * Parse arguments.
+ */
+ const char *pszFilename = NULL;
+ const char *pszCompression = NULL;
+
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--filename", 'f', RTGETOPT_REQ_STRING },
+ { "--compression", 'c', RTGETOPT_REQ_STRING }
+ };
+ int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'c':
+ if (pszCompression)
+ return errorSyntax("The --compression option has already been given");
+ pszCompression = ValueUnion.psz;
+ break;
+ case 'f':
+ if (pszFilename)
+ return errorSyntax("The --filename option has already been given");
+ pszFilename = ValueUnion.psz;
+ break;
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+
+ if (!pszFilename)
+ return errorSyntax("The --filename option is required");
+
+ /*
+ * Make the filename absolute before handing it on to the API.
+ */
+ char szAbsFilename[RTPATH_MAX];
+ rc = RTPathAbs(pszFilename, szAbsFilename, sizeof(szAbsFilename));
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on '%s': %Rrc", pszFilename, rc);
+
+ com::Bstr bstrFilename(szAbsFilename);
+ com::Bstr bstrCompression(pszCompression);
+ CHECK_ERROR2I_RET(pDebugger, DumpGuestCore(bstrFilename.raw(), bstrCompression.raw()), RTEXITCODE_FAILURE);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the osdetect sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param a The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_OSDetect(HandlerArg *a, IMachineDebugger *pDebugger)
+{
+ if (a->argc != 2)
+ return errorTooManyParameters(&a->argv[1]);
+
+ com::Bstr bstrIgnore;
+ com::Bstr bstrAll("all");
+ CHECK_ERROR2I_RET(pDebugger, LoadPlugIn(bstrAll.raw(), bstrIgnore.asOutParam()), RTEXITCODE_FAILURE);
+
+ com::Bstr bstrName;
+ CHECK_ERROR2I_RET(pDebugger, DetectOS(bstrName.asOutParam()), RTEXITCODE_FAILURE);
+ RTPrintf("Detected: %ls\n", bstrName.raw());
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the osinfo sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param a The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_OSInfo(HandlerArg *a, IMachineDebugger *pDebugger)
+{
+ if (a->argc != 2)
+ return errorTooManyParameters(&a->argv[1]);
+
+ com::Bstr bstrName;
+ CHECK_ERROR2I_RET(pDebugger, COMGETTER(OSName)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
+ com::Bstr bstrVersion;
+ CHECK_ERROR2I_RET(pDebugger, COMGETTER(OSVersion)(bstrVersion.asOutParam()), RTEXITCODE_FAILURE);
+ RTPrintf("Name: %ls\n", bstrName.raw());
+ RTPrintf("Version: %ls\n", bstrVersion.raw());
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the osdmsg sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param pArgs The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_OSDmesg(HandlerArg *pArgs, IMachineDebugger *pDebugger)
+{
+ /*
+ * Parse argument.
+ */
+ uint32_t uMaxMessages = 0;
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--lines", 'n', RTGETOPT_REQ_UINT32 },
+ };
+ int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ switch (rc)
+ {
+ case 'n': uMaxMessages = ValueUnion.u32; break;
+ default: return errorGetOpt(rc, &ValueUnion);
+ }
+
+ /*
+ * Do it.
+ */
+ com::Bstr bstrDmesg;
+ CHECK_ERROR2I_RET(pDebugger, QueryOSKernelLog(uMaxMessages, bstrDmesg.asOutParam()), RTEXITCODE_FAILURE);
+ RTPrintf("%ls\n", bstrDmesg.raw());
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the setregisters sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param pArgs The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_SetRegisters(HandlerArg *pArgs, IMachineDebugger *pDebugger)
+{
+ /*
+ * We take a list of register assignments, that is register=value.
+ */
+ ULONG idCpu = 0;
+ com::SafeArray<IN_BSTR> aBstrNames;
+ com::SafeArray<IN_BSTR> aBstrValues;
+
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--cpu", 'c', RTGETOPT_REQ_UINT32 },
+ };
+ int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'c':
+ idCpu = ValueUnion.u32;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ const char *pszEqual = strchr(ValueUnion.psz, '=');
+ if (!pszEqual)
+ return errorSyntax("setregisters expects input on the form 'register=value' got '%s'", ValueUnion.psz);
+ try
+ {
+ com::Bstr bstrName(ValueUnion.psz, pszEqual - ValueUnion.psz);
+ com::Bstr bstrValue(pszEqual + 1);
+ if ( !aBstrNames.push_back(bstrName.raw())
+ || !aBstrValues.push_back(bstrValue.raw()))
+ throw std::bad_alloc();
+ }
+ catch (std::bad_alloc &)
+ {
+ RTMsgError("Out of memory\n");
+ return RTEXITCODE_FAILURE;
+ }
+ break;
+ }
+
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+
+ if (!aBstrNames.size())
+ return errorSyntax("The setregisters sub-command takes at least one register name");
+
+ /*
+ * If it is only one register, use the single register method just so
+ * we expose it and can test it from the command line.
+ */
+ if (aBstrNames.size() == 1)
+ {
+ CHECK_ERROR2I_RET(pDebugger, SetRegister(idCpu, aBstrNames[0], aBstrValues[0]), RTEXITCODE_FAILURE);
+ RTPrintf("Successfully set %ls\n", aBstrNames[0]);
+ }
+ else
+ {
+ CHECK_ERROR2I_RET(pDebugger, SetRegisters(idCpu, ComSafeArrayAsInParam(aBstrNames), ComSafeArrayAsInParam(aBstrValues)),
+ RTEXITCODE_FAILURE);
+ RTPrintf("Successfully set %u registers\n", aBstrNames.size());
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/** @name debugvm show flags
+ * @{ */
+#define DEBUGVM_SHOW_FLAGS_HUMAN_READABLE UINT32_C(0x00000000)
+#define DEBUGVM_SHOW_FLAGS_SH_EXPORT UINT32_C(0x00000001)
+#define DEBUGVM_SHOW_FLAGS_SH_EVAL UINT32_C(0x00000002)
+#define DEBUGVM_SHOW_FLAGS_CMD_SET UINT32_C(0x00000003)
+#define DEBUGVM_SHOW_FLAGS_FMT_MASK UINT32_C(0x00000003)
+/** @} */
+
+/**
+ * Prints a variable according to the @a fFlags.
+ *
+ * @param pszVar The variable name.
+ * @param pbstrValue The variable value.
+ * @param fFlags The debugvm show flags.
+ */
+static void handleDebugVM_Show_PrintVar(const char *pszVar, com::Bstr const *pbstrValue, uint32_t fFlags)
+{
+ switch (fFlags & DEBUGVM_SHOW_FLAGS_FMT_MASK)
+ {
+ case DEBUGVM_SHOW_FLAGS_HUMAN_READABLE: RTPrintf(" %27s=%ls\n", pszVar, pbstrValue->raw()); break;
+ case DEBUGVM_SHOW_FLAGS_SH_EXPORT: RTPrintf("export %s='%ls'\n", pszVar, pbstrValue->raw()); break;
+ case DEBUGVM_SHOW_FLAGS_SH_EVAL: RTPrintf("%s='%ls'\n", pszVar, pbstrValue->raw()); break;
+ case DEBUGVM_SHOW_FLAGS_CMD_SET: RTPrintf("set %s=%ls\n", pszVar, pbstrValue->raw()); break;
+ default: AssertFailed();
+ }
+}
+
+/**
+ * Handles logdbg-settings.
+ *
+ * @returns Exit code.
+ * @param pDebugger The debugger interface.
+ * @param fFlags The debugvm show flags.
+ */
+static RTEXITCODE handleDebugVM_Show_LogDbgSettings(IMachineDebugger *pDebugger, uint32_t fFlags)
+{
+ if ((fFlags & DEBUGVM_SHOW_FLAGS_FMT_MASK) == DEBUGVM_SHOW_FLAGS_HUMAN_READABLE)
+ RTPrintf("Debug logger settings:\n");
+
+ com::Bstr bstr;
+ CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogDbgGroups)(bstr.asOutParam()), RTEXITCODE_FAILURE);
+ handleDebugVM_Show_PrintVar("VBOX_LOG", &bstr, fFlags);
+
+ CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogDbgFlags)(bstr.asOutParam()), RTEXITCODE_FAILURE);
+ handleDebugVM_Show_PrintVar("VBOX_LOG_FLAGS", &bstr, fFlags);
+
+ CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogDbgDestinations)(bstr.asOutParam()), RTEXITCODE_FAILURE);
+ handleDebugVM_Show_PrintVar("VBOX_LOG_DEST", &bstr, fFlags);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles logrel-settings.
+ *
+ * @returns Exit code.
+ * @param pDebugger The debugger interface.
+ * @param fFlags The debugvm show flags.
+ */
+static RTEXITCODE handleDebugVM_Show_LogRelSettings(IMachineDebugger *pDebugger, uint32_t fFlags)
+{
+ if ((fFlags & DEBUGVM_SHOW_FLAGS_FMT_MASK) == DEBUGVM_SHOW_FLAGS_HUMAN_READABLE)
+ RTPrintf("Release logger settings:\n");
+
+ com::Bstr bstr;
+ CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogRelGroups)(bstr.asOutParam()), RTEXITCODE_FAILURE);
+ handleDebugVM_Show_PrintVar("VBOX_RELEASE_LOG", &bstr, fFlags);
+
+ CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogRelFlags)(bstr.asOutParam()), RTEXITCODE_FAILURE);
+ handleDebugVM_Show_PrintVar("VBOX_RELEASE_LOG_FLAGS", &bstr, fFlags);
+
+ CHECK_ERROR2I_RET(pDebugger, COMGETTER(LogRelDestinations)(bstr.asOutParam()), RTEXITCODE_FAILURE);
+ handleDebugVM_Show_PrintVar("VBOX_RELEASE_LOG_DEST", &bstr, fFlags);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the show sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param pArgs The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_Show(HandlerArg *pArgs, IMachineDebugger *pDebugger)
+{
+ /*
+ * Parse arguments and what to show. Order dependent.
+ */
+ uint32_t fFlags = DEBUGVM_SHOW_FLAGS_HUMAN_READABLE;
+
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--human-readable", 'H', RTGETOPT_REQ_NOTHING },
+ { "--sh-export", 'e', RTGETOPT_REQ_NOTHING },
+ { "--sh-eval", 'E', RTGETOPT_REQ_NOTHING },
+ { "--cmd-set", 's', RTGETOPT_REQ_NOTHING },
+ };
+ int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'H':
+ fFlags = (fFlags & ~DEBUGVM_SHOW_FLAGS_FMT_MASK) | DEBUGVM_SHOW_FLAGS_HUMAN_READABLE;
+ break;
+
+ case 'e':
+ fFlags = (fFlags & ~DEBUGVM_SHOW_FLAGS_FMT_MASK) | DEBUGVM_SHOW_FLAGS_SH_EXPORT;
+ break;
+
+ case 'E':
+ fFlags = (fFlags & ~DEBUGVM_SHOW_FLAGS_FMT_MASK) | DEBUGVM_SHOW_FLAGS_SH_EVAL;
+ break;
+
+ case 's':
+ fFlags = (fFlags & ~DEBUGVM_SHOW_FLAGS_FMT_MASK) | DEBUGVM_SHOW_FLAGS_CMD_SET;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ RTEXITCODE rcExit;
+ if (!strcmp(ValueUnion.psz, "log-settings"))
+ {
+ rcExit = handleDebugVM_Show_LogDbgSettings(pDebugger, fFlags);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ rcExit = handleDebugVM_Show_LogRelSettings(pDebugger, fFlags);
+ }
+ else if (!strcmp(ValueUnion.psz, "logdbg-settings"))
+ rcExit = handleDebugVM_Show_LogDbgSettings(pDebugger, fFlags);
+ else if (!strcmp(ValueUnion.psz, "logrel-settings"))
+ rcExit = handleDebugVM_Show_LogRelSettings(pDebugger, fFlags);
+ else
+ rcExit = errorSyntax("The show sub-command has no idea what '%s' might be", ValueUnion.psz);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ break;
+ }
+
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the stack sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param pArgs The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_Stack(HandlerArg *pArgs, IMachineDebugger *pDebugger)
+{
+ /*
+ * Parse arguments.
+ */
+ VMCPUID idCpu = VMCPUID_ALL;
+
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--cpu", 'c', RTGETOPT_REQ_UINT32 },
+ };
+ int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'c':
+ idCpu = ValueUnion.u32;
+ break;
+
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+
+ /*
+ * Dump stack.
+ */
+ com::Bstr bstrGuestStack;
+ if (idCpu != VMCPUID_ALL)
+ {
+ /* Single CPU */
+ CHECK_ERROR2I_RET(pDebugger, DumpGuestStack(idCpu, bstrGuestStack.asOutParam()), RTEXITCODE_FAILURE);
+ RTPrintf("%ls\n", bstrGuestStack.raw());
+ }
+ else
+ {
+ /* All CPUs. */
+ ComPtr<IMachine> ptrMachine;
+ CHECK_ERROR2I_RET(pArgs->session, COMGETTER(Machine)(ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
+ ULONG cCpus;
+ CHECK_ERROR2I_RET(ptrMachine, COMGETTER(CPUCount)(&cCpus), RTEXITCODE_FAILURE);
+
+ for (idCpu = 0; idCpu < (VMCPUID)cCpus; idCpu++)
+ {
+ CHECK_ERROR2I_RET(pDebugger, DumpGuestStack(idCpu, bstrGuestStack.asOutParam()), RTEXITCODE_FAILURE);
+ if (cCpus > 1)
+ {
+ if (idCpu > 0)
+ RTPrintf("\n");
+ RTPrintf("====================== CPU #%u ======================\n", idCpu);
+ }
+ RTPrintf("%ls\n", bstrGuestStack.raw());
+ }
+ }
+
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the statistics sub-command.
+ *
+ * @returns Suitable exit code.
+ * @param pArgs The handler arguments.
+ * @param pDebugger Pointer to the debugger interface.
+ */
+static RTEXITCODE handleDebugVM_Statistics(HandlerArg *pArgs, IMachineDebugger *pDebugger)
+{
+ /*
+ * Parse arguments.
+ */
+ bool fWithDescriptions = false;
+ const char *pszPattern = NULL; /* all */
+ bool fReset = false;
+
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--descriptions", 'd', RTGETOPT_REQ_NOTHING },
+ { "--pattern", 'p', RTGETOPT_REQ_STRING },
+ { "--reset", 'r', RTGETOPT_REQ_NOTHING },
+ };
+ int rc = RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, 0 /*fFlags*/);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ case 'd':
+ fWithDescriptions = true;
+ break;
+
+ case 'p':
+ if (pszPattern)
+ return errorSyntax("Multiple --pattern options are not permitted");
+ pszPattern = ValueUnion.psz;
+ break;
+
+ case 'r':
+ fReset = true;
+ break;
+
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+
+ if (fReset && fWithDescriptions)
+ return errorSyntax("The --reset and --descriptions options does not mix");
+
+ /*
+ * Execute the order.
+ */
+ com::Bstr bstrPattern(pszPattern);
+ if (fReset)
+ CHECK_ERROR2I_RET(pDebugger, ResetStats(bstrPattern.raw()), RTEXITCODE_FAILURE);
+ else
+ {
+ com::Bstr bstrStats;
+ CHECK_ERROR2I_RET(pDebugger, GetStats(bstrPattern.raw(), fWithDescriptions, bstrStats.asOutParam()),
+ RTEXITCODE_FAILURE);
+ /* if (fFormatted)
+ { big mess }
+ else
+ */
+ RTPrintf("%ls\n", bstrStats.raw());
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+RTEXITCODE handleDebugVM(HandlerArg *pArgs)
+{
+ RTEXITCODE rcExit = RTEXITCODE_FAILURE;
+
+ /*
+ * The first argument is the VM name or UUID. Open a session to it.
+ */
+ if (pArgs->argc < 2)
+ return errorNoSubcommand();
+ ComPtr<IMachine> ptrMachine;
+ CHECK_ERROR2I_RET(pArgs->virtualBox, FindMachine(com::Bstr(pArgs->argv[0]).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
+ CHECK_ERROR2I_RET(ptrMachine, LockMachine(pArgs->session, LockType_Shared), RTEXITCODE_FAILURE);
+
+ /*
+ * Get the associated console and machine debugger.
+ */
+ HRESULT hrc;
+ ComPtr<IConsole> ptrConsole;
+ CHECK_ERROR2(hrc, pArgs->session, COMGETTER(Console)(ptrConsole.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ if (ptrConsole.isNotNull())
+ {
+ ComPtr<IMachineDebugger> ptrDebugger;
+ CHECK_ERROR2(hrc, ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()));
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * String switch on the sub-command.
+ */
+ const char *pszSubCmd = pArgs->argv[1];
+ if (!strcmp(pszSubCmd, "dumpvmcore"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_DUMPVMCORE);
+ rcExit = handleDebugVM_DumpVMCore(pArgs, ptrDebugger);
+ }
+ else if (!strcmp(pszSubCmd, "getregisters"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_GETREGISTERS);
+ rcExit = handleDebugVM_GetRegisters(pArgs, ptrDebugger);
+ }
+ else if (!strcmp(pszSubCmd, "info"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_INFO);
+ rcExit = handleDebugVM_Info(pArgs, ptrDebugger);
+ }
+ else if (!strcmp(pszSubCmd, "injectnmi"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_INJECTNMI);
+ rcExit = handleDebugVM_InjectNMI(pArgs, ptrDebugger);
+ }
+ else if (!strcmp(pszSubCmd, "log"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_LOG);
+ rcExit = handleDebugVM_LogXXXX(pArgs, ptrDebugger, pszSubCmd);
+ }
+ else if (!strcmp(pszSubCmd, "logdest"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_LOGDEST);
+ rcExit = handleDebugVM_LogXXXX(pArgs, ptrDebugger, pszSubCmd);
+ }
+ else if (!strcmp(pszSubCmd, "logflags"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_LOGFLAGS);
+ rcExit = handleDebugVM_LogXXXX(pArgs, ptrDebugger, pszSubCmd);
+ }
+ else if (!strcmp(pszSubCmd, "osdetect"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_OSDETECT);
+ rcExit = handleDebugVM_OSDetect(pArgs, ptrDebugger);
+ }
+ else if (!strcmp(pszSubCmd, "osinfo"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_OSINFO);
+ rcExit = handleDebugVM_OSInfo(pArgs, ptrDebugger);
+ }
+ else if (!strcmp(pszSubCmd, "osdmesg"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_OSDMESG);
+ rcExit = handleDebugVM_OSDmesg(pArgs, ptrDebugger);
+ }
+ else if (!strcmp(pszSubCmd, "setregisters"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_SETREGISTERS);
+ rcExit = handleDebugVM_SetRegisters(pArgs, ptrDebugger);
+ }
+ else if (!strcmp(pszSubCmd, "show"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_SHOW);
+ rcExit = handleDebugVM_Show(pArgs, ptrDebugger);
+ }
+ else if (!strcmp(pszSubCmd, "stack"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_STACK);
+ rcExit = handleDebugVM_Stack(pArgs, ptrDebugger);
+ }
+ else if (!strcmp(pszSubCmd, "statistics"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_DEBUGVM_STATISTICS);
+ rcExit = handleDebugVM_Statistics(pArgs, ptrDebugger);
+ }
+ else
+ errorUnknownSubcommand(pszSubCmd);
+ }
+ }
+ else
+ RTMsgError("Machine '%s' is not currently running.\n", pArgs->argv[0]);
+ }
+
+ pArgs->session->UnlockMachine();
+
+ return rcExit;
+}
+
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp
new file mode 100644
index 00000000..ef4db168
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp
@@ -0,0 +1,2560 @@
+/* $Id: VBoxManageDisk.cpp $ */
+/** @file
+ * VBoxManage - The disk/medium related commands.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 VBOX_ONLY_DOCS
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#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>
+
+#include <iprt/asm.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/ctype.h>
+#include <iprt/getopt.h>
+#include <VBox/log.h>
+#include <VBox/vd.h>
+
+#include "VBoxManage.h"
+using namespace com;
+
+/** Medium category. */
+typedef enum MEDIUMCATEGORY
+{
+ MEDIUMCATEGORY_NONE = 0,
+ MEDIUMCATEGORY_DISK,
+ MEDIUMCATEGORY_DVD,
+ MEDIUMCATEGORY_FLOPPY
+} MEDIUMCATEGORY;
+
+
+
+// funcs
+///////////////////////////////////////////////////////////////////////////////
+
+
+static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
+{
+ RT_NOREF(pvUser);
+ RTMsgErrorV(pszFormat, va);
+ RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS);
+}
+
+static int parseMediumVariant(const char *psz, MediumVariant_T *pMediumVariant)
+{
+ int rc = VINF_SUCCESS;
+ unsigned uMediumVariant = (unsigned)(*pMediumVariant);
+ 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)
+ {
+ // Parsing is intentionally inconsistent: "standard" resets the
+ // variant, whereas the other flags are cumulative.
+ if (!RTStrNICmp(psz, "standard", len))
+ uMediumVariant = MediumVariant_Standard;
+ else if ( !RTStrNICmp(psz, "fixed", len)
+ || !RTStrNICmp(psz, "static", len))
+ uMediumVariant |= MediumVariant_Fixed;
+ else if (!RTStrNICmp(psz, "Diff", len))
+ uMediumVariant |= MediumVariant_Diff;
+ else if (!RTStrNICmp(psz, "split2g", len))
+ uMediumVariant |= MediumVariant_VmdkSplit2G;
+ else if ( !RTStrNICmp(psz, "stream", len)
+ || !RTStrNICmp(psz, "streamoptimized", len))
+ uMediumVariant |= MediumVariant_VmdkStreamOptimized;
+ else if (!RTStrNICmp(psz, "esx", len))
+ uMediumVariant |= MediumVariant_VmdkESX;
+ else if (!RTStrNICmp(psz, "formatted", len))
+ uMediumVariant |= MediumVariant_Formatted;
+ else
+ rc = VERR_PARSE_ERROR;
+ }
+ if (pszComma)
+ psz += len + 1;
+ else
+ psz += len;
+ }
+
+ if (RT_SUCCESS(rc))
+ *pMediumVariant = (MediumVariant_T)uMediumVariant;
+ return rc;
+}
+
+int parseMediumType(const char *psz, MediumType_T *penmMediumType)
+{
+ int rc = VINF_SUCCESS;
+ MediumType_T enmMediumType = MediumType_Normal;
+ if (!RTStrICmp(psz, "normal"))
+ enmMediumType = MediumType_Normal;
+ else if (!RTStrICmp(psz, "immutable"))
+ enmMediumType = MediumType_Immutable;
+ else if (!RTStrICmp(psz, "writethrough"))
+ enmMediumType = MediumType_Writethrough;
+ else if (!RTStrICmp(psz, "shareable"))
+ enmMediumType = MediumType_Shareable;
+ else if (!RTStrICmp(psz, "readonly"))
+ enmMediumType = MediumType_Readonly;
+ else if (!RTStrICmp(psz, "multiattach"))
+ enmMediumType = MediumType_MultiAttach;
+ else
+ rc = VERR_PARSE_ERROR;
+
+ if (RT_SUCCESS(rc))
+ *penmMediumType = enmMediumType;
+ return rc;
+}
+
+/** @todo move this into getopt, as getting bool values is generic */
+int parseBool(const char *psz, bool *pb)
+{
+ int rc = VINF_SUCCESS;
+ if ( !RTStrICmp(psz, "on")
+ || !RTStrICmp(psz, "yes")
+ || !RTStrICmp(psz, "true")
+ || !RTStrICmp(psz, "1")
+ || !RTStrICmp(psz, "enable")
+ || !RTStrICmp(psz, "enabled"))
+ {
+ *pb = true;
+ }
+ else if ( !RTStrICmp(psz, "off")
+ || !RTStrICmp(psz, "no")
+ || !RTStrICmp(psz, "false")
+ || !RTStrICmp(psz, "0")
+ || !RTStrICmp(psz, "disable")
+ || !RTStrICmp(psz, "disabled"))
+ {
+ *pb = false;
+ }
+ else
+ rc = VERR_PARSE_ERROR;
+
+ return rc;
+}
+
+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.isValid())
+ {
+ int irc = RTPathAbs(pszFilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
+ if (RT_FAILURE(irc))
+ {
+ if (!fSilent)
+ RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilenameOrUuid);
+ return E_FAIL;
+ }
+ pszFilenameOrUuid = szFilenameAbs;
+ }
+
+ if (!fSilent)
+ CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(pszFilenameOrUuid).raw(),
+ enmDevType,
+ enmAccessMode,
+ fForceNewUuidOnOpen,
+ pMedium.asOutParam()));
+ else
+ rc = a->virtualBox->OpenMedium(Bstr(pszFilenameOrUuid).raw(),
+ enmDevType,
+ enmAccessMode,
+ fForceNewUuidOnOpen,
+ pMedium.asOutParam());
+
+ return rc;
+}
+
+static HRESULT createMedium(HandlerArg *a, const char *pszFormat,
+ const char *pszFilename, DeviceType_T enmDevType,
+ AccessMode_T enmAccessMode, ComPtr<IMedium> &pMedium)
+{
+ HRESULT rc;
+ char szFilenameAbs[RTPATH_MAX] = "";
+
+ /** @todo laziness shortcut. should really check the MediumFormatCapabilities */
+ if (RTStrICmp(pszFormat, "iSCSI"))
+ {
+ int irc = RTPathAbs(pszFilename, szFilenameAbs, sizeof(szFilenameAbs));
+ if (RT_FAILURE(irc))
+ {
+ RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilename);
+ return E_FAIL;
+ }
+ pszFilename = szFilenameAbs;
+ }
+
+ CHECK_ERROR(a->virtualBox, CreateMedium(Bstr(pszFormat).raw(),
+ Bstr(pszFilename).raw(),
+ enmAccessMode,
+ enmDevType,
+ pMedium.asOutParam()));
+ return rc;
+}
+
+static const RTGETOPTDEF g_aCreateMediumOptions[] =
+{
+ { "disk", 'H', RTGETOPT_REQ_NOTHING },
+ { "dvd", 'D', RTGETOPT_REQ_NOTHING },
+ { "floppy", 'L', RTGETOPT_REQ_NOTHING },
+ { "--filename", 'f', RTGETOPT_REQ_STRING },
+ { "-filename", 'f', RTGETOPT_REQ_STRING }, // deprecated
+ { "--diffparent", 'd', RTGETOPT_REQ_STRING },
+ { "--size", 's', RTGETOPT_REQ_UINT64 },
+ { "-size", 's', RTGETOPT_REQ_UINT64 }, // deprecated
+ { "--sizebyte", 'S', RTGETOPT_REQ_UINT64 },
+ { "--format", 'o', RTGETOPT_REQ_STRING },
+ { "-format", 'o', RTGETOPT_REQ_STRING }, // deprecated
+ { "--static", 'F', RTGETOPT_REQ_NOTHING },
+ { "-static", 'F', RTGETOPT_REQ_NOTHING }, // deprecated
+ { "--variant", 'm', RTGETOPT_REQ_STRING },
+ { "-variant", 'm', RTGETOPT_REQ_STRING }, // deprecated
+};
+
+RTEXITCODE handleCreateMedium(HandlerArg *a)
+{
+ HRESULT rc;
+ int vrc;
+ const char *filename = NULL;
+ const char *diffparent = NULL;
+ uint64_t size = 0;
+ enum {
+ CMD_NONE,
+ CMD_DISK,
+ CMD_DVD,
+ CMD_FLOPPY
+ } cmd = CMD_NONE;
+ const char *format = NULL;
+ bool fBase = true;
+ MediumVariant_T enmMediumVariant = MediumVariant_Standard;
+
+ 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_aCreateMediumOptions, RT_ELEMENTS(g_aCreateMediumOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'H': // disk
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_CREATEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_DISK;
+ break;
+
+ case 'D': // DVD
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_CREATEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_DVD;
+ break;
+
+ case 'L': // floppy
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_CREATEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_FLOPPY;
+ break;
+
+ case 'f': // --filename
+ filename = ValueUnion.psz;
+ break;
+
+ case 'd': // --diffparent
+ diffparent = ValueUnion.psz;
+ fBase = false;
+ break;
+
+ case 's': // --size
+ size = ValueUnion.u64 * _1M;
+ break;
+
+ case 'S': // --sizebyte
+ size = ValueUnion.u64;
+ break;
+
+ case 'o': // --format
+ format = ValueUnion.psz;
+ break;
+
+ case 'F': // --static ("fixed"/"flat")
+ {
+ unsigned uMediumVariant = (unsigned)enmMediumVariant;
+ uMediumVariant |= MediumVariant_Fixed;
+ enmMediumVariant = (MediumVariant_T)uMediumVariant;
+ break;
+ }
+
+ case 'm': // --variant
+ vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
+ if (RT_FAILURE(vrc))
+ return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ return errorSyntax(USAGE_CREATEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_PRINT(c))
+ return errorSyntax(USAGE_CREATEMEDIUM, "Invalid option -%c", c);
+ else
+ return errorSyntax(USAGE_CREATEMEDIUM, "Invalid option case %i", c);
+ }
+ else if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_CREATEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
+ else if (ValueUnion.pDef)
+ return errorSyntax(USAGE_CREATEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ else
+ return errorSyntax(USAGE_CREATEMEDIUM, "error: %Rrs", c);
+ }
+ }
+
+ /* check the outcome */
+ if (cmd == CMD_NONE)
+ cmd = CMD_DISK;
+ ComPtr<IMedium> pParentMedium;
+ if (fBase)
+ {
+ if ( !filename
+ || !*filename
+ || size == 0)
+ return errorSyntax(USAGE_CREATEMEDIUM, "Parameters --filename and --size are required");
+ if (!format || !*format)
+ {
+ if (cmd == CMD_DISK)
+ format = "VDI";
+ else if (cmd == CMD_DVD || cmd == CMD_FLOPPY)
+ {
+ format = "RAW";
+ unsigned uMediumVariant = (unsigned)enmMediumVariant;
+ uMediumVariant |= MediumVariant_Fixed;
+ enmMediumVariant = (MediumVariant_T)uMediumVariant;
+ }
+ }
+ }
+ else
+ {
+ if ( !filename
+ || !*filename)
+ return errorSyntax(USAGE_CREATEMEDIUM, "Parameters --filename is required");
+ size = 0;
+ if (cmd != CMD_DISK)
+ return errorSyntax(USAGE_CREATEMEDIUM, "Creating a differencing medium is only supported for hard disks");
+ enmMediumVariant = MediumVariant_Diff;
+ if (!format || !*format)
+ {
+ const char *pszExt = RTPathSuffix(filename);
+ /* Skip over . if there is an extension. */
+ if (pszExt)
+ pszExt++;
+ if (!pszExt || !*pszExt)
+ format = "VDI";
+ else
+ format = pszExt;
+ }
+ rc = openMedium(a, diffparent, DeviceType_HardDisk,
+ AccessMode_ReadWrite, pParentMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+ if (pParentMedium.isNull())
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid parent hard disk reference, avoiding crash");
+ MediumState_T state;
+ CHECK_ERROR(pParentMedium, COMGETTER(State)(&state));
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+ if (state == MediumState_Inaccessible)
+ {
+ CHECK_ERROR(pParentMedium, RefreshState(&state));
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ /* check for filename extension */
+ /** @todo use IMediumFormat to cover all extensions generically */
+ Utf8Str strName(filename);
+ if (!RTPathHasSuffix(strName.c_str()))
+ {
+ Utf8Str strFormat(format);
+ if (cmd == CMD_DISK)
+ {
+ if (strFormat.compare("vmdk", RTCString::CaseInsensitive) == 0)
+ strName.append(".vmdk");
+ else if (strFormat.compare("vhd", RTCString::CaseInsensitive) == 0)
+ strName.append(".vhd");
+ else
+ strName.append(".vdi");
+ } else if (cmd == CMD_DVD)
+ strName.append(".iso");
+ else if (cmd == CMD_FLOPPY)
+ strName.append(".img");
+ filename = strName.c_str();
+ }
+
+ ComPtr<IMedium> pMedium;
+ if (cmd == CMD_DISK)
+ rc = createMedium(a, format, filename, DeviceType_HardDisk,
+ AccessMode_ReadWrite, pMedium);
+ else if (cmd == CMD_DVD)
+ rc = createMedium(a, format, filename, DeviceType_DVD,
+ AccessMode_ReadOnly, pMedium);
+ else if (cmd == CMD_FLOPPY)
+ rc = createMedium(a, format, filename, DeviceType_Floppy,
+ AccessMode_ReadWrite, pMedium);
+ else
+ rc = E_INVALIDARG; /* cannot happen but make gcc happy */
+
+ if (SUCCEEDED(rc) && pMedium)
+ {
+ ComPtr<IProgress> pProgress;
+ com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
+
+ for (ULONG i = 0; i < l_variants.size(); ++i)
+ {
+ ULONG temp = enmMediumVariant;
+ temp &= 1<<i;
+ l_variants [i] = (MediumVariant_T)temp;
+ }
+
+ if (fBase)
+ CHECK_ERROR(pMedium, CreateBaseStorage(size, ComSafeArrayAsInParam(l_variants), pProgress.asOutParam()));
+ else
+ CHECK_ERROR(pParentMedium, CreateDiffStorage(pMedium, ComSafeArrayAsInParam(l_variants), pProgress.asOutParam()));
+ if (SUCCEEDED(rc) && pProgress)
+ {
+ rc = showProgress(pProgress);
+ CHECK_PROGRESS_ERROR(pProgress, ("Failed to create medium"));
+ }
+ }
+
+ if (SUCCEEDED(rc) && pMedium)
+ {
+ Bstr uuid;
+ CHECK_ERROR(pMedium, COMGETTER(Id)(uuid.asOutParam()));
+ RTPrintf("Medium created. UUID: %s\n", Utf8Str(uuid).c_str());
+
+ //CHECK_ERROR(pMedium, Close());
+ }
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static const RTGETOPTDEF g_aModifyMediumOptions[] =
+{
+ { "disk", 'H', RTGETOPT_REQ_NOTHING },
+ { "dvd", 'D', RTGETOPT_REQ_NOTHING },
+ { "floppy", 'L', RTGETOPT_REQ_NOTHING },
+ { "--type", 't', RTGETOPT_REQ_STRING },
+ { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
+ { "settype", 't', RTGETOPT_REQ_STRING }, // deprecated
+ { "--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
+ { "--resize", 'r', RTGETOPT_REQ_UINT64 },
+ { "--resizebyte", 'R', RTGETOPT_REQ_UINT64 },
+ { "--move", 'm', RTGETOPT_REQ_STRING },
+ { "--setlocation", 'l', RTGETOPT_REQ_STRING },
+ { "--description", 'd', RTGETOPT_REQ_STRING }
+};
+
+RTEXITCODE handleModifyMedium(HandlerArg *a)
+{
+ HRESULT rc;
+ int vrc;
+ enum {
+ CMD_NONE,
+ CMD_DISK,
+ CMD_DVD,
+ CMD_FLOPPY
+ } cmd = CMD_NONE;
+ ComPtr<IMedium> pMedium;
+ MediumType_T enmMediumType = MediumType_Normal; /* Shut up MSC */
+ bool AutoReset = false;
+ SafeArray<BSTR> mediumPropNames;
+ SafeArray<BSTR> mediumPropValues;
+ bool fModifyMediumType = false;
+ bool fModifyAutoReset = false;
+ bool fModifyProperties = false;
+ bool fModifyCompact = false;
+ bool fModifyResize = false;
+ bool fModifyResizeMB = false;
+ bool fMoveMedium = false;
+ bool fModifyDescription = false;
+ bool fSetNewLocation = false;
+ uint64_t cbResize = 0;
+ const char *pszFilenameOrUuid = NULL;
+ char *pszNewLocation = 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_aModifyMediumOptions, RT_ELEMENTS(g_aModifyMediumOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'H': // disk
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_MODIFYMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_DISK;
+ break;
+
+ case 'D': // DVD
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_MODIFYMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_DVD;
+ break;
+
+ case 'L': // floppy
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_MODIFYMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_FLOPPY;
+ break;
+
+ case 't': // --type
+ vrc = parseMediumType(ValueUnion.psz, &enmMediumType);
+ if (RT_FAILURE(vrc))
+ return errorArgument("Invalid medium type '%s'", ValueUnion.psz);
+ fModifyMediumType = true;
+ break;
+
+ case 'z': // --autoreset
+ vrc = parseBool(ValueUnion.psz, &AutoReset);
+ if (RT_FAILURE(vrc))
+ return errorArgument("Invalid autoreset parameter '%s'", ValueUnion.psz);
+ 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;
+
+ case 'r': // --resize
+ cbResize = ValueUnion.u64 * _1M;
+ fModifyResize = true;
+ fModifyResizeMB = true; // do sanity check!
+ break;
+
+ case 'R': // --resizebyte
+ cbResize = ValueUnion.u64;
+ fModifyResize = true;
+ break;
+
+ case 'm': // --move
+ /* Get a new location */
+ pszNewLocation = RTPathAbsDup(ValueUnion.psz);
+ fMoveMedium = true;
+ break;
+
+ case 'l': // --setlocation
+ /* Get a new location */
+ pszNewLocation = RTPathAbsDup(ValueUnion.psz);
+ fSetNewLocation = true;
+ break;
+
+ case 'd': // --description
+ /* Get a new description */
+ pszNewLocation = RTStrDup(ValueUnion.psz);
+ fModifyDescription = true;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszFilenameOrUuid)
+ pszFilenameOrUuid = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_MODIFYMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_PRINT(c))
+ return errorSyntax(USAGE_MODIFYMEDIUM, "Invalid option -%c", c);
+ else
+ return errorSyntax(USAGE_MODIFYMEDIUM, "Invalid option case %i", c);
+ }
+ else if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_MODIFYMEDIUM, "unknown option: %s\n", ValueUnion.psz);
+ else if (ValueUnion.pDef)
+ return errorSyntax(USAGE_MODIFYMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ else
+ return errorSyntax(USAGE_MODIFYMEDIUM, "error: %Rrs", c);
+ }
+ }
+
+ if (cmd == CMD_NONE)
+ cmd = CMD_DISK;
+
+ if (!pszFilenameOrUuid)
+ return errorSyntax(USAGE_MODIFYMEDIUM, "Medium name or UUID required");
+
+ if (!fModifyMediumType
+ && !fModifyAutoReset
+ && !fModifyProperties
+ && !fModifyCompact
+ && !fModifyResize
+ && !fMoveMedium
+ && !fSetNewLocation
+ && !fModifyDescription
+ )
+ return errorSyntax(USAGE_MODIFYMEDIUM, "No operation specified");
+
+ /* Always open the medium if necessary, there is no other way. */
+ if (cmd == CMD_DISK)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
+ AccessMode_ReadWrite, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_DVD)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
+ AccessMode_ReadOnly, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_FLOPPY)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
+ AccessMode_ReadWrite, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else
+ rc = E_INVALIDARG; /* cannot happen but make gcc happy */
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+ if (pMedium.isNull())
+ {
+ RTMsgError("Invalid medium reference, avoiding crash");
+ return RTEXITCODE_FAILURE;
+ }
+
+ if ( fModifyResize
+ && fModifyResizeMB)
+ {
+ // Sanity check
+ //
+ // In general users should know what they do but in this case users have no
+ // alternative to VBoxManage. If happens that one wants to resize the disk
+ // and uses --resize and does not consider that this parameter expects the
+ // new medium size in MB not Byte. If the operation is started and then
+ // aborted by the user, the result is most likely a medium which doesn't
+ // work anymore.
+ MediumState_T state;
+ pMedium->RefreshState(&state);
+ LONG64 logicalSize;
+ pMedium->COMGETTER(LogicalSize)(&logicalSize);
+ if (cbResize > (uint64_t)logicalSize * 1000)
+ {
+ RTMsgError("Error: Attempt to resize the medium from %RU64.%RU64 MB to %RU64.%RU64 MB. Use --resizebyte if this is intended!\n",
+ logicalSize / _1M, (logicalSize % _1M) / (_1M / 10), cbResize / _1M, (cbResize % _1M) / (_1M / 10));
+ return RTEXITCODE_FAILURE;
+ }
+ }
+
+ if (fModifyMediumType)
+ {
+ MediumType_T enmCurrMediumType;
+ CHECK_ERROR(pMedium, COMGETTER(Type)(&enmCurrMediumType));
+
+ if (enmCurrMediumType != enmMediumType)
+ CHECK_ERROR(pMedium, COMSETTER(Type)(enmMediumType));
+ }
+
+ if (fModifyAutoReset)
+ {
+ CHECK_ERROR(pMedium, COMSETTER(AutoReset)(AutoReset));
+ }
+
+ if (fModifyProperties)
+ {
+ CHECK_ERROR(pMedium, SetProperties(ComSafeArrayAsInParam(mediumPropNames), ComSafeArrayAsInParam(mediumPropValues)));
+ }
+
+ if (fModifyCompact)
+ {
+ ComPtr<IProgress> pProgress;
+ CHECK_ERROR(pMedium, Compact(pProgress.asOutParam()));
+ if (SUCCEEDED(rc))
+ rc = showProgress(pProgress);
+ if (FAILED(rc))
+ {
+ if (rc == E_NOTIMPL)
+ RTMsgError("Compact medium operation is not implemented!");
+ else if (rc == VBOX_E_NOT_SUPPORTED)
+ RTMsgError("Compact medium operation for this format is not implemented yet!");
+ else if (!pProgress.isNull())
+ CHECK_PROGRESS_ERROR(pProgress, ("Failed to compact medium"));
+ else
+ RTMsgError("Failed to compact medium!");
+ }
+ }
+
+ if (fModifyResize)
+ {
+ ComPtr<IProgress> pProgress;
+ CHECK_ERROR(pMedium, Resize(cbResize, pProgress.asOutParam()));
+ if (SUCCEEDED(rc))
+ rc = showProgress(pProgress);
+ if (FAILED(rc))
+ {
+ if (!pProgress.isNull())
+ CHECK_PROGRESS_ERROR(pProgress, ("Failed to resize medium"));
+ else if (rc == E_NOTIMPL)
+ RTMsgError("Resize medium operation is not implemented!");
+ else if (rc == VBOX_E_NOT_SUPPORTED)
+ RTMsgError("Resize medium operation for this format is not implemented yet!");
+ else
+ RTMsgError("Failed to resize medium!");
+ }
+ }
+
+ if (fMoveMedium)
+ {
+ do
+ {
+ ComPtr<IProgress> pProgress;
+ Utf8Str strLocation(pszNewLocation);
+ RTStrFree(pszNewLocation);
+ CHECK_ERROR(pMedium, MoveTo(Bstr(strLocation).raw(), pProgress.asOutParam()));
+
+ if (SUCCEEDED(rc) && !pProgress.isNull())
+ {
+ rc = showProgress(pProgress);
+ CHECK_PROGRESS_ERROR(pProgress, ("Failed to move medium"));
+ }
+
+ Bstr uuid;
+ CHECK_ERROR_BREAK(pMedium, COMGETTER(Id)(uuid.asOutParam()));
+
+ RTPrintf("Move medium with UUID %s finished\n", Utf8Str(uuid).c_str());
+ }
+ while (0);
+ }
+
+ if (fSetNewLocation)
+ {
+ Utf8Str strLocation(pszNewLocation);
+ RTStrFree(pszNewLocation);
+ CHECK_ERROR(pMedium, COMSETTER(Location)(Bstr(strLocation).raw()));
+
+ Bstr uuid;
+ CHECK_ERROR(pMedium, COMGETTER(Id)(uuid.asOutParam()));
+ RTPrintf("Set new location of medium with UUID %s finished\n", Utf8Str(uuid).c_str());
+ }
+
+ if (fModifyDescription)
+ {
+ CHECK_ERROR(pMedium, COMSETTER(Description)(Bstr(pszNewLocation).raw()));
+
+ RTPrintf("Medium description has been changed.\n");
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static const RTGETOPTDEF g_aCloneMediumOptions[] =
+{
+ { "disk", 'd', RTGETOPT_REQ_NOTHING },
+ { "dvd", 'D', RTGETOPT_REQ_NOTHING },
+ { "floppy", 'f', RTGETOPT_REQ_NOTHING },
+ { "--format", 'o', RTGETOPT_REQ_STRING },
+ { "-format", 'o', RTGETOPT_REQ_STRING },
+ { "--static", 'F', RTGETOPT_REQ_NOTHING },
+ { "-static", 'F', RTGETOPT_REQ_NOTHING },
+ { "--existing", 'E', RTGETOPT_REQ_NOTHING },
+ { "--variant", 'm', RTGETOPT_REQ_STRING },
+ { "-variant", 'm', RTGETOPT_REQ_STRING },
+};
+
+RTEXITCODE handleCloneMedium(HandlerArg *a)
+{
+ HRESULT rc;
+ int vrc;
+ enum {
+ CMD_NONE,
+ CMD_DISK,
+ CMD_DVD,
+ CMD_FLOPPY
+ } cmd = CMD_NONE;
+ const char *pszSrc = NULL;
+ const char *pszDst = NULL;
+ Bstr format;
+ MediumVariant_T enmMediumVariant = MediumVariant_Standard;
+ bool fExisting = false;
+
+ 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_aCloneMediumOptions, RT_ELEMENTS(g_aCloneMediumOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'd': // disk
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_CLONEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_DISK;
+ break;
+
+ case 'D': // DVD
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_CLONEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_DVD;
+ break;
+
+ case 'f': // floppy
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_CLONEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_FLOPPY;
+ break;
+
+ case 'o': // --format
+ format = ValueUnion.psz;
+ break;
+
+ case 'F': // --static
+ {
+ unsigned uMediumVariant = (unsigned)enmMediumVariant;
+ uMediumVariant |= MediumVariant_Fixed;
+ enmMediumVariant = (MediumVariant_T)uMediumVariant;
+ break;
+ }
+
+ case 'E': // --existing
+ fExisting = true;
+ break;
+
+ case 'm': // --variant
+ vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
+ if (RT_FAILURE(vrc))
+ return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszSrc)
+ pszSrc = ValueUnion.psz;
+ else if (!pszDst)
+ pszDst = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_CLONEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_GRAPH(c))
+ return errorSyntax(USAGE_CLONEMEDIUM, "unhandled option: -%c", c);
+ else
+ return errorSyntax(USAGE_CLONEMEDIUM, "unhandled option: %i", c);
+ }
+ else if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_CLONEMEDIUM, "unknown option: %s", ValueUnion.psz);
+ else if (ValueUnion.pDef)
+ return errorSyntax(USAGE_CLONEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ else
+ return errorSyntax(USAGE_CLONEMEDIUM, "error: %Rrs", c);
+ }
+ }
+
+ if (cmd == CMD_NONE)
+ cmd = CMD_DISK;
+ if (!pszSrc)
+ return errorSyntax(USAGE_CLONEMEDIUM, "Mandatory UUID or input file parameter missing");
+ if (!pszDst)
+ return errorSyntax(USAGE_CLONEMEDIUM, "Mandatory output file parameter missing");
+ if (fExisting && (!format.isEmpty() || enmMediumVariant != MediumVariant_Standard))
+ return errorSyntax(USAGE_CLONEMEDIUM, "Specified options which cannot be used with --existing");
+
+ ComPtr<IMedium> pSrcMedium;
+ ComPtr<IMedium> pDstMedium;
+
+ if (cmd == CMD_DISK)
+ rc = openMedium(a, pszSrc, DeviceType_HardDisk, AccessMode_ReadOnly, pSrcMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_DVD)
+ rc = openMedium(a, pszSrc, DeviceType_DVD, AccessMode_ReadOnly, pSrcMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_FLOPPY)
+ rc = openMedium(a, pszSrc, DeviceType_Floppy, AccessMode_ReadOnly, pSrcMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else
+ rc = E_INVALIDARG; /* cannot happen but make gcc happy */
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ do
+ {
+ /* open/create destination medium */
+ if (fExisting)
+ {
+ if (cmd == CMD_DISK)
+ rc = openMedium(a, pszDst, DeviceType_HardDisk, AccessMode_ReadWrite, pDstMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_DVD)
+ rc = openMedium(a, pszDst, DeviceType_DVD, AccessMode_ReadOnly, pDstMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_FLOPPY)
+ rc = openMedium(a, pszDst, DeviceType_Floppy, AccessMode_ReadWrite, pDstMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ if (FAILED(rc))
+ break;
+
+ /* Perform accessibility check now. */
+ MediumState_T state;
+ CHECK_ERROR_BREAK(pDstMedium, RefreshState(&state));
+ CHECK_ERROR_BREAK(pDstMedium, COMGETTER(Format)(format.asOutParam()));
+ }
+ else
+ {
+ /*
+ * In case the format is unspecified check that the source medium supports
+ * image creation and use the same format for the destination image.
+ * Use the default image format if it is not supported.
+ */
+ if (format.isEmpty())
+ {
+ ComPtr<IMediumFormat> pMediumFmt;
+ com::SafeArray<MediumFormatCapabilities_T> l_caps;
+ CHECK_ERROR_BREAK(pSrcMedium, COMGETTER(MediumFormat)(pMediumFmt.asOutParam()));
+ CHECK_ERROR_BREAK(pMediumFmt, COMGETTER(Capabilities)(ComSafeArrayAsOutParam(l_caps)));
+ ULONG caps=0;
+ for (size_t i = 0; i < l_caps.size(); i++)
+ caps |= l_caps[i];
+ if (caps & ( MediumFormatCapabilities_CreateDynamic
+ | MediumFormatCapabilities_CreateFixed))
+ CHECK_ERROR_BREAK(pMediumFmt, COMGETTER(Id)(format.asOutParam()));
+ }
+ Utf8Str strFormat(format);
+ if (cmd == CMD_DISK)
+ rc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_HardDisk,
+ AccessMode_ReadWrite, pDstMedium);
+ else if (cmd == CMD_DVD)
+ rc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_DVD,
+ AccessMode_ReadOnly, pDstMedium);
+ else if (cmd == CMD_FLOPPY)
+ rc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_Floppy,
+ AccessMode_ReadWrite, pDstMedium);
+ if (FAILED(rc))
+ break;
+ }
+
+ ComPtr<IProgress> pProgress;
+ com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
+
+ for (ULONG i = 0; i < l_variants.size(); ++i)
+ {
+ ULONG temp = enmMediumVariant;
+ temp &= 1<<i;
+ l_variants [i] = (MediumVariant_T)temp;
+ }
+
+ CHECK_ERROR_BREAK(pSrcMedium, CloneTo(pDstMedium, ComSafeArrayAsInParam(l_variants), NULL, pProgress.asOutParam()));
+
+ rc = showProgress(pProgress);
+ CHECK_PROGRESS_ERROR_BREAK(pProgress, ("Failed to clone medium"));
+
+ Bstr uuid;
+ CHECK_ERROR_BREAK(pDstMedium, COMGETTER(Id)(uuid.asOutParam()));
+
+ RTPrintf("Clone medium created in format '%ls'. UUID: %s\n",
+ format.raw(), Utf8Str(uuid).c_str());
+ }
+ while (0);
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static const RTGETOPTDEF g_aConvertFromRawHardDiskOptions[] =
+{
+ { "--format", 'o', RTGETOPT_REQ_STRING },
+ { "-format", 'o', RTGETOPT_REQ_STRING },
+ { "--static", 'F', RTGETOPT_REQ_NOTHING },
+ { "-static", 'F', RTGETOPT_REQ_NOTHING },
+ { "--variant", 'm', RTGETOPT_REQ_STRING },
+ { "-variant", 'm', RTGETOPT_REQ_STRING },
+ { "--uuid", 'u', RTGETOPT_REQ_STRING },
+};
+
+RTEXITCODE handleConvertFromRaw(HandlerArg *a)
+{
+ int rc = VINF_SUCCESS;
+ bool fReadFromStdIn = false;
+ const char *format = "VDI";
+ const char *srcfilename = NULL;
+ const char *dstfilename = NULL;
+ const char *filesize = NULL;
+ unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
+ void *pvBuf = NULL;
+ RTUUID uuid;
+ PCRTUUID pUuid = 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_aConvertFromRawHardDiskOptions, RT_ELEMENTS(g_aConvertFromRawHardDiskOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'u': // --uuid
+ if (RT_FAILURE(RTUuidFromStr(&uuid, ValueUnion.psz)))
+ return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid UUID '%s'", ValueUnion.psz);
+ pUuid = &uuid;
+ break;
+ case 'o': // --format
+ format = ValueUnion.psz;
+ break;
+
+ case 'm': // --variant
+ {
+ MediumVariant_T enmMediumVariant = MediumVariant_Standard;
+ rc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
+ if (RT_FAILURE(rc))
+ return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
+ /// @todo cleaner solution than assuming 1:1 mapping?
+ uImageFlags = (unsigned)enmMediumVariant;
+ break;
+ }
+ case VINF_GETOPT_NOT_OPTION:
+ if (!srcfilename)
+ {
+ srcfilename = ValueUnion.psz;
+ fReadFromStdIn = !strcmp(srcfilename, "stdin");
+ }
+ else if (!dstfilename)
+ dstfilename = ValueUnion.psz;
+ else if (fReadFromStdIn && !filesize)
+ filesize = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ return errorGetOpt(USAGE_CONVERTFROMRAW, c, &ValueUnion);
+ }
+ }
+
+ if (!srcfilename || !dstfilename || (fReadFromStdIn && !filesize))
+ return errorSyntax(USAGE_CONVERTFROMRAW, "Incorrect number of parameters");
+ RTStrmPrintf(g_pStdErr, "Converting from raw image file=\"%s\" to file=\"%s\"...\n",
+ srcfilename, dstfilename);
+
+ PVDISK pDisk = NULL;
+
+ PVDINTERFACE pVDIfs = NULL;
+ VDINTERFACEERROR vdInterfaceError;
+ vdInterfaceError.pfnError = handleVDError;
+ vdInterfaceError.pfnMessage = NULL;
+
+ rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+ NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
+ AssertRC(rc);
+
+ /* open raw image file. */
+ RTFILE File;
+ if (fReadFromStdIn)
+ rc = RTFileFromNative(&File, RTFILE_NATIVE_STDIN);
+ else
+ rc = RTFileOpen(&File, srcfilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Cannot open file \"%s\": %Rrc", srcfilename, rc);
+ goto out;
+ }
+
+ uint64_t cbFile;
+ /* get image size. */
+ if (fReadFromStdIn)
+ cbFile = RTStrToUInt64(filesize);
+ else
+ rc = RTFileGetSize(File, &cbFile);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Cannot get image size for file \"%s\": %Rrc", srcfilename, rc);
+ goto out;
+ }
+
+ RTStrmPrintf(g_pStdErr, "Creating %s image with size %RU64 bytes (%RU64MB)...\n",
+ (uImageFlags & VD_IMAGE_FLAGS_FIXED) ? "fixed" : "dynamic", cbFile, (cbFile + _1M - 1) / _1M);
+ char pszComment[256];
+ RTStrPrintf(pszComment, sizeof(pszComment), "Converted image from %s", srcfilename);
+ rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Cannot create the virtual disk container: %Rrc", rc);
+ goto out;
+ }
+
+ Assert(RT_MIN(cbFile / 512 / 16 / 63, 16383) -
+ (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383) == 0);
+ VDGEOMETRY PCHS, LCHS;
+ PCHS.cCylinders = (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383);
+ PCHS.cHeads = 16;
+ PCHS.cSectors = 63;
+ LCHS.cCylinders = 0;
+ LCHS.cHeads = 0;
+ LCHS.cSectors = 0;
+ rc = VDCreateBase(pDisk, format, dstfilename, cbFile,
+ uImageFlags, pszComment, &PCHS, &LCHS, pUuid,
+ VD_OPEN_FLAGS_NORMAL, NULL, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Cannot create the disk image \"%s\": %Rrc", dstfilename, rc);
+ goto out;
+ }
+
+ size_t cbBuffer;
+ cbBuffer = _1M;
+ pvBuf = RTMemAlloc(cbBuffer);
+ if (!pvBuf)
+ {
+ rc = VERR_NO_MEMORY;
+ RTMsgError("Out of memory allocating buffers for image \"%s\": %Rrc", dstfilename, rc);
+ goto out;
+ }
+
+ uint64_t offFile;
+ offFile = 0;
+ while (offFile < cbFile)
+ {
+ size_t cbRead;
+ size_t cbToRead;
+ cbRead = 0;
+ cbToRead = cbFile - offFile >= (uint64_t)cbBuffer ?
+ cbBuffer : (size_t)(cbFile - offFile);
+ rc = RTFileRead(File, pvBuf, cbToRead, &cbRead);
+ if (RT_FAILURE(rc) || !cbRead)
+ break;
+ rc = VDWrite(pDisk, offFile, pvBuf, cbRead);
+ if (RT_FAILURE(rc))
+ {
+ RTMsgError("Failed to write to disk image \"%s\": %Rrc", dstfilename, rc);
+ goto out;
+ }
+ offFile += cbRead;
+ }
+
+out:
+ if (pvBuf)
+ RTMemFree(pvBuf);
+ if (pDisk)
+ VDClose(pDisk, RT_FAILURE(rc));
+ if (File != NIL_RTFILE)
+ RTFileClose(File);
+
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+HRESULT showMediumInfo(const ComPtr<IVirtualBox> &pVirtualBox,
+ const ComPtr<IMedium> &pMedium,
+ const char *pszParentUUID,
+ bool fOptLong)
+{
+ HRESULT rc = S_OK;
+ do
+ {
+ Bstr uuid;
+ pMedium->COMGETTER(Id)(uuid.asOutParam());
+ RTPrintf("UUID: %ls\n", uuid.raw());
+ if (pszParentUUID)
+ RTPrintf("Parent UUID: %s\n", pszParentUUID);
+
+ /* check for accessibility */
+ MediumState_T enmState;
+ CHECK_ERROR_BREAK(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;
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+ case MediumState_32BitHack: break; /* Shut up compiler warnings. */
+#endif
+ }
+ RTPrintf("State: %s\n", pszState);
+
+ if (fOptLong && enmState == MediumState_Inaccessible)
+ {
+ Bstr err;
+ CHECK_ERROR_BREAK(pMedium, COMGETTER(LastAccessError)(err.asOutParam()));
+ RTPrintf("Access Error: %ls\n", err.raw());
+ }
+
+ if (fOptLong)
+ {
+ Bstr description;
+ pMedium->COMGETTER(Description)(description.asOutParam());
+ if (!description.isEmpty())
+ RTPrintf("Description: %ls\n", description.raw());
+ }
+
+ MediumType_T type;
+ pMedium->COMGETTER(Type)(&type);
+ const char *typeStr = "unknown";
+ switch (type)
+ {
+ case MediumType_Normal:
+ if (pszParentUUID && Guid(pszParentUUID).isValid())
+ typeStr = "normal (differencing)";
+ else
+ typeStr = "normal (base)";
+ 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;
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+ case MediumType_32BitHack: break; /* Shut up compiler warnings. */
+#endif
+ }
+ RTPrintf("Type: %s\n", typeStr);
+
+ /* print out information specific for differencing media */
+ 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;
+ pMedium->COMGETTER(Format)(format.asOutParam());
+ RTPrintf("Storage format: %ls\n", format.raw());
+
+ if (fOptLong)
+ {
+ 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);
+ }
+
+ Bstr strCipher;
+ Bstr strPasswordId;
+ HRESULT rc2 = pMedium->GetEncryptionSettings(strCipher.asOutParam(), strPasswordId.asOutParam());
+ if (SUCCEEDED(rc2))
+ {
+ RTPrintf("Encryption: enabled\n");
+ if (fOptLong)
+ {
+ RTPrintf("Cipher: %ls\n", strCipher.raw());
+ RTPrintf("Password ID: %ls\n", strPasswordId.raw());
+ }
+ }
+ else
+ RTPrintf("Encryption: disabled\n");
+
+ 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());
+ fFirst = false;
+ }
+ }
+
+ if (fOptLong)
+ {
+ bool fFirst = true;
+ com::SafeArray<BSTR> machineIds;
+ pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds));
+ for (size_t i = 0; i < machineIds.size(); i++)
+ {
+ ComPtr<IMachine> pMachine;
+ CHECK_ERROR(pVirtualBox, FindMachine(machineIds[i], pMachine.asOutParam()));
+ if (pMachine)
+ {
+ Bstr name;
+ pMachine->COMGETTER(Name)(name.asOutParam());
+ pMachine->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> pSnapshot;
+ pMachine->FindSnapshot(snapshotIds[j], pSnapshot.asOutParam());
+ if (pSnapshot)
+ {
+ Bstr snapshotName;
+ pSnapshot->COMGETTER(Name)(snapshotName.asOutParam());
+ RTPrintf(" [%ls (UUID: %ls)]", snapshotName.raw(), snapshotIds[j]);
+ }
+ }
+ RTPrintf("\n");
+ }
+ }
+ }
+
+ if (fOptLong)
+ {
+ 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);
+
+ return rc;
+}
+
+static const RTGETOPTDEF g_aShowMediumInfoOptions[] =
+{
+ { "disk", 'd', RTGETOPT_REQ_NOTHING },
+ { "dvd", 'D', RTGETOPT_REQ_NOTHING },
+ { "floppy", 'f', RTGETOPT_REQ_NOTHING },
+};
+
+RTEXITCODE handleShowMediumInfo(HandlerArg *a)
+{
+ enum {
+ CMD_NONE,
+ CMD_DISK,
+ CMD_DVD,
+ CMD_FLOPPY
+ } cmd = CMD_NONE;
+ const char *pszFilenameOrUuid = 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_aShowMediumInfoOptions, RT_ELEMENTS(g_aShowMediumInfoOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'd': // disk
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_DISK;
+ break;
+
+ case 'D': // DVD
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_DVD;
+ break;
+
+ case 'f': // floppy
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_FLOPPY;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszFilenameOrUuid)
+ pszFilenameOrUuid = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_PRINT(c))
+ return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid option -%c", c);
+ else
+ return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid option case %i", c);
+ }
+ else if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_SHOWMEDIUMINFO, "unknown option: %s\n", ValueUnion.psz);
+ else if (ValueUnion.pDef)
+ return errorSyntax(USAGE_SHOWMEDIUMINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ else
+ return errorSyntax(USAGE_SHOWMEDIUMINFO, "error: %Rrs", c);
+ }
+ }
+
+ if (cmd == CMD_NONE)
+ cmd = CMD_DISK;
+
+ /* check for required options */
+ if (!pszFilenameOrUuid)
+ return errorSyntax(USAGE_SHOWMEDIUMINFO, "Medium name or UUID required");
+
+ HRESULT rc = S_OK; /* Prevents warning. */
+
+ ComPtr<IMedium> pMedium;
+ if (cmd == CMD_DISK)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
+ AccessMode_ReadOnly, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_DVD)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
+ AccessMode_ReadOnly, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_FLOPPY)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
+ AccessMode_ReadOnly, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ Utf8Str strParentUUID("base");
+ ComPtr<IMedium> pParent;
+ pMedium->COMGETTER(Parent)(pParent.asOutParam());
+ if (!pParent.isNull())
+ {
+ Bstr bstrParentUUID;
+ pParent->COMGETTER(Id)(bstrParentUUID.asOutParam());
+ strParentUUID = bstrParentUUID;
+ }
+
+ rc = showMediumInfo(a->virtualBox, pMedium, strParentUUID.c_str(), true);
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static const RTGETOPTDEF g_aCloseMediumOptions[] =
+{
+ { "disk", 'd', RTGETOPT_REQ_NOTHING },
+ { "dvd", 'D', RTGETOPT_REQ_NOTHING },
+ { "floppy", 'f', RTGETOPT_REQ_NOTHING },
+ { "--delete", 'r', RTGETOPT_REQ_NOTHING },
+};
+
+RTEXITCODE handleCloseMedium(HandlerArg *a)
+{
+ HRESULT rc = S_OK;
+ enum {
+ CMD_NONE,
+ CMD_DISK,
+ CMD_DVD,
+ CMD_FLOPPY
+ } cmd = CMD_NONE;
+ const char *pszFilenameOrUuid = NULL;
+ bool fDelete = false;
+
+ 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_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'd': // disk
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_DISK;
+ break;
+
+ case 'D': // DVD
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_DVD;
+ break;
+
+ case 'f': // floppy
+ if (cmd != CMD_NONE)
+ return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
+ cmd = CMD_FLOPPY;
+ break;
+
+ case 'r': // --delete
+ fDelete = true;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszFilenameOrUuid)
+ pszFilenameOrUuid = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_PRINT(c))
+ return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c);
+ else
+ return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c);
+ }
+ else if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
+ else if (ValueUnion.pDef)
+ return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ else
+ return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c);
+ }
+ }
+
+ /* check for required options */
+ if (cmd == CMD_NONE)
+ cmd = CMD_DISK;
+ if (!pszFilenameOrUuid)
+ return errorSyntax(USAGE_CLOSEMEDIUM, "Medium name or UUID required");
+
+ ComPtr<IMedium> pMedium;
+ if (cmd == CMD_DISK)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
+ AccessMode_ReadWrite, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_DVD)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
+ AccessMode_ReadOnly, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_FLOPPY)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
+ AccessMode_ReadWrite, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+
+ if (SUCCEEDED(rc) && pMedium)
+ {
+ if (fDelete)
+ {
+ ComPtr<IProgress> pProgress;
+ CHECK_ERROR(pMedium, DeleteStorage(pProgress.asOutParam()));
+ if (SUCCEEDED(rc))
+ {
+ rc = showProgress(pProgress);
+ CHECK_PROGRESS_ERROR(pProgress, ("Failed to delete medium"));
+ }
+ else
+ RTMsgError("Failed to delete medium. Error code %Rrc", rc);
+ }
+ CHECK_ERROR(pMedium, Close());
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+RTEXITCODE handleMediumProperty(HandlerArg *a)
+{
+ HRESULT rc = S_OK;
+ const char *pszCmd = NULL;
+ enum {
+ CMD_NONE,
+ CMD_DISK,
+ CMD_DVD,
+ CMD_FLOPPY
+ } cmd = CMD_NONE;
+ const char *pszAction = NULL;
+ const char *pszFilenameOrUuid = NULL;
+ const char *pszProperty = NULL;
+ ComPtr<IMedium> pMedium;
+
+ pszCmd = (a->argc > 0) ? a->argv[0] : "";
+ if ( !RTStrICmp(pszCmd, "disk")
+ || !RTStrICmp(pszCmd, "dvd")
+ || !RTStrICmp(pszCmd, "floppy"))
+ {
+ if (!RTStrICmp(pszCmd, "disk"))
+ cmd = CMD_DISK;
+ else if (!RTStrICmp(pszCmd, "dvd"))
+ cmd = CMD_DVD;
+ else if (!RTStrICmp(pszCmd, "floppy"))
+ cmd = CMD_FLOPPY;
+ else
+ {
+ AssertMsgFailed(("unexpected parameter %s\n", pszCmd));
+ cmd = CMD_DISK;
+ }
+ a->argv++;
+ a->argc--;
+ }
+ else
+ {
+ pszCmd = NULL;
+ cmd = CMD_DISK;
+ }
+
+ if (a->argc == 0)
+ return errorSyntax(USAGE_MEDIUMPROPERTY, "Missing action");
+
+ pszAction = a->argv[0];
+ if ( RTStrICmp(pszAction, "set")
+ && RTStrICmp(pszAction, "get")
+ && RTStrICmp(pszAction, "delete"))
+ return errorSyntax(USAGE_MEDIUMPROPERTY, "Invalid action given: %s", pszAction);
+
+ if ( ( !RTStrICmp(pszAction, "set")
+ && a->argc != 4)
+ || ( RTStrICmp(pszAction, "set")
+ && a->argc != 3))
+ return errorSyntax(USAGE_MEDIUMPROPERTY, "Invalid number of arguments given for action: %s", pszAction);
+
+ pszFilenameOrUuid = a->argv[1];
+ pszProperty = a->argv[2];
+
+ if (cmd == CMD_DISK)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
+ AccessMode_ReadWrite, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_DVD)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
+ AccessMode_ReadOnly, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ else if (cmd == CMD_FLOPPY)
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
+ AccessMode_ReadWrite, pMedium,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ if (SUCCEEDED(rc) && !pMedium.isNull())
+ {
+ if (!RTStrICmp(pszAction, "set"))
+ {
+ const char *pszValue = a->argv[3];
+ CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr(pszValue).raw()));
+ }
+ else if (!RTStrICmp(pszAction, "get"))
+ {
+ Bstr strVal;
+ CHECK_ERROR(pMedium, GetProperty(Bstr(pszProperty).raw(), strVal.asOutParam()));
+ if (SUCCEEDED(rc))
+ RTPrintf("%s=%ls\n", pszProperty, strVal.raw());
+ }
+ else if (!RTStrICmp(pszAction, "delete"))
+ {
+ CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr().raw()));
+ /** @todo */
+ }
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static const RTGETOPTDEF g_aEncryptMediumOptions[] =
+{
+ { "--newpassword", 'n', RTGETOPT_REQ_STRING },
+ { "--oldpassword", 'o', RTGETOPT_REQ_STRING },
+ { "--cipher", 'c', RTGETOPT_REQ_STRING },
+ { "--newpasswordid", 'i', RTGETOPT_REQ_STRING }
+};
+
+RTEXITCODE handleEncryptMedium(HandlerArg *a)
+{
+ HRESULT rc;
+ ComPtr<IMedium> hardDisk;
+ const char *pszPasswordNew = NULL;
+ const char *pszPasswordOld = NULL;
+ const char *pszCipher = NULL;
+ const char *pszFilenameOrUuid = NULL;
+ const char *pszNewPasswordId = NULL;
+ Utf8Str strPasswordNew;
+ Utf8Str strPasswordOld;
+
+ 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_aEncryptMediumOptions, RT_ELEMENTS(g_aEncryptMediumOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'n': // --newpassword
+ pszPasswordNew = ValueUnion.psz;
+ break;
+
+ case 'o': // --oldpassword
+ pszPasswordOld = ValueUnion.psz;
+ break;
+
+ case 'c': // --cipher
+ pszCipher = ValueUnion.psz;
+ break;
+
+ case 'i': // --newpasswordid
+ pszNewPasswordId = ValueUnion.psz;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszFilenameOrUuid)
+ pszFilenameOrUuid = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_PRINT(c))
+ return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid option -%c", c);
+ else
+ return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid option case %i", c);
+ }
+ else if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_ENCRYPTMEDIUM, "unknown option: %s\n", ValueUnion.psz);
+ else if (ValueUnion.pDef)
+ return errorSyntax(USAGE_ENCRYPTMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ else
+ return errorSyntax(USAGE_ENCRYPTMEDIUM, "error: %Rrs", c);
+ }
+ }
+
+ if (!pszFilenameOrUuid)
+ return errorSyntax(USAGE_ENCRYPTMEDIUM, "Disk name or UUID required");
+
+ if (!pszPasswordNew && !pszPasswordOld)
+ return errorSyntax(USAGE_ENCRYPTMEDIUM, "No password specified");
+
+ if ( (pszPasswordNew && !pszNewPasswordId)
+ || (!pszPasswordNew && pszNewPasswordId))
+ return errorSyntax(USAGE_ENCRYPTMEDIUM, "A new password must always have a valid identifier set at the same time");
+
+ if (pszPasswordNew)
+ {
+ if (!RTStrCmp(pszPasswordNew, "-"))
+ {
+ /* Get password from console. */
+ RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordNew, "Enter new password:");
+ if (rcExit == RTEXITCODE_FAILURE)
+ return rcExit;
+ }
+ else
+ {
+ RTEXITCODE rcExit = readPasswordFile(pszPasswordNew, &strPasswordNew);
+ if (rcExit == RTEXITCODE_FAILURE)
+ {
+ RTMsgError("Failed to read new password from file");
+ return rcExit;
+ }
+ }
+ }
+
+ if (pszPasswordOld)
+ {
+ if (!RTStrCmp(pszPasswordOld, "-"))
+ {
+ /* Get password from console. */
+ RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordOld, "Enter old password:");
+ if (rcExit == RTEXITCODE_FAILURE)
+ return rcExit;
+ }
+ else
+ {
+ RTEXITCODE rcExit = readPasswordFile(pszPasswordOld, &strPasswordOld);
+ if (rcExit == RTEXITCODE_FAILURE)
+ {
+ RTMsgError("Failed to read old password from file");
+ return rcExit;
+ }
+ }
+ }
+
+ /* Always open the medium if necessary, there is no other way. */
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
+ AccessMode_ReadWrite, hardDisk,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+ if (hardDisk.isNull())
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid hard disk reference, avoiding crash");
+
+ ComPtr<IProgress> progress;
+ CHECK_ERROR(hardDisk, ChangeEncryption(Bstr(strPasswordOld).raw(), Bstr(pszCipher).raw(),
+ Bstr(strPasswordNew).raw(), Bstr(pszNewPasswordId).raw(),
+ progress.asOutParam()));
+ if (SUCCEEDED(rc))
+ rc = showProgress(progress);
+ if (FAILED(rc))
+ {
+ if (rc == E_NOTIMPL)
+ RTMsgError("Encrypt hard disk operation is not implemented!");
+ else if (rc == VBOX_E_NOT_SUPPORTED)
+ RTMsgError("Encrypt hard disk operation for this cipher is not implemented yet!");
+ else if (!progress.isNull())
+ CHECK_PROGRESS_ERROR(progress, ("Failed to encrypt hard disk"));
+ else
+ RTMsgError("Failed to encrypt hard disk!");
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+RTEXITCODE handleCheckMediumPassword(HandlerArg *a)
+{
+ HRESULT rc;
+ ComPtr<IMedium> hardDisk;
+ const char *pszFilenameOrUuid = NULL;
+ Utf8Str strPassword;
+
+ if (a->argc != 2)
+ return errorSyntax(USAGE_MEDIUMENCCHKPWD, "Invalid number of arguments: %d", a->argc);
+
+ pszFilenameOrUuid = a->argv[0];
+
+ if (!RTStrCmp(a->argv[1], "-"))
+ {
+ /* Get password from console. */
+ RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter password:");
+ if (rcExit == RTEXITCODE_FAILURE)
+ return rcExit;
+ }
+ else
+ {
+ RTEXITCODE rcExit = readPasswordFile(a->argv[1], &strPassword);
+ if (rcExit == RTEXITCODE_FAILURE)
+ {
+ RTMsgError("Failed to read password from file");
+ return rcExit;
+ }
+ }
+
+ /* Always open the medium if necessary, there is no other way. */
+ rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
+ AccessMode_ReadWrite, hardDisk,
+ false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+ if (hardDisk.isNull())
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid hard disk reference, avoiding crash");
+
+ CHECK_ERROR(hardDisk, CheckEncryptionPassword(Bstr(strPassword).raw()));
+ if (SUCCEEDED(rc))
+ RTPrintf("The given password is correct\n");
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/*********************************************************************************************************************************
+* The mediumio command *
+*********************************************************************************************************************************/
+
+/**
+ * Common MediumIO options.
+ */
+typedef struct MEDIUMIOCOMMONOPT
+{
+ const char *pszFilenameOrUuid;
+ DeviceType_T enmDeviceType;
+ const char *pszPasswordFile;
+} MEDIUMIOCOMMONOPT;
+typedef MEDIUMIOCOMMONOPT *PMEDIUMIOCOMMONOPT;
+typedef MEDIUMIOCOMMONOPT const *PCMEDIUMIOCOMMONOPT;
+
+/* For RTGETOPTDEF array initializer. */
+#define MEDIUMIOCOMMONOPT_DEFS() \
+ { "--disk", 'd', RTGETOPT_REQ_STRING }, \
+ { "--harddisk", 'd', RTGETOPT_REQ_STRING }, \
+ { "disk", 'd', RTGETOPT_REQ_STRING }, \
+ { "harddisk", 'd', RTGETOPT_REQ_STRING }, \
+ { "--dvd", 'D', RTGETOPT_REQ_STRING }, \
+ { "--iso", 'D', RTGETOPT_REQ_STRING }, \
+ { "dvd", 'D', RTGETOPT_REQ_STRING }, \
+ { "iso", 'D', RTGETOPT_REQ_STRING }, \
+ { "--floppy", 'f', RTGETOPT_REQ_STRING }, \
+ { "floppy", 'f', RTGETOPT_REQ_STRING }, \
+ { "--password-file", 'P', RTGETOPT_REQ_STRING }
+
+/* For option switch. */
+#define MEDIUMIOCOMMONOPT_CASES(a_pCommonOpts) \
+ case 'd': \
+ (a_pCommonOpts)->enmDeviceType = DeviceType_HardDisk; \
+ (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
+ break; \
+ case 'D': \
+ (a_pCommonOpts)->enmDeviceType = DeviceType_DVD; \
+ (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
+ break; \
+ case 'f': \
+ (a_pCommonOpts)->enmDeviceType = DeviceType_Floppy; \
+ (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
+ break; \
+ case 'P': \
+ (a_pCommonOpts)->pszPasswordFile = ValueUnion.psz; \
+ break
+
+
+/**
+ * Worker for mediumio operations that returns a IMediumIO for the specified
+ * medium.
+ *
+ * @returns Exit code.
+ * @param pHandler The handler state structure (for IVirtualBox).
+ * @param pCommonOpts Common mediumio options.
+ * @param fWritable Whether to open writable (true) or read only
+ * (false).
+ * @param rPtrMediumIO Where to return the IMediumIO pointer.
+ * @param pcbMedium Where to return the meidum size. Optional.
+ */
+static RTEXITCODE mediumIOOpenMediumForIO(HandlerArg *pHandler, PCMEDIUMIOCOMMONOPT pCommonOpts, bool fWritable,
+ ComPtr<IMediumIO> &rPtrMediumIO, uint64_t *pcbMedium = NULL)
+{
+ /* Clear returns. */
+ if (pcbMedium)
+ *pcbMedium = 0;
+ rPtrMediumIO.setNull();
+
+ /*
+ * Make sure a medium was specified already.
+ */
+ if (pCommonOpts->enmDeviceType == DeviceType_Null)
+ return errorSyntax("No medium specified!");
+
+ /*
+ * Read the password.
+ */
+ Bstr bstrPassword;
+ if (pCommonOpts->pszPasswordFile)
+ {
+ Utf8Str strPassword;
+ RTEXITCODE rcExit;
+ if (pCommonOpts->pszPasswordFile[0] == '-' && pCommonOpts->pszPasswordFile[1] == '\0')
+ rcExit = readPasswordFromConsole(&strPassword, "Enter encryption password:");
+ else
+ rcExit = readPasswordFile(pCommonOpts->pszPasswordFile, &strPassword);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ bstrPassword = strPassword;
+ strPassword.assign(strPassword.length(), '*');
+ }
+
+ /*
+ * Open the medium and then get I/O access to it.
+ */
+ ComPtr<IMedium> ptrMedium;
+ HRESULT hrc = openMedium(pHandler, pCommonOpts->pszFilenameOrUuid, pCommonOpts->enmDeviceType,
+ fWritable ? AccessMode_ReadWrite : AccessMode_ReadOnly,
+ ptrMedium, false /* fForceNewUuidOnOpen */, false /* fSilent */);
+ if (SUCCEEDED(hrc))
+ {
+ CHECK_ERROR2I_STMT(ptrMedium, OpenForIO(fWritable, bstrPassword.raw(), rPtrMediumIO.asOutParam()), hrc = hrcCheck);
+
+ /*
+ * If the size is requested get it after we've opened it.
+ */
+ if (pcbMedium && SUCCEEDED(hrc))
+ {
+ LONG64 cbLogical = 0;
+ CHECK_ERROR2I_STMT(ptrMedium, COMGETTER(LogicalSize)(&cbLogical), hrc = hrcCheck);
+ *pcbMedium = cbLogical;
+ if (!SUCCEEDED(hrc))
+ rPtrMediumIO.setNull();
+ }
+ }
+
+ if (bstrPassword.isNotEmpty())
+ memset(bstrPassword.mutableRaw(), '*', bstrPassword.length() * sizeof(RTUTF16));
+ return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * mediumio formatfat
+ */
+static RTEXITCODE handleMediumIOFormatFat(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
+{
+ /*
+ * Parse the options.
+ */
+ bool fQuick = false;
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ MEDIUMIOCOMMONOPT_DEFS(),
+ { "--quick", 'q', RTGETOPT_REQ_NOTHING },
+ };
+
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
+ AssertRC(rc);
+ RTGETOPTUNION ValueUnion;
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
+
+ case 'q':
+ fQuick = true;
+ break;
+
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+
+ /*
+ * Open the medium for I/O and format it.
+ */
+ ComPtr<IMediumIO> ptrMediumIO;
+ RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, true /*fWritable*/, ptrMediumIO);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ CHECK_ERROR2I_RET(ptrMediumIO, FormatFAT(fQuick), RTEXITCODE_FAILURE);
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * mediumio cat
+ */
+static RTEXITCODE handleMediumIOCat(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
+{
+ /*
+ * Parse the options.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ MEDIUMIOCOMMONOPT_DEFS(),
+ { "--hex", 'H', RTGETOPT_REQ_NOTHING },
+ { "--offset", 'o', RTGETOPT_REQ_UINT64 },
+ { "--output", 'O', RTGETOPT_REQ_STRING },
+ { "--size", 's', RTGETOPT_REQ_UINT64 },
+ };
+ bool fHex = false;
+ uint64_t off = 0;
+ const char *pszOutput = NULL;
+ uint64_t cb = UINT64_MAX;
+
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
+ AssertRC(rc);
+ RTGETOPTUNION ValueUnion;
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
+
+ case 'H':
+ fHex = true;
+ break;
+
+ case 'o':
+ off = ValueUnion.u64;
+ break;
+
+ case 'O':
+ pszOutput = ValueUnion.psz;
+ break;
+
+ case 's':
+ cb = ValueUnion.u64;
+ break;
+
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+
+ /*
+ * Open the medium for I/O.
+ */
+ ComPtr<IMediumIO> ptrMediumIO;
+ uint64_t cbMedium;
+ RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, false /*fWritable*/, ptrMediumIO, &cbMedium);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /*
+ * Do we have an output file or do we write to stdout?
+ */
+ PRTSTREAM pOut = NULL;
+ if (pszOutput && (pszOutput[0] != '-' || pszOutput[1] != '\0'))
+ {
+ int vrc = RTStrmOpen(pszOutput, fHex ? "wt" : "wb", &pOut);
+ if (RT_FAILURE(vrc))
+ rcExit = RTMsgErrorExitFailure("Error opening '%s' for writing: %Rrc", pszOutput, vrc);
+ }
+ else
+ {
+ pOut = g_pStdOut;
+ if (!fHex)
+ RTStrmSetMode(pOut, true, -1);
+ }
+
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /*
+ * Adjust 'cb' now that we've got the medium size.
+ */
+ if (off >= cbMedium)
+ {
+ RTMsgWarning("Specified offset (%#RX64) is beyond the end of the medium (%#RX64)", off, cbMedium);
+ cb = 0;
+ }
+ else if ( cb > cbMedium
+ || cb + off > cbMedium)
+ cb = cbMedium - off;
+
+ /*
+ * Hex dump preps. (The duplication detection is making ASSUMPTIONS about
+ * all the reads being a multiple of cchWidth, except for the final one.)
+ */
+ char abHexBuf[16] = { 0 };
+ size_t cbHexBuf = 0;
+ unsigned const cchWidth = RT_ELEMENTS(abHexBuf);
+ uint64_t const offEndDupCheck = cb - cchWidth;
+ uint64_t cDuplicates = 0;
+
+ /*
+ * Do the reading.
+ */
+ while (cb > 0)
+ {
+ char szLine[32 + cchWidth * 4 + 32];
+
+ /* Do the reading. */
+ uint32_t const cbToRead = (uint32_t)RT_MIN(cb, _128K);
+ SafeArray<BYTE> SafeArrayBuf;
+ HRESULT hrc = ptrMediumIO->Read(off, cbToRead, ComSafeArrayAsOutParam(SafeArrayBuf));
+ if (FAILED(hrc))
+ {
+ RTStrPrintf(szLine, sizeof(szLine), "Read(%zu bytes at %#RX64)", cbToRead, off);
+ com::GlueHandleComError(ptrMediumIO, szLine, hrc, __FILE__, __LINE__);
+ break;
+ }
+
+ /* Output the data. */
+ size_t const cbReturned = SafeArrayBuf.size();
+ if (cbReturned)
+ {
+ BYTE const *pbBuf = SafeArrayBuf.raw();
+ int vrc = VINF_SUCCESS;
+ if (!fHex)
+ vrc = RTStrmWrite(pOut, pbBuf, cbReturned);
+ else
+ {
+ /* hexdump -C */
+ uint64_t offHex = off;
+ uint64_t const offHexEnd = off + cbReturned;
+ while (offHex < offHexEnd)
+ {
+ if ( offHex >= offEndDupCheck
+ || cbHexBuf == 0
+ || memcmp(pbBuf, abHexBuf, cchWidth) != 0
+ || ( cDuplicates == 0
+ && ( offHex + cchWidth >= offEndDupCheck
+ || memcmp(pbBuf + cchWidth, pbBuf, cchWidth) != 0)) )
+ {
+ if (cDuplicates > 0)
+ {
+ RTStrmPrintf(pOut, "********** <ditto x %RU64>\n", cDuplicates);
+ cDuplicates = 0;
+ }
+
+ size_t cch = RTStrPrintf(szLine, sizeof(szLine), "%012RX64:", offHex);
+ unsigned i;
+ for (i = 0; i < cchWidth && offHex + i < offHexEnd; i++)
+ {
+ static const char s_szHexDigits[17] = "0123456789abcdef";
+ szLine[cch++] = (i & 7) || i == 0 ? ' ' : '-';
+ uint8_t const u8 = pbBuf[i];
+ szLine[cch++] = s_szHexDigits[u8 >> 4];
+ szLine[cch++] = s_szHexDigits[u8 & 0xf];
+ }
+ while (i++ < cchWidth)
+ {
+ szLine[cch++] = ' ';
+ szLine[cch++] = ' ';
+ szLine[cch++] = ' ';
+ }
+ szLine[cch++] = ' ';
+
+ for (i = 0; i < cchWidth && offHex + i < offHexEnd; i++)
+ {
+ uint8_t const u8 = pbBuf[i];
+ szLine[cch++] = u8 < 127 && u8 >= 32 ? u8 : '.';
+ }
+ szLine[cch++] = '\n';
+ szLine[cch] = '\0';
+
+ vrc = RTStrmWrite(pOut, szLine, cch);
+ if (RT_FAILURE(vrc))
+ break;
+
+
+ /* copy bytes over to the duplication detection buffer. */
+ cbHexBuf = (size_t)RT_MIN(cchWidth, offHexEnd - offHex);
+ memcpy(abHexBuf, pbBuf, cbHexBuf);
+ }
+ else
+ cDuplicates++;
+
+ /* Advance to next line. */
+ pbBuf += cchWidth;
+ offHex += cchWidth;
+ }
+ }
+ if (RT_FAILURE(vrc))
+ {
+ rcExit = RTMsgErrorExitFailure("Error writing to '%s': %Rrc", pszOutput, vrc);
+ break;
+ }
+ }
+
+ /* Advance. */
+ if (cbReturned != cbToRead)
+ {
+ rcExit = RTMsgErrorExitFailure("Expected read() at offset %RU64 (%#RX64) to return %#zx bytes, only got %#zx!\n",
+ off, off, cbReturned, cbToRead);
+ break;
+ }
+ off += cbReturned;
+ cb -= cbReturned;
+ }
+
+ /*
+ * Close output.
+ */
+ if (pOut != g_pStdOut)
+ {
+ int vrc = RTStrmClose(pOut);
+ if (RT_FAILURE(vrc))
+ rcExit = RTMsgErrorExitFailure("Error closing '%s': %Rrc", pszOutput, vrc);
+ }
+ else if (!fHex)
+ RTStrmSetMode(pOut, false, -1);
+ }
+ }
+ return rcExit;
+}
+
+/**
+ * mediumio stream
+ */
+static RTEXITCODE handleMediumIOStream(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
+{
+ /*
+ * Parse the options.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ MEDIUMIOCOMMONOPT_DEFS(),
+ { "--output", 'O', RTGETOPT_REQ_STRING },
+ { "--format", 'F', RTGETOPT_REQ_STRING },
+ { "--variant", 'v', RTGETOPT_REQ_STRING }
+ };
+ const char *pszOutput = NULL;
+ MediumVariant_T enmMediumVariant = MediumVariant_Standard;
+ Bstr strFormat;
+
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
+ AssertRC(rc);
+ RTGETOPTUNION ValueUnion;
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
+
+ case 'O':
+ pszOutput = ValueUnion.psz;
+ break;
+ case 'F':
+ strFormat = ValueUnion.psz;
+ break;
+ case 'v': // --variant
+ {
+ int vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
+ if (RT_FAILURE(vrc))
+ return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
+ break;
+ }
+
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+
+ /*
+ * Open the medium for I/O.
+ */
+ ComPtr<IMediumIO> ptrMediumIO;
+ uint64_t cbMedium;
+ RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, false /*fWritable*/, ptrMediumIO, &cbMedium);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /*
+ * Do we have an output file or do we write to stdout?
+ */
+ PRTSTREAM pOut = NULL;
+ if (pszOutput && (pszOutput[0] != '-' || pszOutput[1] != '\0'))
+ {
+ int vrc = RTStrmOpen(pszOutput, "wb", &pOut);
+ if (RT_FAILURE(vrc))
+ rcExit = RTMsgErrorExitFailure("Error opening '%s' for writing: %Rrc", pszOutput, vrc);
+ }
+ else
+ {
+ pOut = g_pStdOut;
+ RTStrmSetMode(pOut, true, -1);
+ }
+
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ ComPtr<IDataStream> ptrDataStream;
+ ComPtr<IProgress> ptrProgress;
+
+ com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
+
+ for (ULONG i = 0; i < l_variants.size(); ++i)
+ {
+ ULONG temp = enmMediumVariant;
+ temp &= 1<<i;
+ l_variants [i] = (MediumVariant_T)temp;
+ }
+
+ HRESULT hrc = ptrMediumIO->ConvertToStream(strFormat.raw(), ComSafeArrayAsInParam(l_variants), 10 * _1M, ptrDataStream.asOutParam(), ptrProgress.asOutParam());
+ if (hrc == S_OK)
+ {
+ /* Read until we reached the end of the stream. */
+ for (;;)
+ {
+ SafeArray<BYTE> SafeArrayBuf;
+
+ hrc = ptrDataStream->Read(_64K, 0 /*Infinite wait*/, ComSafeArrayAsOutParam(SafeArrayBuf));
+ if ( FAILED(hrc)
+ || SafeArrayBuf.size() == 0)
+ break;
+
+ /* Output the data. */
+ size_t const cbReturned = SafeArrayBuf.size();
+ if (cbReturned)
+ {
+ BYTE const *pbBuf = SafeArrayBuf.raw();
+ int vrc = VINF_SUCCESS;
+ vrc = RTStrmWrite(pOut, pbBuf, cbReturned);
+ if (RT_FAILURE(vrc))
+ {
+ rcExit = RTMsgErrorExitFailure("Error writing to '%s': %Rrc", pszOutput, vrc);
+ break;
+ }
+ }
+
+ /** @todo Check progress. */
+ }
+ }
+ else
+ {
+ com::GlueHandleComError(ptrMediumIO, "ConvertToStream()", hrc, __FILE__, __LINE__);
+ rcExit = RTEXITCODE_FAILURE;
+ }
+
+ /*
+ * Close output.
+ */
+ if (pOut != g_pStdOut)
+ {
+ int vrc = RTStrmClose(pOut);
+ if (RT_FAILURE(vrc))
+ rcExit = RTMsgErrorExitFailure("Error closing '%s': %Rrc", pszOutput, vrc);
+ }
+ else
+ RTStrmSetMode(pOut, false, -1);
+ }
+ }
+ return rcExit;
+}
+
+
+RTEXITCODE handleMediumIO(HandlerArg *a)
+{
+ /*
+ * Parse image-option and sub-command.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ MEDIUMIOCOMMONOPT_DEFS(),
+ /* sub-commands */
+ { "formatfat", 1000, RTGETOPT_REQ_NOTHING },
+ { "cat", 1001, RTGETOPT_REQ_NOTHING },
+ { "stream", 1002, RTGETOPT_REQ_NOTHING },
+ };
+ MEDIUMIOCOMMONOPT CommonOpts = { NULL, DeviceType_Null, NULL };
+
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
+ AssertRC(rc);
+ RTGETOPTUNION ValueUnion;
+ while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (rc)
+ {
+ MEDIUMIOCOMMONOPT_CASES(&CommonOpts);
+
+ /* Sub-commands: */
+ case 1000:
+ setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_FORMATFAT);
+ return handleMediumIOFormatFat(a, GetState.iNext, &CommonOpts);
+ case 1001:
+ setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_CAT);
+ return handleMediumIOCat(a, GetState.iNext, &CommonOpts);
+ case 1002:
+ setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_STREAM);
+ return handleMediumIOStream(a, GetState.iNext, &CommonOpts);
+
+ case VINF_GETOPT_NOT_OPTION:
+ return errorUnknownSubcommand(ValueUnion.psz);
+
+ default:
+ return errorGetOpt(rc, &ValueUnion);
+ }
+ }
+ return errorNoSubcommand();
+}
+
+#endif /* !VBOX_ONLY_DOCS */
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp
new file mode 100644
index 00000000..8d0b3372
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp
@@ -0,0 +1,3435 @@
+/* $Id: VBoxManageGuestCtrl.cpp $ */
+/** @file
+ * VBoxManage - Implementation of guestcontrol command.
+ */
+
+/*
+ * Copyright (C) 2010-2019 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/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/err.h>
+#include <VBox/log.h>
+
+#include <iprt/asm.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/list.h>
+#include <iprt/path.h>
+#include <iprt/process.h> /* For RTProcSelf(). */
+#include <iprt/thread.h>
+#include <iprt/vfs.h>
+
+#include <map>
+#include <vector>
+
+#ifdef USE_XPCOM_QUEUE
+# include <sys/select.h>
+# include <errno.h>
+#endif
+
+#include <signal.h>
+
+#ifdef RT_OS_DARWIN
+# include <CoreFoundation/CFRunLoop.h>
+#endif
+
+using namespace com;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define GCTLCMD_COMMON_OPT_USER 999 /**< The --username option number. */
+#define GCTLCMD_COMMON_OPT_PASSWORD 998 /**< The --password option number. */
+#define GCTLCMD_COMMON_OPT_PASSWORD_FILE 997 /**< The --password-file option number. */
+#define GCTLCMD_COMMON_OPT_DOMAIN 996 /**< The --domain option number. */
+/** Common option definitions. */
+#define GCTLCMD_COMMON_OPTION_DEFS() \
+ { "--username", GCTLCMD_COMMON_OPT_USER, RTGETOPT_REQ_STRING }, \
+ { "--passwordfile", GCTLCMD_COMMON_OPT_PASSWORD_FILE, RTGETOPT_REQ_STRING }, \
+ { "--password", GCTLCMD_COMMON_OPT_PASSWORD, RTGETOPT_REQ_STRING }, \
+ { "--domain", GCTLCMD_COMMON_OPT_DOMAIN, RTGETOPT_REQ_STRING }, \
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING }, \
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+
+/** Handles common options in the typical option parsing switch. */
+#define GCTLCMD_COMMON_OPTION_CASES(a_pCtx, a_ch, a_pValueUnion) \
+ case 'v': \
+ case 'q': \
+ case GCTLCMD_COMMON_OPT_USER: \
+ case GCTLCMD_COMMON_OPT_DOMAIN: \
+ case GCTLCMD_COMMON_OPT_PASSWORD: \
+ case GCTLCMD_COMMON_OPT_PASSWORD_FILE: \
+ { \
+ RTEXITCODE rcExitCommon = gctlCtxSetOption(a_pCtx, a_ch, a_pValueUnion); \
+ if (RT_UNLIKELY(rcExitCommon != RTEXITCODE_SUCCESS)) \
+ return rcExitCommon; \
+ } break
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Set by the signal handler when current guest control
+ * action shall be aborted. */
+static volatile bool g_fGuestCtrlCanceled = false;
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Listener declarations.
+ */
+VBOX_LISTENER_DECLARE(GuestFileEventListenerImpl)
+VBOX_LISTENER_DECLARE(GuestProcessEventListenerImpl)
+VBOX_LISTENER_DECLARE(GuestSessionEventListenerImpl)
+VBOX_LISTENER_DECLARE(GuestEventListenerImpl)
+
+
+/**
+ * Definition of a guestcontrol command, with handler and various flags.
+ */
+typedef struct GCTLCMDDEF
+{
+ /** The command name. */
+ const char *pszName;
+
+ /**
+ * Actual command handler callback.
+ *
+ * @param pCtx Pointer to command context to use.
+ */
+ DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (struct GCTLCMDCTX *pCtx, int argc, char **argv));
+
+ /** The command usage flags. */
+ uint32_t fCmdUsage;
+ /** Command context flags (GCTLCMDCTX_F_XXX). */
+ uint32_t fCmdCtx;
+} GCTLCMD;
+/** Pointer to a const guest control command definition. */
+typedef GCTLCMDDEF const *PCGCTLCMDDEF;
+
+/** @name GCTLCMDCTX_F_XXX - Command context flags.
+ * @{
+ */
+/** No flags set. */
+#define GCTLCMDCTX_F_NONE 0
+/** Don't install a signal handler (CTRL+C trap). */
+#define GCTLCMDCTX_F_NO_SIGNAL_HANDLER RT_BIT(0)
+/** No guest session needed. */
+#define GCTLCMDCTX_F_SESSION_ANONYMOUS RT_BIT(1)
+/** @} */
+
+/**
+ * Context for handling a specific command.
+ */
+typedef struct GCTLCMDCTX
+{
+ HandlerArg *pArg;
+
+ /** Pointer to the command definition. */
+ PCGCTLCMDDEF pCmdDef;
+ /** The VM name or UUID. */
+ const char *pszVmNameOrUuid;
+
+ /** Whether we've done the post option parsing init already. */
+ bool fPostOptionParsingInited;
+ /** Whether we've locked the VM session. */
+ bool fLockedVmSession;
+ /** Whether to detach (@c true) or close the session. */
+ bool fDetachGuestSession;
+ /** Set if we've installed the signal handler. */
+ bool fInstalledSignalHandler;
+ /** The verbosity level. */
+ uint32_t cVerbose;
+ /** 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;
+
+
+/**
+ * An entry for an element which needs to be copied/created to/on the guest.
+ */
+typedef struct DESTFILEENTRY
+{
+ DESTFILEENTRY(Utf8Str strFilename) : mFilename(strFilename) {}
+ Utf8Str mFilename;
+} DESTFILEENTRY, *PDESTFILEENTRY;
+/*
+ * Map for holding destination entries, whereas the key is the destination
+ * directory and the mapped value is a vector holding all elements for this directory.
+ */
+typedef std::map< Utf8Str, std::vector<DESTFILEENTRY> > DESTDIRMAP, *PDESTDIRMAP;
+typedef std::map< Utf8Str, std::vector<DESTFILEENTRY> >::iterator DESTDIRMAPITER, *PDESTDIRMAPITER;
+
+
+/**
+ * RTGetOpt-IDs for the guest execution control command line.
+ */
+enum GETOPTDEF_EXEC
+{
+ GETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES = 1000,
+ GETOPTDEF_EXEC_NO_PROFILE,
+ GETOPTDEF_EXEC_OUTPUTFORMAT,
+ GETOPTDEF_EXEC_DOS2UNIX,
+ GETOPTDEF_EXEC_UNIX2DOS,
+ GETOPTDEF_EXEC_WAITFOREXIT,
+ GETOPTDEF_EXEC_WAITFORSTDOUT,
+ GETOPTDEF_EXEC_WAITFORSTDERR
+};
+
+enum kStreamTransform
+{
+ kStreamTransform_None = 0,
+ kStreamTransform_Dos2Unix,
+ kStreamTransform_Unix2Dos
+};
+#endif /* VBOX_ONLY_DOCS */
+
+
+void usageGuestControl(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2, uint32_t uSubCmd)
+{
+ const uint32_t fAnonSubCmds = USAGE_GSTCTRL_CLOSESESSION
+ | USAGE_GSTCTRL_LIST
+ | USAGE_GSTCTRL_CLOSEPROCESS
+ | USAGE_GSTCTRL_CLOSESESSION
+ | USAGE_GSTCTRL_UPDATEGA
+ | USAGE_GSTCTRL_WATCH;
+
+ /* 0 1 2 3 4 5 6 7 8XXXXXXXXXX */
+ /* 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */
+ if (~fAnonSubCmds & uSubCmd)
+ RTStrmPrintf(pStrm,
+ "%s guestcontrol %s <uuid|vmname> [--verbose|-v] [--quiet|-q]\n"
+ " [--username <name>] [--domain <domain>]\n"
+ " [--passwordfile <file> | --password <password>]\n%s",
+ pcszSep1, pcszSep2, uSubCmd == ~0U ? "\n" : "");
+ if (uSubCmd & USAGE_GSTCTRL_RUN)
+ RTStrmPrintf(pStrm,
+ " run [common-options]\n"
+ " [--exe <path to executable>] [--timeout <msec>]\n"
+ " [-E|--putenv <NAME>[=<VALUE>]] [--unquoted-args]\n"
+ " [--ignore-operhaned-processes] [--profile]\n"
+ " [--no-wait-stdout|--wait-stdout]\n"
+ " [--no-wait-stderr|--wait-stderr]\n"
+ " [--dos2unix] [--unix2dos]\n"
+ " -- <program/arg0> [argument1] ... [argumentN]]\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_START)
+ RTStrmPrintf(pStrm,
+ " start [common-options]\n"
+ " [--exe <path to executable>] [--timeout <msec>]\n"
+ " [-E|--putenv <NAME>[=<VALUE>]] [--unquoted-args]\n"
+ " [--ignore-operhaned-processes] [--profile]\n"
+ " -- <program/arg0> [argument1] ... [argumentN]]\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_COPYFROM)
+ RTStrmPrintf(pStrm,
+ " copyfrom [common-options]\n"
+ " [--follow] [-R|--recursive]\n"
+ " <guest-src0> [guest-src1 [...]] <host-dst>\n"
+ "\n"
+ " copyfrom [common-options]\n"
+ " [--follow] [-R|--recursive]\n"
+ " [--target-directory <host-dst-dir>]\n"
+ " <guest-src0> [guest-src1 [...]]\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_COPYTO)
+ RTStrmPrintf(pStrm,
+ " copyto [common-options]\n"
+ " [--follow] [-R|--recursive]\n"
+ " <host-src0> [host-src1 [...]] <guest-dst>\n"
+ "\n"
+ " copyto [common-options]\n"
+ " [--follow] [-R|--recursive]\n"
+ " [--target-directory <guest-dst>]\n"
+ " <host-src0> [host-src1 [...]]\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_MKDIR)
+ RTStrmPrintf(pStrm,
+ " mkdir|createdir[ectory] [common-options]\n"
+ " [--parents] [--mode <mode>]\n"
+ " <guest directory> [...]\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_RMDIR)
+ RTStrmPrintf(pStrm,
+ " rmdir|removedir[ectory] [common-options]\n"
+ " [-R|--recursive]\n"
+ " <guest directory> [...]\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_RM)
+ RTStrmPrintf(pStrm,
+ " removefile|rm [common-options] [-f|--force]\n"
+ " <guest file> [...]\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_MV)
+ RTStrmPrintf(pStrm,
+ " mv|move|ren[ame] [common-options]\n"
+ " <source> [source1 [...]] <dest>\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_MKTEMP)
+ RTStrmPrintf(pStrm,
+ " mktemp|createtemp[orary] [common-options]\n"
+ " [--secure] [--mode <mode>] [--tmpdir <directory>]\n"
+ " <template>\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_STAT)
+ RTStrmPrintf(pStrm,
+ " stat [common-options]\n"
+ " <file> [...]\n"
+ "\n");
+
+ /*
+ * Command not requiring authentication.
+ */
+ if (fAnonSubCmds & uSubCmd)
+ {
+ /* 0 1 2 3 4 5 6 7 8XXXXXXXXXX */
+ /* 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */
+ RTStrmPrintf(pStrm,
+ "%s guestcontrol %s <uuid|vmname> [--verbose|-v] [--quiet|-q]\n%s",
+ pcszSep1, pcszSep2, uSubCmd == ~0U ? "\n" : "");
+ if (uSubCmd & USAGE_GSTCTRL_LIST)
+ RTStrmPrintf(pStrm,
+ " list <all|sessions|processes|files> [common-opts]\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_CLOSEPROCESS)
+ RTStrmPrintf(pStrm,
+ " closeprocess [common-options]\n"
+ " < --session-id <ID>\n"
+ " | --session-name <name or pattern>\n"
+ " <PID1> [PID1 [...]]\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_CLOSESESSION)
+ RTStrmPrintf(pStrm,
+ " closesession [common-options]\n"
+ " < --all | --session-id <ID>\n"
+ " | --session-name <name or pattern> >\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_UPDATEGA)
+ RTStrmPrintf(pStrm,
+ " updatega|updateguestadditions|updateadditions\n"
+ " [--source <guest additions .ISO>]\n"
+ " [--wait-start] [common-options]\n"
+ " [-- [<argument1>] ... [<argumentN>]]\n"
+ "\n");
+ if (uSubCmd & USAGE_GSTCTRL_WATCH)
+ RTStrmPrintf(pStrm,
+ " watch [common-options]\n"
+ "\n");
+ }
+}
+
+#ifndef VBOX_ONLY_DOCS
+
+
+#ifdef RT_OS_WINDOWS
+static BOOL WINAPI gctlSignalHandler(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. Don't do anything
+ * unnecessary here.
+ */
+static void gctlSignalHandler(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 int gctlSignalHandlerInstall(void)
+{
+ g_fGuestCtrlCanceled = false;
+
+ int rc = VINF_SUCCESS;
+#ifdef RT_OS_WINDOWS
+ if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)gctlSignalHandler, TRUE /* Add handler */))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTMsgError("Unable to install console control handler, rc=%Rrc\n", rc);
+ }
+#else
+ signal(SIGINT, gctlSignalHandler);
+ signal(SIGTERM, gctlSignalHandler);
+# ifdef SIGBREAK
+ signal(SIGBREAK, gctlSignalHandler);
+# endif
+#endif
+ return rc;
+}
+
+
+/**
+ * Uninstalls a previously installed signal handler.
+ */
+static int gctlSignalHandlerUninstall(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);
+ signal(SIGTERM, SIG_DFL);
+# ifdef SIGBREAK
+ signal(SIGBREAK, SIG_DFL);
+# endif
+#endif
+ return rc;
+}
+
+
+/**
+ * Translates a process status to a human readable string.
+ */
+const char *gctlProcessStatusToText(ProcessStatus_T enmStatus)
+{
+ switch (enmStatus)
+ {
+ case ProcessStatus_Starting:
+ return "starting";
+ case ProcessStatus_Started:
+ return "started";
+ case ProcessStatus_Paused:
+ return "paused";
+ case ProcessStatus_Terminating:
+ return "terminating";
+ case ProcessStatus_TerminatedNormally:
+ return "successfully terminated";
+ case ProcessStatus_TerminatedSignal:
+ return "terminated by signal";
+ case ProcessStatus_TerminatedAbnormally:
+ return "abnormally aborted";
+ case ProcessStatus_TimedOutKilled:
+ return "timed out";
+ case ProcessStatus_TimedOutAbnormally:
+ return "timed out, hanging";
+ case ProcessStatus_Down:
+ return "killed";
+ case ProcessStatus_Error:
+ return "error";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+/**
+ * Translates a guest process wait result to a human readable string.
+ */
+const char *gctlProcessWaitResultToText(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 *gctlGuestSessionStatusToText(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 *gctlFileStatusToText(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";
+}
+
+/**
+ * Translates a file system objec type to a string.
+ */
+const char *gctlFsObjTypeToName(FsObjType_T enmType)
+{
+ switch (enmType)
+ {
+ case FsObjType_Unknown: return "unknown";
+ case FsObjType_Fifo: return "fifo";
+ case FsObjType_DevChar: return "char-device";
+ case FsObjType_Directory: return "directory";
+ case FsObjType_DevBlock: return "block-device";
+ case FsObjType_File: return "file";
+ case FsObjType_Symlink: return "symlink";
+ case FsObjType_Socket: return "socket";
+ case FsObjType_WhiteOut: return "white-out";
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+ case FsObjType_32BitHack: break;
+#endif
+ }
+ return "unknown";
+}
+
+static int gctlPrintError(com::ErrorInfo &errorInfo)
+{
+ if ( errorInfo.isFullAvailable()
+ || errorInfo.isBasicAvailable())
+ {
+ /* If we got a VBOX_E_IPRT error we handle the error in a more gentle way
+ * because it contains more accurate info about what went wrong. */
+ if (errorInfo.getResultCode() == VBOX_E_IPRT_ERROR)
+ RTMsgError("%ls.", errorInfo.getText().raw());
+ else
+ {
+ RTMsgError("Error details:");
+ GluePrintErrorInfo(errorInfo);
+ }
+ return VERR_GENERAL_FAILURE; /** @todo */
+ }
+ AssertMsgFailedReturn(("Object has indicated no error (%Rhrc)!?\n", errorInfo.getResultCode()),
+ VERR_INVALID_PARAMETER);
+}
+
+static int gctlPrintError(IUnknown *pObj, const GUID &aIID)
+{
+ com::ErrorInfo ErrInfo(pObj, aIID);
+ return gctlPrintError(ErrInfo);
+}
+
+static int gctlPrintProgressError(ComPtr<IProgress> pProgress)
+{
+ int vrc = VINF_SUCCESS;
+ HRESULT rc;
+
+ do
+ {
+ BOOL fCanceled;
+ CHECK_ERROR_BREAK(pProgress, COMGETTER(Canceled)(&fCanceled));
+ if (!fCanceled)
+ {
+ LONG rcProc;
+ CHECK_ERROR_BREAK(pProgress, COMGETTER(ResultCode)(&rcProc));
+ if (FAILED(rcProc))
+ {
+ com::ProgressErrorInfo ErrInfo(pProgress);
+ vrc = gctlPrintError(ErrInfo);
+ }
+ }
+
+ } while(0);
+
+ AssertMsgStmt(SUCCEEDED(rc), ("Could not lookup progress information\n"), vrc = VERR_COM_UNEXPECTED);
+
+ return vrc;
+}
+
+
+
+/*
+ *
+ *
+ * Guest Control Command Context
+ * Guest Control Command Context
+ * Guest Control Command Context
+ * Guest Control Command Context
+ *
+ *
+ *
+ */
+
+
+/**
+ * Initializes a guest control command context structure.
+ *
+ * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE on failure (after
+ * informing the user of course).
+ * @param pCtx The command context to init.
+ * @param pArg The handle argument package.
+ */
+static RTEXITCODE gctrCmdCtxInit(PGCTLCMDCTX pCtx, HandlerArg *pArg)
+{
+ RT_ZERO(*pCtx);
+ pCtx->pArg = pArg;
+
+ /*
+ * The user name defaults to the host one, if we can get at it.
+ */
+ char szUser[1024];
+ int rc = RTProcQueryUsername(RTProcSelf(), szUser, sizeof(szUser), NULL);
+ if ( RT_SUCCESS(rc)
+ && RTStrIsValidEncoding(szUser)) /* paranoia required on posix */
+ {
+ try
+ {
+ pCtx->strUsername = szUser;
+ }
+ catch (std::bad_alloc &)
+ {
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory");
+ }
+ }
+ /* else: ignore this failure. */
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Worker for GCTLCMD_COMMON_OPTION_CASES.
+ *
+ * @returns RTEXITCODE_SUCCESS if the option was handled successfully. If not,
+ * an error message is printed and an appropriate failure exit code is
+ * returned.
+ * @param pCtx The guest control command context.
+ * @param ch The option char or ordinal.
+ * @param pValueUnion The option value union.
+ */
+static RTEXITCODE gctlCtxSetOption(PGCTLCMDCTX pCtx, int ch, PRTGETOPTUNION pValueUnion)
+{
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ switch (ch)
+ {
+ case GCTLCMD_COMMON_OPT_USER: /* User name */
+ if (!pCtx->pCmdDef || !(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
+ pCtx->strUsername = pValueUnion->psz;
+ else
+ RTMsgWarning("The --username|-u option is ignored by '%s'", pCtx->pCmdDef->pszName);
+ break;
+
+ case GCTLCMD_COMMON_OPT_PASSWORD: /* Password */
+ if (!pCtx->pCmdDef || !(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
+ {
+ if (pCtx->strPassword.isNotEmpty())
+ RTMsgWarning("Password is given more than once.");
+ pCtx->strPassword = pValueUnion->psz;
+ }
+ else
+ RTMsgWarning("The --password option is ignored by '%s'", pCtx->pCmdDef->pszName);
+ break;
+
+ case GCTLCMD_COMMON_OPT_PASSWORD_FILE: /* Password file */
+ if (!pCtx->pCmdDef || !(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
+ rcExit = readPasswordFile(pValueUnion->psz, &pCtx->strPassword);
+ else
+ RTMsgWarning("The --password-file|-p option is ignored by '%s'", pCtx->pCmdDef->pszName);
+ break;
+
+ case GCTLCMD_COMMON_OPT_DOMAIN: /* domain */
+ if (!pCtx->pCmdDef || !(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
+ pCtx->strDomain = pValueUnion->psz;
+ else
+ RTMsgWarning("The --domain option is ignored by '%s'", pCtx->pCmdDef->pszName);
+ break;
+
+ case 'v': /* --verbose */
+ pCtx->cVerbose++;
+ break;
+
+ case 'q': /* --quiet */
+ if (pCtx->cVerbose)
+ pCtx->cVerbose--;
+ break;
+
+ default:
+ AssertFatalMsgFailed(("ch=%d (%c)\n", ch, ch));
+ }
+ return rcExit;
+}
+
+
+/**
+ * Initializes the VM for IGuest operation.
+ *
+ * This opens a shared session to a running VM and gets hold of IGuest.
+ *
+ * @returns RTEXITCODE_SUCCESS on success. RTEXITCODE_FAILURE and user message
+ * on failure.
+ * @param pCtx The guest control command context.
+ * GCTLCMDCTX::pGuest will be set on success.
+ */
+static RTEXITCODE gctlCtxInitVmSession(PGCTLCMDCTX pCtx)
+{
+ HRESULT rc;
+ AssertPtr(pCtx);
+ AssertPtr(pCtx->pArg);
+
+ /*
+ * Find the VM and check if it's running.
+ */
+ ComPtr<IMachine> machine;
+ CHECK_ERROR(pCtx->pArg->virtualBox, FindMachine(Bstr(pCtx->pszVmNameOrUuid).raw(), machine.asOutParam()));
+ if (SUCCEEDED(rc))
+ {
+ MachineState_T enmMachineState;
+ CHECK_ERROR(machine, COMGETTER(State)(&enmMachineState));
+ if ( SUCCEEDED(rc)
+ && enmMachineState == MachineState_Running)
+ {
+ /*
+ * It's running. So, open a session to it and get the IGuest interface.
+ */
+ CHECK_ERROR(machine, LockMachine(pCtx->pArg->session, LockType_Shared));
+ if (SUCCEEDED(rc))
+ {
+ pCtx->fLockedVmSession = true;
+ ComPtr<IConsole> ptrConsole;
+ CHECK_ERROR(pCtx->pArg->session, COMGETTER(Console)(ptrConsole.asOutParam()));
+ if (SUCCEEDED(rc))
+ {
+ if (ptrConsole.isNotNull())
+ {
+ CHECK_ERROR(ptrConsole, COMGETTER(Guest)(pCtx->pGuest.asOutParam()));
+ if (SUCCEEDED(rc))
+ return RTEXITCODE_SUCCESS;
+ }
+ else
+ RTMsgError("Failed to get a IConsole pointer for the machine. Is it still running?\n");
+ }
+ }
+ }
+ else if (SUCCEEDED(rc))
+ RTMsgError("Machine \"%s\" is not running (currently %s)!\n",
+ pCtx->pszVmNameOrUuid, machineStateToName(enmMachineState, false));
+ }
+ return RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Creates a guest session with the VM.
+ *
+ * @retval RTEXITCODE_SUCCESS on success.
+ * @retval RTEXITCODE_FAILURE and user message on failure.
+ * @param pCtx The guest control command context.
+ * GCTCMDCTX::pGuestSession and GCTLCMDCTX::uSessionID
+ * will be set.
+ */
+static RTEXITCODE gctlCtxInitGuestSession(PGCTLCMDCTX pCtx)
+{
+ HRESULT rc;
+ AssertPtr(pCtx);
+ Assert(!(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS));
+ Assert(pCtx->pGuest.isNotNull());
+
+ /*
+ * Build up a reasonable guest session name. Useful for identifying
+ * a specific session when listing / searching for them.
+ */
+ char *pszSessionName;
+ if (RTStrAPrintf(&pszSessionName,
+ "[%RU32] VBoxManage Guest Control [%s] - %s",
+ RTProcSelf(), pCtx->pszVmNameOrUuid, pCtx->pCmdDef->pszName) < 0)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No enough memory for session name");
+
+ /*
+ * Create a guest session.
+ */
+ if (pCtx->cVerbose)
+ RTPrintf("Creating guest session as user '%s'...\n", pCtx->strUsername.c_str());
+ try
+ {
+ CHECK_ERROR(pCtx->pGuest, CreateSession(Bstr(pCtx->strUsername).raw(),
+ Bstr(pCtx->strPassword).raw(),
+ Bstr(pCtx->strDomain).raw(),
+ Bstr(pszSessionName).raw(),
+ pCtx->pGuestSession.asOutParam()));
+ }
+ catch (std::bad_alloc &)
+ {
+ RTMsgError("Out of memory setting up IGuest::CreateSession call");
+ rc = E_OUTOFMEMORY;
+ }
+ if (SUCCEEDED(rc))
+ {
+ /*
+ * Wait for guest session to start.
+ */
+ if (pCtx->cVerbose)
+ RTPrintf("Waiting for guest session to start...\n");
+ GuestSessionWaitResult_T enmWaitResult = GuestSessionWaitResult_None; /* Shut up MSC */
+ try
+ {
+ com::SafeArray<GuestSessionWaitForFlag_T> aSessionWaitFlags;
+ aSessionWaitFlags.push_back(GuestSessionWaitForFlag_Start);
+ CHECK_ERROR(pCtx->pGuestSession, WaitForArray(ComSafeArrayAsInParam(aSessionWaitFlags),
+ /** @todo Make session handling timeouts configurable. */
+ 30 * 1000, &enmWaitResult));
+ }
+ catch (std::bad_alloc &)
+ {
+ RTMsgError("Out of memory setting up IGuestSession::WaitForArray call");
+ rc = E_OUTOFMEMORY;
+ }
+ if (SUCCEEDED(rc))
+ {
+ /* The WaitFlagNotSupported result may happen with GAs older than 4.3. */
+ if ( enmWaitResult == GuestSessionWaitResult_Start
+ || enmWaitResult == GuestSessionWaitResult_WaitFlagNotSupported)
+ {
+ /*
+ * Get the session ID and we're ready to rumble.
+ */
+ CHECK_ERROR(pCtx->pGuestSession, COMGETTER(Id)(&pCtx->uSessionID));
+ if (SUCCEEDED(rc))
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Successfully started guest session (ID %RU32)\n", pCtx->uSessionID);
+ RTStrFree(pszSessionName);
+ return RTEXITCODE_SUCCESS;
+ }
+ }
+ else
+ {
+ GuestSessionStatus_T enmSessionStatus;
+ CHECK_ERROR(pCtx->pGuestSession, COMGETTER(Status)(&enmSessionStatus));
+ RTMsgError("Error starting guest session (current status is: %s)\n",
+ SUCCEEDED(rc) ? gctlGuestSessionStatusToText(enmSessionStatus) : "<unknown>");
+ }
+ }
+ }
+
+ RTStrFree(pszSessionName);
+ return RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Completes the guest control context initialization after parsing arguments.
+ *
+ * Will validate common arguments, open a VM session, and if requested open a
+ * guest session and install the CTRL-C signal handler.
+ *
+ * It is good to validate all the options and arguments you can before making
+ * this call. However, the VM session, IGuest and IGuestSession interfaces are
+ * not availabe till after this call, so take care.
+ *
+ * @retval RTEXITCODE_SUCCESS on success.
+ * @retval RTEXITCODE_FAILURE and user message on failure.
+ * @param pCtx The guest control command context.
+ * GCTCMDCTX::pGuestSession and GCTLCMDCTX::uSessionID
+ * will be set.
+ * @remarks Can safely be called multiple times, will only do work once.
+ */
+static RTEXITCODE gctlCtxPostOptionParsingInit(PGCTLCMDCTX pCtx)
+{
+ if (pCtx->fPostOptionParsingInited)
+ return RTEXITCODE_SUCCESS;
+
+ /*
+ * Check that the user name isn't empty when we need it.
+ */
+ RTEXITCODE rcExit;
+ if ( (pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS)
+ || pCtx->strUsername.isNotEmpty())
+ {
+ /*
+ * Open the VM session and if required, a guest session.
+ */
+ rcExit = gctlCtxInitVmSession(pCtx);
+ if ( rcExit == RTEXITCODE_SUCCESS
+ && !(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
+ rcExit = gctlCtxInitGuestSession(pCtx);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /*
+ * Install signal handler if requested (errors are ignored).
+ */
+ if (!(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_NO_SIGNAL_HANDLER))
+ {
+ int rc = gctlSignalHandlerInstall();
+ pCtx->fInstalledSignalHandler = RT_SUCCESS(rc);
+ }
+ }
+ }
+ else
+ rcExit = errorSyntaxEx(USAGE_GUESTCONTROL, pCtx->pCmdDef->fCmdUsage, "No user name specified!");
+
+ pCtx->fPostOptionParsingInited = rcExit == RTEXITCODE_SUCCESS;
+ return rcExit;
+}
+
+
+/**
+ * Cleans up the context when the command returns.
+ *
+ * This will close any open guest session, unless the DETACH flag is set.
+ * It will also close any VM session that may be been established. Any signal
+ * handlers we've installed will also be removed.
+ *
+ * Un-initializes the VM after guest control usage.
+ * @param pCmdCtx Pointer to command context.
+ */
+static void gctlCtxTerm(PGCTLCMDCTX pCtx)
+{
+ HRESULT rc;
+ AssertPtr(pCtx);
+
+ /*
+ * Uninstall signal handler.
+ */
+ if (pCtx->fInstalledSignalHandler)
+ {
+ gctlSignalHandlerUninstall();
+ pCtx->fInstalledSignalHandler = false;
+ }
+
+ /*
+ * Close, or at least release, the guest session.
+ */
+ if (pCtx->pGuestSession.isNotNull())
+ {
+ if ( !(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS)
+ && !pCtx->fDetachGuestSession)
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Closing guest session ...\n");
+
+ CHECK_ERROR(pCtx->pGuestSession, Close());
+ }
+ else if ( pCtx->fDetachGuestSession
+ && pCtx->cVerbose)
+ RTPrintf("Guest session detached\n");
+
+ pCtx->pGuestSession.setNull();
+ }
+
+ /*
+ * Close the VM session.
+ */
+ if (pCtx->fLockedVmSession)
+ {
+ Assert(pCtx->pArg->session.isNotNull());
+ CHECK_ERROR(pCtx->pArg->session, UnlockMachine());
+ pCtx->fLockedVmSession = false;
+ }
+}
+
+
+
+
+
+/*
+ *
+ *
+ * Guest Control Command Handling.
+ * Guest Control Command Handling.
+ * Guest Control Command Handling.
+ * Guest Control Command Handling.
+ * Guest Control Command Handling.
+ *
+ *
+ */
+
+
+/** @name EXITCODEEXEC_XXX - Special run exit codes.
+ *
+ * Special exit codes for returning errors/information of a started guest
+ * process to the command line VBoxManage was started from. Useful for e.g.
+ * scripting.
+ *
+ * ASSUMING that all platforms have at least 7-bits for the exit code we can do
+ * the following mapping:
+ * - Guest exit code 0 is mapped to 0 on the host.
+ * - Guest exit codes 1 thru 93 (0x5d) are displaced by 32, so that 1
+ * becomes 33 (0x21) on the host and 93 becomes 125 (0x7d) on the host.
+ * - Guest exit codes 94 (0x5e) and above are mapped to 126 (0x5e).
+ *
+ * We ASSUME that all VBoxManage status codes are in the range 0 thru 32.
+ *
+ * @note These are frozen as of 4.1.0.
+ * @note The guest exit code mappings was introduced with 5.0 and the 'run'
+ * command, they are/was not supported by 'exec'.
+ * @sa gctlRunCalculateExitCode
+ */
+/** Process exited normally but with an exit code <> 0. */
+#define EXITCODEEXEC_CODE ((RTEXITCODE)16)
+#define EXITCODEEXEC_FAILED ((RTEXITCODE)17)
+#define EXITCODEEXEC_TERM_SIGNAL ((RTEXITCODE)18)
+#define EXITCODEEXEC_TERM_ABEND ((RTEXITCODE)19)
+#define EXITCODEEXEC_TIMEOUT ((RTEXITCODE)20)
+#define EXITCODEEXEC_DOWN ((RTEXITCODE)21)
+/** Execution was interrupt by user (ctrl-c). */
+#define EXITCODEEXEC_CANCELED ((RTEXITCODE)22)
+/** The first mapped guest (non-zero) exit code. */
+#define EXITCODEEXEC_MAPPED_FIRST 33
+/** The last mapped guest (non-zero) exit code value (inclusive). */
+#define EXITCODEEXEC_MAPPED_LAST 125
+/** The number of exit codes from EXITCODEEXEC_MAPPED_FIRST to
+ * EXITCODEEXEC_MAPPED_LAST. This is also the highest guest exit code number
+ * we're able to map. */
+#define EXITCODEEXEC_MAPPED_RANGE (93)
+/** The guest exit code displacement value. */
+#define EXITCODEEXEC_MAPPED_DISPLACEMENT 32
+/** The guest exit code was too big to be mapped. */
+#define EXITCODEEXEC_MAPPED_BIG ((RTEXITCODE)126)
+/** @} */
+
+/**
+ * Calculates the exit code of VBoxManage.
+ *
+ * @returns The exit code to return.
+ * @param enmStatus The guest process status.
+ * @param uExitCode The associated guest process exit code (where
+ * applicable).
+ * @param fReturnExitCodes Set if we're to use the 32-126 range for guest
+ * exit codes.
+ */
+static RTEXITCODE gctlRunCalculateExitCode(ProcessStatus_T enmStatus, ULONG uExitCode, bool fReturnExitCodes)
+{
+ switch (enmStatus)
+ {
+ case ProcessStatus_TerminatedNormally:
+ if (uExitCode == 0)
+ return RTEXITCODE_SUCCESS;
+ if (!fReturnExitCodes)
+ return EXITCODEEXEC_CODE;
+ if (uExitCode <= EXITCODEEXEC_MAPPED_RANGE)
+ return (RTEXITCODE) (uExitCode + EXITCODEEXEC_MAPPED_DISPLACEMENT);
+ return EXITCODEEXEC_MAPPED_BIG;
+
+ case ProcessStatus_TerminatedAbnormally:
+ return EXITCODEEXEC_TERM_ABEND;
+ case ProcessStatus_TerminatedSignal:
+ return EXITCODEEXEC_TERM_SIGNAL;
+
+#if 0 /* see caller! */
+ case ProcessStatus_TimedOutKilled:
+ return EXITCODEEXEC_TIMEOUT;
+ case ProcessStatus_Down:
+ return EXITCODEEXEC_DOWN; /* Service/OS is stopping, process was killed. */
+ case ProcessStatus_Error:
+ return EXITCODEEXEC_FAILED;
+
+ /* The following is probably for detached? */
+ case ProcessStatus_Starting:
+ return RTEXITCODE_SUCCESS;
+ case ProcessStatus_Started:
+ return RTEXITCODE_SUCCESS;
+ case ProcessStatus_Paused:
+ return RTEXITCODE_SUCCESS;
+ case ProcessStatus_Terminating:
+ return RTEXITCODE_SUCCESS; /** @todo ???? */
+#endif
+
+ default:
+ AssertMsgFailed(("Unknown exit status (%u/%u) from guest process returned!\n", enmStatus, uExitCode));
+ return RTEXITCODE_FAILURE;
+ }
+}
+
+
+/**
+ * Pumps guest output to the host.
+ *
+ * @return IPRT status code.
+ * @param pProcess Pointer to appropriate process object.
+ * @param hVfsIosDst Where to write the data.
+ * @param uHandle Handle where to read the data from.
+ * @param cMsTimeout Timeout (in ms) to wait for the operation to
+ * complete.
+ */
+static int gctlRunPumpOutput(IProcess *pProcess, RTVFSIOSTREAM hVfsIosDst, ULONG uHandle, RTMSINTERVAL cMsTimeout)
+{
+ AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+ Assert(hVfsIosDst != NIL_RTVFSIOSTREAM);
+
+ int vrc;
+
+ SafeArray<BYTE> aOutputData;
+ HRESULT hrc = pProcess->Read(uHandle, _64K, RT_MAX(cMsTimeout, 1), ComSafeArrayAsOutParam(aOutputData));
+ if (SUCCEEDED(hrc))
+ {
+ size_t cbOutputData = aOutputData.size();
+ if (cbOutputData == 0)
+ vrc = VINF_SUCCESS;
+ else
+ {
+ BYTE const *pbBuf = aOutputData.raw();
+ AssertPtr(pbBuf);
+
+ vrc = RTVfsIoStrmWrite(hVfsIosDst, pbBuf, cbOutputData, true /*fBlocking*/, NULL);
+ if (RT_FAILURE(vrc))
+ RTMsgError("Unable to write output, rc=%Rrc\n", vrc);
+ }
+ }
+ else
+ vrc = gctlPrintError(pProcess, COM_IIDOF(IProcess));
+ return vrc;
+}
+
+
+/**
+ * Configures a host handle for pumping guest bits.
+ *
+ * @returns true if enabled and we successfully configured it.
+ * @param fEnabled Whether pumping this pipe is configured.
+ * @param enmHandle The IPRT standard handle designation.
+ * @param pszName The name for user messages.
+ * @param enmTransformation The transformation to apply.
+ * @param phVfsIos Where to return the resulting I/O stream handle.
+ */
+static bool gctlRunSetupHandle(bool fEnabled, RTHANDLESTD enmHandle, const char *pszName,
+ kStreamTransform enmTransformation, PRTVFSIOSTREAM phVfsIos)
+{
+ if (fEnabled)
+ {
+ int vrc = RTVfsIoStrmFromStdHandle(enmHandle, 0, true /*fLeaveOpen*/, phVfsIos);
+ if (RT_SUCCESS(vrc))
+ {
+ if (enmTransformation != kStreamTransform_None)
+ {
+ RTMsgWarning("Unsupported %s line ending conversion", pszName);
+ /** @todo Implement dos2unix and unix2dos stream filters. */
+ }
+ return true;
+ }
+ RTMsgWarning("Error getting %s handle: %Rrc", pszName, vrc);
+ }
+ return false;
+}
+
+
+/**
+ * Returns the remaining time (in ms) based on the start time and a set
+ * timeout value. Returns RT_INDEFINITE_WAIT if no timeout was specified.
+ *
+ * @return RTMSINTERVAL Time left (in ms).
+ * @param u64StartMs Start time (in ms).
+ * @param cMsTimeout Timeout value (in ms).
+ */
+static RTMSINTERVAL gctlRunGetRemainingTime(uint64_t u64StartMs, RTMSINTERVAL cMsTimeout)
+{
+ if (!cMsTimeout || cMsTimeout == RT_INDEFINITE_WAIT) /* If no timeout specified, wait forever. */
+ return RT_INDEFINITE_WAIT;
+
+ uint64_t u64ElapsedMs = RTTimeMilliTS() - u64StartMs;
+ if (u64ElapsedMs >= cMsTimeout)
+ return 0;
+
+ return cMsTimeout - (RTMSINTERVAL)u64ElapsedMs;
+}
+
+/**
+ * Common handler for the 'run' and 'start' commands.
+ *
+ * @returns Command exit code.
+ * @param pCtx Guest session context.
+ * @param argc The argument count.
+ * @param argv The argument vector for this command.
+ * @param fRunCmd Set if it's 'run' clear if 'start'.
+ * @param fHelp The help flag for the command.
+ */
+static RTEXITCODE gctlHandleRunCommon(PGCTLCMDCTX pCtx, int argc, char **argv, bool fRunCmd, uint32_t fHelp)
+{
+ RT_NOREF(fHelp);
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ /*
+ * Parse arguments.
+ */
+ enum kGstCtrlRunOpt
+ {
+ kGstCtrlRunOpt_IgnoreOrphanedProcesses = 1000,
+ kGstCtrlRunOpt_NoProfile, /** @todo Deprecated and will be removed soon; use kGstCtrlRunOpt_Profile instead, if needed. */
+ kGstCtrlRunOpt_Profile,
+ kGstCtrlRunOpt_Dos2Unix,
+ kGstCtrlRunOpt_Unix2Dos,
+ kGstCtrlRunOpt_WaitForStdOut,
+ kGstCtrlRunOpt_NoWaitForStdOut,
+ kGstCtrlRunOpt_WaitForStdErr,
+ kGstCtrlRunOpt_NoWaitForStdErr
+ };
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--putenv", 'E', RTGETOPT_REQ_STRING },
+ { "--exe", 'e', RTGETOPT_REQ_STRING },
+ { "--timeout", 't', RTGETOPT_REQ_UINT32 },
+ { "--unquoted-args", 'u', RTGETOPT_REQ_NOTHING },
+ { "--ignore-operhaned-processes", kGstCtrlRunOpt_IgnoreOrphanedProcesses, RTGETOPT_REQ_NOTHING },
+ { "--no-profile", kGstCtrlRunOpt_NoProfile, RTGETOPT_REQ_NOTHING }, /** @todo Deprecated. */
+ { "--profile", kGstCtrlRunOpt_Profile, RTGETOPT_REQ_NOTHING },
+ /* run only: 6 - options */
+ { "--dos2unix", kGstCtrlRunOpt_Dos2Unix, RTGETOPT_REQ_NOTHING },
+ { "--unix2dos", kGstCtrlRunOpt_Unix2Dos, RTGETOPT_REQ_NOTHING },
+ { "--no-wait-stdout", kGstCtrlRunOpt_NoWaitForStdOut, RTGETOPT_REQ_NOTHING },
+ { "--wait-stdout", kGstCtrlRunOpt_WaitForStdOut, RTGETOPT_REQ_NOTHING },
+ { "--no-wait-stderr", kGstCtrlRunOpt_NoWaitForStdErr, RTGETOPT_REQ_NOTHING },
+ { "--wait-stderr", kGstCtrlRunOpt_WaitForStdErr, RTGETOPT_REQ_NOTHING },
+ };
+
+ /** @todo stdin handling. */
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions) - (fRunCmd ? 0 : 6),
+ 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRC(vrc);
+
+ com::SafeArray<ProcessCreateFlag_T> aCreateFlags;
+ com::SafeArray<ProcessWaitForFlag_T> aWaitFlags;
+ com::SafeArray<IN_BSTR> aArgs;
+ com::SafeArray<IN_BSTR> aEnv;
+ const char * pszImage = NULL;
+ bool fWaitForStdOut = fRunCmd;
+ bool fWaitForStdErr = fRunCmd;
+ RTVFSIOSTREAM hVfsStdOut = NIL_RTVFSIOSTREAM;
+ RTVFSIOSTREAM hVfsStdErr = NIL_RTVFSIOSTREAM;
+ enum kStreamTransform enmStdOutTransform = kStreamTransform_None;
+ enum kStreamTransform enmStdErrTransform = kStreamTransform_None;
+ RTMSINTERVAL cMsTimeout = 0;
+
+ try
+ {
+ /* 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)) != 0)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case 'E':
+ if ( ValueUnion.psz[0] == '\0'
+ || ValueUnion.psz[0] == '=')
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN,
+ "Invalid argument variable[=value]: '%s'", ValueUnion.psz);
+ aEnv.push_back(Bstr(ValueUnion.psz).raw());
+ break;
+
+ case kGstCtrlRunOpt_IgnoreOrphanedProcesses:
+ aCreateFlags.push_back(ProcessCreateFlag_IgnoreOrphanedProcesses);
+ break;
+
+ case kGstCtrlRunOpt_NoProfile:
+ /** @todo Deprecated, will be removed. */
+ RTPrintf("Warning: Deprecated option \"--no-profile\" specified\n");
+ break;
+
+ case kGstCtrlRunOpt_Profile:
+ aCreateFlags.push_back(ProcessCreateFlag_Profile);
+ break;
+
+ case 'e':
+ pszImage = ValueUnion.psz;
+ break;
+
+ case 'u':
+ aCreateFlags.push_back(ProcessCreateFlag_UnquotedArguments);
+ break;
+
+ /** @todo Add a hidden flag. */
+
+ case 't': /* Timeout */
+ cMsTimeout = ValueUnion.u32;
+ break;
+
+ /* run only options: */
+ case kGstCtrlRunOpt_Dos2Unix:
+ Assert(fRunCmd);
+ enmStdErrTransform = enmStdOutTransform = kStreamTransform_Dos2Unix;
+ break;
+ case kGstCtrlRunOpt_Unix2Dos:
+ Assert(fRunCmd);
+ enmStdErrTransform = enmStdOutTransform = kStreamTransform_Unix2Dos;
+ break;
+
+ case kGstCtrlRunOpt_WaitForStdOut:
+ Assert(fRunCmd);
+ fWaitForStdOut = true;
+ break;
+ case kGstCtrlRunOpt_NoWaitForStdOut:
+ Assert(fRunCmd);
+ fWaitForStdOut = false;
+ break;
+
+ case kGstCtrlRunOpt_WaitForStdErr:
+ Assert(fRunCmd);
+ fWaitForStdErr = true;
+ break;
+ case kGstCtrlRunOpt_NoWaitForStdErr:
+ Assert(fRunCmd);
+ fWaitForStdErr = false;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ aArgs.push_back(Bstr(ValueUnion.psz).raw());
+ if (!pszImage)
+ {
+ Assert(aArgs.size() == 1);
+ pszImage = ValueUnion.psz;
+ }
+ break;
+
+ default:
+ return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN, ch, &ValueUnion);
+
+ } /* switch */
+ } /* while RTGetOpt */
+
+ /* Must have something to execute. */
+ if (!pszImage || !*pszImage)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN, "No executable specified!");
+
+ /*
+ * Finalize process creation and wait flags and input/output streams.
+ */
+ if (!fRunCmd)
+ {
+ aCreateFlags.push_back(ProcessCreateFlag_WaitForProcessStartOnly);
+ Assert(!fWaitForStdOut);
+ Assert(!fWaitForStdErr);
+ }
+ else
+ {
+ aWaitFlags.push_back(ProcessWaitForFlag_Terminate);
+ fWaitForStdOut = gctlRunSetupHandle(fWaitForStdOut, RTHANDLESTD_OUTPUT, "stdout", enmStdOutTransform, &hVfsStdOut);
+ if (fWaitForStdOut)
+ {
+ aCreateFlags.push_back(ProcessCreateFlag_WaitForStdOut);
+ aWaitFlags.push_back(ProcessWaitForFlag_StdOut);
+ }
+ fWaitForStdErr = gctlRunSetupHandle(fWaitForStdErr, RTHANDLESTD_ERROR, "stderr", enmStdErrTransform, &hVfsStdErr);
+ if (fWaitForStdErr)
+ {
+ aCreateFlags.push_back(ProcessCreateFlag_WaitForStdErr);
+ aWaitFlags.push_back(ProcessWaitForFlag_StdErr);
+ }
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "VERR_NO_MEMORY\n");
+ }
+
+ RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ HRESULT rc;
+
+ try
+ {
+ do
+ {
+ /* Get current time stamp to later calculate rest of timeout left. */
+ uint64_t msStart = RTTimeMilliTS();
+
+ /*
+ * Create the process.
+ */
+ if (pCtx->cVerbose)
+ {
+ if (cMsTimeout == 0)
+ RTPrintf("Starting guest process ...\n");
+ else
+ RTPrintf("Starting guest process (within %ums)\n", cMsTimeout);
+ }
+ ComPtr<IGuestProcess> pProcess;
+ CHECK_ERROR_BREAK(pCtx->pGuestSession, ProcessCreate(Bstr(pszImage).raw(),
+ ComSafeArrayAsInParam(aArgs),
+ ComSafeArrayAsInParam(aEnv),
+ ComSafeArrayAsInParam(aCreateFlags),
+ gctlRunGetRemainingTime(msStart, 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;
+ CHECK_ERROR_BREAK(pProcess, WaitForArray(ComSafeArrayAsInParam(aWaitStartFlags),
+ gctlRunGetRemainingTime(msStart, cMsTimeout), &waitResult));
+
+ ULONG uPID = 0;
+ CHECK_ERROR_BREAK(pProcess, COMGETTER(PID)(&uPID));
+ if (fRunCmd && pCtx->cVerbose)
+ RTPrintf("Process '%s' (PID %RU32) started\n", pszImage, uPID);
+ else if (!fRunCmd && pCtx->cVerbose)
+ {
+ /* Just print plain PID to make it easier for scripts
+ * invoking VBoxManage. */
+ RTPrintf("[%RU32 - Session %RU32]\n", uPID, pCtx->uSessionID);
+ }
+
+ /*
+ * Wait for process to exit/start...
+ */
+ RTMSINTERVAL cMsTimeLeft = 1; /* Will be calculated. */
+ bool fReadStdOut = false;
+ bool fReadStdErr = false;
+ bool fCompleted = false;
+ bool fCompletedStartCmd = false;
+
+ vrc = VINF_SUCCESS;
+ while ( !fCompleted
+ && cMsTimeLeft > 0)
+ {
+ cMsTimeLeft = gctlRunGetRemainingTime(msStart, cMsTimeout);
+ CHECK_ERROR_BREAK(pProcess, WaitForArray(ComSafeArrayAsInParam(aWaitFlags),
+ RT_MIN(500 /*ms*/, RT_MAX(cMsTimeLeft, 1 /*ms*/)),
+ &waitResult));
+ switch (waitResult)
+ {
+ case ProcessWaitResult_Start:
+ fCompletedStartCmd = fCompleted = !fRunCmd; /* Only wait for startup if the 'start' command. */
+ break;
+ case ProcessWaitResult_StdOut:
+ fReadStdOut = true;
+ break;
+ case ProcessWaitResult_StdErr:
+ fReadStdErr = true;
+ break;
+ case ProcessWaitResult_Terminate:
+ if (pCtx->cVerbose)
+ 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();
+ fReadStdOut = fReadStdErr = true;
+ break;
+ case ProcessWaitResult_Timeout:
+ {
+ /** @todo It is really unclear whether we will get stuck with the timeout
+ * result here if the guest side times out the process and fails to
+ * kill the process... To be on the save side, double the IPC and
+ * check the process status every time we time out. */
+ ProcessStatus_T enmProcStatus;
+ CHECK_ERROR_BREAK(pProcess, COMGETTER(Status)(&enmProcStatus));
+ if ( enmProcStatus == ProcessStatus_TimedOutKilled
+ || enmProcStatus == ProcessStatus_TimedOutAbnormally)
+ fCompleted = true;
+ fReadStdOut = fReadStdErr = true;
+ break;
+ }
+ case ProcessWaitResult_Status:
+ /* ignore. */
+ break;
+ case ProcessWaitResult_Error:
+ /* waitFor is dead in the water, I think, so better leave the loop. */
+ vrc = VERR_CALLBACK_RETURN;
+ break;
+
+ case ProcessWaitResult_StdIn: AssertFailed(); /* did ask for this! */ break;
+ case ProcessWaitResult_None: AssertFailed(); /* used. */ break;
+ default: AssertFailed(); /* huh? */ break;
+ }
+
+ if (g_fGuestCtrlCanceled)
+ break;
+
+ /*
+ * Pump output as needed.
+ */
+ if (fReadStdOut)
+ {
+ cMsTimeLeft = gctlRunGetRemainingTime(msStart, cMsTimeout);
+ int vrc2 = gctlRunPumpOutput(pProcess, hVfsStdOut, 1 /* StdOut */, cMsTimeLeft);
+ if (RT_FAILURE(vrc2) && RT_SUCCESS(vrc))
+ vrc = vrc2;
+ fReadStdOut = false;
+ }
+ if (fReadStdErr)
+ {
+ cMsTimeLeft = gctlRunGetRemainingTime(msStart, cMsTimeout);
+ int vrc2 = gctlRunPumpOutput(pProcess, hVfsStdErr, 2 /* StdErr */, cMsTimeLeft);
+ if (RT_FAILURE(vrc2) && RT_SUCCESS(vrc))
+ vrc = vrc2;
+ fReadStdErr = false;
+ }
+ if ( RT_FAILURE(vrc)
+ || g_fGuestCtrlCanceled)
+ break;
+
+ /*
+ * Process events before looping.
+ */
+ NativeEventQueue::getMainEventQueue()->processEventQueue(0);
+ } /* while */
+
+ /*
+ * Report status back to the user.
+ */
+ if (g_fGuestCtrlCanceled)
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Process execution aborted!\n");
+ rcExit = EXITCODEEXEC_CANCELED;
+ }
+ else if (fCompletedStartCmd)
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Process successfully started!\n");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else if (fCompleted)
+ {
+ ProcessStatus_T procStatus;
+ CHECK_ERROR_BREAK(pProcess, COMGETTER(Status)(&procStatus));
+ if ( procStatus == ProcessStatus_TerminatedNormally
+ || procStatus == ProcessStatus_TerminatedAbnormally
+ || procStatus == ProcessStatus_TerminatedSignal)
+ {
+ LONG lExitCode;
+ CHECK_ERROR_BREAK(pProcess, COMGETTER(ExitCode)(&lExitCode));
+ if (pCtx->cVerbose)
+ RTPrintf("Exit code=%u (Status=%u [%s])\n",
+ lExitCode, procStatus, gctlProcessStatusToText(procStatus));
+
+ rcExit = gctlRunCalculateExitCode(procStatus, lExitCode, true /*fReturnExitCodes*/);
+ }
+ else if ( procStatus == ProcessStatus_TimedOutKilled
+ || procStatus == ProcessStatus_TimedOutAbnormally)
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Process timed out (guest side) and %s\n",
+ procStatus == ProcessStatus_TimedOutAbnormally
+ ? "failed to terminate so far" : "was terminated");
+ rcExit = EXITCODEEXEC_TIMEOUT;
+ }
+ else
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Process now is in status [%s] (unexpected)\n", gctlProcessStatusToText(procStatus));
+ rcExit = RTEXITCODE_FAILURE;
+ }
+ }
+ else if (RT_FAILURE_NP(vrc))
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Process monitor loop quit with vrc=%Rrc\n", vrc);
+ rcExit = RTEXITCODE_FAILURE;
+ }
+ else
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Process monitor loop timed out\n");
+ rcExit = EXITCODEEXEC_TIMEOUT;
+ }
+
+ } while (0);
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = E_OUTOFMEMORY;
+ }
+
+ /*
+ * Decide what to do with the guest session.
+ *
+ * If it's the 'start' command where detach the guest process after
+ * starting, don't close the guest session it is part of, except on
+ * failure or ctrl-c.
+ *
+ * For the 'run' command the guest process quits with us.
+ */
+ if (!fRunCmd && SUCCEEDED(rc) && !g_fGuestCtrlCanceled)
+ pCtx->fDetachGuestSession = true;
+
+ /* Make sure we return failure on failure. */
+ if (FAILED(rc) && rcExit == RTEXITCODE_SUCCESS)
+ rcExit = RTEXITCODE_FAILURE;
+ return rcExit;
+}
+
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleRun(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ return gctlHandleRunCommon(pCtx, argc, argv, true /*fRunCmd*/, USAGE_GSTCTRL_RUN);
+}
+
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleStart(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ return gctlHandleRunCommon(pCtx, argc, argv, false /*fRunCmd*/, USAGE_GSTCTRL_START);
+}
+
+
+static RTEXITCODE gctlHandleCopy(PGCTLCMDCTX pCtx, int argc, char **argv, bool fHostToGuest)
+{
+ 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
+ * arguments against this is that (1) all but two options conflicts with
+ * what 'man cp' tells me on a GNU/Linux system, (2) wildchar matching is
+ * done windows CMD style (though not in a 100% compatible way), and (3)
+ * that only one source is allowed - efficiently sabotaging default
+ * wildcard expansion by a unix shell. The best solution here would be
+ * two different variant, one windowsy (xcopy) and one unixy (gnu cp). */
+
+ /*
+ * IGuest::CopyToGuest is kept as simple as possible to let the developer choose
+ * what and how to implement the file enumeration/recursive lookup, like VBoxManage
+ * does in here.
+ */
+ enum GETOPTDEF_COPY
+ {
+ GETOPTDEF_COPY_FOLLOW = 1000,
+ GETOPTDEF_COPY_TARGETDIR
+ };
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--follow", GETOPTDEF_COPY_FOLLOW, RTGETOPT_REQ_NOTHING },
+ { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
+ { "--target-directory", GETOPTDEF_COPY_TARGETDIR, RTGETOPT_REQ_STRING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ bool fDstMustBeDir = false;
+ const char *pszDst = NULL;
+ bool fFollow = false;
+ bool fRecursive = false;
+ uint32_t uUsage = fHostToGuest ? USAGE_GSTCTRL_COPYTO : USAGE_GSTCTRL_COPYFROM;
+
+ int vrc = VINF_SUCCESS;
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion)) != 0
+ && ch != VINF_GETOPT_NOT_OPTION)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case GETOPTDEF_COPY_FOLLOW:
+ fFollow = true;
+ break;
+
+ case 'R': /* Recursive processing */
+ fRecursive = true;
+ break;
+
+ case GETOPTDEF_COPY_TARGETDIR:
+ pszDst = ValueUnion.psz;
+ fDstMustBeDir = true;
+ break;
+
+ default:
+ return errorGetOptEx(USAGE_GUESTCONTROL, uUsage, ch, &ValueUnion);
+ }
+ }
+
+ char **papszSources = RTGetOptNonOptionArrayPtr(&GetState);
+ size_t cSources = &argv[argc] - papszSources;
+
+ if (!cSources)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, uUsage, "No sources specified!");
+
+ /* Unless a --target-directory is given, the last argument is the destination, so
+ bump it from the source list. */
+ if (pszDst == NULL && cSources >= 2)
+ pszDst = papszSources[--cSources];
+
+ if (pszDst == NULL)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, uUsage, "No destination specified!");
+
+ char szAbsDst[RTPATH_MAX];
+ if (!fHostToGuest)
+ {
+ vrc = RTPathAbs(pszDst, szAbsDst, sizeof(szAbsDst));
+ if (RT_SUCCESS(vrc))
+ pszDst = szAbsDst;
+ else
+ return RTMsgErrorExitFailure("RTPathAbs failed on '%s': %Rrc", pszDst, vrc);
+ }
+
+ RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ /*
+ * Done parsing arguments, do some more preparations.
+ */
+ if (pCtx->cVerbose)
+ {
+ if (fHostToGuest)
+ RTPrintf("Copying from host to guest ...\n");
+ else
+ RTPrintf("Copying from guest to host ...\n");
+ }
+
+ HRESULT rc = S_OK;
+ ComPtr<IProgress> pProgress;
+/** @todo r=bird: This codes does nothing to handle the case where there are
+ * multiple sources. You need to do serveral thing before thats handled
+ * correctly. For starters the progress object handling needs to be moved
+ * inside the loop. Next you need to check what the destination is, because you
+ * can only copy multiple source files/directories to another directory. You
+ * actually need to check whether the target exists and is a directory
+ * regardless of you have 1 or 10 source files/dirs.
+ *
+ * Btw. the original approach to error handling here was APPALING. If some file
+ * couldn't be stat'ed or if it was a file/directory, you only spat out messages
+ * in verbose mode and never set the status code.
+ *
+ * The handling of the wildcard filtering expressions in sources was also just
+ * skipped. I've corrected this, but you still need to make up your mind wrt
+ * wildcards or not.
+ *
+ * Update: I've kicked out the whole SourceFileEntry/vecSources stuff as we can
+ * use argv directly without any unnecessary copying. You just have to
+ * look for the wildcards inside this loop instead.
+ */
+ NOREF(fDstMustBeDir);
+ if (cSources != 1)
+ return RTMsgErrorExitFailure("Only one source file or directory at the moment.");
+ for (size_t iSrc = 0; iSrc < cSources; iSrc++)
+ {
+ const char *pszSource = papszSources[iSrc];
+
+ /* Check if the source contains any wilecards in the last component, if so we
+ don't know how to deal with it yet and refuse. */
+ const char *pszSrcFinalComp = RTPathFilename(pszSource);
+ if (pszSrcFinalComp && strpbrk(pszSrcFinalComp, "*?"))
+ rcExit = RTMsgErrorExitFailure("Skipping '%s' because wildcard expansion isn't implemented yet\n", pszSource);
+ else if (fHostToGuest)
+ {
+ /*
+ * Source is host, destiation guest.
+ */
+ char szAbsSrc[RTPATH_MAX];
+ vrc = RTPathAbs(pszSource, szAbsSrc, sizeof(szAbsSrc));
+ if (RT_SUCCESS(vrc))
+ {
+ RTFSOBJINFO ObjInfo;
+ vrc = RTPathQueryInfo(szAbsSrc, &ObjInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(vrc))
+ {
+ if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("File '%s' -> '%s'\n", szAbsSrc, pszDst);
+
+ SafeArray<FileCopyFlag_T> copyFlags;
+ rc = pCtx->pGuestSession->FileCopyToGuest(Bstr(szAbsSrc).raw(), Bstr(pszDst).raw(),
+ ComSafeArrayAsInParam(copyFlags), pProgress.asOutParam());
+ }
+ else if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Directory '%s' -> '%s'\n", szAbsSrc, pszDst);
+
+ SafeArray<DirectoryCopyFlag_T> copyFlags;
+ copyFlags.push_back(DirectoryCopyFlag_CopyIntoExisting);
+ rc = pCtx->pGuestSession->DirectoryCopyToGuest(Bstr(szAbsSrc).raw(), Bstr(pszDst).raw(),
+ ComSafeArrayAsInParam(copyFlags), pProgress.asOutParam());
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Not a file or directory: %s\n", szAbsSrc);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("RTPathQueryInfo failed on '%s': %Rrc", szAbsSrc, vrc);
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("RTPathAbs failed on '%s': %Rrc", pszSource, vrc);
+ }
+ else
+ {
+ /*
+ * Source guest, destination host.
+ */
+ /* We need to query the source type on the guest first in order to know which copy flavor we need. */
+ ComPtr<IGuestFsObjInfo> pFsObjInfo;
+ rc = pCtx->pGuestSession->FsObjQueryInfo(Bstr(pszSource).raw(), TRUE /* fFollowSymlinks */, pFsObjInfo.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ FsObjType_T enmObjType;
+ CHECK_ERROR(pFsObjInfo,COMGETTER(Type)(&enmObjType));
+ if (SUCCEEDED(rc))
+ {
+ /* Take action according to source file. */
+ if (enmObjType == FsObjType_Directory)
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Directory '%s' -> '%s'\n", pszSource, pszDst);
+
+ SafeArray<DirectoryCopyFlag_T> aCopyFlags;
+ aCopyFlags.push_back(DirectoryCopyFlag_CopyIntoExisting);
+ rc = pCtx->pGuestSession->DirectoryCopyFromGuest(Bstr(pszSource).raw(), Bstr(pszDst).raw(),
+ ComSafeArrayAsInParam(aCopyFlags), pProgress.asOutParam());
+ }
+ else if (enmObjType == FsObjType_File)
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("File '%s' -> '%s'\n", pszSource, pszDst);
+
+ SafeArray<FileCopyFlag_T> aCopyFlags;
+ rc = pCtx->pGuestSession->FileCopyFromGuest(Bstr(pszSource).raw(), Bstr(pszDst).raw(),
+ ComSafeArrayAsInParam(aCopyFlags), pProgress.asOutParam());
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("Not a file or directory: %s\n", pszSource);
+ }
+ else
+ rcExit = RTEXITCODE_FAILURE;
+ }
+ else
+ rcExit = RTMsgErrorExitFailure("FsObjQueryInfo failed on '%s': %Rhrc", pszSource, rc);
+ }
+ }
+
+ if (FAILED(rc))
+ {
+ vrc = gctlPrintError(pCtx->pGuestSession, COM_IIDOF(IGuestSession));
+ }
+ else if (pProgress.isNotNull())
+ {
+ if (pCtx->cVerbose)
+ rc = showProgress(pProgress);
+ else
+ rc = pProgress->WaitForCompletion(-1 /* No timeout */);
+ if (SUCCEEDED(rc))
+ CHECK_PROGRESS_ERROR(pProgress, ("File copy failed"));
+ vrc = gctlPrintProgressError(pProgress);
+ }
+ if (RT_FAILURE(vrc))
+ rcExit = RTEXITCODE_FAILURE;
+
+ return rcExit;
+}
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleCopyFrom(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ return gctlHandleCopy(pCtx, argc, argv, false /* Guest to host */);
+}
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleCopyTo(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ return gctlHandleCopy(pCtx, argc, argv, true /* Host to guest */);
+}
+
+static DECLCALLBACK(RTEXITCODE) gctrlHandleMkDir(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--mode", 'm', RTGETOPT_REQ_UINT32 },
+ { "--parents", 'P', RTGETOPT_REQ_NOTHING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ SafeArray<DirectoryCreateFlag_T> aDirCreateFlags;
+ uint32_t fDirMode = 0; /* Default mode. */
+ uint32_t cDirsCreated = 0;
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case 'm': /* Mode */
+ fDirMode = ValueUnion.u32;
+ break;
+
+ case 'P': /* Create parents */
+ aDirCreateFlags.push_back(DirectoryCreateFlag_Parents);
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (cDirsCreated == 0)
+ {
+ /*
+ * First non-option - no more options now.
+ */
+ rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ if (pCtx->cVerbose)
+ RTPrintf("Creating %RU32 directories...\n", argc - GetState.iNext + 1);
+ }
+ if (g_fGuestCtrlCanceled)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "mkdir was interrupted by Ctrl-C (%u left)\n",
+ argc - GetState.iNext + 1);
+
+ /*
+ * Create the specified directory.
+ *
+ * On failure we'll change the exit status to failure and
+ * continue with the next directory that needs creating. We do
+ * this because we only create new things, and because this is
+ * how /bin/mkdir works on unix.
+ */
+ cDirsCreated++;
+ if (pCtx->cVerbose)
+ RTPrintf("Creating directory \"%s\" ...\n", ValueUnion.psz);
+ try
+ {
+ HRESULT rc;
+ CHECK_ERROR(pCtx->pGuestSession, DirectoryCreate(Bstr(ValueUnion.psz).raw(),
+ fDirMode, ComSafeArrayAsInParam(aDirCreateFlags)));
+ if (FAILED(rc))
+ rcExit = RTEXITCODE_FAILURE;
+ }
+ catch (std::bad_alloc &)
+ {
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory\n");
+ }
+ break;
+
+ default:
+ return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MKDIR, ch, &ValueUnion);
+ }
+ }
+
+ if (!cDirsCreated)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MKDIR, "No directory to create specified!");
+ return rcExit;
+}
+
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleRmDir(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ bool fRecursive = false;
+ uint32_t cDirRemoved = 0;
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case 'R':
+ fRecursive = true;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ if (cDirRemoved == 0)
+ {
+ /*
+ * First non-option - no more options now.
+ */
+ rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ if (pCtx->cVerbose)
+ RTPrintf("Removing %RU32 directorie%s(s)...\n", argc - GetState.iNext + 1, fRecursive ? "tree" : "");
+ }
+ if (g_fGuestCtrlCanceled)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "rmdir was interrupted by Ctrl-C (%u left)\n",
+ argc - GetState.iNext + 1);
+
+ cDirRemoved++;
+ HRESULT rc;
+ if (!fRecursive)
+ {
+ /*
+ * Remove exactly one directory.
+ */
+ if (pCtx->cVerbose)
+ RTPrintf("Removing directory \"%s\" ...\n", ValueUnion.psz);
+ try
+ {
+ CHECK_ERROR(pCtx->pGuestSession, DirectoryRemove(Bstr(ValueUnion.psz).raw()));
+ }
+ catch (std::bad_alloc &)
+ {
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory\n");
+ }
+ }
+ else
+ {
+ /*
+ * Remove the directory and anything under it, that means files
+ * and everything. This is in the tradition of the Windows NT
+ * CMD.EXE "rmdir /s" operation, a tradition which jpsoft's TCC
+ * strongly warns against (and half-ways questions the sense of).
+ */
+ if (pCtx->cVerbose)
+ RTPrintf("Recursively removing directory \"%s\" ...\n", ValueUnion.psz);
+ try
+ {
+ /** @todo Make flags configurable. */
+ com::SafeArray<DirectoryRemoveRecFlag_T> aRemRecFlags;
+ aRemRecFlags.push_back(DirectoryRemoveRecFlag_ContentAndDir);
+
+ ComPtr<IProgress> ptrProgress;
+ CHECK_ERROR(pCtx->pGuestSession, DirectoryRemoveRecursive(Bstr(ValueUnion.psz).raw(),
+ ComSafeArrayAsInParam(aRemRecFlags),
+ ptrProgress.asOutParam()));
+ if (SUCCEEDED(rc))
+ {
+ if (pCtx->cVerbose)
+ rc = showProgress(ptrProgress);
+ else
+ rc = ptrProgress->WaitForCompletion(-1 /* indefinitely */);
+ if (SUCCEEDED(rc))
+ CHECK_PROGRESS_ERROR(ptrProgress, ("Directory deletion failed"));
+ ptrProgress.setNull();
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory during recursive rmdir\n");
+ }
+ }
+
+ /*
+ * This command returns immediately on failure since it's destructive in nature.
+ */
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+ break;
+ }
+
+ default:
+ return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RMDIR, ch, &ValueUnion);
+ }
+ }
+
+ if (!cDirRemoved)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RMDIR, "No directory to remove specified!");
+ return rcExit;
+}
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleRm(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--force", 'f', RTGETOPT_REQ_NOTHING, },
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ uint32_t cFilesDeleted = 0;
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ bool fForce = true;
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (cFilesDeleted == 0)
+ {
+ /*
+ * First non-option - no more options now.
+ */
+ rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ if (pCtx->cVerbose)
+ RTPrintf("Removing %RU32 file(s)...\n", argc - GetState.iNext + 1);
+ }
+ if (g_fGuestCtrlCanceled)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "rm was interrupted by Ctrl-C (%u left)\n",
+ argc - GetState.iNext + 1);
+
+ /*
+ * Remove the specified file.
+ *
+ * On failure we will by default stop, however, the force option will
+ * by unix traditions force us to ignore errors and continue.
+ */
+ cFilesDeleted++;
+ if (pCtx->cVerbose)
+ RTPrintf("Removing file \"%s\" ...\n", ValueUnion.psz);
+ try
+ {
+ /** @todo How does IGuestSession::FsObjRemove work with read-only files? Do we
+ * need to do some chmod or whatever to better emulate the --force flag? */
+ HRESULT rc;
+ CHECK_ERROR(pCtx->pGuestSession, FsObjRemove(Bstr(ValueUnion.psz).raw()));
+ if (FAILED(rc) && !fForce)
+ return RTEXITCODE_FAILURE;
+ }
+ catch (std::bad_alloc &)
+ {
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory\n");
+ }
+ break;
+
+ default:
+ return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RM, ch, &ValueUnion);
+ }
+ }
+
+ if (!cFilesDeleted && !fForce)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RM, "No file to remove specified!");
+ return rcExit;
+}
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleMv(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+/** @todo Missing --force/-f flag. */
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ int vrc = VINF_SUCCESS;
+
+ bool fDryrun = false;
+ std::vector< Utf8Str > vecSources;
+ const char *pszDst = NULL;
+ com::SafeArray<FsObjRenameFlag_T> aRenameFlags;
+
+ try
+ {
+ /** @todo Make flags configurable. */
+ aRenameFlags.push_back(FsObjRenameFlag_NoReplace);
+
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(vrc))
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ /** @todo Implement a --dryrun command. */
+ /** @todo Implement rename flags. */
+
+ case VINF_GETOPT_NOT_OPTION:
+ vecSources.push_back(Utf8Str(ValueUnion.psz));
+ pszDst = ValueUnion.psz;
+ break;
+
+ default:
+ return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MV, 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);
+
+ size_t cSources = vecSources.size();
+ if (!cSources)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MV,
+ "No source(s) to move specified!");
+ if (cSources < 2)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MV,
+ "No destination specified!");
+
+ RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ /* Delete last element, which now is the destination. */
+ vecSources.pop_back();
+ cSources = vecSources.size();
+
+ HRESULT rc = S_OK;
+
+ if (cSources > 1)
+ {
+ BOOL fExists = FALSE;
+ rc = pCtx->pGuestSession->DirectoryExists(Bstr(pszDst).raw(), FALSE /*followSymlinks*/, &fExists);
+ if (FAILED(rc) || !fExists)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Destination must be a directory when specifying multiple sources\n");
+ }
+
+ /*
+ * Rename (move) the entries.
+ */
+ if (pCtx->cVerbose)
+ RTPrintf("Renaming %RU32 %s ...\n", cSources, cSources > 1 ? "entries" : "entry");
+
+ std::vector< Utf8Str >::iterator it = vecSources.begin();
+ while ( it != vecSources.end()
+ && !g_fGuestCtrlCanceled)
+ {
+ Utf8Str strCurSource = (*it);
+
+ ComPtr<IGuestFsObjInfo> pFsObjInfo;
+ FsObjType_T enmObjType = FsObjType_Unknown; /* Shut up MSC */
+ rc = pCtx->pGuestSession->FsObjQueryInfo(Bstr(strCurSource).raw(), FALSE /*followSymlinks*/, pFsObjInfo.asOutParam());
+ if (SUCCEEDED(rc))
+ rc = pFsObjInfo->COMGETTER(Type)(&enmObjType);
+ if (FAILED(rc))
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Warning: Cannot stat for element \"%s\": No such file or directory\n", strCurSource.c_str());
+ ++it;
+ continue; /* Skip. */
+ }
+
+ if (pCtx->cVerbose)
+ RTPrintf("Renaming %s \"%s\" to \"%s\" ...\n",
+ enmObjType == FsObjType_Directory ? "directory" : "file",
+ strCurSource.c_str(), pszDst);
+
+ if (!fDryrun)
+ {
+ if (enmObjType == FsObjType_Directory)
+ {
+ CHECK_ERROR_BREAK(pCtx->pGuestSession, FsObjRename(Bstr(strCurSource).raw(),
+ Bstr(pszDst).raw(),
+ ComSafeArrayAsInParam(aRenameFlags)));
+
+ /* Break here, since it makes no sense to rename mroe than one source to
+ * the same directory. */
+/** @todo r=bird: You are being kind of windowsy (or just DOSish) about the 'sense' part here,
+ * while being totaly buggy about the behavior. 'VBoxManage guestcontrol ren dir1 dir2 dstdir' will
+ * stop after 'dir1' and SILENTLY ignore dir2. If you tried this on Windows, you'd see an error
+ * being displayed. If you 'man mv' on a nearby unixy system, you'd see that they've made perfect
+ * sense out of any situation with more than one source. */
+ it = vecSources.end();
+ break;
+ }
+ else
+ CHECK_ERROR_BREAK(pCtx->pGuestSession, FsObjRename(Bstr(strCurSource).raw(),
+ Bstr(pszDst).raw(),
+ ComSafeArrayAsInParam(aRenameFlags)));
+ }
+
+ ++it;
+ }
+
+ if ( (it != vecSources.end())
+ && pCtx->cVerbose)
+ {
+ RTPrintf("Warning: Not all sources were renamed\n");
+ }
+
+ return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
+}
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleMkTemp(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--mode", 'm', RTGETOPT_REQ_UINT32 },
+ { "--directory", 'D', RTGETOPT_REQ_NOTHING },
+ { "--secure", 's', RTGETOPT_REQ_NOTHING },
+ { "--tmpdir", 't', RTGETOPT_REQ_STRING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ Utf8Str strTemplate;
+ uint32_t fMode = 0; /* Default mode. */
+ bool fDirectory = false;
+ bool fSecure = false;
+ Utf8Str strTempDir;
+
+ DESTDIRMAP mapDirs;
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case 'm': /* Mode */
+ fMode = ValueUnion.u32;
+ break;
+
+ case 'D': /* Create directory */
+ fDirectory = true;
+ break;
+
+ case 's': /* Secure */
+ fSecure = true;
+ break;
+
+ case 't': /* Temp directory */
+ strTempDir = ValueUnion.psz;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (strTemplate.isEmpty())
+ strTemplate = ValueUnion.psz;
+ else
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MKTEMP,
+ "More than one template specified!\n");
+ break;
+
+ default:
+ return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MKTEMP, ch, &ValueUnion);
+ }
+ }
+
+ if (strTemplate.isEmpty())
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MKTEMP,
+ "No template specified!");
+
+ if (!fDirectory)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MKTEMP,
+ "Creating temporary files is currently not supported!");
+
+ RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ /*
+ * Create the directories.
+ */
+ if (pCtx->cVerbose)
+ {
+ if (fDirectory && !strTempDir.isEmpty())
+ RTPrintf("Creating temporary directory from template '%s' in directory '%s' ...\n",
+ strTemplate.c_str(), strTempDir.c_str());
+ else if (fDirectory)
+ RTPrintf("Creating temporary directory from template '%s' in default temporary directory ...\n",
+ strTemplate.c_str());
+ else if (!fDirectory && !strTempDir.isEmpty())
+ RTPrintf("Creating temporary file from template '%s' in directory '%s' ...\n",
+ strTemplate.c_str(), strTempDir.c_str());
+ else if (!fDirectory)
+ RTPrintf("Creating temporary file from template '%s' in default temporary directory ...\n",
+ strTemplate.c_str());
+ }
+
+ HRESULT rc = S_OK;
+ if (fDirectory)
+ {
+ Bstr bstrDirectory;
+ CHECK_ERROR(pCtx->pGuestSession, DirectoryCreateTemp(Bstr(strTemplate).raw(),
+ fMode, Bstr(strTempDir).raw(),
+ fSecure,
+ bstrDirectory.asOutParam()));
+ if (SUCCEEDED(rc))
+ RTPrintf("Directory name: %ls\n", bstrDirectory.raw());
+ }
+ else
+ {
+ // else - temporary file not yet implemented
+ /** @todo implement temporary file creation (we fend it off above, no
+ * worries). */
+ rc = E_FAIL;
+ }
+
+ return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
+}
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleStat(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
+ { "--file-system", 'f', RTGETOPT_REQ_NOTHING },
+ { "--format", 'c', RTGETOPT_REQ_STRING },
+ { "--terse", 't', RTGETOPT_REQ_NOTHING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion)) != 0
+ && ch != VINF_GETOPT_NOT_OPTION)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case 'L': /* Dereference */
+ case 'f': /* File-system */
+ case 'c': /* Format */
+ case 't': /* Terse */
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_STAT,
+ "Command \"%s\" not implemented yet!", ValueUnion.psz);
+
+ default:
+ return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_STAT, ch, &ValueUnion);
+ }
+ }
+
+ if (ch != VINF_GETOPT_NOT_OPTION)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_STAT, "Nothing to stat!");
+
+ RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+
+ /*
+ * Do the file stat'ing.
+ */
+ while (ch == VINF_GETOPT_NOT_OPTION)
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Checking for element \"%s\" ...\n", ValueUnion.psz);
+
+ ComPtr<IGuestFsObjInfo> pFsObjInfo;
+ HRESULT hrc = pCtx->pGuestSession->FsObjQueryInfo(Bstr(ValueUnion.psz).raw(), FALSE /*followSymlinks*/,
+ pFsObjInfo.asOutParam());
+ if (FAILED(hrc))
+ {
+ /** @todo r=bird: There might be other reasons why we end up here than
+ * non-existing "element" (object or file, please, nobody calls it elements). */
+ if (pCtx->cVerbose)
+ RTPrintf("Failed to stat '%s': No such file\n", ValueUnion.psz);
+ rcExit = RTEXITCODE_FAILURE;
+ }
+ else
+ {
+ RTPrintf(" File: '%s'\n", ValueUnion.psz); /** @todo escape this name. */
+
+ FsObjType_T enmType = FsObjType_Unknown;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(Type)(&enmType));
+ LONG64 cbObject = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(ObjectSize)(&cbObject));
+ LONG64 cbAllocated = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(AllocatedSize)(&cbAllocated));
+ LONG uid = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(UID)(&uid));
+ LONG gid = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(GID)(&gid));
+ Bstr bstrUsername;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(UserName)(bstrUsername.asOutParam()));
+ Bstr bstrGroupName;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(GroupName)(bstrGroupName.asOutParam()));
+ Bstr bstrAttribs;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(FileAttributes)(bstrAttribs.asOutParam()));
+ LONG64 idNode = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(NodeId)(&idNode));
+ ULONG uDevNode = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(NodeIdDevice)(&uDevNode));
+ ULONG uDeviceNo = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(DeviceNumber)(&uDeviceNo));
+ ULONG cHardLinks = 1;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(HardLinks)(&cHardLinks));
+ LONG64 nsBirthTime = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(BirthTime)(&nsBirthTime));
+ LONG64 nsChangeTime = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(ChangeTime)(&nsChangeTime));
+ LONG64 nsModificationTime = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(ModificationTime)(&nsModificationTime));
+ LONG64 nsAccessTime = 0;
+ CHECK_ERROR2I(pFsObjInfo, COMGETTER(AccessTime)(&nsAccessTime));
+
+ RTPrintf(" Size: %-17RU64 Alloc: %-19RU64 Type: %s\n", cbObject, cbAllocated, gctlFsObjTypeToName(enmType));
+ RTPrintf("Device: %#-17RX32 INode: %-18RU64 Links: %u\n", uDevNode, idNode, cHardLinks);
+
+ Utf8Str strAttrib(bstrAttribs);
+ char *pszMode = strAttrib.mutableRaw();
+ char *pszAttribs = strchr(pszMode, ' ');
+ if (pszAttribs)
+ do *pszAttribs++ = '\0';
+ while (*pszAttribs == ' ');
+ else
+ pszAttribs = strchr(pszMode, '\0');
+ if (uDeviceNo != 0)
+ RTPrintf(" Mode: %-16s Attrib: %-17s Dev ID: %#RX32\n", pszMode, pszAttribs, uDeviceNo);
+ else
+ RTPrintf(" Mode: %-16s Attrib: %s\n", pszMode, pszAttribs);
+
+ RTPrintf(" Owner: %4d/%-12ls Group: %4d/%ls\n", uid, bstrUsername.raw(), gid, bstrGroupName.raw());
+
+ RTTIMESPEC TimeSpec;
+ char szTmp[RTTIME_STR_LEN];
+ RTPrintf(" Birth: %s\n", RTTimeSpecToString(RTTimeSpecSetNano(&TimeSpec, nsBirthTime), szTmp, sizeof(szTmp)));
+ RTPrintf("Change: %s\n", RTTimeSpecToString(RTTimeSpecSetNano(&TimeSpec, nsChangeTime), szTmp, sizeof(szTmp)));
+ RTPrintf("Modify: %s\n", RTTimeSpecToString(RTTimeSpecSetNano(&TimeSpec, nsModificationTime), szTmp, sizeof(szTmp)));
+ RTPrintf("Access: %s\n", RTTimeSpecToString(RTTimeSpecSetNano(&TimeSpec, nsAccessTime), szTmp, sizeof(szTmp)));
+
+ /* Skiping: Generation ID - only the ISO9660 VFS sets this. FreeBSD user flags. */
+ }
+
+ /* Next file. */
+ ch = RTGetOpt(&GetState, &ValueUnion);
+ }
+
+ return rcExit;
+}
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleUpdateAdditions(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ /*
+ * Check the syntax. We can deduce the correct syntax from the number of
+ * arguments.
+ */
+ Utf8Str strSource;
+ com::SafeArray<IN_BSTR> aArgs;
+ bool fWaitStartOnly = false;
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--source", 's', RTGETOPT_REQ_STRING },
+ { "--wait-start", 'w', RTGETOPT_REQ_NOTHING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ int vrc = VINF_SUCCESS;
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(vrc))
+ {
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case 's':
+ strSource = ValueUnion.psz;
+ 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 errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_UPDATEGA, ch, &ValueUnion);
+ }
+ }
+
+ if (pCtx->cVerbose)
+ RTPrintf("Updating Guest Additions ...\n");
+
+ HRESULT rc = S_OK;
+ while (strSource.isEmpty())
+ {
+ ComPtr<ISystemProperties> pProperties;
+ CHECK_ERROR_BREAK(pCtx->pArg->virtualBox, COMGETTER(SystemProperties)(pProperties.asOutParam()));
+ Bstr strISO;
+ CHECK_ERROR_BREAK(pProperties, COMGETTER(DefaultAdditionsISO)(strISO.asOutParam()));
+ strSource = strISO;
+ break;
+ }
+
+ /* Determine source if not set yet. */
+ if (strSource.isEmpty())
+ {
+ RTMsgError("No Guest Additions source found or specified, aborting\n");
+ vrc = VERR_FILE_NOT_FOUND;
+ }
+ else if (!RTFileExists(strSource.c_str()))
+ {
+ RTMsgError("Source \"%s\" does not exist!\n", strSource.c_str());
+ vrc = VERR_FILE_NOT_FOUND;
+ }
+
+ if (RT_SUCCESS(vrc))
+ {
+ if (pCtx->cVerbose)
+ RTPrintf("Using source: %s\n", strSource.c_str());
+
+
+ RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+
+ com::SafeArray<AdditionsUpdateFlag_T> aUpdateFlags;
+ if (fWaitStartOnly)
+ {
+ aUpdateFlags.push_back(AdditionsUpdateFlag_WaitForUpdateStartOnly);
+ if (pCtx->cVerbose)
+ RTPrintf("Preparing and waiting for Guest Additions installer to start ...\n");
+ }
+
+ ComPtr<IProgress> pProgress;
+ 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 = gctlPrintError(pCtx->pGuest, COM_IIDOF(IGuest));
+ else
+ {
+ if (pCtx->cVerbose)
+ rc = showProgress(pProgress);
+ else
+ rc = pProgress->WaitForCompletion(-1 /* No timeout */);
+
+ if (SUCCEEDED(rc))
+ CHECK_PROGRESS_ERROR(pProgress, ("Guest additions update failed"));
+ vrc = gctlPrintProgressError(pProgress);
+ if ( RT_SUCCESS(vrc)
+ && pCtx->cVerbose)
+ {
+ RTPrintf("Guest Additions update successful\n");
+ }
+ }
+ }
+
+ return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleList(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ bool fSeenListArg = false;
+ bool fListAll = false;
+ bool fListSessions = false;
+ bool fListProcesses = false;
+ bool fListFiles = false;
+
+ int vrc = VINF_SUCCESS;
+ while ( (ch = RTGetOpt(&GetState, &ValueUnion))
+ && RT_SUCCESS(vrc))
+ {
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case VINF_GETOPT_NOT_OPTION:
+ if ( !RTStrICmp(ValueUnion.psz, "sessions")
+ || !RTStrICmp(ValueUnion.psz, "sess"))
+ fListSessions = true;
+ else if ( !RTStrICmp(ValueUnion.psz, "processes")
+ || !RTStrICmp(ValueUnion.psz, "procs"))
+ fListSessions = fListProcesses = true; /* Showing processes implies showing sessions. */
+ else if (!RTStrICmp(ValueUnion.psz, "files"))
+ fListSessions = fListFiles = true; /* Showing files implies showing sessions. */
+ else if (!RTStrICmp(ValueUnion.psz, "all"))
+ fListAll = true;
+ else
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_LIST,
+ "Unknown list: '%s'", ValueUnion.psz);
+ fSeenListArg = true;
+ break;
+
+ default:
+ return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_UPDATEGA, ch, &ValueUnion);
+ }
+ }
+
+ if (!fSeenListArg)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_LIST, "Missing list name");
+ Assert(fListAll || fListSessions);
+
+ RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+
+ /** @todo Do we need a machine-readable output here as well? */
+
+ HRESULT rc;
+ size_t cTotalProcs = 0;
+ size_t cTotalFiles = 0;
+
+ SafeIfaceArray <IGuestSession> collSessions;
+ CHECK_ERROR(pCtx->pGuest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions)));
+ if (SUCCEEDED(rc))
+ {
+ size_t const 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())
+ {
+ do
+ {
+ 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(), gctlGuestSessionStatusToText(sessionStatus), strName.raw());
+ } while (0);
+
+ 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())
+ {
+ do
+ {
+ 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, gctlProcessStatusToText(procStatus), strExecPath.raw());
+ } while (0);
+ }
+ }
+
+ 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())
+ {
+ do
+ {
+ ULONG idFile;
+ CHECK_ERROR_BREAK(pCurFile, COMGETTER(Id)(&idFile));
+ Bstr strName;
+ 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, idFile, gctlFileStatusToText(fileStatus), strName.raw());
+ } while (0);
+ }
+ }
+
+ 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");
+ }
+
+ if (FAILED(rc)) /** @todo yeah, right... Only the last error? */
+ rcExit = RTEXITCODE_FAILURE;
+
+ return rcExit;
+}
+
+static DECLCALLBACK(RTEXITCODE) gctlHandleCloseProcess(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--session-id", 'i', RTGETOPT_REQ_UINT32 },
+ { "--session-name", 'n', RTGETOPT_REQ_STRING }
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ std::vector < uint32_t > vecPID;
+ ULONG idSession = UINT32_MAX;
+ Utf8Str strSessionName;
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case 'n': /* Session name (or pattern) */
+ strSessionName = ValueUnion.psz;
+ break;
+
+ case 'i': /* Session ID */
+ idSession = ValueUnion.u32;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ /* Treat every else specified as a PID to kill. */
+ uint32_t uPid;
+ int rc = RTStrToUInt32Ex(ValueUnion.psz, NULL, 0, &uPid);
+ if ( RT_SUCCESS(rc)
+ && rc != VWRN_TRAILING_CHARS
+ && rc != VWRN_NUMBER_TOO_BIG
+ && rc != VWRN_NEGATIVE_UNSIGNED)
+ {
+ if (uPid != 0)
+ {
+ try
+ {
+ vecPID.push_back(uPid);
+ }
+ catch (std::bad_alloc &)
+ {
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory");
+ }
+ }
+ else
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CLOSEPROCESS, "Invalid PID value: 0");
+ }
+ else
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CLOSEPROCESS, "Error parsing PID value: %Rrc", rc);
+ break;
+ }
+
+ default:
+ return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CLOSEPROCESS, ch, &ValueUnion);
+ }
+ }
+
+ if (vecPID.empty())
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CLOSEPROCESS,
+ "At least one PID must be specified to kill!");
+
+ if ( strSessionName.isEmpty()
+ && idSession == UINT32_MAX)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CLOSEPROCESS, "No session ID specified!");
+
+ if ( strSessionName.isNotEmpty()
+ && idSession != UINT32_MAX)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CLOSEPROCESS,
+ "Either session ID or name (pattern) must be specified");
+
+ RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ HRESULT rc = S_OK;
+
+ ComPtr<IGuestSession> pSession;
+ ComPtr<IGuestProcess> pProcess;
+ do
+ {
+ uint32_t uProcsTerminated = 0;
+
+ SafeIfaceArray <IGuestSession> collSessions;
+ CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(Sessions)(ComSafeArrayAsOutParam(collSessions)));
+ size_t cSessions = collSessions.size();
+
+ uint32_t cSessionsHandled = 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 */
+
+ bool fSessionFound;
+ if (strSessionName.isEmpty()) /* Search by ID. Slow lookup. */
+ fSessionFound = uID == idSession;
+ else /* ... or by naming pattern. */
+ fSessionFound = RTStrSimplePatternMatch(strSessionName.c_str(), strNameUtf8.c_str());
+ if (fSessionFound)
+ {
+ AssertStmt(!pSession.isNull(), break);
+ cSessionsHandled++;
+
+ 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->cVerbose)
+ RTPrintf("Terminating process (PID %RU32) (session ID %RU32) ...\n",
+ uPID, uID);
+ CHECK_ERROR_BREAK(pProcess, Terminate());
+ uProcsTerminated++;
+ }
+ else
+ {
+ if (idSession != UINT32_MAX)
+ RTPrintf("No matching process(es) for session ID %RU32 found\n",
+ idSession);
+ }
+
+ pProcess.setNull();
+ }
+
+ pSession.setNull();
+ }
+ }
+
+ if (!cSessionsHandled)
+ 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) gctlHandleCloseSession(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ enum GETOPTDEF_SESSIONCLOSE
+ {
+ GETOPTDEF_SESSIONCLOSE_ALL = 2000
+ };
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ { "--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, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ ULONG idSession = UINT32_MAX;
+ Utf8Str strSessionName;
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case 'n': /* Session name pattern */
+ strSessionName = ValueUnion.psz;
+ break;
+
+ case 'i': /* Session ID */
+ idSession = 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_CLOSESESSION, ch, &ValueUnion);
+ }
+ }
+
+ if ( strSessionName.isEmpty()
+ && idSession == UINT32_MAX)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CLOSESESSION,
+ "No session ID specified!");
+
+ if ( !strSessionName.isEmpty()
+ && idSession != UINT32_MAX)
+ return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CLOSESESSION,
+ "Either session ID or name (pattern) must be specified");
+
+ RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ HRESULT rc = S_OK;
+
+ do
+ {
+ 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 */
+
+ bool fSessionFound;
+ if (strSessionName.isEmpty()) /* Search by ID. Slow lookup. */
+ fSessionFound = uID == idSession;
+ else /* ... or by naming pattern. */
+ fSessionFound = RTStrSimplePatternMatch(strSessionName.c_str(), strNameUtf8.c_str());
+ if (fSessionFound)
+ {
+ cSessionsHandled++;
+
+ Assert(!pSession.isNull());
+ if (pCtx->cVerbose)
+ RTPrintf("Closing guest session ID=#%RU32 \"%s\" ...\n",
+ uID, strNameUtf8.c_str());
+ CHECK_ERROR_BREAK(pSession, Close());
+ if (pCtx->cVerbose)
+ 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) gctlHandleWatch(PGCTLCMDCTX pCtx, int argc, char **argv)
+{
+ AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
+
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ GCTLCMD_COMMON_OPTION_DEFS()
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ /* For options that require an argument, ValueUnion has received the value. */
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
+
+ case VINF_GETOPT_NOT_OPTION:
+ 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? */
+
+ RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+
+ 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->cVerbose)
+ RTPrintf("Waiting for events ...\n");
+
+/** @todo r=bird: This are-we-there-yet approach to things could easily be
+ * replaced by a global event semaphore that gets signalled from the
+ * signal handler and the callback event. Please fix! */
+ while (!g_fGuestCtrlCanceled)
+ {
+ /** @todo Timeout handling (see above)? */
+ RTThreadSleep(10);
+ }
+
+ if (pCtx->cVerbose)
+ 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.
+ *
+ * @returns program exit code.
+ * @note see the command line API description for parameters
+ */
+RTEXITCODE handleGuestControl(HandlerArg *pArg)
+{
+ AssertPtr(pArg);
+
+#ifdef DEBUG_andy_disabled
+ if (RT_FAILURE(tstTranslatePath()))
+ return RTEXITCODE_FAILURE;
+#endif
+
+ /*
+ * Command definitions.
+ */
+ static const GCTLCMDDEF s_aCmdDefs[] =
+ {
+ { "run", gctlHandleRun, USAGE_GSTCTRL_RUN, 0, },
+ { "start", gctlHandleStart, USAGE_GSTCTRL_START, 0, },
+ { "copyfrom", gctlHandleCopyFrom, USAGE_GSTCTRL_COPYFROM, 0, },
+ { "copyto", gctlHandleCopyTo, USAGE_GSTCTRL_COPYTO, 0, },
+
+ { "mkdir", gctrlHandleMkDir, USAGE_GSTCTRL_MKDIR, 0, },
+ { "md", gctrlHandleMkDir, USAGE_GSTCTRL_MKDIR, 0, },
+ { "createdirectory", gctrlHandleMkDir, USAGE_GSTCTRL_MKDIR, 0, },
+ { "createdir", gctrlHandleMkDir, USAGE_GSTCTRL_MKDIR, 0, },
+
+ { "rmdir", gctlHandleRmDir, USAGE_GSTCTRL_RMDIR, 0, },
+ { "removedir", gctlHandleRmDir, USAGE_GSTCTRL_RMDIR, 0, },
+ { "removedirectory", gctlHandleRmDir, USAGE_GSTCTRL_RMDIR, 0, },
+
+ { "rm", gctlHandleRm, USAGE_GSTCTRL_RM, 0, },
+ { "removefile", gctlHandleRm, USAGE_GSTCTRL_RM, 0, },
+ { "erase", gctlHandleRm, USAGE_GSTCTRL_RM, 0, },
+ { "del", gctlHandleRm, USAGE_GSTCTRL_RM, 0, },
+ { "delete", gctlHandleRm, USAGE_GSTCTRL_RM, 0, },
+
+ { "mv", gctlHandleMv, USAGE_GSTCTRL_MV, 0, },
+ { "move", gctlHandleMv, USAGE_GSTCTRL_MV, 0, },
+ { "ren", gctlHandleMv, USAGE_GSTCTRL_MV, 0, },
+ { "rename", gctlHandleMv, USAGE_GSTCTRL_MV, 0, },
+
+ { "mktemp", gctlHandleMkTemp, USAGE_GSTCTRL_MKTEMP, 0, },
+ { "createtemp", gctlHandleMkTemp, USAGE_GSTCTRL_MKTEMP, 0, },
+ { "createtemporary", gctlHandleMkTemp, USAGE_GSTCTRL_MKTEMP, 0, },
+
+ { "stat", gctlHandleStat, USAGE_GSTCTRL_STAT, 0, },
+
+ { "closeprocess", gctlHandleCloseProcess, USAGE_GSTCTRL_CLOSEPROCESS, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
+ { "closesession", gctlHandleCloseSession, USAGE_GSTCTRL_CLOSESESSION, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
+ { "list", gctlHandleList, USAGE_GSTCTRL_LIST, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
+ { "watch", gctlHandleWatch, USAGE_GSTCTRL_WATCH, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
+
+ {"updateguestadditions",gctlHandleUpdateAdditions, USAGE_GSTCTRL_UPDATEGA, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
+ { "updateadditions", gctlHandleUpdateAdditions, USAGE_GSTCTRL_UPDATEGA, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
+ { "updatega", gctlHandleUpdateAdditions, USAGE_GSTCTRL_UPDATEGA, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
+ };
+
+ /*
+ * VBoxManage guestcontrol [common-options] <VM> [common-options] <sub-command> ...
+ *
+ * Parse common options and VM name until we find a sub-command. Allowing
+ * the user to put the user and password related options before the
+ * sub-command makes it easier to edit the command line when doing several
+ * operations with the same guest user account. (Accidentally, it also
+ * makes the syntax diagram shorter and easier to read.)
+ */
+ GCTLCMDCTX CmdCtx;
+ RTEXITCODE rcExit = gctrCmdCtxInit(&CmdCtx, pArg);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ static const RTGETOPTDEF s_CommonOptions[] = { GCTLCMD_COMMON_OPTION_DEFS() };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_CommonOptions, RT_ELEMENTS(s_CommonOptions), 0, 0 /* No sorting! */);
+
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ GCTLCMD_COMMON_OPTION_CASES(&CmdCtx, ch, &ValueUnion);
+
+ case VINF_GETOPT_NOT_OPTION:
+ /* First comes the VM name or UUID. */
+ if (!CmdCtx.pszVmNameOrUuid)
+ CmdCtx.pszVmNameOrUuid = ValueUnion.psz;
+ /*
+ * The sub-command is next. Look it up and invoke it.
+ * Note! Currently no warnings about user/password options (like we'll do later on)
+ * for GCTLCMDCTX_F_SESSION_ANONYMOUS commands. No reason to be too pedantic.
+ */
+ else
+ {
+ const char *pszCmd = ValueUnion.psz;
+ uint32_t iCmd;
+ for (iCmd = 0; iCmd < RT_ELEMENTS(s_aCmdDefs); iCmd++)
+ if (strcmp(s_aCmdDefs[iCmd].pszName, pszCmd) == 0)
+ {
+ CmdCtx.pCmdDef = &s_aCmdDefs[iCmd];
+
+ rcExit = s_aCmdDefs[iCmd].pfnHandler(&CmdCtx, pArg->argc - GetState.iNext + 1,
+ &pArg->argv[GetState.iNext - 1]);
+
+ gctlCtxTerm(&CmdCtx);
+ return rcExit;
+ }
+ return errorSyntax(USAGE_GUESTCONTROL, "Unknown sub-command: '%s'", pszCmd);
+ }
+ break;
+
+ default:
+ return errorGetOpt(USAGE_GUESTCONTROL, ch, &ValueUnion);
+ }
+ }
+ if (CmdCtx.pszVmNameOrUuid)
+ rcExit = errorSyntax(USAGE_GUESTCONTROL, "Missing sub-command");
+ else
+ rcExit = errorSyntax(USAGE_GUESTCONTROL, "Missing VM name and sub-command");
+ }
+ 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..016fa548
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.h
@@ -0,0 +1,236 @@
+/* $Id: VBoxManageGuestCtrl.h $ */
+/** @file
+ * VBoxManageGuestCtrl.h - Definitions for guest control.
+ */
+
+/*
+ * Copyright (C) 2013-2019 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 VBOX_INCLUDED_SRC_VBoxManage_VBoxManageGuestCtrl_h
+#define VBOX_INCLUDED_SRC_VBoxManage_VBoxManageGuestCtrl_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#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 *gctlFileStatusToText(FileStatus_T enmStatus);
+const char *gctlProcessStatusToText(ProcessStatus_T enmStatus);
+const char *gctlGuestSessionStatusToText(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 /* !VBOX_INCLUDED_SRC_VBoxManage_VBoxManageGuestCtrl_h */
+
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrlListener.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrlListener.cpp
new file mode 100644
index 00000000..20603474
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrlListener.cpp
@@ -0,0 +1,518 @@
+/* $Id: VBoxManageGuestCtrlListener.cpp $ */
+/** @file
+ * VBoxManage - Guest control listener implementations.
+ */
+
+/*
+ * Copyright (C) 2013-2019 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
+ * GuestListenerBase
+ */
+
+GuestListenerBase::GuestListenerBase(void)
+ : mfVerbose(false)
+{
+}
+
+GuestListenerBase::~GuestListenerBase(void)
+{
+}
+
+HRESULT GuestListenerBase::init(bool fVerbose)
+{
+ mfVerbose = fVerbose;
+ return S_OK;
+}
+
+
+
+/*
+ * GuestFileEventListener
+ * GuestFileEventListener
+ * GuestFileEventListener
+ */
+
+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(), gctlFileStatusToText(fileSts));
+
+ } while (0);
+ break;
+ }
+
+ default:
+ AssertFailed();
+ }
+
+ return S_OK;
+}
+
+
+/*
+ * GuestProcessEventListener
+ * GuestProcessEventListener
+ * GuestProcessEventListener
+ */
+
+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(), gctlProcessStatusToText(procSts));
+
+ } while (0);
+ break;
+ }
+
+ default:
+ AssertFailed();
+ }
+
+ return S_OK;
+}
+
+
+/*
+ * GuestSessionEventListener
+ * GuestSessionEventListener
+ * GuestSessionEventListener
+ */
+
+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(), gctlGuestSessionStatusToText(sessSts));
+
+ } while (0);
+ break;
+ }
+
+ default:
+ AssertFailed();
+ }
+
+ return S_OK;
+}
+
+
+/*
+ * GuestEventListener
+ * GuestEventListener
+ * GuestEventListener
+ */
+
+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
new file mode 100644
index 00000000..36ccc472
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageGuestProp.cpp
@@ -0,0 +1,437 @@
+/* $Id: VBoxManageGuestProp.cpp $ */
+/** @file
+ * VBoxManage - Implementation of guestproperty command.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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"
+
+#ifndef VBOX_ONLY_DOCS
+
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/com/VirtualBox.h>
+
+#include <VBox/log.h>
+#include <iprt/asm.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+
+#ifdef USE_XPCOM_QUEUE
+# include <sys/select.h>
+# include <errno.h>
+#endif
+
+#ifdef RT_OS_DARWIN
+# include <CoreFoundation/CFRunLoop.h>
+#endif
+
+using namespace com;
+
+#endif /* !VBOX_ONLY_DOCS */
+
+void usageGuestProperty(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2)
+{
+ RTStrmPrintf(pStrm,
+ "%s guestproperty %s get <uuid|vmname>\n"
+ " <property> [--verbose]\n"
+ "\n", pcszSep1, pcszSep2);
+ RTStrmPrintf(pStrm,
+ "%s guestproperty %s set <uuid|vmname>\n"
+ " <property> [<value> [--flags <flags>]]\n"
+ "\n", pcszSep1, pcszSep2);
+ RTStrmPrintf(pStrm,
+ "%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 <uuid|vmname> <patterns>\n"
+ " [--timeout <msec>] [--fail-on-timeout]\n"
+ "\n", pcszSep1, pcszSep2);
+}
+
+#ifndef VBOX_ONLY_DOCS
+
+static RTEXITCODE handleGetGuestProperty(HandlerArg *a)
+{
+ HRESULT rc = S_OK;
+
+ bool verbose = false;
+ if ( a->argc == 3
+ && ( !strcmp(a->argv[2], "--verbose")
+ || !strcmp(a->argv[2], "-verbose")))
+ verbose = true;
+ else if (a->argc != 2)
+ return errorSyntax(USAGE_GUESTPROPERTY, "Incorrect parameters");
+
+ 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), RTEXITCODE_FAILURE);
+
+ /* get the mutable session machine */
+ a->session->COMGETTER(Machine)(machine.asOutParam());
+
+ Bstr value;
+ LONG64 i64Timestamp;
+ Bstr flags;
+ CHECK_ERROR(machine, GetGuestProperty(Bstr(a->argv[1]).raw(),
+ value.asOutParam(),
+ &i64Timestamp, flags.asOutParam()));
+ if (value.isEmpty())
+ RTPrintf("No value set!\n");
+ else
+ RTPrintf("Value: %ls\n", value.raw());
+ if (!value.isEmpty() && verbose)
+ {
+ RTPrintf("Timestamp: %lld\n", i64Timestamp);
+ RTPrintf("Flags: %ls\n", flags.raw());
+ }
+ }
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static RTEXITCODE handleSetGuestProperty(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;
+ const char *pszValue = NULL;
+ const char *pszFlags = NULL;
+ if (a->argc == 3)
+ pszValue = a->argv[2];
+ else if (a->argc == 4)
+ usageOK = false;
+ else if (a->argc == 5)
+ {
+ pszValue = a->argv[2];
+ if ( strcmp(a->argv[3], "--flags")
+ && strcmp(a->argv[3], "-flags"))
+ usageOK = false;
+ pszFlags = a->argv[4];
+ }
+ else 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), RTEXITCODE_FAILURE);
+
+ /* get the mutable session machine */
+ a->session->COMGETTER(Machine)(machine.asOutParam());
+
+ if (!pszFlags)
+ CHECK_ERROR(machine, SetGuestPropertyValue(Bstr(pszName).raw(),
+ Bstr(pszValue).raw()));
+ else
+ CHECK_ERROR(machine, SetGuestProperty(Bstr(pszName).raw(),
+ Bstr(pszValue).raw(),
+ Bstr(pszFlags).raw()));
+
+ if (SUCCEEDED(rc))
+ CHECK_ERROR(machine, SaveSettings());
+
+ a->session->UnlockMachine();
+ }
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static RTEXITCODE 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), RTEXITCODE_FAILURE);
+
+ /* 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) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+/**
+ * Enumerates the properties in the guest property store.
+ *
+ * @returns 0 on success, 1 on failure
+ * @note see the command line API description for parameters
+ */
+static RTEXITCODE handleEnumGuestProperty(HandlerArg *a)
+{
+ /*
+ * Check the syntax. We can deduce the correct syntax from the number of
+ * arguments.
+ */
+ if ( a->argc < 1
+ || a->argc == 2
+ || ( a->argc > 3
+ && strcmp(a->argv[1], "--patterns")
+ && strcmp(a->argv[1], "-patterns")))
+ return errorSyntax(USAGE_GUESTPROPERTY, "Incorrect parameters");
+
+ /*
+ * Pack the patterns
+ */
+ Utf8Str Utf8Patterns(a->argc > 2 ? a->argv[2] : "");
+ for (int i = 3; i < a->argc; ++i)
+ Utf8Patterns = Utf8StrFmt ("%s,%s", Utf8Patterns.c_str(), a->argv[i]);
+
+ /*
+ * Make the actual call to Main.
+ */
+ ComPtr<IMachine> machine;
+ HRESULT rc;
+ 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), RTEXITCODE_FAILURE);
+
+ /* get the mutable session machine */
+ a->session->COMGETTER(Machine)(machine.asOutParam());
+
+ com::SafeArray<BSTR> names;
+ com::SafeArray<BSTR> values;
+ com::SafeArray<LONG64> timestamps;
+ com::SafeArray<BSTR> flags;
+ CHECK_ERROR(machine, EnumerateGuestProperties(Bstr(Utf8Patterns).raw(),
+ ComSafeArrayAsOutParam(names),
+ ComSafeArrayAsOutParam(values),
+ ComSafeArrayAsOutParam(timestamps),
+ ComSafeArrayAsOutParam(flags)));
+ if (SUCCEEDED(rc))
+ {
+ if (names.size() == 0)
+ RTPrintf("No properties found.\n");
+ for (unsigned i = 0; i < names.size(); ++i)
+ RTPrintf("Name: %ls, value: %ls, timestamp: %lld, flags: %ls\n",
+ names[i], values[i], timestamps[i], flags[i]);
+ }
+ }
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+/**
+ * Enumerates the properties in the guest property store.
+ *
+ * @returns 0 on success, 1 on failure
+ * @note see the command line API description for parameters
+ */
+static RTEXITCODE handleWaitGuestProperty(HandlerArg *a)
+{
+ /*
+ * Handle arguments
+ */
+ bool fFailOnTimeout = false;
+ const char *pszPatterns = NULL;
+ uint32_t cMsTimeout = RT_INDEFINITE_WAIT;
+ bool usageOK = true;
+ if (a->argc < 2)
+ usageOK = false;
+ else
+ pszPatterns = a->argv[1];
+ ComPtr<IMachine> machine;
+ HRESULT rc;
+ CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()));
+ if (!machine)
+ usageOK = false;
+ for (int i = 2; usageOK && i < a->argc; ++i)
+ {
+ if ( !strcmp(a->argv[i], "--timeout")
+ || !strcmp(a->argv[i], "-timeout"))
+ {
+ if ( i + 1 >= a->argc
+ || RTStrToUInt32Full(a->argv[i + 1], 10, &cMsTimeout) != VINF_SUCCESS)
+ usageOK = false;
+ else
+ ++i;
+ }
+ else if (!strcmp(a->argv[i], "--fail-on-timeout"))
+ fFailOnTimeout = true;
+ else
+ usageOK = false;
+ }
+ if (!usageOK)
+ return errorSyntax(USAGE_GUESTPROPERTY, "Incorrect parameters");
+
+ /*
+ * Set up the event listener and wait until found match or timeout.
+ */
+ Bstr aMachStrGuid;
+ machine->COMGETTER(Id)(aMachStrGuid.asOutParam());
+ Guid aMachGuid(aMachStrGuid);
+ ComPtr<IEventSource> es;
+ CHECK_ERROR(a->virtualBox, COMGETTER(EventSource)(es.asOutParam()));
+ ComPtr<IEventListener> listener;
+ CHECK_ERROR(es, CreateListener(listener.asOutParam()));
+ com::SafeArray <VBoxEventType_T> eventTypes(1);
+ eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);
+ CHECK_ERROR(es, RegisterListener(listener, ComSafeArrayAsInParam(eventTypes), false));
+
+ uint64_t u64Started = RTTimeMilliTS();
+ bool fSignalled = false;
+ do
+ {
+ unsigned cMsWait;
+ if (cMsTimeout == RT_INDEFINITE_WAIT)
+ cMsWait = 1000;
+ else
+ {
+ uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
+ if (cMsElapsed >= cMsTimeout)
+ break; /* timed out */
+ cMsWait = RT_MIN(1000, cMsTimeout - (uint32_t)cMsElapsed);
+ }
+
+ ComPtr<IEvent> ev;
+ rc = es->GetEvent(listener, cMsWait, ev.asOutParam());
+ if (ev)
+ {
+ VBoxEventType_T aType;
+ rc = ev->COMGETTER(Type)(&aType);
+ switch (aType)
+ {
+ case VBoxEventType_OnGuestPropertyChanged:
+ {
+ ComPtr<IGuestPropertyChangedEvent> gpcev = ev;
+ Assert(gpcev);
+ Bstr aNextStrGuid;
+ gpcev->COMGETTER(MachineId)(aNextStrGuid.asOutParam());
+ if (aMachGuid != Guid(aNextStrGuid))
+ continue;
+ Bstr aNextName;
+ gpcev->COMGETTER(Name)(aNextName.asOutParam());
+ if (RTStrSimplePatternMultiMatch(pszPatterns, RTSTR_MAX,
+ Utf8Str(aNextName).c_str(), RTSTR_MAX, NULL))
+ {
+ Bstr aNextValue, aNextFlags;
+ gpcev->COMGETTER(Value)(aNextValue.asOutParam());
+ gpcev->COMGETTER(Flags)(aNextFlags.asOutParam());
+ RTPrintf("Name: %ls, value: %ls, flags: %ls\n",
+ aNextName.raw(), aNextValue.raw(), aNextFlags.raw());
+ fSignalled = true;
+ }
+ break;
+ }
+ default:
+ AssertFailed();
+ }
+ }
+ } while (!fSignalled);
+
+ es->UnregisterListener(listener);
+
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ if (!fSignalled)
+ {
+ RTMsgError("Time out or interruption while waiting for a notification.");
+ if (fFailOnTimeout)
+ /* Hysterical rasins: We always returned 2 here, which now translates to syntax error... Which is bad. */
+ rcExit = RTEXITCODE_SYNTAX;
+ }
+ return rcExit;
+}
+
+/**
+ * Access the guest property store.
+ *
+ * @returns 0 on success, 1 on failure
+ * @note see the command line API description for parameters
+ */
+RTEXITCODE handleGuestProperty(HandlerArg *a)
+{
+ HandlerArg arg = *a;
+ arg.argc = a->argc - 1;
+ arg.argv = a->argv + 1;
+
+ /** @todo This command does not follow the syntax where the <uuid|vmname>
+ * comes between the command and subcommand. The commands controlvm,
+ * snapshot and debugvm puts it between.
+ */
+
+ if (a->argc == 0)
+ return errorSyntax(USAGE_GUESTPROPERTY, "Incorrect parameters");
+
+ /* switch (cmd) */
+ if (strcmp(a->argv[0], "get") == 0)
+ 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)
+ return handleWaitGuestProperty(&arg);
+
+ /* default: */
+ return errorSyntax(USAGE_GUESTPROPERTY, "Incorrect parameters");
+}
+
+#endif /* !VBOX_ONLY_DOCS */
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp
new file mode 100644
index 00000000..4b8c7390
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp
@@ -0,0 +1,1404 @@
+/* $Id: VBoxManageHelp.cpp $ */
+/** @file
+ * VBoxManage - help and other message output.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 <VBox/version.h>
+
+#include <iprt/buildconfig.h>
+#include <iprt/ctype.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+#include <iprt/message.h>
+
+#include "VBoxManage.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** If the usage is the given number of length long or longer, the error is
+ * repeated so the user can actually see it. */
+#define ERROR_REPEAT_AFTER_USAGE_LENGTH 16
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifndef VBOX_ONLY_DOCS
+enum HELP_CMD_VBOXMANAGE g_enmCurCommand = HELP_CMD_VBOXMANAGE_INVALID;
+/** The scope maskt for the current subcommand. */
+uint64_t g_fCurSubcommandScope = RTMSGREFENTRYSTR_SCOPE_GLOBAL;
+
+/**
+ * Sets the current command.
+ *
+ * This affects future calls to error and help functions.
+ *
+ * @param enmCommand The command.
+ */
+void setCurrentCommand(enum HELP_CMD_VBOXMANAGE enmCommand)
+{
+ Assert(g_enmCurCommand == HELP_CMD_VBOXMANAGE_INVALID);
+ g_enmCurCommand = enmCommand;
+ g_fCurSubcommandScope = RTMSGREFENTRYSTR_SCOPE_GLOBAL;
+}
+
+
+/**
+ * Sets the current subcommand.
+ *
+ * This affects future calls to error and help functions.
+ *
+ * @param fSubcommandScope The subcommand scope.
+ */
+void setCurrentSubcommand(uint64_t fSubcommandScope)
+{
+ g_fCurSubcommandScope = fSubcommandScope;
+}
+
+
+
+
+/**
+ * Prints brief help for a command or subcommand.
+ *
+ * @returns Number of lines written.
+ * @param enmCommand The command.
+ * @param fSubcommandScope The subcommand scope, REFENTRYSTR_SCOPE_GLOBAL
+ * for all.
+ * @param pStrm The output stream.
+ */
+static uint32_t printBriefCommandOrSubcommandHelp(enum HELP_CMD_VBOXMANAGE enmCommand, uint64_t fSubcommandScope, PRTSTREAM pStrm)
+{
+ uint32_t cLinesWritten = 0;
+ uint32_t cPendingBlankLines = 0;
+ uint32_t cFound = 0;
+ for (uint32_t i = 0; i < g_cHelpEntries; i++)
+ {
+ PCRTMSGREFENTRY pHelp = g_apHelpEntries[i];
+ if (pHelp->idInternal == (int64_t)enmCommand)
+ {
+ cFound++;
+ if (cFound == 1)
+ {
+ if (fSubcommandScope == RTMSGREFENTRYSTR_SCOPE_GLOBAL)
+ RTStrmPrintf(pStrm, "Usage - %c%s:\n", RT_C_TO_UPPER(pHelp->pszBrief[0]), pHelp->pszBrief + 1);
+ else
+ RTStrmPrintf(pStrm, "Usage:\n");
+ }
+ RTMsgRefEntryPrintStringTable(pStrm, &pHelp->Synopsis, fSubcommandScope, &cPendingBlankLines, &cLinesWritten);
+ if (!cPendingBlankLines)
+ cPendingBlankLines = 1;
+ }
+ }
+ Assert(cFound > 0);
+ return cLinesWritten;
+}
+
+
+/**
+ * Prints the brief usage information for the current (sub)command.
+ *
+ * @param pStrm The output stream.
+ */
+void printUsage(PRTSTREAM pStrm)
+{
+ printBriefCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, pStrm);
+}
+
+
+/**
+ * Prints full help for a command or subcommand.
+ *
+ * @param enmCommand The command.
+ * @param fSubcommandScope The subcommand scope, REFENTRYSTR_SCOPE_GLOBAL
+ * for all.
+ * @param pStrm The output stream.
+ */
+static void printFullCommandOrSubcommandHelp(enum HELP_CMD_VBOXMANAGE enmCommand, uint64_t fSubcommandScope, PRTSTREAM pStrm)
+{
+ uint32_t cPendingBlankLines = 0;
+ uint32_t cFound = 0;
+ for (uint32_t i = 0; i < g_cHelpEntries; i++)
+ {
+ PCRTMSGREFENTRY pHelp = g_apHelpEntries[i];
+ if ( pHelp->idInternal == (int64_t)enmCommand
+ || enmCommand == HELP_CMD_VBOXMANAGE_INVALID)
+ {
+ cFound++;
+ RTMsgRefEntryPrintStringTable(pStrm, &pHelp->Help, fSubcommandScope, &cPendingBlankLines, NULL /*pcLinesWritten*/);
+ if (cPendingBlankLines < 2)
+ cPendingBlankLines = 2;
+ }
+ }
+ Assert(cFound > 0);
+}
+
+
+/**
+ * Prints the full help for the current (sub)command.
+ *
+ * @param pStrm The output stream.
+ */
+void printHelp(PRTSTREAM pStrm)
+{
+ printFullCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, pStrm);
+}
+
+
+/**
+ * Display no subcommand error message and current command usage.
+ *
+ * @returns RTEXITCODE_SYNTAX.
+ */
+RTEXITCODE errorNoSubcommand(void)
+{
+ Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
+ Assert(g_fCurSubcommandScope == RTMSGREFENTRYSTR_SCOPE_GLOBAL);
+
+ return errorSyntax("No subcommand specified");
+}
+
+
+/**
+ * Display unknown subcommand error message and current command usage.
+ *
+ * May show full command help instead if the subcommand is a common help option.
+ *
+ * @returns RTEXITCODE_SYNTAX, or RTEXITCODE_SUCCESS if common help option.
+ * @param pszSubcommand The name of the alleged subcommand.
+ */
+RTEXITCODE errorUnknownSubcommand(const char *pszSubcommand)
+{
+ Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
+ Assert(g_fCurSubcommandScope == RTMSGREFENTRYSTR_SCOPE_GLOBAL);
+
+ /* check if help was requested. */
+ if ( strcmp(pszSubcommand, "--help") == 0
+ || strcmp(pszSubcommand, "-h") == 0
+ || strcmp(pszSubcommand, "-?") == 0)
+ {
+ printFullCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdOut);
+ return RTEXITCODE_SUCCESS;
+ }
+
+ return errorSyntax("Unknown subcommand: %s", pszSubcommand);
+}
+
+
+/**
+ * Display too many parameters error message and current command usage.
+ *
+ * May show full command help instead if the subcommand is a common help option.
+ *
+ * @returns RTEXITCODE_SYNTAX, or RTEXITCODE_SUCCESS if common help option.
+ * @param papszArgs The first unwanted parameter. Terminated by
+ * NULL entry.
+ */
+RTEXITCODE errorTooManyParameters(char **papszArgs)
+{
+ Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
+ Assert(g_fCurSubcommandScope != RTMSGREFENTRYSTR_SCOPE_GLOBAL);
+
+ /* check if help was requested. */
+ if (papszArgs)
+ {
+ for (uint32_t i = 0; papszArgs[i]; i++)
+ if ( strcmp(papszArgs[i], "--help") == 0
+ || strcmp(papszArgs[i], "-h") == 0
+ || strcmp(papszArgs[i], "-?") == 0)
+ {
+ printFullCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdOut);
+ return RTEXITCODE_SUCCESS;
+ }
+ else if (!strcmp(papszArgs[i], "--"))
+ break;
+ }
+
+ return errorSyntax("Too many parameters");
+}
+
+
+/**
+ * Display current (sub)command usage and the custom error message.
+ *
+ * @returns RTEXITCODE_SYNTAX.
+ * @param pszFormat Custom error message format string.
+ * @param ... Format arguments.
+ */
+RTEXITCODE errorSyntax(const char *pszFormat, ...)
+{
+ Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
+
+ showLogo(g_pStdErr);
+
+ va_list va;
+ va_start(va, pszFormat);
+ RTMsgErrorV(pszFormat, va);
+ va_end(va);
+
+ RTStrmPutCh(g_pStdErr, '\n');
+ if ( printBriefCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdErr)
+ >= ERROR_REPEAT_AFTER_USAGE_LENGTH)
+ {
+ /* Usage was very long, repeat the error message. */
+ RTStrmPutCh(g_pStdErr, '\n');
+ va_start(va, pszFormat);
+ RTMsgErrorV(pszFormat, va);
+ va_end(va);
+ }
+ return RTEXITCODE_SYNTAX;
+}
+
+
+/**
+ * Worker for errorGetOpt.
+ *
+ * @param rcGetOpt The RTGetOpt return value.
+ * @param pValueUnion The value union returned by RTGetOpt.
+ */
+static void errorGetOptWorker(int rcGetOpt, union RTGETOPTUNION const *pValueUnion)
+{
+ if (rcGetOpt == VINF_GETOPT_NOT_OPTION)
+ RTMsgError("Invalid parameter '%s'", pValueUnion->psz);
+ else if (rcGetOpt > 0)
+ {
+ if (RT_C_IS_PRINT(rcGetOpt))
+ RTMsgError("Invalid option -%c", rcGetOpt);
+ else
+ RTMsgError("Invalid option case %i", rcGetOpt);
+ }
+ else if (rcGetOpt == VERR_GETOPT_UNKNOWN_OPTION)
+ RTMsgError("Unknown option: %s", pValueUnion->psz);
+ else if (rcGetOpt == VERR_GETOPT_INVALID_ARGUMENT_FORMAT)
+ RTMsgError("Invalid argument format: %s", pValueUnion->psz);
+ else if (pValueUnion->pDef)
+ RTMsgError("%s: %Rrs", pValueUnion->pDef->pszLong, rcGetOpt);
+ else
+ RTMsgError("%Rrs", rcGetOpt);
+}
+
+
+/**
+ * Handled an RTGetOpt error or common option.
+ *
+ * This implements the 'V' and 'h' cases. It reports appropriate syntax errors
+ * for other @a rcGetOpt values.
+ *
+ * @retval RTEXITCODE_SUCCESS if help or version request.
+ * @retval RTEXITCODE_SYNTAX if not help or version request.
+ * @param rcGetOpt The RTGetOpt return value.
+ * @param pValueUnion The value union returned by RTGetOpt.
+ */
+RTEXITCODE errorGetOpt(int rcGetOpt, union RTGETOPTUNION const *pValueUnion)
+{
+ Assert(g_enmCurCommand != HELP_CMD_VBOXMANAGE_INVALID);
+
+ /*
+ * Check if it is an unhandled standard option.
+ */
+ if (rcGetOpt == 'V')
+ {
+ RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
+ return RTEXITCODE_SUCCESS;
+ }
+
+ if (rcGetOpt == 'h')
+ {
+ printFullCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdOut);
+ return RTEXITCODE_SUCCESS;
+ }
+
+ /*
+ * We failed.
+ */
+ showLogo(g_pStdErr);
+ errorGetOptWorker(rcGetOpt, pValueUnion);
+ if ( printBriefCommandOrSubcommandHelp(g_enmCurCommand, g_fCurSubcommandScope, g_pStdErr)
+ >= ERROR_REPEAT_AFTER_USAGE_LENGTH)
+ {
+ /* Usage was very long, repeat the error message. */
+ RTStrmPutCh(g_pStdErr, '\n');
+ errorGetOptWorker(rcGetOpt, pValueUnion);
+ }
+ return RTEXITCODE_SYNTAX;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
+
+
+
+void showLogo(PRTSTREAM pStrm)
+{
+ static bool s_fShown; /* show only once */
+
+ if (!s_fShown)
+ {
+ RTStrmPrintf(pStrm, VBOX_PRODUCT " Command Line Management Interface Version "
+ VBOX_VERSION_STRING "\n"
+ "(C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
+ "All rights reserved.\n"
+ "\n");
+ s_fShown = true;
+ }
+}
+
+
+
+
+void printUsage(USAGECATEGORY fCategory, uint32_t fSubCategory, PRTSTREAM pStrm)
+{
+ bool fDumpOpts = false;
+#ifdef RT_OS_LINUX
+ bool fLinux = true;
+#else
+ bool fLinux = false;
+#endif
+#ifdef RT_OS_WINDOWS
+ bool fWin = true;
+#else
+ bool fWin = false;
+#endif
+#ifdef RT_OS_SOLARIS
+ bool fSolaris = true;
+#else
+ bool fSolaris = false;
+#endif
+#ifdef RT_OS_FREEBSD
+ bool fFreeBSD = true;
+#else
+ bool fFreeBSD = false;
+#endif
+#ifdef RT_OS_DARWIN
+ bool fDarwin = true;
+#else
+ bool fDarwin = false;
+#endif
+#ifdef VBOX_WITH_VBOXSDL
+ bool fVBoxSDL = true;
+#else
+ bool fVBoxSDL = false;
+#endif
+
+ if (fCategory == USAGE_DUMPOPTS)
+ {
+ fDumpOpts = true;
+ fLinux = true;
+ fWin = true;
+ fSolaris = true;
+ fFreeBSD = true;
+ fDarwin = true;
+ fVBoxSDL = true;
+ fCategory = USAGE_ALL;
+ }
+
+ RTStrmPrintf(pStrm,
+ "Usage:\n"
+ "\n");
+
+ if (fCategory == USAGE_ALL)
+ RTStrmPrintf(pStrm,
+ " VBoxManage [<general option>] <command>\n"
+ " \n \n"
+ "General Options:\n \n"
+ " [-v|--version] print version number and exit\n"
+ " [-q|--nologo] suppress the logo\n"
+ " [--settingspw <pw>] provide the settings password\n"
+ " [--settingspwfile <file>] provide a file containing the settings password\n"
+ " [@<response-file>] load arguments from the given response file (bourne style)\n"
+ " \n \n"
+ "Commands:\n \n");
+
+ const char *pcszSep1 = " ";
+ const char *pcszSep2 = " ";
+ if (fCategory != USAGE_ALL)
+ {
+ pcszSep1 = "VBoxManage";
+ pcszSep2 = "";
+ }
+
+#define SEP pcszSep1, pcszSep2
+
+ if (fCategory & USAGE_LIST)
+ RTStrmPrintf(pStrm,
+ "%s list [--long|-l] [--sorted|-s]%s vms|runningvms|ostypes|hostdvds|hostfloppies|\n"
+#if defined(VBOX_WITH_NETFLT)
+ " intnets|bridgedifs|hostonlyifs|natnets|dhcpservers|\n"
+#else
+ " intnets|bridgedifs|natnets|dhcpservers|hostinfo|\n"
+#endif
+ " hostinfo|hostcpuids|hddbackends|hdds|dvds|floppies|\n"
+ " usbhost|usbfilters|systemproperties|extpacks|\n"
+ " groups|webcams|screenshotformats|cloudproviders|\n"
+ " cloudprofiles\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_SHOWVMINFO)
+ RTStrmPrintf(pStrm,
+ "%s showvminfo %s <uuid|vmname> [--details]\n"
+ " [--machinereadable]\n"
+ "%s showvminfo %s <uuid|vmname> --log <idx>\n"
+ "\n", SEP, SEP);
+
+ if (fCategory & USAGE_REGISTERVM)
+ RTStrmPrintf(pStrm,
+ "%s registervm %s <filename>\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_UNREGISTERVM)
+ RTStrmPrintf(pStrm,
+ "%s unregistervm %s <uuid|vmname> [--delete]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_CREATEVM)
+ RTStrmPrintf(pStrm,
+ "%s createvm %s --name <name>\n"
+ " [--groups <group>, ...]\n"
+ " [--ostype <ostype>]\n"
+ " [--register]\n"
+ " [--basefolder <path>]\n"
+ " [--uuid <uuid>]\n"
+ " [--default]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_MODIFYVM)
+ {
+ RTStrmPrintf(pStrm,
+ "%s modifyvm %s <uuid|vmname>\n"
+ " [--name <name>]\n"
+ " [--groups <group>, ...]\n"
+ " [--description <desc>]\n"
+ " [--ostype <ostype>]\n"
+ " [--iconfile <filename>]\n"
+ " [--memory <memorysize in MB>]\n"
+ " [--pagefusion on|off]\n"
+ " [--vram <vramsize in MB>]\n"
+ " [--acpi on|off]\n"
+#ifdef VBOX_WITH_PCI_PASSTHROUGH
+ " [--pciattach 03:04.0]\n"
+ " [--pciattach 03:04.0@02:01.0]\n"
+ " [--pcidetach 03:04.0]\n"
+#endif
+ " [--ioapic on|off]\n"
+ " [--hpet on|off]\n"
+ " [--triplefaultreset on|off]\n"
+ " [--apic on|off]\n"
+ " [--x2apic on|off]\n"
+ " [--paravirtprovider none|default|legacy|minimal|\n"
+ " hyperv|kvm]\n"
+ " [--paravirtdebug <key=value> [,<key=value> ...]]\n"
+ " [--hwvirtex 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"
+ " [--ibpb-on-vm-exit on|off]\n"
+ " [--ibpb-on-vm-entry on|off]\n"
+ " [--spec-ctrl on|off]\n"
+ " [--l1d-flush-on-sched on|off]\n"
+ " [--l1d-flush-on-vm-entry on|off]\n"
+ " [--nested-hw-virt on|off]\n"
+ " [--cpu-profile \"host|Intel 80[86|286|386]\"]\n"
+ " [--cpuid-portability-level <0..3>\n"
+ " [--cpuid-set <leaf[:subleaf]> <eax> <ebx> <ecx> <edx>]\n"
+ " [--cpuid-remove <leaf[:subleaf]>]\n"
+ " [--cpuidremoveall]\n"
+ " [--hardwareuuid <uuid>]\n"
+ " [--cpus <number>]\n"
+ " [--cpuhotplug on|off]\n"
+ " [--plugcpu <id>]\n"
+ " [--unplugcpu <id>]\n"
+ " [--cpuexecutioncap <1-100>]\n"
+ " [--rtcuseutc on|off]\n"
+#ifdef VBOX_WITH_VMSVGA
+ " [--graphicscontroller none|vboxvga|vmsvga|vboxsvga]\n"
+#else
+ " [--graphicscontroller none|vboxvga]\n"
+#endif
+ " [--monitorcount <number>]\n"
+ " [--accelerate3d on|off]\n"
+#ifdef VBOX_WITH_VIDEOHWACCEL
+ " [--accelerate2dvideo on|off]\n"
+#endif
+ " [--firmware bios|efi|efi32|efi64]\n"
+ " [--chipset ich9|piix3]\n"
+ " [--bioslogofadein on|off]\n"
+ " [--bioslogofadeout on|off]\n"
+ " [--bioslogodisplaytime <msec>]\n"
+ " [--bioslogoimagepath <imagepath>]\n"
+ " [--biosbootmenu disabled|menuonly|messageandmenu]\n"
+ " [--biosapic disabled|apic|x2apic]\n"
+ " [--biossystemtimeoffset <msec>]\n"
+ " [--biospxedebug on|off]\n"
+ " [--boot<1-4> none|floppy|dvd|disk|net>]\n"
+ " [--nic<1-N> none|null|nat|bridged|intnet"
+#if defined(VBOX_WITH_NETFLT)
+ "|hostonly"
+#endif
+ "|\n"
+ " generic|natnetwork"
+ "]\n"
+ " [--nictype<1-N> Am79C970A|Am79C973"
+#ifdef VBOX_WITH_E1000
+ "|\n 82540EM|82543GC|82545EM"
+#endif
+#ifdef VBOX_WITH_VIRTIO
+ "|\n virtio"
+#endif /* VBOX_WITH_VIRTIO */
+ "]\n"
+ " [--cableconnected<1-N> on|off]\n"
+ " [--nictrace<1-N> on|off]\n"
+ " [--nictracefile<1-N> <filename>]\n"
+ " [--nicproperty<1-N> name=[value]]\n"
+ " [--nicspeed<1-N> <kbps>]\n"
+ " [--nicbootprio<1-N> <priority>]\n"
+ " [--nicpromisc<1-N> deny|allow-vms|allow-all]\n"
+ " [--nicbandwidthgroup<1-N> none|<name>]\n"
+ " [--bridgeadapter<1-N> none|<devicename>]\n"
+#if defined(VBOX_WITH_NETFLT)
+ " [--hostonlyadapter<1-N> none|<devicename>]\n"
+#endif
+ " [--intnet<1-N> <network name>]\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"
+ " [--natpf<1-N> [<rulename>],tcp|udp,[<hostip>],\n"
+ " <hostport>,[<guestip>],<guestport>]\n"
+ " [--natpf<1-N> delete <rulename>]\n"
+ " [--nattftpprefix<1-N> <prefix>]\n"
+ " [--nattftpfile<1-N> <file>]\n"
+ " [--nattftpserver<1-N> <ip>]\n"
+ " [--natbindip<1-N> <ip>\n"
+ " [--natdnspassdomain<1-N> on|off]\n"
+ " [--natdnsproxy<1-N> on|off]\n"
+ " [--natdnshostresolver<1-N> on|off]\n"
+ " [--nataliasmode<1-N> default|[log],[proxyonly],\n"
+ " [sameports]]\n"
+ " [--macaddress<1-N> auto|<mac>]\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"
+ " server <pipe>|\n"
+ " client <pipe>|\n"
+ " tcpserver <port>|\n"
+ " tcpclient <hostname:port>|\n"
+ " file <file>|\n"
+ " <devicename>]\n"
+ " [--uarttype<1-N> 16450|16550A|16750\n"
+#if defined(RT_OS_LINUX) || defined(RT_OS_WINDOWS)
+ " [--lpt<1-N> off|<I/O base> <IRQ>]\n"
+ " [--lptmode<1-N> <devicename>]\n"
+#endif
+ " [--guestmemoryballoon <balloonsize in MB>]\n"
+ " [--audio none|null", SEP);
+ if (fWin)
+ {
+#ifdef VBOX_WITH_WINMM
+ RTStrmPrintf(pStrm, "|winmm|dsound");
+#else
+ RTStrmPrintf(pStrm, "|dsound");
+#endif
+ }
+ if (fLinux || fSolaris)
+ {
+ RTStrmPrintf(pStrm, ""
+#ifdef VBOX_WITH_AUDIO_OSS
+ "|oss"
+#endif
+#ifdef VBOX_WITH_AUDIO_ALSA
+ "|alsa"
+#endif
+#ifdef VBOX_WITH_AUDIO_PULSE
+ "|pulse"
+#endif
+ );
+ }
+ if (fFreeBSD)
+ {
+#ifdef VBOX_WITH_AUDIO_OSS
+ /* Get the line break sorted when dumping all option variants. */
+ if (fDumpOpts)
+ {
+ RTStrmPrintf(pStrm, "|\n"
+ " oss");
+ }
+ else
+ RTStrmPrintf(pStrm, "|oss");
+#endif
+#ifdef VBOX_WITH_AUDIO_PULSE
+ RTStrmPrintf(pStrm, "|pulse");
+#endif
+ }
+ if (fDarwin)
+ {
+ RTStrmPrintf(pStrm, "|coreaudio");
+ }
+ RTStrmPrintf(pStrm, "]\n");
+ RTStrmPrintf(pStrm,
+ " [--audioin on|off]\n"
+ " [--audioout on|off]\n"
+ " [--audiocontroller ac97|hda|sb16]\n"
+ " [--audiocodec stac9700|ad1980|stac9221|sb16]\n"
+ " [--clipboard disabled|hosttoguest|guesttohost|\n"
+ " bidirectional]\n"
+ " [--draganddrop disabled|hosttoguest|guesttohost|\n"
+ " bidirectional]\n");
+ RTStrmPrintf(pStrm,
+ " [--vrde on|off]\n"
+ " [--vrdeextpack default|<name>\n"
+ " [--vrdeproperty <name=[value]>]\n"
+ " [--vrdeport <hostport>]\n"
+ " [--vrdeaddress <hostip>]\n"
+ " [--vrdeauthtype null|external|guest]\n"
+ " [--vrdeauthlibrary default|<name>\n"
+ " [--vrdemulticon on|off]\n"
+ " [--vrdereusecon on|off]\n"
+ " [--vrdevideochannel on|off]\n"
+ " [--vrdevideochannelquality <percent>]\n");
+ RTStrmPrintf(pStrm,
+ " [--usbohci on|off]\n"
+ " [--usbehci on|off]\n"
+ " [--usbxhci on|off]\n"
+ " [--usbrename <oldname> <newname>]\n"
+ " [--snapshotfolder default|<path>]\n"
+ " [--teleporter on|off]\n"
+ " [--teleporterport <port>]\n"
+ " [--teleporteraddress <address|empty>\n"
+ " [--teleporterpassword <password>]\n"
+ " [--teleporterpasswordfile <file>|stdin]\n"
+ " [--tracing-enabled on|off]\n"
+ " [--tracing-config <config-string>]\n"
+ " [--tracing-allow-vm-access on|off]\n"
+#if 0
+ " [--iocache on|off]\n"
+ " [--iocachesize <I/O cache size in MB>]\n"
+#endif
+#if 0
+ " [--faulttolerance master|standby]\n"
+ " [--faulttoleranceaddress <name>]\n"
+ " [--faulttoleranceport <port>]\n"
+ " [--faulttolerancesyncinterval <msec>]\n"
+ " [--faulttolerancepassword <password>]\n"
+#endif
+#ifdef VBOX_WITH_USB_CARDREADER
+ " [--usbcardreader on|off]\n"
+#endif
+ " [--autostart-enabled on|off]\n"
+ " [--autostart-delay <seconds>]\n"
+#if 0
+ " [--autostop-type disabled|savestate|poweroff|\n"
+ " acpishutdown]\n"
+#endif
+#ifdef VBOX_WITH_RECORDING
+ " [--recording on|off]\n"
+ " [--recording screens all|<screen ID> [<screen ID> ...]]\n"
+ " [--recording filename <filename>]\n"
+ " [--recording videores <width> <height>]\n"
+ " [--recording videorate <rate>]\n"
+ " [--recording videofps <fps>]\n"
+ " [--recording maxtime <s>]\n"
+ " [--recording maxfilesize <MB>]\n"
+ " [--recording opts <key=value> [,<key=value> ...]]\n"
+#endif
+ " [--defaultfrontend default|<name>]\n"
+ "\n");
+ }
+
+ if (fCategory & USAGE_CLONEVM)
+ RTStrmPrintf(pStrm,
+ "%s clonevm %s <uuid|vmname>\n"
+ " [--snapshot <uuid>|<name>]\n"
+ " [--mode machine|machineandchildren|all]\n"
+ " [--options link|keepallmacs|keepnatmacs|\n"
+ " keepdisknames|keephwuuids]\n"
+ " [--name <name>]\n"
+ " [--groups <group>, ...]\n"
+ " [--basefolder <basefolder>]\n"
+ " [--uuid <uuid>]\n"
+ " [--register]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_MOVEVM)
+ RTStrmPrintf(pStrm,
+ "%s movevm %s <uuid|vmname>\n"
+ " --type basic\n"
+ " [--folder <path>]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_IMPORTAPPLIANCE)
+ RTStrmPrintf(pStrm,
+ "%s import %s <ovfname/ovaname>\n"
+ " [--dry-run|-n]\n"
+ " [--options keepallmacs|keepnatmacs|importtovdi]\n"
+ " [more options]\n"
+ " (run with -n to have options displayed\n"
+ " for a particular OVF)\n\n", SEP);
+
+ if (fCategory & USAGE_EXPORTAPPLIANCE)
+ RTStrmPrintf(pStrm,
+ "%s export %s <machines> --output|-o <name>.<ovf/ova/tar.gz>\n"
+ " [--legacy09|--ovf09|--ovf10|--ovf20|--opc10]\n"
+ " [--manifest]\n"
+ " [--iso]\n"
+ " [--options manifest|iso|nomacs|nomacsbutnat]\n"
+ " [--vsys <number of virtual system>]\n"
+ " [--vmname <name>]\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"
+ " [--cloud <number of virtual system>]\n"
+ " [--vmname <name>]\n"
+ " [--cloudprofile <cloud profile name>]\n"
+ " [--cloudshape <shape>]\n"
+ " [--clouddomain <domain>]\n"
+ " [--clouddisksize <disk size in GB>]\n"
+ " [--cloudbucket <bucket name>]\n"
+ " [--cloudocivcn <OCI vcn id>]\n"
+ " [--cloudocisubnet <OCI subnet id>]\n"
+ " [--cloudkeepobject <true/false>]\n"
+ " [--cloudlaunchinstance <true/false>]\n"
+ " [--cloudpublicip <true/false>]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_STARTVM)
+ {
+ RTStrmPrintf(pStrm,
+ "%s startvm %s <uuid|vmname>...\n"
+ " [--type gui", SEP);
+ if (fVBoxSDL)
+ RTStrmPrintf(pStrm, "|sdl");
+ RTStrmPrintf(pStrm, "|headless|separate]\n");
+ RTStrmPrintf(pStrm,
+ " [-E|--putenv <NAME>[=<VALUE>]]\n"
+ "\n");
+ }
+
+ if (fCategory & USAGE_CONTROLVM)
+ {
+ RTStrmPrintf(pStrm,
+ "%s controlvm %s <uuid|vmname>\n"
+ " pause|resume|reset|poweroff|savestate|\n"
+ " acpipowerbutton|acpisleepbutton|\n"
+ " keyboardputscancode <hex> [<hex> ...]|\n"
+ " keyboardputstring <string1> [<string2> ...]|\n"
+ " keyboardputfile <filename>|\n"
+ " setlinkstate<1-N> on|off |\n"
+#if defined(VBOX_WITH_NETFLT)
+ " 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|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"
+ " 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"
+ " usbattach <uuid>|<address>\n"
+ " [--capturefile <filename>] |\n"
+ " usbdetach <uuid>|<address> |\n"
+ " audioin on|off |\n"
+ " audioout on|off |\n"
+ " clipboard disabled|hosttoguest|guesttohost|\n"
+ " bidirectional |\n"
+ " draganddrop disabled|hosttoguest|guesttohost|\n"
+ " bidirectional |\n"
+ " vrde on|off |\n"
+ " vrdeport <port> |\n"
+ " vrdeproperty <name=[value]> |\n"
+ " vrdevideochannelquality <percent> |\n"
+ " setvideomodehint <xres> <yres> <bpp>\n"
+ " [[<display>] [<enabled:yes|no> |\n"
+ " [<xorigin> <yorigin>]]] |\n"
+ " setscreenlayout <display> on|primary <xorigin> <yorigin> <xres> <yres> <bpp> | off\n"
+ " screenshotpng <file> [display] |\n"
+#ifdef VBOX_WITH_RECORDING
+ " recording on|off |\n"
+ " recording screens all|none|<screen>,[<screen>...] |\n"
+ " recording filename <file> |\n"
+ " recording videores <width>x<height> |\n"
+ " recording videorate <rate> |\n"
+ " recording videofps <fps> |\n"
+ " recording maxtime <s> |\n"
+ " recording maxfilesize <MB> |\n"
+#endif /* VBOX_WITH_RECORDING */
+ " setcredentials <username>\n"
+ " --passwordfile <file> | <password>\n"
+ " <domain>\n"
+ " [--allowlocallogon <yes|no>] |\n"
+ " teleport --host <name> --port <port>\n"
+ " [--maxdowntime <msec>]\n"
+ " [--passwordfile <file> |\n"
+ " --password <password>] |\n"
+ " plugcpu <id> |\n"
+ " unplugcpu <id> |\n"
+ " cpuexecutioncap <1-100>\n"
+ " webcam <attach [path [settings]]> | <detach [path]> | <list>\n"
+ " addencpassword <id>\n"
+ " <password file>|-\n"
+ " [--removeonsuspend <yes|no>]\n"
+ " removeencpassword <id>\n"
+ " removeallencpasswords\n"
+ " changeuartmode<1-N> disconnected|\n"
+ " server <pipe>|\n"
+ " client <pipe>|\n"
+ " tcpserver <port>|\n"
+ " tcpclient <hostname:port>|\n"
+ " file <file>|\n"
+ " <devicename>]\n"
+ "\n", SEP);
+ }
+
+ if (fCategory & USAGE_DISCARDSTATE)
+ RTStrmPrintf(pStrm,
+ "%s discardstate %s <uuid|vmname>\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_ADOPTSTATE)
+ RTStrmPrintf(pStrm,
+ "%s adoptstate %s <uuid|vmname> <state_file>\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_SNAPSHOT)
+ RTStrmPrintf(pStrm,
+ "%s snapshot %s <uuid|vmname>\n"
+ " take <name> [--description <desc>] [--live]\n"
+ " [--uniquename Number,Timestamp,Space,Force] |\n"
+ " delete <uuid|snapname> |\n"
+ " restore <uuid|snapname> |\n"
+ " restorecurrent |\n"
+ " edit <uuid|snapname>|--current\n"
+ " [--name <name>]\n"
+ " [--description <desc>] |\n"
+ " list [--details|--machinereadable] |\n"
+ " showvminfo <uuid|snapname>\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_CLOSEMEDIUM)
+ RTStrmPrintf(pStrm,
+ "%s closemedium %s [disk|dvd|floppy] <uuid|filename>\n"
+ " [--delete]\n"
+ "\n", SEP);
+
+ 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|additions|\n"
+ " <uuid|filename>|host:<drive>|iscsi]\n"
+ " [--mtype normal|writethrough|immutable|shareable|\n"
+ " readonly|multiattach]\n"
+ " [--comment <text>]\n"
+ " [--setuuid <uuid>]\n"
+ " [--setparentuuid <uuid>]\n"
+ " [--passthrough on|off]\n"
+ " [--tempeject on|off]\n"
+ " [--nonrotational on|off]\n"
+ " [--discard on|off]\n"
+ " [--hotpluggable on|off]\n"
+ " [--bandwidthgroup <name>]\n"
+ " [--forceunmount]\n"
+ " [--server <name>|<ip>]\n"
+ " [--target <target>]\n"
+ " [--tport <port>]\n"
+ " [--lun <lun>]\n"
+ " [--encodedlun <lun>]\n"
+ " [--username <username>]\n"
+ " [--password <password>]\n"
+ " [--passwordfile <file>]\n"
+ " [--initiator <initiator>]\n"
+ " [--intnet]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_STORAGECONTROLLER)
+ RTStrmPrintf(pStrm,
+ "%s storagectl %s <uuid|vmname>\n"
+ " --name <name>\n"
+ " [--add ide|sata|scsi|floppy|sas|usb|pcie]\n"
+ " [--controller LSILogic|LSILogicSAS|BusLogic|\n"
+ " IntelAHCI|PIIX3|PIIX4|ICH6|I82078|\n"
+ " [ USB|NVMe]\n"
+ " [--portcount <1-n>]\n"
+ " [--hostiocache on|off]\n"
+ " [--bootable on|off]\n"
+ " [--rename <name>]\n"
+ " [--remove]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_BANDWIDTHCONTROL)
+ RTStrmPrintf(pStrm,
+ "%s bandwidthctl %s <uuid|vmname>\n"
+ " add <name> --type disk|network\n"
+ " --limit <megabytes per second>[k|m|g|K|M|G] |\n"
+ " set <name>\n"
+ " --limit <megabytes per second>[k|m|g|K|M|G] |\n"
+ " remove <name> |\n"
+ " list [--machinereadable]\n"
+ " (limit units: k=kilobit, m=megabit, g=gigabit,\n"
+ " K=kilobyte, M=megabyte, G=gigabyte)\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_SHOWMEDIUMINFO)
+ RTStrmPrintf(pStrm,
+ "%s showmediuminfo %s [disk|dvd|floppy] <uuid|filename>\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_CREATEMEDIUM)
+ RTStrmPrintf(pStrm,
+ "%s createmedium %s [disk|dvd|floppy] --filename <filename>\n"
+ " [--size <megabytes>|--sizebyte <bytes>]\n"
+ " [--diffparent <uuid>|<filename>\n"
+ " [--format VDI|VMDK|VHD] (default: VDI)\n"
+ " [--variant Standard,Fixed,Split2G,Stream,ESX,\n"
+ " Formatted]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_MODIFYMEDIUM)
+ RTStrmPrintf(pStrm,
+ "%s modifymedium %s [disk|dvd|floppy] <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"
+ " [--move <path>]\n"
+ " [--setlocation <path>]\n"
+ " [--description <description string>]"
+ "\n", SEP);
+
+ if (fCategory & USAGE_CLONEMEDIUM)
+ RTStrmPrintf(pStrm,
+ "%s clonemedium %s [disk|dvd|floppy] <uuid|inputfile> <uuid|outputfile>\n"
+ " [--format VDI|VMDK|VHD|RAW|<other>]\n"
+ " [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
+ " [--existing]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_MEDIUMPROPERTY)
+ RTStrmPrintf(pStrm,
+ "%s mediumproperty %s [disk|dvd|floppy] set <uuid|filename>\n"
+ " <property> <value>\n"
+ "\n"
+ " [disk|dvd|floppy] get <uuid|filename>\n"
+ " <property>\n"
+ "\n"
+ " [disk|dvd|floppy] delete <uuid|filename>\n"
+ " <property>\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_ENCRYPTMEDIUM)
+ RTStrmPrintf(pStrm,
+ "%s encryptmedium %s <uuid|filename>\n"
+ " [--newpassword <file>|-]\n"
+ " [--oldpassword <file>|-]\n"
+ " [--cipher <cipher identifier>]\n"
+ " [--newpasswordid <password identifier>]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_MEDIUMENCCHKPWD)
+ RTStrmPrintf(pStrm,
+ "%s checkmediumpwd %s <uuid|filename>\n"
+ " <pwd file>|-\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_CONVERTFROMRAW)
+ RTStrmPrintf(pStrm,
+ "%s convertfromraw %s <filename> <outputfile>\n"
+ " [--format VDI|VMDK|VHD]\n"
+ " [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
+ " [--uuid <uuid>]\n"
+ "%s convertfromraw %s stdin <outputfile> <bytes>\n"
+ " [--format VDI|VMDK|VHD]\n"
+ " [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
+ " [--uuid <uuid>]\n"
+ "\n", SEP, SEP);
+
+ if (fCategory & USAGE_GETEXTRADATA)
+ RTStrmPrintf(pStrm,
+ "%s getextradata %s global|<uuid|vmname>\n"
+ " <key>|[enumerate]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_SETEXTRADATA)
+ RTStrmPrintf(pStrm,
+ "%s setextradata %s global|<uuid|vmname>\n"
+ " <key>\n"
+ " [<value>] (no value deletes key)\n"
+ "\n", SEP);
+
+ 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"
+ " logginglevel <log setting>\n"
+ " proxymode system|noproxy|manual\n"
+ " proxyurl <url>\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_USBFILTER_ADD)
+ RTStrmPrintf(pStrm,
+ "%s usbfilter %s add <index,0-N>\n"
+ " --target <uuid|vmname>|global\n"
+ " --name <string>\n"
+ " --action ignore|hold (global filters only)\n"
+ " [--active yes|no] (yes)\n"
+ " [--vendorid <XXXX>] (null)\n"
+ " [--productid <XXXX>] (null)\n"
+ " [--revision <IIFF>] (null)\n"
+ " [--manufacturer <string>] (null)\n"
+ " [--product <string>] (null)\n"
+ " [--remote yes|no] (null, VM filters only)\n"
+ " [--serialnumber <string>] (null)\n"
+ " [--maskedinterfaces <XXXXXXXX>]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_USBFILTER_MODIFY)
+ RTStrmPrintf(pStrm,
+ "%s usbfilter %s modify <index,0-N>\n"
+ " --target <uuid|vmname>|global\n"
+ " [--name <string>]\n"
+ " [--action ignore|hold] (global filters only)\n"
+ " [--active yes|no]\n"
+ " [--vendorid <XXXX>|\"\"]\n"
+ " [--productid <XXXX>|\"\"]\n"
+ " [--revision <IIFF>|\"\"]\n"
+ " [--manufacturer <string>|\"\"]\n"
+ " [--product <string>|\"\"]\n"
+ " [--remote yes|no] (null, VM filters only)\n"
+ " [--serialnumber <string>|\"\"]\n"
+ " [--maskedinterfaces <XXXXXXXX>]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_USBFILTER_REMOVE)
+ RTStrmPrintf(pStrm,
+ "%s usbfilter %s remove <index,0-N>\n"
+ " --target <uuid|vmname>|global\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_SHAREDFOLDER_ADD)
+ RTStrmPrintf(pStrm,
+ "%s sharedfolder %s add <uuid|vmname>\n"
+ " --name <name> --hostpath <hostpath>\n"
+ " [--transient] [--readonly] [--automount]\n"
+ "\n", SEP);
+
+ if (fCategory & USAGE_SHAREDFOLDER_REMOVE)
+ RTStrmPrintf(pStrm,
+ "%s sharedfolder %s remove <uuid|vmname>\n"
+ " --name <name> [--transient]\n"
+ "\n", SEP);
+
+#ifdef VBOX_WITH_GUEST_PROPS
+ if (fCategory & USAGE_GUESTPROPERTY)
+ usageGuestProperty(pStrm, SEP);
+#endif /* VBOX_WITH_GUEST_PROPS defined */
+
+#ifdef VBOX_WITH_GUEST_CONTROL
+ if (fCategory & USAGE_GUESTCONTROL)
+ usageGuestControl(pStrm, SEP, fSubCategory);
+#endif /* VBOX_WITH_GUEST_CONTROL defined */
+
+ if (fCategory & USAGE_METRICS)
+ RTStrmPrintf(pStrm,
+ "%s metrics %s list [*|host|<vmname> [<metric_list>]]\n"
+ " (comma-separated)\n\n"
+ "%s metrics %s setup\n"
+ " [--period <seconds>] (default: 1)\n"
+ " [--samples <count>] (default: 1)\n"
+ " [--list]\n"
+ " [*|host|<vmname> [<metric_list>]]\n\n"
+ "%s metrics %s query [*|host|<vmname> [<metric_list>]]\n\n"
+ "%s metrics %s enable\n"
+ " [--list]\n"
+ " [*|host|<vmname> [<metric_list>]]\n\n"
+ "%s metrics %s disable\n"
+ " [--list]\n"
+ " [*|host|<vmname> [<metric_list>]]\n\n"
+ "%s metrics %s collect\n"
+ " [--period <seconds>] (default: 1)\n"
+ " [--samples <count>] (default: 1)\n"
+ " [--list]\n"
+ " [--detach]\n"
+ " [*|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"
+ "%s natnetwork %s list [<pattern>]\n"
+ "\n", SEP, SEP, SEP, SEP, SEP, SEP);
+
+
+ }
+#endif
+
+#if defined(VBOX_WITH_NETFLT)
+ if (fCategory & USAGE_HOSTONLYIFS)
+ {
+ RTStrmPrintf(pStrm,
+ "%s hostonlyif %s ipconfig <name>\n"
+ " [--dhcp |\n"
+ " --ip<ipv4> [--netmask<ipv4> (def: 255.255.255.0)] |\n"
+ " --ipv6<ipv6> [--netmasklengthv6<length> (def: 64)]]\n"
+# if !defined(RT_OS_SOLARIS) || defined(VBOX_ONLY_DOCS)
+ " create |\n"
+ " remove <name>\n"
+# endif
+ "\n", SEP);
+ }
+#endif
+
+ if (fCategory & USAGE_DHCPSERVER)
+ {
+ RTStrmPrintf(pStrm,
+ "%s dhcpserver %s add|modify --netname <network_name> |\n"
+#if defined(VBOX_WITH_NETFLT)
+ " --ifname <hostonly_if_name>\n"
+#endif
+ " [--ip <ip_address>\n"
+ " --netmask <network_mask>\n"
+ " --lowerip <lower_ip>\n"
+ " --upperip <upper_ip>]\n"
+ " [--enable | --disable]\n"
+ " [--options [--vm <name> --nic <1-N>]\n"
+ " --id <number> [--value <string> | --remove]]\n"
+ " (multiple options allowed after --options)\n\n"
+ "%s dhcpserver %s remove --netname <network_name> |\n"
+#if defined(VBOX_WITH_NETFLT)
+ " --ifname <hostonly_if_name>\n"
+#endif
+ "\n", SEP, SEP);
+ }
+
+ if (fCategory & USAGE_USBDEVSOURCE)
+ {
+ RTStrmPrintf(pStrm,
+ "%s usbdevsource %s add <source name>\n"
+ " --backend <backend>\n"
+ " --address <address>\n"
+ "%s usbdevsource %s remove <source name>\n"
+ "\n", SEP, SEP);
+ }
+
+#ifndef VBOX_ONLY_DOCS /* Converted to man page, not needed. */
+ if (fCategory == USAGE_ALL)
+ {
+ uint32_t cPendingBlankLines = 0;
+ for (uint32_t i = 0; i < g_cHelpEntries; i++)
+ {
+ PCRTMSGREFENTRY pHelp = g_apHelpEntries[i];
+ while (cPendingBlankLines-- > 0)
+ RTStrmPutCh(pStrm, '\n');
+ RTStrmPrintf(pStrm, " %c%s:\n", RT_C_TO_UPPER(pHelp->pszBrief[0]), pHelp->pszBrief + 1);
+ cPendingBlankLines = 0;
+ RTMsgRefEntryPrintStringTable(pStrm, &pHelp->Synopsis, RTMSGREFENTRYSTR_SCOPE_GLOBAL,
+ &cPendingBlankLines, NULL /*pcLinesWritten*/);
+ cPendingBlankLines = RT_MAX(cPendingBlankLines, 1);
+ }
+ }
+#endif
+}
+
+/**
+ * Print a usage synopsis and the syntax error message.
+ * @returns RTEXITCODE_SYNTAX.
+ */
+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);
+#else
+ RT_NOREF_PV(fCategory);
+#endif
+ 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(fCategory, g_pStdErr);
+ else
+ printUsage(fCategory, fSubCategory, g_pStdErr);
+#else
+ RT_NOREF2(fCategory, fSubCategory);
+#endif
+ va_start(args, pszFormat);
+ RTStrmPrintf(g_pStdErr, "\nSyntax error: %N\n", pszFormat, &args);
+ va_end(args);
+ return RTEXITCODE_SYNTAX;
+}
+
+/**
+ * errorSyntax for RTGetOpt users.
+ *
+ * @returns RTEXITCODE_SYNTAX.
+ *
+ * @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 errorGetOptEx(USAGECATEGORY fCategory, uint32_t fSubCategory, int rc, union RTGETOPTUNION const *pValueUnion)
+{
+ /*
+ * Check if it is an unhandled standard option.
+ */
+#ifndef VBOX_ONLY_DOCS
+ if (rc == 'V')
+ {
+ RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
+ return RTEXITCODE_SUCCESS;
+ }
+#endif
+
+ if (rc == 'h')
+ {
+ showLogo(g_pStdErr);
+#ifndef VBOX_ONLY_DOCS
+ if (g_fInternalMode)
+ printUsageInternal(fCategory, g_pStdOut);
+ else
+ printUsage(fCategory, fSubCategory, g_pStdOut);
+#endif
+ return RTEXITCODE_SUCCESS;
+ }
+
+ /*
+ * General failure.
+ */
+ showLogo(g_pStdErr); // show logo even if suppressed
+#ifndef VBOX_ONLY_DOCS
+ if (g_fInternalMode)
+ printUsageInternal(fCategory, g_pStdErr);
+ else
+ printUsage(fCategory, fSubCategory, g_pStdErr);
+#else
+ RT_NOREF2(fCategory, fSubCategory);
+#endif
+
+ if (rc == VINF_GETOPT_NOT_OPTION)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid parameter '%s'", pValueUnion->psz);
+ if (rc > 0)
+ {
+ if (RT_C_IS_PRINT(rc))
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid option -%c", rc);
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid option case %i", rc);
+ }
+ if (rc == VERR_GETOPT_UNKNOWN_OPTION)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown option: %s", pValueUnion->psz);
+ if (rc == VERR_GETOPT_INVALID_ARGUMENT_FORMAT)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid argument format: %s", pValueUnion->psz);
+ if (pValueUnion->pDef)
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "%s: %Rrs", pValueUnion->pDef->pszLong, rc);
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "%Rrs", rc);
+}
+
+/**
+ * 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.
+ */
+RTEXITCODE errorArgument(const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ RTMsgErrorV(pszFormat, args);
+ va_end(args);
+ return RTEXITCODE_SYNTAX;
+}
+
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp
new file mode 100644
index 00000000..401a80dc
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageHostonly.cpp
@@ -0,0 +1,283 @@
+/* $Id: VBoxManageHostonly.cpp $ */
+/** @file
+ * VBoxManage - Implementation of hostonlyif command.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 */
+
+#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 "VBoxManage.h"
+
+#ifndef VBOX_ONLY_DOCS
+using namespace com;
+
+static const RTGETOPTDEF g_aHostOnlyCreateOptions[] =
+{
+ { "--machinereadable", 'M', RTGETOPT_REQ_NOTHING },
+};
+
+#if defined(VBOX_WITH_NETFLT) && !defined(RT_OS_SOLARIS)
+static RTEXITCODE handleCreate(HandlerArg *a)
+{
+ /*
+ * Parse input.
+ */
+ bool fMachineReadable = false;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, a->argc, a->argv, g_aHostOnlyCreateOptions,
+ RT_ELEMENTS(g_aHostOnlyCreateOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ int c;
+ while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (c)
+ {
+ case 'M': // --machinereadable
+ fMachineReadable = true;
+ break;
+
+ default:
+ return errorGetOpt(USAGE_HOSTONLYIFS, c, &ValueUnion);
+ }
+ }
+
+ /*
+ * Do the work.
+ */
+ ComPtr<IHost> host;
+ CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE);
+
+ ComPtr<IHostNetworkInterface> hif;
+ ComPtr<IProgress> progress;
+
+ CHECK_ERROR2I_RET(host, CreateHostOnlyNetworkInterface(hif.asOutParam(), progress.asOutParam()), RTEXITCODE_FAILURE);
+
+ if (fMachineReadable)
+ {
+ CHECK_PROGRESS_ERROR_RET(progress, (""), RTEXITCODE_FAILURE);
+ }
+ else
+ {
+ /*HRESULT hrc =*/ showProgress(progress);
+ CHECK_PROGRESS_ERROR_RET(progress, ("Failed to create the host-only adapter"), RTEXITCODE_FAILURE);
+ }
+
+ Bstr bstrName;
+ CHECK_ERROR2I(hif, COMGETTER(Name)(bstrName.asOutParam()));
+
+ if (fMachineReadable)
+ RTPrintf("%ls", bstrName.raw());
+ else
+ RTPrintf("Interface '%ls' was successfully created\n", bstrName.raw());
+ return RTEXITCODE_SUCCESS;
+}
+
+static RTEXITCODE handleRemove(HandlerArg *a)
+{
+ /*
+ * Parse input.
+ */
+ const char *pszName = NULL;
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, a->argc, a->argv, NULL, 0, 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ switch (ch)
+ {
+ case VINF_GETOPT_NOT_OPTION:
+ if (pszName)
+ return errorSyntax(USAGE_HOSTONLYIFS, "Only one interface name can be specified");
+ pszName = ValueUnion.psz;
+ break;
+
+ default:
+ return errorGetOpt(USAGE_HOSTONLYIFS, ch, &ValueUnion);
+ }
+ if (!pszName)
+ return errorSyntax(USAGE_HOSTONLYIFS, "No interface name was specified");
+
+ /*
+ * Do the work.
+ */
+ ComPtr<IHost> host;
+ CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE);
+
+ ComPtr<IHostNetworkInterface> hif;
+ CHECK_ERROR2I_RET(host, FindHostNetworkInterfaceByName(Bstr(pszName).raw(), hif.asOutParam()), RTEXITCODE_FAILURE);
+
+ Bstr guid;
+ CHECK_ERROR2I_RET(hif, COMGETTER(Id)(guid.asOutParam()), RTEXITCODE_FAILURE);
+
+ ComPtr<IProgress> progress;
+ CHECK_ERROR2I_RET(host, RemoveHostOnlyNetworkInterface(guid.raw(), progress.asOutParam()), RTEXITCODE_FAILURE);
+
+ /*HRESULT hrc =*/ showProgress(progress);
+ CHECK_PROGRESS_ERROR_RET(progress, ("Failed to remove the host-only adapter"), RTEXITCODE_FAILURE);
+
+ return RTEXITCODE_SUCCESS;
+}
+#endif
+
+static const RTGETOPTDEF g_aHostOnlyIPOptions[]
+ = {
+ { "--dhcp", 'd', RTGETOPT_REQ_NOTHING },
+ { "-dhcp", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
+ { "--ip", 'a', RTGETOPT_REQ_STRING },
+ { "-ip", 'a', RTGETOPT_REQ_STRING }, // deprecated
+ { "--netmask", 'm', RTGETOPT_REQ_STRING },
+ { "-netmask", 'm', RTGETOPT_REQ_STRING }, // deprecated
+ { "--ipv6", 'b', RTGETOPT_REQ_STRING },
+ { "-ipv6", 'b', RTGETOPT_REQ_STRING }, // deprecated
+ { "--netmasklengthv6", 'l', RTGETOPT_REQ_UINT8 },
+ { "-netmasklengthv6", 'l', RTGETOPT_REQ_UINT8 } // deprecated
+ };
+
+static RTEXITCODE handleIpConfig(HandlerArg *a)
+{
+ bool fDhcp = false;
+ bool fNetmasklengthv6 = false;
+ uint32_t uNetmasklengthv6 = UINT32_MAX;
+ const char *pszIpv6 = NULL;
+ const char *pszIp = NULL;
+ const char *pszNetmask = NULL;
+ const char *pszName = NULL;
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, a->argc, a->argv, g_aHostOnlyIPOptions, RT_ELEMENTS(g_aHostOnlyIPOptions),
+ 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (c)
+ {
+ case 'd': // --dhcp
+ fDhcp = true;
+ break;
+ case 'a': // --ip
+ if (pszIp)
+ RTMsgWarning("The --ip option is specified more than once");
+ pszIp = ValueUnion.psz;
+ break;
+ case 'm': // --netmask
+ if (pszNetmask)
+ RTMsgWarning("The --netmask option is specified more than once");
+ pszNetmask = ValueUnion.psz;
+ break;
+ case 'b': // --ipv6
+ if (pszIpv6)
+ RTMsgWarning("The --ipv6 option is specified more than once");
+ pszIpv6 = ValueUnion.psz;
+ break;
+ case 'l': // --netmasklengthv6
+ if (fNetmasklengthv6)
+ RTMsgWarning("The --netmasklengthv6 option is specified more than once");
+ fNetmasklengthv6 = true;
+ uNetmasklengthv6 = ValueUnion.u8;
+ break;
+ case VINF_GETOPT_NOT_OPTION:
+ if (pszName)
+ return errorSyntax(USAGE_HOSTONLYIFS, "Only one interface name can be specified");
+ pszName = ValueUnion.psz;
+ break;
+ default:
+ return errorGetOpt(USAGE_HOSTONLYIFS, c, &ValueUnion);
+ }
+ }
+
+ /* parameter sanity check */
+ if (fDhcp && (fNetmasklengthv6 || pszIpv6 || pszIp || pszNetmask))
+ return errorSyntax(USAGE_HOSTONLYIFS, "You can not use --dhcp with static ip configuration parameters: --ip, --netmask, --ipv6 and --netmasklengthv6.");
+ if ((pszIp || pszNetmask) && (fNetmasklengthv6 || pszIpv6))
+ return errorSyntax(USAGE_HOSTONLYIFS, "You can not use ipv4 configuration (--ip and --netmask) with ipv6 (--ipv6 and --netmasklengthv6) simultaneously.");
+
+ ComPtr<IHost> host;
+ CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE);
+
+ ComPtr<IHostNetworkInterface> hif;
+ CHECK_ERROR2I_RET(host, FindHostNetworkInterfaceByName(Bstr(pszName).raw(), hif.asOutParam()), RTEXITCODE_FAILURE);
+ if (hif.isNull())
+ return errorArgument("Could not find interface '%s'", pszName);
+
+ if (fDhcp)
+ CHECK_ERROR2I_RET(hif, EnableDynamicIPConfig(), RTEXITCODE_FAILURE);
+ else if (pszIp)
+ {
+ if (!pszNetmask)
+ pszNetmask = "255.255.255.0"; /* ?? */
+ CHECK_ERROR2I_RET(hif, EnableStaticIPConfig(Bstr(pszIp).raw(), Bstr(pszNetmask).raw()), RTEXITCODE_FAILURE);
+ }
+ else if (pszIpv6)
+ {
+ BOOL fIpV6Supported;
+ CHECK_ERROR2I_RET(hif, COMGETTER(IPV6Supported)(&fIpV6Supported), RTEXITCODE_FAILURE);
+ if (!fIpV6Supported)
+ {
+ RTMsgError("IPv6 setting is not supported for this adapter");
+ return RTEXITCODE_FAILURE;
+ }
+
+ if (uNetmasklengthv6 == UINT32_MAX)
+ uNetmasklengthv6 = 64; /* ?? */
+ CHECK_ERROR2I_RET(hif, EnableStaticIPConfigV6(Bstr(pszIpv6).raw(), (ULONG)uNetmasklengthv6), RTEXITCODE_FAILURE);
+ }
+ else
+ return errorSyntax(USAGE_HOSTONLYIFS, "Neither -dhcp nor -ip nor -ipv6 was specfified");
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+RTEXITCODE handleHostonlyIf(HandlerArg *a)
+{
+ if (a->argc < 1)
+ return errorSyntax(USAGE_HOSTONLYIFS, "No sub-command specified");
+
+ RTEXITCODE rcExit;
+ if (!strcmp(a->argv[0], "ipconfig"))
+ rcExit = handleIpConfig(a);
+#if defined(VBOX_WITH_NETFLT) && !defined(RT_OS_SOLARIS)
+ else if (!strcmp(a->argv[0], "create"))
+ rcExit = handleCreate(a);
+ else if (!strcmp(a->argv[0], "remove"))
+ rcExit = handleRemove(a);
+#endif
+ else
+ rcExit = errorSyntax(USAGE_HOSTONLYIFS, "Unknown sub-command '%s'", a->argv[0]);
+ return rcExit;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp
new file mode 100644
index 00000000..afc489a3
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp
@@ -0,0 +1,2691 @@
+/* $Id: VBoxManageInfo.cpp $ */
+/** @file
+ * VBoxManage - The 'showvminfo' command and helper routines.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 VBOX_ONLY_DOCS
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+
+#include <VBox/com/VirtualBox.h>
+
+#ifdef VBOX_WITH_PCI_PASSTHROUGH
+#include <VBox/pci.h>
+#endif
+
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include <iprt/stream.h>
+#include <iprt/time.h>
+#include <iprt/string.h>
+#include <iprt/getopt.h>
+#include <iprt/ctype.h>
+
+#include "VBoxManage.h"
+using namespace com;
+
+
+// funcs
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Helper for formatting an indexed name or some such thing.
+ */
+static const char *FmtNm(char psz[80], const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ RTStrPrintfV(psz, 80, pszFormat, va);
+ va_end(va);
+ return psz;
+}
+
+HRESULT showSnapshots(ComPtr<ISnapshot> &rootSnapshot,
+ ComPtr<ISnapshot> &currentSnapshot,
+ VMINFO_DETAILS details,
+ const Utf8Str &prefix /* = ""*/,
+ int level /*= 0*/)
+{
+ /* start with the root */
+ Bstr name;
+ Bstr uuid;
+ Bstr description;
+ CHECK_ERROR2I_RET(rootSnapshot, COMGETTER(Name)(name.asOutParam()), hrcCheck);
+ CHECK_ERROR2I_RET(rootSnapshot, COMGETTER(Id)(uuid.asOutParam()), hrcCheck);
+ CHECK_ERROR2I_RET(rootSnapshot, COMGETTER(Description)(description.asOutParam()), hrcCheck);
+ bool fCurrent = (rootSnapshot == currentSnapshot);
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ /* print with hierarchical numbering */
+ 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 */
+ 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 */
+ HRESULT hrc = S_OK;
+ SafeIfaceArray <ISnapshot> coll;
+ CHECK_ERROR2I_RET(rootSnapshot,COMGETTER(Children)(ComSafeArrayAsOutParam(coll)), hrcCheck);
+ if (!coll.isNull())
+ {
+ for (size_t index = 0; index < coll.size(); ++index)
+ {
+ ComPtr<ISnapshot> snapshot = coll[index];
+ if (snapshot)
+ {
+ Utf8Str newPrefix;
+ if (details == VMINFO_MACHINEREADABLE)
+ newPrefix = Utf8StrFmt("%s-%d", prefix.c_str(), index + 1);
+ else
+ {
+ newPrefix = Utf8StrFmt("%s ", prefix.c_str());
+ }
+
+ /* recursive call */
+ HRESULT hrc2 = showSnapshots(snapshot, currentSnapshot, details, newPrefix, level + 1);
+ if (FAILED(hrc2))
+ hrc = hrc2;
+ }
+ }
+ }
+ return hrc;
+}
+
+static void makeTimeStr(char *s, int cb, int64_t millies)
+{
+ RTTIME t;
+ RTTIMESPEC ts;
+
+ RTTimeSpecSetMilli(&ts, millies);
+
+ RTTimeExplode(&t, &ts);
+
+ RTStrPrintf(s, cb, "%04d/%02d/%02d %02d:%02d:%02d UTC",
+ t.i32Year, t.u8Month, t.u8MonthDay,
+ t.u8Hour, t.u8Minute, t.u8Second);
+}
+
+const char *machineStateToName(MachineState_T machineState, bool fShort)
+{
+ switch (machineState)
+ {
+ case MachineState_PoweredOff:
+ return fShort ? "poweroff" : "powered off";
+ case MachineState_Saved:
+ return "saved";
+ case MachineState_Teleported:
+ return "teleported";
+ case MachineState_Aborted:
+ return "aborted";
+ case MachineState_Running:
+ return "running";
+ case MachineState_Paused:
+ return "paused";
+ case MachineState_Stuck:
+ return fShort ? "gurumeditation" : "guru meditation";
+ case MachineState_Teleporting:
+ return "teleporting";
+ case MachineState_LiveSnapshotting:
+ return fShort ? "livesnapshotting" : "live snapshotting";
+ case MachineState_Starting:
+ return "starting";
+ case MachineState_Stopping:
+ return "stopping";
+ case MachineState_Saving:
+ return "saving";
+ case MachineState_Restoring:
+ return "restoring";
+ case MachineState_TeleportingPausedVM:
+ return fShort ? "teleportingpausedvm" : "teleporting paused vm";
+ case MachineState_TeleportingIn:
+ return fShort ? "teleportingin" : "teleporting (incoming)";
+ case MachineState_FaultTolerantSyncing:
+ return fShort ? "faulttolerantsyncing" : "fault tolerant syncing";
+ case MachineState_DeletingSnapshotOnline:
+ return fShort ? "deletingsnapshotlive" : "deleting snapshot live";
+ case MachineState_DeletingSnapshotPaused:
+ return fShort ? "deletingsnapshotlivepaused" : "deleting snapshot live paused";
+ case MachineState_OnlineSnapshotting:
+ return fShort ? "onlinesnapshotting" : "online snapshotting";
+ case MachineState_RestoringSnapshot:
+ return fShort ? "restoringsnapshot" : "restoring snapshot";
+ case MachineState_DeletingSnapshot:
+ return fShort ? "deletingsnapshot" : "deleting snapshot";
+ case MachineState_SettingUp:
+ return fShort ? "settingup" : "setting up";
+ case MachineState_Snapshotting:
+ return fShort ? "snapshotting" : "offline snapshotting";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+const char *facilityStateToName(AdditionsFacilityStatus_T faStatus, bool fShort)
+{
+ switch (faStatus)
+ {
+ case AdditionsFacilityStatus_Inactive:
+ return fShort ? "inactive" : "not active";
+ case AdditionsFacilityStatus_Paused:
+ return "paused";
+ case AdditionsFacilityStatus_PreInit:
+ return fShort ? "preinit" : "pre-initializing";
+ case AdditionsFacilityStatus_Init:
+ return fShort ? "init" : "initializing";
+ case AdditionsFacilityStatus_Active:
+ return fShort ? "active" : "active/running";
+ case AdditionsFacilityStatus_Terminating:
+ return "terminating";
+ case AdditionsFacilityStatus_Terminated:
+ return "terminated";
+ case AdditionsFacilityStatus_Failed:
+ return "failed";
+ case AdditionsFacilityStatus_Unknown:
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+/**
+ * This takes care of escaping double quotes and slashes that the string might
+ * contain.
+ *
+ * @param pszName The variable name.
+ * @param pszValue The value.
+ */
+static void outputMachineReadableString(const char *pszName, const char *pszValue)
+{
+ Assert(strpbrk(pszName, "\"\\") == NULL);
+
+ if ( !pszValue
+ || !*pszValue
+ || ( strchr(pszValue, '"') == NULL
+ && strchr(pszValue, '\\') == NULL) )
+ RTPrintf("%s=\"%s\"\n", pszName, pszValue);
+ else
+ {
+ /* The value needs escaping. */
+ RTPrintf("%s=\"", pszName);
+ const char *psz = pszValue;
+ for (;;)
+ {
+ const char *pszNext = strpbrk(psz, "\"\\");
+ if (!pszNext)
+ {
+ RTPrintf("%s", psz);
+ break;
+ }
+ RTPrintf("%.*s\\%c", pszNext - psz, psz, *pszNext);
+ psz = pszNext + 1;
+ }
+ RTPrintf("\"\n");
+ }
+}
+
+
+/**
+ * This takes care of escaping double quotes and slashes that the string might
+ * contain.
+ *
+ * @param pszName The variable name.
+ * @param pbstrValue The value.
+ */
+static void outputMachineReadableString(const char *pszName, Bstr const *pbstrValue)
+{
+ com::Utf8Str strValue(*pbstrValue);
+ outputMachineReadableString(pszName, strValue.c_str());
+}
+
+/**
+ * Converts bandwidth group type to a string.
+ * @returns String representation.
+ * @param enmType Bandwidth control group type.
+ */
+static const char * bwGroupTypeToString(BandwidthGroupType_T enmType)
+{
+ switch (enmType)
+ {
+ case BandwidthGroupType_Null: return "Null";
+ case BandwidthGroupType_Disk: return "Disk";
+ case BandwidthGroupType_Network: return "Network";
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+ case BandwidthGroupType_32BitHack: break; /* Shut up compiler warnings. */
+#endif
+ }
+ return "unknown";
+}
+
+HRESULT showBandwidthGroups(ComPtr<IBandwidthControl> &bwCtrl,
+ VMINFO_DETAILS details)
+{
+ int rc = S_OK;
+ SafeIfaceArray<IBandwidthGroup> bwGroups;
+
+ CHECK_ERROR_RET(bwCtrl, GetAllBandwidthGroups(ComSafeArrayAsOutParam(bwGroups)), rc);
+
+ if (bwGroups.size() && details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\n\n");
+ for (size_t i = 0; i < bwGroups.size(); i++)
+ {
+ Bstr strName;
+ LONG64 cMaxBytesPerSec;
+ BandwidthGroupType_T enmType;
+
+ CHECK_ERROR_RET(bwGroups[i], COMGETTER(Name)(strName.asOutParam()), rc);
+ CHECK_ERROR_RET(bwGroups[i], COMGETTER(Type)(&enmType), rc);
+ CHECK_ERROR_RET(bwGroups[i], COMGETTER(MaxBytesPerSec)(&cMaxBytesPerSec), rc);
+
+ const char *pszType = bwGroupTypeToString(enmType);
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("BandwidthGroup%zu=%ls,%s,%lld\n", i, strName.raw(), pszType, cMaxBytesPerSec);
+ else
+ {
+ const char *pszUnits = "";
+ LONG64 cBytes = cMaxBytesPerSec;
+ if (cBytes == 0)
+ {
+ RTPrintf("Name: '%ls', Type: %s, Limit: none (disabled)\n", strName.raw(), pszType);
+ continue;
+ }
+ else if (!(cBytes % _1G))
+ {
+ pszUnits = "G";
+ cBytes /= _1G;
+ }
+ else if (!(cBytes % _1M))
+ {
+ pszUnits = "M";
+ cBytes /= _1M;
+ }
+ else if (!(cBytes % _1K))
+ {
+ pszUnits = "K";
+ cBytes /= _1K;
+ }
+ const char *pszNetUnits = NULL;
+ if (enmType == BandwidthGroupType_Network)
+ {
+ /*
+ * We want to report network rate limit in bits/s, not bytes.
+ * Only if it cannot be express it in kilobits we will fall
+ * back to reporting it in bytes.
+ */
+ LONG64 cBits = cMaxBytesPerSec;
+ if (!(cBits % 125))
+ {
+ cBits /= 125;
+ pszNetUnits = "k";
+ if (!(cBits % 1000000))
+ {
+ cBits /= 1000000;
+ pszNetUnits = "g";
+ }
+ else if (!(cBits % 1000))
+ {
+ cBits /= 1000;
+ pszNetUnits = "m";
+ }
+ RTPrintf("Name: '%ls', Type: %s, Limit: %lld %sbits/sec (%lld %sbytes/sec)\n", strName.raw(), pszType, cBits, pszNetUnits, cBytes, pszUnits);
+ }
+ }
+ if (!pszNetUnits)
+ RTPrintf("Name: '%ls', Type: %s, Limit: %lld %sbytes/sec\n", strName.raw(), pszType, cBytes, pszUnits);
+ }
+ }
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf(bwGroups.size() != 0 ? "\n" : "<none>\n\n");
+
+ return rc;
+}
+
+/** Shows a shared folder. */
+static HRESULT showSharedFolder(ComPtr<ISharedFolder> &sf, VMINFO_DETAILS details, const char *pszDesc,
+ const char *pszMrInfix, size_t idxMr, bool fFirst)
+{
+ Bstr name, hostPath, bstrAutoMountPoint;
+ BOOL writable = FALSE, fAutoMount = FALSE;
+ CHECK_ERROR2I_RET(sf, COMGETTER(Name)(name.asOutParam()), hrcCheck);
+ CHECK_ERROR2I_RET(sf, COMGETTER(HostPath)(hostPath.asOutParam()), hrcCheck);
+ CHECK_ERROR2I_RET(sf, COMGETTER(Writable)(&writable), hrcCheck);
+ CHECK_ERROR2I_RET(sf, COMGETTER(AutoMount)(&fAutoMount), hrcCheck);
+ CHECK_ERROR2I_RET(sf, COMGETTER(AutoMountPoint)(bstrAutoMountPoint.asOutParam()), hrcCheck);
+
+ if (fFirst && details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\n\n");
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ char szNm[80];
+ outputMachineReadableString(FmtNm(szNm, "SharedFolderName%s%zu", pszMrInfix, idxMr), &name);
+ outputMachineReadableString(FmtNm(szNm, "SharedFolderPath%s%zu", pszMrInfix, idxMr), &hostPath);
+ }
+ else
+ {
+ RTPrintf("Name: '%ls', Host path: '%ls' (%s), %s%s",
+ name.raw(), hostPath.raw(), pszDesc, writable ? "writable" : "readonly", fAutoMount ? ", auto-mount" : "");
+ if (bstrAutoMountPoint.isNotEmpty())
+ RTPrintf(", mount-point: '%ls'\n", bstrAutoMountPoint.raw());
+ else
+ RTPrintf("\n");
+ }
+ return S_OK;
+}
+
+
+static const char *paravirtProviderToString(ParavirtProvider_T provider, VMINFO_DETAILS details)
+{
+ switch (provider)
+ {
+ case ParavirtProvider_None:
+ if (details == VMINFO_MACHINEREADABLE)
+ return "none";
+ return "None";
+
+ case ParavirtProvider_Default:
+ if (details == VMINFO_MACHINEREADABLE)
+ return "default";
+ return "Default";
+
+ case ParavirtProvider_Legacy:
+ if (details == VMINFO_MACHINEREADABLE)
+ return "legacy";
+ return "Legacy";
+
+ case ParavirtProvider_Minimal:
+ if (details == VMINFO_MACHINEREADABLE)
+ return "minimal";
+ return "Minimal";
+
+ case ParavirtProvider_HyperV:
+ if (details == VMINFO_MACHINEREADABLE)
+ return "hyperv";
+ return "HyperV";
+
+ case ParavirtProvider_KVM:
+ if (details == VMINFO_MACHINEREADABLE)
+ return "kvm";
+ return "KVM";
+
+ default:
+ if (details == VMINFO_MACHINEREADABLE)
+ return "unknown";
+ return "Unknown";
+ }
+}
+
+
+/* Disable global optimizations for MSC 8.0/64 to make it compile in reasonable
+ time. MSC 7.1/32 doesn't have quite as much trouble with it, but still
+ sufficient to qualify for this hack as well since this code isn't performance
+ critical and probably won't gain much from the extra optimizing in real life. */
+#if defined(_MSC_VER)
+# pragma optimize("g", off)
+# pragma warning(push)
+# if _MSC_VER < RT_MSC_VER_VC120
+# pragma warning(disable: 4748)
+# endif
+#endif
+
+HRESULT showVMInfo(ComPtr<IVirtualBox> pVirtualBox,
+ ComPtr<IMachine> machine,
+ ComPtr<ISession> pSession,
+ VMINFO_DETAILS details /*= VMINFO_NONE*/)
+{
+ HRESULT rc;
+ ComPtr<IConsole> pConsole;
+ if (pSession)
+ pSession->COMGETTER(Console)(pConsole.asOutParam());
+
+ char szNm[80];
+ char szValue[256];
+
+#define SHOW_UTF8_STRING(a_pszMachine, a_pszHuman, a_szValue) \
+ do \
+ { \
+ Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \
+ if (details == VMINFO_MACHINEREADABLE) \
+ outputMachineReadableString(a_pszMachine, a_szValue); \
+ else \
+ RTPrintf("%-28s %s\n", a_pszHuman, a_szValue); \
+ } while (0)
+
+#define SHOW_BSTR_STRING(a_pszMachine, a_pszHuman, a_bstrValue) \
+ do \
+ { \
+ Assert(a_pszHuman[strlen(a_pszHuman) - 1] == ':'); \
+ if (details == VMINFO_MACHINEREADABLE) \
+ outputMachineReadableString(a_pszMachine, &a_bstrValue); \
+ else \
+ RTPrintf("%-28s %ls\n", a_pszHuman, a_bstrValue.raw()); \
+ } while (0)
+
+#define SHOW_BOOL_VALUE_EX(a_pszMachine, a_pszHuman, a_fValue, a_szTrue, a_szFalse) \
+ do \
+ { \
+ if (details == VMINFO_MACHINEREADABLE) \
+ outputMachineReadableString(a_pszMachine, a_fValue ? "on" : "off"); \
+ else \
+ RTPrintf("%-28s %s\n", a_pszHuman, a_fValue ? a_szTrue: a_szFalse); \
+ } while (0)
+
+#define SHOW_BOOL_VALUE(a_pszMachine, a_pszHuman, a_fValue) \
+ SHOW_BOOL_VALUE_EX(a_pszMachine, a_pszHuman, a_fValue, "enabled", "disabled")
+
+#define SHOW_ULONG_VALUE(a_pszMachine, a_pszHuman, a_uValue, a_pszUnit) \
+ do \
+ { \
+ if (details == VMINFO_MACHINEREADABLE) \
+ RTPrintf("%s=%u\n", a_pszMachine, a_uValue); \
+ else \
+ RTPrintf("%-28s %u%s\n", a_pszHuman, a_uValue, a_pszUnit); \
+ } while (0)
+
+#define SHOW_LONG64_VALUE(a_pszMachine, a_pszHuman, a_llValue, a_pszUnit) \
+ do \
+ { \
+ if (details == VMINFO_MACHINEREADABLE) \
+ RTPrintf("%s=%lld\n", a_pszMachine, a_llValue); \
+ else \
+ RTPrintf("%-28s %lld%s\n", a_pszHuman, a_llValue, a_pszUnit); \
+ } while (0)
+
+#define SHOW_BOOLEAN_PROP(a_pObj, a_Prop, a_pszMachine, a_pszHuman) \
+ SHOW_BOOLEAN_PROP_EX(a_pObj, a_Prop, a_pszMachine, a_pszHuman, "enabled", "disabled")
+
+#define SHOW_BOOLEAN_PROP_EX(a_pObj, a_Prop, a_pszMachine, a_pszHuman, a_szTrue, a_szFalse) \
+ do \
+ { \
+ BOOL f; \
+ CHECK_ERROR2I_RET(a_pObj, COMGETTER(a_Prop)(&f), hrcCheck); \
+ if (details == VMINFO_MACHINEREADABLE) \
+ outputMachineReadableString(a_pszMachine, f ? "on" : "off"); \
+ else \
+ RTPrintf("%-28s %s\n", a_pszHuman, f ? a_szTrue : a_szFalse); \
+ } while (0)
+
+#define SHOW_BOOLEAN_METHOD(a_pObj, a_Invocation, a_pszMachine, a_pszHuman) \
+ do \
+ { \
+ BOOL f; \
+ CHECK_ERROR2I_RET(a_pObj, a_Invocation, hrcCheck); \
+ if (details == VMINFO_MACHINEREADABLE) \
+ outputMachineReadableString(a_pszMachine, f ? "on" : "off"); \
+ else \
+ RTPrintf("%-28s %s\n", a_pszHuman, f ? "enabled" : "disabled"); \
+ } while (0)
+
+#define SHOW_STRING_PROP(a_pObj, a_Prop, a_pszMachine, a_pszHuman) \
+ do \
+ { \
+ Bstr bstr; \
+ CHECK_ERROR2I_RET(a_pObj, COMGETTER(a_Prop)(bstr.asOutParam()), hrcCheck); \
+ if (details == VMINFO_MACHINEREADABLE) \
+ outputMachineReadableString(a_pszMachine, &bstr); \
+ else \
+ RTPrintf("%-28s %ls\n", a_pszHuman, bstr.raw()); \
+ } while (0)
+
+#define SHOW_STRING_PROP_NOT_EMPTY(a_pObj, a_Prop, a_pszMachine, a_pszHuman) \
+ do \
+ { \
+ Bstr bstr; \
+ CHECK_ERROR2I_RET(a_pObj, COMGETTER(a_Prop)(bstr.asOutParam()), hrcCheck); \
+ if (bstr.isNotEmpty()) \
+ { \
+ if (details == VMINFO_MACHINEREADABLE) \
+ outputMachineReadableString(a_pszMachine, &bstr); \
+ else \
+ RTPrintf("%-28s %ls\n", a_pszHuman, bstr.raw()); \
+ } \
+ } while (0)
+
+ /** @def SHOW_STRING_PROP_MAJ
+ * For not breaking the output in a dot release we don't show default values. */
+#define SHOW_STRING_PROP_MAJ(a_pObj, a_Prop, a_pszMachine, a_pszHuman, a_pszUnless, a_uMajorVer) \
+ do \
+ { \
+ Bstr bstr; \
+ CHECK_ERROR2I_RET(a_pObj, COMGETTER(a_Prop)(bstr.asOutParam()), hrcCheck); \
+ if ((a_uMajorVer) <= VBOX_VERSION_MAJOR || !bstr.equals(a_pszUnless)) \
+ { \
+ if (details == VMINFO_MACHINEREADABLE)\
+ outputMachineReadableString(a_pszMachine, &bstr); \
+ else \
+ RTPrintf("%-28s %ls\n", a_pszHuman, bstr.raw()); \
+ } \
+ } while (0)
+
+#define SHOW_STRINGARRAY_PROP(a_pObj, a_Prop, a_pszMachine, a_pszHuman) \
+ do \
+ { \
+ SafeArray<BSTR> array; \
+ CHECK_ERROR2I_RET(a_pObj, COMGETTER(a_Prop)(ComSafeArrayAsOutParam(array)), hrcCheck); \
+ Utf8Str str; \
+ for (size_t i = 0; i < array.size(); i++) \
+ { \
+ if (i != 0) \
+ str.append(","); \
+ str.append(Utf8Str(array[i]).c_str()); \
+ } \
+ Bstr bstr(str); \
+ if (details == VMINFO_MACHINEREADABLE) \
+ outputMachineReadableString(a_pszMachine, &bstr); \
+ else \
+ RTPrintf("%-28s %ls\n", a_pszHuman, bstr.raw()); \
+ } while (0)
+
+#define SHOW_UUID_PROP(a_pObj, a_Prop, a_pszMachine, a_pszHuman) \
+ SHOW_STRING_PROP(a_pObj, a_Prop, a_pszMachine, a_pszHuman)
+
+#define SHOW_USHORT_PROP_EX2(a_pObj, a_Prop, a_pszMachine, a_pszHuman, a_pszUnit, a_szFmtMachine, a_szFmtHuman) \
+ do \
+ { \
+ USHORT u16 = 0; \
+ CHECK_ERROR2I_RET(a_pObj, COMGETTER(a_Prop)(&u16), hrcCheck); \
+ if (details == VMINFO_MACHINEREADABLE) \
+ RTPrintf("%s=" a_szFmtMachine "\n", a_pszMachine, u16); \
+ else \
+ RTPrintf("%-28s " a_szFmtHuman "%s\n", a_pszHuman, u16, u16, a_pszUnit); \
+ } while (0)
+
+#define SHOW_ULONG_PROP(a_pObj, a_Prop, a_pszMachine, a_pszHuman, a_pszUnit) \
+ do \
+ { \
+ ULONG u32 = 0; \
+ CHECK_ERROR2I_RET(a_pObj, COMGETTER(a_Prop)(&u32), hrcCheck); \
+ if (details == VMINFO_MACHINEREADABLE) \
+ RTPrintf("%s=%u\n", a_pszMachine, u32); \
+ else \
+ RTPrintf("%-28s %u%s\n", a_pszHuman, u32, a_pszUnit); \
+ } while (0)
+
+#define SHOW_LONG64_PROP(a_pObj, a_Prop, a_pszMachine, a_pszHuman, a_pszUnit) \
+ do \
+ { \
+ LONG64 i64 = 0; \
+ CHECK_ERROR2I_RET(a_pObj, COMGETTER(a_Prop)(&i64), hrcCheck); \
+ if (details == VMINFO_MACHINEREADABLE) \
+ RTPrintf("%s=%lld\n", a_pszMachine, i64); \
+ else \
+ RTPrintf("%-28s %'lld%s\n", a_pszHuman, i64, a_pszUnit); \
+ } while (0)
+
+ /*
+ * The rules for output in -argdump format:
+ * 1) the key part (the [0-9a-zA-Z_\-]+ string before the '=' delimiter)
+ * is all lowercase for "VBoxManage modifyvm" parameters. Any
+ * other values printed are in CamelCase.
+ * 2) strings (anything non-decimal) are printed surrounded by
+ * double quotes '"'. If the strings themselves contain double
+ * quotes, these characters are escaped by '\'. Any '\' character
+ * in the original string is also escaped by '\'.
+ * 3) numbers (containing just [0-9\-]) are written out unchanged.
+ */
+
+ BOOL fAccessible;
+ CHECK_ERROR2I_RET(machine, COMGETTER(Accessible)(&fAccessible), hrcCheck);
+ if (!fAccessible)
+ {
+ Bstr uuid;
+ machine->COMGETTER(Id)(uuid.asOutParam());
+ if (details == VMINFO_COMPACT)
+ RTPrintf("\"<inaccessible>\" {%s}\n", Utf8Str(uuid).c_str());
+ else
+ {
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("name=\"<inaccessible>\"\n");
+ else
+ RTPrintf("Name: <inaccessible!>\n");
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("UUID=\"%s\"\n", Utf8Str(uuid).c_str());
+ else
+ RTPrintf("UUID: %s\n", Utf8Str(uuid).c_str());
+ if (details != VMINFO_MACHINEREADABLE)
+ {
+ Bstr settingsFilePath;
+ rc = machine->COMGETTER(SettingsFilePath)(settingsFilePath.asOutParam());
+ RTPrintf("Config file: %ls\n", settingsFilePath.raw());
+ ComPtr<IVirtualBoxErrorInfo> accessError;
+ rc = machine->COMGETTER(AccessError)(accessError.asOutParam());
+ RTPrintf("Access error details:\n");
+ ErrorInfo ei(accessError);
+ GluePrintErrorInfo(ei);
+ RTPrintf("\n");
+ }
+ }
+ return S_OK;
+ }
+
+ if (details == VMINFO_COMPACT)
+ {
+ Bstr machineName;
+ machine->COMGETTER(Name)(machineName.asOutParam());
+ Bstr uuid;
+ machine->COMGETTER(Id)(uuid.asOutParam());
+
+ RTPrintf("\"%ls\" {%s}\n", machineName.raw(), Utf8Str(uuid).c_str());
+ return S_OK;
+ }
+
+ SHOW_STRING_PROP( machine, Name, "name", "Name:");
+ SHOW_STRINGARRAY_PROP( machine, Groups, "groups", "Groups:");
+ Bstr osTypeId;
+ CHECK_ERROR2I_RET(machine, COMGETTER(OSTypeId)(osTypeId.asOutParam()), hrcCheck);
+ ComPtr<IGuestOSType> osType;
+ pVirtualBox->GetGuestOSType(osTypeId.raw(), osType.asOutParam());
+ if (!osType.isNull())
+ SHOW_STRING_PROP( osType, Description, "ostype", "Guest OS:");
+ else
+ SHOW_STRING_PROP( machine, OSTypeId, "ostype", "Guest OS:");
+ SHOW_UUID_PROP( machine, Id, "UUID", "UUID:");
+ SHOW_STRING_PROP( machine, SettingsFilePath, "CfgFile", "Config file:");
+ SHOW_STRING_PROP( machine, SnapshotFolder, "SnapFldr", "Snapshot folder:");
+ SHOW_STRING_PROP( machine, LogFolder, "LogFldr", "Log folder:");
+ SHOW_UUID_PROP( machine, HardwareUUID, "hardwareuuid", "Hardware UUID:");
+ SHOW_ULONG_PROP( machine, MemorySize, "memory", "Memory size", "MB");
+ SHOW_BOOLEAN_PROP( machine, PageFusionEnabled, "pagefusion", "Page Fusion:");
+ SHOW_ULONG_PROP( machine, VRAMSize, "vram", "VRAM size:", "MB");
+ SHOW_ULONG_PROP( machine, CPUExecutionCap, "cpuexecutioncap", "CPU exec cap:", "%");
+ SHOW_BOOLEAN_PROP( machine, HPETEnabled, "hpet", "HPET:");
+ SHOW_STRING_PROP_MAJ( machine, CPUProfile, "cpu-profile", "CPUProfile:", "host", 6);
+
+ ChipsetType_T chipsetType;
+ CHECK_ERROR2I_RET(machine, COMGETTER(ChipsetType)(&chipsetType), hrcCheck);
+ const char *pszChipsetType;
+ switch (chipsetType)
+ {
+ case ChipsetType_Null: pszChipsetType = "invalid"; break;
+ case ChipsetType_PIIX3: pszChipsetType = "piix3"; break;
+ case ChipsetType_ICH9: pszChipsetType = "ich9"; break;
+ default: AssertFailed(); pszChipsetType = "unknown"; break;
+ }
+ SHOW_UTF8_STRING("chipset", "Chipset:", pszChipsetType);
+
+ FirmwareType_T firmwareType;
+ CHECK_ERROR2I_RET(machine, COMGETTER(FirmwareType)(&firmwareType), hrcCheck);
+ const char *pszFirmwareType;
+ switch (firmwareType)
+ {
+ case FirmwareType_BIOS: pszFirmwareType = "BIOS"; break;
+ case FirmwareType_EFI: pszFirmwareType = "EFI"; break;
+ case FirmwareType_EFI32: pszFirmwareType = "EFI32"; break;
+ case FirmwareType_EFI64: pszFirmwareType = "EFI64"; break;
+ case FirmwareType_EFIDUAL: pszFirmwareType = "EFIDUAL"; break;
+ default: AssertFailed(); pszFirmwareType = "unknown"; break;
+ }
+ SHOW_UTF8_STRING("firmware", "Firmware:", pszFirmwareType);
+
+ SHOW_ULONG_PROP( machine, CPUCount, "cpus", "Number of CPUs:", "");
+ 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_TripleFaultReset, &f), "triplefaultreset", "Triple Fault Reset:");
+ SHOW_BOOLEAN_METHOD( machine, GetCPUProperty(CPUPropertyType_APIC, &f), "apic", "APIC:");
+ SHOW_BOOLEAN_METHOD( machine, GetCPUProperty(CPUPropertyType_X2APIC, &f), "x2apic", "X2APIC:");
+ SHOW_BOOLEAN_METHOD( machine, GetCPUProperty(CPUPropertyType_HWVirt, &f), "nested-hw-virt", "Nested VT-x/AMD-V:");
+ SHOW_ULONG_PROP( machine, CPUIDPortabilityLevel, "cpuid-portability-level", "CPUID Portability Level:", "");
+
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("%-28s ", "CPUID overrides:");
+ ULONG uOrdinal = 0;
+ for (uOrdinal = 0; uOrdinal < _4K; uOrdinal++)
+ {
+ ULONG uLeaf, uSubLeaf, uEAX, uEBX, uECX, uEDX;
+ rc = machine->GetCPUIDLeafByOrdinal(uOrdinal, &uLeaf, &uSubLeaf, &uEAX, &uEBX, &uECX, &uEDX);
+ if (SUCCEEDED(rc))
+ {
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("cpuid=%08x,%08x,%08x,%08x,%08x,%08x", uLeaf, uSubLeaf, uEAX, uEBX, uECX, uEDX);
+ else
+ {
+ if (!uOrdinal)
+ RTPrintf("Leaf no. EAX EBX ECX EDX\n");
+ RTPrintf("%-28s %08x/%03x %08x %08x %08x %08x\n", "", uLeaf, uSubLeaf, uEAX, uEBX, uECX, uEDX);
+ }
+ }
+ else
+ {
+ if (rc != E_INVALIDARG)
+ com::GlueHandleComError(machine, "GetCPUIDLeaf", rc, __FILE__, __LINE__);
+ break;
+ }
+ }
+ if (!uOrdinal && details != VMINFO_MACHINEREADABLE)
+ RTPrintf("None\n");
+
+ ComPtr<IBIOSSettings> biosSettings;
+ CHECK_ERROR2I_RET(machine, COMGETTER(BIOSSettings)(biosSettings.asOutParam()), hrcCheck);
+
+ BIOSBootMenuMode_T bootMenuMode;
+ CHECK_ERROR2I_RET(biosSettings, COMGETTER(BootMenuMode)(&bootMenuMode), hrcCheck);
+ const char *pszBootMenu;
+ switch (bootMenuMode)
+ {
+ case BIOSBootMenuMode_Disabled:
+ pszBootMenu = "disabled";
+ break;
+ case BIOSBootMenuMode_MenuOnly:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszBootMenu = "menuonly";
+ else
+ pszBootMenu = "menu only";
+ break;
+ default:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszBootMenu = "messageandmenu";
+ else
+ pszBootMenu = "message and menu";
+ }
+ SHOW_UTF8_STRING("bootmenu", "Boot menu mode:", pszBootMenu);
+
+ ComPtr<ISystemProperties> systemProperties;
+ CHECK_ERROR2I_RET(pVirtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam()), hrcCheck);
+ ULONG maxBootPosition = 0;
+ CHECK_ERROR2I_RET(systemProperties, COMGETTER(MaxBootPosition)(&maxBootPosition), hrcCheck);
+ for (ULONG i = 1; i <= maxBootPosition; i++)
+ {
+ DeviceType_T bootOrder;
+ CHECK_ERROR2I_RET(machine, GetBootOrder(i, &bootOrder), hrcCheck);
+ const char *pszDevice;
+ if (bootOrder == DeviceType_Floppy)
+ pszDevice = details == VMINFO_MACHINEREADABLE ? "floppy" : "Floppy";
+ else if (bootOrder == DeviceType_DVD)
+ pszDevice = details == VMINFO_MACHINEREADABLE ? "dvd" : "DVD";
+ else if (bootOrder == DeviceType_HardDisk)
+ pszDevice = details == VMINFO_MACHINEREADABLE ? "disk" : "HardDisk";
+ else if (bootOrder == DeviceType_Network)
+ pszDevice = details == VMINFO_MACHINEREADABLE ? "net" : "Network";
+ else if (bootOrder == DeviceType_USB)
+ pszDevice = details == VMINFO_MACHINEREADABLE ? "usb" : "USB";
+ else if (bootOrder == DeviceType_SharedFolder)
+ pszDevice = details == VMINFO_MACHINEREADABLE ? "sharedfolder" : "Shared Folder";
+ else
+ pszDevice = details == VMINFO_MACHINEREADABLE ? "none" : "Not Assigned";
+ SHOW_UTF8_STRING(FmtNm(szNm, "boot%u", i), FmtNm(szNm, "Boot Device %u:", i), pszDevice);
+ }
+
+ SHOW_BOOLEAN_PROP(biosSettings, ACPIEnabled, "acpi", "ACPI:");
+ SHOW_BOOLEAN_PROP(biosSettings, IOAPICEnabled, "ioapic", "IOAPIC:");
+
+ APICMode_T apicMode;
+ CHECK_ERROR2I_RET(biosSettings, COMGETTER(APICMode)(&apicMode), hrcCheck);
+ const char *pszAPIC;
+ switch (apicMode)
+ {
+ case APICMode_Disabled:
+ pszAPIC = "disabled";
+ break;
+ case APICMode_APIC:
+ default:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszAPIC = "apic";
+ else
+ pszAPIC = "APIC";
+ break;
+ case APICMode_X2APIC:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszAPIC = "x2apic";
+ else
+ pszAPIC = "x2APIC";
+ break;
+ }
+ SHOW_UTF8_STRING("biosapic", "BIOS APIC mode:", pszAPIC);
+
+ 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_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.:");
+
+ ParavirtProvider_T paravirtProvider;
+ CHECK_ERROR2I_RET(machine, COMGETTER(ParavirtProvider)(&paravirtProvider), hrcCheck);
+ const char *pszParavirtProvider = paravirtProviderToString(paravirtProvider, details);
+ SHOW_UTF8_STRING("paravirtprovider", "Paravirt. Provider:", pszParavirtProvider);
+
+ ParavirtProvider_T effParavirtProvider;
+ CHECK_ERROR2I_RET(machine, GetEffectiveParavirtProvider(&effParavirtProvider), hrcCheck);
+ const char *pszEffParavirtProvider = paravirtProviderToString(effParavirtProvider, details);
+ SHOW_UTF8_STRING("effparavirtprovider", "Effective Paravirt. Prov.:", pszEffParavirtProvider);
+
+ Bstr paravirtDebug;
+ CHECK_ERROR2I_RET(machine, COMGETTER(ParavirtDebug)(paravirtDebug.asOutParam()), hrcCheck);
+ if (paravirtDebug.isNotEmpty())
+ SHOW_BSTR_STRING("paravirtdebug", "Paravirt. Debug:", paravirtDebug);
+
+ MachineState_T machineState;
+ CHECK_ERROR2I_RET(machine, COMGETTER(State)(&machineState), hrcCheck);
+ const char *pszState = machineStateToName(machineState, details == VMINFO_MACHINEREADABLE /*=fShort*/);
+
+ LONG64 stateSince;
+ machine->COMGETTER(LastStateChange)(&stateSince);
+ RTTIMESPEC timeSpec;
+ RTTimeSpecSetMilli(&timeSpec, stateSince);
+ char pszTime[30] = {0};
+ RTTimeSpecToString(&timeSpec, pszTime, sizeof(pszTime));
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ RTPrintf("VMState=\"%s\"\n", pszState);
+ RTPrintf("VMStateChangeTime=\"%s\"\n", pszTime);
+
+ Bstr stateFile;
+ machine->COMGETTER(StateFilePath)(stateFile.asOutParam());
+ if (!stateFile.isEmpty())
+ RTPrintf("VMStateFile=\"%ls\"\n", stateFile.raw());
+ }
+ else
+ RTPrintf("%-28s %s (since %s)\n", "State:", pszState, pszTime);
+
+ SHOW_ULONG_PROP( machine, MonitorCount, "monitorcount", "Monitor count:", "");
+ SHOW_BOOLEAN_PROP( machine, Accelerate3DEnabled, "accelerate3d", "3D Acceleration:");
+#ifdef VBOX_WITH_VIDEOHWACCEL
+ SHOW_BOOLEAN_PROP( machine, Accelerate2DVideoEnabled, "accelerate2dvideo", "2D Video Acceleration:");
+#endif
+ SHOW_BOOLEAN_PROP( machine, TeleporterEnabled, "teleporterenabled", "Teleporter Enabled:");
+ SHOW_ULONG_PROP( machine, TeleporterPort, "teleporterport", "Teleporter Port:", "");
+ SHOW_STRING_PROP( machine, TeleporterAddress, "teleporteraddress", "Teleporter Address:");
+ SHOW_STRING_PROP( machine, TeleporterPassword, "teleporterpassword", "Teleporter Password:");
+ SHOW_BOOLEAN_PROP( machine, TracingEnabled, "tracing-enabled", "Tracing Enabled:");
+ SHOW_BOOLEAN_PROP( machine, AllowTracingToAccessVM, "tracing-allow-vm-access", "Allow Tracing to Access VM:");
+ 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. */
+ /*
+ * Storage Controllers and their attached Mediums.
+ */
+ com::SafeIfaceArray<IStorageController> storageCtls;
+ CHECK_ERROR(machine, COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(storageCtls)));
+ for (size_t i = 0; i < storageCtls.size(); ++ i)
+ {
+ ComPtr<IStorageController> storageCtl = storageCtls[i];
+ StorageControllerType_T enmCtlType = StorageControllerType_Null;
+ const char *pszCtl = NULL;
+ ULONG ulValue = 0;
+ BOOL fBootable = FALSE;
+ Bstr storageCtlName;
+
+ storageCtl->COMGETTER(Name)(storageCtlName.asOutParam());
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("storagecontrollername%u=\"%ls\"\n", i, storageCtlName.raw());
+ else
+ RTPrintf("Storage Controller Name (%u): %ls\n", i, storageCtlName.raw());
+
+ storageCtl->COMGETTER(ControllerType)(&enmCtlType);
+ switch (enmCtlType)
+ {
+ case StorageControllerType_LsiLogic:
+ pszCtl = "LsiLogic";
+ break;
+ case StorageControllerType_LsiLogicSas:
+ pszCtl = "LsiLogicSas";
+ break;
+ case StorageControllerType_BusLogic:
+ pszCtl = "BusLogic";
+ break;
+ case StorageControllerType_IntelAhci:
+ pszCtl = "IntelAhci";
+ break;
+ case StorageControllerType_PIIX3:
+ pszCtl = "PIIX3";
+ break;
+ case StorageControllerType_PIIX4:
+ pszCtl = "PIIX4";
+ break;
+ case StorageControllerType_ICH6:
+ pszCtl = "ICH6";
+ break;
+ case StorageControllerType_I82078:
+ pszCtl = "I82078";
+ break;
+ case StorageControllerType_USB:
+ pszCtl = "USB";
+ break;
+
+ default:
+ pszCtl = "unknown";
+ }
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("storagecontrollertype%u=\"%s\"\n", i, pszCtl);
+ else
+ RTPrintf("Storage Controller Type (%u): %s\n", i, pszCtl);
+
+ storageCtl->COMGETTER(Instance)(&ulValue);
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("storagecontrollerinstance%u=\"%lu\"\n", i, ulValue);
+ else
+ RTPrintf("Storage Controller Instance Number (%u): %lu\n", i, ulValue);
+
+ storageCtl->COMGETTER(MaxPortCount)(&ulValue);
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("storagecontrollermaxportcount%u=\"%lu\"\n", i, ulValue);
+ else
+ RTPrintf("Storage Controller Max Port Count (%u): %lu\n", i, ulValue);
+
+ storageCtl->COMGETTER(PortCount)(&ulValue);
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("storagecontrollerportcount%u=\"%lu\"\n", i, ulValue);
+ else
+ RTPrintf("Storage Controller Port Count (%u): %lu\n", i, ulValue);
+
+ storageCtl->COMGETTER(Bootable)(&fBootable);
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("storagecontrollerbootable%u=\"%s\"\n", i, fBootable ? "on" : "off");
+ else
+ RTPrintf("Storage Controller Bootable (%u): %s\n", i, fBootable ? "on" : "off");
+ }
+
+ for (size_t j = 0; j < storageCtls.size(); ++ j)
+ {
+ ComPtr<IStorageController> storageCtl = storageCtls[j];
+ ComPtr<IMedium> medium;
+ Bstr storageCtlName;
+ Bstr filePath;
+ ULONG cDevices;
+ ULONG cPorts;
+
+ storageCtl->COMGETTER(Name)(storageCtlName.asOutParam());
+ storageCtl->COMGETTER(MaxDevicesPerPortCount)(&cDevices);
+ storageCtl->COMGETTER(PortCount)(&cPorts);
+
+ for (ULONG i = 0; i < cPorts; ++ i)
+ {
+ for (ULONG k = 0; k < cDevices; ++ k)
+ {
+ ComPtr<IMediumAttachment> mediumAttach;
+ machine->GetMediumAttachment(storageCtlName.raw(),
+ i, k,
+ mediumAttach.asOutParam());
+ BOOL fIsEjected = FALSE;
+ BOOL fTempEject = FALSE;
+ DeviceType_T devType = DeviceType_Null;
+ if (mediumAttach)
+ {
+ mediumAttach->COMGETTER(TemporaryEject)(&fTempEject);
+ mediumAttach->COMGETTER(IsEjected)(&fIsEjected);
+ mediumAttach->COMGETTER(Type)(&devType);
+ }
+ rc = machine->GetMedium(storageCtlName.raw(), i, k,
+ medium.asOutParam());
+ if (SUCCEEDED(rc) && medium)
+ {
+ BOOL fPassthrough = FALSE;
+
+ if (mediumAttach)
+ mediumAttach->COMGETTER(Passthrough)(&fPassthrough);
+
+ medium->COMGETTER(Location)(filePath.asOutParam());
+ Bstr uuid;
+ medium->COMGETTER(Id)(uuid.asOutParam());
+
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ RTPrintf("\"%ls-%d-%d\"=\"%ls\"\n", storageCtlName.raw(),
+ i, k, filePath.raw());
+ RTPrintf("\"%ls-ImageUUID-%d-%d\"=\"%s\"\n",
+ storageCtlName.raw(), i, k, Utf8Str(uuid).c_str());
+ if (fPassthrough)
+ RTPrintf("\"%ls-dvdpassthrough\"=\"%s\"\n", storageCtlName.raw(),
+ fPassthrough ? "on" : "off");
+ if (devType == DeviceType_DVD)
+ {
+ RTPrintf("\"%ls-tempeject\"=\"%s\"\n", storageCtlName.raw(),
+ fTempEject ? "on" : "off");
+ RTPrintf("\"%ls-IsEjected\"=\"%s\"\n", storageCtlName.raw(),
+ fIsEjected ? "on" : "off");
+ }
+ }
+ else
+ {
+ RTPrintf("%ls (%d, %d): %ls (UUID: %s)",
+ storageCtlName.raw(), i, k, filePath.raw(),
+ Utf8Str(uuid).c_str());
+ if (fPassthrough)
+ RTPrintf(" (passthrough enabled)");
+ if (fTempEject)
+ RTPrintf(" (temp eject)");
+ if (fIsEjected)
+ RTPrintf(" (ejected)");
+ RTPrintf("\n");
+ }
+ }
+ else if (SUCCEEDED(rc))
+ {
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ RTPrintf("\"%ls-%d-%d\"=\"emptydrive\"\n", storageCtlName.raw(), i, k);
+ if (devType == DeviceType_DVD)
+ RTPrintf("\"%ls-IsEjected\"=\"%s\"\n", storageCtlName.raw(),
+ fIsEjected ? "on" : "off");
+ }
+ else
+ {
+ RTPrintf("%ls (%d, %d): Empty", storageCtlName.raw(), i, k);
+ if (fTempEject)
+ RTPrintf(" (temp eject)");
+ if (fIsEjected)
+ RTPrintf(" (ejected)");
+ RTPrintf("\n");
+ }
+ }
+ else
+ {
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("\"%ls-%d-%d\"=\"none\"\n", storageCtlName.raw(), i, k);
+ }
+ }
+ }
+ }
+
+ /* get the maximum amount of NICS */
+ ULONG maxNICs = getMaxNics(pVirtualBox, machine);
+
+ for (ULONG currentNIC = 0; currentNIC < maxNICs; currentNIC++)
+ {
+ ComPtr<INetworkAdapter> nic;
+ rc = machine->GetNetworkAdapter(currentNIC, nic.asOutParam());
+ if (SUCCEEDED(rc) && nic)
+ {
+ FmtNm(szNm, details == VMINFO_MACHINEREADABLE ? "nic%u" : "NIC %u:", currentNIC + 1);
+
+ BOOL fEnabled;
+ nic->COMGETTER(Enabled)(&fEnabled);
+ if (!fEnabled)
+ {
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("%s=\"none\"\n", szNm);
+ else
+ RTPrintf("%-28s disabled\n", szNm);
+ }
+ else
+ {
+ Bstr strMACAddress;
+ nic->COMGETTER(MACAddress)(strMACAddress.asOutParam());
+ Utf8Str strAttachment;
+ Utf8Str strNatSettings = "";
+ Utf8Str strNatForwardings = "";
+ NetworkAttachmentType_T attachment;
+ nic->COMGETTER(AttachmentType)(&attachment);
+ switch (attachment)
+ {
+ case NetworkAttachmentType_Null:
+ if (details == VMINFO_MACHINEREADABLE)
+ strAttachment = "null";
+ else
+ strAttachment = "none";
+ break;
+
+ case NetworkAttachmentType_NAT:
+ {
+ Bstr strNetwork;
+ ComPtr<INATEngine> engine;
+ nic->COMGETTER(NATEngine)(engine.asOutParam());
+ engine->COMGETTER(Network)(strNetwork.asOutParam());
+ com::SafeArray<BSTR> forwardings;
+ engine->COMGETTER(Redirects)(ComSafeArrayAsOutParam(forwardings));
+ strNatForwardings = "";
+ for (size_t i = 0; i < forwardings.size(); ++i)
+ {
+ bool fSkip = false;
+ BSTR r = forwardings[i];
+ Utf8Str utf = Utf8Str(r);
+ Utf8Str strName;
+ Utf8Str strProto;
+ Utf8Str strHostPort;
+ Utf8Str strHostIP;
+ Utf8Str strGuestPort;
+ Utf8Str strGuestIP;
+ size_t pos, ppos;
+ pos = ppos = 0;
+ #define ITERATE_TO_NEXT_TERM(res, str, pos, ppos) \
+ do { \
+ pos = str.find(",", ppos); \
+ if (pos == Utf8Str::npos) \
+ { \
+ Log(( #res " extracting from %s is failed\n", str.c_str())); \
+ fSkip = true; \
+ } \
+ res = str.substr(ppos, pos - ppos); \
+ Log2((#res " %s pos:%d, ppos:%d\n", res.c_str(), pos, ppos)); \
+ ppos = pos + 1; \
+ } while (0)
+ ITERATE_TO_NEXT_TERM(strName, utf, pos, ppos);
+ if (fSkip) continue;
+ ITERATE_TO_NEXT_TERM(strProto, utf, pos, ppos);
+ if (fSkip) continue;
+ ITERATE_TO_NEXT_TERM(strHostIP, utf, pos, ppos);
+ if (fSkip) continue;
+ ITERATE_TO_NEXT_TERM(strHostPort, utf, pos, ppos);
+ if (fSkip) continue;
+ ITERATE_TO_NEXT_TERM(strGuestIP, utf, pos, ppos);
+ if (fSkip) continue;
+ strGuestPort = utf.substr(ppos, utf.length() - ppos);
+ #undef ITERATE_TO_NEXT_TERM
+ switch (strProto.toUInt32())
+ {
+ case NATProtocol_TCP:
+ strProto = "tcp";
+ break;
+ case NATProtocol_UDP:
+ strProto = "udp";
+ break;
+ default:
+ strProto = "unk";
+ break;
+ }
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ strNatForwardings = Utf8StrFmt("%sForwarding(%d)=\"%s,%s,%s,%s,%s,%s\"\n",
+ strNatForwardings.c_str(), i, strName.c_str(), strProto.c_str(),
+ strHostIP.c_str(), strHostPort.c_str(),
+ strGuestIP.c_str(), strGuestPort.c_str());
+ }
+ else
+ {
+ strNatForwardings = Utf8StrFmt("%sNIC %d Rule(%d): name = %s, protocol = %s,"
+ " host ip = %s, host port = %s, guest ip = %s, guest port = %s\n",
+ strNatForwardings.c_str(), currentNIC + 1, i, strName.c_str(), strProto.c_str(),
+ strHostIP.c_str(), strHostPort.c_str(),
+ strGuestIP.c_str(), strGuestPort.c_str());
+ }
+ }
+ ULONG mtu = 0;
+ ULONG sockSnd = 0;
+ ULONG sockRcv = 0;
+ ULONG tcpSnd = 0;
+ ULONG tcpRcv = 0;
+ engine->GetNetworkSettings(&mtu, &sockSnd, &sockRcv, &tcpSnd, &tcpRcv);
+
+/** @todo r=klaus dnsproxy etc needs to be dumped, too */
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ 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);
+ }
+ 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);
+ }
+ break;
+ }
+
+ case NetworkAttachmentType_Bridged:
+ {
+ Bstr strBridgeAdp;
+ nic->COMGETTER(BridgedInterface)(strBridgeAdp.asOutParam());
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ RTPrintf("bridgeadapter%d=\"%ls\"\n", currentNIC + 1, strBridgeAdp.raw());
+ strAttachment = "bridged";
+ }
+ else
+ strAttachment = Utf8StrFmt("Bridged Interface '%ls'", strBridgeAdp.raw());
+ break;
+ }
+
+ case NetworkAttachmentType_Internal:
+ {
+ Bstr strNetwork;
+ nic->COMGETTER(InternalNetwork)(strNetwork.asOutParam());
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ RTPrintf("intnet%d=\"%ls\"\n", currentNIC + 1, strNetwork.raw());
+ strAttachment = "intnet";
+ }
+ else
+ strAttachment = Utf8StrFmt("Internal Network '%s'", Utf8Str(strNetwork).c_str());
+ break;
+ }
+
+ case NetworkAttachmentType_HostOnly:
+ {
+ Bstr strHostonlyAdp;
+ nic->COMGETTER(HostOnlyInterface)(strHostonlyAdp.asOutParam());
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ RTPrintf("hostonlyadapter%d=\"%ls\"\n", currentNIC + 1, strHostonlyAdp.raw());
+ strAttachment = "hostonly";
+ }
+ else
+ strAttachment = Utf8StrFmt("Host-only Interface '%ls'", strHostonlyAdp.raw());
+ break;
+ }
+
+ case NetworkAttachmentType_Generic:
+ {
+ Bstr strGenericDriver;
+ nic->COMGETTER(GenericDriver)(strGenericDriver.asOutParam());
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ RTPrintf("generic%d=\"%ls\"\n", currentNIC + 1, strGenericDriver.raw());
+ strAttachment = "Generic";
+ }
+ else
+ {
+ strAttachment = Utf8StrFmt("Generic '%ls'", strGenericDriver.raw());
+
+ // show the generic properties
+ com::SafeArray<BSTR> aProperties;
+ com::SafeArray<BSTR> aValues;
+ rc = nic->GetProperties(NULL,
+ ComSafeArrayAsOutParam(aProperties),
+ ComSafeArrayAsOutParam(aValues));
+ if (SUCCEEDED(rc))
+ {
+ strAttachment += " { ";
+ for (unsigned i = 0; i < aProperties.size(); ++i)
+ strAttachment += Utf8StrFmt(!i ? "%ls='%ls'" : ", %ls='%ls'",
+ aProperties[i], aValues[i]);
+ strAttachment += " }";
+ }
+ }
+ 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;
+ }
+
+ /* cable connected */
+ BOOL fConnected;
+ nic->COMGETTER(CableConnected)(&fConnected);
+
+ /* promisc policy */
+ NetworkAdapterPromiscModePolicy_T enmPromiscModePolicy;
+ CHECK_ERROR2I_RET(nic, COMGETTER(PromiscModePolicy)(&enmPromiscModePolicy), hrcCheck);
+ const char *pszPromiscuousGuestPolicy;
+ switch (enmPromiscModePolicy)
+ {
+ case NetworkAdapterPromiscModePolicy_Deny: pszPromiscuousGuestPolicy = "deny"; break;
+ case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPromiscuousGuestPolicy = "allow-vms"; break;
+ case NetworkAdapterPromiscModePolicy_AllowAll: pszPromiscuousGuestPolicy = "allow-all"; break;
+ default: AssertFailedReturn(E_INVALIDARG);
+ }
+
+ /* trace stuff */
+ BOOL fTraceEnabled;
+ nic->COMGETTER(TraceEnabled)(&fTraceEnabled);
+ Bstr traceFile;
+ nic->COMGETTER(TraceFile)(traceFile.asOutParam());
+
+ /* NIC type */
+ NetworkAdapterType_T NICType;
+ nic->COMGETTER(AdapterType)(&NICType);
+ const char *pszNICType;
+ switch (NICType)
+ {
+ case NetworkAdapterType_Am79C970A: pszNICType = "Am79C970A"; break;
+ case NetworkAdapterType_Am79C973: pszNICType = "Am79C973"; break;
+#ifdef VBOX_WITH_E1000
+ case NetworkAdapterType_I82540EM: pszNICType = "82540EM"; break;
+ case NetworkAdapterType_I82543GC: pszNICType = "82543GC"; break;
+ case NetworkAdapterType_I82545EM: pszNICType = "82545EM"; break;
+#endif
+#ifdef VBOX_WITH_VIRTIO
+ case NetworkAdapterType_Virtio: pszNICType = "virtio"; break;
+#endif
+ default: AssertFailed(); pszNICType = "unknown"; break;
+ }
+
+ /* reported line speed */
+ ULONG ulLineSpeed;
+ nic->COMGETTER(LineSpeed)(&ulLineSpeed);
+
+ /* boot priority of the adapter */
+ ULONG ulBootPriority;
+ nic->COMGETTER(BootPriority)(&ulBootPriority);
+
+ /* bandwidth group */
+ ComObjPtr<IBandwidthGroup> pBwGroup;
+ Bstr strBwGroup;
+ nic->COMGETTER(BandwidthGroup)(pBwGroup.asOutParam());
+ if (!pBwGroup.isNull())
+ pBwGroup->COMGETTER(Name)(strBwGroup.asOutParam());
+
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ RTPrintf("macaddress%d=\"%ls\"\n", currentNIC + 1, strMACAddress.raw());
+ RTPrintf("cableconnected%d=\"%s\"\n", currentNIC + 1, fConnected ? "on" : "off");
+ RTPrintf("nic%d=\"%s\"\n", currentNIC + 1, strAttachment.c_str());
+ RTPrintf("nictype%d=\"%s\"\n", currentNIC + 1, pszNICType);
+ RTPrintf("nicspeed%d=\"%d\"\n", currentNIC + 1, ulLineSpeed);
+ }
+ else
+ RTPrintf("%-28s MAC: %ls, Attachment: %s, Cable connected: %s, Trace: %s (file: %ls), Type: %s, Reported speed: %d Mbps, Boot priority: %d, Promisc Policy: %s, Bandwidth group: %ls\n",
+ szNm, strMACAddress.raw(), strAttachment.c_str(),
+ fConnected ? "on" : "off",
+ fTraceEnabled ? "on" : "off",
+ traceFile.isEmpty() ? Bstr("none").raw() : traceFile.raw(),
+ pszNICType,
+ ulLineSpeed / 1000,
+ (int)ulBootPriority,
+ pszPromiscuousGuestPolicy,
+ strBwGroup.isEmpty() ? Bstr("none").raw() : strBwGroup.raw());
+ if (strNatSettings.length())
+ RTPrintf(strNatSettings.c_str());
+ if (strNatForwardings.length())
+ RTPrintf(strNatForwardings.c_str());
+ }
+ }
+ }
+
+ /* Pointing device information */
+ PointingHIDType_T aPointingHID;
+ const char *pszHID = "Unknown";
+ const char *pszMrHID = "unknown";
+ machine->COMGETTER(PointingHIDType)(&aPointingHID);
+ switch (aPointingHID)
+ {
+ case PointingHIDType_None:
+ pszHID = "None";
+ pszMrHID = "none";
+ break;
+ case PointingHIDType_PS2Mouse:
+ pszHID = "PS/2 Mouse";
+ pszMrHID = "ps2mouse";
+ break;
+ case PointingHIDType_USBMouse:
+ pszHID = "USB Mouse";
+ pszMrHID = "usbmouse";
+ break;
+ case PointingHIDType_USBTablet:
+ pszHID = "USB Tablet";
+ pszMrHID = "usbtablet";
+ break;
+ case PointingHIDType_ComboMouse:
+ pszHID = "USB Tablet and PS/2 Mouse";
+ pszMrHID = "combomouse";
+ break;
+ case PointingHIDType_USBMultiTouch:
+ pszHID = "USB Multi-Touch";
+ pszMrHID = "usbmultitouch";
+ break;
+ default:
+ break;
+ }
+ SHOW_UTF8_STRING("hidpointing", "Pointing Device:", details == VMINFO_MACHINEREADABLE ? pszMrHID : pszHID);
+
+ /* Keyboard device information */
+ KeyboardHIDType_T aKeyboardHID;
+ machine->COMGETTER(KeyboardHIDType)(&aKeyboardHID);
+ pszHID = "Unknown";
+ pszMrHID = "unknown";
+ switch (aKeyboardHID)
+ {
+ case KeyboardHIDType_None:
+ pszHID = "None";
+ pszMrHID = "none";
+ break;
+ case KeyboardHIDType_PS2Keyboard:
+ pszHID = "PS/2 Keyboard";
+ pszMrHID = "ps2kbd";
+ break;
+ case KeyboardHIDType_USBKeyboard:
+ pszHID = "USB Keyboard";
+ pszMrHID = "usbkbd";
+ break;
+ case KeyboardHIDType_ComboKeyboard:
+ pszHID = "USB and PS/2 Keyboard";
+ pszMrHID = "combokbd";
+ break;
+ default:
+ break;
+ }
+ SHOW_UTF8_STRING("hidkeyboard", "Keyboard Device:", details == VMINFO_MACHINEREADABLE ? pszMrHID : pszHID);
+
+ ComPtr<ISystemProperties> sysProps;
+ pVirtualBox->COMGETTER(SystemProperties)(sysProps.asOutParam());
+
+ /* get the maximum amount of UARTs */
+ ULONG maxUARTs = 0;
+ sysProps->COMGETTER(SerialPortCount)(&maxUARTs);
+ for (ULONG currentUART = 0; currentUART < maxUARTs; currentUART++)
+ {
+ ComPtr<ISerialPort> uart;
+ rc = machine->GetSerialPort(currentUART, uart.asOutParam());
+ if (SUCCEEDED(rc) && uart)
+ {
+ FmtNm(szNm, details == VMINFO_MACHINEREADABLE ? "uart%u" : "UART %u:", currentUART + 1);
+
+ /* show the config of this UART */
+ BOOL fEnabled;
+ uart->COMGETTER(Enabled)(&fEnabled);
+ if (!fEnabled)
+ {
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("%s=\"off\"\n", szNm);
+ else
+ RTPrintf("%-28s disabled\n", szNm);
+ }
+ else
+ {
+ ULONG ulIRQ, ulIOBase;
+ PortMode_T HostMode;
+ Bstr path;
+ BOOL fServer;
+ UartType_T UartType;
+ uart->COMGETTER(IRQ)(&ulIRQ);
+ uart->COMGETTER(IOBase)(&ulIOBase);
+ uart->COMGETTER(Path)(path.asOutParam());
+ uart->COMGETTER(Server)(&fServer);
+ uart->COMGETTER(HostMode)(&HostMode);
+ uart->COMGETTER(UartType)(&UartType);
+
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("%s=\"%#06x,%d\"\n", szNm, ulIOBase, ulIRQ);
+ else
+ RTPrintf("%-28s I/O base: %#06x, IRQ: %d", szNm, ulIOBase, ulIRQ);
+ switch (HostMode)
+ {
+ default:
+ case PortMode_Disconnected:
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("uartmode%d=\"disconnected\"\n", currentUART + 1);
+ else
+ RTPrintf(", disconnected");
+ break;
+ case PortMode_RawFile:
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("uartmode%d=\"file,%ls\"\n", currentUART + 1,
+ path.raw());
+ else
+ RTPrintf(", attached to raw file '%ls'\n",
+ path.raw());
+ break;
+ case PortMode_TCP:
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("uartmode%d=\"%s,%ls\"\n", currentUART + 1,
+ fServer ? "tcpserver" : "tcpclient", path.raw());
+ else
+ RTPrintf(", attached to tcp (%s) '%ls'",
+ fServer ? "server" : "client", path.raw());
+ break;
+ case PortMode_HostPipe:
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("uartmode%d=\"%s,%ls\"\n", currentUART + 1,
+ fServer ? "server" : "client", path.raw());
+ else
+ RTPrintf(", attached to pipe (%s) '%ls'",
+ fServer ? "server" : "client", path.raw());
+ break;
+ case PortMode_HostDevice:
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("uartmode%d=\"%ls\"\n", currentUART + 1,
+ path.raw());
+ else
+ RTPrintf(", attached to device '%ls'", path.raw());
+ break;
+ }
+ switch (UartType)
+ {
+ default:
+ case UartType_U16450:
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("uarttype%d=\"16450\"\n", currentUART + 1);
+ else
+ RTPrintf(", 16450\n");
+ break;
+ case UartType_U16550A:
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("uarttype%d=\"16550A\"\n", currentUART + 1);
+ else
+ RTPrintf(", 16550A\n");
+ break;
+ case UartType_U16750:
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("uarttype%d=\"16750\"\n", currentUART + 1);
+ else
+ RTPrintf(", 16750\n");
+ break;
+ }
+ }
+ }
+ }
+
+ /* get the maximum amount of LPTs */
+ ULONG maxLPTs = 0;
+ sysProps->COMGETTER(ParallelPortCount)(&maxLPTs);
+ for (ULONG currentLPT = 0; currentLPT < maxLPTs; currentLPT++)
+ {
+ ComPtr<IParallelPort> lpt;
+ rc = machine->GetParallelPort(currentLPT, lpt.asOutParam());
+ if (SUCCEEDED(rc) && lpt)
+ {
+ FmtNm(szNm, details == VMINFO_MACHINEREADABLE ? "lpt%u" : "LPT %u:", currentLPT + 1);
+
+ /* show the config of this LPT */
+ BOOL fEnabled;
+ lpt->COMGETTER(Enabled)(&fEnabled);
+ if (!fEnabled)
+ {
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("%s=\"off\"\n", szNm);
+ else
+ RTPrintf("%-28s disabled\n", szNm);
+ }
+ else
+ {
+ ULONG ulIRQ, ulIOBase;
+ Bstr path;
+ lpt->COMGETTER(IRQ)(&ulIRQ);
+ lpt->COMGETTER(IOBase)(&ulIOBase);
+ lpt->COMGETTER(Path)(path.asOutParam());
+
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("%s=\"%#06x,%d\"\n", szNm, ulIOBase, ulIRQ);
+ else
+ RTPrintf("%-28s I/O base: %#06x, IRQ: %d", szNm, ulIOBase, ulIRQ);
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("lptmode%d=\"%ls\"\n", currentLPT + 1, path.raw());
+ else
+ RTPrintf(", attached to device '%ls'\n", path.raw());
+ }
+ }
+ }
+
+ ComPtr<IAudioAdapter> AudioAdapter;
+ rc = machine->COMGETTER(AudioAdapter)(AudioAdapter.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ const char *pszDrv = "Unknown";
+ const char *pszCtrl = "Unknown";
+ const char *pszCodec = "Unknown";
+ BOOL fEnabled;
+ rc = AudioAdapter->COMGETTER(Enabled)(&fEnabled);
+ if (SUCCEEDED(rc) && fEnabled)
+ {
+ AudioDriverType_T enmDrvType;
+ rc = AudioAdapter->COMGETTER(AudioDriver)(&enmDrvType);
+ switch (enmDrvType)
+ {
+ case AudioDriverType_Null:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszDrv = "null";
+ else
+ pszDrv = "Null";
+ break;
+ case AudioDriverType_WinMM:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszDrv = "winmm";
+ else
+ pszDrv = "WINMM";
+ break;
+ case AudioDriverType_DirectSound:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszDrv = "dsound";
+ else
+ pszDrv = "DSOUND";
+ break;
+ case AudioDriverType_OSS:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszDrv = "oss";
+ else
+ pszDrv = "OSS";
+ break;
+ case AudioDriverType_ALSA:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszDrv = "alsa";
+ else
+ pszDrv = "ALSA";
+ break;
+ case AudioDriverType_Pulse:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszDrv = "pulse";
+ else
+ pszDrv = "PulseAudio";
+ break;
+ case AudioDriverType_CoreAudio:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszDrv = "coreaudio";
+ else
+ pszDrv = "CoreAudio";
+ break;
+ case AudioDriverType_SolAudio:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszDrv = "solaudio";
+ else
+ pszDrv = "SolAudio";
+ break;
+ default:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszDrv = "unknown";
+ break;
+ }
+ AudioControllerType_T enmCtrlType;
+ rc = AudioAdapter->COMGETTER(AudioController)(&enmCtrlType);
+ switch (enmCtrlType)
+ {
+ case AudioControllerType_AC97:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszCtrl = "ac97";
+ else
+ pszCtrl = "AC97";
+ break;
+ case AudioControllerType_SB16:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszCtrl = "sb16";
+ else
+ pszCtrl = "SB16";
+ break;
+ case AudioControllerType_HDA:
+ if (details == VMINFO_MACHINEREADABLE)
+ pszCtrl = "hda";
+ else
+ pszCtrl = "HDA";
+ break;
+ default:
+ break;
+ }
+ AudioCodecType_T enmCodecType;
+ rc = AudioAdapter->COMGETTER(AudioCodec)(&enmCodecType);
+ switch (enmCodecType)
+ {
+ case AudioCodecType_SB16:
+ pszCodec = "SB16";
+ break;
+ case AudioCodecType_STAC9700:
+ pszCodec = "STAC9700";
+ break;
+ case AudioCodecType_AD1980:
+ pszCodec = "AD1980";
+ break;
+ case AudioCodecType_STAC9221:
+ pszCodec = "STAC9221";
+ break;
+ case AudioCodecType_Null: break; /* Shut up MSC. */
+ default: break;
+ }
+ }
+ else
+ fEnabled = FALSE;
+
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("audio=\"%s\"\n", fEnabled ? pszDrv : "none");
+ else
+ {
+ RTPrintf("%-28s %s", "Audio:", fEnabled ? "enabled" : "disabled");
+ if (fEnabled)
+ RTPrintf(" (Driver: %s, Controller: %s, Codec: %s)", pszDrv, pszCtrl, pszCodec);
+ RTPrintf("\n");
+ }
+ SHOW_BOOLEAN_PROP(AudioAdapter, EnabledIn, "audio_in", "Audio playback:");
+ SHOW_BOOLEAN_PROP(AudioAdapter, EnabledOut, "audio_out", "Audio capture:");
+ }
+
+ /* Shared clipboard */
+ {
+ const char *psz;
+ ClipboardMode_T enmMode = (ClipboardMode_T)0;
+ rc = machine->COMGETTER(ClipboardMode)(&enmMode);
+ switch (enmMode)
+ {
+ case ClipboardMode_Disabled:
+ psz = "disabled";
+ break;
+ case ClipboardMode_HostToGuest:
+ psz = details == VMINFO_MACHINEREADABLE ? "hosttoguest" : "HostToGuest";
+ break;
+ case ClipboardMode_GuestToHost:
+ psz = details == VMINFO_MACHINEREADABLE ? "guesttohost" : "GuestToHost";
+ break;
+ case ClipboardMode_Bidirectional:
+ psz = details == VMINFO_MACHINEREADABLE ? "bidirectional" : "Bidirectional";
+ break;
+ default:
+ psz = details == VMINFO_MACHINEREADABLE ? "unknown" : "Unknown";
+ break;
+ }
+ SHOW_UTF8_STRING("clipboard", "Clipboard Mode:", psz);
+ }
+
+ /* Drag and drop */
+ {
+ const char *psz;
+ DnDMode_T enmMode;
+ rc = machine->COMGETTER(DnDMode)(&enmMode);
+ switch (enmMode)
+ {
+ case DnDMode_Disabled:
+ psz = "disabled";
+ break;
+ case DnDMode_HostToGuest:
+ psz = details == VMINFO_MACHINEREADABLE ? "hosttoguest" : "HostToGuest";
+ break;
+ case DnDMode_GuestToHost:
+ psz = details == VMINFO_MACHINEREADABLE ? "guesttohost" : "GuestToHost";
+ break;
+ case DnDMode_Bidirectional:
+ psz = details == VMINFO_MACHINEREADABLE ? "bidirectional" : "Bidirectional";
+ break;
+ default:
+ psz = details == VMINFO_MACHINEREADABLE ? "unknown" : "Unknown";
+ break;
+ }
+ SHOW_UTF8_STRING("draganddrop", "Drag and drop Mode:", psz);
+ }
+
+ {
+ SessionState_T sessState;
+ rc = machine->COMGETTER(SessionState)(&sessState);
+ if (SUCCEEDED(rc) && sessState != SessionState_Unlocked)
+ {
+ Bstr sessName;
+ rc = machine->COMGETTER(SessionName)(sessName.asOutParam());
+ if (SUCCEEDED(rc) && !sessName.isEmpty())
+ SHOW_BSTR_STRING("SessionName", "Session name:", sessName);
+ }
+ }
+
+ if (pConsole)
+ {
+ do
+ {
+ ComPtr<IDisplay> display;
+ rc = pConsole->COMGETTER(Display)(display.asOutParam());
+ if (rc == E_ACCESSDENIED || display.isNull())
+ break; /* VM not powered up */
+ if (FAILED(rc))
+ {
+ com::GlueHandleComError(pConsole, "COMGETTER(Display)(display.asOutParam())", rc, __FILE__, __LINE__);
+ return rc;
+ }
+ ULONG xRes, yRes, bpp;
+ LONG xOrigin, yOrigin;
+ GuestMonitorStatus_T monitorStatus;
+ rc = display->GetScreenResolution(0, &xRes, &yRes, &bpp, &xOrigin, &yOrigin, &monitorStatus);
+ if (rc == E_ACCESSDENIED)
+ break; /* VM not powered up */
+ if (FAILED(rc))
+ {
+ com::ErrorInfo info(display, COM_IIDOF(IDisplay));
+ GluePrintErrorInfo(info);
+ return rc;
+ }
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("VideoMode=\"%d,%d,%d\"@%d,%d %d\n", xRes, yRes, bpp, xOrigin, yOrigin, monitorStatus);
+ else
+ {
+ const char *pszMonitorStatus = "unknown status";
+ switch (monitorStatus)
+ {
+ case GuestMonitorStatus_Blank: pszMonitorStatus = "blank"; break;
+ case GuestMonitorStatus_Enabled: pszMonitorStatus = "enabled"; break;
+ case GuestMonitorStatus_Disabled: pszMonitorStatus = "disabled"; break;
+ default: break;
+ }
+ RTPrintf("%-28s %dx%dx%d at %d,%d %s\n", "Video mode:", xRes, yRes, bpp, xOrigin, yOrigin, pszMonitorStatus);
+ }
+ }
+ while (0);
+ }
+
+ /*
+ * Remote Desktop
+ */
+ ComPtr<IVRDEServer> vrdeServer;
+ rc = machine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ if (SUCCEEDED(rc) && vrdeServer)
+ {
+ BOOL fEnabled = false;
+ vrdeServer->COMGETTER(Enabled)(&fEnabled);
+ if (fEnabled)
+ {
+ LONG currentPort = -1;
+ Bstr ports;
+ vrdeServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), ports.asOutParam());
+ Bstr address;
+ vrdeServer->GetVRDEProperty(Bstr("TCP/Address").raw(), address.asOutParam());
+ BOOL fMultiCon;
+ vrdeServer->COMGETTER(AllowMultiConnection)(&fMultiCon);
+ BOOL fReuseCon;
+ vrdeServer->COMGETTER(ReuseSingleConnection)(&fReuseCon);
+ Bstr videoChannel;
+ vrdeServer->GetVRDEProperty(Bstr("VideoChannel/Enabled").raw(), videoChannel.asOutParam());
+ BOOL fVideoChannel = (videoChannel.compare(Bstr("true"), Bstr::CaseInsensitive)== 0)
+ || (videoChannel == "1");
+ Bstr videoChannelQuality;
+ vrdeServer->GetVRDEProperty(Bstr("VideoChannel/Quality").raw(), videoChannelQuality.asOutParam());
+ AuthType_T authType = (AuthType_T)0;
+ const char *strAuthType;
+ vrdeServer->COMGETTER(AuthType)(&authType);
+ switch (authType)
+ {
+ case AuthType_Null:
+ strAuthType = "null";
+ break;
+ case AuthType_External:
+ strAuthType = "external";
+ break;
+ case AuthType_Guest:
+ strAuthType = "guest";
+ break;
+ default:
+ strAuthType = "unknown";
+ break;
+ }
+ if (pConsole)
+ {
+ ComPtr<IVRDEServerInfo> vrdeServerInfo;
+ CHECK_ERROR_RET(pConsole, COMGETTER(VRDEServerInfo)(vrdeServerInfo.asOutParam()), rc);
+ if (!vrdeServerInfo.isNull())
+ {
+ rc = vrdeServerInfo->COMGETTER(Port)(&currentPort);
+ if (rc == E_ACCESSDENIED)
+ {
+ currentPort = -1; /* VM not powered up */
+ }
+ else if (FAILED(rc))
+ {
+ com::ErrorInfo info(vrdeServerInfo, COM_IIDOF(IVRDEServerInfo));
+ GluePrintErrorInfo(info);
+ return rc;
+ }
+ }
+ }
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ RTPrintf("vrde=\"on\"\n");
+ RTPrintf("vrdeport=%d\n", currentPort);
+ RTPrintf("vrdeports=\"%ls\"\n", ports.raw());
+ RTPrintf("vrdeaddress=\"%ls\"\n", address.raw());
+ RTPrintf("vrdeauthtype=\"%s\"\n", strAuthType);
+ RTPrintf("vrdemulticon=\"%s\"\n", fMultiCon ? "on" : "off");
+ RTPrintf("vrdereusecon=\"%s\"\n", fReuseCon ? "on" : "off");
+ RTPrintf("vrdevideochannel=\"%s\"\n", fVideoChannel ? "on" : "off");
+ if (fVideoChannel)
+ RTPrintf("vrdevideochannelquality=\"%ls\"\n", videoChannelQuality.raw());
+ }
+ else
+ {
+ if (address.isEmpty())
+ address = "0.0.0.0";
+ RTPrintf("%-28s enabled (Address %ls, Ports %ls, MultiConn: %s, ReuseSingleConn: %s, Authentication type: %s)\n",
+ "VRDE:", address.raw(), ports.raw(), fMultiCon ? "on" : "off", fReuseCon ? "on" : "off", strAuthType);
+ if (pConsole && currentPort != -1 && currentPort != 0)
+ RTPrintf("%-28s %d\n", "VRDE port:", currentPort);
+ if (fVideoChannel)
+ RTPrintf("%-28s enabled (Quality %ls)\n", "Video redirection:", videoChannelQuality.raw());
+ else
+ RTPrintf("%-28s disabled\n", "Video redirection:");
+ }
+ com::SafeArray<BSTR> aProperties;
+ if (SUCCEEDED(vrdeServer->COMGETTER(VRDEProperties)(ComSafeArrayAsOutParam(aProperties))))
+ {
+ unsigned i;
+ for (i = 0; i < aProperties.size(); ++i)
+ {
+ Bstr value;
+ vrdeServer->GetVRDEProperty(aProperties[i], value.asOutParam());
+ if (details == VMINFO_MACHINEREADABLE)
+ {
+ if (value.isEmpty())
+ RTPrintf("vrdeproperty[%ls]=<not set>\n", aProperties[i]);
+ else
+ RTPrintf("vrdeproperty[%ls]=\"%ls\"\n", aProperties[i], value.raw());
+ }
+ else
+ {
+ if (value.isEmpty())
+ RTPrintf("%-28s: %-10lS = <not set>\n", "VRDE property", aProperties[i]);
+ else
+ RTPrintf("%-28s: %-10lS = \"%ls\"\n", "VRDE property", aProperties[i], value.raw());
+ }
+ }
+ }
+ }
+ else
+ {
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("vrde=\"off\"\n");
+ else
+ RTPrintf("%-28s disabled\n", "VRDE:");
+ }
+ }
+
+ /*
+ * USB.
+ */
+ SafeIfaceArray<IUSBController> USBCtlColl;
+ rc = machine->COMGETTER(USBControllers)(ComSafeArrayAsOutParam(USBCtlColl));
+ if (SUCCEEDED(rc))
+ {
+ bool fOhciEnabled = false;
+ bool fEhciEnabled = false;
+ bool fXhciEnabled = 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;
+ case USBControllerType_XHCI:
+ fXhciEnabled = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ SHOW_BOOL_VALUE("usb", "OHCI USB:", fOhciEnabled);
+ SHOW_BOOL_VALUE("ehci", "EHCI USB:", fEhciEnabled);
+ SHOW_BOOL_VALUE("xhci", "xHCI USB:", fXhciEnabled);
+ }
+
+ ComPtr<IUSBDeviceFilters> USBFlts;
+ rc = machine->COMGETTER(USBDeviceFilters)(USBFlts.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ SafeIfaceArray <IUSBDeviceFilter> Coll;
+ rc = USBFlts->COMGETTER(DeviceFilters)(ComSafeArrayAsOutParam(Coll));
+ if (SUCCEEDED(rc))
+ {
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\nUSB Device Filters:\n\n");
+
+ if (Coll.size() == 0)
+ {
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("<none>\n\n");
+ }
+ else
+ {
+ for (size_t index = 0; index < Coll.size(); ++index)
+ {
+ ComPtr<IUSBDeviceFilter> DevPtr = Coll[index];
+
+ if (details != VMINFO_MACHINEREADABLE)
+ SHOW_UTF8_STRING("index", "Index:", FmtNm(szNm, "%zu", index));
+ SHOW_BOOLEAN_PROP_EX(DevPtr, Active, FmtNm(szNm, "USBFilterActive%zu", index + 1), "Active:", "yes", "no");
+ SHOW_STRING_PROP(DevPtr, Name, FmtNm(szNm, "USBFilterName%zu", index + 1), "Name:");
+ SHOW_STRING_PROP(DevPtr, VendorId, FmtNm(szNm, "USBFilterVendorId%zu", index + 1), "VendorId:");
+ SHOW_STRING_PROP(DevPtr, ProductId, FmtNm(szNm, "USBFilterProductId%zu", index + 1), "ProductId:");
+ SHOW_STRING_PROP(DevPtr, Revision, FmtNm(szNm, "USBFilterRevision%zu", index + 1), "Revision:");
+ SHOW_STRING_PROP(DevPtr, Manufacturer, FmtNm(szNm, "USBFilterManufacturer%zu", index + 1), "Manufacturer:");
+ SHOW_STRING_PROP(DevPtr, Product, FmtNm(szNm, "USBFilterProduct%zu", index + 1), "Product:");
+ SHOW_STRING_PROP(DevPtr, Remote, FmtNm(szNm, "USBFilterRemote%zu", index + 1), "Remote:");
+ SHOW_STRING_PROP(DevPtr, SerialNumber, FmtNm(szNm, "USBFilterSerialNumber%zu", index + 1), "Serial Number:");
+ if (details != VMINFO_MACHINEREADABLE)
+ {
+ ULONG fMaskedIfs;
+ CHECK_ERROR_RET(DevPtr, COMGETTER(MaskedInterfaces)(&fMaskedIfs), rc);
+ if (fMaskedIfs)
+ RTPrintf("%-28s %#010x\n", "Masked Interfaces:", fMaskedIfs);
+ RTPrintf("\n");
+ }
+ }
+ }
+ }
+
+ if (pConsole)
+ {
+ /* scope */
+ {
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("Available remote USB devices:\n\n");
+
+ SafeIfaceArray <IHostUSBDevice> coll;
+ CHECK_ERROR_RET(pConsole, COMGETTER(RemoteUSBDevices)(ComSafeArrayAsOutParam(coll)), rc);
+
+ if (coll.size() == 0)
+ {
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("<none>\n\n");
+ }
+ else
+ {
+ /* This code is duplicated below, with USBAttach as prefix. */
+ const char *pszPfx = "USBRemote";
+ for (size_t i = 0; i < coll.size(); ++i)
+ {
+ ComPtr<IHostUSBDevice> dev = coll[i];
+
+ SHOW_STRING_PROP(dev, Id, FmtNm(szNm, "%sActive%zu", pszPfx, i + 1), "UUID:");
+ SHOW_USHORT_PROP_EX2(dev, VendorId, FmtNm(szNm, "%sVendorId%zu", pszPfx, i + 1), "VendorId:", "", "%#06x", "%#06x (%04X)");
+ SHOW_USHORT_PROP_EX2(dev, ProductId, FmtNm(szNm, "%sProductId%zu", pszPfx, i + 1), "ProductId:", "", "%#06x", "%#06x (%04X)");
+
+ USHORT bcdRevision;
+ CHECK_ERROR_RET(dev, COMGETTER(Revision)(&bcdRevision), rc);
+ if (details == VMINFO_MACHINEREADABLE)
+ RTStrPrintf(szValue, sizeof(szValue), "%#04x%02x", bcdRevision >> 8, bcdRevision & 0xff);
+ else
+ RTStrPrintf(szValue, sizeof(szValue), "%u.%u (%02u%02u)\n",
+ bcdRevision >> 8, bcdRevision & 0xff, bcdRevision >> 8, bcdRevision & 0xff);
+ SHOW_UTF8_STRING(FmtNm(szNm, "%sRevision%zu", pszPfx, i + 1), "Revision:", szValue);
+
+ SHOW_STRING_PROP_NOT_EMPTY(dev, Manufacturer, FmtNm(szNm, "%sManufacturer%zu", pszPfx, i + 1), "Manufacturer:");
+ SHOW_STRING_PROP_NOT_EMPTY(dev, Product, FmtNm(szNm, "%sProduct%zu", pszPfx, i + 1), "Product:");
+ SHOW_STRING_PROP_NOT_EMPTY(dev, SerialNumber, FmtNm(szNm, "%sSerialNumber%zu", pszPfx, i + 1), "SerialNumber:");
+ SHOW_STRING_PROP_NOT_EMPTY(dev, Address, FmtNm(szNm, "%sAddress%zu", pszPfx, i + 1), "Address:");
+
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\n");
+ }
+ }
+ }
+
+ /* scope */
+ {
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("Currently Attached USB Devices:\n\n");
+
+ SafeIfaceArray <IUSBDevice> coll;
+ CHECK_ERROR_RET(pConsole, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)), rc);
+
+ if (coll.size() == 0)
+ {
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("<none>\n\n");
+ }
+ else
+ {
+ /* This code is duplicated below, with USBAttach as prefix. */
+ const char *pszPfx = "USBAttach";
+ for (size_t i = 0; i < coll.size(); ++i)
+ {
+ ComPtr<IHostUSBDevice> dev = coll[i];
+
+ SHOW_STRING_PROP(dev, Id, FmtNm(szNm, "%sActive%zu", pszPfx, i + 1), "UUID:");
+ SHOW_USHORT_PROP_EX2(dev, VendorId, FmtNm(szNm, "%sVendorId%zu", pszPfx, i + 1), "VendorId:", "", "%#06x", "%#06x (%04X)");
+ SHOW_USHORT_PROP_EX2(dev, ProductId, FmtNm(szNm, "%sProductId%zu", pszPfx, i + 1), "ProductId:", "", "%#06x", "%#06x (%04X)");
+
+ USHORT bcdRevision;
+ CHECK_ERROR_RET(dev, COMGETTER(Revision)(&bcdRevision), rc);
+ if (details == VMINFO_MACHINEREADABLE)
+ RTStrPrintf(szValue, sizeof(szValue), "%#04x%02x", bcdRevision >> 8, bcdRevision & 0xff);
+ else
+ RTStrPrintf(szValue, sizeof(szValue), "%u.%u (%02u%02u)\n",
+ bcdRevision >> 8, bcdRevision & 0xff, bcdRevision >> 8, bcdRevision & 0xff);
+ SHOW_UTF8_STRING(FmtNm(szNm, "%sRevision%zu", pszPfx, i + 1), "Revision:", szValue);
+
+ SHOW_STRING_PROP_NOT_EMPTY(dev, Manufacturer, FmtNm(szNm, "%sManufacturer%zu", pszPfx, i + 1), "Manufacturer:");
+ SHOW_STRING_PROP_NOT_EMPTY(dev, Product, FmtNm(szNm, "%sProduct%zu", pszPfx, i + 1), "Product:");
+ SHOW_STRING_PROP_NOT_EMPTY(dev, SerialNumber, FmtNm(szNm, "%sSerialNumber%zu", pszPfx, i + 1), "SerialNumber:");
+ SHOW_STRING_PROP_NOT_EMPTY(dev, Address, FmtNm(szNm, "%sAddress%zu", pszPfx, i + 1), "Address:");
+
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\n");
+ }
+ }
+ }
+ }
+ } /* USB */
+
+#ifdef VBOX_WITH_PCI_PASSTHROUGH
+ /* Host PCI passthrough devices */
+ {
+ SafeIfaceArray <IPCIDeviceAttachment> assignments;
+ rc = machine->COMGETTER(PCIDeviceAssignments)(ComSafeArrayAsOutParam(assignments));
+ if (SUCCEEDED(rc))
+ {
+ if (assignments.size() > 0 && (details != VMINFO_MACHINEREADABLE))
+ {
+ RTPrintf("\nAttached physical PCI devices:\n\n");
+ }
+
+ for (size_t index = 0; index < assignments.size(); ++index)
+ {
+ ComPtr<IPCIDeviceAttachment> Assignment = assignments[index];
+ char szHostPCIAddress[32], szGuestPCIAddress[32];
+ LONG iHostPCIAddress = -1, iGuestPCIAddress = -1;
+ Bstr DevName;
+
+ Assignment->COMGETTER(Name)(DevName.asOutParam());
+ Assignment->COMGETTER(HostAddress)(&iHostPCIAddress);
+ Assignment->COMGETTER(GuestAddress)(&iGuestPCIAddress);
+ PCIBusAddress().fromLong(iHostPCIAddress).format(szHostPCIAddress, sizeof(szHostPCIAddress));
+ PCIBusAddress().fromLong(iGuestPCIAddress).format(szGuestPCIAddress, sizeof(szGuestPCIAddress));
+
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("AttachedHostPCI=%s,%s\n", szHostPCIAddress, szGuestPCIAddress);
+ else
+ RTPrintf(" Host device %ls at %s attached as %s\n", DevName.raw(), szHostPCIAddress, szGuestPCIAddress);
+ }
+
+ if (assignments.size() > 0 && (details != VMINFO_MACHINEREADABLE))
+ {
+ RTPrintf("\n");
+ }
+ }
+ }
+ /* Host PCI passthrough devices */
+#endif
+
+ /*
+ * Bandwidth groups
+ */
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("Bandwidth groups: ");
+ {
+ ComPtr<IBandwidthControl> bwCtrl;
+ CHECK_ERROR_RET(machine, COMGETTER(BandwidthControl)(bwCtrl.asOutParam()), rc);
+
+ rc = showBandwidthGroups(bwCtrl, details);
+ }
+
+
+ /*
+ * Shared folders
+ */
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("Shared folders:");
+ uint32_t numSharedFolders = 0;
+#if 0 // not yet implemented
+ /* globally shared folders first */
+ {
+ SafeIfaceArray <ISharedFolder> sfColl;
+ CHECK_ERROR_RET(pVirtualBox, COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(sfColl)), rc);
+ for (size_t i = 0; i < sfColl.size(); ++i)
+ {
+ ComPtr<ISharedFolder> sf = sfColl[i];
+ showSharedFolder(sf, details, "global mapping", "GlobalMapping", i + 1, numSharedFolders == 0);
+ ++numSharedFolders;
+ }
+ }
+#endif
+ /* now VM mappings */
+ {
+ com::SafeIfaceArray <ISharedFolder> folders;
+ CHECK_ERROR_RET(machine, COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders)), rc);
+ for (size_t i = 0; i < folders.size(); ++i)
+ {
+ ComPtr<ISharedFolder> sf = folders[i];
+ showSharedFolder(sf, details, "machine mapping", "MachineMapping", i + 1, numSharedFolders == 0);
+ ++numSharedFolders;
+ }
+ }
+ /* transient mappings */
+ if (pConsole)
+ {
+ com::SafeIfaceArray <ISharedFolder> folders;
+ CHECK_ERROR_RET(pConsole, COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders)), rc);
+ for (size_t i = 0; i < folders.size(); ++i)
+ {
+ ComPtr<ISharedFolder> sf = folders[i];
+ showSharedFolder(sf, details, "transient mapping", "TransientMapping", i + 1, numSharedFolders == 0);
+ ++numSharedFolders;
+ }
+ }
+ if (!numSharedFolders && details != VMINFO_MACHINEREADABLE)
+ RTPrintf("<none>\n");
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\n");
+
+ if (pConsole)
+ {
+ /*
+ * Live VRDE info.
+ */
+ ComPtr<IVRDEServerInfo> vrdeServerInfo;
+ CHECK_ERROR_RET(pConsole, COMGETTER(VRDEServerInfo)(vrdeServerInfo.asOutParam()), rc);
+ BOOL fActive = FALSE;
+ ULONG cNumberOfClients = 0;
+ LONG64 BeginTime = 0;
+ LONG64 EndTime = 0;
+ LONG64 BytesSent = 0;
+ LONG64 BytesSentTotal = 0;
+ LONG64 BytesReceived = 0;
+ LONG64 BytesReceivedTotal = 0;
+ Bstr User;
+ Bstr Domain;
+ Bstr ClientName;
+ Bstr ClientIP;
+ ULONG ClientVersion = 0;
+ ULONG EncryptionStyle = 0;
+
+ if (!vrdeServerInfo.isNull())
+ {
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(Active)(&fActive), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(NumberOfClients)(&cNumberOfClients), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(BeginTime)(&BeginTime), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(EndTime)(&EndTime), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(BytesSent)(&BytesSent), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(BytesSentTotal)(&BytesSentTotal), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(BytesReceived)(&BytesReceived), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(BytesReceivedTotal)(&BytesReceivedTotal), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(User)(User.asOutParam()), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(Domain)(Domain.asOutParam()), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(ClientName)(ClientName.asOutParam()), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(ClientIP)(ClientIP.asOutParam()), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(ClientVersion)(&ClientVersion), rc);
+ CHECK_ERROR_RET(vrdeServerInfo, COMGETTER(EncryptionStyle)(&EncryptionStyle), rc);
+ }
+
+ SHOW_BOOL_VALUE_EX("VRDEActiveConnection", "VRDE Connection:", fActive, "active", "not active");
+ SHOW_ULONG_VALUE("VRDEClients=", "Clients so far:", cNumberOfClients, "");
+
+ if (cNumberOfClients > 0)
+ {
+ char szTimeValue[128];
+ makeTimeStr(szTimeValue, sizeof(szTimeValue), BeginTime);
+ if (fActive)
+ SHOW_UTF8_STRING("VRDEStartTime", "Start time:", szTimeValue);
+ else
+ {
+ SHOW_UTF8_STRING("VRDELastStartTime", "Last started:", szTimeValue);
+ makeTimeStr(szTimeValue, sizeof(szTimeValue), EndTime);
+ SHOW_UTF8_STRING("VRDELastEndTime", "Last ended:", szTimeValue);
+ }
+
+ int64_t ThroughputSend = 0;
+ int64_t ThroughputReceive = 0;
+ if (EndTime != BeginTime)
+ {
+ ThroughputSend = (BytesSent * 1000) / (EndTime - BeginTime);
+ ThroughputReceive = (BytesReceived * 1000) / (EndTime - BeginTime);
+ }
+ SHOW_LONG64_VALUE("VRDEBytesSent", "Sent:", BytesSent, "Bytes");
+ SHOW_LONG64_VALUE("VRDEThroughputSend", "Average speed:", ThroughputSend, "B/s");
+ SHOW_LONG64_VALUE("VRDEBytesSentTotal", "Sent total:", BytesSentTotal, "Bytes");
+
+ SHOW_LONG64_VALUE("VRDEBytesReceived", "Received:", BytesReceived, "Bytes");
+ SHOW_LONG64_VALUE("VRDEThroughputReceive", "Speed:", ThroughputReceive, "B/s");
+ SHOW_LONG64_VALUE("VRDEBytesReceivedTotal", "Received total:", BytesReceivedTotal, "Bytes");
+
+ if (fActive)
+ {
+ SHOW_BSTR_STRING("VRDEUserName", "User name:", User);
+ SHOW_BSTR_STRING("VRDEDomain", "Domain:", Domain);
+ SHOW_BSTR_STRING("VRDEClientName", "Client name:", ClientName);
+ SHOW_BSTR_STRING("VRDEClientIP", "Client IP:", ClientIP);
+ SHOW_ULONG_VALUE("VRDEClientVersion", "Client version:", ClientVersion, "");
+ SHOW_UTF8_STRING("VRDEEncryption", "Encryption:", EncryptionStyle == 0 ? "RDP4" : "RDP5 (X.509)");
+ }
+ }
+
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\n");
+ }
+
+#ifdef VBOX_WITH_RECORDING
+ {
+ /* Video capture */
+ BOOL fCaptureVideo = FALSE;
+# ifdef VBOX_WITH_AUDIO_RECORDING
+ BOOL fCaptureAudio = FALSE;
+# endif
+
+ ComPtr<IRecordingSettings> recordingSettings;
+ CHECK_ERROR_RET(machine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()), rc);
+
+ SafeIfaceArray <IRecordingScreenSettings> saRecordingScreenScreens;
+ CHECK_ERROR_RET(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saRecordingScreenScreens)), rc);
+
+ /* For now all screens have the same configuration; so take screen 0 and work with that. */
+ ULONG fFeatures;
+ CHECK_ERROR_RET(saRecordingScreenScreens[0], COMGETTER(Features)(&fFeatures), rc);
+ ULONG Width;
+ CHECK_ERROR_RET(saRecordingScreenScreens[0], COMGETTER(VideoWidth)(&Width), rc);
+ ULONG Height;
+ CHECK_ERROR_RET(saRecordingScreenScreens[0], COMGETTER(VideoHeight)(&Height), rc);
+ ULONG Rate;
+ CHECK_ERROR_RET(saRecordingScreenScreens[0], COMGETTER(VideoRate)(&Rate), rc);
+ ULONG Fps;
+ CHECK_ERROR_RET(saRecordingScreenScreens[0], COMGETTER(VideoFPS)(&Fps), rc);
+ Bstr bstrFile;
+ CHECK_ERROR_RET(saRecordingScreenScreens[0], COMGETTER(Filename)(bstrFile.asOutParam()), rc);
+ Bstr bstrOptions;
+ CHECK_ERROR_RET(saRecordingScreenScreens[0], COMGETTER(Options)(bstrOptions.asOutParam()), rc);
+
+ Utf8Str strOptions(bstrOptions);
+ size_t pos = 0;
+ com::Utf8Str key, value;
+ while ((pos = strOptions.parseKeyValue(key, value, pos)) != com::Utf8Str::npos)
+ {
+ if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
+ {
+ fCaptureVideo = value.compare("true", Utf8Str::CaseInsensitive) == 0;
+ }
+ else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
+ {
+# ifdef VBOX_WITH_AUDIO_RECORDING
+ fCaptureAudio = value.compare("true", Utf8Str::CaseInsensitive) == 0;
+# endif
+ }
+ }
+
+ SHOW_BOOL_VALUE_EX("videocap", "Capturing:", fCaptureVideo, "active", "not active");
+# ifdef VBOX_WITH_AUDIO_RECORDING
+ SHOW_BOOL_VALUE_EX("videocapaudio", "Capture audio:", fCaptureAudio, "active", "not active");
+# endif
+ szValue[0] = '\0';
+ for (size_t i = 0, off = 0; i < saRecordingScreenScreens.size(); i++)
+ {
+ BOOL fEnabled;
+ CHECK_ERROR_RET(saRecordingScreenScreens[i], COMGETTER(Enabled)(&fEnabled), rc);
+ if (fEnabled && off < sizeof(szValue) - 3)
+ off += RTStrPrintf(&szValue[off], sizeof(szValue) - off, off ? ",%zu" : "%zu", i);
+ }
+ SHOW_UTF8_STRING("capturescreens", "Capture screens:", szValue);
+ SHOW_BSTR_STRING("capturefilename", "Capture file:", bstrFile);
+ RTStrPrintf(szValue, sizeof(szValue), "%ux%u", Width, Height);
+ SHOW_UTF8_STRING("captureres", "Capture dimensions:", szValue);
+ SHOW_ULONG_VALUE("capturevideorate", "Capture rate:", Rate, "kbps");
+ SHOW_ULONG_VALUE("capturevideofps", "Capture FPS:", Fps, "kbps");
+ SHOW_BSTR_STRING("captureopts", "Capture options:", bstrOptions);
+
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\n");
+ /** @todo Add more audio capturing profile / information here. */
+ }
+#endif /* VBOX_WITH_RECORDING */
+
+ if ( details == VMINFO_STANDARD
+ || details == VMINFO_FULL
+ || details == VMINFO_MACHINEREADABLE)
+ {
+ Bstr description;
+ machine->COMGETTER(Description)(description.asOutParam());
+ if (!description.isEmpty())
+ {
+ if (details == VMINFO_MACHINEREADABLE)
+ outputMachineReadableString("description", &description);
+ else
+ RTPrintf("Description:\n%ls\n", description.raw());
+ }
+ }
+
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("Guest:\n\n");
+
+ SHOW_ULONG_PROP(machine, MemoryBalloonSize, "GuestMemoryBalloon", "Configured memory balloon size:", "MB");
+
+ if (pConsole)
+ {
+ ComPtr<IGuest> guest;
+ rc = pConsole->COMGETTER(Guest)(guest.asOutParam());
+ if (SUCCEEDED(rc) && !guest.isNull())
+ {
+ SHOW_STRING_PROP_NOT_EMPTY(guest, OSTypeId, "GuestOSType", "OS type:");
+
+ AdditionsRunLevelType_T guestRunLevel; /** @todo Add a runlevel-to-string (e.g. 0 = "None") method? */
+ rc = guest->COMGETTER(AdditionsRunLevel)(&guestRunLevel);
+ if (SUCCEEDED(rc))
+ SHOW_ULONG_VALUE("GuestAdditionsRunLevel", "Additions run level:", (ULONG)guestRunLevel, "");
+
+ Bstr guestString;
+ rc = guest->COMGETTER(AdditionsVersion)(guestString.asOutParam());
+ if ( SUCCEEDED(rc)
+ && !guestString.isEmpty())
+ {
+ ULONG uRevision;
+ rc = guest->COMGETTER(AdditionsRevision)(&uRevision);
+ if (FAILED(rc))
+ uRevision = 0;
+ RTStrPrintf(szValue, sizeof(szValue), "%ls r%u", guestString.raw(), uRevision);
+ SHOW_UTF8_STRING("GuestAdditionsVersion", "Additions version", szValue);
+ }
+
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\nGuest Facilities:\n\n");
+
+ /* Print information about known Guest Additions facilities: */
+ SafeIfaceArray <IAdditionsFacility> collFac;
+ CHECK_ERROR_RET(guest, COMGETTER(Facilities)(ComSafeArrayAsOutParam(collFac)), rc);
+ LONG64 lLastUpdatedMS;
+ char szLastUpdated[32];
+ AdditionsFacilityStatus_T curStatus;
+ for (size_t index = 0; index < collFac.size(); ++index)
+ {
+ ComPtr<IAdditionsFacility> fac = collFac[index];
+ if (fac)
+ {
+ CHECK_ERROR_RET(fac, COMGETTER(Name)(guestString.asOutParam()), rc);
+ if (!guestString.isEmpty())
+ {
+ CHECK_ERROR_RET(fac, COMGETTER(Status)(&curStatus), rc);
+ CHECK_ERROR_RET(fac, COMGETTER(LastUpdated)(&lLastUpdatedMS), rc);
+ if (details == VMINFO_MACHINEREADABLE)
+ RTPrintf("GuestAdditionsFacility_%ls=%u,%lld\n",
+ guestString.raw(), curStatus, lLastUpdatedMS);
+ else
+ {
+ makeTimeStr(szLastUpdated, sizeof(szLastUpdated), lLastUpdatedMS);
+ RTPrintf("Facility \"%ls\": %s (last update: %s)\n",
+ guestString.raw(), facilityStateToName(curStatus, false /* No short naming */), szLastUpdated);
+ }
+ }
+ else
+ AssertMsgFailed(("Facility with undefined name retrieved!\n"));
+ }
+ else
+ AssertMsgFailed(("Invalid facility returned!\n"));
+ }
+ if (!collFac.size() && details != VMINFO_MACHINEREADABLE)
+ RTPrintf("No active facilities.\n");
+ }
+ }
+
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\n");
+
+ /*
+ * snapshots
+ */
+ ComPtr<ISnapshot> snapshot;
+ rc = machine->FindSnapshot(Bstr().raw(), snapshot.asOutParam());
+ if (SUCCEEDED(rc) && snapshot)
+ {
+ ComPtr<ISnapshot> currentSnapshot;
+ rc = machine->COMGETTER(CurrentSnapshot)(currentSnapshot.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("Snapshots:\n\n");
+ showSnapshots(snapshot, currentSnapshot, details);
+ }
+ }
+
+ if (details != VMINFO_MACHINEREADABLE)
+ RTPrintf("\n");
+ return S_OK;
+}
+
+#if defined(_MSC_VER)
+# pragma optimize("", on)
+# pragma warning(pop)
+#endif
+
+static const RTGETOPTDEF g_aShowVMInfoOptions[] =
+{
+ { "--details", 'D', RTGETOPT_REQ_NOTHING },
+ { "-details", 'D', RTGETOPT_REQ_NOTHING }, // deprecated
+ { "--machinereadable", 'M', RTGETOPT_REQ_NOTHING },
+ { "-machinereadable", 'M', RTGETOPT_REQ_NOTHING }, // deprecated
+ { "--log", 'l', RTGETOPT_REQ_UINT32 },
+};
+
+RTEXITCODE handleShowVMInfo(HandlerArg *a)
+{
+ HRESULT rc;
+ const char *VMNameOrUuid = NULL;
+ bool fLog = false;
+ uint32_t uLogIdx = 0;
+ bool fDetails = false;
+ bool fMachinereadable = false;
+
+ 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_aShowVMInfoOptions, RT_ELEMENTS(g_aShowVMInfoOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'D': // --details
+ fDetails = true;
+ break;
+
+ case 'M': // --machinereadable
+ fMachinereadable = true;
+ break;
+
+ case 'l': // --log
+ fLog = true;
+ uLogIdx = ValueUnion.u32;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!VMNameOrUuid)
+ VMNameOrUuid = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_SHOWVMINFO, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ return errorGetOpt(USAGE_SHOWVMINFO, c, &ValueUnion);
+ }
+ }
+
+ /* check for required options */
+ if (!VMNameOrUuid)
+ return errorSyntax(USAGE_SHOWVMINFO, "VM name or UUID required");
+
+ /* try to find the given machine */
+ ComPtr<IMachine> machine;
+ CHECK_ERROR(a->virtualBox, FindMachine(Bstr(VMNameOrUuid).raw(),
+ machine.asOutParam()));
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ /* Printing the log is exclusive. */
+ if (fLog && (fMachinereadable || fDetails))
+ return errorSyntax(USAGE_SHOWVMINFO, "Option --log is exclusive");
+
+ if (fLog)
+ {
+ ULONG64 uOffset = 0;
+ SafeArray<BYTE> aLogData;
+ size_t cbLogData;
+ while (true)
+ {
+ /* Reset the array */
+ aLogData.setNull();
+ /* Fetch a chunk of the log file */
+ CHECK_ERROR_BREAK(machine, ReadLog(uLogIdx, uOffset, _1M,
+ ComSafeArrayAsOutParam(aLogData)));
+ cbLogData = aLogData.size();
+ if (cbLogData == 0)
+ break;
+ /* aLogData has a platform dependent line ending, standardize on
+ * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
+ * Windows. Otherwise we end up with CR/CR/LF on Windows. */
+ size_t cbLogDataPrint = cbLogData;
+ for (BYTE *s = aLogData.raw(), *d = s;
+ s - aLogData.raw() < (ssize_t)cbLogData;
+ s++, d++)
+ {
+ if (*s == '\r')
+ {
+ /* skip over CR, adjust destination */
+ d--;
+ cbLogDataPrint--;
+ }
+ else if (s != d)
+ *d = *s;
+ }
+ RTStrmWrite(g_pStdOut, aLogData.raw(), cbLogDataPrint);
+ uOffset += cbLogData;
+ }
+ }
+ else
+ {
+ /* 2nd option can be -details or -argdump */
+ VMINFO_DETAILS details = VMINFO_NONE;
+ if (fMachinereadable)
+ details = VMINFO_MACHINEREADABLE;
+ else if (fDetails)
+ details = VMINFO_FULL;
+ else
+ details = VMINFO_STANDARD;
+
+ /* open an existing session for the VM */
+ rc = machine->LockMachine(a->session, LockType_Shared);
+ if (SUCCEEDED(rc))
+ /* get the session machine */
+ rc = a->session->COMGETTER(Machine)(machine.asOutParam());
+
+ rc = showVMInfo(a->virtualBox, machine, a->session, details);
+
+ a->session->UnlockMachine();
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp
new file mode 100644
index 00000000..d5fb8ee2
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp
@@ -0,0 +1,1608 @@
+/* $Id: VBoxManageList.cpp $ */
+/** @file
+ * VBoxManage - The 'list' command.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 VBOX_ONLY_DOCS
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+
+#include <VBox/com/VirtualBox.h>
+
+#include <VBox/log.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/getopt.h>
+#include <iprt/ctype.h>
+
+#include <vector>
+#include <algorithm>
+
+#include "VBoxManage.h"
+using namespace com;
+
+#ifdef VBOX_WITH_HOSTNETIF_API
+static const char *getHostIfMediumTypeText(HostNetworkInterfaceMediumType_T enmType)
+{
+ switch (enmType)
+ {
+ case HostNetworkInterfaceMediumType_Ethernet: return "Ethernet";
+ case HostNetworkInterfaceMediumType_PPP: return "PPP";
+ case HostNetworkInterfaceMediumType_SLIP: return "SLIP";
+ case HostNetworkInterfaceMediumType_Unknown: return "Unknown";
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+ case HostNetworkInterfaceMediumType_32BitHack: break; /* Shut up compiler warnings. */
+#endif
+ }
+ return "unknown";
+}
+
+static const char *getHostIfStatusText(HostNetworkInterfaceStatus_T enmStatus)
+{
+ switch (enmStatus)
+ {
+ case HostNetworkInterfaceStatus_Up: return "Up";
+ case HostNetworkInterfaceStatus_Down: return "Down";
+ case HostNetworkInterfaceStatus_Unknown: return "Unknown";
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+ case HostNetworkInterfaceStatus_32BitHack: break; /* Shut up compiler warnings. */
+#endif
+ }
+ return "unknown";
+}
+#endif /* VBOX_WITH_HOSTNETIF_API */
+
+static const char*getDeviceTypeText(DeviceType_T enmType)
+{
+ switch (enmType)
+ {
+ case DeviceType_HardDisk: return "HardDisk";
+ case DeviceType_DVD: return "DVD";
+ case DeviceType_Floppy: return "Floppy";
+ /* Make MSC happy */
+ case DeviceType_Null: return "Null";
+ case DeviceType_Network: return "Network";
+ case DeviceType_USB: return "USB";
+ case DeviceType_SharedFolder: return "SharedFolder";
+ case DeviceType_Graphics3D: return "Graphics3D";
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+ case DeviceType_32BitHack: break; /* Shut up compiler warnings. */
+#endif
+ }
+ return "Unknown";
+}
+
+
+/**
+ * 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)
+{
+ HRESULT rc;
+ ComPtr<IHost> host;
+ CHECK_ERROR(pVirtualBox, COMGETTER(Host)(host.asOutParam()));
+ com::SafeIfaceArray<IHostNetworkInterface> hostNetworkInterfaces;
+#if defined(VBOX_WITH_NETFLT)
+ if (fIsBridged)
+ CHECK_ERROR(host, FindHostNetworkInterfacesOfType(HostNetworkInterfaceType_Bridged,
+ ComSafeArrayAsOutParam(hostNetworkInterfaces)));
+ else
+ CHECK_ERROR(host, FindHostNetworkInterfacesOfType(HostNetworkInterfaceType_HostOnly,
+ ComSafeArrayAsOutParam(hostNetworkInterfaces)));
+#else
+ RT_NOREF(fIsBridged);
+ CHECK_ERROR(host, COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(hostNetworkInterfaces)));
+#endif
+ for (size_t i = 0; i < hostNetworkInterfaces.size(); ++i)
+ {
+ ComPtr<IHostNetworkInterface> networkInterface = hostNetworkInterfaces[i];
+#ifndef VBOX_WITH_HOSTNETIF_API
+ Bstr interfaceName;
+ networkInterface->COMGETTER(Name)(interfaceName.asOutParam());
+ RTPrintf("Name: %ls\n", interfaceName.raw());
+ Guid interfaceGuid;
+ networkInterface->COMGETTER(Id)(interfaceGuid.asOutParam());
+ RTPrintf("GUID: %ls\n\n", Bstr(interfaceGuid.toString()).raw());
+#else /* VBOX_WITH_HOSTNETIF_API */
+ Bstr interfaceName;
+ networkInterface->COMGETTER(Name)(interfaceName.asOutParam());
+ RTPrintf("Name: %ls\n", interfaceName.raw());
+ Bstr interfaceGuid;
+ networkInterface->COMGETTER(Id)(interfaceGuid.asOutParam());
+ RTPrintf("GUID: %ls\n", interfaceGuid.raw());
+ BOOL bDHCPEnabled;
+ networkInterface->COMGETTER(DHCPEnabled)(&bDHCPEnabled);
+ RTPrintf("DHCP: %s\n", bDHCPEnabled ? "Enabled" : "Disabled");
+
+ Bstr IPAddress;
+ networkInterface->COMGETTER(IPAddress)(IPAddress.asOutParam());
+ RTPrintf("IPAddress: %ls\n", IPAddress.raw());
+ Bstr NetworkMask;
+ networkInterface->COMGETTER(NetworkMask)(NetworkMask.asOutParam());
+ RTPrintf("NetworkMask: %ls\n", NetworkMask.raw());
+ Bstr IPV6Address;
+ networkInterface->COMGETTER(IPV6Address)(IPV6Address.asOutParam());
+ RTPrintf("IPV6Address: %ls\n", IPV6Address.raw());
+ ULONG IPV6NetworkMaskPrefixLength;
+ networkInterface->COMGETTER(IPV6NetworkMaskPrefixLength)(&IPV6NetworkMaskPrefixLength);
+ RTPrintf("IPV6NetworkMaskPrefixLength: %d\n", IPV6NetworkMaskPrefixLength);
+ Bstr HardwareAddress;
+ networkInterface->COMGETTER(HardwareAddress)(HardwareAddress.asOutParam());
+ RTPrintf("HardwareAddress: %ls\n", HardwareAddress.raw());
+ HostNetworkInterfaceMediumType_T Type;
+ networkInterface->COMGETTER(MediumType)(&Type);
+ RTPrintf("MediumType: %s\n", getHostIfMediumTypeText(Type));
+ BOOL fWireless;
+ networkInterface->COMGETTER(Wireless)(&fWireless);
+ RTPrintf("Wireless: %s\n", fWireless ? "Yes" : "No");
+ HostNetworkInterfaceStatus_T Status;
+ networkInterface->COMGETTER(Status)(&Status);
+ RTPrintf("Status: %s\n", getHostIfStatusText(Status));
+ Bstr netName;
+ networkInterface->COMGETTER(NetworkName)(netName.asOutParam());
+ RTPrintf("VBoxNetworkName: %ls\n\n", netName.raw());
+#endif
+ }
+ return rc;
+}
+
+
+/**
+ * List host information.
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox smart pointer.
+ */
+static HRESULT listHostInfo(const ComPtr<IVirtualBox> pVirtualBox)
+{
+ static struct
+ {
+ ProcessorFeature_T feature;
+ const char *pszName;
+ } features[]
+ =
+ {
+ { ProcessorFeature_HWVirtEx, "HW virtualization" },
+ { ProcessorFeature_PAE, "PAE" },
+ { ProcessorFeature_LongMode, "long mode" },
+ { ProcessorFeature_NestedPaging, "nested paging" },
+ };
+ HRESULT rc;
+ ComPtr<IHost> Host;
+ CHECK_ERROR(pVirtualBox, COMGETTER(Host)(Host.asOutParam()));
+
+ RTPrintf("Host Information:\n\n");
+
+ LONG64 u64UtcTime = 0;
+ CHECK_ERROR(Host, COMGETTER(UTCTime)(&u64UtcTime));
+ RTTIMESPEC timeSpec;
+ char szTime[32];
+ RTPrintf("Host time: %s\n", RTTimeSpecToString(RTTimeSpecSetMilli(&timeSpec, u64UtcTime), szTime, sizeof(szTime)));
+
+ ULONG processorOnlineCount = 0;
+ CHECK_ERROR(Host, COMGETTER(ProcessorOnlineCount)(&processorOnlineCount));
+ RTPrintf("Processor online count: %lu\n", processorOnlineCount);
+ 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);
+ for (unsigned i = 0; i < RT_ELEMENTS(features); i++)
+ {
+ BOOL supported;
+ CHECK_ERROR(Host, GetProcessorFeature(features[i].feature, &supported));
+ RTPrintf("Processor supports %s: %s\n", features[i].pszName, supported ? "yes" : "no");
+ }
+ for (ULONG i = 0; i < processorCount; i++)
+ {
+ ULONG processorSpeed = 0;
+ CHECK_ERROR(Host, GetProcessorSpeed(i, &processorSpeed));
+ if (processorSpeed)
+ RTPrintf("Processor#%u speed: %lu MHz\n", i, processorSpeed);
+ else
+ RTPrintf("Processor#%u speed: unknown\n", i);
+ Bstr processorDescription;
+ CHECK_ERROR(Host, GetProcessorDescription(i, processorDescription.asOutParam()));
+ RTPrintf("Processor#%u description: %ls\n", i, processorDescription.raw());
+ }
+
+ ULONG memorySize = 0;
+ CHECK_ERROR(Host, COMGETTER(MemorySize)(&memorySize));
+ RTPrintf("Memory size: %lu MByte\n", memorySize);
+
+ ULONG memoryAvailable = 0;
+ CHECK_ERROR(Host, COMGETTER(MemoryAvailable)(&memoryAvailable));
+ RTPrintf("Memory available: %lu MByte\n", memoryAvailable);
+
+ Bstr operatingSystem;
+ CHECK_ERROR(Host, COMGETTER(OperatingSystem)(operatingSystem.asOutParam()));
+ RTPrintf("Operating system: %ls\n", operatingSystem.raw());
+
+ Bstr oSVersion;
+ CHECK_ERROR(Host, COMGETTER(OSVersion)(oSVersion.asOutParam()));
+ RTPrintf("Operating system version: %ls\n", oSVersion.raw());
+ return rc;
+}
+
+
+/**
+ * List media information.
+ *
+ * @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,
+ bool fOptLong)
+{
+ HRESULT rc = S_OK;
+ for (size_t i = 0; i < aMedia.size(); ++i)
+ {
+ ComPtr<IMedium> pMedium = aMedia[i];
+
+ rc = showMediumInfo(pVirtualBox, pMedium, pszParentUUIDStr, fOptLong);
+
+ 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(), fOptLong);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * List virtual image backends.
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox smart pointer.
+ */
+static HRESULT listHddBackends(const ComPtr<IVirtualBox> pVirtualBox)
+{
+ HRESULT rc;
+ ComPtr<ISystemProperties> systemProperties;
+ CHECK_ERROR(pVirtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam()));
+ com::SafeIfaceArray<IMediumFormat> mediumFormats;
+ CHECK_ERROR(systemProperties, COMGETTER(MediumFormats)(ComSafeArrayAsOutParam(mediumFormats)));
+
+ RTPrintf("Supported hard disk backends:\n\n");
+ for (size_t i = 0; i < mediumFormats.size(); ++i)
+ {
+ /* General information */
+ Bstr id;
+ CHECK_ERROR(mediumFormats[i], COMGETTER(Id)(id.asOutParam()));
+
+ Bstr description;
+ CHECK_ERROR(mediumFormats[i],
+ COMGETTER(Name)(description.asOutParam()));
+
+ ULONG caps = 0;
+ com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap;
+ CHECK_ERROR(mediumFormats[i],
+ 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;
+ CHECK_ERROR(mediumFormats[i],
+ DescribeFileExtensions(ComSafeArrayAsOutParam(fileExtensions), ComSafeArrayAsOutParam(deviceTypes)));
+ for (size_t j = 0; j < fileExtensions.size(); ++j)
+ {
+ RTPrintf("%ls (%s)", Bstr(fileExtensions[j]).raw(), getDeviceTypeText(deviceTypes[j]));
+ if (j != fileExtensions.size()-1)
+ RTPrintf(",");
+ }
+ RTPrintf("'");
+
+ /* Configuration keys */
+ 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),
+ ComSafeArrayAsOutParam(propertyTypes),
+ ComSafeArrayAsOutParam(propertyFlags),
+ ComSafeArrayAsOutParam(propertyDefaults)));
+
+ RTPrintf(" properties=(");
+ if (propertyNames.size() > 0)
+ {
+ for (size_t j = 0; j < propertyNames.size(); ++j)
+ {
+ RTPrintf("\n name='%ls' desc='%ls' type=",
+ Bstr(propertyNames[j]).raw(), Bstr(propertyDescriptions[j]).raw());
+ switch (propertyTypes[j])
+ {
+ case DataType_Int32: RTPrintf("int"); break;
+ case DataType_Int8: RTPrintf("byte"); break;
+ case DataType_String: RTPrintf("string"); break;
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+ case DataType_32BitHack: break; /* Shut up compiler warnings. */
+#endif
+ }
+ RTPrintf(" flags=%#04x", propertyFlags[j]);
+ RTPrintf(" default='%ls'", Bstr(propertyDefaults[j]).raw());
+ if (j != propertyNames.size()-1)
+ RTPrintf(", ");
+ }
+ }
+ RTPrintf(")\n");
+ }
+ return rc;
+}
+
+
+/**
+ * List USB devices attached to the host.
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox smart pointer.
+ */
+static HRESULT listUsbHost(const ComPtr<IVirtualBox> &pVirtualBox)
+{
+ HRESULT rc;
+ ComPtr<IHost> Host;
+ CHECK_ERROR_RET(pVirtualBox, COMGETTER(Host)(Host.asOutParam()), 1);
+
+ SafeIfaceArray<IHostUSBDevice> CollPtr;
+ CHECK_ERROR_RET(Host, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(CollPtr)), 1);
+
+ RTPrintf("Host USB Devices:\n\n");
+
+ if (CollPtr.size() == 0)
+ {
+ RTPrintf("<none>\n\n");
+ }
+ else
+ {
+ for (size_t i = 0; i < CollPtr.size(); ++i)
+ {
+ ComPtr<IHostUSBDevice> dev = CollPtr[i];
+
+ /* Query info. */
+ Bstr id;
+ CHECK_ERROR_RET(dev, COMGETTER(Id)(id.asOutParam()), 1);
+ USHORT usVendorId;
+ CHECK_ERROR_RET(dev, COMGETTER(VendorId)(&usVendorId), 1);
+ USHORT usProductId;
+ CHECK_ERROR_RET(dev, COMGETTER(ProductId)(&usProductId), 1);
+ USHORT bcdRevision;
+ CHECK_ERROR_RET(dev, COMGETTER(Revision)(&bcdRevision), 1);
+ USHORT usPort;
+ CHECK_ERROR_RET(dev, COMGETTER(Port)(&usPort), 1);
+ USHORT usVersion;
+ CHECK_ERROR_RET(dev, COMGETTER(Version)(&usVersion), 1);
+ USHORT usPortVersion;
+ CHECK_ERROR_RET(dev, COMGETTER(PortVersion)(&usPortVersion), 1);
+ USBConnectionSpeed_T enmSpeed;
+ CHECK_ERROR_RET(dev, COMGETTER(Speed)(&enmSpeed), 1);
+
+ RTPrintf("UUID: %s\n"
+ "VendorId: %#06x (%04X)\n"
+ "ProductId: %#06x (%04X)\n"
+ "Revision: %u.%u (%02u%02u)\n"
+ "Port: %u\n",
+ Utf8Str(id).c_str(),
+ usVendorId, usVendorId, usProductId, usProductId,
+ bcdRevision >> 8, bcdRevision & 0xff,
+ bcdRevision >> 8, bcdRevision & 0xff,
+ usPort);
+
+ const char *pszSpeed = "?";
+ switch (enmSpeed)
+ {
+ case USBConnectionSpeed_Low:
+ pszSpeed = "Low";
+ break;
+ case USBConnectionSpeed_Full:
+ pszSpeed = "Full";
+ break;
+ case USBConnectionSpeed_High:
+ pszSpeed = "High";
+ break;
+ case USBConnectionSpeed_Super:
+ pszSpeed = "Super";
+ break;
+ case USBConnectionSpeed_SuperPlus:
+ pszSpeed = "SuperPlus";
+ break;
+ default:
+ ASSERT(false);
+ break;
+ }
+
+ RTPrintf("USB version/speed: %u/%s\n", usVersion, pszSpeed);
+
+ /* optional stuff. */
+ SafeArray<BSTR> CollDevInfo;
+ Bstr bstr;
+ CHECK_ERROR_RET(dev, COMGETTER(DeviceInfo)(ComSafeArrayAsOutParam(CollDevInfo)), 1);
+ if (CollDevInfo.size() >= 1)
+ bstr = Bstr(CollDevInfo[0]);
+ if (!bstr.isEmpty())
+ RTPrintf("Manufacturer: %ls\n", bstr.raw());
+ if (CollDevInfo.size() >= 2)
+ bstr = Bstr(CollDevInfo[1]);
+ if (!bstr.isEmpty())
+ RTPrintf("Product: %ls\n", bstr.raw());
+ CHECK_ERROR_RET(dev, COMGETTER(SerialNumber)(bstr.asOutParam()), 1);
+ if (!bstr.isEmpty())
+ RTPrintf("SerialNumber: %ls\n", bstr.raw());
+ CHECK_ERROR_RET(dev, COMGETTER(Address)(bstr.asOutParam()), 1);
+ if (!bstr.isEmpty())
+ RTPrintf("Address: %ls\n", bstr.raw());
+
+ /* current state */
+ USBDeviceState_T state;
+ CHECK_ERROR_RET(dev, COMGETTER(State)(&state), 1);
+ const char *pszState = "?";
+ switch (state)
+ {
+ case USBDeviceState_NotSupported:
+ pszState = "Not supported";
+ break;
+ case USBDeviceState_Unavailable:
+ pszState = "Unavailable";
+ break;
+ case USBDeviceState_Busy:
+ pszState = "Busy";
+ break;
+ case USBDeviceState_Available:
+ pszState = "Available";
+ break;
+ case USBDeviceState_Held:
+ pszState = "Held";
+ break;
+ case USBDeviceState_Captured:
+ pszState = "Captured";
+ break;
+ default:
+ ASSERT(false);
+ break;
+ }
+ RTPrintf("Current State: %s\n\n", pszState);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * List USB filters.
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox smart pointer.
+ */
+static HRESULT listUsbFilters(const ComPtr<IVirtualBox> &pVirtualBox)
+{
+ HRESULT rc;
+
+ RTPrintf("Global USB Device Filters:\n\n");
+
+ ComPtr<IHost> host;
+ CHECK_ERROR_RET(pVirtualBox, COMGETTER(Host)(host.asOutParam()), 1);
+
+ SafeIfaceArray<IHostUSBDeviceFilter> coll;
+ CHECK_ERROR_RET(host, COMGETTER(USBDeviceFilters)(ComSafeArrayAsOutParam(coll)), 1);
+
+ if (coll.size() == 0)
+ {
+ RTPrintf("<none>\n\n");
+ }
+ else
+ {
+ for (size_t index = 0; index < coll.size(); ++index)
+ {
+ ComPtr<IHostUSBDeviceFilter> flt = coll[index];
+
+ /* Query info. */
+
+ RTPrintf("Index: %zu\n", index);
+
+ BOOL active = FALSE;
+ CHECK_ERROR_RET(flt, COMGETTER(Active)(&active), 1);
+ RTPrintf("Active: %s\n", active ? "yes" : "no");
+
+ USBDeviceFilterAction_T action;
+ CHECK_ERROR_RET(flt, COMGETTER(Action)(&action), 1);
+ const char *pszAction = "<invalid>";
+ switch (action)
+ {
+ case USBDeviceFilterAction_Ignore:
+ pszAction = "Ignore";
+ break;
+ case USBDeviceFilterAction_Hold:
+ pszAction = "Hold";
+ break;
+ default:
+ break;
+ }
+ RTPrintf("Action: %s\n", pszAction);
+
+ Bstr bstr;
+ CHECK_ERROR_RET(flt, COMGETTER(Name)(bstr.asOutParam()), 1);
+ RTPrintf("Name: %ls\n", bstr.raw());
+ CHECK_ERROR_RET(flt, COMGETTER(VendorId)(bstr.asOutParam()), 1);
+ RTPrintf("VendorId: %ls\n", bstr.raw());
+ CHECK_ERROR_RET(flt, COMGETTER(ProductId)(bstr.asOutParam()), 1);
+ RTPrintf("ProductId: %ls\n", bstr.raw());
+ CHECK_ERROR_RET(flt, COMGETTER(Revision)(bstr.asOutParam()), 1);
+ RTPrintf("Revision: %ls\n", bstr.raw());
+ CHECK_ERROR_RET(flt, COMGETTER(Manufacturer)(bstr.asOutParam()), 1);
+ RTPrintf("Manufacturer: %ls\n", bstr.raw());
+ CHECK_ERROR_RET(flt, COMGETTER(Product)(bstr.asOutParam()), 1);
+ RTPrintf("Product: %ls\n", bstr.raw());
+ CHECK_ERROR_RET(flt, COMGETTER(SerialNumber)(bstr.asOutParam()), 1);
+ RTPrintf("Serial Number: %ls\n\n", bstr.raw());
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * List system properties.
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox smart pointer.
+ */
+static HRESULT listSystemProperties(const ComPtr<IVirtualBox> &pVirtualBox)
+{
+ ComPtr<ISystemProperties> systemProperties;
+ CHECK_ERROR2I_RET(pVirtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam()), hrcCheck);
+
+ Bstr str;
+ ULONG ulValue;
+ LONG64 i64Value;
+ BOOL fValue;
+ const char *psz;
+
+ pVirtualBox->COMGETTER(APIVersion)(str.asOutParam());
+ RTPrintf("API version: %ls\n", str.raw());
+
+ systemProperties->COMGETTER(MinGuestRAM)(&ulValue);
+ RTPrintf("Minimum guest RAM size: %u Megabytes\n", ulValue);
+ systemProperties->COMGETTER(MaxGuestRAM)(&ulValue);
+ RTPrintf("Maximum guest RAM size: %u Megabytes\n", ulValue);
+ systemProperties->COMGETTER(MinGuestVRAM)(&ulValue);
+ 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);
+ RTPrintf("Maximum guest CPU count: %u\n", ulValue);
+ systemProperties->COMGETTER(InfoVDSize)(&i64Value);
+ RTPrintf("Virtual disk limit (info): %lld Bytes\n", i64Value);
+ systemProperties->COMGETTER(SerialPortCount)(&ulValue);
+ RTPrintf("Maximum Serial Port count: %u\n", ulValue);
+ systemProperties->COMGETTER(ParallelPortCount)(&ulValue);
+ RTPrintf("Maximum Parallel Port count: %u\n", ulValue);
+ systemProperties->COMGETTER(MaxBootPosition)(&ulValue);
+ RTPrintf("Maximum Boot Position: %u\n", ulValue);
+ systemProperties->GetMaxNetworkAdapters(ChipsetType_PIIX3, &ulValue);
+ RTPrintf("Maximum PIIX3 Network Adapter count: %u\n", ulValue);
+ systemProperties->GetMaxNetworkAdapters(ChipsetType_ICH9, &ulValue);
+ RTPrintf("Maximum ICH9 Network Adapter count: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_IDE, &ulValue);
+ RTPrintf("Maximum PIIX3 IDE Controllers: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_IDE, &ulValue);
+ RTPrintf("Maximum ICH9 IDE Controllers: %u\n", ulValue);
+ systemProperties->GetMaxPortCountForStorageBus(StorageBus_IDE, &ulValue);
+ RTPrintf("Maximum IDE Port count: %u\n", ulValue);
+ systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_IDE, &ulValue);
+ RTPrintf("Maximum Devices per IDE Port: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_SATA, &ulValue);
+ RTPrintf("Maximum PIIX3 SATA Controllers: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_SATA, &ulValue);
+ RTPrintf("Maximum ICH9 SATA Controllers: %u\n", ulValue);
+ systemProperties->GetMaxPortCountForStorageBus(StorageBus_SATA, &ulValue);
+ RTPrintf("Maximum SATA Port count: %u\n", ulValue);
+ systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_SATA, &ulValue);
+ RTPrintf("Maximum Devices per SATA Port: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_SCSI, &ulValue);
+ RTPrintf("Maximum PIIX3 SCSI Controllers: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_SCSI, &ulValue);
+ RTPrintf("Maximum ICH9 SCSI Controllers: %u\n", ulValue);
+ systemProperties->GetMaxPortCountForStorageBus(StorageBus_SCSI, &ulValue);
+ RTPrintf("Maximum SCSI Port count: %u\n", ulValue);
+ systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_SCSI, &ulValue);
+ RTPrintf("Maximum Devices per SCSI Port: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_SAS, &ulValue);
+ RTPrintf("Maximum SAS PIIX3 Controllers: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_SAS, &ulValue);
+ RTPrintf("Maximum SAS ICH9 Controllers: %u\n", ulValue);
+ systemProperties->GetMaxPortCountForStorageBus(StorageBus_SAS, &ulValue);
+ RTPrintf("Maximum SAS Port count: %u\n", ulValue);
+ systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_SAS, &ulValue);
+ RTPrintf("Maximum Devices per SAS Port: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_PCIe, &ulValue);
+ RTPrintf("Maximum NVMe PIIX3 Controllers: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_PCIe, &ulValue);
+ RTPrintf("Maximum NVMe ICH9 Controllers: %u\n", ulValue);
+ systemProperties->GetMaxPortCountForStorageBus(StorageBus_PCIe, &ulValue);
+ RTPrintf("Maximum NVMe Port count: %u\n", ulValue);
+ systemProperties->GetMaxDevicesPerPortForStorageBus(StorageBus_PCIe, &ulValue);
+ RTPrintf("Maximum Devices per NVMe Port: %u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_PIIX3, StorageBus_Floppy, &ulValue);
+ RTPrintf("Maximum PIIX3 Floppy Controllers:%u\n", ulValue);
+ systemProperties->GetMaxInstancesOfStorageBus(ChipsetType_ICH9, StorageBus_Floppy, &ulValue);
+ RTPrintf("Maximum ICH9 Floppy Controllers: %u\n", ulValue);
+ systemProperties->GetMaxPortCountForStorageBus(StorageBus_Floppy, &ulValue);
+ 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(RawModeSupported)(&fValue);
+ RTPrintf("Raw-mode Supported: %s\n", fValue ? "yes" : "no");
+ systemProperties->COMGETTER(ExclusiveHwVirt)(&fValue);
+ RTPrintf("Exclusive HW virtualization use: %s\n", fValue ? "on" : "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());
+ RTPrintf("Webservice auth. library: %ls\n", str.raw());
+ systemProperties->COMGETTER(DefaultVRDEExtPack)(str.asOutParam());
+ 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());
+ AudioDriverType_T enmAudio;
+ systemProperties->COMGETTER(DefaultAudioDriver)(&enmAudio);
+ switch (enmAudio)
+ {
+ case AudioDriverType_Null: psz = "Null"; break;
+ case AudioDriverType_WinMM: psz = "WinMM"; break;
+ case AudioDriverType_OSS: psz = "OSS"; break;
+ case AudioDriverType_ALSA: psz = "ALSA"; break;
+ case AudioDriverType_DirectSound: psz = "DirectSound"; break;
+ case AudioDriverType_CoreAudio: psz = "CoreAudio"; break;
+ case AudioDriverType_MMPM: psz = "MMPM"; break;
+ case AudioDriverType_Pulse: psz = "Pulse"; break;
+ case AudioDriverType_SolAudio: psz = "SolAudio"; break;
+ default: psz = "Unknown";
+ }
+ RTPrintf("Default audio driver: %s\n", psz);
+ systemProperties->COMGETTER(AutostartDatabasePath)(str.asOutParam());
+ RTPrintf("Autostart database path: %ls\n", str.raw());
+ systemProperties->COMGETTER(DefaultAdditionsISO)(str.asOutParam());
+ RTPrintf("Default Guest Additions ISO: %ls\n", str.raw());
+ systemProperties->COMGETTER(LoggingLevel)(str.asOutParam());
+ RTPrintf("Logging Level: %ls\n", str.raw());
+ ProxyMode_T enmProxyMode = (ProxyMode_T)42;
+ systemProperties->COMGETTER(ProxyMode)(&enmProxyMode);
+ psz = "Unknown";
+ switch (enmProxyMode)
+ {
+ case ProxyMode_System: psz = "System"; break;
+ case ProxyMode_NoProxy: psz = "NoProxy"; break;
+ case ProxyMode_Manual: psz = "Manual"; break;
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+ case ProxyMode_32BitHack: break; /* Shut up compiler warnings. */
+#endif
+ }
+ RTPrintf("Proxy Mode: %s\n", psz);
+ systemProperties->COMGETTER(ProxyURL)(str.asOutParam());
+ RTPrintf("Proxy URL: %ls\n", str.raw());
+ return S_OK;
+}
+
+
+/**
+ * Helper function for querying and displaying DHCP option for an adapter.
+ *
+ * @returns See produceList.
+ * @param pSrv Smart pointer to IDHCPServer.
+ * @param vmSlot String identifying the adapter, like '[vmname]:slot'
+ */
+static HRESULT listVmSlotDhcpOptions(const ComPtr<IDHCPServer> pSrv, const Utf8Str& vmSlot)
+{
+ RTCList<RTCString> lstParts = vmSlot.split(":");
+ if (lstParts.size() < 2)
+ return E_INVALIDARG;
+ if (lstParts[0].length() < 2 || !lstParts[0].startsWith("[") || !lstParts[0].endsWith("]"))
+ return E_INVALIDARG;
+ Bstr vmName(lstParts[0].substr(1, lstParts[0].length()-2));
+ ULONG uSlot = lstParts[1].toUInt32();
+ com::SafeArray<BSTR> options;
+ CHECK_ERROR2I_RET(pSrv, GetVmSlotOptions(vmName.raw(), uSlot, ComSafeArrayAsOutParam(options)), hrcCheck);
+ if (options.size())
+ RTPrintf("Options for NIC %d of '%ls':\n", uSlot + 1, vmName.raw());
+ for (size_t i = 0; i < options.size(); ++i)
+ RTPrintf(" %ls\n", options[i]);
+
+ return S_OK;
+}
+
+
+/**
+ * List DHCP servers.
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox smart pointer.
+ */
+static HRESULT listDhcpServers(const ComPtr<IVirtualBox> &pVirtualBox)
+{
+ HRESULT rc = S_OK;
+ com::SafeIfaceArray<IDHCPServer> svrs;
+ CHECK_ERROR_RET(pVirtualBox, COMGETTER(DHCPServers)(ComSafeArrayAsOutParam(svrs)), rc);
+ for (size_t i = 0; i < svrs.size(); ++i)
+ {
+ ComPtr<IDHCPServer> svr = svrs[i];
+ Bstr netName;
+ svr->COMGETTER(NetworkName)(netName.asOutParam());
+ RTPrintf("NetworkName: %ls\n", netName.raw());
+ Bstr ip;
+ svr->COMGETTER(IPAddress)(ip.asOutParam());
+ RTPrintf("IP: %ls\n", ip.raw());
+ Bstr netmask;
+ svr->COMGETTER(NetworkMask)(netmask.asOutParam());
+ RTPrintf("NetworkMask: %ls\n", netmask.raw());
+ Bstr lowerIp;
+ svr->COMGETTER(LowerIP)(lowerIp.asOutParam());
+ RTPrintf("lowerIPAddress: %ls\n", lowerIp.raw());
+ Bstr upperIp;
+ svr->COMGETTER(UpperIP)(upperIp.asOutParam());
+ RTPrintf("upperIPAddress: %ls\n", upperIp.raw());
+ BOOL fEnabled;
+ svr->COMGETTER(Enabled)(&fEnabled);
+ RTPrintf("Enabled: %s\n", fEnabled ? "Yes" : "No");
+ com::SafeArray<BSTR> globalOptions;
+ CHECK_ERROR_BREAK(svr, COMGETTER(GlobalOptions)(ComSafeArrayAsOutParam(globalOptions)));
+ if (globalOptions.size())
+ {
+ RTPrintf("Global options:\n");
+ for (size_t j = 0; j < globalOptions.size(); ++j)
+ RTPrintf(" %ls\n", globalOptions[j]);
+ }
+ com::SafeArray<BSTR> vmConfigs;
+ CHECK_ERROR_BREAK(svr, COMGETTER(VmConfigs)(ComSafeArrayAsOutParam(vmConfigs)));
+ for (size_t j = 0; j < vmConfigs.size(); ++j)
+ {
+ rc = listVmSlotDhcpOptions(svr, vmConfigs[j]);
+ if (FAILED(rc))
+ break;
+ }
+ RTPrintf("\n");
+ }
+
+ return rc;
+}
+
+/**
+ * List extension packs.
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox smart pointer.
+ */
+static HRESULT listExtensionPacks(const ComPtr<IVirtualBox> &pVirtualBox)
+{
+ ComObjPtr<IExtPackManager> ptrExtPackMgr;
+ CHECK_ERROR2I_RET(pVirtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), hrcCheck);
+
+ SafeIfaceArray<IExtPack> extPacks;
+ CHECK_ERROR2I_RET(ptrExtPackMgr, COMGETTER(InstalledExtPacks)(ComSafeArrayAsOutParam(extPacks)), hrcCheck);
+ RTPrintf("Extension Packs: %u\n", extPacks.size());
+
+ HRESULT hrc = S_OK;
+ for (size_t i = 0; i < extPacks.size(); i++)
+ {
+ /* Read all the properties. */
+ Bstr bstrName;
+ CHECK_ERROR2I_STMT(extPacks[i], COMGETTER(Name)(bstrName.asOutParam()), hrc = hrcCheck; bstrName.setNull());
+ Bstr bstrDesc;
+ CHECK_ERROR2I_STMT(extPacks[i], COMGETTER(Description)(bstrDesc.asOutParam()), hrc = hrcCheck; bstrDesc.setNull());
+ Bstr bstrVersion;
+ CHECK_ERROR2I_STMT(extPacks[i], COMGETTER(Version)(bstrVersion.asOutParam()), hrc = hrcCheck; bstrVersion.setNull());
+ ULONG uRevision;
+ CHECK_ERROR2I_STMT(extPacks[i], COMGETTER(Revision)(&uRevision), hrc = hrcCheck; uRevision = 0);
+ Bstr bstrEdition;
+ CHECK_ERROR2I_STMT(extPacks[i], COMGETTER(Edition)(bstrEdition.asOutParam()), hrc = hrcCheck; bstrEdition.setNull());
+ Bstr bstrVrdeModule;
+ CHECK_ERROR2I_STMT(extPacks[i], COMGETTER(VRDEModule)(bstrVrdeModule.asOutParam()),hrc=hrcCheck; bstrVrdeModule.setNull());
+ BOOL fUsable;
+ CHECK_ERROR2I_STMT(extPacks[i], COMGETTER(Usable)(&fUsable), hrc = hrcCheck; fUsable = FALSE);
+ Bstr bstrWhy;
+ CHECK_ERROR2I_STMT(extPacks[i], COMGETTER(WhyUnusable)(bstrWhy.asOutParam()), hrc = hrcCheck; bstrWhy.setNull());
+
+ /* Display them. */
+ if (i)
+ RTPrintf("\n");
+ RTPrintf("Pack no.%2zu: %ls\n"
+ "Version: %ls\n"
+ "Revision: %u\n"
+ "Edition: %ls\n"
+ "Description: %ls\n"
+ "VRDE Module: %ls\n"
+ "Usable: %RTbool\n"
+ "Why unusable: %ls\n",
+ i, bstrName.raw(),
+ bstrVersion.raw(),
+ uRevision,
+ bstrEdition.raw(),
+ bstrDesc.raw(),
+ bstrVrdeModule.raw(),
+ fUsable != FALSE,
+ bstrWhy.raw());
+
+ /* Query plugins and display them. */
+ }
+ return hrc;
+}
+
+
+/**
+ * List machine groups.
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox smart pointer.
+ */
+static HRESULT listGroups(const ComPtr<IVirtualBox> &pVirtualBox)
+{
+ SafeArray<BSTR> groups;
+ CHECK_ERROR2I_RET(pVirtualBox, COMGETTER(MachineGroups)(ComSafeArrayAsOutParam(groups)), hrcCheck);
+
+ for (size_t i = 0; i < groups.size(); i++)
+ {
+ RTPrintf("\"%ls\"\n", groups[i]);
+ }
+ return S_OK;
+}
+
+
+/**
+ * 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;
+}
+
+/**
+ * List supported screen shot formats.
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox pointer.
+ */
+static HRESULT listScreenShotFormats(const ComPtr<IVirtualBox> pVirtualBox)
+{
+ HRESULT rc = S_OK;
+ ComPtr<ISystemProperties> systemProperties;
+ CHECK_ERROR(pVirtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam()));
+ com::SafeArray<BitmapFormat_T> formats;
+ CHECK_ERROR(systemProperties, COMGETTER(ScreenShotFormats)(ComSafeArrayAsOutParam(formats)));
+
+ RTPrintf("Supported %d screen shot formats:\n", formats.size());
+ for (size_t i = 0; i < formats.size(); ++i)
+ {
+ uint32_t u32Format = (uint32_t)formats[i];
+ char szFormat[5];
+ szFormat[0] = RT_BYTE1(u32Format);
+ szFormat[1] = RT_BYTE2(u32Format);
+ szFormat[2] = RT_BYTE3(u32Format);
+ szFormat[3] = RT_BYTE4(u32Format);
+ szFormat[4] = 0;
+ RTPrintf(" BitmapFormat_%s (0x%08X)\n", szFormat, u32Format);
+ }
+ return rc;
+}
+
+/**
+ * List available cloud providers.
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox pointer.
+ */
+static HRESULT listCloudProviders(const ComPtr<IVirtualBox> pVirtualBox)
+{
+ HRESULT rc = S_OK;
+ ComPtr<ICloudProviderManager> pCloudProviderManager;
+ CHECK_ERROR(pVirtualBox, COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()));
+ com::SafeIfaceArray<ICloudProvider> apCloudProviders;
+ CHECK_ERROR(pCloudProviderManager, COMGETTER(Providers)(ComSafeArrayAsOutParam(apCloudProviders)));
+
+ RTPrintf("Supported %d cloud providers:\n", apCloudProviders.size());
+ for (size_t i = 0; i < apCloudProviders.size(); ++i)
+ {
+ ComPtr<ICloudProvider> pCloudProvider = apCloudProviders[i];
+ Bstr bstrProviderName;
+ pCloudProvider->COMGETTER(Name)(bstrProviderName.asOutParam());
+ RTPrintf("Name: %ls\n", bstrProviderName.raw());
+ pCloudProvider->COMGETTER(ShortName)(bstrProviderName.asOutParam());
+ RTPrintf("Short Name: %ls\n", bstrProviderName.raw());
+ Bstr bstrProviderID;
+ pCloudProvider->COMGETTER(Id)(bstrProviderID.asOutParam());
+ RTPrintf("GUID: %ls\n", bstrProviderID.raw());
+
+ RTPrintf("\n");
+ }
+ return rc;
+}
+
+
+/**
+ * List all available cloud profiles (by iterating over the cloud providers).
+ *
+ * @returns See produceList.
+ * @param pVirtualBox Reference to the IVirtualBox pointer.
+ * @param fOptLong If true, list all profile properties.
+ */
+static HRESULT listCloudProfiles(const ComPtr<IVirtualBox> pVirtualBox, bool fOptLong)
+{
+ HRESULT rc = S_OK;
+ ComPtr<ICloudProviderManager> pCloudProviderManager;
+ CHECK_ERROR(pVirtualBox, COMGETTER(CloudProviderManager)(pCloudProviderManager.asOutParam()));
+ com::SafeIfaceArray<ICloudProvider> apCloudProviders;
+ CHECK_ERROR(pCloudProviderManager, COMGETTER(Providers)(ComSafeArrayAsOutParam(apCloudProviders)));
+
+ for (size_t i = 0; i < apCloudProviders.size(); ++i)
+ {
+ ComPtr<ICloudProvider> pCloudProvider = apCloudProviders[i];
+ com::SafeIfaceArray<ICloudProfile> apCloudProfiles;
+ CHECK_ERROR(pCloudProvider, COMGETTER(Profiles)(ComSafeArrayAsOutParam(apCloudProfiles)));
+ for (size_t j = 0; j < apCloudProfiles.size(); ++j)
+ {
+ ComPtr<ICloudProfile> pCloudProfile = apCloudProfiles[j];
+ Bstr bstrProfileName;
+ pCloudProfile->COMGETTER(Name)(bstrProfileName.asOutParam());
+ RTPrintf("Name: %ls\n", bstrProfileName.raw());
+ Bstr bstrProviderID;
+ pCloudProfile->COMGETTER(ProviderId)(bstrProviderID.asOutParam());
+ RTPrintf("Provider GUID: %ls\n", bstrProviderID.raw());
+
+ if (fOptLong)
+ {
+ com::SafeArray<BSTR> names;
+ com::SafeArray<BSTR> values;
+ pCloudProfile->GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values));
+ size_t cNames = names.size();
+ size_t cValues = values.size();
+ bool fFirst = true;
+ for (size_t k = 0; k < cNames; k++)
+ {
+ Bstr value;
+ if (k < cValues)
+ value = values[k];
+ RTPrintf("%s%ls=%ls\n",
+ fFirst ? "Property: " : " ",
+ names[k], value.raw());
+ fFirst = false;
+ }
+ }
+
+ RTPrintf("\n");
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * The type of lists we can produce.
+ */
+enum enmListType
+{
+ kListNotSpecified = 1000,
+ kListVMs,
+ kListRunningVMs,
+ kListOsTypes,
+ kListHostDvds,
+ kListHostFloppies,
+ kListInternalNetworks,
+ kListBridgedInterfaces,
+#if defined(VBOX_WITH_NETFLT)
+ kListHostOnlyInterfaces,
+#endif
+ kListHostCpuIDs,
+ kListHostInfo,
+ kListHddBackends,
+ kListHdds,
+ kListDvds,
+ kListFloppies,
+ kListUsbHost,
+ kListUsbFilters,
+ kListSystemProperties,
+ kListDhcpServers,
+ kListExtPacks,
+ kListGroups,
+ kListNatNetworks,
+ kListVideoInputDevices,
+ kListScreenShotFormats,
+ kListCloudProviders,
+ kListCloudProfiles,
+};
+
+
+/**
+ * Produces the specified listing.
+ *
+ * @returns S_OK or some COM error code that has been reported in full.
+ * @param enmList The list to produce.
+ * @param fOptLong Long (@c true) or short list format.
+ * @param pVirtualBox Reference to the IVirtualBox smart pointer.
+ */
+static HRESULT produceList(enum enmListType enmCommand, bool fOptLong, bool fOptSorted, const ComPtr<IVirtualBox> &pVirtualBox)
+{
+ HRESULT rc = S_OK;
+ switch (enmCommand)
+ {
+ case kListNotSpecified:
+ AssertFailed();
+ return E_FAIL;
+
+ case kListVMs:
+ {
+ /*
+ * Get the list of all registered VMs
+ */
+ com::SafeIfaceArray<IMachine> machines;
+ rc = pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
+ if (SUCCEEDED(rc))
+ {
+ /*
+ * Display it.
+ */
+ if (!fOptSorted)
+ {
+ for (size_t i = 0; i < machines.size(); ++i)
+ if (machines[i])
+ rc = showVMInfo(pVirtualBox, machines[i], NULL, fOptLong ? VMINFO_STANDARD : VMINFO_COMPACT);
+ }
+ else
+ {
+ /*
+ * Sort the list by name before displaying it.
+ */
+ std::vector<std::pair<com::Bstr, IMachine *> > sortedMachines;
+ for (size_t i = 0; i < machines.size(); ++i)
+ {
+ IMachine *pMachine = machines[i];
+ if (pMachine) /* no idea why we need to do this... */
+ {
+ Bstr bstrName;
+ pMachine->COMGETTER(Name)(bstrName.asOutParam());
+ sortedMachines.push_back(std::pair<com::Bstr, IMachine *>(bstrName, pMachine));
+ }
+ }
+
+ std::sort(sortedMachines.begin(), sortedMachines.end());
+
+ for (size_t i = 0; i < sortedMachines.size(); ++i)
+ rc = showVMInfo(pVirtualBox, sortedMachines[i].second, NULL, fOptLong ? VMINFO_STANDARD : VMINFO_COMPACT);
+ }
+ }
+ break;
+ }
+
+ case kListRunningVMs:
+ {
+ /*
+ * Get the list of all _running_ VMs
+ */
+ com::SafeIfaceArray<IMachine> machines;
+ rc = pVirtualBox->COMGETTER(Machines)(ComSafeArrayAsOutParam(machines));
+ com::SafeArray<MachineState_T> states;
+ if (SUCCEEDED(rc))
+ rc = pVirtualBox->GetMachineStates(ComSafeArrayAsInParam(machines), ComSafeArrayAsOutParam(states));
+ if (SUCCEEDED(rc))
+ {
+ /*
+ * Iterate through the collection
+ */
+ for (size_t i = 0; i < machines.size(); ++i)
+ {
+ if (machines[i])
+ {
+ MachineState_T machineState = states[i];
+ switch (machineState)
+ {
+ case MachineState_Running:
+ case MachineState_Teleporting:
+ case MachineState_LiveSnapshotting:
+ case MachineState_Paused:
+ case MachineState_TeleportingPausedVM:
+ rc = showVMInfo(pVirtualBox, machines[i], NULL, fOptLong ? VMINFO_STANDARD : VMINFO_COMPACT);
+ break;
+ default: break; /* Shut up MSC */
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case kListOsTypes:
+ {
+ com::SafeIfaceArray<IGuestOSType> coll;
+ rc = pVirtualBox->COMGETTER(GuestOSTypes)(ComSafeArrayAsOutParam(coll));
+ if (SUCCEEDED(rc))
+ {
+ /*
+ * Iterate through the collection.
+ */
+ for (size_t i = 0; i < coll.size(); ++i)
+ {
+ ComPtr<IGuestOSType> guestOS;
+ guestOS = coll[i];
+ Bstr guestId;
+ guestOS->COMGETTER(Id)(guestId.asOutParam());
+ RTPrintf("ID: %ls\n", guestId.raw());
+ Bstr guestDescription;
+ guestOS->COMGETTER(Description)(guestDescription.asOutParam());
+ RTPrintf("Description: %ls\n", guestDescription.raw());
+ Bstr familyId;
+ guestOS->COMGETTER(FamilyId)(familyId.asOutParam());
+ RTPrintf("Family ID: %ls\n", familyId.raw());
+ Bstr familyDescription;
+ guestOS->COMGETTER(FamilyDescription)(familyDescription.asOutParam());
+ RTPrintf("Family Desc: %ls\n", familyDescription.raw());
+ BOOL is64Bit;
+ guestOS->COMGETTER(Is64Bit)(&is64Bit);
+ RTPrintf("64 bit: %RTbool\n", is64Bit);
+ RTPrintf("\n");
+ }
+ }
+ break;
+ }
+
+ case kListHostDvds:
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR(pVirtualBox, COMGETTER(Host)(host.asOutParam()));
+ com::SafeIfaceArray<IMedium> coll;
+ CHECK_ERROR(host, COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(coll)));
+ if (SUCCEEDED(rc))
+ {
+ for (size_t i = 0; i < coll.size(); ++i)
+ {
+ ComPtr<IMedium> dvdDrive = coll[i];
+ Bstr uuid;
+ dvdDrive->COMGETTER(Id)(uuid.asOutParam());
+ RTPrintf("UUID: %s\n", Utf8Str(uuid).c_str());
+ Bstr location;
+ dvdDrive->COMGETTER(Location)(location.asOutParam());
+ RTPrintf("Name: %ls\n\n", location.raw());
+ }
+ }
+ break;
+ }
+
+ case kListHostFloppies:
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR(pVirtualBox, COMGETTER(Host)(host.asOutParam()));
+ com::SafeIfaceArray<IMedium> coll;
+ CHECK_ERROR(host, COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(coll)));
+ if (SUCCEEDED(rc))
+ {
+ for (size_t i = 0; i < coll.size(); ++i)
+ {
+ ComPtr<IMedium> floppyDrive = coll[i];
+ Bstr uuid;
+ floppyDrive->COMGETTER(Id)(uuid.asOutParam());
+ RTPrintf("UUID: %s\n", Utf8Str(uuid).c_str());
+ Bstr location;
+ floppyDrive->COMGETTER(Location)(location.asOutParam());
+ RTPrintf("Name: %ls\n\n", location.raw());
+ }
+ }
+ break;
+ }
+
+ case kListInternalNetworks:
+ rc = listInternalNetworks(pVirtualBox);
+ break;
+
+ case kListBridgedInterfaces:
+#if defined(VBOX_WITH_NETFLT)
+ case kListHostOnlyInterfaces:
+#endif
+ rc = listNetworkInterfaces(pVirtualBox, enmCommand == kListBridgedInterfaces);
+ break;
+
+ case kListHostInfo:
+ rc = listHostInfo(pVirtualBox);
+ break;
+
+ case kListHostCpuIDs:
+ {
+ ComPtr<IHost> Host;
+ CHECK_ERROR(pVirtualBox, COMGETTER(Host)(Host.asOutParam()));
+
+ RTPrintf("Host CPUIDs:\n\nLeaf no. EAX EBX ECX EDX\n");
+ ULONG uCpuNo = 0; /* ASSUMES that CPU#0 is online. */
+ static uint32_t const s_auCpuIdRanges[] =
+ {
+ UINT32_C(0x00000000), UINT32_C(0x0000007f),
+ UINT32_C(0x80000000), UINT32_C(0x8000007f),
+ UINT32_C(0xc0000000), UINT32_C(0xc000007f)
+ };
+ for (unsigned i = 0; i < RT_ELEMENTS(s_auCpuIdRanges); i += 2)
+ {
+ ULONG uEAX, uEBX, uECX, uEDX, cLeafs;
+ CHECK_ERROR(Host, GetProcessorCPUIDLeaf(uCpuNo, s_auCpuIdRanges[i], 0, &cLeafs, &uEBX, &uECX, &uEDX));
+ if (cLeafs < s_auCpuIdRanges[i] || cLeafs > s_auCpuIdRanges[i+1])
+ continue;
+ cLeafs++;
+ for (ULONG iLeaf = s_auCpuIdRanges[i]; iLeaf <= cLeafs; iLeaf++)
+ {
+ CHECK_ERROR(Host, GetProcessorCPUIDLeaf(uCpuNo, iLeaf, 0, &uEAX, &uEBX, &uECX, &uEDX));
+ RTPrintf("%08x %08x %08x %08x %08x\n", iLeaf, uEAX, uEBX, uECX, uEDX);
+ }
+ }
+ break;
+ }
+
+ case kListHddBackends:
+ rc = listHddBackends(pVirtualBox);
+ break;
+
+ case kListHdds:
+ {
+ com::SafeIfaceArray<IMedium> hdds;
+ CHECK_ERROR(pVirtualBox, COMGETTER(HardDisks)(ComSafeArrayAsOutParam(hdds)));
+ rc = listMedia(pVirtualBox, hdds, "base", fOptLong);
+ break;
+ }
+
+ case kListDvds:
+ {
+ com::SafeIfaceArray<IMedium> dvds;
+ CHECK_ERROR(pVirtualBox, COMGETTER(DVDImages)(ComSafeArrayAsOutParam(dvds)));
+ rc = listMedia(pVirtualBox, dvds, NULL, fOptLong);
+ break;
+ }
+
+ case kListFloppies:
+ {
+ com::SafeIfaceArray<IMedium> floppies;
+ CHECK_ERROR(pVirtualBox, COMGETTER(FloppyImages)(ComSafeArrayAsOutParam(floppies)));
+ rc = listMedia(pVirtualBox, floppies, NULL, fOptLong);
+ break;
+ }
+
+ case kListUsbHost:
+ rc = listUsbHost(pVirtualBox);
+ break;
+
+ case kListUsbFilters:
+ rc = listUsbFilters(pVirtualBox);
+ break;
+
+ case kListSystemProperties:
+ rc = listSystemProperties(pVirtualBox);
+ break;
+
+ case kListDhcpServers:
+ rc = listDhcpServers(pVirtualBox);
+ break;
+
+ case kListExtPacks:
+ rc = listExtensionPacks(pVirtualBox);
+ break;
+
+ case kListGroups:
+ 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(IPv6Prefix)(ipv6prefix.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;
+
+ case kListScreenShotFormats:
+ rc = listScreenShotFormats(pVirtualBox);
+ break;
+
+ case kListCloudProviders:
+ rc = listCloudProviders(pVirtualBox);
+ break;
+
+ case kListCloudProfiles:
+ rc = listCloudProfiles(pVirtualBox, fOptLong);
+ break;
+
+ /* No default here, want gcc warnings. */
+
+ } /* end switch */
+
+ return rc;
+}
+
+/**
+ * Handles the 'list' command.
+ *
+ * @returns Appropriate exit code.
+ * @param a Handler argument.
+ */
+RTEXITCODE handleList(HandlerArg *a)
+{
+ bool fOptLong = false;
+ bool fOptMultiple = false;
+ bool fOptSorted = false;
+ enum enmListType enmOptCommand = kListNotSpecified;
+
+ static const RTGETOPTDEF s_aListOptions[] =
+ {
+ { "--long", 'l', RTGETOPT_REQ_NOTHING },
+ { "--multiple", 'm', RTGETOPT_REQ_NOTHING }, /* not offical yet */
+ { "--sorted", 's', RTGETOPT_REQ_NOTHING },
+ { "vms", kListVMs, RTGETOPT_REQ_NOTHING },
+ { "runningvms", kListRunningVMs, RTGETOPT_REQ_NOTHING },
+ { "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 },
+ { "hdds", kListHdds, RTGETOPT_REQ_NOTHING },
+ { "dvds", kListDvds, RTGETOPT_REQ_NOTHING },
+ { "floppies", kListFloppies, RTGETOPT_REQ_NOTHING },
+ { "usbhost", kListUsbHost, RTGETOPT_REQ_NOTHING },
+ { "usbfilters", kListUsbFilters, RTGETOPT_REQ_NOTHING },
+ { "systemproperties", kListSystemProperties, RTGETOPT_REQ_NOTHING },
+ { "dhcpservers", kListDhcpServers, RTGETOPT_REQ_NOTHING },
+ { "extpacks", kListExtPacks, RTGETOPT_REQ_NOTHING },
+ { "groups", kListGroups, RTGETOPT_REQ_NOTHING },
+ { "webcams", kListVideoInputDevices, RTGETOPT_REQ_NOTHING },
+ { "screenshotformats", kListScreenShotFormats, RTGETOPT_REQ_NOTHING },
+ { "cloudproviders", kListCloudProviders, RTGETOPT_REQ_NOTHING },
+ { "cloudprofiles", kListCloudProfiles, RTGETOPT_REQ_NOTHING },
+ };
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, a->argc, a->argv, s_aListOptions, RT_ELEMENTS(s_aListOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'l': /* --long */
+ fOptLong = true;
+ break;
+
+ case 's':
+ fOptSorted = true;
+ break;
+
+ case 'm':
+ fOptMultiple = true;
+ if (enmOptCommand == kListNotSpecified)
+ break;
+ ch = enmOptCommand;
+ RT_FALL_THRU();
+
+ case kListVMs:
+ case kListRunningVMs:
+ case kListOsTypes:
+ case kListHostDvds:
+ case kListHostFloppies:
+ case kListInternalNetworks:
+ case kListBridgedInterfaces:
+#if defined(VBOX_WITH_NETFLT)
+ case kListHostOnlyInterfaces:
+#endif
+ case kListHostInfo:
+ case kListHostCpuIDs:
+ case kListHddBackends:
+ case kListHdds:
+ case kListDvds:
+ case kListFloppies:
+ case kListUsbHost:
+ case kListUsbFilters:
+ case kListSystemProperties:
+ case kListDhcpServers:
+ case kListExtPacks:
+ case kListGroups:
+ case kListNatNetworks:
+ case kListVideoInputDevices:
+ case kListScreenShotFormats:
+ case kListCloudProviders:
+ case kListCloudProfiles:
+ enmOptCommand = (enum enmListType)ch;
+ if (fOptMultiple)
+ {
+ HRESULT hrc = produceList((enum enmListType)ch, fOptLong, fOptSorted, a->virtualBox);
+ if (FAILED(hrc))
+ return RTEXITCODE_FAILURE;
+ }
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ return errorSyntax(USAGE_LIST, "Unknown subcommand \"%s\".", ValueUnion.psz);
+
+ default:
+ return errorGetOpt(USAGE_LIST, ch, &ValueUnion);
+ }
+ }
+
+ /*
+ * If not in multiple list mode, we have to produce the list now.
+ */
+ if (enmOptCommand == kListNotSpecified)
+ return errorSyntax(USAGE_LIST, "Missing subcommand for \"list\" command.\n");
+ if (!fOptMultiple)
+ {
+ HRESULT hrc = produceList(enmOptCommand, fOptLong, fOptSorted, a->virtualBox);
+ if (FAILED(hrc))
+ return RTEXITCODE_FAILURE;
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp
new file mode 100644
index 00000000..3f655c47
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageMetrics.cpp
@@ -0,0 +1,651 @@
+/* $Id: VBoxManageMetrics.cpp $ */
+/** @file
+ * VBoxManage - The 'metrics' command.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 VBOX_ONLY_DOCS
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#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>
+
+#include <iprt/asm.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <VBox/log.h>
+
+#include <set>
+#include <utility>
+
+#include "VBoxManage.h"
+using namespace com;
+
+
+// funcs
+///////////////////////////////////////////////////////////////////////////////
+
+
+static HRESULT parseFilterParameters(int argc, char *argv[],
+ ComPtr<IVirtualBox> aVirtualBox,
+ ComSafeArrayOut(BSTR, outMetrics),
+ ComSafeArrayOut(IUnknown *, outObjects))
+{
+ HRESULT rc = S_OK;
+ com::SafeArray<BSTR> retMetrics(1);
+ com::SafeIfaceArray <IUnknown> retObjects;
+
+ Bstr metricNames, baseNames;
+
+ /* Metric list */
+ if (argc > 1)
+ metricNames = argv[1];
+ else
+ {
+ metricNames = L"*";
+ baseNames = L"*";
+ }
+ metricNames.cloneTo(&retMetrics[0]);
+
+ /* Object name */
+ if (argc > 0 && strcmp(argv[0], "*"))
+ {
+ if (!strcmp(argv[0], "host"))
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR(aVirtualBox, COMGETTER(Host)(host.asOutParam()));
+ retObjects.reset(1);
+ host.queryInterfaceTo(&retObjects[0]);
+ }
+ else
+ {
+ ComPtr<IMachine> machine;
+ rc = aVirtualBox->FindMachine(Bstr(argv[0]).raw(),
+ machine.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ retObjects.reset(1);
+ machine.queryInterfaceTo(&retObjects[0]);
+ }
+ else
+ {
+ errorArgument("Invalid machine name: '%s'", argv[0]);
+ return rc;
+ }
+ }
+
+ }
+
+ retMetrics.detachTo(ComSafeArrayOutArg(outMetrics));
+ retObjects.detachTo(ComSafeArrayOutArg(outObjects));
+
+ return rc;
+}
+
+static Bstr toBaseName(Utf8Str& aFullName)
+{
+ char *pszRaw = aFullName.mutableRaw();
+ /*
+ * Currently there are two metrics which base name is the same as the
+ * sub-metric name: CPU/MHz and Net/<iface>/LinkSpeed.
+ */
+ if (pszRaw && strcmp(pszRaw, "CPU/MHz") && !RTStrSimplePatternMatch("Net/*/LinkSpeed", pszRaw))
+ {
+ char *pszSlash = strrchr(pszRaw, '/');
+ if (pszSlash)
+ {
+ *pszSlash = 0;
+ aFullName.jolt();
+ }
+ }
+ return Bstr(aFullName);
+}
+
+static Bstr getObjectName(ComPtr<IUnknown> aObject)
+{
+ HRESULT rc;
+
+ ComPtr<IHost> host = aObject;
+ if (!host.isNull())
+ return Bstr("host");
+
+ ComPtr<IMachine> machine = aObject;
+ if (!machine.isNull())
+ {
+ Bstr name;
+ CHECK_ERROR(machine, COMGETTER(Name)(name.asOutParam()));
+ if (SUCCEEDED(rc))
+ return name;
+ }
+ return Bstr("unknown");
+}
+
+static void listAffectedMetrics(ComSafeArrayIn(IPerformanceMetric*, aMetrics))
+{
+ HRESULT rc;
+ com::SafeIfaceArray<IPerformanceMetric> metrics(ComSafeArrayInArg(aMetrics));
+ if (metrics.size())
+ {
+ ComPtr<IUnknown> object;
+ Bstr metricName;
+ RTPrintf("The following metrics were modified:\n\n"
+ "Object Metric\n"
+ "---------- --------------------\n");
+ for (size_t i = 0; i < metrics.size(); i++)
+ {
+ CHECK_ERROR(metrics[i], COMGETTER(Object)(object.asOutParam()));
+ CHECK_ERROR(metrics[i], COMGETTER(MetricName)(metricName.asOutParam()));
+ RTPrintf("%-10ls %-20ls\n",
+ getObjectName(object).raw(), metricName.raw());
+ }
+ RTPrintf("\n");
+ }
+ else
+ {
+ RTMsgError("No metrics match the specified filter!");
+ }
+}
+
+/**
+ * list
+ */
+static RTEXITCODE handleMetricsList(int argc, char *argv[],
+ ComPtr<IVirtualBox> aVirtualBox,
+ ComPtr<IPerformanceCollector> performanceCollector)
+{
+ HRESULT rc;
+ com::SafeArray<BSTR> metrics;
+ com::SafeIfaceArray<IUnknown> objects;
+
+ rc = parseFilterParameters(argc - 1, &argv[1], aVirtualBox,
+ ComSafeArrayAsOutParam(metrics),
+ ComSafeArrayAsOutParam(objects));
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ com::SafeIfaceArray<IPerformanceMetric> metricInfo;
+
+ CHECK_ERROR(performanceCollector,
+ GetMetrics(ComSafeArrayAsInParam(metrics),
+ ComSafeArrayAsInParam(objects),
+ ComSafeArrayAsOutParam(metricInfo)));
+
+ ComPtr<IUnknown> object;
+ Bstr metricName, unit, description;
+ ULONG period, count;
+ LONG minimum, maximum;
+ RTPrintf(
+"Object Metric Unit Minimum Maximum Period Count Description\n"
+"--------------- ---------------------------------------- ---- ---------- ---------- ---------- ---------- -----------\n");
+ for (size_t i = 0; i < metricInfo.size(); i++)
+ {
+ CHECK_ERROR(metricInfo[i], COMGETTER(Object)(object.asOutParam()));
+ CHECK_ERROR(metricInfo[i], COMGETTER(MetricName)(metricName.asOutParam()));
+ CHECK_ERROR(metricInfo[i], COMGETTER(Period)(&period));
+ CHECK_ERROR(metricInfo[i], COMGETTER(Count)(&count));
+ CHECK_ERROR(metricInfo[i], COMGETTER(MinimumValue)(&minimum));
+ CHECK_ERROR(metricInfo[i], COMGETTER(MaximumValue)(&maximum));
+ CHECK_ERROR(metricInfo[i], COMGETTER(Unit)(unit.asOutParam()));
+ CHECK_ERROR(metricInfo[i], COMGETTER(Description)(description.asOutParam()));
+ RTPrintf("%-15ls %-40ls %-4ls %10d %10d %10u %10u %ls\n",
+ getObjectName(object).raw(), metricName.raw(), unit.raw(),
+ minimum, maximum, period, count, description.raw());
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Metrics setup
+ */
+static RTEXITCODE handleMetricsSetup(int argc, char *argv[],
+ ComPtr<IVirtualBox> aVirtualBox,
+ ComPtr<IPerformanceCollector> performanceCollector)
+{
+ HRESULT rc;
+ com::SafeArray<BSTR> metrics;
+ com::SafeIfaceArray<IUnknown> objects;
+ uint32_t period = 1, samples = 1;
+ bool listMatches = false;
+ int i;
+
+ for (i = 1; i < argc; i++)
+ {
+ if ( !strcmp(argv[i], "--period")
+ || !strcmp(argv[i], "-period"))
+ {
+ if (argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &period)
+ || !period)
+ return errorArgument("Invalid value for 'period' parameter: '%s'", argv[i]);
+ }
+ else if ( !strcmp(argv[i], "--samples")
+ || !strcmp(argv[i], "-samples"))
+ {
+ if (argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &samples)
+ || !samples)
+ return errorArgument("Invalid value for 'samples' parameter: '%s'", argv[i]);
+ }
+ else if ( !strcmp(argv[i], "--list")
+ || !strcmp(argv[i], "-list"))
+ listMatches = true;
+ else
+ break; /* The rest of params should define the filter */
+ }
+
+ rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
+ ComSafeArrayAsOutParam(metrics),
+ ComSafeArrayAsOutParam(objects));
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
+ CHECK_ERROR(performanceCollector,
+ SetupMetrics(ComSafeArrayAsInParam(metrics),
+ ComSafeArrayAsInParam(objects), period, samples,
+ ComSafeArrayAsOutParam(affectedMetrics)));
+ if (FAILED(rc))
+ return RTEXITCODE_SYNTAX; /** @todo figure out why we must return 2 here. */
+
+ if (listMatches)
+ listAffectedMetrics(ComSafeArrayAsInParam(affectedMetrics));
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * metrics query
+ */
+static RTEXITCODE handleMetricsQuery(int argc, char *argv[],
+ ComPtr<IVirtualBox> aVirtualBox,
+ ComPtr<IPerformanceCollector> performanceCollector)
+{
+ HRESULT rc;
+ com::SafeArray<BSTR> metrics;
+ com::SafeIfaceArray<IUnknown> objects;
+
+ rc = parseFilterParameters(argc - 1, &argv[1], aVirtualBox,
+ ComSafeArrayAsOutParam(metrics),
+ ComSafeArrayAsOutParam(objects));
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ com::SafeArray<BSTR> retNames;
+ com::SafeIfaceArray<IUnknown> retObjects;
+ com::SafeArray<BSTR> retUnits;
+ com::SafeArray<ULONG> retScales;
+ com::SafeArray<ULONG> retSequenceNumbers;
+ com::SafeArray<ULONG> retIndices;
+ com::SafeArray<ULONG> retLengths;
+ com::SafeArray<LONG> retData;
+ CHECK_ERROR(performanceCollector, QueryMetricsData(ComSafeArrayAsInParam(metrics),
+ ComSafeArrayAsInParam(objects),
+ ComSafeArrayAsOutParam(retNames),
+ ComSafeArrayAsOutParam(retObjects),
+ ComSafeArrayAsOutParam(retUnits),
+ ComSafeArrayAsOutParam(retScales),
+ ComSafeArrayAsOutParam(retSequenceNumbers),
+ ComSafeArrayAsOutParam(retIndices),
+ ComSafeArrayAsOutParam(retLengths),
+ ComSafeArrayAsOutParam(retData)) );
+
+ RTPrintf("Object Metric Values\n"
+ "--------------- ---------------------------------------- --------------------------------------------\n");
+ for (unsigned i = 0; i < retNames.size(); i++)
+ {
+ Bstr metricUnit(retUnits[i]);
+ Bstr metricName(retNames[i]);
+ RTPrintf("%-15ls %-40ls ", getObjectName(retObjects[i]).raw(), metricName.raw());
+ const char *separator = "";
+ for (unsigned j = 0; j < retLengths[i]; j++)
+ {
+ if (retScales[i] == 1)
+ RTPrintf("%s%d %ls", separator, retData[retIndices[i] + j], metricUnit.raw());
+ else
+ RTPrintf("%s%d.%02d%ls", separator, retData[retIndices[i] + j] / retScales[i],
+ (retData[retIndices[i] + j] * 100 / retScales[i]) % 100, metricUnit.raw());
+ separator = ", ";
+ }
+ RTPrintf("\n");
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+static void getTimestamp(char *pts, size_t tsSize)
+{
+ *pts = 0;
+ AssertReturnVoid(tsSize >= 13); /* 3+3+3+3+1 */
+ RTTIMESPEC TimeSpec;
+ RTTIME Time;
+ RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
+ pts += RTStrFormatNumber(pts, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);
+ *pts++ = ':';
+ pts += RTStrFormatNumber(pts, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);
+ *pts++ = ':';
+ pts += RTStrFormatNumber(pts, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);
+ *pts++ = '.';
+ pts += RTStrFormatNumber(pts, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);
+ *pts = 0;
+}
+
+/** Used by the handleMetricsCollect loop. */
+static bool volatile g_fKeepGoing = true;
+
+#ifdef RT_OS_WINDOWS
+/**
+ * Handler routine for catching Ctrl-C, Ctrl-Break and closing of
+ * the console.
+ *
+ * @returns true if handled, false if not handled.
+ * @param dwCtrlType The type of control signal.
+ *
+ * @remarks This is called on a new thread.
+ */
+static BOOL WINAPI ctrlHandler(DWORD dwCtrlType)
+{
+ switch (dwCtrlType)
+ {
+ /* Ctrl-C or Ctrl-Break or Close */
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ case CTRL_CLOSE_EVENT:
+ /* Let's shut down gracefully. */
+ ASMAtomicWriteBool(&g_fKeepGoing, false);
+ return TRUE;
+ }
+ /* Don't care about the rest -- let it die a horrible death. */
+ return FALSE;
+}
+#endif /* RT_OS_WINDOWS */
+
+/**
+ * collect
+ */
+static RTEXITCODE handleMetricsCollect(int argc, char *argv[],
+ ComPtr<IVirtualBox> aVirtualBox,
+ ComPtr<IPerformanceCollector> performanceCollector)
+{
+ HRESULT rc;
+ com::SafeArray<BSTR> metrics;
+ com::SafeIfaceArray<IUnknown> objects;
+ uint32_t period = 1, samples = 1;
+ bool isDetached = false, listMatches = false;
+ int i;
+ for (i = 1; i < argc; i++)
+ {
+ if ( !strcmp(argv[i], "--period")
+ || !strcmp(argv[i], "-period"))
+ {
+ if (argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &period)
+ || !period)
+ return errorArgument("Invalid value for 'period' parameter: '%s'", argv[i]);
+ }
+ else if ( !strcmp(argv[i], "--samples")
+ || !strcmp(argv[i], "-samples"))
+ {
+ if (argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", argv[i]);
+ if ( VINF_SUCCESS != RTStrToUInt32Full(argv[++i], 10, &samples)
+ || !samples)
+ return errorArgument("Invalid value for 'samples' parameter: '%s'", argv[i]);
+ }
+ else if ( !strcmp(argv[i], "--list")
+ || !strcmp(argv[i], "-list"))
+ listMatches = true;
+ else if ( !strcmp(argv[i], "--detach")
+ || !strcmp(argv[i], "-detach"))
+ isDetached = true;
+ else
+ break; /* The rest of params should define the filter */
+ }
+
+ rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
+ ComSafeArrayAsOutParam(metrics),
+ ComSafeArrayAsOutParam(objects));
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ com::SafeIfaceArray<IPerformanceMetric> metricInfo;
+
+ CHECK_ERROR(performanceCollector,
+ GetMetrics(ComSafeArrayAsInParam(metrics),
+ ComSafeArrayAsInParam(objects),
+ ComSafeArrayAsOutParam(metricInfo)));
+
+ std::set<std::pair<ComPtr<IUnknown>,Bstr> > baseMetrics;
+ ComPtr<IUnknown> objectFiltered;
+ Bstr metricNameFiltered;
+ for (i = 0; i < (int)metricInfo.size(); i++)
+ {
+ CHECK_ERROR(metricInfo[i], COMGETTER(Object)(objectFiltered.asOutParam()));
+ CHECK_ERROR(metricInfo[i], COMGETTER(MetricName)(metricNameFiltered.asOutParam()));
+ Utf8Str baseMetricName(metricNameFiltered);
+ baseMetrics.insert(std::make_pair(objectFiltered, toBaseName(baseMetricName)));
+ }
+ com::SafeArray<BSTR> baseMetricsFiltered(baseMetrics.size());
+ com::SafeIfaceArray<IUnknown> objectsFiltered(baseMetrics.size());
+ std::set<std::pair<ComPtr<IUnknown>,Bstr> >::iterator it;
+ i = 0;
+ for (it = baseMetrics.begin(); it != baseMetrics.end(); ++it)
+ {
+ it->first.queryInterfaceTo(&objectsFiltered[i]);
+ Bstr(it->second).detachTo(&baseMetricsFiltered[i++]);
+ }
+ com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
+ CHECK_ERROR(performanceCollector,
+ SetupMetrics(ComSafeArrayAsInParam(baseMetricsFiltered),
+ ComSafeArrayAsInParam(objectsFiltered), period, samples,
+ ComSafeArrayAsOutParam(affectedMetrics)));
+ if (FAILED(rc))
+ return RTEXITCODE_SYNTAX; /** @todo figure out why we must return 2 here. */
+
+ if (listMatches)
+ listAffectedMetrics(ComSafeArrayAsInParam(affectedMetrics));
+ if (!affectedMetrics.size())
+ return RTEXITCODE_FAILURE;
+
+ if (isDetached)
+ {
+ RTMsgWarning("The background process holding collected metrics will shutdown\n"
+ "in few seconds, discarding all collected data and parameters.");
+ return RTEXITCODE_SUCCESS;
+ }
+
+#ifdef RT_OS_WINDOWS
+ SetConsoleCtrlHandler(ctrlHandler, true);
+#endif /* RT_OS_WINDOWS */
+
+ RTPrintf("Time stamp Object Metric Value\n");
+
+ while (g_fKeepGoing)
+ {
+ RTPrintf("------------ ---------- -------------------- --------------------\n");
+ RTThreadSleep(period * 1000); // Sleep for 'period' seconds
+ char ts[15];
+
+ getTimestamp(ts, sizeof(ts));
+ com::SafeArray<BSTR> retNames;
+ com::SafeIfaceArray<IUnknown> retObjects;
+ com::SafeArray<BSTR> retUnits;
+ com::SafeArray<ULONG> retScales;
+ com::SafeArray<ULONG> retSequenceNumbers;
+ com::SafeArray<ULONG> retIndices;
+ com::SafeArray<ULONG> retLengths;
+ com::SafeArray<LONG> retData;
+ CHECK_ERROR(performanceCollector, QueryMetricsData(ComSafeArrayAsInParam(metrics),
+ ComSafeArrayAsInParam(objects),
+ ComSafeArrayAsOutParam(retNames),
+ ComSafeArrayAsOutParam(retObjects),
+ ComSafeArrayAsOutParam(retUnits),
+ ComSafeArrayAsOutParam(retScales),
+ ComSafeArrayAsOutParam(retSequenceNumbers),
+ ComSafeArrayAsOutParam(retIndices),
+ ComSafeArrayAsOutParam(retLengths),
+ ComSafeArrayAsOutParam(retData)) );
+ for (unsigned j = 0; j < retNames.size(); j++)
+ {
+ Bstr metricUnit(retUnits[j]);
+ Bstr metricName(retNames[j]);
+ RTPrintf("%-12s %-10ls %-20ls ", ts, getObjectName(retObjects[j]).raw(), metricName.raw());
+ const char *separator = "";
+ for (unsigned k = 0; k < retLengths[j]; k++)
+ {
+ if (retScales[j] == 1)
+ RTPrintf("%s%d %ls", separator, retData[retIndices[j] + k], metricUnit.raw());
+ else
+ RTPrintf("%s%d.%02d%ls", separator, retData[retIndices[j] + k] / retScales[j],
+ (retData[retIndices[j] + k] * 100 / retScales[j]) % 100, metricUnit.raw());
+ separator = ", ";
+ }
+ RTPrintf("\n");
+ }
+ RTStrmFlush(g_pStdOut);
+ }
+
+#ifdef RT_OS_WINDOWS
+ SetConsoleCtrlHandler(ctrlHandler, false);
+#endif /* RT_OS_WINDOWS */
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Enable metrics
+ */
+static RTEXITCODE handleMetricsEnable(int argc, char *argv[],
+ ComPtr<IVirtualBox> aVirtualBox,
+ ComPtr<IPerformanceCollector> performanceCollector)
+{
+ HRESULT rc;
+ com::SafeArray<BSTR> metrics;
+ com::SafeIfaceArray<IUnknown> objects;
+ bool listMatches = false;
+ int i;
+
+ for (i = 1; i < argc; i++)
+ {
+ if ( !strcmp(argv[i], "--list")
+ || !strcmp(argv[i], "-list"))
+ listMatches = true;
+ else
+ break; /* The rest of params should define the filter */
+ }
+
+ rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
+ ComSafeArrayAsOutParam(metrics),
+ ComSafeArrayAsOutParam(objects));
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
+ CHECK_ERROR(performanceCollector,
+ EnableMetrics(ComSafeArrayAsInParam(metrics),
+ ComSafeArrayAsInParam(objects),
+ ComSafeArrayAsOutParam(affectedMetrics)));
+ if (FAILED(rc))
+ return RTEXITCODE_SYNTAX; /** @todo figure out why we must return 2 here. */
+
+ if (listMatches)
+ listAffectedMetrics(ComSafeArrayAsInParam(affectedMetrics));
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Disable metrics
+ */
+static RTEXITCODE handleMetricsDisable(int argc, char *argv[],
+ ComPtr<IVirtualBox> aVirtualBox,
+ ComPtr<IPerformanceCollector> performanceCollector)
+{
+ HRESULT rc;
+ com::SafeArray<BSTR> metrics;
+ com::SafeIfaceArray<IUnknown> objects;
+ bool listMatches = false;
+ int i;
+
+ for (i = 1; i < argc; i++)
+ {
+ if ( !strcmp(argv[i], "--list")
+ || !strcmp(argv[i], "-list"))
+ listMatches = true;
+ else
+ break; /* The rest of params should define the filter */
+ }
+
+ rc = parseFilterParameters(argc - i, &argv[i], aVirtualBox,
+ ComSafeArrayAsOutParam(metrics),
+ ComSafeArrayAsOutParam(objects));
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ com::SafeIfaceArray<IPerformanceMetric> affectedMetrics;
+ CHECK_ERROR(performanceCollector,
+ DisableMetrics(ComSafeArrayAsInParam(metrics),
+ ComSafeArrayAsInParam(objects),
+ ComSafeArrayAsOutParam(affectedMetrics)));
+ if (FAILED(rc))
+ return RTEXITCODE_SYNTAX; /** @todo figure out why we must return 2 here. */
+
+ if (listMatches)
+ listAffectedMetrics(ComSafeArrayAsInParam(affectedMetrics));
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+RTEXITCODE handleMetrics(HandlerArg *a)
+{
+ /* at least one option: subcommand name */
+ if (a->argc < 1)
+ return errorSyntax(USAGE_METRICS, "Subcommand missing");
+
+ ComPtr<IPerformanceCollector> performanceCollector;
+ CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(PerformanceCollector)(performanceCollector.asOutParam()), RTEXITCODE_FAILURE);
+
+ RTEXITCODE rcExit;
+ if (!strcmp(a->argv[0], "list"))
+ rcExit = handleMetricsList(a->argc, a->argv, a->virtualBox, performanceCollector);
+ else if (!strcmp(a->argv[0], "setup"))
+ rcExit = handleMetricsSetup(a->argc, a->argv, a->virtualBox, performanceCollector);
+ else if (!strcmp(a->argv[0], "query"))
+ rcExit = handleMetricsQuery(a->argc, a->argv, a->virtualBox, performanceCollector);
+ else if (!strcmp(a->argv[0], "collect"))
+ rcExit = handleMetricsCollect(a->argc, a->argv, a->virtualBox, performanceCollector);
+ else if (!strcmp(a->argv[0], "enable"))
+ rcExit = handleMetricsEnable(a->argc, a->argv, a->virtualBox, performanceCollector);
+ else if (!strcmp(a->argv[0], "disable"))
+ rcExit = handleMetricsDisable(a->argc, a->argv, a->virtualBox, performanceCollector);
+ else
+ return errorSyntax(USAGE_METRICS, "Invalid subcommand '%s'", a->argv[0]);
+
+ return rcExit;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
+
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp
new file mode 100644
index 00000000..4d93833d
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageMisc.cpp
@@ -0,0 +1,1941 @@
+/* $Id: VBoxManageMisc.cpp $ */
+/** @file
+ * VBoxManage - VirtualBox's command-line interface.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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/string.h>
+# include <VBox/com/Guid.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 */
+
+#include <iprt/asm.h>
+#include <iprt/buildconfig.h>
+#include <iprt/cidr.h>
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/sha.h>
+#include <iprt/initterm.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/cpp/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/stdarg.h>
+#include <iprt/thread.h>
+#include <iprt/uuid.h>
+#include <iprt/getopt.h>
+#include <iprt/ctype.h>
+#include <VBox/version.h>
+#include <VBox/log.h>
+
+#include "VBoxManage.h"
+
+#include <list>
+
+using namespace com;
+
+
+
+RTEXITCODE handleRegisterVM(HandlerArg *a)
+{
+ HRESULT rc;
+
+ if (a->argc != 1)
+ return errorSyntax(USAGE_REGISTERVM, "Incorrect number of parameters");
+
+ ComPtr<IMachine> machine;
+ /** @todo Ugly hack to get both the API interpretation of relative paths
+ * and the client's interpretation of relative paths. Remove after the API
+ * has been redesigned. */
+ rc = a->virtualBox->OpenMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam());
+ if (rc == VBOX_E_FILE_ERROR)
+ {
+ char szVMFileAbs[RTPATH_MAX] = "";
+ int vrc = RTPathAbs(a->argv[0], szVMFileAbs, sizeof(szVMFileAbs));
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
+ CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(szVMFileAbs).raw(),
+ machine.asOutParam()));
+ }
+ else if (FAILED(rc))
+ CHECK_ERROR(a->virtualBox, OpenMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()));
+ if (SUCCEEDED(rc))
+ {
+ ASSERT(machine);
+ CHECK_ERROR(a->virtualBox, RegisterMachine(machine));
+ }
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static const RTGETOPTDEF g_aUnregisterVMOptions[] =
+{
+ { "--delete", 'd', RTGETOPT_REQ_NOTHING },
+ { "-delete", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
+};
+
+RTEXITCODE handleUnregisterVM(HandlerArg *a)
+{
+ HRESULT rc;
+ const char *VMName = NULL;
+ bool fDelete = false;
+
+ 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_aUnregisterVMOptions, RT_ELEMENTS(g_aUnregisterVMOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'd': // --delete
+ fDelete = true;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!VMName)
+ VMName = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_UNREGISTERVM, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_PRINT(c))
+ return errorSyntax(USAGE_UNREGISTERVM, "Invalid option -%c", c);
+ return errorSyntax(USAGE_UNREGISTERVM, "Invalid option case %i", c);
+ }
+ if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_UNREGISTERVM, "unknown option: %s\n", ValueUnion.psz);
+ if (ValueUnion.pDef)
+ return errorSyntax(USAGE_UNREGISTERVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ return errorSyntax(USAGE_UNREGISTERVM, "error: %Rrs", c);
+ }
+ }
+
+ /* check for required options */
+ if (!VMName)
+ return errorSyntax(USAGE_UNREGISTERVM, "VM name required");
+
+ ComPtr<IMachine> machine;
+ CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(VMName).raw(),
+ machine.asOutParam()),
+ RTEXITCODE_FAILURE);
+ SafeIfaceArray<IMedium> aMedia;
+ CHECK_ERROR_RET(machine, Unregister(CleanupMode_DetachAllReturnHardDisksOnly,
+ ComSafeArrayAsOutParam(aMedia)),
+ RTEXITCODE_FAILURE);
+ if (fDelete)
+ {
+ ComPtr<IProgress> pProgress;
+ 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;
+}
+
+static const RTGETOPTDEF g_aCreateVMOptions[] =
+{
+ { "--name", 'n', RTGETOPT_REQ_STRING },
+ { "-name", 'n', RTGETOPT_REQ_STRING },
+ { "--groups", 'g', RTGETOPT_REQ_STRING },
+ { "--basefolder", 'p', RTGETOPT_REQ_STRING },
+ { "-basefolder", 'p', RTGETOPT_REQ_STRING },
+ { "--ostype", 'o', RTGETOPT_REQ_STRING },
+ { "-ostype", 'o', RTGETOPT_REQ_STRING },
+ { "--uuid", 'u', RTGETOPT_REQ_UUID },
+ { "-uuid", 'u', RTGETOPT_REQ_UUID },
+ { "--register", 'r', RTGETOPT_REQ_NOTHING },
+ { "-register", 'r', RTGETOPT_REQ_NOTHING },
+ { "--default", 'd', RTGETOPT_REQ_NOTHING },
+ { "-default", 'd', RTGETOPT_REQ_NOTHING },
+};
+
+RTEXITCODE handleCreateVM(HandlerArg *a)
+{
+ HRESULT rc;
+ Bstr bstrBaseFolder;
+ Bstr bstrName;
+ Bstr bstrOsTypeId;
+ Bstr bstrUuid;
+ bool fRegister = false;
+ bool fDefault = false;
+ /* TBD. Now not used */
+ Bstr bstrDefaultFlags;
+ com::SafeArray<BSTR> groups;
+
+ 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_aCreateVMOptions, RT_ELEMENTS(g_aCreateVMOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'n': // --name
+ bstrName = ValueUnion.psz;
+ break;
+
+ case 'g': // --groups
+ parseGroups(ValueUnion.psz, &groups);
+ break;
+
+ case 'p': // --basefolder
+ bstrBaseFolder = ValueUnion.psz;
+ break;
+
+ case 'o': // --ostype
+ bstrOsTypeId = ValueUnion.psz;
+ break;
+
+ case 'u': // --uuid
+ bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
+ break;
+
+ case 'r': // --register
+ fRegister = true;
+ break;
+
+ case 'd': // --default
+ fDefault = true;
+ break;
+
+ default:
+ return errorGetOpt(USAGE_CREATEVM, c, &ValueUnion);
+ }
+ }
+
+ /* check for required options */
+ if (bstrName.isEmpty())
+ return errorSyntax(USAGE_CREATEVM, "Parameter --name is required");
+
+ do
+ {
+ Bstr createFlags;
+ if (!bstrUuid.isEmpty())
+ createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
+ Bstr bstrPrimaryGroup;
+ if (groups.size())
+ bstrPrimaryGroup = groups[0];
+ Bstr bstrSettingsFile;
+ CHECK_ERROR_BREAK(a->virtualBox,
+ ComposeMachineFilename(bstrName.raw(),
+ bstrPrimaryGroup.raw(),
+ createFlags.raw(),
+ bstrBaseFolder.raw(),
+ bstrSettingsFile.asOutParam()));
+ ComPtr<IMachine> machine;
+ CHECK_ERROR_BREAK(a->virtualBox,
+ CreateMachine(bstrSettingsFile.raw(),
+ bstrName.raw(),
+ ComSafeArrayAsInParam(groups),
+ bstrOsTypeId.raw(),
+ createFlags.raw(),
+ machine.asOutParam()));
+
+ CHECK_ERROR_BREAK(machine, SaveSettings());
+ if (fRegister)
+ {
+ CHECK_ERROR_BREAK(a->virtualBox, RegisterMachine(machine));
+ }
+ if (fDefault)
+ {
+ /* ApplyDefaults assumes the machine is already registered */
+ CHECK_ERROR_BREAK(machine, ApplyDefaults(bstrDefaultFlags.raw()));
+ CHECK_ERROR_BREAK(machine, SaveSettings());
+ }
+ Bstr uuid;
+ CHECK_ERROR_BREAK(machine, COMGETTER(Id)(uuid.asOutParam()));
+ Bstr settingsFile;
+ CHECK_ERROR_BREAK(machine, COMGETTER(SettingsFilePath)(settingsFile.asOutParam()));
+ RTPrintf("Virtual machine '%ls' is created%s.\n"
+ "UUID: %s\n"
+ "Settings file: '%ls'\n",
+ bstrName.raw(), fRegister ? " and registered" : "",
+ Utf8Str(uuid).c_str(), settingsFile.raw());
+ }
+ while (0);
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static const RTGETOPTDEF g_aMoveVMOptions[] =
+{
+ { "--type", 't', RTGETOPT_REQ_STRING },
+ { "--folder", 'f', RTGETOPT_REQ_STRING },
+};
+
+RTEXITCODE handleMoveVM(HandlerArg *a)
+{
+ HRESULT rc;
+ const char *pszSrcName = NULL;
+ const char *pszTargetFolder = NULL;
+ const char *pszType = NULL;
+
+ int c;
+ int vrc = VINF_SUCCESS;
+ 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_aMoveVMOptions, RT_ELEMENTS(g_aMoveVMOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 't': // --type
+ pszType = ValueUnion.psz;
+ break;
+
+ case 'f': // --target folder
+
+ char szPath[RTPATH_MAX];
+ pszTargetFolder = ValueUnion.psz;
+
+ vrc = RTPathAbs(pszTargetFolder, szPath, sizeof(szPath));
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszTargetFolder, vrc);
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszSrcName)
+ pszSrcName = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_MOVEVM, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ return errorGetOpt(USAGE_MOVEVM, c, &ValueUnion);
+ }
+ }
+
+
+ if (!pszType)
+ {
+ pszType = "basic";
+ }
+
+ /* Check for required options */
+ if (!pszSrcName)
+ return errorSyntax(USAGE_MOVEVM, "VM name required");
+
+ /* Get the machine object */
+ ComPtr<IMachine> srcMachine;
+ CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
+ srcMachine.asOutParam()),
+ RTEXITCODE_FAILURE);
+
+ if (srcMachine)
+ {
+ /* Start the moving */
+ ComPtr<IProgress> progress;
+
+ /* we have to open a session for this task */
+ CHECK_ERROR_RET(srcMachine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
+ ComPtr<IMachine> sessionMachine;
+
+ CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(sessionMachine, MoveTo(Bstr(pszTargetFolder).raw(),
+ Bstr(pszType).raw(),
+ progress.asOutParam()), RTEXITCODE_FAILURE);
+ rc = showProgress(progress);
+ CHECK_PROGRESS_ERROR_RET(progress, ("Move VM failed"), RTEXITCODE_FAILURE);
+
+ sessionMachine.setNull();
+ CHECK_ERROR_RET(a->session, UnlockMachine(), RTEXITCODE_FAILURE);
+
+// do
+// {
+// /* we have to open a session for this task */
+// CHECK_ERROR_BREAK(srcMachine, LockMachine(a->session, LockType_Write));
+// ComPtr<IMachine> sessionMachine;
+// do
+// {
+// CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
+// CHECK_ERROR_BREAK(sessionMachine, MoveTo(Bstr(pszTargetFolder).raw(),
+// Bstr(pszType).raw(),
+// progress.asOutParam()));
+// rc = showProgress(progress);
+// CHECK_PROGRESS_ERROR_RET(progress, ("Move VM failed"), RTEXITCODE_FAILURE);
+//// CHECK_ERROR_BREAK(sessionMachine, SaveSettings());
+// } while (0);
+//
+// sessionMachine.setNull();
+// CHECK_ERROR_BREAK(a->session, UnlockMachine());
+// } while (0);
+ RTPrintf("Machine has been successfully moved into %s\n", pszTargetFolder);
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+static const RTGETOPTDEF g_aCloneVMOptions[] =
+{
+ { "--snapshot", 's', RTGETOPT_REQ_STRING },
+ { "--name", 'n', RTGETOPT_REQ_STRING },
+ { "--groups", 'g', RTGETOPT_REQ_STRING },
+ { "--mode", 'm', RTGETOPT_REQ_STRING },
+ { "--options", 'o', RTGETOPT_REQ_STRING },
+ { "--register", 'r', RTGETOPT_REQ_NOTHING },
+ { "--basefolder", 'p', RTGETOPT_REQ_STRING },
+ { "--uuid", 'u', RTGETOPT_REQ_UUID },
+};
+
+static int parseCloneMode(const char *psz, CloneMode_T *pMode)
+{
+ if (!RTStrICmp(psz, "machine"))
+ *pMode = CloneMode_MachineState;
+ else if (!RTStrICmp(psz, "machineandchildren"))
+ *pMode = CloneMode_MachineAndChildStates;
+ else if (!RTStrICmp(psz, "all"))
+ *pMode = CloneMode_AllStates;
+ else
+ return VERR_PARSE_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+static int parseCloneOptions(const char *psz, com::SafeArray<CloneOptions_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, "KeepAllMACs", len))
+ options->push_back(CloneOptions_KeepAllMACs);
+ else if (!RTStrNICmp(psz, "KeepNATMACs", len))
+ options->push_back(CloneOptions_KeepNATMACs);
+ else if (!RTStrNICmp(psz, "KeepDiskNames", len))
+ options->push_back(CloneOptions_KeepDiskNames);
+ else if ( !RTStrNICmp(psz, "Link", len)
+ || !RTStrNICmp(psz, "Linked", len))
+ options->push_back(CloneOptions_Link);
+ else if ( !RTStrNICmp(psz, "KeepHwUUIDs", len)
+ || !RTStrNICmp(psz, "KeepHwUUID", len))
+ options->push_back(CloneOptions_KeepHwUUIDs);
+ else
+ rc = VERR_PARSE_ERROR;
+ }
+ if (pszComma)
+ psz += len + 1;
+ else
+ psz += len;
+ }
+
+ return rc;
+}
+
+RTEXITCODE handleCloneVM(HandlerArg *a)
+{
+ HRESULT rc;
+ const char *pszSrcName = NULL;
+ const char *pszSnapshotName = NULL;
+ CloneMode_T mode = CloneMode_MachineState;
+ com::SafeArray<CloneOptions_T> options;
+ const char *pszTrgName = NULL;
+ const char *pszTrgBaseFolder = NULL;
+ bool fRegister = false;
+ Bstr bstrUuid;
+ com::SafeArray<BSTR> groups;
+
+ 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_aCloneVMOptions, RT_ELEMENTS(g_aCloneVMOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 's': // --snapshot
+ pszSnapshotName = ValueUnion.psz;
+ break;
+
+ case 'n': // --name
+ pszTrgName = ValueUnion.psz;
+ break;
+
+ case 'g': // --groups
+ parseGroups(ValueUnion.psz, &groups);
+ break;
+
+ case 'p': // --basefolder
+ pszTrgBaseFolder = ValueUnion.psz;
+ break;
+
+ case 'm': // --mode
+ if (RT_FAILURE(parseCloneMode(ValueUnion.psz, &mode)))
+ return errorArgument("Invalid clone mode '%s'\n", ValueUnion.psz);
+ break;
+
+ case 'o': // --options
+ if (RT_FAILURE(parseCloneOptions(ValueUnion.psz, &options)))
+ return errorArgument("Invalid clone options '%s'\n", ValueUnion.psz);
+ break;
+
+ case 'u': // --uuid
+ bstrUuid = Guid(ValueUnion.Uuid).toUtf16().raw();
+ break;
+
+ case 'r': // --register
+ fRegister = true;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszSrcName)
+ pszSrcName = ValueUnion.psz;
+ else
+ return errorSyntax(USAGE_CLONEVM, "Invalid parameter '%s'", ValueUnion.psz);
+ break;
+
+ default:
+ return errorGetOpt(USAGE_CLONEVM, c, &ValueUnion);
+ }
+ }
+
+ /* Check for required options */
+ if (!pszSrcName)
+ return errorSyntax(USAGE_CLONEVM, "VM name required");
+
+ /* Get the machine object */
+ ComPtr<IMachine> srcMachine;
+ CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(pszSrcName).raw(),
+ srcMachine.asOutParam()),
+ RTEXITCODE_FAILURE);
+
+ /* If a snapshot name/uuid was given, get the particular machine of this
+ * snapshot. */
+ if (pszSnapshotName)
+ {
+ ComPtr<ISnapshot> srcSnapshot;
+ CHECK_ERROR_RET(srcMachine, FindSnapshot(Bstr(pszSnapshotName).raw(),
+ srcSnapshot.asOutParam()),
+ RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(srcSnapshot, COMGETTER(Machine)(srcMachine.asOutParam()),
+ RTEXITCODE_FAILURE);
+ }
+
+ /* Default name necessary? */
+ if (!pszTrgName)
+ pszTrgName = RTStrAPrintf2("%s Clone", pszSrcName);
+
+ Bstr createFlags;
+ if (!bstrUuid.isEmpty())
+ createFlags = BstrFmt("UUID=%ls", bstrUuid.raw());
+ Bstr bstrPrimaryGroup;
+ if (groups.size())
+ bstrPrimaryGroup = groups[0];
+ Bstr bstrSettingsFile;
+ CHECK_ERROR_RET(a->virtualBox,
+ ComposeMachineFilename(Bstr(pszTrgName).raw(),
+ bstrPrimaryGroup.raw(),
+ createFlags.raw(),
+ Bstr(pszTrgBaseFolder).raw(),
+ bstrSettingsFile.asOutParam()),
+ RTEXITCODE_FAILURE);
+
+ ComPtr<IMachine> trgMachine;
+ CHECK_ERROR_RET(a->virtualBox, CreateMachine(bstrSettingsFile.raw(),
+ Bstr(pszTrgName).raw(),
+ ComSafeArrayAsInParam(groups),
+ NULL,
+ createFlags.raw(),
+ trgMachine.asOutParam()),
+ RTEXITCODE_FAILURE);
+
+ /* Start the cloning */
+ ComPtr<IProgress> progress;
+ CHECK_ERROR_RET(srcMachine, CloneTo(trgMachine,
+ mode,
+ ComSafeArrayAsInParam(options),
+ progress.asOutParam()),
+ RTEXITCODE_FAILURE);
+ rc = showProgress(progress);
+ CHECK_PROGRESS_ERROR_RET(progress, ("Clone VM failed"), RTEXITCODE_FAILURE);
+
+ if (fRegister)
+ CHECK_ERROR_RET(a->virtualBox, RegisterMachine(trgMachine), RTEXITCODE_FAILURE);
+
+ Bstr bstrNewName;
+ CHECK_ERROR_RET(trgMachine, COMGETTER(Name)(bstrNewName.asOutParam()), RTEXITCODE_FAILURE);
+ RTPrintf("Machine has been successfully cloned as \"%ls\"\n", bstrNewName.raw());
+
+ return RTEXITCODE_SUCCESS;
+}
+
+RTEXITCODE handleStartVM(HandlerArg *a)
+{
+ HRESULT rc = S_OK;
+ std::list<const char *> VMs;
+ Bstr sessionType;
+ Utf8Str strEnv;
+
+#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
+ /* make sure the VM process will by default start on the same display as VBoxManage */
+ {
+ const char *pszDisplay = RTEnvGet("DISPLAY");
+ if (pszDisplay)
+ strEnv = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
+ const char *pszXAuth = RTEnvGet("XAUTHORITY");
+ if (pszXAuth)
+ strEnv.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
+ }
+#endif
+
+ static const RTGETOPTDEF s_aStartVMOptions[] =
+ {
+ { "--type", 't', RTGETOPT_REQ_STRING },
+ { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
+ { "--putenv", 'E', RTGETOPT_REQ_STRING },
+ };
+ 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, s_aStartVMOptions, RT_ELEMENTS(s_aStartVMOptions),
+ 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 't': // --type
+ if (!RTStrICmp(ValueUnion.psz, "gui"))
+ {
+ sessionType = "gui";
+ }
+#ifdef VBOX_WITH_VBOXSDL
+ else if (!RTStrICmp(ValueUnion.psz, "sdl"))
+ {
+ sessionType = "sdl";
+ }
+#endif
+#ifdef VBOX_WITH_HEADLESS
+ else if (!RTStrICmp(ValueUnion.psz, "capture"))
+ {
+ sessionType = "capture";
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "headless"))
+ {
+ sessionType = "headless";
+ }
+#endif
+ else
+ sessionType = ValueUnion.psz;
+ break;
+
+ case 'E': // --putenv
+ if (!RTStrStr(ValueUnion.psz, "\n"))
+ strEnv.append(Utf8StrFmt("%s\n", ValueUnion.psz));
+ else
+ return errorSyntax(USAGE_STARTVM, "Parameter to option --putenv must not contain any newline character");
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ VMs.push_back(ValueUnion.psz);
+ break;
+
+ default:
+ if (c > 0)
+ {
+ if (RT_C_IS_PRINT(c))
+ return errorSyntax(USAGE_STARTVM, "Invalid option -%c", c);
+ else
+ return errorSyntax(USAGE_STARTVM, "Invalid option case %i", c);
+ }
+ else if (c == VERR_GETOPT_UNKNOWN_OPTION)
+ return errorSyntax(USAGE_STARTVM, "unknown option: %s\n", ValueUnion.psz);
+ else if (ValueUnion.pDef)
+ return errorSyntax(USAGE_STARTVM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
+ else
+ return errorSyntax(USAGE_STARTVM, "error: %Rrs", c);
+ }
+ }
+
+ /* check for required options */
+ if (VMs.empty())
+ return errorSyntax(USAGE_STARTVM, "at least one VM name or uuid required");
+
+ for (std::list<const char *>::const_iterator it = VMs.begin();
+ it != VMs.end();
+ ++it)
+ {
+ HRESULT rc2 = rc;
+ const char *pszVM = *it;
+ ComPtr<IMachine> machine;
+ CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszVM).raw(),
+ machine.asOutParam()));
+ if (machine)
+ {
+ ComPtr<IProgress> progress;
+ CHECK_ERROR(machine, LaunchVMProcess(a->session, sessionType.raw(),
+ Bstr(strEnv).raw(), progress.asOutParam()));
+ if (SUCCEEDED(rc) && !progress.isNull())
+ {
+ RTPrintf("Waiting for VM \"%s\" to power on...\n", pszVM);
+ CHECK_ERROR(progress, WaitForCompletion(-1));
+ if (SUCCEEDED(rc))
+ {
+ BOOL completed = true;
+ CHECK_ERROR(progress, COMGETTER(Completed)(&completed));
+ if (SUCCEEDED(rc))
+ {
+ ASSERT(completed);
+
+ LONG iRc;
+ CHECK_ERROR(progress, COMGETTER(ResultCode)(&iRc));
+ if (SUCCEEDED(rc))
+ {
+ if (SUCCEEDED(iRc))
+ RTPrintf("VM \"%s\" has been successfully started.\n", pszVM);
+ else
+ {
+ ProgressErrorInfo info(progress);
+ com::GluePrintErrorInfo(info);
+ }
+ rc = iRc;
+ }
+ }
+ }
+ }
+ }
+
+ /* it's important to always close sessions */
+ a->session->UnlockMachine();
+
+ /* make sure that we remember the failed state */
+ if (FAILED(rc2))
+ rc = rc2;
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+RTEXITCODE handleDiscardState(HandlerArg *a)
+{
+ HRESULT rc;
+
+ if (a->argc != 1)
+ return errorSyntax(USAGE_DISCARDSTATE, "Incorrect number of parameters");
+
+ ComPtr<IMachine> machine;
+ CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()));
+ if (machine)
+ {
+ do
+ {
+ /* we have to open a session for this task */
+ CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
+ do
+ {
+ ComPtr<IMachine> sessionMachine;
+ CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
+ CHECK_ERROR_BREAK(sessionMachine, DiscardSavedState(true /* fDeleteFile */));
+ } while (0);
+ CHECK_ERROR_BREAK(a->session, UnlockMachine());
+ } while (0);
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+RTEXITCODE handleAdoptState(HandlerArg *a)
+{
+ HRESULT rc;
+
+ if (a->argc != 2)
+ return errorSyntax(USAGE_ADOPTSTATE, "Incorrect number of parameters");
+
+ ComPtr<IMachine> machine;
+ CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()));
+ if (machine)
+ {
+ char szStateFileAbs[RTPATH_MAX] = "";
+ int vrc = RTPathAbs(a->argv[1], szStateFileAbs, sizeof(szStateFileAbs));
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot convert filename \"%s\" to absolute path: %Rrc", a->argv[0], vrc);
+
+ do
+ {
+ /* we have to open a session for this task */
+ CHECK_ERROR_BREAK(machine, LockMachine(a->session, LockType_Write));
+ do
+ {
+ ComPtr<IMachine> sessionMachine;
+ CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
+ CHECK_ERROR_BREAK(sessionMachine, AdoptSavedState(Bstr(szStateFileAbs).raw()));
+ } while (0);
+ CHECK_ERROR_BREAK(a->session, UnlockMachine());
+ } while (0);
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+RTEXITCODE handleGetExtraData(HandlerArg *a)
+{
+ HRESULT rc = S_OK;
+
+ if (a->argc > 2 || a->argc < 1)
+ return errorSyntax(USAGE_GETEXTRADATA, "Incorrect number of parameters");
+
+ /* global data? */
+ if (!strcmp(a->argv[0], "global"))
+ {
+ /* enumeration? */
+ if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
+ {
+ SafeArray<BSTR> aKeys;
+ CHECK_ERROR(a->virtualBox, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
+
+ for (size_t i = 0;
+ i < aKeys.size();
+ ++i)
+ {
+ Bstr bstrKey(aKeys[i]);
+ Bstr bstrValue;
+ CHECK_ERROR(a->virtualBox, GetExtraData(bstrKey.raw(),
+ bstrValue.asOutParam()));
+
+ RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
+ }
+ }
+ else
+ {
+ Bstr value;
+ CHECK_ERROR(a->virtualBox, GetExtraData(Bstr(a->argv[1]).raw(),
+ value.asOutParam()));
+ if (!value.isEmpty())
+ RTPrintf("Value: %ls\n", value.raw());
+ else
+ RTPrintf("No value set!\n");
+ }
+ }
+ else
+ {
+ ComPtr<IMachine> machine;
+ CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()));
+ if (machine)
+ {
+ /* enumeration? */
+ if (a->argc < 2 || !strcmp(a->argv[1], "enumerate"))
+ {
+ SafeArray<BSTR> aKeys;
+ CHECK_ERROR(machine, GetExtraDataKeys(ComSafeArrayAsOutParam(aKeys)));
+
+ for (size_t i = 0;
+ i < aKeys.size();
+ ++i)
+ {
+ Bstr bstrKey(aKeys[i]);
+ Bstr bstrValue;
+ CHECK_ERROR(machine, GetExtraData(bstrKey.raw(),
+ bstrValue.asOutParam()));
+
+ RTPrintf("Key: %ls, Value: %ls\n", bstrKey.raw(), bstrValue.raw());
+ }
+ }
+ else
+ {
+ Bstr value;
+ CHECK_ERROR(machine, GetExtraData(Bstr(a->argv[1]).raw(),
+ value.asOutParam()));
+ if (!value.isEmpty())
+ RTPrintf("Value: %ls\n", value.raw());
+ else
+ RTPrintf("No value set!\n");
+ }
+ }
+ }
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+RTEXITCODE handleSetExtraData(HandlerArg *a)
+{
+ HRESULT rc = S_OK;
+
+ if (a->argc < 2)
+ return errorSyntax(USAGE_SETEXTRADATA, "Not enough parameters");
+
+ /* global data? */
+ if (!strcmp(a->argv[0], "global"))
+ {
+ /** @todo passing NULL is deprecated */
+ if (a->argc < 3)
+ CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
+ NULL));
+ else if (a->argc == 3)
+ CHECK_ERROR(a->virtualBox, SetExtraData(Bstr(a->argv[1]).raw(),
+ Bstr(a->argv[2]).raw()));
+ else
+ return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
+ }
+ else
+ {
+ ComPtr<IMachine> machine;
+ CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()));
+ if (machine)
+ {
+ /* open an existing session for the VM */
+ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
+ /* get the session machine */
+ ComPtr<IMachine> sessionMachine;
+ CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
+ /** @todo passing NULL is deprecated */
+ if (a->argc < 3)
+ CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
+ NULL));
+ else if (a->argc == 3)
+ CHECK_ERROR(sessionMachine, SetExtraData(Bstr(a->argv[1]).raw(),
+ Bstr(a->argv[2]).raw()));
+ else
+ return errorSyntax(USAGE_SETEXTRADATA, "Too many parameters");
+ }
+ }
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+RTEXITCODE handleSetProperty(HandlerArg *a)
+{
+ HRESULT rc;
+
+ /* there must be two arguments: property name and value */
+ if (a->argc != 2)
+ return errorSyntax(USAGE_SETPROPERTY, "Incorrect number of parameters");
+
+ ComPtr<ISystemProperties> systemProperties;
+ a->virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
+
+ if (!strcmp(a->argv[0], "machinefolder"))
+ {
+ /* reset to default? */
+ if (!strcmp(a->argv[1], "default"))
+ CHECK_ERROR(systemProperties, COMSETTER(DefaultMachineFolder)(NULL));
+ 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"))
+ {
+ if (!strcmp(a->argv[0], "vrdpauthlibrary"))
+ RTStrmPrintf(g_pStdErr, "Warning: 'vrdpauthlibrary' is deprecated. Use 'vrdeauthlibrary'.\n");
+
+ /* reset to default? */
+ if (!strcmp(a->argv[1], "default"))
+ CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(NULL));
+ else
+ CHECK_ERROR(systemProperties, COMSETTER(VRDEAuthLibrary)(Bstr(a->argv[1]).raw()));
+ }
+ else if (!strcmp(a->argv[0], "websrvauthlibrary"))
+ {
+ /* reset to default? */
+ if (!strcmp(a->argv[1], "default"))
+ CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(NULL));
+ else
+ CHECK_ERROR(systemProperties, COMSETTER(WebServiceAuthLibrary)(Bstr(a->argv[1]).raw()));
+ }
+ else if (!strcmp(a->argv[0], "vrdeextpack"))
+ {
+ /* disable? */
+ if (!strcmp(a->argv[1], "null"))
+ CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(NULL));
+ else
+ CHECK_ERROR(systemProperties, COMSETTER(DefaultVRDEExtPack)(Bstr(a->argv[1]).raw()));
+ }
+ else if (!strcmp(a->argv[0], "loghistorycount"))
+ {
+ uint32_t uVal;
+ int vrc;
+ vrc = RTStrToUInt32Ex(a->argv[1], NULL, 0, &uVal);
+ if (vrc != VINF_SUCCESS)
+ return errorArgument("Error parsing Log history count '%s'", a->argv[1]);
+ CHECK_ERROR(systemProperties, COMSETTER(LogHistoryCount)(uVal));
+ }
+ else if (!strcmp(a->argv[0], "autostartdbpath"))
+ {
+ /* disable? */
+ if (!strcmp(a->argv[1], "null"))
+ CHECK_ERROR(systemProperties, COMSETTER(AutostartDatabasePath)(NULL));
+ 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 if (!strcmp(a->argv[0], "logginglevel"))
+ {
+ Bstr bstrLoggingLevel(a->argv[1]);
+ if (!strcmp(a->argv[1], "default"))
+ bstrLoggingLevel.setNull();
+ CHECK_ERROR(systemProperties, COMSETTER(LoggingLevel)(bstrLoggingLevel.raw()));
+ }
+ else if (!strcmp(a->argv[0], "proxymode"))
+ {
+ ProxyMode_T enmProxyMode;
+ if (!RTStrICmpAscii(a->argv[1], "system"))
+ enmProxyMode = ProxyMode_System;
+ else if (!RTStrICmpAscii(a->argv[1], "noproxy"))
+ enmProxyMode = ProxyMode_NoProxy;
+ else if (!RTStrICmpAscii(a->argv[1], "manual"))
+ enmProxyMode = ProxyMode_Manual;
+ else
+ return errorArgument("Unknown proxy mode: '%s'", a->argv[1]);
+ CHECK_ERROR(systemProperties, COMSETTER(ProxyMode)(enmProxyMode));
+ }
+ else if (!strcmp(a->argv[0], "proxyurl"))
+ {
+ Bstr bstrProxyUrl(a->argv[1]);
+ CHECK_ERROR(systemProperties, COMSETTER(ProxyURL)(bstrProxyUrl.raw()));
+ }
+ else
+ return errorSyntax(USAGE_SETPROPERTY, "Invalid parameter '%s'", a->argv[0]);
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+RTEXITCODE handleSharedFolder(HandlerArg *a)
+{
+ HRESULT rc;
+
+ /* we need at least a command and target */
+ if (a->argc < 2)
+ return errorSyntax(USAGE_SHAREDFOLDER, "Not enough parameters");
+
+ const char *pszMachineName = a->argv[1];
+ ComPtr<IMachine> machine;
+ CHECK_ERROR(a->virtualBox, FindMachine(Bstr(pszMachineName).raw(), machine.asOutParam()));
+ if (!machine)
+ return RTEXITCODE_FAILURE;
+
+ if (!strcmp(a->argv[0], "add"))
+ {
+ /* we need at least four more parameters */
+ if (a->argc < 5)
+ return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Not enough parameters");
+
+ char *name = NULL;
+ char *hostpath = NULL;
+ bool fTransient = false;
+ bool fWritable = true;
+ bool fAutoMount = false;
+ const char *pszAutoMountPoint = "";
+
+ for (int i = 2; i < a->argc; i++)
+ {
+ if ( !strcmp(a->argv[i], "--name")
+ || !strcmp(a->argv[i], "-name"))
+ {
+ if (a->argc <= i + 1 || !*a->argv[i+1])
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ name = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--hostpath")
+ || !strcmp(a->argv[i], "-hostpath"))
+ {
+ if (a->argc <= i + 1 || !*a->argv[i+1])
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ hostpath = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--readonly")
+ || !strcmp(a->argv[i], "-readonly"))
+ {
+ fWritable = false;
+ }
+ else if ( !strcmp(a->argv[i], "--transient")
+ || !strcmp(a->argv[i], "-transient"))
+ {
+ fTransient = true;
+ }
+ else if ( !strcmp(a->argv[i], "--automount")
+ || !strcmp(a->argv[i], "-automount"))
+ {
+ fAutoMount = true;
+ }
+ else if (!strcmp(a->argv[i], "--auto-mount-point"))
+ {
+ if (a->argc <= i + 1 || !*a->argv[i+1])
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ pszAutoMountPoint = a->argv[i];
+ }
+ else
+ return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
+ }
+
+ if (NULL != strstr(name, " "))
+ return errorSyntax(USAGE_SHAREDFOLDER_ADD, "No spaces allowed in parameter '-name'!");
+
+ /* required arguments */
+ if (!name || !hostpath)
+ {
+ return errorSyntax(USAGE_SHAREDFOLDER_ADD, "Parameters --name and --hostpath are required");
+ }
+
+ if (fTransient)
+ {
+ ComPtr<IConsole> console;
+
+ /* open an existing session for the VM */
+ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
+
+ /* get the session machine */
+ ComPtr<IMachine> sessionMachine;
+ CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
+
+ /* get the session console */
+ CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
+ if (console.isNull())
+ return RTMsgErrorExit(RTEXITCODE_FAILURE,
+ "Machine '%s' is not currently running.\n", pszMachineName);
+
+ CHECK_ERROR(console, CreateSharedFolder(Bstr(name).raw(), Bstr(hostpath).raw(),
+ fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
+ a->session->UnlockMachine();
+ }
+ else
+ {
+ /* open a session for the VM */
+ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
+
+ /* get the mutable session machine */
+ ComPtr<IMachine> sessionMachine;
+ a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
+
+ CHECK_ERROR(sessionMachine, CreateSharedFolder(Bstr(name).raw(), Bstr(hostpath).raw(),
+ fWritable, fAutoMount, Bstr(pszAutoMountPoint).raw()));
+ if (SUCCEEDED(rc))
+ CHECK_ERROR(sessionMachine, SaveSettings());
+
+ a->session->UnlockMachine();
+ }
+ }
+ else if (!strcmp(a->argv[0], "remove"))
+ {
+ /* we need at least two more parameters */
+ if (a->argc < 3)
+ return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Not enough parameters");
+
+ char *name = NULL;
+ bool fTransient = false;
+
+ for (int i = 2; i < a->argc; i++)
+ {
+ if ( !strcmp(a->argv[i], "--name")
+ || !strcmp(a->argv[i], "-name"))
+ {
+ if (a->argc <= i + 1 || !*a->argv[i+1])
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ name = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--transient")
+ || !strcmp(a->argv[i], "-transient"))
+ {
+ fTransient = true;
+ }
+ else
+ return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
+ }
+
+ /* required arguments */
+ if (!name)
+ return errorSyntax(USAGE_SHAREDFOLDER_REMOVE, "Parameter --name is required");
+
+ if (fTransient)
+ {
+ /* open an existing session for the VM */
+ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
+ /* get the session machine */
+ ComPtr<IMachine> sessionMachine;
+ CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
+ /* get the session console */
+ ComPtr<IConsole> console;
+ CHECK_ERROR_RET(a->session, COMGETTER(Console)(console.asOutParam()), RTEXITCODE_FAILURE);
+ if (console.isNull())
+ return RTMsgErrorExit(RTEXITCODE_FAILURE,
+ "Machine '%s' is not currently running.\n", pszMachineName);
+
+ CHECK_ERROR(console, RemoveSharedFolder(Bstr(name).raw()));
+
+ a->session->UnlockMachine();
+ }
+ else
+ {
+ /* open a session for the VM */
+ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
+
+ /* get the mutable session machine */
+ ComPtr<IMachine> sessionMachine;
+ a->session->COMGETTER(Machine)(sessionMachine.asOutParam());
+
+ CHECK_ERROR(sessionMachine, RemoveSharedFolder(Bstr(name).raw()));
+
+ /* commit and close the session */
+ CHECK_ERROR(sessionMachine, SaveSettings());
+ a->session->UnlockMachine();
+ }
+ }
+ else
+ return errorSyntax(USAGE_SHAREDFOLDER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
+
+ return RTEXITCODE_SUCCESS;
+}
+
+RTEXITCODE handleExtPack(HandlerArg *a)
+{
+ if (a->argc < 1)
+ return errorNoSubcommand();
+
+ ComObjPtr<IExtPackManager> ptrExtPackMgr;
+ CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(ExtensionPackManager)(ptrExtPackMgr.asOutParam()), RTEXITCODE_FAILURE);
+
+ RTGETOPTSTATE GetState;
+ RTGETOPTUNION ValueUnion;
+ int ch;
+ HRESULT hrc = S_OK;
+
+ if (!strcmp(a->argv[0], "install"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_EXTPACK_INSTALL);
+ const char *pszName = NULL;
+ bool fReplace = false;
+
+ static const RTGETOPTDEF s_aInstallOptions[] =
+ {
+ { "--replace", 'r', RTGETOPT_REQ_NOTHING },
+ { "--accept-license", 'a', RTGETOPT_REQ_STRING },
+ };
+
+ RTCList<RTCString> lstLicenseHashes;
+ RTGetOptInit(&GetState, a->argc, a->argv, s_aInstallOptions, RT_ELEMENTS(s_aInstallOptions), 1, 0 /*fFlags*/);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'r':
+ fReplace = true;
+ break;
+
+ case 'a':
+ lstLicenseHashes.append(ValueUnion.psz);
+ lstLicenseHashes[lstLicenseHashes.size() - 1].toLower();
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (pszName)
+ return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
+ pszName = ValueUnion.psz;
+ break;
+
+ default:
+ return errorGetOpt(ch, &ValueUnion);
+ }
+ }
+ if (!pszName)
+ return errorSyntax("No extension pack name was given to \"extpack install\"");
+
+ char szPath[RTPATH_MAX];
+ int vrc = RTPathAbs(pszName, szPath, sizeof(szPath));
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs(%s,,) failed with rc=%Rrc", pszName, vrc);
+
+ Bstr bstrTarball(szPath);
+ Bstr bstrName;
+ ComPtr<IExtPackFile> ptrExtPackFile;
+ CHECK_ERROR2I_RET(ptrExtPackMgr, OpenExtPackFile(bstrTarball.raw(), ptrExtPackFile.asOutParam()), RTEXITCODE_FAILURE);
+ CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
+ BOOL fShowLicense = true;
+ CHECK_ERROR2I_RET(ptrExtPackFile, COMGETTER(ShowLicense)(&fShowLicense), RTEXITCODE_FAILURE);
+ if (fShowLicense)
+ {
+ Bstr bstrLicense;
+ CHECK_ERROR2I_RET(ptrExtPackFile,
+ QueryLicense(Bstr("").raw() /* PreferredLocale */,
+ Bstr("").raw() /* PreferredLanguage */,
+ Bstr("txt").raw() /* Format */,
+ bstrLicense.asOutParam()), RTEXITCODE_FAILURE);
+ Utf8Str strLicense(bstrLicense);
+ uint8_t abHash[RTSHA256_HASH_SIZE];
+ char szDigest[RTSHA256_DIGEST_LEN + 1];
+ RTSha256(strLicense.c_str(), strLicense.length(), abHash);
+ vrc = RTSha256ToString(abHash, szDigest, sizeof(szDigest));
+ AssertRCStmt(vrc, szDigest[0] = '\0');
+ if (lstLicenseHashes.contains(szDigest))
+ RTPrintf("License accepted.\n");
+ else
+ {
+ RTPrintf("%s\n", strLicense.c_str());
+ RTPrintf("Do you agree to these license terms and conditions (y/n)? " );
+ ch = RTStrmGetCh(g_pStdIn);
+ RTPrintf("\n");
+ if (ch != 'y' && ch != 'Y')
+ {
+ RTPrintf("Installation of \"%ls\" aborted.\n", bstrName.raw());
+ return RTEXITCODE_FAILURE;
+ }
+ if (szDigest[0])
+ RTPrintf("License accepted. For batch installaltion add\n"
+ "--accept-license=%s\n"
+ "to the VBoxManage command line.\n\n", szDigest);
+ }
+ }
+ ComPtr<IProgress> ptrProgress;
+ CHECK_ERROR2I_RET(ptrExtPackFile, Install(fReplace, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
+ hrc = showProgress(ptrProgress);
+ CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to install \"%s\"", szPath), RTEXITCODE_FAILURE);
+
+ RTPrintf("Successfully installed \"%ls\".\n", bstrName.raw());
+ }
+ else if (!strcmp(a->argv[0], "uninstall"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_EXTPACK_UNINSTALL);
+ const char *pszName = NULL;
+ bool fForced = false;
+
+ static const RTGETOPTDEF s_aUninstallOptions[] =
+ {
+ { "--force", 'f', RTGETOPT_REQ_NOTHING },
+ };
+
+ RTGetOptInit(&GetState, a->argc, a->argv, s_aUninstallOptions, RT_ELEMENTS(s_aUninstallOptions), 1, 0);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'f':
+ fForced = true;
+ break;
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (pszName)
+ return errorSyntax("Too many extension pack names given to \"extpack uninstall\"");
+ pszName = ValueUnion.psz;
+ break;
+
+ default:
+ return errorGetOpt(ch, &ValueUnion);
+ }
+ }
+ if (!pszName)
+ return errorSyntax("No extension pack name was given to \"extpack uninstall\"");
+
+ Bstr bstrName(pszName);
+ ComPtr<IProgress> ptrProgress;
+ CHECK_ERROR2I_RET(ptrExtPackMgr, Uninstall(bstrName.raw(), fForced, NULL, ptrProgress.asOutParam()), RTEXITCODE_FAILURE);
+ hrc = showProgress(ptrProgress);
+ CHECK_PROGRESS_ERROR_RET(ptrProgress, ("Failed to uninstall \"%s\"", pszName), RTEXITCODE_FAILURE);
+
+ RTPrintf("Successfully uninstalled \"%s\".\n", pszName);
+ }
+ else if (!strcmp(a->argv[0], "cleanup"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_EXTPACK_CLEANUP);
+ if (a->argc > 1)
+ return errorTooManyParameters(&a->argv[1]);
+ CHECK_ERROR2I_RET(ptrExtPackMgr, Cleanup(), RTEXITCODE_FAILURE);
+ RTPrintf("Successfully performed extension pack cleanup\n");
+ }
+ else
+ return errorUnknownSubcommand(a->argv[0]);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+RTEXITCODE handleUnattendedDetect(HandlerArg *a)
+{
+ HRESULT hrc;
+
+ /*
+ * Options. We work directly on an IUnattended instace while parsing
+ * the options. This saves a lot of extra clutter.
+ */
+ bool fMachineReadable = false;
+ char szIsoPath[RTPATH_MAX];
+ szIsoPath[0] = '\0';
+
+ /*
+ * Parse options.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--iso", 'i', RTGETOPT_REQ_STRING },
+ { "--machine-readable", 'M', RTGETOPT_REQ_NOTHING },
+ };
+
+ RTGETOPTSTATE GetState;
+ int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(vrc, RTEXITCODE_FAILURE);
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (c)
+ {
+ case 'i': // --iso
+ vrc = RTPathAbs(ValueUnion.psz, szIsoPath, sizeof(szIsoPath));
+ if (RT_FAILURE(vrc))
+ return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
+ break;
+
+ case 'M': // --machine-readable.
+ fMachineReadable = true;
+ break;
+
+ default:
+ return errorGetOpt(c, &ValueUnion);
+ }
+ }
+
+ /*
+ * Check for required stuff.
+ */
+ if (szIsoPath[0] == '\0')
+ return errorSyntax("No ISO specified");
+
+ /*
+ * Do the job.
+ */
+ ComPtr<IUnattended> ptrUnattended;
+ CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szIsoPath).raw()), RTEXITCODE_FAILURE);
+ CHECK_ERROR2(hrc, ptrUnattended, DetectIsoOS());
+ RTEXITCODE rcExit = SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+
+ /*
+ * Retrieve the results.
+ */
+ Bstr bstrDetectedOSTypeId;
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSTypeId)(bstrDetectedOSTypeId.asOutParam()), RTEXITCODE_FAILURE);
+ Bstr bstrDetectedVersion;
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSVersion)(bstrDetectedVersion.asOutParam()), RTEXITCODE_FAILURE);
+ Bstr bstrDetectedFlavor;
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSFlavor)(bstrDetectedFlavor.asOutParam()), RTEXITCODE_FAILURE);
+ Bstr bstrDetectedLanguages;
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSLanguages)(bstrDetectedLanguages.asOutParam()), RTEXITCODE_FAILURE);
+ Bstr bstrDetectedHints;
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMGETTER(DetectedOSHints)(bstrDetectedHints.asOutParam()), RTEXITCODE_FAILURE);
+ if (fMachineReadable)
+ RTPrintf("OSTypeId=\"%ls\"\n"
+ "OSVersion=\"%ls\"\n"
+ "OSFlavor=\"%ls\"\n"
+ "OSLanguages=\"%ls\"\n"
+ "OSHints=\"%ls\"\n",
+ bstrDetectedOSTypeId.raw(),
+ bstrDetectedVersion.raw(),
+ bstrDetectedFlavor.raw(),
+ bstrDetectedLanguages.raw(),
+ bstrDetectedHints.raw());
+ else
+ {
+ RTMsgInfo("Detected '%s' to be:\n", szIsoPath);
+ RTPrintf(" OS TypeId = %ls\n"
+ " OS Version = %ls\n"
+ " OS Flavor = %ls\n"
+ " OS Languages = %ls\n"
+ " OS Hints = %ls\n",
+ bstrDetectedOSTypeId.raw(),
+ bstrDetectedVersion.raw(),
+ bstrDetectedFlavor.raw(),
+ bstrDetectedLanguages.raw(),
+ bstrDetectedHints.raw());
+ }
+
+ return rcExit;
+}
+
+RTEXITCODE handleUnattendedInstall(HandlerArg *a)
+{
+ HRESULT hrc;
+ char szAbsPath[RTPATH_MAX];
+
+ /*
+ * Options. We work directly on an IUnattended instace while parsing
+ * the options. This saves a lot of extra clutter.
+ */
+ ComPtr<IUnattended> ptrUnattended;
+ CHECK_ERROR2_RET(hrc, a->virtualBox, CreateUnattendedInstaller(ptrUnattended.asOutParam()), RTEXITCODE_FAILURE);
+ RTCList<RTCString> arrPackageSelectionAdjustments;
+ ComPtr<IMachine> ptrMachine;
+ bool fDryRun = false;
+ const char *pszSessionType = "none";
+
+ /*
+ * Parse options.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--iso", 'i', RTGETOPT_REQ_STRING },
+ { "--user", 'u', RTGETOPT_REQ_STRING },
+ { "--password", 'p', RTGETOPT_REQ_STRING },
+ { "--password-file", 'X', RTGETOPT_REQ_STRING },
+ { "--full-user-name", 'U', RTGETOPT_REQ_STRING },
+ { "--key", 'k', RTGETOPT_REQ_STRING },
+ { "--install-additions", 'A', RTGETOPT_REQ_NOTHING },
+ { "--no-install-additions", 'N', RTGETOPT_REQ_NOTHING },
+ { "--additions-iso", 'a', RTGETOPT_REQ_STRING },
+ { "--install-txs", 't', RTGETOPT_REQ_NOTHING },
+ { "--no-install-txs", 'T', RTGETOPT_REQ_NOTHING },
+ { "--validation-kit-iso", 'K', RTGETOPT_REQ_STRING },
+ { "--locale", 'l', RTGETOPT_REQ_STRING },
+ { "--country", 'Y', RTGETOPT_REQ_STRING },
+ { "--time-zone", 'z', RTGETOPT_REQ_STRING },
+ { "--proxy", 'y', RTGETOPT_REQ_STRING },
+ { "--hostname", 'H', RTGETOPT_REQ_STRING },
+ { "--package-selection-adjustment", 's', RTGETOPT_REQ_STRING },
+ { "--dry-run", 'D', RTGETOPT_REQ_NOTHING },
+ // advance options:
+ { "--auxiliary-base-path", 'x', RTGETOPT_REQ_STRING },
+ { "--image-index", 'm', RTGETOPT_REQ_UINT32 },
+ { "--script-template", 'c', RTGETOPT_REQ_STRING },
+ { "--post-install-template", 'C', RTGETOPT_REQ_STRING },
+ { "--post-install-command", 'P', RTGETOPT_REQ_STRING },
+ { "--extra-install-kernel-parameters", 'I', RTGETOPT_REQ_STRING },
+ { "--language", 'L', RTGETOPT_REQ_STRING },
+ // start vm related options:
+ { "--start-vm", 'S', RTGETOPT_REQ_STRING },
+ /** @todo Add a --wait option too for waiting for the VM to shut down or
+ * something like that...? */
+ };
+
+ RTGETOPTSTATE GetState;
+ int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(vrc, RTEXITCODE_FAILURE);
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (c)
+ {
+ case VINF_GETOPT_NOT_OPTION:
+ if (ptrMachine.isNotNull())
+ return errorSyntax("VM name/UUID given more than once!");
+ CHECK_ERROR2_RET(hrc, a->virtualBox, FindMachine(Bstr(ValueUnion.psz).raw(), ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Machine)(ptrMachine), RTEXITCODE_FAILURE);
+ break;
+
+ case 'i': // --iso
+ vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
+ if (RT_FAILURE(vrc))
+ return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(IsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'u': // --user
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(User)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'p': // --password
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'X': // --password-file
+ {
+ Utf8Str strPassword;
+ RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &strPassword);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Password)(Bstr(strPassword).raw()), RTEXITCODE_FAILURE);
+ break;
+ }
+
+ case 'U': // --full-user-name
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(FullUserName)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'k': // --key
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ProductKey)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'A': // --install-additions
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(TRUE), RTEXITCODE_FAILURE);
+ break;
+ case 'N': // --no-install-additions
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallGuestAdditions)(FALSE), RTEXITCODE_FAILURE);
+ break;
+ case 'a': // --additions-iso
+ vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
+ if (RT_FAILURE(vrc))
+ return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AdditionsIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 't': // --install-txs
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(TRUE), RTEXITCODE_FAILURE);
+ break;
+ case 'T': // --no-install-txs
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(InstallTestExecService)(FALSE), RTEXITCODE_FAILURE);
+ break;
+ case 'K': // --valiation-kit-iso
+ vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
+ if (RT_FAILURE(vrc))
+ return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ValidationKitIsoPath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'l': // --locale
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Locale)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'Y': // --country
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Country)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'z': // --time-zone;
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(TimeZone)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'y': // --proxy
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Proxy)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'H': // --hostname
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Hostname)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 's': // --package-selection-adjustment
+ arrPackageSelectionAdjustments.append(ValueUnion.psz);
+ break;
+
+ case 'D':
+ fDryRun = true;
+ break;
+
+ case 'x': // --auxiliary-base-path
+ vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
+ if (RT_FAILURE(vrc))
+ return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(AuxiliaryBasePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'm': // --image-index
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ImageIndex)(ValueUnion.u32), RTEXITCODE_FAILURE);
+ break;
+
+ case 'c': // --script-template
+ vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
+ if (RT_FAILURE(vrc))
+ return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'C': // --post-install-script-template
+ vrc = RTPathAbs(ValueUnion.psz, szAbsPath, sizeof(szAbsPath));
+ if (RT_FAILURE(vrc))
+ return errorSyntax("RTPathAbs failed on '%s': %Rrc", ValueUnion.psz, vrc);
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallScriptTemplatePath)(Bstr(szAbsPath).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'P': // --post-install-command.
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PostInstallCommand)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'I': // --extra-install-kernel-parameters
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(ExtraInstallKernelParameters)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'L': // --language
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(Language)(Bstr(ValueUnion.psz).raw()), RTEXITCODE_FAILURE);
+ break;
+
+ case 'S': // --start-vm
+ pszSessionType = ValueUnion.psz;
+ break;
+
+ default:
+ return errorGetOpt(c, &ValueUnion);
+ }
+ }
+
+ /*
+ * Check for required stuff.
+ */
+ if (ptrMachine.isNull())
+ return errorSyntax("Missing VM name/UUID");
+
+ /*
+ * Set accumulative attributes.
+ */
+ if (arrPackageSelectionAdjustments.size() == 1)
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(arrPackageSelectionAdjustments[0]).raw()),
+ RTEXITCODE_FAILURE);
+ else if (arrPackageSelectionAdjustments.size() > 1)
+ {
+ RTCString strAdjustments;
+ strAdjustments.join(arrPackageSelectionAdjustments, ";");
+ CHECK_ERROR2_RET(hrc, ptrUnattended, COMSETTER(PackageSelectionAdjustments)(Bstr(strAdjustments).raw()), RTEXITCODE_FAILURE);
+ }
+
+ /*
+ * Get details about the machine so we can display them below.
+ */
+ Bstr bstrMachineName;
+ CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Name)(bstrMachineName.asOutParam()), RTEXITCODE_FAILURE);
+ Bstr bstrUuid;
+ CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(Id)(bstrUuid.asOutParam()), RTEXITCODE_FAILURE);
+ BSTR bstrInstalledOS;
+ CHECK_ERROR2_RET(hrc, ptrMachine, COMGETTER(OSTypeId)(&bstrInstalledOS), RTEXITCODE_FAILURE);
+ Utf8Str strInstalledOS(bstrInstalledOS);
+
+ /*
+ * Temporarily lock the machine to check whether it's running or not.
+ * We take this opportunity to disable the first run wizard.
+ */
+ CHECK_ERROR2_RET(hrc, ptrMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
+ {
+ ComPtr<IConsole> ptrConsole;
+ CHECK_ERROR2(hrc, a->session, COMGETTER(Console)(ptrConsole.asOutParam()));
+
+ if ( ptrConsole.isNull()
+ && SUCCEEDED(hrc)
+ && ( RTStrICmp(pszSessionType, "gui") == 0
+ || RTStrICmp(pszSessionType, "none") == 0))
+ {
+ ComPtr<IMachine> ptrSessonMachine;
+ CHECK_ERROR2(hrc, a->session, COMGETTER(Machine)(ptrSessonMachine.asOutParam()));
+ if (ptrSessonMachine.isNotNull())
+ {
+ CHECK_ERROR2(hrc, ptrSessonMachine, SetExtraData(Bstr("GUI/FirstRun").raw(), Bstr("0").raw()));
+ }
+ }
+
+ a->session->UnlockMachine();
+ if (FAILED(hrc))
+ return RTEXITCODE_FAILURE;
+ if (ptrConsole.isNotNull())
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine '%ls' is currently running", bstrMachineName.raw());
+ }
+
+ /*
+ * Do the work.
+ */
+ RTMsgInfo("%s unattended installation of %s in machine '%ls' (%ls).\n",
+ RTStrICmp(pszSessionType, "none") == 0 ? "Preparing" : "Starting",
+ strInstalledOS.c_str(), bstrMachineName.raw(), bstrUuid.raw());
+
+ CHECK_ERROR2_RET(hrc, ptrUnattended,Prepare(), RTEXITCODE_FAILURE);
+ if (!fDryRun)
+ {
+ CHECK_ERROR2_RET(hrc, ptrUnattended, ConstructMedia(), RTEXITCODE_FAILURE);
+ CHECK_ERROR2_RET(hrc, ptrUnattended,ReconfigureVM(), RTEXITCODE_FAILURE);
+ }
+
+ /*
+ * Retrieve and display the parameters actually used.
+ */
+ RTMsgInfo("Using values:\n");
+#define SHOW_ATTR(a_Attr, a_szText, a_Type, a_szFmt) do { \
+ a_Type Value; \
+ HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(&Value); \
+ if (SUCCEEDED(hrc2)) \
+ RTPrintf(" %32s = " a_szFmt "\n", a_szText, Value); \
+ else \
+ RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
+ } while (0)
+#define SHOW_STR_ATTR(a_Attr, a_szText) do { \
+ Bstr bstrString; \
+ HRESULT hrc2 = ptrUnattended->COMGETTER(a_Attr)(bstrString.asOutParam()); \
+ if (SUCCEEDED(hrc2)) \
+ RTPrintf(" %32s = %ls\n", a_szText, bstrString.raw()); \
+ else \
+ RTPrintf(" %32s = failed: %Rhrc\n", a_szText, hrc2); \
+ } while (0)
+
+ SHOW_STR_ATTR(IsoPath, "isoPath");
+ SHOW_STR_ATTR(User, "user");
+ SHOW_STR_ATTR(Password, "password");
+ SHOW_STR_ATTR(FullUserName, "fullUserName");
+ SHOW_STR_ATTR(ProductKey, "productKey");
+ SHOW_STR_ATTR(AdditionsIsoPath, "additionsIsoPath");
+ SHOW_ATTR( InstallGuestAdditions, "installGuestAdditions", BOOL, "%RTbool");
+ SHOW_STR_ATTR(ValidationKitIsoPath, "validationKitIsoPath");
+ SHOW_ATTR( InstallTestExecService, "installTestExecService", BOOL, "%RTbool");
+ SHOW_STR_ATTR(Locale, "locale");
+ SHOW_STR_ATTR(Country, "country");
+ SHOW_STR_ATTR(TimeZone, "timeZone");
+ SHOW_STR_ATTR(Proxy, "proxy");
+ SHOW_STR_ATTR(Hostname, "hostname");
+ SHOW_STR_ATTR(PackageSelectionAdjustments, "packageSelectionAdjustments");
+ SHOW_STR_ATTR(AuxiliaryBasePath, "auxiliaryBasePath");
+ SHOW_ATTR( ImageIndex, "imageIndex", ULONG, "%u");
+ SHOW_STR_ATTR(ScriptTemplatePath, "scriptTemplatePath");
+ SHOW_STR_ATTR(PostInstallScriptTemplatePath, "postInstallScriptTemplatePath");
+ SHOW_STR_ATTR(PostInstallCommand, "postInstallCommand");
+ SHOW_STR_ATTR(ExtraInstallKernelParameters, "extraInstallKernelParameters");
+ SHOW_STR_ATTR(Language, "language");
+ SHOW_STR_ATTR(DetectedOSTypeId, "detectedOSTypeId");
+ SHOW_STR_ATTR(DetectedOSVersion, "detectedOSVersion");
+ SHOW_STR_ATTR(DetectedOSFlavor, "detectedOSFlavor");
+ SHOW_STR_ATTR(DetectedOSLanguages, "detectedOSLanguages");
+ SHOW_STR_ATTR(DetectedOSHints, "detectedOSHints");
+
+#undef SHOW_STR_ATTR
+#undef SHOW_ATTR
+
+ /* We can drop the IUnatteded object now. */
+ ptrUnattended.setNull();
+
+ /*
+ * Start the VM if requested.
+ */
+ if ( fDryRun
+ || RTStrICmp(pszSessionType, "none") == 0)
+ {
+ if (!fDryRun)
+ RTMsgInfo("VM '%ls' (%ls) is ready to be started (e.g. VBoxManage startvm).\n", bstrMachineName.raw(), bstrUuid.raw());
+ hrc = S_OK;
+ }
+ else
+ {
+ Bstr env;
+#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
+ /* make sure the VM process will start on the same display as VBoxManage */
+ Utf8Str str;
+ const char *pszDisplay = RTEnvGet("DISPLAY");
+ if (pszDisplay)
+ str = Utf8StrFmt("DISPLAY=%s\n", pszDisplay);
+ const char *pszXAuth = RTEnvGet("XAUTHORITY");
+ if (pszXAuth)
+ str.append(Utf8StrFmt("XAUTHORITY=%s\n", pszXAuth));
+ env = str;
+#endif
+ ComPtr<IProgress> ptrProgress;
+ CHECK_ERROR2(hrc, ptrMachine, LaunchVMProcess(a->session, Bstr(pszSessionType).raw(), env.raw(), ptrProgress.asOutParam()));
+ if (SUCCEEDED(hrc) && !ptrProgress.isNull())
+ {
+ RTMsgInfo("Waiting for VM '%ls' to power on...\n", bstrMachineName.raw());
+ CHECK_ERROR2(hrc, ptrProgress, WaitForCompletion(-1));
+ if (SUCCEEDED(hrc))
+ {
+ BOOL fCompleted = true;
+ CHECK_ERROR2(hrc, ptrProgress, COMGETTER(Completed)(&fCompleted));
+ if (SUCCEEDED(hrc))
+ {
+ ASSERT(fCompleted);
+
+ LONG iRc;
+ CHECK_ERROR2(hrc, ptrProgress, COMGETTER(ResultCode)(&iRc));
+ if (SUCCEEDED(hrc))
+ {
+ if (SUCCEEDED(iRc))
+ RTMsgInfo("VM '%ls' (%ls) has been successfully started.\n", bstrMachineName.raw(), bstrUuid.raw());
+ else
+ {
+ ProgressErrorInfo info(ptrProgress);
+ com::GluePrintErrorInfo(info);
+ }
+ hrc = iRc;
+ }
+ }
+ }
+ }
+
+ /*
+ * Do we wait for the VM to power down?
+ */
+ }
+
+ return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+RTEXITCODE handleUnattended(HandlerArg *a)
+{
+ /*
+ * Sub-command switch.
+ */
+ if (a->argc < 1)
+ return errorNoSubcommand();
+
+ if (!strcmp(a->argv[0], "detect"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_UNATTENDED_DETECT);
+ return handleUnattendedDetect(a);
+ }
+
+ if (!strcmp(a->argv[0], "install"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_UNATTENDED_INSTALL);
+ return handleUnattendedInstall(a);
+ }
+
+ /* Consider some kind of create-vm-and-install-guest-os command. */
+ return errorUnknownSubcommand(a->argv[0]);
+}
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp
new file mode 100644
index 00000000..6ecda59e
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp
@@ -0,0 +1,3202 @@
+/* $Id: VBoxManageModifyVM.cpp $ */
+/** @file
+ * VBoxManage - Implementation of modifyvm command.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 */
+
+#include <iprt/cidr.h>
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/getopt.h>
+#include <VBox/log.h>
+#include "VBoxManage.h"
+
+#ifndef VBOX_ONLY_DOCS
+using namespace com;
+/** @todo refine this after HDD changes; MSC 8.0/64 has trouble with handleModifyVM. */
+#if defined(_MSC_VER)
+# pragma optimize("g", off)
+# if _MSC_VER < RT_MSC_VER_VC120
+# pragma warning(disable:4748)
+# endif
+#endif
+
+enum
+{
+ MODIFYVM_NAME = 1000,
+ MODIFYVM_GROUPS,
+ MODIFYVM_DESCRIPTION,
+ MODIFYVM_OSTYPE,
+ MODIFYVM_ICONFILE,
+ MODIFYVM_MEMORY,
+ MODIFYVM_PAGEFUSION,
+ MODIFYVM_VRAM,
+ MODIFYVM_FIRMWARE,
+ MODIFYVM_ACPI,
+ MODIFYVM_IOAPIC,
+ MODIFYVM_PAE,
+ MODIFYVM_LONGMODE,
+ MODIFYVM_CPUID_PORTABILITY,
+ MODIFYVM_TFRESET,
+ MODIFYVM_APIC,
+ MODIFYVM_X2APIC,
+ MODIFYVM_PARAVIRTPROVIDER,
+ MODIFYVM_PARAVIRTDEBUG,
+ MODIFYVM_HWVIRTEX,
+ MODIFYVM_NESTEDPAGING,
+ MODIFYVM_LARGEPAGES,
+ MODIFYVM_VTXVPID,
+ MODIFYVM_VTXUX,
+ MODIFYVM_IBPB_ON_VM_EXIT,
+ MODIFYVM_IBPB_ON_VM_ENTRY,
+ MODIFYVM_SPEC_CTRL,
+ MODIFYVM_L1D_FLUSH_ON_SCHED,
+ MODIFYVM_L1D_FLUSH_ON_VM_ENTRY,
+ MODIFYVM_NESTED_HW_VIRT,
+ MODIFYVM_CPUS,
+ MODIFYVM_CPUHOTPLUG,
+ MODIFYVM_CPU_PROFILE,
+ MODIFYVM_PLUGCPU,
+ MODIFYVM_UNPLUGCPU,
+ MODIFYVM_SETCPUID,
+ MODIFYVM_SETCPUID_OLD,
+ MODIFYVM_DELCPUID,
+ MODIFYVM_DELCPUID_OLD,
+ MODIFYVM_DELALLCPUID,
+ MODIFYVM_GRAPHICSCONTROLLER,
+ MODIFYVM_MONITORCOUNT,
+ MODIFYVM_ACCELERATE3D,
+#ifdef VBOX_WITH_VIDEOHWACCEL
+ MODIFYVM_ACCELERATE2DVIDEO,
+#endif
+ MODIFYVM_BIOSLOGOFADEIN,
+ MODIFYVM_BIOSLOGOFADEOUT,
+ MODIFYVM_BIOSLOGODISPLAYTIME,
+ MODIFYVM_BIOSLOGOIMAGEPATH,
+ MODIFYVM_BIOSBOOTMENU,
+ MODIFYVM_BIOSAPIC,
+ MODIFYVM_BIOSSYSTEMTIMEOFFSET,
+ MODIFYVM_BIOSPXEDEBUG,
+ MODIFYVM_BOOT,
+ MODIFYVM_HDA, // deprecated
+ MODIFYVM_HDB, // deprecated
+ MODIFYVM_HDD, // deprecated
+ MODIFYVM_IDECONTROLLER, // deprecated
+ MODIFYVM_SATAPORTCOUNT, // deprecated
+ MODIFYVM_SATAPORT, // deprecated
+ MODIFYVM_SATA, // deprecated
+ MODIFYVM_SCSIPORT, // deprecated
+ MODIFYVM_SCSITYPE, // deprecated
+ MODIFYVM_SCSI, // deprecated
+ MODIFYVM_DVDPASSTHROUGH, // deprecated
+ MODIFYVM_DVD, // deprecated
+ MODIFYVM_FLOPPY, // deprecated
+ MODIFYVM_NICTRACEFILE,
+ MODIFYVM_NICTRACE,
+ MODIFYVM_NICPROPERTY,
+ MODIFYVM_NICTYPE,
+ MODIFYVM_NICSPEED,
+ MODIFYVM_NICBOOTPRIO,
+ MODIFYVM_NICPROMISC,
+ MODIFYVM_NICBWGROUP,
+ MODIFYVM_NIC,
+ MODIFYVM_CABLECONNECTED,
+ MODIFYVM_BRIDGEADAPTER,
+ MODIFYVM_HOSTONLYADAPTER,
+ MODIFYVM_INTNET,
+ MODIFYVM_GENERICDRV,
+ MODIFYVM_NATNETWORKNAME,
+ MODIFYVM_NATNET,
+ MODIFYVM_NATBINDIP,
+ MODIFYVM_NATSETTINGS,
+ MODIFYVM_NATPF,
+ MODIFYVM_NATALIASMODE,
+ MODIFYVM_NATTFTPPREFIX,
+ MODIFYVM_NATTFTPFILE,
+ MODIFYVM_NATTFTPSERVER,
+ MODIFYVM_NATDNSPASSDOMAIN,
+ MODIFYVM_NATDNSPROXY,
+ MODIFYVM_NATDNSHOSTRESOLVER,
+ MODIFYVM_MACADDRESS,
+ MODIFYVM_HIDPTR,
+ MODIFYVM_HIDKBD,
+ MODIFYVM_UARTMODE,
+ MODIFYVM_UARTTYPE,
+ MODIFYVM_UART,
+#if defined(RT_OS_LINUX) || defined(RT_OS_WINDOWS)
+ MODIFYVM_LPTMODE,
+ MODIFYVM_LPT,
+#endif
+ MODIFYVM_GUESTMEMORYBALLOON,
+ MODIFYVM_AUDIOCONTROLLER,
+ MODIFYVM_AUDIOCODEC,
+ MODIFYVM_AUDIO,
+ MODIFYVM_AUDIOIN,
+ MODIFYVM_AUDIOOUT,
+ MODIFYVM_CLIPBOARD,
+ MODIFYVM_DRAGANDDROP,
+ MODIFYVM_VRDPPORT, /* VRDE: deprecated */
+ MODIFYVM_VRDPADDRESS, /* VRDE: deprecated */
+ MODIFYVM_VRDPAUTHTYPE, /* VRDE: deprecated */
+ MODIFYVM_VRDPMULTICON, /* VRDE: deprecated */
+ MODIFYVM_VRDPREUSECON, /* VRDE: deprecated */
+ MODIFYVM_VRDPVIDEOCHANNEL, /* VRDE: deprecated */
+ MODIFYVM_VRDPVIDEOCHANNELQUALITY, /* VRDE: deprecated */
+ MODIFYVM_VRDP, /* VRDE: deprecated */
+ MODIFYVM_VRDEPROPERTY,
+ MODIFYVM_VRDEPORT,
+ MODIFYVM_VRDEADDRESS,
+ MODIFYVM_VRDEAUTHTYPE,
+ MODIFYVM_VRDEAUTHLIBRARY,
+ MODIFYVM_VRDEMULTICON,
+ MODIFYVM_VRDEREUSECON,
+ MODIFYVM_VRDEVIDEOCHANNEL,
+ MODIFYVM_VRDEVIDEOCHANNELQUALITY,
+ MODIFYVM_VRDE_EXTPACK,
+ MODIFYVM_VRDE,
+ MODIFYVM_RTCUSEUTC,
+ MODIFYVM_USBRENAME,
+ MODIFYVM_USBXHCI,
+ MODIFYVM_USBEHCI,
+ MODIFYVM_USBOHCI,
+ MODIFYVM_SNAPSHOTFOLDER,
+ MODIFYVM_TELEPORTER_ENABLED,
+ MODIFYVM_TELEPORTER_PORT,
+ MODIFYVM_TELEPORTER_ADDRESS,
+ MODIFYVM_TELEPORTER_PASSWORD,
+ MODIFYVM_TELEPORTER_PASSWORD_FILE,
+ MODIFYVM_TRACING_ENABLED,
+ MODIFYVM_TRACING_CONFIG,
+ MODIFYVM_TRACING_ALLOW_VM_ACCESS,
+ MODIFYVM_HARDWARE_UUID,
+ MODIFYVM_HPET,
+ MODIFYVM_IOCACHE,
+ MODIFYVM_IOCACHESIZE,
+ MODIFYVM_FAULT_TOLERANCE,
+ MODIFYVM_FAULT_TOLERANCE_ADDRESS,
+ MODIFYVM_FAULT_TOLERANCE_PORT,
+ MODIFYVM_FAULT_TOLERANCE_PASSWORD,
+ MODIFYVM_FAULT_TOLERANCE_SYNC_INTERVAL,
+ MODIFYVM_CPU_EXECTUION_CAP,
+ MODIFYVM_AUTOSTART_ENABLED,
+ MODIFYVM_AUTOSTART_DELAY,
+ MODIFYVM_AUTOSTOP_TYPE,
+#ifdef VBOX_WITH_PCI_PASSTHROUGH
+ MODIFYVM_ATTACH_PCI,
+ MODIFYVM_DETACH_PCI,
+#endif
+#ifdef VBOX_WITH_USB_CARDREADER
+ MODIFYVM_USBCARDREADER,
+#endif
+#ifdef VBOX_WITH_RECORDING
+ MODIFYVM_RECORDING,
+ MODIFYVM_RECORDING_FEATURES,
+ MODIFYVM_RECORDING_SCREENS,
+ MODIFYVM_RECORDING_FILENAME,
+ MODIFYVM_RECORDING_VIDEO_WIDTH,
+ MODIFYVM_RECORDING_VIDEO_HEIGHT,
+ MODIFYVM_RECORDING_VIDEO_RES,
+ MODIFYVM_RECORDING_VIDEO_RATE,
+ MODIFYVM_RECORDING_VIDEO_FPS,
+ MODIFYVM_RECORDING_MAXTIME,
+ MODIFYVM_RECORDING_MAXSIZE,
+ MODIFYVM_RECORDING_OPTIONS,
+#endif
+ MODIFYVM_CHIPSET,
+ MODIFYVM_DEFAULTFRONTEND
+};
+
+static const RTGETOPTDEF g_aModifyVMOptions[] =
+{
+/** @todo Convert to dash separated names like --triple-fault-reset! Please
+ * do that for all new options as we don't need more character soups
+ * around VirtualBox - typedefs more than covers that demand! */
+ { "--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 },
+ { "--firmware", MODIFYVM_FIRMWARE, RTGETOPT_REQ_STRING },
+ { "--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 },
+ { "--cpuid-portability-level", MODIFYVM_CPUID_PORTABILITY, RTGETOPT_REQ_UINT32 },
+ { "--triplefaultreset", MODIFYVM_TFRESET, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--apic", MODIFYVM_APIC, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--x2apic", MODIFYVM_X2APIC, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--paravirtprovider", MODIFYVM_PARAVIRTPROVIDER, RTGETOPT_REQ_STRING },
+ { "--paravirtdebug", MODIFYVM_PARAVIRTDEBUG, RTGETOPT_REQ_STRING },
+ { "--hwvirtex", MODIFYVM_HWVIRTEX, 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 },
+ { "--ibpb-on-vm-exit", MODIFYVM_IBPB_ON_VM_EXIT, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--ibpb-on-vm-entry", MODIFYVM_IBPB_ON_VM_ENTRY, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--spec-ctrl", MODIFYVM_SPEC_CTRL, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--l1d-flush-on-sched", MODIFYVM_L1D_FLUSH_ON_SCHED, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--l1d-flush-on-vm-entry", MODIFYVM_L1D_FLUSH_ON_VM_ENTRY, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--nested-hw-virt", MODIFYVM_NESTED_HW_VIRT, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--cpuid-set", MODIFYVM_SETCPUID, RTGETOPT_REQ_UINT32_OPTIONAL_PAIR | RTGETOPT_FLAG_HEX },
+ { "--cpuid-remove", MODIFYVM_DELCPUID, RTGETOPT_REQ_UINT32_OPTIONAL_PAIR | RTGETOPT_FLAG_HEX },
+ { "--cpuidset", MODIFYVM_SETCPUID_OLD, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX },
+ { "--cpuidremove", MODIFYVM_DELCPUID_OLD, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX },
+ { "--cpuidremoveall", MODIFYVM_DELALLCPUID, RTGETOPT_REQ_NOTHING},
+ { "--cpus", MODIFYVM_CPUS, RTGETOPT_REQ_UINT32 },
+ { "--cpuhotplug", MODIFYVM_CPUHOTPLUG, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--cpu-profile", MODIFYVM_CPU_PROFILE, RTGETOPT_REQ_STRING },
+ { "--plugcpu", MODIFYVM_PLUGCPU, RTGETOPT_REQ_UINT32 },
+ { "--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
+ { "--accelerate2dvideo", MODIFYVM_ACCELERATE2DVIDEO, RTGETOPT_REQ_BOOL_ONOFF },
+#endif
+ { "--bioslogofadein", MODIFYVM_BIOSLOGOFADEIN, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--bioslogofadeout", MODIFYVM_BIOSLOGOFADEOUT, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--bioslogodisplaytime", MODIFYVM_BIOSLOGODISPLAYTIME, RTGETOPT_REQ_UINT32 },
+ { "--bioslogoimagepath", MODIFYVM_BIOSLOGOIMAGEPATH, RTGETOPT_REQ_STRING },
+ { "--biosbootmenu", MODIFYVM_BIOSBOOTMENU, RTGETOPT_REQ_STRING },
+ { "--biossystemtimeoffset", MODIFYVM_BIOSSYSTEMTIMEOFFSET, RTGETOPT_REQ_INT64 },
+ { "--biosapic", MODIFYVM_BIOSAPIC, RTGETOPT_REQ_STRING },
+ { "--biospxedebug", MODIFYVM_BIOSPXEDEBUG, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--boot", MODIFYVM_BOOT, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--hda", MODIFYVM_HDA, RTGETOPT_REQ_STRING },
+ { "--hdb", MODIFYVM_HDB, RTGETOPT_REQ_STRING },
+ { "--hdd", MODIFYVM_HDD, RTGETOPT_REQ_STRING },
+ { "--idecontroller", MODIFYVM_IDECONTROLLER, RTGETOPT_REQ_STRING },
+ { "--sataportcount", MODIFYVM_SATAPORTCOUNT, RTGETOPT_REQ_UINT32 },
+ { "--sataport", MODIFYVM_SATAPORT, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--sata", MODIFYVM_SATA, RTGETOPT_REQ_STRING },
+ { "--scsiport", MODIFYVM_SCSIPORT, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--scsitype", MODIFYVM_SCSITYPE, RTGETOPT_REQ_STRING },
+ { "--scsi", MODIFYVM_SCSI, RTGETOPT_REQ_STRING },
+ { "--dvdpassthrough", MODIFYVM_DVDPASSTHROUGH, RTGETOPT_REQ_STRING },
+ { "--dvd", MODIFYVM_DVD, RTGETOPT_REQ_STRING },
+ { "--floppy", MODIFYVM_FLOPPY, RTGETOPT_REQ_STRING },
+ { "--nictracefile", MODIFYVM_NICTRACEFILE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--nictrace", MODIFYVM_NICTRACE, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX },
+ { "--nicproperty", MODIFYVM_NICPROPERTY, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--nictype", MODIFYVM_NICTYPE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--nicspeed", MODIFYVM_NICSPEED, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_INDEX },
+ { "--nicbootprio", MODIFYVM_NICBOOTPRIO, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_INDEX },
+ { "--nicpromisc", MODIFYVM_NICPROMISC, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--nicbandwidthgroup", MODIFYVM_NICBWGROUP, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--nic", MODIFYVM_NIC, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--cableconnected", MODIFYVM_CABLECONNECTED, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX },
+ { "--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 },
+ { "--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 },
+ { "--nataliasmode", MODIFYVM_NATALIASMODE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--nattftpprefix", MODIFYVM_NATTFTPPREFIX, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--nattftpfile", MODIFYVM_NATTFTPFILE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--nattftpserver", MODIFYVM_NATTFTPSERVER, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--natdnspassdomain", MODIFYVM_NATDNSPASSDOMAIN, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX },
+ { "--natdnsproxy", MODIFYVM_NATDNSPROXY, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX },
+ { "--natdnshostresolver", MODIFYVM_NATDNSHOSTRESOLVER, RTGETOPT_REQ_BOOL_ONOFF | RTGETOPT_FLAG_INDEX },
+ { "--macaddress", MODIFYVM_MACADDRESS, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--mouse", MODIFYVM_HIDPTR, RTGETOPT_REQ_STRING },
+ { "--keyboard", MODIFYVM_HIDKBD, RTGETOPT_REQ_STRING },
+ { "--uartmode", MODIFYVM_UARTMODE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--uarttype", MODIFYVM_UARTTYPE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--uart", MODIFYVM_UART, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+#if defined(RT_OS_LINUX) || defined(RT_OS_WINDOWS)
+ { "--lptmode", MODIFYVM_LPTMODE, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+ { "--lpt", MODIFYVM_LPT, RTGETOPT_REQ_STRING | RTGETOPT_FLAG_INDEX },
+#endif
+ { "--guestmemoryballoon", MODIFYVM_GUESTMEMORYBALLOON, RTGETOPT_REQ_UINT32 },
+ { "--audiocontroller", MODIFYVM_AUDIOCONTROLLER, RTGETOPT_REQ_STRING },
+ { "--audiocodec", MODIFYVM_AUDIOCODEC, RTGETOPT_REQ_STRING },
+ { "--audio", MODIFYVM_AUDIO, RTGETOPT_REQ_STRING },
+ { "--audioin", MODIFYVM_AUDIOIN, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--audioout", MODIFYVM_AUDIOOUT, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--clipboard", MODIFYVM_CLIPBOARD, RTGETOPT_REQ_STRING },
+ { "--draganddrop", MODIFYVM_DRAGANDDROP, RTGETOPT_REQ_STRING },
+ { "--vrdpport", MODIFYVM_VRDPPORT, RTGETOPT_REQ_STRING }, /* deprecated */
+ { "--vrdpaddress", MODIFYVM_VRDPADDRESS, RTGETOPT_REQ_STRING }, /* deprecated */
+ { "--vrdpauthtype", MODIFYVM_VRDPAUTHTYPE, RTGETOPT_REQ_STRING }, /* deprecated */
+ { "--vrdpmulticon", MODIFYVM_VRDPMULTICON, RTGETOPT_REQ_BOOL_ONOFF }, /* deprecated */
+ { "--vrdpreusecon", MODIFYVM_VRDPREUSECON, RTGETOPT_REQ_BOOL_ONOFF }, /* deprecated */
+ { "--vrdpvideochannel", MODIFYVM_VRDPVIDEOCHANNEL, RTGETOPT_REQ_BOOL_ONOFF }, /* deprecated */
+ { "--vrdpvideochannelquality", MODIFYVM_VRDPVIDEOCHANNELQUALITY, RTGETOPT_REQ_STRING }, /* deprecated */
+ { "--vrdp", MODIFYVM_VRDP, RTGETOPT_REQ_BOOL_ONOFF }, /* deprecated */
+ { "--vrdeproperty", MODIFYVM_VRDEPROPERTY, RTGETOPT_REQ_STRING },
+ { "--vrdeport", MODIFYVM_VRDEPORT, RTGETOPT_REQ_STRING },
+ { "--vrdeaddress", MODIFYVM_VRDEADDRESS, RTGETOPT_REQ_STRING },
+ { "--vrdeauthtype", MODIFYVM_VRDEAUTHTYPE, RTGETOPT_REQ_STRING },
+ { "--vrdeauthlibrary", MODIFYVM_VRDEAUTHLIBRARY, RTGETOPT_REQ_STRING },
+ { "--vrdemulticon", MODIFYVM_VRDEMULTICON, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--vrdereusecon", MODIFYVM_VRDEREUSECON, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--vrdevideochannel", MODIFYVM_VRDEVIDEOCHANNEL, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--vrdevideochannelquality", MODIFYVM_VRDEVIDEOCHANNELQUALITY, RTGETOPT_REQ_STRING },
+ { "--vrdeextpack", MODIFYVM_VRDE_EXTPACK, RTGETOPT_REQ_STRING },
+ { "--vrde", MODIFYVM_VRDE, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--usbrename", MODIFYVM_USBRENAME, RTGETOPT_REQ_STRING },
+ { "--usbxhci", MODIFYVM_USBXHCI, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--usbehci", MODIFYVM_USBEHCI, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--usbohci", MODIFYVM_USBOHCI, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--usb", MODIFYVM_USBOHCI, RTGETOPT_REQ_BOOL_ONOFF }, /* deprecated */
+ { "--snapshotfolder", MODIFYVM_SNAPSHOTFOLDER, RTGETOPT_REQ_STRING },
+ { "--teleporter", MODIFYVM_TELEPORTER_ENABLED, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--teleporterenabled", MODIFYVM_TELEPORTER_ENABLED, RTGETOPT_REQ_BOOL_ONOFF }, /* deprecated */
+ { "--teleporterport", MODIFYVM_TELEPORTER_PORT, RTGETOPT_REQ_UINT32 },
+ { "--teleporteraddress", MODIFYVM_TELEPORTER_ADDRESS, RTGETOPT_REQ_STRING },
+ { "--teleporterpassword", MODIFYVM_TELEPORTER_PASSWORD, RTGETOPT_REQ_STRING },
+ { "--teleporterpasswordfile", MODIFYVM_TELEPORTER_PASSWORD_FILE, RTGETOPT_REQ_STRING },
+ { "--tracing-enabled", MODIFYVM_TRACING_ENABLED, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--tracing-config", MODIFYVM_TRACING_CONFIG, RTGETOPT_REQ_STRING },
+ { "--tracing-allow-vm-access", MODIFYVM_TRACING_ALLOW_VM_ACCESS, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--hardwareuuid", MODIFYVM_HARDWARE_UUID, RTGETOPT_REQ_STRING },
+ { "--hpet", MODIFYVM_HPET, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--iocache", MODIFYVM_IOCACHE, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--iocachesize", MODIFYVM_IOCACHESIZE, RTGETOPT_REQ_UINT32 },
+ { "--faulttolerance", MODIFYVM_FAULT_TOLERANCE, RTGETOPT_REQ_STRING },
+ { "--faulttoleranceaddress", MODIFYVM_FAULT_TOLERANCE_ADDRESS, RTGETOPT_REQ_STRING },
+ { "--faulttoleranceport", MODIFYVM_FAULT_TOLERANCE_PORT, RTGETOPT_REQ_UINT32 },
+ { "--faulttolerancepassword", MODIFYVM_FAULT_TOLERANCE_PASSWORD, RTGETOPT_REQ_STRING },
+ { "--faulttolerancesyncinterval", MODIFYVM_FAULT_TOLERANCE_SYNC_INTERVAL, RTGETOPT_REQ_UINT32 },
+ { "--chipset", MODIFYVM_CHIPSET, RTGETOPT_REQ_STRING },
+#ifdef VBOX_WITH_RECORDING
+ { "--recording", MODIFYVM_RECORDING, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--recordingscreens", MODIFYVM_RECORDING_SCREENS, RTGETOPT_REQ_STRING },
+ { "--recordingfile", MODIFYVM_RECORDING_FILENAME, RTGETOPT_REQ_STRING },
+ { "--recordingmaxtime", MODIFYVM_RECORDING_MAXTIME, RTGETOPT_REQ_INT32 },
+ { "--recordingmaxsize", MODIFYVM_RECORDING_MAXSIZE, RTGETOPT_REQ_INT32 },
+ { "--recordingopts", MODIFYVM_RECORDING_OPTIONS, RTGETOPT_REQ_STRING },
+ { "--recordingoptions", MODIFYVM_RECORDING_OPTIONS, RTGETOPT_REQ_STRING },
+ { "--recordingvideores", MODIFYVM_RECORDING_VIDEO_RES, RTGETOPT_REQ_STRING },
+ { "--recordingvideoresolution", MODIFYVM_RECORDING_VIDEO_RES, RTGETOPT_REQ_STRING },
+ { "--recordingvideorate", MODIFYVM_RECORDING_VIDEO_RATE, RTGETOPT_REQ_UINT32 },
+ { "--recordingvideofps", MODIFYVM_RECORDING_VIDEO_FPS, RTGETOPT_REQ_UINT32 },
+#endif
+ { "--autostart-enabled", MODIFYVM_AUTOSTART_ENABLED, RTGETOPT_REQ_BOOL_ONOFF },
+ { "--autostart-delay", MODIFYVM_AUTOSTART_DELAY, RTGETOPT_REQ_UINT32 },
+ { "--autostop-type", MODIFYVM_AUTOSTOP_TYPE, RTGETOPT_REQ_STRING },
+#ifdef VBOX_WITH_PCI_PASSTHROUGH
+ { "--pciattach", MODIFYVM_ATTACH_PCI, RTGETOPT_REQ_STRING },
+ { "--pcidetach", MODIFYVM_DETACH_PCI, RTGETOPT_REQ_STRING },
+#endif
+#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)
+{
+ RTStrmPrintf(g_pStdErr, "Warning: '--vrdp%s' is deprecated. Use '--vrde%s'.\n", pszOption, pszOption);
+}
+
+/** Parse PCI address in format 01:02.03 and convert it to the numeric representation. */
+static int32_t parsePci(const char* szPciAddr)
+{
+ char* pszNext = (char*)szPciAddr;
+ int rc;
+ uint8_t aVals[3] = {0, 0, 0};
+
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &aVals[0]);
+ if (RT_FAILURE(rc) || pszNext == NULL || *pszNext != ':')
+ return -1;
+
+ rc = RTStrToUInt8Ex(pszNext+1, &pszNext, 16, &aVals[1]);
+ if (RT_FAILURE(rc) || pszNext == NULL || *pszNext != '.')
+ return -1;
+
+ rc = RTStrToUInt8Ex(pszNext+1, &pszNext, 16, &aVals[2]);
+ if (RT_FAILURE(rc) || pszNext == NULL)
+ return -1;
+
+ return (aVals[0] << 8) | (aVals[1] << 3) | (aVals[2] << 0);
+}
+
+void parseGroups(const char *pcszGroups, com::SafeArray<BSTR> *pGroups)
+{
+ while (pcszGroups)
+ {
+ char *pComma = RTStrStr(pcszGroups, ",");
+ if (pComma)
+ {
+ Bstr(pcszGroups, pComma - pcszGroups).detachTo(pGroups->appendedRaw());
+ pcszGroups = pComma + 1;
+ }
+ else
+ {
+ Bstr(pcszGroups).detachTo(pGroups->appendedRaw());
+ pcszGroups = NULL;
+ }
+ }
+}
+
+#ifdef VBOX_WITH_RECORDING
+static 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;
+}
+#endif
+
+static int parseNum(uint32_t uIndex, unsigned cMaxIndex, const char *pszName)
+{
+ if ( uIndex >= 1
+ && uIndex <= cMaxIndex)
+ return uIndex;
+ errorArgument("Invalid %s number %u", pszName, uIndex);
+ return 0;
+}
+
+RTEXITCODE handleModifyVM(HandlerArg *a)
+{
+ int c;
+ HRESULT rc;
+ Bstr name;
+
+ /* VM ID + at least one parameter. Parameter arguments are checked
+ * individually. */
+ if (a->argc < 2)
+ return errorSyntax(USAGE_MODIFYVM, "Not enough parameters");
+
+ /* try to find the given sessionMachine */
+ ComPtr<IMachine> machine;
+ CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()), RTEXITCODE_FAILURE);
+
+
+ /* Get the number of network adapters */
+ ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, machine);
+
+ /* open a session for the VM */
+ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
+
+ /* get the mutable session sessionMachine */
+ ComPtr<IMachine> sessionMachine;
+ CHECK_ERROR_RET(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()), RTEXITCODE_FAILURE);
+
+ ComPtr<IBIOSSettings> biosSettings;
+ sessionMachine->COMGETTER(BIOSSettings)(biosSettings.asOutParam());
+
+ RTGETOPTSTATE GetOptState;
+ RTGetOptInit(&GetOptState, a->argc, a->argv, g_aModifyVMOptions,
+ RT_ELEMENTS(g_aModifyVMOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+
+ RTGETOPTUNION ValueUnion;
+ while ( SUCCEEDED (rc)
+ && (c = RTGetOpt(&GetOptState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case MODIFYVM_NAME:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(Name)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+ case MODIFYVM_GROUPS:
+ {
+ com::SafeArray<BSTR> groups;
+ parseGroups(ValueUnion.psz, &groups);
+ CHECK_ERROR(sessionMachine, COMSETTER(Groups)(ComSafeArrayAsInParam(groups)));
+ break;
+ }
+ case MODIFYVM_DESCRIPTION:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(Description)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+ case MODIFYVM_OSTYPE:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(OSTypeId)(Bstr(ValueUnion.psz).raw()));
+ 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(sessionMachine, COMSETTER(Icon)(ComSafeArrayAsInParam(icon)));
+ break;
+ }
+
+ case MODIFYVM_MEMORY:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(MemorySize)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_PAGEFUSION:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(PageFusionEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_VRAM:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(VRAMSize)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_FIRMWARE:
+ {
+ if (!RTStrICmp(ValueUnion.psz, "efi"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FirmwareType)(FirmwareType_EFI));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "efi32"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FirmwareType)(FirmwareType_EFI32));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "efi64"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FirmwareType)(FirmwareType_EFI64));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "efidual"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FirmwareType)(FirmwareType_EFIDUAL));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "bios"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FirmwareType)(FirmwareType_BIOS));
+ }
+ else
+ {
+ errorArgument("Invalid --firmware argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_ACPI:
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(ACPIEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_IOAPIC:
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(IOAPICEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_PAE:
+ {
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_PAE, ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_LONGMODE:
+ {
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_LongMode, ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_CPUID_PORTABILITY:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(CPUIDPortabilityLevel)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_TFRESET:
+ {
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_TripleFaultReset, ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_APIC:
+ {
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_APIC, ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_X2APIC:
+ {
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_X2APIC, ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_PARAVIRTPROVIDER:
+ {
+ if ( !RTStrICmp(ValueUnion.psz, "none")
+ || !RTStrICmp(ValueUnion.psz, "disabled"))
+ CHECK_ERROR(sessionMachine, COMSETTER(ParavirtProvider)(ParavirtProvider_None));
+ else if (!RTStrICmp(ValueUnion.psz, "default"))
+ CHECK_ERROR(sessionMachine, COMSETTER(ParavirtProvider)(ParavirtProvider_Default));
+ else if (!RTStrICmp(ValueUnion.psz, "legacy"))
+ CHECK_ERROR(sessionMachine, COMSETTER(ParavirtProvider)(ParavirtProvider_Legacy));
+ else if (!RTStrICmp(ValueUnion.psz, "minimal"))
+ CHECK_ERROR(sessionMachine, COMSETTER(ParavirtProvider)(ParavirtProvider_Minimal));
+ else if (!RTStrICmp(ValueUnion.psz, "hyperv"))
+ CHECK_ERROR(sessionMachine, COMSETTER(ParavirtProvider)(ParavirtProvider_HyperV));
+ else if (!RTStrICmp(ValueUnion.psz, "kvm"))
+ CHECK_ERROR(sessionMachine, COMSETTER(ParavirtProvider)(ParavirtProvider_KVM));
+ else
+ {
+ errorArgument("Invalid --paravirtprovider argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_PARAVIRTDEBUG:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(ParavirtDebug)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_HWVIRTEX:
+ {
+ CHECK_ERROR(sessionMachine, SetHWVirtExProperty(HWVirtExPropertyType_Enabled, ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_SETCPUID:
+ case MODIFYVM_SETCPUID_OLD:
+ {
+ uint32_t const idx = c == MODIFYVM_SETCPUID ? ValueUnion.PairU32.uFirst : ValueUnion.u32;
+ uint32_t const idxSub = c == MODIFYVM_SETCPUID ? ValueUnion.PairU32.uSecond : UINT32_MAX;
+ uint32_t aValue[4];
+ for (unsigned i = 0; i < 4; i++)
+ {
+ int vrc = RTGetOptFetchValue(&GetOptState, &ValueUnion, RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX);
+ if (RT_FAILURE(vrc))
+ return errorSyntax(USAGE_MODIFYVM, "Missing or Invalid argument to '%s'", GetOptState.pDef->pszLong);
+ aValue[i] = ValueUnion.u32;
+ }
+ CHECK_ERROR(sessionMachine, SetCPUIDLeaf(idx, idxSub, aValue[0], aValue[1], aValue[2], aValue[3]));
+ break;
+ }
+
+ case MODIFYVM_DELCPUID:
+ CHECK_ERROR(sessionMachine, RemoveCPUIDLeaf(ValueUnion.PairU32.uFirst, ValueUnion.PairU32.uSecond));
+ break;
+
+ case MODIFYVM_DELCPUID_OLD:
+ CHECK_ERROR(sessionMachine, RemoveCPUIDLeaf(ValueUnion.u32, UINT32_MAX));
+ break;
+
+ case MODIFYVM_DELALLCPUID:
+ {
+ CHECK_ERROR(sessionMachine, RemoveAllCPUIDLeaves());
+ break;
+ }
+
+ case MODIFYVM_NESTEDPAGING:
+ {
+ CHECK_ERROR(sessionMachine, SetHWVirtExProperty(HWVirtExPropertyType_NestedPaging, ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_LARGEPAGES:
+ {
+ CHECK_ERROR(sessionMachine, SetHWVirtExProperty(HWVirtExPropertyType_LargePages, ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_VTXVPID:
+ {
+ CHECK_ERROR(sessionMachine, SetHWVirtExProperty(HWVirtExPropertyType_VPID, ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_VTXUX:
+ {
+ CHECK_ERROR(sessionMachine, SetHWVirtExProperty(HWVirtExPropertyType_UnrestrictedExecution, ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_IBPB_ON_VM_EXIT:
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_IBPBOnVMExit, ValueUnion.f));
+ break;
+
+ case MODIFYVM_IBPB_ON_VM_ENTRY:
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_IBPBOnVMEntry, ValueUnion.f));
+ break;
+
+ case MODIFYVM_SPEC_CTRL:
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_SpecCtrl, ValueUnion.f));
+ break;
+
+ case MODIFYVM_L1D_FLUSH_ON_SCHED:
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_L1DFlushOnEMTScheduling, ValueUnion.f));
+ break;
+
+ case MODIFYVM_L1D_FLUSH_ON_VM_ENTRY:
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_L1DFlushOnVMEntry, ValueUnion.f));
+ break;
+
+ case MODIFYVM_NESTED_HW_VIRT:
+ CHECK_ERROR(sessionMachine, SetCPUProperty(CPUPropertyType_HWVirt, ValueUnion.f));
+ break;
+
+ case MODIFYVM_CPUS:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(CPUCount)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_RTCUSEUTC:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(RTCUseUTC)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_CPUHOTPLUG:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(CPUHotPlugEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_CPU_PROFILE:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(CPUProfile)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_PLUGCPU:
+ {
+ CHECK_ERROR(sessionMachine, HotPlugCPU(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_UNPLUGCPU:
+ {
+ CHECK_ERROR(sessionMachine, HotUnplugCPU(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_CPU_EXECTUION_CAP:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(CPUExecutionCap)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_GRAPHICSCONTROLLER:
+ {
+ if ( !RTStrICmp(ValueUnion.psz, "none")
+ || !RTStrICmp(ValueUnion.psz, "disabled"))
+ CHECK_ERROR(sessionMachine, COMSETTER(GraphicsControllerType)(GraphicsControllerType_Null));
+ else if ( !RTStrICmp(ValueUnion.psz, "vboxvga")
+ || !RTStrICmp(ValueUnion.psz, "vbox")
+ || !RTStrICmp(ValueUnion.psz, "vga")
+ || !RTStrICmp(ValueUnion.psz, "vesa"))
+ CHECK_ERROR(sessionMachine, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VBoxVGA));
+#ifdef VBOX_WITH_VMSVGA
+ else if ( !RTStrICmp(ValueUnion.psz, "vmsvga")
+ || !RTStrICmp(ValueUnion.psz, "vmware"))
+ CHECK_ERROR(sessionMachine, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VMSVGA));
+ else if ( !RTStrICmp(ValueUnion.psz, "vboxsvga")
+ || !RTStrICmp(ValueUnion.psz, "svga"))
+ CHECK_ERROR(sessionMachine, COMSETTER(GraphicsControllerType)(GraphicsControllerType_VBoxSVGA));
+#endif
+ else
+ {
+ errorArgument("Invalid --graphicscontroller argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_MONITORCOUNT:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(MonitorCount)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_ACCELERATE3D:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(Accelerate3DEnabled)(ValueUnion.f));
+ break;
+ }
+
+#ifdef VBOX_WITH_VIDEOHWACCEL
+ case MODIFYVM_ACCELERATE2DVIDEO:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(Accelerate2DVideoEnabled)(ValueUnion.f));
+ break;
+ }
+#endif
+
+ case MODIFYVM_BIOSLOGOFADEIN:
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(LogoFadeIn)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_BIOSLOGOFADEOUT:
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(LogoFadeOut)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_BIOSLOGODISPLAYTIME:
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(LogoDisplayTime)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_BIOSLOGOIMAGEPATH:
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(LogoImagePath)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_BIOSBOOTMENU:
+ {
+ if (!RTStrICmp(ValueUnion.psz, "disabled"))
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(BootMenuMode)(BIOSBootMenuMode_Disabled));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "menuonly"))
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(BootMenuMode)(BIOSBootMenuMode_MenuOnly));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "messageandmenu"))
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(BootMenuMode)(BIOSBootMenuMode_MessageAndMenu));
+ }
+ else
+ {
+ errorArgument("Invalid --biosbootmenu argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_BIOSAPIC:
+ {
+ if (!RTStrICmp(ValueUnion.psz, "disabled"))
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(APICMode)(APICMode_Disabled));
+ }
+ else if ( !RTStrICmp(ValueUnion.psz, "apic")
+ || !RTStrICmp(ValueUnion.psz, "lapic")
+ || !RTStrICmp(ValueUnion.psz, "xapic"))
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(APICMode)(APICMode_APIC));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "x2apic"))
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(APICMode)(APICMode_X2APIC));
+ }
+ else
+ {
+ errorArgument("Invalid --biosapic argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_BIOSSYSTEMTIMEOFFSET:
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(TimeOffset)(ValueUnion.i64));
+ break;
+ }
+
+ case MODIFYVM_BIOSPXEDEBUG:
+ {
+ CHECK_ERROR(biosSettings, COMSETTER(PXEDebugEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_BOOT:
+ {
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ {
+ CHECK_ERROR(sessionMachine, SetBootOrder(GetOptState.uIndex, DeviceType_Null));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "floppy"))
+ {
+ CHECK_ERROR(sessionMachine, SetBootOrder(GetOptState.uIndex, DeviceType_Floppy));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "dvd"))
+ {
+ CHECK_ERROR(sessionMachine, SetBootOrder(GetOptState.uIndex, DeviceType_DVD));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "disk"))
+ {
+ CHECK_ERROR(sessionMachine, SetBootOrder(GetOptState.uIndex, DeviceType_HardDisk));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "net"))
+ {
+ CHECK_ERROR(sessionMachine, SetBootOrder(GetOptState.uIndex, DeviceType_Network));
+ }
+ else
+ return errorArgument("Invalid boot device '%s'", ValueUnion.psz);
+ break;
+ }
+
+ case MODIFYVM_HDA: // deprecated
+ case MODIFYVM_HDB: // deprecated
+ case MODIFYVM_HDD: // deprecated
+ case MODIFYVM_SATAPORT: // deprecated
+ {
+ uint32_t u1 = 0, u2 = 0;
+ Bstr bstrController = L"IDE Controller";
+
+ switch (c)
+ {
+ case MODIFYVM_HDA: // deprecated
+ u1 = 0;
+ break;
+
+ case MODIFYVM_HDB: // deprecated
+ u1 = 0;
+ u2 = 1;
+ break;
+
+ case MODIFYVM_HDD: // deprecated
+ u1 = 1;
+ u2 = 1;
+ break;
+
+ case MODIFYVM_SATAPORT: // deprecated
+ u1 = GetOptState.uIndex;
+ bstrController = L"SATA";
+ break;
+ }
+
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ {
+ sessionMachine->DetachDevice(bstrController.raw(), u1, u2);
+ }
+ else
+ {
+ ComPtr<IMedium> hardDisk;
+ rc = openMedium(a, ValueUnion.psz, DeviceType_HardDisk,
+ AccessMode_ReadWrite, hardDisk,
+ false /* fForceNewUuidOnOpen */,
+ false /* fSilent */);
+ if (FAILED(rc))
+ break;
+ if (hardDisk)
+ {
+ CHECK_ERROR(sessionMachine, AttachDevice(bstrController.raw(),
+ u1, u2,
+ DeviceType_HardDisk,
+ hardDisk));
+ }
+ else
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_IDECONTROLLER: // deprecated
+ {
+ ComPtr<IStorageController> storageController;
+ CHECK_ERROR(sessionMachine, GetStorageControllerByName(Bstr("IDE Controller").raw(),
+ storageController.asOutParam()));
+
+ if (!RTStrICmp(ValueUnion.psz, "PIIX3"))
+ {
+ CHECK_ERROR(storageController, COMSETTER(ControllerType)(StorageControllerType_PIIX3));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "PIIX4"))
+ {
+ CHECK_ERROR(storageController, COMSETTER(ControllerType)(StorageControllerType_PIIX4));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "ICH6"))
+ {
+ CHECK_ERROR(storageController, COMSETTER(ControllerType)(StorageControllerType_ICH6));
+ }
+ else
+ {
+ errorArgument("Invalid --idecontroller argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_SATAPORTCOUNT: // deprecated
+ {
+ ComPtr<IStorageController> SataCtl;
+ CHECK_ERROR(sessionMachine, GetStorageControllerByName(Bstr("SATA").raw(),
+ SataCtl.asOutParam()));
+
+ if (SUCCEEDED(rc) && ValueUnion.u32 > 0)
+ CHECK_ERROR(SataCtl, COMSETTER(PortCount)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_SATA: // deprecated
+ {
+ if (!RTStrICmp(ValueUnion.psz, "on") || !RTStrICmp(ValueUnion.psz, "enable"))
+ {
+ ComPtr<IStorageController> ctl;
+ CHECK_ERROR(sessionMachine, AddStorageController(Bstr("SATA").raw(),
+ StorageBus_SATA,
+ ctl.asOutParam()));
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_IntelAhci));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "off") || !RTStrICmp(ValueUnion.psz, "disable"))
+ CHECK_ERROR(sessionMachine, RemoveStorageController(Bstr("SATA").raw()));
+ else
+ return errorArgument("Invalid --usb argument '%s'", ValueUnion.psz);
+ break;
+ }
+
+ case MODIFYVM_SCSIPORT: // deprecated
+ {
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ {
+ rc = sessionMachine->DetachDevice(Bstr("LsiLogic").raw(),
+ GetOptState.uIndex, 0);
+ if (FAILED(rc))
+ CHECK_ERROR(sessionMachine, DetachDevice(Bstr("BusLogic").raw(),
+ GetOptState.uIndex, 0));
+ }
+ else
+ {
+ ComPtr<IMedium> hardDisk;
+ rc = openMedium(a, ValueUnion.psz, DeviceType_HardDisk,
+ AccessMode_ReadWrite, hardDisk,
+ false /* fForceNewUuidOnOpen */,
+ false /* fSilent */);
+ if (FAILED(rc))
+ break;
+ if (hardDisk)
+ {
+ rc = sessionMachine->AttachDevice(Bstr("LsiLogic").raw(),
+ GetOptState.uIndex, 0,
+ DeviceType_HardDisk,
+ hardDisk);
+ if (FAILED(rc))
+ CHECK_ERROR(sessionMachine,
+ AttachDevice(Bstr("BusLogic").raw(),
+ GetOptState.uIndex, 0,
+ DeviceType_HardDisk,
+ hardDisk));
+ }
+ else
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_SCSITYPE: // deprecated
+ {
+ ComPtr<IStorageController> ctl;
+
+ if (!RTStrICmp(ValueUnion.psz, "LsiLogic"))
+ {
+ rc = sessionMachine->RemoveStorageController(Bstr("BusLogic").raw());
+ if (FAILED(rc))
+ CHECK_ERROR(sessionMachine, RemoveStorageController(Bstr("LsiLogic").raw()));
+
+ CHECK_ERROR(sessionMachine,
+ AddStorageController(Bstr("LsiLogic").raw(),
+ StorageBus_SCSI,
+ ctl.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_LsiLogic));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "BusLogic"))
+ {
+ rc = sessionMachine->RemoveStorageController(Bstr("LsiLogic").raw());
+ if (FAILED(rc))
+ CHECK_ERROR(sessionMachine, RemoveStorageController(Bstr("BusLogic").raw()));
+
+ CHECK_ERROR(sessionMachine,
+ AddStorageController(Bstr("BusLogic").raw(),
+ StorageBus_SCSI,
+ ctl.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_BusLogic));
+ }
+ else
+ return errorArgument("Invalid --scsitype argument '%s'", ValueUnion.psz);
+ break;
+ }
+
+ case MODIFYVM_SCSI: // deprecated
+ {
+ if (!RTStrICmp(ValueUnion.psz, "on") || !RTStrICmp(ValueUnion.psz, "enable"))
+ {
+ ComPtr<IStorageController> ctl;
+
+ CHECK_ERROR(sessionMachine, AddStorageController(Bstr("BusLogic").raw(),
+ StorageBus_SCSI,
+ ctl.asOutParam()));
+ if (SUCCEEDED(rc))
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_BusLogic));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "off") || !RTStrICmp(ValueUnion.psz, "disable"))
+ {
+ rc = sessionMachine->RemoveStorageController(Bstr("BusLogic").raw());
+ if (FAILED(rc))
+ CHECK_ERROR(sessionMachine, RemoveStorageController(Bstr("LsiLogic").raw()));
+ }
+ break;
+ }
+
+ case MODIFYVM_DVDPASSTHROUGH: // deprecated
+ {
+ CHECK_ERROR(sessionMachine, PassthroughDevice(Bstr("IDE Controller").raw(),
+ 1, 0,
+ !RTStrICmp(ValueUnion.psz, "on")));
+ break;
+ }
+
+ case MODIFYVM_DVD: // deprecated
+ {
+ ComPtr<IMedium> dvdMedium;
+
+ /* unmount? */
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ {
+ /* nothing to do, NULL object will cause unmount */
+ }
+ /* host drive? */
+ else if (!RTStrNICmp(ValueUnion.psz, RT_STR_TUPLE("host:")))
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
+ rc = host->FindHostDVDDrive(Bstr(ValueUnion.psz + 5).raw(),
+ dvdMedium.asOutParam());
+ if (!dvdMedium)
+ {
+ /* 2nd try: try with the real name, important on Linux+libhal */
+ char szPathReal[RTPATH_MAX];
+ if (RT_FAILURE(RTPathReal(ValueUnion.psz + 5, szPathReal, sizeof(szPathReal))))
+ {
+ errorArgument("Invalid host DVD drive name \"%s\"", ValueUnion.psz + 5);
+ rc = E_FAIL;
+ break;
+ }
+ rc = host->FindHostDVDDrive(Bstr(szPathReal).raw(),
+ dvdMedium.asOutParam());
+ if (!dvdMedium)
+ {
+ errorArgument("Invalid host DVD drive name \"%s\"", ValueUnion.psz + 5);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ }
+ else
+ {
+ rc = openMedium(a, ValueUnion.psz, DeviceType_DVD,
+ AccessMode_ReadOnly, dvdMedium,
+ false /* fForceNewUuidOnOpen */,
+ false /* fSilent */);
+ if (FAILED(rc))
+ break;
+ if (!dvdMedium)
+ {
+ rc = E_FAIL;
+ break;
+ }
+ }
+
+ CHECK_ERROR(sessionMachine, MountMedium(Bstr("IDE Controller").raw(),
+ 1, 0,
+ dvdMedium,
+ FALSE /* aForce */));
+ break;
+ }
+
+ case MODIFYVM_FLOPPY: // deprecated
+ {
+ ComPtr<IMedium> floppyMedium;
+ ComPtr<IMediumAttachment> floppyAttachment;
+ sessionMachine->GetMediumAttachment(Bstr("Floppy Controller").raw(),
+ 0, 0, floppyAttachment.asOutParam());
+
+ /* disable? */
+ if (!RTStrICmp(ValueUnion.psz, "disabled"))
+ {
+ /* disable the controller */
+ if (floppyAttachment)
+ CHECK_ERROR(sessionMachine, DetachDevice(Bstr("Floppy Controller").raw(),
+ 0, 0));
+ }
+ else
+ {
+ /* enable the controller */
+ if (!floppyAttachment)
+ CHECK_ERROR(sessionMachine, AttachDeviceWithoutMedium(Bstr("Floppy Controller").raw(),
+ 0, 0,
+ DeviceType_Floppy));
+
+ /* unmount? */
+ if ( !RTStrICmp(ValueUnion.psz, "none")
+ || !RTStrICmp(ValueUnion.psz, "empty")) // deprecated
+ {
+ /* nothing to do, NULL object will cause unmount */
+ }
+ /* host drive? */
+ else if (!RTStrNICmp(ValueUnion.psz, RT_STR_TUPLE("host:")))
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
+ rc = host->FindHostFloppyDrive(Bstr(ValueUnion.psz + 5).raw(),
+ floppyMedium.asOutParam());
+ if (!floppyMedium)
+ {
+ errorArgument("Invalid host floppy drive name \"%s\"", ValueUnion.psz + 5);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ else
+ {
+ rc = openMedium(a, ValueUnion.psz, DeviceType_Floppy,
+ AccessMode_ReadWrite, floppyMedium,
+ false /* fForceNewUuidOnOpen */,
+ false /* fSilent */);
+ if (FAILED(rc))
+ break;
+ if (!floppyMedium)
+ {
+ rc = E_FAIL;
+ break;
+ }
+ }
+ CHECK_ERROR(sessionMachine, MountMedium(Bstr("Floppy Controller").raw(),
+ 0, 0,
+ floppyMedium,
+ FALSE /* aForce */));
+ }
+ break;
+ }
+
+ case MODIFYVM_NICTRACEFILE:
+ {
+
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ CHECK_ERROR(nic, COMSETTER(TraceFile)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_NICTRACE:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ CHECK_ERROR(nic, COMSETTER(TraceEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_NICPROPERTY:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ if (nic)
+ {
+ /* 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];
+ CHECK_ERROR(nic, SetProperty(bstrName.raw(), bstrValue.raw()));
+ }
+ else
+ {
+ errorArgument("Invalid --nicproperty%d argument '%s'", GetOptState.uIndex, ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ RTStrFree(pszProperty);
+ }
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for --nicproperty%d '%s'\n", GetOptState.uIndex, ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ }
+ break;
+ }
+ case MODIFYVM_NICTYPE:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ if (!RTStrICmp(ValueUnion.psz, "Am79C970A"))
+ {
+ CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_Am79C970A));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "Am79C973"))
+ {
+ CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_Am79C973));
+ }
+#ifdef VBOX_WITH_E1000
+ else if (!RTStrICmp(ValueUnion.psz, "82540EM"))
+ {
+ CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_I82540EM));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "82543GC"))
+ {
+ CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_I82543GC));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "82545EM"))
+ {
+ CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_I82545EM));
+ }
+#endif
+#ifdef VBOX_WITH_VIRTIO
+ else if (!RTStrICmp(ValueUnion.psz, "virtio"))
+ {
+ CHECK_ERROR(nic, COMSETTER(AdapterType)(NetworkAdapterType_Virtio));
+ }
+#endif /* VBOX_WITH_VIRTIO */
+ else
+ {
+ errorArgument("Invalid NIC type '%s' specified for NIC %u", ValueUnion.psz, GetOptState.uIndex);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_NICSPEED:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ CHECK_ERROR(nic, COMSETTER(LineSpeed)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_NICBOOTPRIO:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ /* Somewhat arbitrary limitation - we can pass a list of up to 4 PCI devices
+ * to the PXE ROM, hence only boot priorities 1-4 are allowed (in addition to
+ * 0 for the default lowest priority).
+ */
+ if (ValueUnion.u32 > 4)
+ {
+ errorArgument("Invalid boot priority '%u' specfied for NIC %u", ValueUnion.u32, GetOptState.uIndex);
+ rc = E_FAIL;
+ }
+ else
+ {
+ CHECK_ERROR(nic, COMSETTER(BootPriority)(ValueUnion.u32));
+ }
+ break;
+ }
+
+ case MODIFYVM_NICPROMISC:
+ {
+ NetworkAdapterPromiscModePolicy_T enmPromiscModePolicy;
+ if (!RTStrICmp(ValueUnion.psz, "deny"))
+ enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny;
+ else if ( !RTStrICmp(ValueUnion.psz, "allow-vms")
+ || !RTStrICmp(ValueUnion.psz, "allow-network"))
+ enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork;
+ else if (!RTStrICmp(ValueUnion.psz, "allow-all"))
+ enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll;
+ else
+ {
+ errorArgument("Unknown promiscuous mode policy '%s'", ValueUnion.psz);
+ rc = E_INVALIDARG;
+ break;
+ }
+
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ CHECK_ERROR(nic, COMSETTER(PromiscModePolicy)(enmPromiscModePolicy));
+ break;
+ }
+
+ case MODIFYVM_NICBWGROUP:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ {
+ /* Just remove the bandwidth group. */
+ CHECK_ERROR(nic, COMSETTER(BandwidthGroup)(NULL));
+ }
+ else
+ {
+ ComPtr<IBandwidthControl> bwCtrl;
+ ComPtr<IBandwidthGroup> bwGroup;
+
+ CHECK_ERROR(sessionMachine, COMGETTER(BandwidthControl)(bwCtrl.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR(bwCtrl, GetBandwidthGroup(Bstr(ValueUnion.psz).raw(), bwGroup.asOutParam()));
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR(nic, COMSETTER(BandwidthGroup)(bwGroup));
+ }
+ }
+ }
+ break;
+ }
+
+ case MODIFYVM_NIC:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ {
+ CHECK_ERROR(nic, COMSETTER(Enabled)(FALSE));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "null"))
+ {
+ CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE));
+ CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Null));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "nat"))
+ {
+ CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE));
+ CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_NAT));
+ }
+ 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 (!RTStrICmp(ValueUnion.psz, "intnet"))
+ {
+ CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE));
+ CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_Internal));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "hostonly"))
+ {
+
+ CHECK_ERROR(nic, COMSETTER(Enabled)(TRUE));
+ CHECK_ERROR(nic, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly));
+ }
+ 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);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_CABLECONNECTED:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ CHECK_ERROR(nic, COMSETTER(CableConnected)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_BRIDGEADAPTER:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ /* remove it? */
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ {
+ CHECK_ERROR(nic, COMSETTER(BridgedInterface)(Bstr().raw()));
+ }
+ else
+ {
+ CHECK_ERROR(nic, COMSETTER(BridgedInterface)(Bstr(ValueUnion.psz).raw()));
+ }
+ break;
+ }
+
+ case MODIFYVM_HOSTONLYADAPTER:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ /* remove it? */
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ {
+ CHECK_ERROR(nic, COMSETTER(HostOnlyInterface)(Bstr().raw()));
+ }
+ else
+ {
+ CHECK_ERROR(nic, COMSETTER(HostOnlyInterface)(Bstr(ValueUnion.psz).raw()));
+ }
+ break;
+ }
+
+ case MODIFYVM_INTNET:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ /* remove it? */
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ {
+ CHECK_ERROR(nic, COMSETTER(InternalNetwork)(Bstr().raw()));
+ }
+ else
+ {
+ CHECK_ERROR(nic, COMSETTER(InternalNetwork)(Bstr(ValueUnion.psz).raw()));
+ }
+ break;
+ }
+
+ case MODIFYVM_GENERICDRV:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ CHECK_ERROR(nic, COMSETTER(GenericDriver)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_NATNETWORKNAME:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ CHECK_ERROR(nic, COMSETTER(NATNetwork)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_NATNET:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ ComPtr<INATEngine> engine;
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+
+ const char *psz = ValueUnion.psz;
+ if (!RTStrICmp("default", psz))
+ psz = "";
+
+ CHECK_ERROR(engine, COMSETTER(Network)(Bstr(psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_NATBINDIP:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ ComPtr<INATEngine> engine;
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+
+ CHECK_ERROR(engine, COMSETTER(HostIP)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+#define ITERATE_TO_NEXT_TERM(ch) \
+ do { \
+ while (*ch != ',') \
+ { \
+ if (*ch == 0) \
+ { \
+ return errorSyntax(USAGE_MODIFYVM, \
+ "Missing or Invalid argument to '%s'", \
+ GetOptState.pDef->pszLong); \
+ } \
+ ch++; \
+ } \
+ *ch = '\0'; \
+ ch++; \
+ } while(0)
+
+ case MODIFYVM_NATSETTINGS:
+ {
+ ComPtr<INetworkAdapter> nic;
+ ComPtr<INATEngine> engine;
+ char *strMtu;
+ char *strSockSnd;
+ char *strSockRcv;
+ char *strTcpSnd;
+ char *strTcpRcv;
+ char *strRaw = RTStrDup(ValueUnion.psz);
+ char *ch = strRaw;
+ strMtu = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strSockSnd = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strSockRcv = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strTcpSnd = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strTcpRcv = RTStrStrip(ch);
+
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+ CHECK_ERROR(engine, SetNetworkSettings(RTStrToUInt32(strMtu), RTStrToUInt32(strSockSnd), RTStrToUInt32(strSockRcv),
+ RTStrToUInt32(strTcpSnd), RTStrToUInt32(strTcpRcv)));
+ break;
+ }
+
+
+ case MODIFYVM_NATPF:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ ComPtr<INATEngine> engine;
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+
+ /* format name:proto:hostip:hostport:guestip:guestport*/
+ if (RTStrCmp(ValueUnion.psz, "delete") != 0)
+ {
+ char *strName;
+ char *strProto;
+ char *strHostIp;
+ char *strHostPort;
+ char *strGuestIp;
+ char *strGuestPort;
+ char *strRaw = RTStrDup(ValueUnion.psz);
+ char *ch = strRaw;
+ strName = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strProto = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strHostIp = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strHostPort = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strGuestIp = RTStrStrip(ch);
+ ITERATE_TO_NEXT_TERM(ch);
+ strGuestPort = RTStrStrip(ch);
+ NATProtocol_T proto;
+ if (RTStrICmp(strProto, "udp") == 0)
+ proto = NATProtocol_UDP;
+ else if (RTStrICmp(strProto, "tcp") == 0)
+ proto = NATProtocol_TCP;
+ else
+ {
+ errorArgument("Invalid proto '%s' specfied for NIC %u", ValueUnion.psz, GetOptState.uIndex);
+ rc = E_FAIL;
+ break;
+ }
+ CHECK_ERROR(engine, AddRedirect(Bstr(strName).raw(), proto,
+ Bstr(strHostIp).raw(),
+ RTStrToUInt16(strHostPort),
+ Bstr(strGuestIp).raw(),
+ RTStrToUInt16(strGuestPort)));
+ }
+ else
+ {
+ /* delete NAT Rule operation */
+ int vrc;
+ vrc = RTGetOptFetchValue(&GetOptState, &ValueUnion, RTGETOPT_REQ_STRING);
+ if (RT_FAILURE(vrc))
+ return errorSyntax(USAGE_MODIFYVM, "Not enough parameters");
+ CHECK_ERROR(engine, RemoveRedirect(Bstr(ValueUnion.psz).raw()));
+ }
+ break;
+ }
+ #undef ITERATE_TO_NEXT_TERM
+ case MODIFYVM_NATALIASMODE:
+ {
+ ComPtr<INetworkAdapter> nic;
+ ComPtr<INATEngine> engine;
+ uint32_t aliasMode = 0;
+
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+ if (RTStrCmp(ValueUnion.psz, "default") == 0)
+ aliasMode = 0;
+ else
+ {
+ char *token = (char *)ValueUnion.psz;
+ while (token)
+ {
+ if (RTStrNCmp(token, RT_STR_TUPLE("log")) == 0)
+ aliasMode |= NATAliasMode_AliasLog;
+ else if (RTStrNCmp(token, RT_STR_TUPLE("proxyonly")) == 0)
+ aliasMode |= NATAliasMode_AliasProxyOnly;
+ else if (RTStrNCmp(token, RT_STR_TUPLE("sameports")) == 0)
+ aliasMode |= NATAliasMode_AliasUseSamePorts;
+ token = RTStrStr(token, ",");
+ if (token == NULL)
+ break;
+ token++;
+ }
+ }
+ CHECK_ERROR(engine, COMSETTER(AliasMode)(aliasMode));
+ break;
+ }
+
+ case MODIFYVM_NATTFTPPREFIX:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ ComPtr<INATEngine> engine;
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+
+ CHECK_ERROR(engine, COMSETTER(TFTPPrefix)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_NATTFTPFILE:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ ComPtr<INATEngine> engine;
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+
+ CHECK_ERROR(engine, COMSETTER(TFTPBootFile)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_NATTFTPSERVER:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ ComPtr<INATEngine> engine;
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+
+ CHECK_ERROR(engine, COMSETTER(TFTPNextServer)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+ case MODIFYVM_NATDNSPASSDOMAIN:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ ComPtr<INATEngine> engine;
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+
+ CHECK_ERROR(engine, COMSETTER(DNSPassDomain)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_NATDNSPROXY:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ ComPtr<INATEngine> engine;
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+
+ CHECK_ERROR(engine, COMSETTER(DNSProxy)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_NATDNSHOSTRESOLVER:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ ComPtr<INATEngine> engine;
+ CHECK_ERROR(nic, COMGETTER(NATEngine)(engine.asOutParam()));
+
+ CHECK_ERROR(engine, COMSETTER(DNSUseHostResolver)(ValueUnion.f));
+ break;
+ }
+ case MODIFYVM_MACADDRESS:
+ {
+ if (!parseNum(GetOptState.uIndex, NetworkAdapterCount, "NIC"))
+ break;
+
+ ComPtr<INetworkAdapter> nic;
+ CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(GetOptState.uIndex - 1, nic.asOutParam()));
+ ASSERT(nic);
+
+ /* generate one? */
+ if (!RTStrICmp(ValueUnion.psz, "auto"))
+ {
+ CHECK_ERROR(nic, COMSETTER(MACAddress)(Bstr().raw()));
+ }
+ else
+ {
+ CHECK_ERROR(nic, COMSETTER(MACAddress)(Bstr(ValueUnion.psz).raw()));
+ }
+ break;
+ }
+
+ case MODIFYVM_HIDPTR:
+ {
+ bool fEnableUsb = false;
+ if (!RTStrICmp(ValueUnion.psz, "ps2"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(PointingHIDType)(PointingHIDType_PS2Mouse));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "usb"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(PointingHIDType)(PointingHIDType_USBMouse));
+ if (SUCCEEDED(rc))
+ fEnableUsb = true;
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "usbtablet"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(PointingHIDType)(PointingHIDType_USBTablet));
+ if (SUCCEEDED(rc))
+ fEnableUsb = true;
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "usbmultitouch"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(PointingHIDType)(PointingHIDType_USBMultiTouch));
+ if (SUCCEEDED(rc))
+ fEnableUsb = true;
+ }
+ else
+ {
+ errorArgument("Invalid type '%s' specfied for pointing device", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ if (fEnableUsb)
+ {
+ /* Make sure either the OHCI or xHCI controller is enabled. */
+ ULONG cOhciCtrls = 0;
+ ULONG cXhciCtrls = 0;
+ rc = sessionMachine->GetUSBControllerCountByType(USBControllerType_OHCI, &cOhciCtrls);
+ if (SUCCEEDED(rc)) {
+ rc = sessionMachine->GetUSBControllerCountByType(USBControllerType_XHCI, &cXhciCtrls);
+ if ( SUCCEEDED(rc)
+ && cOhciCtrls + cXhciCtrls == 0)
+ {
+ /* If there's nothing, enable OHCI (always available). */
+ ComPtr<IUSBController> UsbCtl;
+ CHECK_ERROR(sessionMachine, AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI,
+ UsbCtl.asOutParam()));
+ }
+ }
+ }
+ break;
+ }
+
+ case MODIFYVM_HIDKBD:
+ {
+ bool fEnableUsb = false;
+ if (!RTStrICmp(ValueUnion.psz, "ps2"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(KeyboardHIDType)(KeyboardHIDType_PS2Keyboard));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "usb"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(KeyboardHIDType)(KeyboardHIDType_USBKeyboard));
+ if (SUCCEEDED(rc))
+ fEnableUsb = true;
+ }
+ else
+ {
+ errorArgument("Invalid type '%s' specfied for keyboard", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ if (fEnableUsb)
+ {
+ /* Make sure either the OHCI or xHCI controller is enabled. */
+ ULONG cOhciCtrls = 0;
+ ULONG cXhciCtrls = 0;
+ rc = sessionMachine->GetUSBControllerCountByType(USBControllerType_OHCI, &cOhciCtrls);
+ if (SUCCEEDED(rc)) {
+ rc = sessionMachine->GetUSBControllerCountByType(USBControllerType_XHCI, &cXhciCtrls);
+ if ( SUCCEEDED(rc)
+ && cOhciCtrls + cXhciCtrls == 0)
+ {
+ /* If there's nothing, enable OHCI (always available). */
+ ComPtr<IUSBController> UsbCtl;
+ CHECK_ERROR(sessionMachine, AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI,
+ UsbCtl.asOutParam()));
+ }
+ }
+ }
+ break;
+ }
+
+ case MODIFYVM_UARTMODE:
+ {
+ ComPtr<ISerialPort> uart;
+
+ CHECK_ERROR_BREAK(sessionMachine, GetSerialPort(GetOptState.uIndex - 1, uart.asOutParam()));
+ ASSERT(uart);
+
+ if (!RTStrICmp(ValueUnion.psz, "disconnected"))
+ {
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_Disconnected));
+ }
+ else if ( !RTStrICmp(ValueUnion.psz, "server")
+ || !RTStrICmp(ValueUnion.psz, "client")
+ || !RTStrICmp(ValueUnion.psz, "tcpserver")
+ || !RTStrICmp(ValueUnion.psz, "tcpclient")
+ || !RTStrICmp(ValueUnion.psz, "file"))
+ {
+ const char *pszMode = ValueUnion.psz;
+
+ int vrc = RTGetOptFetchValue(&GetOptState, &ValueUnion, RTGETOPT_REQ_STRING);
+ if (RT_FAILURE(vrc))
+ return errorSyntax(USAGE_MODIFYVM,
+ "Missing or Invalid argument to '%s'",
+ GetOptState.pDef->pszLong);
+
+ CHECK_ERROR(uart, COMSETTER(Path)(Bstr(ValueUnion.psz).raw()));
+
+ if (!RTStrICmp(pszMode, "server"))
+ {
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
+ CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
+ }
+ else if (!RTStrICmp(pszMode, "client"))
+ {
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostPipe));
+ CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
+ }
+ else if (!RTStrICmp(pszMode, "tcpserver"))
+ {
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
+ CHECK_ERROR(uart, COMSETTER(Server)(TRUE));
+ }
+ else if (!RTStrICmp(pszMode, "tcpclient"))
+ {
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_TCP));
+ CHECK_ERROR(uart, COMSETTER(Server)(FALSE));
+ }
+ else if (!RTStrICmp(pszMode, "file"))
+ {
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_RawFile));
+ }
+ }
+ else
+ {
+ CHECK_ERROR(uart, COMSETTER(Path)(Bstr(ValueUnion.psz).raw()));
+ CHECK_ERROR(uart, COMSETTER(HostMode)(PortMode_HostDevice));
+ }
+ break;
+ }
+
+ case MODIFYVM_UARTTYPE:
+ {
+ ComPtr<ISerialPort> uart;
+
+ CHECK_ERROR_BREAK(sessionMachine, GetSerialPort(GetOptState.uIndex - 1, uart.asOutParam()));
+ ASSERT(uart);
+
+ if (!RTStrICmp(ValueUnion.psz, "16450"))
+ {
+ CHECK_ERROR(uart, COMSETTER(UartType)(UartType_U16450));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "16550A"))
+ {
+ CHECK_ERROR(uart, COMSETTER(UartType)(UartType_U16550A));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "16750"))
+ {
+ CHECK_ERROR(uart, COMSETTER(UartType)(UartType_U16750));
+ }
+ else
+ return errorSyntax(USAGE_MODIFYVM,
+ "Invalid argument to '%s'",
+ GetOptState.pDef->pszLong);
+ break;
+ }
+
+ case MODIFYVM_UART:
+ {
+ ComPtr<ISerialPort> uart;
+
+ CHECK_ERROR_BREAK(sessionMachine, GetSerialPort(GetOptState.uIndex - 1, uart.asOutParam()));
+ ASSERT(uart);
+
+ if (!RTStrICmp(ValueUnion.psz, "off") || !RTStrICmp(ValueUnion.psz, "disable"))
+ CHECK_ERROR(uart, COMSETTER(Enabled)(FALSE));
+ else
+ {
+ const char *pszIOBase = ValueUnion.psz;
+ uint32_t uVal = 0;
+
+ int vrc = RTGetOptFetchValue(&GetOptState, &ValueUnion, RTGETOPT_REQ_UINT32) != MODIFYVM_UART;
+ if (RT_FAILURE(vrc))
+ return errorSyntax(USAGE_MODIFYVM,
+ "Missing or Invalid argument to '%s'",
+ GetOptState.pDef->pszLong);
+
+ CHECK_ERROR(uart, COMSETTER(IRQ)(ValueUnion.u32));
+
+ vrc = RTStrToUInt32Ex(pszIOBase, NULL, 0, &uVal);
+ if (vrc != VINF_SUCCESS || uVal == 0)
+ return errorArgument("Error parsing UART I/O base '%s'", pszIOBase);
+ CHECK_ERROR(uart, COMSETTER(IOBase)(uVal));
+
+ CHECK_ERROR(uart, COMSETTER(Enabled)(TRUE));
+ }
+ break;
+ }
+
+#if defined(RT_OS_LINUX) || defined(RT_OS_WINDOWS)
+ case MODIFYVM_LPTMODE:
+ {
+ ComPtr<IParallelPort> lpt;
+
+ CHECK_ERROR_BREAK(sessionMachine, GetParallelPort(GetOptState.uIndex - 1, lpt.asOutParam()));
+ ASSERT(lpt);
+
+ CHECK_ERROR(lpt, COMSETTER(Path)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_LPT:
+ {
+ ComPtr<IParallelPort> lpt;
+
+ CHECK_ERROR_BREAK(sessionMachine, GetParallelPort(GetOptState.uIndex - 1, lpt.asOutParam()));
+ ASSERT(lpt);
+
+ if (!RTStrICmp(ValueUnion.psz, "off") || !RTStrICmp(ValueUnion.psz, "disable"))
+ CHECK_ERROR(lpt, COMSETTER(Enabled)(FALSE));
+ else
+ {
+ const char *pszIOBase = ValueUnion.psz;
+ uint32_t uVal = 0;
+
+ int vrc = RTGetOptFetchValue(&GetOptState, &ValueUnion, RTGETOPT_REQ_UINT32) != MODIFYVM_LPT;
+ if (RT_FAILURE(vrc))
+ return errorSyntax(USAGE_MODIFYVM,
+ "Missing or Invalid argument to '%s'",
+ GetOptState.pDef->pszLong);
+
+ CHECK_ERROR(lpt, COMSETTER(IRQ)(ValueUnion.u32));
+
+ vrc = RTStrToUInt32Ex(pszIOBase, NULL, 0, &uVal);
+ if (vrc != VINF_SUCCESS || uVal == 0)
+ return errorArgument("Error parsing LPT I/O base '%s'", pszIOBase);
+ CHECK_ERROR(lpt, COMSETTER(IOBase)(uVal));
+
+ CHECK_ERROR(lpt, COMSETTER(Enabled)(TRUE));
+ }
+ break;
+ }
+#endif
+
+ case MODIFYVM_GUESTMEMORYBALLOON:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(MemoryBalloonSize)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_AUDIOCONTROLLER:
+ {
+ ComPtr<IAudioAdapter> audioAdapter;
+ sessionMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
+ ASSERT(audioAdapter);
+
+ if (!RTStrICmp(ValueUnion.psz, "sb16"))
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioController)(AudioControllerType_SB16));
+ else if (!RTStrICmp(ValueUnion.psz, "ac97"))
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioController)(AudioControllerType_AC97));
+ else if (!RTStrICmp(ValueUnion.psz, "hda"))
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioController)(AudioControllerType_HDA));
+ else
+ {
+ errorArgument("Invalid --audiocontroller argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_AUDIOCODEC:
+ {
+ ComPtr<IAudioAdapter> audioAdapter;
+ sessionMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
+ ASSERT(audioAdapter);
+
+ if (!RTStrICmp(ValueUnion.psz, "sb16"))
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioCodec)(AudioCodecType_SB16));
+ else if (!RTStrICmp(ValueUnion.psz, "stac9700"))
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioCodec)(AudioCodecType_STAC9700));
+ else if (!RTStrICmp(ValueUnion.psz, "ad1980"))
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioCodec)(AudioCodecType_AD1980));
+ else if (!RTStrICmp(ValueUnion.psz, "stac9221"))
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioCodec)(AudioCodecType_STAC9221));
+ else
+ {
+ errorArgument("Invalid --audiocodec argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_AUDIO:
+ {
+ ComPtr<IAudioAdapter> audioAdapter;
+ sessionMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
+ ASSERT(audioAdapter);
+
+ /* disable? */
+ if (!RTStrICmp(ValueUnion.psz, "none"))
+ {
+ CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(false));
+ }
+ 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 (!RTStrICmp(ValueUnion.psz, "winmm"))
+ {
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_WinMM));
+ CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true));
+ }
+#endif
+ else if (!RTStrICmp(ValueUnion.psz, "dsound"))
+ {
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_DirectSound));
+ CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true));
+ }
+#endif /* RT_OS_WINDOWS */
+#ifdef VBOX_WITH_AUDIO_OSS
+ else if (!RTStrICmp(ValueUnion.psz, "oss"))
+ {
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_OSS));
+ CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true));
+ }
+#endif
+#ifdef VBOX_WITH_AUDIO_ALSA
+ else if (!RTStrICmp(ValueUnion.psz, "alsa"))
+ {
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_ALSA));
+ CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true));
+ }
+#endif
+#ifdef VBOX_WITH_AUDIO_PULSE
+ else if (!RTStrICmp(ValueUnion.psz, "pulse"))
+ {
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_Pulse));
+ CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true));
+ }
+#endif
+#ifdef RT_OS_DARWIN
+ else if (!RTStrICmp(ValueUnion.psz, "coreaudio"))
+ {
+ CHECK_ERROR(audioAdapter, COMSETTER(AudioDriver)(AudioDriverType_CoreAudio));
+ CHECK_ERROR(audioAdapter, COMSETTER(Enabled)(true));
+ }
+#endif /* !RT_OS_DARWIN */
+ else
+ {
+ errorArgument("Invalid --audio argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_AUDIOIN:
+ {
+ ComPtr<IAudioAdapter> audioAdapter;
+ sessionMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
+ ASSERT(audioAdapter);
+
+ CHECK_ERROR(audioAdapter, COMSETTER(EnabledIn)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_AUDIOOUT:
+ {
+ ComPtr<IAudioAdapter> audioAdapter;
+ sessionMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
+ ASSERT(audioAdapter);
+
+ CHECK_ERROR(audioAdapter, COMSETTER(EnabledOut)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_CLIPBOARD:
+ {
+ ClipboardMode_T mode = ClipboardMode_Disabled; /* Shut up MSC */
+ if (!RTStrICmp(ValueUnion.psz, "disabled"))
+ mode = ClipboardMode_Disabled;
+ else if (!RTStrICmp(ValueUnion.psz, "hosttoguest"))
+ mode = ClipboardMode_HostToGuest;
+ else if (!RTStrICmp(ValueUnion.psz, "guesttohost"))
+ mode = ClipboardMode_GuestToHost;
+ else if (!RTStrICmp(ValueUnion.psz, "bidirectional"))
+ mode = ClipboardMode_Bidirectional;
+ else
+ {
+ errorArgument("Invalid --clipboard argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(ClipboardMode)(mode));
+ }
+ break;
+ }
+
+ case MODIFYVM_DRAGANDDROP:
+ {
+ DnDMode_T mode = DnDMode_Disabled; /* Shut up MSC */
+ if (!RTStrICmp(ValueUnion.psz, "disabled"))
+ mode = DnDMode_Disabled;
+ else if (!RTStrICmp(ValueUnion.psz, "hosttoguest"))
+ mode = DnDMode_HostToGuest;
+ else if (!RTStrICmp(ValueUnion.psz, "guesttohost"))
+ mode = DnDMode_GuestToHost;
+ else if (!RTStrICmp(ValueUnion.psz, "bidirectional"))
+ mode = DnDMode_Bidirectional;
+ else
+ {
+ errorArgument("Invalid --draganddrop argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(DnDMode)(mode));
+ }
+ break;
+ }
+
+ case MODIFYVM_VRDE_EXTPACK:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ if (vrdeServer)
+ {
+ if (RTStrICmp(ValueUnion.psz, "default") != 0)
+ {
+ Bstr bstr(ValueUnion.psz);
+ CHECK_ERROR(vrdeServer, COMSETTER(VRDEExtPack)(bstr.raw()));
+ }
+ else
+ CHECK_ERROR(vrdeServer, COMSETTER(VRDEExtPack)(Bstr().raw()));
+ }
+ break;
+ }
+
+ case MODIFYVM_VRDEPROPERTY:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ if (vrdeServer)
+ {
+ /* 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];
+ CHECK_ERROR(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
+ }
+ else
+ {
+ RTStrFree(pszProperty);
+
+ errorArgument("Invalid --vrdeproperty argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ break;
+ }
+ RTStrFree(pszProperty);
+ }
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for VRDE property '%s'\n", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ }
+ break;
+ }
+
+ case MODIFYVM_VRDPPORT:
+ vrdeWarningDeprecatedOption("port");
+ RT_FALL_THRU();
+
+ case MODIFYVM_VRDEPORT:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ 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()));
+ break;
+ }
+
+ case MODIFYVM_VRDPADDRESS:
+ vrdeWarningDeprecatedOption("address");
+ RT_FALL_THRU();
+
+ case MODIFYVM_VRDEADDRESS:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("TCP/Address").raw(), Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_VRDPAUTHTYPE:
+ vrdeWarningDeprecatedOption("authtype");
+ RT_FALL_THRU();
+ case MODIFYVM_VRDEAUTHTYPE:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ if (!RTStrICmp(ValueUnion.psz, "null"))
+ {
+ CHECK_ERROR(vrdeServer, COMSETTER(AuthType)(AuthType_Null));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "external"))
+ {
+ CHECK_ERROR(vrdeServer, COMSETTER(AuthType)(AuthType_External));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "guest"))
+ {
+ CHECK_ERROR(vrdeServer, COMSETTER(AuthType)(AuthType_Guest));
+ }
+ else
+ {
+ errorArgument("Invalid --vrdeauthtype argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_VRDEAUTHLIBRARY:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ if (vrdeServer)
+ {
+ if (RTStrICmp(ValueUnion.psz, "default") != 0)
+ {
+ Bstr bstr(ValueUnion.psz);
+ CHECK_ERROR(vrdeServer, COMSETTER(AuthLibrary)(bstr.raw()));
+ }
+ else
+ CHECK_ERROR(vrdeServer, COMSETTER(AuthLibrary)(Bstr().raw()));
+ }
+ break;
+ }
+
+ case MODIFYVM_VRDPMULTICON:
+ vrdeWarningDeprecatedOption("multicon");
+ RT_FALL_THRU();
+ case MODIFYVM_VRDEMULTICON:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ CHECK_ERROR(vrdeServer, COMSETTER(AllowMultiConnection)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_VRDPREUSECON:
+ vrdeWarningDeprecatedOption("reusecon");
+ RT_FALL_THRU();
+ case MODIFYVM_VRDEREUSECON:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ CHECK_ERROR(vrdeServer, COMSETTER(ReuseSingleConnection)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_VRDPVIDEOCHANNEL:
+ vrdeWarningDeprecatedOption("videochannel");
+ RT_FALL_THRU();
+ case MODIFYVM_VRDEVIDEOCHANNEL:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("VideoChannel/Enabled").raw(),
+ ValueUnion.f? Bstr("true").raw(): Bstr("false").raw()));
+ break;
+ }
+
+ case MODIFYVM_VRDPVIDEOCHANNELQUALITY:
+ vrdeWarningDeprecatedOption("videochannelquality");
+ RT_FALL_THRU();
+ case MODIFYVM_VRDEVIDEOCHANNELQUALITY:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("VideoChannel/Quality").raw(),
+ Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_VRDP:
+ vrdeWarningDeprecatedOption("");
+ RT_FALL_THRU();
+ case MODIFYVM_VRDE:
+ {
+ ComPtr<IVRDEServer> vrdeServer;
+ sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
+ ASSERT(vrdeServer);
+
+ CHECK_ERROR(vrdeServer, COMSETTER(Enabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_USBRENAME:
+ {
+ const char *pszName = ValueUnion.psz;
+ int vrc = RTGetOptFetchValue(&GetOptState, &ValueUnion, RTGETOPT_REQ_STRING);
+ if (RT_FAILURE(vrc))
+ return errorSyntax(USAGE_MODIFYVM,
+ "Missing or Invalid argument to '%s'",
+ GetOptState.pDef->pszLong);
+ const char *pszNewName = ValueUnion.psz;
+
+ SafeIfaceArray<IUSBController> ctrls;
+ CHECK_ERROR(sessionMachine, COMGETTER(USBControllers)(ComSafeArrayAsOutParam(ctrls)));
+ bool fRenamed = false;
+ for (size_t i = 0; i < ctrls.size(); i++)
+ {
+ ComPtr<IUSBController> pCtrl = ctrls[i];
+ Bstr bstrName;
+ CHECK_ERROR(pCtrl, COMGETTER(Name)(bstrName.asOutParam()));
+ if (bstrName == pszName)
+ {
+ bstrName = pszNewName;
+ CHECK_ERROR(pCtrl, COMSETTER(Name)(bstrName.raw()));
+ fRenamed = true;
+ }
+ }
+ if (!fRenamed)
+ {
+ errorArgument("Invalid --usbrename parameters, nothing renamed");
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_USBXHCI:
+ {
+ ULONG cXhciCtrls = 0;
+ rc = sessionMachine->GetUSBControllerCountByType(USBControllerType_XHCI, &cXhciCtrls);
+ if (SUCCEEDED(rc))
+ {
+ if (!cXhciCtrls && ValueUnion.f)
+ {
+ ComPtr<IUSBController> UsbCtl;
+ CHECK_ERROR(sessionMachine, AddUSBController(Bstr("xHCI").raw(), USBControllerType_XHCI,
+ UsbCtl.asOutParam()));
+ }
+ else if (cXhciCtrls && !ValueUnion.f)
+ {
+ SafeIfaceArray<IUSBController> ctrls;
+ CHECK_ERROR(sessionMachine, COMGETTER(USBControllers)(ComSafeArrayAsOutParam(ctrls)));
+ for (size_t i = 0; i < ctrls.size(); i++)
+ {
+ ComPtr<IUSBController> pCtrl = ctrls[i];
+ USBControllerType_T enmType;
+ CHECK_ERROR(pCtrl, COMGETTER(Type)(&enmType));
+ if (enmType == USBControllerType_XHCI)
+ {
+ Bstr ctrlName;
+ CHECK_ERROR(pCtrl, COMGETTER(Name)(ctrlName.asOutParam()));
+ CHECK_ERROR(sessionMachine, RemoveUSBController(ctrlName.raw()));
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case MODIFYVM_USBEHCI:
+ {
+ ULONG cEhciCtrls = 0;
+ rc = sessionMachine->GetUSBControllerCountByType(USBControllerType_EHCI, &cEhciCtrls);
+ if (SUCCEEDED(rc))
+ {
+ if (!cEhciCtrls && ValueUnion.f)
+ {
+ ComPtr<IUSBController> UsbCtl;
+ CHECK_ERROR(sessionMachine, AddUSBController(Bstr("EHCI").raw(), USBControllerType_EHCI,
+ UsbCtl.asOutParam()));
+ }
+ else if (cEhciCtrls && !ValueUnion.f)
+ {
+ SafeIfaceArray<IUSBController> ctrls;
+ CHECK_ERROR(sessionMachine, COMGETTER(USBControllers)(ComSafeArrayAsOutParam(ctrls)));
+ for (size_t i = 0; i < ctrls.size(); i++)
+ {
+ ComPtr<IUSBController> pCtrl = ctrls[i];
+ USBControllerType_T enmType;
+ CHECK_ERROR(pCtrl, COMGETTER(Type)(&enmType));
+ if (enmType == USBControllerType_EHCI)
+ {
+ Bstr ctrlName;
+ CHECK_ERROR(pCtrl, COMGETTER(Name)(ctrlName.asOutParam()));
+ CHECK_ERROR(sessionMachine, RemoveUSBController(ctrlName.raw()));
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case MODIFYVM_USBOHCI:
+ {
+ ULONG cOhciCtrls = 0;
+ rc = sessionMachine->GetUSBControllerCountByType(USBControllerType_OHCI, &cOhciCtrls);
+ if (SUCCEEDED(rc))
+ {
+ if (!cOhciCtrls && ValueUnion.f)
+ {
+ ComPtr<IUSBController> UsbCtl;
+ CHECK_ERROR(sessionMachine, AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI,
+ UsbCtl.asOutParam()));
+ }
+ else if (cOhciCtrls && !ValueUnion.f)
+ {
+ SafeIfaceArray<IUSBController> ctrls;
+ CHECK_ERROR(sessionMachine, COMGETTER(USBControllers)(ComSafeArrayAsOutParam(ctrls)));
+ for (size_t i = 0; i < ctrls.size(); i++)
+ {
+ ComPtr<IUSBController> pCtrl = ctrls[i];
+ USBControllerType_T enmType;
+ CHECK_ERROR(pCtrl, COMGETTER(Type)(&enmType));
+ if (enmType == USBControllerType_OHCI)
+ {
+ Bstr ctrlName;
+ CHECK_ERROR(pCtrl, COMGETTER(Name)(ctrlName.asOutParam()));
+ CHECK_ERROR(sessionMachine, RemoveUSBController(ctrlName.raw()));
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case MODIFYVM_SNAPSHOTFOLDER:
+ {
+ if (!RTStrICmp(ValueUnion.psz, "default"))
+ CHECK_ERROR(sessionMachine, COMSETTER(SnapshotFolder)(Bstr().raw()));
+ else
+ CHECK_ERROR(sessionMachine, COMSETTER(SnapshotFolder)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_TELEPORTER_ENABLED:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(TeleporterEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_TELEPORTER_PORT:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(TeleporterPort)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_TELEPORTER_ADDRESS:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(TeleporterAddress)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_TELEPORTER_PASSWORD:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(TeleporterPassword)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_TELEPORTER_PASSWORD_FILE:
+ {
+ Utf8Str password;
+ RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &password);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ rc = E_FAIL;
+ else
+ CHECK_ERROR(sessionMachine, COMSETTER(TeleporterPassword)(Bstr(password).raw()));
+ break;
+ }
+
+ case MODIFYVM_TRACING_ENABLED:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(TracingEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_TRACING_CONFIG:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(TracingConfig)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_TRACING_ALLOW_VM_ACCESS:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(AllowTracingToAccessVM)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_FAULT_TOLERANCE:
+ {
+ if (!RTStrICmp(ValueUnion.psz, "master"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FaultToleranceState(FaultToleranceState_Master)));
+ }
+ else
+ if (!RTStrICmp(ValueUnion.psz, "standby"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FaultToleranceState(FaultToleranceState_Standby)));
+ }
+ else
+ {
+ errorArgument("Invalid --faulttolerance argument '%s'", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+
+ case MODIFYVM_FAULT_TOLERANCE_ADDRESS:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FaultToleranceAddress)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_FAULT_TOLERANCE_PORT:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FaultTolerancePort)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_FAULT_TOLERANCE_PASSWORD:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FaultTolerancePassword)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_FAULT_TOLERANCE_SYNC_INTERVAL:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(FaultToleranceSyncInterval)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_HARDWARE_UUID:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(HardwareUUID)(Bstr(ValueUnion.psz).raw()));
+ break;
+ }
+
+ case MODIFYVM_HPET:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(HPETEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_IOCACHE:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(IOCacheEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_IOCACHESIZE:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(IOCacheSize)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_CHIPSET:
+ {
+ if (!RTStrICmp(ValueUnion.psz, "piix3"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(ChipsetType)(ChipsetType_PIIX3));
+ }
+ else if (!RTStrICmp(ValueUnion.psz, "ich9"))
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(ChipsetType)(ChipsetType_ICH9));
+ BOOL fIoApic = FALSE;
+ CHECK_ERROR(biosSettings, COMGETTER(IOAPICEnabled)(&fIoApic));
+ if (!fIoApic)
+ {
+ RTStrmPrintf(g_pStdErr, "*** I/O APIC must be enabled for ICH9, enabling. ***\n");
+ CHECK_ERROR(biosSettings, COMSETTER(IOAPICEnabled)(TRUE));
+ }
+ }
+ else
+ {
+ errorArgument("Invalid --chipset argument '%s' (valid: piix3,ich9)", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ break;
+ }
+#ifdef VBOX_WITH_RECORDING
+ case MODIFYVM_RECORDING:
+ RT_FALL_THROUGH();
+ case MODIFYVM_RECORDING_SCREENS:
+ RT_FALL_THROUGH();
+ case MODIFYVM_RECORDING_FILENAME:
+ RT_FALL_THROUGH();
+ case MODIFYVM_RECORDING_VIDEO_WIDTH:
+ RT_FALL_THROUGH();
+ case MODIFYVM_RECORDING_VIDEO_HEIGHT:
+ RT_FALL_THROUGH();
+ case MODIFYVM_RECORDING_VIDEO_RES:
+ RT_FALL_THROUGH();
+ case MODIFYVM_RECORDING_VIDEO_RATE:
+ RT_FALL_THROUGH();
+ case MODIFYVM_RECORDING_VIDEO_FPS:
+ RT_FALL_THROUGH();
+ case MODIFYVM_RECORDING_MAXTIME:
+ RT_FALL_THROUGH();
+ case MODIFYVM_RECORDING_MAXSIZE:
+ RT_FALL_THROUGH();
+ case MODIFYVM_RECORDING_OPTIONS:
+ {
+ ComPtr<IRecordingSettings> recordingSettings;
+ CHECK_ERROR_BREAK(machine, COMGETTER(RecordingSettings)(recordingSettings.asOutParam()));
+ SafeIfaceArray <IRecordingScreenSettings> saRecordingScreenScreens;
+ CHECK_ERROR_BREAK(recordingSettings, COMGETTER(Screens)(ComSafeArrayAsOutParam(saRecordingScreenScreens)));
+
+ switch (c)
+ {
+ case MODIFYVM_RECORDING:
+ {
+ CHECK_ERROR(recordingSettings, COMSETTER(Enabled)(ValueUnion.f));
+ break;
+ }
+ case MODIFYVM_RECORDING_SCREENS:
+ {
+ ULONG cMonitors = 64;
+ CHECK_ERROR(sessionMachine, COMGETTER(MonitorCount)(&cMonitors));
+ com::SafeArray<BOOL> screens(cMonitors);
+ if (parseScreens(ValueUnion.psz, &screens))
+ {
+ errorArgument("Invalid list of screens specified\n");
+ rc = E_FAIL;
+ break;
+ }
+
+ if (cMonitors > saRecordingScreenScreens.size()) /* Paranoia. */
+ cMonitors = (ULONG)saRecordingScreenScreens.size();
+
+ for (size_t i = 0; i < cMonitors; ++i)
+ CHECK_ERROR_BREAK(saRecordingScreenScreens[i], COMSETTER(Enabled)(screens[i]));
+ break;
+ }
+ case MODIFYVM_RECORDING_FILENAME:
+ {
+ 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;
+ }
+
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR(saRecordingScreenScreens[i], COMSETTER(Filename)(bstr.raw()));
+ break;
+ }
+ case MODIFYVM_RECORDING_VIDEO_WIDTH:
+ {
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR(saRecordingScreenScreens[i], COMSETTER(VideoWidth)(ValueUnion.u32));
+ break;
+ }
+ case MODIFYVM_RECORDING_VIDEO_HEIGHT:
+ {
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR(saRecordingScreenScreens[i], COMSETTER(VideoHeight)(ValueUnion.u32));
+ break;
+ }
+ case MODIFYVM_RECORDING_VIDEO_RES:
+ {
+ uint32_t uWidth = 0;
+ char *pszNext;
+ int vrc = RTStrToUInt32Ex(ValueUnion.psz, &pszNext, 0, &uWidth);
+ if (RT_FAILURE(vrc) || vrc != VWRN_TRAILING_CHARS || !pszNext || *pszNext != 'x')
+ {
+ errorArgument("Error parsing video resolution '%s' (expected <width>x<height>)", ValueUnion.psz);
+ rc = E_FAIL;
+ break;
+ }
+ uint32_t uHeight = 0;
+ vrc = RTStrToUInt32Ex(pszNext+1, NULL, 0, &uHeight);
+ if (vrc != VINF_SUCCESS)
+ {
+ errorArgument("Error parsing video resolution '%s' (expected <width>x<height>)", ValueUnion.psz);
+ rc = E_FAIL;
+ break;
+ }
+
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ {
+ CHECK_ERROR(saRecordingScreenScreens[i], COMSETTER(VideoWidth)(uWidth));
+ CHECK_ERROR(saRecordingScreenScreens[i], COMSETTER(VideoHeight)(uHeight));
+ }
+ break;
+ }
+ case MODIFYVM_RECORDING_VIDEO_RATE:
+ {
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR(saRecordingScreenScreens[i], COMSETTER(VideoRate)(ValueUnion.u32));
+ break;
+ }
+ case MODIFYVM_RECORDING_VIDEO_FPS:
+ {
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR(saRecordingScreenScreens[i], COMSETTER(VideoFPS)(ValueUnion.u32));
+ break;
+ }
+ case MODIFYVM_RECORDING_MAXTIME:
+ {
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR(saRecordingScreenScreens[i], COMSETTER(MaxTime)(ValueUnion.u32));
+ break;
+ }
+ case MODIFYVM_RECORDING_MAXSIZE:
+ {
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR(saRecordingScreenScreens[i], COMSETTER(MaxFileSize)(ValueUnion.u32));
+ break;
+ }
+ case MODIFYVM_RECORDING_OPTIONS:
+ {
+ Bstr bstr(ValueUnion.psz);
+ for (size_t i = 0; i < saRecordingScreenScreens.size(); ++i)
+ CHECK_ERROR(saRecordingScreenScreens[i], COMSETTER(Options)(bstr.raw()));
+ break;
+ }
+ }
+
+ break;
+ }
+#endif
+ case MODIFYVM_AUTOSTART_ENABLED:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(AutostartEnabled)(ValueUnion.f));
+ break;
+ }
+
+ case MODIFYVM_AUTOSTART_DELAY:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(AutostartDelay)(ValueUnion.u32));
+ break;
+ }
+
+ case MODIFYVM_AUTOSTOP_TYPE:
+ {
+ AutostopType_T enmAutostopType = AutostopType_Disabled;
+
+ if (!RTStrICmp(ValueUnion.psz, "disabled"))
+ enmAutostopType = AutostopType_Disabled;
+ else if (!RTStrICmp(ValueUnion.psz, "savestate"))
+ enmAutostopType = AutostopType_SaveState;
+ else if (!RTStrICmp(ValueUnion.psz, "poweroff"))
+ enmAutostopType = AutostopType_PowerOff;
+ else if (!RTStrICmp(ValueUnion.psz, "acpishutdown"))
+ enmAutostopType = AutostopType_AcpiShutdown;
+ else
+ {
+ errorArgument("Invalid --autostop-type argument '%s' (valid: disabled, savestate, poweroff, acpishutdown)", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+
+ if (SUCCEEDED(rc))
+ CHECK_ERROR(sessionMachine, COMSETTER(AutostopType)(enmAutostopType));
+ break;
+ }
+#ifdef VBOX_WITH_PCI_PASSTHROUGH
+ case MODIFYVM_ATTACH_PCI:
+ {
+ const char* pAt = strchr(ValueUnion.psz, '@');
+ int32_t iHostAddr, iGuestAddr;
+
+ iHostAddr = parsePci(ValueUnion.psz);
+ iGuestAddr = pAt != NULL ? parsePci(pAt + 1) : iHostAddr;
+
+ if (iHostAddr == -1 || iGuestAddr == -1)
+ {
+ errorArgument("Invalid --pciattach argument '%s' (valid: 'HB:HD.HF@GB:GD.GF' or just 'HB:HD.HF')", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ else
+ {
+ CHECK_ERROR(sessionMachine, AttachHostPCIDevice(iHostAddr, iGuestAddr, TRUE));
+ }
+
+ break;
+ }
+ case MODIFYVM_DETACH_PCI:
+ {
+ int32_t iHostAddr;
+
+ iHostAddr = parsePci(ValueUnion.psz);
+ if (iHostAddr == -1)
+ {
+ errorArgument("Invalid --pcidetach argument '%s' (valid: 'HB:HD.HF')", ValueUnion.psz);
+ rc = E_FAIL;
+ }
+ else
+ {
+ CHECK_ERROR(sessionMachine, DetachHostPCIDevice(iHostAddr));
+ }
+
+ break;
+ }
+#endif
+
+#ifdef VBOX_WITH_USB_CARDREADER
+ case MODIFYVM_USBCARDREADER:
+ {
+ CHECK_ERROR(sessionMachine, COMSETTER(EmulatedUSBCardReaderEnabled)(ValueUnion.f));
+ break;
+ }
+#endif /* VBOX_WITH_USB_CARDREADER */
+
+ case MODIFYVM_DEFAULTFRONTEND:
+ {
+ Bstr bstr(ValueUnion.psz);
+ if (bstr == "default")
+ bstr = Bstr::Empty;
+ CHECK_ERROR(sessionMachine, COMSETTER(DefaultFrontend)(bstr.raw()));
+ break;
+ }
+
+ default:
+ {
+ errorGetOpt(USAGE_MODIFYVM, c, &ValueUnion);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ }
+
+ /* commit changes */
+ if (SUCCEEDED(rc))
+ CHECK_ERROR(sessionMachine, SaveSettings());
+
+ /* it's important to always close sessions */
+ a->session->UnlockMachine();
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp
new file mode 100644
index 00000000..6312bb03
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageNATNetwork.cpp
@@ -0,0 +1,523 @@
+/* $Id: VBoxManageNATNetwork.cpp $ */
+/** @file
+ * VBoxManage - Implementation of NAT Network command command.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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
+{
+ OP_ADD = 1000,
+ OP_REMOVE,
+ OP_MODIFY,
+ OP_START,
+ OP_STOP
+} OPCODE;
+
+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 HRESULT printNATNetwork(const ComPtr<INATNetwork> &pNATNet)
+{
+ HRESULT rc;
+
+ do
+ {
+ Bstr strVal;
+ CHECK_ERROR_BREAK(pNATNet, COMGETTER(NetworkName)(strVal.asOutParam()));
+ RTPrintf("Name: %ls\n", strVal.raw());
+ CHECK_ERROR_BREAK(pNATNet, COMGETTER(Network)(strVal.asOutParam()));
+ RTPrintf("Network: %ls\n", strVal.raw());
+ CHECK_ERROR_BREAK(pNATNet, COMGETTER(Gateway)(strVal.asOutParam()));
+ RTPrintf("Gateway: %ls\n", strVal.raw());
+ BOOL fVal;
+ CHECK_ERROR_BREAK(pNATNet, COMGETTER(IPv6Enabled)(&fVal));
+ RTPrintf("IPv6: %s\n", fVal ? "Yes" : "No");
+ if (fVal)
+ {
+ CHECK_ERROR_BREAK(pNATNet, COMGETTER(IPv6Prefix)(strVal.asOutParam()));
+ RTPrintf("IPv6 Prefix: %s\n", strVal.raw());
+ }
+ CHECK_ERROR_BREAK(pNATNet, COMGETTER(Enabled)(&fVal));
+ RTPrintf("Enabled: %s\n", fVal ? "Yes" : "No");
+ /** @todo Add more information here. */
+ RTPrintf("\n");
+
+ } while (0);
+
+ return rc;
+}
+
+static RTEXITCODE handleNATList(HandlerArg *a)
+{
+ HRESULT rc;
+
+ RTPrintf("NAT Networks:\n\n");
+
+ const char *pszFilter = NULL;
+ if (a->argc > 1)
+ pszFilter = a->argv[1];
+
+ size_t cFound = 0;
+
+ com::SafeIfaceArray<INATNetwork> arrNetNets;
+ CHECK_ERROR(a->virtualBox, COMGETTER(NATNetworks)(ComSafeArrayAsOutParam(arrNetNets)));
+ for (size_t i = 0; i < arrNetNets.size(); ++i)
+ {
+ ComPtr<INATNetwork> pNATNet = arrNetNets[i];
+
+ if (pszFilter)
+ {
+ Bstr strVal;
+ CHECK_ERROR_BREAK(pNATNet, COMGETTER(NetworkName)(strVal.asOutParam()));
+
+ Utf8Str strValUTF8 = Utf8Str(strVal);
+ if (!RTStrSimplePatternMatch(pszFilter, strValUTF8.c_str()))
+ continue;
+ }
+
+ if (i > 0)
+ RTPrintf("\n");
+ rc = printNATNetwork(pNATNet);
+ if (FAILED(rc))
+ break;
+
+ cFound++;
+ }
+
+ if (SUCCEEDED(rc))
+ RTPrintf("%zu %s found\n", cFound, cFound == 1 ? "network" : "networks");
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+static RTEXITCODE handleOp(HandlerArg *a, OPCODE enmCode)
+{
+ if (a->argc - 1 <= 1)
+ return errorSyntax(USAGE_NATNETWORK, "Not enough parameters");
+
+ 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 */
+
+ 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 },
+ };
+
+ 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*/
+ 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (c)
+ {
+ case 't': // --netname
+ if (pNetName)
+ return errorSyntax(USAGE_NATNETWORK, "You can only specify --netname only once.");
+ pNetName = ValueUnion.psz;
+ break;
+
+ case 'n': // --network
+ if (pNetworkCidr)
+ return errorSyntax(USAGE_NATNETWORK, "You can only specify --network only once.");
+ pNetworkCidr = ValueUnion.psz;
+ break;
+
+ case 'e': // --enable
+ if (enable >= 0)
+ return errorSyntax(USAGE_NATNETWORK, "You can specify either --enable or --disable once.");
+ enable = 1;
+ break;
+
+ case 'd': // --disable
+ if (enable >= 0)
+ return errorSyntax(USAGE_NATNETWORK, "You can specify either --enable or --disable once.");
+ 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)
+ {
+ /* addition */
+ /* netPfStrToPf will clean up the Pfr */
+ PORTFORWARDRULE 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
+ {
+ /* deletion */
+ if (enmCode != OP_MODIFY)
+ return errorSyntax(USAGE_NATNETWORK,
+ "Port-forward could be deleted on modify \n");
+
+ RTGETOPTUNION NamePf2DeleteUnion;
+ int 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");
+
+ PFNAME2DELETE Name2Delete;
+ RT_ZERO(Name2Delete);
+ RTStrCopy(Name2Delete.szName, PF_NAMELEN, NamePf2DeleteUnion.psz);
+ Name2Delete.fIPv6 = (c == 'P');
+ vPfName2Delete.push_back(Name2Delete);
+ }
+ break;
+ }
+
+ default:
+ return errorGetOpt(USAGE_NATNETWORK, c, &ValueUnion);
+ }
+ }
+
+ 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), RTEXITCODE_FAILURE);
+ }
+
+ HRESULT rc;
+ 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), RTEXITCODE_FAILURE);
+ }
+
+ /* 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;
+ size_t pos = it->find('=');
+ LONG lOffset = 0;
+ Bstr bstrAddress;
+
+ AssertReturn(pos != std::string::npos, 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), RTEXITCODE_FAILURE);
+ }
+ }
+
+ 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), RTEXITCODE_FAILURE);
+ }
+ }
+
+ 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 RTEXITCODE_SUCCESS;
+}
+
+
+RTEXITCODE handleNATNetwork(HandlerArg *a)
+{
+ if (a->argc < 1)
+ return errorSyntax(USAGE_NATNETWORK, "Not enough parameters");
+
+ RTEXITCODE rcExit;
+ if (strcmp(a->argv[0], "modify") == 0)
+ rcExit = handleOp(a, OP_MODIFY);
+ else if (strcmp(a->argv[0], "add") == 0)
+ rcExit = handleOp(a, OP_ADD);
+ else if (strcmp(a->argv[0], "remove") == 0)
+ rcExit = handleOp(a, OP_REMOVE);
+ else if (strcmp(a->argv[0], "start") == 0)
+ rcExit = handleOp(a, OP_START);
+ else if (strcmp(a->argv[0], "stop") == 0)
+ rcExit = handleOp(a, OP_STOP);
+ else if (strcmp(a->argv[0], "list") == 0)
+ rcExit = handleNATList(a);
+ else
+ rcExit = errorSyntax(USAGE_NATNETWORK, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
+ return rcExit;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp
new file mode 100644
index 00000000..61156bf2
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageSnapshot.cpp
@@ -0,0 +1,641 @@
+/* $Id: VBoxManageSnapshot.cpp $ */
+/** @file
+ * VBoxManage - The 'snapshot' command.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+
+#include <VBox/com/VirtualBox.h>
+
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+#include <iprt/time.h>
+
+#include "VBoxManage.h"
+using namespace com;
+
+/**
+ * Helper function used with "VBoxManage snapshot ... dump". Gets called to find the
+ * snapshot in the machine's snapshot tree that uses a particular diff image child of
+ * a medium.
+ * Horribly inefficient since we keep re-querying the snapshots tree for each image,
+ * but this is for quick debugging only.
+ * @param pMedium
+ * @param pThisSnapshot
+ * @param pCurrentSnapshot
+ * @param uMediumLevel
+ * @param uSnapshotLevel
+ * @return
+ */
+bool FindAndPrintSnapshotUsingMedium(ComPtr<IMedium> &pMedium,
+ ComPtr<ISnapshot> &pThisSnapshot,
+ ComPtr<ISnapshot> &pCurrentSnapshot,
+ uint32_t uMediumLevel,
+ uint32_t uSnapshotLevel)
+{
+ HRESULT rc;
+
+ do
+ {
+ // get snapshot machine so we can figure out which diff image this created
+ ComPtr<IMachine> pSnapshotMachine;
+ CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Machine)(pSnapshotMachine.asOutParam()));
+
+ // get media attachments
+ SafeIfaceArray<IMediumAttachment> aAttachments;
+ CHECK_ERROR_BREAK(pSnapshotMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aAttachments)));
+
+ for (uint32_t i = 0;
+ i < aAttachments.size();
+ ++i)
+ {
+ ComPtr<IMediumAttachment> pAttach(aAttachments[i]);
+ DeviceType_T type;
+ CHECK_ERROR_BREAK(pAttach, COMGETTER(Type)(&type));
+ if (type == DeviceType_HardDisk)
+ {
+ ComPtr<IMedium> pMediumInSnapshot;
+ CHECK_ERROR_BREAK(pAttach, COMGETTER(Medium)(pMediumInSnapshot.asOutParam()));
+
+ if (pMediumInSnapshot == pMedium)
+ {
+ // get snapshot name
+ Bstr bstrSnapshotName;
+ CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Name)(bstrSnapshotName.asOutParam()));
+
+ RTPrintf("%*s \"%ls\"%s\n",
+ 50 + uSnapshotLevel * 2, "", // indent
+ bstrSnapshotName.raw(),
+ (pThisSnapshot == pCurrentSnapshot) ? " (CURSNAP)" : "");
+ return true; // found
+ }
+ }
+ }
+
+ // not found: then recurse into child snapshots
+ SafeIfaceArray<ISnapshot> aSnapshots;
+ CHECK_ERROR_BREAK(pThisSnapshot, COMGETTER(Children)(ComSafeArrayAsOutParam(aSnapshots)));
+
+ for (uint32_t i = 0;
+ i < aSnapshots.size();
+ ++i)
+ {
+ ComPtr<ISnapshot> pChild(aSnapshots[i]);
+ if (FindAndPrintSnapshotUsingMedium(pMedium,
+ pChild,
+ pCurrentSnapshot,
+ uMediumLevel,
+ uSnapshotLevel + 1))
+ // found:
+ break;
+ }
+ } while (0);
+
+ return false;
+}
+
+/**
+ * Helper function used with "VBoxManage snapshot ... dump". Called from DumpSnapshot()
+ * for each hard disk attachment found in a virtual machine. This then writes out the
+ * root (base) medium for that hard disk attachment and recurses into the children
+ * tree of that medium, correlating it with the snapshots of the machine.
+ * @param pCurrentStateMedium constant, the medium listed in the current machine data (latest diff image).
+ * @param pMedium variant, initially the base medium, then a child of the base medium when recursing.
+ * @param pRootSnapshot constant, the root snapshot of the machine, if any; this then looks into the child snapshots.
+ * @param pCurrentSnapshot constant, the machine's current snapshot (so we can mark it in the output).
+ * @param uLevel variant, the recursion level for output indentation.
+ */
+void DumpMediumWithChildren(ComPtr<IMedium> &pCurrentStateMedium,
+ ComPtr<IMedium> &pMedium,
+ ComPtr<ISnapshot> &pRootSnapshot,
+ ComPtr<ISnapshot> &pCurrentSnapshot,
+ uint32_t uLevel)
+{
+ HRESULT rc;
+ do
+ {
+ // print this medium
+ Bstr bstrMediumName;
+ CHECK_ERROR_BREAK(pMedium, COMGETTER(Name)(bstrMediumName.asOutParam()));
+ RTPrintf("%*s \"%ls\"%s\n",
+ uLevel * 2, "", // indent
+ bstrMediumName.raw(),
+ (pCurrentStateMedium == pMedium) ? " (CURSTATE)" : "");
+
+ // find and print the snapshot that uses this particular medium (diff image)
+ FindAndPrintSnapshotUsingMedium(pMedium, pRootSnapshot, pCurrentSnapshot, uLevel, 0);
+
+ // recurse into children
+ SafeIfaceArray<IMedium> aChildren;
+ CHECK_ERROR_BREAK(pMedium, COMGETTER(Children)(ComSafeArrayAsOutParam(aChildren)));
+ for (uint32_t i = 0;
+ i < aChildren.size();
+ ++i)
+ {
+ ComPtr<IMedium> pChild(aChildren[i]);
+ DumpMediumWithChildren(pCurrentStateMedium, pChild, pRootSnapshot, pCurrentSnapshot, uLevel + 1);
+ }
+ } while (0);
+}
+
+
+/**
+ * Handles the 'snapshot myvm list' sub-command.
+ * @returns Exit code.
+ * @param pArgs The handler argument package.
+ * @param pMachine Reference to the VM (locked) we're operating on.
+ */
+static RTEXITCODE handleSnapshotList(HandlerArg *pArgs, ComPtr<IMachine> &pMachine)
+{
+ static const RTGETOPTDEF g_aOptions[] =
+ {
+ { "--details", 'D', RTGETOPT_REQ_NOTHING },
+ { "--machinereadable", 'M', RTGETOPT_REQ_NOTHING },
+ };
+
+ VMINFO_DETAILS enmDetails = VMINFO_STANDARD;
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, pArgs->argc, pArgs->argv, g_aOptions, RT_ELEMENTS(g_aOptions), 2 /*iArg*/, 0 /*fFlags*/);
+ while ((c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 'D': enmDetails = VMINFO_FULL; break;
+ case 'M': enmDetails = VMINFO_MACHINEREADABLE; break;
+ default: return errorGetOpt(USAGE_SNAPSHOT, c, &ValueUnion);
+ }
+ }
+
+ ComPtr<ISnapshot> pSnapshot;
+ HRESULT hrc = pMachine->FindSnapshot(Bstr().raw(), pSnapshot.asOutParam());
+ if (FAILED(hrc))
+ {
+ RTPrintf("This machine does not have any snapshots\n");
+ return RTEXITCODE_FAILURE;
+ }
+ if (pSnapshot)
+ {
+ ComPtr<ISnapshot> pCurrentSnapshot;
+ CHECK_ERROR2I_RET(pMachine, COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam()), RTEXITCODE_FAILURE);
+ hrc = showSnapshots(pSnapshot, pCurrentSnapshot, enmDetails);
+ if (FAILED(hrc))
+ return RTEXITCODE_FAILURE;
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Implementation for "VBoxManage snapshot ... dump". This goes thru the machine's
+ * medium attachments and calls DumpMediumWithChildren() for each hard disk medium found,
+ * which then dumps the parent/child tree of that medium together with the corresponding
+ * snapshots.
+ * @param pMachine Machine to dump snapshots for.
+ */
+void DumpSnapshot(ComPtr<IMachine> &pMachine)
+{
+ HRESULT rc;
+
+ do
+ {
+ // get root snapshot
+ ComPtr<ISnapshot> pSnapshot;
+ CHECK_ERROR_BREAK(pMachine, FindSnapshot(Bstr("").raw(), pSnapshot.asOutParam()));
+
+ // get current snapshot
+ ComPtr<ISnapshot> pCurrentSnapshot;
+ CHECK_ERROR_BREAK(pMachine, COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam()));
+
+ // get media attachments
+ SafeIfaceArray<IMediumAttachment> aAttachments;
+ CHECK_ERROR_BREAK(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aAttachments)));
+ for (uint32_t i = 0;
+ i < aAttachments.size();
+ ++i)
+ {
+ ComPtr<IMediumAttachment> pAttach(aAttachments[i]);
+ DeviceType_T type;
+ CHECK_ERROR_BREAK(pAttach, COMGETTER(Type)(&type));
+ if (type == DeviceType_HardDisk)
+ {
+ ComPtr<IMedium> pCurrentStateMedium;
+ CHECK_ERROR_BREAK(pAttach, COMGETTER(Medium)(pCurrentStateMedium.asOutParam()));
+
+ ComPtr<IMedium> pBaseMedium;
+ CHECK_ERROR_BREAK(pCurrentStateMedium, COMGETTER(Base)(pBaseMedium.asOutParam()));
+
+ Bstr bstrBaseMediumName;
+ CHECK_ERROR_BREAK(pBaseMedium, COMGETTER(Name)(bstrBaseMediumName.asOutParam()));
+
+ RTPrintf("[%RI32] Images and snapshots for medium \"%ls\"\n", i, bstrBaseMediumName.raw());
+
+ DumpMediumWithChildren(pCurrentStateMedium,
+ pBaseMedium,
+ pSnapshot,
+ pCurrentSnapshot,
+ 0);
+ }
+ }
+ } while (0);
+}
+
+typedef enum SnapshotUniqueFlags
+{
+ SnapshotUniqueFlags_Null = 0,
+ SnapshotUniqueFlags_Number = RT_BIT(1),
+ SnapshotUniqueFlags_Timestamp = RT_BIT(2),
+ SnapshotUniqueFlags_Space = RT_BIT(16),
+ SnapshotUniqueFlags_Force = RT_BIT(30)
+} SnapshotUniqueFlags;
+
+static int parseSnapshotUniqueFlags(const char *psz, SnapshotUniqueFlags *pUnique)
+{
+ int rc = VINF_SUCCESS;
+ unsigned uUnique = 0;
+ 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, "number", len))
+ uUnique |= SnapshotUniqueFlags_Number;
+ else if (!RTStrNICmp(psz, "timestamp", len))
+ uUnique |= SnapshotUniqueFlags_Timestamp;
+ else if (!RTStrNICmp(psz, "space", len))
+ uUnique |= SnapshotUniqueFlags_Space;
+ else if (!RTStrNICmp(psz, "force", len))
+ uUnique |= SnapshotUniqueFlags_Force;
+ else
+ rc = VERR_PARSE_ERROR;
+ }
+ if (pszComma)
+ psz += len + 1;
+ else
+ psz += len;
+ }
+
+ if (RT_SUCCESS(rc))
+ *pUnique = (SnapshotUniqueFlags)uUnique;
+ return rc;
+}
+
+/**
+ * Implementation for all VBoxManage snapshot ... subcommands.
+ * @param a
+ * @return
+ */
+RTEXITCODE handleSnapshot(HandlerArg *a)
+{
+ HRESULT rc;
+
+ /* we need at least a VM and a command */
+ if (a->argc < 2)
+ return errorSyntax(USAGE_SNAPSHOT, "Not enough parameters");
+
+ /* the first argument must be the VM */
+ Bstr bstrMachine(a->argv[0]);
+ ComPtr<IMachine> pMachine;
+ CHECK_ERROR(a->virtualBox, FindMachine(bstrMachine.raw(),
+ pMachine.asOutParam()));
+ if (!pMachine)
+ return RTEXITCODE_FAILURE;
+
+ /* we have to open a session for this task (new or shared) */
+ CHECK_ERROR_RET(pMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
+ do
+ {
+ /* replace the (read-only) IMachine object by a writable one */
+ ComPtr<IMachine> sessionMachine;
+ CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));
+
+ /* switch based on the command */
+ bool fDelete = false,
+ fRestore = false,
+ fRestoreCurrent = false;
+
+ if (!strcmp(a->argv[1], "take"))
+ {
+ /* there must be a name */
+ if (a->argc < 3)
+ {
+ errorSyntax(USAGE_SNAPSHOT, "Missing snapshot name");
+ rc = E_FAIL;
+ break;
+ }
+ Bstr name(a->argv[2]);
+
+ /* parse the optional arguments */
+ Bstr desc;
+ bool fPause = true; /* default is NO live snapshot */
+ SnapshotUniqueFlags enmUnique = SnapshotUniqueFlags_Null;
+ 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 },
+ { "--live", 'l', RTGETOPT_REQ_NOTHING },
+ { "--uniquename", 'u', RTGETOPT_REQ_STRING }
+ };
+ RTGETOPTSTATE GetOptState;
+ RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTakeOptions, RT_ELEMENTS(s_aTakeOptions),
+ 3, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ int ch;
+ RTGETOPTUNION Value;
+ int vrc;
+ while ( SUCCEEDED(rc)
+ && (ch = RTGetOpt(&GetOptState, &Value)))
+ {
+ switch (ch)
+ {
+ case 'p':
+ fPause = true;
+ break;
+
+ case 'l':
+ fPause = false;
+ break;
+
+ case 'd':
+ desc = Value.psz;
+ break;
+
+ case 'u':
+ vrc = parseSnapshotUniqueFlags(Value.psz, &enmUnique);
+ if (RT_FAILURE(vrc))
+ return errorArgument("Invalid unique name description '%s'", Value.psz);
+ break;
+
+ default:
+ errorGetOpt(USAGE_SNAPSHOT, ch, &Value);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ if (FAILED(rc))
+ break;
+
+ if (enmUnique & (SnapshotUniqueFlags_Number | SnapshotUniqueFlags_Timestamp))
+ {
+ ComPtr<ISnapshot> pSnapshot;
+ rc = sessionMachine->FindSnapshot(name.raw(),
+ pSnapshot.asOutParam());
+ if (SUCCEEDED(rc) || (enmUnique & SnapshotUniqueFlags_Force))
+ {
+ /* there is a duplicate, need to create a unique name */
+ uint32_t count = 0;
+ RTTIMESPEC now;
+
+ if (enmUnique & SnapshotUniqueFlags_Number)
+ {
+ if (enmUnique & SnapshotUniqueFlags_Force)
+ count = 1;
+ else
+ count = 2;
+ RTTimeSpecSetNano(&now, 0); /* Shut up MSC */
+ }
+ else
+ RTTimeNow(&now);
+
+ while (count < 500)
+ {
+ Utf8Str suffix;
+ if (enmUnique & SnapshotUniqueFlags_Number)
+ suffix = Utf8StrFmt("%u", count);
+ else
+ {
+ RTTIMESPEC nowplus = now;
+ RTTimeSpecAddSeconds(&nowplus, count);
+ RTTIME stamp;
+ RTTimeExplode(&stamp, &nowplus);
+ suffix = Utf8StrFmt("%04u-%02u-%02uT%02u:%02u:%02uZ", stamp.i32Year, stamp.u8Month, stamp.u8MonthDay, stamp.u8Hour, stamp.u8Minute, stamp.u8Second);
+ }
+ Bstr tryName = name;
+ if (enmUnique & SnapshotUniqueFlags_Space)
+ tryName = BstrFmt("%ls %s", name.raw(), suffix.c_str());
+ else
+ tryName = BstrFmt("%ls%s", name.raw(), suffix.c_str());
+ count++;
+ rc = sessionMachine->FindSnapshot(tryName.raw(),
+ pSnapshot.asOutParam());
+ if (FAILED(rc))
+ {
+ name = tryName;
+ break;
+ }
+ }
+ if (SUCCEEDED(rc))
+ {
+ errorArgument("Failed to generate a unique snapshot name");
+ rc = E_FAIL;
+ break;
+ }
+ }
+ rc = S_OK;
+ }
+
+ ComPtr<IProgress> progress;
+ Bstr snapId;
+ CHECK_ERROR_BREAK(sessionMachine, TakeSnapshot(name.raw(), desc.raw(),
+ fPause, snapId.asOutParam(),
+ progress.asOutParam()));
+
+ rc = showProgress(progress);
+ if (SUCCEEDED(rc))
+ RTPrintf("Snapshot taken. UUID: %ls\n", snapId.raw());
+ else
+ CHECK_PROGRESS_ERROR(progress, ("Failed to take snapshot"));
+ }
+ else if ( (fDelete = !strcmp(a->argv[1], "delete"))
+ || (fRestore = !strcmp(a->argv[1], "restore"))
+ || (fRestoreCurrent = !strcmp(a->argv[1], "restorecurrent"))
+ )
+ {
+ if (fRestoreCurrent)
+ {
+ if (a->argc > 2)
+ {
+ errorSyntax(USAGE_SNAPSHOT, "Too many arguments");
+ rc = E_FAIL;
+ break;
+ }
+ }
+ /* exactly one parameter: snapshot name */
+ else if (a->argc != 3)
+ {
+ errorSyntax(USAGE_SNAPSHOT, "Expecting snapshot name only");
+ rc = E_FAIL;
+ break;
+ }
+
+ ComPtr<ISnapshot> pSnapshot;
+
+ if (fRestoreCurrent)
+ {
+ CHECK_ERROR_BREAK(sessionMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam()));
+ if (pSnapshot.isNull())
+ {
+ RTPrintf("This machine does not have any snapshots\n");
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ else
+ {
+ // restore or delete snapshot: then resolve cmd line argument to snapshot instance
+ CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
+ pSnapshot.asOutParam()));
+ }
+
+ Bstr bstrSnapGuid;
+ CHECK_ERROR_BREAK(pSnapshot, COMGETTER(Id)(bstrSnapGuid.asOutParam()));
+
+ Bstr bstrSnapName;
+ CHECK_ERROR_BREAK(pSnapshot, COMGETTER(Name)(bstrSnapName.asOutParam()));
+
+ ComPtr<IProgress> pProgress;
+
+ RTPrintf("%s snapshot '%ls' (%ls)\n",
+ fDelete ? "Deleting" : "Restoring", bstrSnapName.raw(), bstrSnapGuid.raw());
+
+ if (fDelete)
+ {
+ CHECK_ERROR_BREAK(sessionMachine, DeleteSnapshot(bstrSnapGuid.raw(),
+ pProgress.asOutParam()));
+ }
+ else
+ {
+ // restore or restore current
+ CHECK_ERROR_BREAK(sessionMachine, RestoreSnapshot(pSnapshot, pProgress.asOutParam()));
+ }
+
+ rc = showProgress(pProgress);
+ CHECK_PROGRESS_ERROR(pProgress, ("Snapshot operation failed"));
+ }
+ else if (!strcmp(a->argv[1], "edit"))
+ {
+ if (a->argc < 3)
+ {
+ errorSyntax(USAGE_SNAPSHOT, "Missing snapshot name");
+ rc = E_FAIL;
+ break;
+ }
+
+ ComPtr<ISnapshot> pSnapshot;
+
+ if ( !strcmp(a->argv[2], "--current")
+ || !strcmp(a->argv[2], "-current"))
+ {
+ CHECK_ERROR_BREAK(sessionMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam()));
+ if (pSnapshot.isNull())
+ {
+ RTPrintf("This machine does not have any snapshots\n");
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ else
+ {
+ CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
+ pSnapshot.asOutParam()));
+ }
+
+ /* parse options */
+ for (int i = 3; i < a->argc; i++)
+ {
+ if ( !strcmp(a->argv[i], "--name")
+ || !strcmp(a->argv[i], "-name")
+ || !strcmp(a->argv[i], "-newname"))
+ {
+ if (a->argc <= i + 1)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[i]);
+ rc = E_FAIL;
+ break;
+ }
+ i++;
+ pSnapshot->COMSETTER(Name)(Bstr(a->argv[i]).raw());
+ }
+ else if ( !strcmp(a->argv[i], "--description")
+ || !strcmp(a->argv[i], "-description")
+ || !strcmp(a->argv[i], "-newdesc"))
+ {
+ if (a->argc <= i + 1)
+ {
+ errorArgument("Missing argument to '%s'", a->argv[i]);
+ rc = E_FAIL;
+ break;
+ }
+ i++;
+ pSnapshot->COMSETTER(Description)(Bstr(a->argv[i]).raw());
+ }
+ else
+ {
+ errorSyntax(USAGE_SNAPSHOT, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
+ rc = E_FAIL;
+ break;
+ }
+ }
+
+ }
+ else if (!strcmp(a->argv[1], "showvminfo"))
+ {
+ /* exactly one parameter: snapshot name */
+ if (a->argc != 3)
+ {
+ errorSyntax(USAGE_SNAPSHOT, "Expecting snapshot name only");
+ rc = E_FAIL;
+ break;
+ }
+
+ ComPtr<ISnapshot> pSnapshot;
+
+ CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
+ pSnapshot.asOutParam()));
+
+ /* get the machine of the given snapshot */
+ ComPtr<IMachine> pMachine2;
+ pSnapshot->COMGETTER(Machine)(pMachine2.asOutParam());
+ showVMInfo(a->virtualBox, pMachine2, NULL, VMINFO_NONE);
+ }
+ else if (!strcmp(a->argv[1], "list"))
+ rc = handleSnapshotList(a, sessionMachine) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL;
+ else if (!strcmp(a->argv[1], "dump")) // undocumented parameter to debug snapshot info
+ DumpSnapshot(sessionMachine);
+ else
+ {
+ errorSyntax(USAGE_SNAPSHOT, "Invalid parameter '%s'", Utf8Str(a->argv[1]).c_str());
+ rc = E_FAIL;
+ }
+ } while (0);
+
+ a->session->UnlockMachine();
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp
new file mode 100644
index 00000000..0e1b7a28
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp
@@ -0,0 +1,1290 @@
+/* $Id: VBoxManageStorageController.cpp $ */
+/** @file
+ * VBoxManage - The storage controller related commands.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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 VBOX_ONLY_DOCS
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#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>
+
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/ctype.h>
+#include <iprt/stream.h>
+#include <iprt/getopt.h>
+#include <VBox/log.h>
+
+#include "VBoxManage.h"
+using namespace com;
+
+
+// funcs
+///////////////////////////////////////////////////////////////////////////////
+
+
+static const RTGETOPTDEF g_aStorageAttachOptions[] =
+{
+ { "--storagectl", 's', RTGETOPT_REQ_STRING },
+ { "--port", 'p', RTGETOPT_REQ_UINT32 },
+ { "--device", 'd', RTGETOPT_REQ_UINT32 },
+ { "--type", 't', RTGETOPT_REQ_STRING },
+ { "--medium", 'm', RTGETOPT_REQ_STRING },
+ { "--mtype", 'M', RTGETOPT_REQ_STRING },
+ { "--passthrough", 'h', RTGETOPT_REQ_STRING },
+ { "--tempeject", 'e', RTGETOPT_REQ_STRING },
+ { "--nonrotational", 'n', RTGETOPT_REQ_STRING },
+ { "--discard", 'u', RTGETOPT_REQ_STRING },
+ { "--hotpluggable", 'o', RTGETOPT_REQ_STRING },
+ { "--bandwidthgroup", 'b', RTGETOPT_REQ_STRING },
+ { "--forceunmount", 'f', RTGETOPT_REQ_NOTHING },
+ { "--comment", 'C', RTGETOPT_REQ_STRING },
+ { "--setuuid", 'q', RTGETOPT_REQ_STRING },
+ { "--setparentuuid", 'Q', RTGETOPT_REQ_STRING },
+ // iSCSI options
+ { "--server", 'S', RTGETOPT_REQ_STRING },
+ { "--target", 'T', RTGETOPT_REQ_STRING },
+ { "--tport", 'P', RTGETOPT_REQ_STRING },
+ { "--lun", 'L', RTGETOPT_REQ_STRING },
+ { "--encodedlun", 'E', RTGETOPT_REQ_STRING },
+ { "--username", 'U', RTGETOPT_REQ_STRING },
+ { "--password", 'W', RTGETOPT_REQ_STRING },
+ { "--passwordfile", 'w', RTGETOPT_REQ_STRING },
+ { "--initiator", 'N', RTGETOPT_REQ_STRING },
+ { "--intnet", 'I', RTGETOPT_REQ_NOTHING },
+};
+
+RTEXITCODE handleStorageAttach(HandlerArg *a)
+{
+ int c = VERR_INTERNAL_ERROR; /* initialized to shut up gcc */
+ HRESULT rc = S_OK;
+ ULONG port = ~0U;
+ ULONG device = ~0U;
+ bool fForceUnmount = false;
+ bool fSetMediumType = false;
+ bool fSetNewUuid = false;
+ bool fSetNewParentUuid = false;
+ MediumType_T enmMediumType = MediumType_Normal;
+ Bstr bstrComment;
+ const char *pszCtl = NULL;
+ DeviceType_T devTypeRequested = DeviceType_Null;
+ const char *pszMedium = NULL;
+ const char *pszPassThrough = NULL;
+ const char *pszTempEject = NULL;
+ const char *pszNonRotational = NULL;
+ const char *pszDiscard = NULL;
+ const char *pszHotPluggable = NULL;
+ const char *pszBandwidthGroup = NULL;
+ Bstr bstrNewUuid;
+ Bstr bstrNewParentUuid;
+ // iSCSI options
+ Bstr bstrServer;
+ Bstr bstrTarget;
+ Bstr bstrPort;
+ Bstr bstrLun;
+ Bstr bstrUsername;
+ Bstr bstrPassword;
+ Bstr bstrInitiator;
+ Bstr bstrIso;
+ Utf8Str strIso;
+ bool fIntNet = false;
+
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ ComPtr<IMachine> machine;
+ ComPtr<IStorageController> storageCtl;
+ ComPtr<ISystemProperties> systemProperties;
+
+ RTGetOptInit(&GetState, a->argc, a->argv, g_aStorageAttachOptions,
+ RT_ELEMENTS(g_aStorageAttachOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+
+ while ( SUCCEEDED(rc)
+ && (c = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (c)
+ {
+ case 's': // storage controller name
+ {
+ if (ValueUnion.psz)
+ pszCtl = ValueUnion.psz;
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ case 'p': // port
+ {
+ port = ValueUnion.u32;
+ break;
+ }
+
+ case 'd': // device
+ {
+ device = ValueUnion.u32;
+ break;
+ }
+
+ case 'm': // medium <none|emptydrive|additions|uuid|filename|host:<drive>|iSCSI>
+ {
+ if (ValueUnion.psz)
+ pszMedium = ValueUnion.psz;
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ case 't': // type <dvddrive|hdd|fdd>
+ {
+ if (ValueUnion.psz)
+ {
+ if (!RTStrICmp(ValueUnion.psz, "hdd"))
+ devTypeRequested = DeviceType_HardDisk;
+ else if (!RTStrICmp(ValueUnion.psz, "fdd"))
+ devTypeRequested = DeviceType_Floppy;
+ else if (!RTStrICmp(ValueUnion.psz, "dvddrive"))
+ devTypeRequested = DeviceType_DVD;
+ else
+ return errorArgument("Invalid --type argument '%s'", ValueUnion.psz);
+ }
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ case 'h': // passthrough <on|off>
+ {
+ if (ValueUnion.psz)
+ pszPassThrough = ValueUnion.psz;
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ case 'e': // tempeject <on|off>
+ {
+ if (ValueUnion.psz)
+ pszTempEject = ValueUnion.psz;
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ case 'n': // nonrotational <on|off>
+ {
+ if (ValueUnion.psz)
+ pszNonRotational = ValueUnion.psz;
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ case 'u': // discard <on|off>
+ {
+ if (ValueUnion.psz)
+ pszDiscard = ValueUnion.psz;
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ case 'o': // hotpluggable <on|off>
+ {
+ if (ValueUnion.psz)
+ pszHotPluggable = ValueUnion.psz;
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ case 'b': // bandwidthgroup <name>
+ {
+ if (ValueUnion.psz)
+ pszBandwidthGroup = ValueUnion.psz;
+ else
+ rc = E_FAIL;
+ break;
+ }
+
+ case 'f': // force unmount medium during runtime
+ {
+ fForceUnmount = true;
+ break;
+ }
+
+ case 'C':
+ if (ValueUnion.psz)
+ bstrComment = ValueUnion.psz;
+ else
+ rc = E_FAIL;
+ break;
+
+ case 'q':
+ if (ValueUnion.psz)
+ {
+ bstrNewUuid = ValueUnion.psz;
+ fSetNewUuid = true;
+ }
+ else
+ rc = E_FAIL;
+ break;
+
+ case 'Q':
+ if (ValueUnion.psz)
+ {
+ bstrNewParentUuid = ValueUnion.psz;
+ fSetNewParentUuid = true;
+ }
+ else
+ rc = E_FAIL;
+ break;
+
+ case 'S': // --server
+ bstrServer = ValueUnion.psz;
+ break;
+
+ case 'T': // --target
+ bstrTarget = ValueUnion.psz;
+ break;
+
+ case 'P': // --tport
+ bstrPort = ValueUnion.psz;
+ break;
+
+ case 'L': // --lun
+ bstrLun = ValueUnion.psz;
+ break;
+
+ case 'E': // --encodedlun
+ bstrLun = BstrFmt("enc%s", ValueUnion.psz);
+ break;
+
+ case 'U': // --username
+ bstrUsername = ValueUnion.psz;
+ break;
+
+ case 'W': // --password
+ bstrPassword = ValueUnion.psz;
+ break;
+
+ case 'w': // --passwordFile
+ {
+ Utf8Str utf8Password;
+ RTEXITCODE rcExit = readPasswordFile(ValueUnion.psz, &utf8Password);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ rc = E_FAIL;
+ bstrPassword = utf8Password;
+ break;
+ }
+ case 'N': // --initiator
+ bstrInitiator = ValueUnion.psz;
+ break;
+
+ case 'M': // --type
+ {
+ int vrc = parseMediumType(ValueUnion.psz, &enmMediumType);
+ if (RT_FAILURE(vrc))
+ return errorArgument("Invalid medium type '%s'", ValueUnion.psz);
+ fSetMediumType = true;
+ break;
+ }
+
+ case 'I': // --intnet
+ fIntNet = true;
+ break;
+
+ default:
+ {
+ errorGetOpt(USAGE_STORAGEATTACH, c, &ValueUnion);
+ rc = E_FAIL;
+ break;
+ }
+ }
+ }
+
+ if (FAILED(rc))
+ return RTEXITCODE_FAILURE;
+
+ if (!pszCtl)
+ return errorSyntax(USAGE_STORAGEATTACH, "Storage controller name not specified");
+
+ /* get the virtualbox system properties */
+ CHECK_ERROR_RET(a->virtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam()), RTEXITCODE_FAILURE);
+
+ // find the machine, lock it, get the mutable session machine
+ CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
+ SessionType_T st;
+ CHECK_ERROR_RET(a->session, COMGETTER(Type)(&st), RTEXITCODE_FAILURE);
+ a->session->COMGETTER(Machine)(machine.asOutParam());
+
+ try
+ {
+ bool fRunTime = (st == SessionType_Shared);
+
+ if (fRunTime)
+ {
+ if (pszPassThrough)
+ throw Utf8Str("Drive passthrough state cannot be changed while the VM is running\n");
+ else if (pszBandwidthGroup)
+ throw Utf8Str("Bandwidth group cannot be changed while the VM is running\n");
+ }
+
+ /* check if the storage controller is present */
+ rc = machine->GetStorageControllerByName(Bstr(pszCtl).raw(),
+ storageCtl.asOutParam());
+ if (FAILED(rc))
+ throw Utf8StrFmt("Could not find a controller named '%s'\n", pszCtl);
+
+ StorageBus_T storageBus = StorageBus_Null;
+ CHECK_ERROR_RET(storageCtl, COMGETTER(Bus)(&storageBus), RTEXITCODE_FAILURE);
+ ULONG maxPorts = 0;
+ CHECK_ERROR_RET(systemProperties, GetMaxPortCountForStorageBus(storageBus, &maxPorts), RTEXITCODE_FAILURE);
+ ULONG maxDevices = 0;
+ CHECK_ERROR_RET(systemProperties, GetMaxDevicesPerPortForStorageBus(storageBus, &maxDevices), RTEXITCODE_FAILURE);
+
+ if (port == ~0U)
+ {
+ if (maxPorts == 1)
+ port = 0;
+ else
+ return errorSyntax(USAGE_STORAGEATTACH, "Port not specified");
+ }
+ if (device == ~0U)
+ {
+ if (maxDevices == 1)
+ device = 0;
+ else
+ return errorSyntax(USAGE_STORAGEATTACH, "Device not specified");
+ }
+
+ /* for sata controller check if the port count is big enough
+ * to accommodate the current port which is being assigned
+ * else just increase the port count
+ */
+ {
+ ULONG ulPortCount = 0;
+ ULONG ulMaxPortCount = 0;
+
+ CHECK_ERROR(storageCtl, COMGETTER(MaxPortCount)(&ulMaxPortCount));
+ CHECK_ERROR(storageCtl, COMGETTER(PortCount)(&ulPortCount));
+
+ if ( (ulPortCount != ulMaxPortCount)
+ && (port >= ulPortCount)
+ && (port < ulMaxPortCount))
+ CHECK_ERROR(storageCtl, COMSETTER(PortCount)(port + 1));
+ }
+
+ StorageControllerType_T ctlType = StorageControllerType_Null;
+ CHECK_ERROR(storageCtl, COMGETTER(ControllerType)(&ctlType));
+
+ if (!RTStrICmp(pszMedium, "none"))
+ {
+ CHECK_ERROR(machine, DetachDevice(Bstr(pszCtl).raw(), port, device));
+ }
+ else if (!RTStrICmp(pszMedium, "emptydrive"))
+ {
+ if (fRunTime)
+ {
+ ComPtr<IMediumAttachment> mediumAttachment;
+ DeviceType_T deviceType = DeviceType_Null;
+ rc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port, device,
+ mediumAttachment.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ mediumAttachment->COMGETTER(Type)(&deviceType);
+
+ if ( (deviceType == DeviceType_DVD)
+ || (deviceType == DeviceType_Floppy))
+ {
+ /* just unmount the floppy/dvd */
+ CHECK_ERROR(machine, UnmountMedium(Bstr(pszCtl).raw(),
+ port,
+ device,
+ fForceUnmount));
+ }
+ }
+ else if (devTypeRequested == DeviceType_DVD)
+ {
+ /*
+ * Try to attach an empty DVD drive as a hotplug operation.
+ * Main will complain if the controller doesn't support hotplugging.
+ */
+ CHECK_ERROR(machine, AttachDeviceWithoutMedium(Bstr(pszCtl).raw(), port, device,
+ devTypeRequested));
+ deviceType = DeviceType_DVD; /* To avoid the error message below. */
+ }
+
+ if ( FAILED(rc)
+ || !( deviceType == DeviceType_DVD
+ || deviceType == DeviceType_Floppy)
+ )
+ throw Utf8StrFmt("No DVD/Floppy Drive attached to the controller '%s'"
+ "at the port: %u, device: %u", pszCtl, port, device);
+
+ }
+ else
+ {
+ DeviceType_T deviceType = DeviceType_Null;
+ com::SafeArray <DeviceType_T> saDeviceTypes;
+ ULONG driveCheck = 0;
+
+ /* check if the device type is supported by the controller */
+ CHECK_ERROR(systemProperties, GetDeviceTypesForStorageBus(storageBus, ComSafeArrayAsOutParam(saDeviceTypes)));
+ for (size_t i = 0; i < saDeviceTypes.size(); ++ i)
+ {
+ if ( (saDeviceTypes[i] == DeviceType_DVD)
+ || (saDeviceTypes[i] == DeviceType_Floppy))
+ driveCheck++;
+ }
+
+ if (!driveCheck)
+ throw Utf8StrFmt("The attachment is not supported by the storage controller '%s'", pszCtl);
+
+ if (storageBus == StorageBus_Floppy)
+ deviceType = DeviceType_Floppy;
+ else
+ deviceType = DeviceType_DVD;
+
+ /* attach a empty floppy/dvd drive after removing previous attachment */
+ machine->DetachDevice(Bstr(pszCtl).raw(), port, device);
+ CHECK_ERROR(machine, AttachDeviceWithoutMedium(Bstr(pszCtl).raw(), port, device,
+ deviceType));
+ }
+ } // end if (!RTStrICmp(pszMedium, "emptydrive"))
+ else
+ {
+ ComPtr<IMedium> pMedium2Mount;
+
+ // not "none", not "emptydrive": then it must be a UUID or filename or hostdrive or iSCSI;
+ // for all these we first need to know the type of drive we're attaching to
+ {
+ /*
+ * try to determine the type of the drive from the
+ * storage controller chipset, the attachment and
+ * the medium being attached
+ */
+ if (ctlType == StorageControllerType_I82078) // floppy controller
+ devTypeRequested = DeviceType_Floppy;
+ else
+ {
+ /*
+ * for SATA/SCSI/IDE it is hard to tell if it is a harddisk or
+ * a dvd being attached so lets check if the medium attachment
+ * and the medium, both are of same type. if yes then we are
+ * sure of its type and don't need the user to enter it manually
+ * else ask the user for the type.
+ */
+ ComPtr<IMediumAttachment> mediumAttachment;
+ rc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port,
+ device,
+ mediumAttachment.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ DeviceType_T deviceType;
+ mediumAttachment->COMGETTER(Type)(&deviceType);
+
+ 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 = openMedium(a, pszMedium, deviceType,
+ AccessMode_ReadWrite,
+ pExistingMedium,
+ false /* fForceNewUuidOnOpen */,
+ true /* fSilent */);
+ if (SUCCEEDED(rc) && pExistingMedium)
+ {
+ if ( (deviceType == DeviceType_DVD)
+ || (deviceType == DeviceType_HardDisk)
+ )
+ devTypeRequested = deviceType;
+ }
+ }
+ else
+ devTypeRequested = deviceType;
+ }
+ }
+ }
+
+ if (devTypeRequested == DeviceType_Null) // still the initializer value?
+ throw Utf8Str("Argument --type must be specified\n");
+
+ /* check if the device type is supported by the controller */
+ {
+ com::SafeArray <DeviceType_T> saDeviceTypes;
+
+ CHECK_ERROR(systemProperties, GetDeviceTypesForStorageBus(storageBus, ComSafeArrayAsOutParam(saDeviceTypes)));
+ if (SUCCEEDED(rc))
+ {
+ ULONG driveCheck = 0;
+ for (size_t i = 0; i < saDeviceTypes.size(); ++ i)
+ if (saDeviceTypes[i] == devTypeRequested)
+ driveCheck++;
+ if (!driveCheck)
+ throw Utf8StrFmt("The given attachment is not supported by the storage controller '%s'", pszCtl);
+ }
+ else
+ goto leave;
+ }
+
+ // find the medium given
+ /* host drive? */
+ if (!RTStrNICmp(pszMedium, RT_STR_TUPLE("host:")))
+ {
+ ComPtr<IHost> host;
+ CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
+
+ if (devTypeRequested == DeviceType_DVD)
+ {
+ rc = host->FindHostDVDDrive(Bstr(pszMedium + 5).raw(),
+ pMedium2Mount.asOutParam());
+ if (!pMedium2Mount)
+ {
+ /* 2nd try: try with the real name, important on Linux+libhal */
+ char szPathReal[RTPATH_MAX];
+ if (RT_FAILURE(RTPathReal(pszMedium + 5, szPathReal, sizeof(szPathReal))))
+ throw Utf8StrFmt("Invalid host DVD drive name \"%s\"", pszMedium + 5);
+ rc = host->FindHostDVDDrive(Bstr(szPathReal).raw(),
+ pMedium2Mount.asOutParam());
+ if (!pMedium2Mount)
+ throw Utf8StrFmt("Invalid host DVD drive name \"%s\"", pszMedium + 5);
+ }
+ }
+ else
+ {
+ // floppy
+ rc = host->FindHostFloppyDrive(Bstr(pszMedium + 5).raw(),
+ pMedium2Mount.asOutParam());
+ if (!pMedium2Mount)
+ throw Utf8StrFmt("Invalid host floppy drive name \"%s\"", pszMedium + 5);
+ }
+ }
+ else if (!RTStrICmp(pszMedium, "iSCSI"))
+ {
+ /* check for required options */
+ if (bstrServer.isEmpty() || bstrTarget.isEmpty())
+ throw Utf8StrFmt("Parameters --server and --target are required for iSCSI media");
+
+ /** @todo move the location stuff to Main, which can use pfnComposeName
+ * from the disk backends to construct the location properly. Also do
+ * not use slashes to separate the parts, as otherwise only the last
+ * element containing information will be shown. */
+ Bstr bstrISCSIMedium;
+ if ( bstrLun.isEmpty()
+ || (bstrLun == "0")
+ || (bstrLun == "enc0")
+ )
+ bstrISCSIMedium = BstrFmt("%ls|%ls", bstrServer.raw(), bstrTarget.raw());
+ else
+ bstrISCSIMedium = BstrFmt("%ls|%ls|%ls", bstrServer.raw(), bstrTarget.raw(), bstrLun.raw());
+
+ CHECK_ERROR(a->virtualBox, CreateMedium(Bstr("iSCSI").raw(),
+ bstrISCSIMedium.raw(),
+ AccessMode_ReadWrite,
+ DeviceType_HardDisk,
+ pMedium2Mount.asOutParam()));
+ if (FAILED(rc)) goto leave;
+ if (!bstrPort.isEmpty())
+ bstrServer = BstrFmt("%ls:%ls", bstrServer.raw(), bstrPort.raw());
+
+ // set the other iSCSI parameters as properties
+ com::SafeArray <BSTR> names;
+ com::SafeArray <BSTR> values;
+ Bstr("TargetAddress").detachTo(names.appendedRaw());
+ bstrServer.detachTo(values.appendedRaw());
+ Bstr("TargetName").detachTo(names.appendedRaw());
+ bstrTarget.detachTo(values.appendedRaw());
+
+ if (!bstrLun.isEmpty())
+ {
+ Bstr("LUN").detachTo(names.appendedRaw());
+ bstrLun.detachTo(values.appendedRaw());
+ }
+ if (!bstrUsername.isEmpty())
+ {
+ Bstr("InitiatorUsername").detachTo(names.appendedRaw());
+ bstrUsername.detachTo(values.appendedRaw());
+ }
+ if (!bstrPassword.isEmpty())
+ {
+ Bstr("InitiatorSecret").detachTo(names.appendedRaw());
+ bstrPassword.detachTo(values.appendedRaw());
+ }
+ if (!bstrInitiator.isEmpty())
+ {
+ Bstr("InitiatorName").detachTo(names.appendedRaw());
+ bstrInitiator.detachTo(values.appendedRaw());
+ }
+
+ /// @todo add --targetName and --targetPassword options
+
+ if (fIntNet)
+ {
+ Bstr("HostIPStack").detachTo(names.appendedRaw());
+ Bstr("0").detachTo(values.appendedRaw());
+ }
+
+ CHECK_ERROR(pMedium2Mount, SetProperties(ComSafeArrayAsInParam(names),
+ ComSafeArrayAsInParam(values)));
+ if (FAILED(rc)) goto leave;
+ Bstr guid;
+ CHECK_ERROR(pMedium2Mount, COMGETTER(Id)(guid.asOutParam()));
+ if (FAILED(rc)) goto leave;
+ RTPrintf("iSCSI disk created. UUID: %s\n", Utf8Str(guid).c_str());
+ }
+ else
+ {
+ if (!pszMedium)
+ {
+ ComPtr<IMediumAttachment> mediumAttachment;
+ rc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port,
+ device,
+ mediumAttachment.asOutParam());
+ if (FAILED(rc))
+ throw Utf8Str("Missing --medium argument");
+ }
+ else
+ {
+ Bstr bstrMedium(pszMedium);
+ rc = openMedium(a, pszMedium, devTypeRequested,
+ AccessMode_ReadWrite, pMedium2Mount,
+ fSetNewUuid, false /* fSilent */);
+ if (FAILED(rc) || !pMedium2Mount)
+ throw Utf8StrFmt("Invalid UUID or filename \"%s\"", pszMedium);
+ }
+ }
+
+ // set medium/parent medium UUID, if so desired
+ if (pMedium2Mount && (fSetNewUuid || fSetNewParentUuid))
+ {
+ CHECK_ERROR(pMedium2Mount, SetIds(fSetNewUuid, bstrNewUuid.raw(),
+ fSetNewParentUuid, bstrNewParentUuid.raw()));
+ if (FAILED(rc))
+ throw Utf8Str("Failed to set the medium/parent medium UUID");
+ }
+
+ // set medium type, if so desired
+ if (pMedium2Mount && fSetMediumType)
+ {
+ MediumType_T enmMediumTypeOld;
+ CHECK_ERROR(pMedium2Mount, COMGETTER(Type)(&enmMediumTypeOld));
+ if (SUCCEEDED(rc))
+ {
+ if (enmMediumTypeOld != enmMediumType)
+ {
+ CHECK_ERROR(pMedium2Mount, COMSETTER(Type)(enmMediumType));
+ if (FAILED(rc))
+ throw Utf8Str("Failed to set the medium type");
+ }
+ }
+ }
+
+ if (pMedium2Mount && !bstrComment.isEmpty())
+ {
+ CHECK_ERROR(pMedium2Mount, COMSETTER(Description)(bstrComment.raw()));
+ }
+
+ if (pszMedium)
+ {
+ switch (devTypeRequested)
+ {
+ case DeviceType_DVD:
+ case DeviceType_Floppy:
+ {
+ if (!fRunTime)
+ {
+ ComPtr<IMediumAttachment> mediumAttachment;
+ // check if there is a dvd/floppy drive at the given location, if not attach one first
+ rc = machine->GetMediumAttachment(Bstr(pszCtl).raw(),
+ port,
+ device,
+ mediumAttachment.asOutParam());
+ if (SUCCEEDED(rc))
+ {
+ DeviceType_T deviceType;
+ mediumAttachment->COMGETTER(Type)(&deviceType);
+ if (deviceType != devTypeRequested)
+ {
+ machine->DetachDevice(Bstr(pszCtl).raw(), port, device);
+ rc = machine->AttachDeviceWithoutMedium(Bstr(pszCtl).raw(),
+ port,
+ device,
+ devTypeRequested); // DeviceType_DVD or DeviceType_Floppy
+ }
+ }
+ else
+ {
+ rc = machine->AttachDeviceWithoutMedium(Bstr(pszCtl).raw(),
+ port,
+ device,
+ devTypeRequested); // DeviceType_DVD or DeviceType_Floppy
+ }
+ }
+
+ if (pMedium2Mount)
+ {
+ CHECK_ERROR(machine, MountMedium(Bstr(pszCtl).raw(),
+ port,
+ device,
+ pMedium2Mount,
+ fForceUnmount));
+ }
+ break;
+ } // end DeviceType_DVD or DeviceType_Floppy:
+
+ case DeviceType_HardDisk:
+ {
+ // if there is anything attached at the given location, remove it
+ machine->DetachDevice(Bstr(pszCtl).raw(), port, device);
+ CHECK_ERROR(machine, AttachDevice(Bstr(pszCtl).raw(),
+ port,
+ device,
+ DeviceType_HardDisk,
+ pMedium2Mount));
+ break;
+ }
+
+ default: break; /* Shut up MSC */
+ }
+ }
+ }
+
+ if ( pszPassThrough
+ && (SUCCEEDED(rc)))
+ {
+ ComPtr<IMediumAttachment> mattach;
+ CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
+ device, mattach.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ if (!RTStrICmp(pszPassThrough, "on"))
+ {
+ CHECK_ERROR(machine, PassthroughDevice(Bstr(pszCtl).raw(),
+ port, device, TRUE));
+ }
+ else if (!RTStrICmp(pszPassThrough, "off"))
+ {
+ CHECK_ERROR(machine, PassthroughDevice(Bstr(pszCtl).raw(),
+ port, device, FALSE));
+ }
+ else
+ throw Utf8StrFmt("Invalid --passthrough argument '%s'", pszPassThrough);
+ }
+ else
+ throw Utf8StrFmt("Couldn't find the controller attachment for the controller '%s'\n", pszCtl);
+ }
+
+ if ( pszTempEject
+ && (SUCCEEDED(rc)))
+ {
+ ComPtr<IMediumAttachment> mattach;
+ CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
+ device, mattach.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ if (!RTStrICmp(pszTempEject, "on"))
+ {
+ CHECK_ERROR(machine, TemporaryEjectDevice(Bstr(pszCtl).raw(),
+ port, device, TRUE));
+ }
+ else if (!RTStrICmp(pszTempEject, "off"))
+ {
+ CHECK_ERROR(machine, TemporaryEjectDevice(Bstr(pszCtl).raw(),
+ port, device, FALSE));
+ }
+ else
+ throw Utf8StrFmt("Invalid --tempeject argument '%s'", pszTempEject);
+ }
+ else
+ throw Utf8StrFmt("Couldn't find the controller attachment for the controller '%s'\n", pszCtl);
+ }
+
+ if ( pszNonRotational
+ && (SUCCEEDED(rc)))
+ {
+ ComPtr<IMediumAttachment> mattach;
+ CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
+ device, mattach.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ if (!RTStrICmp(pszNonRotational, "on"))
+ {
+ CHECK_ERROR(machine, NonRotationalDevice(Bstr(pszCtl).raw(),
+ port, device, TRUE));
+ }
+ else if (!RTStrICmp(pszNonRotational, "off"))
+ {
+ CHECK_ERROR(machine, NonRotationalDevice(Bstr(pszCtl).raw(),
+ port, device, FALSE));
+ }
+ else
+ throw Utf8StrFmt("Invalid --nonrotational argument '%s'", pszNonRotational);
+ }
+ else
+ throw Utf8StrFmt("Couldn't find the controller attachment for the controller '%s'\n", pszCtl);
+ }
+
+ if ( pszDiscard
+ && (SUCCEEDED(rc)))
+ {
+ ComPtr<IMediumAttachment> mattach;
+ CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
+ device, mattach.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ if (!RTStrICmp(pszDiscard, "on"))
+ {
+ CHECK_ERROR(machine, SetAutoDiscardForDevice(Bstr(pszCtl).raw(),
+ port, device, TRUE));
+ }
+ else if (!RTStrICmp(pszDiscard, "off"))
+ {
+ CHECK_ERROR(machine, SetAutoDiscardForDevice(Bstr(pszCtl).raw(),
+ port, device, FALSE));
+ }
+ else
+ throw Utf8StrFmt("Invalid --discard argument '%s'", pszDiscard);
+ }
+ else
+ throw Utf8StrFmt("Couldn't find the controller attachment for the controller '%s'\n", pszCtl);
+ }
+
+ if ( pszHotPluggable
+ && (SUCCEEDED(rc)))
+ {
+ ComPtr<IMediumAttachment> mattach;
+ CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
+ device, mattach.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ if (!RTStrICmp(pszHotPluggable, "on"))
+ {
+ CHECK_ERROR(machine, SetHotPluggableForDevice(Bstr(pszCtl).raw(),
+ port, device, TRUE));
+ }
+ else if (!RTStrICmp(pszHotPluggable, "off"))
+ {
+ CHECK_ERROR(machine, SetHotPluggableForDevice(Bstr(pszCtl).raw(),
+ port, device, FALSE));
+ }
+ else
+ throw Utf8StrFmt("Invalid --hotpluggable argument '%s'", pszHotPluggable);
+ }
+ else
+ throw Utf8StrFmt("Couldn't find the controller attachment for the controller '%s'\n", pszCtl);
+ }
+
+ if ( pszBandwidthGroup
+ && !fRunTime
+ && SUCCEEDED(rc))
+ {
+
+ if (!RTStrICmp(pszBandwidthGroup, "none"))
+ {
+ /* Just remove the bandwidth gorup. */
+ CHECK_ERROR(machine, SetNoBandwidthGroupForDevice(Bstr(pszCtl).raw(),
+ port, device));
+ }
+ else
+ {
+ ComPtr<IBandwidthControl> bwCtrl;
+ ComPtr<IBandwidthGroup> bwGroup;
+
+ CHECK_ERROR(machine, COMGETTER(BandwidthControl)(bwCtrl.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR(bwCtrl, GetBandwidthGroup(Bstr(pszBandwidthGroup).raw(), bwGroup.asOutParam()));
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR(machine, SetBandwidthGroupForDevice(Bstr(pszCtl).raw(),
+ port, device, bwGroup));
+ }
+ }
+ }
+ }
+
+ /* commit changes */
+ if (SUCCEEDED(rc))
+ CHECK_ERROR(machine, SaveSettings());
+ }
+ catch (const Utf8Str &strError)
+ {
+ errorArgument("%s", strError.c_str());
+ rc = E_FAIL;
+ }
+
+ // machine must always be unlocked, even on errors
+leave:
+ a->session->UnlockMachine();
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+static const RTGETOPTDEF g_aStorageControllerOptions[] =
+{
+ { "--name", 'n', RTGETOPT_REQ_STRING },
+ { "--add", 'a', RTGETOPT_REQ_STRING },
+ { "--controller", 'c', RTGETOPT_REQ_STRING },
+ { "--portcount", 'p', RTGETOPT_REQ_UINT32 },
+ { "--remove", 'r', RTGETOPT_REQ_NOTHING },
+ { "--rename", 'R', RTGETOPT_REQ_STRING },
+ { "--hostiocache", 'i', RTGETOPT_REQ_STRING },
+ { "--bootable", 'b', RTGETOPT_REQ_STRING },
+};
+
+RTEXITCODE handleStorageController(HandlerArg *a)
+{
+ int c;
+ const char *pszCtl = NULL;
+ const char *pszBusType = NULL;
+ const char *pszCtlType = NULL;
+ const char *pszHostIOCache = NULL;
+ const char *pszBootable = NULL;
+ const char *pszCtlNewName = NULL;
+ ULONG portcount = ~0U;
+ bool fRemoveCtl = false;
+ ComPtr<IMachine> machine;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+
+ if (a->argc < 4)
+ return errorSyntax(USAGE_STORAGECONTROLLER, "Too few parameters");
+
+ RTGetOptInit (&GetState, a->argc, a->argv, g_aStorageControllerOptions,
+ RT_ELEMENTS(g_aStorageControllerOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+
+ while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (c)
+ {
+ case 'n': // controller name
+ Assert(ValueUnion.psz);
+ pszCtl = ValueUnion.psz;
+ break;
+
+ case 'a': // controller bus type <ide/sata/scsi/floppy>
+ Assert(ValueUnion.psz);
+ pszBusType = ValueUnion.psz;
+ break;
+
+ case 'c': // controller <lsilogic/buslogic/intelahci/piix3/piix4/ich6/i82078>
+ Assert(ValueUnion.psz);
+ pszCtlType = ValueUnion.psz;
+ break;
+
+ case 'p': // portcount
+ portcount = ValueUnion.u32;
+ break;
+
+ case 'r': // remove controller
+ fRemoveCtl = true;
+ break;
+
+ case 'R': // rename controller
+ Assert(ValueUnion.psz);
+ pszCtlNewName = ValueUnion.psz;
+ break;
+
+ case 'i':
+ pszHostIOCache = ValueUnion.psz;
+ break;
+
+ case 'b':
+ pszBootable = ValueUnion.psz;
+ break;
+
+ default:
+ return errorGetOpt(USAGE_STORAGECONTROLLER, c, &ValueUnion);
+ }
+ }
+
+ HRESULT rc;
+
+ /* try to find the given machine */
+ CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
+ machine.asOutParam()), RTEXITCODE_FAILURE);
+
+ /* open a session for the VM */
+ CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), RTEXITCODE_FAILURE);
+
+ /* get the mutable session machine */
+ a->session->COMGETTER(Machine)(machine.asOutParam());
+
+ if (!pszCtl)
+ {
+ /* it's important to always close sessions */
+ a->session->UnlockMachine();
+ return errorSyntax(USAGE_STORAGECONTROLLER, "Storage controller name not specified\n");
+ }
+
+ if (fRemoveCtl)
+ {
+ CHECK_ERROR(machine, RemoveStorageController(Bstr(pszCtl).raw()));
+ }
+ else
+ {
+ if (pszBusType)
+ {
+ ComPtr<IStorageController> ctl;
+
+ if (!RTStrICmp(pszBusType, "ide"))
+ {
+ CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
+ StorageBus_IDE,
+ ctl.asOutParam()));
+ }
+ else if (!RTStrICmp(pszBusType, "sata"))
+ {
+ CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
+ StorageBus_SATA,
+ ctl.asOutParam()));
+ }
+ else if (!RTStrICmp(pszBusType, "scsi"))
+ {
+ CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
+ StorageBus_SCSI,
+ ctl.asOutParam()));
+ }
+ else if (!RTStrICmp(pszBusType, "floppy"))
+ {
+ CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
+ StorageBus_Floppy,
+ ctl.asOutParam()));
+ }
+ else if (!RTStrICmp(pszBusType, "sas"))
+ {
+ CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
+ StorageBus_SAS,
+ ctl.asOutParam()));
+ }
+ else if (!RTStrICmp(pszBusType, "usb"))
+ {
+ CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
+ StorageBus_USB,
+ ctl.asOutParam()));
+ }
+ else if (!RTStrICmp(pszBusType, "pcie"))
+ {
+ CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
+ StorageBus_PCIe,
+ ctl.asOutParam()));
+ }
+ else
+ {
+ errorArgument("Invalid --add argument '%s'", pszBusType);
+ rc = E_FAIL;
+ }
+ }
+
+ if ( pszCtlType
+ && SUCCEEDED(rc))
+ {
+ ComPtr<IStorageController> ctl;
+
+ CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
+ ctl.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ if (!RTStrICmp(pszCtlType, "lsilogic"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_LsiLogic));
+ }
+ else if (!RTStrICmp(pszCtlType, "buslogic"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_BusLogic));
+ }
+ else if (!RTStrICmp(pszCtlType, "intelahci"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_IntelAhci));
+ }
+ else if (!RTStrICmp(pszCtlType, "piix3"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_PIIX3));
+ }
+ else if (!RTStrICmp(pszCtlType, "piix4"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_PIIX4));
+ }
+ else if (!RTStrICmp(pszCtlType, "ich6"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_ICH6));
+ }
+ else if (!RTStrICmp(pszCtlType, "i82078"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_I82078));
+ }
+ else if (!RTStrICmp(pszCtlType, "lsilogicsas"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_LsiLogicSas));
+ }
+ else if (!RTStrICmp(pszCtlType, "usb"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_USB));
+ }
+ else if (!RTStrICmp(pszCtlType, "nvme"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_NVMe));
+ }
+ else
+ {
+ errorArgument("Invalid --type argument '%s'", pszCtlType);
+ rc = E_FAIL;
+ }
+ }
+ else
+ {
+ errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
+ rc = E_FAIL;
+ }
+ }
+
+ if ( (portcount != ~0U)
+ && SUCCEEDED(rc))
+ {
+ ComPtr<IStorageController> ctl;
+
+ CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
+ ctl.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR(ctl, COMSETTER(PortCount)(portcount));
+ }
+ else
+ {
+ errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
+ rc = E_FAIL;
+ }
+ }
+
+ if ( pszHostIOCache
+ && SUCCEEDED(rc))
+ {
+ ComPtr<IStorageController> ctl;
+
+ CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
+ ctl.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ if (!RTStrICmp(pszHostIOCache, "on"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(UseHostIOCache)(TRUE));
+ }
+ else if (!RTStrICmp(pszHostIOCache, "off"))
+ {
+ CHECK_ERROR(ctl, COMSETTER(UseHostIOCache)(FALSE));
+ }
+ else
+ {
+ errorArgument("Invalid --hostiocache argument '%s'", pszHostIOCache);
+ rc = E_FAIL;
+ }
+ }
+ else
+ {
+ errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
+ rc = E_FAIL;
+ }
+ }
+
+ if ( pszBootable
+ && SUCCEEDED(rc))
+ {
+ if (SUCCEEDED(rc))
+ {
+ if (!RTStrICmp(pszBootable, "on"))
+ {
+ CHECK_ERROR(machine, SetStorageControllerBootable(Bstr(pszCtl).raw(), TRUE));
+ }
+ else if (!RTStrICmp(pszBootable, "off"))
+ {
+ CHECK_ERROR(machine, SetStorageControllerBootable(Bstr(pszCtl).raw(), FALSE));
+ }
+ else
+ {
+ errorArgument("Invalid --bootable argument '%s'", pszBootable);
+ rc = E_FAIL;
+ }
+ }
+ else
+ {
+ errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
+ rc = E_FAIL;
+ }
+ }
+
+ if ( pszCtlNewName
+ && SUCCEEDED(rc))
+ {
+ ComPtr<IStorageController> ctl;
+
+ CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
+ ctl.asOutParam()));
+
+ if (SUCCEEDED(rc))
+ {
+ CHECK_ERROR(ctl, COMSETTER(Name)(Bstr(pszCtlNewName).raw()));
+ }
+ else
+ {
+ errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
+ rc = E_FAIL;
+ }
+ }
+
+ }
+
+ /* commit changes */
+ if (SUCCEEDED(rc))
+ CHECK_ERROR(machine, SaveSettings());
+
+ /* it's important to always close sessions */
+ a->session->UnlockMachine();
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+#endif /* !VBOX_ONLY_DOCS */
+
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp
new file mode 100644
index 00000000..237c85d0
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageUSB.cpp
@@ -0,0 +1,600 @@
+/* $Id: VBoxManageUSB.cpp $ */
+/** @file
+ * VBoxManage - VirtualBox's command-line interface.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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.
+ */
+
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/array.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/com/VirtualBox.h>
+
+#include "VBoxManage.h"
+
+#include <iprt/asm.h>
+
+using namespace com;
+
+/**
+ * Quick IUSBDevice implementation for detaching / attaching
+ * devices to the USB Controller.
+ */
+class MyUSBDevice : public IUSBDevice
+{
+public:
+ // public initializer/uninitializer for internal purposes only
+ MyUSBDevice(uint16_t a_u16VendorId, uint16_t a_u16ProductId, uint16_t a_bcdRevision, uint64_t a_u64SerialHash, const char *a_pszComment)
+ : m_usVendorId(a_u16VendorId), m_usProductId(a_u16ProductId),
+ m_bcdRevision(a_bcdRevision), m_u64SerialHash(a_u64SerialHash),
+ m_bstrComment(a_pszComment),
+ m_cRefs(0)
+ {
+ }
+ virtual ~MyUSBDevice() {}
+
+ STDMETHOD_(ULONG, AddRef)(void)
+ {
+ return ASMAtomicIncU32(&m_cRefs);
+ }
+ STDMETHOD_(ULONG, Release)(void)
+ {
+ ULONG cRefs = ASMAtomicDecU32(&m_cRefs);
+ if (!cRefs)
+ delete this;
+ return cRefs;
+ }
+ STDMETHOD(QueryInterface)(const IID &iid, void **ppvObject)
+ {
+ Guid guid(iid);
+ if (guid == Guid(COM_IIDOF(IUnknown)))
+ *ppvObject = (IUnknown *)this;
+#ifdef RT_OS_WINDOWS
+ else if (guid == Guid(COM_IIDOF(IDispatch)))
+ *ppvObject = (IDispatch *)this;
+#endif
+ else if (guid == Guid(COM_IIDOF(IUSBDevice)))
+ *ppvObject = (IUSBDevice *)this;
+ else
+ return E_NOINTERFACE;
+ AddRef();
+ return S_OK;
+ }
+
+ STDMETHOD(COMGETTER(Id))(OUT_GUID a_pId) { NOREF(a_pId); return E_NOTIMPL; }
+ STDMETHOD(COMGETTER(VendorId))(USHORT *a_pusVendorId) { *a_pusVendorId = m_usVendorId; return S_OK; }
+ STDMETHOD(COMGETTER(ProductId))(USHORT *a_pusProductId) { *a_pusProductId = m_usProductId; return S_OK; }
+ STDMETHOD(COMGETTER(Revision))(USHORT *a_pusRevision) { *a_pusRevision = m_bcdRevision; return S_OK; }
+ STDMETHOD(COMGETTER(SerialHash))(ULONG64 *a_pullSerialHash) { *a_pullSerialHash = m_u64SerialHash; return S_OK; }
+ STDMETHOD(COMGETTER(Manufacturer))(BSTR *a_pManufacturer) { NOREF(a_pManufacturer); return E_NOTIMPL; }
+ STDMETHOD(COMGETTER(Product))(BSTR *a_pProduct) { NOREF(a_pProduct); return E_NOTIMPL; }
+ STDMETHOD(COMGETTER(SerialNumber))(BSTR *a_pSerialNumber) { NOREF(a_pSerialNumber); return E_NOTIMPL; }
+ STDMETHOD(COMGETTER(Address))(BSTR *a_pAddress) { NOREF(a_pAddress); return E_NOTIMPL; }
+
+private:
+ /** The vendor id of this USB device. */
+ USHORT m_usVendorId;
+ /** The product id of this USB device. */
+ USHORT m_usProductId;
+ /** The product revision number of this USB device.
+ * (high byte = integer; low byte = decimal) */
+ USHORT m_bcdRevision;
+ /** The USB serial hash of the device. */
+ uint64_t m_u64SerialHash;
+ /** The user comment string. */
+ Bstr m_bstrComment;
+ /** Reference counter. */
+ uint32_t volatile m_cRefs;
+};
+
+
+// types
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+class Nullable
+{
+public:
+
+ Nullable() : mIsNull(true) {}
+ Nullable(const T &aValue, bool aIsNull = false)
+ : mIsNull(aIsNull), mValue(aValue) {}
+
+ bool isNull() const { return mIsNull; };
+ void setNull(bool aIsNull = true) { mIsNull = aIsNull; }
+
+ operator const T&() const { return mValue; }
+
+ Nullable &operator= (const T &aValue)
+ {
+ mValue = aValue;
+ mIsNull = false;
+ return *this;
+ }
+
+private:
+
+ bool mIsNull;
+ T mValue;
+};
+
+/** helper structure to encapsulate USB filter manipulation commands */
+struct USBFilterCmd
+{
+ struct USBFilter
+ {
+ USBFilter()
+ : mAction(USBDeviceFilterAction_Null)
+ {}
+
+ Bstr mName;
+ Nullable <bool> mActive;
+ Bstr mVendorId;
+ Bstr mProductId;
+ Bstr mRevision;
+ Bstr mManufacturer;
+ Bstr mProduct;
+ Bstr mRemote;
+ Bstr mSerialNumber;
+ Nullable <ULONG> mMaskedInterfaces;
+ USBDeviceFilterAction_T mAction;
+ };
+
+ enum Action { Invalid, Add, Modify, Remove };
+
+ USBFilterCmd() : mAction(Invalid), mIndex(0), mGlobal(false) {}
+
+ Action mAction;
+ uint32_t mIndex;
+ /** flag whether the command target is a global filter */
+ bool mGlobal;
+ /** machine this command is targeted at (null for global filters) */
+ ComPtr<IMachine> mMachine;
+ USBFilter mFilter;
+};
+
+RTEXITCODE handleUSBFilter(HandlerArg *a)
+{
+ HRESULT rc = S_OK;
+ USBFilterCmd cmd;
+
+ /* at least: 0: command, 1: index, 2: --target, 3: <target value> */
+ if (a->argc < 4)
+ return errorSyntax(USAGE_USBFILTER, "Not enough parameters");
+
+ /* which command? */
+ cmd.mAction = USBFilterCmd::Invalid;
+ if (!strcmp(a->argv[0], "add")) cmd.mAction = USBFilterCmd::Add;
+ else if (!strcmp(a->argv[0], "modify")) cmd.mAction = USBFilterCmd::Modify;
+ else if (!strcmp(a->argv[0], "remove")) cmd.mAction = USBFilterCmd::Remove;
+
+ if (cmd.mAction == USBFilterCmd::Invalid)
+ return errorSyntax(USAGE_USBFILTER, "Invalid parameter '%s'", a->argv[0]);
+
+ /* which index? */
+ if (VINF_SUCCESS != RTStrToUInt32Full(a->argv[1], 10, &cmd.mIndex))
+ return errorSyntax(USAGE_USBFILTER, "Invalid index '%s'", a->argv[1]);
+
+ switch (cmd.mAction)
+ {
+ case USBFilterCmd::Add:
+ case USBFilterCmd::Modify:
+ {
+ /* at least: 0: command, 1: index, 2: --target, 3: <target value>, 4: --name, 5: <name value> */
+ if (a->argc < 6)
+ {
+ if (cmd.mAction == USBFilterCmd::Add)
+ return errorSyntax(USAGE_USBFILTER_ADD, "Not enough parameters");
+
+ return errorSyntax(USAGE_USBFILTER_MODIFY, "Not enough parameters");
+ }
+
+ // set Active to true by default
+ // (assuming that the user sets up all necessary attributes
+ // at once and wants the filter to be active immediately)
+ if (cmd.mAction == USBFilterCmd::Add)
+ cmd.mFilter.mActive = true;
+
+ for (int i = 2; i < a->argc; i++)
+ {
+ if ( !strcmp(a->argv[i], "--target")
+ || !strcmp(a->argv[i], "-target"))
+ {
+ if (a->argc <= i + 1 || !*a->argv[i+1])
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ if (!strcmp(a->argv[i], "global"))
+ cmd.mGlobal = true;
+ else
+ {
+ /* assume it's a UUID of a machine */
+ CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[i]).raw(),
+ cmd.mMachine.asOutParam()), RTEXITCODE_FAILURE);
+ }
+ }
+ else if ( !strcmp(a->argv[i], "--name")
+ || !strcmp(a->argv[i], "-name"))
+ {
+ if (a->argc <= i + 1 || !*a->argv[i+1])
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ cmd.mFilter.mName = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--active")
+ || !strcmp(a->argv[i], "-active"))
+ {
+ if (a->argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ if (!strcmp(a->argv[i], "yes"))
+ cmd.mFilter.mActive = true;
+ else if (!strcmp(a->argv[i], "no"))
+ cmd.mFilter.mActive = false;
+ else
+ return errorArgument("Invalid --active argument '%s'", a->argv[i]);
+ }
+ else if ( !strcmp(a->argv[i], "--vendorid")
+ || !strcmp(a->argv[i], "-vendorid"))
+ {
+ if (a->argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ cmd.mFilter.mVendorId = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--productid")
+ || !strcmp(a->argv[i], "-productid"))
+ {
+ if (a->argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ cmd.mFilter.mProductId = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--revision")
+ || !strcmp(a->argv[i], "-revision"))
+ {
+ if (a->argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ cmd.mFilter.mRevision = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--manufacturer")
+ || !strcmp(a->argv[i], "-manufacturer"))
+ {
+ if (a->argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ cmd.mFilter.mManufacturer = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--product")
+ || !strcmp(a->argv[i], "-product"))
+ {
+ if (a->argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ cmd.mFilter.mProduct = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--remote")
+ || !strcmp(a->argv[i], "-remote"))
+ {
+ if (a->argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ cmd.mFilter.mRemote = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--serialnumber")
+ || !strcmp(a->argv[i], "-serialnumber"))
+ {
+ if (a->argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ cmd.mFilter.mSerialNumber = a->argv[i];
+ }
+ else if ( !strcmp(a->argv[i], "--maskedinterfaces")
+ || !strcmp(a->argv[i], "-maskedinterfaces"))
+ {
+ if (a->argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ uint32_t u32;
+ int vrc = RTStrToUInt32Full(a->argv[i], 0, &u32);
+ if (RT_FAILURE(vrc))
+ return errorArgument("Failed to convert the --maskedinterfaces value '%s' to a number, vrc=%Rrc", a->argv[i], vrc);
+ cmd.mFilter.mMaskedInterfaces = u32;
+ }
+ else if ( !strcmp(a->argv[i], "--action")
+ || !strcmp(a->argv[i], "-action"))
+ {
+ if (a->argc <= i + 1)
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ if (!strcmp(a->argv[i], "ignore"))
+ cmd.mFilter.mAction = USBDeviceFilterAction_Ignore;
+ else if (!strcmp(a->argv[i], "hold"))
+ cmd.mFilter.mAction = USBDeviceFilterAction_Hold;
+ else
+ return errorArgument("Invalid USB filter action '%s'", a->argv[i]);
+ }
+ else
+ return errorSyntax(cmd.mAction == USBFilterCmd::Add ? USAGE_USBFILTER_ADD : USAGE_USBFILTER_MODIFY,
+ "Unknown option '%s'", a->argv[i]);
+ }
+
+ if (cmd.mAction == USBFilterCmd::Add)
+ {
+ // mandatory/forbidden options
+ if ( cmd.mFilter.mName.isEmpty()
+ ||
+ ( cmd.mGlobal
+ && cmd.mFilter.mAction == USBDeviceFilterAction_Null
+ )
+ || ( !cmd.mGlobal
+ && !cmd.mMachine)
+ || ( cmd.mGlobal
+ && !cmd.mFilter.mRemote.isEmpty())
+ )
+ {
+ return errorSyntax(USAGE_USBFILTER_ADD, "Mandatory options not supplied");
+ }
+ }
+ break;
+ }
+
+ case USBFilterCmd::Remove:
+ {
+ /* at least: 0: command, 1: index, 2: --target, 3: <target value> */
+ if (a->argc < 4)
+ return errorSyntax(USAGE_USBFILTER_REMOVE, "Not enough parameters");
+
+ for (int i = 2; i < a->argc; i++)
+ {
+ if ( !strcmp(a->argv[i], "--target")
+ || !strcmp(a->argv[i], "-target"))
+ {
+ if (a->argc <= i + 1 || !*a->argv[i+1])
+ return errorArgument("Missing argument to '%s'", a->argv[i]);
+ i++;
+ if (!strcmp(a->argv[i], "global"))
+ cmd.mGlobal = true;
+ else
+ {
+ CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[i]).raw(),
+ cmd.mMachine.asOutParam()), RTEXITCODE_FAILURE);
+ }
+ }
+ }
+
+ // mandatory options
+ if (!cmd.mGlobal && !cmd.mMachine)
+ return errorSyntax(USAGE_USBFILTER_REMOVE, "Mandatory options not supplied");
+
+ break;
+ }
+
+ default: break;
+ }
+
+ USBFilterCmd::USBFilter &f = cmd.mFilter;
+
+ ComPtr<IHost> host;
+ ComPtr<IUSBDeviceFilters> flts;
+ if (cmd.mGlobal)
+ CHECK_ERROR_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE);
+ else
+ {
+ /* open a session for the VM */
+ CHECK_ERROR_RET(cmd.mMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
+ /* get the mutable session machine */
+ a->session->COMGETTER(Machine)(cmd.mMachine.asOutParam());
+ /* and get the USB device filters */
+ CHECK_ERROR_RET(cmd.mMachine, COMGETTER(USBDeviceFilters)(flts.asOutParam()), RTEXITCODE_FAILURE);
+ }
+
+ switch (cmd.mAction)
+ {
+ case USBFilterCmd::Add:
+ {
+ if (cmd.mGlobal)
+ {
+ ComPtr<IHostUSBDeviceFilter> flt;
+ CHECK_ERROR_BREAK(host, CreateUSBDeviceFilter(f.mName.raw(),
+ flt.asOutParam()));
+
+ if (!f.mActive.isNull())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Active)(f.mActive));
+ if (!f.mVendorId.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(VendorId)(f.mVendorId.raw()));
+ if (!f.mProductId.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(ProductId)(f.mProductId.raw()));
+ if (!f.mRevision.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Revision)(f.mRevision.raw()));
+ if (!f.mManufacturer.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Manufacturer)(f.mManufacturer.raw()));
+ if (!f.mSerialNumber.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(SerialNumber)(f.mSerialNumber.raw()));
+ if (!f.mMaskedInterfaces.isNull())
+ CHECK_ERROR_BREAK(flt, COMSETTER(MaskedInterfaces)(f.mMaskedInterfaces));
+
+ if (f.mAction != USBDeviceFilterAction_Null)
+ CHECK_ERROR_BREAK(flt, COMSETTER(Action)(f.mAction));
+
+ CHECK_ERROR_BREAK(host, InsertUSBDeviceFilter(cmd.mIndex, flt));
+ }
+ else
+ {
+ ComPtr<IUSBDeviceFilter> flt;
+ CHECK_ERROR_BREAK(flts, CreateDeviceFilter(f.mName.raw(),
+ flt.asOutParam()));
+
+ if (!f.mActive.isNull())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Active)(f.mActive));
+ if (!f.mVendorId.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(VendorId)(f.mVendorId.raw()));
+ if (!f.mProductId.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(ProductId)(f.mProductId.raw()));
+ if (!f.mRevision.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Revision)(f.mRevision.raw()));
+ if (!f.mManufacturer.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Manufacturer)(f.mManufacturer.raw()));
+ if (!f.mRemote.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Remote)(f.mRemote.raw()));
+ if (!f.mSerialNumber.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(SerialNumber)(f.mSerialNumber.raw()));
+ if (!f.mMaskedInterfaces.isNull())
+ CHECK_ERROR_BREAK(flt, COMSETTER(MaskedInterfaces)(f.mMaskedInterfaces));
+
+ CHECK_ERROR_BREAK(flts, InsertDeviceFilter(cmd.mIndex, flt));
+ }
+ break;
+ }
+ case USBFilterCmd::Modify:
+ {
+ if (cmd.mGlobal)
+ {
+ SafeIfaceArray <IHostUSBDeviceFilter> coll;
+ CHECK_ERROR_BREAK(host, COMGETTER(USBDeviceFilters)(ComSafeArrayAsOutParam(coll)));
+
+ ComPtr<IHostUSBDeviceFilter> flt = coll[cmd.mIndex];
+
+ if (!f.mName.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Name)(f.mName.raw()));
+ if (!f.mActive.isNull())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Active)(f.mActive));
+ if (!f.mVendorId.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(VendorId)(f.mVendorId.raw()));
+ if (!f.mProductId.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(ProductId)(f.mProductId.raw()));
+ if (!f.mRevision.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Revision)(f.mRevision.raw()));
+ if (!f.mManufacturer.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Manufacturer)(f.mManufacturer.raw()));
+ if (!f.mSerialNumber.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(SerialNumber)(f.mSerialNumber.raw()));
+ if (!f.mMaskedInterfaces.isNull())
+ CHECK_ERROR_BREAK(flt, COMSETTER(MaskedInterfaces)(f.mMaskedInterfaces));
+
+ if (f.mAction != USBDeviceFilterAction_Null)
+ CHECK_ERROR_BREAK(flt, COMSETTER(Action)(f.mAction));
+ }
+ else
+ {
+ SafeIfaceArray <IUSBDeviceFilter> coll;
+ CHECK_ERROR_BREAK(flts, COMGETTER(DeviceFilters)(ComSafeArrayAsOutParam(coll)));
+
+ ComPtr<IUSBDeviceFilter> flt = coll[cmd.mIndex];
+
+ if (!f.mName.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Name)(f.mName.raw()));
+ if (!f.mActive.isNull())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Active)(f.mActive));
+ if (!f.mVendorId.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(VendorId)(f.mVendorId.raw()));
+ if (!f.mProductId.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(ProductId)(f.mProductId.raw()));
+ if (!f.mRevision.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Revision)(f.mRevision.raw()));
+ if (!f.mManufacturer.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Manufacturer)(f.mManufacturer.raw()));
+ if (!f.mRemote.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(Remote)(f.mRemote.raw()));
+ if (!f.mSerialNumber.isEmpty())
+ CHECK_ERROR_BREAK(flt, COMSETTER(SerialNumber)(f.mSerialNumber.raw()));
+ if (!f.mMaskedInterfaces.isNull())
+ CHECK_ERROR_BREAK(flt, COMSETTER(MaskedInterfaces)(f.mMaskedInterfaces));
+ }
+ break;
+ }
+ case USBFilterCmd::Remove:
+ {
+ if (cmd.mGlobal)
+ {
+ ComPtr<IHostUSBDeviceFilter> flt;
+ CHECK_ERROR_BREAK(host, RemoveUSBDeviceFilter(cmd.mIndex));
+ }
+ else
+ {
+ ComPtr<IUSBDeviceFilter> flt;
+ CHECK_ERROR_BREAK(flts, RemoveDeviceFilter(cmd.mIndex, flt.asOutParam()));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (cmd.mMachine)
+ {
+ if (SUCCEEDED(rc))
+ {
+ /* commit the session */
+ CHECK_ERROR(cmd.mMachine, SaveSettings());
+ }
+ /* close the session */
+ a->session->UnlockMachine();
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+RTEXITCODE handleUSBDevSource(HandlerArg *a)
+{
+ HRESULT rc = S_OK;
+
+ /* at least: 0: command, 1: source id */
+ if (a->argc < 2)
+ return errorSyntax(USAGE_USBDEVSOURCE, "Not enough parameters");
+
+ ComPtr<IHost> host;
+ if (!strcmp(a->argv[0], "add"))
+ {
+ Bstr strBackend;
+ Bstr strAddress;
+ if (a->argc != 6)
+ return errorSyntax(USAGE_USBDEVSOURCE, "Invalid number of parameters");
+
+ for (int i = 2; i < a->argc; i++)
+ {
+ if (!strcmp(a->argv[i], "--backend"))
+ {
+ i++;
+ strBackend = a->argv[i];
+ }
+ else if (!strcmp(a->argv[i], "--address"))
+ {
+ i++;
+ strAddress = a->argv[i];
+ }
+ else
+ return errorSyntax(USAGE_USBDEVSOURCE, "Parameter \"%s\" is invalid", a->argv[i]);
+ }
+
+ SafeArray<BSTR> usbSourcePropNames;
+ SafeArray<BSTR> usbSourcePropValues;
+
+ CHECK_ERROR_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(host, AddUSBDeviceSource(strBackend.raw(), Bstr(a->argv[1]).raw(), strAddress.raw(),
+ ComSafeArrayAsInParam(usbSourcePropNames), ComSafeArrayAsInParam(usbSourcePropValues)),
+ RTEXITCODE_FAILURE);
+ }
+ else if (!strcmp(a->argv[0], "remove"))
+ {
+ CHECK_ERROR_RET(a->virtualBox, COMGETTER(Host)(host.asOutParam()), RTEXITCODE_FAILURE);
+ CHECK_ERROR_RET(host, RemoveUSBDeviceSource(Bstr(a->argv[1]).raw()), RTEXITCODE_FAILURE);
+ }
+
+ return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */